shithub: choc

Download patch

ref: 0df2cb80cf03d7259746834220d209b306a8c503
parent: 6a294daa7859eaf0250aa4a77484dd11550e5c5e
author: Simon Howard <fraggle@gmail.com>
date: Thu Sep 4 19:15:36 EDT 2008

Add GPLed Heretic/Hexen source.

Subversion-branch: /branches/raven-branch
Subversion-revision: 1195

--- /dev/null
+++ b/src/heretic/am_data.h
@@ -1,0 +1,90 @@
+// AM_data.h : The vector graphics for the automap
+
+#ifndef __AMDATA_H__
+#define __AMDATA_H__
+
+#pragma once
+
+// 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[] = {
+  { { -.867*R, -.5*R }, { .867*R, -.5*R } },
+  { { .867*R, -.5*R } , { 0, R } },
+  { { 0, R }, { -.867*R, -.5*R } }
+  };
+#undef R
+#define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t))
+
+#define R (FRACUNIT)
+mline_t thintriangle_guy[] = {
+  { { -.5*R, -.7*R }, { R, 0 } },
+  { { R, 0 }, { -.5*R, .7*R } },
+  { { -.5*R, .7*R }, { -.5*R, -.7*R } }
+  };
+#undef R
+#define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t))
+
+#endif
--- /dev/null
+++ b/src/heretic/am_map.c
@@ -1,0 +1,1352 @@
+
+// AM_map.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "AM_map.h"
+#include "AM_data.h"
+#include <stdio.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 = 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?-MAXINT:MAXINT);
+  else is->islp = FixedDiv(dx, dy);
+  if (!dx) is->slp = (dy<0?-MAXINT:MAXINT);
+  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 = MAXINT;
+  max_x = max_y = -MAXINT;
+  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 = MAXINT;
+  }
+
+  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 = screen;
+
+  f_oldloc.x = MAXINT;
+  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("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;
+  static int cheatstate=0;
+  static int bigstate=0;
+
+  rc = false;
+  if (!automapactive)
+  {
+    if (ev->type == ev_keydown && ev->data1 == AM_STARTKEY
+		&& gamestate == GS_LEVEL)
+    {
+      AM_Start ();
+		viewactive = false;
+//      viewactive = true;
+      rc = true;
+    }
+  }
+  else if (ev->type == ev_keydown)
+  {
+
+    rc = true;
+    switch(ev->data1)
+    {
+      case AM_PANRIGHTKEY: // pan right
+	if (!followplayer) m_paninc.x = FTOM(F_PANINC);
+	else rc = false;
+	break;
+      case AM_PANLEFTKEY: // pan left
+	if (!followplayer) m_paninc.x = -FTOM(F_PANINC);
+	else rc = false;
+	break;
+      case AM_PANUPKEY: // pan up
+	if (!followplayer) m_paninc.y = FTOM(F_PANINC);
+	else rc = false;
+	break;
+      case AM_PANDOWNKEY: // pan down
+	if (!followplayer) m_paninc.y = -FTOM(F_PANINC);
+	else rc = false;
+	break;
+      case AM_ZOOMOUTKEY: // zoom out
+	mtof_zoommul = M_ZOOMOUT;
+	ftom_zoommul = M_ZOOMIN;
+	break;
+      case AM_ZOOMINKEY: // zoom in
+	mtof_zoommul = M_ZOOMIN;
+	ftom_zoommul = M_ZOOMOUT;
+	break;
+      case AM_ENDKEY:
+	bigstate = 0;
+	viewactive = true;
+	AM_Stop ();
+	break;
+      case AM_GOBIGKEY:
+	bigstate = !bigstate;
+	if (bigstate)
+	{
+	  AM_saveScaleAndLoc();
+	  AM_minOutWindowScale();
+	}
+	else AM_restoreScaleAndLoc();
+	break;
+      case AM_FOLLOWKEY:
+	followplayer = !followplayer;
+	f_oldloc.x = MAXINT;
+	P_SetMessage(plr, followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF, true);
+	break;
+/*
+      case AM_GRIDKEY:
+	grid = !grid;
+	plr->message = grid ? AMSTR_GRIDON : AMSTR_GRIDOFF;
+	break;
+      case AM_MARKKEY:
+	sprintf(buffer, "%s %d", AMSTR_MARKEDSPOT, markpointnum);
+	plr->message = buffer;
+  	AM_addMark();
+  	break;
+      case AM_CLEARMARKKEY:
+  	AM_clearMarks();
+	plr->message = AMSTR_MARKSCLEARED;
+  	break;
+*/
+      default:
+	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;
+    switch (ev->data1)
+    {
+      case AM_PANRIGHTKEY:
+	if (!followplayer) m_paninc.x = 0;
+	break;
+      case AM_PANLEFTKEY:
+	if (!followplayer) m_paninc.x = 0;
+	break;
+      case AM_PANUPKEY:
+	if (!followplayer) m_paninc.y = 0;
+	break;
+      case AM_PANDOWNKEY:
+	if (!followplayer) m_paninc.y = 0;
+	break;
+      case AM_ZOOMOUTKEY:
+      case AM_ZOOMINKEY:
+	mtof_zoommul = FRACUNIT;
+	ftom_zoommul = FRACUNIT;
+	break;
+    }
+  }
+
+  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 == MAXINT) //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 == MAXINT) //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(screen+i*finit_width, maplump+j+mapxstart, finit_width-mapxstart);
+		memcpy(screen+i*finit_width+finit_width-mapxstart, maplump+j, mapxstart);
+		j += finit_width;
+		if(j >= finit_height*finit_width)
+			j=0;
+	}
+
+//	 memcpy(screen, 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 };
+  register outcode1 = 0, outcode2 = 0, outside;
+  fpoint_t tmp;
+  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 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 long) DeltaX << 16) / (unsigned long) 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 long) DeltaY << 16) / (unsigned long) 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)
+{
+	int highestEpisode;
+
+	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((gameepisode < (ExtendedWAD ? 6 : 4)) && gamemap < 10)
+	{
+		MN_DrTextA(LevelNames[(gameepisode-1)*9+gamemap-1], 20, 145);
+	}
+//  I_Update();
+//  V_MarkRect(f_x, f_y, f_w, f_h);
+}
--- /dev/null
+++ b/src/heretic/am_map.h
@@ -1,0 +1,121 @@
+#ifndef __AMMAP_H__
+#define __AMMAP_H__
+
+#pragma once
+
+// 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 KEY_TAB	9
+#define AM_PANDOWNKEY	KEY_DOWNARROW
+#define AM_PANUPKEY	KEY_UPARROW
+#define AM_PANRIGHTKEY	KEY_RIGHTARROW
+#define AM_PANLEFTKEY	KEY_LEFTARROW
+//#define AM_PANDOWNKEY	SC_DOWNARROW
+//#define AM_PANUPKEY		SC_UPARROW
+//#define AM_PANRIGHTKEY	SC_RIGHTARROW
+//#define AM_PANLEFTKEY	SC_LEFTARROW
+
+#define AM_ZOOMINKEY	'='
+//#define AM_ZOOMINKEY		13
+//#define AM_ZOOMOUTKEY 	12
+ #define AM_ZOOMOUTKEY	'-'
+#define AM_STARTKEY	KEY_TAB
+#define AM_ENDKEY	KEY_TAB
+#define AM_GOBIGKEY	'0'
+//#define AM_GOBIGKEY		11
+//#define AM_FOLLOWKEY 	33
+//#define AM_GRIDKEY		34
+#define AM_FOLLOWKEY	'f'
+#define AM_GRIDKEY	'g'
+
+#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
--- /dev/null
+++ b/src/heretic/ct_chat.c
@@ -1,0 +1,462 @@
+//
+// Chat mode
+//
+
+#include <string.h>
+#include <ctype.h>
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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("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)
+				{
+					strcpy(plr_lastmsg[i], CT_FromPlrText[i]);
+					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], "-MESSAGE SENT-", 
+							true);
+						S_StartSound(NULL, sfx_chat);
+					}
+					else
+					{
+						P_SetMessage(&players[consoleplayer],
+							"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("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;
+}
--- /dev/null
+++ b/src/heretic/ct_chat.h
@@ -1,0 +1,15 @@
+//
+// Chat mode stuff
+//
+
+#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'
--- /dev/null
+++ b/src/heretic/d_main.c
@@ -1,0 +1,1040 @@
+
+// D_main.c
+
+#ifdef __WATCOMC__
+#include <dos.h>
+#include <graph.h>
+#include <sys\types.h>
+#include <direct.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.h"
+
+boolean shareware = false;		// true if only episode 1 present
+boolean ExtendedWAD = false;	// true if episodes 4 and 5 present
+
+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;
+boolean autostart;
+extern boolean automapactive;
+
+boolean advancedemo;
+
+FILE *debugfile;
+
+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);
+
+//---------------------------------------------------------------------------
+//
+// FUNC FixedDiv
+//
+//---------------------------------------------------------------------------
+
+fixed_t FixedDiv(fixed_t a, fixed_t b)
+{
+	if((abs(a)>>14) >= abs(b))
+	{
+		return((a^b)<0 ? MININT : MAXINT);
+	}
+	return(FixedDiv2(a, b));
+}
+
+/*
+===============================================================================
+
+							EVENT HANDLING
+
+Events are asyncronous inputs generally generated by the game user.
+
+Events can be discarded if no responder claims them
+
+===============================================================================
+*/
+
+event_t events[MAXEVENTS];
+int eventhead;
+int eventtail;
+
+//---------------------------------------------------------------------------
+//
+// PROC D_PostEvent
+//
+// Called by the I/O functions when input is detected.
+//
+//---------------------------------------------------------------------------
+
+void D_PostEvent(event_t *ev)
+{
+	events[eventhead] = *ev;
+	eventhead = (++eventhead)&(MAXEVENTS-1);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC D_ProcessEvents
+//
+// Send all the events of the given timestamp down the responder chain.
+//
+//---------------------------------------------------------------------------
+
+void D_ProcessEvents(void)
+{
+	event_t *ev;
+
+	for(; eventtail != eventhead; eventtail = (++eventtail)&(MAXEVENTS-1))
+	{
+		ev = &events[eventtail];
+		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("PAUSED",
+				PU_CACHE));
+		}
+		else
+		{
+			V_DrawPatch(160, 70, W_CacheLumpName("PAUSED",
+				PU_CACHE));
+		}
+	}
+	// Handle player messages
+	DrawMessage();
+
+	// Menu drawing
+	MN_Drawer();
+
+	// Send out any new accumulation
+	NetUpdate();
+
+	// Flush buffered stuff to screen
+	I_Update();
+}
+
+//---------------------------------------------------------------------------
+//
+// 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_InitGraphics();
+	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("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 = "TITLE";
+			S_StartSong(mus_titl, false);
+			break;
+		case 1:
+			pagetic = 140;
+			gamestate = GS_DEMOSCREEN;
+			pagename = "TITLE";
+			break;
+		case 2:
+			BorderNeedRefresh = true;
+			UpdateState |= I_FULLSCRN;
+			G_DeferedPlayDemo ("demo1");
+			break;
+		case 3:
+			pagetic = 200;
+			gamestate = GS_DEMOSCREEN;
+			pagename = "CREDIT";
+			break;
+		case 4:
+			BorderNeedRefresh = true;
+			UpdateState |= I_FULLSCRN;
+			G_DeferedPlayDemo ("demo2");
+			break;
+		case 5:
+			pagetic = 200;
+			gamestate = GS_DEMOSCREEN;
+			if(shareware)
+			{
+				pagename = "ORDER";
+			}
+			else
+			{
+				pagename = "CREDIT";
+			}
+			break;
+		case 6:
+			BorderNeedRefresh = true;
+			UpdateState |= I_FULLSCRN;
+			G_DeferedPlayDemo ("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
+=
+===============
+*/
+
+#define MAXWADFILES 20
+
+// MAPDIR should be defined as the directory that holds development maps
+// for the -wart # # command
+
+#ifdef __NeXT__
+
+#define MAPDIR "/Novell/Heretic/data/"
+
+#define SHAREWAREWADNAME "/Novell/Heretic/source/heretic1.wad"
+
+char *wadfiles[MAXWADFILES] =
+{
+	"/Novell/Heretic/source/heretic.wad",
+	"/Novell/Heretic/data/texture1.lmp",
+	"/Novell/Heretic/data/texture2.lmp",
+	"/Novell/Heretic/data/pnames.lmp"
+};
+
+#else
+
+#define MAPDIR "\\data\\"
+
+#define SHAREWAREWADNAME "heretic1.wad"
+
+char *wadfiles[MAXWADFILES] =
+{
+	"heretic.wad",
+	"texture1.lmp",
+	"texture2.lmp",
+	"pnames.lmp"
+};
+
+#endif
+
+char *basedefault = "heretic.cfg";
+
+char exrnwads[80];
+char exrnwads2[80];
+
+void wadprintf(void)
+{
+	if(debugmode)
+	{
+		return;
+	}
+	#ifdef __WATCOMC__
+	_settextposition(23, 2);
+	_setbkcolor(1);
+	_settextcolor(0);
+	_outtext(exrnwads);
+	_settextposition(24, 2);
+	_outtext(exrnwads2);
+	#endif
+}
+
+void D_AddFile(char *file)
+{
+	int numwadfiles;
+	char *new;
+//	char text[256];
+
+	for(numwadfiles = 0; wadfiles[numwadfiles]; numwadfiles++);
+	new = malloc(strlen(file)+1);
+	strcpy(new, file);
+	if(strlen(exrnwads)+strlen(file) < 78)
+	{
+		if(strlen(exrnwads))
+		{
+			strcat(exrnwads, ", ");
+		}
+		else
+		{
+			strcpy(exrnwads, "External Wadfiles: ");
+		}
+		strcat(exrnwads, file);
+	}
+	else if(strlen(exrnwads2)+strlen(file) < 79)
+	{
+		if(strlen(exrnwads2))
+		{
+			strcat(exrnwads2, ", ");
+		}
+		else
+		{
+			strcpy(exrnwads2, "     ");
+			strcat(exrnwads, ",");
+		}
+		strcat(exrnwads2, file);
+	}
+	wadfiles[numwadfiles] = new;
+}
+
+//==========================================================
+//
+//  Startup Thermo code
+//
+//==========================================================
+#define MSG_Y       9
+//#define THERM_X 15
+//#define THERM_Y 16
+//#define THERMCOLOR  3
+#define THERM_X     14
+#define THERM_Y     14
+
+int thermMax;
+int thermCurrent;
+char    *startup;           // * to text screen
+char smsg[80];      // status bar line
+
+//
+//  Heretic startup screen shit
+//
+
+byte *hscreen;
+
+void hgotoxy(int x,int y)
+{
+	hscreen = (byte *)(0xb8000 + y*160 + x*2);
+}
+
+void hput(unsigned char c, unsigned char a)
+{
+	*hscreen++ = c;
+	*hscreen++ = a;
+}
+
+void hprintf(char *string, unsigned char a)
+{
+#ifdef __WATCOMC__
+	int i;
+
+	if(debugmode)
+	{
+		puts(string);
+		return;
+	}
+	for(i = 0; i < strlen(string); i++)
+	{
+		hput(string[i], a);
+	}
+#endif
+}
+
+void drawstatus(void)
+{
+	if(debugmode)
+	{
+		return;
+	}
+	#ifdef __WATCOMC__
+	_settextposition(25, 2);
+	_setbkcolor(1);
+	_settextcolor(15);
+	_outtext(smsg);
+	_settextposition(25, 1);
+	#endif
+}
+
+void status(char *string)
+{
+	strcat(smsg,string);
+	drawstatus();
+}
+
+void DrawThermo(void)
+{
+	#ifdef __WATCOMC__
+	unsigned char       *screen;
+	int     progress;
+	int     i;
+
+	if(debugmode)
+	{
+		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
+	progress = (50*thermCurrent)/thermMax+2;
+//  screen = (char *)0xb8000 + (THERM_Y*160 + THERM_X*2);
+	hgotoxy(THERM_X,THERM_Y);
+	for (i = 0; i < progress; i++)
+	{
+//      *screen++ = 0xdb;
+//      *screen++ = 0x2a;
+		hput(0xdb,0x2a);
+	}
+#endif
+	#endif
+}
+
+#ifdef __WATCOMC__
+void blitStartup(void)
+{
+	byte *textScreen;
+
+	if(debugmode)
+	{
+		return;
+	}
+
+	// Blit main screen
+	textScreen = (byte *)0xb8000;
+	memcpy(textScreen, startup, 4000);
+
+	// Print version string
+	_setbkcolor(4); // Red
+	_settextcolor(14); // Yellow
+	_settextposition(3, 47);
+	_outtext(VERSION_TEXT);
+
+	// Hide cursor
+	_settextcursor(0x2000);
+}
+#endif
+
+char tmsg[300];
+void tprintf(char *msg,int initflag)
+{
+#if 0
+	#ifdef __WATCOMC__
+	char    temp[80];
+	int start;
+	int add;
+	int i;
+	#endif
+
+	if(debugmode)
+	{
+		printf(msg);
+		return;
+	}
+	#ifdef __WATCOMC__
+	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(msg);
+	#endif
+#endif
+}
+
+void CheckAbortStartup(void)
+{
+#ifdef __WATCOMC__
+	extern int lastpress;
+
+	if(lastpress == 1)
+	{ // Abort if escape pressed
+		CleanExit();
+	}
+#endif
+}
+
+void IncThermo(void)
+{
+	thermCurrent++;
+	DrawThermo();
+	CheckAbortStartup();
+}
+
+void InitThermo(int max)
+{
+	thermMax = max;
+	thermCurrent = 0;
+}
+
+#ifdef __WATCOMC__
+void CleanExit(void)
+{
+	union REGS regs;
+
+	I_ShutdownKeyboard();
+	regs.x.eax = 0x3;
+	int386(0x10, &regs, &regs);
+	printf("Exited from HERETIC.\n");
+	exit(1);
+}
+#endif
+
+//---------------------------------------------------------------------------
+//
+// PROC D_DoomMain
+//
+//---------------------------------------------------------------------------
+
+void D_DoomMain(void)
+{
+	int p;
+	int e;
+	int m;
+	char file[256];
+	FILE *fp;
+	boolean devMap;
+	//char *screen;
+
+	M_FindResponseFile();
+	setbuf(stdout, NULL);
+	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;
+
+	// wadfiles[0] is a char * to the main wad
+	fp = fopen(wadfiles[0], "rb");
+	if(fp)
+	{
+		fclose(fp);
+	}
+	else
+	{ // Change to look for shareware wad
+		wadfiles[0] = SHAREWAREWADNAME;
+	}
+
+	// Check for -CDROM
+	cdrom = false;
+#ifdef __WATCOMC__
+	if(M_CheckParm("-cdrom"))
+	{
+		cdrom = true;
+		mkdir("c:\\heretic.cd");
+	}
+#endif
+
+	// -FILE [filename] [filename] ...
+	// Add files to the wad list.
+	p = M_CheckParm("-file");
+	if(p)
+	{	// the parms after p are wadfile/lump names, until end of parms
+		// or another - preceded parm
+		while(++p != myargc && myargv[p][0] != '-')
+		{
+			D_AddFile(myargv[p]);
+		}
+	}
+
+	// -DEVMAP <episode> <map>
+	// Adds a map wad from the development directory to the wad list,
+	// and sets the start episode and the start map.
+	devMap = false;
+	p = M_CheckParm("-devmap");
+	if(p && p < myargc-2)
+	{
+		e = myargv[p+1][0];
+		m = myargv[p+2][0];
+		sprintf(file, MAPDIR"E%cM%c.wad", e, m);
+		D_AddFile(file);
+		printf("DEVMAP: Episode %c, Map %c.\n", e, m);
+		startepisode = e-'0';
+		startmap = m-'0';
+		autostart = true;
+		devMap = true;
+	}
+
+	p = M_CheckParm("-playdemo");
+	if(!p)
+	{
+		p = M_CheckParm("-timedemo");
+	}
+	if (p && p < myargc-1)
+	{
+		sprintf(file, "%s.lmp", myargv[p+1]);
+		D_AddFile(file);
+		printf("Playing demo %s.lmp.\n", myargv[p+1]);
+	}
+
+//
+// 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
+//
+	printf("V_Init: allocate screens.\n");
+	V_Init();
+
+	// Load defaults before initing other systems
+	printf("M_LoadDefaults: Load system defaults.\n");
+	M_LoadDefaults();
+
+	printf("Z_Init: Init zone memory allocation daemon.\n");
+	Z_Init();
+
+	printf("W_Init: Init WADfiles.\n");
+	W_InitMultipleFiles(wadfiles);
+
+	if(W_CheckNumForName("E2M1") == -1)
+	{ // Can't find episode 2 maps, must be the shareware WAD
+		shareware = true;
+	}
+	else if(W_CheckNumForName("EXTENDED") != -1)
+	{ // Found extended lump, must be the extended WAD
+		ExtendedWAD = true;
+	}
+
+#ifdef __WATCOMC__
+	I_StartupKeyboard();
+	I_StartupJoystick();
+#endif
+	startup = W_CacheLumpName("LOADING", PU_CACHE);
+	#ifdef __WATCOMC__
+	blitStartup();
+	#endif
+
+	//
+	//  Build status bar line!
+	//
+	smsg[0] = 0;
+	if (deathmatch)
+		status("DeathMatch...");
+	if (nomonsters)
+		status("No Monsters...");
+	if (respawnparm)
+		status("Respawning...");
+	if (autostart)
+	{
+		char temp[64];
+		sprintf(temp, "Warp to Episode %d, Map %d, Skill %d ",
+			startepisode, startmap, startskill+1);
+		status(temp);
+	}
+	wadprintf(); // print the added wadfiles
+
+	tprintf("MN_Init: Init menu system.\n",1);
+	MN_Init();
+
+	CT_Init();
+
+	tprintf("R_Init: Init Heretic refresh daemon.",1);
+	hgotoxy(17,7);
+	hprintf("Loading graphics",0x3f);
+	R_Init();
+
+	tprintf("P_Init: Init Playloop state.",1);
+	hgotoxy(17,8);
+	hprintf("Init game engine.",0x3f);
+	P_Init();
+	IncThermo();
+
+	tprintf("I_Init: Setting up machine state.\n",1);
+	I_Init();
+	IncThermo();
+
+	tprintf("D_CheckNetGame: Checking network game status.\n",1);
+	hgotoxy(17,9);
+	hprintf("Checking network game status.", 0x3f);
+	D_CheckNetGame();
+	IncThermo();
+
+#ifdef __WATCOMC__
+	I_CheckExternDriver(); // Check for an external device driver
+#endif
+
+	tprintf("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) && (devMap == false))
+	{
+		if(M_ValidEpisodeMap(startepisode, startmap) == false)
+		{
+			startepisode = 1;
+			startmap = 1;
+		}
+	}
+
+	if(gameaction != ga_loadgame)
+	{
+		UpdateState |= I_FULLSCRN;
+		BorderNeedRefresh = true;
+		if(autostart || netgame)
+		{
+			G_InitNew(startskill, startepisode, startmap);
+		}
+		else
+		{
+			D_StartTitle();
+		}
+	}
+#ifdef __WATCOMC__
+	_settextcursor(0x0607); // bring the cursor back
+#endif
+	D_DoomLoop(); // Never returns
+}
--- /dev/null
+++ b/src/heretic/d_net.c
@@ -1,0 +1,784 @@
+
+// d_net.c
+// This version has the fixed ticdup code
+
+#include "DoomDef.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");
+}
+
+	I_NetCmd ();
+}
+
+/*
+==============
+=
+= 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;
+	I_NetCmd ();
+	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 ();
+	for ( ; eventtail != eventhead
+	; eventtail = (++eventtail)&(MAXEVENTS-1) )
+	{
+		ev = &events[eventtail];
+		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 != 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 = 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);
+	}
+}
+
+/*
+===================
+=
+= 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 = MAXINT;
+	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 = MAXINT;
+
+			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;
+			}
+		}
+
+//
+// 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
+	}
+}
--- /dev/null
+++ b/src/heretic/d_netbak.c
@@ -1,0 +1,797 @@
+
+// I_pcnet.m
+//
+// Modified 12-21-94 by Chris Rhinehart for use with multiple ticdups
+//		actually, it wasn't modified, but rather we are currently using this
+// 	older version of D_NET.C, since the new one doesn't seem to work with
+//		ticdup set to greater than one.
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.h"
+
+#define	NCMD_EXIT		0x80000000
+#define	NCMD_RETRANSMIT	0x40000000
+#define	NCMD_SETUP		0x20000000
+#define	NCMD_CHECKSUM	0x0fffffff
+
+/*
+if more space needs to be crunched out of the protocol...
+
+1	drone
+2	player
+8	tic
+5	numtics
+
+#define	NCMD_EXIT		0x80000000
+#define	NCMD_RETRANSMIT	0x40000000			// a retransmit will have 0 tics
+#define	NCMD_DRONE		0x20000000
+#define	NCMD_PLAYER		0x18000000
+#define	NCMD_PLAYERSHIFT	27
+#define	NCMD_TIC		0x00ff0000
+#define	NCMD_TICSHIFT	16
+#define	NCMD_NUMTICS	0x0000ff00
+#define	NCMD_NUMTICSSHIFT	8
+#define	NCMD_CHECKSUM	0x000000ff
+
+*/
+
+
+
+
+
+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             gametime;
+int             maketic;
+int				lastnettic, skiptics;
+int				ticdup;
+
+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;
+
+	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 (!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");
+}
+
+	I_NetCmd ();
+}
+
+/*
+==============
+=
+= 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;
+
+	doomcom->command = CMD_GET;
+	I_NetCmd ();
+	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;
+	int		netdrone;
+	int		j;
+	ticcmd_t	*src, *dest;
+	int		dupedstart, dupedend;
+	int		skiptics;
+	int		realstart;
+
+	while (HGetPacket ())
+	{
+		if (netbuffer->checksum & NCMD_SETUP)
+			continue;		// extra setup packet
+
+		netdrone = netbuffer->player & PL_DRONE;
+		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);
+		dupedstart = realstart*doomcom->ticdup;
+		dupedend = (realstart+netbuffer->numtics)*doomcom->ticdup;
+
+		//
+		// check for exiting the game
+		//
+		if (netbuffer->checksum & NCMD_EXIT)
+		{
+			if (!nodeingame[netnode])
+				continue;
+			nodeingame[netnode] = false;
+			if (!netdrone)
+			{
+				playeringame[netconsole] = false;
+				strcpy (exitmsg, "PLAYER 1 HAS LEFT THE GAME");
+				exitmsg[7] += netconsole;
+				P_SetMessage(&players[consoleplayer], exitmsg, true);
+				UpdateState |= I_MESSAGES;
+				S_StartSound(NULL, sfx_chat);
+			}
+			continue;
+		}
+
+		//
+		// drone packets are just notifications
+		//
+		if (netdrone)
+		{
+			nettics[netnode] = dupedend;
+			continue;
+		}
+
+		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 (dupedend == nettics[netnode])
+			continue;
+
+		if (dupedend < nettics[netnode])
+		{
+if (debugfile)
+fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
+			continue;
+		}
+
+		//
+		// check for a missed packet
+		//
+		if (dupedstart > nettics[netnode])
+		{
+		// stop processing until the other system resends the missed tics
+if (debugfile)
+fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, dupedstart, nettics[netnode]);
+			remoteresend[netnode] = true;
+			continue;
+		}
+
+//
+// update command store from the packet
+//
+		remoteresend[netnode] = false;
+
+		skiptics = nettics[netnode]/doomcom->ticdup - realstart;
+		src = &netbuffer->cmds[skiptics];
+
+		while (nettics[netnode] < dupedend)
+		{
+			for (j=0 ; j<doomcom->ticdup ; j++)
+			{
+				dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
+				nettics[netnode]++;
+				*dest = *src;
+				src->chatchar = 0;
+				if (src->buttons & BT_SPECIAL)
+					src->buttons = 0;
+			}
+			src++;
+		}
+	}
+}
+
+/*
+=============
+=
+= NetUpdate
+=
+= Builds ticcmds for console player
+= sends out a packet
+=============
+*/
+
+void NetUpdate (void)
+{
+	int             nowtime;
+	int             newtics;
+	int				i,j;
+	int				gameticdiv;
+	int				realstart;
+
+	if (singletics)
+		return;         // singletic update is syncronous
+
+//
+// check time
+//
+	nowtime = I_GetTime ()/doomcom->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;
+	if (doomcom->drone)
+		netbuffer->player |= PL_DRONE;
+
+//
+// drone packets
+//
+	if (doomcom->drone)
+	{
+		I_StartTic ();
+		D_ProcessEvents ();
+		goto sendit;
+	}
+
+//
+// build new ticcmds for console player
+//
+	gameticdiv = (gametic+doomcom->ticdup-1)/doomcom->ticdup;
+	for (i=0 ; i<newtics ; i++)
+	{
+		I_StartTic ();
+		D_ProcessEvents ();
+		if (maketic - gameticdiv >= BACKUPTICS/2 /* /doomcom->ticdup */- 1)
+		{
+			newtics = i;
+			break;          // can't hold any more
+		}
+//printf ("mk:%i ",maketic);
+		G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
+		maketic++;
+	}
+
+//
+// send the packet to the other nodes
+//
+sendit:
+	for (i=0 ; i<doomcom->numnodes ; i++)
+		if (nodeingame[i])
+		{
+			if (doomcom->drone)
+			{
+				netbuffer->starttic = realstart = maketic + BACKUPTICS/2;
+				netbuffer->numtics = 0;
+			}
+			else
+			{
+				netbuffer->starttic = realstart = resendto[i];
+				netbuffer->numtics = maketic - realstart;
+				resendto[i] = maketic - doomcom->extratics;
+			}
+
+			if (netbuffer->numtics > BACKUPTICS)
+				I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
+
+			for (j=0 ; j< netbuffer->numtics ; j++)
+				netbuffer->cmds[j] =
+					localcmds[(realstart+j)%BACKUPTICS];
+
+			if (remoteresend[i])
+			{
+				netbuffer->retransmitfrom = nettics[i]/doomcom->ticdup;
+				HSendPacket (i, NCMD_RETRANSMIT);
+			}
+			else
+			{
+				netbuffer->retransmitfrom = 0;
+				HSendPacket (i, 0);
+			}
+		}
+
+//
+// listen for other packets
+//
+listen:
+
+	GetPackets ();
+}
+
+
+/*
+=====================
+=
+= CheckAbort
+=
+=====================
+*/
+
+void CheckAbort (void)
+{
+	event_t *ev;
+
+	I_WaitVBL(2);
+
+	I_StartTic ();
+	for ( ; eventtail != eventhead
+	; eventtail = (++eventtail)&(MAXEVENTS-1) )
+	{
+		ev = &events[eventtail];
+		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
+		//printf ("listening for network start info...\n");
+		while (1)
+		{
+			CheckAbort ();
+			if (!HGetPacket ())
+				continue;
+			if (netbuffer->checksum & NCMD_SETUP)
+			{
+				if (netbuffer->player != VERSION)
+					I_Error ("Different HERETIC versions cannot play a net game!");
+				startskill = netbuffer->retransmitfrom & 15;
+				deathmatch = (netbuffer->retransmitfrom & 0x80) > 0;
+				startmap = netbuffer->starttic & 15;
+				startepisode = netbuffer->starttic >> 4;
+				return;
+			}
+		}
+	}
+	else
+	{	// key player, send the setup info
+//		printf ("sending network start info...\n");
+		do
+		{
+			CheckAbort ();
+			for (i=0 ; i<doomcom->numnodes ; i++)
+			{
+				netbuffer->retransmitfrom = startskill;
+				if (deathmatch)
+					netbuffer->retransmitfrom |= 0x80;
+				netbuffer->starttic = startepisode * 16 + startmap;
+				netbuffer->player = VERSION;
+				netbuffer->numtics = 0;
+				HSendPacket (i, NCMD_SETUP);
+			}
+
+			while (HGetPacket ())
+			{
+//printf ("got packet\n");
+				gotinfo[netbuffer->player&0x7f] = true;
+			}
+
+			for (i=1 ; i<doomcom->numnodes ; i++)
+				if (!gotinfo[i])
+					break;
+		} while (i < doomcom->numnodes);
+	}
+}
+
+/*
+===================
+=
+= 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;
+
+	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)
+		return;
+
+// send a bunch of packets for security
+	netbuffer->player = consoleplayer;
+	if (doomcom->drone)
+		netbuffer->player |= PL_DRONE;
+	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, nextlowest;
+	int             entertic;
+	int	static		oldentertics;
+	int				realtics, availabletics;
+	int				counts;
+	int				numplaying;
+
+//
+// get real tics
+//
+	entertic = I_GetTime ();
+	realtics = entertic - oldentertics;
+	oldentertics = entertic;
+
+//
+// get available tics
+//
+	NetUpdate ();
+
+	lowtic = nextlowest = MAXINT;
+	numplaying = 0;
+	for (i=0 ; i<doomcom->numnodes ; i++)
+		if (nodeingame[i])
+		{
+			numplaying++;
+			if (nettics[i] < lowtic)
+			{
+				nextlowest = lowtic;
+				lowtic = nettics[i];
+			}
+			else if (nettics[i] < nextlowest)
+				nextlowest = nettics[i];
+		}
+	availabletics = lowtic - gametic;
+
+
+//
+// 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);
+
+//=============================================================================
+//
+//	ideally nettics[0] should be 1 - 3 tics above lowtic
+//	if we are consistantly slower, speed up time
+//	drones should never hold up the other players
+//
+	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 ("+");
+		}
+	}
+//=============================================================================
+
+//
+// wait for new tics if needed
+//
+	while (lowtic < gametic + counts)
+	{
+
+		NetUpdate ();
+		lowtic = MAXINT;
+
+		for (i=0 ; i<doomcom->numnodes ; i++)
+			if (nodeingame[i] && nettics[i] < lowtic)
+				lowtic = nettics[i];
+
+		if (lowtic < gametic)
+			I_Error ("TryRunTics: lowtic < gametic");
+
+		// don't stay in here forever -- give the menu a chance to work
+		if (I_GetTime () - entertic >= 20)
+		{
+			MN_Ticker ();
+			return;
+		}
+	}
+
+
+//
+// run the tics
+//
+	while (counts--)
+	{
+		if (advancedemo)
+			D_DoAdvanceDemo ();
+		MN_Ticker ();
+		G_Ticker ();
+		NetUpdate ();					// check for new console commands
+		gametic++;
+	}
+}
--- /dev/null
+++ b/src/heretic/doomdata.h
@@ -1,0 +1,195 @@
+// DoomData.h
+
+// all external data is defined here
+// most of the data is loaded into different structures at run time
+
+#ifndef __DOOMDATA__
+#define __DOOMDATA__
+
+#ifndef __BYTEBOOL__
+#define __BYTEBOOL__
+typedef enum {false, true} boolean;
+typedef unsigned char byte;
+#endif
+
+/*
+===============================================================================
+
+						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;
+} mapvertex_t;
+
+typedef struct
+{
+	short		textureoffset;
+	short		rowoffset;
+	char		toptexture[8], bottomtexture[8], midtexture[8];
+	short		sector;				// on viewer's side
+} mapsidedef_t;
+
+typedef struct
+{
+	short		v1, v2;
+	short		flags;
+	short		special, tag;
+	short		sidenum[2];			// sidenum[1] will be -1 if one sided
+} 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;
+} mapsector_t;
+
+typedef struct
+{
+	short		numsegs;
+	short		firstseg;			// segs are stored sequentially
+} mapsubsector_t;
+
+typedef struct
+{
+	short		v1, v2;
+	short		angle;		
+	short		linedef, side;
+	short		offset;
+} mapseg_t;
+
+enum {BOXTOP,BOXBOTTOM,BOXLEFT,BOXRIGHT};	// bbox coordinates
+
+#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
+} mapnode_t;
+
+typedef struct
+{
+	short		x,y;
+	short		angle;
+	short		type;
+	short		options;
+} 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;
+} mappatch_t;
+
+typedef struct
+{
+	char		name[8];
+	boolean		masked;	
+	short		width;
+	short		height;
+	void		**columndirectory;	// OBSOLETE
+	short		patchcount;
+	mappatch_t	patches[1];
+} maptexture_t;
+
+
+/*
+===============================================================================
+
+							graphics
+
+===============================================================================
+*/
+
+// posts are runs of non masked source pixels
+typedef struct
+{
+	byte		topdelta;		// -1 is the last post in a column
+	byte		length;
+// length data bytes follows
+} post_t;
+
+// column_t is a list of 0 or more post_t, (byte)-1 terminated
+typedef post_t	column_t;
+
+// a patch holds one or more columns
+// patches are used for sprites and all masked pictures
+typedef struct
+{
+	short		width;				// bounding box size
+	short		height;
+	short		leftoffset;			// pixels to the left of origin
+	short		topoffset;			// pixels below the origin
+	int			columnofs[8];		// only [width] used
+									// the [0] is &columnofs[width]
+} patch_t;
+
+// a pic is an unmasked block of pixels
+typedef struct
+{
+	byte		width,height;
+	byte		data;
+} pic_t;
+
+
+
+
+/*
+===============================================================================
+
+							status
+
+===============================================================================
+*/
+
+
+
+
+#endif			// __DOOMDATA__
+
--- /dev/null
+++ b/src/heretic/doomdef.h
@@ -1,0 +1,1182 @@
+
+// DoomDef.h
+
+#ifndef __DOOMDEF__
+#define __DOOMDEF__
+#include <stdio.h>
+#include <string.h>
+#ifdef __WATCOMC__
+#include <malloc.h>
+#define	strcasecmp strcmpi
+#define	strncasecmp strnicmp
+#endif
+
+#define VERSION 130
+#define 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"
+
+extern byte *destview, *destscreen;	// PC direct to screen pointers
+
+//
+// most key data are simple ascii (uppercased)
+//
+#define	KEY_RIGHTARROW		0xae
+#define	KEY_LEFTARROW		0xac
+#define	KEY_UPARROW			0xad
+#define	KEY_DOWNARROW		0xaf
+#define	KEY_ESCAPE			27
+#define	KEY_ENTER			13
+#define	KEY_F1				(0x80+0x3b)
+#define	KEY_F2				(0x80+0x3c)
+#define	KEY_F3				(0x80+0x3d)
+#define	KEY_F4				(0x80+0x3e)
+#define	KEY_F5				(0x80+0x3f)
+#define	KEY_F6				(0x80+0x40)
+#define	KEY_F7				(0x80+0x41)
+#define	KEY_F8				(0x80+0x42)
+#define	KEY_F9				(0x80+0x43)
+#define	KEY_F10				(0x80+0x44)
+#define	KEY_F11				(0x80+0x57)
+#define	KEY_F12				(0x80+0x58)
+
+#define	KEY_BACKSPACE		127
+#define	KEY_PAUSE			0xff
+
+#define KEY_EQUALS			0x3d
+#define KEY_MINUS			0x2d
+
+#define	KEY_RSHIFT			(0x80+0x36)
+#define	KEY_RCTRL			(0x80+0x1d)
+#define	KEY_RALT			(0x80+0x38)
+
+#define	KEY_LALT			KEY_RALT
+
+
+
+#define MAXCHAR ((char)0x7f)
+#define MAXSHORT ((short)0x7fff)
+#define MAXINT	((int)0x7fffffff)	/* max pos 32-bit int */
+#define MAXLONG ((long)0x7fffffff)
+
+#define MINCHAR ((char)0x80)
+#define MINSHORT ((short)0x8000)
+#define MININT 	((int)0x80000000)	/* max negative 32-bit integer */
+#define MINLONG ((long)0x80000000)
+
+#define	FINEANGLES			8192
+#define	FINEMASK			(FINEANGLES-1)
+#define	ANGLETOFINESHIFT	19	// 0x100000000 to 0x2000
+
+#define	SAVEGAMENAME "hticsav"
+#define SAVEGAMENAMECD "c:\\heretic.cd\\hticsav"
+
+/*
+===============================================================================
+
+						GLOBAL TYPES
+
+===============================================================================
+*/
+
+#define NUMARTIFCTS	28
+#define MAXPLAYERS	4
+#define TICRATE		35			// number of tics / second
+#define TICSPERSEC	35
+
+#define	FRACBITS		16
+#define	FRACUNIT		(1<<FRACBITS)
+typedef int fixed_t;
+
+#define ANGLE_1		0x01000000
+#define ANGLE_45	0x20000000
+#define ANGLE_90	0x40000000
+#define ANGLE_180	0x80000000
+#define ANGLE_MAX	0xffffffff
+
+#define	ANG45	0x20000000
+#define	ANG90	0x40000000
+#define	ANG180	0x80000000
+#define	ANG270	0xc0000000
+
+typedef unsigned angle_t;
+
+typedef enum
+{
+	sk_baby,
+	sk_easy,
+	sk_medium,
+	sk_hard,
+	sk_nightmare
+} skill_t;
+
+typedef enum
+{
+	ev_keydown,
+	ev_keyup,
+	ev_mouse,
+	ev_joystick
+} evtype_t;
+
+typedef struct
+{
+	evtype_t	type;
+	int			data1;		// keys / mouse/joystick buttons
+	int			data2;		// mouse/joystick x move
+	int			data3;		// mouse/joystick y move
+} event_t;
+
+typedef struct
+{
+	char		forwardmove;		// *2048 for move
+	char		sidemove;			// *2048 for move
+	short		angleturn;			// <<16 for angle delta
+	short		consistancy;		// checks for net game
+	byte		chatchar;
+	byte		buttons;
+	byte		lookfly;			// look/fly up/down/centering
+	byte		arti;				// artitype_t to use
+} ticcmd_t;
+
+#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;
+
+// Most damage defined using HITDICE
+#define HITDICE(a) ((1+(P_Random()&7))*a)
+
+//
+// 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 fuzzy 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
+{
+	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;
+
+#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)
+
+#define MAXEVENTS 64
+
+extern event_t events[MAXEVENTS];
+extern int eventhead;
+extern int eventtail;
+
+extern fixed_t finesine[5*FINEANGLES/4];
+extern fixed_t *finecosine;
+
+extern gameaction_t gameaction;
+
+extern boolean paused;
+
+extern boolean shareware; // true if main WAD is the shareware version
+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	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 rndindex;
+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 byte *screen; // off screen work buffer, from V_video.c
+
+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
+
+===============================================================================
+*/
+
+
+fixed_t	FixedMul (fixed_t a, fixed_t b);
+fixed_t	FixedDiv (fixed_t a, fixed_t b);
+fixed_t	FixedDiv2 (fixed_t a, fixed_t b);
+
+#ifdef __WATCOMC__
+#pragma aux FixedMul =	\
+	"imul ebx",			\
+	"shrd eax,edx,16"	\
+	parm	[eax] [ebx] \
+	value	[eax]		\
+	modify exact [eax edx]
+
+#pragma aux FixedDiv2 =	\
+	"cdq",				\
+	"shld edx,eax,16",	\
+	"sal eax,16",		\
+	"idiv ebx"			\
+	parm	[eax] [ebx] \
+	value	[eax]		\
+	modify exact [eax edx]
+#endif
+
+#ifdef __BIG_ENDIAN__
+short ShortSwap(short);
+long LongSwap(long);
+#define SHORT(x)	ShortSwap(x)
+#define LONG(x)		LongSwap(x)
+#else
+#define SHORT(x)	(x)
+#define LONG(x)		(x)
+#endif
+
+
+//-----------
+//MEMORY ZONE
+//-----------
+// tags < 100 are not overwritten until freed
+#define	PU_STATIC		1			// static entire execution time
+#define	PU_SOUND		2			// static while playing
+#define	PU_MUSIC		3			// static while playing
+#define	PU_DAVE			4			// anything else Dave wants static
+#define	PU_LEVEL		50			// static until level exited
+#define	PU_LEVSPEC		51			// a special thinker in a level
+// tags >= 100 are purgable whenever needed
+#define	PU_PURGELEVEL	100
+#define	PU_CACHE		101
+
+void	Z_Init (void);
+void 	*Z_Malloc (int size, int tag, void *ptr);
+void 	Z_Free (void *ptr);
+void 	Z_FreeTags (int lowtag, int hightag);
+void 	Z_DumpHeap (int lowtag, int hightag);
+void	Z_FileDumpHeap (FILE *f);
+void	Z_CheckHeap (void);
+void	Z_ChangeTag2 (void *ptr, int tag);
+int 	Z_FreeMemory (void);
+
+extern boolean MallocFailureOk;
+
+typedef struct memblock_s
+{
+	int                     size;           // including the header and possibly tiny fragments
+	void            **user;         // NULL if a free block
+	int                     tag;            // purgelevel
+	int                     id;                     // should be ZONEID
+	struct memblock_s       *next, *prev;
+} memblock_t;
+
+#define Z_ChangeTag(p,t) \
+{ \
+if (( (memblock_t *)( (byte *)(p) - sizeof(memblock_t)))->id!=0x1d4a11) \
+	I_Error("Z_CT at "__FILE__":%i",__LINE__); \
+Z_ChangeTag2(p,t); \
+};
+
+//-------
+//WADFILE
+//-------
+typedef struct
+{
+	char		name[8];
+	int			handle,position,size;
+} lumpinfo_t;
+
+extern lumpinfo_t *lumpinfo;
+extern	int			numlumps;
+
+void	W_InitMultipleFiles (char **filenames);
+
+int		W_CheckNumForName (char *name);
+int		W_GetNumForName (char *name);
+
+int		W_LumpLength (int lump);
+void	W_ReadLump (int lump, void *dest);
+
+void	*W_CacheLumpNum (int lump, int tag);
+void	*W_CacheLumpName (char *name, int tag);
+
+
+
+
+//----------
+//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 D_PostEvent (event_t *ev);
+// called by IO functions when input is detected
+
+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
+//---------
+#if 1
+#define	SCREENWIDTH		320
+#define	SCREENHEIGHT	200
+#else
+#define	SCREENWIDTH		560
+#define	SCREENHEIGHT	375
+#endif
+
+byte *I_ZoneBase (int *size);
+// called by startup code to get the ammount of memory to malloc
+// for the zone management
+
+int I_GetTime (void);
+// called by D_DoomLoop
+// returns current time in tics
+
+void I_StartFrame (void);
+// called by D_DoomLoop
+// called before processing any tics in a frame (just after displaying a frame)
+// time consuming syncronous operations are performed here (joystick reading)
+// can call D_PostEvent
+
+void I_StartTic (void);
+// called by D_DoomLoop
+// called before processing each tic in a frame
+// quick syncronous operations are performed here
+// can call D_PostEvent
+
+// asyncronous interrupt functions should maintain private ques that are
+// read by the syncronous functions to be converted into events
+
+void I_Init (void);
+// called by D_DoomMain
+// determines the hardware configuration and sets up the video mode
+
+void I_InitGraphics (void);
+
+void I_InitNetwork (void);
+void I_NetCmd (void);
+
+void I_Error (char *error, ...);
+// called by anything that can generate a terminal error
+// bad exit with diagnostic message
+
+void I_Quit (void);
+// called by M_Responder when quit is selected
+// clean exit, displays sell blurb
+
+void I_SetPalette (byte *palette);
+// takes full 8 bit values
+
+void I_Update(void);
+// Copy buffer to video
+
+void I_WipeUpdate(wipe_t wipe);
+// Copy buffer to video with wipe effect
+
+void I_WaitVBL(int count);
+// wait for vertical retrace or pause a bit
+
+void I_BeginRead (void);
+void I_EndRead (void);
+
+byte	*I_AllocLow (int length);
+// allocates from low memory under dos, just mallocs under unix
+
+void I_Tactile (int on, int off, int total);
+
+#ifdef __WATCOMC__
+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
+//----
+extern	int		myargc;
+extern	char	**myargv;
+
+int	M_CheckParm (char *check);
+// returns the position of the given parameter in the arg list (0 if not found)
+
+boolean M_ValidEpisodeMap(int episode, int map);
+// returns true if the episode/map combo is valid for the current
+// game configuration
+
+void M_ForceUppercase(char *text);
+// Changes a string to uppercase
+
+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
+
+void M_FindResponseFile(void);
+
+void M_ClearBox (fixed_t *box);
+void M_AddToBox (fixed_t *box, fixed_t x, fixed_t y);
+// bounding box functions
+
+boolean M_WriteFile (char const *name, void *source, int length);
+int M_ReadFile (char const *name, byte **buffer);
+
+void M_ScreenShot (void);
+
+void M_LoadDefaults (void);
+
+void M_SaveDefaults (void);
+
+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)
+//-----------------
+
+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);
+
+//------
+// VIDEO
+//------
+
+extern int dirtybox[4];
+extern byte gammatable[5][256];
+extern int usegamma;
+
+void V_Init(void); // Allocates buffer screens, call before R_Init
+void V_DrawPatch(int x, int y, patch_t *patch);
+void V_DrawFuzzPatch(int x, int y, patch_t *patch);
+void V_DrawShadowedPatch(int x, int y, patch_t *patch);
+void V_DrawRawScreen(byte *raw);
+
+#include "sounds.h"
+
+#endif // __DOOMDEF__
--- /dev/null
+++ b/src/heretic/drcoord.h
@@ -1,0 +1,18 @@
+
+#import <appkit/appkit.h>
+
+@interface DRCoord:Object
+{
+	id	players_i;
+	id	console_i;
+	id	skill_i;
+	id	episode_i;
+	id	map_i;
+}
+
+- newGame: sender;
+- scale1: sender;
+- scale2: sender;
+- scale4: sender;
+
+@end
--- /dev/null
+++ b/src/heretic/dstrings.bak
@@ -1,0 +1,406 @@
+
+// DStrings.h
+
+//---------------------------------------------------------------------------
+//
+// M_menu.c
+//
+//---------------------------------------------------------------------------
+#define PRESSKEY 	"press a key."
+#define PRESSYN 	"press y or n."
+#define TXT_PAUSED "PAUSED"
+#define QUITMSG		"are you sure you want to\nquit this great game?"
+#define LOADNET 	"you can't do load while in a net game!\n\n"PRESSKEY
+#define QLOADNET	"you can't quickload during a netgame!\n\n"PRESSKEY
+#define QSAVESPOT	"you haven't picked a quicksave slot yet!\n\n"PRESSKEY
+#define SAVEDEAD 	"you can't save if you aren't playing!\n\n"PRESSKEY
+#define QSPROMPT 	"quicksave over your game named\n\n'%s'?\n\n"PRESSYN
+#define QLPROMPT	"do you want to quickload the game named"\
+					"\n\n'%s'?\n\n"PRESSYN
+#define NEWGAME		"you can't start a new game\n"\
+					"while in a network game.\n\n"PRESSKEY
+#define NIGHTMARE	"are you sure? this skill level\n"\
+					"isn't even remotely fair.\n\n"PRESSYN
+#define SWSTRING	"this is the shareware version of doom.\n\n"\
+					"you need to order the entire trilogy.\n\n"PRESSKEY
+#define MSGOFF		"Messages OFF"
+#define MSGON		"Messages ON"
+#define NETEND		"you can't end a netgame!\n\n"PRESSKEY
+#define ENDGAME		"are you sure you want to end the game?\n\n"PRESSYN
+#define DOSY		"(press y to quit to dos.)"
+#define DETAILHI	"High detail"
+#define DETAILLO	"Low detail"
+#define GAMMALVL0	"Gamma correction OFF"
+#define GAMMALVL1	"Gamma correction level 1"
+#define GAMMALVL2	"Gamma correction level 2"
+#define GAMMALVL3	"Gamma correction level 3"
+#define GAMMALVL4	"Gamma correction level 4"
+#define	EMPTYSTRING	"empty slot"
+
+//---------------------------------------------------------------------------
+//
+// P_inter.c
+//
+//---------------------------------------------------------------------------
+
+// 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"
+
+//---------------------------------------------------------------------------
+//
+// HU_stuff.c
+//
+//---------------------------------------------------------------------------
+
+#define HUSTR_E1M1	"E1M1: Hangar"
+#define HUSTR_E1M2	"E1M2: Nuclear Plant"
+#define HUSTR_E1M3	"E1M3: Toxin Refinery"
+#define HUSTR_E1M4	"E1M4: Command Control"
+#define HUSTR_E1M5	"E1M5: Phobos Lab"
+#define HUSTR_E1M6	"E1M6: Central Processing"
+#define HUSTR_E1M7	"E1M7: Computer Station"
+#define HUSTR_E1M8	"E1M8: Phobos Anomaly"
+#define HUSTR_E1M9	"E1M9: Military Base"
+
+#define HUSTR_E2M1	"E2M1: Deimos Anomaly"
+#define HUSTR_E2M2	"E2M2: Containment Area"
+#define HUSTR_E2M3	"E2M3: Refinery"
+#define HUSTR_E2M4	"E2M4: Deimos Lab"
+#define HUSTR_E2M5	"E2M5: Command Center"
+#define HUSTR_E2M6	"E2M6: Halls of the Damned"
+#define HUSTR_E2M7	"E2M7: Spawning Vats"
+#define HUSTR_E2M8	"E2M8: Tower of Babel"
+#define HUSTR_E2M9	"E2M9: Fortress of Mystery"
+
+#define HUSTR_E3M1	"E3M1: Hell Keep"
+#define HUSTR_E3M2	"E3M2: Slough of Despair"
+#define HUSTR_E3M3	"E3M3: Pandemonium"
+#define HUSTR_E3M4	"E3M4: House of Pain"
+#define HUSTR_E3M5	"E3M5: Unholy Cathedral"
+#define HUSTR_E3M6	"E3M6: Mt. Erebus"
+#define HUSTR_E3M7	"E3M7: Limbo"
+#define HUSTR_E3M8	"E3M8: Dis"
+#define HUSTR_E3M9	"E3M9: Warrens"
+
+#define HUSTR_CHATMACRO1	"I'm ready to kick butt!"
+#define HUSTR_CHATMACRO2	"I'm OK."
+#define HUSTR_CHATMACRO3	"I'm not looking too good!"
+#define HUSTR_CHATMACRO4	"Help!"
+#define HUSTR_CHATMACRO5	"You suck!"
+#define HUSTR_CHATMACRO6	"Next time, scumbag..."
+#define HUSTR_CHATMACRO7	"Come here!"
+#define HUSTR_CHATMACRO8	"I'll take care of it."
+#define HUSTR_CHATMACRO9	"Yes"
+#define HUSTR_CHATMACRO0	"No"
+
+#define HUSTR_TALKTOSELF1	"You mumble to yourself"
+#define HUSTR_TALKTOSELF2	"Who's there?"
+#define HUSTR_TALKTOSELF3	"You scare yourself"
+#define HUSTR_TALKTOSELF4	"You start to rave"
+#define HUSTR_TALKTOSELF5	"You've lost it..."
+
+#define HUSTR_MESSAGESENT	"[Message Sent]"
+
+// The following should NOT be changed unless it seems
+// just AWFULLY necessary
+
+#define HUSTR_PLRGREEN	"Green: "
+#define HUSTR_PLRINDIGO	"Indigo: "
+#define HUSTR_PLRBROWN	"Brown: "
+#define HUSTR_PLRRED		"Red: "
+
+#define HUSTR_KEYGREEN	'g'
+#define HUSTR_KEYINDIGO	'i'
+#define HUSTR_KEYBROWN	'b'
+#define HUSTR_KEYRED		'r'
+
+//---------------------------------------------------------------------------
+//
+// AM_map.c
+//
+//---------------------------------------------------------------------------
+
+#define AMSTR_FOLLOWON		"FOLLOW MODE ON"
+#define AMSTR_FOLLOWOFF		"FOLLOW MODE OFF"
+
+#define AMSTR_GRIDON		"Grid ON"
+#define AMSTR_GRIDOFF		"Grid OFF"
+
+#define AMSTR_MARKEDSPOT	"Marked Spot"
+#define AMSTR_MARKSCLEARED	"All Marks Cleared"
+
+//---------------------------------------------------------------------------
+//
+// ST_stuff.c
+//
+//---------------------------------------------------------------------------
+
+#define STSTR_DQDON			"Degreelessness Mode On"
+#define STSTR_DQDOFF		"Degreelessness Mode Off"
+
+#define STSTR_KFAADDED		"Very Happy Ammo Added"
+
+#define STSTR_NCON			"No Clipping Mode ON"
+#define STSTR_NCOFF			"No Clipping Mode OFF"
+
+#define STSTR_BEHOLD		"inVuln, Str, Inviso, Rad, Allmap, or Lite-amp"
+#define STSTR_BEHOLDX		"Power-up Toggled"
+
+#define STSTR_CHOPPERS		"... doesn't suck - GM"
+#define STSTR_CLEV			"Changing Level..."
+
+//---------------------------------------------------------------------------
+//
+// 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."
+
+/*
+#define E1TEXT	"Once you beat the big badasses and\n"\
+				"clean out the moon base you're supposed\n"\
+				"to win, aren't you? Aren't you? Where's\n"\
+				"your fat reward and ticket home? What\n"\
+				"the hell is this? It's not supposed to\n"\
+				"end this way!\n"\
+				"\n" \
+				"It stinks like rotten meat, but looks\n"\
+				"like the lost Deimos base.  Looks like\n"\
+				"you're stuck on The Shores of Hell.\n"\
+				"The only way out is through.\n"\
+				"\n"\
+				"To continue the DOOM experience, play\n"\
+				"The Shores of Hell and its amazing\n"\
+				"sequel, Inferno!\n"
+
+#define E2TEXT	"You've done it! The hideous cyber-\n"\
+				"demon lord that ruled the lost Deimos\n"\
+				"moon base has been slain and you\n"\
+				"are triumphant! But ... where are\n"\
+				"you? You clamber to the edge of the\n"\
+				"moon and look down to see the awful\n"\
+				"truth.\n" \
+				"\n"\
+				"Deimos floats above Hell itself!\n"\
+				"You've never heard of anyone escaping\n"\
+				"from Hell, but you'll make the bastards\n"\
+				"sorry they ever heard of you! Quickly,\n"\
+				"you rappel down to  the surface of\n"\
+				"Hell.\n"\
+				"\n" \
+				"Now, it's on to the final chapter of\n"\
+				"DOOM! -- Inferno."
+
+#define E3TEXT	"The loathsome spiderdemon that\n"\
+				"masterminded the invasion of the moon\n"\
+				"bases and caused so much death has had\n"\
+				"its ass kicked for all time.\n"\
+				"\n"\
+				"A hidden doorway opens and you enter.\n"\
+				"You've proven too tough for Hell to\n"\
+				"contain, and now Hell at last plays\n"\
+				"fair -- for you emerge from the door\n"\
+				"to see the green fields of Earth!\n"\
+				"Home at last.\n" \
+				"\n"\
+				"You wonder what's been happening on\n"\
+				"Earth while you were battling evil\n"\
+				"unleashed. It's good that no Hell-\n"\
+				"spawn could have come through that\n"\
+				"door with you ..."
+*/
--- /dev/null
+++ b/src/heretic/dstrings.h
@@ -1,0 +1,406 @@
+
+// DStrings.h
+
+//---------------------------------------------------------------------------
+//
+// M_menu.c
+//
+//---------------------------------------------------------------------------
+#define PRESSKEY 	"press a key."
+#define PRESSYN 	"press y or n."
+#define TXT_PAUSED "PAUSED"
+#define QUITMSG		"are you sure you want to\nquit this great game?"
+#define LOADNET 	"you can't do load while in a net game!\n\n"PRESSKEY
+#define QLOADNET	"you can't quickload during a netgame!\n\n"PRESSKEY
+#define QSAVESPOT	"you haven't picked a quicksave slot yet!\n\n"PRESSKEY
+#define SAVEDEAD 	"you can't save if you aren't playing!\n\n"PRESSKEY
+#define QSPROMPT 	"quicksave over your game named\n\n'%s'?\n\n"PRESSYN
+#define QLPROMPT	"do you want to quickload the game named"\
+					"\n\n'%s'?\n\n"PRESSYN
+#define NEWGAME		"you can't start a new game\n"\
+					"while in a network game.\n\n"PRESSKEY
+#define NIGHTMARE	"are you sure? this skill level\n"\
+					"isn't even remotely fair.\n\n"PRESSYN
+#define SWSTRING	"this is the shareware version of doom.\n\n"\
+					"you need to order the entire trilogy.\n\n"PRESSKEY
+#define MSGOFF		"Messages OFF"
+#define MSGON		"Messages ON"
+#define NETEND		"you can't end a netgame!\n\n"PRESSKEY
+#define ENDGAME		"are you sure you want to end the game?\n\n"PRESSYN
+#define DOSY		"(press y to quit to dos.)"
+#define DETAILHI	"High detail"
+#define DETAILLO	"Low detail"
+#define GAMMALVL0	"Gamma correction OFF"
+#define GAMMALVL1	"Gamma correction level 1"
+#define GAMMALVL2	"Gamma correction level 2"
+#define GAMMALVL3	"Gamma correction level 3"
+#define GAMMALVL4	"Gamma correction level 4"
+#define	EMPTYSTRING	"empty slot"
+
+//---------------------------------------------------------------------------
+//
+// P_inter.c
+//
+//---------------------------------------------------------------------------
+
+// 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"
+
+//---------------------------------------------------------------------------
+//
+// HU_stuff.c
+//
+//---------------------------------------------------------------------------
+
+#define HUSTR_E1M1	"E1M1: Hangar"
+#define HUSTR_E1M2	"E1M2: Nuclear Plant"
+#define HUSTR_E1M3	"E1M3: Toxin Refinery"
+#define HUSTR_E1M4	"E1M4: Command Control"
+#define HUSTR_E1M5	"E1M5: Phobos Lab"
+#define HUSTR_E1M6	"E1M6: Central Processing"
+#define HUSTR_E1M7	"E1M7: Computer Station"
+#define HUSTR_E1M8	"E1M8: Phobos Anomaly"
+#define HUSTR_E1M9	"E1M9: Military Base"
+
+#define HUSTR_E2M1	"E2M1: Deimos Anomaly"
+#define HUSTR_E2M2	"E2M2: Containment Area"
+#define HUSTR_E2M3	"E2M3: Refinery"
+#define HUSTR_E2M4	"E2M4: Deimos Lab"
+#define HUSTR_E2M5	"E2M5: Command Center"
+#define HUSTR_E2M6	"E2M6: Halls of the Damned"
+#define HUSTR_E2M7	"E2M7: Spawning Vats"
+#define HUSTR_E2M8	"E2M8: Tower of Babel"
+#define HUSTR_E2M9	"E2M9: Fortress of Mystery"
+
+#define HUSTR_E3M1	"E3M1: Hell Keep"
+#define HUSTR_E3M2	"E3M2: Slough of Despair"
+#define HUSTR_E3M3	"E3M3: Pandemonium"
+#define HUSTR_E3M4	"E3M4: House of Pain"
+#define HUSTR_E3M5	"E3M5: Unholy Cathedral"
+#define HUSTR_E3M6	"E3M6: Mt. Erebus"
+#define HUSTR_E3M7	"E3M7: Limbo"
+#define HUSTR_E3M8	"E3M8: Dis"
+#define HUSTR_E3M9	"E3M9: Warrens"
+
+#define HUSTR_CHATMACRO1	"I'm ready to kick butt!"
+#define HUSTR_CHATMACRO2	"I'm OK."
+#define HUSTR_CHATMACRO3	"I'm not looking too good!"
+#define HUSTR_CHATMACRO4	"Help!"
+#define HUSTR_CHATMACRO5	"You suck!"
+#define HUSTR_CHATMACRO6	"Next time, scumbag..."
+#define HUSTR_CHATMACRO7	"Come here!"
+#define HUSTR_CHATMACRO8	"I'll take care of it."
+#define HUSTR_CHATMACRO9	"Yes"
+#define HUSTR_CHATMACRO0	"No"
+
+#define HUSTR_TALKTOSELF1	"You mumble to yourself"
+#define HUSTR_TALKTOSELF2	"Who's there?"
+#define HUSTR_TALKTOSELF3	"You scare yourself"
+#define HUSTR_TALKTOSELF4	"You start to rave"
+#define HUSTR_TALKTOSELF5	"You've lost it..."
+
+#define HUSTR_MESSAGESENT	"[Message Sent]"
+
+// The following should NOT be changed unless it seems
+// just AWFULLY necessary
+
+#define HUSTR_PLRGREEN	"Green: "
+#define HUSTR_PLRINDIGO	"Indigo: "
+#define HUSTR_PLRBROWN	"Brown: "
+#define HUSTR_PLRRED		"Red: "
+
+#define HUSTR_KEYGREEN	'g'
+#define HUSTR_KEYINDIGO	'i'
+#define HUSTR_KEYBROWN	'b'
+#define HUSTR_KEYRED		'r'
+
+//---------------------------------------------------------------------------
+//
+// AM_map.c
+//
+//---------------------------------------------------------------------------
+
+#define AMSTR_FOLLOWON		"FOLLOW MODE ON"
+#define AMSTR_FOLLOWOFF		"FOLLOW MODE OFF"
+
+#define AMSTR_GRIDON		"Grid ON"
+#define AMSTR_GRIDOFF		"Grid OFF"
+
+#define AMSTR_MARKEDSPOT	"Marked Spot"
+#define AMSTR_MARKSCLEARED	"All Marks Cleared"
+
+//---------------------------------------------------------------------------
+//
+// ST_stuff.c
+//
+//---------------------------------------------------------------------------
+
+#define STSTR_DQDON			"Degreelessness Mode On"
+#define STSTR_DQDOFF		"Degreelessness Mode Off"
+
+#define STSTR_KFAADDED		"Very Happy Ammo Added"
+
+#define STSTR_NCON			"No Clipping Mode ON"
+#define STSTR_NCOFF			"No Clipping Mode OFF"
+
+#define STSTR_BEHOLD		"inVuln, Str, Inviso, Rad, Allmap, or Lite-amp"
+#define STSTR_BEHOLDX		"Power-up Toggled"
+
+#define STSTR_CHOPPERS		"... doesn't suck - GM"
+#define STSTR_CLEV			"Changing Level..."
+
+//---------------------------------------------------------------------------
+//
+// 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."
+
+/*
+#define E1TEXT	"Once you beat the big badasses and\n"\
+				"clean out the moon base you're supposed\n"\
+				"to win, aren't you? Aren't you? Where's\n"\
+				"your fat reward and ticket home? What\n"\
+				"the hell is this? It's not supposed to\n"\
+				"end this way!\n"\
+				"\n" \
+				"It stinks like rotten meat, but looks\n"\
+				"like the lost Deimos base.  Looks like\n"\
+				"you're stuck on The Shores of Hell.\n"\
+				"The only way out is through.\n"\
+				"\n"\
+				"To continue the DOOM experience, play\n"\
+				"The Shores of Hell and its amazing\n"\
+				"sequel, Inferno!\n"
+
+#define E2TEXT	"You've done it! The hideous cyber-\n"\
+				"demon lord that ruled the lost Deimos\n"\
+				"moon base has been slain and you\n"\
+				"are triumphant! But ... where are\n"\
+				"you? You clamber to the edge of the\n"\
+				"moon and look down to see the awful\n"\
+				"truth.\n" \
+				"\n"\
+				"Deimos floats above Hell itself!\n"\
+				"You've never heard of anyone escaping\n"\
+				"from Hell, but you'll make the bastards\n"\
+				"sorry they ever heard of you! Quickly,\n"\
+				"you rappel down to  the surface of\n"\
+				"Hell.\n"\
+				"\n" \
+				"Now, it's on to the final chapter of\n"\
+				"DOOM! -- Inferno."
+
+#define E3TEXT	"The loathsome spiderdemon that\n"\
+				"masterminded the invasion of the moon\n"\
+				"bases and caused so much death has had\n"\
+				"its ass kicked for all time.\n"\
+				"\n"\
+				"A hidden doorway opens and you enter.\n"\
+				"You've proven too tough for Hell to\n"\
+				"contain, and now Hell at last plays\n"\
+				"fair -- for you emerge from the door\n"\
+				"to see the green fields of Earth!\n"\
+				"Home at last.\n" \
+				"\n"\
+				"You wonder what's been happening on\n"\
+				"Earth while you were battling evil\n"\
+				"unleashed. It's good that no Hell-\n"\
+				"spawn could have come through that\n"\
+				"door with you ..."
+*/
--- /dev/null
+++ b/src/heretic/f_finale.c
@@ -1,0 +1,452 @@
+// F_finale.c
+
+#include "DoomDef.h"
+#include "soundst.h"
+#include <ctype.h>
+
+int             finalestage;            // 0 = text, 1 = art screen
+int             finalecount;
+
+#define TEXTSPEED       3
+#define TEXTWAIT        250
+
+char    *e1text = E1TEXT;
+char    *e2text = E2TEXT;
+char    *e3text = E3TEXT;
+char    *e4text = E4TEXT;
+char    *e5text = E5TEXT;
+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 = "FLOOR25";
+			finaletext = e1text;
+			break;
+		case 2:
+			finaleflat = "FLATHUH1";
+			finaletext = e2text;
+			break;
+		case 3:
+			finaleflat = "FLTWAWA2";
+			finaletext = e3text;
+			break;
+		case 4:
+			finaleflat = "FLOOR28";
+			finaletext = e4text;
+			break;
+		case 5:
+			finaleflat = "FLOOR08";
+			finaletext = e5text;
+			break;
+	}
+
+	finalestage = 0;
+	finalecount = 0;
+	FontABaseLump = W_GetNumForName("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(screen, 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 = screen;
+	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;
+	}
+
+}
+
+
+#if 0
+/*
+=======================
+=
+= F_Cast
+=
+=======================
+*/
+
+void F_Cast (void)
+{
+	byte    *src, *dest;
+	int             x,y,w;
+	int             count;
+	char    *ch;
+	int             c;
+	int             cx, cy;
+
+//
+// erase the entire screen to a tiled background
+//
+	src = W_CacheLumpName ( "FWATER1" , PU_CACHE);
+	dest = screen;
+
+	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);
+
+	V_DrawPatch (105,21,0, W_CacheLumpName ( "
+}
+#endif
+
+
+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 = screen+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("FINAL1", PU_LEVEL);
+	p2 = W_CacheLumpName("FINAL2", PU_LEVEL);
+	if(finalecount < 70)
+	{
+		memcpy(screen, p1, SCREENHEIGHT*SCREENWIDTH);
+		nextscroll = finalecount;
+		return;
+	}
+	if(yval < 64000)
+	{
+		memcpy(screen, p2+SCREENHEIGHT*SCREENWIDTH-yval, yval);
+		memcpy(screen+yval, p1, SCREENHEIGHT*SCREENWIDTH-yval);
+		yval += SCREENWIDTH;
+		nextscroll = finalecount+3;
+	}
+	else
+	{ //else, we'll just sit here and wait, for now
+		memcpy(screen, 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("E2PAL", PU_CACHE));
+				memcpy(screen, W_CacheLumpName("E2END", PU_CACHE),
+					SCREENWIDTH*SCREENHEIGHT);
+			}
+			paused = false;
+			MenuActive = false;
+			askforquit = false;
+
+			break;
+		case 2:
+			memcpy(screen, W_CacheLumpName("TITLE", PU_CACHE),
+				SCREENWIDTH*SCREENHEIGHT);
+			//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(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;
+		}
+	}
+}
--- /dev/null
+++ b/src/heretic/g_game.c
@@ -1,0 +1,1952 @@
+
+// G_game.c
+
+#include <stdio.h>
+#include <string.h>
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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
+
+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)
+//
+int             key_right, key_left, key_up, key_down;
+int             key_strafeleft, key_straferight;
+int             key_fire, key_use, key_strafe, key_speed;
+int                             key_flyup, key_flydown, key_flycenter;
+int                             key_lookup, key_lookdown, key_lookcenter;
+int                             key_invleft, key_invright, key_useartifact;
+
+int             mousebfire;
+int             mousebstrafe;
+int             mousebforward;
+
+int             joybfire;
+int             joybstrafe;
+int             joybuse;
+int             joybspeed;
+
+
+
+#define MAXPLMOVE       0x32
+
+fixed_t         forwardmove[2] = {0x19, 0x32};
+fixed_t         sidemove[2] = {0x18, 0x28};
+fixed_t         angleturn[3] = {640, 1280, 320};     // + slow turn
+#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;
+
+int             joyxmove, joyymove;         // joystick values are repeated
+boolean         joyarray[5];
+boolean         *joybuttons = &joyarray[1];     // allow [-1]
+
+int     savegameslot;
+char    savedescription[32];
+
+int inventoryTics;
+
+#ifdef __WATCOMC__
+extern externdata_t *i_ExternData;
+#endif
+
+//=============================================================================
+// 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;
+
+extern  int             isCyberPresent;     // is CyberMan present?
+boolean usearti = true;
+void I_ReadCyberCmd (ticcmd_t *cmd);
+
+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;
+
+#ifdef __WATCOMC__
+	int angleDelta;
+	static int oldAngle;
+	extern int newViewAngleOff;
+	static int externInvKey;
+	extern boolean automapactive;
+	event_t ev;
+#endif
+
+
+	memset (cmd,0,sizeof(*cmd));
+	//cmd->consistancy =
+	//      consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS];
+	cmd->consistancy =
+		consistancy[consoleplayer][maketic%BACKUPTICS];
+	if (isCyberPresent)
+		I_ReadCyberCmd (cmd);
+
+//printf ("cons: %i\n",cmd->consistancy);
+
+	strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
+		|| joybuttons[joybstrafe];
+	speed = gamekeydown[key_speed] || joybuttons[joybspeed]
+		|| joybuttons[joybspeed];
+#ifdef __WATCOMC__
+	if(useexterndriver)
+	{
+		speed |= (i_ExternData->buttons&EBT_SPEED);
+		strafe |= (i_ExternData->buttons&EBT_STRAFE);
+	}
+#endif
+
+	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])
+		side += sidemove[speed];
+	if (gamekeydown[key_strafeleft])
+		side -= sidemove[speed];
+
+	// Look up/down/center keys
+	if(gamekeydown[key_lookup])
+	{
+		look = lspeed;
+	}
+	if(gamekeydown[key_lookdown])
+	{
+		look = -lspeed;
+	}
+#ifdef __WATCOMC__
+	if(gamekeydown[key_lookcenter] && !useexterndriver)
+	{
+		look = TOCENTER;
+	}
+#else
+	if(gamekeydown[key_lookcenter])
+	{
+		look = TOCENTER;
+	}
+#endif
+
+#ifdef __WATCOMC__
+	if(useexterndriver && look != TOCENTER && (gamestate == GS_LEVEL ||
+		gamestate == GS_INTERMISSION))
+	{
+		if(i_ExternData->moveForward)
+		{
+			forward += i_ExternData->moveForward;
+			if(speed)
+			{
+				forward <<= 1;
+			}
+		}
+		if(i_ExternData->angleTurn)
+		{
+			if(strafe)
+			{
+				side += i_ExternData->angleTurn;
+			}
+			else
+			{
+				cmd->angleturn += i_ExternData->angleTurn;
+			}
+		}
+		if(i_ExternData->moveSideways)
+		{
+			side += i_ExternData->moveSideways;
+			if(speed)
+			{
+				side <<= 1;
+			}
+		}
+		if(i_ExternData->buttons&EBT_CENTERVIEW)
+		{
+			look = TOCENTER;
+			oldAngle = 0;
+		}
+		else if(i_ExternData->pitch)
+		{
+			angleDelta = i_ExternData->pitch-oldAngle;
+			if(abs(angleDelta) < 35)
+			{
+				look = angleDelta/5;
+			}
+			else
+			{
+				look = 7*(angleDelta > 0 ? 1 : -1);
+			}
+			if(look == TOCENTER)
+			{
+				look++;
+			}
+			oldAngle += look*5;
+		}
+		if(i_ExternData->flyDirection)
+		{
+			if(i_ExternData->flyDirection > 0)
+			{
+				flyheight = 5;
+			}
+			else
+			{
+				flyheight = -5;
+			}
+		}
+		if(abs(newViewAngleOff-i_ExternData->angleHead) < 3000)
+		{
+			newViewAngleOff = i_ExternData->angleHead;
+		}
+		if(i_ExternData->buttons&EBT_FIRE)
+		{
+			cmd->buttons |= BT_ATTACK;
+		}
+		if(i_ExternData->buttons&EBT_OPENDOOR)
+		{
+			cmd->buttons |= BT_USE;
+		}
+		if(i_ExternData->buttons&EBT_PAUSE)
+		{
+			cmd->buttons = BT_SPECIAL | BTS_PAUSE;
+			i_ExternData->buttons &= ~EBT_PAUSE;
+		}
+		if(externInvKey&EBT_USEARTIFACT)
+		{
+			ev.type = ev_keyup;
+			ev.data1 = key_useartifact;
+			D_PostEvent(&ev);
+			externInvKey &= ~EBT_USEARTIFACT;
+		}
+		else if(i_ExternData->buttons&EBT_USEARTIFACT)
+		{
+			externInvKey |= EBT_USEARTIFACT;
+			ev.type = ev_keydown;
+			ev.data1 = key_useartifact;
+			D_PostEvent(&ev);
+		}
+		if(externInvKey&EBT_INVENTORYRIGHT)
+		{
+			ev.type = ev_keyup;
+			ev.data1 = key_invright;
+			D_PostEvent(&ev);
+			externInvKey &= ~EBT_INVENTORYRIGHT;
+		}
+		else if(i_ExternData->buttons&EBT_INVENTORYRIGHT)
+		{
+			externInvKey |= EBT_INVENTORYRIGHT;
+			ev.type = ev_keydown;
+			ev.data1 = key_invright;
+			D_PostEvent(&ev);
+		}
+		if(externInvKey&EBT_INVENTORYLEFT)
+		{
+			ev.type = ev_keyup;
+			ev.data1 = key_invleft;
+			D_PostEvent(&ev);
+			externInvKey &= ~EBT_INVENTORYLEFT;
+		}
+		else if(i_ExternData->buttons&EBT_INVENTORYLEFT)
+		{
+			externInvKey |= EBT_INVENTORYLEFT;
+			ev.type = ev_keydown;
+			ev.data1 = key_invleft;
+			D_PostEvent(&ev);
+		}
+		if(i_ExternData->buttons&EBT_FLYDROP)
+		{
+			flyheight = TOCENTER;
+		}
+		if(gamestate == GS_LEVEL)
+		{
+			if(externInvKey&EBT_MAP)
+			{ // AutoMap
+				ev.type = ev_keyup;
+				ev.data1 = AM_STARTKEY;
+				D_PostEvent(&ev);
+				externInvKey &= ~EBT_MAP;
+			}
+			else if(i_ExternData->buttons&EBT_MAP)
+			{
+				externInvKey |= EBT_MAP;
+				ev.type = ev_keydown;
+				ev.data1 = AM_STARTKEY;
+				D_PostEvent(&ev);
+			}
+		}
+#if 0
+		if((i = (i_ExternData->buttons>>EBT_WEAPONSHIFT)&EBT_WEAPONMASK) != 0)
+		{
+			cmd->buttons |= BT_CHANGE;
+			cmd->buttons |= (i-1)<<BT_WEAPONSHIFT;
+		}
+#endif
+		if(i_ExternData->buttons&EBT_WEAPONCYCLE)
+		{
+			int curWeapon;
+			player_t *pl;
+
+			pl = &players[consoleplayer];
+			curWeapon = pl->readyweapon;
+			for(curWeapon = (curWeapon+1)&7; curWeapon != pl->readyweapon;
+				curWeapon = (curWeapon+1)&7)
+			{
+				if(pl->weaponowned[curWeapon])
+				{
+					if(curWeapon >= wp_goldwand && curWeapon <= wp_mace &&
+						!pl->ammo[wpnlev1info[curWeapon].ammo])
+					{ // weapon that requires ammo is empty
+						continue;
+					}
+					break;
+				}
+			}
+			cmd->buttons |= BT_CHANGE;
+			cmd->buttons |= curWeapon<<BT_WEAPONSHIFT;
+		}
+	}
+#endif
+
+	// 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;
+#ifdef __WATCOMC__
+		if(!useexterndriver)
+		{
+			look = TOCENTER;
+		}
+#else
+		look = TOCENTER;
+#endif
+	}
+
+	// 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] )
+	{
+		cmd->buttons |= BT_USE;
+		dclicks = 0;                    // clear double clicks if hit use button
+	}
+
+	for(i = 0; i < NUMWEAPONS-2; i++)
+	{
+		if(gamekeydown['1'+i])
+		{
+			cmd->buttons |= BT_CHANGE;
+			cmd->buttons |= i<<BT_WEAPONSHIFT;
+			break;
+		}
+	}
+
+//
+// mouse
+//
+	if (mousebuttons[mousebforward])
+	{
+		forward += forwardmove[speed];
+	}
+
+//
+// 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));
+}
+
+
+/*
+===============================================================================
+=
+= 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:
+			joybuttons[0] = ev->data1&1;
+			joybuttons[1] = ev->data1&2;
+			joybuttons[2] = ev->data1&4;
+			joybuttons[3] = ev->data1&8;
+			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;
+
+//
+// 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:
+			M_ScreenShot ();
+			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)
+						{
+							strcpy (savedescription, "NET GAME");
+						}
+						else
+						{
+							strcpy(savedescription, "SAVE GAME");
+						}
+					}
+					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));
+	sprintf(vcheck, "version %i", VERSION);
+	if (strcmp (save_p, vcheck))
+	{ // 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("SKY1");
+	}
+	else
+	{
+		skytexture = R_TextureNumForName(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 ();
+
+		Z_ChangeTag (demobuffer, PU_CACHE);
+		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));
+	sprintf(verString, "version %i", 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], TXT_GAMESAVED, true);
+}
+
+//==========================================================================
+//
+// SV_Open
+//
+//==========================================================================
+
+void SV_Open(char *fileName)
+{
+	MallocFailureOk = true;
+	save_p = savebuffer = Z_Malloc(SAVEGAMESIZE, PU_STATIC, NULL);
+	MallocFailureOk = false;
+	if(savebuffer == NULL)
+	{ // Not enough memory - use file save method
+		SaveGameType = SVG_FILE;
+		SaveGameFP = fopen(fileName, "wb");
+	}
+	else
+	{
+		SaveGameType = SVG_RAM;
+	}
+}
+
+//==========================================================================
+//
+// 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));
+}
--- /dev/null
+++ b/src/heretic/g_old.c
@@ -1,0 +1,1819 @@
+
+// G_game.c
+
+#include <string.h>
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.h"
+
+// Macros
+
+#define SAVE_GAME_TERMINATOR 0x1d
+
+// 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
+};
+
+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
+
+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)
+//
+int             key_right, key_left, key_up, key_down;
+int             key_strafeleft, key_straferight;
+int             key_fire, key_use, key_strafe, key_speed;
+int				key_flyup, key_flydown, key_flycenter;
+int				key_lookup, key_lookdown, key_lookcenter;
+int				key_invleft, key_invright, key_useartifact;
+
+int             mousebfire;
+int             mousebstrafe;
+int             mousebforward;
+
+int             joybfire;
+int             joybstrafe;
+int             joybuse;
+int             joybspeed;
+
+
+
+#define MAXPLMOVE       0x32
+
+fixed_t         forwardmove[2] = {0x19, 0x32};
+fixed_t         sidemove[2] = {0x18, 0x28};
+fixed_t         angleturn[3] = {640, 1280, 320};     // + slow turn
+#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;
+
+int             joyxmove, joyymove;         // joystick values are repeated
+boolean         joyarray[5];
+boolean         *joybuttons = &joyarray[1];     // allow [-1]
+
+int     savegameslot;
+char    savedescription[32];
+
+int inventoryTics;
+
+#ifdef __WATCOMC__
+extern externdata_t *i_ExternData;
+#endif
+
+//=============================================================================
+// 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;
+
+extern  int             isCyberPresent;     // is CyberMan present?
+boolean usearti = true;
+void I_ReadCyberCmd (ticcmd_t *cmd);
+
+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;
+
+#ifdef __WATCOMC__
+	int angleDelta;
+	static int oldAngle;
+	extern int newViewAngleOff;
+	static int externInvKey;
+	extern boolean automapactive;
+	event_t ev;
+#endif
+
+
+	memset (cmd,0,sizeof(*cmd));
+	cmd->consistancy =
+		consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS];
+	if (isCyberPresent)
+		I_ReadCyberCmd (cmd);
+
+//printf ("cons: %i\n",cmd->consistancy);
+	
+	strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
+		|| joybuttons[joybstrafe];
+	speed = gamekeydown[key_speed] || joybuttons[joybspeed]
+		|| joybuttons[joybspeed];
+#ifdef __WATCOMC__
+	if(useexterndriver)
+	{
+		speed |= (i_ExternData->buttons&EBT_SPEED);
+		strafe |= (i_ExternData->buttons&EBT_STRAFE);
+	}
+#endif
+
+	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 = 3;
+	}
+	else
+	{
+		lspeed = 5;
+	}
+
+//
+// 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])
+		side += sidemove[speed];
+	if (gamekeydown[key_strafeleft])
+		side -= sidemove[speed];
+
+	// Look up/down/center keys
+	if(gamekeydown[key_lookup])
+	{
+		look = lspeed;
+	}
+	if(gamekeydown[key_lookdown])
+	{
+		look = -lspeed;
+	}
+	if(gamekeydown[key_lookcenter])
+	{
+		look = TOCENTER;
+	}
+
+#ifdef __WATCOMC__
+	if(useexterndriver && i_ExternData->buttons&EBT_CENTERVIEW)
+	{
+		look = TOCENTER;
+	}
+	if(useexterndriver && look != TOCENTER && (gamestate == GS_LEVEL ||
+		gamestate == GS_INTERMISSION))
+	{
+		if(i_ExternData->moveForward)
+		{
+			forward += i_ExternData->moveForward;
+			if(speed)
+			{
+				forward <<= 1;
+			}
+		}
+		if(i_ExternData->angleTurn)
+		{
+			if(strafe)
+			{
+				side += i_ExternData->angleTurn;
+			}
+			else
+			{
+				cmd->angleturn += i_ExternData->angleTurn;
+			}
+		}
+		if(i_ExternData->moveSideways)
+		{
+			side += i_ExternData->moveSideways;
+			if(speed)
+			{
+				side <<= 1;
+			}
+		}
+		if(i_ExternData->pitch)
+		{
+			angleDelta = i_ExternData->pitch-oldAngle;
+			if(abs(angleDelta < 14))
+			{
+				look = angleDelta/2;
+			}
+			else
+			{
+				look = 7*(angleDelta > 0 ? 1 : -1);
+			}
+			if(look == TOCENTER)
+			{
+				look++;
+			}
+			oldAngle += look;
+		}
+		if(i_ExternData->flyDirection)
+		{
+			if(i_ExternData->flyDirection > 0)
+			{
+				flyheight = 5;
+			}
+			else
+			{
+				flyheight = -5;
+			}
+		}
+		if(abs(newViewAngleOff-i_ExternData->angleHead) < 3000)
+		{
+			newViewAngleOff = i_ExternData->angleHead;
+		}
+		if(i_ExternData->buttons&EBT_FIRE)
+		{
+			cmd->buttons |= BT_ATTACK;
+		}
+		if(i_ExternData->buttons&EBT_OPENDOOR)
+		{
+			cmd->buttons |= BT_USE;
+		}
+		if(i_ExternData->buttons&EBT_PAUSE)
+		{
+			sendpause ^= 1;
+		}
+		if(externInvKey&EBT_USEARTIFACT)
+		{
+			ev.type = ev_keyup;
+			ev.data1 = key_useartifact;
+			D_PostEvent(&ev);
+			externInvKey &= ~EBT_USEARTIFACT;
+		}
+		else if(i_ExternData->buttons&EBT_USEARTIFACT)
+		{
+			externInvKey |= EBT_USEARTIFACT;
+			ev.type = ev_keydown;
+			ev.data1 = key_useartifact;
+			D_PostEvent(&ev);
+		}
+		if(externInvKey&EBT_INVENTORYRIGHT)
+		{
+			ev.type = ev_keyup;
+			ev.data1 = key_invright;
+			D_PostEvent(&ev);
+			externInvKey &= ~EBT_INVENTORYRIGHT;
+		}
+		else if(i_ExternData->buttons&EBT_INVENTORYRIGHT)
+		{
+			externInvKey |= EBT_INVENTORYRIGHT;
+			ev.type = ev_keydown;
+			ev.data1 = key_invright;
+			D_PostEvent(&ev);
+		}
+		if(externInvKey&EBT_INVENTORYLEFT)
+		{
+			ev.type = ev_keyup;
+			ev.data1 = key_invleft;
+			D_PostEvent(&ev);
+			externInvKey &= ~EBT_INVENTORYLEFT;
+		}
+		else if(i_ExternData->buttons&EBT_INVENTORYLEFT)
+		{
+			externInvKey |= EBT_INVENTORYLEFT;
+			ev.type = ev_keydown;
+			ev.data1 = key_invleft;
+			D_PostEvent(&ev);
+		}
+		if(i_ExternData->buttons&EBT_FLYDROP)
+		{
+			flyheight = TOCENTER;
+		}
+		if(i_ExternData->buttons&EBT_MAP && gamestate == GS_LEVEL)
+		{
+			if(automapactive)
+			{
+				AM_Stop();
+			}
+			else
+			{
+				AM_Start();
+			}
+		}
+	}
+#endif
+
+	// 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;
+		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] )
+	{
+		cmd->buttons |= BT_USE;
+		dclicks = 0;                    // clear double clicks if hit use button
+	}
+	
+	for(i = 0; i < NUMWEAPONS-2; i++)
+	{
+		if(gamekeydown['1'+i])
+		{
+			cmd->buttons |= BT_CHANGE;
+			cmd->buttons |= i<<BT_WEAPONSHIFT;
+			break;
+		}
+	}
+
+//
+// mouse
+//
+	if (mousebuttons[mousebforward])
+		forward += forwardmove[speed];
+		
+//
+// 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));
+}
+
+
+/*
+===============================================================================
+=
+= 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:
+			joybuttons[0] = ev->data1&1;
+			joybuttons[1] = ev->data1&2;
+			joybuttons[2] = ev->data1&4;
+			joybuttons[3] = ev->data1&8;
+			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;
+			
+//
+// 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:
+			M_ScreenShot ();
+			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;
+
+	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)
+						{
+							strcpy (savedescription, "NET GAME");
+						}
+						else
+						{
+							strcpy(savedescription, "SAVE GAME");
+						}
+					}
+					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
+	}
+}
+
+/*
+====================
+=
+= 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[3] = { 7, 5, 5 };
+
+	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));
+	sprintf(vcheck, "version %i", VERSION);
+	if (strcmp (save_p, vcheck))
+	{ // 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);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC 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;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC G_DoSaveGame
+//
+// Called by G_Ticker based on gameaction.
+//
+//---------------------------------------------------------------------------
+
+void G_DoSaveGame(void)
+{
+	char name[100];
+	char name2[VERSIONSIZE];
+	char *description;
+	int length;
+	int i;
+
+	if(cdrom)
+	{
+		sprintf(name, SAVEGAMENAMECD"%d.hsg", savegameslot);
+	}
+	else
+	{
+		sprintf(name, SAVEGAMENAME"%d.hsg", savegameslot);
+	}
+	description = savedescription;
+
+	// Allocate save game buffer
+	save_p = savebuffer = Z_Malloc(SAVEGAMESIZE, PU_STATIC, NULL);
+
+	memcpy(save_p, description, SAVESTRINGSIZE);
+	save_p += SAVESTRINGSIZE;
+	memset(name2, 0, sizeof(name2));
+	sprintf(name2, "version %i",VERSION);
+	memcpy(save_p, name2, VERSIONSIZE);
+	save_p += VERSIONSIZE;
+
+	*save_p++ = gameskill;
+	*save_p++ = gameepisode;
+	*save_p++ = gamemap;
+	for(i = 0; i < MAXPLAYERS; i++)
+	{
+		*save_p++ = playeringame[i];
+	}
+	*save_p++ = leveltime>>16;
+	*save_p++ = leveltime>>8;
+	*save_p++ = leveltime;
+
+	P_ArchivePlayers();
+	P_ArchiveWorld();
+	P_ArchiveThinkers();
+	P_ArchiveSpecials();
+
+	// Send a savegame termination marker
+	*save_p++ = SAVE_GAME_TERMINATOR;
+
+	length = save_p-savebuffer;
+	if(length > SAVEGAMESIZE)
+	{
+		I_Error("Savegame buffer overrun");
+	}
+	M_WriteFile(name, savebuffer, length);
+	gameaction = ga_nothing;
+	savedescription[0] = 0;
+	Z_Free(savebuffer);
+	P_SetMessage(&players[consoleplayer], TXT_GAMESAVED, true);
+}
+
+/*
+====================
+=
+= 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;
+
+	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;
+	}
+	// 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
+	switch(episode)
+	{
+		case 1:
+			skytexture = R_TextureNumForName("SKY1");
+			break;
+		case 2:
+			skytexture = R_TextureNumForName("SKY2");
+			break;
+		case 3:
+			skytexture = R_TextureNumForName("SKY3");
+			break;
+		default:
+			skytexture = R_TextureNumForName("SKY1");
+			break;
+	}
+
+//
+// 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 ();
+			
+		Z_ChangeTag (demobuffer, PU_CACHE);
+		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;
+}
+
+
+
--- /dev/null
+++ b/src/heretic/i_cyber.c
@@ -1,0 +1,259 @@
+// I_cyber.c
+
+#include <dos.h>
+#include <stdlib.h>
+
+
+/*
+====================================================
+
+Doom control structure
+
+The keybaord and joystick will add to the values set by the cyberman,
+to a maximum of 0x19000 for forwardmove and sidemove.  Angleturn is
+not bounded at all.
+
+parm                    normal          fast
+-----           ------          ----
+forwardmove             0xc800          0x19000
+sidemove                0xc000          0x14000
+angleturn               0x2800000       0x5000000
+
+The keyboard and joystick have a 1/3 second slow turn of 0x1400000 under
+normal speed to help aiming.
+
+
+
+====================================================
+*/
+
+typedef struct
+{
+	char            forwardmove;            // *2048 for move
+	char            sidemove;                       // *2048 for move
+	short           angleturn;                      // <<16 for angle delta
+	short           consistancy;            // checks for net game
+	unsigned char            chatchar;
+	unsigned char           buttons;
+} ticcmd_t;
+
+#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
+
+
+
+//==================================================
+//
+// CyberMan detection and usage info
+//
+//==================================================
+#define DPMI_INT        0x31
+#define MOUSE_INT       0x33
+
+#define DOSMEMSIZE      64      // enough for any SWIFT structure
+
+typedef struct {
+   short        x;
+   short        y;
+   short        z;
+   short        pitch;
+   short        roll;
+   short        yaw;
+   short        buttons;
+} SWIFT_3DStatus;
+
+// DPMI real mode interrupt structure
+static struct rminfo {
+	long EDI;
+	long ESI;
+	long EBP;
+	long reserved_by_system;
+	long EBX;
+	long EDX;
+	long ECX;
+	long EAX;
+	short flags;
+	short ES,DS,FS,GS,IP,CS,SP,SS;
+} RMI;
+
+typedef struct {
+   unsigned char        deviceType;
+   unsigned char        majorVersion;
+   unsigned char        minorVersion;
+   unsigned char        absRelFlags;
+   unsigned char        centeringFlags;
+   unsigned char        reserved[5];
+} StaticDeviceData;
+
+// values for deviceType:
+#define DEVTYPE_CYBERMAN        1
+
+short                   selector;
+unsigned short  segment;                // segment of DOS memory block
+SWIFT_3DStatus  *cyberstat;
+int                             isCyberPresent;         // is CyberMan present?
+
+
+static  union REGS regs;
+static  struct SREGS sregs;
+
+
+extern  int mousepresent;
+
+//===========================================================
+//
+// I_StartupCyberMan
+//
+// If a cyberman is present, init it and set isCyberPresent to 1
+//===========================================================
+void I_StartupCyberMan(void)
+{
+   StaticDeviceData *pbuf;
+   int success = 0;
+
+   isCyberPresent = 0;
+
+   cyberstat = (SWIFT_3DStatus *)I_AllocLow (DOSMEMSIZE);
+   segment = (int)cyberstat>>4;
+
+   pbuf = (StaticDeviceData *)cyberstat;
+   memset(pbuf, 0, sizeof (StaticDeviceData));
+
+   // Use DPMI call 300h to issue mouse interrupt
+   memset(&RMI, 0, sizeof(RMI));
+   RMI.EAX = 0x53C1;            // SWIFT: Get Static Device Data
+   RMI.ES = segment;
+   RMI.EDX = 0;
+   memset(&sregs, 0, sizeof (sregs));
+   regs.w.ax = 0x0300;          // DPMI: simulate interrupt
+   regs.w.bx = MOUSE_INT;
+   regs.w.cx = 0;
+   regs.x.edi = FP_OFF(&RMI);
+   sregs.es = FP_SEG(&RMI);
+   int386x( DPMI_INT, &regs, &regs, &sregs );
+
+   if ((short)RMI.EAX != 1)
+   {
+	  // SWIFT functions not present
+	  tprintf("CyberMan: Wrong mouse driver - no SWIFT support (AX=%04x).\n",
+			 (unsigned)(short)RMI.EAX);
+   }
+   else
+   if (pbuf->deviceType != DEVTYPE_CYBERMAN)
+   {
+	  // no SWIFT device, or not CyberMan
+	  if (pbuf->deviceType == 0)
+	  {
+		 tprintf("CyberMan: no SWIFT device connected.\n");
+	  }
+	  else
+	  {
+		 tprintf("CyberMan: SWIFT device is not a CyberMan! (type=%d)\n",
+				pbuf->deviceType);
+	  }
+   }
+   else
+   {
+	  tprintf("CyberMan: CyberMan %d.%02d connected.\n",
+			 pbuf->majorVersion, pbuf->minorVersion);
+	  isCyberPresent = 1;
+	  mousepresent = 0;
+   }
+}
+
+
+
+/*
+===============
+=
+= I_ReadCyberCmds
+=
+===============
+*/
+
+
+int             oldpos;
+
+void I_ReadCyberCmd (ticcmd_t *cmd)
+{
+	int             delta;
+
+	// Use DPMI call 300h to issue mouse interrupt
+	memset(&RMI, 0, sizeof(RMI));
+	RMI.EAX = 0x5301;            // SWIFT: Get Position and Buttons
+	RMI.ES = segment;
+	RMI.EDX = 0;
+	memset(&sregs, 0, sizeof (sregs));
+	regs.w.ax = 0x0300;          // DPMI: simulate interrupt
+	regs.w.bx = MOUSE_INT;
+	regs.w.cx = 0;
+	regs.x.edi = FP_OFF(&RMI);
+	sregs.es = FP_SEG(&RMI);
+	int386x( DPMI_INT, &regs, &regs, &sregs );
+
+	if (cyberstat->y < -7900)
+		cmd->forwardmove = 0xc800/2048;
+	else if (cyberstat->y > 7900)
+		cmd->forwardmove = -0xc800/2048;
+
+	if (cyberstat->buttons & 4)
+		cmd->buttons |= BT_ATTACK;
+	if (cyberstat->buttons & 2)
+		cmd->buttons |= BT_USE;
+
+	delta = cyberstat->x - oldpos;
+	oldpos = cyberstat->x;
+
+	if (cyberstat->buttons & 1)
+	{       // strafe
+		if (cyberstat->x < -7900)
+			cmd->sidemove = -0xc800/2048;
+		else if (cyberstat->x > 7900)
+			cmd->sidemove = 0xc800/2048;
+		else
+			cmd->sidemove = delta*40/2048;
+	}
+	else
+	{
+		if (cyberstat->x < -7900)
+			cmd->angleturn = 0x280;
+		else if (cyberstat->x > 7900)
+			cmd->angleturn = -0x280;
+		else
+			cmd->angleturn = -delta*0xa/16;
+
+	}
+
+}
+
+
+void I_Tactile (int on, int off, int total)
+{
+	if (!isCyberPresent)
+		return;
+
+	on /= 5;
+	off /= 5;
+	total /= 40;
+	if (on > 255)
+		on = 255;
+	if (off > 255)
+		off = 255;
+	if (total > 255)
+		total = 255;
+
+	memset(&RMI, 0, sizeof(RMI));
+	RMI.EAX = 0x5330;            // SWIFT: Get Position and Buttons
+	RMI.EBX = on*256+off;
+	RMI.ECX = total;
+	memset(&sregs, 0, sizeof (sregs));
+	regs.w.ax = 0x0300;          // DPMI: simulate interrupt
+	regs.w.bx = MOUSE_INT;
+	regs.w.cx = 0;
+	regs.x.edi = FP_OFF(&RMI);
+	sregs.es = FP_SEG(&RMI);
+	int386x( DPMI_INT, &regs, &regs, &sregs );
+}
--- /dev/null
+++ b/src/heretic/i_header.h
@@ -1,0 +1,77 @@
+#ifndef __I_HEADER_H__
+#define __I_HEADER_H__
+
+#include "DoomDef.h"
+
+//--------
+//SOUND IO
+//--------
+#define	FREQ_LOW		0x40
+#define FREQ_NORM		0x80
+#define FREQ_HIGH		0xff
+
+void I_SetMasterVolume(int volume);
+
+void I_TurnOffSfx(void);
+void I_TurnOnSfx(void);
+void I_TurnOffMusic(void);
+void I_TurnOnMusic(void);
+
+//  MUSIC I/O
+//
+
+int I_RegisterSong(void *songdata);
+// called by anything that wants to register a song lump with the sound lib
+// calls Paul's function of the similar name to register music only.
+// note that the song data is the same for any sound card and is paul's
+// MUS format.  Returns a handle which will be passed to all other music
+// functions.
+
+void I_UnregisterSong(int handle);
+// called by anything which is finished with a song and no longer needs
+// the sound library to be aware of it.  All songs should be stopped
+// before calling this, but it will double check and stop it if necessary.
+
+void I_LoopSong(int handle);
+// called by anything that wishes to start music.
+// plays a song, and when the song is done, starts playing it again in
+// an endless loop.  the start is faded in over three seconds.
+
+void I_FadeOutSong(int handle, int fotime);
+// called by anything that wishes to stop music.
+// fades out the song over <fotime> milliseconds.
+
+void I_StopSong(int handle);
+// called by anything that wishes to stop music.
+// stops a song abruptly.
+
+//  SFX I/O
+//
+
+void *I_GetSoundEffect (char *soundname);
+// called by routines which wish to play a sound effect at some later
+// time.  Pass it the lump name of a sound effect WITHOUT the sfx
+// prefix.  This means the maximum name length is 7 letters/digits.
+// The prefixes for different sound cards are 'S','M','A', and 'P'.
+// They refer to the card type.  The routine will cache in the
+// appropriate sound effect when it is played.
+
+void I_UngetSoundEffect (void *soundset);
+// called by routines which wish to no longer use the sounds at all
+// frees up the associated structure.  It stops any currently playing
+// sound effects.
+
+void I_StartSound (channel_t *c, int vol, int sep, int pitch, int priority);
+// Starts a sound in a particular sound channel
+
+void I_UpdateSoundParams(channel_t *c, int vol, int sep, int pitch);
+// Updates the volume, separation, and pitch of a sound channel
+
+void I_StopSound(channel_t *c);
+// Stops a sound channel
+
+int I_SoundIsPlaying(channel_t *c);
+// called by S_*()'s to see if a channel is still playing.  Returns 0
+// if no longer playing, 1 if playing.
+
+#endif
--- /dev/null
+++ b/src/heretic/i_ibm.c
@@ -1,0 +1,2204 @@
+
+// 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 "sounds.h"
+#include "i_sound.h"
+#include "dmx.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;
+
+/*
+===============================================================================
+
+		MUSIC & SFX API
+
+===============================================================================
+*/
+
+static channel_t channel[MAX_CHANNELS];
+
+static int rs; //the current registered song.
+int mus_song = -1;
+int mus_lumpnum;
+void *mus_sndptr;
+byte *soundCurve;
+
+extern sfxinfo_t S_sfx[];
+extern musicinfo_t S_music[];
+
+extern int snd_DesiredMusicDevice;
+extern int snd_DesiredSfxDevice;
+extern int snd_MaxVolume;
+extern int snd_MusicVolume;
+extern int snd_Channels;
+
+extern int startepisode;
+extern int startmap;
+
+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)
+{
+	if(song == mus_song)
+	{ // don't replay an old song
+		return;
+	}
+	if(rs)
+	{
+		I_StopSong(rs);
+		I_UnRegisterSong(rs);
+		Z_ChangeTag(lumpcache[mus_lumpnum], PU_CACHE);
+		#ifdef __WATCOMC__
+			_dpmi_unlockregion(mus_sndptr, lumpinfo[mus_lumpnum].size);
+		#endif
+	}
+	if(song < mus_e1m1 || song > NUMMUSIC)
+	{
+		return;
+	}
+	mus_lumpnum = W_GetNumForName(S_music[song].name);
+	mus_sndptr = W_CacheLumpNum(mus_lumpnum, PU_MUSIC);
+	#ifdef __WATCOMC__
+		_dpmi_lockregion(mus_sndptr, lumpinfo[mus_lumpnum].size);
+	#endif
+	rs = I_RegisterSong(mus_sndptr);
+	I_PlaySong(rs, loop); //'true' denotes endless looping.
+	mus_song = song;
+}
+
+void S_StartSound(mobj_t *origin, int sound_id)
+{
+	int dist, vol;
+	int i;
+	int sound;
+	int priority;
+	int sep;
+	int angle;
+	int absx;
+	int absy;
+
+	static int sndcount = 0;
+	int chan;
+
+	if(sound_id==0 || snd_MaxVolume == 0)
+		return;
+	if(origin == NULL)
+	{
+		origin = players[consoleplayer].mo;
+	}
+
+// calculate the distance before other stuff so that we can throw out
+// sounds that are beyond the hearing range.
+	absx = abs(origin->x-players[consoleplayer].mo->x);
+	absy = abs(origin->y-players[consoleplayer].mo->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]);
+	}
+	if(S_sfx[sound_id].snd_ptr == NULL)
+	{
+		S_sfx[sound_id].snd_ptr = W_CacheLumpNum(S_sfx[sound_id].lumpnum,
+			PU_SOUND);
+		#ifdef __WATCOMC__
+		_dpmi_lockregion(S_sfx[sound_id].snd_ptr,
+			lumpinfo[S_sfx[sound_id].lumpnum].size);
+		#endif
+	}
+
+	// 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 == players[consoleplayer].mo)
+	{
+		sep = 128;
+	}
+	else
+	{
+		angle = R_PointToAngle2(players[consoleplayer].mo->x,
+			players[consoleplayer].mo->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;
+	}
+
+	channel[i].pitch = (byte)(127+(M_Random()&7)-(M_Random()&7));
+	channel[i].handle = I_StartSound(sound_id, S_sfx[sound_id].snd_ptr, vol, sep, channel[i].pitch, 0);
+	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(mobj_t *origin, int sound_id, int volume)
+{
+	int dist;
+	int i;
+	int sep;
+
+	static int sndcount;
+	int chan;
+
+	if(sound_id == 0 || snd_MaxVolume == 0)
+		return;
+	if(origin == NULL)
+	{
+		origin = players[consoleplayer].mo;
+	}
+
+	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]);
+	}
+	if(S_sfx[sound_id].snd_ptr == NULL)
+	{
+		S_sfx[sound_id].snd_ptr = W_CacheLumpNum(S_sfx[sound_id].lumpnum,
+			PU_SOUND);
+		#ifdef __WATCOMC__
+		_dpmi_lockregion(S_sfx[sound_id].snd_ptr,
+			lumpinfo[S_sfx[sound_id].lumpnum].size);
+		#endif
+	}
+	channel[i].pitch = (byte)(127-(M_Random()&3)+(M_Random()&3));
+	channel[i].handle = I_StartSound(sound_id, S_sfx[sound_id].snd_ptr, volume, 128, channel[i].pitch, 0);
+	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(mobj_t *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(rs);
+}
+
+void S_ResumeSound(void)
+{
+	I_ResumeSong(rs);
+}
+
+static int nextcleanup;
+
+void S_UpdateSounds(mobj_t *listener)
+{
+	int i, dist, vol;
+	int angle;
+	int sep;
+	int priority;
+	int absx;
+	int absy;
+
+	listener = players[consoleplayer].mo;
+	if(snd_MaxVolume == 0)
+	{
+		return;
+	}
+	if(nextcleanup < gametic)
+	{
+		for(i=0; i < NUMSFX; i++)
+		{
+			if(S_sfx[i].usefulness == 0 && S_sfx[i].snd_ptr)
+			{
+				if(lumpcache[S_sfx[i].lumpnum])
+				{
+					if(((memblock_t *)((byte *)(lumpcache[S_sfx[i].lumpnum])-
+						sizeof(memblock_t)))->id == 0x1d4a11)
+					{ // taken directly from the Z_ChangeTag macro
+						Z_ChangeTag2(lumpcache[S_sfx[i].lumpnum], PU_CACHE);
+						#ifdef __WATCOMC__
+							_dpmi_unlockregion(S_sfx[i].snd_ptr, lumpinfo[S_sfx[i].lumpnum].size);
+						#endif
+					}
+				}
+				S_sfx[i].usefulness = -1;
+				S_sfx[i].snd_ptr = NULL;
+			}
+		}
+		nextcleanup = gametic+35; //CLEANUP DEBUG cleans every second
+	}
+	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 == players[consoleplayer].mo)
+		{
+			continue;
+		}
+		else
+		{
+			absx = abs(channel[i].mo->x-players[consoleplayer].mo->x);
+			absy = abs(channel[i].mo->y-players[consoleplayer].mo->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(players[consoleplayer].mo->x,
+				players[consoleplayer].mo->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;
+			I_UpdateSoundParams(channel[i].handle, vol, sep, channel[i].pitch);
+			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_StartupSound();
+	if(snd_Channels > 8)
+	{
+		snd_Channels = 8;
+	}
+	I_SetChannels(snd_Channels);
+	I_SetMusicVolume(snd_MusicVolume);
+	S_SetMaxVolume(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;
+		c->distance = P_AproxDistance(c->mo->x-viewx, c->mo->y-viewy)
+			>>FRACBITS;
+	}
+}
+
+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);
+	if(snd_MusicVolume == 0)
+	{
+		I_PauseSong(rs);
+		musicPaused = true;
+	}
+	else if(musicPaused)
+	{
+		musicPaused = false;
+		I_ResumeSong(rs);
+	}
+}
+
+void S_ShutDown(void)
+{
+	extern int tsm_ID;
+	if(tsm_ID != -1)
+	{
+  		I_StopSong(rs);
+  		I_UnRegisterSong(rs);
+  		I_ShutdownSound();
+	}
+}
+
+/*
+=============================================================================
+
+							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();
+	tprintf("S_Init... ",1);
+	S_Init();
+	//IO_StartupTimer();
+	S_Start();
+}
+
+
+/*
+===============
+=
+= 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);
+	}
+}
--- /dev/null
+++ b/src/heretic/i_ibm_a.asm
@@ -1,0 +1,135 @@
+	.386
+	.MODEL  small
+
+.DATA
+
+
+
+.CODE
+
+IF 0
+#define PEL_WRITE_ADR   0x3c8
+#define PEL_READ_ADR    0x3c7
+#define PEL_DATA                0x3c9
+ENDIF
+
+;================
+;
+; I_DivException
+;
+;================
+
+PROC  	I_DivException_
+PUBLIC 	I_DivException_
+	mov	edx,03c9h
+	mov	al,63
+	out	dx,al
+
+	mov	ebx,0ffffffh
+	mov	eax,[ebx]
+	retf
+ENDP
+
+;================
+;
+; I_SetDivException
+;
+;================
+
+PROC  	I_SetDivException_
+PUBLIC 	I_SetDivException_
+	pusha
+
+	mov	eax,0212h
+	mov	ebx,0
+	mov	ecx,cs
+	mov	edx,OFFSET I_DivException_
+	int 31h
+	jnc	good
+
+	popa
+	mov	eax,0
+	ret
+
+good:
+	popa
+	mov	eax,1
+	ret
+
+ENDP
+
+
+;================
+;
+; I_ReadJoystick
+;
+; Read the absolute joystick values
+; returns false if not connected
+;================
+
+.data
+
+_joystickx	dd	0
+_joysticky	dd	0
+PUBLIC	_joystickx, _joysticky
+
+.code
+
+PROC  	I_ReadJoystick_
+PUBLIC 	I_ReadJoystick_
+	pusha
+	pushf					; state of interrupt flag
+	cli
+
+	mov		dx,0201h
+	in		al,dx
+	out		dx,al		; Clear the resistors
+
+	mov		ah,1		; Get masks into registers
+	mov		ch,2
+
+	xor		esi,esi		; Clear count registers
+	xor		edi,edi
+	xor		ebx,ebx		; Clear high byte of bx for later
+
+	mov		ebp,10000	; joystick is disconnected if value is this big
+
+jloop:
+	in		al,dx		; Get bits indicating whether all are finished
+
+	dec		ebp			; Check bounding register
+	jz		bad			; We have a silly value - abort
+
+	mov		bl,al		; Duplicate the bits
+	and		bl,ah		; Mask off useless bits (in [xb])
+	add		esi,ebx		; Possibly increment count register
+	mov		cl,bl		; Save for testing later
+
+	mov		bl,al
+	and		bl,ch		; [yb]
+	add		edi,ebx
+
+	add		cl,bl
+	jnz		jloop 		; If both bits were 0, drop out
+
+done:
+	mov		[_joystickx],esi
+	shr		edi,1		; because 2s were added
+	mov		[_joysticky],edi
+
+	popf			; restore interrupt flag
+	popa
+	mov	eax,1		; read was ok
+	ret
+
+bad:
+	popf			; restore interrupt flag
+	popa
+	xor     eax, eax	; read was bad
+	ret
+
+ENDP
+
+
+END
+
--- /dev/null
+++ b/src/heretic/i_sound.c
@@ -1,0 +1,391 @@
+
+// I_SOUND.C
+
+#include <stdio.h>
+#include "doomdef.h"
+#include "dmx.h"
+#include "sounds.h"
+#include "i_sound.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);
+}
--- /dev/null
+++ b/src/heretic/i_sound.h
@@ -1,0 +1,23 @@
+#ifndef __SOUND__
+#define __SOUND__
+
+#define SND_TICRATE             140             // tic rate for updating sound
+#define SND_MAXSONGS    40              // max number of songs in game
+#define SND_SAMPLERATE  11025   // sample rate of sound effects
+
+typedef enum
+{
+  snd_none,
+  snd_PC,
+  snd_Adlib,
+  snd_SB,
+  snd_PAS,
+  snd_GUS,
+  snd_MPU,
+  snd_MPU2,
+  snd_MPU3,
+  snd_AWE,
+  NUM_SCARDS
+} cardenum_t;
+
+#endif
--- /dev/null
+++ b/src/heretic/in_lude.c
@@ -1,0 +1,1007 @@
+/*
+========================
+=
+= IN_lude.c
+=
+========================
+*/
+
+#include "DoomDef.h"
+#include "soundst.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 time;
+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("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;
+
+
+	if(!netgame)
+	{
+		gametype = SINGLE;
+		time = leveltime/35;
+		hours = time/3600;
+		time -= hours*3600;
+		minutes = time/60;
+		time -= minutes*60;
+		seconds = time;
+	}
+	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;
+		}
+	}
+}
+
+//========================================================================
+//
+// IN_LoadPics
+//
+//========================================================================
+
+void IN_LoadPics(void)
+{
+	int i;
+
+	switch(gameepisode)
+	{
+		case 1:
+			patchINTERPIC = W_CacheLumpName("MAPE1", PU_STATIC);
+			break;
+		case 2:
+			patchINTERPIC = W_CacheLumpName("MAPE2", PU_STATIC);
+			break;
+		case 3:
+			patchINTERPIC = W_CacheLumpName("MAPE3", PU_STATIC);
+			break;
+		default:
+			break;
+	}
+	patchBEENTHERE = W_CacheLumpName("IN_X", PU_STATIC);
+	patchGOINGTHERE = W_CacheLumpName("IN_YAH", PU_STATIC);
+	FontBLumpBase = W_GetNumForName("FONTB16");
+	for(i=0; i<10; i++)
+	{
+		FontBNumbers[i] = W_CacheLumpNum(FontBLumpBase+i, PU_STATIC);
+	}
+	FontBLump = W_GetNumForName("FONTB_S")+1;
+	FontBNegative = W_CacheLumpName("FONTB13", PU_STATIC);
+
+	FontBSlash = W_CacheLumpName("FONTB15", PU_STATIC);
+	FontBPercent = W_CacheLumpName("FONTB05", PU_STATIC);
+	patchFaceOkayBase = W_GetNumForName("FACEA0");
+	patchFaceDeadBase = W_GetNumForName("FACEB0");
+}
+
+//========================================================================
+//
+// IN_UnloadPics
+//
+//========================================================================
+
+void IN_UnloadPics(void)
+{
+	int i;
+
+	if(patchINTERPIC)
+	{
+		Z_ChangeTag(patchINTERPIC, PU_CACHE);
+	}
+	Z_ChangeTag(patchBEENTHERE, PU_CACHE);
+	Z_ChangeTag(patchGOINGTHERE, PU_CACHE);
+	for(i=0; i<10; i++)
+	{
+		Z_ChangeTag(FontBNumbers[i], PU_CACHE);
+	}
+	Z_ChangeTag(FontBNegative, PU_CACHE);
+	Z_ChangeTag(FontBSlash, PU_CACHE);
+	Z_ChangeTag(FontBPercent, PU_CACHE);
+}
+
+//========================================================================
+//
+// 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 = MAXINT;
+				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 ("FLOOR16", PU_CACHE);
+	dest = screen;
+
+	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("FINISHED")/2;
+	MN_DrTextA("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("NOW ENTERING:")/2;
+	MN_DrTextA("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("KILLS", 50, 65);
+	IN_DrTextB("ITEMS", 50, 90);
+	IN_DrTextB("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("FINISHED")/2;
+	MN_DrTextA("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(!ExtendedWAD || gameepisode < 4)
+	{
+		IN_DrTextB("TIME", 85, 160);
+		IN_DrawTime(155, 160, hours, minutes, seconds);
+	}
+	else
+	{
+		x = 160-MN_TextAWidth("NOW ENTERING:")/2;
+		MN_DrTextA("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("KILLS", 95, 35);
+	IN_DrTextB("BONUS", 155, 35);
+	IN_DrTextB("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("FINISHED")/2;
+	MN_DrTextA("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;
+	int x;
+
+	static int sounds;
+
+	xpos = 90;
+	ypos = 55;
+
+	IN_DrTextB("TOTAL", 265, 30);
+	MN_DrTextA("VICTIMS", 140, 8);
+	for(i=0; i<7; i++)
+	{
+		MN_DrTextA(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_DrawFuzzPatch(40, ypos, W_CacheLumpNum(patchFaceOkayBase+i, PU_CACHE));
+				V_DrawFuzzPatch(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(":", x+26, y);
+	}
+	x += 34;
+	if(m || h)
+	{
+		IN_DrawNumber(m, x, y, 2);
+	}
+	x += 34;
+	if(s)
+	{
+		IN_DrTextB(":", 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;
+		}
+	}
+}
--- /dev/null
+++ b/src/heretic/info.c
@@ -1,0 +1,5677 @@
+#include "DoomDef.h"
+// generated by multigen
+
+char *sprnames[NUMSPRITES] = {
+"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"
+};
+
+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_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 ();
+
+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_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
+ },
+
+{		// 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
+ }
+};
+
--- /dev/null
+++ b/src/heretic/info.h
@@ -1,0 +1,1550 @@
+// generated by multigen
+
+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_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,
+S_PLAY_FDTH20,
+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;
+	 long			frame;
+	 long			tics;
+	 void			(*action) ();
+	 statenum_t		nextstate;
+	 long			misc1, misc2;
+} state_t;
+
+extern state_t	states[NUMSTATES];
+extern char *sprnames[NUMSPRITES];
+
+
+
+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_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];
+
--- /dev/null
+++ b/src/heretic/linear.asm
@@ -1,0 +1,258 @@
+	.386
+	.MODEL  small
+	INCLUDE defs.inc
+
+
+;============================================================================
+;
+; unwound vertical scaling code
+;
+; eax   light table pointer, 0 lowbyte overwritten
+; ebx   all 0, low byte overwritten
+; ecx   fractional step value
+; edx   fractional scale value
+; esi   start of source pixels
+; edi   bottom pixel in screenbuffer to blit into
+;
+; ebx should be set to 0 0 0 dh to feed the pipeline
+;
+; The graphics wrap vertically at 128 pixels
+;============================================================================
+
+.DATA
+
+EXTRN	_centery:DWORD
+
+SCALEDEFINE     MACRO   number
+	dd      vscale&number
+ENDM
+
+	ALIGN   4
+scalecalls      LABEL
+LINE    =       0
+REPT    SCREENHEIGHT+1
+	SCALEDEFINE     %LINE
+LINE    =       LINE+1
+ENDM
+
+
+;=================================
+
+
+.CODE
+
+;================
+;
+; R_DrawColumn
+;
+;================
+
+PROC   R_DrawColumn_
+PUBLIC   R_DrawColumn_
+	PUSHR
+
+	mov		ebp,[_dc_yh]
+	mov		ebx,ebp
+	mov     edi,[_ylookup+ebx*4]
+	mov		ebx,[_dc_x]
+	add     edi,[_columnofs + ebx*4]
+
+	mov		eax,[_dc_yl]
+	sub     ebp,eax                    ; ebp = pixel count
+	or		ebp,ebp
+	js		done
+
+	mov     ecx,[_dc_iscale]
+
+	sub		eax,[_centery]
+	imul	ecx
+	mov		edx,[_dc_texturemid]
+	add		edx,eax
+	shl		edx,9							; 7 significant bits, 25 frac
+
+	shl		ecx,9							; 7 significant bits, 25 frac
+	mov     esi,[_dc_source]
+
+	mov     eax,[_dc_colormap]
+
+	xor     ebx,ebx
+	shld    ebx,edx,7						; get address of first location
+	call    [scalecalls+4+ebp*4]
+
+done:
+	POPR
+	ret
+
+;============ HIGH DETAIL ============
+
+SCALELABEL      MACRO   number
+vscale&number:
+ENDM
+
+LINE    =       SCREENHEIGHT
+REPT SCREENHEIGHT-1
+	SCALELABEL      %LINE
+	mov     al,[esi+ebx]                    ; get source pixel
+	add     edx,ecx                         ; calculate next location
+	mov     al,[eax]                        ; translate the color
+;	xor             ebx,ebx
+;	shld    ebx,edx,7                      ; get address of next location
+	mov		ebx,edx
+	shr		ebx,25
+	mov     [edi-(LINE-1)*SCREENWIDTH],al   ; draw a pixel to the buffer
+LINE    =       LINE-1
+ENDM
+vscale1:
+	mov     al,[esi+ebx]
+	mov     al,[eax]
+	mov     [edi],al
+vscale0:
+	ret
+
+ENDP
+
+
+
+;============================================================================
+;
+; unwound horizontal texture mapping code
+;
+; eax   lighttable
+; ebx   scratch register
+; ecx   position 6.10 bits x, 6.10 bits y
+; edx   step 6.10 bits x, 6.10 bits y
+; esi   start of block
+; edi   dest
+; ebp   fff to mask bx
+;
+; ebp should by preset from ebx / ecx before calling
+;============================================================================
+
+OP_SHLD	=	0fh
+
+
+.DATA
+
+
+MAPDEFINE     MACRO   number
+	dd      hmap&number
+ENDM
+
+	ALIGN   4
+mapcalls      LABEL
+LINE    =       0
+REPT    SCREENWIDTH+1
+	MAPDEFINE     %LINE
+LINE    =       LINE+1
+ENDM
+
+
+callpoint	dd  0
+returnpoint	dd	0
+
+
+.CODE
+
+;================
+;
+; R_DrawSpan
+;
+; Horizontal texture mapping
+;
+;================
+
+
+PROC   R_DrawSpan_
+PUBLIC	R_DrawSpan_
+	PUSHR
+
+IFE SKIPPRIMITIVES
+
+	mov	eax,[_ds_x1]
+	mov	ebx,[_ds_x2]
+	mov	eax,[mapcalls+eax*4]
+	mov	[callpoint],eax       ; spot to jump into unwound
+	mov	eax,[mapcalls+4+ebx*4]
+	mov	[returnpoint],eax     ; spot to patch a ret at
+	mov	BYTE PTR [eax], OP_RET
+
+;
+; build composite position
+;
+	mov	ecx,[_ds_xfrac]
+	shl	ecx,10
+	and	ecx,0ffff0000h
+	mov	eax,[_ds_yfrac]
+	shr	eax,6
+	and	eax,0ffffh
+	or	ecx,eax
+
+;
+; build composite step
+;
+	mov	edx,[_ds_xstep]
+	shl	edx,10
+	and	edx,0ffff0000h
+	mov	eax,[_ds_ystep]
+	shr	eax,6
+	and	eax,0ffffh
+	or	edx,eax
+
+	mov	esi,[_ds_source]
+
+	mov	edi,[_ds_y]
+	mov	edi,[_ylookup+edi*4]
+	add edi,[_columnofs]
+
+	mov	eax,[_ds_colormap]
+
+;
+; feed the pipeline and jump in
+;
+	mov		ebp,0fffh		; used to mask off slop high bits from position
+	shld	ebx,ecx,22				; shift y units in
+	shld	ebx,ecx,6				; shift x units in
+	and		ebx,ebp					; mask off slop bits
+	call    [callpoint]
+
+	mov	ebx,[returnpoint]
+	mov	BYTE PTR [ebx],OP_MOVAL		; remove the ret patched in
+
+ENDIF
+	POPR
+	ret
+
+
+;============= HIGH DETAIL ============
+
+.CODE
+
+MAPLABEL      MACRO   number
+hmap&number:
+ENDM
+
+LINE    =      0
+PCOL	=	0
+REPT SCREENWIDTH/4
+PLANE	=	0
+REPT	4
+	MAPLABEL      %LINE
+LINE    =	LINE + 1
+
+	mov     al,[esi+ebx]            ; get source pixel
+	shld	ebx,ecx,22				; shift y units in
+	shld	ebx,ecx,6				; shift x units in
+	mov     al,[eax]                ; translate color
+	and		ebx,ebp					; mask off slop bits
+	add		ecx,edx					; position += step
+	mov     [edi+PLANE+PCOL*4],al       ; write pixel
+PLANE	=	PLANE + 1
+ENDM
+PCOL	=	PCOL + 1
+ENDM
+hmap320:
+	ret
+
+ENDP
+
+END
--- /dev/null
+++ b/src/heretic/m_misc.c
@@ -1,0 +1,798 @@
+
+// M_misc.c
+
+#ifdef __NeXT__
+#include <libc.h>
+#else
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <direct.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#endif
+
+#include <ctype.h>
+
+#include "DoomDef.h"
+#include "soundst.h"
+
+int myargc;
+char **myargv;
+
+//---------------------------------------------------------------------------
+//
+// FUNC M_ValidEpisodeMap
+//
+//---------------------------------------------------------------------------
+
+boolean M_ValidEpisodeMap(int episode, int map)
+{
+	if(episode < 1 || map < 1 || map > 9)
+	{
+		return false;
+	}
+	if(shareware)
+	{ // Shareware version checks
+		if(episode != 1)
+		{
+			return false;
+		}
+	}
+	else if(ExtendedWAD)
+	{ // Extended version checks
+		if(episode == 6)
+		{
+			if(map > 3)
+			{
+				return false;
+			}
+		}
+		else if(episode > 5)
+		{
+			return false;
+		}
+	}
+	else
+	{ // Registered version checks
+		if(episode == 4)
+		{
+			if(map != 1)
+			{
+				return false;
+			}
+		}
+		else if(episode > 3)
+		{
+			return false;
+		}
+	}
+	return true;
+}
+
+/*
+=================
+=
+= M_CheckParm
+=
+= Checks for the given parameter in the program's command line arguments
+=
+= Returns the argument number (1 to argc-1) or 0 if not present
+=
+=================
+*/
+
+int M_CheckParm (char *check)
+{
+	int     i;
+
+	for (i = 1;i<myargc;i++)
+	{
+		if ( !strcasecmp(check, myargv[i]) )
+			return i;
+	}
+
+	return 0;
+}
+
+
+
+/*
+===============
+=
+= M_Random
+=
+= Returns a 0-255 number
+=
+===============
+*/
+
+unsigned char 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;
+}
+
+
+void M_ClearBox (fixed_t *box)
+{
+	box[BOXTOP] = box[BOXRIGHT] = MININT;
+	box[BOXBOTTOM] = box[BOXLEFT] = MAXINT;
+}
+
+void M_AddToBox (fixed_t *box, fixed_t x, fixed_t y)
+{
+	if (x<box[BOXLEFT])
+		box[BOXLEFT] = x;
+	else if (x>box[BOXRIGHT])
+		box[BOXRIGHT] = x;
+	if (y<box[BOXBOTTOM])
+		box[BOXBOTTOM] = y;
+	else if (y>box[BOXTOP])
+		box[BOXTOP] = y;
+}
+
+
+
+/*
+==================
+=
+= M_WriteFile
+=
+==================
+*/
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+boolean M_WriteFile (char const *name, void *source, int length)
+{
+	int handle, count;
+
+	handle = open (name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
+	if (handle == -1)
+		return false;
+	count = write (handle, source, length);
+	close (handle);
+
+	if (count < length)
+		return false;
+
+	return true;
+}
+
+
+/*
+==================
+=
+= M_ReadFile
+=
+==================
+*/
+
+int M_ReadFile (char const *name, byte **buffer)
+{
+	int handle, count, length;
+	struct stat fileinfo;
+	byte        *buf;
+
+	handle = open (name, O_RDONLY | O_BINARY, 0666);
+	if (handle == -1)
+		I_Error ("Couldn't read file %s", name);
+	if (fstat (handle,&fileinfo) == -1)
+		I_Error ("Couldn't read file %s", name);
+	length = fileinfo.st_size;
+	buf = Z_Malloc (length, PU_STATIC, NULL);
+	count = read (handle, buf, length);
+	close (handle);
+
+	if (count < length)
+		I_Error ("Couldn't read file %s", name);
+
+	*buffer = buf;
+	return length;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC M_FindResponseFile
+//
+//---------------------------------------------------------------------------
+
+#define MAXARGVS 100
+
+void M_FindResponseFile(void)
+{
+	int i;
+
+	for(i = 1; i < myargc; i++)
+	{
+		if(myargv[i][0] == '@')
+		{
+			FILE *handle;
+			int size;
+			int k;
+			int index;
+			int indexinfile;
+			char *infile;
+			char *file;
+			char *moreargs[20];
+			char *firstargv;
+
+			// READ THE RESPONSE FILE INTO MEMORY
+			handle = fopen(&myargv[i][1], "rb");
+			if(!handle)
+			{
+
				printf("\nNo such response file!");
+				exit(1);
+			}
+			printf("Found response file %s!\n",&myargv[i][1]);
+			fseek (handle,0,SEEK_END);
+			size = ftell(handle);
+			fseek (handle,0,SEEK_SET);
+			file = malloc (size);
+			fread (file,size,1,handle);
+			fclose (handle);
+
+			// KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG
+			for (index = 0,k = i+1; k < myargc; k++)
+				moreargs[index++] = myargv[k];
+			
+			firstargv = myargv[0];
+			myargv = malloc(sizeof(char *)*MAXARGVS);
+			memset(myargv,0,sizeof(char *)*MAXARGVS);
+			myargv[0] = firstargv;
+			
+			infile = file;
+			indexinfile = k = 0;
+			indexinfile++;  // SKIP PAST ARGV[0] (KEEP IT)
+			do
+			{
+				myargv[indexinfile++] = infile+k;
+				while(k < size &&  
+
+					((*(infile+k)>= ' '+1) && (*(infile+k)<='z')))
+					k++;
+				*(infile+k) = 0;
+				while(k < size &&
+					((*(infile+k)<= ' ') || (*(infile+k)>'z')))
+					k++;
+			} while(k < size);
+			
+			for (k = 0;k < index;k++)
+				myargv[indexinfile++] = moreargs[k];
+			myargc = indexinfile;
+			// DISPLAY ARGS
+			if(M_CheckParm("-debug"))
+			{
+				printf("%d command-line args:\n", myargc);
+				for(k = 1; k < myargc; k++)
+				{
+					printf("%s\n", myargv[k]);
+				}
+			}
+			break;
+		}
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC M_ForceUppercase
+//
+// Change string to uppercase.
+//
+//---------------------------------------------------------------------------
+
+void M_ForceUppercase(char *text)
+{
+	char c;
+
+	while((c = *text) != 0)
+	{
+		if(c >= 'a' && c <= 'z')
+		{
+			*text++ = c-('a'-'A');
+		}
+		else
+		{
+			text++;
+		}
+	}
+}
+
+/*
+==============================================================================
+
+							DEFAULTS
+
+==============================================================================
+*/
+
+int     usemouse;
+int     usejoystick;
+
+extern  int     key_right, key_left, key_up, key_down;
+extern  int     key_strafeleft, key_straferight;
+extern  int     key_fire, key_use, key_strafe, key_speed;
+extern	int		key_flyup, key_flydown, key_flycenter;
+extern	int		key_lookup, key_lookdown, key_lookcenter;
+extern	int		key_invleft, key_invright, key_useartifact;
+
+extern  int         mousebfire;
+extern  int         mousebstrafe;
+extern  int         mousebforward;
+
+extern  int         joybfire;
+extern  int         joybstrafe;
+extern  int         joybuse;
+extern  int         joybspeed;
+
+extern  int     viewwidth, viewheight;
+
+int mouseSensitivity;
+
+extern  int screenblocks;
+
+extern char *chat_macros[10];
+
+typedef struct
+{
+	char    *name;
+	int     *location;
+	int     defaultvalue;
+	int     scantranslate;      // PC scan code hack
+	int     untranslated;       // lousy hack
+} default_t;
+
+#ifndef __NeXT__
+extern int snd_Channels;
+extern int snd_DesiredMusicDevice, snd_DesiredSfxDevice;
+extern int snd_MusicDevice, // current music card # (index to dmxCodes)
+	snd_SfxDevice; // current sfx card # (index to dmxCodes)
+
+extern int     snd_SBport, snd_SBirq, snd_SBdma;       // sound blaster variables
+extern int     snd_Mport;                              // midi variables
+#endif
+
+default_t defaults[] =
+{
+	{ "mouse_sensitivity", &mouseSensitivity, 5 },
+
+#ifndef __NeXT__
+	{ "sfx_volume", &snd_MaxVolume, 10},
+	{ "music_volume", &snd_MusicVolume, 10},
+#endif
+
+#ifdef __WATCOMC__
+#define SC_UPARROW              0x48
+#define SC_DOWNARROW            0x50
+#define SC_LEFTARROW            0x4b
+#define SC_RIGHTARROW           0x4d
+#define SC_RCTRL                0x1d
+#define SC_RALT                 0x38
+#define SC_RSHIFT               0x36
+#define SC_SPACE                0x39
+#define SC_COMMA                0x33
+#define SC_PERIOD               0x34
+#define SC_PAGEUP				0x49
+#define SC_INSERT				0x52
+#define SC_HOME					0x47
+#define SC_PAGEDOWN				0x51
+#define SC_DELETE				0x53
+#define SC_END					0x4f
+#define SC_ENTER				0x1c
+
+	{ "key_right", &key_right, SC_RIGHTARROW, 1 },
+	{ "key_left", &key_left, SC_LEFTARROW, 1 },
+	{ "key_up", &key_up, SC_UPARROW, 1 },
+	{ "key_down", &key_down, SC_DOWNARROW, 1 },
+	{ "key_strafeleft", &key_strafeleft, SC_COMMA, 1 },
+	{ "key_straferight", &key_straferight, SC_PERIOD, 1 },
+	{ "key_flyup", &key_flyup, SC_PAGEUP, 1 },
+	{ "key_flydown", &key_flydown, SC_INSERT, 1 },
+	{ "key_flycenter", &key_flycenter, SC_HOME, 1 },
+	{ "key_lookup", &key_lookup, SC_PAGEDOWN, 1 },
+	{ "key_lookdown", &key_lookdown, SC_DELETE, 1 },
+	{ "key_lookcenter", &key_lookcenter, SC_END, 1 },
+	{ "key_invleft", &key_invleft, 0x1a, 1 },
+	{ "key_invright", &key_invright, 0x1b, 1 },
+	{ "key_useartifact", &key_useartifact, SC_ENTER, 1 },
+
+	{ "key_fire", &key_fire, SC_RCTRL, 1 },
+	{ "key_use", &key_use, SC_SPACE, 1 },
+	{ "key_strafe", &key_strafe, SC_RALT, 1 },
+	{ "key_speed", &key_speed, SC_RSHIFT, 1 },
+#endif
+
+#ifdef __NeXT__
+	{ "key_right", &key_right, KEY_RIGHTARROW },
+	{ "key_left", &key_left, KEY_LEFTARROW },
+	{ "key_up", &key_up, KEY_UPARROW },
+	{ "key_down", &key_down, KEY_DOWNARROW },
+	{ "key_strafeleft", &key_strafeleft, ',' },
+	{ "key_straferight", &key_straferight, '.' },
+	{ "key_flyup", &key_flyup, 'u' },
+	{ "key_flydown", &key_flydown, 'j' },
+	{ "key_flycenter", &key_flycenter, 'k' },
+	{ "key_lookup", &key_lookup, 'm' },
+	{ "key_lookdown", &key_lookdown, 'b' },
+	{ "key_lookcenter", &key_lookcenter, 'n' },
+	{ "key_invleft", &key_invleft, '[' },
+	{ "key_invright", &key_invright, ']' },
+	{ "key_useartifact", &key_useartifact, 13 },
+
+	{ "key_fire", &key_fire, ' ', 1 },
+	{ "key_use", &key_use, 'x', 1 },
+	{ "key_strafe", &key_strafe, 'c', 1 },
+	{ "key_speed", &key_speed, 'z', 1 },
+#endif
+
+	{ "use_mouse", &usemouse, 1 },
+	{ "mouseb_fire", &mousebfire, 0 },
+	{ "mouseb_strafe", &mousebstrafe, 1 },
+	{ "mouseb_forward", &mousebforward, 2 },
+
+	{ "use_joystick", &usejoystick, 0 },
+	{ "joyb_fire", &joybfire, 0 },
+	{ "joyb_strafe", &joybstrafe, 1 },
+	{ "joyb_use", &joybuse, 3 },
+	{ "joyb_speed", &joybspeed, 2 },
+
+	{ "screenblocks", &screenblocks, 10 },
+
+#ifndef __NeXT__
+	{ "snd_channels", &snd_Channels, 3 },
+	{ "snd_musicdevice", &snd_DesiredMusicDevice, 0 },
+	{ "snd_sfxdevice", &snd_DesiredSfxDevice, 0 },
+	{ "snd_sbport", &snd_SBport, 544 },
+	{ "snd_sbirq", &snd_SBirq, -1 },
+	{ "snd_sbdma", &snd_SBdma, -1 },
+	{ "snd_mport", &snd_Mport, -1 },
+#endif
+
+	{ "usegamma", &usegamma, 0 },
+
+	{ "chatmacro0", (int *) &chat_macros[0], (int) HUSTR_CHATMACRO0 },
+	{ "chatmacro1", (int *) &chat_macros[1], (int) HUSTR_CHATMACRO1 },
+	{ "chatmacro2", (int *) &chat_macros[2], (int) HUSTR_CHATMACRO2 },
+	{ "chatmacro3", (int *) &chat_macros[3], (int) HUSTR_CHATMACRO3 },
+	{ "chatmacro4", (int *) &chat_macros[4], (int) HUSTR_CHATMACRO4 },
+	{ "chatmacro5", (int *) &chat_macros[5], (int) HUSTR_CHATMACRO5 },
+	{ "chatmacro6", (int *) &chat_macros[6], (int) HUSTR_CHATMACRO6 },
+	{ "chatmacro7", (int *) &chat_macros[7], (int) HUSTR_CHATMACRO7 },
+	{ "chatmacro8", (int *) &chat_macros[8], (int) HUSTR_CHATMACRO8 },
+	{ "chatmacro9", (int *) &chat_macros[9], (int) HUSTR_CHATMACRO9 }
+};
+
+int numdefaults;
+char *defaultfile;
+
+/*
+==============
+=
+= M_SaveDefaults
+=
+==============
+*/
+
+void M_SaveDefaults (void)
+{
+	int     i,v;
+	FILE    *f;
+
+	f = fopen (defaultfile, "w");
+	if (!f)
+		return;         // can't write the file, but don't complain
+
+	for (i=0 ; i<numdefaults ; i++)
+	{
+#ifdef __WATCOMC__
+		if (defaults[i].scantranslate)
+			defaults[i].location = &defaults[i].untranslated;
+#endif
+		if (defaults[i].defaultvalue > -0xfff
+		  && defaults[i].defaultvalue < 0xfff)
+		{
+			v = *defaults[i].location;
+			fprintf (f,"%s\t\t%i\n",defaults[i].name,v);
+		} else {
+			fprintf (f,"%s\t\t\"%s\"\n",defaults[i].name,
+			  * (char **) (defaults[i].location));
+		}
+	}
+
+	fclose (f);
+}
+
+
+/*
+==============
+=
+= M_LoadDefaults
+=
+==============
+*/
+
+extern byte scantokey[128];
+extern char *basedefault;
+
+void M_LoadDefaults (void)
+{
+	int     i, len;
+	FILE    *f;
+	char    def[80];
+	char        strparm[100];
+	char    *newstring;
+	int     parm;
+	boolean     isstring;
+
+//
+// set everything to base values
+//
+	numdefaults = sizeof(defaults)/sizeof(defaults[0]);
+	for (i=0 ; i<numdefaults ; i++)
+		*defaults[i].location = defaults[i].defaultvalue;
+
+//
+// check for a custom default file
+//
+	i = M_CheckParm("-config");
+	if(i && i<myargc-1)
+	{
+		defaultfile = myargv[i+1];
+		printf("default file: %s\n", defaultfile);
+	}
+	else if(cdrom)
+	{
+		defaultfile = "c:\\heretic.cd\\heretic.cfg";
+	}
+	else
+	{
+		defaultfile = basedefault;
+	}
+
+//
+// read the file in, overriding any set defaults
+//
+	f = fopen (defaultfile, "r");
+	if (f)
+	{
+		while (!feof(f))
+		{
+			isstring = false;
+			if (fscanf (f, "%79s %[^\n]\n", def, strparm) == 2)
+			{
+			  if (strparm[0] == '"')
+			  {
+				// get a string default
+				isstring = true;
+				len = strlen(strparm);
+				newstring = (char *) malloc(len);
+				strparm[len-1] = 0;
+				strcpy(newstring, strparm+1);
+			  }
+			  else if (strparm[0] == '0' && strparm[1] == 'x')
+				  sscanf(strparm+2, "%x", &parm);
+			  else
+				  sscanf(strparm, "%i", &parm);
+			  for (i=0 ; i<numdefaults ; i++)
+				  if (!strcmp(def, defaults[i].name))
+				  {
+					  if (!isstring)
+						*defaults[i].location = parm;
+					  else
+						*defaults[i].location =
+						  (int) newstring;
+					  break;
+				  }
+			}
+		}
+
+		fclose (f);
+	}
+
+
+#ifdef __WATCOMC__
+	for(i = 0; i < numdefaults; i++)
+	{
+		if(defaults[i].scantranslate)
+		{
+			parm = *defaults[i].location;
+			defaults[i].untranslated = parm;
+			*defaults[i].location = scantokey[parm];
+		}
+	}
+#endif
+}
+
+
+/*
+==============================================================================
+
+						SCREEN SHOTS
+
+==============================================================================
+*/
+
+
+typedef struct
+{
+	char    manufacturer;
+	char    version;
+	char    encoding;
+	char    bits_per_pixel;
+	unsigned short  xmin,ymin,xmax,ymax;
+	unsigned short  hres,vres;
+	unsigned char   palette[48];
+	char    reserved;
+	char    color_planes;
+	unsigned short  bytes_per_line;
+	unsigned short  palette_type;
+	char    filler[58];
+	unsigned char   data;           // unbounded
+} pcx_t;
+
+/*
+==============
+=
+= WritePCXfile
+=
+==============
+*/
+
+void WritePCXfile (char *filename, byte *data, int width, int height, byte *palette)
+{
+	int     i, length;
+	pcx_t   *pcx;
+	byte        *pack;
+	
+	pcx = Z_Malloc (width*height*2+1000, PU_STATIC, NULL);
+
+	pcx->manufacturer = 0x0a;   // PCX id
+	pcx->version = 5;           // 256 color
+	pcx->encoding = 1;      // uncompressed
+	pcx->bits_per_pixel = 8;        // 256 color
+	pcx->xmin = 0;
+	pcx->ymin = 0;
+	pcx->xmax = SHORT(width-1);
+	pcx->ymax = SHORT(height-1);
+	pcx->hres = SHORT(width);
+	pcx->vres = SHORT(height);
+	memset (pcx->palette,0,sizeof(pcx->palette));
+	pcx->color_planes = 1;      // chunky image
+	pcx->bytes_per_line = SHORT(width);
+	pcx->palette_type = SHORT(2);       // not a grey scale
+	memset (pcx->filler,0,sizeof(pcx->filler));
+
+//
+// pack the image
+//
+	pack = &pcx->data;
+
+	for (i=0 ; i<width*height ; i++)
+		if ( (*data & 0xc0) != 0xc0)
+			*pack++ = *data++;
+		else
+		{
+			*pack++ = 0xc1;
+			*pack++ = *data++;
+		}
+
+//
+// write the palette
+//
+	*pack++ = 0x0c; // palette ID byte
+	for (i=0 ; i<768 ; i++)
+		*pack++ = *palette++;
+
+//
+// write output file
+//
+	length = pack - (byte *)pcx;
+	M_WriteFile (filename, pcx, length);
+
+	Z_Free (pcx);
+}
+
+
+//==============================================================================
+
+/*
+==================
+=
+= M_ScreenShot
+=
+==================
+*/
+
+void M_ScreenShot (void)
+{
+	int     i;
+	byte    *linear;
+	char    lbmname[12];
+	byte *pal;
+
+#ifdef _WATCOMC_
+	extern  byte *pcscreen;
+#endif
+//
+// munge planar buffer to linear
+//
+#ifdef _WATCOMC_
+	linear = pcscreen;
+#else
+	linear = screen;
+#endif
+//
+// find a file name to save it to
+//
+	strcpy(lbmname,"HRTIC00.pcx");
+
+	for (i=0 ; i<=99 ; i++)
+	{
+		lbmname[5] = i/10 + '0';
+		lbmname[6] = i%10 + '0';
+		if (access(lbmname,0) == -1)
+			break;  // file doesn't exist
+	}
+	if (i==100)
+		I_Error ("M_ScreenShot: Couldn't create a PCX");
+
+//
+// save the pcx file
+//
+#ifdef __WATCOMC__
+	pal = (byte *)Z_Malloc(768, PU_STATIC, NULL);
+	outp(0x3c7, 0);
+	for(i = 0; i < 768; i++)
+	{
+		*(pal+i) = inp(0x3c9)<<2;
+	}
+#else
+	pal = (byte *)W_CacheLumpName("PLAYPAL", PU_CACHE);
+#endif
+
+	WritePCXfile (lbmname, linear, SCREENWIDTH, SCREENHEIGHT
+		, pal);
+
+	players[consoleplayer].message = "SCREEN SHOT";
+#ifdef __WATCOMC__
+	Z_Free(pal);
+#endif
+}
--- /dev/null
+++ b/src/heretic/mn_menu.c
@@ -1,0 +1,1588 @@
+
+// MN_menu.c
+
+#include <ctype.h>
+#include "DoomDef.h"
+#include "P_local.h"
+#include "R_local.h"
+#include "soundst.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 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("M_SKL00");
+	if(ExtendedWAD)
+	{ // Add episodes 4 and 5 to the menu
+		EpisodeMenu.itemCount = 5;
+		EpisodeMenu.y -= ITEM_HEIGHT;
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC InitFonts
+//
+//---------------------------------------------------------------------------
+
+static void InitFonts(void)
+{
+	FontABaseLump = W_GetNumForName("FONTA_S")+1;
+	FontBBaseLump = W_GetNumForName("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 += 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 += 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 += 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 += 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 *selName;
+
+	if(MenuActive == false)
+	{
+		if(askforquit)
+		{
+			MN_DrTextA(QuitEndMsg[typeofask-1], 160-
+				MN_TextAWidth(QuitEndMsg[typeofask-1])/2, 80);
+			if(typeofask == 3)
+			{
+				MN_DrTextA(SlotText[quicksave-1], 160-
+					MN_TextAWidth(SlotText[quicksave-1])/2, 90);
+				MN_DrTextA("?", 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("?", 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(item->text, x, y);
+			}
+			y += ITEM_HEIGHT;
+			item++;
+		}
+		y = CurrentMenu->y+(CurrentItPos*ITEM_HEIGHT)+SELECTOR_YOFFSET;
+		selName = 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("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)
+{
+	MN_DrTextB("LOAD GAME", 160-MN_TextBWidth("LOAD GAME")/2, 10);
+	if(!slottextloaded)
+	{
+		MN_LoadSlotText();
+	}
+	DrawFileSlots(&LoadMenu);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawSaveMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawSaveMenu(void)
+{
+	MN_DrTextB("SAVE GAME", 160-MN_TextBWidth("SAVE GAME")/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("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("ON", 196, 50);
+	}
+	else
+	{
+		MN_DrTextB("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], "MESSAGES ON", true);
+	}
+	else
+	{
+		P_SetMessage(&players[consoleplayer], "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(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 key;
+	int i;
+	MenuItem_t *item;
+	extern boolean automapactive;
+	static boolean shiftdown;
+	extern void D_StartTitle(void);
+	extern void G_CheckDemoStatus(void);
+	char *textBuffer;
+
+	if(event->data1 == KEY_RSHIFT)
+	{
+		shiftdown = (event->type == ev_keydown);
+	}
+	if(event->type != ev_keydown)
+	{
+		return(false);
+	}
+	key = event->data1;
+	if(InfoType)
+	{
+		if(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)
+	{
+		switch(key)
+		{
+			case 'y':
+				if(askforquit)
+				{
+					switch(typeofask)
+					{
+						case 1:
+							G_CheckDemoStatus();
+							I_Quit();
+							break;
+						case 2:
+							players[consoleplayer].messageTics = 0;
+								//set the msg to be cleared
+							players[consoleplayer].message = NULL;
+							typeofask = 0;
+							askforquit = false;
+							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);
+							askforquit = false;
+							typeofask = 0;
+							BorderNeedRefresh = true;
+							return true;
+						case 4:
+							P_SetMessage(&players[consoleplayer], "QUICKLOADING....", false);
+							SCLoadGame(quickload-1);
+							askforquit = false;
+							typeofask = 0;
+							BorderNeedRefresh = true;
+							return true;
+						default:
+							return true; // eat the 'y' keypress
+					}
+				}
+				return false;
+			case 'n':
+			case KEY_ESCAPE:
+				if(askforquit)
+				{
+					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;
+		}
+		return false; // don't let the keys filter thru
+	}
+	if(MenuActive == false && !chatmodeon)
+	{
+		switch(key)
+		{
+			case KEY_MINUS:
+				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);
+			case KEY_EQUALS:
+				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);
+#ifndef __NeXT__
+			case KEY_F1: // help screen
+				SCInfo(0); // start up info screens
+				MenuActive = true;
+				return(true);
+			case KEY_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;
+			case KEY_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;
+			case KEY_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;
+			case KEY_F5: // F5 isn't used in Heretic. (detail level)
+				return true;
+			case KEY_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;
+			case KEY_F7: // endgame
+				if(gamestate == GS_LEVEL && !demoplayback)
+				{
+					S_StartSound(NULL, sfx_chat);
+					SCEndGame(0);
+				}
+				return true;
+			case KEY_F8: // toggle messages
+				SCMessages(0);
+				return true;
+			case KEY_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;
+			case KEY_F10: // quit
+				if(gamestate == GS_LEVEL)
+				{
+					SCQuitGame(0);
+					S_StartSound(NULL, sfx_chat);
+				}
+				return true;
+			case KEY_F11: // F11 - gamma mode correction
+				usegamma++;
+				if(usegamma > 4)
+				{
+					usegamma = 0;
+				}
+				I_SetPalette((byte *)W_CacheLumpName("PLAYPAL", PU_CACHE));
+				return true;
+#endif
+		}
+
+	}
+
+	if(MenuActive == false)
+	{
+		if(key == KEY_ESCAPE || gamestate == GS_DEMOSCREEN || demoplayback)
+		{
+			MN_ActivateMenu();
+			return(true);
+		}
+		return(false);
+	}
+	if(!FileMenuKeySteal)
+	{
+		item = &CurrentMenu->items[CurrentItPos];
+		switch(key)
+		{
+			case KEY_DOWNARROW:
+				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);
+				break;
+			case KEY_UPARROW:
+				do
+				{
+					if(CurrentItPos == 0)
+					{
+						CurrentItPos = CurrentMenu->itemCount-1;
+					}
+					else
+					{
+						CurrentItPos--;
+					}
+				} while(CurrentMenu->items[CurrentItPos].type == ITT_EMPTY);
+				S_StartSound(NULL, sfx_switch);
+				return(true);
+				break;
+			case KEY_LEFTARROW:
+				if(item->type == ITT_LRFUNC && item->func != NULL)
+				{
+					item->func(LEFT_DIR);
+					S_StartSound(NULL, sfx_keyup);
+				}
+				return(true);
+				break;
+			case KEY_RIGHTARROW:
+				if(item->type == ITT_LRFUNC && item->func != NULL)
+				{
+					item->func(RIGHT_DIR);
+					S_StartSound(NULL, sfx_keyup);
+				}
+				return(true);
+				break;
+			case KEY_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);
+				break;
+			case KEY_ESCAPE:
+				MN_DeactivateMenu();
+				return(true);
+			case KEY_BACKSPACE:
+				S_StartSound(NULL, sfx_switch);
+				if(CurrentMenu->prevMenu == MENU_NONE)
+				{
+					MN_DeactivateMenu();
+				}
+				else
+				{
+					SetMenu(CurrentMenu->prevMenu);
+				}
+				return(true);
+			default:
+				for(i = 0; i < CurrentMenu->itemCount; i++)
+				{
+					if(CurrentMenu->items[i].text)
+					{
+						if(toupper(key)
+							== toupper(CurrentMenu->items[i].text[0]))
+						{
+							CurrentItPos = i;
+							return(true);
+						}
+					}
+				}
+				break;
+		}
+		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((key >= 'a' && key <= 'z'))
+			{
+				*textBuffer++ = key-32;
+				*textBuffer = ASCII_CURSOR;
+				slotptr++;
+				return(true);
+			}
+			if(((key >= '0' && key <= '9') || key == ' '
+				|| key == ',' || key == '.' || key == '-')
+				&& !shiftdown)
+			{
+				*textBuffer++ = key;
+				*textBuffer = ASCII_CURSOR;
+				slotptr++;
+				return(true);
+			}
+			if(shiftdown && key == '1')
+			{
+				*textBuffer++ = '!';
+				*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));
+	memcpy(screen, (byte *)W_CacheLumpNum(W_GetNumForName("TITLE")+InfoType,
+		PU_CACHE), SCREENWIDTH*SCREENHEIGHT);
+//      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("M_SLDLT", PU_CACHE));
+	for(x2 = x, count = width; count--; x2 += 8)
+	{
+		V_DrawPatch(x2, y, W_CacheLumpName(count&1 ? "M_SLDMD1"
+			: "M_SLDMD2", PU_CACHE));
+	}
+	V_DrawPatch(x2, y, W_CacheLumpName("M_SLDRT", PU_CACHE));
+	V_DrawPatch(x+4+slot*8, y+7, W_CacheLumpName("M_SLDKB", PU_CACHE));
+}
--- /dev/null
+++ b/src/heretic/netold.c
@@ -1,0 +1,783 @@
+// I_pcnet.m
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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;
+
+#ifdef NeXT
+	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");
+}
+
+	I_NetCmd ();
+}
+
+/*
+==============
+=
+= 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;
+	I_NetCmd ();
+	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");
+			S_StartSound(NULL, sfx_chat);
+			exitmsg[7] += netconsole;
+			//players[consoleplayer].message = exitmsg;
+			P_SetMessage(&players[consoleplayer], exitmsg, true);
+/*                      if (demorecording)
+				G_CheckDemoStatus ();
+*/ // DEBUG
+			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 ();
+	for ( ; eventtail != eventhead 
+	; eventtail = (++eventtail)&(MAXEVENTS-1) ) 
+	{ 
+		ev = &events[eventtail]; 
+		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
+		while (1)
+		{
+			CheckAbort ();
+			if (!HGetPacket ())
+				continue;
+			if (netbuffer->checksum & NCMD_SETUP)
+			{
+				if (netbuffer->player != 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;
+				return;
+			}
+		}
+	}
+	else
+	{       // key player, send the setup info
+		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->player = 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);
+	}
+}
+
+/*
+===================
+=
+= 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-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 = MAXINT;
+	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 = MAXINT;
+			
+			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;
+			} 
+		}
+
+//
+// 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
+	}
+}
--- /dev/null
+++ b/src/heretic/oldd_net.c
@@ -1,0 +1,790 @@
+// I_pcnet.m
+
+#include "DoomDef.h"
+
+#define	NCMD_EXIT		0x80000000
+#define	NCMD_RETRANSMIT	0x40000000
+#define	NCMD_SETUP		0x20000000
+#define	NCMD_CHECKSUM	0x0fffffff
+
+/*
+if more space needs to be crunched out of the protocol...
+
+1	drone
+2	player
+8	tic
+5	numtics
+
+#define	NCMD_EXIT		0x80000000
+#define	NCMD_RETRANSMIT	0x40000000			// a retransmit will have 0 tics
+#define	NCMD_DRONE		0x20000000
+#define	NCMD_PLAYER		0x18000000
+#define	NCMD_PLAYERSHIFT	27
+#define	NCMD_TIC		0x00ff0000
+#define	NCMD_TICSHIFT	16
+#define	NCMD_NUMTICS	0x0000ff00
+#define	NCMD_NUMTICSSHIFT	8
+#define	NCMD_CHECKSUM	0x000000ff
+
+*/
+
+
+
+
+
+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             gametime;
+int             maketic;
+int				lastnettic, skiptics;
+int				ticdup;		
+
+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;
+
+#ifdef NeXT
+	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 (!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");
+}
+
+	I_NetCmd ();
+}
+
+/*
+==============
+=
+= 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;
+		
+	doomcom->command = CMD_GET;
+	I_NetCmd ();
+	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;
+	int		netdrone;
+	int		j;
+	ticcmd_t	*src, *dest;
+	int		dupedstart, dupedend;
+	int		skiptics;
+	int		realstart;
+				 
+	while (HGetPacket ())
+	{
+		if (netbuffer->checksum & NCMD_SETUP)
+			continue;		// extra setup packet
+			
+		netdrone = netbuffer->player & PL_DRONE;
+		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);		
+		dupedstart = realstart*doomcom->ticdup;
+		dupedend = (realstart+netbuffer->numtics)*doomcom->ticdup;
+		
+		//
+		// check for exiting the game
+		//
+		if (netbuffer->checksum & NCMD_EXIT)
+		{
+			if (!nodeingame[netnode])
+				continue;
+			nodeingame[netnode] = false;
+			if (!netdrone)
+			{
+				playeringame[netconsole] = false;
+				strcpy (exitmsg, "Player 1 left the game");
+				exitmsg[7] += netconsole;
+				players[consoleplayer].message = exitmsg;
+			}
+			continue;
+		}
+
+		//
+		// drone packets are just notifications
+		//
+		if (netdrone)
+		{
+			nettics[netnode] = dupedend;
+			continue;
+		}
+
+		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 (dupedend == nettics[netnode])
+			continue;
+			
+		if (dupedend < nettics[netnode])
+		{
+if (debugfile)
+fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
+			continue;
+		}
+
+		//
+		// check for a missed packet
+		//
+		if (dupedstart > nettics[netnode])
+		{
+		// stop processing until the other system resends the missed tics
+if (debugfile)
+fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, dupedstart, nettics[netnode]);
+			remoteresend[netnode] = true;
+			continue;
+		}
+	
+//
+// update command store from the packet
+//
+		remoteresend[netnode] = false;
+		
+		skiptics = nettics[netnode]/doomcom->ticdup - realstart;		
+		src = &netbuffer->cmds[skiptics];
+
+		while (nettics[netnode] < dupedend)
+		{
+			for (j=0 ; j<doomcom->ticdup ; j++)
+			{
+				dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
+				nettics[netnode]++;
+				*dest = *src;
+				src->chatchar = 0;
+				if (src->buttons & BT_SPECIAL)
+					src->buttons = 0;
+			}
+			src++;
+		}
+	}
+}
+
+/*
+=============
+=
+= NetUpdate
+=
+= Builds ticcmds for console player
+= sends out a packet
+=============
+*/
+
+void NetUpdate (void)
+{
+	int             nowtime;
+	int             newtics;
+	int				i,j;
+	int				gameticdiv;
+	int				realstart;
+		
+	if (singletics)
+		return;         // singletic update is syncronous
+		
+//
+// check time
+//      
+	nowtime = I_GetTime ()/doomcom->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;
+	if (doomcom->drone)
+		netbuffer->player |= PL_DRONE;
+	
+//
+// drone packets
+//
+	if (doomcom->drone)
+	{
+		I_StartTic ();
+		D_ProcessEvents ();
+		goto sendit;
+	}
+	
+//
+// build new ticcmds for console player
+//
+	gameticdiv = (gametic+doomcom->ticdup-1)/doomcom->ticdup;
+	for (i=0 ; i<newtics ; i++)
+	{
+		I_StartTic ();
+		D_ProcessEvents ();
+		if (maketic - gameticdiv >= BACKUPTICS/2 /* /doomcom->ticdup */- 1)
+		{
+			newtics = i;
+			break;          // can't hold any more
+		}
+//printf ("mk:%i ",maketic);
+		G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
+		maketic++;
+	}
+
+//
+// send the packet to the other nodes
+//
+sendit:
+	for (i=0 ; i<doomcom->numnodes ; i++)
+		if (nodeingame[i])
+		{
+			if (doomcom->drone)
+			{
+				netbuffer->starttic = realstart = maketic + BACKUPTICS/2;
+				netbuffer->numtics = 0;
+			}
+			else
+			{
+				netbuffer->starttic = realstart = resendto[i];
+				netbuffer->numtics = maketic - realstart;
+				resendto[i] = maketic - doomcom->extratics;
+			}
+	
+			if (netbuffer->numtics > BACKUPTICS)
+				I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
+
+			for (j=0 ; j< netbuffer->numtics ; j++)
+				netbuffer->cmds[j] = 
+					localcmds[(realstart+j)%BACKUPTICS];
+					
+			if (remoteresend[i])
+			{
+				netbuffer->retransmitfrom = nettics[i]/doomcom->ticdup;
+				HSendPacket (i, NCMD_RETRANSMIT);
+			}
+			else
+			{
+				netbuffer->retransmitfrom = 0;
+				HSendPacket (i, 0);
+			}
+		}
+
+//
+// listen for other packets
+//		
+listen:
+
+	GetPackets ();
+}
+
+
+/*
+=====================
+=
+= CheckAbort
+=
+=====================
+*/
+
+void CheckAbort (void)
+{
+	event_t *ev;
+	
+	I_WaitVBL(2);
+	
+	I_StartTic ();
+	for ( ; eventtail != eventhead 
+	; eventtail = (++eventtail)&(MAXEVENTS-1) )
+	{
+		ev = &events[eventtail];
+		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
+		printf ("listening for network start info...\n");
+		while (1)
+		{
+			CheckAbort ();
+			if (!HGetPacket ())
+				continue;
+			if (netbuffer->checksum & NCMD_SETUP)
+			{
+				if (netbuffer->player != VERSION)
+					I_Error ("Different DOOM versions cannot play a net game!");
+				startskill = netbuffer->retransmitfrom & 15;
+				deathmatch = (netbuffer->retransmitfrom & 0x80) > 0;
+				nomonsters = (netbuffer->retransmitfrom & 0x40) > 0;
+				respawnparm = (netbuffer->retransmitfrom & 0x20) > 0;
+				startmap = netbuffer->starttic & 15;
+				startepisode = netbuffer->starttic >> 4;
+				return;
+			}
+		}
+	}
+	else
+	{	// key player, send the setup info
+		printf ("sending network start info...\n");
+		do
+		{
+			CheckAbort ();
+			for (i=0 ; i<doomcom->numnodes ; i++)
+			{
+				netbuffer->retransmitfrom = startskill;
+				if (deathmatch)
+					netbuffer->retransmitfrom |= 0x80;
+				if (nomonsters)
+					netbuffer->retransmitfrom |= 0x40;
+				if (respawnparm)
+					netbuffer->retransmitfrom |= 0x20;
+				netbuffer->starttic = startepisode * 16 + startmap;
+				netbuffer->player = VERSION;
+				netbuffer->numtics = 0;
+				HSendPacket (i, NCMD_SETUP);
+			}
+	
+			while (HGetPacket ())
+			{
+				gotinfo[netbuffer->player&0x7f] = true;
+			}
+
+			for (i=1 ; i<doomcom->numnodes ; i++)
+				if (!gotinfo[i])
+					break;
+		} while (i < doomcom->numnodes);
+	}
+}
+
+/*
+===================
+=
+= 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;
+					
+	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)
+		return;
+	
+// send a bunch of packets for security
+	netbuffer->player = consoleplayer;
+	if (doomcom->drone)
+		netbuffer->player |= PL_DRONE;
+	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, nextlowest;
+	int             entertic;
+	int	static		oldentertics;
+	int				realtics, availabletics;
+	int				counts;
+	int				numplaying;
+
+//
+// get real tics
+//			
+	entertic = I_GetTime ();
+	realtics = entertic - oldentertics;
+	oldentertics = entertic;
+
+//
+// get available tics
+//
+	NetUpdate ();
+	
+	lowtic = nextlowest = MAXINT;
+	numplaying = 0;
+	for (i=0 ; i<doomcom->numnodes ; i++)
+		if (nodeingame[i])
+		{
+			numplaying++;
+			if (nettics[i] < lowtic)
+			{
+				nextlowest = lowtic;
+				lowtic = nettics[i];
+			}
+			else if (nettics[i] < nextlowest)
+				nextlowest = nettics[i]; 
+		}
+	availabletics = lowtic - gametic;
+	
+
+//
+// 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);
+
+//=============================================================================
+//
+//	ideally nettics[0] should be 1 - 3 tics above lowtic
+//	if we are consistantly slower, speed up time
+//	drones should never hold up the other players
+//
+	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 ("+");
+		}
+	}
+//=============================================================================
+
+//
+// wait for new tics if needed
+//
+	while (lowtic < gametic + counts)	
+	{
+
+		NetUpdate ();   
+		lowtic = MAXINT;
+		
+		for (i=0 ; i<doomcom->numnodes ; i++)
+			if (nodeingame[i] && nettics[i] < lowtic)
+				lowtic = nettics[i];
+
+		if (lowtic < gametic)
+			I_Error ("TryRunTics: lowtic < gametic");
+			
+		// don't stay in here forever -- give the menu a chance to work
+		if (I_GetTime () - entertic >= 20)
+			return;
+	}
+			
+
+//
+// run the tics
+//	
+	while (counts--)
+	{
+		G_Ticker ();
+		NetUpdate ();					// check for new console commands
+		gametic++;
+	}
+}
--- /dev/null
+++ b/src/heretic/p_ceilng.c
@@ -1,0 +1,236 @@
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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((mobj_t *)&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((mobj_t *)&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;
+}
--- /dev/null
+++ b/src/heretic/p_doors.c
@@ -1,0 +1,368 @@
+
+// P_doors.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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((mobj_t *)
+							&door->sector->soundorg, sfx_doropn);
+						break;
+					case close30ThenOpen:
+						door->direction = 1;
+						S_StartSound((mobj_t *)
+							&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((mobj_t *)
+							&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((mobj_t *)
+							&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((mobj_t *)
+							&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((mobj_t *)&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((mobj_t *)&door->sector->soundorg, sfx_doropn);
+				break;
+			case close30ThenOpen:
+				door->topheight = sec->ceilingheight;
+				door->direction = -1;
+				S_StartSound((mobj_t *)&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((mobj_t *)&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, 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, 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, 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((mobj_t *)&sec->soundorg, sfx_doropn);
+			//S_StartSound((mobj_t *)&sec->soundorg, sfx_dormov);
+			break;
+		default: // LOCKED DOOR SOUND
+			S_StartSound((mobj_t *)&sec->soundorg, sfx_doropn);
+			//S_StartSound((mobj_t *)&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;
+}
--- /dev/null
+++ b/src/heretic/p_enemy.c
@@ -1,0 +1,2636 @@
+
+// P_enemy.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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;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, ANGLE_1*10, ANGLE_1*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-ANGLE_1*3, momz);
+			P_SpawnMissileAngle(actor, MT_SRCRFX1, angle+ANGLE_1*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*TICSPERSEC; // 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, ANGLE_1*10, ANGLE_1*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 = -.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;
+
+	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;
+
+	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 = (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(FRACUNIT*.7, finecosine[angle]);
+		tiny->momy = FixedMul(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;
+}
--- /dev/null
+++ b/src/heretic/p_floor.c
@@ -1,0 +1,445 @@
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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((mobj_t *)&floor->sector->soundorg, sfx_dormov);
+	}
+
+	if (res == pastdest)
+	{
+		floor->sector->specialdata = NULL;
+		if(floor->type == raiseBuildStep)
+		{
+			S_StartSound((mobj_t *)&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 = MAXINT;
+					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);
+}
--- /dev/null
+++ b/src/heretic/p_inter.c
@@ -1,0 +1,1473 @@
+
+// P_inter.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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
+};
+
+static 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, TXT_ITEMHEALTH, false);
+			break;
+		case SPR_SHLD: // Item_Shield1
+			if(!P_GiveArmor(player, 1))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_ITEMSHIELD1, false);
+			break;
+		case SPR_SHD2: // Item_Shield2
+			if(!P_GiveArmor(player, 2))
+			{
+				return;
+			}
+			P_SetMessage(player, 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, TXT_ITEMBAGOFHOLDING, false);
+			break;
+		case SPR_SPMP: // Item_SuperMap
+			if(!P_GivePower(player, pw_allmap))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_ITEMSUPERMAP, false);
+			break;
+
+		// Keys
+		case SPR_BKYY: // Key_Blue
+			if(!player->keys[key_blue])
+			{
+				P_SetMessage(player, 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, 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, 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, TXT_ARTIHEALTH, false);
+				P_SetDormantArtifact(special);
+			}
+			return;
+		case SPR_SOAR: // Arti_Fly
+			if(P_GiveArtifact(player, arti_fly, special))
+			{
+				P_SetMessage(player, TXT_ARTIFLY, false);
+				P_SetDormantArtifact(special);
+			}
+			return;
+		case SPR_INVU: // Arti_Invulnerability
+			if(P_GiveArtifact(player, arti_invulnerability, special))
+			{
+				P_SetMessage(player, TXT_ARTIINVULNERABILITY, false);
+				P_SetDormantArtifact(special);
+			}
+			return;
+		case SPR_PWBK: // Arti_TomeOfPower
+			if(P_GiveArtifact(player, arti_tomeofpower, special))
+			{
+				P_SetMessage(player, TXT_ARTITOMEOFPOWER, false);
+				P_SetDormantArtifact(special);
+			}
+			return;
+		case SPR_INVS: // Arti_Invisibility
+			if(P_GiveArtifact(player, arti_invisibility, special))
+			{
+				P_SetMessage(player, TXT_ARTIINVISIBILITY, false);
+				P_SetDormantArtifact(special);
+			}
+			return;
+		case SPR_EGGC: // Arti_Egg
+			if(P_GiveArtifact(player, arti_egg, special))
+			{
+				P_SetMessage(player, TXT_ARTIEGG, false);
+				P_SetDormantArtifact(special);
+			}
+			return;
+		case SPR_SPHL: // Arti_SuperHealth
+			if(P_GiveArtifact(player, arti_superhealth, special))
+			{
+				P_SetMessage(player, TXT_ARTISUPERHEALTH, false);
+				P_SetDormantArtifact(special);
+			}
+			return;
+		case SPR_TRCH: // Arti_Torch
+			if(P_GiveArtifact(player, arti_torch, special))
+			{
+				P_SetMessage(player, TXT_ARTITORCH, false);
+				P_SetDormantArtifact(special);
+			}
+			return;
+		case SPR_FBMB: // Arti_FireBomb
+			if(P_GiveArtifact(player, arti_firebomb, special))
+			{
+				P_SetMessage(player, TXT_ARTIFIREBOMB, false);
+				P_SetDormantArtifact(special);
+			}
+			return;
+		case SPR_ATLP: // Arti_Teleport
+			if(P_GiveArtifact(player, arti_teleport, special))
+			{
+				P_SetMessage(player, 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, TXT_AMMOGOLDWAND1, false);
+			break;
+		case SPR_AMG2: // Ammo_GoldWandHefty
+			if(!P_GiveAmmo(player, am_goldwand, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOGOLDWAND2, false);
+			break;
+		case SPR_AMM1: // Ammo_MaceWimpy
+			if(!P_GiveAmmo(player, am_mace, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOMACE1, false);
+			break;
+		case SPR_AMM2: // Ammo_MaceHefty
+			if(!P_GiveAmmo(player, am_mace, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOMACE2, false);
+			break;
+		case SPR_AMC1: // Ammo_CrossbowWimpy
+			if(!P_GiveAmmo(player, am_crossbow, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOCROSSBOW1, false);
+			break;
+		case SPR_AMC2: // Ammo_CrossbowHefty
+			if(!P_GiveAmmo(player, am_crossbow, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOCROSSBOW2, false);
+			break;
+		case SPR_AMB1: // Ammo_BlasterWimpy
+			if(!P_GiveAmmo(player, am_blaster, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOBLASTER1, false);
+			break;
+		case SPR_AMB2: // Ammo_BlasterHefty
+			if(!P_GiveAmmo(player, am_blaster, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOBLASTER2, false);
+			break;
+		case SPR_AMS1: // Ammo_SkullRodWimpy
+			if(!P_GiveAmmo(player, am_skullrod, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOSKULLROD1, false);
+			break;
+		case SPR_AMS2: // Ammo_SkullRodHefty
+			if(!P_GiveAmmo(player, am_skullrod, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOSKULLROD2, false);
+			break;
+		case SPR_AMP1: // Ammo_PhoenixRodWimpy
+			if(!P_GiveAmmo(player, am_phoenixrod, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOPHOENIXROD1, false);
+			break;
+		case SPR_AMP2: // Ammo_PhoenixRodHefty
+			if(!P_GiveAmmo(player, am_phoenixrod, special->health))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_AMMOPHOENIXROD2, false);
+			break;
+
+		// Weapons
+		case SPR_WMCE: // Weapon_Mace
+			if(!P_GiveWeapon(player, wp_mace))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_WPNMACE, false);
+			sound = sfx_wpnup;
+			break;
+		case SPR_WBOW: // Weapon_Crossbow
+			if(!P_GiveWeapon(player, wp_crossbow))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_WPNCROSSBOW, false);
+			sound = sfx_wpnup;
+			break;
+		case SPR_WBLS: // Weapon_Blaster
+			if(!P_GiveWeapon(player, wp_blaster))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_WPNBLASTER, false);
+			sound = sfx_wpnup;
+			break;
+		case SPR_WSKL: // Weapon_SkullRod
+			if(!P_GiveWeapon(player, wp_skullrod))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_WPNSKULLROD, false);
+			sound = sfx_wpnup;
+			break;
+		case SPR_WPHX: // Weapon_PhoenixRod
+			if(!P_GiveWeapon(player, wp_phoenixrod))
+			{
+				return;
+			}
+			P_SetMessage(player, TXT_WPNPHOENIXROD, false);
+			sound = sfx_wpnup;
+			break;
+		case SPR_WGNT: // Weapon_Gauntlets
+			if(!P_GiveWeapon(player, wp_gauntlets))
+			{
+				return;
+			}
+			P_SetMessage(player, 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-TICSPERSEC)
+			&& !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 = superCount = 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);
+		}
+	}
+}
--- /dev/null
+++ b/src/heretic/p_lights.c
@@ -1,0 +1,258 @@
+#include "DoomDef.h"
+#include "P_local.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;
+}
+
--- /dev/null
+++ b/src/heretic/p_local.h
@@ -1,0 +1,262 @@
+
+// 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 MININT
+#define ONCEILINGZ MAXINT
+#define FLOATRANDZ (MAXINT-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__
--- /dev/null
+++ b/src/heretic/p_map.c
@@ -1,0 +1,1638 @@
+// P_map.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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;
+}
+
--- /dev/null
+++ b/src/heretic/p_maputl.c
@@ -1,0 +1,760 @@
+
+// P_maputl.c
+
+#include "DoomDef.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, p2;
+	
+	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 = MAXINT;
+		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 = MAXINT;
+	}
+	
+	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 );
+}
+
+
+
--- /dev/null
+++ b/src/heretic/p_mobj.c
@@ -1,0 +1,1599 @@
+
+// P_mobj.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "sounds.h"
+#include "soundst.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 = 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 = 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 > ANGLE_180)
+		{
+			*delta = ANGLE_MAX-diff;
+			return(0);
+		}
+		else
+		{
+			*delta = diff;
+			return(1);
+		}
+	}
+	else
+	{
+		diff = angle1-angle2;
+		if(diff > ANGLE_180)
+		{
+			*delta = ANGLE_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);
+#ifdef __WATCOMC__
+				if(!useexterndriver)
+				{
+					mo->player->centering = true;
+				}
+#else
+				mo->player->centering = true;
+#endif
+			}
+			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(shareware)
+			{ // Don't place on map in shareware version
+				return;
+			}
+			break;
+		case MT_WMACE:
+			if(!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 = .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;
+	}
+}
--- /dev/null
+++ b/src/heretic/p_plats.c
@@ -1,0 +1,240 @@
+
+// P_plats.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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((mobj_t *)&plat->sector->soundorg,
+					sfx_stnmov);
+			}
+			if(plat->type == raiseAndChange
+				|| plat->type == raiseToNearestAndChange)
+			{
+				if(!(leveltime&7))
+				{
+					S_StartSound((mobj_t *)&plat->sector->soundorg,
+						sfx_stnmov);
+				}
+			}
+			if (res == crushed && (!plat->crush))
+			{
+				plat->count = plat->wait;
+				plat->status = down;
+				S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstart);
+			}
+			else
+			if (res == pastdest)
+			{
+				plat->count = plat->wait;
+				plat->status = waiting;
+				S_StartSound((mobj_t *)&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((mobj_t *)&plat->sector->soundorg, sfx_pstop);
+			}
+			else
+			{
+				if(!(leveltime&31))
+				{
+					S_StartSound((mobj_t *)&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((mobj_t *)&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((mobj_t *)&sec->soundorg, sfx_stnmov);
+				break;
+			case raiseAndChange:
+				plat->speed = PLATSPEED/2;
+				sec->floorpic = sides[line->sidenum[0]].sector->floorpic;
+				plat->high = sec->floorheight + amount*FRACUNIT;
+				plat->wait = 0;
+				plat->status = up;
+				S_StartSound((mobj_t *)&sec->soundorg, sfx_stnmov);
+				break;
+			case downWaitUpStay:
+				plat->speed = PLATSPEED * 4;
+				plat->low = P_FindLowestFloorSurrounding(sec);
+				if (plat->low > sec->floorheight)
+					plat->low = sec->floorheight;
+				plat->high = sec->floorheight;
+				plat->wait = 35*PLATWAIT;
+				plat->status = down;
+				S_StartSound((mobj_t *)&sec->soundorg, sfx_pstart);
+				break;
+			case 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((mobj_t *)&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!");
+}
--- /dev/null
+++ b/src/heretic/p_pspr.c
@@ -1,0 +1,1869 @@
+
+// P_pspr.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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 += ANGLE_45/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, ANGLE_1*10, ANGLE_1*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, ANGLE_1*5, ANGLE_1*10);
+	puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PHOENIXPUFF);
+	angle = actor->angle+ANG90;
+	angle >>= ANGLETOFINESHIFT;
+	puff->momx = FixedMul(FRACUNIT*1.3, finecosine[angle]);
+	puff->momy = FixedMul(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(FRACUNIT*1.3, finecosine[angle]);
+	puff->momy = FixedMul(FRACUNIT*1.3, finesine[angle]);
+	puff->momz = 0;
+}
+
+//----------------------------------------------------------------------------
+//
+// 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 += 1.5*FRACUNIT;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FloatPuff
+//
+//----------------------------------------------------------------------------
+
+void A_FloatPuff(mobj_t *puff)
+{
+	puff->momz += 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;
+}
--- /dev/null
+++ b/src/heretic/p_setup.c
@@ -1,0 +1,624 @@
+
+// P_main.c
+
+#include <math.h>
+#include <stdlib.h>
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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;
+	}
+	
+	Z_Free (data);
+}
+
+
+/*
+=================
+=
+= 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;
+	}
+	
+	Z_Free (data);
+}
+
+
+/*
+=================
+=
+= 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);
+	}
+	
+	Z_Free (data);
+}
+
+
+/*
+=================
+=
+= 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;
+	}
+	
+	Z_Free (data);
+}
+
+
+/*
+=================
+=
+= 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;
+		}
+	}
+	
+	Z_Free (data);
+}
+
+
+
+/*
+=================
+=
+= P_LoadThings
+=
+=================
+*/
+
+void P_LoadThings (int lump)
+{
+	byte			*data;
+	int				i;
+	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++)
+	{
+		mt->x = SHORT(mt->x);
+		mt->y = SHORT(mt->y);
+		mt->angle = SHORT(mt->angle);
+		mt->type = SHORT(mt->type);
+		mt->options = SHORT(mt->options);
+		P_SpawnMapThing (mt);
+	}
+	
+	Z_Free (data);
+}
+
+
+
+/*
+=================
+=
+= 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;
+	}
+	
+	Z_Free (data);
+}
+
+
+/*
+=================
+=
+= 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)];
+	}
+	
+	Z_Free (data);
+}
+
+
+
+/*
+=================
+=
+= P_LoadBlockMap
+=
+=================
+*/
+
+void P_LoadBlockMap (int lump)
+{
+	int		i, count;
+	
+	blockmaplump = W_CacheLumpNum (lump,PU_LEVEL);
+	blockmap = blockmaplump+4;
+	count = W_LumpLength (lump)/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*4, 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);
+}
--- /dev/null
+++ b/src/heretic/p_sight.c
@@ -1,0 +1,340 @@
+// P_sight.c
+
+#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 = MAXINT;
+		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 = MAXINT;
+	}
+	
+	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 );
+}
+
+
+
--- /dev/null
+++ b/src/heretic/p_spec.c
@@ -1,0 +1,1253 @@
+
+// P_Spec.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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)
+{
+	int i;
+
+	lastanim = anims;
+	for(i = 0; animdefs[i].istexture != -1; i++)
+	{
+		if(animdefs[i].istexture)
+		{ // Texture animation
+			if(R_CheckTextureNumForName(animdefs[i].startname) == -1)
+			{ // Texture doesn't exist
+				continue;
+			}
+			lastanim->picnum = R_TextureNumForName(animdefs[i].endname);
+			lastanim->basepic = R_TextureNumForName(animdefs[i].startname);
+		}
+		else
+		{ // Flat animation
+			if(W_CheckNumForName(animdefs[i].startname) == -1)
+			{ // Flat doesn't exist
+				continue;
+			}
+			lastanim->picnum = R_FlatNumForName(animdefs[i].endname);
+			lastanim->basepic = R_FlatNumForName(animdefs[i].startname);
+		}
+		lastanim->istexture = animdefs[i].istexture;
+		lastanim->numpics = lastanim->picnum-lastanim->basepic+1;
+		if(lastanim->numpics < 2)
+		{
+			I_Error("P_InitPicAnims: bad cycle from %s to %s",
+				animdefs[i].startname, animdefs[i].endname);
+		}
+		lastanim->speed = 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;
+	int			min;
+	line_t		*check;
+	sector_t	*other;
+	fixed_t		height = currentheight;
+	fixed_t		heightlist[20];		// 20 adjoining sectors max!
+	
+	for (i =0,h = 0 ;i < sec->linecount ; i++)
+	{
+		check = sec->lines[i];
+		other = getNextSector(check,sec);
+		if (!other)
+			continue;
+		if (other->floorheight > height)
+			heightlist[h++] = other->floorheight;
+	}
+	
+	//
+	// Find lowest height in list
+	//
+	min = heightlist[0];
+	for (i = 1;i < h;i++)
+		if (heightlist[i] < min)
+			min = heightlist[i];
+			
+	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 = MAXINT;
+	
+	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((mobj_t *)&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("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*TICSPERSEC;
+	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*TICSPERSEC+P_Random();
+				AmbSfxPtr = LevelAmbientSfx[P_Random()%AmbSfxCount];
+				done = true;
+				break;
+			default:
+				I_Error("P_AmbientSound: Unknown afxcmd %d", cmd);
+				break;
+		}
+	} while(done == false);
+}
--- /dev/null
+++ b/src/heretic/p_spec.h
@@ -1,0 +1,374 @@
+
+// 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;
+	mobj_t		*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);
--- /dev/null
+++ b/src/heretic/p_switch.c
@@ -1,0 +1,390 @@
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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 (!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(alphSwitchList[i].name1);
+			switchlist[index++] = R_TextureNumForName(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 = (mobj_t *)&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;
+}
+
--- /dev/null
+++ b/src/heretic/p_telept.c
@@ -1,0 +1,148 @@
+
+// P_telept.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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);
+}
--- /dev/null
+++ b/src/heretic/p_tick.c
@@ -1,0 +1,643 @@
+
+// P_tick.c
+
+#include "DoomDef.h"
+#include "P_local.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++;
+}
--- /dev/null
+++ b/src/heretic/p_user.c
@@ -1,0 +1,1007 @@
+
+// P_user.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.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((byte *)W_CacheLumpName("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] || !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])
+		{
+#ifdef __WATCOMC__
+			if(player->mo->z != player->mo->floorz && !useexterndriver)
+			{
+				player->centering = true;
+			}
+#else
+			if(player->mo->z != player->mo->floorz)
+			{
+				player->centering = true;
+			}
+#endif
+
+			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);
+}
--- /dev/null
+++ b/src/heretic/r_bsp.c
@@ -1,0 +1,474 @@
+// R_bsp.c
+
+#include "DoomDef.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;
+	
+#ifdef __NeXT__
+	RD_DrawLineCheck (line);
+#endif 	
+	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;
+	
+#ifdef __NeXT__
+	RD_DrawBBox (bspcoord);
+#endif
+
+// 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]];
+
+
+#ifdef __NeXT__
+//	RD_DisplayLine (x1, y1, x2, y2, 0.1);
+#endif
+	
+//
+// 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];
+	
+#ifdef __NeXT__
+	RD_DrawNodeLine (bsp);
+#endif
+	
+//
+// 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]);
+}
+
+
--- /dev/null
+++ b/src/heretic/r_data.c
@@ -1,0 +1,701 @@
+
+// R_data.c
+
+#include "DoomDef.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 *)alloca (texture->width);
+	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;
+		}
+	}	
+}
+
+
+/*
+================
+=
+= 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;
+
+//
+// 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 = alloca (nummappatches*sizeof(*patchlookup));
+	for (i=0 ; i<nummappatches ; i++)
+	{
+		strncpy (name,name_p+i*8, 8);
+		patchlookup[i] = W_CheckNumForName (name);
+	}
+	Z_Free (names);
+
+//
+// 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	spramount;
+		spramount = W_GetNumForName("S_END") - W_GetNumForName("S_START") + 1;
+		InitThermo(spramount + numtextures + 6);
+	}
+
+	textures = Z_Malloc (numtextures*4, PU_STATIC, 0);
+	texturecolumnlump = Z_Malloc (numtextures*4, PU_STATIC, 0);
+	texturecolumnofs = Z_Malloc (numtextures*4, PU_STATIC, 0);
+	texturecomposite = Z_Malloc (numtextures*4, PU_STATIC, 0);
+	texturecompositesize = Z_Malloc (numtextures*4, PU_STATIC, 0);
+	texturewidthmask = Z_Malloc (numtextures*4, PU_STATIC, 0);
+	textureheight = Z_Malloc (numtextures*4, 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*2, PU_STATIC,0);
+		texturecolumnofs[i] = Z_Malloc (texture->width*2, 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 (maptex1);
+	if (maptex2)
+		Z_Free (maptex2);
+
+//
+// precalculate whatever possible
+//		
+	for(i = 0; i < numtextures; i++)
+	{
+		R_GenerateLookup(i);
+		CheckAbortStartup();
+	}
+
+//
+// translation table for global animation
+//
+	texturetranslation = Z_Malloc ((numtextures+1)*4, PU_STATIC, 0);
+	for (i=0 ; i<numtextures ; i++)
+		texturetranslation[i] = i;
+}
+
+
+/*
+================
+=
+= R_InitFlats
+=
+=================
+*/
+
+void R_InitFlats (void)
+{
+	int		i;
+	
+	firstflat = W_GetNumForName ("F_START") + 1;
+	lastflat = W_GetNumForName ("F_END") - 1;
+	numflats = lastflat - firstflat + 1;
+	
+// translation table for global animation
+	flattranslation = Z_Malloc ((numflats+1)*4, 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 ("S_START") + 1;
+	lastspritelump = W_GetNumForName ("S_END") - 1;
+	numspritelumps = lastspritelump - firstspritelump + 1;
+	spritewidth = Z_Malloc (numspritelumps*4, PU_STATIC, 0);
+	spriteoffset = Z_Malloc (numspritelumps*4, PU_STATIC, 0);
+	spritetopoffset = Z_Malloc (numspritelumps*4, 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("COLORMAP");
+	length = W_LumpLength (lump) + 255;
+	colormaps = Z_Malloc (length, PU_STATIC, 0);
+	colormaps = (byte *)( ((int)colormaps + 255)&~0xff);
+	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 = alloca(numflats);
+	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);
+		}
+		
+//
+// precache textures
+//
+	texturepresent = alloca(numtextures);
+	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);
+		}
+	}
+	
+//
+// precache sprites
+//
+	spritepresent = alloca(numsprites);
+	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);
+			}
+		}
+	}
+}
+
+
+
+
--- /dev/null
+++ b/src/heretic/r_draw.c
@@ -1,0 +1,498 @@
+// R_draw.c
+
+#include "DoomDef.h"
+#include "R_local.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
+byte *tinttable; // used for translucent sprites
+
+/*
+==================
+=
+= 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
+
+#ifndef __WATCOMC__
+#ifndef __i386
+#ifndef __m68k
+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--);
+}
+#endif		// __m68k
+#endif		// __i386
+#endif
+
+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--);
+}
+
+
+#define FUZZTABLE	50
+
+#define FUZZOFF	(SCREENWIDTH)
+int		fuzzoffset[FUZZTABLE] = {
+FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF
+};
+int fuzzpos = 0;
+
+void R_DrawFuzzColumn (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_DrawFuzzColumn: %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;
+
+// OLD FUZZY INVISO SPRITE STUFF
+/*	do
+	{
+		*dest = colormaps[6*256+dest[fuzzoffset[fuzzpos]]];
+		if (++fuzzpos == FUZZTABLE)
+			fuzzpos = 0;
+		dest += SCREENWIDTH;
+		frac += fracstep;
+	} while (count--);
+*/
+
+	do
+	{
+		*dest = tinttable[((*dest)<<8)+dc_colormap[dc_source[(frac>>FRACBITS)&127]]];
+
+		//*dest = dest[SCREENWIDTH*10+5];
+
+//		*dest = //tinttable[((*dest)<<8)+colormaps[dc_source[(frac>>FRACBITS)&127]]];
+
+//		*dest = 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_DrawTranslatedFuzzColumn (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;
+
+	// Load tint table
+	tinttable = W_CacheLumpName("TINTTAB", PU_STATIC);
+
+	// Allocate translation tables
+	translationtables = Z_Malloc(256*3+255, PU_STATIC, 0);
+	translationtables = (byte *)(( (int)translationtables + 255 )& ~255);
+
+	// 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
+
+#ifndef __WATCOMC__
+#ifndef __i386
+#ifndef __m68k
+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--);
+}
+#endif
+#endif
+#endif
+
+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] = screen + (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(shareware)
+	{
+		src = W_CacheLumpName ("FLOOR04", PU_CACHE);
+	}
+	else
+	{
+		src = W_CacheLumpName ("FLAT513", PU_CACHE);
+	}
+	dest = screen;
+	
+	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("bordt", PU_CACHE));
+		V_DrawPatch(x, viewwindowy+viewheight, W_CacheLumpName("bordb", 
+			PU_CACHE));
+	}
+	for(y=viewwindowy; y < viewwindowy+viewheight; y += 16)
+	{
+		V_DrawPatch(viewwindowx-4, y, W_CacheLumpName("bordl", PU_CACHE));
+		V_DrawPatch(viewwindowx+viewwidth, y, W_CacheLumpName("bordr", 
+			PU_CACHE));
+	}
+	V_DrawPatch(viewwindowx-4, viewwindowy-4, W_CacheLumpName("bordtl", 
+		PU_CACHE));
+	V_DrawPatch(viewwindowx+viewwidth, viewwindowy-4, 
+		W_CacheLumpName("bordtr", PU_CACHE));
+	V_DrawPatch(viewwindowx+viewwidth, viewwindowy+viewheight, 
+		W_CacheLumpName("bordbr", PU_CACHE));
+	V_DrawPatch(viewwindowx-4, viewwindowy+viewheight, 
+		W_CacheLumpName("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(shareware)
+	{
+		src = W_CacheLumpName ("FLOOR04", PU_CACHE);
+	}
+	else
+	{
+		src = W_CacheLumpName ("FLAT513", PU_CACHE);
+	}
+	dest = screen;
+	
+	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("bordt", PU_CACHE));
+		}
+		V_DrawPatch(viewwindowx-4, viewwindowy, W_CacheLumpName("bordl", 
+			PU_CACHE));
+		V_DrawPatch(viewwindowx+viewwidth, viewwindowy, 
+			W_CacheLumpName("bordr", PU_CACHE));
+		V_DrawPatch(viewwindowx-4, viewwindowy+16, W_CacheLumpName("bordl", 
+			PU_CACHE));
+		V_DrawPatch(viewwindowx+viewwidth, viewwindowy+16, 
+			W_CacheLumpName("bordr", PU_CACHE));
+
+		V_DrawPatch(viewwindowx-4, viewwindowy-4, W_CacheLumpName("bordtl", 
+			PU_CACHE));
+		V_DrawPatch(viewwindowx+viewwidth, viewwindowy-4, 
+			W_CacheLumpName("bordtr", PU_CACHE));
+	}
+}
+
+
--- /dev/null
+++ b/src/heretic/r_local.h
@@ -1,0 +1,468 @@
+// R_local.h
+
+#ifndef __R_LOCAL__
+#define __R_LOCAL__
+
+#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		finetangent[FINEANGLES/2];
+
+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		(*fuzzcolfunc) (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;
+
+void RD_OpenMapWindow (void);
+void RD_ClearMapWindow (void);
+void RD_DisplayLine (int x1, int y1, int x2, int y2, float gray);
+void RD_DrawNodeLine (node_t *node);
+void RD_DrawLineCheck (seg_t *line);
+void RD_DrawLine (seg_t *line);
+void RD_DrawBBox (fixed_t *bbox);
+
+
+//
+// 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_DrawFuzzColumn (void);
+void 	R_DrawFuzzColumnLow (void);
+void	R_DrawTranslatedColumn (void);
+void	R_DrawTranslatedFuzzColumn (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__
+
--- /dev/null
+++ b/src/heretic/r_main.c
@@ -1,0 +1,847 @@
+// R_main.c
+
+#include <math.h>
+#include "DoomDef.h"
+#include "R_local.h"
+/*
+
+*/
+
+int			viewangleoffset;
+
+#ifdef __WATCOMC__
+int newViewAngleOff;
+#endif
+
+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];
+
+// the finetangentgent[angle+FINEANGLES/4] table holds the fixed_t tangent
+// values for view angles, ranging from MININT to 0 to MAXINT.
+// fixed_t		finetangent[FINEANGLES/2];
+
+// fixed_t		finesine[5*FINEANGLES/4];
+fixed_t		*finecosine = &finesine[FINEANGLES/4];
+
+
+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		(*fuzzcolfunc) (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
+=
+===============================================================================
+*/
+
+// to get a global angle from cartesian coordinates, the coordinates are
+// flipped until they are in the first octant of the coordinate system, then
+// the y (<=x) is scaled and divided by x to get a tangent (slope) value
+// which is looked up in the tantoangle[] table.  The +1 size is to handle
+// the case when x==y without additional checking.
+#define	SLOPERANGE	2048
+#define	SLOPEBITS	11
+#define	DBITS		(FRACBITS-SLOPEBITS)
+
+
+extern	int	tantoangle[SLOPERANGE+1];		// get from tables.c
+
+// int	tantoangle[SLOPERANGE+1];
+
+int SlopeDiv (unsigned num, unsigned den)
+{
+	unsigned ans;
+	if (den < 512)
+		return SLOPERANGE;
+	ans = (num<<3)/(den>>8);
+	return ans <= SLOPERANGE ? ans : SLOPERANGE;
+}
+
+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;
+		fuzzcolfunc = R_DrawFuzzColumn;
+		transcolfunc = R_DrawTranslatedColumn;
+		spanfunc = R_DrawSpan;
+	}
+	else
+	{
+		colfunc = basecolfunc = R_DrawColumnLow;
+		fuzzcolfunc = R_DrawFuzzColumn;
+		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;
+
+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;
+#ifdef __WATCOMC__
+	viewangleoffset = newViewAngleOff<<ANGLETOFINESHIFT;
+#endif
+	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;
+	}
+
+#ifdef __NeXT__
+	RD_ClearMapWindow ();
+#endif
+#ifdef __WATCOMC__
+	destview = destscreen+(viewwindowx>>2)+viewwindowy*80;
+#endif
+
+#if 0
+{
+static int frame;
+memset (screen, frame, SCREENWIDTH*SCREENHEIGHT);
+frame++;
+}
+#endif
+}
+
+/*
+==============
+=
+= 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
+}
--- /dev/null
+++ b/src/heretic/r_plane.c
@@ -1,0 +1,472 @@
+// R_planes.c
+
+#include "DoomDef.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 ("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			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
+	//
+		tempSource = W_CacheLumpNum(firstflat +
+			flattranslation[pl->picnum], 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]);
+		
+		Z_ChangeTag (tempSource, PU_CACHE);
+	}
+}
--- /dev/null
+++ b/src/heretic/r_segs.c
@@ -1,0 +1,645 @@
+
+//**************************************************************************
+//**
+//** R_SEGS.C
+//**
+//** This version has the tall-sector-crossing-precision-bug fixed.
+//**
+//**************************************************************************
+
+#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] != MAXSHORT)
+		{
+			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] = MAXSHORT;
+		}
+		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
+#ifdef __NeXT__
+	RD_DrawLine (curline);
+#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 = MAXINT;
+		ds_p->tsilheight = MININT;
+	}
+	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 = MAXINT;
+//                      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 = MININT;
+//                      ds_p->sprtopclip = screenheightarray;
+		}
+
+		if (backsector->ceilingheight <= frontsector->floorheight)
+		{
+			ds_p->sprbottomclip = negonearray;
+			ds_p->bsilheight = MAXINT;
+			ds_p->silhouette |= SIL_BOTTOM;
+		}
+		if (backsector->floorheight >= frontsector->ceilingheight)
+		{
+			ds_p->sprtopclip = screenheightarray;
+			ds_p->tsilheight = MININT;
+			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 = MININT;
+	}
+	if (maskedtexture && !(ds_p->silhouette&SIL_BOTTOM))
+	{
+		ds_p->silhouette |= SIL_BOTTOM;
+		ds_p->bsilheight = MAXINT;
+	}
+	ds_p++;
+}
+
--- /dev/null
+++ b/src/heretic/r_things.c
@@ -1,0 +1,992 @@
+// R_things.c
+#include <stdio.h>
+#include <stdlib.h>
+#include "DoomDef.h"
+#include "R_local.h"
+
+void R_DrawColumn (void);
+void R_DrawFuzzColumn (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, intname, 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 = namelist[i];
+		memset (sprtemp,-1, sizeof(sprtemp));
+
+		maxframe = -1;
+		intname = *(int *)namelist[i];
+
+		//
+		// scan the lumps, filling in the frames for whatever is found
+		//
+		for (l=start+1 ; l<end ; l++)
+			if (*(int *)lumpinfo[l].name == intname)
+			{
+				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 (shareware)
+				continue;
+			I_Error ("R_InitSprites: No lumps found for sprite %s"
+			,namelist[i]);
+		}
+
+		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"
+				, namelist[i], 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"
+						, namelist[i], 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_DrawFuzzColumn
+		}
+		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 = fuzzcolfunc;	// NULL colormap = shadow draw
+
+	if(vis->mobjflags&MF_SHADOW)
+	{
+		if(vis->mobjflags&MF_TRANSLATION)
+		{
+			colfunc = R_DrawTranslatedFuzzColumn;
+			dc_translation = translationtables - 256 +
+				((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT-8));
+		}
+		else
+		{ // Draw using shadow column function
+			colfunc = fuzzcolfunc;
+		}
+	}
+	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 = MAXINT;
+		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 ();
+}
+
+
--- /dev/null
+++ b/src/heretic/sb_bar.c
@@ -1,0 +1,1456 @@
+
+// SB_bar.c
+
+#include "DoomDef.h"
+#include "P_local.h"
+#include "soundst.h"
+
+// Macros
+
+#define CHEAT_ENCRYPT(a) \
+	((((a)&1)<<5)+ \
+	(((a)&2)<<1)+ \
+	(((a)&4)<<4)+ \
+	(((a)&8)>>3)+ \
+	(((a)&16)>>3)+ \
+	(((a)&32)<<2)+ \
+	(((a)&64)>>2)+ \
+	(((a)&128)>>4))
+
+// Types
+
+typedef struct Cheat_s
+{
+	void (*func)(player_t *player, struct Cheat_s *cheat);
+	byte *sequence;
+	byte *pos;
+	int args[2];
+	int currentArg;
+} 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 boolean CheatAddKey(Cheat_t *cheat, byte key, boolean *eat);
+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;
+
+// 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;
+extern byte *screen;
+int FontBNumBase;
+int spinbooklump;
+int spinflylump;
+
+static byte CheatLookup[256];
+
+// Toggle god mode
+static byte CheatGodSeq[] =
+{
+	CHEAT_ENCRYPT('q'),
+	CHEAT_ENCRYPT('u'),
+	CHEAT_ENCRYPT('i'),
+	CHEAT_ENCRYPT('c'),
+	CHEAT_ENCRYPT('k'),
+	CHEAT_ENCRYPT('e'),
+	CHEAT_ENCRYPT('n'),
+	0xff
+};
+
+// Toggle no clipping mode
+static byte CheatNoClipSeq[] =
+{
+	CHEAT_ENCRYPT('k'),
+	CHEAT_ENCRYPT('i'),
+	CHEAT_ENCRYPT('t'),
+	CHEAT_ENCRYPT('t'),
+	CHEAT_ENCRYPT('y'),
+	0xff
+};
+
+// Get all weapons and ammo
+static byte CheatWeaponsSeq[] =
+{
+	CHEAT_ENCRYPT('r'),
+	CHEAT_ENCRYPT('a'),
+	CHEAT_ENCRYPT('m'),
+	CHEAT_ENCRYPT('b'),
+	CHEAT_ENCRYPT('o'),
+	0xff
+};
+
+// Toggle tome of power
+static byte CheatPowerSeq[] =
+{
+	CHEAT_ENCRYPT('s'),
+	CHEAT_ENCRYPT('h'),
+	CHEAT_ENCRYPT('a'),
+	CHEAT_ENCRYPT('z'),
+	CHEAT_ENCRYPT('a'),
+	CHEAT_ENCRYPT('m'),
+	0xff, 0
+};
+
+// Get full health
+static byte CheatHealthSeq[] =
+{
+	CHEAT_ENCRYPT('p'),
+	CHEAT_ENCRYPT('o'),
+	CHEAT_ENCRYPT('n'),
+	CHEAT_ENCRYPT('c'),
+	CHEAT_ENCRYPT('e'),
+	0xff
+};
+
+// Get all keys
+static byte CheatKeysSeq[] =
+{
+	CHEAT_ENCRYPT('s'),
+	CHEAT_ENCRYPT('k'),
+	CHEAT_ENCRYPT('e'),
+	CHEAT_ENCRYPT('l'),
+	0xff, 0
+};
+
+// Toggle sound debug info
+static byte CheatSoundSeq[] =
+{
+	CHEAT_ENCRYPT('n'),
+	CHEAT_ENCRYPT('o'),
+	CHEAT_ENCRYPT('i'),
+	CHEAT_ENCRYPT('s'),
+	CHEAT_ENCRYPT('e'),
+	0xff
+};
+
+// Toggle ticker
+static byte CheatTickerSeq[] =
+{
+	CHEAT_ENCRYPT('t'),
+	CHEAT_ENCRYPT('i'),
+	CHEAT_ENCRYPT('c'),
+	CHEAT_ENCRYPT('k'),
+	CHEAT_ENCRYPT('e'),
+	CHEAT_ENCRYPT('r'),
+	0xff, 0
+};
+
+// Get an artifact 1st stage (ask for type)
+static byte CheatArtifact1Seq[] =
+{
+	CHEAT_ENCRYPT('g'),
+	CHEAT_ENCRYPT('i'),
+	CHEAT_ENCRYPT('m'),
+	CHEAT_ENCRYPT('m'),
+	CHEAT_ENCRYPT('e'),
+	0xff
+};
+
+// Get an artifact 2nd stage (ask for count)
+static byte CheatArtifact2Seq[] =
+{
+	CHEAT_ENCRYPT('g'),
+	CHEAT_ENCRYPT('i'),
+	CHEAT_ENCRYPT('m'),
+	CHEAT_ENCRYPT('m'),
+	CHEAT_ENCRYPT('e'),
+	0, 0xff, 0
+};
+
+// Get an artifact final stage
+static byte CheatArtifact3Seq[] =
+{
+	CHEAT_ENCRYPT('g'),
+	CHEAT_ENCRYPT('i'),
+	CHEAT_ENCRYPT('m'),
+	CHEAT_ENCRYPT('m'),
+	CHEAT_ENCRYPT('e'),
+	0, 0, 0xff
+};
+
+// Warp to new level
+static byte CheatWarpSeq[] =
+{
+	CHEAT_ENCRYPT('e'),
+	CHEAT_ENCRYPT('n'),
+	CHEAT_ENCRYPT('g'),
+	CHEAT_ENCRYPT('a'),
+	CHEAT_ENCRYPT('g'),
+	CHEAT_ENCRYPT('e'),
+	0, 0, 0xff, 0
+};
+
+// Save a screenshot
+static byte CheatChickenSeq[] =
+{
+	CHEAT_ENCRYPT('c'),
+	CHEAT_ENCRYPT('o'),
+	CHEAT_ENCRYPT('c'),
+	CHEAT_ENCRYPT('k'),
+	CHEAT_ENCRYPT('a'),
+	CHEAT_ENCRYPT('d'),
+	CHEAT_ENCRYPT('o'),
+	CHEAT_ENCRYPT('o'),
+	CHEAT_ENCRYPT('d'),
+	CHEAT_ENCRYPT('l'),
+	CHEAT_ENCRYPT('e'),
+	CHEAT_ENCRYPT('d'),
+	CHEAT_ENCRYPT('o'),
+	CHEAT_ENCRYPT('o'),
+	0xff, 0
+};
+
+// Kill all monsters
+static byte CheatMassacreSeq[] =
+{
+	CHEAT_ENCRYPT('m'),
+	CHEAT_ENCRYPT('a'),
+	CHEAT_ENCRYPT('s'),
+	CHEAT_ENCRYPT('s'),
+	CHEAT_ENCRYPT('a'),
+	CHEAT_ENCRYPT('c'),
+	CHEAT_ENCRYPT('r'),
+	CHEAT_ENCRYPT('e'),
+	0xff, 0
+};
+
+static byte CheatIDKFASeq[] =
+{
+	CHEAT_ENCRYPT('i'),
+	CHEAT_ENCRYPT('d'),
+	CHEAT_ENCRYPT('k'),
+	CHEAT_ENCRYPT('f'),
+	CHEAT_ENCRYPT('a'),
+	0xff, 0
+};
+
+static byte CheatIDDQDSeq[] =
+{
+	CHEAT_ENCRYPT('i'),
+	CHEAT_ENCRYPT('d'),
+	CHEAT_ENCRYPT('d'),
+	CHEAT_ENCRYPT('q'),
+	CHEAT_ENCRYPT('d'),
+	0xff, 0
+};
+
+static Cheat_t Cheats[] =
+{
+	{ CheatGodFunc, CheatGodSeq, NULL, 0, 0, 0 },
+	{ CheatNoClipFunc, CheatNoClipSeq, NULL, 0, 0, 0 },
+	{ CheatWeaponsFunc, CheatWeaponsSeq, NULL, 0, 0, 0 },
+	{ CheatPowerFunc, CheatPowerSeq, NULL, 0, 0, 0 },
+	{ CheatHealthFunc, CheatHealthSeq, NULL, 0, 0, 0 },
+	{ CheatKeysFunc, CheatKeysSeq, NULL, 0, 0, 0 },
+	{ CheatSoundFunc, CheatSoundSeq, NULL, 0, 0, 0 },
+	{ CheatTickerFunc, CheatTickerSeq, NULL, 0, 0, 0 },
+	{ CheatArtifact1Func, CheatArtifact1Seq, NULL, 0, 0, 0 },
+	{ CheatArtifact2Func, CheatArtifact2Seq, NULL, 0, 0, 0 },
+	{ CheatArtifact3Func, CheatArtifact3Seq, NULL, 0, 0, 0 },
+	{ CheatWarpFunc, CheatWarpSeq, NULL, 0, 0, 0 },
+	{ CheatChickenFunc, CheatChickenSeq, NULL, 0, 0, 0 },
+	{ CheatMassacreFunc, CheatMassacreSeq, NULL, 0, 0, 0 },
+	{ CheatIDKFAFunc, CheatIDKFASeq, NULL, 0, 0, 0 },
+	{ CheatIDDQDFunc, CheatIDDQDSeq, NULL, 0, 0, 0 },
+	{ NULL, NULL, NULL, 0, 0, 0 } // Terminator
+};
+
+//---------------------------------------------------------------------------
+//
+// PROC SB_Init
+//
+//---------------------------------------------------------------------------
+
+void SB_Init(void)
+{
+	int i;
+	int startLump;
+
+	PatchLTFACE = W_CacheLumpName("LTFACE", PU_STATIC);
+	PatchRTFACE = W_CacheLumpName("RTFACE", PU_STATIC);
+	PatchBARBACK = W_CacheLumpName("BARBACK", PU_STATIC);
+	PatchINVBAR = W_CacheLumpName("INVBAR", PU_STATIC);
+	PatchCHAIN = W_CacheLumpName("CHAIN", PU_STATIC);
+	if(deathmatch)
+	{
+		PatchSTATBAR = W_CacheLumpName("STATBAR", PU_STATIC);
+	}
+	else
+	{
+		PatchSTATBAR = W_CacheLumpName("LIFEBAR", PU_STATIC);
+	}
+	if(!netgame)
+	{ // single player game uses red life gem
+		PatchLIFEGEM = W_CacheLumpName("LIFEGEM2", PU_STATIC);
+	}
+	else
+	{
+		PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName("LIFEGEM0")
+			+ consoleplayer, PU_STATIC);
+	}
+	PatchLTFCTOP = W_CacheLumpName("LTFCTOP", PU_STATIC);
+	PatchRTFCTOP = W_CacheLumpName("RTFCTOP", PU_STATIC);
+	PatchSELECTBOX = W_CacheLumpName("SELECTBOX", PU_STATIC);
+	PatchINVLFGEM1 = W_CacheLumpName("INVGEML1", PU_STATIC);
+	PatchINVLFGEM2 = W_CacheLumpName("INVGEML2", PU_STATIC);
+	PatchINVRTGEM1 = W_CacheLumpName("INVGEMR1", PU_STATIC);
+	PatchINVRTGEM2 = W_CacheLumpName("INVGEMR2", PU_STATIC);
+	PatchBLACKSQ    =   W_CacheLumpName("BLACKSQ", PU_STATIC);
+	PatchARMCLEAR = W_CacheLumpName("ARMCLEAR", PU_STATIC);
+	PatchCHAINBACK = W_CacheLumpName("CHAINBACK", PU_STATIC);
+	startLump = W_GetNumForName("IN0");
+	for(i = 0; i < 10; i++)
+	{
+		PatchINumbers[i] = W_CacheLumpNum(startLump+i, PU_STATIC);
+	}
+	PatchNEGATIVE = W_CacheLumpName("NEGNUM", PU_STATIC);
+	FontBNumBase = W_GetNumForName("FONTB16");
+	startLump = W_GetNumForName("SMALLIN0");
+	for(i = 0; i < 10; i++)
+	{
+		PatchSmNumbers[i] = W_CacheLumpNum(startLump+i, PU_STATIC);
+	}
+	playpalette = W_GetNumForName("PLAYPAL");
+	spinbooklump = W_GetNumForName("SPINBK0");
+	spinflylump = W_GetNumForName("SPFLY0");
+	for(i = 0; i < 256; i++)
+	{
+		CheatLookup[i] = CHEAT_ENCRYPT(i);
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// 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("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 = screen+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("*** SOUND DEBUG INFO ***", xPos[0], 20);
+	}
+	S_GetChannelInfo(&s);
+	if(s.channelCount == 0)
+	{
+		return;
+	}
+	x = 0;
+	MN_DrTextA("NAME", xPos[x++], 30);
+	MN_DrTextA("MO.T", xPos[x++], 30);
+	MN_DrTextA("MO.X", xPos[x++], 30);
+	MN_DrTextA("MO.Y", xPos[x++], 30);
+	MN_DrTextA("ID", xPos[x++], 30);
+	MN_DrTextA("PRI", xPos[x++], 30);
+	MN_DrTextA("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("------", 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("GOD1", PU_CACHE));
+				V_DrawPatch(287, 167, W_CacheLumpName("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);
+		V_DrawPatch(182, 161, W_CacheLumpNum(W_GetNumForName("useartia")
+			+ ArtifactFlash - 1, 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(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("ykeyicon", PU_CACHE));
+		}
+		if(CPlayer->keys[key_green])
+		{
+			V_DrawPatch(153, 172, W_CacheLumpName("gkeyicon", PU_CACHE));
+		}
+		if(CPlayer->keys[key_blue])
+		{
+			V_DrawPatch(153, 180, W_CacheLumpName("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(
+				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)
+{
+	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)
+		{
+			V_DrawPatch(50+i*31, 160, W_CacheLumpName(
+				patcharti[CPlayer->inventory[x+i].type], 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)
+{
+	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)
+		{
+			V_DrawFuzzPatch(286, 170, W_CacheLumpName("ARTIBOX",
+				PU_CACHE));
+			V_DrawPatch(286, 170,
+				W_CacheLumpName(patcharti[CPlayer->readyArtifact], PU_CACHE));
+			DrSmallNumber(CPlayer->inventory[inv_ptr].count, 307, 192);
+		}
+	}
+	else
+	{
+		x = inv_ptr-curpos;
+		for(i = 0; i < 7; i++)
+		{
+			V_DrawFuzzPatch(50+i*31, 168, W_CacheLumpName("ARTIBOX",
+				PU_CACHE));
+			if(CPlayer->inventorySlotNum > x+i
+				&& CPlayer->inventory[x+i].type != arti_none)
+			{
+				V_DrawPatch(50+i*31, 168, W_CacheLumpName(
+					patcharti[CPlayer->inventory[x+i].type], 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(CheatAddKey(&Cheats[i], key, &eat))
+		{
+			Cheats[i].func(&players[consoleplayer], &Cheats[i]);
+			S_StartSound(NULL, sfx_dorcls);
+		}
+	}
+	return(eat);
+}
+
+//--------------------------------------------------------------------------
+//
+// FUNC CheatAddkey
+//
+// Returns true if the added key completed the cheat, false otherwise.
+//
+//--------------------------------------------------------------------------
+
+static boolean CheatAddKey(Cheat_t *cheat, byte key, boolean *eat)
+{
+	if(!cheat->pos)
+	{
+		cheat->pos = cheat->sequence;
+		cheat->currentArg = 0;
+	}
+	if(*cheat->pos == 0)
+	{
+		*eat = true;
+		cheat->args[cheat->currentArg++] = key;
+		cheat->pos++;
+	}
+	else if(CheatLookup[key] == *cheat->pos)
+	{
+		cheat->pos++;
+	}
+	else
+	{
+		cheat->pos = cheat->sequence;
+		cheat->currentArg = 0;
+	}
+	if(*cheat->pos == 0xff)
+	{
+		cheat->pos = cheat->sequence;
+		cheat->currentArg = 0;
+		return(true);
+	}
+	return(false);
+}
+
+//--------------------------------------------------------------------------
+//
+// CHEAT FUNCTIONS
+//
+//--------------------------------------------------------------------------
+
+static void CheatGodFunc(player_t *player, Cheat_t *cheat)
+{
+	player->cheats ^= CF_GODMODE;
+	if(player->cheats&CF_GODMODE)
+	{
+		P_SetMessage(player, TXT_CHEATGODON, false);
+	}
+	else
+	{
+		P_SetMessage(player, 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, TXT_CHEATNOCLIPON, false);
+	}
+	else
+	{
+		P_SetMessage(player, 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(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, 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, TXT_CHEATPOWEROFF, false);
+	}
+	else
+	{
+		P_UseArtifact(player, arti_tomeofpower);
+		P_SetMessage(player, 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, 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, TXT_CHEATKEYS, false);
+}
+
+static void CheatSoundFunc(player_t *player, Cheat_t *cheat)
+{
+	DebugSound = !DebugSound;
+	if(DebugSound)
+	{
+		P_SetMessage(player, TXT_CHEATSOUNDON, false);
+	}
+	else
+	{
+		P_SetMessage(player, TXT_CHEATSOUNDOFF, false);
+	}
+}
+
+static void CheatTickerFunc(player_t *player, Cheat_t *cheat)
+{
+	extern int DisplayTicker;
+
+	DisplayTicker = !DisplayTicker;
+	if(DisplayTicker)
+	{
+		P_SetMessage(player, TXT_CHEATTICKERON, false);
+	}
+	else
+	{
+		P_SetMessage(player, TXT_CHEATTICKEROFF, false);
+	}
+}
+
+static void CheatArtifact1Func(player_t *player, Cheat_t *cheat)
+{
+	P_SetMessage(player, TXT_CHEATARTIFACTS1, false);
+}
+
+static void CheatArtifact2Func(player_t *player, Cheat_t *cheat)
+{
+	P_SetMessage(player, TXT_CHEATARTIFACTS2, false);
+}
+
+static void CheatArtifact3Func(player_t *player, Cheat_t *cheat)
+{
+	int i;
+	int j;
+	artitype_t type;
+	int count;
+
+	type = cheat->args[0]-'a'+1;
+	count = cheat->args[1]-'0';
+	if(type == 26 && count == 0)
+	{ // All artifacts
+		for(i = arti_none+1; i < NUMARTIFACTS; i++)
+		{
+			if(shareware && (i == arti_superhealth
+				|| i == arti_teleport))
+			{
+				continue;
+			}
+			for(j = 0; j < 16; j++)
+			{
+				P_GiveArtifact(player, i, NULL);
+			}
+		}
+		P_SetMessage(player, TXT_CHEATARTIFACTS3, false);
+	}
+	else if(type > arti_none && type < NUMARTIFACTS
+		&& count > 0 && count < 10)
+	{
+		if(shareware && (type == arti_superhealth || type == arti_teleport))
+		{
+			P_SetMessage(player, TXT_CHEATARTIFACTSFAIL, false);
+			return;
+		}
+		for(i = 0; i < count; i++)
+		{
+			P_GiveArtifact(player, type, NULL);
+		}
+		P_SetMessage(player, TXT_CHEATARTIFACTS3, false);
+	}
+	else
+	{ // Bad input
+		P_SetMessage(player, TXT_CHEATARTIFACTSFAIL, false);
+	}
+}
+
+static void CheatWarpFunc(player_t *player, Cheat_t *cheat)
+{
+	int episode;
+	int map;
+
+	episode = cheat->args[0]-'0';
+	map = cheat->args[1]-'0';
+	if(M_ValidEpisodeMap(episode, map))
+	{
+		G_DeferedInitNew(gameskill, episode, map);
+		P_SetMessage(player, 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, TXT_CHEATCHICKENOFF, false);
+		}
+	}
+	else if(P_ChickenMorphPlayer(player))
+	{
+		P_SetMessage(player, TXT_CHEATCHICKENON, false);
+	}
+}
+
+static void CheatMassacreFunc(player_t *player, Cheat_t *cheat)
+{
+	P_Massacre();
+	P_SetMessage(player, 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, TXT_CHEATIDKFA, true);
+}
+
+static void CheatIDDQDFunc(player_t *player, Cheat_t *cheat)
+{
+	P_DamageMobj(player->mo, NULL, player->mo, 10000);
+	P_SetMessage(player, TXT_CHEATIDDQD, true);
+}
--- /dev/null
+++ b/src/heretic/sounds.c
@@ -1,0 +1,220 @@
+
+
+// sounds.c
+
+#include "DoomDef.h"
+#include "sounds.h"
+
+// Music info
+
+musicinfo_t S_music[] =
+{
+	{ "MUS_E1M1", 0 }, // 1-1
+	{ "MUS_E1M2", 0 },
+	{ "MUS_E1M3", 0 },
+	{ "MUS_E1M4", 0 },
+	{ "MUS_E1M5", 0 },
+	{ "MUS_E1M6", 0 },
+	{ "MUS_E1M7", 0 },
+	{ "MUS_E1M8", 0 },
+	{ "MUS_E1M9", 0 },
+
+	{ "MUS_E2M1", 0 }, // 2-1
+	{ "MUS_E2M2", 0 },
+	{ "MUS_E2M3", 0 },
+	{ "MUS_E2M4", 0 },
+	{ "MUS_E1M4", 0 },
+	{ "MUS_E2M6", 0 },
+	{ "MUS_E2M7", 0 },
+	{ "MUS_E2M8", 0 },
+	{ "MUS_E2M9", 0 },
+
+	{ "MUS_E1M1", 0 }, // 3-1
+	{ "MUS_E3M2", 0 },
+	{ "MUS_E3M3", 0 },
+	{ "MUS_E1M6", 0 },
+	{ "MUS_E1M3", 0 },
+	{ "MUS_E1M2", 0 },
+	{ "MUS_E1M5", 0 },
+	{ "MUS_E1M9", 0 },
+	{ "MUS_E2M6", 0 },
+
+	{ "MUS_E1M6", 0 }, // 4-1
+	{ "MUS_E1M2", 0 },
+	{ "MUS_E1M3", 0 },
+	{ "MUS_E1M4", 0 },
+	{ "MUS_E1M5", 0 },
+	{ "MUS_E1M1", 0 },
+	{ "MUS_E1M7", 0 },
+	{ "MUS_E1M8", 0 },
+	{ "MUS_E1M9", 0 },
+
+	{ "MUS_E2M1", 0 }, // 5-1
+	{ "MUS_E2M2", 0 },
+	{ "MUS_E2M3", 0 },
+	{ "MUS_E2M4", 0 },
+	{ "MUS_E1M4", 0 },
+	{ "MUS_E2M6", 0 },
+	{ "MUS_E2M7", 0 },
+	{ "MUS_E2M8", 0 },
+	{ "MUS_E2M9", 0 },
+
+	{ "MUS_E3M2", 0 }, // 6-1
+	{ "MUS_E3M3", 0 }, // 6-2
+	{ "MUS_E1M6", 0 }, // 6-3
+
+	{ "MUS_TITL", 0 },
+	{ "MUS_INTR", 0 },
+	{ "MUS_CPTD", 0 }
+};
+
+// Sound info
+
+sfxinfo_t S_sfx[] =
+{
+	{ {0,0,0,0,0,0,0,0}, NULL, 0, -1, NULL, 0, 0 },
+	{ "gldhit", NULL, 32, -1, NULL, 0, 2 },
+	{ "gntful", NULL, 32, -1, NULL, 0, -1 },
+	{ "gnthit", NULL, 32, -1, NULL, 0, -1 },
+	{ "gntpow", NULL, 32, -1, NULL, 0, -1 },
+	{ "gntact", NULL, 32, -1, NULL, 0, -1 },
+	{ "gntuse", NULL, 32, -1, NULL, 0, -1 },
+	{ "phosht", NULL, 32, -1, NULL, 0, 2 },
+	{ "phohit", NULL, 32, -1, NULL, 0, -1 },
+	{ "-phopow", &S_sfx[sfx_hedat1], 32, -1, NULL, 0, 1 },
+	{ "lobsht", NULL, 20, -1, NULL, 0, 2 },
+	{ "lobhit", NULL, 20, -1, NULL, 0, 2 },
+	{ "lobpow", NULL, 20, -1, NULL, 0, 2 },
+	{ "hrnsht", NULL, 32, -1, NULL, 0, 2 },
+	{ "hrnhit", NULL, 32, -1, NULL, 0, 2 },
+	{ "hrnpow", NULL, 32, -1, NULL, 0, 2 },
+	{ "ramphit", NULL, 32, -1, NULL, 0, 2 },
+	{ "ramrain", NULL, 10, -1, NULL, 0, 2 },
+	{ "bowsht", NULL, 32, -1, NULL, 0, 2 },
+	{ "stfhit", NULL, 32, -1, NULL, 0, 2 },
+	{ "stfpow", NULL, 32, -1, NULL, 0, 2 },
+	{ "stfcrk", NULL, 32, -1, NULL, 0, 2 },
+	{ "impsit", NULL, 32, -1, NULL, 0, 2 },
+	{ "impat1", NULL, 32, -1, NULL, 0, 2 },
+	{ "impat2", NULL, 32, -1, NULL, 0, 2 },
+	{ "impdth", NULL, 80, -1, NULL, 0, 2 },
+	{ "-impact", &S_sfx[sfx_impsit], 20, -1, NULL, 0, 2 },
+	{ "imppai", NULL, 32, -1, NULL, 0, 2 },
+	{ "mumsit", NULL, 32, -1, NULL, 0, 2 },
+	{ "mumat1", NULL, 32, -1, NULL, 0, 2 },
+	{ "mumat2", NULL, 32, -1, NULL, 0, 2 },
+	{ "mumdth", NULL, 80, -1, NULL, 0, 2 },
+	{ "-mumact", &S_sfx[sfx_mumsit], 20, -1, NULL, 0, 2 },
+	{ "mumpai", NULL, 32, -1, NULL, 0, 2 },
+	{ "mumhed", NULL, 32, -1, NULL, 0, 2 },
+	{ "bstsit", NULL, 32, -1, NULL, 0, 2 },
+	{ "bstatk", NULL, 32, -1, NULL, 0, 2 },
+	{ "bstdth", NULL, 80, -1, NULL, 0, 2 },
+	{ "bstact", NULL, 20, -1, NULL, 0, 2 },
+	{ "bstpai", NULL, 32, -1, NULL, 0, 2 },
+	{ "clksit", NULL, 32, -1, NULL, 0, 2 },
+	{ "clkatk", NULL, 32, -1, NULL, 0, 2 },
+	{ "clkdth", NULL, 80, -1, NULL, 0, 2 },
+	{ "clkact", NULL, 20, -1, NULL, 0, 2 },
+	{ "clkpai", NULL, 32, -1, NULL, 0, 2 },
+	{ "snksit", NULL, 32, -1, NULL, 0, 2 },
+	{ "snkatk", NULL, 32, -1, NULL, 0, 2 },
+	{ "snkdth", NULL, 80, -1, NULL, 0, 2 },
+	{ "snkact", NULL, 20, -1, NULL, 0, 2 },
+	{ "snkpai", NULL, 32, -1, NULL, 0, 2 },
+	{ "kgtsit", NULL, 32, -1, NULL, 0, 2 },
+	{ "kgtatk", NULL, 32, -1, NULL, 0, 2 },
+	{ "kgtat2", NULL, 32, -1, NULL, 0, 2 },
+	{ "kgtdth", NULL, 80, -1, NULL, 0, 2 },
+	{ "-kgtact", &S_sfx[sfx_kgtsit], 20, -1, NULL, 0, 2 },
+	{ "kgtpai", NULL, 32, -1, NULL, 0, 2 },
+	{ "wizsit", NULL, 32, -1, NULL, 0, 2 },
+	{ "wizatk", NULL, 32, -1, NULL, 0, 2 },
+	{ "wizdth", NULL, 80, -1, NULL, 0, 2 },
+	{ "wizact", NULL, 20, -1, NULL, 0, 2 },
+	{ "wizpai", NULL, 32, -1, NULL, 0, 2 },
+	{ "minsit", NULL, 32, -1, NULL, 0, 2 },
+	{ "minat1", NULL, 32, -1, NULL, 0, 2 },
+	{ "minat2", NULL, 32, -1, NULL, 0, 2 },
+	{ "minat3", NULL, 32, -1, NULL, 0, 2 },
+	{ "mindth", NULL, 80, -1, NULL, 0, 2 },
+	{ "minact", NULL, 20, -1, NULL, 0, 2 },
+	{ "minpai", NULL, 32, -1, NULL, 0, 2 },
+	{ "hedsit", NULL, 32, -1, NULL, 0, 2 },
+	{ "hedat1", NULL, 32, -1, NULL, 0, 2 },
+	{ "hedat2", NULL, 32, -1, NULL, 0, 2 },
+	{ "hedat3", NULL, 32, -1, NULL, 0, 2 },
+	{ "heddth", NULL, 80, -1, NULL, 0, 2 },
+	{ "hedact", NULL, 20, -1, NULL, 0, 2 },
+	{ "hedpai", NULL, 32, -1, NULL, 0, 2 },
+	{ "sorzap", NULL, 32, -1, NULL, 0, 2 },
+	{ "sorrise", NULL, 32, -1, NULL, 0, 2 },
+	{ "sorsit", NULL, 200, -1, NULL, 0, 2 },
+	{ "soratk", NULL, 32, -1, NULL, 0, 2 },
+	{ "soract", NULL, 200, -1, NULL, 0, 2 },
+	{ "sorpai", NULL, 200, -1, NULL, 0, 2 },
+	{ "sordsph", NULL, 200, -1, NULL, 0, 2 },
+	{ "sordexp", NULL, 200, -1, NULL, 0, 2 },
+	{ "sordbon", NULL, 200, -1, NULL, 0, 2 },
+	{ "-sbtsit", &S_sfx[sfx_bstsit], 32, -1, NULL, 0, 2 },
+	{ "-sbtatk", &S_sfx[sfx_bstatk], 32, -1, NULL, 0, 2 },
+	{ "sbtdth", NULL, 80, -1, NULL, 0, 2 },
+	{ "sbtact", NULL, 20, -1, NULL, 0, 2 },
+	{ "sbtpai", NULL, 32, -1, NULL, 0, 2 },
+	{ "plroof", NULL, 32, -1, NULL, 0, 2 },
+	{ "plrpai", NULL, 32, -1, NULL, 0, 2 },
+ 	{ "plrdth", NULL, 80, -1, NULL, 0, 2 },
+	{ "gibdth", NULL, 100, -1, NULL, 0, 2 },
+	{ "plrwdth", NULL, 80, -1, NULL, 0, 2 },
+	{ "plrcdth", NULL, 100, -1, NULL, 0, 2 },
+	{ "itemup", NULL, 32, -1, NULL, 0, 2 },
+	{ "wpnup", NULL, 32, -1, NULL, 0, 2 },
+	{ "telept", NULL, 50, -1, NULL, 0, 2 },
+	{ "doropn", NULL, 40, -1, NULL, 0, 2 },
+	{ "dorcls", NULL, 40, -1, NULL, 0, 2 },
+	{ "dormov", NULL, 40, -1, NULL, 0, 2 },
+	{ "artiup", NULL, 32, -1, NULL, 0, 2 },
+	{ "switch", NULL, 40, -1, NULL, 0, 2 },
+	{ "pstart", NULL, 40, -1, NULL, 0, 2 },
+	{ "pstop", NULL, 40, -1, NULL, 0, 2 },
+	{ "stnmov", NULL, 40, -1, NULL, 0, 2 },
+	{ "chicpai", NULL, 32, -1, NULL, 0, 2 },
+	{ "chicatk", NULL, 32, -1, NULL, 0, 2 },
+	{ "chicdth", NULL, 40, -1, NULL, 0, 2 },
+	{ "chicact", NULL, 32, -1, NULL, 0, 2 },
+	{ "chicpk1", NULL, 32, -1, NULL, 0, 2 },
+	{ "chicpk2", NULL, 32, -1, NULL, 0, 2 },
+	{ "chicpk3", NULL, 32, -1, NULL, 0, 2 },
+	{ "keyup", NULL, 50, -1, NULL, 0, 2 },
+	{ "ripslop", NULL, 16, -1, NULL, 0, 2 },
+	{ "newpod", NULL, 16, -1, NULL, 0, -1 },
+	{ "podexp", NULL, 40, -1, NULL, 0, -1 },
+	{ "bounce", NULL, 16, -1, NULL, 0, 2 },
+	{ "-volsht", &S_sfx[sfx_bstatk], 16, -1, NULL, 0, 2 },
+	{ "-volhit", &S_sfx[sfx_lobhit], 16, -1, NULL, 0, 2 },
+	{ "burn", NULL, 10, -1, NULL, 0, 2 },
+	{ "splash", NULL, 10, -1, NULL, 0, 1 },
+	{ "gloop", NULL, 10, -1, NULL, 0, 2 },
+	{ "respawn", NULL, 10, -1, NULL, 0, 1 },
+	{ "blssht", NULL, 32, -1, NULL, 0, 2 },
+	{ "blshit", NULL, 32, -1, NULL, 0, 2 },
+	{ "chat", NULL, 100, -1, NULL, 0, 1 },
+	{ "artiuse", NULL, 32, -1, NULL, 0, 1 },
+	{ "gfrag", NULL, 100, -1, NULL, 0, 1 },
+	{ "waterfl", NULL, 16, -1, NULL, 0, 2 },
+
+	// Monophonic sounds
+
+	{ "wind", NULL, 16, -1, NULL, 0, 1 },
+	{ "amb1", NULL, 1, -1, NULL, 0, 1 },
+	{ "amb2", NULL, 1, -1, NULL, 0, 1 },
+	{ "amb3", NULL, 1, -1, NULL, 0, 1 },
+	{ "amb4", NULL, 1, -1, NULL, 0, 1 },
+	{ "amb5", NULL, 1, -1, NULL, 0, 1 },
+	{ "amb6", NULL, 1, -1, NULL, 0, 1 },
+	{ "amb7", NULL, 1, -1, NULL, 0, 1 },
+	{ "amb8", NULL, 1, -1, NULL, 0, 1 },
+	{ "amb9", NULL, 1, -1, NULL, 0, 1 },
+	{ "amb10", NULL, 1, -1, NULL, 0, 1 },
+	{ "amb11", NULL, 1, -1, NULL, 0, 0 }
+};
--- /dev/null
+++ b/src/heretic/sounds.h
@@ -1,0 +1,269 @@
+
+// sounds.h
+
+#ifndef __SOUNDSH__
+#define __SOUNDSH__
+
+#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;
+
+typedef struct
+{
+	char name[8];
+	int p1;
+} 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;
+
+typedef struct
+{
+	mobj_t *mo;
+	long sound_id;
+	long handle;
+	long pitch;
+	int priority;
+} channel_t;
+
+typedef struct
+{
+	long 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;
+
+#endif
--- /dev/null
+++ b/src/heretic/soundst.h
@@ -1,0 +1,23 @@
+
+// soundst.h
+
+#ifndef __SOUNDSTH__
+#define __SOUNDSTH__
+
+extern int snd_MaxVolume;
+extern int snd_MusicVolume;
+
+void S_Start(void);
+void S_StartSound(mobj_t *origin, int sound_id);
+void S_StartSoundAtVolume(mobj_t *origin, int sound_id, int volume);
+void S_StopSound(mobj_t *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
--- /dev/null
+++ b/src/heretic/tables.c
@@ -1,0 +1,2062 @@
+#include "DoomDef.h"
+
+int finetangent[4096] = {
+-170910304,-56965752,-34178904,-24413316,-18988036,-15535599,-13145455,-11392683,
+-10052327,-8994149,-8137527,-7429880,-6835455,-6329090,-5892567,-5512368,
+-5178251,-4882318,-4618375,-4381502,-4167737,-3973855,-3797206,-3635590,
+-3487165,-3350381,-3223918,-3106651,-2997613,-2895966,-2800983,-2712030,
+-2628549,-2550052,-2476104,-2406322,-2340362,-2277919,-2218719,-2162516,
+-2109087,-2058233,-2009771,-1963536,-1919378,-1877161,-1836758,-1798063,
+-1760956,-1725348,-1691149,-1658278,-1626658,-1596220,-1566898,-1538632,
+-1511367,-1485049,-1459630,-1435065,-1411312,-1388330,-1366084,-1344537,
+-1323658,-1303416,-1283783,-1264730,-1246234,-1228269,-1210813,-1193846,
+-1177345,-1161294,-1145673,-1130465,-1115654,-1101225,-1087164,-1073455,
+-1060087,-1047046,-1034322,-1021901,-1009774,-997931,-986361,-975054,
+-964003,-953199,-942633,-932298,-922186,-912289,-902602,-893117,
+-883829,-874730,-865817,-857081,-848520,-840127,-831898,-823827,
+-815910,-808143,-800521,-793041,-785699,-778490,-771411,-764460,
+-757631,-750922,-744331,-737853,-731486,-725227,-719074,-713023,
+-707072,-701219,-695462,-689797,-684223,-678737,-673338,-668024,
+-662792,-657640,-652568,-647572,-642651,-637803,-633028,-628323,
+-623686,-619117,-614613,-610174,-605798,-601483,-597229,-593033,
+-588896,-584815,-580789,-576818,-572901,-569035,-565221,-561456,
+-557741,-554074,-550455,-546881,-543354,-539870,-536431,-533034,
+-529680,-526366,-523094,-519861,-516667,-513512,-510394,-507313,
+-504269,-501261,-498287,-495348,-492443,-489571,-486732,-483925,
+-481150,-478406,-475692,-473009,-470355,-467730,-465133,-462565,
+-460024,-457511,-455024,-452564,-450129,-447720,-445337,-442978,
+-440643,-438332,-436045,-433781,-431540,-429321,-427125,-424951,
+-422798,-420666,-418555,-416465,-414395,-412344,-410314,-408303,
+-406311,-404338,-402384,-400448,-398530,-396630,-394747,-392882,
+-391034,-389202,-387387,-385589,-383807,-382040,-380290,-378555,
+-376835,-375130,-373440,-371765,-370105,-368459,-366826,-365208,
+-363604,-362013,-360436,-358872,-357321,-355783,-354257,-352744,
+-351244,-349756,-348280,-346816,-345364,-343924,-342495,-341078,
+-339671,-338276,-336892,-335519,-334157,-332805,-331464,-330133,
+-328812,-327502,-326201,-324910,-323629,-322358,-321097,-319844,
+-318601,-317368,-316143,-314928,-313721,-312524,-311335,-310154,
+-308983,-307819,-306664,-305517,-304379,-303248,-302126,-301011,
+-299904,-298805,-297714,-296630,-295554,-294485,-293423,-292369,
+-291322,-290282,-289249,-288223,-287204,-286192,-285186,-284188,
+-283195,-282210,-281231,-280258,-279292,-278332,-277378,-276430,
+-275489,-274553,-273624,-272700,-271782,-270871,-269965,-269064,
+-268169,-267280,-266397,-265519,-264646,-263779,-262917,-262060,
+-261209,-260363,-259522,-258686,-257855,-257029,-256208,-255392,
+-254581,-253774,-252973,-252176,-251384,-250596,-249813,-249035,
+-248261,-247492,-246727,-245966,-245210,-244458,-243711,-242967,
+-242228,-241493,-240763,-240036,-239314,-238595,-237881,-237170,
+-236463,-235761,-235062,-234367,-233676,-232988,-232304,-231624,
+-230948,-230275,-229606,-228941,-228279,-227621,-226966,-226314,
+-225666,-225022,-224381,-223743,-223108,-222477,-221849,-221225,
+-220603,-219985,-219370,-218758,-218149,-217544,-216941,-216341,
+-215745,-215151,-214561,-213973,-213389,-212807,-212228,-211652,
+-211079,-210509,-209941,-209376,-208815,-208255,-207699,-207145,
+-206594,-206045,-205500,-204956,-204416,-203878,-203342,-202809,
+-202279,-201751,-201226,-200703,-200182,-199664,-199149,-198636,
+-198125,-197616,-197110,-196606,-196105,-195606,-195109,-194614,
+-194122,-193631,-193143,-192658,-192174,-191693,-191213,-190736,
+-190261,-189789,-189318,-188849,-188382,-187918,-187455,-186995,
+-186536,-186080,-185625,-185173,-184722,-184274,-183827,-183382,
+-182939,-182498,-182059,-181622,-181186,-180753,-180321,-179891,
+-179463,-179037,-178612,-178190,-177769,-177349,-176932,-176516,
+-176102,-175690,-175279,-174870,-174463,-174057,-173653,-173251,
+-172850,-172451,-172053,-171657,-171263,-170870,-170479,-170089,
+-169701,-169315,-168930,-168546,-168164,-167784,-167405,-167027,
+-166651,-166277,-165904,-165532,-165162,-164793,-164426,-164060,
+-163695,-163332,-162970,-162610,-162251,-161893,-161537,-161182,
+-160828,-160476,-160125,-159775,-159427,-159079,-158734,-158389,
+-158046,-157704,-157363,-157024,-156686,-156349,-156013,-155678,
+-155345,-155013,-154682,-154352,-154024,-153697,-153370,-153045,
+-152722,-152399,-152077,-151757,-151438,-151120,-150803,-150487,
+-150172,-149859,-149546,-149235,-148924,-148615,-148307,-148000,
+-147693,-147388,-147084,-146782,-146480,-146179,-145879,-145580,
+-145282,-144986,-144690,-144395,-144101,-143808,-143517,-143226,
+-142936,-142647,-142359,-142072,-141786,-141501,-141217,-140934,
+-140651,-140370,-140090,-139810,-139532,-139254,-138977,-138701,
+-138426,-138152,-137879,-137607,-137335,-137065,-136795,-136526,
+-136258,-135991,-135725,-135459,-135195,-134931,-134668,-134406,
+-134145,-133884,-133625,-133366,-133108,-132851,-132594,-132339,
+-132084,-131830,-131576,-131324,-131072,-130821,-130571,-130322,
+-130073,-129825,-129578,-129332,-129086,-128841,-128597,-128353,
+-128111,-127869,-127627,-127387,-127147,-126908,-126669,-126432,
+-126195,-125959,-125723,-125488,-125254,-125020,-124787,-124555,
+-124324,-124093,-123863,-123633,-123404,-123176,-122949,-122722,
+-122496,-122270,-122045,-121821,-121597,-121374,-121152,-120930,
+-120709,-120489,-120269,-120050,-119831,-119613,-119396,-119179,
+-118963,-118747,-118532,-118318,-118104,-117891,-117678,-117466,
+-117254,-117044,-116833,-116623,-116414,-116206,-115998,-115790,
+-115583,-115377,-115171,-114966,-114761,-114557,-114354,-114151,
+-113948,-113746,-113545,-113344,-113143,-112944,-112744,-112546,
+-112347,-112150,-111952,-111756,-111560,-111364,-111169,-110974,
+-110780,-110586,-110393,-110200,-110008,-109817,-109626,-109435,
+-109245,-109055,-108866,-108677,-108489,-108301,-108114,-107927,
+-107741,-107555,-107369,-107184,-107000,-106816,-106632,-106449,
+-106266,-106084,-105902,-105721,-105540,-105360,-105180,-105000,
+-104821,-104643,-104465,-104287,-104109,-103933,-103756,-103580,
+-103404,-103229,-103054,-102880,-102706,-102533,-102360,-102187,
+-102015,-101843,-101671,-101500,-101330,-101159,-100990,-100820,
+-100651,-100482,-100314,-100146,-99979,-99812,-99645,-99479,
+-99313,-99148,-98982,-98818,-98653,-98489,-98326,-98163,
+-98000,-97837,-97675,-97513,-97352,-97191,-97030,-96870,
+-96710,-96551,-96391,-96233,-96074,-95916,-95758,-95601,
+-95444,-95287,-95131,-94975,-94819,-94664,-94509,-94354,
+-94200,-94046,-93892,-93739,-93586,-93434,-93281,-93129,
+-92978,-92826,-92675,-92525,-92375,-92225,-92075,-91926,
+-91777,-91628,-91480,-91332,-91184,-91036,-90889,-90742,
+-90596,-90450,-90304,-90158,-90013,-89868,-89724,-89579,
+-89435,-89292,-89148,-89005,-88862,-88720,-88577,-88435,
+-88294,-88152,-88011,-87871,-87730,-87590,-87450,-87310,
+-87171,-87032,-86893,-86755,-86616,-86479,-86341,-86204,
+-86066,-85930,-85793,-85657,-85521,-85385,-85250,-85114,
+-84980,-84845,-84710,-84576,-84443,-84309,-84176,-84043,
+-83910,-83777,-83645,-83513,-83381,-83250,-83118,-82987,
+-82857,-82726,-82596,-82466,-82336,-82207,-82078,-81949,
+-81820,-81691,-81563,-81435,-81307,-81180,-81053,-80925,
+-80799,-80672,-80546,-80420,-80294,-80168,-80043,-79918,
+-79793,-79668,-79544,-79420,-79296,-79172,-79048,-78925,
+-78802,-78679,-78557,-78434,-78312,-78190,-78068,-77947,
+-77826,-77705,-77584,-77463,-77343,-77223,-77103,-76983,
+-76864,-76744,-76625,-76506,-76388,-76269,-76151,-76033,
+-75915,-75797,-75680,-75563,-75446,-75329,-75213,-75096,
+-74980,-74864,-74748,-74633,-74517,-74402,-74287,-74172,
+-74058,-73944,-73829,-73715,-73602,-73488,-73375,-73262,
+-73149,-73036,-72923,-72811,-72699,-72587,-72475,-72363,
+-72252,-72140,-72029,-71918,-71808,-71697,-71587,-71477,
+-71367,-71257,-71147,-71038,-70929,-70820,-70711,-70602,
+-70494,-70385,-70277,-70169,-70061,-69954,-69846,-69739,
+-69632,-69525,-69418,-69312,-69205,-69099,-68993,-68887,
+-68781,-68676,-68570,-68465,-68360,-68255,-68151,-68046,
+-67942,-67837,-67733,-67629,-67526,-67422,-67319,-67216,
+-67113,-67010,-66907,-66804,-66702,-66600,-66498,-66396,
+-66294,-66192,-66091,-65989,-65888,-65787,-65686,-65586,
+-65485,-65385,-65285,-65185,-65085,-64985,-64885,-64786,
+-64687,-64587,-64488,-64389,-64291,-64192,-64094,-63996,
+-63897,-63799,-63702,-63604,-63506,-63409,-63312,-63215,
+-63118,-63021,-62924,-62828,-62731,-62635,-62539,-62443,
+-62347,-62251,-62156,-62060,-61965,-61870,-61775,-61680,
+-61585,-61491,-61396,-61302,-61208,-61114,-61020,-60926,
+-60833,-60739,-60646,-60552,-60459,-60366,-60273,-60181,
+-60088,-59996,-59903,-59811,-59719,-59627,-59535,-59444,
+-59352,-59261,-59169,-59078,-58987,-58896,-58805,-58715,
+-58624,-58534,-58443,-58353,-58263,-58173,-58083,-57994,
+-57904,-57815,-57725,-57636,-57547,-57458,-57369,-57281,
+-57192,-57104,-57015,-56927,-56839,-56751,-56663,-56575,
+-56487,-56400,-56312,-56225,-56138,-56051,-55964,-55877,
+-55790,-55704,-55617,-55531,-55444,-55358,-55272,-55186,
+-55100,-55015,-54929,-54843,-54758,-54673,-54587,-54502,
+-54417,-54333,-54248,-54163,-54079,-53994,-53910,-53826,
+-53741,-53657,-53574,-53490,-53406,-53322,-53239,-53156,
+-53072,-52989,-52906,-52823,-52740,-52657,-52575,-52492,
+-52410,-52327,-52245,-52163,-52081,-51999,-51917,-51835,
+-51754,-51672,-51591,-51509,-51428,-51347,-51266,-51185,
+-51104,-51023,-50942,-50862,-50781,-50701,-50621,-50540,
+-50460,-50380,-50300,-50221,-50141,-50061,-49982,-49902,
+-49823,-49744,-49664,-49585,-49506,-49427,-49349,-49270,
+-49191,-49113,-49034,-48956,-48878,-48799,-48721,-48643,
+-48565,-48488,-48410,-48332,-48255,-48177,-48100,-48022,
+-47945,-47868,-47791,-47714,-47637,-47560,-47484,-47407,
+-47331,-47254,-47178,-47102,-47025,-46949,-46873,-46797,
+-46721,-46646,-46570,-46494,-46419,-46343,-46268,-46193,
+-46118,-46042,-45967,-45892,-45818,-45743,-45668,-45593,
+-45519,-45444,-45370,-45296,-45221,-45147,-45073,-44999,
+-44925,-44851,-44778,-44704,-44630,-44557,-44483,-44410,
+-44337,-44263,-44190,-44117,-44044,-43971,-43898,-43826,
+-43753,-43680,-43608,-43535,-43463,-43390,-43318,-43246,
+-43174,-43102,-43030,-42958,-42886,-42814,-42743,-42671,
+-42600,-42528,-42457,-42385,-42314,-42243,-42172,-42101,
+-42030,-41959,-41888,-41817,-41747,-41676,-41605,-41535,
+-41465,-41394,-41324,-41254,-41184,-41113,-41043,-40973,
+-40904,-40834,-40764,-40694,-40625,-40555,-40486,-40416,
+-40347,-40278,-40208,-40139,-40070,-40001,-39932,-39863,
+-39794,-39726,-39657,-39588,-39520,-39451,-39383,-39314,
+-39246,-39178,-39110,-39042,-38973,-38905,-38837,-38770,
+-38702,-38634,-38566,-38499,-38431,-38364,-38296,-38229,
+-38161,-38094,-38027,-37960,-37893,-37826,-37759,-37692,
+-37625,-37558,-37491,-37425,-37358,-37291,-37225,-37158,
+-37092,-37026,-36959,-36893,-36827,-36761,-36695,-36629,
+-36563,-36497,-36431,-36365,-36300,-36234,-36168,-36103,
+-36037,-35972,-35907,-35841,-35776,-35711,-35646,-35580,
+-35515,-35450,-35385,-35321,-35256,-35191,-35126,-35062,
+-34997,-34932,-34868,-34803,-34739,-34675,-34610,-34546,
+-34482,-34418,-34354,-34289,-34225,-34162,-34098,-34034,
+-33970,-33906,-33843,-33779,-33715,-33652,-33588,-33525,
+-33461,-33398,-33335,-33272,-33208,-33145,-33082,-33019,
+-32956,-32893,-32830,-32767,-32705,-32642,-32579,-32516,
+-32454,-32391,-32329,-32266,-32204,-32141,-32079,-32017,
+-31955,-31892,-31830,-31768,-31706,-31644,-31582,-31520,
+-31458,-31396,-31335,-31273,-31211,-31150,-31088,-31026,
+-30965,-30904,-30842,-30781,-30719,-30658,-30597,-30536,
+-30474,-30413,-30352,-30291,-30230,-30169,-30108,-30048,
+-29987,-29926,-29865,-29805,-29744,-29683,-29623,-29562,
+-29502,-29441,-29381,-29321,-29260,-29200,-29140,-29080,
+-29020,-28959,-28899,-28839,-28779,-28719,-28660,-28600,
+-28540,-28480,-28420,-28361,-28301,-28241,-28182,-28122,
+-28063,-28003,-27944,-27884,-27825,-27766,-27707,-27647,
+-27588,-27529,-27470,-27411,-27352,-27293,-27234,-27175,
+-27116,-27057,-26998,-26940,-26881,-26822,-26763,-26705,
+-26646,-26588,-26529,-26471,-26412,-26354,-26295,-26237,
+-26179,-26120,-26062,-26004,-25946,-25888,-25830,-25772,
+-25714,-25656,-25598,-25540,-25482,-25424,-25366,-25308,
+-25251,-25193,-25135,-25078,-25020,-24962,-24905,-24847,
+-24790,-24732,-24675,-24618,-24560,-24503,-24446,-24389,
+-24331,-24274,-24217,-24160,-24103,-24046,-23989,-23932,
+-23875,-23818,-23761,-23704,-23647,-23591,-23534,-23477,
+-23420,-23364,-23307,-23250,-23194,-23137,-23081,-23024,
+-22968,-22911,-22855,-22799,-22742,-22686,-22630,-22573,
+-22517,-22461,-22405,-22349,-22293,-22237,-22181,-22125,
+-22069,-22013,-21957,-21901,-21845,-21789,-21733,-21678,
+-21622,-21566,-21510,-21455,-21399,-21343,-21288,-21232,
+-21177,-21121,-21066,-21010,-20955,-20900,-20844,-20789,
+-20734,-20678,-20623,-20568,-20513,-20457,-20402,-20347,
+-20292,-20237,-20182,-20127,-20072,-20017,-19962,-19907,
+-19852,-19797,-19742,-19688,-19633,-19578,-19523,-19469,
+-19414,-19359,-19305,-19250,-19195,-19141,-19086,-19032,
+-18977,-18923,-18868,-18814,-18760,-18705,-18651,-18597,
+-18542,-18488,-18434,-18380,-18325,-18271,-18217,-18163,
+-18109,-18055,-18001,-17946,-17892,-17838,-17784,-17731,
+-17677,-17623,-17569,-17515,-17461,-17407,-17353,-17300,
+-17246,-17192,-17138,-17085,-17031,-16977,-16924,-16870,
+-16817,-16763,-16710,-16656,-16603,-16549,-16496,-16442,
+-16389,-16335,-16282,-16229,-16175,-16122,-16069,-16015,
+-15962,-15909,-15856,-15802,-15749,-15696,-15643,-15590,
+-15537,-15484,-15431,-15378,-15325,-15272,-15219,-15166,
+-15113,-15060,-15007,-14954,-14901,-14848,-14795,-14743,
+-14690,-14637,-14584,-14531,-14479,-14426,-14373,-14321,
+-14268,-14215,-14163,-14110,-14057,-14005,-13952,-13900,
+-13847,-13795,-13742,-13690,-13637,-13585,-13533,-13480,
+-13428,-13375,-13323,-13271,-13218,-13166,-13114,-13062,
+-13009,-12957,-12905,-12853,-12800,-12748,-12696,-12644,
+-12592,-12540,-12488,-12436,-12383,-12331,-12279,-12227,
+-12175,-12123,-12071,-12019,-11967,-11916,-11864,-11812,
+-11760,-11708,-11656,-11604,-11552,-11501,-11449,-11397,
+-11345,-11293,-11242,-11190,-11138,-11086,-11035,-10983,
+-10931,-10880,-10828,-10777,-10725,-10673,-10622,-10570,
+-10519,-10467,-10415,-10364,-10312,-10261,-10209,-10158,
+-10106,-10055,-10004,-9952,-9901,-9849,-9798,-9747,
+-9695,-9644,-9592,-9541,-9490,-9438,-9387,-9336,
+-9285,-9233,-9182,-9131,-9080,-9028,-8977,-8926,
+-8875,-8824,-8772,-8721,-8670,-8619,-8568,-8517,
+-8466,-8414,-8363,-8312,-8261,-8210,-8159,-8108,
+-8057,-8006,-7955,-7904,-7853,-7802,-7751,-7700,
+-7649,-7598,-7547,-7496,-7445,-7395,-7344,-7293,
+-7242,-7191,-7140,-7089,-7038,-6988,-6937,-6886,
+-6835,-6784,-6733,-6683,-6632,-6581,-6530,-6480,
+-6429,-6378,-6327,-6277,-6226,-6175,-6124,-6074,
+-6023,-5972,-5922,-5871,-5820,-5770,-5719,-5668,
+-5618,-5567,-5517,-5466,-5415,-5365,-5314,-5264,
+-5213,-5162,-5112,-5061,-5011,-4960,-4910,-4859,
+-4808,-4758,-4707,-4657,-4606,-4556,-4505,-4455,
+-4404,-4354,-4303,-4253,-4202,-4152,-4101,-4051,
+-4001,-3950,-3900,-3849,-3799,-3748,-3698,-3648,
+-3597,-3547,-3496,-3446,-3395,-3345,-3295,-3244,
+-3194,-3144,-3093,-3043,-2992,-2942,-2892,-2841,
+-2791,-2741,-2690,-2640,-2590,-2539,-2489,-2439,
+-2388,-2338,-2288,-2237,-2187,-2137,-2086,-2036,
+-1986,-1935,-1885,-1835,-1784,-1734,-1684,-1633,
+-1583,-1533,-1483,-1432,-1382,-1332,-1281,-1231,
+-1181,-1131,-1080,-1030,-980,-929,-879,-829,
+-779,-728,-678,-628,-578,-527,-477,-427,
+-376,-326,-276,-226,-175,-125,-75,-25,
+25,75,125,175,226,276,326,376,
+427,477,527,578,628,678,728,779,
+829,879,929,980,1030,1080,1131,1181,
+1231,1281,1332,1382,1432,1483,1533,1583,
+1633,1684,1734,1784,1835,1885,1935,1986,
+2036,2086,2137,2187,2237,2288,2338,2388,
+2439,2489,2539,2590,2640,2690,2741,2791,
+2841,2892,2942,2992,3043,3093,3144,3194,
+3244,3295,3345,3395,3446,3496,3547,3597,
+3648,3698,3748,3799,3849,3900,3950,4001,
+4051,4101,4152,4202,4253,4303,4354,4404,
+4455,4505,4556,4606,4657,4707,4758,4808,
+4859,4910,4960,5011,5061,5112,5162,5213,
+5264,5314,5365,5415,5466,5517,5567,5618,
+5668,5719,5770,5820,5871,5922,5972,6023,
+6074,6124,6175,6226,6277,6327,6378,6429,
+6480,6530,6581,6632,6683,6733,6784,6835,
+6886,6937,6988,7038,7089,7140,7191,7242,
+7293,7344,7395,7445,7496,7547,7598,7649,
+7700,7751,7802,7853,7904,7955,8006,8057,
+8108,8159,8210,8261,8312,8363,8414,8466,
+8517,8568,8619,8670,8721,8772,8824,8875,
+8926,8977,9028,9080,9131,9182,9233,9285,
+9336,9387,9438,9490,9541,9592,9644,9695,
+9747,9798,9849,9901,9952,10004,10055,10106,
+10158,10209,10261,10312,10364,10415,10467,10519,
+10570,10622,10673,10725,10777,10828,10880,10931,
+10983,11035,11086,11138,11190,11242,11293,11345,
+11397,11449,11501,11552,11604,11656,11708,11760,
+11812,11864,11916,11967,12019,12071,12123,12175,
+12227,12279,12331,12383,12436,12488,12540,12592,
+12644,12696,12748,12800,12853,12905,12957,13009,
+13062,13114,13166,13218,13271,13323,13375,13428,
+13480,13533,13585,13637,13690,13742,13795,13847,
+13900,13952,14005,14057,14110,14163,14215,14268,
+14321,14373,14426,14479,14531,14584,14637,14690,
+14743,14795,14848,14901,14954,15007,15060,15113,
+15166,15219,15272,15325,15378,15431,15484,15537,
+15590,15643,15696,15749,15802,15856,15909,15962,
+16015,16069,16122,16175,16229,16282,16335,16389,
+16442,16496,16549,16603,16656,16710,16763,16817,
+16870,16924,16977,17031,17085,17138,17192,17246,
+17300,17353,17407,17461,17515,17569,17623,17677,
+17731,17784,17838,17892,17946,18001,18055,18109,
+18163,18217,18271,18325,18380,18434,18488,18542,
+18597,18651,18705,18760,18814,18868,18923,18977,
+19032,19086,19141,19195,19250,19305,19359,19414,
+19469,19523,19578,19633,19688,19742,19797,19852,
+19907,19962,20017,20072,20127,20182,20237,20292,
+20347,20402,20457,20513,20568,20623,20678,20734,
+20789,20844,20900,20955,21010,21066,21121,21177,
+21232,21288,21343,21399,21455,21510,21566,21622,
+21678,21733,21789,21845,21901,21957,22013,22069,
+22125,22181,22237,22293,22349,22405,22461,22517,
+22573,22630,22686,22742,22799,22855,22911,22968,
+23024,23081,23137,23194,23250,23307,23364,23420,
+23477,23534,23591,23647,23704,23761,23818,23875,
+23932,23989,24046,24103,24160,24217,24274,24331,
+24389,24446,24503,24560,24618,24675,24732,24790,
+24847,24905,24962,25020,25078,25135,25193,25251,
+25308,25366,25424,25482,25540,25598,25656,25714,
+25772,25830,25888,25946,26004,26062,26120,26179,
+26237,26295,26354,26412,26471,26529,26588,26646,
+26705,26763,26822,26881,26940,26998,27057,27116,
+27175,27234,27293,27352,27411,27470,27529,27588,
+27647,27707,27766,27825,27884,27944,28003,28063,
+28122,28182,28241,28301,28361,28420,28480,28540,
+28600,28660,28719,28779,28839,28899,28959,29020,
+29080,29140,29200,29260,29321,29381,29441,29502,
+29562,29623,29683,29744,29805,29865,29926,29987,
+30048,30108,30169,30230,30291,30352,30413,30474,
+30536,30597,30658,30719,30781,30842,30904,30965,
+31026,31088,31150,31211,31273,31335,31396,31458,
+31520,31582,31644,31706,31768,31830,31892,31955,
+32017,32079,32141,32204,32266,32329,32391,32454,
+32516,32579,32642,32705,32767,32830,32893,32956,
+33019,33082,33145,33208,33272,33335,33398,33461,
+33525,33588,33652,33715,33779,33843,33906,33970,
+34034,34098,34162,34225,34289,34354,34418,34482,
+34546,34610,34675,34739,34803,34868,34932,34997,
+35062,35126,35191,35256,35321,35385,35450,35515,
+35580,35646,35711,35776,35841,35907,35972,36037,
+36103,36168,36234,36300,36365,36431,36497,36563,
+36629,36695,36761,36827,36893,36959,37026,37092,
+37158,37225,37291,37358,37425,37491,37558,37625,
+37692,37759,37826,37893,37960,38027,38094,38161,
+38229,38296,38364,38431,38499,38566,38634,38702,
+38770,38837,38905,38973,39042,39110,39178,39246,
+39314,39383,39451,39520,39588,39657,39726,39794,
+39863,39932,40001,40070,40139,40208,40278,40347,
+40416,40486,40555,40625,40694,40764,40834,40904,
+40973,41043,41113,41184,41254,41324,41394,41465,
+41535,41605,41676,41747,41817,41888,41959,42030,
+42101,42172,42243,42314,42385,42457,42528,42600,
+42671,42743,42814,42886,42958,43030,43102,43174,
+43246,43318,43390,43463,43535,43608,43680,43753,
+43826,43898,43971,44044,44117,44190,44263,44337,
+44410,44483,44557,44630,44704,44778,44851,44925,
+44999,45073,45147,45221,45296,45370,45444,45519,
+45593,45668,45743,45818,45892,45967,46042,46118,
+46193,46268,46343,46419,46494,46570,46646,46721,
+46797,46873,46949,47025,47102,47178,47254,47331,
+47407,47484,47560,47637,47714,47791,47868,47945,
+48022,48100,48177,48255,48332,48410,48488,48565,
+48643,48721,48799,48878,48956,49034,49113,49191,
+49270,49349,49427,49506,49585,49664,49744,49823,
+49902,49982,50061,50141,50221,50300,50380,50460,
+50540,50621,50701,50781,50862,50942,51023,51104,
+51185,51266,51347,51428,51509,51591,51672,51754,
+51835,51917,51999,52081,52163,52245,52327,52410,
+52492,52575,52657,52740,52823,52906,52989,53072,
+53156,53239,53322,53406,53490,53574,53657,53741,
+53826,53910,53994,54079,54163,54248,54333,54417,
+54502,54587,54673,54758,54843,54929,55015,55100,
+55186,55272,55358,55444,55531,55617,55704,55790,
+55877,55964,56051,56138,56225,56312,56400,56487,
+56575,56663,56751,56839,56927,57015,57104,57192,
+57281,57369,57458,57547,57636,57725,57815,57904,
+57994,58083,58173,58263,58353,58443,58534,58624,
+58715,58805,58896,58987,59078,59169,59261,59352,
+59444,59535,59627,59719,59811,59903,59996,60088,
+60181,60273,60366,60459,60552,60646,60739,60833,
+60926,61020,61114,61208,61302,61396,61491,61585,
+61680,61775,61870,61965,62060,62156,62251,62347,
+62443,62539,62635,62731,62828,62924,63021,63118,
+63215,63312,63409,63506,63604,63702,63799,63897,
+63996,64094,64192,64291,64389,64488,64587,64687,
+64786,64885,64985,65085,65185,65285,65385,65485,
+65586,65686,65787,65888,65989,66091,66192,66294,
+66396,66498,66600,66702,66804,66907,67010,67113,
+67216,67319,67422,67526,67629,67733,67837,67942,
+68046,68151,68255,68360,68465,68570,68676,68781,
+68887,68993,69099,69205,69312,69418,69525,69632,
+69739,69846,69954,70061,70169,70277,70385,70494,
+70602,70711,70820,70929,71038,71147,71257,71367,
+71477,71587,71697,71808,71918,72029,72140,72252,
+72363,72475,72587,72699,72811,72923,73036,73149,
+73262,73375,73488,73602,73715,73829,73944,74058,
+74172,74287,74402,74517,74633,74748,74864,74980,
+75096,75213,75329,75446,75563,75680,75797,75915,
+76033,76151,76269,76388,76506,76625,76744,76864,
+76983,77103,77223,77343,77463,77584,77705,77826,
+77947,78068,78190,78312,78434,78557,78679,78802,
+78925,79048,79172,79296,79420,79544,79668,79793,
+79918,80043,80168,80294,80420,80546,80672,80799,
+80925,81053,81180,81307,81435,81563,81691,81820,
+81949,82078,82207,82336,82466,82596,82726,82857,
+82987,83118,83250,83381,83513,83645,83777,83910,
+84043,84176,84309,84443,84576,84710,84845,84980,
+85114,85250,85385,85521,85657,85793,85930,86066,
+86204,86341,86479,86616,86755,86893,87032,87171,
+87310,87450,87590,87730,87871,88011,88152,88294,
+88435,88577,88720,88862,89005,89148,89292,89435,
+89579,89724,89868,90013,90158,90304,90450,90596,
+90742,90889,91036,91184,91332,91480,91628,91777,
+91926,92075,92225,92375,92525,92675,92826,92978,
+93129,93281,93434,93586,93739,93892,94046,94200,
+94354,94509,94664,94819,94975,95131,95287,95444,
+95601,95758,95916,96074,96233,96391,96551,96710,
+96870,97030,97191,97352,97513,97675,97837,98000,
+98163,98326,98489,98653,98818,98982,99148,99313,
+99479,99645,99812,99979,100146,100314,100482,100651,
+100820,100990,101159,101330,101500,101671,101843,102015,
+102187,102360,102533,102706,102880,103054,103229,103404,
+103580,103756,103933,104109,104287,104465,104643,104821,
+105000,105180,105360,105540,105721,105902,106084,106266,
+106449,106632,106816,107000,107184,107369,107555,107741,
+107927,108114,108301,108489,108677,108866,109055,109245,
+109435,109626,109817,110008,110200,110393,110586,110780,
+110974,111169,111364,111560,111756,111952,112150,112347,
+112546,112744,112944,113143,113344,113545,113746,113948,
+114151,114354,114557,114761,114966,115171,115377,115583,
+115790,115998,116206,116414,116623,116833,117044,117254,
+117466,117678,117891,118104,118318,118532,118747,118963,
+119179,119396,119613,119831,120050,120269,120489,120709,
+120930,121152,121374,121597,121821,122045,122270,122496,
+122722,122949,123176,123404,123633,123863,124093,124324,
+124555,124787,125020,125254,125488,125723,125959,126195,
+126432,126669,126908,127147,127387,127627,127869,128111,
+128353,128597,128841,129086,129332,129578,129825,130073,
+130322,130571,130821,131072,131324,131576,131830,132084,
+132339,132594,132851,133108,133366,133625,133884,134145,
+134406,134668,134931,135195,135459,135725,135991,136258,
+136526,136795,137065,137335,137607,137879,138152,138426,
+138701,138977,139254,139532,139810,140090,140370,140651,
+140934,141217,141501,141786,142072,142359,142647,142936,
+143226,143517,143808,144101,144395,144690,144986,145282,
+145580,145879,146179,146480,146782,147084,147388,147693,
+148000,148307,148615,148924,149235,149546,149859,150172,
+150487,150803,151120,151438,151757,152077,152399,152722,
+153045,153370,153697,154024,154352,154682,155013,155345,
+155678,156013,156349,156686,157024,157363,157704,158046,
+158389,158734,159079,159427,159775,160125,160476,160828,
+161182,161537,161893,162251,162610,162970,163332,163695,
+164060,164426,164793,165162,165532,165904,166277,166651,
+167027,167405,167784,168164,168546,168930,169315,169701,
+170089,170479,170870,171263,171657,172053,172451,172850,
+173251,173653,174057,174463,174870,175279,175690,176102,
+176516,176932,177349,177769,178190,178612,179037,179463,
+179891,180321,180753,181186,181622,182059,182498,182939,
+183382,183827,184274,184722,185173,185625,186080,186536,
+186995,187455,187918,188382,188849,189318,189789,190261,
+190736,191213,191693,192174,192658,193143,193631,194122,
+194614,195109,195606,196105,196606,197110,197616,198125,
+198636,199149,199664,200182,200703,201226,201751,202279,
+202809,203342,203878,204416,204956,205500,206045,206594,
+207145,207699,208255,208815,209376,209941,210509,211079,
+211652,212228,212807,213389,213973,214561,215151,215745,
+216341,216941,217544,218149,218758,219370,219985,220603,
+221225,221849,222477,223108,223743,224381,225022,225666,
+226314,226966,227621,228279,228941,229606,230275,230948,
+231624,232304,232988,233676,234367,235062,235761,236463,
+237170,237881,238595,239314,240036,240763,241493,242228,
+242967,243711,244458,245210,245966,246727,247492,248261,
+249035,249813,250596,251384,252176,252973,253774,254581,
+255392,256208,257029,257855,258686,259522,260363,261209,
+262060,262917,263779,264646,265519,266397,267280,268169,
+269064,269965,270871,271782,272700,273624,274553,275489,
+276430,277378,278332,279292,280258,281231,282210,283195,
+284188,285186,286192,287204,288223,289249,290282,291322,
+292369,293423,294485,295554,296630,297714,298805,299904,
+301011,302126,303248,304379,305517,306664,307819,308983,
+310154,311335,312524,313721,314928,316143,317368,318601,
+319844,321097,322358,323629,324910,326201,327502,328812,
+330133,331464,332805,334157,335519,336892,338276,339671,
+341078,342495,343924,345364,346816,348280,349756,351244,
+352744,354257,355783,357321,358872,360436,362013,363604,
+365208,366826,368459,370105,371765,373440,375130,376835,
+378555,380290,382040,383807,385589,387387,389202,391034,
+392882,394747,396630,398530,400448,402384,404338,406311,
+408303,410314,412344,414395,416465,418555,420666,422798,
+424951,427125,429321,431540,433781,436045,438332,440643,
+442978,445337,447720,450129,452564,455024,457511,460024,
+462565,465133,467730,470355,473009,475692,478406,481150,
+483925,486732,489571,492443,495348,498287,501261,504269,
+507313,510394,513512,516667,519861,523094,526366,529680,
+533034,536431,539870,543354,546881,550455,554074,557741,
+561456,565221,569035,572901,576818,580789,584815,588896,
+593033,597229,601483,605798,610174,614613,619117,623686,
+628323,633028,637803,642651,647572,652568,657640,662792,
+668024,673338,678737,684223,689797,695462,701219,707072,
+713023,719074,725227,731486,737853,744331,750922,757631,
+764460,771411,778490,785699,793041,800521,808143,815910,
+823827,831898,840127,848520,857081,865817,874730,883829,
+893117,902602,912289,922186,932298,942633,953199,964003,
+975054,986361,997931,1009774,1021901,1034322,1047046,1060087,
+1073455,1087164,1101225,1115654,1130465,1145673,1161294,1177345,
+1193846,1210813,1228269,1246234,1264730,1283783,1303416,1323658,
+1344537,1366084,1388330,1411312,1435065,1459630,1485049,1511367,
+1538632,1566898,1596220,1626658,1658278,1691149,1725348,1760956,
+1798063,1836758,1877161,1919378,1963536,2009771,2058233,2109087,
+2162516,2218719,2277919,2340362,2406322,2476104,2550052,2628549,
+2712030,2800983,2895966,2997613,3106651,3223918,3350381,3487165,
+3635590,3797206,3973855,4167737,4381502,4618375,4882318,5178251,
+5512368,5892567,6329090,6835455,7429880,8137527,8994149,10052327,
+11392683,13145455,15535599,18988036,24413316,34178904,56965752,170910304
+
+};
+
+int finesine[10240] = {
+25,75,125,175,226,276,326,376,
+427,477,527,578,628,678,728,779,
+829,879,929,980,1030,1080,1130,1181,
+1231,1281,1331,1382,1432,1482,1532,1583,
+1633,1683,1733,1784,1834,1884,1934,1985,
+2035,2085,2135,2186,2236,2286,2336,2387,
+2437,2487,2537,2587,2638,2688,2738,2788,
+2839,2889,2939,2989,3039,3090,3140,3190,
+3240,3291,3341,3391,3441,3491,3541,3592,
+3642,3692,3742,3792,3843,3893,3943,3993,
+4043,4093,4144,4194,4244,4294,4344,4394,
+4445,4495,4545,4595,4645,4695,4745,4796,
+4846,4896,4946,4996,5046,5096,5146,5197,
+5247,5297,5347,5397,5447,5497,5547,5597,
+5647,5697,5748,5798,5848,5898,5948,5998,
+6048,6098,6148,6198,6248,6298,6348,6398,
+6448,6498,6548,6598,6648,6698,6748,6798,
+6848,6898,6948,6998,7048,7098,7148,7198,
+7248,7298,7348,7398,7448,7498,7548,7598,
+7648,7697,7747,7797,7847,7897,7947,7997,
+8047,8097,8147,8196,8246,8296,8346,8396,
+8446,8496,8545,8595,8645,8695,8745,8794,
+8844,8894,8944,8994,9043,9093,9143,9193,
+9243,9292,9342,9392,9442,9491,9541,9591,
+9640,9690,9740,9790,9839,9889,9939,9988,
+10038,10088,10137,10187,10237,10286,10336,10386,
+10435,10485,10534,10584,10634,10683,10733,10782,
+10832,10882,10931,10981,11030,11080,11129,11179,
+11228,11278,11327,11377,11426,11476,11525,11575,
+11624,11674,11723,11773,11822,11872,11921,11970,
+12020,12069,12119,12168,12218,12267,12316,12366,
+12415,12464,12514,12563,12612,12662,12711,12760,
+12810,12859,12908,12957,13007,13056,13105,13154,
+13204,13253,13302,13351,13401,13450,13499,13548,
+13597,13647,13696,13745,13794,13843,13892,13941,
+13990,14040,14089,14138,14187,14236,14285,14334,
+14383,14432,14481,14530,14579,14628,14677,14726,
+14775,14824,14873,14922,14971,15020,15069,15118,
+15167,15215,15264,15313,15362,15411,15460,15509,
+15557,15606,15655,15704,15753,15802,15850,15899,
+15948,15997,16045,16094,16143,16191,16240,16289,
+16338,16386,16435,16484,16532,16581,16629,16678,
+16727,16775,16824,16872,16921,16970,17018,17067,
+17115,17164,17212,17261,17309,17358,17406,17455,
+17503,17551,17600,17648,17697,17745,17793,17842,
+17890,17939,17987,18035,18084,18132,18180,18228,
+18277,18325,18373,18421,18470,18518,18566,18614,
+18663,18711,18759,18807,18855,18903,18951,19000,
+19048,19096,19144,19192,19240,19288,19336,19384,
+19432,19480,19528,19576,19624,19672,19720,19768,
+19816,19864,19912,19959,20007,20055,20103,20151,
+20199,20246,20294,20342,20390,20438,20485,20533,
+20581,20629,20676,20724,20772,20819,20867,20915,
+20962,21010,21057,21105,21153,21200,21248,21295,
+21343,21390,21438,21485,21533,21580,21628,21675,
+21723,21770,21817,21865,21912,21960,22007,22054,
+22102,22149,22196,22243,22291,22338,22385,22433,
+22480,22527,22574,22621,22668,22716,22763,22810,
+22857,22904,22951,22998,23045,23092,23139,23186,
+23233,23280,23327,23374,23421,23468,23515,23562,
+23609,23656,23703,23750,23796,23843,23890,23937,
+23984,24030,24077,24124,24171,24217,24264,24311,
+24357,24404,24451,24497,24544,24591,24637,24684,
+24730,24777,24823,24870,24916,24963,25009,25056,
+25102,25149,25195,25241,25288,25334,25381,25427,
+25473,25520,25566,25612,25658,25705,25751,25797,
+25843,25889,25936,25982,26028,26074,26120,26166,
+26212,26258,26304,26350,26396,26442,26488,26534,
+26580,26626,26672,26718,26764,26810,26856,26902,
+26947,26993,27039,27085,27131,27176,27222,27268,
+27313,27359,27405,27450,27496,27542,27587,27633,
+27678,27724,27770,27815,27861,27906,27952,27997,
+28042,28088,28133,28179,28224,28269,28315,28360,
+28405,28451,28496,28541,28586,28632,28677,28722,
+28767,28812,28858,28903,28948,28993,29038,29083,
+29128,29173,29218,29263,29308,29353,29398,29443,
+29488,29533,29577,29622,29667,29712,29757,29801,
+29846,29891,29936,29980,30025,30070,30114,30159,
+30204,30248,30293,30337,30382,30426,30471,30515,
+30560,30604,30649,30693,30738,30782,30826,30871,
+30915,30959,31004,31048,31092,31136,31181,31225,
+31269,31313,31357,31402,31446,31490,31534,31578,
+31622,31666,31710,31754,31798,31842,31886,31930,
+31974,32017,32061,32105,32149,32193,32236,32280,
+32324,32368,32411,32455,32499,32542,32586,32630,
+32673,32717,32760,32804,32847,32891,32934,32978,
+33021,33065,33108,33151,33195,33238,33281,33325,
+33368,33411,33454,33498,33541,33584,33627,33670,
+33713,33756,33799,33843,33886,33929,33972,34015,
+34057,34100,34143,34186,34229,34272,34315,34358,
+34400,34443,34486,34529,34571,34614,34657,34699,
+34742,34785,34827,34870,34912,34955,34997,35040,
+35082,35125,35167,35210,35252,35294,35337,35379,
+35421,35464,35506,35548,35590,35633,35675,35717,
+35759,35801,35843,35885,35927,35969,36011,36053,
+36095,36137,36179,36221,36263,36305,36347,36388,
+36430,36472,36514,36555,36597,36639,36681,36722,
+36764,36805,36847,36889,36930,36972,37013,37055,
+37096,37137,37179,37220,37262,37303,37344,37386,
+37427,37468,37509,37551,37592,37633,37674,37715,
+37756,37797,37838,37879,37920,37961,38002,38043,
+38084,38125,38166,38207,38248,38288,38329,38370,
+38411,38451,38492,38533,38573,38614,38655,38695,
+38736,38776,38817,38857,38898,38938,38979,39019,
+39059,39100,39140,39180,39221,39261,39301,39341,
+39382,39422,39462,39502,39542,39582,39622,39662,
+39702,39742,39782,39822,39862,39902,39942,39982,
+40021,40061,40101,40141,40180,40220,40260,40300,
+40339,40379,40418,40458,40497,40537,40576,40616,
+40655,40695,40734,40773,40813,40852,40891,40931,
+40970,41009,41048,41087,41127,41166,41205,41244,
+41283,41322,41361,41400,41439,41478,41517,41556,
+41595,41633,41672,41711,41750,41788,41827,41866,
+41904,41943,41982,42020,42059,42097,42136,42174,
+42213,42251,42290,42328,42366,42405,42443,42481,
+42520,42558,42596,42634,42672,42711,42749,42787,
+42825,42863,42901,42939,42977,43015,43053,43091,
+43128,43166,43204,43242,43280,43317,43355,43393,
+43430,43468,43506,43543,43581,43618,43656,43693,
+43731,43768,43806,43843,43880,43918,43955,43992,
+44029,44067,44104,44141,44178,44215,44252,44289,
+44326,44363,44400,44437,44474,44511,44548,44585,
+44622,44659,44695,44732,44769,44806,44842,44879,
+44915,44952,44989,45025,45062,45098,45135,45171,
+45207,45244,45280,45316,45353,45389,45425,45462,
+45498,45534,45570,45606,45642,45678,45714,45750,
+45786,45822,45858,45894,45930,45966,46002,46037,
+46073,46109,46145,46180,46216,46252,46287,46323,
+46358,46394,46429,46465,46500,46536,46571,46606,
+46642,46677,46712,46747,46783,46818,46853,46888,
+46923,46958,46993,47028,47063,47098,47133,47168,
+47203,47238,47273,47308,47342,47377,47412,47446,
+47481,47516,47550,47585,47619,47654,47688,47723,
+47757,47792,47826,47860,47895,47929,47963,47998,
+48032,48066,48100,48134,48168,48202,48237,48271,
+48305,48338,48372,48406,48440,48474,48508,48542,
+48575,48609,48643,48676,48710,48744,48777,48811,
+48844,48878,48911,48945,48978,49012,49045,49078,
+49112,49145,49178,49211,49244,49278,49311,49344,
+49377,49410,49443,49476,49509,49542,49575,49608,
+49640,49673,49706,49739,49771,49804,49837,49869,
+49902,49935,49967,50000,50032,50065,50097,50129,
+50162,50194,50226,50259,50291,50323,50355,50387,
+50420,50452,50484,50516,50548,50580,50612,50644,
+50675,50707,50739,50771,50803,50834,50866,50898,
+50929,50961,50993,51024,51056,51087,51119,51150,
+51182,51213,51244,51276,51307,51338,51369,51401,
+51432,51463,51494,51525,51556,51587,51618,51649,
+51680,51711,51742,51773,51803,51834,51865,51896,
+51926,51957,51988,52018,52049,52079,52110,52140,
+52171,52201,52231,52262,52292,52322,52353,52383,
+52413,52443,52473,52503,52534,52564,52594,52624,
+52653,52683,52713,52743,52773,52803,52832,52862,
+52892,52922,52951,52981,53010,53040,53069,53099,
+53128,53158,53187,53216,53246,53275,53304,53334,
+53363,53392,53421,53450,53479,53508,53537,53566,
+53595,53624,53653,53682,53711,53739,53768,53797,
+53826,53854,53883,53911,53940,53969,53997,54026,
+54054,54082,54111,54139,54167,54196,54224,54252,
+54280,54308,54337,54365,54393,54421,54449,54477,
+54505,54533,54560,54588,54616,54644,54672,54699,
+54727,54755,54782,54810,54837,54865,54892,54920,
+54947,54974,55002,55029,55056,55084,55111,55138,
+55165,55192,55219,55246,55274,55300,55327,55354,
+55381,55408,55435,55462,55489,55515,55542,55569,
+55595,55622,55648,55675,55701,55728,55754,55781,
+55807,55833,55860,55886,55912,55938,55965,55991,
+56017,56043,56069,56095,56121,56147,56173,56199,
+56225,56250,56276,56302,56328,56353,56379,56404,
+56430,56456,56481,56507,56532,56557,56583,56608,
+56633,56659,56684,56709,56734,56760,56785,56810,
+56835,56860,56885,56910,56935,56959,56984,57009,
+57034,57059,57083,57108,57133,57157,57182,57206,
+57231,57255,57280,57304,57329,57353,57377,57402,
+57426,57450,57474,57498,57522,57546,57570,57594,
+57618,57642,57666,57690,57714,57738,57762,57785,
+57809,57833,57856,57880,57903,57927,57950,57974,
+57997,58021,58044,58067,58091,58114,58137,58160,
+58183,58207,58230,58253,58276,58299,58322,58345,
+58367,58390,58413,58436,58459,58481,58504,58527,
+58549,58572,58594,58617,58639,58662,58684,58706,
+58729,58751,58773,58795,58818,58840,58862,58884,
+58906,58928,58950,58972,58994,59016,59038,59059,
+59081,59103,59125,59146,59168,59190,59211,59233,
+59254,59276,59297,59318,59340,59361,59382,59404,
+59425,59446,59467,59488,59509,59530,59551,59572,
+59593,59614,59635,59656,59677,59697,59718,59739,
+59759,59780,59801,59821,59842,59862,59883,59903,
+59923,59944,59964,59984,60004,60025,60045,60065,
+60085,60105,60125,60145,60165,60185,60205,60225,
+60244,60264,60284,60304,60323,60343,60363,60382,
+60402,60421,60441,60460,60479,60499,60518,60537,
+60556,60576,60595,60614,60633,60652,60671,60690,
+60709,60728,60747,60766,60785,60803,60822,60841,
+60859,60878,60897,60915,60934,60952,60971,60989,
+61007,61026,61044,61062,61081,61099,61117,61135,
+61153,61171,61189,61207,61225,61243,61261,61279,
+61297,61314,61332,61350,61367,61385,61403,61420,
+61438,61455,61473,61490,61507,61525,61542,61559,
+61577,61594,61611,61628,61645,61662,61679,61696,
+61713,61730,61747,61764,61780,61797,61814,61831,
+61847,61864,61880,61897,61913,61930,61946,61963,
+61979,61995,62012,62028,62044,62060,62076,62092,
+62108,62125,62141,62156,62172,62188,62204,62220,
+62236,62251,62267,62283,62298,62314,62329,62345,
+62360,62376,62391,62407,62422,62437,62453,62468,
+62483,62498,62513,62528,62543,62558,62573,62588,
+62603,62618,62633,62648,62662,62677,62692,62706,
+62721,62735,62750,62764,62779,62793,62808,62822,
+62836,62850,62865,62879,62893,62907,62921,62935,
+62949,62963,62977,62991,63005,63019,63032,63046,
+63060,63074,63087,63101,63114,63128,63141,63155,
+63168,63182,63195,63208,63221,63235,63248,63261,
+63274,63287,63300,63313,63326,63339,63352,63365,
+63378,63390,63403,63416,63429,63441,63454,63466,
+63479,63491,63504,63516,63528,63541,63553,63565,
+63578,63590,63602,63614,63626,63638,63650,63662,
+63674,63686,63698,63709,63721,63733,63745,63756,
+63768,63779,63791,63803,63814,63825,63837,63848,
+63859,63871,63882,63893,63904,63915,63927,63938,
+63949,63960,63971,63981,63992,64003,64014,64025,
+64035,64046,64057,64067,64078,64088,64099,64109,
+64120,64130,64140,64151,64161,64171,64181,64192,
+64202,64212,64222,64232,64242,64252,64261,64271,
+64281,64291,64301,64310,64320,64330,64339,64349,
+64358,64368,64377,64387,64396,64405,64414,64424,
+64433,64442,64451,64460,64469,64478,64487,64496,
+64505,64514,64523,64532,64540,64549,64558,64566,
+64575,64584,64592,64601,64609,64617,64626,64634,
+64642,64651,64659,64667,64675,64683,64691,64699,
+64707,64715,64723,64731,64739,64747,64754,64762,
+64770,64777,64785,64793,64800,64808,64815,64822,
+64830,64837,64844,64852,64859,64866,64873,64880,
+64887,64895,64902,64908,64915,64922,64929,64936,
+64943,64949,64956,64963,64969,64976,64982,64989,
+64995,65002,65008,65015,65021,65027,65033,65040,
+65046,65052,65058,65064,65070,65076,65082,65088,
+65094,65099,65105,65111,65117,65122,65128,65133,
+65139,65144,65150,65155,65161,65166,65171,65177,
+65182,65187,65192,65197,65202,65207,65212,65217,
+65222,65227,65232,65237,65242,65246,65251,65256,
+65260,65265,65270,65274,65279,65283,65287,65292,
+65296,65300,65305,65309,65313,65317,65321,65325,
+65329,65333,65337,65341,65345,65349,65352,65356,
+65360,65363,65367,65371,65374,65378,65381,65385,
+65388,65391,65395,65398,65401,65404,65408,65411,
+65414,65417,65420,65423,65426,65429,65431,65434,
+65437,65440,65442,65445,65448,65450,65453,65455,
+65458,65460,65463,65465,65467,65470,65472,65474,
+65476,65478,65480,65482,65484,65486,65488,65490,
+65492,65494,65496,65497,65499,65501,65502,65504,
+65505,65507,65508,65510,65511,65513,65514,65515,
+65516,65518,65519,65520,65521,65522,65523,65524,
+65525,65526,65527,65527,65528,65529,65530,65530,
+65531,65531,65532,65532,65533,65533,65534,65534,
+65534,65535,65535,65535,65535,65535,65535,65535,
+65535,65535,65535,65535,65535,65535,65535,65534,
+65534,65534,65533,65533,65532,65532,65531,65531,
+65530,65530,65529,65528,65527,65527,65526,65525,
+65524,65523,65522,65521,65520,65519,65518,65516,
+65515,65514,65513,65511,65510,65508,65507,65505,
+65504,65502,65501,65499,65497,65496,65494,65492,
+65490,65488,65486,65484,65482,65480,65478,65476,
+65474,65472,65470,65467,65465,65463,65460,65458,
+65455,65453,65450,65448,65445,65442,65440,65437,
+65434,65431,65429,65426,65423,65420,65417,65414,
+65411,65408,65404,65401,65398,65395,65391,65388,
+65385,65381,65378,65374,65371,65367,65363,65360,
+65356,65352,65349,65345,65341,65337,65333,65329,
+65325,65321,65317,65313,65309,65305,65300,65296,
+65292,65287,65283,65279,65274,65270,65265,65260,
+65256,65251,65246,65242,65237,65232,65227,65222,
+65217,65212,65207,65202,65197,65192,65187,65182,
+65177,65171,65166,65161,65155,65150,65144,65139,
+65133,65128,65122,65117,65111,65105,65099,65094,
+65088,65082,65076,65070,65064,65058,65052,65046,
+65040,65033,65027,65021,65015,65008,65002,64995,
+64989,64982,64976,64969,64963,64956,64949,64943,
+64936,64929,64922,64915,64908,64902,64895,64887,
+64880,64873,64866,64859,64852,64844,64837,64830,
+64822,64815,64808,64800,64793,64785,64777,64770,
+64762,64754,64747,64739,64731,64723,64715,64707,
+64699,64691,64683,64675,64667,64659,64651,64642,
+64634,64626,64617,64609,64600,64592,64584,64575,
+64566,64558,64549,64540,64532,64523,64514,64505,
+64496,64487,64478,64469,64460,64451,64442,64433,
+64424,64414,64405,64396,64387,64377,64368,64358,
+64349,64339,64330,64320,64310,64301,64291,64281,
+64271,64261,64252,64242,64232,64222,64212,64202,
+64192,64181,64171,64161,64151,64140,64130,64120,
+64109,64099,64088,64078,64067,64057,64046,64035,
+64025,64014,64003,63992,63981,63971,63960,63949,
+63938,63927,63915,63904,63893,63882,63871,63859,
+63848,63837,63825,63814,63803,63791,63779,63768,
+63756,63745,63733,63721,63709,63698,63686,63674,
+63662,63650,63638,63626,63614,63602,63590,63578,
+63565,63553,63541,63528,63516,63504,63491,63479,
+63466,63454,63441,63429,63416,63403,63390,63378,
+63365,63352,63339,63326,63313,63300,63287,63274,
+63261,63248,63235,63221,63208,63195,63182,63168,
+63155,63141,63128,63114,63101,63087,63074,63060,
+63046,63032,63019,63005,62991,62977,62963,62949,
+62935,62921,62907,62893,62879,62865,62850,62836,
+62822,62808,62793,62779,62764,62750,62735,62721,
+62706,62692,62677,62662,62648,62633,62618,62603,
+62588,62573,62558,62543,62528,62513,62498,62483,
+62468,62453,62437,62422,62407,62391,62376,62360,
+62345,62329,62314,62298,62283,62267,62251,62236,
+62220,62204,62188,62172,62156,62141,62125,62108,
+62092,62076,62060,62044,62028,62012,61995,61979,
+61963,61946,61930,61913,61897,61880,61864,61847,
+61831,61814,61797,61780,61764,61747,61730,61713,
+61696,61679,61662,61645,61628,61611,61594,61577,
+61559,61542,61525,61507,61490,61473,61455,61438,
+61420,61403,61385,61367,61350,61332,61314,61297,
+61279,61261,61243,61225,61207,61189,61171,61153,
+61135,61117,61099,61081,61062,61044,61026,61007,
+60989,60971,60952,60934,60915,60897,60878,60859,
+60841,60822,60803,60785,60766,60747,60728,60709,
+60690,60671,60652,60633,60614,60595,60576,60556,
+60537,60518,60499,60479,60460,60441,60421,60402,
+60382,60363,60343,60323,60304,60284,60264,60244,
+60225,60205,60185,60165,60145,60125,60105,60085,
+60065,60045,60025,60004,59984,59964,59944,59923,
+59903,59883,59862,59842,59821,59801,59780,59759,
+59739,59718,59697,59677,59656,59635,59614,59593,
+59572,59551,59530,59509,59488,59467,59446,59425,
+59404,59382,59361,59340,59318,59297,59276,59254,
+59233,59211,59190,59168,59146,59125,59103,59081,
+59059,59038,59016,58994,58972,58950,58928,58906,
+58884,58862,58840,58818,58795,58773,58751,58729,
+58706,58684,58662,58639,58617,58594,58572,58549,
+58527,58504,58481,58459,58436,58413,58390,58367,
+58345,58322,58299,58276,58253,58230,58207,58183,
+58160,58137,58114,58091,58067,58044,58021,57997,
+57974,57950,57927,57903,57880,57856,57833,57809,
+57785,57762,57738,57714,57690,57666,57642,57618,
+57594,57570,57546,57522,57498,57474,57450,57426,
+57402,57377,57353,57329,57304,57280,57255,57231,
+57206,57182,57157,57133,57108,57083,57059,57034,
+57009,56984,56959,56935,56910,56885,56860,56835,
+56810,56785,56760,56734,56709,56684,56659,56633,
+56608,56583,56557,56532,56507,56481,56456,56430,
+56404,56379,56353,56328,56302,56276,56250,56225,
+56199,56173,56147,56121,56095,56069,56043,56017,
+55991,55965,55938,55912,55886,55860,55833,55807,
+55781,55754,55728,55701,55675,55648,55622,55595,
+55569,55542,55515,55489,55462,55435,55408,55381,
+55354,55327,55300,55274,55246,55219,55192,55165,
+55138,55111,55084,55056,55029,55002,54974,54947,
+54920,54892,54865,54837,54810,54782,54755,54727,
+54699,54672,54644,54616,54588,54560,54533,54505,
+54477,54449,54421,54393,54365,54337,54308,54280,
+54252,54224,54196,54167,54139,54111,54082,54054,
+54026,53997,53969,53940,53911,53883,53854,53826,
+53797,53768,53739,53711,53682,53653,53624,53595,
+53566,53537,53508,53479,53450,53421,53392,53363,
+53334,53304,53275,53246,53216,53187,53158,53128,
+53099,53069,53040,53010,52981,52951,52922,52892,
+52862,52832,52803,52773,52743,52713,52683,52653,
+52624,52594,52564,52534,52503,52473,52443,52413,
+52383,52353,52322,52292,52262,52231,52201,52171,
+52140,52110,52079,52049,52018,51988,51957,51926,
+51896,51865,51834,51803,51773,51742,51711,51680,
+51649,51618,51587,51556,51525,51494,51463,51432,
+51401,51369,51338,51307,51276,51244,51213,51182,
+51150,51119,51087,51056,51024,50993,50961,50929,
+50898,50866,50834,50803,50771,50739,50707,50675,
+50644,50612,50580,50548,50516,50484,50452,50420,
+50387,50355,50323,50291,50259,50226,50194,50162,
+50129,50097,50065,50032,50000,49967,49935,49902,
+49869,49837,49804,49771,49739,49706,49673,49640,
+49608,49575,49542,49509,49476,49443,49410,49377,
+49344,49311,49278,49244,49211,49178,49145,49112,
+49078,49045,49012,48978,48945,48911,48878,48844,
+48811,48777,48744,48710,48676,48643,48609,48575,
+48542,48508,48474,48440,48406,48372,48338,48304,
+48271,48237,48202,48168,48134,48100,48066,48032,
+47998,47963,47929,47895,47860,47826,47792,47757,
+47723,47688,47654,47619,47585,47550,47516,47481,
+47446,47412,47377,47342,47308,47273,47238,47203,
+47168,47133,47098,47063,47028,46993,46958,46923,
+46888,46853,46818,46783,46747,46712,46677,46642,
+46606,46571,46536,46500,46465,46429,46394,46358,
+46323,46287,46252,46216,46180,46145,46109,46073,
+46037,46002,45966,45930,45894,45858,45822,45786,
+45750,45714,45678,45642,45606,45570,45534,45498,
+45462,45425,45389,45353,45316,45280,45244,45207,
+45171,45135,45098,45062,45025,44989,44952,44915,
+44879,44842,44806,44769,44732,44695,44659,44622,
+44585,44548,44511,44474,44437,44400,44363,44326,
+44289,44252,44215,44178,44141,44104,44067,44029,
+43992,43955,43918,43880,43843,43806,43768,43731,
+43693,43656,43618,43581,43543,43506,43468,43430,
+43393,43355,43317,43280,43242,43204,43166,43128,
+43091,43053,43015,42977,42939,42901,42863,42825,
+42787,42749,42711,42672,42634,42596,42558,42520,
+42481,42443,42405,42366,42328,42290,42251,42213,
+42174,42136,42097,42059,42020,41982,41943,41904,
+41866,41827,41788,41750,41711,41672,41633,41595,
+41556,41517,41478,41439,41400,41361,41322,41283,
+41244,41205,41166,41127,41088,41048,41009,40970,
+40931,40891,40852,40813,40773,40734,40695,40655,
+40616,40576,40537,40497,40458,40418,40379,40339,
+40300,40260,40220,40180,40141,40101,40061,40021,
+39982,39942,39902,39862,39822,39782,39742,39702,
+39662,39622,39582,39542,39502,39462,39422,39382,
+39341,39301,39261,39221,39180,39140,39100,39059,
+39019,38979,38938,38898,38857,38817,38776,38736,
+38695,38655,38614,38573,38533,38492,38451,38411,
+38370,38329,38288,38248,38207,38166,38125,38084,
+38043,38002,37961,37920,37879,37838,37797,37756,
+37715,37674,37633,37592,37551,37509,37468,37427,
+37386,37344,37303,37262,37220,37179,37137,37096,
+37055,37013,36972,36930,36889,36847,36805,36764,
+36722,36681,36639,36597,36556,36514,36472,36430,
+36388,36347,36305,36263,36221,36179,36137,36095,
+36053,36011,35969,35927,35885,35843,35801,35759,
+35717,35675,35633,35590,35548,35506,35464,35421,
+35379,35337,35294,35252,35210,35167,35125,35082,
+35040,34997,34955,34912,34870,34827,34785,34742,
+34699,34657,34614,34571,34529,34486,34443,34400,
+34358,34315,34272,34229,34186,34143,34100,34057,
+34015,33972,33929,33886,33843,33799,33756,33713,
+33670,33627,33584,33541,33498,33454,33411,33368,
+33325,33281,33238,33195,33151,33108,33065,33021,
+32978,32934,32891,32847,32804,32760,32717,32673,
+32630,32586,32542,32499,32455,32411,32368,32324,
+32280,32236,32193,32149,32105,32061,32017,31974,
+31930,31886,31842,31798,31754,31710,31666,31622,
+31578,31534,31490,31446,31402,31357,31313,31269,
+31225,31181,31136,31092,31048,31004,30959,30915,
+30871,30826,30782,30738,30693,30649,30604,30560,
+30515,30471,30426,30382,30337,30293,30248,30204,
+30159,30114,30070,30025,29980,29936,29891,29846,
+29801,29757,29712,29667,29622,29577,29533,29488,
+29443,29398,29353,29308,29263,29218,29173,29128,
+29083,29038,28993,28948,28903,28858,28812,28767,
+28722,28677,28632,28586,28541,28496,28451,28405,
+28360,28315,28269,28224,28179,28133,28088,28042,
+27997,27952,27906,27861,27815,27770,27724,27678,
+27633,27587,27542,27496,27450,27405,27359,27313,
+27268,27222,27176,27131,27085,27039,26993,26947,
+26902,26856,26810,26764,26718,26672,26626,26580,
+26534,26488,26442,26396,26350,26304,26258,26212,
+26166,26120,26074,26028,25982,25936,25889,25843,
+25797,25751,25705,25658,25612,25566,25520,25473,
+25427,25381,25334,25288,25241,25195,25149,25102,
+25056,25009,24963,24916,24870,24823,24777,24730,
+24684,24637,24591,24544,24497,24451,24404,24357,
+24311,24264,24217,24171,24124,24077,24030,23984,
+23937,23890,23843,23796,23750,23703,23656,23609,
+23562,23515,23468,23421,23374,23327,23280,23233,
+23186,23139,23092,23045,22998,22951,22904,22857,
+22810,22763,22716,22668,22621,22574,22527,22480,
+22433,22385,22338,22291,22243,22196,22149,22102,
+22054,22007,21960,21912,21865,21817,21770,21723,
+21675,21628,21580,21533,21485,21438,21390,21343,
+21295,21248,21200,21153,21105,21057,21010,20962,
+20915,20867,20819,20772,20724,20676,20629,20581,
+20533,20485,20438,20390,20342,20294,20246,20199,
+20151,20103,20055,20007,19959,19912,19864,19816,
+19768,19720,19672,19624,19576,19528,19480,19432,
+19384,19336,19288,19240,19192,19144,19096,19048,
+19000,18951,18903,18855,18807,18759,18711,18663,
+18614,18566,18518,18470,18421,18373,18325,18277,
+18228,18180,18132,18084,18035,17987,17939,17890,
+17842,17793,17745,17697,17648,17600,17551,17503,
+17455,17406,17358,17309,17261,17212,17164,17115,
+17067,17018,16970,16921,16872,16824,16775,16727,
+16678,16629,16581,16532,16484,16435,16386,16338,
+16289,16240,16191,16143,16094,16045,15997,15948,
+15899,15850,15802,15753,15704,15655,15606,15557,
+15509,15460,15411,15362,15313,15264,15215,15167,
+15118,15069,15020,14971,14922,14873,14824,14775,
+14726,14677,14628,14579,14530,14481,14432,14383,
+14334,14285,14236,14187,14138,14089,14040,13990,
+13941,13892,13843,13794,13745,13696,13646,13597,
+13548,13499,13450,13401,13351,13302,13253,13204,
+13154,13105,13056,13007,12957,12908,12859,12810,
+12760,12711,12662,12612,12563,12514,12464,12415,
+12366,12316,12267,12218,12168,12119,12069,12020,
+11970,11921,11872,11822,11773,11723,11674,11624,
+11575,11525,11476,11426,11377,11327,11278,11228,
+11179,11129,11080,11030,10981,10931,10882,10832,
+10782,10733,10683,10634,10584,10534,10485,10435,
+10386,10336,10286,10237,10187,10137,10088,10038,
+9988,9939,9889,9839,9790,9740,9690,9640,
+9591,9541,9491,9442,9392,9342,9292,9243,
+9193,9143,9093,9043,8994,8944,8894,8844,
+8794,8745,8695,8645,8595,8545,8496,8446,
+8396,8346,8296,8246,8196,8147,8097,8047,
+7997,7947,7897,7847,7797,7747,7697,7648,
+7598,7548,7498,7448,7398,7348,7298,7248,
+7198,7148,7098,7048,6998,6948,6898,6848,
+6798,6748,6698,6648,6598,6548,6498,6448,
+6398,6348,6298,6248,6198,6148,6098,6048,
+5998,5948,5898,5848,5798,5748,5697,5647,
+5597,5547,5497,5447,5397,5347,5297,5247,
+5197,5146,5096,5046,4996,4946,4896,4846,
+4796,4745,4695,4645,4595,4545,4495,4445,
+4394,4344,4294,4244,4194,4144,4093,4043,
+3993,3943,3893,3843,3792,3742,3692,3642,
+3592,3541,3491,3441,3391,3341,3291,3240,
+3190,3140,3090,3039,2989,2939,2889,2839,
+2788,2738,2688,2638,2587,2537,2487,2437,
+2387,2336,2286,2236,2186,2135,2085,2035,
+1985,1934,1884,1834,1784,1733,1683,1633,
+1583,1532,1482,1432,1382,1331,1281,1231,
+1181,1130,1080,1030,980,929,879,829,
+779,728,678,628,578,527,477,427,
+376,326,276,226,175,125,75,25,
+-25,-75,-125,-175,-226,-276,-326,-376,
+-427,-477,-527,-578,-628,-678,-728,-779,
+-829,-879,-929,-980,-1030,-1080,-1130,-1181,
+-1231,-1281,-1331,-1382,-1432,-1482,-1532,-1583,
+-1633,-1683,-1733,-1784,-1834,-1884,-1934,-1985,
+-2035,-2085,-2135,-2186,-2236,-2286,-2336,-2387,
+-2437,-2487,-2537,-2588,-2638,-2688,-2738,-2788,
+-2839,-2889,-2939,-2989,-3039,-3090,-3140,-3190,
+-3240,-3291,-3341,-3391,-3441,-3491,-3541,-3592,
+-3642,-3692,-3742,-3792,-3843,-3893,-3943,-3993,
+-4043,-4093,-4144,-4194,-4244,-4294,-4344,-4394,
+-4445,-4495,-4545,-4595,-4645,-4695,-4745,-4796,
+-4846,-4896,-4946,-4996,-5046,-5096,-5146,-5197,
+-5247,-5297,-5347,-5397,-5447,-5497,-5547,-5597,
+-5647,-5697,-5748,-5798,-5848,-5898,-5948,-5998,
+-6048,-6098,-6148,-6198,-6248,-6298,-6348,-6398,
+-6448,-6498,-6548,-6598,-6648,-6698,-6748,-6798,
+-6848,-6898,-6948,-6998,-7048,-7098,-7148,-7198,
+-7248,-7298,-7348,-7398,-7448,-7498,-7548,-7598,
+-7648,-7697,-7747,-7797,-7847,-7897,-7947,-7997,
+-8047,-8097,-8147,-8196,-8246,-8296,-8346,-8396,
+-8446,-8496,-8545,-8595,-8645,-8695,-8745,-8794,
+-8844,-8894,-8944,-8994,-9043,-9093,-9143,-9193,
+-9243,-9292,-9342,-9392,-9442,-9491,-9541,-9591,
+-9640,-9690,-9740,-9790,-9839,-9889,-9939,-9988,
+-10038,-10088,-10137,-10187,-10237,-10286,-10336,-10386,
+-10435,-10485,-10534,-10584,-10634,-10683,-10733,-10782,
+-10832,-10882,-10931,-10981,-11030,-11080,-11129,-11179,
+-11228,-11278,-11327,-11377,-11426,-11476,-11525,-11575,
+-11624,-11674,-11723,-11773,-11822,-11872,-11921,-11970,
+-12020,-12069,-12119,-12168,-12218,-12267,-12316,-12366,
+-12415,-12464,-12514,-12563,-12612,-12662,-12711,-12760,
+-12810,-12859,-12908,-12957,-13007,-13056,-13105,-13154,
+-13204,-13253,-13302,-13351,-13401,-13450,-13499,-13548,
+-13597,-13647,-13696,-13745,-13794,-13843,-13892,-13941,
+-13990,-14040,-14089,-14138,-14187,-14236,-14285,-14334,
+-14383,-14432,-14481,-14530,-14579,-14628,-14677,-14726,
+-14775,-14824,-14873,-14922,-14971,-15020,-15069,-15118,
+-15167,-15215,-15264,-15313,-15362,-15411,-15460,-15509,
+-15557,-15606,-15655,-15704,-15753,-15802,-15850,-15899,
+-15948,-15997,-16045,-16094,-16143,-16191,-16240,-16289,
+-16338,-16386,-16435,-16484,-16532,-16581,-16629,-16678,
+-16727,-16775,-16824,-16872,-16921,-16970,-17018,-17067,
+-17115,-17164,-17212,-17261,-17309,-17358,-17406,-17455,
+-17503,-17551,-17600,-17648,-17697,-17745,-17793,-17842,
+-17890,-17939,-17987,-18035,-18084,-18132,-18180,-18228,
+-18277,-18325,-18373,-18421,-18470,-18518,-18566,-18614,
+-18663,-18711,-18759,-18807,-18855,-18903,-18951,-19000,
+-19048,-19096,-19144,-19192,-19240,-19288,-19336,-19384,
+-19432,-19480,-19528,-19576,-19624,-19672,-19720,-19768,
+-19816,-19864,-19912,-19959,-20007,-20055,-20103,-20151,
+-20199,-20246,-20294,-20342,-20390,-20438,-20485,-20533,
+-20581,-20629,-20676,-20724,-20772,-20819,-20867,-20915,
+-20962,-21010,-21057,-21105,-21153,-21200,-21248,-21295,
+-21343,-21390,-21438,-21485,-21533,-21580,-21628,-21675,
+-21723,-21770,-21817,-21865,-21912,-21960,-22007,-22054,
+-22102,-22149,-22196,-22243,-22291,-22338,-22385,-22433,
+-22480,-22527,-22574,-22621,-22668,-22716,-22763,-22810,
+-22857,-22904,-22951,-22998,-23045,-23092,-23139,-23186,
+-23233,-23280,-23327,-23374,-23421,-23468,-23515,-23562,
+-23609,-23656,-23703,-23750,-23796,-23843,-23890,-23937,
+-23984,-24030,-24077,-24124,-24171,-24217,-24264,-24311,
+-24357,-24404,-24451,-24497,-24544,-24591,-24637,-24684,
+-24730,-24777,-24823,-24870,-24916,-24963,-25009,-25056,
+-25102,-25149,-25195,-25241,-25288,-25334,-25381,-25427,
+-25473,-25520,-25566,-25612,-25658,-25705,-25751,-25797,
+-25843,-25889,-25936,-25982,-26028,-26074,-26120,-26166,
+-26212,-26258,-26304,-26350,-26396,-26442,-26488,-26534,
+-26580,-26626,-26672,-26718,-26764,-26810,-26856,-26902,
+-26947,-26993,-27039,-27085,-27131,-27176,-27222,-27268,
+-27313,-27359,-27405,-27450,-27496,-27542,-27587,-27633,
+-27678,-27724,-27770,-27815,-27861,-27906,-27952,-27997,
+-28042,-28088,-28133,-28179,-28224,-28269,-28315,-28360,
+-28405,-28451,-28496,-28541,-28586,-28632,-28677,-28722,
+-28767,-28812,-28858,-28903,-28948,-28993,-29038,-29083,
+-29128,-29173,-29218,-29263,-29308,-29353,-29398,-29443,
+-29488,-29533,-29577,-29622,-29667,-29712,-29757,-29801,
+-29846,-29891,-29936,-29980,-30025,-30070,-30114,-30159,
+-30204,-30248,-30293,-30337,-30382,-30426,-30471,-30515,
+-30560,-30604,-30649,-30693,-30738,-30782,-30826,-30871,
+-30915,-30959,-31004,-31048,-31092,-31136,-31181,-31225,
+-31269,-31313,-31357,-31402,-31446,-31490,-31534,-31578,
+-31622,-31666,-31710,-31754,-31798,-31842,-31886,-31930,
+-31974,-32017,-32061,-32105,-32149,-32193,-32236,-32280,
+-32324,-32368,-32411,-32455,-32499,-32542,-32586,-32630,
+-32673,-32717,-32760,-32804,-32847,-32891,-32934,-32978,
+-33021,-33065,-33108,-33151,-33195,-33238,-33281,-33325,
+-33368,-33411,-33454,-33498,-33541,-33584,-33627,-33670,
+-33713,-33756,-33799,-33843,-33886,-33929,-33972,-34015,
+-34057,-34100,-34143,-34186,-34229,-34272,-34315,-34358,
+-34400,-34443,-34486,-34529,-34571,-34614,-34657,-34699,
+-34742,-34785,-34827,-34870,-34912,-34955,-34997,-35040,
+-35082,-35125,-35167,-35210,-35252,-35294,-35337,-35379,
+-35421,-35464,-35506,-35548,-35590,-35633,-35675,-35717,
+-35759,-35801,-35843,-35885,-35927,-35969,-36011,-36053,
+-36095,-36137,-36179,-36221,-36263,-36305,-36347,-36388,
+-36430,-36472,-36514,-36555,-36597,-36639,-36681,-36722,
+-36764,-36805,-36847,-36889,-36930,-36972,-37013,-37055,
+-37096,-37137,-37179,-37220,-37262,-37303,-37344,-37386,
+-37427,-37468,-37509,-37551,-37592,-37633,-37674,-37715,
+-37756,-37797,-37838,-37879,-37920,-37961,-38002,-38043,
+-38084,-38125,-38166,-38207,-38248,-38288,-38329,-38370,
+-38411,-38451,-38492,-38533,-38573,-38614,-38655,-38695,
+-38736,-38776,-38817,-38857,-38898,-38938,-38979,-39019,
+-39059,-39100,-39140,-39180,-39221,-39261,-39301,-39341,
+-39382,-39422,-39462,-39502,-39542,-39582,-39622,-39662,
+-39702,-39742,-39782,-39822,-39862,-39902,-39942,-39982,
+-40021,-40061,-40101,-40141,-40180,-40220,-40260,-40299,
+-40339,-40379,-40418,-40458,-40497,-40537,-40576,-40616,
+-40655,-40695,-40734,-40773,-40813,-40852,-40891,-40931,
+-40970,-41009,-41048,-41087,-41127,-41166,-41205,-41244,
+-41283,-41322,-41361,-41400,-41439,-41478,-41517,-41556,
+-41595,-41633,-41672,-41711,-41750,-41788,-41827,-41866,
+-41904,-41943,-41982,-42020,-42059,-42097,-42136,-42174,
+-42213,-42251,-42290,-42328,-42366,-42405,-42443,-42481,
+-42520,-42558,-42596,-42634,-42672,-42711,-42749,-42787,
+-42825,-42863,-42901,-42939,-42977,-43015,-43053,-43091,
+-43128,-43166,-43204,-43242,-43280,-43317,-43355,-43393,
+-43430,-43468,-43506,-43543,-43581,-43618,-43656,-43693,
+-43731,-43768,-43806,-43843,-43880,-43918,-43955,-43992,
+-44029,-44067,-44104,-44141,-44178,-44215,-44252,-44289,
+-44326,-44363,-44400,-44437,-44474,-44511,-44548,-44585,
+-44622,-44659,-44695,-44732,-44769,-44806,-44842,-44879,
+-44915,-44952,-44989,-45025,-45062,-45098,-45135,-45171,
+-45207,-45244,-45280,-45316,-45353,-45389,-45425,-45462,
+-45498,-45534,-45570,-45606,-45642,-45678,-45714,-45750,
+-45786,-45822,-45858,-45894,-45930,-45966,-46002,-46037,
+-46073,-46109,-46145,-46180,-46216,-46252,-46287,-46323,
+-46358,-46394,-46429,-46465,-46500,-46536,-46571,-46606,
+-46642,-46677,-46712,-46747,-46783,-46818,-46853,-46888,
+-46923,-46958,-46993,-47028,-47063,-47098,-47133,-47168,
+-47203,-47238,-47273,-47308,-47342,-47377,-47412,-47446,
+-47481,-47516,-47550,-47585,-47619,-47654,-47688,-47723,
+-47757,-47792,-47826,-47860,-47895,-47929,-47963,-47998,
+-48032,-48066,-48100,-48134,-48168,-48202,-48236,-48271,
+-48304,-48338,-48372,-48406,-48440,-48474,-48508,-48542,
+-48575,-48609,-48643,-48676,-48710,-48744,-48777,-48811,
+-48844,-48878,-48911,-48945,-48978,-49012,-49045,-49078,
+-49112,-49145,-49178,-49211,-49244,-49278,-49311,-49344,
+-49377,-49410,-49443,-49476,-49509,-49542,-49575,-49608,
+-49640,-49673,-49706,-49739,-49771,-49804,-49837,-49869,
+-49902,-49935,-49967,-50000,-50032,-50065,-50097,-50129,
+-50162,-50194,-50226,-50259,-50291,-50323,-50355,-50387,
+-50420,-50452,-50484,-50516,-50548,-50580,-50612,-50644,
+-50675,-50707,-50739,-50771,-50803,-50834,-50866,-50898,
+-50929,-50961,-50993,-51024,-51056,-51087,-51119,-51150,
+-51182,-51213,-51244,-51276,-51307,-51338,-51369,-51401,
+-51432,-51463,-51494,-51525,-51556,-51587,-51618,-51649,
+-51680,-51711,-51742,-51773,-51803,-51834,-51865,-51896,
+-51926,-51957,-51988,-52018,-52049,-52079,-52110,-52140,
+-52171,-52201,-52231,-52262,-52292,-52322,-52353,-52383,
+-52413,-52443,-52473,-52503,-52534,-52564,-52594,-52624,
+-52653,-52683,-52713,-52743,-52773,-52803,-52832,-52862,
+-52892,-52922,-52951,-52981,-53010,-53040,-53069,-53099,
+-53128,-53158,-53187,-53216,-53246,-53275,-53304,-53334,
+-53363,-53392,-53421,-53450,-53479,-53508,-53537,-53566,
+-53595,-53624,-53653,-53682,-53711,-53739,-53768,-53797,
+-53826,-53854,-53883,-53911,-53940,-53969,-53997,-54026,
+-54054,-54082,-54111,-54139,-54167,-54196,-54224,-54252,
+-54280,-54308,-54337,-54365,-54393,-54421,-54449,-54477,
+-54505,-54533,-54560,-54588,-54616,-54644,-54672,-54699,
+-54727,-54755,-54782,-54810,-54837,-54865,-54892,-54920,
+-54947,-54974,-55002,-55029,-55056,-55084,-55111,-55138,
+-55165,-55192,-55219,-55246,-55274,-55300,-55327,-55354,
+-55381,-55408,-55435,-55462,-55489,-55515,-55542,-55569,
+-55595,-55622,-55648,-55675,-55701,-55728,-55754,-55781,
+-55807,-55833,-55860,-55886,-55912,-55938,-55965,-55991,
+-56017,-56043,-56069,-56095,-56121,-56147,-56173,-56199,
+-56225,-56250,-56276,-56302,-56328,-56353,-56379,-56404,
+-56430,-56456,-56481,-56507,-56532,-56557,-56583,-56608,
+-56633,-56659,-56684,-56709,-56734,-56760,-56785,-56810,
+-56835,-56860,-56885,-56910,-56935,-56959,-56984,-57009,
+-57034,-57059,-57083,-57108,-57133,-57157,-57182,-57206,
+-57231,-57255,-57280,-57304,-57329,-57353,-57377,-57402,
+-57426,-57450,-57474,-57498,-57522,-57546,-57570,-57594,
+-57618,-57642,-57666,-57690,-57714,-57738,-57762,-57785,
+-57809,-57833,-57856,-57880,-57903,-57927,-57950,-57974,
+-57997,-58021,-58044,-58067,-58091,-58114,-58137,-58160,
+-58183,-58207,-58230,-58253,-58276,-58299,-58322,-58345,
+-58367,-58390,-58413,-58436,-58459,-58481,-58504,-58527,
+-58549,-58572,-58594,-58617,-58639,-58662,-58684,-58706,
+-58729,-58751,-58773,-58795,-58818,-58840,-58862,-58884,
+-58906,-58928,-58950,-58972,-58994,-59016,-59038,-59059,
+-59081,-59103,-59125,-59146,-59168,-59190,-59211,-59233,
+-59254,-59276,-59297,-59318,-59340,-59361,-59382,-59404,
+-59425,-59446,-59467,-59488,-59509,-59530,-59551,-59572,
+-59593,-59614,-59635,-59656,-59677,-59697,-59718,-59739,
+-59759,-59780,-59801,-59821,-59842,-59862,-59883,-59903,
+-59923,-59944,-59964,-59984,-60004,-60025,-60045,-60065,
+-60085,-60105,-60125,-60145,-60165,-60185,-60205,-60225,
+-60244,-60264,-60284,-60304,-60323,-60343,-60363,-60382,
+-60402,-60421,-60441,-60460,-60479,-60499,-60518,-60537,
+-60556,-60576,-60595,-60614,-60633,-60652,-60671,-60690,
+-60709,-60728,-60747,-60766,-60785,-60803,-60822,-60841,
+-60859,-60878,-60897,-60915,-60934,-60952,-60971,-60989,
+-61007,-61026,-61044,-61062,-61081,-61099,-61117,-61135,
+-61153,-61171,-61189,-61207,-61225,-61243,-61261,-61279,
+-61297,-61314,-61332,-61350,-61367,-61385,-61403,-61420,
+-61438,-61455,-61473,-61490,-61507,-61525,-61542,-61559,
+-61577,-61594,-61611,-61628,-61645,-61662,-61679,-61696,
+-61713,-61730,-61747,-61764,-61780,-61797,-61814,-61831,
+-61847,-61864,-61880,-61897,-61913,-61930,-61946,-61963,
+-61979,-61995,-62012,-62028,-62044,-62060,-62076,-62092,
+-62108,-62125,-62141,-62156,-62172,-62188,-62204,-62220,
+-62236,-62251,-62267,-62283,-62298,-62314,-62329,-62345,
+-62360,-62376,-62391,-62407,-62422,-62437,-62453,-62468,
+-62483,-62498,-62513,-62528,-62543,-62558,-62573,-62588,
+-62603,-62618,-62633,-62648,-62662,-62677,-62692,-62706,
+-62721,-62735,-62750,-62764,-62779,-62793,-62808,-62822,
+-62836,-62850,-62865,-62879,-62893,-62907,-62921,-62935,
+-62949,-62963,-62977,-62991,-63005,-63019,-63032,-63046,
+-63060,-63074,-63087,-63101,-63114,-63128,-63141,-63155,
+-63168,-63182,-63195,-63208,-63221,-63235,-63248,-63261,
+-63274,-63287,-63300,-63313,-63326,-63339,-63352,-63365,
+-63378,-63390,-63403,-63416,-63429,-63441,-63454,-63466,
+-63479,-63491,-63504,-63516,-63528,-63541,-63553,-63565,
+-63578,-63590,-63602,-63614,-63626,-63638,-63650,-63662,
+-63674,-63686,-63698,-63709,-63721,-63733,-63745,-63756,
+-63768,-63779,-63791,-63803,-63814,-63825,-63837,-63848,
+-63859,-63871,-63882,-63893,-63904,-63915,-63927,-63938,
+-63949,-63960,-63971,-63981,-63992,-64003,-64014,-64025,
+-64035,-64046,-64057,-64067,-64078,-64088,-64099,-64109,
+-64120,-64130,-64140,-64151,-64161,-64171,-64181,-64192,
+-64202,-64212,-64222,-64232,-64242,-64252,-64261,-64271,
+-64281,-64291,-64301,-64310,-64320,-64330,-64339,-64349,
+-64358,-64368,-64377,-64387,-64396,-64405,-64414,-64424,
+-64433,-64442,-64451,-64460,-64469,-64478,-64487,-64496,
+-64505,-64514,-64523,-64532,-64540,-64549,-64558,-64566,
+-64575,-64584,-64592,-64601,-64609,-64617,-64626,-64634,
+-64642,-64651,-64659,-64667,-64675,-64683,-64691,-64699,
+-64707,-64715,-64723,-64731,-64739,-64747,-64754,-64762,
+-64770,-64777,-64785,-64793,-64800,-64808,-64815,-64822,
+-64830,-64837,-64844,-64852,-64859,-64866,-64873,-64880,
+-64887,-64895,-64902,-64908,-64915,-64922,-64929,-64936,
+-64943,-64949,-64956,-64963,-64969,-64976,-64982,-64989,
+-64995,-65002,-65008,-65015,-65021,-65027,-65033,-65040,
+-65046,-65052,-65058,-65064,-65070,-65076,-65082,-65088,
+-65094,-65099,-65105,-65111,-65117,-65122,-65128,-65133,
+-65139,-65144,-65150,-65155,-65161,-65166,-65171,-65177,
+-65182,-65187,-65192,-65197,-65202,-65207,-65212,-65217,
+-65222,-65227,-65232,-65237,-65242,-65246,-65251,-65256,
+-65260,-65265,-65270,-65274,-65279,-65283,-65287,-65292,
+-65296,-65300,-65305,-65309,-65313,-65317,-65321,-65325,
+-65329,-65333,-65337,-65341,-65345,-65349,-65352,-65356,
+-65360,-65363,-65367,-65371,-65374,-65378,-65381,-65385,
+-65388,-65391,-65395,-65398,-65401,-65404,-65408,-65411,
+-65414,-65417,-65420,-65423,-65426,-65429,-65431,-65434,
+-65437,-65440,-65442,-65445,-65448,-65450,-65453,-65455,
+-65458,-65460,-65463,-65465,-65467,-65470,-65472,-65474,
+-65476,-65478,-65480,-65482,-65484,-65486,-65488,-65490,
+-65492,-65494,-65496,-65497,-65499,-65501,-65502,-65504,
+-65505,-65507,-65508,-65510,-65511,-65513,-65514,-65515,
+-65516,-65518,-65519,-65520,-65521,-65522,-65523,-65524,
+-65525,-65526,-65527,-65527,-65528,-65529,-65530,-65530,
+-65531,-65531,-65532,-65532,-65533,-65533,-65534,-65534,
+-65534,-65535,-65535,-65535,-65535,-65535,-65535,-65535,
+-65535,-65535,-65535,-65535,-65535,-65535,-65535,-65534,
+-65534,-65534,-65533,-65533,-65532,-65532,-65531,-65531,
+-65530,-65530,-65529,-65528,-65527,-65527,-65526,-65525,
+-65524,-65523,-65522,-65521,-65520,-65519,-65518,-65516,
+-65515,-65514,-65513,-65511,-65510,-65508,-65507,-65505,
+-65504,-65502,-65501,-65499,-65497,-65496,-65494,-65492,
+-65490,-65488,-65486,-65484,-65482,-65480,-65478,-65476,
+-65474,-65472,-65470,-65467,-65465,-65463,-65460,-65458,
+-65455,-65453,-65450,-65448,-65445,-65442,-65440,-65437,
+-65434,-65431,-65429,-65426,-65423,-65420,-65417,-65414,
+-65411,-65408,-65404,-65401,-65398,-65395,-65391,-65388,
+-65385,-65381,-65378,-65374,-65371,-65367,-65363,-65360,
+-65356,-65352,-65349,-65345,-65341,-65337,-65333,-65329,
+-65325,-65321,-65317,-65313,-65309,-65305,-65300,-65296,
+-65292,-65287,-65283,-65279,-65274,-65270,-65265,-65260,
+-65256,-65251,-65246,-65242,-65237,-65232,-65227,-65222,
+-65217,-65212,-65207,-65202,-65197,-65192,-65187,-65182,
+-65177,-65171,-65166,-65161,-65155,-65150,-65144,-65139,
+-65133,-65128,-65122,-65117,-65111,-65105,-65099,-65094,
+-65088,-65082,-65076,-65070,-65064,-65058,-65052,-65046,
+-65040,-65033,-65027,-65021,-65015,-65008,-65002,-64995,
+-64989,-64982,-64976,-64969,-64963,-64956,-64949,-64943,
+-64936,-64929,-64922,-64915,-64908,-64902,-64895,-64887,
+-64880,-64873,-64866,-64859,-64852,-64844,-64837,-64830,
+-64822,-64815,-64808,-64800,-64793,-64785,-64777,-64770,
+-64762,-64754,-64747,-64739,-64731,-64723,-64715,-64707,
+-64699,-64691,-64683,-64675,-64667,-64659,-64651,-64642,
+-64634,-64626,-64617,-64609,-64601,-64592,-64584,-64575,
+-64566,-64558,-64549,-64540,-64532,-64523,-64514,-64505,
+-64496,-64487,-64478,-64469,-64460,-64451,-64442,-64433,
+-64424,-64414,-64405,-64396,-64387,-64377,-64368,-64358,
+-64349,-64339,-64330,-64320,-64310,-64301,-64291,-64281,
+-64271,-64261,-64252,-64242,-64232,-64222,-64212,-64202,
+-64192,-64181,-64171,-64161,-64151,-64140,-64130,-64120,
+-64109,-64099,-64088,-64078,-64067,-64057,-64046,-64035,
+-64025,-64014,-64003,-63992,-63981,-63971,-63960,-63949,
+-63938,-63927,-63915,-63904,-63893,-63882,-63871,-63859,
+-63848,-63837,-63825,-63814,-63803,-63791,-63779,-63768,
+-63756,-63745,-63733,-63721,-63709,-63698,-63686,-63674,
+-63662,-63650,-63638,-63626,-63614,-63602,-63590,-63578,
+-63565,-63553,-63541,-63528,-63516,-63504,-63491,-63479,
+-63466,-63454,-63441,-63429,-63416,-63403,-63390,-63378,
+-63365,-63352,-63339,-63326,-63313,-63300,-63287,-63274,
+-63261,-63248,-63235,-63221,-63208,-63195,-63182,-63168,
+-63155,-63141,-63128,-63114,-63101,-63087,-63074,-63060,
+-63046,-63032,-63019,-63005,-62991,-62977,-62963,-62949,
+-62935,-62921,-62907,-62893,-62879,-62865,-62850,-62836,
+-62822,-62808,-62793,-62779,-62764,-62750,-62735,-62721,
+-62706,-62692,-62677,-62662,-62648,-62633,-62618,-62603,
+-62588,-62573,-62558,-62543,-62528,-62513,-62498,-62483,
+-62468,-62453,-62437,-62422,-62407,-62391,-62376,-62360,
+-62345,-62329,-62314,-62298,-62283,-62267,-62251,-62236,
+-62220,-62204,-62188,-62172,-62156,-62141,-62125,-62108,
+-62092,-62076,-62060,-62044,-62028,-62012,-61995,-61979,
+-61963,-61946,-61930,-61913,-61897,-61880,-61864,-61847,
+-61831,-61814,-61797,-61780,-61764,-61747,-61730,-61713,
+-61696,-61679,-61662,-61645,-61628,-61611,-61594,-61577,
+-61559,-61542,-61525,-61507,-61490,-61473,-61455,-61438,
+-61420,-61403,-61385,-61367,-61350,-61332,-61314,-61297,
+-61279,-61261,-61243,-61225,-61207,-61189,-61171,-61153,
+-61135,-61117,-61099,-61081,-61062,-61044,-61026,-61007,
+-60989,-60971,-60952,-60934,-60915,-60897,-60878,-60859,
+-60841,-60822,-60803,-60785,-60766,-60747,-60728,-60709,
+-60690,-60671,-60652,-60633,-60614,-60595,-60576,-60556,
+-60537,-60518,-60499,-60479,-60460,-60441,-60421,-60402,
+-60382,-60363,-60343,-60323,-60304,-60284,-60264,-60244,
+-60225,-60205,-60185,-60165,-60145,-60125,-60105,-60085,
+-60065,-60045,-60025,-60004,-59984,-59964,-59944,-59923,
+-59903,-59883,-59862,-59842,-59821,-59801,-59780,-59759,
+-59739,-59718,-59697,-59677,-59656,-59635,-59614,-59593,
+-59572,-59551,-59530,-59509,-59488,-59467,-59446,-59425,
+-59404,-59382,-59361,-59340,-59318,-59297,-59276,-59254,
+-59233,-59211,-59189,-59168,-59146,-59125,-59103,-59081,
+-59059,-59038,-59016,-58994,-58972,-58950,-58928,-58906,
+-58884,-58862,-58840,-58818,-58795,-58773,-58751,-58729,
+-58706,-58684,-58662,-58639,-58617,-58594,-58572,-58549,
+-58527,-58504,-58481,-58459,-58436,-58413,-58390,-58367,
+-58345,-58322,-58299,-58276,-58253,-58230,-58207,-58183,
+-58160,-58137,-58114,-58091,-58067,-58044,-58021,-57997,
+-57974,-57950,-57927,-57903,-57880,-57856,-57833,-57809,
+-57785,-57762,-57738,-57714,-57690,-57666,-57642,-57618,
+-57594,-57570,-57546,-57522,-57498,-57474,-57450,-57426,
+-57402,-57377,-57353,-57329,-57304,-57280,-57255,-57231,
+-57206,-57182,-57157,-57133,-57108,-57083,-57059,-57034,
+-57009,-56984,-56959,-56935,-56910,-56885,-56860,-56835,
+-56810,-56785,-56760,-56734,-56709,-56684,-56659,-56633,
+-56608,-56583,-56557,-56532,-56507,-56481,-56456,-56430,
+-56404,-56379,-56353,-56328,-56302,-56276,-56250,-56225,
+-56199,-56173,-56147,-56121,-56095,-56069,-56043,-56017,
+-55991,-55965,-55938,-55912,-55886,-55860,-55833,-55807,
+-55781,-55754,-55728,-55701,-55675,-55648,-55622,-55595,
+-55569,-55542,-55515,-55489,-55462,-55435,-55408,-55381,
+-55354,-55327,-55300,-55274,-55246,-55219,-55192,-55165,
+-55138,-55111,-55084,-55056,-55029,-55002,-54974,-54947,
+-54920,-54892,-54865,-54837,-54810,-54782,-54755,-54727,
+-54699,-54672,-54644,-54616,-54588,-54560,-54533,-54505,
+-54477,-54449,-54421,-54393,-54365,-54337,-54308,-54280,
+-54252,-54224,-54196,-54167,-54139,-54111,-54082,-54054,
+-54026,-53997,-53969,-53940,-53911,-53883,-53854,-53826,
+-53797,-53768,-53739,-53711,-53682,-53653,-53624,-53595,
+-53566,-53537,-53508,-53479,-53450,-53421,-53392,-53363,
+-53334,-53304,-53275,-53246,-53216,-53187,-53158,-53128,
+-53099,-53069,-53040,-53010,-52981,-52951,-52922,-52892,
+-52862,-52832,-52803,-52773,-52743,-52713,-52683,-52653,
+-52624,-52594,-52564,-52534,-52503,-52473,-52443,-52413,
+-52383,-52353,-52322,-52292,-52262,-52231,-52201,-52171,
+-52140,-52110,-52079,-52049,-52018,-51988,-51957,-51926,
+-51896,-51865,-51834,-51803,-51773,-51742,-51711,-51680,
+-51649,-51618,-51587,-51556,-51525,-51494,-51463,-51432,
+-51401,-51369,-51338,-51307,-51276,-51244,-51213,-51182,
+-51150,-51119,-51087,-51056,-51024,-50993,-50961,-50929,
+-50898,-50866,-50834,-50803,-50771,-50739,-50707,-50675,
+-50644,-50612,-50580,-50548,-50516,-50484,-50452,-50420,
+-50387,-50355,-50323,-50291,-50259,-50226,-50194,-50162,
+-50129,-50097,-50065,-50032,-50000,-49967,-49935,-49902,
+-49869,-49837,-49804,-49771,-49739,-49706,-49673,-49640,
+-49608,-49575,-49542,-49509,-49476,-49443,-49410,-49377,
+-49344,-49311,-49278,-49244,-49211,-49178,-49145,-49112,
+-49078,-49045,-49012,-48978,-48945,-48911,-48878,-48844,
+-48811,-48777,-48744,-48710,-48676,-48643,-48609,-48575,
+-48542,-48508,-48474,-48440,-48406,-48372,-48338,-48305,
+-48271,-48237,-48202,-48168,-48134,-48100,-48066,-48032,
+-47998,-47963,-47929,-47895,-47860,-47826,-47792,-47757,
+-47723,-47688,-47654,-47619,-47585,-47550,-47516,-47481,
+-47446,-47412,-47377,-47342,-47307,-47273,-47238,-47203,
+-47168,-47133,-47098,-47063,-47028,-46993,-46958,-46923,
+-46888,-46853,-46818,-46783,-46747,-46712,-46677,-46642,
+-46606,-46571,-46536,-46500,-46465,-46429,-46394,-46358,
+-46323,-46287,-46251,-46216,-46180,-46145,-46109,-46073,
+-46037,-46002,-45966,-45930,-45894,-45858,-45822,-45786,
+-45750,-45714,-45678,-45642,-45606,-45570,-45534,-45498,
+-45462,-45425,-45389,-45353,-45316,-45280,-45244,-45207,
+-45171,-45135,-45098,-45062,-45025,-44989,-44952,-44915,
+-44879,-44842,-44806,-44769,-44732,-44695,-44659,-44622,
+-44585,-44548,-44511,-44474,-44437,-44400,-44363,-44326,
+-44289,-44252,-44215,-44178,-44141,-44104,-44067,-44029,
+-43992,-43955,-43918,-43880,-43843,-43806,-43768,-43731,
+-43693,-43656,-43618,-43581,-43543,-43506,-43468,-43430,
+-43393,-43355,-43317,-43280,-43242,-43204,-43166,-43128,
+-43091,-43053,-43015,-42977,-42939,-42901,-42863,-42825,
+-42787,-42749,-42711,-42672,-42634,-42596,-42558,-42520,
+-42481,-42443,-42405,-42366,-42328,-42290,-42251,-42213,
+-42174,-42136,-42097,-42059,-42020,-41982,-41943,-41904,
+-41866,-41827,-41788,-41750,-41711,-41672,-41633,-41595,
+-41556,-41517,-41478,-41439,-41400,-41361,-41322,-41283,
+-41244,-41205,-41166,-41127,-41087,-41048,-41009,-40970,
+-40931,-40891,-40852,-40813,-40773,-40734,-40695,-40655,
+-40616,-40576,-40537,-40497,-40458,-40418,-40379,-40339,
+-40299,-40260,-40220,-40180,-40141,-40101,-40061,-40021,
+-39982,-39942,-39902,-39862,-39822,-39782,-39742,-39702,
+-39662,-39622,-39582,-39542,-39502,-39462,-39422,-39382,
+-39341,-39301,-39261,-39221,-39180,-39140,-39100,-39059,
+-39019,-38979,-38938,-38898,-38857,-38817,-38776,-38736,
+-38695,-38655,-38614,-38573,-38533,-38492,-38451,-38411,
+-38370,-38329,-38288,-38248,-38207,-38166,-38125,-38084,
+-38043,-38002,-37961,-37920,-37879,-37838,-37797,-37756,
+-37715,-37674,-37633,-37592,-37550,-37509,-37468,-37427,
+-37386,-37344,-37303,-37262,-37220,-37179,-37137,-37096,
+-37055,-37013,-36972,-36930,-36889,-36847,-36805,-36764,
+-36722,-36681,-36639,-36597,-36556,-36514,-36472,-36430,
+-36388,-36347,-36305,-36263,-36221,-36179,-36137,-36095,
+-36053,-36011,-35969,-35927,-35885,-35843,-35801,-35759,
+-35717,-35675,-35633,-35590,-35548,-35506,-35464,-35421,
+-35379,-35337,-35294,-35252,-35210,-35167,-35125,-35082,
+-35040,-34997,-34955,-34912,-34870,-34827,-34785,-34742,
+-34699,-34657,-34614,-34571,-34529,-34486,-34443,-34400,
+-34358,-34315,-34272,-34229,-34186,-34143,-34100,-34057,
+-34015,-33972,-33929,-33886,-33843,-33799,-33756,-33713,
+-33670,-33627,-33584,-33541,-33498,-33454,-33411,-33368,
+-33325,-33281,-33238,-33195,-33151,-33108,-33065,-33021,
+-32978,-32934,-32891,-32847,-32804,-32760,-32717,-32673,
+-32630,-32586,-32542,-32499,-32455,-32411,-32368,-32324,
+-32280,-32236,-32193,-32149,-32105,-32061,-32017,-31974,
+-31930,-31886,-31842,-31798,-31754,-31710,-31666,-31622,
+-31578,-31534,-31490,-31446,-31402,-31357,-31313,-31269,
+-31225,-31181,-31136,-31092,-31048,-31004,-30959,-30915,
+-30871,-30826,-30782,-30738,-30693,-30649,-30604,-30560,
+-30515,-30471,-30426,-30382,-30337,-30293,-30248,-30204,
+-30159,-30114,-30070,-30025,-29980,-29936,-29891,-29846,
+-29801,-29757,-29712,-29667,-29622,-29577,-29533,-29488,
+-29443,-29398,-29353,-29308,-29263,-29218,-29173,-29128,
+-29083,-29038,-28993,-28948,-28903,-28858,-28812,-28767,
+-28722,-28677,-28632,-28586,-28541,-28496,-28451,-28405,
+-28360,-28315,-28269,-28224,-28179,-28133,-28088,-28042,
+-27997,-27952,-27906,-27861,-27815,-27770,-27724,-27678,
+-27633,-27587,-27542,-27496,-27450,-27405,-27359,-27313,
+-27268,-27222,-27176,-27131,-27085,-27039,-26993,-26947,
+-26902,-26856,-26810,-26764,-26718,-26672,-26626,-26580,
+-26534,-26488,-26442,-26396,-26350,-26304,-26258,-26212,
+-26166,-26120,-26074,-26028,-25982,-25936,-25889,-25843,
+-25797,-25751,-25705,-25658,-25612,-25566,-25520,-25473,
+-25427,-25381,-25334,-25288,-25241,-25195,-25149,-25102,
+-25056,-25009,-24963,-24916,-24870,-24823,-24777,-24730,
+-24684,-24637,-24591,-24544,-24497,-24451,-24404,-24357,
+-24311,-24264,-24217,-24171,-24124,-24077,-24030,-23984,
+-23937,-23890,-23843,-23796,-23750,-23703,-23656,-23609,
+-23562,-23515,-23468,-23421,-23374,-23327,-23280,-23233,
+-23186,-23139,-23092,-23045,-22998,-22951,-22904,-22857,
+-22810,-22763,-22716,-22668,-22621,-22574,-22527,-22480,
+-22432,-22385,-22338,-22291,-22243,-22196,-22149,-22102,
+-22054,-22007,-21960,-21912,-21865,-21817,-21770,-21723,
+-21675,-21628,-21580,-21533,-21485,-21438,-21390,-21343,
+-21295,-21248,-21200,-21153,-21105,-21057,-21010,-20962,
+-20915,-20867,-20819,-20772,-20724,-20676,-20629,-20581,
+-20533,-20485,-20438,-20390,-20342,-20294,-20246,-20199,
+-20151,-20103,-20055,-20007,-19959,-19912,-19864,-19816,
+-19768,-19720,-19672,-19624,-19576,-19528,-19480,-19432,
+-19384,-19336,-19288,-19240,-19192,-19144,-19096,-19048,
+-19000,-18951,-18903,-18855,-18807,-18759,-18711,-18663,
+-18614,-18566,-18518,-18470,-18421,-18373,-18325,-18277,
+-18228,-18180,-18132,-18084,-18035,-17987,-17939,-17890,
+-17842,-17793,-17745,-17697,-17648,-17600,-17551,-17503,
+-17455,-17406,-17358,-17309,-17261,-17212,-17164,-17115,
+-17067,-17018,-16970,-16921,-16872,-16824,-16775,-16727,
+-16678,-16629,-16581,-16532,-16484,-16435,-16386,-16338,
+-16289,-16240,-16191,-16143,-16094,-16045,-15997,-15948,
+-15899,-15850,-15802,-15753,-15704,-15655,-15606,-15557,
+-15509,-15460,-15411,-15362,-15313,-15264,-15215,-15167,
+-15118,-15069,-15020,-14971,-14922,-14873,-14824,-14775,
+-14726,-14677,-14628,-14579,-14530,-14481,-14432,-14383,
+-14334,-14285,-14236,-14187,-14138,-14089,-14040,-13990,
+-13941,-13892,-13843,-13794,-13745,-13696,-13647,-13597,
+-13548,-13499,-13450,-13401,-13351,-13302,-13253,-13204,
+-13154,-13105,-13056,-13007,-12957,-12908,-12859,-12810,
+-12760,-12711,-12662,-12612,-12563,-12514,-12464,-12415,
+-12366,-12316,-12267,-12217,-12168,-12119,-12069,-12020,
+-11970,-11921,-11872,-11822,-11773,-11723,-11674,-11624,
+-11575,-11525,-11476,-11426,-11377,-11327,-11278,-11228,
+-11179,-11129,-11080,-11030,-10981,-10931,-10882,-10832,
+-10782,-10733,-10683,-10634,-10584,-10534,-10485,-10435,
+-10386,-10336,-10286,-10237,-10187,-10137,-10088,-10038,
+-9988,-9939,-9889,-9839,-9790,-9740,-9690,-9640,
+-9591,-9541,-9491,-9442,-9392,-9342,-9292,-9243,
+-9193,-9143,-9093,-9043,-8994,-8944,-8894,-8844,
+-8794,-8745,-8695,-8645,-8595,-8545,-8496,-8446,
+-8396,-8346,-8296,-8246,-8196,-8147,-8097,-8047,
+-7997,-7947,-7897,-7847,-7797,-7747,-7697,-7648,
+-7598,-7548,-7498,-7448,-7398,-7348,-7298,-7248,
+-7198,-7148,-7098,-7048,-6998,-6948,-6898,-6848,
+-6798,-6748,-6698,-6648,-6598,-6548,-6498,-6448,
+-6398,-6348,-6298,-6248,-6198,-6148,-6098,-6048,
+-5998,-5948,-5898,-5848,-5798,-5747,-5697,-5647,
+-5597,-5547,-5497,-5447,-5397,-5347,-5297,-5247,
+-5197,-5146,-5096,-5046,-4996,-4946,-4896,-4846,
+-4796,-4745,-4695,-4645,-4595,-4545,-4495,-4445,
+-4394,-4344,-4294,-4244,-4194,-4144,-4093,-4043,
+-3993,-3943,-3893,-3843,-3792,-3742,-3692,-3642,
+-3592,-3541,-3491,-3441,-3391,-3341,-3291,-3240,
+-3190,-3140,-3090,-3039,-2989,-2939,-2889,-2839,
+-2788,-2738,-2688,-2638,-2588,-2537,-2487,-2437,
+-2387,-2336,-2286,-2236,-2186,-2135,-2085,-2035,
+-1985,-1934,-1884,-1834,-1784,-1733,-1683,-1633,
+-1583,-1532,-1482,-1432,-1382,-1331,-1281,-1231,
+-1181,-1130,-1080,-1030,-980,-929,-879,-829,
+-779,-728,-678,-628,-578,-527,-477,-427,
+-376,-326,-276,-226,-175,-125,-75,-25,
+25,75,125,175,226,276,326,376,
+427,477,527,578,628,678,728,779,
+829,879,929,980,1030,1080,1130,1181,
+1231,1281,1331,1382,1432,1482,1532,1583,
+1633,1683,1733,1784,1834,1884,1934,1985,
+2035,2085,2135,2186,2236,2286,2336,2387,
+2437,2487,2537,2587,2638,2688,2738,2788,
+2839,2889,2939,2989,3039,3090,3140,3190,
+3240,3291,3341,3391,3441,3491,3542,3592,
+3642,3692,3742,3792,3843,3893,3943,3993,
+4043,4093,4144,4194,4244,4294,4344,4394,
+4445,4495,4545,4595,4645,4695,4745,4796,
+4846,4896,4946,4996,5046,5096,5146,5197,
+5247,5297,5347,5397,5447,5497,5547,5597,
+5647,5697,5747,5798,5848,5898,5948,5998,
+6048,6098,6148,6198,6248,6298,6348,6398,
+6448,6498,6548,6598,6648,6698,6748,6798,
+6848,6898,6948,6998,7048,7098,7148,7198,
+7248,7298,7348,7398,7448,7498,7548,7598,
+7648,7697,7747,7797,7847,7897,7947,7997,
+8047,8097,8147,8196,8246,8296,8346,8396,
+8446,8496,8545,8595,8645,8695,8745,8794,
+8844,8894,8944,8994,9043,9093,9143,9193,
+9243,9292,9342,9392,9442,9491,9541,9591,
+9640,9690,9740,9790,9839,9889,9939,9988,
+10038,10088,10137,10187,10237,10286,10336,10386,
+10435,10485,10534,10584,10634,10683,10733,10782,
+10832,10882,10931,10981,11030,11080,11129,11179,
+11228,11278,11327,11377,11426,11476,11525,11575,
+11624,11674,11723,11773,11822,11872,11921,11970,
+12020,12069,12119,12168,12218,12267,12316,12366,
+12415,12464,12514,12563,12612,12662,12711,12760,
+12810,12859,12908,12957,13007,13056,13105,13154,
+13204,13253,13302,13351,13401,13450,13499,13548,
+13597,13647,13696,13745,13794,13843,13892,13941,
+13990,14040,14089,14138,14187,14236,14285,14334,
+14383,14432,14481,14530,14579,14628,14677,14726,
+14775,14824,14873,14922,14971,15020,15069,15118,
+15167,15215,15264,15313,15362,15411,15460,15509,
+15557,15606,15655,15704,15753,15802,15850,15899,
+15948,15997,16045,16094,16143,16191,16240,16289,
+16338,16386,16435,16484,16532,16581,16629,16678,
+16727,16775,16824,16872,16921,16970,17018,17067,
+17115,17164,17212,17261,17309,17358,17406,17455,
+17503,17551,17600,17648,17697,17745,17793,17842,
+17890,17939,17987,18035,18084,18132,18180,18228,
+18277,18325,18373,18421,18470,18518,18566,18614,
+18663,18711,18759,18807,18855,18903,18951,19000,
+19048,19096,19144,19192,19240,19288,19336,19384,
+19432,19480,19528,19576,19624,19672,19720,19768,
+19816,19864,19912,19959,20007,20055,20103,20151,
+20199,20246,20294,20342,20390,20438,20485,20533,
+20581,20629,20676,20724,20772,20819,20867,20915,
+20962,21010,21057,21105,21153,21200,21248,21295,
+21343,21390,21438,21485,21533,21580,21628,21675,
+21723,21770,21817,21865,21912,21960,22007,22054,
+22102,22149,22196,22243,22291,22338,22385,22432,
+22480,22527,22574,22621,22668,22716,22763,22810,
+22857,22904,22951,22998,23045,23092,23139,23186,
+23233,23280,23327,23374,23421,23468,23515,23562,
+23609,23656,23703,23750,23796,23843,23890,23937,
+23984,24030,24077,24124,24171,24217,24264,24311,
+24357,24404,24451,24497,24544,24591,24637,24684,
+24730,24777,24823,24870,24916,24963,25009,25056,
+25102,25149,25195,25241,25288,25334,25381,25427,
+25473,25520,25566,25612,25658,25705,25751,25797,
+25843,25889,25936,25982,26028,26074,26120,26166,
+26212,26258,26304,26350,26396,26442,26488,26534,
+26580,26626,26672,26718,26764,26810,26856,26902,
+26947,26993,27039,27085,27131,27176,27222,27268,
+27313,27359,27405,27450,27496,27542,27587,27633,
+27678,27724,27770,27815,27861,27906,27952,27997,
+28042,28088,28133,28179,28224,28269,28315,28360,
+28405,28451,28496,28541,28586,28632,28677,28722,
+28767,28812,28858,28903,28948,28993,29038,29083,
+29128,29173,29218,29263,29308,29353,29398,29443,
+29488,29533,29577,29622,29667,29712,29757,29801,
+29846,29891,29936,29980,30025,30070,30114,30159,
+30204,30248,30293,30337,30382,30427,30471,30516,
+30560,30604,30649,30693,30738,30782,30826,30871,
+30915,30959,31004,31048,31092,31136,31181,31225,
+31269,31313,31357,31402,31446,31490,31534,31578,
+31622,31666,31710,31754,31798,31842,31886,31930,
+31974,32017,32061,32105,32149,32193,32236,32280,
+32324,32368,32411,32455,32499,32542,32586,32630,
+32673,32717,32760,32804,32847,32891,32934,32978,
+33021,33065,33108,33151,33195,33238,33281,33325,
+33368,33411,33454,33498,33541,33584,33627,33670,
+33713,33756,33799,33843,33886,33929,33972,34015,
+34057,34100,34143,34186,34229,34272,34315,34358,
+34400,34443,34486,34529,34571,34614,34657,34699,
+34742,34785,34827,34870,34912,34955,34997,35040,
+35082,35125,35167,35210,35252,35294,35337,35379,
+35421,35464,35506,35548,35590,35633,35675,35717,
+35759,35801,35843,35885,35927,35969,36011,36053,
+36095,36137,36179,36221,36263,36305,36347,36388,
+36430,36472,36514,36556,36597,36639,36681,36722,
+36764,36805,36847,36889,36930,36972,37013,37055,
+37096,37137,37179,37220,37262,37303,37344,37386,
+37427,37468,37509,37551,37592,37633,37674,37715,
+37756,37797,37838,37879,37920,37961,38002,38043,
+38084,38125,38166,38207,38248,38288,38329,38370,
+38411,38451,38492,38533,38573,38614,38655,38695,
+38736,38776,38817,38857,38898,38938,38979,39019,
+39059,39100,39140,39180,39221,39261,39301,39341,
+39382,39422,39462,39502,39542,39582,39622,39662,
+39702,39742,39782,39822,39862,39902,39942,39982,
+40021,40061,40101,40141,40180,40220,40260,40299,
+40339,40379,40418,40458,40497,40537,40576,40616,
+40655,40695,40734,40773,40813,40852,40891,40931,
+40970,41009,41048,41087,41127,41166,41205,41244,
+41283,41322,41361,41400,41439,41478,41517,41556,
+41595,41633,41672,41711,41750,41788,41827,41866,
+41904,41943,41982,42020,42059,42097,42136,42174,
+42213,42251,42290,42328,42366,42405,42443,42481,
+42520,42558,42596,42634,42672,42711,42749,42787,
+42825,42863,42901,42939,42977,43015,43053,43091,
+43128,43166,43204,43242,43280,43317,43355,43393,
+43430,43468,43506,43543,43581,43618,43656,43693,
+43731,43768,43806,43843,43880,43918,43955,43992,
+44029,44067,44104,44141,44178,44215,44252,44289,
+44326,44363,44400,44437,44474,44511,44548,44585,
+44622,44659,44695,44732,44769,44806,44842,44879,
+44915,44952,44989,45025,45062,45098,45135,45171,
+45207,45244,45280,45316,45353,45389,45425,45462,
+45498,45534,45570,45606,45642,45678,45714,45750,
+45786,45822,45858,45894,45930,45966,46002,46037,
+46073,46109,46145,46180,46216,46252,46287,46323,
+46358,46394,46429,46465,46500,46536,46571,46606,
+46642,46677,46712,46747,46783,46818,46853,46888,
+46923,46958,46993,47028,47063,47098,47133,47168,
+47203,47238,47273,47308,47342,47377,47412,47446,
+47481,47516,47550,47585,47619,47654,47688,47723,
+47757,47792,47826,47861,47895,47929,47963,47998,
+48032,48066,48100,48134,48168,48202,48237,48271,
+48305,48338,48372,48406,48440,48474,48508,48542,
+48575,48609,48643,48676,48710,48744,48777,48811,
+48844,48878,48911,48945,48978,49012,49045,49078,
+49112,49145,49178,49211,49244,49278,49311,49344,
+49377,49410,49443,49476,49509,49542,49575,49608,
+49640,49673,49706,49739,49771,49804,49837,49869,
+49902,49935,49967,50000,50032,50064,50097,50129,
+50162,50194,50226,50259,50291,50323,50355,50387,
+50420,50452,50484,50516,50548,50580,50612,50644,
+50675,50707,50739,50771,50803,50834,50866,50898,
+50929,50961,50993,51024,51056,51087,51119,51150,
+51182,51213,51244,51276,51307,51338,51369,51401,
+51432,51463,51494,51525,51556,51587,51618,51649,
+51680,51711,51742,51773,51803,51834,51865,51896,
+51926,51957,51988,52018,52049,52079,52110,52140,
+52171,52201,52231,52262,52292,52322,52353,52383,
+52413,52443,52473,52503,52534,52564,52594,52624,
+52653,52683,52713,52743,52773,52803,52832,52862,
+52892,52922,52951,52981,53010,53040,53069,53099,
+53128,53158,53187,53216,53246,53275,53304,53334,
+53363,53392,53421,53450,53479,53508,53537,53566,
+53595,53624,53653,53682,53711,53739,53768,53797,
+53826,53854,53883,53912,53940,53969,53997,54026,
+54054,54082,54111,54139,54167,54196,54224,54252,
+54280,54309,54337,54365,54393,54421,54449,54477,
+54505,54533,54560,54588,54616,54644,54672,54699,
+54727,54755,54782,54810,54837,54865,54892,54920,
+54947,54974,55002,55029,55056,55084,55111,55138,
+55165,55192,55219,55246,55274,55300,55327,55354,
+55381,55408,55435,55462,55489,55515,55542,55569,
+55595,55622,55648,55675,55701,55728,55754,55781,
+55807,55833,55860,55886,55912,55938,55965,55991,
+56017,56043,56069,56095,56121,56147,56173,56199,
+56225,56250,56276,56302,56328,56353,56379,56404,
+56430,56456,56481,56507,56532,56557,56583,56608,
+56633,56659,56684,56709,56734,56760,56785,56810,
+56835,56860,56885,56910,56935,56959,56984,57009,
+57034,57059,57083,57108,57133,57157,57182,57206,
+57231,57255,57280,57304,57329,57353,57377,57402,
+57426,57450,57474,57498,57522,57546,57570,57594,
+57618,57642,57666,57690,57714,57738,57762,57785,
+57809,57833,57856,57880,57903,57927,57950,57974,
+57997,58021,58044,58067,58091,58114,58137,58160,
+58183,58207,58230,58253,58276,58299,58322,58345,
+58367,58390,58413,58436,58459,58481,58504,58527,
+58549,58572,58594,58617,58639,58662,58684,58706,
+58729,58751,58773,58795,58818,58840,58862,58884,
+58906,58928,58950,58972,58994,59016,59038,59059,
+59081,59103,59125,59146,59168,59190,59211,59233,
+59254,59276,59297,59318,59340,59361,59382,59404,
+59425,59446,59467,59488,59509,59530,59551,59572,
+59593,59614,59635,59656,59677,59697,59718,59739,
+59759,59780,59801,59821,59842,59862,59883,59903,
+59923,59944,59964,59984,60004,60025,60045,60065,
+60085,60105,60125,60145,60165,60185,60205,60225,
+60244,60264,60284,60304,60323,60343,60363,60382,
+60402,60421,60441,60460,60479,60499,60518,60537,
+60556,60576,60595,60614,60633,60652,60671,60690,
+60709,60728,60747,60766,60785,60803,60822,60841,
+60859,60878,60897,60915,60934,60952,60971,60989,
+61007,61026,61044,61062,61081,61099,61117,61135,
+61153,61171,61189,61207,61225,61243,61261,61279,
+61297,61314,61332,61350,61367,61385,61403,61420,
+61438,61455,61473,61490,61507,61525,61542,61559,
+61577,61594,61611,61628,61645,61662,61679,61696,
+61713,61730,61747,61764,61780,61797,61814,61831,
+61847,61864,61880,61897,61913,61930,61946,61963,
+61979,61995,62012,62028,62044,62060,62076,62092,
+62108,62125,62141,62156,62172,62188,62204,62220,
+62236,62251,62267,62283,62298,62314,62329,62345,
+62360,62376,62391,62407,62422,62437,62453,62468,
+62483,62498,62513,62528,62543,62558,62573,62588,
+62603,62618,62633,62648,62662,62677,62692,62706,
+62721,62735,62750,62764,62779,62793,62808,62822,
+62836,62850,62865,62879,62893,62907,62921,62935,
+62949,62963,62977,62991,63005,63019,63032,63046,
+63060,63074,63087,63101,63114,63128,63141,63155,
+63168,63182,63195,63208,63221,63235,63248,63261,
+63274,63287,63300,63313,63326,63339,63352,63365,
+63378,63390,63403,63416,63429,63441,63454,63466,
+63479,63491,63504,63516,63528,63541,63553,63565,
+63578,63590,63602,63614,63626,63638,63650,63662,
+63674,63686,63698,63709,63721,63733,63745,63756,
+63768,63779,63791,63803,63814,63825,63837,63848,
+63859,63871,63882,63893,63904,63915,63927,63938,
+63949,63960,63971,63981,63992,64003,64014,64025,
+64035,64046,64057,64067,64078,64088,64099,64109,
+64120,64130,64140,64151,64161,64171,64181,64192,
+64202,64212,64222,64232,64242,64252,64261,64271,
+64281,64291,64301,64310,64320,64330,64339,64349,
+64358,64368,64377,64387,64396,64405,64414,64424,
+64433,64442,64451,64460,64469,64478,64487,64496,
+64505,64514,64523,64532,64540,64549,64558,64566,
+64575,64584,64592,64600,64609,64617,64626,64634,
+64642,64651,64659,64667,64675,64683,64691,64699,
+64707,64715,64723,64731,64739,64747,64754,64762,
+64770,64777,64785,64793,64800,64808,64815,64822,
+64830,64837,64844,64852,64859,64866,64873,64880,
+64887,64895,64902,64908,64915,64922,64929,64936,
+64943,64949,64956,64963,64969,64976,64982,64989,
+64995,65002,65008,65015,65021,65027,65033,65040,
+65046,65052,65058,65064,65070,65076,65082,65088,
+65094,65099,65105,65111,65117,65122,65128,65133,
+65139,65144,65150,65155,65161,65166,65171,65177,
+65182,65187,65192,65197,65202,65207,65212,65217,
+65222,65227,65232,65237,65242,65246,65251,65256,
+65260,65265,65270,65274,65279,65283,65287,65292,
+65296,65300,65305,65309,65313,65317,65321,65325,
+65329,65333,65337,65341,65345,65349,65352,65356,
+65360,65363,65367,65371,65374,65378,65381,65385,
+65388,65391,65395,65398,65401,65404,65408,65411,
+65414,65417,65420,65423,65426,65429,65431,65434,
+65437,65440,65442,65445,65448,65450,65453,65455,
+65458,65460,65463,65465,65467,65470,65472,65474,
+65476,65478,65480,65482,65484,65486,65488,65490,
+65492,65494,65496,65497,65499,65501,65502,65504,
+65505,65507,65508,65510,65511,65513,65514,65515,
+65516,65518,65519,65520,65521,65522,65523,65524,
+65525,65526,65527,65527,65528,65529,65530,65530,
+65531,65531,65532,65532,65533,65533,65534,65534,
+65534,65535,65535,65535,65535,65535,65535,65535
+
+};
+
+int tantoangle[2049] = {
+0,333772,667544,1001315,1335086,1668857,2002626,2336395,
+2670163,3003929,3337694,3671457,4005219,4338979,4672736,5006492,
+5340245,5673995,6007743,6341488,6675230,7008968,7342704,7676435,
+8010164,8343888,8677609,9011325,9345037,9678744,10012447,10346145,
+10679838,11013526,11347209,11680887,12014558,12348225,12681885,13015539,
+13349187,13682829,14016464,14350092,14683714,15017328,15350936,15684536,
+16018129,16351714,16685291,17018860,17352422,17685974,18019518,18353054,
+18686582,19020100,19353610,19687110,20020600,20354080,20687552,21021014,
+21354466,21687906,22021338,22354758,22688168,23021568,23354956,23688332,
+24021698,24355052,24688396,25021726,25355046,25688352,26021648,26354930,
+26688200,27021456,27354702,27687932,28021150,28354356,28687548,29020724,
+29353888,29687038,30020174,30353296,30686404,31019496,31352574,31685636,
+32018684,32351718,32684734,33017736,33350722,33683692,34016648,34349584,
+34682508,35015412,35348300,35681172,36014028,36346868,36679688,37012492,
+37345276,37678044,38010792,38343524,38676240,39008936,39341612,39674272,
+40006912,40339532,40672132,41004716,41337276,41669820,42002344,42334848,
+42667332,42999796,43332236,43664660,43997060,44329444,44661800,44994140,
+45326456,45658752,45991028,46323280,46655512,46987720,47319908,47652072,
+47984212,48316332,48648428,48980500,49312548,49644576,49976580,50308556,
+50640512,50972444,51304352,51636236,51968096,52299928,52631740,52963524,
+53295284,53627020,53958728,54290412,54622068,54953704,55285308,55616888,
+55948444,56279972,56611472,56942948,57274396,57605816,57937212,58268576,
+58599916,58931228,59262512,59593768,59924992,60256192,60587364,60918508,
+61249620,61580704,61911760,62242788,62573788,62904756,63235692,63566604,
+63897480,64228332,64559148,64889940,65220696,65551424,65882120,66212788,
+66543420,66874024,67204600,67535136,67865648,68196120,68526568,68856984,
+69187360,69517712,69848024,70178304,70508560,70838776,71168960,71499112,
+71829224,72159312,72489360,72819376,73149360,73479304,73809216,74139096,
+74468936,74798744,75128520,75458264,75787968,76117632,76447264,76776864,
+77106424,77435952,77765440,78094888,78424304,78753688,79083032,79412336,
+79741608,80070840,80400032,80729192,81058312,81387392,81716432,82045440,
+82374408,82703336,83032224,83361080,83689896,84018664,84347400,84676096,
+85004760,85333376,85661952,85990488,86318984,86647448,86975864,87304240,
+87632576,87960872,88289128,88617344,88945520,89273648,89601736,89929792,
+90257792,90585760,90913688,91241568,91569408,91897200,92224960,92552672,
+92880336,93207968,93535552,93863088,94190584,94518040,94845448,95172816,
+95500136,95827416,96154648,96481832,96808976,97136080,97463136,97790144,
+98117112,98444032,98770904,99097736,99424520,99751256,100077944,100404592,
+100731192,101057744,101384248,101710712,102037128,102363488,102689808,103016080,
+103342312,103668488,103994616,104320696,104646736,104972720,105298656,105624552,
+105950392,106276184,106601928,106927624,107253272,107578872,107904416,108229920,
+108555368,108880768,109206120,109531416,109856664,110181872,110507016,110832120,
+111157168,111482168,111807112,112132008,112456856,112781648,113106392,113431080,
+113755720,114080312,114404848,114729328,115053760,115378136,115702464,116026744,
+116350960,116675128,116999248,117323312,117647320,117971272,118295176,118619024,
+118942816,119266560,119590248,119913880,120237456,120560984,120884456,121207864,
+121531224,121854528,122177784,122500976,122824112,123147200,123470224,123793200,
+124116120,124438976,124761784,125084528,125407224,125729856,126052432,126374960,
+126697424,127019832,127342184,127664472,127986712,128308888,128631008,128953072,
+129275080,129597024,129918912,130240744,130562520,130884232,131205888,131527480,
+131849016,132170496,132491912,132813272,133134576,133455816,133776992,134098120,
+134419184,134740176,135061120,135382000,135702816,136023584,136344272,136664912,
+136985488,137306016,137626464,137946864,138267184,138587456,138907664,139227808,
+139547904,139867920,140187888,140507776,140827616,141147392,141467104,141786752,
+142106336,142425856,142745312,143064720,143384048,143703312,144022512,144341664,
+144660736,144979744,145298704,145617584,145936400,146255168,146573856,146892480,
+147211040,147529536,147847968,148166336,148484640,148802880,149121056,149439152,
+149757200,150075168,150393072,150710912,151028688,151346400,151664048,151981616,
+152299136,152616576,152933952,153251264,153568496,153885680,154202784,154519824,
+154836784,155153696,155470528,155787296,156104000,156420624,156737200,157053696,
+157370112,157686480,158002768,158318976,158635136,158951216,159267232,159583168,
+159899040,160214848,160530592,160846256,161161840,161477376,161792832,162108208,
+162423520,162738768,163053952,163369040,163684080,163999040,164313936,164628752,
+164943504,165258176,165572784,165887312,166201776,166516160,166830480,167144736,
+167458912,167773008,168087040,168400992,168714880,169028688,169342432,169656096,
+169969696,170283216,170596672,170910032,171223344,171536576,171849728,172162800,
+172475808,172788736,173101600,173414384,173727104,174039728,174352288,174664784,
+174977200,175289536,175601792,175913984,176226096,176538144,176850096,177161984,
+177473792,177785536,178097200,178408784,178720288,179031728,179343088,179654368,
+179965568,180276704,180587744,180898720,181209616,181520448,181831184,182141856,
+182452448,182762960,183073408,183383760,183694048,184004240,184314368,184624416,
+184934400,185244288,185554096,185863840,186173504,186483072,186792576,187102000,
+187411344,187720608,188029808,188338912,188647936,188956896,189265760,189574560,
+189883264,190191904,190500448,190808928,191117312,191425632,191733872,192042016,
+192350096,192658096,192966000,193273840,193581584,193889264,194196848,194504352,
+194811792,195119136,195426400,195733584,196040688,196347712,196654656,196961520,
+197268304,197574992,197881616,198188144,198494592,198800960,199107248,199413456,
+199719584,200025616,200331584,200637456,200943248,201248960,201554576,201860128,
+202165584,202470960,202776256,203081456,203386592,203691632,203996592,204301472,
+204606256,204910976,205215600,205520144,205824592,206128960,206433248,206737456,
+207041584,207345616,207649568,207953424,208257216,208560912,208864512,209168048,
+209471488,209774832,210078112,210381296,210684384,210987408,211290336,211593184,
+211895936,212198608,212501184,212803680,213106096,213408432,213710672,214012816,
+214314880,214616864,214918768,215220576,215522288,215823920,216125472,216426928,
+216728304,217029584,217330784,217631904,217932928,218233856,218534704,218835472,
+219136144,219436720,219737216,220037632,220337952,220638192,220938336,221238384,
+221538352,221838240,222138032,222437728,222737344,223036880,223336304,223635664,
+223934912,224234096,224533168,224832160,225131072,225429872,225728608,226027232,
+226325776,226624240,226922608,227220880,227519056,227817152,228115168,228413088,
+228710912,229008640,229306288,229603840,229901312,230198688,230495968,230793152,
+231090256,231387280,231684192,231981024,232277760,232574416,232870960,233167440,
+233463808,233760096,234056288,234352384,234648384,234944304,235240128,235535872,
+235831504,236127056,236422512,236717888,237013152,237308336,237603424,237898416,
+238193328,238488144,238782864,239077488,239372016,239666464,239960816,240255072,
+240549232,240843312,241137280,241431168,241724960,242018656,242312256,242605776,
+242899200,243192512,243485744,243778896,244071936,244364880,244657744,244950496,
+245243168,245535744,245828224,246120608,246412912,246705104,246997216,247289216,
+247581136,247872960,248164688,248456320,248747856,249039296,249330640,249621904,
+249913056,250204128,250495088,250785968,251076736,251367424,251658016,251948512,
+252238912,252529200,252819408,253109520,253399536,253689456,253979280,254269008,
+254558640,254848176,255137632,255426976,255716224,256005376,256294432,256583392,
+256872256,257161024,257449696,257738272,258026752,258315136,258603424,258891600,
+259179696,259467696,259755600,260043392,260331104,260618704,260906224,261193632,
+261480960,261768176,262055296,262342320,262629248,262916080,263202816,263489456,
+263776000,264062432,264348784,264635024,264921168,265207216,265493168,265779024,
+266064784,266350448,266636000,266921472,267206832,267492096,267777264,268062336,
+268347312,268632192,268916960,269201632,269486208,269770688,270055072,270339360,
+270623552,270907616,271191616,271475488,271759296,272042976,272326560,272610048,
+272893440,273176736,273459936,273743040,274026048,274308928,274591744,274874432,
+275157024,275439520,275721920,276004224,276286432,276568512,276850528,277132416,
+277414240,277695936,277977536,278259040,278540448,278821728,279102944,279384032,
+279665056,279945952,280226752,280507456,280788064,281068544,281348960,281629248,
+281909472,282189568,282469568,282749440,283029248,283308960,283588544,283868032,
+284147424,284426720,284705920,284985024,285264000,285542912,285821696,286100384,
+286378976,286657440,286935840,287214112,287492320,287770400,288048384,288326240,
+288604032,288881696,289159264,289436768,289714112,289991392,290268576,290545632,
+290822592,291099456,291376224,291652896,291929440,292205888,292482272,292758528,
+293034656,293310720,293586656,293862496,294138240,294413888,294689440,294964864,
+295240192,295515424,295790560,296065600,296340512,296615360,296890080,297164704,
+297439200,297713632,297987936,298262144,298536256,298810240,299084160,299357952,
+299631648,299905248,300178720,300452128,300725408,300998592,301271680,301544640,
+301817536,302090304,302362976,302635520,302908000,303180352,303452608,303724768,
+303996800,304268768,304540608,304812320,305083968,305355520,305626944,305898272,
+306169472,306440608,306711616,306982528,307253344,307524064,307794656,308065152,
+308335552,308605856,308876032,309146112,309416096,309685984,309955744,310225408,
+310494976,310764448,311033824,311303072,311572224,311841280,312110208,312379040,
+312647776,312916416,313184960,313453376,313721696,313989920,314258016,314526016,
+314793920,315061728,315329408,315597024,315864512,316131872,316399168,316666336,
+316933408,317200384,317467232,317733984,318000640,318267200,318533632,318799968,
+319066208,319332352,319598368,319864288,320130112,320395808,320661408,320926912,
+321192320,321457632,321722816,321987904,322252864,322517760,322782528,323047200,
+323311744,323576192,323840544,324104800,324368928,324632992,324896928,325160736,
+325424448,325688096,325951584,326215008,326478304,326741504,327004608,327267584,
+327530464,327793248,328055904,328318496,328580960,328843296,329105568,329367712,
+329629760,329891680,330153536,330415264,330676864,330938400,331199808,331461120,
+331722304,331983392,332244384,332505280,332766048,333026752,333287296,333547776,
+333808128,334068384,334328544,334588576,334848512,335108352,335368064,335627712,
+335887200,336146624,336405920,336665120,336924224,337183200,337442112,337700864,
+337959552,338218112,338476576,338734944,338993184,339251328,339509376,339767296,
+340025120,340282848,340540480,340797984,341055392,341312704,341569888,341826976,
+342083968,342340832,342597600,342854272,343110848,343367296,343623648,343879904,
+344136032,344392064,344648000,344903808,345159520,345415136,345670656,345926048,
+346181344,346436512,346691616,346946592,347201440,347456224,347710880,347965440,
+348219872,348474208,348728448,348982592,349236608,349490528,349744320,349998048,
+350251648,350505152,350758528,351011808,351264992,351518048,351771040,352023872,
+352276640,352529280,352781824,353034272,353286592,353538816,353790944,354042944,
+354294880,354546656,354798368,355049952,355301440,355552800,355804096,356055264,
+356306304,356557280,356808128,357058848,357309504,357560032,357810464,358060768,
+358311008,358561088,358811104,359060992,359310784,359560480,359810048,360059520,
+360308896,360558144,360807296,361056352,361305312,361554144,361802880,362051488,
+362300032,362548448,362796736,363044960,363293056,363541024,363788928,364036704,
+364284384,364531936,364779392,365026752,365274016,365521152,365768192,366015136,
+366261952,366508672,366755296,367001792,367248192,367494496,367740704,367986784,
+368232768,368478656,368724416,368970080,369215648,369461088,369706432,369951680,
+370196800,370441824,370686752,370931584,371176288,371420896,371665408,371909792,
+372154080,372398272,372642336,372886304,373130176,373373952,373617600,373861152,
+374104608,374347936,374591168,374834304,375077312,375320224,375563040,375805760,
+376048352,376290848,376533248,376775520,377017696,377259776,377501728,377743584,
+377985344,378227008,378468544,378709984,378951328,379192544,379433664,379674688,
+379915584,380156416,380397088,380637696,380878176,381118560,381358848,381599040,
+381839104,382079072,382318912,382558656,382798304,383037856,383277280,383516640,
+383755840,383994976,384233984,384472896,384711712,384950400,385188992,385427488,
+385665888,385904160,386142336,386380384,386618368,386856224,387093984,387331616,
+387569152,387806592,388043936,388281152,388518272,388755296,388992224,389229024,
+389465728,389702336,389938816,390175200,390411488,390647680,390883744,391119712,
+391355584,391591328,391826976,392062528,392297984,392533312,392768544,393003680,
+393238720,393473632,393708448,393943168,394177760,394412256,394646656,394880960,
+395115136,395349216,395583200,395817088,396050848,396284512,396518080,396751520,
+396984864,397218112,397451264,397684288,397917248,398150080,398382784,398615424,
+398847936,399080320,399312640,399544832,399776928,400008928,400240832,400472608,
+400704288,400935872,401167328,401398720,401629984,401861120,402092192,402323136,
+402553984,402784736,403015360,403245888,403476320,403706656,403936896,404167008,
+404397024,404626944,404856736,405086432,405316032,405545536,405774912,406004224,
+406233408,406462464,406691456,406920320,407149088,407377760,407606336,407834784,
+408063136,408291392,408519520,408747584,408975520,409203360,409431072,409658720,
+409886240,410113664,410340992,410568192,410795296,411022304,411249216,411476032,
+411702720,411929312,412155808,412382176,412608480,412834656,413060736,413286720,
+413512576,413738336,413964000,414189568,414415040,414640384,414865632,415090784,
+415315840,415540800,415765632,415990368,416215008,416439552,416663968,416888288,
+417112512,417336640,417560672,417784576,418008384,418232096,418455712,418679200,
+418902624,419125920,419349120,419572192,419795200,420018080,420240864,420463552,
+420686144,420908608,421130976,421353280,421575424,421797504,422019488,422241344,
+422463104,422684768,422906336,423127776,423349120,423570400,423791520,424012576,
+424233536,424454368,424675104,424895744,425116288,425336736,425557056,425777280,
+425997408,426217440,426437376,426657184,426876928,427096544,427316064,427535488,
+427754784,427974016,428193120,428412128,428631040,428849856,429068544,429287168,
+429505664,429724064,429942368,430160576,430378656,430596672,430814560,431032352,
+431250048,431467616,431685120,431902496,432119808,432336992,432554080,432771040,
+432987936,433204736,433421408,433637984,433854464,434070848,434287104,434503296,
+434719360,434935360,435151232,435367008,435582656,435798240,436013696,436229088,
+436444352,436659520,436874592,437089568,437304416,437519200,437733856,437948416,
+438162880,438377248,438591520,438805696,439019744,439233728,439447584,439661344,
+439875008,440088576,440302048,440515392,440728672,440941824,441154880,441367872,
+441580736,441793472,442006144,442218720,442431168,442643552,442855808,443067968,
+443280032,443492000,443703872,443915648,444127296,444338880,444550336,444761696,
+444972992,445184160,445395232,445606176,445817056,446027840,446238496,446449088,
+446659552,446869920,447080192,447290400,447500448,447710432,447920320,448130112,
+448339776,448549376,448758848,448968224,449177536,449386720,449595808,449804800,
+450013664,450222464,450431168,450639776,450848256,451056640,451264960,451473152,
+451681248,451889248,452097152,452304960,452512672,452720288,452927808,453135232,
+453342528,453549760,453756864,453963904,454170816,454377632,454584384,454791008,
+454997536,455203968,455410304,455616544,455822688,456028704,456234656,456440512,
+456646240,456851904,457057472,457262912,457468256,457673536,457878688,458083744,
+458288736,458493600,458698368,458903040,459107616,459312096,459516480,459720768,
+459924960,460129056,460333056,460536960,460740736,460944448,461148064,461351584,
+461554976,461758304,461961536,462164640,462367680,462570592,462773440,462976160,
+463178816,463381344,463583776,463786144,463988384,464190560,464392608,464594560,
+464796448,464998208,465199872,465401472,465602944,465804320,466005600,466206816,
+466407904,466608896,466809824,467010624,467211328,467411936,467612480,467812896,
+468013216,468213440,468413600,468613632,468813568,469013440,469213184,469412832,
+469612416,469811872,470011232,470210528,470409696,470608800,470807776,471006688,
+471205472,471404192,471602784,471801312,471999712,472198048,472396288,472594400,
+472792448,472990400,473188256,473385984,473583648,473781216,473978688,474176064,
+474373344,474570528,474767616,474964608,475161504,475358336,475555040,475751648,
+475948192,476144608,476340928,476537184,476733312,476929376,477125344,477321184,
+477516960,477712640,477908224,478103712,478299104,478494400,478689600,478884704,
+479079744,479274656,479469504,479664224,479858880,480053408,480247872,480442240,
+480636512,480830656,481024736,481218752,481412640,481606432,481800128,481993760,
+482187264,482380704,482574016,482767264,482960416,483153472,483346432,483539296,
+483732064,483924768,484117344,484309856,484502240,484694560,484886784,485078912,
+485270944,485462880,485654720,485846464,486038144,486229696,486421184,486612576,
+486803840,486995040,487186176,487377184,487568096,487758912,487949664,488140320,
+488330880,488521312,488711712,488901984,489092160,489282240,489472256,489662176,
+489851968,490041696,490231328,490420896,490610336,490799712,490988960,491178144,
+491367232,491556224,491745120,491933920,492122656,492311264,492499808,492688256,
+492876608,493064864,493253056,493441120,493629120,493817024,494004832,494192544,
+494380160,494567712,494755136,494942496,495129760,495316928,495504000,495691008,
+495877888,496064704,496251424,496438048,496624608,496811040,496997408,497183680,
+497369856,497555936,497741920,497927840,498113632,498299360,498484992,498670560,
+498856000,499041376,499226656,499411840,499596928,499781920,499966848,500151680,
+500336416,500521056,500705600,500890080,501074464,501258752,501442944,501627040,
+501811072,501995008,502178848,502362592,502546240,502729824,502913312,503096704,
+503280000,503463232,503646368,503829408,504012352,504195200,504377984,504560672,
+504743264,504925760,505108192,505290496,505472736,505654912,505836960,506018944,
+506200832,506382624,506564320,506745952,506927488,507108928,507290272,507471552,
+507652736,507833824,508014816,508195744,508376576,508557312,508737952,508918528,
+509099008,509279392,509459680,509639904,509820032,510000064,510180000,510359872,
+510539648,510719328,510898944,511078432,511257856,511437216,511616448,511795616,
+511974688,512153664,512332576,512511392,512690112,512868768,513047296,513225792,
+513404160,513582432,513760640,513938784,514116800,514294752,514472608,514650368,
+514828064,515005664,515183168,515360608,515537952,515715200,515892352,516069440,
+516246432,516423328,516600160,516776896,516953536,517130112,517306592,517482976,
+517659264,517835488,518011616,518187680,518363648,518539520,518715296,518891008,
+519066624,519242144,519417600,519592960,519768256,519943424,520118528,520293568,
+520468480,520643328,520818112,520992800,521167392,521341888,521516320,521690656,
+521864896,522039072,522213152,522387168,522561056,522734912,522908640,523082304,
+523255872,523429376,523602784,523776096,523949312,524122464,524295552,524468512,
+524641440,524814240,524986976,525159616,525332192,525504640,525677056,525849344,
+526021568,526193728,526365792,526537760,526709632,526881440,527053152,527224800,
+527396352,527567840,527739200,527910528,528081728,528252864,528423936,528594880,
+528765760,528936576,529107296,529277920,529448480,529618944,529789344,529959648,
+530129856,530300000,530470048,530640000,530809888,530979712,531149440,531319072,
+531488608,531658080,531827488,531996800,532166016,532335168,532504224,532673184,
+532842080,533010912,533179616,533348288,533516832,533685312,533853728,534022048,
+534190272,534358432,534526496,534694496,534862400,535030240,535197984,535365632,
+535533216,535700704,535868128,536035456,536202720,536369888,536536992,536704000,
+536870912
+};
+
--- /dev/null
+++ b/src/heretic/v_video.c
@@ -1,0 +1,200 @@
+
+// V_video.c
+
+#include "DoomDef.h"
+
+#define SC_INDEX 0x3c4
+
+byte *screen;
+int dirtybox[4];
+int usegamma;
+
+byte gammatable[5][256] =
+{
+{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255},
+
+{2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,24,25,26,27,29,30,31,32,33,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,54,55,56,57,58,59,60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,163,164,165,166,167,168,169,170,171,172,173,174,175,175,176,177,178,179,180,181,182,183,184,185,186,186,187,188,189,190,191,192,193,194,195,196,196,197,198,199,200,201,202,203,204,205,205,206,207,208,209,210,211,212,213,214,214,215,216,217,218,219,220,221,222,222,223,224,225,226,227,228,229,230,230,231,232,233,234,235,236,237,237,238,239,240,241,242,243,244,245,245,246,247,248,249,250,251,252,252,253,254,255},
+
+{4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,33,35,36,38,39,40,42,43,45,46,47,48,50,51,52,54,55,56,57,59,60,61,62,63,65,66,67,68,69,70,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159,160,160,161,162,163,164,165,166,166,167,168,169,170,171,172,172,173,174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,187,188,188,189,190,191,192,193,193,194,195,196,197,197,198,199,200,201,201,202,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,216,217,217,218,219,220,221,221,222,223,224,224,225,226,227,228,228,229,230,231,231,232,233,234,235,235,236,237,238,238,239,240,241,241,242,243,244,244,245,246,247,247,248,249,250,251,251,252,253,254,254,255},
+
+{8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,47,49,50,52,53,55,57,58,60,61,63,64,65,67,68,70,71,72,74,75,76,77,79,80,81,82,84,85,86,87,88,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,135,136,137,138,139,140,141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,164,165,165,166,167,168,169,169,170,171,172,173,173,174,175,176,176,177,178,179,180,180,181,182,183,183,184,185,186,186,187,188,189,189,190,191,192,192,193,194,195,195,196,197,197,198,199,200,200,201,202,202,203,204,205,205,206,207,207,208,209,210,210,211,212,212,213,214,214,215,216,216,217,218,219,219,220,221,221,222,223,223,224,225,225,226,227,227,228,229,229,230,231,231,232,233,233,234,235,235,236,237,237,238,238,239,240,240,241,242,242,243,244,244,245,246,246,247,247,248,249,249,250,251,251,252,253,253,254,254,255},
+
+{16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,66,68,69,71,73,75,76,78,80,81,83,84,86,87,89,90,92,93,94,96,97,98,100,101,102,103,105,106,107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169,169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,181,182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193,193,194,195,195,196,196,197,198,198,199,200,200,201,202,202,203,203,204,205,205,206,207,207,208,208,209,210,210,211,211,212,213,213,214,214,215,216,216,217,217,218,219,219,220,220,221,221,222,223,223,224,224,225,225,226,227,227,228,228,229,229,230,230,231,232,232,233,233,234,234,235,235,236,236,237,237,238,239,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,254,254,255,255}
+};
+
+//---------------------------------------------------------------------------
+//
+// PROC V_DrawPatch
+//
+// Draws a column based masked pic to the screen.
+//
+//---------------------------------------------------------------------------
+
+void V_DrawPatch(int x, int y, patch_t *patch)
+{
+	int count;
+	int col;
+	column_t *column;
+	byte *desttop;
+	byte *dest;
+	byte *source;
+	int w;
+
+	y -= SHORT(patch->topoffset);
+	x -= SHORT(patch->leftoffset);
+	if(x < 0 || x+SHORT(patch->width) > SCREENWIDTH || y < 0
+		|| y+SHORT(patch->height) > SCREENHEIGHT)
+	{
+		I_Error("Bad V_DrawPatch");
+	}
+	col = 0;
+	desttop = screen+y*SCREENWIDTH+x;
+	w = SHORT(patch->width);
+	for(; col < w; x++, col++, desttop++)
+	{
+		column = (column_t *)((byte *)patch+LONG(patch->columnofs[col]));
+		// 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);
+		}
+	}
+}
+
+/*
+==================
+=
+= V_DrawFuzzPatch
+=
+= Masks a column based translucent masked pic to the screen.
+=
+==================
+*/
+extern byte *tinttable;
+
+void V_DrawFuzzPatch (int x, int y, patch_t *patch)
+{
+	int			count,col;
+	column_t	*column;
+	byte		*desttop, *dest, *source;
+	int			w;
+	
+	y -= SHORT(patch->topoffset);
+	x -= SHORT(patch->leftoffset);
+
+	if (x<0||x+SHORT(patch->width) >SCREENWIDTH || y<0 || y+SHORT(patch->height)>SCREENHEIGHT
)
+		I_Error ("Bad V_DrawPatch");
+
+	col = 0;
+	desttop = screen+y*SCREENWIDTH+x;
+	
+	w = SHORT(patch->width);
+	for ( ; col<w ; x++, col++, desttop++)
+	{
+		column = (column_t *)((byte *)patch + LONG(patch->columnofs[col]));
+
+// 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 = tinttable[((*dest)<<8) + *source++];
+				dest += SCREENWIDTH;
+			}
+			column = (column_t *)(  (byte *)column + column->length
++ 4 );
+		}
+	}			
+}
+
+/*
+==================
+=
+= V_DrawShadowedPatch
+=
+= Masks a column based masked pic to the screen.
+=
+==================
+*/
+
+void V_DrawShadowedPatch(int x, int y, patch_t *patch)
+{
+	int			count,col;
+	column_t	*column;
+	byte		*desttop, *dest, *source;
+	byte		*desttop2, *dest2;
+	int			w;
+	
+	y -= SHORT(patch->topoffset);
+	x -= SHORT(patch->leftoffset);
+
+	if (x<0||x+SHORT(patch->width) >SCREENWIDTH || y<0 || y+SHORT(patch->height)>SCREENHEIGHT
)
+		I_Error ("Bad V_DrawPatch");
+
+	col = 0;
+	desttop = screen+y*SCREENWIDTH+x;
+	desttop2 = screen+(y+2)*SCREENWIDTH+x+2;
+	
+	w = SHORT(patch->width);
+	for ( ; col<w ; x++, col++, desttop++, desttop2++)
+	{
+		column = (column_t *)((byte *)patch + LONG(patch->columnofs[col]));
+
+// step through the posts in a column
+	
+		while (column->topdelta != 0xff )
+		{
+			source = (byte *)column + 3;
+			dest = desttop + column->topdelta*SCREENWIDTH;
+			dest2 = desttop2 + column->topdelta*SCREENWIDTH;
+			count = column->length;
+			
+			while (count--)
+			{
+				*dest2 = tinttable[((*dest2)<<8)];
+				dest2 += SCREENWIDTH;
+				*dest = *source++;
+				dest += SCREENWIDTH;
+
+			}
+			column = (column_t *)(  (byte *)column + column->length
++ 4 );
+		}
+	}			
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC V_DrawRawScreen
+//
+//---------------------------------------------------------------------------
+
+void V_DrawRawScreen(byte *raw)
+{
+	memcpy(screen, raw, SCREENWIDTH*SCREENHEIGHT);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC V_Init
+//
+//---------------------------------------------------------------------------
+
+void V_Init(void)
+{
+	// I_AllocLow will put screen in low dos memory on PCs.
+	screen = I_AllocLow(SCREENWIDTH*SCREENHEIGHT);
+}
--- /dev/null
+++ b/src/heretic/vgaview.h
@@ -1,0 +1,24 @@
+
+#import <appkit/appkit.h>
+
+#import "DoomDef.h"
+
+// a few globals
+extern byte	*bytebuffer;
+
+
+@interface VGAView:View
+{
+    id		game;
+    int		nextpalette[256];	// color lookup table
+    int		*nextimage;		// palette expanded and scaled
+    unsigned	scale;
+    NXWindowDepth	depth;
+}
+
+- updateView;
+- (unsigned)scale;
+- setPalette:(byte *)pal;
+- setScale:(int)newscale;
+
+@end
--- /dev/null
+++ b/src/heretic/w_wad.c
@@ -1,0 +1,495 @@
+// W_wad.c
+
+#ifdef NeXT
+#include <libc.h>
+#include <ctype.h>
+
+// next doesn't need a binary flag in open call
+#define	O_BINARY	0
+
+#else
+
+#include <malloc.h>
+#include <io.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#endif
+
+#include "DoomDef.h"
+
+//===============
+//   TYPES
+//===============
+
+
+typedef struct
+{
+	char		identification[4];		// should be IWAD
+	int			numlumps;
+	int			infotableofs;
+} wadinfo_t;
+
+
+typedef struct
+{
+	int			filepos;
+	int			size;
+	char		name[8];
+} filelump_t;
+
+
+//=============
+// GLOBALS
+//=============
+
+lumpinfo_t	*lumpinfo;		// location of each lump on disk
+int			numlumps;
+
+void		**lumpcache;
+
+
+//===================
+
+#ifdef NeXT
+
+#define strcmpi strcasecmp
+
+void strupr (char *s)
+{
+    while (*s)
+	*s++ = toupper(*s);
+}
+
+/*
+================
+=
+= filelength
+=
+================
+*/
+
+int filelength (int handle)
+{
+    struct stat	fileinfo;
+    
+    if (fstat (handle,&fileinfo) == -1)
+	I_Error ("Error fstating");
+
+    return fileinfo.st_size;
+}
+
+#endif
+
+
+void ExtractFileBase (char *path, char *dest)
+{
+	char	*src;
+	int		length;
+
+	src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+	while (src != path && *(src-1) != '\\' && *(src-1) != '/')
+		src--;
+
+//
+// copy up to eight characters
+//
+	memset (dest,0,8);
+	length = 0;
+	while (*src && *src != '.')
+	{
+		if (++length == 9)
+			I_Error ("Filename base of %s >8 chars",path);
+		*dest++ = toupper((int)*src++);
+	}
+}
+
+/*
+============================================================================
+
+						LUMP BASED ROUTINES
+
+============================================================================
+*/
+
+/*
+====================
+=
+= W_AddFile
+=
+= All files are optional, but at least one file must be found
+= Files with a .wad extension are wadlink files with multiple lumps
+= Other files are single lumps with the base filename for the lump name
+=
+====================
+*/
+
+void W_AddFile (char *filename)
+{
+	wadinfo_t		header;
+	lumpinfo_t		*lump_p;
+	unsigned		i;
+	int				handle, length;
+	int				startlump;
+	filelump_t		*fileinfo, singleinfo;
+	
+//
+// open the file and add to directory
+//	
+	if ( (handle = open (filename,O_RDONLY | O_BINARY)) == -1)
+		return;
+
+	startlump = numlumps;
+	
+	if (strcmpi (filename+strlen(filename)-3 , "wad" ) )
+	{
+	// single lump file
+		fileinfo = &singleinfo;
+		singleinfo.filepos = 0;
+		singleinfo.size = LONG(filelength(handle));
+		ExtractFileBase (filename, singleinfo.name);
+		numlumps++;
+	}
+	else 
+	{
+	// WAD file
+		read (handle, &header, sizeof(header));
+		if (strncmp(header.identification,"IWAD",4))
+		{
+			if (strncmp(header.identification,"PWAD",4))
+				I_Error ("Wad file %s doesn't have IWAD or PWAD id\n"
+				,filename);
+		}
+		header.numlumps = LONG(header.numlumps);
+		header.infotableofs = LONG(header.infotableofs);
+		length = header.numlumps*sizeof(filelump_t);
+		fileinfo = alloca (length);
+		lseek (handle, header.infotableofs, SEEK_SET);
+		read (handle, fileinfo, length);
+		numlumps += header.numlumps;
+	}
+
+//
+// Fill in lumpinfo
+//
+	lumpinfo = realloc (lumpinfo, numlumps*sizeof(lumpinfo_t));
+	if (!lumpinfo)
+		I_Error ("Couldn't realloc lumpinfo");
+	lump_p = &lumpinfo[startlump];
+	
+	for (i=startlump ; i<numlumps ; i++,lump_p++, fileinfo++)
+	{
+		lump_p->handle = handle;
+		lump_p->position = LONG(fileinfo->filepos);
+		lump_p->size = LONG(fileinfo->size);
+		strncpy (lump_p->name, fileinfo->name, 8);
+	}
+}
+
+
+
+
+
+/*
+====================
+=
+= W_InitMultipleFiles
+=
+= Pass a null terminated list of files to use.
+=
+= All files are optional, but at least one file must be found
+=
+= Files with a .wad extension are idlink files with multiple lumps
+=
+= Other files are single lumps with the base filename for the lump name
+=
+= Lump names can appear multiple times. The name searcher looks backwards,
+= so a later file can override an earlier one.
+=
+====================
+*/
+
+void W_InitMultipleFiles (char **filenames)
+{	
+	int		size;
+	
+//
+// open all the files, load headers, and count lumps
+//
+	numlumps = 0;
+	lumpinfo = malloc(1);	// will be realloced as lumps are added
+
+	for ( ; *filenames ; filenames++)
+		W_AddFile (*filenames);
+
+	if (!numlumps)
+		I_Error ("W_InitFiles: no files found");
+		
+//
+// set up caching
+//
+	size = numlumps * sizeof(*lumpcache);
+	lumpcache = malloc (size);
+	if (!lumpcache)
+		I_Error ("Couldn't allocate lumpcache");
+	memset (lumpcache,0, size);
+}
+
+
+
+/*
+====================
+=
+= W_InitFile
+=
+= Just initialize from a single file
+=
+====================
+*/
+
+void W_InitFile (char *filename)
+{
+	char	*names[2];
+
+	names[0] = filename;
+	names[1] = NULL;
+	W_InitMultipleFiles (names);
+}
+
+
+
+/*
+====================
+=
+= W_NumLumps
+=
+====================
+*/
+
+int	W_NumLumps (void)
+{
+	return numlumps;
+}
+
+
+
+/*
+====================
+=
+= W_CheckNumForName
+=
+= Returns -1 if name not found
+=
+====================
+*/
+
+int	W_CheckNumForName (char *name)
+{
+	char	name8[9];
+	int		v1,v2;
+	lumpinfo_t	*lump_p;
+
+// make the name into two integers for easy compares
+
+	strncpy (name8,name,8);
+	name8[8] = 0;			// in case the name was a fill 8 chars
+	strupr (name8);			// case insensitive
+
+	v1 = *(int *)name8;
+	v2 = *(int *)&name8[4];
+
+
+// scan backwards so patch lump files take precedence
+
+	lump_p = lumpinfo + numlumps;
+
+	while (lump_p-- != lumpinfo)
+		if ( *(int *)lump_p->name == v1 && *(int *)&lump_p->name[4] == v2)
+			return lump_p - lumpinfo;
+
+
+	return -1;
+}
+
+
+/*
+====================
+=
+= W_GetNumForName
+=
+= Calls W_CheckNumForName, but bombs out if not found
+=
+====================
+*/
+
+int	W_GetNumForName (char *name)
+{
+	int	i;
+
+	i = W_CheckNumForName (name);
+	if (i != -1)
+		return i;
+
+	I_Error ("W_GetNumForName: %s not found!",name);
+	return -1;
+}
+
+
+/*
+====================
+=
+= W_LumpLength
+=
+= Returns the buffer size needed to load the given lump
+=
+====================
+*/
+
+int W_LumpLength (int lump)
+{
+	if (lump >= numlumps)
+		I_Error ("W_LumpLength: %i >= numlumps",lump);
+	return lumpinfo[lump].size;
+}
+
+
+/*
+====================
+=
+= W_ReadLump
+=
+= Loads the lump into the given buffer, which must be >= W_LumpLength()
+=
+====================
+*/
+
+void W_ReadLump (int lump, void *dest)
+{
+	int			c;
+	lumpinfo_t	*l;
+	
+	if (lump >= numlumps)
+		I_Error ("W_ReadLump: %i >= numlumps",lump);
+	l = lumpinfo+lump;
+	
+	I_BeginRead ();
+	
+	lseek (l->handle, l->position, SEEK_SET);
+	c = read (l->handle, dest, l->size);
+	if (c < l->size)
+		I_Error ("W_ReadLump: only read %i of %i on lump %i",c,l->size,lump);	
+	I_EndRead ();
+}
+
+
+
+/*
+====================
+=
+= W_CacheLumpNum
+=
+====================
+*/
+
+void	*W_CacheLumpNum (int lump, int tag)
+{
+byte *ptr;
+
+	if ((unsigned)lump >= numlumps)
+		I_Error ("W_CacheLumpNum: %i >= numlumps",lump);
+		
+	if (!lumpcache[lump])
+	{	// read the lump in
+//printf ("cache miss on lump %i\n",lump);
+		ptr = Z_Malloc (W_LumpLength (lump), tag, &lumpcache[lump]);
+		W_ReadLump (lump, lumpcache[lump]);
+	}
+	else
+	{
+//printf ("cache hit on lump %i\n",lump);
+		Z_ChangeTag (lumpcache[lump],tag);
+	}
+	
+	return lumpcache[lump];
+}
+
+
+/*
+====================
+=
+= W_CacheLumpName
+=
+====================
+*/
+
+void	*W_CacheLumpName (char *name, int tag)
+{
+	return W_CacheLumpNum (W_GetNumForName(name), tag);
+}
+
+
+/*
+====================
+=
+= W_Profile
+=
+====================
+*/
+
+// Ripped out for Heretic
+/*
+int	info[2500][10];
+int	profilecount;
+
+void W_Profile (void)
+{
+	int		i;
+	memblock_t	*block;
+	void	*ptr;
+	char	ch;
+	FILE	*f;
+	int		j;
+	char	name[9];
+	
+	
+	for (i=0 ; i<numlumps ; i++)
+	{	
+		ptr = lumpcache[i];
+		if (!ptr)
+		{
+			ch = ' ';
+			continue;
+		}
+		else
+		{
+			block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
+			if (block->tag < PU_PURGELEVEL)
+				ch = 'S';
+			else
+				ch = 'P';
+		}
+		info[i][profilecount] = ch;
+	}
+	profilecount++;
+	
+	f = fopen ("waddump.txt","w");
+	name[8] = 0;
+	for (i=0 ; i<numlumps ; i++)
+	{
+		memcpy (name,lumpinfo[i].name,8);
+		for (j=0 ; j<8 ; j++)
+			if (!name[j])
+				break;
+		for ( ; j<8 ; j++)
+			name[j] = ' ';
+		fprintf (f,"%s ",name);
+		for (j=0 ; j<profilecount ; j++)
+			fprintf (f,"    %c",info[i][j]);
+		fprintf (f,"\n");
+	}
+	fclose (f);
+}
+*/
--- /dev/null
+++ b/src/heretic/z_zone.c
@@ -1,0 +1,389 @@
+// Z_zone.c
+
+#include <stdlib.h>
+#include "DoomDef.h"
+
+/*
+==============================================================================
+
+						ZONE MEMORY ALLOCATION
+
+There is never any space between memblocks, and there will never be two
+contiguous free memblocks.
+
+The rover can be left pointing at a non-empty block
+
+It is of no value to free a cachable block, because it will get overwritten
+automatically if needed
+
+==============================================================================
+*/
+
+#define	ZONEID	0x1d4a11
+
+typedef struct
+{
+	int		size;		// total bytes malloced, including header
+	memblock_t	blocklist;		// start / end cap for linked list
+	memblock_t	*rover;
+} memzone_t;
+
+boolean MallocFailureOk;
+memzone_t *mainzone;
+
+/*
+========================
+=
+= Z_ClearZone
+=
+========================
+*/
+
+void Z_ClearZone (memzone_t *zone)
+{
+	memblock_t	*block;
+	
+// set the entire zone to one free block
+
+	zone->blocklist.next = zone->blocklist.prev = block =
+		(memblock_t *)( (byte *)zone + sizeof(memzone_t) );
+	zone->blocklist.user = (void *)zone;
+	zone->blocklist.tag = PU_STATIC;
+	zone->rover = block;
+	
+	block->prev = block->next = &zone->blocklist;
+	block->user = NULL;	// free block
+	block->size = zone->size - sizeof(memzone_t);
+}
+
+
+/*
+========================
+=
+= Z_Init
+=
+========================
+*/
+
+void Z_Init (void)
+{
+	memblock_t	*block;
+	int		size;
+
+	MallocFailureOk = false;
+	mainzone = (memzone_t *)I_ZoneBase (&size);
+	mainzone->size = size;
+
+// set the entire zone to one free block
+
+	mainzone->blocklist.next = mainzone->blocklist.prev = block =
+		(memblock_t *)( (byte *)mainzone + sizeof(memzone_t) );
+	mainzone->blocklist.user = (void *)mainzone;
+	mainzone->blocklist.tag = PU_STATIC;
+	mainzone->rover = block;
+	
+	block->prev = block->next = &mainzone->blocklist;
+	block->user = NULL;	// free block
+	block->size = mainzone->size - sizeof(memzone_t);
+}
+
+
+/*
+========================
+=
+= Z_Free
+=
+========================
+*/
+
+void Z_Free (void *ptr)
+{
+	memblock_t	*block, *other;
+	
+	block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
+	if (block->id != ZONEID)
+		I_Error ("Z_Free: freed a pointer without ZONEID");
+		
+	if (block->user > (void **)0x100)	// smaller values are not pointers
+		*block->user = 0;		// clear the user's mark
+	block->user = NULL;	// mark as free
+	block->tag = 0;
+	block->id = 0;
+	
+	other = block->prev;
+	if (!other->user)
+	{	// merge with previous free block
+		other->size += block->size;
+		other->next = block->next;
+		other->next->prev = other;
+		if (block == mainzone->rover)
+			mainzone->rover = other;
+		block = other;
+	}
+	
+	other = block->next;
+	if (!other->user)
+	{	// merge the next free block onto the end
+		block->size += other->size;
+		block->next = other->next;
+		block->next->prev = block;
+		if (other == mainzone->rover)
+			mainzone->rover = block;
+	}
+}
+
+
+/*
+========================
+=
+= Z_Malloc
+=
+= You can pass a NULL user if the tag is < PU_PURGELEVEL
+========================
+*/
+
+#define MINFRAGMENT	64
+
+void *Z_Malloc (int size, int tag, void *user)
+{
+	int		extra;
+	memblock_t	*start, *rover, *new, *base;
+
+//
+// scan through the block list looking for the first free block
+// of sufficient size, throwing out any purgable blocks along the way
+//
+	size += sizeof(memblock_t);	// account for size of block header
+	
+	
+//
+// if there is a free block behind the rover, back up over them
+//
+	base = mainzone->rover;
+	if (!base->prev->user)
+		base = base->prev;
+	
+	rover = base;
+	start = base->prev;
+	
+	do
+	{
+		if(rover == start)
+		{ // Scanned all the way around the list
+			if(MallocFailureOk == true)
+			{
+				return NULL;
+			}
+			else
+			{
+				I_Error("Z_Malloc: failed on allocation of %i bytes", size);
+			}
+		}
+		if (rover->user)
+		{
+			if (rover->tag < PU_PURGELEVEL)
+			// hit a block that can't be purged, so move base past it
+				base = rover = rover->next;
+			else
+			{
+			// free the rover block (adding the size to base)
+				base = base->prev;	// the rover can be the base block
+				Z_Free ((byte *)rover+sizeof(memblock_t));
+				base = base->next;
+				rover = base->next;
+			}
+		}
+		else
+			rover = rover->next;
+	} while (base->user || base->size < size);
+	
+//
+// found a block big enough
+//
+	extra = base->size - size;
+	if (extra >  MINFRAGMENT)
+	{	// there will be a free fragment after the allocated block
+		new = (memblock_t *) ((byte *)base + size );
+		new->size = extra;
+		new->user = NULL;		// free block
+		new->tag = 0;
+		new->prev = base;
+		new->next = base->next;
+		new->next->prev = new;
+		base->next = new;
+		base->size = size;
+	}
+	
+	if (user)
+	{
+		base->user = user;			// mark as an in use block
+		*(void **)user = (void *) ((byte *)base + sizeof(memblock_t));
+	}
+	else
+	{
+		if (tag >= PU_PURGELEVEL)
+			I_Error ("Z_Malloc: an owner is required for purgable blocks");
+		base->user = (void *)2;		// mark as in use, but unowned	
+	}
+	base->tag = tag;
+	
+	mainzone->rover = base->next;	// next allocation will start looking here
+	
+	base->id = ZONEID;
+	return (void *) ((byte *)base + sizeof(memblock_t));
+}
+
+
+/*
+========================
+=
+= Z_FreeTags
+=
+========================
+*/
+
+void Z_FreeTags (int lowtag, int hightag)
+{
+	memblock_t	*block, *next;
+	
+	for (block = mainzone->blocklist.next ; block != &mainzone->blocklist 
+	; block = next)
+	{
+		next = block->next;		// get link before freeing
+		if (!block->user)
+			continue;			// free block
+		if (block->tag >= lowtag && block->tag <= hightag)
+			Z_Free ( (byte *)block+sizeof(memblock_t));
+	}
+}
+
+/*
+========================
+=
+= Z_DumpHeap
+=
+========================
+*/
+
+void Z_DumpHeap (int lowtag, int hightag)
+{
+	memblock_t	*block;
+	
+	printf ("zone size: %i  location: %p\n",mainzone->size,mainzone);
+	printf ("tag range: %i to %i\n",lowtag, hightag);
+	
+	for (block = mainzone->blocklist.next ; ; block = block->next)
+	{
+		if (block->tag >= lowtag && block->tag <= hightag)
+			printf ("block:%p    size:%7i    user:%p    tag:%3i\n",
+			block, block->size, block->user, block->tag);
+		
+		if (block->next == &mainzone->blocklist)
+			break;			// all blocks have been hit	
+		if ( (byte *)block + block->size != (byte *)block->next)
+			printf ("ERROR: block size does not touch the next block\n");
+		if ( block->next->prev != block)
+			printf ("ERROR: next block doesn't have proper back link\n");
+		if (!block->user && !block->next->user)
+			printf ("ERROR: two consecutive free blocks\n");
+	}
+}
+
+/*
+========================
+=
+= Z_FileDumpHeap
+=
+========================
+*/
+
+void Z_FileDumpHeap (FILE *f)
+{
+	memblock_t	*block;
+	
+	fprintf (f,"zone size: %i  location: %p\n",mainzone->size,mainzone);
+	
+	for (block = mainzone->blocklist.next ; ; block = block->next)
+	{
+		fprintf (f,"block:%p    size:%7i    user:%p    tag:%3i\n",
+		block, block->size, block->user, block->tag);
+		
+		if (block->next == &mainzone->blocklist)
+			break;			// all blocks have been hit	
+		if ( (byte *)block + block->size != (byte *)block->next)
+			fprintf (f,"ERROR: block size does not touch the next block\n");
+		if ( block->next->prev != block)
+			fprintf (f,"ERROR: next block doesn't have proper back link\n");
+		if (!block->user && !block->next->user)
+			fprintf (f,"ERROR: two consecutive free blocks\n");
+	}
+}
+
+/*
+========================
+=
+= Z_CheckHeap
+=
+========================
+*/
+
+void Z_CheckHeap (void)
+{
+	memblock_t	*block;
+	
+	for (block = mainzone->blocklist.next ; ; block = block->next)
+	{
+		if (block->next == &mainzone->blocklist)
+			break;			// all blocks have been hit	
+		if ( (byte *)block + block->size != (byte *)block->next)
+			I_Error ("Z_CheckHeap: block size does not touch the next block\n");
+		if ( block->next->prev != block)
+			I_Error ("Z_CheckHeap: next block doesn't have proper back link\n");
+		if (!block->user && !block->next->user)
+			I_Error ("Z_CheckHeap: two consecutive free blocks\n");
+	}
+}
+
+
+/*
+========================
+=
+= Z_ChangeTag
+=
+========================
+*/
+
+void Z_ChangeTag2 (void *ptr, int tag)
+{
+	memblock_t	*block;
+	
+	block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
+	if (block->id != ZONEID)
+		I_Error ("Z_ChangeTag: freed a pointer without ZONEID");
+	if (tag >= PU_PURGELEVEL && (unsigned)block->user < 0x100)
+		I_Error ("Z_ChangeTag: an owner is required for purgable blocks");
+	block->tag = tag;
+}
+
+
+/*
+========================
+=
+= Z_FreeMemory
+=
+========================
+*/
+
+int Z_FreeMemory (void)
+{
+	memblock_t	*block;
+	int			free;
+	
+	free = 0;
+	for (block = mainzone->blocklist.next ; block != &mainzone->blocklist 
+	; block = block->next)
+		if (!block->user || block->tag >= PU_PURGELEVEL)
+			free += block->size;
+	return free;
+}
+
--- /dev/null
+++ b/src/hexen/a_action.c
@@ -1,0 +1,1319 @@
+
+//**************************************************************************
+//**
+//** a_action.c : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: a_action.c,v $
+//** $Revision: 1.86 $
+//** $Date: 96/03/22 13:15:33 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2def.h"
+#include "p_local.h"
+#include "soundst.h"
+
+// MACROS ------------------------------------------------------------------
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+extern fixed_t FloatBobOffsets[64];
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+int orbitTableX[256]=
+{
+983025, 982725, 981825, 980340, 978255, 975600, 972330, 968490,
+964065, 959070, 953475, 947325, 940590, 933300, 925440, 917025,
+908055, 898545, 888495, 877905, 866775, 855135, 842985, 830310,
+817155, 803490, 789360, 774735, 759660, 744120, 728130, 711690,
+694845, 677565, 659880, 641805, 623340, 604500, 585285, 565725,
+545820, 525600, 505050, 484200, 463065, 441645, 419955, 398010,
+375840, 353430, 330810, 307995, 285000, 261825, 238485, 215010,
+191400, 167685, 143865, 119955, 95970, 71940, 47850, 23745,
+-375, -24495, -48600, -72690, -96720, -120705, -144600, -168420,
+-192150, -215745, -239220, -262545, -285720, -308715, -331530, -354135,
+-376530, -398700, -420630, -442320, -463725, -484860, -505695, -526230,
+-546450, -566340, -585885, -605085, -623925, -642375, -660435, -678105,
+-695370, -712215, -728625, -744600, -760125, -775200, -789795, -803925,
+-817575, -830715, -843375, -855510, -867135, -878235, -888810, -898845,
+-908340, -917295, -925695, -933540, -940815, -947520, -953670, -959235,
+-964215, -968625, -972450, -975690, -978330, -980400, -981870, -982740,
+-983025, -982725, -981825, -980340, -978255, -975600, -972330, -968490,
+-964065, -959070, -953475, -947325, -940590, -933300, -925440, -917025,
+-908055, -898545, -888495, -877905, -866775, -855135, -842985, -830310,
+-817155, -803490, -789360, -774735, -759660, -744120, -728130, -711690,
+-694845, -677565, -659880, -641805, -623340, -604485, -585285, -565725,
+-545820, -525600, -505050, -484200, -463065, -441645, -419955, -398010,
+-375840, -353430, -330810, -307995, -285000, -261825, -238485, -215010,
+-191400, -167685, -143865, -119955, -95970, -71940, -47850, -23745,
+375, 24495, 48600, 72690, 96720, 120705, 144600, 168420,
+192150, 215745, 239220, 262545, 285720, 308715, 331530, 354135,
+376530, 398700, 420630, 442320, 463725, 484860, 505695, 526230,
+546450, 566340, 585885, 605085, 623925, 642375, 660435, 678105,
+695370, 712215, 728625, 744600, 760125, 775200, 789795, 803925,
+817575, 830715, 843375, 855510, 867135, 878235, 888810, 898845,
+908340, 917295, 925695, 933540, 940815, 947520, 953670, 959235,
+964215, 968625, 972450, 975690, 978330, 980400, 981870, 982740
+};
+
+int orbitTableY[256]=
+{
+375, 24495, 48600, 72690, 96720, 120705, 144600, 168420,
+192150, 215745, 239220, 262545, 285720, 308715, 331530, 354135,
+376530, 398700, 420630, 442320, 463725, 484860, 505695, 526230,
+546450, 566340, 585885, 605085, 623925, 642375, 660435, 678105,
+695370, 712215, 728625, 744600, 760125, 775200, 789795, 803925,
+817575, 830715, 843375, 855510, 867135, 878235, 888810, 898845,
+908340, 917295, 925695, 933540, 940815, 947520, 953670, 959235,
+964215, 968625, 972450, 975690, 978330, 980400, 981870, 982740,
+983025, 982725, 981825, 980340, 978255, 975600, 972330, 968490,
+964065, 959070, 953475, 947325, 940590, 933300, 925440, 917025,
+908055, 898545, 888495, 877905, 866775, 855135, 842985, 830310,
+817155, 803490, 789360, 774735, 759660, 744120, 728130, 711690,
+694845, 677565, 659880, 641805, 623340, 604500, 585285, 565725,
+545820, 525600, 505050, 484200, 463065, 441645, 419955, 398010,
+375840, 353430, 330810, 307995, 285000, 261825, 238485, 215010,
+191400, 167685, 143865, 119955, 95970, 71940, 47850, 23745,
+-375, -24495, -48600, -72690, -96720, -120705, -144600, -168420,
+-192150, -215745, -239220, -262545, -285720, -308715, -331530, -354135,
+-376530, -398700, -420630, -442320, -463725, -484860, -505695, -526230,
+-546450, -566340, -585885, -605085, -623925, -642375, -660435, -678105,
+-695370, -712215, -728625, -744600, -760125, -775200, -789795, -803925,
+-817575, -830715, -843375, -855510, -867135, -878235, -888810, -898845,
+-908340, -917295, -925695, -933540, -940815, -947520, -953670, -959235,
+-964215, -968625, -972450, -975690, -978330, -980400, -981870, -982740,
+-983025, -982725, -981825, -980340, -978255, -975600, -972330, -968490,
+-964065, -959070, -953475, -947325, -940590, -933300, -925440, -917025,
+-908055, -898545, -888495, -877905, -866775, -855135, -842985, -830310,
+-817155, -803490, -789360, -774735, -759660, -744120, -728130, -711690,
+-694845, -677565, -659880, -641805, -623340, -604485, -585285, -565725,
+-545820, -525600, -505050, -484200, -463065, -441645, -419955, -398010,
+-375840, -353430, -330810, -307995, -285000, -261825, -238485, -215010,
+-191400, -167685, -143865, -119955, -95970, -71940, -47850, -23745
+};
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+// CODE --------------------------------------------------------------------
+
+//--------------------------------------------------------------------------
+//
+// Environmental Action routines
+//
+//--------------------------------------------------------------------------
+
+//==========================================================================
+//
+// 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;
+}
+*/
+
+//============================================================================
+//
+// A_PotteryExplode
+//
+//============================================================================
+
+void A_PotteryExplode(mobj_t *actor)
+{
+	mobj_t *mo=NULL;
+	int i;
+
+	for(i = (P_Random()&3)+3; i; i--)
+	{
+		mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_POTTERYBIT1);
+		P_SetMobjState(mo, mo->info->spawnstate+(P_Random()%5));
+		if(mo)
+		{
+			mo->momz = ((P_Random()&7)+5)*(3*FRACUNIT/4);
+			mo->momx = (P_Random()-P_Random())<<(FRACBITS-6);
+			mo->momy = (P_Random()-P_Random())<<(FRACBITS-6);
+		}
+	}
+	S_StartSound(mo, SFX_POTTERY_EXPLODE);
+	if(actor->args[0])
+	{ // Spawn an item
+		if(!nomonsters 
+		|| !(mobjinfo[TranslateThingType[actor->args[0]]].flags&MF_COUNTKILL))
+		{ // Only spawn monsters if not -nomonsters
+			P_SpawnMobj(actor->x, actor->y, actor->z,
+				TranslateThingType[actor->args[0]]);
+		}
+	}
+	P_RemoveMobj(actor);
+}
+
+//============================================================================
+//
+// A_PotteryChooseBit
+//
+//============================================================================
+
+void A_PotteryChooseBit(mobj_t *actor)
+{
+	P_SetMobjState(actor, actor->info->deathstate+(P_Random()%5)+1);
+	actor->tics = 256+(P_Random()<<1);
+}
+
+//============================================================================
+//
+// A_PotteryCheck
+//
+//============================================================================
+
+void A_PotteryCheck(mobj_t *actor)
+{
+	int i;
+	mobj_t *pmo;
+
+	if(!netgame)
+	{
+		pmo = players[consoleplayer].mo;
+		if(P_CheckSight(actor, pmo) && (abs(R_PointToAngle2(pmo->x,
+			pmo->y, actor->x, actor->y)-pmo->angle) <= ANGLE_45))
+		{ // Previous state (pottery bit waiting state)
+			P_SetMobjState(actor, actor->state-&states[0]-1);
+		}
+		else
+		{
+			return;
+		}
+	}
+	else
+	{
+		for(i = 0; i < MAXPLAYERS; i++)
+		{
+			if(!playeringame[i])
+			{
+				continue;
+			}
+			pmo = players[i].mo;
+			if(P_CheckSight(actor, pmo) && (abs(R_PointToAngle2(pmo->x,
+				pmo->y, actor->x, actor->y)-pmo->angle) <= ANGLE_45))
+			{ // Previous state (pottery bit waiting state)
+				P_SetMobjState(actor, actor->state-&states[0]-1);
+				return;
+			}
+		}
+	}		
+}
+
+//============================================================================
+//
+// A_CorpseBloodDrip
+//
+//============================================================================
+
+void A_CorpseBloodDrip(mobj_t *actor)
+{
+	if(P_Random() > 128)
+	{
+		return;
+	}
+	P_SpawnMobj(actor->x, actor->y, actor->z+actor->height/2, 
+		MT_CORPSEBLOODDRIP);
+}
+
+//============================================================================
+//
+// A_CorpseExplode
+//
+//============================================================================
+
+void A_CorpseExplode(mobj_t *actor)
+{
+	mobj_t *mo;
+	int i;
+
+	for(i = (P_Random()&3)+3; i; i--)
+	{
+		mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_CORPSEBIT);
+		P_SetMobjState(mo, mo->info->spawnstate+(P_Random()%3));
+		if(mo)
+		{
+			mo->momz = ((P_Random()&7)+5)*(3*FRACUNIT/4);
+			mo->momx = (P_Random()-P_Random())<<(FRACBITS-6);
+			mo->momy = (P_Random()-P_Random())<<(FRACBITS-6);
+		}
+	}
+	// Spawn a skull
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_CORPSEBIT);
+	P_SetMobjState(mo, S_CORPSEBIT_4);
+	if(mo)
+	{
+		mo->momz = ((P_Random()&7)+5)*(3*FRACUNIT/4);
+		mo->momx = (P_Random()-P_Random())<<(FRACBITS-6);
+		mo->momy = (P_Random()-P_Random())<<(FRACBITS-6);
+		S_StartSound(mo, SFX_FIRED_DEATH);
+	}
+	P_RemoveMobj(actor);
+}
+
+//============================================================================
+//
+// A_LeafSpawn
+//
+//============================================================================
+
+void A_LeafSpawn(mobj_t *actor)
+{
+	mobj_t *mo;
+	int i;
+
+	for(i = (P_Random()&3)+1; i; i--)
+	{
+		mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<14), actor->y+
+			((P_Random()-P_Random())<<14), actor->z+(P_Random()<<14), 
+			MT_LEAF1+(P_Random()&1));
+		if(mo)
+		{
+			P_ThrustMobj(mo, actor->angle, (P_Random()<<9)+3*FRACUNIT);
+			mo->target = actor;
+			mo->special1 = 0;
+		}
+	}
+}
+
+//============================================================================
+//
+// A_LeafThrust
+//
+//============================================================================
+
+void A_LeafThrust(mobj_t *actor)
+{
+	if(P_Random() > 96)
+	{
+		return;
+	}
+	actor->momz += (P_Random()<<9)+FRACUNIT;
+}
+
+//============================================================================
+//
+// A_LeafCheck
+//
+//============================================================================
+
+void A_LeafCheck(mobj_t *actor)
+{
+	actor->special1++;
+	if(actor->special1 >= 20)
+	{
+		P_SetMobjState(actor, S_NULL);
+		return;
+	}
+	if(P_Random() > 64)
+	{
+		if(!actor->momx && !actor->momy)
+		{
+			P_ThrustMobj(actor, actor->target->angle,
+				(P_Random()<<9)+FRACUNIT);
+		}
+		return;
+	}
+	P_SetMobjState(actor, S_LEAF1_8);
+	actor->momz = (P_Random()<<9)+FRACUNIT;
+	P_ThrustMobj(actor, actor->target->angle, (P_Random()<<9)+2*FRACUNIT);
+	actor->flags |= MF_MISSILE;
+}
+
+/*
+#define ORBIT_RADIUS	(15*FRACUNIT)
+void GenerateOrbitTable(void)
+{
+	int angle;
+
+	for (angle=0; angle<256; angle++)
+	{
+		orbitTableX[angle] = FixedMul(ORBIT_RADIUS, finecosine[angle<<5]);
+		orbitTableY[angle] = FixedMul(ORBIT_RADIUS, finesine[angle<<5]);
+	}
+
+	printf("int orbitTableX[256]=\n{\n");
+	for (angle=0; angle<256; angle+=8)
+	{
+		printf("%d, %d, %d, %d, %d, %d, %d, %d,\n",
+			orbitTableX[angle],
+			orbitTableX[angle+1],
+			orbitTableX[angle+2],
+			orbitTableX[angle+3],
+			orbitTableX[angle+4],
+			orbitTableX[angle+5],
+			orbitTableX[angle+6],
+			orbitTableX[angle+7]);
+	}
+	printf("};\n\n");
+
+	printf("int orbitTableY[256]=\n{\n");
+	for (angle=0; angle<256; angle+=8)
+	{
+		printf("%d, %d, %d, %d, %d, %d, %d, %d,\n",
+			orbitTableY[angle],
+			orbitTableY[angle+1],
+			orbitTableY[angle+2],
+			orbitTableY[angle+3],
+			orbitTableY[angle+4],
+			orbitTableY[angle+5],
+			orbitTableY[angle+6],
+			orbitTableY[angle+7]);
+	}
+	printf("};\n");
+}
+*/
+
+// New bridge stuff
+//	Parent
+//		special1	true == removing from world
+//
+//	Child
+//		target		pointer to center mobj
+//		args[0]		angle of ball
+
+void A_BridgeOrbit(mobj_t *actor)
+{
+	if (actor->target->special1)
+	{
+		P_SetMobjState(actor, S_NULL);
+	}
+	actor->args[0]+=3;
+	actor->x = actor->target->x + orbitTableX[actor->args[0]];
+	actor->y = actor->target->y + orbitTableY[actor->args[0]];
+	actor->z = actor->target->z;
+}
+
+
+void A_BridgeInit(mobj_t *actor)
+{
+	byte startangle;
+	mobj_t *ball1, *ball2, *ball3;
+	fixed_t cx,cy,cz;
+
+//	GenerateOrbitTable();
+
+	cx = actor->x;
+	cy = actor->y;
+	cz = actor->z;
+	startangle = P_Random();
+	actor->special1 = 0;
+
+	// Spawn triad into world
+	ball1 = P_SpawnMobj(cx, cy, cz, MT_BRIDGEBALL);
+	ball1->args[0] = startangle;
+	ball1->target = actor;
+
+	ball2 = P_SpawnMobj(cx, cy, cz, MT_BRIDGEBALL);
+	ball2->args[0] = (startangle+85)&255;
+	ball2->target = actor;
+
+	ball3 = P_SpawnMobj(cx, cy, cz, MT_BRIDGEBALL);
+	ball3->args[0] = (startangle+170)&255;
+	ball3->target = actor;
+
+	A_BridgeOrbit(ball1);
+	A_BridgeOrbit(ball2);
+	A_BridgeOrbit(ball3);
+}
+
+void A_BridgeRemove(mobj_t *actor)
+{
+	actor->special1 = true;		// Removing the bridge
+	actor->flags &= ~MF_SOLID;
+	P_SetMobjState(actor, S_FREE_BRIDGE1);
+}
+
+
+//==========================================================================
+//
+// A_GhostOn
+//
+//==========================================================================
+
+/*
+void A_GhostOn(mobj_t *actor)
+{
+	actor->flags |= MF_SHADOW;
+}
+*/
+
+//==========================================================================
+//
+// A_GhostOff
+//
+//==========================================================================
+
+/*
+void A_GhostOff(mobj_t *actor)
+{
+	actor->flags &= ~MF_SHADOW;
+}
+*/
+
+//==========================================================================
+//
+// A_HideThing
+//
+//==========================================================================
+
+void A_HideThing(mobj_t *actor)
+{
+	actor->flags2 |= MF2_DONTDRAW;
+}
+
+//==========================================================================
+//
+// A_UnHideThing
+//
+//==========================================================================
+
+void A_UnHideThing(mobj_t *actor)
+{
+	actor->flags2 &= ~MF2_DONTDRAW;
+}
+
+//==========================================================================
+//
+// A_SetShootable
+//
+//==========================================================================
+
+void A_SetShootable(mobj_t *actor)
+{
+	actor->flags2 &= ~MF2_NONSHOOTABLE;
+	actor->flags |= MF_SHOOTABLE;
+}
+
+//==========================================================================
+//
+// A_UnSetShootable
+//
+//==========================================================================
+
+void A_UnSetShootable(mobj_t *actor)
+{
+	actor->flags2 |= MF2_NONSHOOTABLE;
+	actor->flags &= ~MF_SHOOTABLE;
+}
+
+//==========================================================================
+//
+// A_SetAltShadow
+//
+//==========================================================================
+
+void A_SetAltShadow(mobj_t *actor)
+{
+	actor->flags &= ~MF_SHADOW;
+	actor->flags |= MF_ALTSHADOW;
+}
+
+//==========================================================================
+//
+// A_UnSetAltShadow
+//
+//==========================================================================
+
+/*
+void A_UnSetAltShadow(mobj_t *actor)
+{
+	actor->flags &= ~MF_ALTSHADOW;
+}
+*/
+
+//--------------------------------------------------------------------------
+//
+// Sound Action Routines
+//
+//--------------------------------------------------------------------------
+
+//==========================================================================
+//
+// A_ContMobjSound
+//
+//==========================================================================
+
+void A_ContMobjSound(mobj_t *actor)
+{
+	switch(actor->type)
+	{
+		case MT_SERPENTFX:
+			S_StartSound(actor, SFX_SERPENTFX_CONTINUOUS);
+			break;
+		case MT_HAMMER_MISSILE:
+			S_StartSound(actor, SFX_FIGHTER_HAMMER_CONTINUOUS);
+			break;
+		case MT_QUAKE_FOCUS:
+			S_StartSound(actor, SFX_EARTHQUAKE);
+			break;
+		default:
+			break;
+	}
+}
+
+//==========================================================================
+//
+// PROC A_ESound
+//
+//==========================================================================
+
+void A_ESound(mobj_t *mo)
+{
+	int sound;
+
+	switch(mo->type)
+	{
+		case MT_SOUNDWIND:
+			sound = SFX_WIND;
+			break;
+		default:
+			sound = SFX_NONE;
+			break;
+	}
+	S_StartSound(mo, sound);	
+}
+
+
+
+//==========================================================================
+// Summon Minotaur -- see p_enemy for variable descriptions
+//==========================================================================
+
+
+void A_Summon(mobj_t *actor)
+{
+	mobj_t *mo;
+	mobj_t *master;
+
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_MINOTAUR);
+	if (mo)
+	{
+		if(P_TestMobjLocation(mo) == false || !actor->special1)
+		{ // Didn't fit - change back to artifact
+			P_SetMobjState(mo, S_NULL);
+			mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SUMMONMAULATOR);
+			if (mo) mo->flags2 |= MF2_DROPPED;
+			return;
+		}
+
+		memcpy((void *)mo->args, &leveltime, sizeof(leveltime));
+		master = (mobj_t *)actor->special1;
+		if (master->flags&MF_CORPSE)
+		{	// Master dead
+			mo->special1 = 0;		// No master
+		}
+		else
+		{
+			mo->special1 = actor->special1;		// Pointer to master (mobj_t *)
+			P_GivePower(master->player, pw_minotaur);
+		}
+
+		// Make smoke puff
+		P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKE);
+		S_StartSound(actor, SFX_MAULATOR_ACTIVE);
+	}
+}
+
+
+
+//==========================================================================
+// Fog Variables:
+//
+//		args[0]		Speed (0..10) of fog
+//		args[1]		Angle of spread (0..128)
+// 		args[2]		Frequency of spawn (1..10)
+//		args[3]		Lifetime countdown
+//		args[4]		Boolean: fog moving?
+//		special1		Internal:  Counter for spawn frequency
+//		special2		Internal:  Index into floatbob table
+//
+//==========================================================================
+
+void A_FogSpawn(mobj_t *actor)
+{
+	mobj_t *mo=NULL;
+	angle_t delta;
+
+	if (actor->special1-- > 0) return;
+
+	actor->special1 = actor->args[2];		// Reset frequency count
+
+	switch(P_Random()%3)
+	{
+		case 0:
+			mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FOGPATCHS);
+			break;
+		case 1:
+			mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FOGPATCHM);
+			break;
+		case 2:
+			mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FOGPATCHL);
+			break;
+	}
+
+	if (mo)
+	{
+		delta = actor->args[1];
+		if (delta==0) delta=1;
+		mo->angle = actor->angle + (((P_Random()%delta)-(delta>>1))<<24);
+		mo->target = actor;
+		if (actor->args[0] < 1) actor->args[0] = 1;
+		mo->args[0] = (P_Random() % (actor->args[0]))+1;	// Random speed
+		mo->args[3] = actor->args[3];						// Set lifetime
+		mo->args[4] = 1;									// Set to moving
+		mo->special2 = P_Random()&63;
+	}
+}
+
+
+void A_FogMove(mobj_t *actor)
+{
+	int speed = actor->args[0]<<FRACBITS;
+	angle_t angle;
+	int weaveindex;
+
+	if (!(actor->args[4])) return;
+
+	if (actor->args[3]-- <= 0)
+	{
+		P_SetMobjStateNF(actor, actor->info->deathstate);
+		return;
+	}
+
+	if ((actor->args[3] % 4) == 0)
+	{
+		weaveindex = actor->special2;
+		actor->z += FloatBobOffsets[weaveindex]>>1;
+		actor->special2 = (weaveindex+1)&63;
+	}
+
+	angle = actor->angle>>ANGLETOFINESHIFT;
+	actor->momx = FixedMul(speed, finecosine[angle]);
+	actor->momy = FixedMul(speed, finesine[angle]);
+}
+
+//===========================================================================
+//
+// A_PoisonBagInit
+//
+//===========================================================================
+
+void A_PoisonBagInit(mobj_t *actor)
+{
+	mobj_t *mo;
+
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z+28*FRACUNIT,
+		MT_POISONCLOUD);
+	if(!mo)
+	{
+		return;
+	}
+	mo->momx = 1; // missile objects must move to impact other objects
+	mo->special1 = 24+(P_Random()&7);
+	mo->special2 = 0;
+	mo->target = actor->target;
+	mo->radius = 20*FRACUNIT;
+	mo->height = 30*FRACUNIT;
+	mo->flags &= ~MF_NOCLIP;
+}
+
+//===========================================================================
+//
+// A_PoisonBagCheck
+//
+//===========================================================================
+
+void A_PoisonBagCheck(mobj_t *actor)
+{
+	if(!--actor->special1)
+	{
+		P_SetMobjState(actor, S_POISONCLOUD_X1);
+	}
+	else
+	{
+		return;
+	}
+}
+
+//===========================================================================
+//
+// A_PoisonBagDamage
+//
+//===========================================================================
+
+void A_PoisonBagDamage(mobj_t *actor)
+{
+	int bobIndex;
+	
+	extern void A_Explode(mobj_t *actor);
+
+	A_Explode(actor);	
+
+	bobIndex = actor->special2;
+	actor->z += FloatBobOffsets[bobIndex]>>4;
+	actor->special2 = (bobIndex+1)&63;
+}
+
+//===========================================================================
+//
+// A_PoisonShroom
+//
+//===========================================================================
+
+void A_PoisonShroom(mobj_t *actor)
+{
+	actor->tics = 128+(P_Random()<<1);
+}
+
+//===========================================================================
+//
+// A_CheckThrowBomb
+//
+//===========================================================================
+
+void A_CheckThrowBomb(mobj_t *actor)
+{
+	if(abs(actor->momx) < 1.5*FRACUNIT && abs(actor->momy) < 1.5*FRACUNIT
+		&& actor->momz < 2*FRACUNIT
+		&& actor->state == &states[S_THROWINGBOMB6])
+	{
+		P_SetMobjState(actor, S_THROWINGBOMB7);
+		actor->z = actor->floorz;
+		actor->momz = 0;
+		actor->flags2 &= ~MF2_FLOORBOUNCE;
+		actor->flags &= ~MF_MISSILE;
+	}
+	if(!--actor->health)
+	{
+		P_SetMobjState(actor, actor->info->deathstate);
+	}
+}
+
+//===========================================================================
+// Quake variables
+//
+//		args[0]		Intensity on richter scale (2..9)
+//		args[1]		Duration in tics
+//		args[2]		Radius for damage
+//		args[3]		Radius for tremor
+//		args[4]		TID of map thing for focus of quake
+//
+//===========================================================================
+
+//===========================================================================
+//
+// A_LocalQuake
+//
+//===========================================================================
+
+boolean A_LocalQuake(byte *args, mobj_t *actor)
+{
+	mobj_t *focus, *target;
+	int lastfound=0;
+	int success=false;
+
+	actor=actor;	// suppress warning
+
+	// Find all quake foci
+	do
+	{
+		target = P_FindMobjFromTID(args[4], &lastfound);
+		if (target)
+		{
+			focus = P_SpawnMobj(target->x,
+								target->y,
+								target->z, MT_QUAKE_FOCUS);
+			if (focus)
+			{
+				focus->args[0] = args[0];
+				focus->args[1] = args[1]>>1;	// decremented every 2 tics
+				focus->args[2] = args[2];
+				focus->args[3] = args[3];
+				focus->args[4] = args[4];
+				success = true;
+			}
+		}
+	}while(target != NULL);
+
+	return(success);
+}
+
+
+//===========================================================================
+//
+// A_Quake
+//
+//===========================================================================
+int	localQuakeHappening[MAXPLAYERS];
+
+void A_Quake(mobj_t *actor)
+{
+	angle_t an;
+	player_t *player;
+	mobj_t *victim;
+	int richters = actor->args[0];
+	int playnum;
+	fixed_t dist;
+
+	if (actor->args[1]-- > 0)
+	{
+		for (playnum=0; playnum < MAXPLAYERS; playnum++)
+		{
+			player = &players[playnum];
+			if (!playeringame[playnum]) continue;
+
+			victim = player->mo;
+			dist = P_AproxDistance(actor->x - victim->x,
+						actor->y - victim->y) >> (FRACBITS+6);
+			// Tested in tile units (64 pixels)
+			if (dist < actor->args[3])		// In tremor radius
+			{
+				localQuakeHappening[playnum] = richters;
+			}
+			// Check if in damage radius
+			if ((dist < actor->args[2]) &&
+				(victim->z <= victim->floorz))
+			{
+				if (P_Random() < 50)
+				{
+					P_DamageMobj(victim, NULL, NULL, HITDICE(1));
+				}
+				// Thrust player around
+				an = victim->angle + ANGLE_1*P_Random();
+				P_ThrustMobj(victim,an,richters<<(FRACBITS-1));
+			}
+		}
+	}
+	else
+	{
+		for (playnum=0; playnum < MAXPLAYERS; playnum++)
+		{
+			localQuakeHappening[playnum] = false;
+		}
+		P_SetMobjState(actor, S_NULL);
+	}
+}
+
+
+
+
+//===========================================================================
+//
+// Teleport other stuff
+//
+//===========================================================================
+
+#define TELEPORT_LIFE 1
+
+void A_TeloSpawnA(mobj_t *actor)
+{
+	mobj_t *mo;
+
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX2);
+	if (mo)
+	{
+		mo->special1 = TELEPORT_LIFE;			// Lifetime countdown
+		mo->angle = actor->angle;
+		mo->target = actor->target;
+		mo->momx = actor->momx>>1;
+		mo->momy = actor->momy>>1;
+		mo->momz = actor->momz>>1;
+	}
+}
+
+void A_TeloSpawnB(mobj_t *actor)
+{
+	mobj_t *mo;
+
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX3);
+	if (mo)
+	{
+		mo->special1 = TELEPORT_LIFE;			// Lifetime countdown
+		mo->angle = actor->angle;
+		mo->target = actor->target;
+		mo->momx = actor->momx>>1;
+		mo->momy = actor->momy>>1;
+		mo->momz = actor->momz>>1;
+	}
+}
+
+void A_TeloSpawnC(mobj_t *actor)
+{
+	mobj_t *mo;
+
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX4);
+	if (mo)
+	{
+		mo->special1 = TELEPORT_LIFE;			// Lifetime countdown
+		mo->angle = actor->angle;
+		mo->target = actor->target;
+		mo->momx = actor->momx>>1;
+		mo->momy = actor->momy>>1;
+		mo->momz = actor->momz>>1;
+	}
+}
+
+void A_TeloSpawnD(mobj_t *actor)
+{
+	mobj_t *mo;
+
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX5);
+	if (mo)
+	{
+		mo->special1 = TELEPORT_LIFE;			// Lifetime countdown
+		mo->angle = actor->angle;
+		mo->target = actor->target;
+		mo->momx = actor->momx>>1;
+		mo->momy = actor->momy>>1;
+		mo->momz = actor->momz>>1;
+	}
+}
+
+void A_CheckTeleRing(mobj_t *actor)
+{
+	if (actor->special1-- <= 0)
+	{
+		P_SetMobjState(actor, actor->info->deathstate);
+	}
+}
+
+
+
+
+// Dirt stuff
+
+void P_SpawnDirt(mobj_t *actor, fixed_t radius)
+{
+	fixed_t x,y,z;
+	int dtype=0;
+	mobj_t *mo;
+	angle_t angle;
+
+	angle = P_Random()<<5;		// <<24 >>19
+	x = actor->x + FixedMul(radius,finecosine[angle]);
+	y = actor->y + FixedMul(radius,finesine[angle]);
+//	x = actor->x + ((P_Random()-P_Random())%radius)<<FRACBITS;
+//	y = actor->y + ((P_Random()-P_Random()<<FRACBITS)%radius);
+	z = actor->z + (P_Random()<<9) + FRACUNIT;
+	switch(P_Random()%6)
+	{
+		case 0:
+			dtype = MT_DIRT1;
+			break;
+		case 1:
+			dtype = MT_DIRT2;
+			break;
+		case 2:
+			dtype = MT_DIRT3;
+			break;
+		case 3:
+			dtype = MT_DIRT4;
+			break;
+		case 4:
+			dtype = MT_DIRT5;
+			break;
+		case 5:
+			dtype = MT_DIRT6;
+			break;
+	}
+	mo = P_SpawnMobj(x,y,z,dtype);
+	if (mo)
+	{
+		mo->momz = P_Random()<<10;
+	}
+}
+
+
+
+
+//===========================================================================
+//
+// Thrust floor stuff
+//
+// Thrust Spike Variables
+//		special1		pointer to dirt clump mobj
+//		special2		speed of raise
+//		args[0]		0 = lowered,  1 = raised
+//		args[1]		0 = normal,   1 = bloody
+//===========================================================================
+
+void A_ThrustInitUp(mobj_t *actor)
+{
+	actor->special2 = 5;		// Raise speed
+	actor->args[0] = 1;		// Mark as up
+	actor->floorclip = 0;
+	actor->flags = MF_SOLID;
+	actor->flags2 = MF2_NOTELEPORT|MF2_FLOORCLIP;
+	actor->special1 = 0L;
+}
+
+void A_ThrustInitDn(mobj_t *actor)
+{
+	mobj_t *mo;
+	actor->special2 = 5;		// Raise speed
+	actor->args[0] = 0;		// Mark as down
+	actor->floorclip = actor->info->height;
+	actor->flags = 0;
+	actor->flags2 = MF2_NOTELEPORT|MF2_FLOORCLIP|MF2_DONTDRAW;
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_DIRTCLUMP);
+	actor->special1 = (int)mo;
+}
+
+
+void A_ThrustRaise(mobj_t *actor)
+{
+	if (A_RaiseMobj(actor))
+	{	// Reached it's target height
+		actor->args[0] = 1;
+		if (actor->args[1])
+			P_SetMobjStateNF(actor, S_BTHRUSTINIT2_1);
+		else
+			P_SetMobjStateNF(actor, S_THRUSTINIT2_1);
+	}
+
+	// Lose the dirt clump
+	if ((actor->floorclip < actor->height) && actor->special1)
+	{
+		P_RemoveMobj( (mobj_t *)actor->special1 );
+		actor->special1 = 0;
+	}
+
+	// Spawn some dirt
+	if (P_Random()<40)
+		P_SpawnDirt(actor, actor->radius);
+	actor->special2++;							// Increase raise speed
+}
+
+void A_ThrustLower(mobj_t *actor)
+{
+	if (A_SinkMobj(actor))
+	{
+		actor->args[0] = 0;
+		if (actor->args[1])
+			P_SetMobjStateNF(actor, S_BTHRUSTINIT1_1);
+		else
+			P_SetMobjStateNF(actor, S_THRUSTINIT1_1);
+	}
+}
+
+void A_ThrustBlock(mobj_t *actor)
+{
+	actor->flags |= MF_SOLID;
+}
+
+void A_ThrustImpale(mobj_t *actor)
+{
+	// Impale all shootables in radius
+	PIT_ThrustSpike(actor);
+}
+
+//===========================================================================
+//
+// A_SoAExplode - Suit of Armor Explode
+//
+//===========================================================================
+
+void A_SoAExplode(mobj_t *actor)
+{
+	mobj_t *mo;
+	int i;
+
+	for(i = 0; i < 10; i++)
+	{
+		mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12), 
+			actor->y+((P_Random()-128)<<12), 
+			actor->z+(P_Random()*actor->height/256), MT_ZARMORCHUNK);
+		P_SetMobjState(mo, mo->info->spawnstate+i);
+		if(mo)
+		{
+			mo->momz = ((P_Random()&7)+5)*FRACUNIT;
+			mo->momx = (P_Random()-P_Random())<<(FRACBITS-6);
+			mo->momy = (P_Random()-P_Random())<<(FRACBITS-6);
+		}
+	}
+	if(actor->args[0])
+	{ // Spawn an item
+		if(!nomonsters 
+		|| !(mobjinfo[TranslateThingType[actor->args[0]]].flags&MF_COUNTKILL))
+		{ // Only spawn monsters if not -nomonsters
+			P_SpawnMobj(actor->x, actor->y, actor->z,
+				TranslateThingType[actor->args[0]]);
+		}
+	}
+	S_StartSound(mo, SFX_SUITOFARMOR_BREAK);
+	P_RemoveMobj(actor);
+}
+
+//===========================================================================
+//
+// A_BellReset1
+//
+//===========================================================================
+
+void A_BellReset1(mobj_t *actor)
+{
+	actor->flags |= MF_NOGRAVITY;
+	actor->height <<= 2;	
+}
+
+//===========================================================================
+//
+// A_BellReset2
+//
+//===========================================================================
+
+void A_BellReset2(mobj_t *actor)
+{
+	actor->flags |= MF_SHOOTABLE;
+	actor->flags &= ~MF_CORPSE;
+	actor->health = 5;
+}
+
+
+//===========================================================================
+//
+// A_FlameCheck
+//
+//===========================================================================
+
+void A_FlameCheck(mobj_t *actor)
+{
+	if(!actor->args[0]--)		// Called every 8 tics
+	{
+		P_SetMobjState(actor, S_NULL);
+	}
+}
+
+
+//===========================================================================
+// Bat Spawner Variables
+//	special1	frequency counter
+//	special2	
+//	args[0]		frequency of spawn (1=fastest, 10=slowest)
+//	args[1]		spread angle (0..255)
+//	args[2]		
+//	args[3]		duration of bats (in octics)
+//	args[4]		turn amount per move (in degrees)
+//
+// Bat Variables
+//	special2	lifetime counter
+//	args[4]		turn amount per move (in degrees)
+//===========================================================================
+
+void A_BatSpawnInit(mobj_t *actor)
+{
+	actor->special1 = 0;	// Frequency count
+}
+
+void A_BatSpawn(mobj_t *actor)
+{
+	mobj_t *mo;
+	int delta;
+	angle_t angle;
+
+	// Countdown until next spawn
+	if (actor->special1-- > 0) return;
+	actor->special1 = actor->args[0];		// Reset frequency count
+
+	delta = actor->args[1];
+	if (delta==0) delta=1;
+	angle = actor->angle + (((P_Random()%delta)-(delta>>1))<<24);
+	mo = P_SpawnMissileAngle(actor, MT_BAT, angle, 0);
+	if (mo)
+	{
+		mo->args[0] = P_Random()&63;			// floatbob index
+		mo->args[4] = actor->args[4];			// turn degrees
+		mo->special2 = actor->args[3]<<3;		// Set lifetime
+		mo->target = actor;
+	}
+}
+
+
+void A_BatMove(mobj_t *actor)
+{
+	angle_t newangle;
+	fixed_t speed;
+
+	if (actor->special2 < 0)
+	{
+		P_SetMobjState(actor, actor->info->deathstate);
+	}
+	actor->special2 -= 2;		// Called every 2 tics
+
+	if (P_Random()<128)
+	{
+		newangle = actor->angle + ANGLE_1*actor->args[4];
+	}
+	else
+	{
+		newangle = actor->angle - ANGLE_1*actor->args[4];
+	}
+
+	// Adjust momentum vector to new direction
+	newangle >>= ANGLETOFINESHIFT;
+	speed = FixedMul(actor->info->speed, P_Random()<<10);
+	actor->momx = FixedMul(speed, finecosine[newangle]);
+	actor->momy = FixedMul(speed, finesine[newangle]);
+
+	if (P_Random()<15)
+		S_StartSound(actor, SFX_BAT_SCREAM);
+
+	// Handle Z movement
+	actor->z = actor->target->z + 2*FloatBobOffsets[actor->args[0]];
+	actor->args[0] = (actor->args[0]+3)&63;	
+}
+
+//===========================================================================
+//
+// A_TreeDeath
+//
+//===========================================================================
+
+void A_TreeDeath(mobj_t *actor)
+{
+	if(!(actor->flags2&MF2_FIREDAMAGE))
+	{
+		actor->height <<= 2;
+		actor->flags |= MF_SHOOTABLE;
+		actor->flags &= ~(MF_CORPSE+MF_DROPOFF);
+		actor->health = 35;
+		return;
+	}
+	else
+	{
+		P_SetMobjState(actor, actor->info->meleestate);
+	}
+}
+
+//===========================================================================
+//
+// A_NoGravity
+//
+//===========================================================================
+
+void A_NoGravity(mobj_t *actor)
+{
+	actor->flags |= MF_NOGRAVITY;
+}
--- /dev/null
+++ b/src/hexen/am_data.h
@@ -1,0 +1,107 @@
+
+//**************************************************************************
+//**
+//** am_data.h : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: am_data.h,v $
+//** $Revision: 1.2 $
+//** $Date: 96/01/06 18:37:22 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+#ifndef __AMDATA_H__
+#define __AMDATA_H__
+
+#pragma once
+
+// 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[] = {
+  { { -.867*R, -.5*R }, { .867*R, -.5*R } },
+  { { .867*R, -.5*R } , { 0, R } },
+  { { 0, R }, { -.867*R, -.5*R } }
+  };
+#undef R
+#define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t))
+*/
+
+#define R (FRACUNIT)
+mline_t thintriangle_guy[] = {
+  { { -.5*R, -.7*R }, { R, 0 } },
+  { { R, 0 }, { -.5*R, .7*R } },
+  { { -.5*R, .7*R }, { -.5*R, -.7*R } }
+  };
+#undef R
+#define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t))
+
+#endif
--- /dev/null
+++ b/src/hexen/am_map.c
@@ -1,0 +1,1400 @@
+
+//**************************************************************************
+//**
+//** am_map.c : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: am_map.c,v $
+//** $Revision: 1.22 $
+//** $Date: 96/01/06 18:37:23 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+#include "h2def.h"
+#include "p_local.h"
+#include "am_map.h"
+#include "am_data.h"
+#include <stdio.h>
+
+#define NUMALIAS 3 // Number of antialiased lines.
+
+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-SBARHEIGHT-3;
+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 = 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_kills[] = { 'k', 'i', 'l', 'l', 's' };
+static boolean ShowKills = 0;
+static unsigned ShowKillsCount = 0;
+
+extern boolean viewactive;
+
+static byte antialias[NUMALIAS][8]=
+{
+	{ 83, 84, 85, 86, 87, 88, 89, 90 },
+	{ 96, 96, 95, 94, 93, 92, 91, 90 },
+	{ 107, 108, 109, 110, 111, 112, 89, 90 }
+};
+
+/*
+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 parallax 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);
+
+void AM_DrawDeathmatchStats(void);
+static void DrawWorldTimer(void);
+
+// 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?-MAXINT:MAXINT);
+  else is->islp = FixedDiv(dx, dy);
+  if (!dx) is->slp = (dy<0?-MAXINT:MAXINT);
+  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 = MAXINT;
+  max_x = max_y = -MAXINT;
+  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 = MAXINT;
+  }
+
+  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 = screen;
+
+  f_oldloc.x = MAXINT;
+  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;
+		}
+	}
+
+  // inform the status bar of the change
+//c  ST_Responder(&st_notify);
+}
+
+void AM_loadPics(void)
+{
+  maplump = W_CacheLumpName("AUTOPAGE", PU_STATIC);
+}
+
+
+/*
+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;
+	static int cheatstate=0;
+	static int bigstate=0;
+	
+	rc = false;
+	if (!automapactive)
+	{
+		if (ev->type == ev_keydown && ev->data1 == AM_STARTKEY
+			&& gamestate == GS_LEVEL)
+		{
+			AM_Start ();
+			SB_state = -1;
+			viewactive = false;
+			rc = true;
+		}
+	}
+	else if (ev->type == ev_keydown)
+	{
+		rc = true;
+		switch(ev->data1)
+		{
+			case AM_PANRIGHTKEY: // pan right
+				if (!followplayer) m_paninc.x = FTOM(F_PANINC);
+				else rc = false;
+				break;
+			case AM_PANLEFTKEY: // pan left
+				if (!followplayer) m_paninc.x = -FTOM(F_PANINC);
+				else rc = false;
+				break;
+			case AM_PANUPKEY: // pan up
+				if (!followplayer) m_paninc.y = FTOM(F_PANINC);
+				else rc = false;
+				break;
+			case AM_PANDOWNKEY: // pan down
+				if (!followplayer) m_paninc.y = -FTOM(F_PANINC);
+				else rc = false;
+				break;
+			case AM_ZOOMOUTKEY: // zoom out
+				mtof_zoommul = M_ZOOMOUT;
+				ftom_zoommul = M_ZOOMIN;
+				break;
+			case AM_ZOOMINKEY: // zoom in
+				mtof_zoommul = M_ZOOMIN;
+				ftom_zoommul = M_ZOOMOUT;
+				break;
+			case AM_ENDKEY:
+				bigstate = 0;
+				viewactive = true;
+				AM_Stop ();
+				SB_state = -1;
+				break;
+			case AM_GOBIGKEY:
+				bigstate = !bigstate;
+				if (bigstate)
+				{
+					AM_saveScaleAndLoc();
+					AM_minOutWindowScale();
+				}
+				else AM_restoreScaleAndLoc();
+				break;
+			case AM_FOLLOWKEY:
+				followplayer = !followplayer;
+				f_oldloc.x = MAXINT;
+				P_SetMessage(plr, 
+					followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF, true);
+				break;
+			default:
+				cheatstate=0;
+				rc = false;
+		}
+
+		if(cheat_kills[ShowKillsCount] == ev->data1 && netgame && deathmatch)
+		{
+			ShowKillsCount++;
+			if(ShowKillsCount == 5)
+			{
+				ShowKillsCount = 0;
+				rc = false;
+				ShowKills ^= 1;
+			}
+		}
+		else
+		{
+			ShowKillsCount = 0;
+		}
+	}
+	else if (ev->type == ev_keyup)
+	{
+		rc = false;
+		switch (ev->data1)
+		{
+			case AM_PANRIGHTKEY:
+				if (!followplayer) m_paninc.x = 0;
+				break;
+			case AM_PANLEFTKEY:
+				if (!followplayer) m_paninc.x = 0;
+				break;
+			case AM_PANUPKEY:
+				if (!followplayer) m_paninc.y = 0;
+				break;
+			case AM_PANDOWNKEY:
+				if (!followplayer) m_paninc.y = 0;
+				break;
+			case AM_ZOOMOUTKEY:
+			case AM_ZOOMINKEY:
+				mtof_zoommul = FRACUNIT;
+				ftom_zoommul = FRACUNIT;
+				break;
+		}
+	}
+	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 == MAXINT) //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 == MAXINT) //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 < SCREENHEIGHT-SBARHEIGHT; i++)
+	{
+		memcpy(screen+i*finit_width, maplump+j+mapxstart, 	
+			finit_width-mapxstart);
+		memcpy(screen+i*finit_width+finit_width-mapxstart, maplump+j, 
+			mapxstart);
+		j += finit_width;
+		if(j >= finit_height*finit_width)
+			j=0;
+	}
+
+//	 memcpy(screen, 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 };
+  register outcode1 = 0, outcode2 = 0, outside;
+  fpoint_t tmp;
+  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 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 long) DeltaX << 16) / (unsigned long) 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 long) DeltaY << 16) / (unsigned long) 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].flags & ML_SECRET) // secret door
+	{
+	  if (cheating) AM_drawMline(&l, 0);
+	  else AM_drawMline(&l, WALLCOLORS+lightlev);
+	}
+	else if(lines[i].special == 13 || lines[i].special == 83)
+	{ // Locked door line -- all locked doors are greed
+		AM_drawMline(&l, GREENKEY);
+	}
+	else if(lines[i].special == 70 || lines[i].special == 71)
+	{ // intra-level teleports are blue
+		AM_drawMline(&l, BLUEKEY);
+	}
+	else if(lines[i].special == 74 || lines[i].special == 75)
+	{ // inter-level teleport/game-winning exit -- both are red
+		AM_drawMline(&l, BLOODRED);
+	}
+	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[] =
+	{
+		AM_PLR1_COLOR,
+		AM_PLR2_COLOR,
+		AM_PLR3_COLOR,
+		AM_PLR4_COLOR,
+		AM_PLR5_COLOR,
+		AM_PLR6_COLOR,
+		AM_PLR7_COLOR,
+		AM_PLR8_COLOR
+	};
+	int their_color = -1;
+	int color;
+
+	if(!netgame)
+	{
+		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;
+		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)
+{
+  if (!automapactive) return;
+
+  UpdateState |= I_FULLSCRN;
+  AM_clearFB(BACKGROUND);
+  if (grid) AM_drawGrid(GRIDCOLORS);
+  AM_drawWalls();
+  AM_drawPlayers();
+	DrawWorldTimer();
+
+  if (cheating==2) AM_drawThings(THINGCOLORS, THINGRANGE);
+
+//  AM_drawCrosshair(XHAIRCOLORS);
+//  AM_drawMarks();
+//	if(gameskill == sk_baby) AM_drawkeys();
+
+	MN_DrTextA(P_GetMapName(gamemap), 38, 144);
+	if(ShowKills && netgame && deathmatch)
+	{
+		AM_DrawDeathmatchStats();
+	}
+//  I_Update();
+//  V_MarkRect(f_x, f_y, f_w, f_h);
+
+}
+
+//===========================================================================
+//
+// AM_DrawDeathmatchStats
+//
+//===========================================================================
+
+// 8-player note:  Proper player color names here, too
+
+char *PlayerColorText[MAXPLAYERS] =
+{
+	"BLUE:",
+	"RED:",
+	"YELLOW:",
+	"GREEN:",
+	"JADE:",
+	"WHITE:",
+	"HAZEL:",
+	"PURPLE:"
+};
+
+void AM_DrawDeathmatchStats(void)
+{
+	int i, j, k, m;
+	int fragCount[MAXPLAYERS];
+	int order[MAXPLAYERS];
+	char textBuffer[80];
+	int yPosition;
+
+	for(i = 0; i < MAXPLAYERS; i++)
+	{
+		fragCount[i] = 0;
+		order[i] = -1;
+	}
+	for(i = 0; i < MAXPLAYERS; i++)
+	{
+		if(!playeringame[i])
+		{
+			continue;
+		}
+		else
+		{
+			for(j = 0; j < MAXPLAYERS; j++)
+			{
+				if(playeringame[j])
+				{
+					fragCount[i] += players[i].frags[j];
+				}
+			}
+			for(k = 0; k < MAXPLAYERS; k++)
+			{
+				if(order[k] == -1)
+				{
+					order[k] = i;
+					break;
+				}
+				else if(fragCount[i] > fragCount[order[k]])
+				{
+					for(m = MAXPLAYERS-1; m > k; m--)
+					{
+						 order[m] = order[m-1];
+					}
+					order[k] = i;
+					break;
+				}
+			}
+		}
+	}
+	yPosition = 15;
+	for(i = 0; i < MAXPLAYERS; i++)
+	{
+		if(!playeringame[order[i]])
+		{
+			continue;
+		}
+		else
+		{
+			MN_DrTextA(PlayerColorText[order[i]], 8, yPosition);
+			sprintf(textBuffer, "%d", fragCount[order[i]]);
+			MN_DrTextA(textBuffer, 80, yPosition);
+			yPosition += 10;
+		}
+	}
+}
+
+//===========================================================================
+//
+// DrawWorldTimer
+//
+//===========================================================================
+
+static void DrawWorldTimer(void)
+{
+	int days;
+	int hours;
+	int minutes;
+	int seconds;
+	int worldTimer;
+	char timeBuffer[15];
+	char dayBuffer[20];
+
+	worldTimer = players[consoleplayer].worldTimer;
+
+	worldTimer /= 35;
+	days = worldTimer/86400;
+	worldTimer -= days*86400;
+	hours = worldTimer/3600;
+	worldTimer -= hours*3600;
+	minutes = worldTimer/60;
+	worldTimer -= minutes*60;
+	seconds = worldTimer;
+
+	sprintf(timeBuffer, "%.2d : %.2d : %.2d", hours, minutes,seconds);
+	MN_DrTextA(timeBuffer, 240, 8);
+
+	if (days)
+	{
+		if (days==1)
+		{
+			sprintf(dayBuffer, "%.2d DAY", days);
+		}
+		else
+		{
+			sprintf(dayBuffer, "%.2d DAYS", days);
+		}
+		MN_DrTextA(dayBuffer, 240, 20);
+		if (days >= 5)
+		{
+			MN_DrTextA("YOU FREAK!!!", 230, 35);
+		}
+	}
+}
--- /dev/null
+++ b/src/hexen/am_map.h
@@ -1,0 +1,143 @@
+
+//**************************************************************************
+//**
+//** am_map.h : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: am_map.h,v $
+//** $Revision: 1.3 $
+//** $Date: 95/12/31 19:47:21 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+#ifndef __AMMAP_H__
+#define __AMMAP_H__
+
+#pragma once
+
+// 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  	177
+#define BLUEKEY 	157
+#define YELLOWKEY 	137
+#define GREENKEY  	198
+
+// Automap colors
+
+#define AM_PLR1_COLOR 157 // Blue
+#define AM_PLR2_COLOR 177 // Red
+#define AM_PLR3_COLOR 137 // Yellow
+#define AM_PLR4_COLOR 198 // Green
+#define AM_PLR5_COLOR 215 // Jade
+#define AM_PLR6_COLOR 32  // White
+#define AM_PLR7_COLOR 106 // Hazel
+#define AM_PLR8_COLOR 234 // Purple
+
+#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 KEY_TAB	9
+#define AM_PANDOWNKEY	KEY_DOWNARROW
+#define AM_PANUPKEY	KEY_UPARROW
+#define AM_PANRIGHTKEY	KEY_RIGHTARROW
+#define AM_PANLEFTKEY	KEY_LEFTARROW
+//#define AM_PANDOWNKEY	SC_DOWNARROW
+//#define AM_PANUPKEY		SC_UPARROW
+//#define AM_PANRIGHTKEY	SC_RIGHTARROW
+//#define AM_PANLEFTKEY	SC_LEFTARROW
+
+#define AM_ZOOMINKEY	'='
+//#define AM_ZOOMINKEY		13
+//#define AM_ZOOMOUTKEY 	12
+ #define AM_ZOOMOUTKEY	'-'
+#define AM_STARTKEY	KEY_TAB
+#define AM_ENDKEY	KEY_TAB
+#define AM_GOBIGKEY	'0'
+//#define AM_GOBIGKEY		11
+//#define AM_FOLLOWKEY 	33
+//#define AM_GRIDKEY		34
+#define AM_FOLLOWKEY	'f'
+#define AM_GRIDKEY	'g'
+
+#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
--- /dev/null
+++ b/src/hexen/ct_chat.c
@@ -1,0 +1,497 @@
+
+//**************************************************************************
+//**
+//** ct_chat.c : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: ct_chat.c,v $
+//** $Revision: 1.12 $
+//** $Date: 96/01/16 10:35:26 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+#include <string.h>
+#include <ctype.h>
+#include "h2def.h"
+#include "p_local.h"
+#include "soundst.h"
+
+#define NUMKEYS 256
+
+#define QUEUESIZE		128
+#define MESSAGESIZE		128
+#define MESSAGELEN 		265
+
+// 8-player note:  Change this stuff (CT_PLR_*, and the key mappings)
+enum
+{
+	CT_PLR_BLUE	= 1,
+	CT_PLR_RED,
+	CT_PLR_YELLOW,
+	CT_PLR_GREEN,
+	CT_PLR_PLAYER5,
+	CT_PLR_PLAYER6,
+	CT_PLR_PLAYER7,
+	CT_PLR_PLAYER8,
+	CT_PLR_ALL
+};
+
+#define CT_KEY_BLUE		'b'
+#define CT_KEY_RED		'r'
+#define CT_KEY_YELLOW	'y'
+#define CT_KEY_GREEN	'g'
+#define CT_KEY_PLAYER5	'j' // Jade
+#define CT_KEY_PLAYER6	'w' // White
+#define CT_KEY_PLAYER7	'h' // Hazel
+#define CT_KEY_PLAYER8	'p' // Purple
+#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];
+int msgptr[MAXPLAYERS];
+int msglen[MAXPLAYERS];
+
+boolean cheated;
+
+static int FontABaseLump;
+
+char *CT_FromPlrText[MAXPLAYERS] =
+{
+	"BLUE:  ",
+	"RED:  ",
+	"YELLOW:  ",
+	"GREEN:  ",
+	"JADE:  ",
+	"WHITE:  ",
+	"HAZEL:  ",
+	"PURPLE:  "
+};
+
+char *chat_macros[10];
+
+boolean altdown;
+boolean shiftdown;
+
+extern boolean usearti;
+
+//===========================================================================
+//
+// 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("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_RALT)
+	{
+		altdown = (ev->type == ev_keydown);
+		return false;
+	}
+	if(ev->data1 == KEY_RSHIFT)
+	{
+		shiftdown = (ev->type == ev_keydown);
+		return false;
+	}
+	if(gamestate != GS_LEVEL || 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;
+		}
+		else if(ev->data1 == CT_KEY_PLAYER5)
+		{
+			sendto = CT_PLR_PLAYER5;
+		}
+		else if(ev->data1 == CT_KEY_PLAYER6)
+		{
+			sendto = CT_PLR_PLAYER6;
+		}
+		else if(ev->data1 == CT_KEY_PLAYER7)
+		{
+			sendto = CT_PLR_PLAYER7;
+		}
+		else if(ev->data1 == CT_KEY_PLAYER8)
+		{
+			sendto = CT_PLR_PLAYER8;
+		}
+		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);
+			usearti = false;
+			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;
+			}
+		}
+		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 <= CT_PLR_ALL)
+			{
+				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)
+				{
+					strcpy(plr_lastmsg[i], CT_FromPlrText[i]);
+					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],
+							"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("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;
+}
--- /dev/null
+++ b/src/hexen/ct_chat.h
@@ -1,0 +1,15 @@
+//
+// Chat mode stuff
+//
+
+#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'
--- /dev/null
+++ b/src/hexen/d_net.c
@@ -1,0 +1,927 @@
+
+//**************************************************************************
+//**
+//** d_net.c : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: d_net.c,v $
+//** $Revision: 1.16 $
+//** $Date: 96/01/01 03:39:44 $
+//** $Author: bgokey $
+//**
+//** This version has the fixed ticdup code.
+//**
+//**************************************************************************
+
+#include "h2def.h"
+#include "p_local.h"
+#include <stdlib.h> // for atoi()
+
+#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 H2_ProcessEvents (void);
+void G_BuildTiccmd (ticcmd_t *cmd);
+void H2_DoAdvanceDemo (void);
+extern void ST_NetProgress(void);
+extern void ST_NetDone(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");
+}
+
+	I_NetCmd ();
+}
+
+//==========================================================================
+//
+// NET_SendFrags
+//
+//==========================================================================
+
+void NET_SendFrags(player_t *player)
+{
+	int i;
+	int frags;
+
+	netbuffer->checksum = NetbufferChecksum();
+
+	if (demoplayback)
+	{
+		return;
+	}
+	if (!netgame)
+	{
+		I_Error ("Tried to transmit to another node");
+	}
+
+	frags = 0;
+	for(i = 0; i < MAXPLAYERS; i++)
+	{
+		frags += player->frags[i];
+	}
+	doomcom->command = CMD_FRAG;
+	doomcom->remotenode = frags;
+	doomcom->datalength = NetbufferSize ();
+
+	I_NetCmd ();
+}
+
+/*
+==============
+=
+= 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;
+	I_NetCmd ();
+	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;
+			P_SetMessage(&players[consoleplayer], exitmsg, true);
+			S_StartSound(NULL, SFX_CHAT);
+//			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 ();
+		H2_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 ();
+	for ( ; eventtail != eventhead
+	; eventtail = (++eventtail)&(MAXEVENTS-1) )
+	{
+		ev = &events[eventtail];
+		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];
+	boolean gotClass[MAXNETNODES];
+#ifdef __WATCOMC__
+	int nextTic;
+	extern volatile int ticcount;
+
+	nextTic = ticcount+8;
+#endif
+
+	autostart = true;
+
+	memset (gotClass,0,sizeof(gotClass));
+	memset (gotinfo,0,sizeof(gotinfo));
+	gotClass[doomcom->consoleplayer] = true;
+	do
+	{
+		i = 0;
+
+		CheckAbort();
+		while(HGetPacket())
+		{ // Check for any incoming packets
+			if(netbuffer->checksum&NCMD_SETUP && netbuffer->starttic >= 64)
+			{
+				
+				PlayerClass[netbuffer->player] = netbuffer->starttic&0x3f;
+				if(!gotClass[netbuffer->player])
+				{
+					gotClass[netbuffer->player] = true;
+					ST_NetProgress();
+					ST_Message("\n");
+				}
+				if(netbuffer->retransmitfrom)
+				{ // that node has received info from all other nodes
+					gotinfo[netbuffer->player] = true;
+				}
+			}
+		}
+#ifdef __WATCOMC__
+		if(ticcount <= nextTic)
+		{ // only send packets every half second
+			continue;
+		}
+		nextTic = ticcount+8;
+#endif
+		// Keep sending out packets containing the console class
+		for(i = 0; i < doomcom->numnodes; i++)
+		{
+			netbuffer->player = doomcom->consoleplayer;
+			netbuffer->starttic = PlayerClass[doomcom->consoleplayer]+64;
+			netbuffer->retransmitfrom = gotinfo[doomcom->consoleplayer];
+			netbuffer->numtics = 0;
+			HSendPacket(i, NCMD_SETUP);
+		}
+		for(i = 0; i < doomcom->numnodes; i++)
+		{ // Make sure that all nodes have sent class info
+			if (!gotClass[i])
+			{
+				ST_Message(".");
+				break;
+			}
+		}
+		if(i < doomcom->numnodes)
+		{
+			continue;
+		}
+		else
+		{ // consoleplayer has received all player classes
+			if(gotinfo[doomcom->consoleplayer])
+			{
+				CheckAbort();
+			}
+			else
+			{
+				gotinfo[doomcom->consoleplayer] = true;
+				ST_Message("All player classes received, ready to proceed\n");
+				ST_NetDone();
+			}
+		}
+		for (i = 0; i < doomcom->numnodes; i++)
+		{ // Make sure that all nodes are ready to proceed
+			if (!gotinfo[i])
+			{
+				break;
+			}
+		}
+	} while(i < doomcom->numnodes);
+
+	memset (gotinfo,0,sizeof(gotinfo));
+
+	if (doomcom->consoleplayer)
+	{       // listen for setup info from key player
+//              ST_Message ("listening for network start info...\n");
+		while (1)
+		{
+			CheckAbort ();
+			if (!HGetPacket ())
+				continue;
+			if(netbuffer->checksum & NCMD_SETUP && netbuffer->starttic < 64)
+			{
+				if (netbuffer->player != VERSION)
+					I_Error ("Different HEXEN 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 = 1;
+				return;
+			}
+		}
+	}
+	else
+	{       // key player, send the setup info
+//              ST_Message ("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 = startmap&0x3f;
+				netbuffer->player = 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);
+	}
+}
+
+/*
+===================
+=
+= D_CheckNetGame
+=
+= Works out player numbers among the net participants
+===================
+*/
+
+extern  int                     viewangleoffset;
+
+void D_CheckNetGame (void)
+{
+	int             i;
+	int pClass;
+
+	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;
+	pClass = PCLASS_FIGHTER;
+	if(i = M_CheckParm("-class"))
+	{
+		pClass = atoi(myargv[i+1]);
+		if(pClass > PCLASS_MAGE || pClass < PCLASS_FIGHTER)
+		{
+			I_Error("Invalid player class: %d\n", pClass);
+		}
+		ST_Message("\nPlayer Class: %d\n", pClass);
+	}
+	PlayerClass[consoleplayer] = pClass;
+	if (netgame)
+		D_ArbitrateNetStart ();
+//ST_Message ("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;
+
+//ST_Message ("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 = MAXINT;
+	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 = MAXINT;
+
+			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;
+			}
+		}
+
+//
+// run the count * ticdup dics
+//
+	while (counts--)
+	{
+		for (i=0 ; i<ticdup ; i++)
+		{
+			if (gametic/ticdup > lowtic)
+				I_Error ("gametic>lowtic");
+			if (advancedemo)
+				H2_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
+	}
+}
--- /dev/null
+++ b/src/hexen/defs.inc
@@ -1,0 +1,52 @@
+SKIPPRIMITIVES	=  0			; set to 1 to skip unwound drawing
+
+
+SCREEN  =       0a0000h
+SCREENWIDTH     =   320
+SCREENHEIGHT	=	200
+PLANEWIDTH		=	80
+PLANESIZE		=	80*200
+
+PEL_WRITE_ADR	=	03c8h
+PEL_DATA		=	03c9h
+
+SC_INDEX		=	03C4h
+SC_MAPMASK		=	2
+
+OP_RET		=	0c3h
+OP_MOVAL	= 	08ah
+OP_MOVDEST	= 	088h
+
+
+	.DATA
+
+EXTRN	_dc_colormap:DWORD
+EXTRN _tinttable:DWORD
+EXTRN	_dc_x:DWORD
+EXTRN	_dc_yl:DWORD
+EXTRN	_dc_yh:DWORD
+EXTRN	_dc_iscale:DWORD
+EXTRN	_dc_texturemid:DWORD
+EXTRN	_dc_source:DWORD
+
+EXTRN	_ylookup:DWORD
+EXTRN	_columnofs:DWORD
+
+
+EXTRN	_ds_y:DWORD
+EXTRN	_ds_x1:DWORD
+EXTRN	_ds_x2:DWORD
+EXTRN	_ds_colormap:DWORD
+EXTRN	_ds_xfrac:DWORD
+EXTRN	_ds_yfrac:DWORD
+EXTRN	_ds_xstep:DWORD
+EXTRN	_ds_ystep:DWORD
+EXTRN	_ds_source:DWORD
+
+PUSHR	MACRO
+	pushad
+ENDM
+
+POPR	MACRO
+	popad
+ENDM
--- /dev/null
+++ b/src/hexen/drcoord.h
@@ -1,0 +1,29 @@
+
+//**************************************************************************
+//**
+//** DRCoord.h : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: DRCoord.h,v $
+//** $Revision: 1.1 $
+//** $Date: 95/05/11 00:19:30 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+#import <appkit/appkit.h>
+
+@interface DRCoord:Object
+{
+	id	players_i;
+	id	console_i;
+	id	skill_i;
+	id	episode_i;
+	id	map_i;
+}
+
+- newGame: sender;
+- scale1: sender;
+- scale2: sender;
+- scale4: sender;
+
+@end
--- /dev/null
+++ b/src/hexen/dstrings.h
@@ -1,0 +1,205 @@
+
+//**************************************************************************
+//**
+//** DStrings.H
+//**
+//**************************************************************************
+
+// MN_menu.c ---------------------------------------------------------------
+
+#define PRESSKEY	"press a key."
+#define PRESSYN		"press y or n."
+#define TXT_PAUSED	"PAUSED"
+#define QUITMSG		"are you sure you want to\nquit this great game?"
+#define LOADNET		"you can't do load while in a net game!\n\n"PRESSKEY
+#define QLOADNET	"you can't quickload during a netgame!\n\n"PRESSKEY
+#define QSAVESPOT	"you haven't picked a quicksave slot yet!\n\n"PRESSKEY
+#define SAVEDEAD 	"you can't save if you aren't playing!\n\n"PRESSKEY
+#define QSPROMPT 	"quicksave over your game named\n\n'%s'?\n\n"PRESSYN
+#define QLPROMPT	"do you want to quickload the game named"\
+					"\n\n'%s'?\n\n"PRESSYN
+#define NEWGAME		"you can't start a new game\n"\
+					"while in a network game.\n\n"PRESSKEY
+#define NIGHTMARE	"are you sure? this skill level\n"\
+					"isn't even remotely fair.\n\n"PRESSYN
+#define SWSTRING	"this is the shareware version of doom.\n\n"\
+					"you need to order the entire trilogy.\n\n"PRESSKEY
+#define MSGOFF		"Messages OFF"
+#define MSGON		"Messages ON"
+#define NETEND		"you can't end a netgame!\n\n"PRESSKEY
+#define ENDGAME		"are you sure you want to end the game?\n\n"PRESSYN
+#define DOSY		"(press y to quit to dos.)"
+#define DETAILHI	"High detail"
+#define DETAILLO	"Low detail"
+#define GAMMALVL0	"Gamma correction OFF"
+#define GAMMALVL1	"Gamma correction level 1"
+#define GAMMALVL2	"Gamma correction level 2"
+#define GAMMALVL3	"Gamma correction level 3"
+#define GAMMALVL4	"Gamma correction level 4"
+#define	EMPTYSTRING	"empty slot"
+
+// P_inter.c ---------------------------------------------------------------
+
+// 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"
+
+// M_misc.c ----------------------------------------------------------------
+
+#define HUSTR_CHATMACRO1 "I'm ready to kick butt!"
+#define HUSTR_CHATMACRO2 "I'm OK."
+#define HUSTR_CHATMACRO3 "I'm not looking too good!"
+#define HUSTR_CHATMACRO4 "Help!"
+#define HUSTR_CHATMACRO5 "You suck!"
+#define HUSTR_CHATMACRO6 "Next time, scumbag..."
+#define HUSTR_CHATMACRO7 "Come here!"
+#define HUSTR_CHATMACRO8 "I'll take care of it."
+#define HUSTR_CHATMACRO9 "Yes"
+#define HUSTR_CHATMACRO0 "No"
+
+// AM_map.c ----------------------------------------------------------------
+
+#define AMSTR_FOLLOWON		"FOLLOW MODE ON"
+#define AMSTR_FOLLOWOFF		"FOLLOW MODE OFF"
+
+// 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?"
--- /dev/null
+++ b/src/hexen/f_finale.c
@@ -1,0 +1,376 @@
+
+//**************************************************************************
+//**
+//** f_finale.c : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: f_finale.c,v $
+//** $Revision: 1.7 $
+//** $Date: 96/01/05 23:33:26 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2def.h"
+#include "soundst.h"
+#include "p_local.h"
+#include <ctype.h>
+
+// MACROS ------------------------------------------------------------------
+
+#define	TEXTSPEED	3
+#define	TEXTWAIT	250
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+static void TextWrite(void);
+static void DrawPic(void);
+static void InitializeFade(boolean fadeIn);
+static void DeInitializeFade(void);
+static void FadePic(void);
+static char *GetFinaleText(int sequence);
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+extern boolean automapactive;
+extern boolean viewactive;
+
+// PUBLIC DATA DECLARATIONS ------------------------------------------------
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+static int FinaleStage;
+static int FinaleCount;
+static int FinaleEndCount;
+static int FinaleLumpNum;
+static int FontABaseLump;
+static char *FinaleText;
+
+static fixed_t *Palette;
+static fixed_t *PaletteDelta;
+static byte *RealPalette;
+
+// CODE --------------------------------------------------------------------
+
+//===========================================================================
+//
+// F_StartFinale
+//
+//===========================================================================
+
+void F_StartFinale (void)
+{
+	gameaction = ga_nothing;
+	gamestate = GS_FINALE;
+	viewactive = false;
+	automapactive = false;
+	P_ClearMessage(&players[consoleplayer]);
+
+	FinaleStage = 0;
+	FinaleCount = 0;
+	FinaleText = GetFinaleText(0);
+	FinaleEndCount = 70;
+	FinaleLumpNum = W_GetNumForName("FINALE1");
+	FontABaseLump = W_GetNumForName("FONTA_S")+1;
+	InitializeFade(1);
+
+//	S_ChangeMusic(mus_victor, true);
+	S_StartSongName("hall", false); // don't loop the song
+}
+
+//===========================================================================
+//
+// F_Responder
+//
+//===========================================================================
+
+boolean F_Responder(event_t *event)
+{
+	return false;
+}
+
+//===========================================================================
+//
+// F_Ticker
+//
+//===========================================================================
+
+void F_Ticker (void)
+{
+	FinaleCount++;
+	if(FinaleStage < 5 && FinaleCount >= FinaleEndCount)
+	{
+		FinaleCount = 0;
+		FinaleStage++;
+		switch(FinaleStage)
+		{
+			case 1: // Text 1
+				FinaleEndCount = strlen(FinaleText)*TEXTSPEED+TEXTWAIT;
+				break;
+			case 2: // Pic 2, Text 2
+				FinaleText = GetFinaleText(1);
+				FinaleEndCount = strlen(FinaleText)*TEXTSPEED+TEXTWAIT;
+				FinaleLumpNum = W_GetNumForName("FINALE2");
+				S_StartSongName("orb", false);
+				break;
+			case 3: // Pic 2 -- Fade out
+				FinaleEndCount = 70;
+				DeInitializeFade();
+				InitializeFade(0);
+				break;
+			case 4: // Pic 3 -- Fade in
+				FinaleLumpNum = W_GetNumForName("FINALE3");
+				FinaleEndCount = 71;
+				DeInitializeFade();
+				InitializeFade(1);
+				S_StartSongName("chess", true);
+				break;
+			case 5: // Pic 3 , Text 3
+				FinaleText = GetFinaleText(2);
+				DeInitializeFade();
+				break;
+			default:
+				 break;
+		}
+		return;
+	}
+	if(FinaleStage == 0 || FinaleStage == 3 || FinaleStage == 4)
+	{
+		FadePic();
+	}
+}
+
+//===========================================================================
+//
+// TextWrite
+//
+//===========================================================================
+
+static void TextWrite (void)
+{
+	int		count;
+	char	*ch;
+	int		c;
+	int		cx, cy;
+	patch_t *w;
+
+	memcpy(screen, W_CacheLumpNum(FinaleLumpNum, PU_CACHE), 
+		SCREENWIDTH*SCREENHEIGHT);
+	if(FinaleStage == 5)
+	{ // Chess pic, draw the correct character graphic
+		if(netgame)
+		{
+			V_DrawPatch(20, 0, W_CacheLumpName("chessall", PU_CACHE));
+		}
+		else if(PlayerClass[consoleplayer])
+		{
+			V_DrawPatch(60, 0, W_CacheLumpNum(W_GetNumForName("chessc")
+				+PlayerClass[consoleplayer]-1, PU_CACHE));
+		}
+	}
+	// Draw the actual text
+	if(FinaleStage == 5)
+	{
+		cy = 135;
+	}
+	else
+	{
+		cy = 5;
+	}
+	cx = 20;
+	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;
+		}
+		if(c < 32)
+		{
+			continue;
+		}
+		c = toupper(c);
+		if(c == 32)
+		{
+			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;
+	}
+}
+
+//===========================================================================
+//
+// InitializeFade
+//
+//===========================================================================
+
+static void InitializeFade(boolean fadeIn)
+{
+	unsigned i;
+
+	Palette = Z_Malloc(768*sizeof(fixed_t), PU_STATIC, 0);
+	PaletteDelta = Z_Malloc(768*sizeof(fixed_t), PU_STATIC, 0);
+	RealPalette = Z_Malloc(768*sizeof(byte), PU_STATIC, 0);
+
+	if(fadeIn)
+	{
+		memset(RealPalette, 0, 768*sizeof(byte));
+		for(i = 0; i < 768; i++)
+		{
+			Palette[i] = 0;
+			PaletteDelta[i] = FixedDiv((*((byte *)W_CacheLumpName("playpal", 
+				PU_CACHE)+i))<<FRACBITS, 70*FRACUNIT);
+		}
+	}
+	else
+	{
+		for(i = 0; i < 768; i++)
+		{
+			RealPalette[i] = *((byte *)W_CacheLumpName("playpal", PU_CACHE)+i);
+			Palette[i] = RealPalette[i]<<FRACBITS;
+			PaletteDelta[i] = FixedDiv(Palette[i], -70*FRACUNIT);
+		}
+	}
+	I_SetPalette(RealPalette);
+}
+
+//===========================================================================
+//
+// DeInitializeFade
+//
+//===========================================================================
+
+static void DeInitializeFade(void)
+{
+	Z_Free(Palette);
+	Z_Free(PaletteDelta);
+	Z_Free(RealPalette);
+}
+
+//===========================================================================
+//
+// FadePic
+//
+//===========================================================================
+
+static void FadePic(void)
+{
+	unsigned i;
+
+	for(i = 0; i < 768; i++)
+	{
+		Palette[i] += PaletteDelta[i];
+		RealPalette[i] = Palette[i]>>FRACBITS;
+	}
+	I_SetPalette(RealPalette);
+}
+
+//===========================================================================
+//
+// DrawPic
+//
+//===========================================================================
+
+static void DrawPic(void)
+{
+	memcpy(screen, W_CacheLumpNum(FinaleLumpNum, PU_CACHE), 
+		SCREENWIDTH*SCREENHEIGHT);
+	if(FinaleStage == 4 || FinaleStage == 5)
+	{ // Chess pic, draw the correct character graphic
+		if(netgame)
+		{
+			V_DrawPatch(20, 0, W_CacheLumpName("chessall", PU_CACHE));
+		}
+		else if(PlayerClass[consoleplayer])
+		{
+			V_DrawPatch(60, 0, W_CacheLumpNum(W_GetNumForName("chessc")
+				+PlayerClass[consoleplayer]-1, PU_CACHE));
+		}
+	}
+}
+
+//===========================================================================
+//
+// F_Drawer
+//
+//===========================================================================
+
+void F_Drawer(void)
+{
+	switch(FinaleStage)
+	{
+		case 0: // Fade in initial finale screen
+			DrawPic();
+			break;
+		case 1:
+		case 2:
+			TextWrite();
+			break;
+		case 3: // Fade screen out
+			DrawPic();
+			break;
+		case 4: // Fade in chess screen
+			DrawPic();
+			break;
+		case 5:
+			TextWrite();
+			break;
+	}
+	UpdateState |= I_FULLSCRN;	
+}
+
+//==========================================================================
+//
+// GetFinaleText
+//
+//==========================================================================
+
+static char *GetFinaleText(int sequence)
+{
+	char *msgLumpName;
+	int msgSize;
+	int msgLump;
+	static char *winMsgLumpNames[] =
+	{
+		"win1msg",
+		"win2msg",
+		"win3msg"
+	};
+
+	msgLumpName = winMsgLumpNames[sequence];
+	msgLump = W_GetNumForName(msgLumpName);
+	msgSize = W_LumpLength(msgLump);
+	if(msgSize >= MAX_INTRMSN_MESSAGE_SIZE)
+	{
+		I_Error("Finale message too long (%s)", msgLumpName);
+	}
+	W_ReadLump(msgLump, ClusterMessage);
+	ClusterMessage[msgSize] = 0; // Append terminator
+	return ClusterMessage;
+}
--- /dev/null
+++ b/src/hexen/g_game.c
@@ -1,0 +1,2014 @@
+
+//**************************************************************************
+//**
+//** g_game.c : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: g_game.c,v $
+//** $Revision: 1.68 $
+//** $Date: 96/01/12 13:18:07 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+#include <string.h>
+#include "h2def.h"
+#include "p_local.h"
+#include "soundst.h"
+
+#define AM_STARTKEY	9
+
+// External functions
+
+extern void R_InitSky(int map);
+extern void P_PlayerNextArtifact(player_t *player);
+
+// Functions
+
+boolean G_CheckDemoStatus (void);
+void G_ReadDemoTiccmd (ticcmd_t *cmd);
+void G_WriteDemoTiccmd (ticcmd_t *cmd);
+void G_InitNew (skill_t skill, int episode, int map);
+
+void G_DoReborn (int playernum);
+
+void G_DoLoadLevel(void);
+void G_DoInitNew(void);
+void G_DoNewGame(void);
+void G_DoLoadGame(void);
+void G_DoPlayDemo(void);
+void G_DoTeleportNewMap(void);
+void G_DoCompleted(void);
+void G_DoVictory(void);
+void G_DoWorldDone(void);
+void G_DoSaveGame(void);
+void G_DoSingleReborn(void);
+
+void H2_PageTicker(void);
+void H2_AdvanceDemo(void);
+
+extern boolean mn_SuicideConsole;
+
+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];
+pclass_t		PlayerClass[MAXPLAYERS];
+
+// Position indicator for cooperative net-play reborn
+int RebornPosition;
+
+int             consoleplayer;          // player taking events and displaying
+int             displayplayer;          // view being displayed
+int             gametic;
+int             levelstarttic;          // gametic at level start
+
+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];
+
+//
+// controls (have defaults)
+//
+int key_right, key_left, key_up, key_down;
+int key_strafeleft, key_straferight, key_jump;
+int key_fire, key_use, key_strafe, key_speed;
+int	key_flyup, key_flydown, key_flycenter;
+int	key_lookup, key_lookdown, key_lookcenter;
+int	key_invleft, key_invright, key_useartifact;
+
+int mousebfire;
+int mousebstrafe;
+int mousebforward;
+int mousebjump;
+
+int joybfire;
+int joybstrafe;
+int joybuse;
+int joybspeed;
+int joybjump;
+
+int LeaveMap;
+static int LeavePosition;
+
+//#define MAXPLMOVE       0x32 // Old Heretic Max move
+
+fixed_t MaxPlayerMove[NUMCLASSES] = { 0x3C, 0x32, 0x2D, 0x31 };
+fixed_t forwardmove[NUMCLASSES][2] = 
+{
+	{ 0x1D, 0x3C },
+	{ 0x19, 0x32 },
+	{ 0x16, 0x2E },
+	{ 0x18, 0x31 }
+};
+	
+fixed_t sidemove[NUMCLASSES][2] = 
+{
+	{ 0x1B, 0x3B },
+	{ 0x18, 0x28 },
+	{ 0x15, 0x25 },
+	{ 0x17, 0x27 }
+};
+
+fixed_t angleturn[3] = {640, 1280, 320};     // + slow turn
+#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;
+
+int             joyxmove, joyymove;         // joystick values are repeated
+boolean         joyarray[5];
+boolean         *joybuttons = &joyarray[1];     // allow [-1]
+
+int     savegameslot;
+char    savedescription[32];
+
+int inventoryTics;
+
+#ifdef __WATCOMC__
+extern externdata_t *i_ExternData;
+#endif
+
+static skill_t TempSkill;
+static int TempEpisode;
+static int TempMap;
+
+//=============================================================================
+/*
+====================
+=
+= 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;
+
+extern  int             isCyberPresent;     // is CyberMan present?
+boolean usearti = true;
+void I_ReadCyberCmd (ticcmd_t *cmd);
+
+void G_BuildTiccmd (ticcmd_t *cmd)
+{
+	int             i;
+	boolean         strafe, bstrafe;
+	int             speed, tspeed, lspeed;
+	int             forward, side;
+	int look, arti;
+	int flyheight;
+	int pClass;
+
+	extern boolean artiskip;
+
+#ifdef __WATCOMC__
+	int angleDelta;
+	static int oldAngle;
+	extern int newViewAngleOff;
+	static int externInvKey;
+	extern boolean automapactive;
+	event_t ev;
+#endif
+
+	pClass = players[consoleplayer].class;
+	memset (cmd,0,sizeof(*cmd));
+
+//	cmd->consistancy =
+//		consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS];
+
+	cmd->consistancy =
+		consistancy[consoleplayer][maketic%BACKUPTICS];
+	if (isCyberPresent)
+		I_ReadCyberCmd (cmd);
+
+//printf ("cons: %i\n",cmd->consistancy);
+	
+	strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
+		|| joybuttons[joybstrafe];
+	speed = gamekeydown[key_speed] || joybuttons[joybspeed]
+		|| joybuttons[joybspeed];
+#ifdef __WATCOMC__
+	if(useexterndriver)
+	{
+		speed |= (i_ExternData->buttons&EBT_SPEED);
+		strafe |= (i_ExternData->buttons&EBT_STRAFE);
+	}
+#endif
+
+	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; // 3;
+	}
+	else
+	{
+		lspeed = 2; // 5;
+	}
+
+//
+// let movement keys cancel each other out
+//
+	if(strafe)
+	{
+		if (gamekeydown[key_right])
+		{
+			side += sidemove[pClass][speed];
+		}
+		if (gamekeydown[key_left])
+		{
+			side -= sidemove[pClass][speed];
+		}
+		if (joyxmove > 0)
+		{
+			side += sidemove[pClass][speed];
+		}
+		if (joyxmove < 0)
+		{
+			side -= sidemove[pClass][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[pClass][speed];
+	}
+	if (gamekeydown[key_down])
+	{
+		forward -= forwardmove[pClass][speed];
+	}
+	if (joyymove < 0)
+	{
+		forward += forwardmove[pClass][speed];
+	}
+	if (joyymove > 0)
+	{
+		forward -= forwardmove[pClass][speed];
+	}
+	if (gamekeydown[key_straferight])
+	{
+		side += sidemove[pClass][speed];
+	}
+	if (gamekeydown[key_strafeleft])
+	{
+		side -= sidemove[pClass][speed];
+	}
+
+	// Look up/down/center keys
+	if(gamekeydown[key_lookup])
+	{
+		look = lspeed;
+	}
+	if(gamekeydown[key_lookdown])
+	{
+		look = -lspeed;
+	}
+#ifdef __WATCOMC__
+	if(gamekeydown[key_lookcenter] && !useexterndriver)
+	{
+		look = TOCENTER;
+	}
+#else
+	if(gamekeydown[key_lookcenter])
+	{
+		look = TOCENTER;
+	}
+#endif
+
+#ifdef __WATCOMC__
+	if(useexterndriver && look != TOCENTER && (gamestate == GS_LEVEL ||
+		gamestate == GS_INTERMISSION))
+	{
+		if(i_ExternData->moveForward)
+		{
+			forward += i_ExternData->moveForward;
+			if(speed)
+			{
+				forward <<= 1;
+			}
+		}
+		if(i_ExternData->angleTurn)
+		{
+			if(strafe)
+			{
+				side += i_ExternData->angleTurn;
+			}
+			else
+			{
+				cmd->angleturn += i_ExternData->angleTurn;
+			}
+		}
+		if(i_ExternData->moveSideways)
+		{
+			side += i_ExternData->moveSideways;
+			if(speed)
+			{
+				side <<= 1;
+			}
+		}
+		if(i_ExternData->buttons&EBT_CENTERVIEW)
+		{
+			look = TOCENTER;
+			oldAngle = 0;
+		}
+		else if(i_ExternData->pitch)
+		{
+			angleDelta = i_ExternData->pitch-oldAngle;
+			if(abs(angleDelta) < 35)
+			{
+				look = angleDelta/5;
+			}
+			else
+			{
+				look = 7*(angleDelta > 0 ? 1 : -1);
+			}
+			if(look == TOCENTER)
+			{
+				look++;
+			}
+			oldAngle += look*5;
+		}
+		if(i_ExternData->flyDirection)
+		{
+			if(i_ExternData->flyDirection > 0)
+			{
+				flyheight = 5;
+			}
+			else
+			{
+				flyheight = -5;
+			}
+		}
+		if(abs(newViewAngleOff-i_ExternData->angleHead) < 3000)
+		{
+			newViewAngleOff = i_ExternData->angleHead;
+		}
+		if(i_ExternData->buttons&EBT_FIRE)
+		{
+			cmd->buttons |= BT_ATTACK;
+		}
+		if(i_ExternData->buttons&EBT_OPENDOOR)
+		{
+			cmd->buttons |= BT_USE;
+		}
+		if(i_ExternData->buttons&EBT_PAUSE)
+		{
+			cmd->buttons = BT_SPECIAL|BTS_PAUSE;
+			i_ExternData->buttons &= ~EBT_PAUSE;
+		}
+		if(externInvKey&EBT_USEARTIFACT)
+		{
+			ev.type = ev_keyup;
+			ev.data1 = key_useartifact;
+			H2_PostEvent(&ev);
+			externInvKey &= ~EBT_USEARTIFACT;
+		}
+		else if(i_ExternData->buttons&EBT_USEARTIFACT)
+		{
+			externInvKey |= EBT_USEARTIFACT;
+			ev.type = ev_keydown;
+			ev.data1 = key_useartifact;
+			H2_PostEvent(&ev);
+		}
+		if(externInvKey&EBT_INVENTORYRIGHT)
+		{
+			ev.type = ev_keyup;
+			ev.data1 = key_invright;
+			H2_PostEvent(&ev);
+			externInvKey &= ~EBT_INVENTORYRIGHT;
+		}
+		else if(i_ExternData->buttons&EBT_INVENTORYRIGHT)
+		{
+			externInvKey |= EBT_INVENTORYRIGHT;
+			ev.type = ev_keydown;
+			ev.data1 = key_invright;
+			H2_PostEvent(&ev);
+		}
+		if(externInvKey&EBT_INVENTORYLEFT)
+		{
+			ev.type = ev_keyup;
+			ev.data1 = key_invleft;
+			H2_PostEvent(&ev);
+			externInvKey &= ~EBT_INVENTORYLEFT;
+		}
+		else if(i_ExternData->buttons&EBT_INVENTORYLEFT)
+		{
+			externInvKey |= EBT_INVENTORYLEFT;
+			ev.type = ev_keydown;
+			ev.data1 = key_invleft;
+			H2_PostEvent(&ev);
+		}
+		if(i_ExternData->buttons&EBT_FLYDROP)
+		{
+			flyheight = TOCENTER;
+		}
+		if(gamestate == GS_LEVEL)
+		{
+			if(externInvKey&EBT_MAP)
+			{ // automap
+				ev.type = ev_keyup;
+				ev.data1 = AM_STARTKEY;
+				H2_PostEvent(&ev);
+				externInvKey &= ~EBT_MAP;
+			}
+			else if(i_ExternData->buttons&EBT_MAP)
+			{
+				externInvKey |= EBT_MAP;
+				ev.type = ev_keydown;
+				ev.data1 = AM_STARTKEY;
+				H2_PostEvent(&ev);
+			}
+		}
+		if(i_ExternData->buttons&EBT_WEAPONCYCLE)
+		{
+			int curWeapon;
+			player_t *pl;
+		
+			pl = &players[consoleplayer];
+			curWeapon = pl->readyweapon;
+			for(curWeapon = (curWeapon+1)&3; curWeapon != pl->readyweapon;
+				curWeapon = (curWeapon+1)&3)
+			{
+				if(pl->weaponowned[curWeapon])
+				{
+					cmd->buttons |= BT_CHANGE;
+					cmd->buttons |= curWeapon<<BT_WEAPONSHIFT;
+					break;
+				}
+			}
+		}
+		if(i_ExternData->buttons&EBT_JUMP)
+		{
+			cmd->arti |= AFLAG_JUMP;
+		}
+	}
+#endif
+
+	// 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;
+#ifdef __WATCOMC__
+		if(!useexterndriver)
+		{
+			look = TOCENTER;
+		}
+#else
+		look = TOCENTER;
+#endif
+	}
+	// Use artifact key
+	if(gamekeydown[key_useartifact])
+	{
+		if(gamekeydown[key_speed] && artiskip)
+		{
+			if(players[consoleplayer].inventory[inv_ptr].type != arti_none)
+			{ // Skip an artifact
+				gamekeydown[key_useartifact] = false;
+				P_PlayerNextArtifact(&players[consoleplayer]);			
+			}
+		}
+		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&AFLAG_MASK;
+				usearti = false;
+			}
+		}
+	}
+	if(gamekeydown[key_jump] || mousebuttons[mousebjump]
+		|| joybuttons[joybjump])
+	{
+		cmd->arti |= AFLAG_JUMP;
+	}
+	if(mn_SuicideConsole)
+	{
+		cmd->arti |= AFLAG_SUICIDE;
+		mn_SuicideConsole = false;
+	}
+
+	// Artifact hot keys
+	if(gamekeydown[KEY_BACKSPACE] && !cmd->arti)
+	{
+		gamekeydown[KEY_BACKSPACE] = false; 	// Use one of each artifact
+		cmd->arti = NUMARTIFACTS;
+	}
+	else if(gamekeydown[KEY_BACKSLASH] && !cmd->arti 
+	&& (players[consoleplayer].mo->health < MAXHEALTH))
+	{
+		gamekeydown[KEY_BACKSLASH] = false;
+		cmd->arti = arti_health;						
+	}
+	else if(gamekeydown[KEY_ZERO] && !cmd->arti)
+	{
+		gamekeydown[KEY_ZERO] = false;
+		cmd->arti = arti_poisonbag;						
+	}
+	else if(gamekeydown[KEY_NINE] && !cmd->arti)
+	{
+		gamekeydown[KEY_NINE] = false;
+		cmd->arti = arti_blastradius;					
+	}
+	else if(gamekeydown[KEY_EIGHT] && !cmd->arti)
+	{
+		gamekeydown[KEY_EIGHT] = false;
+		cmd->arti = arti_teleport;						
+	}
+	else if(gamekeydown[KEY_SEVEN] && !cmd->arti)
+	{
+		gamekeydown[KEY_SEVEN] = false;
+		cmd->arti = arti_teleportother;						
+	}
+	else if(gamekeydown[KEY_SIX] && !cmd->arti)
+	{
+		gamekeydown[KEY_SIX] = false;
+		cmd->arti = arti_egg;						
+	}
+	else if(gamekeydown[KEY_FIVE] && !cmd->arti
+	&& !players[consoleplayer].powers[pw_invulnerability])
+	{
+		gamekeydown[KEY_FIVE] = false;
+		cmd->arti = arti_invulnerability;				
+	}
+
+//
+// buttons
+//
+	cmd->chatchar = CT_dequeueChatChar();
+
+	if (gamekeydown[key_fire] || mousebuttons[mousebfire]
+		|| joybuttons[joybfire])
+		cmd->buttons |= BT_ATTACK;
+
+	if (gamekeydown[key_use] || joybuttons[joybuse] )
+	{
+		cmd->buttons |= BT_USE;
+		dclicks = 0;                    // clear double clicks if hit use button
+	}
+	
+	for(i = 0; i < NUMWEAPONS; i++)
+	{
+		if(gamekeydown['1'+i])
+		{
+			cmd->buttons |= BT_CHANGE;
+			cmd->buttons |= i<<BT_WEAPONSHIFT;
+			break;
+		}
+	}
+
+//
+// mouse
+//
+	if (mousebuttons[mousebforward])
+	{
+		forward += forwardmove[pClass][speed];
+	}
+		
+//
+// 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 > MaxPlayerMove[pClass])
+	{
+		forward = MaxPlayerMove[pClass];
+	}
+	else if (forward < -MaxPlayerMove[pClass])
+	{
+		forward = -MaxPlayerMove[pClass];
+	}
+	if (side > MaxPlayerMove[pClass])
+	{
+		side = MaxPlayerMove[pClass];
+	}
+	else if (side < -MaxPlayerMove[pClass])
+	{
+		side = -MaxPlayerMove[pClass];
+	}
+
	if(players[consoleplayer].powers[pw_speed]
+		&& !players[consoleplayer].morphTics)
+	{ // Adjust for a player with a speed artifact
+		forward = (3*forward)>>1;
+		side = (3*side)>>1;
+	}
+	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));
+	}
+
+	SN_StopAllSequences();	
+	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));
+}
+
+
+/*
+===============================================================================
+=
+= 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(CT_Responder(ev))
+	{ // Chat ate the event
+		return(true);
+	}
+	if(gamestate == GS_LEVEL)
+	{
+		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:
+			joybuttons[0] = ev->data1&1;
+			joybuttons[1] = ev->data1&2;
+			joybuttons[2] = ev->data1&4;
+			joybuttons[3] = ev->data1&8;
+			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_initnew:
+				G_DoInitNew();
+				break;
+			case ga_newgame:
+				G_DoNewGame();
+				break;
+			case ga_loadgame:
+				Draw_LoadIcon();
+				G_DoLoadGame();
+				break;
+			case ga_savegame:
+				Draw_SaveIcon();
+				G_DoSaveGame();
+				break;
+			case ga_singlereborn:
+				G_DoSingleReborn();
+				break;
+			case ga_playdemo:
+				G_DoPlayDemo();
+				break;
+			case ga_screenshot:
+				M_ScreenShot();
+				gameaction = ga_nothing;
+				break;
+			case ga_leavemap:
+				Draw_TeleportIcon();
+				G_DoTeleportNewMap();
+				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)
+						{
+							strcpy (savedescription, "NET GAME");
+						}
+						else
+						{
+							strcpy(savedescription, "SAVE GAME");
+						}
+					}
+					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:
+			H2_PageTicker ();
+			break;
+	}       
+}
+
+
+/*
+==============================================================================
+
+						PLAYER STRUCTURE FUNCTIONS
+
+also see P_SpawnPlayer in P_Things
+==============================================================================
+*/
+
+//==========================================================================
+//
+// G_PlayerExitMap
+//
+// Called when the player leaves a map.
+//
+//==========================================================================
+
+void G_PlayerExitMap(int playerNumber)
+{
+	int i;
+	player_t *player;
+	int flightPower;
+
+	player = &players[playerNumber];
+
+//	if(deathmatch)
+//	{
+//		// Strip all but one of each type of artifact
+//		for(i = 0; i < player->inventorySlotNum; i++)
+//		{
+//			player->inventory[i].count = 1;
+//		}
+//		player->artifactCount = player->inventorySlotNum;
+//	}
+//	else
+
+	// Strip all current powers (retain flight)
+	flightPower = player->powers[pw_flight];
+	memset(player->powers, 0, sizeof(player->powers));
+	player->powers[pw_flight] = flightPower;
+
+	if(deathmatch)
+	{
+		player->powers[pw_flight] = 0;
+	}
+	else
+	{
+		if(P_GetMapCluster(gamemap) != P_GetMapCluster(LeaveMap))
+		{ // Entering new cluster
+			// Strip all keys
+			player->keys = 0;
+
+			// Strip flight artifact
+			for(i = 0; i < 25; i++)
+			{
+				player->powers[pw_flight] = 0;
+				P_PlayerUseArtifact(player, arti_fly);
+			}
+			player->powers[pw_flight] = 0;
+		}
+	}
+
+	if(player->morphTics)
+	{
+		player->readyweapon = player->mo->special1; // Restore weapon
+		player->morphTics = 0;
+	}
+	player->messageTics = 0;
+	player->lookdir = 0;
+	player->mo->flags &= ~MF_SHADOW; // Remove invisibility
+	player->extralight = 0; // Remove weapon flashes
+	player->fixedcolormap = 0; // Remove torch
+	player->damagecount = 0; // No palette changes
+	player->bonuscount = 0;
+	player->poisoncount = 0;
+	if(player == &players[consoleplayer])
+	{
+		SB_state = -1; // refresh the status bar
+		viewangleoffset = 0;
+	}
+}
+
+//==========================================================================
+//
+// G_PlayerReborn
+//
+// Called after a player dies.  Almost everything is cleared and
+// initialized.
+//
+//==========================================================================
+
+void G_PlayerReborn(int player)
+{
+	player_t *p;
+	int frags[MAXPLAYERS];
+	int killcount, itemcount, secretcount;
+	uint worldTimer;
+
+	memcpy(frags, players[player].frags, sizeof(frags));
+	killcount = players[player].killcount;
+	itemcount = players[player].itemcount;
+	secretcount = players[player].secretcount;
+	worldTimer = players[player].worldTimer;
+
+	p = &players[player];
+	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;
+	players[player].worldTimer = worldTimer;
+	players[player].class = PlayerClass[player];
+
+	p->usedown = p->attackdown = true; // don't do anything immediately
+	p->playerstate = PST_LIVE;
+	p->health = MAXHEALTH;
+	p->readyweapon = p->pendingweapon = WP_FIRST;
+	p->weaponowned[WP_FIRST] = true;
+	p->messageTics = 0;
+	p->lookdir = 0;
+	localQuakeHappening[player] = false;
+	if(p == &players[consoleplayer])
+	{
+		SB_state = -1; // refresh the status bar
+		inv_ptr = 0; // reset the inventory pointer
+		curpos = 0;
+		viewangleoffset = 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_TELEPORT);  // 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;
+
+	// This check has been moved to p_setup.c:P_LoadThings()
+	//if (selections < 8)
+	//	I_Error ("Only %i deathmatch spots, 8 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[0][playernum]);
+}
+
+//==========================================================================
+//
+// G_DoReborn
+//
+//==========================================================================
+
+void G_DoReborn(int playernum)
+{
+	int i;
+	boolean oldWeaponowned[NUMWEAPONS];
+	int oldKeys;
+	int oldPieces;
+	boolean foundSpot;
+	int bestWeapon;
+
+	if(G_CheckDemoStatus())
+	{
+		return;
+	}
+	if(!netgame)
+	{
+		if(SV_RebornSlotAvailable())
+		{ // Use the reborn code if the slot is available
+			gameaction = ga_singlereborn;
+		}
+		else
+		{ // Start a new game if there's no reborn info
+			gameaction = ga_newgame;
+		}
+	}
+	else
+	{ // Net-game
+		players[playernum].mo->player = NULL; // Dissassociate the corpse
+
+		if(deathmatch)
+		{ // Spawn at random spot if in death match
+			G_DeathMatchSpawnPlayer(playernum);
+			return;
+		}
+
+		// Cooperative net-play, retain keys and weapons
+		oldKeys = players[playernum].keys;
+		oldPieces = players[playernum].pieces;
+		for(i = 0; i < NUMWEAPONS; i++)
+		{
+			oldWeaponowned[i] = players[playernum].weaponowned[i];
+		}
+
+		foundSpot = false;
+		if(G_CheckSpot(playernum,
+			&playerstarts[RebornPosition][playernum]))
+		{ // Appropriate player start spot is open
+			P_SpawnPlayer(&playerstarts[RebornPosition][playernum]);
+			foundSpot = true;
+		}
+		else
+		{
+			// Try to spawn at one of the other player start spots
+			for(i = 0; i < MAXPLAYERS; i++)
+			{
+				if(G_CheckSpot(playernum, &playerstarts[RebornPosition][i]))
+				{ // Found an open start spot
+
+					// Fake as other player
+					playerstarts[RebornPosition][i].type = playernum+1;
+					P_SpawnPlayer(&playerstarts[RebornPosition][i]);
+
+					// Restore proper player type
+					playerstarts[RebornPosition][i].type = i+1;
+	
+					foundSpot = true;
+					break;
+				}
+			}
+		}
+
+		if(foundSpot == false)
+		{ // Player's going to be inside something
+			P_SpawnPlayer(&playerstarts[RebornPosition][playernum]);
+		}
+
+		// Restore keys and weapons
+		players[playernum].keys = oldKeys;
+		players[playernum].pieces = oldPieces;
+		for(bestWeapon = 0, i = 0; i < NUMWEAPONS; i++)
+		{
+			if(oldWeaponowned[i])
+			{
+				bestWeapon = i;
+				players[playernum].weaponowned[i] = true;
+			}
+		}
+		players[playernum].mana[MANA_1] = 25;
+		players[playernum].mana[MANA_2] = 25;
+		if(bestWeapon)
+		{ // Bring up the best weapon
+			players[playernum].pendingweapon = bestWeapon;
+		}
+	}
+}
+
+void G_ScreenShot (void)
+{
+	gameaction = ga_screenshot;
+}
+
+//==========================================================================
+//
+// G_StartNewInit
+//
+//==========================================================================
+
+void G_StartNewInit(void)
+{
+	SV_InitBaseSlot();
+	SV_ClearRebornSlot();
+	P_ACSInitNewGame();
+	// Default the player start spot group to 0
+	RebornPosition = 0;
+}
+
+//==========================================================================
+//
+// G_StartNewGame
+//
+//==========================================================================
+
+void G_StartNewGame(skill_t skill)
+{
+	int realMap;
+
+	G_StartNewInit();
+	realMap = P_TranslateMap(1);
+	if(realMap == -1)
+	{
+		realMap = 1;
+	}
+	G_InitNew(TempSkill, 1, realMap);
+}
+
+//==========================================================================
+//
+// G_TeleportNewMap
+//
+// Only called by the warp cheat code.  Works just like normal map to map
+// teleporting, but doesn't do any interlude stuff.
+//
+//==========================================================================
+
+void G_TeleportNewMap(int map, int position)
+{
+	gameaction = ga_leavemap;
+	LeaveMap = map;
+	LeavePosition = position;
+}
+
+//==========================================================================
+//
+// G_DoTeleportNewMap
+//
+//==========================================================================
+
+void G_DoTeleportNewMap(void)
+{
+	SV_MapTeleport(LeaveMap, LeavePosition);
+	gamestate = GS_LEVEL;
+	gameaction = ga_nothing;
+	RebornPosition = LeavePosition;
+}
+
+/*
+boolean secretexit;
+void G_ExitLevel (void)
+{
+	secretexit = false;
+	gameaction = ga_completed;
+}
+void G_SecretExitLevel (void)
+{
+	secretexit = true;
+	gameaction = ga_completed;
+}
+*/
+
+//==========================================================================
+//
+// G_Completed
+//
+// Starts intermission routine, which is used only during hub exits,
+// and DeathMatch games.
+//==========================================================================
+
+void G_Completed(int map, int position)
+{
+	gameaction = ga_completed;
+	LeaveMap = map;
+	LeavePosition = position;
+}
+
+void G_DoCompleted(void)
+{
+	int i;
+
+	gameaction = ga_nothing;
+	if(G_CheckDemoStatus())
+	{
+		return;
+	}
+	for(i = 0; i < MAXPLAYERS; i++)
+	{
+		if(playeringame[i])
+		{
+			G_PlayerExitMap(i);
+		}
+	}
+	if(LeaveMap == -1 && LeavePosition == -1)
+	{
+		gameaction = ga_victory;
+		return;
+	}
+	else
+	{		
+		gamestate = GS_INTERMISSION;
+		IN_Start();
+	}
+
+/*
+	int i;
+	static int afterSecret[3] = { 7, 5, 5 };
+
+	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;
+}
+
+//==========================================================================
+//
+// G_DoSingleReborn
+//
+// Called by G_Ticker based on gameaction.  Loads a game from the reborn
+// save slot.
+//
+//==========================================================================
+
+void G_DoSingleReborn(void)
+{
+	gameaction = ga_nothing;
+	SV_LoadGame(SV_GetRebornSlot());
+	SB_SetClassData();
+}
+
+//==========================================================================
+//
+// G_LoadGame
+//
+// Can be called by the startup code or the menu task.
+//
+//==========================================================================
+
+static int GameLoadSlot;
+
+void G_LoadGame(int slot)
+{
+	GameLoadSlot = slot;
+	gameaction = ga_loadgame;
+}
+
+//==========================================================================
+//
+// G_DoLoadGame
+//
+// Called by G_Ticker based on gameaction.
+//
+//==========================================================================
+
+void G_DoLoadGame(void)
+{
+	gameaction = ga_nothing;
+	SV_LoadGame(GameLoadSlot);
+	if(!netgame)
+	{ // Copy the base slot to the reborn slot
+		SV_UpdateRebornSlot();
+	}
+	SB_SetClassData();
+}
+
+//==========================================================================
+//
+// 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)
+{
+	SV_SaveGame(savegameslot, savedescription);
+	gameaction = ga_nothing;
+	savedescription[0] = 0;
+	P_SetMessage(&players[consoleplayer], TXT_GAMESAVED, true);
+}
+
+//==========================================================================
+//
+// G_DeferredNewGame
+//
+//==========================================================================
+
+void G_DeferredNewGame(skill_t skill)
+{
+	TempSkill = skill;
+	gameaction = ga_newgame;
+}
+
+//==========================================================================
+//
+// G_DoNewGame
+//
+//==========================================================================
+
+void G_DoNewGame(void)
+{
+	G_StartNewGame(TempSkill);
+	gameaction = ga_nothing;
+}
+
+/*
+====================
+=
+= G_InitNew
+=
+= Can be called by the startup code or the menu task
+= consoleplayer, displayplayer, playeringame[] should be set
+====================
+*/
+
+void G_DeferedInitNew(skill_t skill, int episode, int map)
+{
+	TempSkill = skill;
+	TempEpisode = episode;
+	TempMap = map;
+	gameaction = ga_initnew;
+}
+
+void G_DoInitNew(void)
+{
+	SV_InitBaseSlot();
+	G_InitNew(TempSkill, TempEpisode, TempMap);
+	gameaction = ga_nothing;
+}
+
+void G_InitNew(skill_t skill, int episode, int map)
+{
+	int i;
+
+	if(paused)
+	{
+		paused = false;
+		S_ResumeSound();
+	}
+	if(skill < sk_baby)
+	{
+		skill = sk_baby;
+	}
+	if(skill > sk_nightmare)
+	{
+		skill = sk_nightmare;
+	}
+	if(map < 1)
+	{
+		map = 1;
+	}
+	if(map > 99)
+	{
+		map = 99;
+	}
+	M_ClearRandom();
+	// Force players to be initialized upon first level load
+	for(i = 0; i < MAXPLAYERS; i++)
+	{
+		players[i].playerstate = PST_REBORN;
+		players[i].worldTimer = 0;
+	}
+
+	// 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;
+	BorderNeedRefresh = true;
+
+	// Initialize the sky
+	R_InitSky(map);
+
+	// Give one null ticcmd_t
+	//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));
+
+	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];
+		*demo_p++ = PlayerClass[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++;
+		PlayerClass[i] = *demo_p++;
+	}
+
+	// Initialize world info, etc.
+	G_StartNewInit();
+
+	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 ();
+			
+		Z_ChangeTag (demobuffer, PU_CACHE);
+		demoplayback = false;
+		H2_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;
+}
--- /dev/null
+++ b/src/hexen/h2_main.c
@@ -1,0 +1,934 @@
+
+//**************************************************************************
+//**
+//** h2_main.c : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: h2_main.c,v $
+//** $Revision: 1.50 $
+//** $Date: 96/01/16 13:02:28 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+// HEADER FILES ------------------------------------------------------------
+
+#ifdef __WATCOMC__
+#include <dos.h>
+#include <sys\types.h>
+#include <direct.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "h2def.h"
+#include "p_local.h"
+#include "soundst.h"
+
+// MACROS ------------------------------------------------------------------
+
+#define CONFIG_FILE_NAME "hexen.cfg"
+#define MAXWADFILES 20
+
+// TYPES -------------------------------------------------------------------
+
+typedef struct
+{
+	char *name;
+	void (*func)(char **args, int tag);
+	int requiredArgs;
+	int tag;
+} execOpt_t;
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+void R_ExecuteSetViewSize(void);
+void D_CheckNetGame(void);
+void G_BuildTiccmd(ticcmd_t *cmd);
+void F_Drawer(void);
+boolean F_Responder(event_t *ev);
+void I_StartupKeyboard(void);
+void I_StartupJoystick(void);
+void I_ShutdownKeyboard(void);
+void S_InitScript(void);
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+void H2_ProcessEvents(void);
+void H2_DoAdvanceDemo(void);
+void H2_AdvanceDemo(void);
+void H2_StartTitle(void);
+void H2_PageTicker(void);
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+static void DrawMessage(void);
+static void PageDrawer(void);
+static void HandleArgs(void);
+static void CheckRecordFrom(void);
+static void AddWADFile(char *file);
+static void DrawAndBlit(void);
+static void ExecOptionFILE(char **args, int tag);
+static void ExecOptionSCRIPTS(char **args, int tag);
+static void ExecOptionDEVMAPS(char **args, int tag);
+static void ExecOptionSKILL(char **args, int tag);
+static void ExecOptionPLAYDEMO(char **args, int tag);
+static void ExecOptionMAXZONE(char **args, int tag);
+static void CreateSavePath(void);
+static void WarpCheck(void);
+
+#ifdef TIMEBOMB
+static void DoTimeBomb(void);
+#endif
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+extern boolean automapactive;
+extern boolean MenuActive;
+extern boolean askforquit;
+extern char *SavePath;
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+boolean DevMaps;			// true = Map development mode
+char *DevMapsDir = "";		// development maps directory
+boolean shareware;			// true if only episode 1 present
+boolean nomonsters;			// checkparm of -nomonsters
+boolean respawnparm;		// checkparm of -respawn
+boolean randomclass;		// checkparm of -randclass
+boolean debugmode;			// checkparm of -debug
+boolean ravpic;				// checkparm of -ravpic
+boolean cdrom;				// true if cd-rom mode active
+boolean cmdfrag;			// true if a CMD_FRAG packet should be sent out
+boolean singletics;			// debug flag to cancel adaptiveness
+boolean artiskip;			// whether shift-enter skips an artifact
+int maxzone = 0x800000;		// Maximum allocated for zone heap (8meg default)
+skill_t startskill;
+int startepisode;
+int startmap;
+boolean autostart;
+boolean advancedemo;
+FILE *debugfile;
+event_t events[MAXEVENTS];
+int eventhead;
+int eventtail;
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+static int WarpMap;
+static int demosequence;
+static int pagetic;
+static char *pagename;
+#ifdef __NeXT__
+static char *wadfiles[MAXWADFILES] =
+{
+	"/Novell/H2/source/hexen.wad"
+};
+#else
+static char *wadfiles[MAXWADFILES] =
+{
+	"hexen.wad"
+};
+#endif
+static execOpt_t ExecOptions[] =
+{
+	{ "-file", ExecOptionFILE, 1, 0 },
+	{ "-scripts", ExecOptionSCRIPTS, 1, 0 },
+	{ "-devmaps", ExecOptionDEVMAPS, 1, 0 },
+	{ "-skill", ExecOptionSKILL, 1, 0 },
+	{ "-playdemo", ExecOptionPLAYDEMO, 1, 0 },
+	{ "-timedemo", ExecOptionPLAYDEMO, 1, 0 },
+	{ "-maxzone", ExecOptionMAXZONE, 1, 0 },
+	{ NULL, NULL, 0, 0 } // Terminator
+};
+
+// CODE --------------------------------------------------------------------
+
+//==========================================================================
+//
+// H2_Main
+//
+//==========================================================================
+void InitMapMusicInfo(void);
+
+void H2_Main(void)
+{
+	int p;
+
+	M_FindResponseFile();
+	setbuf(stdout, NULL);
+	startepisode = 1;
+	autostart = false;
+	startskill = sk_medium;
+	startmap = 1;
+	shareware = false; // Always false for Hexen
+
+	HandleArgs();
+
+	// Initialize subsystems
+
+	ST_Message("V_Init: allocate screens.\n");
+	V_Init();
+
+	// Load defaults before initing other systems
+	ST_Message("M_LoadDefaults: Load system defaults.\n");
+	M_LoadDefaults(CONFIG_FILE_NAME);
+
+	// Now that the savedir is loaded from .CFG, make sure it exists
+	CreateSavePath();
+
+	// HEXEN MODIFICATION:
+	// There is a realloc() in W_AddFile() that might fail if the zone
+	// heap has been previously allocated, so we need to initialize the
+	// WAD files BEFORE the zone memory initialization.
+	ST_Message("W_Init: Init WADfiles.\n");
+	W_InitMultipleFiles(wadfiles);
+
+#ifdef TIMEBOMB
+	DoTimeBomb();
+#endif
+
+	ST_Message("Z_Init: Init zone memory allocation daemon.\n");
+	Z_Init();
+
+#ifdef __WATCOMC__
+	I_StartupKeyboard();
+	I_StartupJoystick();
+#endif
+
+	ST_Message("MN_Init: Init menu system.\n");
+	MN_Init();
+
+	ST_Message("CT_Init: Init chat mode data.\n");
+	CT_Init();
+
+	InitMapMusicInfo();		// Init music fields in mapinfo
+
+#ifdef __WATCOMC__
+	ST_Message("S_InitScript\n");
+	S_InitScript();
+#endif
+
+	ST_Message("SN_InitSequenceScript: Registering sound sequences.\n");
+	SN_InitSequenceScript();
+	ST_Message("I_Init: Setting up machine state.\n");
+	I_Init();
+
+	ST_Message("ST_Init: Init startup screen.\n");
+	ST_Init();
+
+	S_StartSongName("orb", true);
+
+	// Show version message now, so it's visible during R_Init()
+	ST_Message("Executable: "VERSIONTEXT".\n");
+
+	ST_Message("R_Init: Init Hexen refresh daemon");
+	R_Init();
+	ST_Message("\n");
+
+	if (M_CheckParm("-net")) ST_NetProgress();	// Console player found
+
+	ST_Message("P_Init: Init Playloop state.\n");
+	P_Init();
+
+	// Check for command line warping. Follows P_Init() because the
+	// MAPINFO.TXT script must be already processed.
+	WarpCheck();
+
+	if(autostart)
+	{
+		ST_Message("Warp to Map %d (\"%s\":%d), Skill %d\n",
+			WarpMap, P_GetMapName(startmap), startmap, startskill+1);
+	}
+
+	ST_Message("D_CheckNetGame: Checking network game status.\n");
+	D_CheckNetGame();
+
+	ST_Message("SB_Init: Loading patches.\n");
+	SB_Init();
+	
+	CheckRecordFrom();
+
+	p = M_CheckParm("-record");
+	if(p && p < myargc-1)
+	{
+		G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p+1]);
+		H2_GameLoop(); // Never returns
+	}
+
+	p = M_CheckParm("-playdemo");
+	if(p && p < myargc-1)
+	{
+		singledemo = true; // Quit after one demo
+		G_DeferedPlayDemo(myargv[p+1]);
+		H2_GameLoop(); // Never returns
+	}
+
+	p = M_CheckParm("-timedemo");
+	if(p && p < myargc-1)
+	{
+		G_TimeDemo(myargv[p+1]);
+		H2_GameLoop(); // Never returns
+	}
+
+	p = M_CheckParm("-loadgame");
+	if(p && p < myargc-1)
+	{
+		G_LoadGame(atoi(myargv[p+1]));
+	}
+
+	if(gameaction != ga_loadgame)
+	{
+		UpdateState |= I_FULLSCRN;
+		BorderNeedRefresh = true;
+		if(autostart || netgame)
+		{
+			G_StartNewInit();
+			G_InitNew(startskill, startepisode, startmap);
+		}
+		else
+		{
+			H2_StartTitle();
+		}
+	}
+	H2_GameLoop(); // Never returns
+}
+
+//==========================================================================
+//
+// HandleArgs
+//
+//==========================================================================
+
+static void HandleArgs(void)
+{
+	int p;
+	execOpt_t *opt;
+
+	nomonsters = M_ParmExists("-nomonsters");
+	respawnparm = M_ParmExists("-respawn");
+	randomclass = M_ParmExists("-randclass");
+	ravpic = M_ParmExists("-ravpic");
+	artiskip = M_ParmExists("-artiskip");
+	debugmode = M_ParmExists("-debug");
+	deathmatch = M_ParmExists("-deathmatch");
+	cdrom = M_ParmExists("-cdrom");
+	cmdfrag = M_ParmExists("-cmdfrag");
+
+	// Process command line options
+	for(opt = ExecOptions; opt->name != NULL; opt++)
+	{
+		p = M_CheckParm(opt->name);
+		if(p && p < myargc-opt->requiredArgs)
+		{
+			opt->func(&myargv[p], opt->tag);
+		}
+	}
+
+	// Look for an external device driver
+	I_CheckExternDriver();
+}
+
+//==========================================================================
+//
+// WarpCheck
+//
+//==========================================================================
+
+static void WarpCheck(void)
+{
+	int p;
+	int map;
+
+	p = M_CheckParm("-warp");
+	if(p && p < myargc-1)
+	{
+		WarpMap = atoi(myargv[p+1]);
+		map = P_TranslateMap(WarpMap);
+		if(map == -1)
+		{ // Couldn't find real map number
+			startmap = 1;
+			ST_Message("-WARP: Invalid map number.\n");
+		}
+		else
+		{ // Found a valid startmap
+			startmap = map;
+			autostart = true;
+		}
+	}
+	else
+	{
+		WarpMap = 1;
+		startmap = P_TranslateMap(1);
+		if(startmap == -1)
+		{
+			startmap = 1;
+		}
+	}
+}
+
+//==========================================================================
+//
+// ExecOptionSKILL
+//
+//==========================================================================
+
+static void ExecOptionSKILL(char **args, int tag)
+{
+	startskill = args[1][0]-'1';
+	autostart = true;
+}
+
+//==========================================================================
+//
+// ExecOptionFILE
+//
+//==========================================================================
+
+static void ExecOptionFILE(char **args, int tag)
+{
+	int p;
+
+	p = M_CheckParm("-file");
+	while(++p != myargc && myargv[p][0] != '-')
+	{
+		AddWADFile(myargv[p]);
+	}
+}
+
+
+//==========================================================================
+//
+// ExecOptionPLAYDEMO
+//
+//==========================================================================
+
+static void ExecOptionPLAYDEMO(char **args, int tag)
+{
+	char file[256];
+
+	sprintf(file, "%s.lmp", args[1]);
+	AddWADFile(file);
+	ST_Message("Playing demo %s.lmp.\n", args[1]);
+}
+
+//==========================================================================
+//
+// ExecOptionSCRIPTS
+//
+//==========================================================================
+
+static void ExecOptionSCRIPTS(char **args, int tag)
+{
+	sc_FileScripts = true;
+	sc_ScriptsDir = args[1];
+}
+
+//==========================================================================
+//
+// ExecOptionDEVMAPS
+//
+//==========================================================================
+
+static void ExecOptionDEVMAPS(char **args, int tag)
+{
+	DevMaps = true;
+	ST_Message("Map development mode enabled:\n");
+	ST_Message("[config    ] = %s\n", args[1]);
+	SC_OpenFileCLib(args[1]);
+	SC_MustGetStringName("mapsdir");
+	SC_MustGetString();
+	ST_Message("[mapsdir   ] = %s\n", sc_String);
+	DevMapsDir = malloc(strlen(sc_String)+1);
+	strcpy(DevMapsDir, sc_String);
+	SC_MustGetStringName("scriptsdir");
+	SC_MustGetString();
+	ST_Message("[scriptsdir] = %s\n", sc_String);
+	sc_FileScripts = true;
+	sc_ScriptsDir = malloc(strlen(sc_String)+1);
+	strcpy(sc_ScriptsDir, sc_String);
+	while(SC_GetString())
+	{
+		if(SC_Compare("file"))
+		{
+			SC_MustGetString();
+			AddWADFile(sc_String);
+		}
+		else
+		{
+			SC_ScriptError(NULL);
+		}
+	}
+	SC_Close();
+}
+
+
+long superatol(char *s)
+{
+	long int n=0, r=10, x, mul=1;
+	char *c=s;
+
+	for (; *c; c++)
+	{
+		x = (*c & 223) - 16;
+
+		if (x == -3)
+		{
+			mul = -mul;
+		}
+		else if (x == 72 && r == 10)
+		{
+			n -= (r=n);
+			if (!r) r=16;
+			if (r<2 || r>36) return -1;
+		}
+		else
+		{
+			if (x>10) x-=39;
+			if (x >= r) return -1;
+			n = (n*r) + x;
+		}
+	}
+	return(mul*n);
+}
+
+
+static void ExecOptionMAXZONE(char **args, int tag)
+{
+	int size;
+	
+	size = superatol(args[1]);
+	if (size < MINIMUM_HEAP_SIZE) size = MINIMUM_HEAP_SIZE;
+	if (size > MAXIMUM_HEAP_SIZE) size = MAXIMUM_HEAP_SIZE;
+	maxzone = size;
+}
+
+//==========================================================================
+//
+// H2_GameLoop
+//
+//==========================================================================
+
+void H2_GameLoop(void)
+{
+	if(M_CheckParm("-debugfile"))
+	{
+		char filename[20];
+		sprintf(filename, "debug%i.txt", consoleplayer);
+		debugfile = fopen(filename,"w");
+	}
+	I_InitGraphics();
+	while(1)
+	{
+		// Frame syncronous IO operations
+		I_StartFrame();
+
+		// Process one or more tics
+		if(singletics)
+		{
+			I_StartTic();
+			H2_ProcessEvents();
+			G_BuildTiccmd(&netcmds[consoleplayer][maketic%BACKUPTICS]);
+			if(advancedemo)
+			{
+				H2_DoAdvanceDemo();
+			}
+			G_Ticker();
+			gametic++;
+			maketic++;
+		}
+		else
+		{
+			// Will run at least one tic
+			TryRunTics();
+		}
+
+		// Move positional sounds
+		S_UpdateSounds(players[displayplayer].mo);
+
+		DrawAndBlit();
+	}
+}
+
+//==========================================================================
+//
+// H2_ProcessEvents
+//
+// Send all the events of the given timestamp down the responder chain.
+//
+//==========================================================================
+
+void H2_ProcessEvents(void)
+{
+	event_t *ev;
+
+	for(; eventtail != eventhead; eventtail = (++eventtail)&(MAXEVENTS-1))
+	{
+		ev = &events[eventtail];
+		if(F_Responder(ev))
+		{
+			continue;
+		}
+		if(MN_Responder(ev))
+		{
+			continue;
+		}
+		G_Responder(ev);
+	}
+}
+
+//==========================================================================
+//
+// H2_PostEvent
+//
+// Called by the I/O functions when input is detected.
+//
+//==========================================================================
+
+void H2_PostEvent(event_t *ev)
+{
+	events[eventhead] = *ev;
+	eventhead = (++eventhead)&(MAXEVENTS-1);
+}
+
+//==========================================================================
+//
+// DrawAndBlit
+//
+//==========================================================================
+
+static void DrawAndBlit(void)
+{
+	// 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:
+			PageDrawer();
+			break;
+	}
+
+	if(paused && !MenuActive && !askforquit)
+	{
+		if(!netgame)
+		{
+			V_DrawPatch(160, viewwindowy+5, W_CacheLumpName("PAUSED",
+				PU_CACHE));
+		}
+		else
+		{
+			V_DrawPatch(160, 70, W_CacheLumpName("PAUSED",
+				PU_CACHE));
+		}
+	}
+
+	// Draw current message
+	DrawMessage();
+
+	// Draw Menu
+	MN_Drawer();
+
+	// Send out any new accumulation
+	NetUpdate();
+
+	// Flush buffered stuff to screen
+	I_Update();
+}
+
+//==========================================================================
+//
+// DrawMessage
+//
+//==========================================================================
+
+static void DrawMessage(void)
+{
+	player_t *player;
+
+	player = &players[consoleplayer];
+	if(player->messageTics <= 0 || !player->message)
+	{ // No message
+		return;
+	}
+	if(player->yellowMessage)
+	{
+		MN_DrTextAYellow(player->message, 
+			160-MN_TextAWidth(player->message)/2, 1);
+	}
+	else
+	{
+		MN_DrTextA(player->message, 160-MN_TextAWidth(player->message)/2, 1);
+	}
+}
+
+//==========================================================================
+//
+// H2_PageTicker
+//
+//==========================================================================
+
+void H2_PageTicker(void)
+{
+	if(--pagetic < 0)
+	{
+		H2_AdvanceDemo();
+	}
+}
+
+//==========================================================================
+//
+// PageDrawer
+//
+//==========================================================================
+
+static void PageDrawer(void)
+{
+	V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE));
+	if(demosequence == 1)
+	{
+		V_DrawPatch(4, 160, W_CacheLumpName("ADVISOR", PU_CACHE));
+	}
+	UpdateState |= I_FULLSCRN;
+}
+
+//==========================================================================
+//
+// H2_AdvanceDemo
+//
+// Called after each demo or intro demosequence finishes.
+//
+//==========================================================================
+
+void H2_AdvanceDemo(void)
+{
+	advancedemo = true;
+}
+
+//==========================================================================
+//
+// H2_DoAdvanceDemo
+//
+//==========================================================================
+
+void H2_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 = 280;
+			gamestate = GS_DEMOSCREEN;
+			pagename = "TITLE";
+			S_StartSongName("hexen", true);
+			break;
+		case 1:
+			pagetic = 210;
+			gamestate = GS_DEMOSCREEN;
+			pagename = "TITLE";
+			break;
+		case 2:
+			BorderNeedRefresh = true;
+			UpdateState |= I_FULLSCRN;
+			G_DeferedPlayDemo("demo1");
+			break;
+		case 3:
+			pagetic = 200;
+			gamestate = GS_DEMOSCREEN;
+			pagename = "CREDIT";
+			break;
+		case 4:
+			BorderNeedRefresh = true;
+			UpdateState |= I_FULLSCRN;
+			G_DeferedPlayDemo("demo2");
+			break;
+		case 5:
+			pagetic = 200;
+			gamestate = GS_DEMOSCREEN;
+			pagename = "CREDIT";
+			break;
+		case 6:
+			BorderNeedRefresh = true;
+			UpdateState |= I_FULLSCRN;
+			G_DeferedPlayDemo("demo3");
+			break;
+	}
+}
+
+//==========================================================================
+//
+// H2_StartTitle
+//
+//==========================================================================
+
+void H2_StartTitle(void)
+{
+	gameaction = ga_nothing;
+	demosequence = -1;
+	H2_AdvanceDemo();
+}
+
+//==========================================================================
+//
+// CheckRecordFrom
+//
+// -recordfrom <savegame num> <demoname>
+//
+//==========================================================================
+
+static void CheckRecordFrom(void)
+{
+	int p;
+
+	p = M_CheckParm("-recordfrom");
+	if(!p || p > myargc-2)
+	{ // Bad args
+		return;
+	}
+	G_LoadGame(atoi(myargv[p+1]));
+	G_DoLoadGame(); // Load the gameskill etc info from savegame
+	G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p+2]);
+	H2_GameLoop(); // Never returns
+}
+
+//==========================================================================
+//
+// AddWADFile
+//
+//==========================================================================
+
+static void AddWADFile(char *file)
+{
+	int i;
+	char *new;
+
+	ST_Message("Adding external file: %s\n", file);
+	i = 0;
+	while(wadfiles[i])
+	{
+		i++;
+	}
+	new = malloc(strlen(file)+1);
+	strcpy(new, file);
+	wadfiles[i] = new;
+}
+
+#ifdef __WATCOMC__
+/*
+void CleanExit(void)
+{
+	union REGS regs;
+
+	I_ShutdownKeyboard();
+	regs.x.eax = 0x3;
+	int386(0x10, &regs, &regs);
+	printf("Exited from HEXEN: Beyond Heretic.\n");
+	exit(1);
+}
+*/
+#endif
+
+//==========================================================================
+//
+// FixedDiv
+//
+//==========================================================================
+
+fixed_t FixedDiv(fixed_t a, fixed_t b)
+{
+	if((abs(a)>>14) >= abs(b))
+	{
+		return((a^b)<0 ? MININT : MAXINT);
+	}
+	return(FixedDiv2(a, b));
+}
+
+
+//==========================================================================
+//
+// CreateSavePath
+//
+//==========================================================================
+
+static void CreateSavePath(void)
+{
+	char creationPath[121];
+	int len;
+
+	if(cdrom == true)
+	{
+		SavePath = "c:\\hexndata\\";
+	}
+	len = strlen(SavePath);
+	if (len >= 120) I_Error("Save path too long\n");
+	strcpy(creationPath, SavePath);
+#ifdef __WATCOMC__
+	creationPath[len-1] = 0;
+	mkdir(creationPath);
+#endif
+}
+
+#ifdef TIMEBOMB
+//==========================================================================
+//
+// DoTimeBomb
+//
+//==========================================================================
+
+static void DoTimeBomb(void)
+{
+#ifdef __WATCOMC__
+	time_t timeOfDay;
+	struct tm timeBuffer;
+
+	timeOfDay = time(NULL);
+	_localtime(&timeOfDay, &timeBuffer);
+	if(timeBuffer.tm_year != TIMEBOMB_YEAR 
+	|| timeBuffer.tm_yday < TIMEBOMB_STARTDATE 
+	|| timeBuffer.tm_yday > TIMEBOMB_ENDDATE)
+	{
+		I_Error("W_InitWadfiles:  Wad file doesn't have IWAD or PWAD id\n");
+	}
+ 
+	printf("\n===============================================================================\n");
+	printf("                             Hexen:  Beyond Heretic\n\n");
+	printf("                           Beta -- Do Not Distribute!\n");
+	printf("                           Press any key to continue.\n");
+	printf("===============================================================================\n");
+	
+	getch();
+	printf("\n");
+#endif
+}
+#endif
--- /dev/null
+++ b/src/hexen/h2def.h
@@ -1,0 +1,1459 @@
+
+//**************************************************************************
+//**
+//** h2def.h : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: h2def.h,v $
+//** $Revision: 1.128 $
+//** $Date: 96/01/16 10:35:31 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+#ifndef __H2DEF__
+#define __H2DEF__
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "st_start.h"
+#ifdef __WATCOMC__
+#include <malloc.h>
+#define	strcasecmp strcmpi
+#define	strncasecmp strnicmp
+#endif
+
+#define VERSION 110
+#define VERSION_TEXT "v1.1"
+
+// Uncomment, to enable all timebomb stuff
+//#define TIMEBOMB
+#define TIMEBOMB_YEAR	95		// years since 1900
+#define TIMEBOMB_STARTDATE	268	// initial date (9/26)
+#define TIMEBOMB_ENDDATE	301	// end date (10/29)
+
+// if rangecheck is undefined, most parameter validation debugging code
+// will not be compiled
+#ifndef NORANGECHECKING
+#define RANGECHECK
+#endif
+
+// Past distributions
+#ifndef VER_ID
+#define VER_ID "DVL"
+#endif
+//#define VERSIONTEXT "ID V1.2"
+//#define VERSIONTEXT "RETAIL STORE BETA"		// 9/26/95
+//#define VERSIONTEXT "DVL BETA 10 05 95" // Used for GT for testing
+//#define VERSIONTEXT "DVL BETA 10 07 95" // Just an update for Romero
+//#define VERSIONTEXT "FINAL 1.0 (10 13 95)" // Just an update for Romero
+#ifdef RANGECHECK
+#define VERSIONTEXT "Version 1.1 +R "__DATE__" ("VER_ID")"
+#else
+#define VERSIONTEXT "Version 1.1 "__DATE__" ("VER_ID")"
+#endif
+
+// all exterior data is defined here
+#include "xddefs.h"
+
+// all important printed strings
+#include "textdefs.h"
+
+// header generated by multigen utility
+#include "info.h"
+
+extern byte *destview, *destscreen;	// PC direct to screen pointers
+
+//
+// most key data are simple ascii (uppercased)
+//
+#define	KEY_RIGHTARROW		0xae
+#define	KEY_LEFTARROW		0xac
+#define	KEY_UPARROW			0xad
+#define	KEY_DOWNARROW		0xaf
+#define	KEY_ESCAPE			27
+#define	KEY_ENTER			13
+#define	KEY_F1				(0x80+0x3b)
+#define	KEY_F2				(0x80+0x3c)
+#define	KEY_F3				(0x80+0x3d)
+#define	KEY_F4				(0x80+0x3e)
+#define	KEY_F5				(0x80+0x3f)
+#define	KEY_F6				(0x80+0x40)
+#define	KEY_F7				(0x80+0x41)
+#define	KEY_F8				(0x80+0x42)
+#define	KEY_F9				(0x80+0x43)
+#define	KEY_F10				(0x80+0x44)
+#define	KEY_F11				(0x80+0x57)
+#define	KEY_F12				(0x80+0x58)
+
+#define	KEY_BACKSPACE		127
+#define	KEY_PAUSE			0xff
+
+#define KEY_EQUALS			0x3d
+#define KEY_MINUS			0x2d
+
+#define	KEY_RSHIFT			(0x80+0x36)
+#define	KEY_RCTRL			(0x80+0x1d)
+#define	KEY_RALT			(0x80+0x38)
+
+#define	KEY_LALT			KEY_RALT
+
+#define KEY_FIVE			0x35
+#define KEY_SIX				0x36
+#define KEY_SEVEN			0x37
+#define KEY_EIGHT			0x38
+#define KEY_NINE			0x39
+#define KEY_ZERO			0x30
+#define KEY_BACKSLASH		0x5C
+
+
+#define MAXCHAR ((char)0x7f)
+#define MAXSHORT ((short)0x7fff)
+#define MAXINT	((int)0x7fffffff)	/* max pos 32-bit int */
+#define MAXLONG ((long)0x7fffffff)
+
+#define MINCHAR ((char)0x80)
+#define MINSHORT ((short)0x8000)
+#define MININT 	((int)0x80000000)	/* max negative 32-bit integer */
+#define MINLONG ((long)0x80000000)
+
+#define	FINEANGLES			8192
+#define	FINEMASK			(FINEANGLES-1)
+#define	ANGLETOFINESHIFT	19	// 0x100000000 to 0x2000
+
+/*
+===============================================================================
+
+						GLOBAL TYPES
+
+===============================================================================
+*/
+
+//#define NUMARTIFCTS	28
+#define MAXPLAYERS	8
+#define TICRATE		35			// number of tics / second
+#define TICSPERSEC	35
+
+#define MINIMUM_HEAP_SIZE	0x800000		//  8 meg
+#define MAXIMUM_HEAP_SIZE	0x2000000		// 32 meg
+
+#define	FRACBITS		16
+#define	FRACUNIT		(1<<FRACBITS)
+typedef int fixed_t;
+
+typedef unsigned int uint;
+
+//#define ANGLE_1		0x01000000
+#define ANGLE_45	0x20000000
+#define ANGLE_90	0x40000000
+#define ANGLE_180	0x80000000
+#define ANGLE_MAX	0xffffffff
+#define ANGLE_1		(ANGLE_45/45)
+#define ANGLE_60	(ANGLE_180/3)
+
+#define	ANG45	0x20000000
+#define	ANG90	0x40000000
+#define	ANG180	0x80000000
+#define	ANG270	0xc0000000
+
+typedef unsigned angle_t;
+
+typedef enum
+{
+	sk_baby,
+	sk_easy,
+	sk_medium,
+	sk_hard,
+	sk_nightmare
+} skill_t;
+
+typedef enum
+{
+	ev_keydown,
+	ev_keyup,
+	ev_mouse,
+	ev_joystick
+} evtype_t;
+
+typedef struct
+{
+	evtype_t	type;
+	int			data1;		// keys / mouse/joystick buttons
+	int			data2;		// mouse/joystick x move
+	int			data3;		// mouse/joystick y move
+} event_t;
+
+typedef struct
+{
+	char		forwardmove;		// *2048 for move
+	char		sidemove;			// *2048 for move
+	short		angleturn;			// <<16 for angle delta
+	short		consistancy;		// checks for net game
+	byte		chatchar;
+	byte		buttons;
+	byte		lookfly;			// look/fly up/down/centering
+	byte		arti;				// artitype_t to use
+} ticcmd_t;
+
+#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
+
+// The top 3 bits of the artifact field in the ticcmd_t struct are used
+// 		as additional flags 
+#define AFLAG_MASK			0x3F
+#define AFLAG_SUICIDE		0x40
+#define AFLAG_JUMP			0x80
+
+typedef enum
+{
+	GS_LEVEL,
+	GS_INTERMISSION,
+	GS_FINALE,
+	GS_DEMOSCREEN
+} gamestate_t;
+
+typedef enum
+{
+	ga_nothing,
+	ga_loadlevel,
+	ga_initnew,
+	ga_newgame,
+	ga_loadgame,
+	ga_savegame,
+	ga_playdemo,
+	ga_completed,
+	ga_leavemap,
+	ga_singlereborn,
+	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 node
+
+// 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			floorpic;			// contacted sec floorpic
+	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
+	fixed_t			floorclip;		// value to use for floor clipping
+	int				archiveNum;		// Identity during archive
+	short			tid;			// thing identifier
+	byte			special;		// special
+	byte			args[5];		// special arguments
+} 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;
+
+// Most damage defined using HITDICE
+#define HITDICE(a) ((1+(P_Random()&7))*a)
+
+//
+// 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_ALTSHADOW	0x20000		// alternate fuzzy draw
+#define	MF_SHADOW		0x40000		// use fuzzy 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_ICECORPSE	0x800000	// a frozen corpse (for blasting)
+
+#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_TRANSLATION	0x1c000000	// use a translation table (>>MF_TRANSHIFT)
+#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_BLASTED			0x00000008	// missile will pass through ghosts
+#define MF2_FLY				0x00000010	// fly mode is active
+#define MF2_FLOORCLIP		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_DROPPED			0x00004000	// dropped by a demon
+#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
+#define MF2_IMPACT			0x00200000 	// an MF_MISSILE mobj can activate
+								 		// SPAC_IMPACT
+#define MF2_PUSHWALL		0x00400000 	// mobj can push walls
+#define MF2_MCROSS			0x00800000	// can activate monster cross lines
+#define MF2_PCROSS			0x01000000	// can activate projectile cross lines
+#define MF2_CANTLEAVEFLOORPIC 0x02000000 // stay within a certain floor type
+#define MF2_NONSHOOTABLE	0x04000000	// mobj is totally non-shootable, 
+										// but still considered solid
+#define MF2_INVULNERABLE	0x08000000	// mobj is invulnerable
+#define MF2_DORMANT			0x10000000	// thing is dormant
+#define MF2_ICEDAMAGE		0x20000000  // does ice damage
+#define MF2_SEEKERMISSILE	0x40000000	// is a seeker (for reflection)
+#define MF2_REFLECTIVE		0x80000000	// reflects missiles
+
+//=============================================================================
+
+// ===== Player Class Types =====
+typedef enum
+{
+	PCLASS_FIGHTER,
+	PCLASS_CLERIC,
+	PCLASS_MAGE,
+	PCLASS_PIG,
+	NUMCLASSES
+} pclass_t;
+
+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;
+
+/* Old Heretic key type
+typedef enum
+{
+	key_yellow,
+	key_green,
+	key_blue,
+	NUMKEYS
+} keytype_t;
+*/
+
+typedef enum
+{
+	KEY_1,
+	KEY_2,
+	KEY_3,
+	KEY_4,
+	KEY_5,
+	KEY_6,
+	KEY_7,
+	KEY_8,
+	KEY_9,
+	KEY_A,
+	KEY_B,
+	NUMKEYS
+} keytype_t;
+
+typedef enum
+{
+	ARMOR_ARMOR,
+	ARMOR_SHIELD,
+	ARMOR_HELMET,
+	ARMOR_AMULET,
+	NUMARMOR
+} armortype_t;
+
+typedef enum
+{
+	WP_FIRST,
+	WP_SECOND,
+	WP_THIRD,
+	WP_FOURTH,
+	NUMWEAPONS,
+	WP_NOCHANGE
+} weapontype_t;
+	
+typedef enum
+{
+	MANA_1,
+	MANA_2,
+	NUMMANA,
+	MANA_BOTH,
+	MANA_NONE
+} manatype_t;
+
+#define MAX_MANA	200
+
+#define WPIECE1		1
+#define WPIECE2		2
+#define WPIECE3		4
+
+typedef struct
+{
+	manatype_t mana;
+	int upstate;
+	int downstate;
+	int readystate;
+	int atkstate;
+	int holdatkstate;
+	int flashstate;
+} weaponinfo_t;
+
+extern weaponinfo_t WeaponInfo[NUMWEAPONS][NUMCLASSES];
+
+typedef enum
+{
+	arti_none,
+	arti_invulnerability,
+	arti_health,
+	arti_superhealth,
+	arti_healingradius,
+	arti_summon,
+	arti_torch,
+	arti_egg,
+	arti_fly,
+	arti_blastradius,
+	arti_poisonbag,
+	arti_teleportother,
+	arti_speed,
+	arti_boostmana,
+	arti_boostarmor,
+	arti_teleport,
+	// Puzzle artifacts
+	arti_firstpuzzitem,
+	arti_puzzskull = arti_firstpuzzitem,
+	arti_puzzgembig,
+	arti_puzzgemred,
+	arti_puzzgemgreen1,
+	arti_puzzgemgreen2,
+	arti_puzzgemblue1,
+	arti_puzzgemblue2,
+	arti_puzzbook1,
+	arti_puzzbook2,
+	arti_puzzskull2,
+	arti_puzzfweapon,
+	arti_puzzcweapon,
+	arti_puzzmweapon,
+	arti_puzzgear1,
+	arti_puzzgear2,
+	arti_puzzgear3,
+	arti_puzzgear4,
+	NUMARTIFACTS
+} artitype_t;
+
+typedef enum
+{
+	pw_None,
+	pw_invulnerability,
+	pw_allmap,
+	pw_infrared,
+	pw_flight,
+	pw_shield,
+	pw_health2,
+	pw_speed,
+	pw_minotaur,
+	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 SPEEDTICS (45*35)
+#define MORPHTICS (40*35)
+#define MAULATORTICS (25*35)
+
+#define MESSAGETICS (4*35)
+#define BLINKTHRESHOLD (4*35)
+
+#define NUMINVENTORYSLOTS	NUMARTIFACTS
+
+typedef struct
+{
+	int type;
+	int count;
+} inventory_t;
+
+/*
+================
+=
+= player_t
+=
+================
+*/
+
+typedef struct player_s
+{
+	mobj_t *mo;
+	playerstate_t playerstate;
+	ticcmd_t cmd;
+
+	pclass_t	class;					// player class type
+
+	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[NUMARMOR];
+
+	inventory_t	inventory[NUMINVENTORYSLOTS];
+	artitype_t	readyArtifact;
+	int			artifactCount;
+	int 		inventorySlotNum;
+	int			powers[NUMPOWERS];
+	int			keys;
+	int			pieces;					// Fourth Weapon pieces
+	signed int			frags[MAXPLAYERS];		// kills of other players
+	weapontype_t	readyweapon;
+	weapontype_t	pendingweapon;		// wp_nochange if not changing
+	boolean		weaponowned[NUMWEAPONS];
+	int			mana[NUMMANA];
+	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[80];			// hint messages
+	int			messageTics;			// counter for showing messages
+	short		ultimateMessage;
+	short		yellowMessage;
+	int			damagecount, bonuscount;// for screen flashing
+	int			poisoncount;			// screen flash for poison damage
+	mobj_t		*poisoner;				// NULL for non-player mobjs
+	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)
+	int			morphTics;				// player is a pig if > 0
+	uint		jumpTics;				// delay the next jump for a moment
+	unsigned int worldTimer;			// total time the player's been playing
+} 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
+
+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
+{
+	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;
+
+#define	DOOMCOM_ID		0x12345678l
+
+extern	doomcom_t		*doomcom;
+extern	doomdata_t		*netbuffer;		// points inside doomcom
+
+#define	MAXNETNODES		16			// max computers in a game
+
+#define	CMD_SEND	1
+#define	CMD_GET		2
+#define CMD_FRAG	3
+
+#define	SBARHEIGHT	39			// status bar height at bottom of screen
+
+void NET_SendFrags(player_t *player);
+
+/*
+===============================================================================
+
+					GLOBAL VARIABLES
+
+===============================================================================
+*/
+
+#define TELEFOGHEIGHT (32*FRACUNIT)
+
+#define MAXEVENTS 64
+
+extern event_t events[MAXEVENTS];
+extern int eventhead;
+extern int eventtail;
+
+extern fixed_t finesine[5*FINEANGLES/4];
+extern fixed_t *finecosine;
+
+extern gameaction_t gameaction;
+
+extern boolean paused;
+
+extern boolean shareware; // true if other episodes not present
+
+extern boolean DevMaps; // true = map development mode
+extern char *DevMapsDir; // development maps directory
+
+extern boolean nomonsters; // checkparm of -nomonsters
+
+extern boolean respawnparm; // checkparm of -respawn
+
+extern boolean randomclass; // checkparm of -randclass
+
+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 cmdfrag; // true if a CMD_FRAG packet should be sent out every
+						// kill
+
+extern boolean playeringame[MAXPLAYERS];
+extern pclass_t PlayerClass[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 boolean demoplayback;
+extern int maxzone;				// Maximum chunk allocated for zone heap
+
+extern int Sky1Texture;
+extern int Sky2Texture;
+
+extern	gamestate_t	gamestate;
+extern	skill_t		gameskill;
+//extern	boolean		respawnmonsters;
+extern	int			gameepisode;
+extern	int			gamemap;
+extern 	int 			prevmap;
+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 rndindex;
+extern int gametic, maketic;
+extern	int        	nettics[MAXNETNODES];
+
+#define MAXDEATHMATCHSTARTS 16
+extern mapthing_t *deathmatch_p;
+extern mapthing_t deathmatchstarts[MAXDEATHMATCHSTARTS];
+
+// Position indicator for cooperative net-play reborn
+extern int RebornPosition;
+
+#define MAX_PLAYER_STARTS 8
+extern mapthing_t playerstarts[MAX_PLAYER_STARTS][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 byte *screen; // off screen work buffer, from V_video.c
+
+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
+
+===============================================================================
+*/
+
+
+fixed_t	FixedMul (fixed_t a, fixed_t b);
+fixed_t	FixedDiv (fixed_t a, fixed_t b);
+fixed_t	FixedDiv2 (fixed_t a, fixed_t b);
+
+#ifdef __WATCOMC__
+#pragma aux FixedMul =	\
+	"imul ebx",			\
+	"shrd eax,edx,16"	\
+	parm	[eax] [ebx] \
+	value	[eax]		\
+	modify exact [eax edx]
+
+#pragma aux FixedDiv2 =	\
+	"cdq",				\
+	"shld edx,eax,16",	\
+	"sal eax,16",		\
+	"idiv ebx"			\
+	parm	[eax] [ebx] \
+	value	[eax]		\
+	modify exact [eax edx]
+#endif
+
+#ifdef __BIG_ENDIAN__
+short ShortSwap(short);
+long LongSwap(long);
+#define SHORT(x)	ShortSwap(x)
+#define LONG(x)		LongSwap(x)
+#else
+#define SHORT(x)	(x)
+#define LONG(x)		(x)
+#endif
+
+
+//-----------
+//MEMORY ZONE
+//-----------
+// tags < 100 are not overwritten until freed
+#define	PU_STATIC		1			// static entire execution time
+#define	PU_SOUND		2			// static while playing
+#define	PU_MUSIC		3			// static while playing
+#define	PU_DAVE			4			// anything else Dave wants static
+#define	PU_LEVEL		50			// static until level exited
+#define	PU_LEVSPEC		51			// a special thinker in a level
+// tags >= 100 are purgable whenever needed
+#define	PU_PURGELEVEL	100
+#define	PU_CACHE		101
+
+
+void	Z_Init (void);
+void 	*Z_Malloc (int size, int tag, void *ptr);
+void 	Z_Free (void *ptr);
+void 	Z_FreeTags (int lowtag, int hightag);
+//void 	Z_DumpHeap (int lowtag, int hightag);
+//void	Z_FileDumpHeap (FILE *f);
+void	Z_CheckHeap (void);
+void	Z_ChangeTag2 (void *ptr, int tag);
+//int 	Z_FreeMemory (void);
+
+typedef struct memblock_s
+{
+	int                     size;           // including the header and possibly tiny fragments
+	void            **user;         // NULL if a free block
+	int                     tag;            // purgelevel
+	int                     id;                     // should be ZONEID
+	struct memblock_s       *next, *prev;
+} memblock_t;
+
+#define Z_ChangeTag(p,t) \
+{ \
+if (( (memblock_t *)( (byte *)(p) - sizeof(memblock_t)))->id!=0x1d4a11) \
+	I_Error("Z_CT at "__FILE__":%i",__LINE__); \
+Z_ChangeTag2(p,t); \
+};
+
+//-------
+//WADFILE
+//-------
+typedef struct
+{
+	char		name[8];
+	int			handle,position,size;
+} lumpinfo_t;
+
+extern lumpinfo_t *lumpinfo;
+extern int numlumps;
+
+void W_InitMultipleFiles(char **filenames);
+void W_OpenAuxiliary(char *filename);
+void W_CloseAuxiliaryFile(void);
+void W_CloseAuxiliary(void);
+void W_UsePrimary(void);
+void W_UseAuxiliary(void);
+int W_CheckNumForName(char *name);
+int W_GetNumForName(char *name);
+int W_LumpLength(int lump);
+void W_ReadLump(int lump, void *dest);
+void *W_CacheLumpNum(int lump, int tag);
+void *W_CacheLumpName(char *name, int tag);
+
+//----------
+//BASE LEVEL
+//----------
+void H2_Main(void);
+// 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 H2_GameLoop(void);
+// not a globally visible function, just included for source reference
+// called by H2_Main, never exits
+// manages timing and IO
+// calls all ?_Responder, ?_Ticker, and ?_Drawer functions
+// calls I_GetTime, I_StartFrame, and I_StartTic
+
+void H2_PostEvent(event_t *ev);
+// called by IO functions when input is detected
+
+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
+//---------
+#if 1
+#define	SCREENWIDTH		320
+#define	SCREENHEIGHT	200
+#else
+#define	SCREENWIDTH		560
+#define	SCREENHEIGHT	375
+#endif
+
+byte *I_ZoneBase (int *size);
+// called by startup code to get the ammount of memory to malloc
+// for the zone management
+
+int I_GetTime (void);
+// called by H2_GameLoop
+// returns current time in tics
+
+void I_StartFrame (void);
+// called by H2_GameLoop
+// called before processing any tics in a frame (just after displaying a frame)
+// time consuming syncronous operations are performed here (joystick reading)
+// can call H2_PostEvent
+
+void I_StartTic (void);
+// called by H2_GameLoop
+// called before processing each tic in a frame
+// quick syncronous operations are performed here
+// can call H2_PostEvent
+
+// asyncronous interrupt functions should maintain private ques that are
+// read by the syncronous functions to be converted into events
+
+void I_Init (void);
+// called by H2_Main
+// determines the hardware configuration and sets up the video mode
+
+void I_InitGraphics (void);
+
+void I_InitNetwork (void);
+void I_NetCmd (void);
+
+void I_CheckExternDriver(void);
+
+void I_Error (char *error, ...);
+// called by anything that can generate a terminal error
+// bad exit with diagnostic message
+
+void I_Quit (void);
+// called by M_Responder when quit is selected
+// clean exit, displays sell blurb
+
+void I_SetPalette (byte *palette);
+// takes full 8 bit values
+
+void I_Update(void);
+// Copy buffer to video
+
+void I_WipeUpdate(wipe_t wipe);
+// Copy buffer to video with wipe effect
+
+void I_WaitVBL(int count);
+// wait for vertical retrace or pause a bit
+
+void I_BeginRead (void);
+void I_EndRead (void);
+
+byte	*I_AllocLow (int length);
+// allocates from low memory under dos, just mallocs under unix
+
+void I_Tactile (int on, int off, int total);
+
+#ifdef __WATCOMC__
+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
+#define EBT_JUMP			0x1000
+
+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_DeferredNewGame(skill_t skill);
+
+void G_DeferedPlayDemo (char *demo);
+
+void G_LoadGame(int slot);
+// 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
+
+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_TeleportNewMap(int map, int position);
+
+void G_Completed(int map, int position);
+//void G_ExitLevel (void);
+//void G_SecretExitLevel (void);
+
+void G_StartNewGame(skill_t skill);
+void G_StartNewInit(void);
+
+void G_WorldDone (void);
+
+void G_Ticker (void);
+boolean G_Responder (event_t *ev);
+
+void G_ScreenShot (void);
+
+//-------
+//SV_SAVE
+//-------
+
+#define HXS_VERSION_TEXT "HXS Ver 2.37"
+#define HXS_VERSION_TEXT_LENGTH 16
+#define HXS_DESCRIPTION_LENGTH 24
+
+void SV_SaveGame(int slot, char *description);
+void SV_SaveMap(boolean savePlayers);
+void SV_LoadGame(int slot);
+void SV_MapTeleport(int map, int position);
+void SV_LoadMap(void);
+void SV_InitBaseSlot(void);
+void SV_UpdateRebornSlot(void);
+void SV_ClearRebornSlot(void);
+boolean SV_RebornSlotAvailable(void);
+int SV_GetRebornSlot(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
+
+int P_GetMapCluster(int map);
+int P_TranslateMap(int map);
+int P_GetMapCDTrack(int map);
+int P_GetMapWarpTrans(int map);
+int P_GetMapNextMap(int map);
+int P_GetMapSky1Texture(int map);
+int P_GetMapSky2Texture(int map);
+char *P_GetMapName(int map);
+fixed_t P_GetMapSky1ScrollDelta(int map);
+fixed_t P_GetMapSky2ScrollDelta(int map);
+boolean P_GetMapDoubleSky(int map);
+boolean P_GetMapLightning(int map);
+boolean P_GetMapFadeTable(int map);
+char *P_GetMapSongLump(int map);
+void P_PutMapSongLump(int map, char *lumpName);
+int P_GetCDStartTrack(void);
+int P_GetCDEnd1Track(void);
+int P_GetCDEnd2Track(void);
+int P_GetCDEnd3Track(void);
+int P_GetCDIntermissionTrack(void);
+int P_GetCDTitleTrack(void);
+
+//-------
+//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
+//----
+extern	int		myargc;
+extern	char	**myargv;
+extern	int		localQuakeHappening[MAXPLAYERS];
+
+int	M_CheckParm(char *check);
+// returns the position of the given parameter in the arg list (0 if not found)
+boolean M_ParmExists(char *check);
+
+void M_ExtractFileBase(char *path, char *dest);
+
+void M_ForceUppercase(char *text);
+// Changes a string to uppercase
+
+int M_Random (void);
+// returns a number from 0 to 255
+
+extern unsigned char rndtable[256];
+extern int prndindex;
+#define P_Random() rndtable[(++prndindex)&0xff]
+// as M_Random, but used only by the play simulation
+
+void M_ClearRandom (void);
+// fix randoms for demos
+
+void M_FindResponseFile(void);
+
+void M_ClearBox (fixed_t *box);
+void M_AddToBox (fixed_t *box, fixed_t x, fixed_t y);
+// bounding box functions
+
+boolean M_WriteFile(char const *name, void *source, int length);
+int M_ReadFile(char const *name, byte **buffer);
+int M_ReadFileCLib(char const *name, byte **buffer);
+
+void M_ScreenShot (void);
+
+void M_LoadDefaults(char *fileName);
+
+void M_SaveDefaults (void);
+
+int M_DrawText (int x, int y, boolean direct, char *string);
+
+//------------------------------
+// SC_man.c
+//------------------------------
+
+void SC_Open(char *name);
+void SC_OpenLump(char *name);
+void SC_OpenFile(char *name);
+void SC_OpenFileCLib(char *name);
+void SC_Close(void);
+boolean SC_GetString(void);
+void SC_MustGetString(void);
+void SC_MustGetStringName(char *name);
+boolean SC_GetNumber(void);
+void SC_MustGetNumber(void);
+void SC_UnGet(void);
+//boolean SC_Check(void);
+boolean SC_Compare(char *text);
+int SC_MatchString(char **strings);
+int SC_MustMatchString(char **strings);
+void SC_ScriptError(char *message);
+
+extern char *sc_String;
+extern int sc_Number;
+extern int sc_Line;
+extern boolean sc_End;
+extern boolean sc_Crossed;
+extern boolean sc_FileScripts;
+extern char *sc_ScriptsDir;
+
+//------------------------------
+// SN_sonix.c
+//------------------------------
+
+enum
+{
+	SEQ_PLATFORM,
+	SEQ_PLATFORM_HEAVY,		// same script as a normal platform
+	SEQ_PLATFORM_METAL,
+	SEQ_PLATFORM_CREAK,		// same script as a normal platform
+	SEQ_PLATFORM_SILENCE,
+	SEQ_PLATFORM_LAVA,
+	SEQ_PLATFORM_WATER,
+	SEQ_PLATFORM_ICE,
+	SEQ_PLATFORM_EARTH,
+	SEQ_PLATFORM_METAL2,
+	SEQ_DOOR_STONE,
+	SEQ_DOOR_HEAVY,
+	SEQ_DOOR_METAL,
+	SEQ_DOOR_CREAK,
+	SEQ_DOOR_SILENCE,
+	SEQ_DOOR_LAVA,
+	SEQ_DOOR_WATER,
+	SEQ_DOOR_ICE,
+	SEQ_DOOR_EARTH,
+	SEQ_DOOR_METAL2,
+	SEQ_ESOUND_WIND,
+	SEQ_NUMSEQ
+};
+
+typedef enum
+{
+	SEQTYPE_STONE,
+	SEQTYPE_HEAVY,
+	SEQTYPE_METAL,
+	SEQTYPE_CREAK,
+	SEQTYPE_SILENCE,
+	SEQTYPE_LAVA,
+	SEQTYPE_WATER,
+	SEQTYPE_ICE,
+	SEQTYPE_EARTH,
+	SEQTYPE_METAL2,
+	SEQTYPE_NUMSEQ
+} seqtype_t;
+
+void SN_InitSequenceScript(void);
+void SN_StartSequence(mobj_t *mobj, int sequence);
+void SN_StartSequenceName(mobj_t *mobj, char *name);
+void SN_StopSequence(mobj_t *mobj);
+void SN_UpdateActiveSequences(void);
+void SN_StopAllSequences(void);
+int SN_GetSequenceOffset(int sequence, int *sequencePtr);
+void SN_ChangeNodeData(int nodeNum, int seqOffset, int delayTics, int volume,
+	int currentSoundID);
+
+
+typedef struct seqnode_s seqnode_t;
+struct seqnode_s
+{
+	int *sequencePtr;
+	int	sequence;
+	mobj_t *mobj;
+	int currentSoundID;
+	int delayTics;
+	int volume;
+	int stopSound;
+	seqnode_t *prev;
+	seqnode_t *next;
+};
+
+extern int ActiveSequences;
+extern seqnode_t *SequenceListHead;
+
+//----------------------
+// Interlude (IN_lude.c)
+//----------------------
+
+#define MAX_INTRMSN_MESSAGE_SIZE 1024
+
+extern boolean intermission;
+extern char ClusterMessage[MAX_INTRMSN_MESSAGE_SIZE];
+
+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;
+
+//--------------------
+// Finale (F_finale.c)
+//--------------------
+
+void F_Drawer(void);
+void F_Ticker(void);
+void F_StartFinale(void);
+
+//----------------------
+// STATUS BAR (SB_bar.c)
+//----------------------
+
+extern int inv_ptr;
+extern int curpos;
+extern int SB_state;
+void SB_Init(void);
+void SB_SetClassData(void);
+boolean SB_Responder(event_t *event);
+void SB_Ticker(void);
+void SB_Drawer(void);
+void Draw_TeleportIcon(void);
+void Draw_SaveIcon(void);
+void Draw_LoadIcon(void);
+
+//-----------------
+// MENU (MN_menu.c)
+//-----------------
+
+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);
+void MN_DrTextAYellow(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);
+
+//------
+// VIDEO
+//------
+
+extern int dirtybox[4];
+extern byte gammatable[5][256];
+extern int usegamma;
+
+void V_Init(void); // Allocates buffer screens, call before R_Init
+void V_DrawPatch(int x, int y, patch_t *patch);
+void V_DrawFuzzPatch(int x, int y, patch_t *patch);
+void V_DrawAltFuzzPatch(int x, int y, patch_t *patch);
+void V_DrawShadowedPatch(int x, int y, patch_t *patch);
+void V_DrawRawScreen(byte *raw);
+
+#include "sounds.h"
+
+#endif // __H2DEF__
--- /dev/null
+++ b/src/hexen/i_cdmus.c
@@ -1,0 +1,1023 @@
+
+//**************************************************************************
+//**
+//** i_cdmus.c
+//**
+//**************************************************************************
+
+// HEADER FILES ------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dos.h>
+#include <stddef.h>
+#include <string.h>
+#include "h2def.h"
+#include "i_sound.h"
+
+// MACROS ------------------------------------------------------------------
+
+#define MAX_AUDIO_TRACKS 25
+#define MULTIPLEX_INT 0x2f
+#define CDROM_GETDRIVECOUNT 0x1500
+#define CDROM_SENDDEVICEREQ 0x1510
+#define CDROM_GETVERSION 0x150c
+#define HSG_MODE 0
+#define RED_MODE 1
+#define DRC_IOCTLINPUT 0x03
+#define DRC_IOCTLOUTPUT 0x0c
+#define DRC_PLAYAUDIO 0x84
+#define DRC_STOPAUDIO 0x85
+#define DRC_RESUMEAUDIO 0x88
+
+#define DPMI_INT 0x31
+#define DPMI_ALLOCREALMEM 0x0100
+#define DPMI_FREEREALMEM 0x0101
+#define DPMI_SIMREALINT 0x0300
+
+// IOCTL input commands
+
+#define ADRDEVHEAD       0 // Return Address of Device Header
+#define HEADLOCATION     1 // Location of Head
+#define RESERVED         2 // Reserved
+#define ERRSTATISTICS    3 // Error Statistics
+#define AUDIOCHANINFO    4 // Audio Channel Info
+#define READDRVBYTES     5 // Read Drive Bytes
+#define DEVICESTATUS     6 // Device Status
+#define GETSECTORSIZE    7 // Return Sector Size
+#define GETVOLSIZE       8 // Return Volume Size
+#define MEDIACHANGED     9 // Media Changed
+#define AUDIODISKINFO   10 // Audio Disk Info
+#define AUDIOTRACKINFO  11 // Audio Track Info
+#define AUDIOQCHANINFO  12 // Audio Q-Channel Info
+#define AUDIOSUBINFO    13 // Audio Sub-Channel Info
+#define UPCCODE         14 // UPC Code
+#define AUDIOSTATUSINFO 15 // Audio Status Info
+
+// IOCTL output commands
+
+#define EJECTDISK        0 // Eject Disk
+#define DOORLOCK         1 // Lock/Unlock Door
+#define RESETDRIVE       2 // Reset Drive
+#define AUDIOCHANCONTROL 3 // Audio Channel Control
+#define WRITEDEVCONTROL  4 // Write Device Control String
+#define CLOSETRAY        5 // Close Tray
+
+// TYPES -------------------------------------------------------------------
+
+typedef signed char		S_BYTE;
+typedef unsigned char	U_BYTE;
+typedef signed short	S_WORD;
+typedef unsigned short	U_WORD;
+typedef signed int		S_LONG;
+typedef unsigned int	U_LONG;
+
+typedef struct {
+	U_LONG size;
+	void **address;
+	U_WORD *segment;
+	U_WORD *selector;
+} DOSChunk_t;
+
+typedef struct {
+	U_LONG edi;
+	U_LONG esi;
+	U_LONG ebp;
+	U_LONG reserved;
+	U_LONG ebx;
+	U_LONG edx;
+	U_LONG ecx;
+	U_LONG eax;
+	U_WORD flags;
+	U_WORD es;
+	U_WORD ds;
+	U_WORD fs;
+	U_WORD gs;
+	U_WORD ip;
+	U_WORD cs;
+	U_WORD sp;
+	U_WORD ss;
+} RegBlock_t;
+
+typedef struct {
+	short lengthMin;
+	short lengthSec;
+	int redStart;
+	int sectorStart;
+	int sectorLength;
+} AudioTrack_t;
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+static U_WORD InputIOCTL(S_WORD request, U_LONG pctrlblk);
+static U_WORD OutputIOCTL(S_WORD request, U_LONG pctrlblk);
+static U_LONG RedToSectors(U_LONG red);
+static int AllocIOCTLBuffers(void);
+static void DPMI_SimRealInt(U_LONG intr, RegBlock_t *rBlock);
+static void *DPMI_AllocRealMem(U_LONG size, U_WORD *segment,
+	U_WORD *selector);
+static void DPMI_FreeRealMem(U_WORD selector);
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+int cd_Error;
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+static int cd_DriveCount;
+static int cd_FirstDrive;
+static int cd_CurDrive;
+static U_WORD cd_Version;
+static int cd_FirstTrack;
+static int cd_LastTrack;
+static int cd_TrackCount;
+static U_WORD cd_LeadOutMin;
+static U_WORD cd_LeadOutSec;
+static U_LONG cd_LeadOutRed;
+static U_LONG cd_LeadOutSector;
+static U_LONG cd_IOCTLBufferTotal;
+static AudioTrack_t cd_AudioTracks[MAX_AUDIO_TRACKS];
+
+static int OkInit = 0;
+
+static RegBlock_t RegBlock;
+
+static struct PlayReq_s {       // CD-ROM Play Audio Device Request Struct
+	U_BYTE headerSize;
+	U_BYTE subUnitCode;
+	U_BYTE command;               // = DRC_PLAYAUDIO
+	U_WORD status;
+	U_BYTE reserved[8];
+	U_BYTE addressMode;
+	U_LONG startSector;
+	U_LONG numberToRead;
+} *cd_PlayReq;
+static U_WORD cd_PlayReqSeg;
+static U_WORD cd_PlayReqSel;
+
+static struct StopReq_s {       // CD-ROM Stop Audio Device Request Struct
+	U_BYTE headerSize;
+	U_BYTE subUnitCode;
+	U_BYTE command;               // = DRC_STOPAUDIO
+	U_WORD status;
+	U_BYTE reserved[8];
+} *cd_StopReq;
+static U_WORD cd_StopReqSeg;
+static U_WORD cd_StopReqSel;
+
+static struct ResumeReq_s {     // CD-ROM Resume Audio Device Request Struct
+	U_BYTE headerSize;
+	U_BYTE subUnitCode;
+	U_BYTE command;               // = DRC_RESUMEAUDIO
+	U_WORD status;
+	U_BYTE reserved[8];
+} *cd_ResumeReq;
+static U_WORD cd_ResumeReqSeg;
+static U_WORD cd_ResumeReqSel;
+
+// IOCTL Input command data buffer structures
+
+static struct IOCTLIn_s {       // IOCTL Input Struct
+	U_BYTE headerSize;
+	U_BYTE subUnitCode;
+	U_BYTE command;               // = DRC_IOCTLINPUT
+	U_WORD status;
+	U_BYTE reserved[8];
+	U_BYTE mediaDescriptor;
+	U_LONG ctrlBlkAddr;
+	U_WORD tranSize;
+	U_WORD startSector;
+	U_LONG volPtr;
+} *cd_IOCTLIn;
+static U_WORD cd_IOCTLInSeg;
+static U_WORD cd_IOCTLInSel;
+
+static struct RAddrDevHead_s {
+	U_BYTE code;                  // ADRDEVHEAD
+	U_LONG devHdrAddr;            // Address of device header
+} *cd_RAddrDevHead;
+static U_WORD cd_RAddrDevHeadSeg;
+static U_WORD cd_RAddrDevHeadSel;
+
+static struct LocHead_s {
+	U_BYTE code;                  // HEADLOCATION
+	U_BYTE addrMode;              // Addressing mode
+	U_LONG headLocation;          // Location of drive head
+} *cd_LocHead;
+static U_WORD cd_LocHeadSeg;
+static U_WORD cd_LocHeadSel;
+
+static struct ErrStat_s {
+	U_BYTE code;                  // ERRSTATISTICS
+	U_BYTE errVal;                // Error statistics
+} *cd_ErrStat;
+static U_WORD cd_ErrStatSeg;
+static U_WORD cd_ErrStatSel;
+
+static struct AudChanInfo_s {
+	U_BYTE code;               // AUDIOCHANINFO
+	U_BYTE inChanOut0;         // Input chan(0,1,2,or 3) for output chan 0
+	U_BYTE volumeOut0;         // Volume control (0-0xff) for output chan 0
+	U_BYTE inChanOut1;         // Input chan(0,1,2,or 3) for output chan 1
+	U_BYTE volumeOut1;         // Volume control (0-0xff) for output chan 1
+	U_BYTE inChanOut2;         // Input chan(0,1,2,or 3) for output chan 2
+	U_BYTE volumeOut2;         // Volume control (0-0xff) for output chan 2
+	U_BYTE inChanOut3;         // Input chan(0,1,2,or 3) for output chan 3
+	U_BYTE volumeOut3;         // Volume control (0-0xff) for output chan 3
+} *cd_AudChanInfo;
+static U_WORD cd_AudChanInfoSeg;
+static U_WORD cd_AudChanInfoSel;
+
+static struct RDrvBytes_s {
+	U_BYTE code;                  // READDRVBYTES
+	U_BYTE numBytes;              // Number of bytes to read
+	U_BYTE rBuffer[128];          // Read buffer
+} *cd_RDrvBytes;
+static U_WORD cd_RDrvBytesSeg;
+static U_WORD cd_RDrvBytesSel;
+
+static struct DevStat_s {
+	U_BYTE code;                  // DEVICESTATUS
+	U_LONG devParams;             // Device parameters
+} *cd_DevStat;
+static U_WORD cd_DevStatSeg;
+static U_WORD cd_DevStatSel;
+
+static struct SectSize_s {
+	U_BYTE code;                  // GETSECTORSIZE
+	U_BYTE readMode;              // Read mode
+	U_WORD sectorSize;           // Sector size
+} *cd_SectSize;
+static U_WORD cd_SectSizeSeg;
+static U_WORD cd_SectSizeSel;
+
+static struct VolSize_s {
+	U_BYTE code;                  // GETVOLSIZE
+	U_LONG volumeSize;            // Volume size
+} *cd_VolSize;
+static U_WORD cd_VolSizeSeg;
+static U_WORD cd_VolSizeSel;
+
+static struct MedChng_s {
+	U_BYTE code;                  // MEDIACHANGED
+	U_BYTE changed;               // Media byte
+} *cd_MedChng;
+static U_WORD cd_MedChngSeg;
+static U_WORD cd_MedChngSel;
+
+static struct DiskInfo_s {
+	U_BYTE code;                  // AUDIODISKINFO
+	U_BYTE lowTrack;              // Lowest track number
+	U_BYTE highTrack;             // Highest track number
+	U_LONG startLeadOut;          // Starting point of the lead-out track
+} *cd_DiskInfo;
+static U_WORD cd_DiskInfoSeg;
+static U_WORD cd_DiskInfoSel;
+
+static struct TrackInfo_s {
+	U_BYTE code;                  // AUDIOTRACKINFO
+	U_BYTE track;                 // Track number
+	U_LONG start;                 // Starting point of the track
+	U_BYTE ctrlInfo;              // Track control information
+} *cd_TrackInfo;
+static U_WORD cd_TrackInfoSeg;
+static U_WORD cd_TrackInfoSel;
+
+static struct QInfo_s {
+	U_BYTE code;                  // AUDIOQCHANINFO
+	U_BYTE control;               // CONTROL and ADR byte
+	U_BYTE tno;                   // Track number (TNO)
+	U_BYTE index;                 // (POINT) or Index(X)
+	U_BYTE min;                   // (MIN) Running time within a track
+	U_BYTE sec;                   // (SEC)    "      "    "    "   "
+	U_BYTE frame;                 // (FRAME)  "      "    "    "   "
+	U_BYTE zero;                  // (ZERO)   "      "    "    "   "
+	U_BYTE aMin;                  // (AMIN) or (PMIN) Running time on disk
+	U_BYTE aSec;                  // (ASEC) or (PSEC)    "      "   "   "
+	U_BYTE aFrame;                // (AFRAME) or (PFRAME)"      "   "   "
+} *cd_QInfo;
+static U_WORD cd_QInfoSeg;
+static U_WORD cd_QInfoSel;
+
+static struct SubChanInfo_s {
+	U_BYTE code;                  // AUDIOSUBINFO
+	U_LONG startSectAddr;         // Starting sector address
+	U_LONG transAddr;             // Transfer address
+	U_LONG numSects;              // Number of sectors to read
+} *cd_SubChanInfo;
+static U_WORD cd_SubChanInfoSeg;
+static U_WORD cd_SubChanInfoSel;
+
+static struct UPCCode_s {
+	U_BYTE code;                  // UPCCODE
+	U_BYTE control;               // CONTROL and ADR byte
+	U_BYTE upc[7];                // UPC/EAN code
+	U_BYTE zero;                  // Zero
+	U_BYTE aFrame;                // Aframe
+} *cd_UPCCode;
+static U_WORD cd_UPCCodeSeg;
+static U_WORD cd_UPCCodeSel;
+
+static struct AudStat_s {
+	U_BYTE code;                  // AUDIOSTATUSINFO
+	U_WORD status;                // Audio status bits
+	U_LONG startPlay;             // Starting location of last Play/Resume
+	U_LONG endPlay;               // Ending location for last Play/Resume
+} *cd_AudStat;
+static U_WORD cd_AudStatSeg;
+static U_WORD cd_AudStatSel;
+
+// IOCTL Output command data buffer structures
+
+static struct IOCTLOut_s {      // IOCTL Output struct
+	U_BYTE headerSize;
+	U_BYTE subUnitCode;
+	U_BYTE command;               // = DRC_IOCTLOUTPUT
+	U_WORD status;
+	U_BYTE reserved[8];
+	U_BYTE mediaDescriptor;
+	U_LONG ctrlBlkAddr;
+	U_WORD tranSize;
+	U_WORD startSector;
+	U_LONG volPtr;
+} *cd_IOCTLOut;
+static U_WORD cd_IOCTLOutSeg;
+static U_WORD cd_IOCTLOutSel;
+
+static struct Eject_s {
+	U_BYTE code;                  // EJECTDISK
+} *cd_Eject;
+static U_WORD cd_EjectSeg;
+static U_WORD cd_EjectSel;
+
+static struct LockDoor_s {
+	U_BYTE code;                  // DOORLOCK
+	U_BYTE lock;                  // Lock function : 0 = unlock, 1 = lock
+} *cd_LockDoor;
+static U_WORD cd_LockDoorSeg;
+static U_WORD cd_LockDoorSel;
+
+static struct ResetDrv_s {
+	U_BYTE code;                  // RESETDRIVE
+} *cd_ResetDrv;
+static U_WORD cd_ResetDrvSeg;
+static U_WORD cd_ResetDrvSel;
+
+static struct AudInfo_s {
+	U_BYTE code;               // AUDIOCHANCONTROL
+	U_BYTE inChanOut0;         // Input chan(0,1,2,or 3) for output chan 0
+	U_BYTE volumeOut0;         // Volume control (0-0xff) for output chan 0
+	U_BYTE inChanOut1;         // Input chan(0,1,2,or 3) for output chan 1
+	U_BYTE volumeOut1;         // Volume control (0-0xff) for output chan 1
+	U_BYTE inChanOut2;         // Input chan(0,1,2,or 3) for output chan 2
+	U_BYTE volumeOut2;         // Volume control (0-0xff) for output chan 2
+	U_BYTE inChanOut3;         // Input chan(0,1,2,or 3) for output chan 3
+	U_BYTE volumeOut3;         // Volume control (0-0xff) for output chan 3
+} *cd_AudInfo;
+static U_WORD cd_AudInfoSeg;
+static U_WORD cd_AudInfoSel;
+
+static struct WDrvBytes_s {
+	U_BYTE code;                  // WRITEDEVCONTROL
+	U_BYTE buf[5];                // Write buffer - size ??
+} *cd_WDrvBytes;
+static U_WORD cd_WDrvBytesSeg;
+static U_WORD cd_WDrvBytesSel;
+
+static struct CloseTray_s {
+	U_BYTE code;                  // CLOSETRAY
+} *cd_CloseTray;
+static U_WORD cd_CloseTraySeg;
+static U_WORD cd_CloseTraySel;
+
+static U_WORD InCtrlBlkSize[16] = {
+	0x05, 0x06, 0x00, 0x00,
+	0x09, 0x82, 0x05, 0x04,
+	0x05, 0x02, 0x07, 0x07,
+	0x0b, 0x0d, 0x0b, 0x0b
+};
+
+static U_WORD OutCtrlBlkSize[6] = {
+	0x01, 0x02,
+	0x01, 0x09,
+	0x06, 0x01
+};
+
+// Structures for allocating conventional memory
+
+static DOSChunk_t DOSChunks[] = {
+	{
+		sizeof(struct PlayReq_s),
+		&cd_PlayReq, &cd_PlayReqSeg, &cd_PlayReqSel
+	},
+	{
+		sizeof(struct StopReq_s),
+		&cd_StopReq, &cd_StopReqSeg, &cd_StopReqSel
+	},
+	{
+		sizeof(struct ResumeReq_s),
+		&cd_ResumeReq, &cd_ResumeReqSeg, &cd_ResumeReqSel
+	},
+	{
+		sizeof(struct IOCTLOut_s),
+		&cd_IOCTLOut, &cd_IOCTLOutSeg, &cd_IOCTLOutSel
+	},
+	{
+		sizeof(struct Eject_s),
+		&cd_Eject, &cd_EjectSeg, &cd_EjectSel
+	},
+	{
+		sizeof(struct LockDoor_s),
+		&cd_LockDoor, &cd_LockDoorSeg, &cd_LockDoorSel
+	},
+	{
+		sizeof(struct ResetDrv_s),
+		&cd_ResetDrv, &cd_ResetDrvSeg, &cd_ResetDrvSel
+	},
+	{
+		sizeof(struct AudInfo_s),
+		&cd_AudInfo, &cd_AudInfoSeg, &cd_AudInfoSel
+	},
+	{
+		sizeof(struct WDrvBytes_s),
+		&cd_WDrvBytes, &cd_WDrvBytesSeg, &cd_WDrvBytesSel
+	},
+	{
+		sizeof(struct CloseTray_s),
+		&cd_CloseTray, &cd_CloseTraySeg, &cd_CloseTraySel
+	},
+	{
+		sizeof(struct IOCTLIn_s),
+		&cd_IOCTLIn, &cd_IOCTLInSeg, &cd_IOCTLInSel
+	},
+	{
+		sizeof(struct RAddrDevHead_s),
+		&cd_RAddrDevHead, &cd_RAddrDevHeadSeg, &cd_RAddrDevHeadSel
+	},
+	{
+		sizeof(struct LocHead_s),
+		&cd_LocHead, &cd_LocHeadSeg, &cd_LocHeadSel
+	},
+	{
+		sizeof(struct ErrStat_s),
+		&cd_ErrStat, &cd_ErrStatSeg, &cd_ErrStatSel
+	},
+	{
+		sizeof(struct AudChanInfo_s),
+		&cd_AudChanInfo, &cd_AudChanInfoSeg, &cd_AudChanInfoSel
+	},
+	{
+		sizeof(struct RDrvBytes_s),
+		&cd_RDrvBytes, &cd_RDrvBytesSeg, &cd_RDrvBytesSel
+	},
+	{
+		sizeof(struct DevStat_s),
+		&cd_DevStat, &cd_DevStatSeg, &cd_DevStatSel
+	},
+	{
+		sizeof(struct SectSize_s),
+		&cd_SectSize, &cd_SectSizeSeg, &cd_SectSizeSel
+	},
+	{
+		sizeof(struct VolSize_s),
+		&cd_VolSize, &cd_VolSizeSeg, &cd_VolSizeSel
+	},
+	{
+		sizeof(struct MedChng_s),
+		&cd_MedChng, &cd_MedChngSeg, &cd_MedChngSel
+	},
+	{
+		sizeof(struct DiskInfo_s),
+		&cd_DiskInfo, &cd_DiskInfoSeg, &cd_DiskInfoSel
+	},
+	{
+		sizeof(struct TrackInfo_s),
+		&cd_TrackInfo, &cd_TrackInfoSeg, &cd_TrackInfoSel
+	},
+	{
+		sizeof(struct QInfo_s),
+		&cd_QInfo, &cd_QInfoSeg, &cd_QInfoSel
+	},
+	{
+		sizeof(struct SubChanInfo_s),
+		&cd_SubChanInfo, &cd_SubChanInfoSeg, &cd_SubChanInfoSel
+	},
+	{
+		sizeof(struct UPCCode_s),
+		&cd_UPCCode, &cd_UPCCodeSeg, &cd_UPCCodeSel
+	},
+	{
+		sizeof(struct AudStat_s),
+		&cd_AudStat, &cd_AudStatSeg, &cd_AudStatSel
+	},
+	{
+		0, NULL, NULL, NULL
+	}
+};
+
+// CODE --------------------------------------------------------------------
+
+//==========================================================================
+//
+// I_CDMusInit
+//
+// Initializes the CD audio system.  Must be called before using any
+// other I_CDMus functions.
+//
+// Returns: 0 (ok) or -1 (error, in cd_Error).
+//
+//==========================================================================
+
+int I_CDMusInit(void)
+{
+	int i;
+	int sect;
+	int maxTrack;
+	S_BYTE startMin1 = 0;
+	S_BYTE startSec1 = 0;
+	S_BYTE startMin2 = 0;
+	S_BYTE startSec2 = 0;
+	S_BYTE lengthMin = 0;
+	S_BYTE lengthSec = 0;
+
+	if(OkInit != 1)
+	{ // Only execute if uninitialized
+
+		// Get number of CD-ROM drives and first drive
+		memset(&RegBlock, 0, sizeof(RegBlock));
+		RegBlock.eax = CDROM_GETDRIVECOUNT;
+		RegBlock.ebx = 0;
+		DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock);
+		cd_DriveCount = RegBlock.ebx;
+
+		// MSCDEX not installed if number of drives = 0
+		if(cd_DriveCount == 0)
+		{
+			cd_Error = CDERR_NOTINSTALLED;
+			return -1;
+		}
+		cd_FirstDrive = RegBlock.ecx;
+		cd_CurDrive = cd_FirstDrive;
+
+		// Allocate the IOCTL buffers
+		if(AllocIOCTLBuffers() == -1)
+		{
+			cd_Error = CDERR_IOCTLBUFFMEM;
+			return -1;
+		}
+
+		// Get MSCDEX version
+		// Major version in upper byte, minor version in lower byte
+		memset(&RegBlock, 0, sizeof(RegBlock));
+		RegBlock.eax = CDROM_GETVERSION;
+		DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock);
+		cd_Version = RegBlock.ebx;
+
+		// Check device status to make sure we can read Audio CD's
+		InputIOCTL(DEVICESTATUS, cd_DevStatSeg);
+		if((cd_DevStat->devParams & 0x0010) == 0)
+		{
+			cd_Error = CDERR_NOAUDIOSUPPORT;
+			return -1;
+		}
+	}
+
+	// Force audio to stop playing
+	I_CDMusStop();
+
+	// Make sure we have the current TOC
+	InputIOCTL(MEDIACHANGED, cd_MedChngSeg);
+
+	// Set track variables
+	InputIOCTL(AUDIODISKINFO, cd_DiskInfoSeg);
+	cd_FirstTrack = cd_DiskInfo->lowTrack;
+	cd_LastTrack = cd_DiskInfo->highTrack;
+	if(cd_FirstTrack == 0 && cd_FirstTrack == cd_LastTrack)
+	{
+		cd_Error = CDERR_NOAUDIOTRACKS;
+		return -1;
+	}
+	cd_TrackCount = cd_LastTrack-cd_FirstTrack+1;
+	cd_LeadOutMin = cd_DiskInfo->startLeadOut>>16 & 0xFF;
+	cd_LeadOutSec = cd_DiskInfo->startLeadOut>>8 & 0xFF;
+	cd_LeadOutRed = cd_DiskInfo->startLeadOut;
+	cd_LeadOutSector = RedToSectors(cd_DiskInfo->startLeadOut);
+
+	// Create Red Book start, sector start, and sector length
+	// for all tracks
+	sect = cd_LeadOutSector;
+	for(i = cd_LastTrack; i >= cd_FirstTrack; i--)
+	{
+		cd_TrackInfo->track = i;
+		InputIOCTL(AUDIOTRACKINFO, cd_TrackInfoSeg);
+		if(i < MAX_AUDIO_TRACKS)
+		{
+			cd_AudioTracks[i].redStart = cd_TrackInfo->start;
+			cd_AudioTracks[i].sectorStart =
+				RedToSectors(cd_TrackInfo->start);
+			cd_AudioTracks[i].sectorLength =
+				sect-RedToSectors(cd_TrackInfo->start);
+		}
+		sect = RedToSectors(cd_TrackInfo->start);
+	}
+
+	// Create track lengths in minutes and seconds
+	if(cd_LastTrack >= MAX_AUDIO_TRACKS)
+	{
+		maxTrack = MAX_AUDIO_TRACKS-1;
+	}
+	else
+	{
+		maxTrack = cd_LastTrack;
+	}
+	cd_TrackInfo->track = cd_FirstTrack;
+	InputIOCTL(AUDIOTRACKINFO, cd_TrackInfoSeg);
+	startMin1 = (cd_TrackInfo->start >> 16);
+	startSec1 = (cd_TrackInfo->start >> 8);
+	for(i = cd_FirstTrack; i <= maxTrack; i++)
+	{
+		cd_TrackInfo->track = i+1;
+		if(i < cd_LastTrack)
+		{
+			InputIOCTL(AUDIOTRACKINFO, cd_TrackInfoSeg);
+			startMin2 = (cd_TrackInfo->start >> 16);
+			startSec2 = (cd_TrackInfo->start >> 8);
+		}
+		else
+		{
+			startMin2 = cd_LeadOutRed>>16;
+			startSec2 = cd_LeadOutRed>>8;
+		}
+		lengthSec = startSec2 - startSec1;
+		lengthMin = startMin2 - startMin1;
+		if(lengthSec < 0)
+		{
+			lengthSec += 60;
+			lengthMin--;
+		}
+		cd_AudioTracks[i].lengthMin = lengthMin;
+		cd_AudioTracks[i].lengthSec = lengthSec;
+		startMin1 = startMin2;
+		startSec1 = startSec2;
+	}
+
+	// Clip high tracks
+	cd_LastTrack = maxTrack;
+
+	OkInit = 1;
+	return 0;
+}
+
+//==========================================================================
+//
+// I_CDMusPlay
+//
+// Play an audio CD track.
+//
+// Returns: 0 (ok) or -1 (error, in cd_Error).
+//
+//==========================================================================
+
+int I_CDMusPlay(int track)
+{
+	int start;
+	int len;
+
+	if(track < cd_FirstTrack || track > cd_LastTrack)
+	{
+		cd_Error = CDERR_BADTRACK;
+		return(-1);
+	}
+	I_CDMusStop();
+	start = cd_AudioTracks[track].redStart;
+	len = cd_AudioTracks[track].sectorLength;
+	cd_PlayReq->addressMode = RED_MODE;
+	cd_PlayReq->startSector = start;
+	cd_PlayReq->numberToRead = len;
+	memset(&RegBlock, 0, sizeof(RegBlock));
+	RegBlock.eax = CDROM_SENDDEVICEREQ;
+	RegBlock.ecx = cd_CurDrive;
+	RegBlock.ebx = 0;
+	RegBlock.es = cd_PlayReqSeg;
+	DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock);
+	if(cd_PlayReq->status&0x8000)
+	{
+		cd_Error = CDERR_DEVREQBASE+(cd_PlayReq->status)&0x00ff;
+		return(-1);
+	}
+	return(0);
+}
+
+//==========================================================================
+//
+// I_CDMusStop
+//
+// Stops the playing of an audio CD.
+//
+// Returns: 0 (ok) or -1 (error, in cd_Error).
+//
+//==========================================================================
+
+int I_CDMusStop(void)
+{
+	memset(&RegBlock, 0, sizeof(RegBlock));
+	RegBlock.eax = CDROM_SENDDEVICEREQ;
+	RegBlock.ecx = cd_CurDrive;
+	RegBlock.ebx = 0;
+	RegBlock.es = cd_StopReqSeg;
+	DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock);
+	if(cd_StopReq->status&0x8000)
+	{
+		cd_Error = CDERR_DEVREQBASE+(cd_StopReq->status)&0x00ff;
+		return -1;
+	}
+	return 0;
+}
+
+//==========================================================================
+//
+// I_CDMusResume
+//
+// Resumes the playing of an audio CD.
+//
+// Returns: 0 (ok) or -1 (error, in cd_Error).
+//
+//==========================================================================
+
+int I_CDMusResume(void)
+{
+	memset(&RegBlock, 0, sizeof(RegBlock));
+	RegBlock.eax = CDROM_SENDDEVICEREQ;
+	RegBlock.ecx = cd_CurDrive;
+	RegBlock.ebx = 0;
+	RegBlock.es = cd_ResumeReqSeg;
+	DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock);
+	if(cd_ResumeReq->status&0x8000)
+	{
+		cd_Error = CDERR_DEVREQBASE+(cd_ResumeReq->status)&0x00ff;
+		return -1;
+	}
+	return 0;
+}
+
+//==========================================================================
+//
+// I_CDMusSetVolume
+//
+// Sets the CD audio volume (0 - 255).
+//
+// Returns: 0 (ok) or -1 (error, in cd_Error).
+//
+//==========================================================================
+
+int I_CDMusSetVolume(int volume)
+{
+
+	if(!OkInit)
+	{
+		cd_Error = CDERR_NOTINSTALLED;
+	   	return -1;
+	}
+
+	// Read current channel info
+	InputIOCTL(AUDIOCHANINFO, cd_AudChanInfoSeg);
+
+	// Change the volumes
+	cd_AudChanInfo->volumeOut0 =
+		cd_AudChanInfo->volumeOut1 =
+		cd_AudChanInfo->volumeOut2 =
+		cd_AudChanInfo->volumeOut3 = volume;
+
+	// Write modified channel info
+	OutputIOCTL(AUDIOCHANCONTROL, cd_AudChanInfoSeg);
+
+	return 0;
+}
+
+//==========================================================================
+//
+// I_CDMusFirstTrack
+//
+// Returns: the number of the first track.
+//
+//==========================================================================
+
+int I_CDMusFirstTrack(void)
+{
+	return cd_FirstTrack;
+}
+
+//==========================================================================
+//
+// I_CDMusLastTrack
+//
+// Returns: the number of the last track.
+//
+//==========================================================================
+
+int I_CDMusLastTrack(void)
+{
+	return cd_LastTrack;
+}
+
+//==========================================================================
+//
+// I_CDMusTrackLength
+//
+// Returns: Length of the given track in seconds, or -1 (error, in
+// cd_Error).
+//
+//==========================================================================
+
+int I_CDMusTrackLength(int track)
+{
+	if(track < cd_FirstTrack || track > cd_LastTrack)
+	{
+		cd_Error = CDERR_BADTRACK;
+		return -1;
+	}
+	return cd_AudioTracks[track].lengthMin*60
+		+cd_AudioTracks[track].lengthSec;
+}
+
+//==========================================================================
+//
+// AllocIOCTLBuffers
+//
+// Allocates conventional memory for the IOCTL input and output buffers.
+// Sets cd_IOCTLBufferTotal to the total allocated.
+//
+// Returns: 0 (ok) or -1 (error, in cd_Error).
+//
+//==========================================================================
+
+static int AllocIOCTLBuffers(void)
+{
+	int i;
+	int size;
+	DOSChunk_t *ck;
+
+	cd_IOCTLBufferTotal = 0;
+	for(i = 0; DOSChunks[i].size != 0; i++)
+	{
+		ck = &DOSChunks[i];
+		size = ck->size;
+		cd_IOCTLBufferTotal += (size+15)&0xfffffff0;
+		*ck->address = DPMI_AllocRealMem(size, ck->segment, ck->selector);
+		if(*ck->address == NULL)
+		{
+			return -1;
+		}
+		memset(*ck->address, 0, size);
+	}
+	cd_IOCTLIn->headerSize = sizeof(struct IOCTLIn_s);
+	cd_IOCTLIn->command = DRC_IOCTLINPUT;
+	cd_IOCTLOut->headerSize = sizeof(struct IOCTLOut_s);
+	cd_IOCTLOut->command = DRC_IOCTLOUTPUT;
+	cd_PlayReq->headerSize = sizeof(struct PlayReq_s);
+	cd_PlayReq->command = DRC_PLAYAUDIO;
+	cd_StopReq->headerSize = sizeof(struct StopReq_s);
+	cd_StopReq->command = DRC_STOPAUDIO;
+	cd_ResumeReq->headerSize = sizeof(struct ResumeReq_s);
+	cd_ResumeReq->command = DRC_RESUMEAUDIO;
+	return 0;
+}
+
+//==========================================================================
+//
+// InputIOCTL
+//
+// Sends an IOCTL input device request command.
+//
+// Returns: the status of the request.
+//
+//==========================================================================
+
+static U_WORD InputIOCTL(S_LONG request, U_WORD buffSeg)
+{
+	U_BYTE *code;
+
+	code = (U_BYTE *)(buffSeg<<4);
+	*code = (U_BYTE)request;
+	cd_IOCTLIn->ctrlBlkAddr = buffSeg<<16;
+	cd_IOCTLIn->tranSize = InCtrlBlkSize[request];
+	memset(&RegBlock, 0, sizeof(RegBlock));
+	RegBlock.eax = CDROM_SENDDEVICEREQ;
+	RegBlock.ecx = cd_CurDrive;
+	RegBlock.ebx = 0;
+	RegBlock.es = cd_IOCTLInSeg;
+	DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock);
+	return cd_IOCTLIn->status;
+}
+
+//==========================================================================
+//
+// OutputIOCTL
+//
+// Sends an IOCTL output device request command.
+//
+// Returns: the status of the request.
+//
+//==========================================================================
+
+static U_WORD OutputIOCTL(S_LONG request, U_WORD buffSeg)
+{
+	U_BYTE *code;
+
+	code = (U_BYTE *)(buffSeg<<4);
+	*code = (U_BYTE)request;
+	cd_IOCTLOut->ctrlBlkAddr = buffSeg<<16;
+	cd_IOCTLOut->tranSize = OutCtrlBlkSize[request];
+	RegBlock.eax = CDROM_SENDDEVICEREQ;
+	RegBlock.ecx = cd_CurDrive;
+	RegBlock.ebx = 0;
+	RegBlock.es = cd_IOCTLOutSeg;
+	DPMI_SimRealInt(MULTIPLEX_INT, &RegBlock);
+	return cd_IOCTLOut->status;
+}
+
+//==========================================================================
+//
+// RedToSectors
+//
+// Converts Red Book addresses to HSG sectors.
+// Sectors = Minutes * 60 * 75 + Seconds * 75 + Frame - 150
+//
+// Returns: HSG sectors.
+//
+//==========================================================================
+
+static U_LONG RedToSectors(U_LONG red)
+{
+	U_LONG sector;
+
+	sector = ((red&0x00ff0000) >> 16) * 60 * 75;
+	sector += ((red&0x0000ff00) >> 8) * 75;
+	sector += (red&0x000000ff);
+	return sector-150;
+}
+
+//==========================================================================
+//
+// DPMI_SimRealInt
+//
+//==========================================================================
+
+static void DPMI_SimRealInt(U_LONG intr, RegBlock_t *rBlock)
+{
+	union REGS regs;
+	struct SREGS sRegs;
+
+	regs.x.eax = DPMI_SIMREALINT;
+	regs.x.ebx = intr;
+	regs.x.ecx = 0;
+	regs.x.edi = FP_OFF((void far *)rBlock);
+	sRegs.es = FP_SEG((void far *)rBlock);
+	sRegs.ds = FP_SEG((void far *)rBlock);
+	int386x(DPMI_INT, &regs, &regs, &sRegs);
+}
+
+//==========================================================================
+//
+// DPMI_AllocRealMem
+//
+//==========================================================================
+
+static void *DPMI_AllocRealMem(U_LONG size, U_WORD *segment,
+	U_WORD *selector)
+{
+	union REGS inRegs;
+	union REGS outRegs;
+
+	inRegs.x.eax = DPMI_ALLOCREALMEM;
+	inRegs.x.ebx = (size+15)/16;
+	int386(DPMI_INT, &inRegs, &outRegs);
+	if(outRegs.x.cflag)
+	{
+		return NULL;
+	}
+	*segment = outRegs.x.eax&0xffff;
+	*selector = outRegs.x.edx&0xffff;
+	return (void *)(outRegs.x.eax<<4);
+}
+
+//==========================================================================
+//
+// DPMI_FreeRealMem
+//
+//==========================================================================
+
+static void DPMI_FreeRealMem(U_WORD selector)
+{
+	union REGS regs;
+
+	regs.x.eax = DPMI_FREEREALMEM;
+	regs.x.edx = selector;
+	int386(DPMI_INT, &regs, &regs);
+}
--- /dev/null
+++ b/src/hexen/i_cdmus.h
@@ -1,0 +1,26 @@
+
+// i_cdmus.h
+
+#ifndef __ICDMUS__
+#define __ICDMUS__
+
+#define CDERR_NOTINSTALLED   10     // MSCDEX not installed
+#define CDERR_NOAUDIOSUPPORT 11     // CD-ROM Doesn't support audio
+#define CDERR_NOAUDIOTRACKS  12     // Current CD has no audio tracks
+#define CDERR_BADDRIVE       20     // Bad drive number
+#define CDERR_BADTRACK       21     // Bad track number
+#define CDERR_IOCTLBUFFMEM   22     // Not enough low memory for IOCTL
+#define CDERR_DEVREQBASE     100    // DevReq errors
+
+extern int cd_Error;
+
+int I_CDMusInit(void);
+int I_CDMusPlay(int track);
+int I_CDMusStop(void);
+int I_CDMusResume(void);
+int I_CDMusSetVolume(int volume);
+int I_CDMusFirstTrack(void);
+int I_CDMusLastTrack(void);
+int I_CDMusTrackLength(int track);
+
+#endif
--- /dev/null
+++ b/src/hexen/i_cyber.c
@@ -1,0 +1,277 @@
+// I_cyber.c
+
+#include <dos.h>
+#include <stdlib.h>
+#include <string.h>
+#include "st_start.h"	// For ST_Message()
+
+
+// Prototypes
+unsigned char *I_AllocLow (int length);
+
+
+/*
+====================================================
+
+Doom control structure
+
+The keybaord and joystick will add to the values set by the cyberman,
+to a maximum of 0x19000 for forwardmove and sidemove.  Angleturn is
+not bounded at all.
+
+parm                    normal          fast
+-----           ------          ----
+forwardmove             0xc800          0x19000
+sidemove                0xc000          0x14000
+angleturn               0x2800000       0x5000000
+
+The keyboard and joystick have a 1/3 second slow turn of 0x1400000 under
+normal speed to help aiming.
+
+
+
+====================================================
+*/
+/* old ticcmd_t
+typedef struct
+{
+	char            forwardmove;            // *2048 for move
+	char            sidemove;                       // *2048 for move
+	short           angleturn;                      // <<16 for angle delta
+	short           consistancy;            // checks for net game
+	unsigned char            chatchar;
+	unsigned char           buttons;
+} ticcmd_t;
+*/
+// ticcmd_t as it appears in h2def.h
+typedef struct
+{
+	char forwardmove;
+	char sidemove;
+	short angleturn;
+	short consistancy;
+	unsigned char chatchar;
+	unsigned char buttons;
+	unsigned char lookfly;
+	unsigned char arti;
+}ticcmd_t;
+
+
+#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
+
+//==================================================
+//
+// CyberMan detection and usage info
+//
+//==================================================
+#define DPMI_INT        0x31
+#define MOUSE_INT       0x33
+
+#define DOSMEMSIZE      64      // enough for any SWIFT structure
+
+typedef struct {
+   short        x;
+   short        y;
+   short        z;
+   short        pitch;
+   short        roll;
+   short        yaw;
+   short        buttons;
+} SWIFT_3DStatus;
+
+// DPMI real mode interrupt structure
+static struct rminfo {
+	long EDI;
+	long ESI;
+	long EBP;
+	long reserved_by_system;
+	long EBX;
+	long EDX;
+	long ECX;
+	long EAX;
+	short flags;
+	short ES,DS,FS,GS,IP,CS,SP,SS;
+} RMI;
+
+typedef struct {
+   unsigned char        deviceType;
+   unsigned char        majorVersion;
+   unsigned char        minorVersion;
+   unsigned char        absRelFlags;
+   unsigned char        centeringFlags;
+   unsigned char        reserved[5];
+} StaticDeviceData;
+
+// values for deviceType:
+#define DEVTYPE_CYBERMAN        1
+
+short                   selector;
+unsigned short  segment;                // segment of DOS memory block
+SWIFT_3DStatus  *cyberstat;
+int                             isCyberPresent;         // is CyberMan present?
+
+
+static  union REGS regs;
+static  struct SREGS sregs;
+
+
+extern  int mousepresent;
+
+//===========================================================
+//
+// I_StartupCyberMan
+//
+// If a cyberman is present, init it and set isCyberPresent to 1
+//===========================================================
+void I_StartupCyberMan(void)
+{
+   StaticDeviceData *pbuf;
+
+   ST_Message("  CyberMan: ");
+   isCyberPresent = 0;
+
+   cyberstat = (SWIFT_3DStatus *)I_AllocLow (DOSMEMSIZE);
+   segment = (int)cyberstat>>4;
+
+   pbuf = (StaticDeviceData *)cyberstat;
+   memset(pbuf, 0, sizeof (StaticDeviceData));
+
+
+   // Use DPMI call 300h to issue mouse interrupt
+   memset(&RMI, 0, sizeof(RMI));
+   RMI.EAX = 0x53C1;            // SWIFT: Get Static Device Data
+   RMI.ES = segment;
+   RMI.EDX = 0;
+   memset(&sregs, 0, sizeof (sregs));
+   regs.w.ax = 0x0300;          // DPMI: simulate interrupt
+   regs.w.bx = MOUSE_INT;
+   regs.w.cx = 0;
+   regs.x.edi = FP_OFF(&RMI);
+   sregs.es = FP_SEG(&RMI);
+   int386x( DPMI_INT, &regs, &regs, &sregs );
+
+   if ((short)RMI.EAX != 1)
+   {
+	  // SWIFT functions not present
+	  ST_Message("Wrong mouse driver - no SWIFT support (AX=%04x).\n",
+			 (unsigned)(short)RMI.EAX);
+   }
+   else if (pbuf->deviceType != DEVTYPE_CYBERMAN)
+   {
+	  // no SWIFT device, or not CyberMan
+	  if (pbuf->deviceType == 0)
+	  {
+		 ST_Message("no SWIFT device connected.\n");
+	  }
+	  else
+	  {
+		 ST_Message("SWIFT device is not a CyberMan! (type=%d)\n",
+				pbuf->deviceType);
+	  }
+   }
+   else
+   {
+	  ST_Message("CyberMan %d.%02d connected.\n",
+			 pbuf->majorVersion, pbuf->minorVersion);
+	  isCyberPresent = 1;
+	  mousepresent = 0;
+   }
+}
+
+
+
+/*
+===============
+=
+= I_ReadCyberCmds
+=
+===============
+*/
+
+
+int             oldpos;
+
+void I_ReadCyberCmd (ticcmd_t *cmd)
+{
+	int             delta;
+
+	// Use DPMI call 300h to issue mouse interrupt
+	memset(&RMI, 0, sizeof(RMI));
+	RMI.EAX = 0x5301;            // SWIFT: Get Position and Buttons
+	RMI.ES = segment;
+	RMI.EDX = 0;
+	memset(&sregs, 0, sizeof (sregs));
+	regs.w.ax = 0x0300;          // DPMI: simulate interrupt
+	regs.w.bx = MOUSE_INT;
+	regs.w.cx = 0;
+	regs.x.edi = FP_OFF(&RMI);
+	sregs.es = FP_SEG(&RMI);
+	int386x( DPMI_INT, &regs, &regs, &sregs );
+
+	if (cyberstat->y < -7900)
+		cmd->forwardmove = 0xc800/2048;
+	else if (cyberstat->y > 7900)
+		cmd->forwardmove = -0xc800/2048;
+
+	if (cyberstat->buttons & 4)
+		cmd->buttons |= BT_ATTACK;
+	if (cyberstat->buttons & 2)
+		cmd->buttons |= BT_USE;
+
+	delta = cyberstat->x - oldpos;
+	oldpos = cyberstat->x;
+
+	if (cyberstat->buttons & 1)
+	{       // strafe
+		if (cyberstat->x < -7900)
+			cmd->sidemove = -0xc800/2048;
+		else if (cyberstat->x > 7900)
+			cmd->sidemove = 0xc800/2048;
+		else
+			cmd->sidemove = delta*40/2048;
+	}
+	else
+	{
+		if (cyberstat->x < -7900)
+			cmd->angleturn = 0x280;
+		else if (cyberstat->x > 7900)
+			cmd->angleturn = -0x280;
+		else
+			cmd->angleturn = -delta*0xa/16;
+
+	}
+
+}
+
+
+void I_Tactile (int on, int off, int total)
+{
+	if (!isCyberPresent)
+		return;
+
+	on /= 5;
+	off /= 5;
+	total /= 40;
+	if (on > 255)
+		on = 255;
+	if (off > 255)
+		off = 255;
+	if (total > 255)
+		total = 255;
+
+	memset(&RMI, 0, sizeof(RMI));
+	RMI.EAX = 0x5330;            // SWIFT: Get Position and Buttons
+	RMI.EBX = on*256+off;
+	RMI.ECX = total;
+	memset(&sregs, 0, sizeof (sregs));
+	regs.w.ax = 0x0300;          // DPMI: simulate interrupt
+	regs.w.bx = MOUSE_INT;
+	regs.w.cx = 0;
+	regs.x.edi = FP_OFF(&RMI);
+	sregs.es = FP_SEG(&RMI);
+	int386x( DPMI_INT, &regs, &regs, &sregs );
+}
--- /dev/null
+++ b/src/hexen/i_header.h
@@ -1,0 +1,77 @@
+#ifndef __I_HEADER_H__
+#define __I_HEADER_H__
+
+#include "h2def.h"
+
+//--------
+//SOUND IO
+//--------
+#define	FREQ_LOW		0x40
+#define FREQ_NORM		0x80
+#define FREQ_HIGH		0xff
+
+void I_SetMasterVolume(int volume);
+
+void I_TurnOffSfx(void);
+void I_TurnOnSfx(void);
+void I_TurnOffMusic(void);
+void I_TurnOnMusic(void);
+
+//  MUSIC I/O
+//
+
+int I_RegisterSong(void *songdata);
+// called by anything that wants to register a song lump with the sound lib
+// calls Paul's function of the similar name to register music only.
+// note that the song data is the same for any sound card and is paul's
+// MUS format.  Returns a handle which will be passed to all other music
+// functions.
+
+void I_UnregisterSong(int handle);
+// called by anything which is finished with a song and no longer needs
+// the sound library to be aware of it.  All songs should be stopped
+// before calling this, but it will double check and stop it if necessary.
+
+void I_LoopSong(int handle);
+// called by anything that wishes to start music.
+// plays a song, and when the song is done, starts playing it again in
+// an endless loop.  the start is faded in over three seconds.
+
+void I_FadeOutSong(int handle, int fotime);
+// called by anything that wishes to stop music.
+// fades out the song over <fotime> milliseconds.
+
+void I_StopSong(int handle);
+// called by anything that wishes to stop music.
+// stops a song abruptly.
+
+//  SFX I/O
+//
+
+void *I_GetSoundEffect (char *soundname);
+// called by routines which wish to play a sound effect at some later
+// time.  Pass it the lump name of a sound effect WITHOUT the sfx
+// prefix.  This means the maximum name length is 7 letters/digits.
+// The prefixes for different sound cards are 'S','M','A', and 'P'.
+// They refer to the card type.  The routine will cache in the
+// appropriate sound effect when it is played.
+
+void I_UngetSoundEffect (void *soundset);
+// called by routines which wish to no longer use the sounds at all
+// frees up the associated structure.  It stops any currently playing
+// sound effects.
+
+void I_StartSound (channel_t *c, int vol, int sep, int pitch, int priority);
+// Starts a sound in a particular sound channel
+
+void I_UpdateSoundParams(channel_t *c, int vol, int sep, int pitch);
+// Updates the volume, separation, and pitch of a sound channel
+
+void I_StopSound(channel_t *c);
+// Stops a sound channel
+
+int I_SoundIsPlaying(channel_t *c);
+// called by S_*()'s to see if a channel is still playing.  Returns 0
+// if no longer playing, 1 if playing.
+
+#endif
--- /dev/null
+++ b/src/hexen/i_ibm.c
@@ -1,0 +1,2782 @@
+
+//**************************************************************************
+//**
+//** I_IBM.C
+//**
+//**************************************************************************
+
+#include <dos.h>
+#include <conio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <graph.h>
+#include "h2def.h"
+#include "r_local.h"
+#include "p_local.h"    // for P_AproxDistance
+#include "sounds.h"
+#include "i_sound.h"
+#include "soundst.h"
+#include "st_start.h"
+#include "dmx.h"
+#include "dpmiapi.h"
+
+// Macros
+
+#define DEFAULT_ARCHIVEPATH     "o:\\sound\\archive\\"
+#define PRIORITY_MAX_ADJUST 10
+#define DIST_ADJUST (MAX_SND_DIST/PRIORITY_MAX_ADJUST)
+
+#define DPMI_INT 0x31
+
+#define SEQ_ADDR 0x3C4
+#define SEQ_DATA 0x3C5
+#define REG_MAPMASK 0x02
+
+#define MASK_PLANE0 0x01
+#define MASK_PLANE1 0x02
+#define MASK_PLANE2 0x04
+#define MASK_PLANE3 0x08
+
+#define P0OFFSET 38400*0
+#define P1OFFSET 38400*1
+#define P2OFFSET 38400*2
+#define P3OFFSET 38400*3
+
+#define VID_INT 0x10
+#define VB_SYNC {while(!(inp(0x3da)&8)); while(inp(0x3da)&8);}
+#define BITPLANE(p) (outp(SEQ_ADDR,REG_MAPMASK),outp(SEQ_DATA,(p)))
+
+//#define NOKBD
+//#define NOTIMER
+
+// Public Data
+
+int DisplayTicker = 0;
+
+// Code
+
+void main(int argc, char **argv)
+{
+	myargc = argc;
+	myargv = argv;
+	H2_Main();
+}
+
+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);
+
+extern  int     usemouse, usejoystick;
+
+extern void **lumpcache;
+
+int i_Vector;
+externdata_t *i_ExternData;
+boolean useexterndriver;
+
+boolean i_CDMusic;
+int i_CDTrack;
+int i_CDCurrentTrack;
+int i_CDMusicLength;
+int oldTic;
+
+/*
+===============================================================================
+
+		MUSIC & SFX API
+
+===============================================================================
+*/
+
+//static channel_t channel[MAX_CHANNELS];
+
+//static int rs; //the current registered song.
+//int mus_song = -1;
+//int mus_lumpnum;
+//void *mus_sndptr;
+//byte *soundCurve;
+
+extern sfxinfo_t S_sfx[];
+extern musicinfo_t S_music[];
+
+static channel_t Channel[MAX_CHANNELS];
+static int RegisteredSong; //the current registered song.
+static int NextCleanup;
+static boolean MusicPaused;
+static int Mus_Song = -1;
+static int Mus_LumpNum;
+static void *Mus_SndPtr;
+static byte *SoundCurve;
+
+static boolean UseSndScript;
+static char ArchivePath[128];
+
+extern int snd_MusicDevice;
+extern int snd_SfxDevice;
+extern int snd_MaxVolume;
+extern int snd_MusicVolume;
+extern int snd_Channels;
+
+extern int startepisode;
+extern int startmap;
+
+// int AmbChan;
+
+//==========================================================================
+//
+// S_Start
+//
+//==========================================================================
+
+void S_Start(void)
+{
+	S_StopAllSound();
+	S_StartSong(gamemap, true);
+}
+
+//==========================================================================
+//
+// S_StartSong
+//
+//==========================================================================
+
+void S_StartSong(int song, boolean loop)
+{
+	char *songLump;
+	int track;
+
+	if(i_CDMusic)
+	{ // Play a CD track, instead
+		if(i_CDTrack)
+		{ // Default to the player-chosen track
+			track = i_CDTrack;
+		}
+		else
+		{
+			track = P_GetMapCDTrack(gamemap);
+		}
+		if(track == i_CDCurrentTrack && i_CDMusicLength > 0)
+		{
+			return;
+		}
+		if(!I_CDMusPlay(track))
+		{
+			if(loop)
+			{
+				i_CDMusicLength = 35*I_CDMusTrackLength(track);
+				oldTic = gametic;
+			}
+			else
+			{
+				i_CDMusicLength = -1;
+			}
+			i_CDCurrentTrack = track;
+		}
+	}
+	else
+	{
+		if(song == Mus_Song)
+		{ // don't replay an old song
+			return;
+		}
+		if(RegisteredSong)
+		{
+			I_StopSong(RegisteredSong);
+			I_UnRegisterSong(RegisteredSong);
+			if(UseSndScript)
+			{
+				Z_Free(Mus_SndPtr);
+			}
+			else
+			{
+				Z_ChangeTag(lumpcache[Mus_LumpNum], PU_CACHE);
+			}
+			#ifdef __WATCOMC__
+				_dpmi_unlockregion(Mus_SndPtr, lumpinfo[Mus_LumpNum].size);
+			#endif
+			RegisteredSong = 0;
+		}
+		songLump = P_GetMapSongLump(song);
+		if(!songLump)
+		{
+			return;
+		}
+		if(UseSndScript)
+		{
+			char name[128];
+			sprintf(name, "%s%s.lmp", ArchivePath, songLump);
+			M_ReadFile(name, (byte **)&Mus_SndPtr);
+		}
+		else
+		{
+			Mus_LumpNum = W_GetNumForName(songLump);
+			Mus_SndPtr = W_CacheLumpNum(Mus_LumpNum, PU_MUSIC);
+		}
+		#ifdef __WATCOMC__
+			_dpmi_lockregion(Mus_SndPtr, lumpinfo[Mus_LumpNum].size);
+		#endif
+		RegisteredSong = I_RegisterSong(Mus_SndPtr);
+		I_PlaySong(RegisteredSong, loop); // 'true' denotes endless looping.
+		Mus_Song = song;
+	}
+}
+
+//==========================================================================
+//
+// S_StartSongName
+//
+//==========================================================================
+
+void S_StartSongName(char *songLump, boolean loop)
+{
+	int cdTrack;
+
+	if(!songLump)
+	{
+		return;
+	}
+	if(i_CDMusic)
+	{
+		cdTrack = 0;
+
+		if(!strcmp(songLump, "hexen"))
+		{
+			cdTrack = P_GetCDTitleTrack();
+		}
+		else if(!strcmp(songLump, "hub"))
+		{
+			cdTrack = P_GetCDIntermissionTrack();
+		}
+		else if(!strcmp(songLump, "hall"))
+		{
+			cdTrack = P_GetCDEnd1Track();
+		}
+		else if(!strcmp(songLump, "orb"))
+		{
+			cdTrack = P_GetCDEnd2Track();
+		}
+		else if(!strcmp(songLump, "chess") && !i_CDTrack)
+		{
+			cdTrack = P_GetCDEnd3Track();
+		}
+/*	Uncomment this, if Kevin writes a specific song for startup
+		else if(!strcmp(songLump, "start"))
+		{
+			cdTrack = P_GetCDStartTrack();
+		}
+*/
+		if(!cdTrack || (cdTrack == i_CDCurrentTrack && i_CDMusicLength > 0))
+		{
+			return;
+		}
+		if(!I_CDMusPlay(cdTrack))
+		{
+			if(loop)
+			{
+				i_CDMusicLength = 35*I_CDMusTrackLength(cdTrack);
+				oldTic = gametic;
+			}
+			else
+			{
+				i_CDMusicLength = -1;
+			}
+			i_CDCurrentTrack = cdTrack;
+			i_CDTrack = false;
+		}
+	}
+	else
+	{
+		if(RegisteredSong)
+		{
+			I_StopSong(RegisteredSong);
+			I_UnRegisterSong(RegisteredSong);
+			if(UseSndScript)
+			{
+				Z_Free(Mus_SndPtr);
+			}
+			else
+			{
+				Z_ChangeTag(lumpcache[Mus_LumpNum], PU_CACHE);
+			}
+			#ifdef __WATCOMC__
+				_dpmi_unlockregion(Mus_SndPtr, lumpinfo[Mus_LumpNum].size);
+			#endif
+			RegisteredSong = 0;
+		}
+		if(UseSndScript)
+		{
+			char name[128];
+			sprintf(name, "%s%s.lmp", ArchivePath, songLump);
+			M_ReadFile(name, (byte **)&Mus_SndPtr);
+		}
+		else
+		{
+			Mus_LumpNum = W_GetNumForName(songLump);
+			Mus_SndPtr = W_CacheLumpNum(Mus_LumpNum, PU_MUSIC);
+		}
+		#ifdef __WATCOMC__
+			_dpmi_lockregion(Mus_SndPtr, lumpinfo[Mus_LumpNum].size);
+		#endif
+		RegisteredSong = I_RegisterSong(Mus_SndPtr);
+		I_PlaySong(RegisteredSong, loop); // 'true' denotes endless looping.
+		Mus_Song = -1;
+	}
+}
+
+//==========================================================================
+//
+// S_GetSoundID
+//
+//==========================================================================
+
+int S_GetSoundID(char *name)
+{
+	int i;
+
+	for(i = 0; i < NUMSFX; i++)
+	{
+		if(!strcmp(S_sfx[i].tagName, name))
+		{
+			return i;
+		}
+	}
+	return 0;
+}
+
+//==========================================================================
+//
+// S_StartSound
+//
+//==========================================================================
+
+void S_StartSound(mobj_t *origin, int sound_id)
+{
+	S_StartSoundAtVolume(origin, sound_id, 127);
+}
+
+//==========================================================================
+//
+// S_StartSoundAtVolume
+//
+//==========================================================================
+
+void S_StartSoundAtVolume(mobj_t *origin, int sound_id, int volume)
+{
+	int dist, vol;
+	int i;
+	int priority;
+	int sep;
+	int angle;
+	int absx;
+	int absy;
+
+	static int sndcount = 0;
+	int chan;
+
+	if(sound_id == 0 || snd_MaxVolume == 0)
+		return;
+	if(origin == NULL)
+	{
+		origin = players[displayplayer].mo;
+	}
+	if(volume == 0)
+	{
+		return;
+	}
+
+	// calculate the distance before other stuff so that we can throw out
+	// sounds that are beyond the hearing range.
+	absx = abs(origin->x-players[displayplayer].mo->x);
+	absy = abs(origin->y-players[displayplayer].mo->y);
+	dist = absx+absy-(absx > absy ? absy>>1 : absx>>1);
+	dist >>= FRACBITS;
+	if(dist >= MAX_SND_DIST)
+	{
+	  return; // sound is beyond the hearing range...
+	}
+	if(dist < 0)
+	{
+		dist = 0;
+	}
+	priority = S_sfx[sound_id].priority;
+	priority *= (PRIORITY_MAX_ADJUST-(dist/DIST_ADJUST));
+	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)
+	{
+		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(S_sfx[sound_id].lumpnum == 0)
+	{
+		S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]);
+	}
+	if(S_sfx[sound_id].snd_ptr == NULL)
+	{
+		if(UseSndScript)
+		{
+			char name[128];
+			sprintf(name, "%s%s.lmp", ArchivePath, S_sfx[sound_id].lumpname);
+			M_ReadFile(name, (byte **)&S_sfx[sound_id].snd_ptr);
+		}
+		else
+		{
+			S_sfx[sound_id].snd_ptr = W_CacheLumpNum(S_sfx[sound_id].lumpnum,
+				PU_SOUND);
+		}
+		#ifdef __WATCOMC__
+		_dpmi_lockregion(S_sfx[sound_id].snd_ptr,
+			lumpinfo[S_sfx[sound_id].lumpnum].size);
+		#endif
+	}
+
+	vol = (SoundCurve[dist]*(snd_MaxVolume*8)*volume)>>14;
+	if(origin == players[displayplayer].mo)
+	{
+		sep = 128;
+//              vol = (volume*(snd_MaxVolume+1)*8)>>7;
+	}
+	else
+	{
+		angle = R_PointToAngle2(players[displayplayer].mo->x,
+			players[displayplayer].mo->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;
+//              vol = SoundCurve[dist];
+	}
+
+	if(S_sfx[sound_id].changePitch)
+	{
+		Channel[i].pitch = (byte)(127+(M_Random()&7)-(M_Random()&7));
+	}
+	else
+	{
+		Channel[i].pitch = 127;
+	}
+	Channel[i].handle = I_StartSound(sound_id, S_sfx[sound_id].snd_ptr, vol,
+		sep, Channel[i].pitch, 0);
+	Channel[i].mo = origin;
+	Channel[i].sound_id = sound_id;
+	Channel[i].priority = priority;
+	Channel[i].volume = volume;
+	if(S_sfx[sound_id].usefulness < 0)
+	{
+		S_sfx[sound_id].usefulness = 1;
+	}
+	else
+	{
+		S_sfx[sound_id].usefulness++;
+	}
+}
+
+//==========================================================================
+//
+// S_StopSoundID
+//
+//==========================================================================
+
+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[lp].sound_id].usefulness > 0)
+		{
+			S_sfx[Channel[lp].sound_id].usefulness--;
+		}
+		Channel[lp].mo = NULL;
+	}
+	return(true);
+}
+
+//==========================================================================
+//
+// S_StopSound
+//
+//==========================================================================
+
+void S_StopSound(mobj_t *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;
+		}
+	}
+}
+
+//==========================================================================
+//
+// S_StopAllSound
+//
+//==========================================================================
+
+void S_StopAllSound(void)
+{
+	int i;
+
+	//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));
+}
+
+//==========================================================================
+//
+// S_SoundLink
+//
+//==========================================================================
+
+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;
+	}
+}
+
+//==========================================================================
+//
+// S_PauseSound
+//
+//==========================================================================
+
+void S_PauseSound(void)
+{
+	if(i_CDMusic)
+	{
+		I_CDMusStop();
+	}
+	else
+	{
+		I_PauseSong(RegisteredSong);
+	}
+}
+
+//==========================================================================
+//
+// S_ResumeSound
+//
+//==========================================================================
+
+void S_ResumeSound(void)
+{
+	if(i_CDMusic)
+	{
+		I_CDMusResume();
+	}
+	else
+	{
+		I_ResumeSong(RegisteredSong);
+	}
+}
+
+//==========================================================================
+//
+// S_UpdateSounds
+//
+//==========================================================================
+
+void S_UpdateSounds(mobj_t *listener)
+{
+	int i, dist, vol;
+	int angle;
+	int sep;
+	int priority;
+	int absx;
+	int absy;
+
+	if(i_CDMusic)
+	{
+		I_UpdateCDMusic();
+	}
+	if(snd_MaxVolume == 0)
+	{
+		return;
+	}
+
+	// Update any Sequences
+	SN_UpdateActiveSequences();
+
+	if(NextCleanup < gametic)
+	{
+		if(UseSndScript)
+		{
+			for(i = 0; i < NUMSFX; i++)
+			{
+				if(S_sfx[i].usefulness == 0 && S_sfx[i].snd_ptr)
+				{
+					S_sfx[i].usefulness = -1;
+				}
+			}
+		}
+		else
+		{
+			for(i = 0; i < NUMSFX; i++)
+			{
+				if(S_sfx[i].usefulness == 0 && S_sfx[i].snd_ptr)
+				{
+					if(lumpcache[S_sfx[i].lumpnum])
+					{
+						if(((memblock_t *)((byte*)
+							(lumpcache[S_sfx[i].lumpnum])-
+							sizeof(memblock_t)))->id == 0x1d4a11)
+						{ // taken directly from the Z_ChangeTag macro
+							Z_ChangeTag2(lumpcache[S_sfx[i].lumpnum],
+								PU_CACHE);
+#ifdef __WATCOMC__
+								_dpmi_unlockregion(S_sfx[i].snd_ptr,
+									lumpinfo[S_sfx[i].lumpnum].size);
+#endif
+						}
+					}
+					S_sfx[i].usefulness = -1;
+					S_sfx[i].snd_ptr = NULL;
+				}
+			}
+		}
+		NextCleanup = gametic+35*30; // every 30 seconds
+	}
+	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(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;
+
+			if(dist >= MAX_SND_DIST)
+			{
+				S_StopSound(Channel[i].mo);
+				continue;
+			}
+			if(dist < 0)
+			{
+				dist = 0;
+			}
+			//vol = SoundCurve[dist];
+			vol = (SoundCurve[dist]*(snd_MaxVolume*8)*Channel[i].volume)>>14;
+			if(Channel[i].mo == listener)
+			{
+				sep = 128;
+			}
+			else
+			{
+				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;
+			}
+			I_UpdateSoundParams(Channel[i].handle, vol, sep,
+				Channel[i].pitch);
+			priority = S_sfx[Channel[i].sound_id].priority;
+			priority *= PRIORITY_MAX_ADJUST-(dist/DIST_ADJUST);
+			Channel[i].priority = priority;
+		}
+	}
+}
+
+//==========================================================================
+//
+// S_Init
+//
+//==========================================================================
+
+void S_Init(void)
+{
+	SoundCurve = W_CacheLumpName("SNDCURVE", PU_STATIC);
+//      SoundCurve = Z_Malloc(MAX_SND_DIST, PU_STATIC, NULL);
+	I_StartupSound();
+	if(snd_Channels > 8)
+	{
+		snd_Channels = 8;
+	}
+	I_SetChannels(snd_Channels);
+	I_SetMusicVolume(snd_MusicVolume);
+
+	// Attempt to setup CD music
+	if(snd_MusicDevice == snd_CDMUSIC)
+	{
+	   	ST_Message("    Attempting to initialize CD Music: ");
+		if(!cdrom)
+		{
+			i_CDMusic = (I_CDMusInit() != -1);
+		}
+		else
+		{ // The user is trying to use the cdrom for both game and music
+			i_CDMusic = false;
+		}
+	   	if(i_CDMusic)
+	   	{
+	   		ST_Message("initialized.\n");
+	   	}
+	   	else
+	   	{
+	   		ST_Message("failed.\n");
+	   	}
+	}
+}
+
+//==========================================================================
+//
+// S_GetChannelInfo
+//
+//==========================================================================
+
+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].lumpname;
+		c->mo = Channel[i].mo;
+		c->distance = P_AproxDistance(c->mo->x-viewx, c->mo->y-viewy)
+			>>FRACBITS;
+	}
+}
+
+//==========================================================================
+//
+// S_GetSoundPlayingInfo
+//
+//==========================================================================
+
+boolean S_GetSoundPlayingInfo(mobj_t *mobj, int sound_id)
+{
+	int i;
+
+	for(i = 0; i < snd_Channels; i++)
+	{
+		if(Channel[i].sound_id == sound_id && Channel[i].mo == mobj)
+		{
+			if(I_SoundIsPlaying(Channel[i].handle))
+			{
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+//==========================================================================
+//
+// S_SetMusicVolume
+//
+//==========================================================================
+
+void S_SetMusicVolume(void)
+{
+	if(i_CDMusic)
+	{
+		I_CDMusSetVolume(snd_MusicVolume*16); // 0-255
+	}
+	else
+	{
+		I_SetMusicVolume(snd_MusicVolume);
+	}
+	if(snd_MusicVolume == 0)
+	{
+		if(!i_CDMusic)
+		{
+			I_PauseSong(RegisteredSong);
+		}
+		MusicPaused = true;
+	}
+	else if(MusicPaused)
+	{
+		if(!i_CDMusic)
+		{
+			I_ResumeSong(RegisteredSong);
+		}
+		MusicPaused = false;
+	}
+}
+
+//==========================================================================
+//
+// S_ShutDown
+//
+//==========================================================================
+
+void S_ShutDown(void)
+{
+	extern int tsm_ID;
+	if(tsm_ID != -1)
+	{
+		I_StopSong(RegisteredSong);
+		I_UnRegisterSong(RegisteredSong);
+		I_ShutdownSound();
+	}
+	if(i_CDMusic)
+	{
+		I_CDMusStop();
+	}
+}
+
+//==========================================================================
+//
+// S_InitScript
+//
+//==========================================================================
+
+void S_InitScript(void)
+{
+	int p;
+	int i;
+
+	strcpy(ArchivePath, DEFAULT_ARCHIVEPATH);
+	if(!(p = M_CheckParm("-devsnd")))
+	{
+		UseSndScript = false;
+		SC_OpenLump("sndinfo");
+	}
+	else
+	{
+		UseSndScript = true;
+		SC_OpenFile(myargv[p+1]);
+	}
+	while(SC_GetString())
+	{
+		if(*sc_String == '$')
+		{
+			if(!stricmp(sc_String, "$ARCHIVEPATH"))
+			{
+				SC_MustGetString();
+				strcpy(ArchivePath, sc_String);
+			}
+			else if(!stricmp(sc_String, "$MAP"))
+			{
+				SC_MustGetNumber();
+				SC_MustGetString();
+				if(sc_Number)
+				{
+					P_PutMapSongLump(sc_Number, sc_String);
+				}
+			}
+			continue;
+		}
+		else
+		{
+			for(i = 0; i < NUMSFX; i++)
+			{
+				if(!strcmp(S_sfx[i].tagName, sc_String))
+				{
+					SC_MustGetString();
+					if(*sc_String != '?')
+					{
+						strcpy(S_sfx[i].lumpname, sc_String);
+					}
+					else
+					{
+						strcpy(S_sfx[i].lumpname, "default");
+					}
+					break;
+				}
+			}
+			if(i == NUMSFX)
+			{
+				SC_MustGetString();
+			}
+		}
+	}
+	SC_Close();
+
+	for(i = 0; i < NUMSFX; i++)
+	{
+		if(!strcmp(S_sfx[i].lumpname, ""))
+		{
+			strcpy(S_sfx[i].lumpname, "default");
+		}
+	}
+}
+
+//==========================================================================
+//
+// I_UpdateCDMusic
+//
+// Updates playing time for current track, and restarts the track, if
+// needed
+//
+//==========================================================================
+
+void I_UpdateCDMusic(void)
+{
+	extern boolean MenuActive;
+
+	if(MusicPaused || i_CDMusicLength < 0
+	|| (paused && !MenuActive))
+	{ // Non-looping song/song paused
+		return;
+	}
+	i_CDMusicLength -= gametic-oldTic;
+	oldTic = gametic;
+	if(i_CDMusicLength <= 0)
+	{
+		S_StartSong(gamemap, true);
+	}
+}
+
+/*
+============================================================================
+
+							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 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;
+		}
+	}
+
+	//memset(pcscreen, 255, SCREENHEIGHT*SCREENWIDTH);
+
+	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));
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_ShutdownGraphics
+//
+//--------------------------------------------------------------------------
+
+void I_ShutdownGraphics(void)
+{
+	byte mode;
+
+	// don't reset mode if it didn't get set
+	mode = *(byte *)0x449;
+	if(mode == 0x12 || mode == 0x13)
+	{
+		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;
+			H2_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;
+		}
+		H2_PostEvent (&ev);
+	}
+
+}
+
+
+/*
+void   I_ReadKeys (void)
+{
+	int             k;
+
+
+	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 ();
+	if(useexterndriver)
+	{
+		DPMIInt(i_Vector);
+	}
+}
+
+/*
+============================================================================
+
+					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)
+{
+   //
+   // General mouse detection
+   //
+	mousepresent = 0;
+	if ( M_CheckParm ("-nomouse") || !usemouse )
+		return;
+
+	if (I_ResetMouse () != 0xffff)
+	{
+		ST_Message("Mouse: not present\n");
+		return;
+	}
+	ST_Message("Mouse: detected\n");
+
+	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;
+
+	H2_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     centerx, centery;
+
+	joystickpresent = 0;
+	if ( M_CheckParm ("-nojoy") || !usejoystick )
+		return;
+
+	if (!I_ReadJoystick ())
+	{
+		joystickpresent = false;
+		ST_Message("joystick not found\n ");
+		return;
+	}
+	ST_Message("joystick found\n");
+	joystickpresent = true;
+
+	ST_RealMessage("CENTER the joystick and press button 1:");
+	if (!WaitJoyButton ())
+		return;
+	I_ReadJoystick ();
+	centerx = joystickx;
+	centery = joysticky;
+
+	ST_RealMessage("\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;
+
+	ST_RealMessage("\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;
+	ST_RealMessage("\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;
+
+	H2_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");
+	ST_Message("  I_StartupDPMI\n");
+	I_StartupDPMI();
+	ST_Message("  I_StartupMouse ");
+	I_StartupMouse();
+//	tprintf("I_StartupJoystick ",1);
+//	I_StartupJoystick();
+//	tprintf("I_StartupKeyboard ",1);
+//	I_StartupKeyboard();
+	ST_Message("  S_Init... ");
+	S_Init();
+	//IO_StartupTimer();
+	S_Start();
+}
+
+
+/*
+===============
+=
+= 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)
+{
+	D_QuitNetGame();
+	M_SaveDefaults();
+	I_Shutdown();
+
+//	scr = (byte *)W_CacheLumpName("ENDTEXT", PU_CACHE);
+/*
+	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);
+*/
+	printf("\nHexen: Beyond Heretic\n");
+	exit(0);
+}
+
+/*
+===============
+=
+= I_ZoneBase
+=
+===============
+*/
+
+byte *I_ZoneBase (int *size)
+{
+	int meminfo[32];
+	int heap;
+	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];
+	ST_Message ("  DPMI memory: 0x%x, ",heap);
+	ST_Message ("Maxzone: 0x%x\n", maxzone);
+
+	do
+	{
+		heap -= 0x10000;                // leave 64k alone
+		if (heap > maxzone)
+			heap = maxzone;
+		ptr = malloc (heap);
+	} while (!ptr);
+
+	ST_Message ("  0x%x allocated for zone, ", heap);
+	ST_Message ("ZoneBase: 0x%X\n", (int)ptr);
+
+	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;
+}
+
+/*
+=============
+=
+= 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);
+}
+
+//=========================================================================
+//
+// 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;
+}
+
+//=========================================================================
+//=========================================================================
+// Hi-Res (mode 12) stuff
+//=========================================================================
+//=========================================================================
+
+
+//==========================================================================
+//
+// SetVideoModeHR - Set video mode to 640x480x16
+//
+//==========================================================================
+
+
+void SetVideoModeHR(void)
+{
+	union REGS regs;
+	regs.x.eax = 0x12;
+	int386(VID_INT, &regs, &regs);
+}
+
+
+//==========================================================================
+//
+// ClearScreenHR - Clear the screen to color 0
+//
+//==========================================================================
+
+void ClearScreenHR(void)
+{
+	BITPLANE(MASK_PLANE0|MASK_PLANE1|MASK_PLANE2|MASK_PLANE3);
+	memset((char *)0xa0000,0,38400);
+}
+
+
+//==========================================================================
+//
+// SlamHR - copy 4-plane buffer to screen
+//
+//==========================================================================
+
+void SlamHR(char *buffer)
+{
+	BITPLANE(MASK_PLANE0);
+	memcpy((char *)0xA0000, buffer+P0OFFSET, 38400);
+	BITPLANE(MASK_PLANE1);
+	memcpy((char *)0xA0000, buffer+P1OFFSET, 38400);
+	BITPLANE(MASK_PLANE2);
+	memcpy((char *)0xA0000, buffer+P2OFFSET, 38400);
+	BITPLANE(MASK_PLANE3);
+	memcpy((char *)0xA0000, buffer+P3OFFSET, 38400);
+}
+
+
+//==========================================================================
+//
+// SlamHR - copy 4-plane buffer to screen
+//
+// X and Width should be a multiple of 8
+// src should be 4 planes of block size, back to back
+//==========================================================================
+
+void SlamBlockHR(int x, int y, int w, int h, char *src)
+{
+	int srcwid = w>>3;
+	char *dest = ((char *)0xA0000) + (y*(640/8)) + (x>>3);
+	char *dst;
+	int i;
+
+	VB_SYNC;
+
+	BITPLANE(MASK_PLANE0);
+	dst = dest;
+	for ( i=0; i<h; i++ )
+	{
+		memcpy(dst, src, srcwid);
+		dst += 640/8;
+		src += srcwid;
+	}
+	BITPLANE(MASK_PLANE1);
+	dst = dest;
+	for ( i=0; i<h; i++ )
+	{
+		memcpy(dst, src, srcwid);
+		dst += 640/8;
+		src += srcwid;
+	}
+	BITPLANE(MASK_PLANE2);
+	dst = dest;
+	for ( i=0; i<h; i++ )
+	{
+		memcpy(dst, src, srcwid);
+		dst += 640/8;
+		src += srcwid;
+	}
+	BITPLANE(MASK_PLANE3);
+	dst = dest;
+	for ( i=0; i<h; i++ )
+	{
+		memcpy(dst, src, srcwid);
+		dst += 640/8;
+		src += srcwid;
+	}
+}
+
+//==========================================================================
+//
+// InitPaletteHR
+//
+//==========================================================================
+
+void InitPaletteHR(void)
+{
+	int i;
+	union REGS regs;
+
+	// Set palette registers to point into color registers
+	for ( i=0; i<16; i++ )
+	{
+		regs.x.eax = (0x10<<8)|0x00;
+		regs.x.ebx = (i<<8)|i;
+		int386(VID_INT, &regs, &regs);
+	}
+
+}
+
+
+//==========================================================================
+//
+// SetPaletteHR - Set the HR palette
+//
+//==========================================================================
+
+void SetPaletteHR(byte *palette)
+{
+	int i;
+	VB_SYNC;
+	outp(PEL_WRITE_ADR, 0);
+
+	for(i = 0; i < 16*3; i++)
+	{
+		outp(PEL_DATA, (*palette++));
+	}
+}
+
+
+//==========================================================================
+//
+// GetPaletteHR - Get the HR palette
+//
+//==========================================================================
+
+void GetPaletteHR(byte *palette)
+{
+	int i;
+	outp(PEL_READ_ADR, 0);
+	for (i=0; i<16*3; i++)
+	{
+		*palette++ = inp(PEL_DATA);
+	}
+}
+
+
+//==========================================================================
+//
+// FadeToPaletteHR
+//
+//==========================================================================
+
+void FadeToPaletteHR(byte *palette)
+{
+	int i,j;
+	int steps=140;          // two-seconds
+	byte basep[16*3];
+	byte work[16*3];
+	int delta;
+
+	GetPaletteHR(basep);
+	for(i = 0; i < steps; i++)
+	{
+		for(j = 0; j < 16*3; j++)
+		{
+			delta = palette[j]-basep[j];
+			work[j] = basep[j]+delta*i/steps;
+		}
+		SetPaletteHR(work);
+	}
+	SetPaletteHR(palette);
+}
+
+
+//==========================================================================
+//
+// FadeToBlackHR - Fades the palette out to black
+//
+//==========================================================================
+
+/*
+void FadeToBlackHR(void)
+{
+	char work[16*3];
+	char base[16*3];
+	int i,j,steps=70;
+
+	GetPaletteHR(base);
+	for (i=0; i<steps; i++)
+	{
+		for (j=0; j<16*3; j++)
+		{
+			work[j] = base[j]-(base[j]*i/steps);
+		}
+		VB_SYNC;
+		SetPaletteHR(work);
+	}
+	memset(work,0,16*3);
+	SetPaletteHR(work);
+}
+*/
+
+//==========================================================================
+//
+// BlackPaletteHR - Instantly blacks out the palette
+//
+//==========================================================================
+
+void BlackPaletteHR(void)
+{
+	char blackpal[16*3];
+
+	memset(blackpal,0,16*3);
+	SetPaletteHR(blackpal);
+}
+
+//==========================================================================
+//
+//
+// I_StartupReadKeys
+//
+//
+//==========================================================================
+
+void I_StartupReadKeys(void)
+{
+	int k;
+
+   while (kbdtail < kbdhead)
+   {
+	   k = keyboardque[kbdtail&(KBDQUESIZE-1)];
+	   kbdtail++;
+	   if (k == 1)
+		   I_Quit ();
+   }
+}
--- /dev/null
+++ b/src/hexen/i_ibm_a.asm
@@ -1,0 +1,135 @@
+	.386
+	.MODEL  small
+
+.DATA
+
+
+
+.CODE
+
+IF 0
+#define PEL_WRITE_ADR   0x3c8
+#define PEL_READ_ADR    0x3c7
+#define PEL_DATA                0x3c9
+ENDIF
+
+;================
+;
+; I_DivException
+;
+;================
+
+PROC  	I_DivException_
+PUBLIC 	I_DivException_
+	mov	edx,03c9h
+	mov	al,63
+	out	dx,al
+
+	mov	ebx,0ffffffh
+	mov	eax,[ebx]
+	retf
+ENDP
+
+;================
+;
+; I_SetDivException
+;
+;================
+
+PROC  	I_SetDivException_
+PUBLIC 	I_SetDivException_
+	pusha
+
+	mov	eax,0212h
+	mov	ebx,0
+	mov	ecx,cs
+	mov	edx,OFFSET I_DivException_
+	int 31h
+	jnc	good
+
+	popa
+	mov	eax,0
+	ret
+
+good:
+	popa
+	mov	eax,1
+	ret
+
+ENDP
+
+
+;================
+;
+; I_ReadJoystick
+;
+; Read the absolute joystick values
+; returns false if not connected
+;================
+
+.data
+
+_joystickx	dd	0
+_joysticky	dd	0
+PUBLIC	_joystickx, _joysticky
+
+.code
+
+PROC  	I_ReadJoystick_
+PUBLIC 	I_ReadJoystick_
+	pushad
+	pushf					; state of interrupt flag
+	cli
+
+	mov		dx,0201h
+	in		al,dx
+	out		dx,al		; Clear the resistors
+
+	mov		ah,1		; Get masks into registers
+	mov		ch,2
+
+	xor		esi,esi		; Clear count registers
+	xor		edi,edi
+	xor		ebx,ebx		; Clear high byte of bx for later
+
+	mov		ebp,10000	; joystick is disconnected if value is this big
+
+jloop:
+	in		al,dx		; Get bits indicating whether all are finished
+
+	dec		ebp			; Check bounding register
+	jz		bad			; We have a silly value - abort
+
+	mov		bl,al		; Duplicate the bits
+	and		bl,ah		; Mask off useless bits (in [xb])
+	add		esi,ebx		; Possibly increment count register
+	mov		cl,bl		; Save for testing later
+
+	mov		bl,al
+	and		bl,ch		; [yb]
+	add		edi,ebx
+
+	add		cl,bl
+	jnz		jloop 		; If both bits were 0, drop out
+
+done:
+	mov		[_joystickx],esi
+	shr		edi,1		; because 2s were added
+	mov		[_joysticky],edi
+
+	popf			; restore interrupt flag
+	popad
+	mov	eax,1		; read was ok
+	ret
+
+bad:
+	popf			; restore interrupt flag
+	popad
+	xor     eax, eax	; read was bad
+	ret
+
+ENDP
+
+
+END
+
--- /dev/null
+++ b/src/hexen/i_sound.c
@@ -1,0 +1,369 @@
+
+// I_SOUND.C
+
+#include <stdio.h>
+#include "h2def.h"
+#include "dmx.h"
+#include "sounds.h"
+#include "i_sound.h"
+
+/*
+===============
+=
+= I_StartupTimer
+=
+===============
+*/
+
+int tsm_ID = -1;
+
+void I_StartupTimer (void)
+{
+#ifndef NOTIMER
+	extern int I_TimerISR(void);
+
+	ST_Message("  I_StartupTimer()\n");
+	// 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) ST_Message("    MUS_Reg() returned %d\n", rc);
+#endif
+  return rc;
+}
+
+void I_UnRegisterSong(int handle)
+{
+  int rc = MUS_UnregisterSong(handle);
+#ifdef SNDDEBUG
+  if (rc < 0) ST_Message("    MUS_Unreg() returned %d\n", rc);
+#endif
+}
+
+int I_QrySongPlaying(int handle)
+{
+  int rc = MUS_QrySongPlaying(handle);
+#ifdef SNDDEBUG
+  if (rc < 0) ST_Message("    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) ST_Message("    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) ST_Message("    MUS_ChainSong() returned %d\n", rc);
+#endif
+  rc = MUS_PlaySong(handle, snd_MusicVolume);
+#ifdef SNDDEBUG
+  if (rc < 0) ST_Message("    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)
+{
+  return W_GetNumForName(sound->lumpname);
+
+}
+
+int I_StartSound (int id, void *data, int vol, int sep, int pitch, int priority)
+{
+	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_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()) ST_Message("    Dude.  The GUS ain't responding.\n");
+	else
+	{
+	  dmxlump = W_GetNumForName("dmxgus");
+	  GF1_SetMap(W_CacheLumpNum(dmxlump, PU_CACHE), lumpinfo[dmxlump].size);
+	}
+
+  }
+  if (sb)
+  {
+	if(debugmode)
+	{
+	  ST_Message("  Sound cfg p=0x%x, i=%d, d=%d\n",
+	  	snd_SBport, snd_SBirq, snd_SBdma);
+	}
+	if (SB_Detect(&snd_SBport, &snd_SBirq, &snd_SBdma, 0))
+	{
+	  ST_Message("    SB isn't responding at p=0x%x, i=%d, d=%d\n",
+	  	snd_SBport, snd_SBirq, snd_SBdma);
+	}
+	else SB_SetCard(snd_SBport, snd_SBirq, snd_SBdma);
+
+	if(debugmode)
+	{
+	  ST_Message("    SB_Detect returned p=0x%x, i=%d, d=%d\n",
+	  	snd_SBport, snd_SBirq, snd_SBdma);
+	}
+  }
+
+  if (adlib)
+  {
+	if (AL_Detect(&wait,0))
+	{
+	  	ST_Message("    Dude.  The Adlib isn't responding.\n");
+	}
+	else
+	{
+		AL_SetCard(wait, W_CacheLumpName("genmidi", PU_STATIC));
+	}
+  }
+
+  if (midi)
+  {
+	if (debugmode)
+	{
+		ST_Message("    cfg p=0x%x\n", snd_Mport);
+	}
+
+	if (MPU_Detect(&snd_Mport, &i))
+	{
+	  ST_Message("    The MPU-401 isn't reponding @ p=0x%x.\n", snd_Mport);
+	}
+	else MPU_SetCard(snd_Mport);
+  }
+
+}
+
+// inits all sound stuff
+
+void I_StartupSound (void)
+{
+  int rc, i;
+
+  if (debugmode)
+	ST_Message("I_StartupSound: Hope you hear a pop.\n");
+
+  // 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_MPU2] = AHW_MPU_401;
+  dmxCodes[snd_MPU3] = AHW_MPU_401;
+  dmxCodes[snd_AWE] = AHW_AWE32;
+  dmxCodes[snd_CDMUSIC] = 0;
+
+  // inits sound library timer stuff
+  I_StartupTimer();
+
+  // pick the sound cards i'm going to use
+  //
+  I_sndArbitrateCards();
+
+  if (debugmode)
+  {
+	ST_Message("    Music device #%d & dmxCode=%d,", snd_MusicDevice,
+	  dmxCodes[snd_MusicDevice]);
+	ST_Message(" Sfx device #%d & dmxCode=%d\n", snd_SfxDevice,
+	  dmxCodes[snd_SfxDevice]);
+  }
+
+  // inits DMX sound library
+  ST_Message("    Calling DMX_Init...");
+  rc = DMX_Init(SND_TICRATE, SND_MAXSONGS, dmxCodes[snd_MusicDevice],
+	dmxCodes[snd_SfxDevice]);
+
+  if (debugmode)
+  {
+	ST_Message(" DMX_Init() returned %d\n", rc);
+  }
+
+}
+
+// shuts down all sound stuff
+
+void I_ShutdownSound (void)
+{
+  DMX_DeInit();
+  I_ShutdownTimer();
+}
+
+void I_SetChannels(int channels)
+{
+  WAV_PlayMode(channels, SND_SAMPLERATE);
+}
--- /dev/null
+++ b/src/hexen/i_sound.h
@@ -1,0 +1,68 @@
+#ifndef __SOUND__
+#define __SOUND__
+
+#define SND_TICRATE             140             // tic rate for updating sound
+#define SND_MAXSONGS    40              // max number of songs in game
+#define SND_SAMPLERATE  11025   // sample rate of sound effects
+
+typedef enum
+{
+  snd_none,
+  snd_PC,
+  snd_Adlib,
+  snd_SB,
+  snd_PAS,
+  snd_GUS,
+  snd_MPU,
+  snd_MPU2,
+  snd_MPU3,
+  snd_AWE,
+  snd_CDMUSIC,
+  NUM_SCARDS
+} cardenum_t;
+
+void I_PauseSong(int handle);
+void I_ResumeSong(int handle);
+void I_SetMusicVolume(int volume);
+void I_SetSfxVolume(int volume);
+int I_RegisterSong(void *data);
+void I_UnRegisterSong(int handle);
+int I_QrySongPlaying(int handle);
+void I_StopSong(int handle);
+void I_PlaySong(int handle, int looping);
+int I_GetSfxLumpNum(sfxinfo_t *sound);
+int I_StartSound (int id, void *data, int vol, int sep, int pitch, int priority);
+void I_StopSound(int handle);
+int I_SoundIsPlaying(int handle);
+void I_UpdateSoundParams(int handle, int vol, int sep, int pitch);
+void I_sndArbitrateCards(void);
+void I_StartupSound (void);
+void I_ShutdownSound (void);
+void I_SetChannels(int channels);
+
+#endif
+
+#ifndef __ICDMUS__
+#define __ICDMUS__
+
+#define CDERR_NOTINSTALLED   10		// MSCDEX not installed
+#define CDERR_NOAUDIOSUPPORT 11		// CD-ROM Doesn't support audio
+#define CDERR_NOAUDIOTRACKS  12		// Current CD has no audio tracks
+#define CDERR_BADDRIVE       20		// Bad drive number
+#define CDERR_BADTRACK       21		// Bad track number
+#define CDERR_IOCTLBUFFMEM   22		// Not enough low memory for IOCTL
+#define CDERR_DEVREQBASE     100	// DevReq errors
+
+extern int cd_Error;
+
+int I_CDMusInit(void);
+int I_CDMusPlay(int track);
+int I_CDMusStop(void);
+int I_CDMusResume(void);
+int I_CDMusSetVolume(int volume);
+int I_CDMusFirstTrack(void);
+int I_CDMusLastTrack(void);
+int I_CDMusTrackLength(int track);
+
+#endif
+
--- /dev/null
+++ b/src/hexen/in_lude.c
@@ -1,0 +1,597 @@
+
+//**************************************************************************
+//**
+//** in_lude.c : Heretic 2 : Raven Software, Corp.
+//**
+//** $RCSfile: in_lude.c,v $
+//** $Revision: 1.19 $
+//** $Date: 96/01/05 23:33:19 $
+//** $Author: bgokey $
+//**
+//**************************************************************************
+
+#include "h2def.h"
+#include <ctype.h>
+
+// MACROS ------------------------------------------------------------------
+
+#define	TEXTSPEED 3
+#define	TEXTWAIT 140
+
+// TYPES -------------------------------------------------------------------
+
+typedef enum
+{
+	SINGLE,
+	COOPERATIVE,
+	DEATHMATCH
+} gametype_t;
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+void IN_Start(void);
+void IN_Ticker(void);
+void IN_Drawer(void);
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+static void WaitStop(void);
+static void Stop(void);
+static void LoadPics(void);
+static void UnloadPics(void);
+static void CheckForSkip(void);
+static void InitStats(void);
+static void DrDeathTally(void);
+static void DrNumber(int val, int x, int y, int wrapThresh);
+static void DrNumberBold(int val, int x, int y, int wrapThresh);
+static void DrawHubText(void);
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DECLARATIONS ------------------------------------------------
+
+boolean intermission;
+char ClusterMessage[MAX_INTRMSN_MESSAGE_SIZE];
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+static boolean skipintermission;
+static int interstate = 0;
+static int intertime = -1;
+static gametype_t gametype;
+static int cnt;
+static int slaughterboy; // in DM, the player with the most kills
+static patch_t *patchINTERPIC;
+static patch_t *FontBNumbers[10];
+static patch_t *FontBNegative;
+static patch_t *FontBSlash;
+static patch_t *FontBPercent;
+static int FontABaseLump;
+static int FontBLump;
+static int FontBLumpBase;
+
+static signed int totalFrags[MAXPLAYERS];
+
+static int HubCount;
+static char *HubText;
+
+// CODE --------------------------------------------------------------------
+
+//========================================================================
+//
+// IN_Start
+//
+//========================================================================
+
+extern void AM_Stop (void);
+
+void IN_Start(void)
+{
+	int i;
+	I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE));
+	InitStats();
+	LoadPics();
+	intermission = true;
+	interstate = 0;
+	skipintermission = false;
+	intertime = 0;
+	AM_Stop();
+	for(i = 0; i < MAXPLAYERS; i++)
+	{
+		players[i].messageTics = 0;
+		players[i].message[0] = 0;
+	}
+	SN_StopAllSequences();	
+}
+
+//========================================================================
+//
+// WaitStop
+//
+//========================================================================
+
+void WaitStop(void)
+{
+	if(!--cnt)
+	{
+		Stop();
+//		gamestate = GS_LEVEL;
+//		G_DoLoadLevel();
+		gameaction = ga_leavemap;
+//		G_WorldDone();
+	}
+}
+
+//========================================================================
+//
+// Stop
+//
+//========================================================================
+
+static void Stop(void)
+{
+	intermission = false;
+	UnloadPics();
+	SB_state = -1;
+	BorderNeedRefresh = true;
+}
+
+//========================================================================
+//
+// InitStats
+//
+// 	Initializes the stats for single player mode
+//========================================================================
+
+static char *ClusMsgLumpNames[] =
+{
+	"clus1msg",
+	"clus2msg",
+	"clus3msg",
+	"clus4msg",
+	"clus5msg"
+};
+
+static void InitStats(void)
+{
+	int i;
+	int j;
+	int oldCluster;
+	signed int slaughterfrags;
+	int posnum;
+	int slaughtercount;
+	int playercount;
+	char *msgLumpName;
+	int msgSize;
+	int msgLump;
+
+	extern int LeaveMap;
+
+	if(!deathmatch)
+	{
+		gametype = SINGLE;
+		HubCount = 0;
+		oldCluster = P_GetMapCluster(gamemap);
+		if(oldCluster != P_GetMapCluster(LeaveMap))
+		{
+			if(oldCluster >= 1 && oldCluster <= 5)
+			{
+				msgLumpName = ClusMsgLumpNames[oldCluster-1];
+				msgLump = W_GetNumForName(msgLumpName);
+				msgSize = W_LumpLength(msgLump);
+				if(msgSize >= MAX_INTRMSN_MESSAGE_SIZE)
+				{
+					I_Error("Cluster message too long (%s)", msgLumpName);
+				}
+				W_ReadLump(msgLump, ClusterMessage);
+				ClusterMessage[msgSize] = 0; // Append terminator
+				HubText = ClusterMessage;
+				HubCount = strlen(HubText)*TEXTSPEED+TEXTWAIT;
+				S_StartSongName("hub", true);
+			}
+		}
+	}
+	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];
+					}
+				}
+				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;
+		}
+		S_StartSongName("hub", true);
+	}
+}
+
+//========================================================================
+//
+// LoadPics
+//
+//========================================================================
+
+static void LoadPics(void)
+{
+	int i;
+
+	if(HubCount || gametype == DEATHMATCH)
+	{
+		patchINTERPIC = W_CacheLumpName("INTERPIC", PU_STATIC);
+		FontBLumpBase = W_GetNumForName("FONTB16");
+		for(i=0; i<10; i++)
+		{
+			FontBNumbers[i] = W_CacheLumpNum(FontBLumpBase+i, PU_STATIC);
+		}
+		FontBLump = W_GetNumForName("FONTB_S")+1;
+		FontBNegative = W_CacheLumpName("FONTB13", PU_STATIC);
+		FontABaseLump = W_GetNumForName("FONTA_S")+1;
+	
+		FontBSlash = W_CacheLumpName("FONTB15", PU_STATIC);
+		FontBPercent = W_CacheLumpName("FONTB05", PU_STATIC);
+	}
+}
+
+//========================================================================
+//
+// UnloadPics
+//
+//========================================================================
+
+static void UnloadPics(void)
+{
+	int i;
+
+	if(HubCount || gametype == DEATHMATCH)
+	{
+		Z_ChangeTag(patchINTERPIC, PU_CACHE);
+		for(i=0; i<10; i++)
+		{
+			Z_ChangeTag(FontBNumbers[i], PU_CACHE);
+		}
+		Z_ChangeTag(FontBNegative, PU_CACHE);
+		Z_ChangeTag(FontBSlash, PU_CACHE);
+		Z_ChangeTag(FontBPercent, PU_CACHE);
+	}
+}
+
+//========================================================================
+//
+// IN_Ticker
+//
+//========================================================================
+
+void IN_Ticker(void)
+{
+	if(!intermission)
+	{
+		return;
+	}
+	if(interstate)
+	{
+		WaitStop();
+		return;
+	}
+	skipintermission = false;
+	CheckForSkip();
+	intertime++;
+	if(skipintermission || (gametype == SINGLE && !HubCount))
+	{
+		interstate = 1;
+		cnt = 10;
+		skipintermission = false;
+		//S_StartSound(NULL, sfx_dorcls);
+	}
+}
+
+//========================================================================
+//
+// CheckForSkip
+//
+// 	Check to see if any player hit a key
+//========================================================================
+
+static void CheckForSkip(void)
+{
+  	int i;
+	player_t *player;
+	static boolean triedToSkip;
+
+  	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;
+			}
+		}
+	}
+	if(deathmatch && intertime < 140)
+	{ // wait for 4 seconds before allowing a skip
+		if(skipintermission == 1)
+		{
+			triedToSkip = true;
+			skipintermission = 0;
+		}
+	}
+	else
+	{
+		if(triedToSkip)
+		{
+			skipintermission = 1;
+			triedToSkip = false;
+		}
+	}
+}
+
+//========================================================================
+//
+// IN_Drawer
+//
+//========================================================================
+
+void IN_Drawer(void)
+{
+	if(!intermission)
+	{
+		return;
+	}
+	if(interstate)
+	{
+		return;
+	}
+	UpdateState |= I_FULLSCRN;
+	memcpy(screen, (byte *)patchINTERPIC, SCREENWIDTH*SCREENHEIGHT);
+
+	if(gametype == SINGLE)
+	{
+		if(HubCount)
+		{
+			DrawHubText();
+		}
+	}
+	else
+	{
+		DrDeathTally();
+	}
+}
+
+//========================================================================
+//
+// DrDeathTally
+//
+//========================================================================
+
+#define TALLY_EFFECT_TICKS 20
+#define TALLY_FINAL_X_DELTA (23*FRACUNIT)
+#define TALLY_FINAL_Y_DELTA (13*FRACUNIT)
+#define TALLY_START_XPOS (178*FRACUNIT)
+#define TALLY_STOP_XPOS (90*FRACUNIT)
+#define TALLY_START_YPOS (132*FRACUNIT)
+#define TALLY_STOP_YPOS (83*FRACUNIT)
+#define TALLY_TOP_X 85
+#define TALLY_TOP_Y 9
+#define TALLY_LEFT_X 7
+#define TALLY_LEFT_Y 71
+#define TALLY_TOTALS_X 291
+
+static void DrDeathTally(void)
+{
+	int i, j;
+	fixed_t xPos, yPos;
+	fixed_t xDelta, yDelta;
+	fixed_t xStart, scale;
+	int x, y;
+	boolean bold;
+	static boolean showTotals;
+	int temp;
+
+	V_DrawPatch(TALLY_TOP_X, TALLY_TOP_Y,
+		W_CacheLumpName("tallytop", PU_CACHE));
+	V_DrawPatch(TALLY_LEFT_X, TALLY_LEFT_Y,
+		W_CacheLumpName("tallylft", PU_CACHE));
+	if(intertime < TALLY_EFFECT_TICKS)
+	{
+		showTotals = false;
+		scale = (intertime*FRACUNIT)/TALLY_EFFECT_TICKS;
+		xDelta = FixedMul(scale, TALLY_FINAL_X_DELTA);
+		yDelta = FixedMul(scale, TALLY_FINAL_Y_DELTA);
+		xStart = TALLY_START_XPOS-FixedMul(scale,
+			TALLY_START_XPOS-TALLY_STOP_XPOS);
+		yPos = TALLY_START_YPOS-FixedMul(scale,
+			TALLY_START_YPOS-TALLY_STOP_YPOS);
+	}
+	else
+	{
+		xDelta = TALLY_FINAL_X_DELTA;
+		yDelta = TALLY_FINAL_Y_DELTA;
+		xStart = TALLY_STOP_XPOS;
+		yPos = TALLY_STOP_YPOS;
+	}
+	if(intertime >= TALLY_EFFECT_TICKS && showTotals == false)
+	{
+		showTotals = true;
+		S_StartSound(NULL, SFX_PLATFORM_STOP);
+	}
+	y = yPos>>FRACBITS;
+	for(i = 0; i < MAXPLAYERS; i++)
+	{
+		xPos = xStart;
+		for(j = 0; j < MAXPLAYERS; j++, xPos += xDelta)
+		{
+			x = xPos>>FRACBITS;
+			bold = (i == consoleplayer || j == consoleplayer);
+			if(playeringame[i] && playeringame[j])
+			{
+				if(bold)
+				{
+					DrNumberBold(players[i].frags[j], x, y, 100);
+				}
+				else
+				{
+					DrNumber(players[i].frags[j], x, y, 100);
+				}
+			}
+			else
+			{
+				temp = MN_TextAWidth("--")/2;
+				if(bold)
+				{
+					MN_DrTextAYellow("--", x-temp, y);
+				}
+				else
+				{
+					MN_DrTextA("--", x-temp, y);
+				}
+			}
+		}
+		if(showTotals && playeringame[i]
+			&& !((slaughterboy&(1<<i)) && !(intertime&16)))
+		{
+			DrNumber(totalFrags[i], TALLY_TOTALS_X, y, 1000);
+		}
+		yPos += yDelta;
+		y = yPos>>FRACBITS;
+	}
+}
+
+//==========================================================================
+//
+// DrNumber
+//
+//==========================================================================
+
+static void DrNumber(int val, int x, int y, int wrapThresh)
+{
+	char buff[8] = "XX";
+
+	if(!(val < -9 && wrapThresh < 1000))
+	{
+		sprintf(buff, "%d", val >= wrapThresh ? val%wrapThresh : val);
+	}
+	MN_DrTextA(buff, x-MN_TextAWidth(buff)/2, y);
+}
+
+//==========================================================================
+//
+// DrNumberBold
+//
+//==========================================================================
+
+static void DrNumberBold(int val, int x, int y, int wrapThresh)
+{
+	char buff[8] = "XX";
+
+	if(!(val < -9 && wrapThresh < 1000))
+	{
+		sprintf(buff, "%d", val >= wrapThresh ? val%wrapThresh : val);
+	}
+	MN_DrTextAYellow(buff, x-MN_TextAWidth(buff)/2, y);
+}
+
+//===========================================================================
+//
+// DrawHubText
+//
+//===========================================================================
+
+static void DrawHubText(void)
+{
+	int		count;
+	char	*ch;
+	int		c;
+	int		cx, cy;
+	patch_t *w;
+
+	cy = 5;
+	cx = 10;
+	ch = HubText;
+	count = (intertime-10)/TEXTSPEED;
+	if (count < 0)
+	{
+		count = 0;
+	}
+	for(; count; count--)
+	{
+		c = *ch++;
+		if(!c)
+		{
+			break;
+		}
+		if(c == '\n')
+		{
+			cx = 10;
+			cy += 9;
+			continue;
+		}
+		if(c < 32)
+		{
+			continue;
+		}
+		c = toupper(c);
+		if(c == 32)
+		{
+			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;
+	}
+}
--- /dev/null
+++ b/src/hexen/info.c
@@ -1,0 +1,13862 @@
+#include "h2def.h"
+// generated by stateco
+
+char *sprnames[NUMSPRITES] = {
+"MAN1","ACLO","TLGL","FBL1","XPL1","ARRW","DART","RIPP","CFCF","BLAD",
+"SHRD","FFSM","FFLG","PTN1","PTN2","SOAR","INVU","SUMN","TSPK","TELO",
+"TRNG","ROCK","FOGS","FOGM","FOGL","SGSA","SGSB","PORK","EGGM","FHFX",
+"SPHL","STWN","GMPD","ASKU","ABGM","AGMR","AGMG","AGG2","AGMB","AGB2",
+"ABK1","ABK2","ASK2","AFWP","ACWP","AMWP","AGER","AGR2","AGR3","AGR4",
+"TRCH","PSBG","ATLP","THRW","SPED","BMAN","BRAC","BLST","HRAD","SPSH",
+"LVAS","SLDG","STTW","RCK1","RCK2","RCK3","RCK4","CDLR","TRE1","TRDT",
+"TRE2","TRE3","STM1","STM2","STM3","STM4","MSH1","MSH2","MSH3","MSH4",
+"MSH5","MSH6","MSH7","MSH8","SGMP","SGM1","SGM2","SGM3","SLC1","SLC2",
+"SLC3","MSS1","MSS2","SWMV","CPS1","CPS2","TMS1","TMS2","TMS3","TMS4",
+"TMS5","TMS6","TMS7","CPS3","STT2","STT3","STT4","STT5","GAR1","GAR2",
+"GAR3","GAR4","GAR5","GAR6","GAR7","GAR8","GAR9","BNR1","TRE4","TRE5",
+"TRE6","TRE7","LOGG","ICT1","ICT2","ICT3","ICT4","ICM1","ICM2","ICM3",
+"ICM4","RKBL","RKBS","RKBK","RBL1","RBL2","RBL3","VASE","POT1","POT2",
+"POT3","PBIT","CPS4","CPS5","CPS6","CPB1","CPB2","CPB3","CPB4","BDRP",
+"BDSH","BDPL","CNDL","LEF1","LEF3","LEF2","TWTR","WLTR","BARL","SHB1",
+"SHB2","BCKT","SHRM","FBUL","FSKL","BRTR","SUIT","BBLL","CAND","IRON",
+"XMAS","CDRN","CHNS","TST1","TST2","TST3","TST4","TST5","TST6","TST7",
+"TST8","TST9","TST0","TELE","TSMK","FPCH","WFAX","FAXE","WFHM","FHMR",
+"FSRD","FSFX","CMCE","WCSS","CSSF","WCFM","CFLM","CFFX","CHLY","SPIR",
+"MWND","WMLG","MLNG","MLFX","MLF2","MSTF","MSP1","MSP2","WFR1","WFR2",
+"WFR3","WCH1","WCH2","WCH3","WMS1","WMS2","WMS3","WPIG","WMCS","CONE",
+"SHEX","BLOD","GIBS","PLAY","FDTH","BSKL","ICEC","CLER","MAGE","PIGY",
+"CENT","CTXD","CTFX","CTDP","DEMN","DEMA","DEMB","DEMC","DEMD","DEME",
+"DMFX","DEM2","DMBA","DMBB","DMBC","DMBD","DMBE","D2FX","WRTH","WRT2",
+"WRBL","MNTR","FX12","FX13","MNSM","SSPT","SSDV","SSXD","SSFX","BISH",
+"BPFX","DRAG","DRFX","ARM1","ARM2","ARM3","ARM4","MAN2","MAN3","KEY1",
+"KEY2","KEY3","KEY4","KEY5","KEY6","KEY7","KEY8","KEY9","KEYA","KEYB",
+"ETTN","ETTB","FDMN","FDMB","ICEY","ICPR","ICWS","SORC","SBMP","SBS4",
+"SBMB","SBS3","SBMG","SBS1","SBS2","SBFX","RADE","WATR","KORX","ABAT"
+};
+
+void A_FreeTargMobj ();
+void A_FlameCheck ();
+void A_HideThing ();
+void A_UnHideThing ();
+void A_RestoreSpecialThing1 ();
+void A_RestoreSpecialThing2 ();
+void A_RestoreArtifact ();
+void A_Summon ();
+void A_ThrustInitUp ();
+void A_ThrustInitDn ();
+void A_ThrustRaise ();
+void A_ThrustBlock ();
+void A_ThrustImpale ();
+void A_ThrustLower ();
+void A_TeloSpawnC ();
+void A_TeloSpawnB ();
+void A_TeloSpawnA ();
+void A_TeloSpawnD ();
+void A_CheckTeleRing ();
+void A_FogSpawn ();
+void A_FogMove ();
+void A_Quake ();
+void A_ContMobjSound ();
+void A_Scream ();
+void A_Explode ();
+void A_PoisonBagInit ();
+void A_PoisonBagDamage ();
+void A_PoisonBagCheck ();
+void A_CheckThrowBomb ();
+void A_NoGravity ();
+void A_PotteryExplode ();
+void A_PotteryChooseBit ();
+void A_PotteryCheck ();
+void A_CorpseBloodDrip ();
+void A_CorpseExplode ();
+void A_LeafSpawn ();
+void A_LeafThrust ();
+void A_LeafCheck ();
+void A_BridgeInit ();
+void A_BridgeOrbit ();
+void A_TreeDeath ();
+void A_PoisonShroom ();
+void A_Pain ();
+void A_SoAExplode ();
+void A_BellReset1 ();
+void A_BellReset2 ();
+void A_NoBlocking ();
+void A_Light0 ();
+void A_WeaponReady ();
+void A_Lower ();
+void A_Raise ();
+void A_FPunchAttack ();
+void A_ReFire ();
+void A_FAxeAttack ();
+void A_FHammerAttack ();
+void A_FHammerThrow ();
+void A_FSwordAttack ();
+void A_FSwordFlames ();
+void A_CMaceAttack ();
+void A_CStaffInitBlink ();
+void A_CStaffCheckBlink ();
+void A_CStaffCheck ();
+void A_CStaffAttack ();
+void A_CStaffMissileSlither ();
+void A_CFlameAttack ();
+void A_CFlameRotate ();
+void A_CFlamePuff ();
+void A_CFlameMissile ();
+void A_CHolyAttack ();
+void A_CHolyPalette ();
+void A_CHolySeek ();
+void A_CHolyCheckScream ();
+void A_CHolyTail ();
+void A_CHolySpawnPuff ();
+void A_CHolyAttack2 ();
+void A_MWandAttack ();
+void A_LightningReady ();
+void A_MLightningAttack ();
+void A_LightningZap ();
+void A_LightningClip ();
+void A_LightningRemove ();
+void A_LastZap ();
+void A_ZapMimic ();
+void A_MStaffAttack ();
+void A_MStaffPalette ();
+void A_MStaffWeave ();
+void A_MStaffTrack ();
+void A_SnoutAttack ();
+void A_FireConePL1 ();
+void A_ShedShard ();
+void A_AddPlayerCorpse ();
+void A_SkullPop ();
+void A_FreezeDeath ();
+void A_FreezeDeathChunks ();
+void A_CheckBurnGone ();
+void A_CheckSkullFloor ();
+void A_CheckSkullDone ();
+void A_SpeedFade ();
+void A_IceSetTics ();
+void A_IceCheckHeadDone ();
+void A_PigPain ();
+void A_PigLook ();
+void A_PigChase ();
+void A_FaceTarget ();
+void A_PigAttack ();
+void A_QueueCorpse ();
+void A_Look ();
+void A_Chase ();
+void A_CentaurAttack ();
+void A_CentaurAttack2 ();
+void A_SetReflective ();
+void A_CentaurDefend ();
+void A_UnSetReflective ();
+void A_CentaurDropStuff ();
+void A_CheckFloor ();
+void A_DemonAttack1 ();
+void A_DemonAttack2 ();
+void A_DemonDeath ();
+void A_Demon2Death ();
+void A_WraithRaiseInit ();
+void A_WraithRaise ();
+void A_WraithInit ();
+void A_WraithLook ();
+void A_WraithChase ();
+void A_WraithFX3 ();
+void A_WraithMelee ();
+void A_WraithMissile ();
+void A_WraithFX2 ();
+void A_MinotaurFade1 ();
+void A_MinotaurFade2 ();
+void A_MinotaurLook ();
+void A_MinotaurChase ();
+void A_MinotaurRoam ();
+void A_MinotaurAtk1 ();
+void A_MinotaurDecide ();
+void A_MinotaurAtk2 ();
+void A_MinotaurAtk3 ();
+void A_MinotaurCharge ();
+void A_SmokePuffExit ();
+void A_MinotaurFade0 ();
+void A_MntrFloorFire ();
+void A_SerpentChase ();
+void A_SerpentHumpDecide ();
+void A_SerpentUnHide ();
+void A_SerpentRaiseHump ();
+void A_SerpentLowerHump ();
+void A_SerpentHide ();
+void A_SerpentBirthScream ();
+void A_SetShootable ();
+void A_SerpentCheckForAttack ();
+void A_UnSetShootable ();
+void A_SerpentDiveSound ();
+void A_SerpentWalk ();
+void A_SerpentChooseAttack ();
+void A_SerpentMeleeAttack ();
+void A_SerpentMissileAttack ();
+void A_SerpentHeadPop ();
+void A_SerpentSpawnGibs ();
+void A_SerpentHeadCheck ();
+void A_FloatGib ();
+void A_DelayGib ();
+void A_SinkGib ();
+void A_BishopDecide ();
+void A_BishopDoBlur ();
+void A_BishopSpawnBlur ();
+void A_BishopChase ();
+void A_BishopAttack ();
+void A_BishopAttack2 ();
+void A_BishopPainBlur ();
+void A_BishopPuff ();
+void A_SetAltShadow ();
+void A_BishopMissileWeave ();
+void A_BishopMissileSeek ();
+void A_DragonInitFlight ();
+void A_DragonFlap ();
+void A_DragonFlight ();
+void A_DragonAttack ();
+void A_DragonPain ();
+void A_DragonCheckCrash ();
+void A_DragonFX2 ();
+void A_ESound ();
+void A_EttinAttack ();
+void A_DropMace ();
+void A_FiredRocks ();
+void A_UnSetInvulnerable ();
+void A_FiredChase ();
+void A_FiredAttack ();
+void A_FiredSplotch ();
+void A_SmBounce ();
+void A_IceGuyLook ();
+void A_IceGuyChase ();
+void A_IceGuyAttack ();
+void A_IceGuyDie ();
+void A_IceGuyMissilePuff ();
+void A_IceGuyMissileExplode ();
+void A_ClassBossHealth ();
+void A_FastChase ();
+void A_FighterAttack ();
+void A_ClericAttack ();
+void A_MageAttack ();
+void A_SorcSpinBalls ();
+void A_SpeedBalls ();
+void A_SpawnFizzle ();
+void A_SorcBossAttack ();
+void A_SorcBallOrbit ();
+void A_SorcBallPop ();
+void A_BounceCheck ();
+void A_SorcFX1Seek ();
+void A_SorcFX2Split ();
+void A_SorcFX2Orbit ();
+void A_SorcererBishopEntry ();
+void A_SpawnBishop ();
+void A_SorcFX4Check ();
+void A_KoraxStep2 ();
+void A_KoraxChase ();
+void A_KoraxStep ();
+void A_KoraxDecide ();
+void A_KoraxMissile ();
+void A_KoraxCommand ();
+void A_KoraxBonePop ();
+void A_KSpiritRoam ();
+void A_KBoltRaise ();
+void A_KBolt ();
+void A_BatSpawnInit ();
+void A_BatSpawn ();
+void A_BatMove ();
+
+state_t	states[NUMSTATES] = {
+{SPR_MAN1,0,-1,NULL,S_NULL,0,0},	// S_NULL
+{SPR_ACLO,4,1050,A_FreeTargMobj,S_NULL,0,0},	// S_FREETARGMOBJ
+{SPR_TLGL,0,-1,NULL,S_NULL,0,0},	// S_MAPSPOT
+{SPR_FBL1,32768,4,NULL,S_FIREBALL1_2,0,0},	// S_FIREBALL1_1
+{SPR_FBL1,32769,4,NULL,S_FIREBALL1_1,0,0},	// S_FIREBALL1_2
+{SPR_XPL1,32768,4,NULL,S_FIREBALL1_X2,0,0},	// S_FIREBALL1_X1
+{SPR_XPL1,32769,4,NULL,S_FIREBALL1_X3,0,0},	// S_FIREBALL1_X2
+{SPR_XPL1,32770,4,NULL,S_FIREBALL1_X4,0,0},	// S_FIREBALL1_X3
+{SPR_XPL1,32771,4,NULL,S_FIREBALL1_X5,0,0},	// S_FIREBALL1_X4
+{SPR_XPL1,32772,4,NULL,S_FIREBALL1_X6,0,0},	// S_FIREBALL1_X5
+{SPR_XPL1,32773,4,NULL,S_NULL,0,0},	// S_FIREBALL1_X6
+{SPR_ARRW,0,-1,NULL,S_NULL,0,0},	// S_ARROW_1
+{SPR_ARRW,0,1,NULL,S_NULL,0,0},	// S_ARROW_X1
+{SPR_DART,0,-1,NULL,S_NULL,0,0},	// S_DART_1
+{SPR_DART,0,1,NULL,S_NULL,0,0},	// S_DART_X1
+{SPR_DART,0,-1,NULL,S_NULL,0,0},	// S_POISONDART_1
+{SPR_DART,0,1,NULL,S_NULL,0,0},	// S_POISONDART_X1
+{SPR_RIPP,0,3,NULL,S_RIPPERBALL_2,0,0},	// S_RIPPERBALL_1
+{SPR_RIPP,1,3,NULL,S_RIPPERBALL_3,0,0},	// S_RIPPERBALL_2
+{SPR_RIPP,2,3,NULL,S_RIPPERBALL_1,0,0},	// S_RIPPERBALL_3
+{SPR_CFCF,32784,4,NULL,S_RIPPERBALL_X2,0,0},	// S_RIPPERBALL_X1
+{SPR_CFCF,32785,3,NULL,S_RIPPERBALL_X3,0,0},	// S_RIPPERBALL_X2
+{SPR_CFCF,32786,4,NULL,S_RIPPERBALL_X4,0,0},	// S_RIPPERBALL_X3
+{SPR_CFCF,32787,3,NULL,S_RIPPERBALL_X5,0,0},	// S_RIPPERBALL_X4
+{SPR_CFCF,32788,4,NULL,S_RIPPERBALL_X6,0,0},	// S_RIPPERBALL_X5
+{SPR_CFCF,32789,3,NULL,S_RIPPERBALL_X7,0,0},	// S_RIPPERBALL_X6
+{SPR_CFCF,32790,4,NULL,S_RIPPERBALL_X8,0,0},	// S_RIPPERBALL_X7
+{SPR_CFCF,32791,3,NULL,S_RIPPERBALL_X9,0,0},	// S_RIPPERBALL_X8
+{SPR_CFCF,32792,4,NULL,S_RIPPERBALL_X10,0,0},	// S_RIPPERBALL_X9
+{SPR_CFCF,32793,3,NULL,S_NULL,0,0},	// S_RIPPERBALL_X10
+{SPR_BLAD,0,-1,NULL,S_NULL,0,0},	// S_PRJ_BLADE1
+{SPR_BLAD,0,1,NULL,S_NULL,0,0},	// S_PRJ_BLADE_X1
+{SPR_SHRD,32768,3,NULL,S_ICESHARD2,0,0},	// S_ICESHARD1
+{SPR_SHRD,32769,3,NULL,S_ICESHARD3,0,0},	// S_ICESHARD2
+{SPR_SHRD,32770,3,NULL,S_ICESHARD1,0,0},	// S_ICESHARD3
+{SPR_FFSM,32768,3,NULL,S_FLAME_TSMALL2,0,0},	// S_FLAME_TSMALL1
+{SPR_FFSM,32769,3,NULL,S_FLAME_TSMALL3,0,0},	// S_FLAME_TSMALL2
+{SPR_FFSM,32770,2,A_FlameCheck,S_FLAME_TSMALL4,0,0},	// S_FLAME_TSMALL3
+{SPR_FFSM,32770,2,NULL,S_FLAME_TSMALL5,0,0},	// S_FLAME_TSMALL4
+{SPR_FFSM,32771,3,NULL,S_FLAME_TSMALL6,0,0},	// S_FLAME_TSMALL5
+{SPR_FFSM,32772,3,A_FlameCheck,S_FLAME_TSMALL1,0,0},	// S_FLAME_TSMALL6
+{SPR_FFLG,32768,4,NULL,S_FLAME_TLARGE2,0,0},	// S_FLAME_TLARGE1
+{SPR_FFLG,32769,4,A_FlameCheck,S_FLAME_TLARGE3,0,0},	// S_FLAME_TLARGE2
+{SPR_FFLG,32770,4,NULL,S_FLAME_TLARGE4,0,0},	// S_FLAME_TLARGE3
+{SPR_FFLG,32771,4,A_FlameCheck,S_FLAME_TLARGE5,0,0},	// S_FLAME_TLARGE4
+{SPR_FFLG,32772,4,NULL,S_FLAME_TLARGE6,0,0},	// S_FLAME_TLARGE5
+{SPR_FFLG,32773,4,A_FlameCheck,S_FLAME_TLARGE7,0,0},	// S_FLAME_TLARGE6
+{SPR_FFLG,32774,4,NULL,S_FLAME_TLARGE8,0,0},	// S_FLAME_TLARGE7
+{SPR_FFLG,32775,4,A_FlameCheck,S_FLAME_TLARGE9,0,0},	// S_FLAME_TLARGE8
+{SPR_FFLG,32776,4,NULL,S_FLAME_TLARGE10,0,0},	// S_FLAME_TLARGE9
+{SPR_FFLG,32777,4,A_FlameCheck,S_FLAME_TLARGE11,0,0},	// S_FLAME_TLARGE10
+{SPR_FFLG,32778,4,NULL,S_FLAME_TLARGE12,0,0},	// S_FLAME_TLARGE11
+{SPR_FFLG,32779,4,A_FlameCheck,S_FLAME_TLARGE13,0,0},	// S_FLAME_TLARGE12
+{SPR_FFLG,32780,4,NULL,S_FLAME_TLARGE14,0,0},	// S_FLAME_TLARGE13
+{SPR_FFLG,32781,4,A_FlameCheck,S_FLAME_TLARGE15,0,0},	// S_FLAME_TLARGE14
+{SPR_FFLG,32782,4,NULL,S_FLAME_TLARGE16,0,0},	// S_FLAME_TLARGE15
+{SPR_FFLG,32783,4,A_FlameCheck,S_FLAME_TLARGE5,0,0},	// S_FLAME_TLARGE16
+{SPR_FFSM,0,2,NULL,S_FLAME_SDORM2,0,0},	// S_FLAME_SDORM1
+{SPR_FFSM,1,2,A_HideThing,S_FLAME_SDORM3,0,0},	// S_FLAME_SDORM2
+{SPR_FFSM,2,200,NULL,S_FLAME_SDORM3,0,0},	// S_FLAME_SDORM3
+{SPR_FFSM,32768,3,NULL,S_FLAME_SMALL2,0,0},	// S_FLAME_SMALL1
+{SPR_FFSM,32768,3,A_UnHideThing,S_FLAME_SMALL3,0,0},	// S_FLAME_SMALL2
+{SPR_FFSM,32768,3,NULL,S_FLAME_SMALL4,0,0},	// S_FLAME_SMALL3
+{SPR_FFSM,32769,3,NULL,S_FLAME_SMALL5,0,0},	// S_FLAME_SMALL4
+{SPR_FFSM,32770,3,NULL,S_FLAME_SMALL6,0,0},	// S_FLAME_SMALL5
+{SPR_FFSM,32771,3,NULL,S_FLAME_SMALL7,0,0},	// S_FLAME_SMALL6
+{SPR_FFSM,32772,3,NULL,S_FLAME_SMALL3,0,0},	// S_FLAME_SMALL7
+{SPR_FFLG,3,2,NULL,S_FLAME_LDORM2,0,0},	// S_FLAME_LDORM1
+{SPR_FFLG,2,2,NULL,S_FLAME_LDORM3,0,0},	// S_FLAME_LDORM2
+{SPR_FFLG,1,2,NULL,S_FLAME_LDORM4,0,0},	// S_FLAME_LDORM3
+{SPR_FFLG,0,2,A_HideThing,S_FLAME_LDORM5,0,0},	// S_FLAME_LDORM4
+{SPR_FFLG,0,200,NULL,S_FLAME_LDORM5,0,0},	// S_FLAME_LDORM5
+{SPR_FFLG,32768,2,NULL,S_FLAME_LARGE2,0,0},	// S_FLAME_LARGE1
+{SPR_FFLG,32768,2,A_UnHideThing,S_FLAME_LARGE3,0,0},	// S_FLAME_LARGE2
+{SPR_FFLG,32768,4,NULL,S_FLAME_LARGE4,0,0},	// S_FLAME_LARGE3
+{SPR_FFLG,32769,4,NULL,S_FLAME_LARGE5,0,0},	// S_FLAME_LARGE4
+{SPR_FFLG,32770,4,NULL,S_FLAME_LARGE6,0,0},	// S_FLAME_LARGE5
+{SPR_FFLG,32771,4,NULL,S_FLAME_LARGE7,0,0},	// S_FLAME_LARGE6
+{SPR_FFLG,32772,4,NULL,S_FLAME_LARGE8,0,0},	// S_FLAME_LARGE7
+{SPR_FFLG,32773,4,NULL,S_FLAME_LARGE9,0,0},	// S_FLAME_LARGE8
+{SPR_FFLG,32774,4,NULL,S_FLAME_LARGE10,0,0},	// S_FLAME_LARGE9
+{SPR_FFLG,32775,4,NULL,S_FLAME_LARGE11,0,0},	// S_FLAME_LARGE10
+{SPR_FFLG,32776,4,NULL,S_FLAME_LARGE12,0,0},	// S_FLAME_LARGE11
+{SPR_FFLG,32777,4,NULL,S_FLAME_LARGE13,0,0},	// S_FLAME_LARGE12
+{SPR_FFLG,32778,4,NULL,S_FLAME_LARGE14,0,0},	// S_FLAME_LARGE13
+{SPR_FFLG,32779,4,NULL,S_FLAME_LARGE15,0,0},	// S_FLAME_LARGE14
+{SPR_FFLG,32780,4,NULL,S_FLAME_LARGE16,0,0},	// S_FLAME_LARGE15
+{SPR_FFLG,32781,4,NULL,S_FLAME_LARGE17,0,0},	// S_FLAME_LARGE16
+{SPR_FFLG,32782,4,NULL,S_FLAME_LARGE18,0,0},	// S_FLAME_LARGE17
+{SPR_FFLG,32783,4,NULL,S_FLAME_LARGE7,0,0},	// S_FLAME_LARGE18
+{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_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_DORMANTARTI1_2,0,0},	// S_DORMANTARTI1_1
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_3,0,0},	// S_DORMANTARTI1_2
+{SPR_ACLO,3,3,NULL,S_DORMANTARTI1_4,0,0},	// S_DORMANTARTI1_3
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_5,0,0},	// S_DORMANTARTI1_4
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_6,0,0},	// S_DORMANTARTI1_5
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_7,0,0},	// S_DORMANTARTI1_6
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_8,0,0},	// S_DORMANTARTI1_7
+{SPR_ACLO,0,3,NULL,S_DORMANTARTI1_9,0,0},	// S_DORMANTARTI1_8
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_10,0,0},	// S_DORMANTARTI1_9
+{SPR_ACLO,0,3,NULL,S_DORMANTARTI1_11,0,0},	// S_DORMANTARTI1_10
+{SPR_ACLO,0,1400,A_HideThing,S_DORMANTARTI1_12,0,0},	// S_DORMANTARTI1_11
+{SPR_ACLO,0,3,A_UnHideThing,S_DORMANTARTI1_13,0,0},	// S_DORMANTARTI1_12
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_14,0,0},	// S_DORMANTARTI1_13
+{SPR_ACLO,0,3,NULL,S_DORMANTARTI1_15,0,0},	// S_DORMANTARTI1_14
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_16,0,0},	// S_DORMANTARTI1_15
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_17,0,0},	// S_DORMANTARTI1_16
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI1_18,0,0},	// S_DORMANTARTI1_17
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_19,0,0},	// S_DORMANTARTI1_18
+{SPR_ACLO,3,3,NULL,S_DORMANTARTI1_20,0,0},	// S_DORMANTARTI1_19
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI1_21,0,0},	// S_DORMANTARTI1_20
+{SPR_ACLO,3,3,A_RestoreArtifact,S_NULL,0,0},	// S_DORMANTARTI1_21
+{SPR_ACLO,3,3,NULL,S_DORMANTARTI2_2,0,0},	// S_DORMANTARTI2_1
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_3,0,0},	// S_DORMANTARTI2_2
+{SPR_ACLO,3,3,NULL,S_DORMANTARTI2_4,0,0},	// S_DORMANTARTI2_3
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_5,0,0},	// S_DORMANTARTI2_4
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_6,0,0},	// S_DORMANTARTI2_5
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_7,0,0},	// S_DORMANTARTI2_6
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_8,0,0},	// S_DORMANTARTI2_7
+{SPR_ACLO,0,3,NULL,S_DORMANTARTI2_9,0,0},	// S_DORMANTARTI2_8
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_10,0,0},	// S_DORMANTARTI2_9
+{SPR_ACLO,0,3,NULL,S_DORMANTARTI2_11,0,0},	// S_DORMANTARTI2_10
+{SPR_ACLO,0,4200,A_HideThing,S_DORMANTARTI2_12,0,0},	// S_DORMANTARTI2_11
+{SPR_ACLO,0,3,A_UnHideThing,S_DORMANTARTI2_13,0,0},	// S_DORMANTARTI2_12
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_14,0,0},	// S_DORMANTARTI2_13
+{SPR_ACLO,0,3,NULL,S_DORMANTARTI2_15,0,0},	// S_DORMANTARTI2_14
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_16,0,0},	// S_DORMANTARTI2_15
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_17,0,0},	// S_DORMANTARTI2_16
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI2_18,0,0},	// S_DORMANTARTI2_17
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_19,0,0},	// S_DORMANTARTI2_18
+{SPR_ACLO,3,3,NULL,S_DORMANTARTI2_20,0,0},	// S_DORMANTARTI2_19
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI2_21,0,0},	// S_DORMANTARTI2_20
+{SPR_ACLO,3,3,A_RestoreArtifact,S_NULL,0,0},	// S_DORMANTARTI2_21
+{SPR_ACLO,3,3,NULL,S_DORMANTARTI3_2,0,0},	// S_DORMANTARTI3_1
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_3,0,0},	// S_DORMANTARTI3_2
+{SPR_ACLO,3,3,NULL,S_DORMANTARTI3_4,0,0},	// S_DORMANTARTI3_3
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_5,0,0},	// S_DORMANTARTI3_4
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_6,0,0},	// S_DORMANTARTI3_5
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_7,0,0},	// S_DORMANTARTI3_6
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_8,0,0},	// S_DORMANTARTI3_7
+{SPR_ACLO,0,3,NULL,S_DORMANTARTI3_9,0,0},	// S_DORMANTARTI3_8
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_10,0,0},	// S_DORMANTARTI3_9
+{SPR_ACLO,0,3,NULL,S_DORMANTARTI3_11,0,0},	// S_DORMANTARTI3_10
+{SPR_ACLO,0,21000,A_HideThing,S_DORMANTARTI3_12,0,0},	// S_DORMANTARTI3_11
+{SPR_ACLO,0,3,A_UnHideThing,S_DORMANTARTI3_13,0,0},	// S_DORMANTARTI3_12
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_14,0,0},	// S_DORMANTARTI3_13
+{SPR_ACLO,0,3,NULL,S_DORMANTARTI3_15,0,0},	// S_DORMANTARTI3_14
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_16,0,0},	// S_DORMANTARTI3_15
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_17,0,0},	// S_DORMANTARTI3_16
+{SPR_ACLO,1,3,NULL,S_DORMANTARTI3_18,0,0},	// S_DORMANTARTI3_17
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_19,0,0},	// S_DORMANTARTI3_18
+{SPR_ACLO,3,3,NULL,S_DORMANTARTI3_20,0,0},	// S_DORMANTARTI3_19
+{SPR_ACLO,2,3,NULL,S_DORMANTARTI3_21,0,0},	// S_DORMANTARTI3_20
+{SPR_ACLO,3,3,A_RestoreArtifact,S_NULL,0,0},	// S_DORMANTARTI3_21
+{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_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_SUMN,0,350,NULL,S_ARTI_SUMMON,0,0},	// S_ARTI_SUMMON
+{SPR_SUMN,0,4,NULL,S_SUMMON_FX1_1,0,0},	// S_SUMMON_FX1_1
+{SPR_SUMN,0,4,NULL,S_SUMMON_FX2_2,0,0},	// S_SUMMON_FX2_1
+{SPR_SUMN,0,4,NULL,S_SUMMON_FX2_3,0,0},	// S_SUMMON_FX2_2
+{SPR_SUMN,0,4,A_Summon,S_NULL,0,0},	// S_SUMMON_FX2_3
+{SPR_TSPK,0,3,NULL,S_THRUSTINIT2_2,0,0},	// S_THRUSTINIT2_1
+{SPR_TSPK,0,4,A_ThrustInitUp,S_THRUSTBLOCK,0,0},	// S_THRUSTINIT2_2
+{SPR_TSPK,1,3,NULL,S_BTHRUSTINIT2_2,0,0},	// S_BTHRUSTINIT2_1
+{SPR_TSPK,1,4,A_ThrustInitUp,S_BTHRUSTBLOCK,0,0},	// S_BTHRUSTINIT2_2
+{SPR_TSPK,0,3,NULL,S_THRUSTINIT1_2,0,0},	// S_THRUSTINIT1_1
+{SPR_TSPK,0,4,A_ThrustInitDn,S_THRUSTSTAY,0,0},	// S_THRUSTINIT1_2
+{SPR_TSPK,1,3,NULL,S_BTHRUSTINIT1_2,0,0},	// S_BTHRUSTINIT1_1
+{SPR_TSPK,1,4,A_ThrustInitDn,S_BTHRUSTSTAY,0,0},	// S_BTHRUSTINIT1_2
+{SPR_TSPK,0,8,A_ThrustRaise,S_THRUSTRAISE2,0,0},	// S_THRUSTRAISE1
+{SPR_TSPK,0,6,A_ThrustRaise,S_THRUSTRAISE3,0,0},	// S_THRUSTRAISE2
+{SPR_TSPK,0,4,A_ThrustRaise,S_THRUSTRAISE4,0,0},	// S_THRUSTRAISE3
+{SPR_TSPK,0,3,A_ThrustBlock,S_THRUSTIMPALE,0,0},	// S_THRUSTRAISE4
+{SPR_TSPK,1,8,A_ThrustRaise,S_BTHRUSTRAISE2,0,0},	// S_BTHRUSTRAISE1
+{SPR_TSPK,1,6,A_ThrustRaise,S_BTHRUSTRAISE3,0,0},	// S_BTHRUSTRAISE2
+{SPR_TSPK,1,4,A_ThrustRaise,S_BTHRUSTRAISE4,0,0},	// S_BTHRUSTRAISE3
+{SPR_TSPK,1,3,A_ThrustBlock,S_BTHRUSTIMPALE,0,0},	// S_BTHRUSTRAISE4
+{SPR_TSPK,0,2,A_ThrustImpale,S_THRUSTRAISE,0,0},	// S_THRUSTIMPALE
+{SPR_TSPK,1,2,A_ThrustImpale,S_BTHRUSTRAISE,0,0},	// S_BTHRUSTIMPALE
+{SPR_TSPK,0,2,A_ThrustRaise,S_THRUSTRAISE,0,0},	// S_THRUSTRAISE
+{SPR_TSPK,1,2,A_ThrustRaise,S_BTHRUSTRAISE,0,0},	// S_BTHRUSTRAISE
+{SPR_TSPK,0,10,NULL,S_THRUSTBLOCK,0,0},	// S_THRUSTBLOCK
+{SPR_TSPK,1,10,NULL,S_BTHRUSTBLOCK,0,0},	// S_BTHRUSTBLOCK
+{SPR_TSPK,0,2,A_ThrustLower,S_THRUSTLOWER,0,0},	// S_THRUSTLOWER
+{SPR_TSPK,1,2,A_ThrustLower,S_BTHRUSTLOWER,0,0},	// S_BTHRUSTLOWER
+{SPR_TSPK,0,-1,NULL,S_THRUSTSTAY,0,0},	// S_THRUSTSTAY
+{SPR_TSPK,1,-1,NULL,S_BTHRUSTSTAY,0,0},	// S_BTHRUSTSTAY
+{SPR_TELO,0,5,NULL,S_ARTI_TELOTHER2,0,0},	// S_ARTI_TELOTHER1
+{SPR_TELO,1,5,NULL,S_ARTI_TELOTHER3,0,0},	// S_ARTI_TELOTHER2
+{SPR_TELO,2,5,NULL,S_ARTI_TELOTHER4,0,0},	// S_ARTI_TELOTHER3
+{SPR_TELO,3,5,NULL,S_ARTI_TELOTHER1,0,0},	// S_ARTI_TELOTHER4
+{SPR_TRNG,32772,5,NULL,S_TELO_FX2,0,0},	// S_TELO_FX1
+{SPR_TRNG,32771,4,NULL,S_TELO_FX3,0,0},	// S_TELO_FX2
+{SPR_TRNG,32770,3,A_TeloSpawnC,S_TELO_FX4,0,0},	// S_TELO_FX3
+{SPR_TRNG,32769,3,A_TeloSpawnB,S_TELO_FX5,0,0},	// S_TELO_FX4
+{SPR_TRNG,32768,3,A_TeloSpawnA,S_TELO_FX6,0,0},	// S_TELO_FX5
+{SPR_TRNG,32769,3,A_TeloSpawnB,S_TELO_FX7,0,0},	// S_TELO_FX6
+{SPR_TRNG,32770,3,A_TeloSpawnC,S_TELO_FX8,0,0},	// S_TELO_FX7
+{SPR_TRNG,32771,3,A_TeloSpawnD,S_TELO_FX3,0,0},	// S_TELO_FX8
+{SPR_TRNG,32772,3,NULL,S_NULL,0,0},	// S_TELO_FX9
+{SPR_TRNG,32769,4,NULL,S_TELO_FX2_2,0,0},	// S_TELO_FX2_1
+{SPR_TRNG,32770,4,NULL,S_TELO_FX2_3,0,0},	// S_TELO_FX2_2
+{SPR_TRNG,32771,4,NULL,S_TELO_FX2_4,0,0},	// S_TELO_FX2_3
+{SPR_TRNG,32770,4,NULL,S_TELO_FX2_5,0,0},	// S_TELO_FX2_4
+{SPR_TRNG,32769,4,NULL,S_TELO_FX2_6,0,0},	// S_TELO_FX2_5
+{SPR_TRNG,32768,4,A_CheckTeleRing,S_TELO_FX2_1,0,0},	// S_TELO_FX2_6
+{SPR_TRNG,32770,4,NULL,S_TELO_FX3_2,0,0},	// S_TELO_FX3_1
+{SPR_TRNG,32771,4,NULL,S_TELO_FX3_3,0,0},	// S_TELO_FX3_2
+{SPR_TRNG,32770,4,NULL,S_TELO_FX3_4,0,0},	// S_TELO_FX3_3
+{SPR_TRNG,32769,4,NULL,S_TELO_FX3_5,0,0},	// S_TELO_FX3_4
+{SPR_TRNG,32768,4,NULL,S_TELO_FX3_6,0,0},	// S_TELO_FX3_5
+{SPR_TRNG,32769,4,A_CheckTeleRing,S_TELO_FX3_1,0,0},	// S_TELO_FX3_6
+{SPR_TRNG,32771,4,NULL,S_TELO_FX4_2,0,0},	// S_TELO_FX4_1
+{SPR_TRNG,32770,4,NULL,S_TELO_FX4_3,0,0},	// S_TELO_FX4_2
+{SPR_TRNG,32769,4,NULL,S_TELO_FX4_4,0,0},	// S_TELO_FX4_3
+{SPR_TRNG,32768,4,NULL,S_TELO_FX4_5,0,0},	// S_TELO_FX4_4
+{SPR_TRNG,32769,4,NULL,S_TELO_FX4_6,0,0},	// S_TELO_FX4_5
+{SPR_TRNG,32770,4,A_CheckTeleRing,S_TELO_FX4_1,0,0},	// S_TELO_FX4_6
+{SPR_TRNG,32770,4,NULL,S_TELO_FX5_2,0,0},	// S_TELO_FX5_1
+{SPR_TRNG,32769,4,NULL,S_TELO_FX5_3,0,0},	// S_TELO_FX5_2
+{SPR_TRNG,32768,4,NULL,S_TELO_FX5_4,0,0},	// S_TELO_FX5_3
+{SPR_TRNG,32769,4,NULL,S_TELO_FX5_5,0,0},	// S_TELO_FX5_4
+{SPR_TRNG,32770,4,NULL,S_TELO_FX5_6,0,0},	// S_TELO_FX5_5
+{SPR_TRNG,32771,4,A_CheckTeleRing,S_TELO_FX5_1,0,0},	// S_TELO_FX5_6
+{SPR_ROCK,3,20,NULL,S_DIRT1_1,0,0},	// S_DIRT1_1
+{SPR_ROCK,3,10,NULL,S_NULL,0,0},	// S_DIRT1_D
+{SPR_ROCK,4,20,NULL,S_DIRT2_1,0,0},	// S_DIRT2_1
+{SPR_ROCK,4,10,NULL,S_NULL,0,0},	// S_DIRT2_D
+{SPR_ROCK,5,20,NULL,S_DIRT3_1,0,0},	// S_DIRT3_1
+{SPR_ROCK,5,10,NULL,S_NULL,0,0},	// S_DIRT3_D
+{SPR_ROCK,6,20,NULL,S_DIRT4_1,0,0},	// S_DIRT4_1
+{SPR_ROCK,6,10,NULL,S_NULL,0,0},	// S_DIRT4_D
+{SPR_ROCK,7,20,NULL,S_DIRT5_1,0,0},	// S_DIRT5_1
+{SPR_ROCK,7,10,NULL,S_NULL,0,0},	// S_DIRT5_D
+{SPR_ROCK,8,20,NULL,S_DIRT6_1,0,0},	// S_DIRT6_1
+{SPR_ROCK,8,10,NULL,S_NULL,0,0},	// S_DIRT6_D
+{SPR_TSPK,2,20,NULL,S_DIRTCLUMP1,0,0},	// S_DIRTCLUMP1
+{SPR_ROCK,0,20,NULL,S_ROCK1_1,0,0},	// S_ROCK1_1
+{SPR_ROCK,0,10,NULL,S_NULL,0,0},	// S_ROCK1_D
+{SPR_ROCK,1,20,NULL,S_ROCK2_1,0,0},	// S_ROCK2_1
+{SPR_ROCK,1,10,NULL,S_NULL,0,0},	// S_ROCK2_D
+{SPR_ROCK,2,20,NULL,S_ROCK3_1,0,0},	// S_ROCK3_1
+{SPR_ROCK,2,10,NULL,S_NULL,0,0},	// S_ROCK3_D
+{SPR_MAN1,0,20,A_FogSpawn,S_SPAWNFOG1,0,0},	// S_SPAWNFOG1
+{SPR_FOGS,0,7,A_FogMove,S_FOGPATCHS2,0,0},	// S_FOGPATCHS1
+{SPR_FOGS,1,7,A_FogMove,S_FOGPATCHS3,0,0},	// S_FOGPATCHS2
+{SPR_FOGS,2,7,A_FogMove,S_FOGPATCHS4,0,0},	// S_FOGPATCHS3
+{SPR_FOGS,3,7,A_FogMove,S_FOGPATCHS5,0,0},	// S_FOGPATCHS4
+{SPR_FOGS,4,7,A_FogMove,S_FOGPATCHS1,0,0},	// S_FOGPATCHS5
+{SPR_FOGS,4,5,NULL,S_NULL,0,0},	// S_FOGPATCHS0
+{SPR_FOGM,0,7,A_FogMove,S_FOGPATCHM2,0,0},	// S_FOGPATCHM1
+{SPR_FOGM,1,7,A_FogMove,S_FOGPATCHM3,0,0},	// S_FOGPATCHM2
+{SPR_FOGM,2,7,A_FogMove,S_FOGPATCHM4,0,0},	// S_FOGPATCHM3
+{SPR_FOGM,3,7,A_FogMove,S_FOGPATCHM5,0,0},	// S_FOGPATCHM4
+{SPR_FOGM,4,7,A_FogMove,S_FOGPATCHM1,0,0},	// S_FOGPATCHM5
+{SPR_FOGS,0,5,NULL,S_FOGPATCHMA,0,0},	// S_FOGPATCHM0
+{SPR_FOGS,1,5,NULL,S_FOGPATCHMB,0,0},	// S_FOGPATCHMA
+{SPR_FOGS,2,5,NULL,S_FOGPATCHMC,0,0},	// S_FOGPATCHMB
+{SPR_FOGS,3,5,NULL,S_FOGPATCHMD,0,0},	// S_FOGPATCHMC
+{SPR_FOGS,4,5,NULL,S_FOGPATCHS0,0,0},	// S_FOGPATCHMD
+{SPR_FOGL,0,7,A_FogMove,S_FOGPATCHL2,0,0},	// S_FOGPATCHL1
+{SPR_FOGL,1,7,A_FogMove,S_FOGPATCHL3,0,0},	// S_FOGPATCHL2
+{SPR_FOGL,2,7,A_FogMove,S_FOGPATCHL4,0,0},	// S_FOGPATCHL3
+{SPR_FOGL,3,7,A_FogMove,S_FOGPATCHL5,0,0},	// S_FOGPATCHL4
+{SPR_FOGL,4,7,A_FogMove,S_FOGPATCHL1,0,0},	// S_FOGPATCHL5
+{SPR_FOGM,0,4,NULL,S_FOGPATCHLA,0,0},	// S_FOGPATCHL0
+{SPR_FOGM,1,4,NULL,S_FOGPATCHLB,0,0},	// S_FOGPATCHLA
+{SPR_FOGM,2,4,NULL,S_FOGPATCHLC,0,0},	// S_FOGPATCHLB
+{SPR_FOGM,3,4,NULL,S_FOGPATCHLD,0,0},	// S_FOGPATCHLC
+{SPR_FOGM,4,4,NULL,S_FOGPATCHM0,0,0},	// S_FOGPATCHLD
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE2,0,0},	// S_QUAKE_ACTIVE1
+{SPR_MAN1,0,1,A_ContMobjSound,S_QUAKE_ACTIVE3,0,0},	// S_QUAKE_ACTIVE2
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE4,0,0},	// S_QUAKE_ACTIVE3
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE5,0,0},	// S_QUAKE_ACTIVE4
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE6,0,0},	// S_QUAKE_ACTIVE5
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE7,0,0},	// S_QUAKE_ACTIVE6
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE8,0,0},	// S_QUAKE_ACTIVE7
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE9,0,0},	// S_QUAKE_ACTIVE8
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE0,0,0},	// S_QUAKE_ACTIVE9
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEA,0,0},	// S_QUAKE_ACTIVE0
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEB,0,0},	// S_QUAKE_ACTIVEA
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEC,0,0},	// S_QUAKE_ACTIVEB
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVED,0,0},	// S_QUAKE_ACTIVEC
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEE,0,0},	// S_QUAKE_ACTIVED
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEF,0,0},	// S_QUAKE_ACTIVEE
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEG,0,0},	// S_QUAKE_ACTIVEF
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEH,0,0},	// S_QUAKE_ACTIVEG
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEI,0,0},	// S_QUAKE_ACTIVEH
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEJ,0,0},	// S_QUAKE_ACTIVEI
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEK,0,0},	// S_QUAKE_ACTIVEJ
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEL,0,0},	// S_QUAKE_ACTIVEK
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEM,0,0},	// S_QUAKE_ACTIVEL
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEN,0,0},	// S_QUAKE_ACTIVEM
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEO,0,0},	// S_QUAKE_ACTIVEN
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEP,0,0},	// S_QUAKE_ACTIVEO
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEQ,0,0},	// S_QUAKE_ACTIVEP
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVER,0,0},	// S_QUAKE_ACTIVEQ
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVES,0,0},	// S_QUAKE_ACTIVER
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVET,0,0},	// S_QUAKE_ACTIVES
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEU,0,0},	// S_QUAKE_ACTIVET
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEV,0,0},	// S_QUAKE_ACTIVEU
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEW,0,0},	// S_QUAKE_ACTIVEV
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEX,0,0},	// S_QUAKE_ACTIVEW
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEY,0,0},	// S_QUAKE_ACTIVEX
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVEZ,0,0},	// S_QUAKE_ACTIVEY
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT1,0,0},	// S_QUAKE_ACTIVEZ
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT2,0,0},	// S_QUAKE_ACT1
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT3,0,0},	// S_QUAKE_ACT2
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT4,0,0},	// S_QUAKE_ACT3
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT5,0,0},	// S_QUAKE_ACT4
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT6,0,0},	// S_QUAKE_ACT5
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT7,0,0},	// S_QUAKE_ACT6
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT8,0,0},	// S_QUAKE_ACT7
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT9,0,0},	// S_QUAKE_ACT8
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACT0,0,0},	// S_QUAKE_ACT9
+{SPR_MAN1,0,2,A_Quake,S_QUAKE_ACTIVE1,0,0},	// S_QUAKE_ACT0
+{SPR_SGSA,0,4,NULL,S_SGSHARD1_2,0,0},	// S_SGSHARD1_1
+{SPR_SGSA,1,4,NULL,S_SGSHARD1_3,0,0},	// S_SGSHARD1_2
+{SPR_SGSA,2,4,NULL,S_SGSHARD1_4,0,0},	// S_SGSHARD1_3
+{SPR_SGSA,3,4,NULL,S_SGSHARD1_5,0,0},	// S_SGSHARD1_4
+{SPR_SGSA,4,4,NULL,S_SGSHARD1_1,0,0},	// S_SGSHARD1_5
+{SPR_SGSA,4,30,NULL,S_NULL,0,0},	// S_SGSHARD1_D
+{SPR_SGSA,5,4,NULL,S_SGSHARD2_2,0,0},	// S_SGSHARD2_1
+{SPR_SGSA,6,4,NULL,S_SGSHARD2_3,0,0},	// S_SGSHARD2_2
+{SPR_SGSA,7,4,NULL,S_SGSHARD2_4,0,0},	// S_SGSHARD2_3
+{SPR_SGSA,8,4,NULL,S_SGSHARD2_5,0,0},	// S_SGSHARD2_4
+{SPR_SGSA,9,4,NULL,S_SGSHARD2_1,0,0},	// S_SGSHARD2_5
+{SPR_SGSA,9,30,NULL,S_NULL,0,0},	// S_SGSHARD2_D
+{SPR_SGSA,10,4,NULL,S_SGSHARD3_2,0,0},	// S_SGSHARD3_1
+{SPR_SGSA,11,4,NULL,S_SGSHARD3_3,0,0},	// S_SGSHARD3_2
+{SPR_SGSA,12,4,NULL,S_SGSHARD3_4,0,0},	// S_SGSHARD3_3
+{SPR_SGSA,13,4,NULL,S_SGSHARD3_5,0,0},	// S_SGSHARD3_4
+{SPR_SGSA,14,4,NULL,S_SGSHARD3_1,0,0},	// S_SGSHARD3_5
+{SPR_SGSA,14,30,NULL,S_NULL,0,0},	// S_SGSHARD3_D
+{SPR_SGSA,15,4,NULL,S_SGSHARD4_2,0,0},	// S_SGSHARD4_1
+{SPR_SGSA,16,4,NULL,S_SGSHARD4_3,0,0},	// S_SGSHARD4_2
+{SPR_SGSA,17,4,NULL,S_SGSHARD4_4,0,0},	// S_SGSHARD4_3
+{SPR_SGSA,18,4,NULL,S_SGSHARD4_5,0,0},	// S_SGSHARD4_4
+{SPR_SGSA,19,4,NULL,S_SGSHARD4_1,0,0},	// S_SGSHARD4_5
+{SPR_SGSA,19,30,NULL,S_NULL,0,0},	// S_SGSHARD4_D
+{SPR_SGSA,20,4,NULL,S_SGSHARD5_2,0,0},	// S_SGSHARD5_1
+{SPR_SGSA,21,4,NULL,S_SGSHARD5_3,0,0},	// S_SGSHARD5_2
+{SPR_SGSA,22,4,NULL,S_SGSHARD5_4,0,0},	// S_SGSHARD5_3
+{SPR_SGSA,23,4,NULL,S_SGSHARD5_5,0,0},	// S_SGSHARD5_4
+{SPR_SGSA,24,4,NULL,S_SGSHARD5_1,0,0},	// S_SGSHARD5_5
+{SPR_SGSA,24,30,NULL,S_NULL,0,0},	// S_SGSHARD5_D
+{SPR_SGSB,0,4,NULL,S_SGSHARD6_1,0,0},	// S_SGSHARD6_1
+{SPR_SGSB,0,30,NULL,S_NULL,0,0},	// S_SGSHARD6_D
+{SPR_SGSB,1,4,NULL,S_SGSHARD7_1,0,0},	// S_SGSHARD7_1
+{SPR_SGSB,1,30,NULL,S_NULL,0,0},	// S_SGSHARD7_D
+{SPR_SGSB,2,4,NULL,S_SGSHARD8_1,0,0},	// S_SGSHARD8_1
+{SPR_SGSB,2,30,NULL,S_NULL,0,0},	// S_SGSHARD8_D
+{SPR_SGSB,3,4,NULL,S_SGSHARD9_1,0,0},	// S_SGSHARD9_1
+{SPR_SGSB,3,30,NULL,S_NULL,0,0},	// S_SGSHARD9_D
+{SPR_SGSB,4,4,NULL,S_SGSHARD0_1,0,0},	// S_SGSHARD0_1
+{SPR_SGSB,4,30,NULL,S_NULL,0,0},	// S_SGSHARD0_D
+{SPR_PORK,0,5,NULL,S_ARTI_EGGC2,0,0},	// S_ARTI_EGGC1
+{SPR_PORK,1,5,NULL,S_ARTI_EGGC3,0,0},	// S_ARTI_EGGC2
+{SPR_PORK,2,5,NULL,S_ARTI_EGGC4,0,0},	// S_ARTI_EGGC3
+{SPR_PORK,3,5,NULL,S_ARTI_EGGC5,0,0},	// S_ARTI_EGGC4
+{SPR_PORK,4,5,NULL,S_ARTI_EGGC6,0,0},	// S_ARTI_EGGC5
+{SPR_PORK,5,5,NULL,S_ARTI_EGGC7,0,0},	// S_ARTI_EGGC6
+{SPR_PORK,6,5,NULL,S_ARTI_EGGC8,0,0},	// S_ARTI_EGGC7
+{SPR_PORK,7,5,NULL,S_ARTI_EGGC1,0,0},	// S_ARTI_EGGC8
+{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_FHFX,32776,3,NULL,S_EGGFXI1_2,0,0},	// S_EGGFXI1_1
+{SPR_FHFX,32777,3,NULL,S_EGGFXI1_3,0,0},	// S_EGGFXI1_2
+{SPR_FHFX,32778,3,NULL,S_EGGFXI1_4,0,0},	// S_EGGFXI1_3
+{SPR_FHFX,32779,3,NULL,S_NULL,0,0},	// S_EGGFXI1_4
+{SPR_SPHL,0,350,NULL,S_ARTI_SPHL1,0,0},	// S_ARTI_SPHL1
+{SPR_STWN,0,-1,NULL,S_NULL,0,0},	// S_ZWINGEDSTATUENOSKULL
+{SPR_STWN,1,-1,NULL,S_NULL,0,0},	// S_ZWINGEDSTATUENOSKULL2
+{SPR_GMPD,0,-1,NULL,S_NULL,0,0},	// S_ZGEMPEDESTAL1
+{SPR_GMPD,1,-1,NULL,S_NULL,0,0},	// S_ZGEMPEDESTAL2
+{SPR_ASKU,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZSKULL
+{SPR_ABGM,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZGEMBIG
+{SPR_AGMR,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZGEMRED
+{SPR_AGMG,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZGEMGREEN1
+{SPR_AGG2,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZGEMGREEN2
+{SPR_AGMB,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZGEMBLUE1
+{SPR_AGB2,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZGEMBLUE2
+{SPR_ABK1,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZBOOK1
+{SPR_ABK2,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZBOOK2
+{SPR_ASK2,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZSKULL2
+{SPR_AFWP,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZFWEAPON
+{SPR_ACWP,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZCWEAPON
+{SPR_AMWP,0,-1,NULL,S_NULL,0,0},	// S_ARTIPUZZMWEAPON
+{SPR_AGER,32768,4,NULL,S_ARTIPUZZGEAR_2,0,0},	// S_ARTIPUZZGEAR_1
+{SPR_AGER,32769,4,NULL,S_ARTIPUZZGEAR_3,0,0},	// S_ARTIPUZZGEAR_2
+{SPR_AGER,32770,4,NULL,S_ARTIPUZZGEAR_4,0,0},	// S_ARTIPUZZGEAR_3
+{SPR_AGER,32771,4,NULL,S_ARTIPUZZGEAR_5,0,0},	// S_ARTIPUZZGEAR_4
+{SPR_AGER,32772,4,NULL,S_ARTIPUZZGEAR_6,0,0},	// S_ARTIPUZZGEAR_5
+{SPR_AGER,32773,4,NULL,S_ARTIPUZZGEAR_7,0,0},	// S_ARTIPUZZGEAR_6
+{SPR_AGER,32774,4,NULL,S_ARTIPUZZGEAR_8,0,0},	// S_ARTIPUZZGEAR_7
+{SPR_AGER,32775,4,NULL,S_ARTIPUZZGEAR_1,0,0},	// S_ARTIPUZZGEAR_8
+{SPR_AGR2,32768,4,NULL,S_ARTIPUZZGEAR2_2,0,0},	// S_ARTIPUZZGEAR2_1
+{SPR_AGR2,32769,4,NULL,S_ARTIPUZZGEAR2_3,0,0},	// S_ARTIPUZZGEAR2_2
+{SPR_AGR2,32770,4,NULL,S_ARTIPUZZGEAR2_4,0,0},	// S_ARTIPUZZGEAR2_3
+{SPR_AGR2,32771,4,NULL,S_ARTIPUZZGEAR2_5,0,0},	// S_ARTIPUZZGEAR2_4
+{SPR_AGR2,32772,4,NULL,S_ARTIPUZZGEAR2_6,0,0},	// S_ARTIPUZZGEAR2_5
+{SPR_AGR2,32773,4,NULL,S_ARTIPUZZGEAR2_7,0,0},	// S_ARTIPUZZGEAR2_6
+{SPR_AGR2,32774,4,NULL,S_ARTIPUZZGEAR2_8,0,0},	// S_ARTIPUZZGEAR2_7
+{SPR_AGR2,32775,4,NULL,S_ARTIPUZZGEAR2_1,0,0},	// S_ARTIPUZZGEAR2_8
+{SPR_AGR3,32768,4,NULL,S_ARTIPUZZGEAR3_2,0,0},	// S_ARTIPUZZGEAR3_1
+{SPR_AGR3,32769,4,NULL,S_ARTIPUZZGEAR3_3,0,0},	// S_ARTIPUZZGEAR3_2
+{SPR_AGR3,32770,4,NULL,S_ARTIPUZZGEAR3_4,0,0},	// S_ARTIPUZZGEAR3_3
+{SPR_AGR3,32771,4,NULL,S_ARTIPU