shithub: heretic

Download patch

ref: 811f13ff30a4375798a8aee344c0010172ca69af
author: Jacob Moody <moody@posixcafe.org>
date: Mon Jan 23 03:15:24 EST 2023

initial commit

--- /dev/null
+++ b/LICENSE
@@ -1,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null
+++ b/README
@@ -1,0 +1,3 @@
+Plan9 port of HHeretic
+
+https://github.com/sezero/hheretic
--- /dev/null
+++ b/am_data.h
@@ -1,0 +1,97 @@
+// AM_data.h : The vector graphics for the automap
+
+#ifndef __AMDATA_H__
+#define __AMDATA_H__
+
+/* a line drawing of the player pointing right, starting from the middle. */
+
+#define R	((8 * PLAYERRADIUS) / 7)
+
+mline_t player_arrow[] =
+{
+	{ { -R+R/4, 0 }, { 0, 0} },		// center line.
+	{ { -R+R/4, R/8 }, { R, 0} },		// blade
+	{ { -R+R/4, -R/8 }, { R, 0 } },
+	{ { -R+R/4, -R/4 }, { -R+R/4, R/4 } },	// crosspiece
+	{ { -R+R/8, -R/4 }, { -R+R/8, R/4 } },
+	{ { -R+R/8, -R/4 }, { -R+R/4, -R/4} },	// crosspiece connectors
+	{ { -R+R/8, R/4 }, { -R+R/4, R/4} },
+	{ { -R-R/4, R/8 }, { -R-R/4, -R/8 } },	// pommel
+	{ { -R-R/4, R/8 }, { -R+R/8, R/8 } },
+	{ { -R-R/4, -R/8}, { -R+R/8, -R/8 } }
+};
+
+mline_t keysquare[] =
+{
+	{ { 0, 0 }, { R/4, -R/2 } },
+	{ { R/4, -R/2 }, { R/2, -R/2 } },
+	{ { R/2, -R/2 }, { R/2, R/2 } },
+	{ { R/2, R/2 }, { R/4, R/2 } },
+	{ { R/4, R/2 }, { 0, 0 } },		// handle part type thing
+	{ { 0, 0 }, { -R, 0 } },		// stem
+	{ { -R, 0 }, { -R, -R/2 } },		// end lockpick part
+	{ { -3*R/4, 0 }, { -3*R/4, -R/4 } }
+};
+
+/*
+mline_t player_arrow[] =
+{
+	{ { -R+R/8, 0 }, { R, 0 } },		// -----
+	{ { R, 0 }, { R-R/2, R/4 } },		// ----->
+	{ { R, 0 }, { R-R/2, -R/4 } },
+	{ { -R+R/8, 0 }, { -R-R/8, R/4 } },	// >---->
+	{ { -R+R/8, 0 }, { -R-R/8, -R/4 } },
+	{ { -R+3*R/8, 0 }, { -R+R/8, R/4 } },	// >>--->
+	{ { -R+3*R/8, 0 }, { -R+R/8, -R/4 } }
+};
+*/
+#undef R
+
+#define NUMPLYRLINES		(sizeof(player_arrow) / sizeof(mline_t))
+#define NUMKEYSQUARELINES	(sizeof(keysquare) / sizeof(mline_t))
+
+#define R	((8 * PLAYERRADIUS) / 7)
+mline_t cheat_player_arrow[] =
+{
+	{ { -R+R/8, 0 }, { R, 0 } },		// -----
+	{ { R, 0 }, { R-R/2, R/6 } },		// ----->
+	{ { R, 0 }, { R-R/2, -R/6 } },
+	{ { -R+R/8, 0 }, { -R-R/8, R/6 } },	// >----->
+	{ { -R+R/8, 0 }, { -R-R/8, -R/6 } },
+	{ { -R+3*R/8, 0 }, { -R+R/8, R/6 } },	// >>----->
+	{ { -R+3*R/8, 0 }, { -R+R/8, -R/6 } },
+	{ { -R/2, 0 }, { -R/2, -R/6 } },	// >>-d--->
+	{ { -R/2, -R/6 }, { -R/2+R/6, -R/6 } },
+	{ { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } },
+	{ { -R/6, 0 }, { -R/6, -R/6 } },	// >>-dd-->
+	{ { -R/6, -R/6 }, { 0, -R/6 } },
+	{ { 0, -R/6 }, { 0, R/4 } },
+	{ { R/6, R/4 }, { R/6, -R/7 } },	// >>-ddt->
+	{ { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } },
+	{ { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } }
+};
+#undef R
+#define NUMCHEATPLYRLINES	(sizeof(cheat_player_arrow) / sizeof(mline_t))
+
+#define R	(FRACUNIT)
+mline_t triangle_guy[] =
+{
+	{ { -.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	/* __AMDATA_H__ */
+
--- /dev/null
+++ b/am_map.c
@@ -1,0 +1,1545 @@
+// AM_map.c
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+#include "am_map.h"
+#include "am_data.h"
+
+#ifdef RENDER3D
+#include "ogl_def.h"
+
+#define MTOFX(x)	FixedMul((x),scale_mtof)
+
+#define CXMTOFX(x)	((f_x<<16) + MTOFX((x)-m_x))
+#define CYMTOFX(y)	((f_y<<16) + ((f_h<<16) - MTOFX((y)-m_y)))
+
+static int maplumpnum;
+#endif	/* RENDER3D */
+
+vertex_t KeyPoints[NUMKEYS];
+
+#define NUMALIAS	3	/* Number of antialiased lines. */
+
+const 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",
+	// EPISODE 6: FATE'S PATH
+	"E6M1:  RAVEN'S LAIR",		/* OFFICES */
+	"E6M2:  THE WATER SHRINE",	/* RUINED TEMPLE */
+	"E6M3:  AMERICAN'S LEGACY"	/* AMERICAN LEGACY */
+	/* Episode 6 names are taken from the Heretic FAQ. */
+};
+
+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 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 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;
+
+#ifndef RENDER3D
+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 byte *fb;	// pseudo-frame buffer
+#endif	/* RENDER3D */
+
+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.
+
+// Functions
+
+#ifndef RENDER3D
+static void DrawWuLine (int X0, int Y0, int X1, int Y1, byte *BaseColor,
+			int NumLevels, unsigned short IntensityBits);
+#endif	/* RENDER3D */
+
+// 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
+/*
+static 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 ? -H2MAXINT : H2MAXINT);
+	else
+		is->islp = FixedDiv(dx, dy);
+	if (!dx)
+		is->slp = (dy < 0 ? -H2MAXINT : H2MAXINT);
+	else
+		is->slp = FixedDiv(dy, dx);
+}
+*/
+
+static void AM_activateNewScale(void)
+{
+	m_x += m_w/2;
+	m_y += m_h/2;
+	m_w = FTOM(f_w);
+	m_h = FTOM(f_h);
+	m_x -= m_w/2;
+	m_y -= m_h/2;
+	m_x2 = m_x + m_w;
+	m_y2 = m_y + m_h;
+}
+
+static void AM_saveScaleAndLoc(void)
+{
+	old_m_x = m_x;
+	old_m_y = m_y;
+	old_m_w = m_w;
+	old_m_h = m_h;
+}
+
+static 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);
+}
+
+static void AM_findMinMaxBoundaries(void)
+{
+	int i;
+	fixed_t a, b;
+
+	min_x = min_y = H2MAXINT;
+	max_x = max_y = -H2MAXINT;
+	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);
+}
+
+static void AM_changeWindowLoc(void)
+{
+	if (m_paninc.x || m_paninc.y)
+	{
+		followplayer = 0;
+		f_oldloc.x = H2MAXINT;
+	}
+
+	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;
+}
+
+static void AM_initVariables(void)
+{
+	int pnum;
+	thinker_t *think;
+	mobj_t *mo;
+
+	automapactive = true;
+#ifndef RENDER3D
+	fb = screens;
+#endif
+
+	f_oldloc.x = H2MAXINT;
+	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;
+			}
+		}
+	}
+}
+
+static void AM_loadPics(void)
+{
+#ifdef RENDER3D
+	maplumpnum = W_GetNumForName("AUTOPAGE");
+#else
+	maplump = (byte *) W_CacheLumpName("AUTOPAGE", PU_STATIC);
+#endif
+}
+
+// should be called at the start of every level
+// right now, i figure it out myself
+
+static void AM_LevelInit(void)
+{
+	leveljuststarted = 0;
+
+	f_x = f_y = 0;
+	f_w = finit_width;
+	f_h = finit_height;
+	mapxstart = mapystart = 0;
+
+	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)
+{
+	automapactive = false;
+	stopped = true;
+	BorderNeedRefresh = true;
+}
+
+static 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
+
+static 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
+
+static 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 bigstate = 0;
+
+	rc = false;
+	if (!automapactive)
+	{
+		if (ev->type == ev_keydown && ev->data1 == AM_STARTKEY
+			&& gamestate == GS_LEVEL)
+		{
+			AM_Start ();
+			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 ();
+			break;
+		case AM_GOBIGKEY:
+			bigstate = !bigstate;
+			if (bigstate)
+			{
+				AM_saveScaleAndLoc();
+				AM_minOutWindowScale();
+			}
+			else AM_restoreScaleAndLoc();
+			break;
+		case AM_FOLLOWKEY:
+			followplayer = !followplayer;
+			f_oldloc.x = H2MAXINT;
+			P_SetMessage(plr, 
+				followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF, true);
+			break;
+		default:
+			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;
+}
+
+static void AM_changeWindowScale(void)
+{
+	// Change the scaling multipliers
+	scale_mtof = FixedMul(scale_mtof, mtof_zoommul);
+	scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
+
+	if (scale_mtof < min_scale_mtof)
+		AM_minOutWindowScale();
+	else if (scale_mtof > max_scale_mtof)
+		AM_maxOutWindowScale();
+	else
+		AM_activateNewScale();
+}
+
+static void AM_doFollowPlayer(void)
+{
+	if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y)
+	{
+	//	m_x = FTOM(MTOF(plr->mo->x - 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 == H2MAXINT)	//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
+/*
+static 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();
+	*/
+}
+
+static void AM_clearFB(int color)
+{
+#ifdef RENDER3D
+	float scaler;
+	int lump;
+#else
+# if !AM_TRANSPARENT
+	int i, j;
+# endif
+#endif
+
+	if (followplayer)
+	{
+		int dmapx = (MTOF(plr->mo->x) - MTOF(oldplr.x));	//fixed point
+		int dmapy = (MTOF(oldplr.y) - MTOF(plr->mo->y));
+
+		oldplr.x = plr->mo->x;
+		oldplr.y = plr->mo->y;
+	//	if (f_oldloc.x == H2MAXINT)	//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;
+	}
+
+#ifdef RENDER3D
+	OGL_SetColorAndAlpha(1, 1, 1, 1);
+	if (shareware)
+	{
+		OGL_SetFlat(W_GetNumForName ("FLOOR04")-firstflat);
+	}
+	else
+	{
+		OGL_SetFlat(W_GetNumForName("FLAT513")-firstflat);
+	}
+	scaler = sbarscale/20.0;
+	OGL_DrawCutRectTiled(0, finit_height+4, 320, 200-finit_height-4, 64, 64,
+				160-160*scaler+1, finit_height, 320*scaler-2, 200-finit_height);
+
+	lump = W_GetNumForName("bordb");
+	OGL_SetPatch(lump);
+	OGL_DrawCutRectTiled(0, finit_height, 320, 4,
+				lumptexsizes[lump].w, lumptexsizes[lump].h,
+				160-160*scaler+1, finit_height, 320*scaler-2, 4);
+# if !AM_TRANSPARENT
+	OGL_SetRawImage(maplumpnum, 0);	// We only want the left portion.
+	OGL_DrawRectTiled(0, 0, finit_width,
+				/*(sbarscale<20)?200:*/ finit_height, 128, 128);
+# endif
+
+#else	/* RENDER3D */
+
+# if !AM_TRANSPARENT
+	// 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;
+	}
+# endif
+#endif	/* !RENDER3D */
+}
+
+
+#ifndef RENDER3D
+/* 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
+ */
+static 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);
+}
+
+static 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);
+}
+
+// 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.
+
+static boolean AM_clipMline(mline_t *ml, fline_t *fl)
+{
+	enum { LEFT = 1, RIGHT = 2, BOTTOM = 4, TOP = 8 };
+	register int 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;
+		}
+		else /* avoid compiler warning */
+		{
+			tmp.x = 0;
+			tmp.y = 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
+
+static void AM_drawFline(fline_t *fl, int color)
+{
+	register int x, y, dx, dy, sx, sy, ax, ay, d;
+//	static int fuck = 0;
+
+	switch (color)
+	{
+	case WALLCOLORS:
+		DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y,
+				&antialias[0][0], 8, 3);
+		break;
+	case FDWALLCOLORS:
+		DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y,
+				&antialias[1][0], 8, 3);
+		break;
+	case CDWALLCOLORS:
+		DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y,
+				&antialias[2][0], 8, 3);
+		break;
+	default:
+		// For debugging only
+		if (fl->a.x < 0 || fl->a.x >= f_w
+			|| fl->a.y < 0 || fl->a.y >= f_h
+			|| fl->b.x < 0 || fl->b.x >= f_w
+			|| fl->b.y < 0 || fl->b.y >= f_h)
+		{
+		//	fprintf(stderr, "fuck %d \r", fuck++);
+			return;
+		}
+
+		#define DOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc) //the MACRO!
+
+		dx = fl->b.x - fl->a.x;
+		ax = 2 * (dx<0 ? -dx : dx);
+		sx = dx<0 ? -1 : 1;
+
+		dy = fl->b.y - fl->a.y;
+		ay = 2 * (dy < 0 ? -dy : dy);
+		sy = dy < 0 ? -1 : 1;
+
+		x = fl->a.x;
+		y = fl->a.y;
+
+		if (ax > ay)
+		{
+			d = ay - ax/2;
+			while (1)
+			{
+				DOT(x, y, color);
+				if (x == fl->b.x)
+					return;
+				if (d >= 0)
+				{
+					y += sy;
+					d -= ax;
+				}
+				x += sx;
+				d += ay;
+			}
+		}
+		else
+		{
+			d = ax - ay/2;
+			while (1)
+			{
+				DOT(x, y, color);
+				if (y == fl->b.y)
+					return;
+				if (d >= 0)
+				{
+					x += sx;
+					d -= ay;
+				}
+				y += sy;
+				d += ax;
+			}
+		}
+		#undef DOT
+	}
+}
+
+static 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
+}
+#endif	/* ! RENDER3D */
+
+#ifdef RENDER3D
+static void AM_drawMline(mline_t *ml, int color)
+{
+	/* bbm: disabled this. doing it more directly below.
+	byte	*palette = (byte *) W_CacheLumpName("PLAYPAL", PU_CACHE);
+	byte	r = palette[color*3],
+		g = palette[color*3 + 1],
+		b = palette[color*3 + 2];
+
+	OGL_DrawLine(FIX2FLT(CXMTOFX(ml->a.x)), FIX2FLT(CYMTOFX(ml->a.y))/1.2,
+		     FIX2FLT(CXMTOFX(ml->b.x)), FIX2FLT(CYMTOFX(ml->b.y))/1.2,
+		     r/255.0, g/255.0, b/255.0, 1);
+	*/
+	OGL_SetColor(color);
+	// Draw the line. 1.2 is the to-square aspect corrector.
+	glVertex2f(FIX2FLT(CXMTOFX(ml->a.x)), FIX2FLT(CYMTOFX(ml->a.y))/1.2);
+	glVertex2f(FIX2FLT(CXMTOFX(ml->b.x)), FIX2FLT(CYMTOFX(ml->b.y))/1.2);
+}
+#endif	/* RENDER3D */
+
+static void AM_drawGrid(int color)
+{
+	fixed_t x, y;
+	fixed_t start, end;
+	mline_t ml;
+
+	// Figure out start of vertical gridlines
+	start = m_x;
+	if ((start-bmaporgx) % (MAPBLOCKUNITS<<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);
+	}
+}
+
+static void AM_drawWalls(void)
+{
+	int i;
+
+#ifdef RENDER3D
+	/* bbm: disabled this to draw all lines at once, see AM_Drawer()
+	glDisable(GL_TEXTURE_2D);
+	glLineWidth(2.5);
+	glBegin(GL_LINES);	// We'll draw pretty much all of them.
+	*/
+	for (i = 0; i < numlines; i++)
+	{
+		if (cheating || (lines[i].flags & ML_MAPPED))
+		{
+			if ((lines[i].flags & LINE_NEVERSEE) && !cheating)
+				continue;
+			if (!lines[i].backsector)
+			{
+				OGL_SetColor(WALLCOLORS);
+			}
+			else
+			{
+				if (lines[i].flags & ML_SECRET) // secret door
+				{
+					if (cheating) //AM_drawMline(&l, 0);
+						OGL_SetColor(0);
+					else
+						OGL_SetColor(WALLCOLORS);
+				}
+				else if (lines[i].special == 13 || lines[i].special == 83)
+				{ // Locked door line -- all locked doors are greed
+					OGL_SetColor(GREENKEY);
+				}
+				else if (lines[i].special == 70 || lines[i].special == 71)
+				{ // intra-level teleports are blue
+					OGL_SetColor(BLUEKEY);
+				}
+				else if (lines[i].special == 74 || lines[i].special == 75)
+				{ // inter-level teleport/game-winning exit -- both are red
+					OGL_SetColor(BLOODRED);
+				}
+				else if (lines[i].backsector->floorheight != lines[i].frontsector->floorheight)
+				{ // floor level change
+					OGL_SetColor(FDWALLCOLORS);
+				}
+				else if (lines[i].backsector->ceilingheight != lines[i].frontsector->ceilingheight)
+				{ // ceiling level change
+					OGL_SetColor(CDWALLCOLORS);
+				}
+				else if (cheating)
+				{
+					OGL_SetColor(TSWALLCOLORS);
+				}
+				else
+					continue;
+			}
+		}
+		else if (plr->powers[pw_allmap])
+		{
+			if (!(lines[i].flags & LINE_NEVERSEE))
+				OGL_SetColor(GRAYS+3);
+			else
+				continue;
+		}
+		else
+			continue;
+
+		// Draw the line. 1.2 is the to-square aspect corrector.
+		glVertex2f (FIX2FLT(CXMTOFX(lines[i].v1->x)),
+			    FIX2FLT(CYMTOFX(lines[i].v1->y))/1.2);
+		glVertex2f (FIX2FLT(CXMTOFX(lines[i].v2->x)),
+			    FIX2FLT(CYMTOFX(lines[i].v2->y))/1.2);
+	}
+	/* bbm .
+	glEnd();
+	glLineWidth(1.0);
+	glEnable(GL_TEXTURE_2D);
+	*/
+#else
+	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)
+				{ // floor level change
+					AM_drawMline(&l, FDWALLCOLORS + lightlev);
+				}
+				else if (lines[i].backsector->ceilingheight != lines[i].frontsector->ceilingheight)
+				{ // ceiling level change
+					AM_drawMline(&l, CDWALLCOLORS+lightlev);
+				}
+				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);
+		}
+	}
+#endif
+}
+
+static 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;
+}
+
+static void AM_drawLineCharacter(mline_t *lineguy, int lineguylines, fixed_t scale,
+				 angle_t angle, int color, fixed_t x, fixed_t y)
+{
+	int i;
+	mline_t l;
+
+	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);
+	}
+}
+
+static 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)
+	{
+		//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);
+	}
+}
+
+static 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;
+		}
+	}
+}
+
+static 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);
+	}
+}
+
+
+#ifdef RENDER3D
+static void AM_OGL_SetupState(void)
+{
+	float ys = screenHeight/200.0;
+
+	// Let's set up a scissor box to clip the map lines and stuff.
+	glPushAttrib(GL_SCISSOR_BIT);
+	glScissor(0, screenHeight-finit_height*ys, screenWidth, finit_height*ys);
+	glEnable(GL_SCISSOR_TEST);
+}
+
+static void AM_OGL_RestoreState(void)
+{
+	glPopAttrib();
+}
+#endif	/* RENDER3D */
+
+
+void AM_Drawer (void)
+{
+	if (!automapactive)
+		return;
+
+	UpdateState |= I_FULLSCRN;
+
+#ifdef RENDER3D
+	// Update the height (in case sbarscale has been changed).
+	finit_height = SCREENHEIGHT - SBARHEIGHT * sbarscale / 20;
+	AM_OGL_SetupState();
+#endif
+
+	AM_clearFB(BACKGROUND);
+
+#ifdef RENDER3D
+	/* bbm 3/9/2003: start drawing all lines at once */
+	glDisable(GL_TEXTURE_2D);
+	glLineWidth(1.0);
+	glBegin(GL_LINES);
+#endif
+
+	if (grid)
+		AM_drawGrid(GRIDCOLORS);
+	AM_drawWalls();
+	AM_drawPlayers();
+
+	if (cheating == 2)
+		AM_drawThings(THINGCOLORS, THINGRANGE);
+
+	if (gameskill == sk_baby)
+	{
+		AM_drawkeys();
+	}
+
+#ifdef RENDER3D
+	/* bbm: finish drawing all lines at once */
+	glEnd();
+	glLineWidth(1.0);
+	glEnable(GL_TEXTURE_2D);
+
+	AM_OGL_RestoreState();
+#endif
+
+	if ((gameepisode <= (ExtendedWAD ? 6 : 3)) && gamemap <= 9)
+	{
+		MN_DrTextA(LevelNames[(gameepisode-1)*9+gamemap-1], 20, 145);
+	}
+}
+
--- /dev/null
+++ b/am_map.h
@@ -1,0 +1,113 @@
+// am_map.h
+
+#ifndef __AMMAP_H__
+#define __AMMAP_H__
+
+/* For use if I do walls with outsides/insides */
+#define REDS		(12 * 8)
+#define REDRANGE	1 /* 16 */
+#define BLUES		(256 - 4*16 + 8)
+#define BLUERANGE	1 /* 8 */
+#define GREENS		(33 * 8)
+#define GREENRANGE	1 /* 16 */
+#define GRAYS		(5 * 8)
+#define GRAYSRANGE	1 /* 16 */
+#define BROWNS		(14 * 8)
+#define BROWNRANGE	1 /* 16 */
+#define YELLOWS		(10 * 8)
+#define YELLOWRANGE	1
+#define BLACK		0
+#define WHITE		(4 * 8)
+#define PARCH		(13*8 - 1)
+#define BLOODRED	150
+#define BLUEKEY 	197
+#define YELLOWKEY	144
+#define GREENKEY	220
+
+/* Automap colors */
+#define BACKGROUND	PARCH
+#define YOURCOLORS	WHITE
+#define YOURRANGE	0
+#define WALLCOLORS	REDS
+#define WALLRANGE	REDRANGE
+#define TSWALLCOLORS	GRAYS
+#define TSWALLRANGE	GRAYSRANGE
+#define FDWALLCOLORS	BROWNS
+#define FDWALLRANGE	BROWNRANGE
+#define CDWALLCOLORS	YELLOWS
+#define CDWALLRANGE	YELLOWRANGE
+#define THINGCOLORS	GREENS
+#define THINGRANGE	GREENRANGE
+#define SECRETWALLCOLORS WALLCOLORS
+#define SECRETWALLRANGE	WALLRANGE
+#define GRIDCOLORS	(GRAYS + GRAYSRANGE/2)
+#define GRIDRANGE	0
+#define XHAIRCOLORS	GRAYS
+
+/* drawing stuff */
+#define	FB		0
+
+#define AM_PANDOWNKEY	KEY_DOWNARROW
+#define AM_PANUPKEY	KEY_UPARROW
+#define AM_PANRIGHTKEY	KEY_RIGHTARROW
+#define AM_PANLEFTKEY	KEY_LEFTARROW
+
+#define AM_ZOOMINKEY	'='
+#define AM_ZOOMOUTKEY	'-'
+#define AM_STARTKEY	KEY_TAB
+#define AM_ENDKEY	KEY_TAB
+#define AM_GOBIGKEY	'0'
+#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;
+
+#endif	/* __AMMAP_H__ */
--- /dev/null
+++ b/audio_plugin.h
@@ -1,0 +1,63 @@
+/*  XMMS - Cross-platform multimedia player
+ *  Copyright (C) 1998-1999  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef AUDIO_PLUGIN_H
+#define AUDIO_PLUGIN_H
+
+
+typedef enum
+{
+	FMT_U8, FMT_S8, FMT_U16_LE, FMT_U16_BE, FMT_U16_NE, FMT_S16_LE, FMT_S16_BE, FMT_S16_NE
+}
+AFormat;
+
+typedef struct
+{
+	void *handle;			/* Filled in by xmms */
+	char *filename;			/* Filled in by xmms */
+	const char *description;	/* The description that is shown in the preferences box */
+	void (*init) (void);
+	void (*about) (void);		/* Show the about box */
+	void (*configure) (void);	/* Show the configuration dialog */
+	void (*get_volume) (int *l, int *r);
+	void (*set_volume) (int l, int r);	/* Set the volume */
+
+	int (*open_audio) (AFormat fmt, int rate, int nch);
+					/* Open the device, if the device can't handle the given 
+					   parameters the plugin is responsible for downmixing
+					   the data to the right format before outputting it */
+
+	void (*write_audio) (void *ptr, int length);
+					/* The input plugin calls this to write data to the output buffer */
+
+	void (*close_audio) (void);	/* No comment... */
+	void (*flush) (int flushtime);	/* Flush the buffer and set the plugins internal timers to time */
+	void (*pause) (short paused);	/* Pause or unpause the output */
+	int (*buffer_free) (void);	/* Return the amount of data that can be written to the buffer,
+					   two calls to this without a call to write_audio should make
+					   the plugin output audio directly */
+	int (*buffer_playing) (void);	/* Returns TRUE if the plugin currently is playing some audio,
+					   otherwise return FALSE */
+	int (*output_time) (void);	/* Return the current playing time */
+	int (*written_time) (void);	/* Return the length of all the data that has been written to
+					   the buffer */
+}
+OutputPlugin;
+
+#endif	/* AUDIO_PLUGIN_H */
+
--- /dev/null
+++ b/ct_chat.c
@@ -1,0 +1,477 @@
+// ct_chat.c
+//
+// Chat mode
+//
+
+#include "h2stdinc.h"
+#include <ctype.h>
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+#ifdef RENDER3D
+#include "ogl_def.h"
+#endif
+
+#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_BLUE		'b'
+#define CT_KEY_RED		'r'
+#define CT_KEY_YELLOW		'y'
+#define CT_KEY_GREEN		'g'
+#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;
+
+char chat_macros[10][80] =
+{
+	HUSTR_CHATMACRO0,
+	HUSTR_CHATMACRO1,
+	HUSTR_CHATMACRO2,
+	HUSTR_CHATMACRO3,
+	HUSTR_CHATMACRO4,
+	HUSTR_CHATMACRO5,
+	HUSTR_CHATMACRO6,
+	HUSTR_CHATMACRO7,
+	HUSTR_CHATMACRO8,
+	HUSTR_CHATMACRO9
+};
+
+// Private data
+
+static void CT_queueChatChar(char ch);
+static void CT_ClearChatMessage(int player);
+static void CT_AddChar(int player, char c);
+static void CT_BackSpace(int player);
+
+static int head, tail;
+static byte ChatQueue[QUEUESIZE];
+static int chat_dest[MAXPLAYERS];
+static char chat_msg[MAXPLAYERS][MESSAGESIZE];
+static char plr_lastmsg[MAXPLAYERS][MESSAGESIZE+9]; /* add in the length of the pre-string */
+static int msgptr[MAXPLAYERS];
+static int msglen[MAXPLAYERS];
+
+static int FontABaseLump;
+
+static const char *CT_FromPlrText[MAXPLAYERS] =
+{
+	"GREEN:  ",
+	"YELLOW:  ",
+	"RED:  ",
+	"BLUE:  "
+};
+
+static boolean altdown, 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)
+{
+	const char *macro;
+	int sendtarget;
+
+	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)
+	{
+		sendtarget = 0;
+		if (ev->data1 == CT_KEY_ALL)
+		{
+			sendtarget = CT_PLR_ALL;
+		}
+		else if (ev->data1 == CT_KEY_GREEN)
+		{
+			sendtarget = CT_PLR_GREEN;
+		}
+		else if (ev->data1 == CT_KEY_YELLOW)
+		{
+			sendtarget = CT_PLR_YELLOW;
+		}
+		else if (ev->data1 == CT_KEY_RED)
+		{
+			sendtarget = CT_PLR_RED;
+		}
+		else if (ev->data1 == CT_KEY_BLUE)
+		{
+			sendtarget = CT_PLR_BLUE;
+		}
+		if (sendtarget == 0 || (sendtarget != CT_PLR_ALL && !playeringame[sendtarget - 1])
+			|| sendtarget == consoleplayer + 1)
+		{
+			return false;
+		}
+		CT_queueChatChar(sendtarget);
+		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, 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], "-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 = (patch_t *) W_CacheLumpNum(FontABaseLump + chat_msg[consoleplayer][i] - 33, PU_CACHE);
+#ifdef RENDER3D
+				OGL_DrawPatch_CS(x, 10, FontABaseLump + chat_msg[consoleplayer][i] - 33);
+#else
+				V_DrawPatch(x, 10, patch);
+#endif
+				x += SHORT(patch->width);
+			}
+		}
+#ifdef RENDER3D
+		OGL_DrawPatch_CS(x, 10, W_GetNumForName("FONTA59"));
+#else
+		patch = (patch_t *) W_CacheLumpName("FONTA59", PU_CACHE);
+		V_DrawPatch(x, 10, patch);
+#endif
+		BorderTopRefresh = true;
+		UpdateState |= I_MESSAGES;
+	}
+}
+
+//===========================================================================
+//
+// CT_queueChatChar
+//
+//===========================================================================
+
+static 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
+//
+//===========================================================================
+
+static 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 = (patch_t *) W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
+		msglen[player] += SHORT(patch->width);
+	}
+}
+
+//===========================================================================
+//
+// CT_BackSpace
+//
+// 	Backs up a space, when the user hits (obviously) backspace
+//===========================================================================
+
+static 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 = (patch_t *) W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
+		msglen[player] -= SHORT(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.
+//===========================================================================
+
+static void CT_ClearChatMessage(int player)
+{
+	memset(chat_msg[player], 0, MESSAGESIZE);
+	msgptr[player] = 0;
+	msglen[player] = 0;
+}
+
--- /dev/null
+++ b/d_main.c
@@ -1,0 +1,1069 @@
+// D_main.c
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#if defined(__WATCOMC__) && defined(_DOS)
+#include <dos.h>
+#include <graph.h>
+#include <direct.h>
+#endif
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+#include "v_compat.h"
+
+// MACROS ------------------------------------------------------------------
+
+#define MAXWADFILES		20
+#define SHAREWAREWADNAME	"heretic1.wad"
+
+#ifdef RENDER3D
+#define V_DrawPatch(x,y,p)		OGL_DrawPatch((x),(y),(p))
+#define V_DrawRawScreen(a)		OGL_DrawRawScreen((a))
+#endif
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+void D_CheckNetGame(void);
+void G_BuildTiccmd(ticcmd_t *cmd);
+void F_Drawer(void);
+boolean F_Responder(event_t *ev);
+void R_ExecuteSetViewSize(void);
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+void D_ProcessEvents(void);
+void D_DoAdvanceDemo(void);
+void D_AdvanceDemo (void);
+void D_PageDrawer (void);
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+extern boolean MenuActive;
+extern boolean askforquit;
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+const char *basePath = DUMMY_BASEPATH;
+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 singletics;			// debug flag to cancel adaptiveness
+boolean noartiskip;			// whether shift-enter skips an artifact
+
+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 demosequence;
+static int pagetic;
+static const char *pagename;
+
+static const char *wadfiles[MAXWADFILES + 1] =
+{
+	"heretic.wad",
+	"texture1.lmp",
+	"texture2.lmp",
+	"pnames.lmp",
+	NULL	/* the last entry MUST be NULL */
+};
+
+// CODE --------------------------------------------------------------------
+
+#if !(defined(__WATCOMC__) || defined(__DJGPP__) || defined(_WIN32))
+char *strlwr (char *str)
+{
+	char	*c;
+	c = str;
+	while (*c)
+	{
+		*c = tolower(*c);
+		c++;
+	}
+	return str;
+}
+
+char *strupr (char *str)
+{
+	char	*c;
+	c = str;
+	while (*c)
+	{
+		*c = toupper(*c);
+		c++;
+	}
+	return str;
+}
+
+int filelength(int handle)
+{
+	Dir *d;
+	int length;
+
+	d = dirfstat(handle);
+	if (d == nil)
+	{
+		I_Error("Error fstating");
+	}
+	length = d->length;
+	free(d);
+	return length;
+}
+#endif
+
+//==========================================================================
+//
+// Fixed Point math
+//
+//==========================================================================
+
+#if defined(_HAVE_FIXED_ASM)
+
+#if defined(__i386__) || defined(__386__) || defined(_M_IX86)
+#if defined(__GNUC__) && !defined(_INLINE_FIXED_ASM)
+fixed_t	FixedMul (fixed_t a, fixed_t b)
+{
+	fixed_t retval;
+	__asm__ __volatile__(
+		"imull  %%edx			\n\t"
+		"shrdl  $16, %%edx, %%eax	\n\t"
+		: "=a" (retval)
+		: "a" (a), "d" (b)
+		: "cc"
+	);
+	return retval;
+}
+
+fixed_t	FixedDiv2 (fixed_t a, fixed_t b)
+{
+	fixed_t retval;
+	__asm__ __volatile__(
+		"cdq				\n\t"
+		"shldl  $16, %%eax, %%edx	\n\t"
+		"sall   $16, %%eax		\n\t"
+		"idivl  %%ebx			\n\t"
+		: "=a" (retval)
+		: "a" (a), "b" (b)
+		: "%edx", "cc"
+	);
+	return retval;
+}
+#endif	/* GCC and !_INLINE_FIXED_ASM */
+#endif	/* x86 */
+
+#else	/* C-only versions */
+
+fixed_t FixedMul (fixed_t a, fixed_t b)
+{
+	return ((int64_t) a * (int64_t) b) >> 16;
+}
+
+fixed_t FixedDiv2 (fixed_t a, fixed_t b)
+{
+	if (!b)
+		return 0;
+	return (fixed_t) (((double) a / (double) b) * FRACUNIT);
+}
+#endif
+
+fixed_t FixedDiv (fixed_t a, fixed_t b)
+{
+	if ((abs(a) >> 14) >= abs(b))
+	{
+		return ((a^b) < 0 ? H2MININT : H2MAXINT);
+	}
+	return (FixedDiv2(a, b));
+}
+
+//==========================================================================
+//
+// Byte swap functions
+//
+//==========================================================================
+
+int16_t ShortSwap (int16_t x)
+{
+	return (int16_t) (((uint16_t)x << 8) | ((uint16_t)x >> 8));
+}
+
+int32_t LongSwap (int32_t x)
+{
+	return (int32_t) (((uint32_t)x << 24) | ((uint32_t)x >> 24) |
+			  (((uint32_t)x & (uint32_t)0x0000ff00UL) << 8) |
+			  (((uint32_t)x & (uint32_t)0x00ff0000UL) >> 8));
+}
+
+/*
+===============================================================================
+
+							EVENT HANDLING
+
+Events are asyncronous inputs generally generated by the game user.
+
+Events can be discarded if no responder claims them
+
+===============================================================================
+*/
+
+//---------------------------------------------------------------------------
+//
+// 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;
+
+	while (eventtail != eventhead)
+	{
+		ev = &events[eventtail];
+		if (F_Responder(ev))
+		{
+			goto _next_ev;
+		}
+		if (MN_Responder(ev))
+		{
+			goto _next_ev;
+		}
+		G_Responder(ev);
+	_next_ev:
+		eventtail = (eventtail + 1) & (MAXEVENTS - 1);
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// 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 D_Display(void)
+{
+	// Change the view size if needed
+	if (setsizeneeded)
+	{
+		R_ExecuteSetViewSize();
+	}
+
+//
+// do buffered drawing
+//
+	switch (gamestate)
+	{
+	case GS_LEVEL:
+		if (!gametic)
+			break;
+#if  AM_TRANSPARENT
+		R_RenderPlayerView (&players[displayplayer]);
+#endif
+		if (automapactive)
+			AM_Drawer ();
+#if !AM_TRANSPARENT
+		else
+		{
+			R_RenderPlayerView (&players[displayplayer]);
+		}
+#endif
+		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, (PATCH_REF)WR_CacheLumpName("PAUSED", PU_CACHE));
+		}
+		else
+		{
+			V_DrawPatch(160, 70, (PATCH_REF)WR_CacheLumpName("PAUSED", PU_CACHE));
+		}
+	}
+
+#ifdef RENDER3D
+	if (OGL_DrawFilter())
+		BorderNeedRefresh = true;
+#endif
+
+	// 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];
+		snprintf(filename, sizeof(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
+
+===============================================================================
+*/
+
+/*
+================
+=
+= 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((BYTE_REF)WR_CacheLumpName(pagename, PU_CACHE));
+	if (demosequence == 1)
+	{
+		V_DrawPatch(4, 160, (PATCH_REF)WR_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;
+
+	p = M_CheckParm ("-recordfrom");
+	if (!p || p >= myargc - 2)
+		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]);
+	D_DoomLoop(); // never returns
+}
+
+/*
+===============
+=
+= D_AddFile
+=
+===============
+*/
+
+void D_AddFile(const char *file)
+{
+	int i = 0;
+	char *newwad;
+
+	while (wadfiles[i])
+	{
+		if (++i == MAXWADFILES)
+			I_Error ("MAXWADFILES reached for %s", file);
+	}
+	newwad = (char *) malloc(strlen(file) + 1);
+	strcpy(newwad, file);
+	wadfiles[i] = newwad;
+	if (++i <= MAXWADFILES)
+		wadfiles[i] = NULL;
+}
+
+//==========================================================
+//
+//  Startup Thermo code
+//  FIXME : MOVE OR REMOVE THIS...
+//==========================================================
+#if defined(__WATCOMC__) && defined(_DOS)
+
+#define MSG_Y		9
+/*
+#define THERM_X		15
+#define THERM_Y		16
+#define THERMCOLOR	3
+*/
+#define THERM_X		14
+#define THERM_Y		14
+
+static int thermMax;
+static int thermCurrent;
+
+//
+//  Heretic startup screen shit
+//
+static byte *hscreen;
+static char *startup;	// * to text screen
+static char smsg[80];	// status bar line
+static char tmsg[300];
+
+static void hgotoxy(int x,int y)
+{
+	hscreen = (byte *)(0xb8000 + y*160 + x*2);
+}
+
+static void hput(unsigned char c, unsigned char a)
+{
+	*hscreen++ = c;
+	*hscreen++ = a;
+}
+
+static void hprintf(const char *string, unsigned char a)
+{
+	int i;
+
+	if (debugmode)
+	{
+		puts(string);
+		return;
+	}
+	for (i = 0; i < strlen(string); i++)
+	{
+		hput(string[i], a);
+	}
+}
+
+static void drawstatus(void)
+{
+	if(debugmode)
+	{
+		return;
+	}
+	_settextposition(25, 2);
+	_setbkcolor(1);
+	_settextcolor(15);
+	_outtext(smsg);
+	_settextposition(25, 1);
+}
+
+static void status(const char *string)
+{
+	strcat(smsg, string);
+	drawstatus();
+}
+
+static void DrawThermo(void)
+{
+	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
+}
+
+static 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);
+}
+
+void tprintf(const char *msg, int initflag)
+{
+# if 0
+	char temp[80];
+	int i, start, add;
+
+	if (debugmode)
+	{
+		printf(msg);
+		return;
+	}
+	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();
+# endif
+}
+
+void CheckAbortStartup(void)
+{
+#if defined(__WATCOMC__) && defined(_DOS)
+	extern int lastpress;
+	if (lastpress == 1)
+	{ // Abort if escape pressed
+		union REGS regs;
+		I_ShutdownKeyboard();
+		regs.x.eax = 0x3;
+		int386(0x10, &regs, &regs);
+		printf("Exited from HERETIC.\n");
+		exit(1);
+	}
+#endif
+}
+
+void IncThermo(void)
+{
+	thermCurrent++;
+	DrawThermo();
+	CheckAbortStartup();
+}
+
+void InitThermo(int max)
+{
+	thermMax = max;
+	thermCurrent = 0;
+}
+
+#else
+
+#define hgotoxy(x,y)	do {} while (0)
+#define hprintf(s,a)	puts((s))
+#define status(s)	puts((s))
+#define DrawThermo()	do {} while (0)
+void tprintf(const char *msg, int initflag)
+{
+# if 0
+	printf(msg);
+# endif
+}
+void CheckAbortStartup(void) {}
+void IncThermo(void) {}
+void InitThermo(int x) {}
+#endif
+
+//---------------------------------------------------------------------------
+//
+// PROC D_DoomMain
+//
+//---------------------------------------------------------------------------
+
+void D_DoomMain(void)
+{
+	int p, e, m;
+	char file[MAX_OSPATH];
+	boolean devMap;
+
+	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
+	if (!W_IsWadPresent(wadfiles[0]))
+	{ // Change to look for shareware wad
+		wadfiles[0] = SHAREWAREWADNAME;
+	}
+
+	// -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];
+		snprintf(file, sizeof(file), "%se%cm%c.wad", DEVMAPDIR, 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)
+	{
+		snprintf(file, sizeof(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(CONFIG_FILE_NAME);
+
+	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;
+	}
+
+#if defined(__WATCOMC__) && defined(_DOS)
+	I_StartupKeyboard();
+	I_StartupJoystick();
+	startup = (char *) W_CacheLumpName("LOADING", PU_CACHE);
+	blitStartup();
+
+	//  Build status bar line!
+	smsg[0] = 0;
+#endif
+	if (deathmatch)
+		status("DeathMatch...");
+	if (nomonsters)
+		status("No Monsters...");
+	if (respawnparm)
+		status("Respawning...");
+	if (autostart)
+	{
+		char temp[64];
+		snprintf(temp, sizeof(temp), "Warp to Episode %d, Map %d, Skill %d ",
+					startepisode, startmap, startskill + 1);
+		status(temp);
+	}
+
+	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();
+
+#if defined(__WATCOMC__) && defined(_DOS)
+	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)
+	{
+		G_LoadGame(atoi(myargv[p+1]));
+	}
+
+	// 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();
+		}
+	}
+#if defined(__WATCOMC__) && defined(_DOS)
+	_settextcursor(0x0607); // bring the cursor back
+#endif
+	D_DoomLoop(); // Never returns
+}
+
--- /dev/null
+++ b/d_net.c
@@ -1,0 +1,801 @@
+// d_net.c
+// This version has the fixed ticdup code
+
+#include "h2stdinc.h"
+#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];
+
+int		maketic;
+int		lastnettic, skiptics;
+int		ticdup;
+int		maxsend;			/* BACKUPTICS/(2*ticdup)-1 */
+
+static boolean	nodeingame[MAXNETNODES];	/* set false as nodes leave game */
+static boolean	remoteresend[MAXNETNODES];	/* set when local needs tics */
+static int	resendto[MAXNETNODES];		/* set when remote needs tics */
+static int	resendcount[MAXNETNODES];
+
+static int	nodeforplayer[MAXPLAYERS];
+
+static boolean	reboundpacket;
+static doomdata_t	reboundstore;
+
+/* extern functions */
+void D_ProcessEvents (void);
+void G_BuildTiccmd (ticcmd_t *cmd);
+void D_DoAdvanceDemo (void);
+
+
+//============================================================================
+
+
+static int NetbufferSize (void)
+{
+	return (int) ((ptrdiff_t)&(((doomdata_t *)0)->cmds[netbuffer->numtics]));
+}
+
+static unsigned int NetbufferChecksum (void)
+{
+	unsigned int	c;
+	int		i, l;
+
+	c = 0x1234567;
+
+#if defined(NeXT) || defined(NORMALUNIX)
+	return 0;	/* byte order problems */
+#endif
+
+	l = (NetbufferSize() - (int)((ptrdiff_t) &(((doomdata_t *)0)->retransmitfrom))) / 4;
+	for (i = 0; i < l; i++)
+		c += ((unsigned int *)&netbuffer->retransmitfrom)[i] * (i + 1);
+
+	return c & NCMD_CHECKSUM;
+}
+
+static 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
+=
+==============
+*/
+
+static 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
+=
+==============
+*/
+
+static 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
+=
+===================
+*/
+
+static char	exitmsg[80];
+
+static 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
+=============
+*/
+
+static 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
+=
+=====================
+*/
+
+static void CheckAbort (void)
+{
+	event_t	*ev;
+	int		stoptic;
+
+	stoptic = I_GetTime () + 2;
+	while (I_GetTime() < stoptic)
+		I_StartTic ();
+
+	I_StartTic ();
+	while (eventtail != eventhead)
+	{
+		ev = &events[eventtail];
+		if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
+			I_Error ("Network game synchronization aborted.");
+		eventtail = (eventtail + 1) & (MAXEVENTS - 1);
+	}
+}
+
+/*
+=====================
+=
+= D_ArbitrateNetStart
+=
+=====================
+*/
+
+static 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 & 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
+	//	printf ("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 = H2MAXINT;
+	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 ("+");
+			}
+		}
+	}
+
+	//
+	// wait for new tics if needed
+	//
+	while (lowtic < gametic/ticdup + counts)
+	{
+		NetUpdate ();
+		lowtic = H2MAXINT;
+
+		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/doomdata.h
@@ -1,0 +1,175 @@
+// DoomData.h
+
+/* all external data is defined here
+ * most of the data is loaded into different structures at run time
+ */
+
+#ifndef __DOOMDATA__
+#define __DOOMDATA__
+
+/* ---- 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
+};
+
+#pragma pack on
+
+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;
+
+/* bbox coordinates */
+enum
+{
+	BOXTOP,
+	BOXBOTTOM,
+	BOXLEFT,
+	BOXRIGHT
+};
+
+#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;
+	int32_t		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;
+
+#pragma pack off
+
+#endif	/* __DOOMDATA__ */
+
--- /dev/null
+++ b/doomdef.h
@@ -1,0 +1,1382 @@
+// DoomDef.h
+
+#ifndef __DOOMDEF__
+#define __DOOMDEF__
+
+/* if rangecheck is undefined, most parameter
+ * validation debugging code will not be compiled
+ */
+#ifndef NORANGECHECKING
+#define RANGECHECK	1
+#endif
+
+#define __STRINGIFY(x) #x
+#define STRINGIFY(x) __STRINGIFY(x)
+
+#define VERSION			130
+#define VERSION_TEXT		"v1.3"
+
+#define VERSION_MAJ	0
+#define VERSION_MIN	2
+#define VERSION_PATCH	4
+#define HHERETIC_VERSION	"v" STRINGIFY(VERSION_MAJ) "." STRINGIFY(VERSION_MIN) "." STRINGIFY(VERSION_PATCH)
+
+#if defined(__linux)
+#define VERSION_PLATFORM "Linux"
+#elif defined (__FreeBSD__)
+#define VERSION_PLATFORM "FreeBSD"
+#elif defined (_WIN32)
+#define VERSION_PLATFORM "Windows"
+#else
+#define VERSION_PLATFORM "Unknown"
+#endif
+
+/* compatibility definitions: */
+#if defined(_WIN32) && !defined(F_OK)
+/* values for the mode argument of access(). MS does not define them.
+   these aren't in h2stdinc.h, because not all files include io.h or
+   unistd.h */
+#define	R_OK	4		/* Test for read permission.  */
+#define	W_OK	2		/* Test for write permission.  */
+#define	X_OK	1		/* Test for execute permission.  */
+#define	F_OK	0		/* Test for existence.  */
+#endif
+
+/* max length of a filesystem pathname	*/
+#define	MAX_OSPATH		256
+
+#define	DATA_ENVVAR		"HERETIC_DATA"
+#define	H_USERDIR		".hheretic"
+
+/* path to the user directory with a trailing
+ * directory separator character. initialized
+ * to DUMMY_BASEPATH string, which is "./" or
+ * empty string "", so that it will have no
+ * effect when user directories are disabled.
+ */
+extern	const char		*basePath;
+#define	DUMMY_BASEPATH		""
+
+#define	SAVEGAMENAME		"hticsav"
+#define	CONFIG_FILE_NAME	"heretic.cfg"
+
+/* the directory that holds development maps */
+/* for the -wart # # command. */
+#define	DEVMAPDIR		"/heretic/data/"
+
+
+#ifdef __WATCOMC__
+#include <malloc.h>
+#define strcasecmp strcmpi
+#define strncasecmp strnicmp
+#endif
+
+/* 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_TAB			9
+#define	KEY_ENTER		13
+#define	KEY_ESCAPE		27
+#define	KEY_SPACE		32	/* 0x20 */
+#define	KEY_MINUS		45	/* 0x2D */
+
+#define	KEY_FIVE		53	/* 0x35 */
+#define	KEY_SIX			54	/* 0x36 */
+#define	KEY_SEVEN		55	/* 0x37 */
+#define	KEY_EIGHT		56	/* 0x38 */
+#define	KEY_NINE		57	/* 0x39 */
+#define	KEY_ZERO		48	/* 0x30 */
+
+#define	KEY_EQUALS		61	/* 0x3D */
+
+/* These added by S.A. */
+#define KEY_LEFTBRACKET		91
+#define KEY_RIGHTBRACKET	93
+#define KEY_BACKQUOTE		96
+#define KEY_QUOTEDBL		34
+#define KEY_QUOTE		39
+#define KEY_SEMICOLON		59
+#define KEY_PERIOD		46
+#define KEY_COMMA		44
+#define KEY_SLASH		47
+
+#define	KEY_BACKSLASH		92	/* 0x5C */
+
+#define	KEY_BACKSPACE		127	/* 0x7F */
+
+#define	KEY_UPARROW		128	/* 0x80 */
+#define	KEY_DOWNARROW		129
+#define	KEY_LEFTARROW		130
+#define	KEY_RIGHTARROW		131
+
+#define	KEY_ALT			132
+#define	KEY_LALT		KEY_ALT
+#define	KEY_RALT		KEY_ALT
+
+#define	KEY_CTRL		133
+#define	KEY_LCTRL		KEY_CTRL
+#define	KEY_RCTRL		KEY_CTRL
+
+#define	KEY_SHIFT		134
+#define	KEY_LSHIFT		KEY_SHIFT
+#define	KEY_RSHIFT		KEY_SHIFT
+
+#define	KEY_F1			135
+#define	KEY_F2			136
+#define	KEY_F3			137
+#define	KEY_F4			138
+#define	KEY_F5			139
+#define	KEY_F6			140
+#define	KEY_F7			141
+#define	KEY_F8			142
+#define	KEY_F9			143
+#define	KEY_F10			144
+#define	KEY_F11			145
+#define	KEY_F12			146
+#define	KEY_INS			147
+#define	KEY_DEL			148
+#define	KEY_PGDN		149
+#define	KEY_PGUP		150
+#define	KEY_HOME		151
+#define	KEY_END			152
+
+#define KEY_PAUSE		255	/* 0xFF */
+
+/* mouse buttons */
+#define	KEY_MOUSE1		200	/* 0xC8 */
+#define	KEY_MOUSE2		201	/* right mouse button			*/
+#define	KEY_MOUSE3		202	/* middle mouse button			*/
+#define	KEY_MWHEELUP		203	/* wheel-up as a virtual button		*/
+#define	KEY_MWHEELDOWN		204	/* wheel-down as a virtual button	*/
+#define	KEY_MOUSE4		205	/* thumb buttons			*/
+#define	KEY_MOUSE5		206	/* thumb buttons			*/
+
+/* joystick buttons */
+#define	KEY_JOY1		207
+#define	KEY_JOY2		208
+#define	KEY_JOY3		209
+#define	KEY_JOY4		210
+
+/* aux keys, for multi-buttoned joysticks */
+#define	KEY_AUX1		211
+#define	KEY_AUX2		212
+#define	KEY_AUX3		213
+#define	KEY_AUX4		214
+#define	KEY_AUX5		215
+#define	KEY_AUX6		216
+#define	KEY_AUX7		217
+#define	KEY_AUX8		218
+#define	KEY_AUX9		219
+#define	KEY_AUX10		220
+#define	KEY_AUX11		221
+#define	KEY_AUX12		222
+#define	KEY_AUX13		223
+#define	KEY_AUX14		224
+#define	KEY_AUX15		225
+#define	KEY_AUX16		226
+#define	KEY_AUX17		227
+#define	KEY_AUX18		228
+#define	KEY_AUX19		229
+#define	KEY_AUX20		230
+#define	KEY_AUX21		231
+#define	KEY_AUX22		232
+#define	KEY_AUX23		233
+#define	KEY_AUX24		234
+#define	KEY_AUX25		235
+#define	KEY_AUX26		236
+#define	KEY_AUX27		237
+#define	KEY_AUX28		238
+#define	KEY_AUX29		239
+#define	KEY_AUX30		240
+#define	KEY_AUX31		241
+#define	KEY_AUX32		242
+
+#define	MAXKEYS				256
+
+
+#define	FINEANGLES		8192
+#define	FINEMASK		(FINEANGLES - 1)
+#define	ANGLETOFINESHIFT	19	/* 0x100000000 to 0x2000 */
+
+/*
+===============================================================================
+
+						GLOBAL TYPES
+
+===============================================================================
+*/
+
+#define NUMARTIFCTS	28
+#define MAXPLAYERS	4
+#define TICRATE		35		/* number of tics / second */
+#define TICSPERSEC	35
+
+#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
+
+#pragma pack on
+
+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,
+	ev_char,
+} 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
+{
+	signed char	forwardmove;	/* *2048 for move */
+	signed 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;
+
+typedef struct
+{
+	const char	*name;
+	int	*location;
+	int	defaultvalue;
+	int	minvalue;
+	int	maxvalue;
+} default_t;
+
+typedef struct
+{
+	const char	*name;
+	char	*location;	/* pointer to an 80 char array, null terminated */
+	char	*defaultvalue;	/* backup of the default value. malloc'ed at init */
+} default_str_t;
+
+/*
+===============================================================================
+
+							MAPOBJ DATA
+
+===============================================================================
+*/
+
+/* think_t is a function pointer to a routine to handle an actor */
+typedef void (*think_t) (void*);
+
+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 */
+
+	/* be doubly careful with these two: they may be used to store
+	 * a state or the address of an mobj_t or player_t structure!!!
+	 */
+	intptr_t		special1;	/* Special info */
+	intptr_t		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 */
+	const 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 int	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
+{
+	int32_t		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		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	int		ticcount;
+
+extern	ticcmd_t	netcmds[MAXPLAYERS][BACKUPTICS];
+extern	int		ticdup;
+
+extern	ticcmd_t	localcmds[BACKUPTICS];
+extern	int		rndindex;
+extern	int		gametic, maketic;
+extern	int		nettics[MAXNETNODES];
+
+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		*screens;	/* 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);
+
+#undef	_HAVE_FIXED_ASM
+
+#if !defined(_DISABLE_ASM)
+#if defined(__i386__) || defined(__386__) || defined(_M_IX86)
+#if defined(__WATCOMC__)
+
+#define	_HAVE_FIXED_ASM			1
+
+#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]
+
+#elif defined(__GNUC__)
+
+#define	_HAVE_FIXED_ASM			1
+
+# if defined(_INLINE_FIXED_ASM)
+# if (__GNUC__ == 2) && (__GNUC_MINOR__ <= 91)
+# define FixedMul(fa,fb) ({ int __value, __fb = (fb);	\
+	__asm__("imul %%ebx; shrd $16,%%edx,%%eax"	\
+		: "=a" (__value)			\
+		: "0" (fa), "b" (__fb)			\
+		: "eax", "edx" ); __value; })
+
+# define FixedDiv2(fa,fb) ({ int __value;		\
+	__asm__("cdq; shld $16,%%eax,%%edx; sall $16,%%eax; idiv %%ebx"	\
+		: "=a" (__value)			\
+		: "0" (fa), "b" (fb)			\
+		: "eax", "edx" ); __value; })
+
+# else	/* GCC > 2.91.x */
+# define FixedMul(fa,fb) ({ int __value, __fb = (fb);	\
+	__asm__("imul %%ebx; shrd $16,%%edx,%%eax"	\
+		: "=a" (__value)			\
+		: "0" (fa), "b" (__fb)			\
+		: "edx" ); __value; })
+
+# define FixedDiv2(fa,fb) ({ int __value;		\
+	__asm__("cdq; shld $16,%%eax,%%edx; sall $16,%%eax; idiv %%ebx"	\
+		: "=a" (__value)			\
+		: "0" (fa), "b" (fb)			\
+		: "edx" ); __value; })
+
+# endif	/* GCC/EGCS versions */
+
+# endif	/* _INLINE_FIXED_ASM */
+#endif
+#endif	/* X86 */
+#endif	/* !_DISABLE_ASM */
+
+#define FIX2FLT(x)	((float)((x)>>FRACBITS) + (float)((x)&(FRACUNIT-1)) / (float)(FRACUNIT))
+#define Q_FIX2FLT(x)	((float)((x)>>FRACBITS))
+
+
+int16_t ShortSwap(int16_t) __attribute__((__const__));
+int32_t LongSwap (int32_t) __attribute__((__const__));
+
+#if defined(__GNUC__)
+static inline __attribute__((__const__)) int16_t _H2_SWAP16(int16_t x)
+{
+	return (int16_t) (((uint16_t)x << 8) | ((uint16_t)x >> 8));
+}
+static inline __attribute__((__const__)) int32_t _H2_SWAP32(int32_t x)
+{
+	return (int32_t) (((uint32_t)x << 24) | ((uint32_t)x >> 24) |
+			  (((uint32_t)x & (uint32_t)0x0000ff00UL) << 8) |
+			  (((uint32_t)x & (uint32_t)0x00ff0000UL) >> 8));
+}
+#endif	/* GCC */
+
+/*
+#ifdef __BIG_ENDIAN__
+*/
+#ifdef WORDS_BIGENDIAN
+# ifdef __GNUC__
+#  define SHORT(a)	_H2_SWAP16((a))
+#  define LONG(a)	_H2_SWAP32((a))
+# else
+#  define SHORT(x)	ShortSwap((x))
+#  define LONG(x)	LongSwap((x))
+# endif
+#else
+#define SHORT(x)	(x)
+#define LONG(x)		(x)
+#endif
+
+/* ---- READ_INT16/32 --- */
+
+#define READ_INT16(b)	((b)[0] | ((b)[1] << 8))
+#define READ_INT32(b)	((b)[0] | ((b)[1] << 8) | ((b)[2] << 16) | ((b)[3] << 24))
+#define INCR_INT16(b)	(b)+=2
+#define INCR_INT32(b)	(b)+=4
+
+
+/* ----- 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
+
+#define	ZONEID		0x1d4a11
+
+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_CheckHeap (void);
+void Z_ChangeTag2 (void *ptr, int tag);
+void Z_DumpHeap (int lowtag, int hightag);
+void Z_FileDumpHeap (FILE *f);
+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 != ZONEID)	\
+		I_Error("Z_CT at %s:%i", __FILE__, __LINE__);			\
+	Z_ChangeTag2((p),(t));							\
+};
+
+/* ------- WADFILE ------- */
+typedef struct
+{
+	char		name[8];
+	int		handle, position, size;
+} lumpinfo_t;
+
+extern	lumpinfo_t	*lumpinfo;
+extern	int		numlumps;
+extern	const char	*waddir;
+
+boolean W_IsWadPresent(const char *filename);
+void W_InitMultipleFiles(const char **filenames);
+int W_CheckNumForName(const char *name);
+int W_GetNumForName(const char *name);
+int W_LumpLength(int lump);
+void W_ReadLump(int lump, void *dest);
+void *W_CacheLumpNum(int lump, int tag);
+void *W_CacheLumpName(const char *name, int tag);
+
+
+/* ---------- BASE LEVEL ---------- */
+
+void D_DoomMain(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 IncThermo(void);
+void InitThermo(int max);
+void tprintf(const char *string, int initflag);
+
+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);
+
+#if !(defined(__WATCOMC__) || defined(__DJGPP__) || defined(_WIN32))
+char *strupr (char *str);
+char *strlwr (char *str);
+int filelength(int handle);
+#endif
+
+
+/* --------- 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 (const char *error, ...) __attribute__((__format__(__printf__,1,2), __noreturn__));
+/* called by anything that can generate a terminal error
+ * bad exit with diagnostic message
+ */
+
+void I_Quit (void) __attribute__((__noreturn__));
+/* 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 */
+
+extern	boolean		useexterndriver;
+
+#if defined(__WATCOMC__) && defined(_DOS)
+#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;
+
+void I_Tactile (int on, int off, int total);
+#endif	/* externdriver, DOS */
+
+
+/* ---- 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 (const 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, const char *description);
+/* called by M_Responder */
+
+void G_RecordDemo (skill_t skill, int numplayers, int episode,
+		   int map, const char *name);
+/* only called by startup code */
+
+void G_PlayDemo (const char *name);
+void G_TimeDemo (const 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);
+
+
+/* ------- SV_SAVE ------- */
+
+#define SAVEVERSIONSIZE		16
+#define SAVESTRINGSIZE		24
+
+void SV_SaveGame(int slot, const char *description);
+void SV_LoadGame(int slot);
+
+
+/* ----- 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 */
+
+
+/* ------- 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 (const char *name);
+
+int R_TextureNumForName (const char *name);
+int R_CheckTextureNumForName (const char *name);
+/* called by P_Ticker for switches and animations
+ * returns the texture number for the texture name
+ */
+
+
+/* ---- MISC ---- */
+extern	const char	**myargv;
+extern	int		myargc;
+
+int M_CheckParm(const 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_ExtractFileBase(const 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 */
+
+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, const void *source, int length);
+int M_ReadFile(char const *name, void **buffer);
+
+void M_ScreenShot (void);
+
+void M_LoadDefaults(const char *fileName);
+
+void M_SaveDefaults (void);
+
+int M_DrawText (int x, int y, boolean direct, const char *string);
+
+
+/* ---- Interlude (IN_lude.c) ---- */
+
+extern	boolean		intermission;
+
+void IN_Start(void);
+void IN_Ticker(void);
+void IN_Drawer(void);
+
+/* ---- Finale (F_finale.c) ------ */
+
+void F_Drawer(void);
+void F_Ticker(void);
+void F_StartFinale(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	char		chat_macros[10][80];
+extern	boolean		chatmodeon;
+extern	boolean		ultimatemsg;
+
+
+/* ---- 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(const char *text, int x, int y);
+int MN_TextAWidth(const char *text);
+void MN_DrTextB(const char *text, int x, int y);
+int MN_TextBWidth(const char *text);
+
+
+/* --- AUTOMAP---- */
+
+#define	AM_TRANSPARENT	1	/* compile time option. 0: old style map drawn	*/
+				/* onto solid background.  1:  transparent map.	*/
+
+extern	boolean		automapactive;
+
+
+/* ---- 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);
+
+#pragma pack off
+
+#include "sounds.h"
+
+#endif	/* __DOOMDEF__ */
+
--- /dev/null
+++ b/dstrings.h
@@ -1,0 +1,230 @@
+// DStrings.h
+
+#ifndef __DSTRINGS_H
+#define __DSTRINGS_H
+
+/* ---- MN_menu.c ---- */
+
+#define TXT_GAMMA_LEVEL_OFF	"GAMMA CORRECTION OFF"
+#define TXT_GAMMA_LEVEL_1	"GAMMA CORRECTION LEVEL 1"
+#define TXT_GAMMA_LEVEL_2	"GAMMA CORRECTION LEVEL 2"
+#define TXT_GAMMA_LEVEL_3	"GAMMA CORRECTION LEVEL 3"
+#define TXT_GAMMA_LEVEL_4	"GAMMA CORRECTION LEVEL 4"
+
+
+/* ---- 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?"
+
+#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."
+
+#endif	/* __DSTRINGS_H */
--- /dev/null
+++ b/f_finale.c
@@ -1,0 +1,364 @@
+// F_finale.c
+
+#include "h2stdinc.h"
+#include <ctype.h>
+#include "doomdef.h"
+#include "soundst.h"
+#include "v_compat.h"
+
+#define TEXTSPEED	3
+#define TEXTWAIT	250
+
+#ifdef RENDER3D
+#define V_DrawPatch(x,y,p)		OGL_DrawPatch((x),(y),(p))
+#define V_DrawRawScreen(a)		OGL_DrawRawScreen((a))
+#endif
+
+extern boolean	viewactive;
+extern boolean	MenuActive;
+extern boolean	askforquit;
+
+static int	finalestage;		/* 0 = text, 1 = art screen */
+static int	finalecount;
+static int	finaletextcount;
+
+static boolean	underwater_init;
+static int	nextscroll;
+static int	scroll_yval;
+
+static int	FontABaseLump;
+
+static const char *e1text = E1TEXT;
+static const char *e2text = E2TEXT;
+static const char *e3text = E3TEXT;
+static const char *e4text = E4TEXT;
+static const char *e5text = E5TEXT;
+static const char *finaletext;
+static const char *finaleflat;
+
+static void F_TextWrite (void);
+static void F_DrawBackground(void);
+static void F_DemonScroll(void);
+static void F_DrawUnderwater(void);
+static void F_InitUnderWater(void);
+static void F_KillUnderWater(void);
+
+
+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;
+	finaletextcount = strlen(finaletext)*TEXTSPEED + TEXTWAIT;
+#ifndef RENDER3D
+	scroll_yval = 0;
+#else
+	scroll_yval = 200;
+#endif
+	nextscroll = 0;
+	underwater_init = false;
+	FontABaseLump = W_GetNumForName("FONTA_S") + 1;
+
+	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++;
+		F_KillUnderWater();
+		return true;
+	}
+	return false;
+}
+
+void F_Ticker (void)
+{
+	finalecount++;
+	if (!finalestage && finalecount > finaletextcount)
+	{
+		finalecount = 0;
+		finalestage = 1;
+	}
+}
+
+static void F_TextWrite (void)
+{
+	int		count;
+	const char	*ch;
+	int		c;
+	int		cx, cy;
+	patch_t		*w;
+	int		width;
+
+	F_DrawBackground();
+
+	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 = (patch_t *) W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
+		width = SHORT(w->width);
+		if (cx + width > SCREENWIDTH)
+			break;
+#ifdef RENDER3D
+		OGL_DrawPatch (cx, cy, FontABaseLump + c - 33);
+#else
+		V_DrawPatch(cx, cy, w);
+#endif
+		cx += width;
+	}
+}
+
+#if defined(RENDER3D)
+static void F_DrawBackground(void)
+{
+/* erase the entire screen to a tiled background.
+ */
+	OGL_SetFlat (R_FlatNumForName(finaleflat));
+	OGL_DrawRectTiled(0, 0, SCREENWIDTH, SCREENHEIGHT, 64, 64);
+}
+
+static void F_DemonScroll(void)
+{
+	if (finalecount < 70)
+	{
+		OGL_DrawRawScreen(W_GetNumForName("FINAL1"));
+		nextscroll = finalecount;
+		return;
+	}
+	if (finalecount >= nextscroll && scroll_yval > 0)
+		--scroll_yval;
+	if (scroll_yval > 0)
+	{
+		OGL_DrawRawScreenOfs(W_GetNumForName("FINAL2"), 0, -scroll_yval);
+		OGL_DrawRawScreenOfs(W_GetNumForName("FINAL1"), 0, 200 - scroll_yval);
+		if (finalecount >= nextscroll)
+			nextscroll = finalecount + 2;	// + 3;
+	}
+	else
+	{
+		OGL_DrawRawScreen(W_GetNumForName("FINAL2"));
+	}
+}
+
+static void F_InitUnderWater(void)
+{
+	OGL_SetPaletteLump("E2PAL");
+}
+
+static void F_KillUnderWater(void)
+{
+	OGL_SetPaletteLump("PLAYPAL");
+}
+
+static void F_DrawUnderwater(void)
+{
+	switch (finalestage)
+	{
+	case 1:
+		paused = false;
+		MenuActive = false;
+		askforquit = false;
+
+		if (!underwater_init)
+		{
+			underwater_init = true;
+			F_InitUnderWater();
+		}
+		OGL_DrawRawScreen(W_GetNumForName("E2END"));
+		break;
+
+	case 2:
+		OGL_DrawRawScreen(W_GetNumForName("TITLE"));
+		break;
+	}
+}
+
+#else	/* RENDER3D */
+
+static void F_DrawBackground(void)
+{
+/* erase the entire screen to a tiled background.
+ */
+	int		x, y;
+	byte	*src, *dest;
+
+	src = (byte *) W_CacheLumpName(finaleflat, PU_CACHE);
+	dest = screens;
+	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);
+		}
+	}
+}
+
+static void F_DemonScroll(void)
+{
+	byte *p1, *p2;
+
+	if (finalecount < nextscroll)
+	{
+		return;
+	}
+	p1 = (byte *) W_CacheLumpName("FINAL1", PU_LEVEL);
+	p2 = (byte *) W_CacheLumpName("FINAL2", PU_LEVEL);
+	if (finalecount < 70)
+	{
+		memcpy(screens, p1, SCREENHEIGHT*SCREENWIDTH);
+		nextscroll = finalecount;
+		return;
+	}
+	if (scroll_yval < 64000)
+	{
+		memcpy(screens, p2 + SCREENHEIGHT*SCREENWIDTH - scroll_yval, scroll_yval);
+		memcpy(screens + scroll_yval, p1, SCREENHEIGHT*SCREENWIDTH - scroll_yval);
+		scroll_yval += SCREENWIDTH;
+		nextscroll = finalecount + 3;
+	}
+	else
+	{
+		memcpy(screens, p2, SCREENWIDTH*SCREENHEIGHT);
+	}
+}
+
+static void F_InitUnderWater(void)
+{
+# if defined(__WATCOMC__) && defined(_DOS)
+	memset((byte *)0xa0000, 0, SCREENWIDTH * SCREENHEIGHT);	/* pcscreen */
+# endif /* DOS */
+	I_SetPalette((byte *)W_CacheLumpName("E2PAL", PU_CACHE));
+}
+
+static void F_KillUnderWater(void)
+{
+# if defined(__WATCOMC__) && defined(_DOS)
+	memset((byte *)0xa0000, 0, SCREENWIDTH * SCREENHEIGHT);	/* pcscreen */
+	memset(screen, 0, SCREENWIDTH * SCREENHEIGHT);
+# endif /* DOS */
+	I_SetPalette((byte *)W_CacheLumpName("PLAYPAL", PU_CACHE));
+}
+
+static void F_DrawUnderwater(void)
+{
+	switch (finalestage)
+	{
+	case 1:
+		paused = false;
+		MenuActive = false;
+		askforquit = false;
+
+		if (!underwater_init)
+		{
+			underwater_init = true;
+			F_InitUnderWater();
+			/* draw underwater picture only once during finalestage 1,
+			 * no need to update it thereafter. */
+			V_DrawRawScreen((byte *)W_CacheLumpName("E2END", PU_CACHE));
+		}
+		break;
+
+	case 2:
+		V_DrawRawScreen((byte *) W_CacheLumpName("TITLE", PU_CACHE));
+		break;
+	}
+}
+#endif	/* ! RENDER3D */
+
+void F_Drawer(void)
+{
+	UpdateState |= I_FULLSCRN;
+	if (!finalestage)
+		F_TextWrite ();
+	else
+	{
+		switch (gameepisode)
+		{
+		case 1:
+			if (shareware)
+			  V_DrawRawScreen((BYTE_REF) WR_CacheLumpName("ORDER", PU_CACHE));
+			else
+			  V_DrawRawScreen((BYTE_REF) WR_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((BYTE_REF) WR_CacheLumpName("CREDIT", PU_CACHE));
+			break;
+		}
+	}
+}
+
--- /dev/null
+++ b/g_game.c
@@ -1,0 +1,1775 @@
+// G_game.c
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+
+// Macros
+
+#define AM_STARTKEY		9
+
+// Functions
+
+boolean G_CheckDemoStatus (void);
+static void G_ReadDemoTiccmd (ticcmd_t *cmd);
+static void G_WriteDemoTiccmd (ticcmd_t *cmd);
+
+void G_InitNew (skill_t skill, int episode, int map);
+void G_PlayerReborn (int player);
+
+static void G_DoReborn (int playernum);
+
+static void G_DoLoadLevel(void);
+static void G_DoNewGame(void);
+
+void G_DoLoadGame(void);
+
+static void G_DoPlayDemo(void);
+static void G_DoCompleted(void);
+static void G_DoWorldDone(void);
+static void G_DoSaveGame(void);
+
+void D_PageTicker(void);
+void D_AdvanceDemo(void);
+
+static 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		usergame;		// ok to save / end game
+
+static boolean	sendpause;		// send a pause event next tic
+static boolean	sendsave;		// send a save event next tic
+
+static boolean	timingdemo;		// if true, exit with report on completion
+static 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
+static skill_t	d_skill;
+static int	d_episode;
+static int	d_map;
+
+boolean		demorecording;
+boolean		demoplayback;
+boolean		singledemo;		// quit after playing a demo from cmdline
+static byte	*demobuffer, *demo_p;
+static const char	*defdemoname;
+static char	demoname[MAX_OSPATH];
+
+static short	consistancy[MAXPLAYERS][BACKUPTICS];
+
+static int	loadgameslot;
+static int	savegameslot;
+static char	savedescription[32];
+
+boolean		precache = true;	// if true, load all graphics at start
+
+//
+// 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	mouselook;
+int	alwaysrun;	/* boolean */
+
+int	mousebfire;
+int	mousebstrafe;
+int	mousebforward;
+
+int	joybfire;
+int	joybstrafe;
+int	joybuse;
+int	joybspeed;
+
+#define MAXPLMOVE		0x32
+
+static fixed_t	forwardmove[2] = {0x19, 0x32};
+static fixed_t	sidemove[2] = {0x18, 0x28};
+static fixed_t	angleturn[3] = {640, 1280, 320};	// + slow turn
+#define SLOWTURNTICS		6
+
+boolean	gamekeydown[MAXKEYS];
+static int	turnheld;			// for accelerative turning
+static int	lookheld;
+
+static boolean	mousearray[4];
+static boolean	*mousebuttons = &mousearray[1];	// allow [-1]
+
+static int	mousex, mousey;			// mouse values are used once
+static int	dclicktime, dclickstate, dclicks;
+static int	dclicktime2, dclickstate2, dclicks2;
+
+static int	joyxmove, joyymove;		// joystick values are repeated
+static boolean	joyarray[5];
+static boolean	*joybuttons = &joyarray[1];	// allow [-1]
+
+static int	inventoryTics;
+
+boolean		usearti = true;
+
+#if defined(__WATCOMC__) && defined(_DOS)
+extern externdata_t *i_ExternData;
+#endif
+
+//=============================================================================
+
+/*
+====================
+=
+= 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 boolean noartiskip;
+extern int curpos;
+extern int inv_ptr;
+
+extern int isCyberPresent;	// is CyberMan present?
+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;
+#if defined(__WATCOMC__) && defined(_DOS)
+	int angleDelta;
+	static int oldAngle;
+	extern int newViewAngleOff;
+	static int externInvKey;
+	event_t ev;
+#endif
+
+	memset (cmd, 0, sizeof(*cmd));
+//	cmd->consistancy =
+//		consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS];
+	cmd->consistancy =
+		consistancy[consoleplayer][maketic%BACKUPTICS];
+
+//printf ("cons: %i\n",cmd->consistancy);
+	strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] || joybuttons[joybstrafe];
+	speed = gamekeydown[key_speed] || joybuttons[joybspeed] || joybuttons[joybspeed];
+#if defined(__WATCOMC__) && defined(_DOS)
+	if (useexterndriver)
+	{
+		speed |= (i_ExternData->buttons & EBT_SPEED);
+		strafe |= (i_ExternData->buttons & EBT_STRAFE);
+	}
+#endif
+
+	if (alwaysrun && !demoplayback && !demorecording)
+		speed = !speed;
+	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;
+	}
+#if defined(__WATCOMC__) && defined(_DOS)
+	if (gamekeydown[key_lookcenter] && !useexterndriver)
+	{
+		look = TOCENTER;
+	}
+#else
+	if (gamekeydown[key_lookcenter])
+	{
+		look = TOCENTER;
+	}
+#endif
+
+#if defined(__WATCOMC__) && defined(_DOS)
+	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;
+#if defined(__WATCOMC__) && defined(_DOS)
+		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;
+	}
+
+	if (demorecording || demoplayback || (mouselook == 0))
+	{
+		forward += mousey;
+	}
+	else if (mousey && !paused)	/* mouselook, but not when paused */
+	{
+		/* We'll directly change the viewing pitch of the console player. */
+		float adj = ((mousey*0x4) << 16) / (float) ANGLE_180*180*110.0/85.0;
+		float newlookdir = 0;
+
+		adj *= 2;	/* Speed up the X11 mlook a little. */
+
+		if (mouselook == 1)
+			newlookdir = players[consoleplayer].lookdir + adj;
+		else if (mouselook == 2)
+			newlookdir = players[consoleplayer].lookdir - adj;
+
+		// vertical view angle taken from p_user.c line 249.
+		if (newlookdir > 90)
+			newlookdir = 90;
+		else if (newlookdir < -110)
+			newlookdir = -110;
+
+		players[consoleplayer].lookdir = newlookdir;
+	}
+
+	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
+=
+==============
+*/
+
+static 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));
+	memset (joyarray, 0, sizeof(joyarray));
+	memset (mousearray, 0, sizeof(mousearray));
+}
+
+
+/*
+===============================================================================
+=
+= 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)
+		{
+			if (!MenuActive && gamestate != GS_FINALE)
+				sendpause = true;
+			return true;
+		}
+		if (ev->data1 < MAXKEYS)
+		{
+			gamekeydown[ev->data1] = true;
+		}
+		return true;	// eat key down events
+
+	case ev_keyup:
+		if (ev->data1 < MAXKEYS)
+		{
+			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) / 2;
+		mousey = ev->data3 * (mouseSensitivity + 5) / 2;
+		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_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
+//
+	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 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 int	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
+=
+====================
+*/
+
+static 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;
+}
+
+static 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
+//
+//============================================================================
+
+static 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.
+//
+//---------------------------------------------------------------------------
+
+void G_LoadGame(int slot)
+{
+	loadgameslot = slot;
+	gameaction = ga_loadgame;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC G_DoLoadGame
+//
+// Called by G_Ticker based on gameaction.
+//
+//---------------------------------------------------------------------------
+
+void G_DoLoadGame(void)
+{
+	gameaction = ga_nothing;
+	SV_LoadGame(loadgameslot);
+}
+
+
+/*
+====================
+=
+= 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)
+{
+	d_skill = skill;
+	d_episode = episode;
+	d_map = map;
+	gameaction = ga_newgame;
+}
+
+static void G_DoNewGame (void)
+{
+	G_InitNew (d_skill, d_episode, d_map);
+	gameaction = ga_nothing;
+}
+
+void G_InitNew(skill_t skill, int episode, int map)
+{
+	int i;
+	int speed;
+	static const 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
+
+static 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++;
+}
+
+static void G_WriteDemoTiccmd (ticcmd_t *cmd)
+{
+	if (gamekeydown['q'])		// press q to end demo recording
+		G_CheckDemoStatus ();
+	*demo_p++ = (byte) cmd->forwardmove;
+	*demo_p++ = (byte) 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, const char *name)
+{
+	int		i;
+
+	G_InitNew (skill, episode, map);
+	usergame = false;
+	snprintf (demoname, sizeof(demoname), "%s%s.lmp", basePath, name);
+	demobuffer = demo_p = (byte *) 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
+=
+===================
+*/
+
+void G_DeferedPlayDemo (const char *name)
+{
+	defdemoname = name;
+	gameaction = ga_playdemo;
+}
+
+static void G_DoPlayDemo (void)
+{
+	skill_t	skill;
+	int	i, episode, map;
+
+	gameaction = ga_nothing;
+	demobuffer = demo_p = (byte *) 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 (const char *name)
+{
+	skill_t	skill;
+	int	episode, map;
+
+	demobuffer = demo_p = (byte *) 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 ("Recorded demo: %s", demoname);
+	}
+
+	return false;
+}
+
+/**************************************************************************/
+
+//==========================================================================
+//
+// G_SaveGame
+//
+// Called by the menu task.  <description> is a 24 byte text string.
+//
+//==========================================================================
+
+void G_SaveGame(int slot, const char *description)
+{
+	savegameslot = slot;
+	strcpy(savedescription, description);
+	sendsave = true;
+}
+
+//==========================================================================
+//
+// G_DoSaveGame
+//
+// Called by G_Ticker based on gameaction.
+//
+//==========================================================================
+
+static void G_DoSaveGame(void)
+{
+	SV_SaveGame(savegameslot, savedescription);
+	gameaction = ga_nothing;
+	savedescription[0] = 0;
+	P_SetMessage(&players[consoleplayer], TXT_GAMESAVED, true);
+}
+
--- /dev/null
+++ b/h2stdinc.h
@@ -1,0 +1,131 @@
+/*
+	h2stdinc.h
+	includes the minimum necessary stdc headers,
+	defines common and / or missing types.
+*/
+
+#ifndef __H2STDINC_H
+#define __H2STDINC_H
+
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+
+#define uint32_t u32int
+#define int32_t s32int
+#define int16_t s16int
+#define uint16_t u16int
+#define uint64_t u64int
+#define int64_t s64int
+
+#define intptr_t vlong
+#define uintptr_t uvlong
+#define ptrdiff_t vlong
+
+#define size_t uvlong
+
+#undef PI
+
+#define strcasecmp cistrcmp
+#define strncasecmp cistrncmp
+
+
+/*==========================================================================*/
+
+#ifndef NULL
+#if defined(__cplusplus)
+#define	NULL		0
+#else
+#define	NULL		((void *)0)
+#endif
+#endif
+
+#define	H2MAXCHAR	((char)0x7f)
+#define	H2MAXSHORT	((short)0x7fff)
+#define	H2MAXINT	((int)0x7fffffff)	/* max positive 32-bit integer */
+#define	H2MINCHAR	((char)0x80)
+#define	H2MINSHORT	((short)0x8000)
+#define	H2MININT	((int)0x80000000)	/* max negative 32-bit integer */
+
+/* make sure enums are the size of ints for structure packing */
+typedef enum {
+	THE_DUMMY_VALUE
+} THE_DUMMY_ENUM;
+
+
+/*==========================================================================*/
+
+typedef unsigned char		byte;
+
+#undef true
+#undef false
+#if defined(__cplusplus)
+/* some structures have boolean members and the x86 asm code expect
+ * those members to be 4 bytes long. therefore, boolean must be 32
+ * bits and it can NOT be binary compatible with the 8 bit C++ bool.  */
+typedef int	boolean;
+#else
+typedef enum {
+	false = 0,
+	true  = 1
+} boolean;
+#endif
+
+/*==========================================================================*/
+
+/* math */
+#define	FRACBITS		16
+#define	FRACUNIT		(1 << FRACBITS)
+
+typedef int	fixed_t;
+
+
+/*==========================================================================*/
+
+/* compatibility with M$ types */
+#if !defined(_WIN32)
+#define	PASCAL
+#define	FAR
+#define	APIENTRY
+#endif	/* ! WINDOWS */
+
+/*==========================================================================*/
+
+/* compiler specific definitions */
+
+#if !defined(__GNUC__)
+#define	__attribute__(x)
+#endif	/* __GNUC__ */
+
+/* argument format attributes for function
+ * pointers are supported for gcc >= 3.1
+ */
+#if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0))
+#define	__fp_attribute__	__attribute__
+#else
+#define	__fp_attribute__(x)
+#endif
+
+/* function optimize attribute is added
+ * starting with gcc 4.4.0
+ */
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3))
+#define	__no_optimize		__attribute__((__optimize__("0")))
+#else
+#define	__no_optimize
+#endif
+
+/*==========================================================================*/
+
+/* Provide a substitute for offsetof() if we don't have one.
+ * This variant works on most (but not *all*) systems...
+ */
+#ifndef offsetof
+#define offsetof(t,m) ((size_t)&(((t *)0)->m))
+#endif
+
+/*==========================================================================*/
+
+
+#endif	/* __H2STDINC_H */
+
--- /dev/null
+++ b/i_cdmus.c
@@ -1,0 +1,186 @@
+
+//**************************************************************************
+//**
+//** i_cdmus.c
+//**
+//** $Revision: 562 $
+//** $Date: 2010-10-20 15:45:52 +0300 (Wed, 20 Oct 2010) $
+//**
+//**************************************************************************
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "i_cdmus.h"
+
+// MACROS ------------------------------------------------------------------
+
+#define MAX_AUDIO_TRACKS	25
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+int cdaudio;	/* boolean: enabled or disabled */
+
+boolean i_CDMusic;
+int i_CDTrack;
+int i_CDCurrentTrack;
+int i_CDMusicLength;
+int oldTic;
+
+int cd_Error;
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+#if 0 /* nothing is here yet */
+static int cd_FirstTrack;
+static int cd_LastTrack;
+static char cd_dev[64] = "/dev/cdrom";
+#endif
+static int cdfile = -1;
+
+// CODE --------------------------------------------------------------------
+
+static int I_CDGetDiskInfo(void)
+{
+	return 0;
+}
+
+//==========================================================================
+//
+// 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)
+{
+	//open CD device
+	I_CDGetDiskInfo ();
+	return -1;	// not implemented yet
+}
+
+//==========================================================================
+//
+// I_CDMusPlay
+//
+// Play an audio CD track.
+//
+// Returns: 0 (ok) or -1 (error, in cd_Error).
+//
+//==========================================================================
+
+int I_CDMusPlay(int)
+{
+	return 0;
+}
+
+//==========================================================================
+//
+// I_CDMusStop
+//
+// Stops the playing of an audio CD.
+//
+// Returns: 0 (ok) or -1 (error, in cd_Error).
+//
+//==========================================================================
+
+int I_CDMusStop(void)
+{
+	return 0;
+}
+
+//==========================================================================
+//
+// I_CDMusResume
+//
+// Resumes the playing of an audio CD.
+//
+// Returns: 0 (ok) or -1 (error, in cd_Error).
+//
+//==========================================================================
+
+int I_CDMusResume(void)
+{
+	return 0;
+}
+
+//==========================================================================
+//
+// I_CDMusSetVolume
+//
+// Sets the CD audio volume (0 - 255).
+//
+// Returns: 0 (ok) or -1 (error, in cd_Error).
+//
+//==========================================================================
+
+int I_CDMusSetVolume(int)
+{
+	return 0;
+}
+
+//==========================================================================
+//
+// I_CDMusFirstTrack
+//
+// Returns: the number of the first track.
+//
+//==========================================================================
+
+int I_CDMusFirstTrack(void)
+{
+	return 0;
+}
+
+//==========================================================================
+//
+// I_CDMusLastTrack
+//
+// Returns: the number of the last track.
+//
+//==========================================================================
+
+int I_CDMusLastTrack(void)
+{
+	return 0;
+}
+
+//==========================================================================
+//
+// I_CDMusShutDown
+//
+//==========================================================================
+
+void I_CDMusShutdown(void)
+{
+	if (cdfile != -1)
+		close(cdfile);
+	cdfile = -1;
+}
+
+//==========================================================================
+//
+// I_CDMusUpdate
+//
+//==========================================================================
+
+void I_CDMusUpdate(void)
+{
+
+}
+
--- /dev/null
+++ b/i_cdmus.h
@@ -1,0 +1,44 @@
+
+//**************************************************************************
+//**
+//** i_cdmus.h : Heretic 2 : Raven Software, Corp.
+//**
+//** $Revision: 421 $
+//** $Date: 2009-05-22 16:08:37 +0300 (Fri, 22 May 2009) $
+//**
+//**************************************************************************
+
+#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 boolean i_CDMusic;	/* is cdaudio initialized */
+extern int cdaudio;		/* boolean: is cd audio enabled or disabled */
+
+extern int i_CDTrack;
+extern int i_CDCurrentTrack;
+extern int i_CDMusicLength;
+extern int oldTic;
+
+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);
+void I_CDMusUpdate(void);
+void I_CDMusShutdown(void);
+
+#endif	/* __ICDMUS__ */
+
--- /dev/null
+++ b/i_main.c
@@ -1,0 +1,12 @@
+/* i_main.c */
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "soundst.h"
+
+void main(int argc, char **argv)
+{
+	myargc = argc; 
+	myargv = argv; 
+	D_DoomMain ();
+} 
--- /dev/null
+++ b/i_net.c
@@ -1,0 +1,161 @@
+// Emacs style mode select   -*- C++ -*- 
+//-----------------------------------------------------------------------------
+//
+// $Id:$
+//
+// Copyright (C) 1993-1996 by id Software, Inc.
+//
+// This source is available for distribution and/or modification
+// only under the terms of the DOOM Source Code License as
+// published by id Software. All rights reserved.
+//
+// The source is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
+// for more details.
+//
+// $Log:$
+//
+// DESCRIPTION:
+//
+//-----------------------------------------------------------------------------
+
+static const char
+rcsid[] = "$Id: m_bbox.c,v 1.1 1997/02/03 22:45:10 b1 Exp $";
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+
+// #include "i_system.h"
+// #include "d_event.h"
+//#include "d_net.h"
+//#include "m_argv.h"
+
+#include "i_net.h"
+
+
+//
+// I_InitNetwork
+//
+void I_InitNetwork (void)
+{
+printf("PORTME i_net.c I_InitNetwork (use 9P)\n");
+
+	doomcom = malloc (sizeof(*doomcom));
+	memset (doomcom, 0, sizeof(*doomcom));
+
+	/* set up for network */
+	doomcom->ticdup = 1;
+	doomcom->extratics = 0;
+
+//	netsend = PacketSend;
+//	netget = PacketGet;
+//	netgame = true;
+
+	/* parse player number and host list */
+//	doomcom->consoleplayer = myargv[i+1][0]-'1';
+
+	doomcom->numnodes = 1;	// this node for sure
+
+	doomcom->id = DOOMCOM_ID;
+	doomcom->numplayers = doomcom->numnodes;
+
+/*
+    boolean		trueval = true;
+    int			i;
+    int			p;
+    struct hostent*	hostentry;	// host information entry
+	
+    doomcom = malloc (sizeof (*doomcom) );
+    memset (doomcom, 0, sizeof(*doomcom) );
+    
+    // set up for network
+    i = M_CheckParm ("-dup");
+    if (i && i< myargc-1)
+    {
+	doomcom->ticdup = myargv[i+1][0]-'0';
+	if (doomcom->ticdup < 1)
+	    doomcom->ticdup = 1;
+	if (doomcom->ticdup > 9)
+	    doomcom->ticdup = 9;
+    }
+    else
+	doomcom-> ticdup = 1;
+	
+    if (M_CheckParm ("-extratic"))
+	doomcom-> extratics = 1;
+    else
+	doomcom-> extratics = 0;
+		
+    p = M_CheckParm ("-port");
+    if (p && p<myargc-1)
+    {
+	DOOMPORT = atoi (myargv[p+1]);
+	printf ("using alternate port %i\n",DOOMPORT);
+    }
+    
+    // parse network game options,
+    //  -net <consoleplayer> <host> <host> ...
+    i = M_CheckParm ("-net");
+    if (!i)
+    {
+	// single player game
+	netgame = false;
+	doomcom->id = DOOMCOM_ID;
+	doomcom->numplayers = doomcom->numnodes = 1;
+	doomcom->deathmatch = false;
+	doomcom->consoleplayer = 0;
+	return;
+    }
+
+    netsend = PacketSend;
+    netget = PacketGet;
+    netgame = true;
+
+    // parse player number and host list
+    doomcom->consoleplayer = myargv[i+1][0]-'1';
+
+    doomcom->numnodes = 1;	// this node for sure
+	
+    i++;
+    while (++i < myargc && myargv[i][0] != '-')
+    {
+	sendaddress[doomcom->numnodes].sin_family = AF_INET;
+	sendaddress[doomcom->numnodes].sin_port = htons(DOOMPORT);
+	if (myargv[i][0] == '.')
+	{
+	    sendaddress[doomcom->numnodes].sin_addr.s_addr 
+		= inet_addr (myargv[i]+1);
+	}
+	else
+	{
+	    hostentry = gethostbyname (myargv[i]);
+	    if (!hostentry)
+		I_Error ("gethostbyname: couldn't find %s", myargv[i]);
+	    sendaddress[doomcom->numnodes].sin_addr.s_addr 
+		= *(int *)hostentry->h_addr_list[0];
+	}
+	doomcom->numnodes++;
+    }
+	
+    doomcom->id = DOOMCOM_ID;
+    doomcom->numplayers = doomcom->numnodes;
+*/
+}
+
+
+void I_NetCmd (void)
+{
+/*
+    if (doomcom->command == CMD_SEND)
+    {
+	netsend ();
+    }
+    else if (doomcom->command == CMD_GET)
+    {
+	netget ();
+    }
+    else
+	I_Error ("Bad net cmd: %i\n",doomcom->command);
+*/
+}
--- /dev/null
+++ b/i_net.h
@@ -1,0 +1,45 @@
+// Emacs style mode select   -*- C++ -*- 
+//-----------------------------------------------------------------------------
+//
+// $Id:$
+//
+// Copyright (C) 1993-1996 by id Software, Inc.
+//
+// This source is available for distribution and/or modification
+// only under the terms of the DOOM Source Code License as
+// published by id Software. All rights reserved.
+//
+// The source is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
+// for more details.
+//
+// DESCRIPTION:
+//	System specific network interface stuff.
+//
+//-----------------------------------------------------------------------------
+
+
+#ifndef __I_NET__
+#define __I_NET__
+
+
+#ifdef __GNUG__
+#pragma interface
+#endif
+
+
+
+// Called by D_DoomMain.
+
+
+void I_InitNetwork (void);
+void I_NetCmd (void);
+
+
+#endif
+//-----------------------------------------------------------------------------
+//
+// $Log:$
+//
+//-----------------------------------------------------------------------------
--- /dev/null
+++ b/i_sound.c
@@ -1,0 +1,520 @@
+//**************************************************************************
+//**
+//** i_soundpi.c: unix sound driver using a plugin interface
+//**
+//** $Revision: 512 $
+//** $Date: 2009-06-04 18:00:34 +0300 (Thu, 04 Jun 2009) $
+//**
+//**************************************************************************
+
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "sounds.h"
+#include "i_sound.h"
+#include "audio_plugin.h"
+
+#define SAMPLE_ZERO	0
+#define SAMPLE_RATE	11025	/* Hz */
+#define SAMPLE_CHANNELS	2
+#define TARGET_RATE	44100
+
+#define SAMPLE_TYPE	short
+
+/*
+ *	SOUND HEADER & DATA
+ */
+
+int snd_Channels;
+int snd_MaxVolume,		/* maximum volume for sound */
+	snd_MusicVolume;	/* maximum volume for music */
+boolean snd_MusicAvail,		/* whether music is available */
+	snd_SfxAvail;		/* whether sfx are available */
+
+/*
+ *	SOUND FX API
+ */
+
+typedef struct
+{
+	unsigned char	*begin;		/* pointers into Sample.firstSample */
+	unsigned char	*end;
+
+	SAMPLE_TYPE	*lvol_table;	/* point into vol_lookup */
+	SAMPLE_TYPE	*rvol_table;
+
+	unsigned int	pitch_step;
+	unsigned int	step_remainder;	/* 0.16 bit remainder of last step. */
+
+	int		pri;
+	unsigned int	time;
+} Channel;
+
+#pragma pack on
+typedef struct
+{
+/* Sample data is a lump from a wad: byteswap the a, freq
+ * and the length fields before using them		*/
+	short		a;		/* always 3	*/
+	short		freq;		/* always 11025	*/
+	int32_t		length;		/* sample length */
+	unsigned char	firstSample;
+} Sample;
+#pragma pack off
+
+static int	audio_exit_thread = 1;
+
+#define CHAN_COUNT	8
+static Channel	channel[CHAN_COUNT];
+
+#define MAX_VOL		64	/* 64 keeps our table down to 16Kb */
+static SAMPLE_TYPE	vol_lookup[MAX_VOL * 256];
+
+static int	steptable[256];		/* Pitch to stepping lookup */
+
+#define BUF_LEN		(256 * 2 * 4)
+
+static int audiofd;
+static int audiopid = -1;
+
+static QLock audiolk;
+
+boolean mus_paused = false;
+
+static int mpfd[2] = {-1, -1};
+
+void I_ShutdownMusic(void);
+
+static void audioproc(void)
+{
+	Channel* chan;
+	Channel* cend;
+	static char buf[BUF_LEN];
+	SAMPLE_TYPE *begin;
+	SAMPLE_TYPE *end;
+	unsigned int sample;
+	register int dl, ml;
+	register int dr, mr;
+	int i;
+
+	end = (SAMPLE_TYPE *) (buf + BUF_LEN);
+	cend = channel + CHAN_COUNT;
+
+	for(;;){
+		memset(buf, 0, sizeof buf);
+		if(mpfd[0]>=0 && !mus_paused && readn(mpfd[0], buf, sizeof buf) < 0){
+			fprint(2, "I_UpdateSound: disabling music: %r\n");
+			I_ShutdownMusic();
+		}
+		begin = (SAMPLE_TYPE *) buf;
+		while (begin < end){
+			// Mix all the channels together.
+			dl = SAMPLE_ZERO;
+			dr = SAMPLE_ZERO;
+
+			qlock(&audiolk);
+			chan = channel;
+			for ( ; chan < cend; chan++){
+				if(!chan->begin)
+					continue;
+				// Get the sample from the channel.
+				sample = *chan->begin;
+
+				// Adjust volume accordingly.
+				dl += chan->lvol_table[sample];
+				dr += chan->rvol_table[sample];
+
+				// Increment sample pointer with pitch adjustment.
+				chan->step_remainder += chan->pitch_step;
+				chan->begin += chan->step_remainder >> 16;
+				chan->step_remainder &= 65535;
+
+				// Check whether we are done.
+				if (chan->begin >= chan->end)
+				{
+					chan->begin = NULL;
+				//printf ("  channel done %d\n", chan);
+				}
+			}
+			qunlock(&audiolk);
+			for(i=0; i < TARGET_RATE/SAMPLE_RATE; i++){
+				ml = dl + *begin;
+				if (ml > 0x7fff)
+					ml = 0x7fff;
+				else if (ml < -0x8000)
+					ml = -0x8000;
+				*begin++ = ml;
+
+				mr = dr + *begin;
+				if (mr > 0x7fff)
+					mr = 0x7fff;
+				else if (mr < -0x8000)
+					mr = -0x8000;
+				*begin++ = mr;
+			}
+		}
+		write(audiofd, buf, BUF_LEN);
+	}
+}
+
+
+void I_SetSfxVolume(int volume)
+{
+	USED(volume);
+}
+
+// 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)
+{
+	if (sound->name[0] == 0)
+		return 0;
+	if (sound->link)
+		sound = sound->link;
+	return W_GetNumForName(sound->name);
+}
+
+
+// Id is unused.
+// Data is a pointer to a Sample structure.
+// Volume ranges from 0 to 127.
+// Separation (orientation/stereo) ranges from 0 to 255.  128 is balanced.
+// Pitch ranges from 0 to 255.  Normal is 128.
+// Priority looks to be unused (always 0).
+
+int I_StartSound(int id, void *data, int vol, int sep, int pitch, int priority)
+{
+	// Relative time order to find oldest sound.
+	static unsigned int soundTime = 0;
+	int chanId;
+	Sample *sample;
+	Channel *chan;
+	int oldest;
+	int i;
+
+	USED(id);
+	// Find an empty channel, the oldest playing channel, or default to 0.
+	// Currently ignoring priority.
+
+	chanId = 0;
+	oldest = soundTime;
+	for (i = 0; i < CHAN_COUNT; i++)
+	{
+		if (! channel[ i ].begin)
+		{
+			chanId = i;
+			break;
+		}
+		if (channel[ i ].time < oldest)
+		{
+			chanId = i;
+			oldest = channel[ i ].time;
+		}
+	}
+
+	sample = (Sample *) data;
+	chan = &channel[chanId];
+
+	I_UpdateSoundParams(chanId + 1, vol, sep, pitch);
+
+	// begin must be set last because the audio thread will access the channel
+	// once it is non-zero.  Perhaps this should be protected by a mutex.
+	chan->pri = priority;
+	chan->time = soundTime;
+	chan->end = &sample->firstSample + LONG(sample->length);
+	chan->begin = &sample->firstSample;
+
+	soundTime++;
+
+#if 0
+	printf ("I_StartSound %d: v:%d s:%d p:%d pri:%d | %d %d %d %d\n",
+		id, vol, sep, pitch, priority,
+		chanId, chan->pitch_step, SHORT(sample->a), SHORT(sample->freq));
+#endif
+
+	return chanId + 1;
+}
+
+void I_StopSound(int handle)
+{
+	handle--;
+	handle &= 7;
+	channel[handle].begin = NULL;
+}
+
+int I_SoundIsPlaying(int handle)
+{
+	handle--;
+	handle &= 7;
+	return (channel[ handle ].begin != NULL);
+}
+
+void I_UpdateSoundParams(int handle, int vol, int sep, int pitch)
+{
+	int lvol, rvol;
+	Channel *chan;
+
+	qlock(&audiolk);
+	// Set left/right channel volume based on seperation.
+	sep += 1;	// range 1 - 256
+	lvol = vol - ((vol * sep * sep) >> 16);	// (256*256);
+	sep = sep - 257;
+	rvol = vol - ((vol * sep * sep) >> 16);
+
+	// Sanity check, clamp volume.
+	if (rvol < 0)
+	{
+	//	printf ("rvol out of bounds %d, id %d\n", rvol, handle);
+		rvol = 0;
+	}
+	else if (rvol > 127)
+	{
+	//	printf ("rvol out of bounds %d, id %d\n", rvol, handle);
+		rvol = 127;
+	}
+
+	if (lvol < 0)
+	{
+	//	printf ("lvol out of bounds %d, id %d\n", lvol, handle);
+		lvol = 0;
+	}
+	else if (lvol > 127)
+	{
+	//	printf ("lvol out of bounds %d, id %d\n", lvol, handle);
+		lvol = 127;
+	}
+
+	// Limit to MAX_VOL (64)
+	lvol >>= 1;
+	rvol >>= 1;
+
+	handle--;
+	handle &= 7;
+	chan = &channel[handle];
+	chan->pitch_step = steptable[pitch];
+	chan->step_remainder = 0;
+	chan->lvol_table = &vol_lookup[lvol * 256];
+	chan->rvol_table = &vol_lookup[rvol * 256];
+	qunlock(&audiolk);
+}
+
+
+/*
+ *	SOUND STARTUP STUFF
+ */
+
+// inits all sound stuff
+void I_StartupSound (void)
+{
+	snd_SfxAvail = false;
+
+	if (M_CheckParm("--nosound") || M_CheckParm("-s") || M_CheckParm("-nosound"))
+	{
+		fprintf(stderr, "I_StartupSound: Sound Disabled.\n");
+		return;
+	}
+
+	audiofd = open("/dev/audio", OWRITE);
+	if(audiofd < 0){
+		fprintf(stderr, "I_StartupSound: /dev/audio could not be opened\n");
+		return;
+	}
+
+	snd_SfxAvail = true;
+
+	if((audiopid = rfork(RFPROC|RFMEM)) == 0){
+		audioproc();
+		exits(nil);
+	}
+}
+
+// shuts down all sound stuff
+void I_ShutdownSound (void)
+{
+	snd_SfxAvail = false;
+
+	if(audiopid != -1){
+		postnote(PNPROC, audiopid, "shutdown");
+		audiopid = -1;
+	}
+	I_ShutdownMusic();
+}
+
+void I_SetChannels(int channels)
+{
+	int v, j;
+	int *steptablemid;
+
+	// We always have CHAN_COUNT channels.
+	USED(channels);
+
+	for (j = 0; j < CHAN_COUNT; j++)
+	{
+		channel[j].begin = NULL;
+		channel[j].end   = NULL;
+		channel[j].time = 0;
+	}
+
+	// This table provides step widths for pitch parameters.
+	steptablemid = steptable + 128;
+	for (j = -128; j < 128; j++)
+	{
+		steptablemid[j] = (int) (pow(2.0, (j/64.0)) * 65536.0);
+	}
+
+	// Generate the volume lookup tables.
+	for (v = 0; v < MAX_VOL; v++)
+	{
+		for (j = 0; j < 256; j++)
+		{
+		//	vol_lookup[v*256+j] = 128 + ((v * (j-128)) / (MAX_VOL-1));
+
+		// Turn the unsigned samples into signed samples.
+
+			vol_lookup[v*256+j] = (v * (j-128) * 256) / (MAX_VOL-1);
+		//	printf ("vol_lookup[%d*256+%d] = %d\n", v, j, vol_lookup[v*256+j]);
+		}
+	}
+}
+
+
+/*
+ *	SONG API
+ */
+
+static int didgen = 0;
+
+static void genmidi(void)
+{
+	int fd, n, sz;
+	char name[64];
+	uchar *gm;
+
+	n = W_GetNumForName("GENMIDI");
+	sz = W_LumpLength(n);
+	gm = (uchar *)W_CacheLumpNum(n, PU_STATIC);
+	snprint(name, sizeof(name), "/tmp/genmidi.%d", getpid());
+	if((fd = create(name, ORDWR|ORCLOSE, 0666)) < 0)
+		sysfatal("create: %r");
+	if(write(fd, gm, sz) != sz)
+			sysfatal("write: %r");
+	Z_Free(gm);
+}
+
+void I_ShutdownMusic(void)
+{
+	if(mpfd[0] >= 0){
+		close(mpfd[0]);
+		mpfd[0] = -1;
+		waitpid();
+	}
+}
+
+
+/* In theory this allows register step allows 
+ * the use of external files in place of internal ones. */
+static void *currentsong = nil;
+static int currentsize = 0;
+
+int I_RegisterSong(void *data, int siz)
+{
+	if(!didgen){
+		genmidi();
+		didgen++;
+	}
+	if(currentsong != nil)
+		return 0;
+
+	currentsong = data;
+	currentsize = siz;
+	return 1;
+}
+
+int I_RegisterExternalSong(const char *nm)
+{
+	USED(nm);
+	return 0;
+}
+
+void I_UnRegisterSong(int handle)
+{
+	USED(handle);
+	currentsong = nil;
+}
+
+void I_PauseSong(int handle)
+{
+	if(handle <= 0)
+		return;
+	mus_paused = true;
+}
+
+void I_ResumeSong(int handle)
+{
+	if(handle <= 0)
+		return;
+	mus_paused = false;
+}
+
+void I_SetMusicVolume(int volume)
+{
+	USED(volume);
+}
+
+int I_QrySongPlaying(int handle)
+{
+	USED(handle);
+	return 0;
+}
+
+// Stops a song.  MUST be called before I_UnregisterSong().
+void I_StopSong(int handle)
+{
+	if(handle <= 0)
+		return;
+	I_ShutdownMusic();
+}
+
+void I_PlaySong(int handle, boolean loop)
+{
+	char name[64];
+	int n;
+
+	if(M_CheckParm("-nomusic") || audiofd < 0 || handle <= 0)
+		return;
+
+	I_ShutdownMusic();
+	if(pipe(mpfd) < 0)
+		return;
+	switch(rfork(RFPROC|RFFDG|RFNAMEG)){
+	case -1:
+		fprint(2, "I_PlaySong: %r\n");
+		break;
+	case 0:
+		dup(mpfd[1], 1);
+		for(n=3; n<20; n++) close(n);
+		close(0);
+		snprint(name, sizeof(name), "/tmp/heretic.%d", getpid());
+		if(create(name, ORDWR|ORCLOSE, 0666) != 0)
+			sysfatal("create: %r");
+		if(write(0, currentsong, currentsize) != currentsize)
+			sysfatal("write: %r");
+		if(seek(0, 0, 0) != 0)
+			sysfatal("seek: %r");
+		if(bind("/fd/1", "/dev/audio", MREPL) == -1)
+			sysfatal("bind: %r");
+		while(loop && fork() > 0){
+			if(waitpid() < 0 || write(1, "", 0) < 0)
+				exits(nil);
+		}
+		execl("/bin/dmus", "dmus", name, nil);
+		execl("/bin/play", "play", name, nil);
+		sysfatal("execl: %r");
+	default:
+		close(mpfd[1]);
+	}
+}
+
--- /dev/null
+++ b/i_sound.h
@@ -1,0 +1,46 @@
+// i_sound.h
+
+#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;
+
+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, int siz);
+int I_RegisterExternalSong(const char *name);	/* External music file support */
+void I_UnRegisterSong(int handle);
+int I_QrySongPlaying(int handle);
+void I_StopSong(int handle);
+void I_PlaySong(int handle, boolean 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	/* __SOUND__ */
+
--- /dev/null
+++ b/i_system.c
@@ -1,0 +1,147 @@
+/* i_system.c */
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+
+#include "i_system.h"
+#include "i_video.h"
+#include "i_sound.h"
+
+#include "soundst.h"
+
+int mb_used = 32;	/* 32MB heap */
+
+void I_Init (void)
+{
+	S_Init();
+	I_MouseEnable(1);
+}
+
+byte* I_ZoneBase (int *size)
+{
+	*size = mb_used*1024*1024;
+	return (byte *) malloc(*size);
+}
+
+/* returns time in 1/70th second tics */
+int I_GetTime (void)
+{
+	return (int)((nsec()*TICRATE)/1000000000);
+}
+
+static ticcmd_t emptycmd;
+ticcmd_t* I_BaseTiccmd (void)
+{
+	return &emptycmd;
+}
+
+extern void G_CheckDemoStatus(void);
+
+void I_Quit (void)
+{
+	D_QuitNetGame ();
+	I_ShutdownSound();
+	M_SaveDefaults ();
+	I_ShutdownGraphics();
+	exits(nil);
+}
+
+byte* I_AllocLow (int length)
+{
+	byte *mem;
+        
+	mem = (byte *)malloc (length);
+	memset (mem,0,length);
+	return mem;
+}
+
+void I_Tactile(int on, int off, int total)
+{
+	USED(on, off, total);
+}
+
+//
+// I_Error
+//
+extern boolean demorecording;
+
+void I_Error (char *error, ...)
+{
+    va_list	argptr;
+
+    // Message first.
+    va_start (argptr,error);
+    fprintf (stderr, "Error: ");
+    vfprintf (stderr,error,argptr);
+    fprintf (stderr, "\n");
+    va_end (argptr);
+
+    fflush( stderr );
+
+    // Shutdown. Here might be other errors.
+    if (demorecording)
+	G_CheckDemoStatus();
+
+    D_QuitNetGame ();
+    I_ShutdownGraphics();
+
+    exits("I_Error");
+}
+
+int I_FileExists (char *filepath)
+{
+	return access(filepath, AEXIST) == 0;
+}
+
+int I_Open (char *filepath)
+{
+	return open(filepath, OREAD);
+}
+
+void I_Close (int handle)
+{
+	close (handle);
+}
+
+int I_Seek (int handle, int n)
+{
+	return seek(handle, n, 0);
+}
+
+int I_Read (int handle, void *buf, int n)
+{
+	return read(handle, buf, n);
+}
+
+void I_CheckExternDriver (void)
+{
+}
+
+
+char* I_IdentifyWAD(char *wadname)
+{
+	static char path[1024];
+	char *home;
+
+	snprint(path, sizeof path, wadname);
+	if (I_FileExists (path))
+		return path;
+
+	if(home = getenv("home")){
+		snprintf(path, sizeof path, "%s/lib/hexen/%s", home, wadname);
+		free(home);
+
+		if (I_FileExists (path))
+			return path;
+	}
+
+	snprintf(path, sizeof path, "/sys/lib/hexen/%s", wadname);
+	if (I_FileExists (path))
+		return path;
+
+	snprintf(path, sizeof path, "/sys/games/lib/hexen/%s", wadname);
+	if (I_FileExists (path))
+		return path;
+
+	return nil;
+}
--- /dev/null
+++ b/i_system.h
@@ -1,0 +1,109 @@
+// Emacs style mode select   -*- C++ -*- 
+//-----------------------------------------------------------------------------
+//
+// $Id:$
+//
+// Copyright (C) 1993-1996 by id Software, Inc.
+//
+// This source is available for distribution and/or modification
+// only under the terms of the DOOM Source Code License as
+// published by id Software. All rights reserved.
+//
+// The source is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
+// for more details.
+//
+// DESCRIPTION:
+//	System specific interface stuff.
+//
+//-----------------------------------------------------------------------------
+
+
+#ifndef __I_SYSTEM__
+#define __I_SYSTEM__
+
+//#include "d_ticcmd.h"
+//#include "d_event.h"
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+
+#ifdef __GNUG__
+#pragma interface
+#endif
+
+
+// Called by DoomMain.
+void I_Init (void);
+
+// Called by startup code
+// to get the ammount of memory to malloc
+// for the zone management.
+byte*	I_ZoneBase (int *size);
+
+
+// Called by D_DoomLoop,
+// returns current time in tics.
+int I_GetTime (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_StartFrame (void);
+
+
+//
+// Called by D_DoomLoop,
+// called before processing each tic in a frame.
+// Quick syncronous operations are performed here.
+// Can call D_PostEvent.
+void I_StartTic (void);
+
+// Asynchronous interrupt functions should maintain private queues
+// that are read by the synchronous functions
+// to be converted into events.
+
+// Either returns a null ticcmd,
+// or calls a loadable driver to build it.
+// This ticcmd will then be modified by the gameloop
+// for normal input.
+ticcmd_t* I_BaseTiccmd (void);
+
+
+// Called by M_Responder when quit is selected.
+// Clean exit, displays sell blurb.
+void I_Quit (void);
+
+
+// Allocates from low memory under dos,
+// just mallocs under unix
+byte* I_AllocLow (int length);
+
+void I_Tactile (int on, int off, int total);
+
+
+void I_Error (char *error, ...);
+
+int I_FileExists (char *filepath);
+
+int I_Open (char *filepath);
+void I_Close (int handle);
+
+int I_Seek(int handle, int n);
+int I_Read(int handle, void *buf, int n);
+
+char* I_IdentifyWAD(char *wadname);
+
+#endif
+//-----------------------------------------------------------------------------
+//
+// $Log:$
+//
+//-----------------------------------------------------------------------------
--- /dev/null
+++ b/i_video.c
@@ -1,0 +1,423 @@
+/* i_video.c */
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+
+#include "i_system.h"
+
+extern byte *screens;
+int DisplayTicker = 0;
+int UpdateState = 0;
+
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+
+static int resized;
+static int mouseactive;
+extern int usemouse;
+
+static Rectangle grabout;
+static Point center;
+
+static void kbdproc(void);
+static void mouseproc(void);
+
+static uchar cmap[3*256];
+
+static int kbdpid = -1;
+static int mousepid = -1;
+
+static void
+catch(void *, char *msg)
+{
+	/* in case we crash, disable mouse grab */
+	if(strncmp(msg, "sys:", 4) == 0)
+		mouseactive = 0;
+	noted(NDFLT);
+}
+
+void I_InitGraphics(void)
+{
+	int pid;
+
+	notify(catch);
+
+	if(initdraw(nil, nil, "heretic") < 0)
+		I_Error("I_InitGraphics failed");
+
+	draw(screen, screen->r, display->black, nil, ZP);
+
+	center = addpt(screen->r.min, Pt(Dx(screen->r)/2, Dy(screen->r)/2));
+	grabout = insetrect(screen->r, Dx(screen->r)/8);
+
+	if((pid = rfork(RFPROC|RFMEM)) == 0){
+		kbdproc();
+		exits(nil);
+	}
+	kbdpid = pid;
+
+	if((pid = rfork(RFPROC|RFMEM)) == 0){
+		mouseproc();
+		exits(nil);
+	}
+	mousepid = pid;
+
+	I_SetPalette ((byte *)W_CacheLumpName("PLAYPAL", PU_CACHE));
+}
+
+void I_ShutdownGraphics(void)
+{
+	if(kbdpid != -1){
+		postnote(PNPROC, kbdpid, "shutdown");
+		kbdpid = -1;
+	}
+	if(mousepid != -1){
+		postnote(PNPROC, mousepid, "shutdown");
+		mousepid = -1;
+	}
+}
+
+void I_SetPalette(byte *palette)
+{
+	uchar *c;
+
+	c = cmap;
+	while(c < cmap+3*256)
+		*c++ = gammatable[usegamma][*palette++];
+}
+
+void I_UpdateNoBlit(void)
+{
+	// DELETEME?
+}
+
+void I_StartFrame(void)
+{
+}
+
+void I_Update(void)
+{
+	Image *rowimg;
+	Rectangle r;
+	int y, scale;
+	uchar *s, *e, *d, *m;
+	uchar buf[SCREENWIDTH*3*12];
+
+	if(UpdateState == I_NOUPDATE)
+		return;
+
+	if(resized){
+		resized = 0;
+		if(getwindow(display, Refnone) < 0)
+			sysfatal("getwindow: %r");
+
+		/* make black background */
+		draw(screen, screen->r, display->black, nil, ZP);
+
+		center = addpt(screen->r.min, Pt(Dx(screen->r)/2, Dy(screen->r)/2));
+		grabout = insetrect(screen->r, Dx(screen->r)/8);
+	}
+
+	scale = Dx(screen->r)/SCREENWIDTH;
+	if(scale <= 0)
+		scale = 1;
+	else if(scale > 12)
+		scale = 12;
+	if (UpdateState & I_FULLSCRN){
+		UpdateState = I_NOUPDATE;
+	}
+
+	/* where to draw the scaled row */
+	r = rectsubpt(rectaddpt(Rect(0, 0, scale*SCREENWIDTH, scale), center),
+		Pt(scale*SCREENWIDTH/2, scale*SCREENHEIGHT/2));
+
+	/* the row image, y-axis gets scaled with repl flag */
+	rowimg = allocimage(display, Rect(0, 0, scale*SCREENWIDTH, 1), RGB24, scale > 1, DNofill);
+	if(rowimg == nil)
+		sysfatal("allocimage: %r");
+
+	s = screens;
+	for(y = 0; y < SCREENHEIGHT; y++){
+		d = buf;
+		e = s + SCREENWIDTH;
+		for(; s < e; s++){
+			m = &cmap[*s * 3];
+			switch(scale){
+			case 12:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 11:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 10:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 9:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 8:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 7:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 6:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 5:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 4:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 3:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 2:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			case 1:
+				*d++ = m[2];
+				*d++ = m[1];
+				*d++ = m[0];
+			}
+		}
+		loadimage(rowimg, rowimg->r, buf, d - buf);
+		draw(screen, r, rowimg, nil, ZP);
+		r.min.y += scale;
+		r.max.y += scale;
+	}
+	freeimage(rowimg);
+
+	flushimage(display, 1);
+}
+
+void I_MouseEnable(int on)
+{
+	static char nocurs[2*4+2*2*16];
+	static int fd = -1;
+
+	if(mouseactive == on || !usemouse)
+		return;
+	if(mouseactive = on){
+		if((fd = open("/dev/cursor", ORDWR|OCEXEC)) < 0)
+			return;
+		write(fd, nocurs, sizeof(nocurs));
+	}else if(fd >= 0) {
+		close(fd);
+		fd = -1;
+	}
+}
+
+void I_ReadScreen(byte *scr)
+{
+	memcpy (scr, screens, SCREENWIDTH*SCREENHEIGHT);
+}
+
+void I_BeginRead(void)
+{
+	I_Error("PORTME i_video.c I_BeginRead");
+}
+
+void I_EndRead(void)
+{
+	I_Error("PORTME i_video.c I_EndRead");
+}
+
+void I_StartTic(void)
+{
+}
+
+void I_WaitVBL(int)
+{
+}
+
+
+static int
+runetokey(Rune r)
+{
+	switch(r){
+	case Kleft:
+		return KEY_LEFTARROW;
+	case Kright:
+		return KEY_RIGHTARROW;
+	case Kup:
+		return KEY_UPARROW;
+	case Kdown:
+		return KEY_DOWNARROW;
+
+	case Kshift:
+		return KEY_RSHIFT;
+	case Kctl:
+		return KEY_RCTRL;
+	case Kalt:
+		return KEY_LALT;
+	case Kaltgr:
+		return KEY_RALT;
+
+	case Kbs:
+		return KEY_BACKSPACE;
+	case '\n':
+		return KEY_ENTER;
+	case Kprint:
+		return KEY_PAUSE;
+
+	case KF|1:
+	case KF|2:
+	case KF|3:
+	case KF|4:
+	case KF|5:
+	case KF|6:
+	case KF|7:
+	case KF|8:
+	case KF|9:
+	case KF|10:
+	case KF|11:
+	case KF|12:
+		return KEY_F1+(r-(KF|1));
+
+	default:
+		if(r < 0x80)
+			return r;
+	}
+	return 0;
+}
+
+static void
+kbdproc(void)
+{
+	char buf[128], buf2[128], *s;
+	int kfd, n;
+	Rune r;
+	event_t e;
+
+	if((kfd = open("/dev/kbd", OREAD)) < 0)
+		sysfatal("can't open kbd: %r");
+
+	buf2[0] = 0;
+	buf2[1] = 0;
+	buf[0] = 0;
+	for(;;){
+		if(buf[0] != 0){
+			n = strlen(buf)+1;
+			memmove(buf, buf+n, sizeof(buf)-n);
+		}
+		if(buf[0] == 0){
+			n = read(kfd, buf, sizeof(buf)-1);
+			if(n <= 0)
+				break;
+			buf[n-1] = 0;
+			buf[n] = 0;
+		}
+
+		e.data1 = -1;
+		e.data2 = -1;
+		e.data3 = -1;
+
+		switch(buf[0]){
+		case 'c':
+			chartorune(&r, buf+1);
+			if(r){
+				e.data1 = r;
+				e.type = ev_char;
+				D_PostEvent(&e);
+			}
+			/* no break */
+		default:
+			continue;
+		case 'k':
+			s = buf+1;
+			while(*s){
+				s += chartorune(&r, s);
+				if(utfrune(buf2+1, r) == nil){
+					if(e.data1 = runetokey(r)){
+						e.type = ev_keydown;
+						D_PostEvent(&e);
+					}
+				}
+			}
+			break;
+		case 'K':
+			s = buf2+1;
+			while(*s){
+				s += chartorune(&r, s);
+				if(utfrune(buf+1, r) == nil){
+					if(e.data1 = runetokey(r)){
+						e.type = ev_keyup;
+						D_PostEvent(&e);
+					}
+				}
+			}
+			break;
+		}
+		strcpy(buf2, buf);
+	}
+}
+
+static void
+mouseproc(void)
+{
+	int fd, n, nerr;
+	Mouse m, om;
+	char buf[1+5*12];
+	event_t e;
+
+	if((fd = open("/dev/mouse", ORDWR)) < 0)
+		sysfatal("can't open mouse: %r");
+
+	memset(&m, 0, sizeof m);
+	memset(&om, 0, sizeof om);
+	nerr = 0;
+	for(;;){
+		n = read(fd, buf, sizeof buf);
+		if(n != 1+4*12){
+			fprint(2, "mouse: bad count %d not 49: %r\n", n);
+			if(n<0 || ++nerr>10)
+				break;
+			continue;
+		}
+		nerr = 0;
+		switch(buf[0]){
+		case 'r':
+			resized = 1;
+			/* fall through */
+		case 'm':
+			if(!mouseactive)
+				break;
+
+			m.xy.x = atoi(buf+1+0*12);
+			m.xy.y = atoi(buf+1+1*12);
+			m.buttons = atoi(buf+1+2*12);
+			m.msec = atoi(buf+1+3*12);
+
+			if(!ptinrect(m.xy, grabout)){
+				fprint(fd, "m%d %d", center.x, center.y);
+
+				m.xy = center;
+				om.xy = center;
+			}
+			
+			e.type = ev_mouse;
+			e.data1 = m.buttons;
+			e.data2 = m.xy.x - om.xy.x;
+			e.data3 = om.xy.y - m.xy.y;
+			D_PostEvent(&e);
+			om = m;
+
+			break;
+		}
+	}
+}
+
--- /dev/null
+++ b/i_video.h
@@ -1,0 +1,63 @@
+// Emacs style mode select   -*- C++ -*- 
+//-----------------------------------------------------------------------------
+//
+// $Id:$
+//
+// Copyright (C) 1993-1996 by id Software, Inc.
+//
+// This source is available for distribution and/or modification
+// only under the terms of the DOOM Source Code License as
+// published by id Software. All rights reserved.
+//
+// The source is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
+// for more details.
+//
+// DESCRIPTION:
+//	System specific interface stuff.
+//
+//-----------------------------------------------------------------------------
+
+
+#ifndef __I_VIDEO__
+#define __I_VIDEO__
+
+
+//#include "doomtype.h"
+
+#ifdef __GNUG__
+#pragma interface
+#endif
+
+
+// Called by D_DoomMain,
+// determines the hardware configuration
+// and sets up the video mode
+void I_InitGraphics (void);
+
+
+void I_ShutdownGraphics(void);
+
+// Takes full 8 bit values.
+void I_SetPalette (byte* palette);
+
+void I_UpdateNoBlit (void);
+void I_FinishUpdate (void);
+void I_MouseEnable (int);
+
+// Wait for vertical retrace or pause a bit.
+void I_WaitVBL(int count);
+
+void I_ReadScreen (byte* scr);
+
+void I_BeginRead (void);
+void I_EndRead (void);
+
+
+#endif
+//-----------------------------------------------------------------------------
+//
+// $Log:$
+//
+//-----------------------------------------------------------------------------
--- /dev/null
+++ b/in_lude.c
@@ -1,0 +1,1084 @@
+/*
+========================
+=
+= IN_lude.c
+=
+========================
+*/
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "soundst.h"
+#include "v_compat.h"
+
+// MACROS ------------------------------------------------------------------
+
+#ifdef RENDER3D
+#define V_DrawPatch(x,y,p)		OGL_DrawPatch((x),(y),(p))
+#define V_DrawFuzzPatch(x,y,p)		OGL_DrawFuzzPatch((x),(y),(p))
+#define V_DrawAltFuzzPatch(x,y,p)	OGL_DrawAltFuzzPatch((x),(y),(p))
+#define V_DrawShadowedPatch(x,y,p)	OGL_DrawShadowedPatch((x),(y),(p))
+#endif
+
+// TYPES -------------------------------------------------------------------
+
+typedef enum
+{
+	SINGLE,
+	COOPERATIVE,
+	DEATHMATCH
+} gametype_t;
+
+typedef struct
+{
+	int x;
+	int y;
+} yahpt_t;
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+extern void AM_Stop(void);
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+void IN_Start(void);
+void IN_Ticker(void);
+void IN_Drawer(void);
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+static void IN_WaitStop(void);
+static void IN_Stop(void);
+static void IN_LoadPics(void);
+static void IN_UnloadPics(void);
+static void IN_CheckForSkip(void);
+static void IN_InitStats(void);
+static void IN_DrawOldLevel(void);
+static void IN_DrawYAH(void);
+static void IN_DrawStatBack(void);
+static void IN_DrawSingleStats(void);
+static void IN_DrawCoopStats(void);
+static void IN_DrawDMStats(void);
+static void IN_DrawNumber(int val, int x, int y, int digits);
+static void IN_DrawTime(int x, int y, int h, int m, int s);
+static void IN_DrTextB(const char *text, int x, int y);
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+extern const char *LevelNames[];
+
+// PUBLIC DATA DECLARATIONS ------------------------------------------------
+
+boolean intermission;
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+static boolean skipintermission;
+static int interstate = 0;
+static int intertime = -1;
+static int oldintertime = 0;
+static gametype_t gametype;
+
+static int cnt;
+
+static int thetime;
+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_REF patchINTERPIC;
+static PATCH_REF patchBEENTHERE;
+static PATCH_REF patchGOINGTHERE;
+static PATCH_REF FontBNumbers[10];
+static PATCH_REF FontBNegative;
+#ifdef RENDER3D
+static patch_t *FontB3DNumbers[10];
+static patch_t *FontB3DNegative;
+#endif
+static PATCH_REF FontBSlash;
+static PATCH_REF 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 const char *KillersText[] =
+	{ "K", "I", "L", "L", "E", "R", "S" };
+
+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 }
+	}
+};
+
+// CODE --------------------------------------------------------------------
+
+//========================================================================
+//
+// IN_Start
+//
+//========================================================================
+
+void IN_Start(void)
+{
+	V_SetPaletteBase();
+	IN_LoadPics();
+	IN_InitStats();
+	intermission = true;
+	interstate = -1;
+	skipintermission = false;
+	intertime = 0;
+	oldintertime = 0;
+	AM_Stop();
+	S_StartSong(mus_intr, true);
+}
+
+//========================================================================
+//
+// IN_WaitStop
+//
+//========================================================================
+
+static void IN_WaitStop(void)
+{
+	if (!--cnt)
+	{
+		IN_Stop();
+		G_WorldDone();
+	}
+}
+
+//========================================================================
+//
+// IN_Stop
+//
+//========================================================================
+
+static void IN_Stop(void)
+{
+	intermission = false;
+	IN_UnloadPics();
+	SB_state = -1;
+	BorderNeedRefresh = true;
+}
+
+//========================================================================
+//
+// IN_InitStats
+//
+// 	Initializes the stats for single player mode
+//========================================================================
+
+static void IN_InitStats(void)
+{
+	int i;
+	int j;
+	signed int slaughterfrags;
+	int posnum;
+	int slaughtercount;
+	int playercount;
+
+	if (!netgame)
+	{
+		gametype = SINGLE;
+		thetime = leveltime/35;
+		hours = thetime/3600;
+		thetime -= hours*3600;
+		minutes = thetime/60;
+		thetime -= minutes*60;
+		seconds = thetime;
+	}
+	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
+//
+//========================================================================
+
+static void IN_LoadPics(void)
+{
+	int i;
+
+	switch (gameepisode)
+	{
+	case 1:
+		patchINTERPIC = (PATCH_REF)WR_CacheLumpName("MAPE1", PU_STATIC);
+		break;
+	case 2:
+		patchINTERPIC = (PATCH_REF)WR_CacheLumpName("MAPE2", PU_STATIC);
+		break;
+	case 3:
+		patchINTERPIC = (PATCH_REF)WR_CacheLumpName("MAPE3", PU_STATIC);
+		break;
+	default:
+		break;
+	}
+	patchBEENTHERE = (PATCH_REF)WR_CacheLumpName("IN_X", PU_STATIC);
+	patchGOINGTHERE = (PATCH_REF)WR_CacheLumpName("IN_YAH", PU_STATIC);
+	FontBLumpBase = W_GetNumForName("FONTB16");
+	for (i = 0; i < 10; i++)
+	{
+		FontBNumbers[i] = (PATCH_REF)WR_CacheLumpNum(FontBLumpBase + i, PU_STATIC);
+#ifdef RENDER3D
+		FontB3DNumbers[i] = (patch_t *)W_CacheLumpNum(FontBLumpBase + i, PU_STATIC);
+#endif
+	}
+	FontBLump = W_GetNumForName("FONTB_S")+1;
+	FontBNegative = (PATCH_REF)WR_CacheLumpName("FONTB13", PU_STATIC);
+#ifdef RENDER3D
+	FontB3DNegative = (patch_t *)W_CacheLumpName("FONTB13", PU_STATIC);
+#endif
+
+	FontBSlash = (PATCH_REF)WR_CacheLumpName("FONTB15", PU_STATIC);
+	FontBPercent = (PATCH_REF)WR_CacheLumpName("FONTB05", PU_STATIC);
+	patchFaceOkayBase = W_GetNumForName("FACEA0");
+	patchFaceDeadBase = W_GetNumForName("FACEB0");
+}
+
+//========================================================================
+//
+// IN_UnloadPics
+//
+//========================================================================
+
+static void IN_UnloadPics(void)
+{
+	int i;
+
+	if (patchINTERPIC)
+	{
+		ZR_ChangeTag(patchINTERPIC, PU_CACHE);
+	}
+	ZR_ChangeTag(patchBEENTHERE, PU_CACHE);
+	ZR_ChangeTag(patchGOINGTHERE, PU_CACHE);
+	for(i = 0; i < 10; i++)
+	{
+		ZR_ChangeTag(FontBNumbers[i], PU_CACHE);
+#ifdef RENDER3D
+		Z_ChangeTag(FontB3DNumbers[i], PU_CACHE);
+#endif
+	}
+	ZR_ChangeTag(FontBNegative, PU_CACHE);
+#ifdef RENDER3D
+	Z_ChangeTag (FontB3DNegative, PU_CACHE);
+#endif
+	ZR_ChangeTag(FontBSlash, PU_CACHE);
+	ZR_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 = H2MAXINT;
+			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
+//========================================================================
+
+static 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
+//
+//========================================================================
+
+static void IN_DrawStatBack(void)
+{
+#ifndef RENDER3D
+	int x;
+	int y;
+
+	byte *src;
+	byte *dest;
+
+	src = (byte *) W_CacheLumpName ("FLOOR16", PU_CACHE);
+	dest = screens;
+
+	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);
+		}
+	}
+#else
+	OGL_SetFlat(R_FlatNumForName("FLOOR16"));
+	OGL_DrawRectTiled(0, 0, SCREENWIDTH, SCREENHEIGHT, 64, 64);
+#endif
+}
+
+//========================================================================
+//
+// IN_DrawOldLevel
+//
+//========================================================================
+
+static 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
+//
+//========================================================================
+
+static 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
+//
+//========================================================================
+
+static 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
+//
+//========================================================================
+
+static 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, (PATCH_REF)WR_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
+//
+//========================================================================
+
+static void IN_DrawDMStats(void)
+{
+	int i;
+	int j;
+	int ypos;
+	int xpos;
+	int kpos;
+	//int x;	// unused variable
+
+	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,
+							(PATCH_REF)WR_CacheLumpNum(patchFaceOkayBase + i, PU_CACHE));
+				V_DrawShadowedPatch(((xpos<<FRACBITS) + dSlideX[i]*intertime)>>FRACBITS, 18,
+							(PATCH_REF)WR_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, (PATCH_REF)WR_CacheLumpNum(patchFaceOkayBase + i, PU_CACHE));
+				V_DrawShadowedPatch(xpos, 18, (PATCH_REF)WR_CacheLumpNum(patchFaceDeadBase + i, PU_CACHE));
+			}
+			else
+			{
+				V_DrawFuzzPatch(40, ypos, (PATCH_REF)WR_CacheLumpNum(patchFaceOkayBase + i, PU_CACHE));
+				V_DrawFuzzPatch(xpos, 18, (PATCH_REF)WR_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
+//
+//========================================================================
+
+static 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
+//
+//========================================================================
+
+static void IN_DrawNumber(int val, int x, int y, int digits)
+{
+	patch_t *patch;
+	int width;
+	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)
+	{
+#ifdef RENDER3D
+		patch = FontB3DNumbers[val/1000];
+		width = SHORT(patch->width) / 2;
+		OGL_DrawShadowedPatch(xpos + 6 - width - 12, y, FontBNumbers[val/1000]);
+#else
+		patch = FontBNumbers[val/1000];
+		width = SHORT(patch->width) / 2;
+		V_DrawShadowedPatch(xpos + 6 - width - 12, y, patch);
+#endif
+	}
+	if (digits > 2)
+	{
+		if (realdigits > 2)
+		{
+#ifdef RENDER3D
+			patch = FontB3DNumbers[val/100];
+			width = SHORT(patch->width) / 2;
+			OGL_DrawShadowedPatch(xpos + 6 - width, y, FontBNumbers[val/100]);
+#else
+			patch = FontBNumbers[val/100];
+			width = SHORT(patch->width) / 2;
+			V_DrawShadowedPatch(xpos + 6 - width, y, patch);
+#endif
+		}
+		xpos += 12;
+	}
+	val = val % 100;
+	if (digits > 1)
+	{
+		if (val > 9)
+		{
+#ifdef RENDER3D
+			patch = FontB3DNumbers[val/10];
+			width = SHORT(patch->width) / 2;
+			OGL_DrawShadowedPatch(xpos + 6 - width, y, FontBNumbers[val/10]);
+#else
+			patch = FontBNumbers[val/10];
+			width = SHORT(patch->width) / 2;
+			V_DrawShadowedPatch(xpos + 6 - width, y, patch);
+#endif
+		}
+		else if (digits == 2 || oldval > 99)
+		{
+			V_DrawShadowedPatch(xpos, y, FontBNumbers[0]);
+		}
+		xpos += 12;
+	}
+	val = val % 10;
+#ifdef RENDER3D
+	patch = FontB3DNumbers[val];
+	width = SHORT(patch->width) / 2;
+	OGL_DrawShadowedPatch(xpos + 6 - width, y, FontBNumbers[val]);
+#else
+	patch = FontBNumbers[val];
+	width = SHORT(patch->width) / 2;
+	V_DrawShadowedPatch(xpos + 6 - width, y, patch);
+#endif
+	if (neg)
+	{
+#ifdef RENDER3D
+		patch = FontB3DNegative;
+		width = SHORT(patch->width) / 2;
+		OGL_DrawShadowedPatch(xpos + 6 - width - 12*(realdigits), y, FontBNegative);
+#else
+		patch = FontBNegative;
+		width = SHORT(patch->width) / 2;
+		V_DrawShadowedPatch(xpos + 6 - width - 12*(realdigits), y, patch);
+#endif
+	}
+}
+
+//========================================================================
+//
+// IN_DrTextB
+//
+//========================================================================
+
+static void IN_DrTextB(const char *text, int x, int y)
+{
+	char c;
+	patch_t *p;
+
+	while ((c = *text++) != 0)
+	{
+		if (c < 33)
+		{
+			x += 8;
+		}
+		else
+		{
+			p = (patch_t *) W_CacheLumpNum(FontBLump + c - 33, PU_CACHE);
+#ifdef RENDER3D
+			OGL_DrawShadowedPatch(x, y, FontBLump + c - 33);
+#else
+			V_DrawShadowedPatch(x, y, p);
+#endif
+			x += SHORT(p->width) - 1;
+		}
+	}
+}
+
--- /dev/null
+++ b/info.c
@@ -1,0 +1,5679 @@
+// info.c
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+// generated by multigen
+
+const 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", NULL
+};
+
+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/info.h
@@ -1,0 +1,1557 @@
+// info.h
+
+#ifndef __INFO_H
+#define __INFO_H
+
+// 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,
+SPR_NULL, /* for the terminator in sprnames[] */
+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;
+	int32_t			frame;
+	int32_t			tics;
+	void			(*action) (void*);
+	statenum_t		nextstate;
+	int32_t			misc1, misc2;
+} state_t;
+
+extern state_t	states[NUMSTATES];
+extern const 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];
+
+#endif	/* __INFO_H */
--- /dev/null
+++ b/m_bams.h
@@ -1,0 +1,42 @@
+//**************************************************************************
+//**
+//** m_bams.h
+//**
+//**************************************************************************
+
+#ifndef __MY_BAMS_MATH_H__
+#define __MY_BAMS_MATH_H__
+
+typedef unsigned short	binangle;
+
+/* Some predefined angles */
+#define	BANG_0		0		/* To the east.		*/
+#define BANG_45		0x2000		/* To the northeast.	*/
+#define BANG_90		0x4000		/* To the north.	*/
+#define BANG_135	0x6000		/* To the northwest.	*/
+#define BANG_180	0x8000		/* To the west.		*/
+#define BANG_225	0xa000		/* To the southwest.	*/
+#define BANG_270	0xc000		/* To the south.	*/
+#define BANG_315	0xe000		/* To the southeast.	*/
+#define BANG_360	0x10000		/* The same as angle 0.	*/
+#define BANG_MAX	0xffff		/* The largest possible angle.	*/
+
+/* Compass directions, for convenience. */
+#define BANG_EAST	BANG_0
+#define BANG_NORTHEAST	BANG_45
+#define BANG_NORTH	BANG_90
+#define BANG_NORTHWEST	BANG_135
+#define BANG_WEST	BANG_180
+#define BANG_SOUTHWEST	BANG_225
+#define BANG_SOUTH	BANG_270
+#define BANG_SOUTHEAST	BANG_315
+
+#define BAMS_PI		3.14159265359
+#define RAD2BANG(x)	((binangle)((x)/BAMS_PI*BANG_180))
+#define BANG2RAD(x)	((float)((x)/(float)BANG_180*BAMS_PI))
+
+void bamsInit(void);	/* Fill in the tables */
+binangle bamsAtan2(int y,int x);
+
+#endif	/* __MY_BAMS_MATH_H__ */
+
--- /dev/null
+++ b/m_misc.c
@@ -1,0 +1,794 @@
+// M_misc.c
+
+#include "h2stdinc.h"
+#include <ctype.h>
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+#if defined(__WATCOMC__) && defined(_DOS)
+#include "i_sound.h"
+#endif
+#ifdef RENDER3D
+#include "ogl_def.h"
+#endif
+
+#ifndef O_BINARY
+# if defined(_O_BINARY)
+#  define O_BINARY	_O_BINARY
+# else
+#  define O_BINARY		0
+# endif
+#endif
+
+int		myargc;
+const 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 (const char *check)
+{
+	int i;
+
+	for (i = 1; i < myargc; i++)
+	{
+		if (!strcasecmp(check, myargv[i]))
+			return i;
+	}
+
+	return 0;
+}
+
+//==========================================================================
+//
+// M_ExtractFileBase
+//
+//==========================================================================
+
+void M_ExtractFileBase(const char *path, char *dest)
+{
+	const char *src;
+	int length;
+
+	src = path + strlen(path) - 1;
+	if (src <= path)
+	{
+		*dest = '\0';
+		return;
+	}
+
+	// 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++);
+	}
+}
+
+/*
+===============
+=
+= 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] = H2MININT;
+	box[BOXBOTTOM] = box[BOXLEFT] = H2MAXINT;
+}
+
+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
+=
+==================
+*/
+
+boolean M_WriteFile (char const *name, const void *source, int length)
+{
+	int handle, count;
+
+	handle = create (name, OWRITE, 0666);
+	if (handle < 0)
+		return false;
+	count = write (handle, source, length);
+	close (handle);
+
+	if (count < length)
+		return false;
+
+	return true;
+}
+
+
+/*
+==================
+=
+= M_ReadFile
+=
+==================
+*/
+
+int M_ReadFile (char const *name, void **buffer)
+{
+	int handle, count, length;
+	void *buf;
+	Dir *d;
+
+	handle = open (name, OREAD);
+	if (handle == -1)
+		I_Error ("Couldn't read file %s", name);
+	d = dirfstat(handle);
+	if (d == nil)
+	{
+		I_Error("Couldn't read file %s", name);
+	}
+	length = d->length;
+	free(d);
+	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, k, idx;
+			int indexinfile;
+			char *infile;
+			char *file;
+			const char *moreargs[20];
+			const char *firstargv;
+
+			// READ THE RESPONSE FILE INTO MEMORY
+			handle = fopen(&myargv[i][1], "rb");
+			if (!handle)
+			{
+				printf("\nNo such response file!");
+				exits("No such response file!");
+			}
+			printf("Found response file %s!\n", &myargv[i][1]);
+			fseek (handle, 0, SEEK_END);
+			size = ftell(handle);
+			fseek (handle, 0, SEEK_SET);
+			file = (char *) malloc (size);
+			fread (file, size, 1, handle);
+			fclose (handle);
+
+			// KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG
+			for (idx = 0, k = i + 1; k < myargc; k++)
+				moreargs[idx++] = myargv[k];
+
+			firstargv = myargv[0];
+			myargv = (const char **) calloc(1, 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 < idx; 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;
+
+int	mouseSensitivity;
+
+extern int mouselook;
+extern int alwaysrun;
+
+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 messageson;
+
+extern int viewwidth, viewheight;
+
+extern int screenblocks;
+
+extern int snd_Channels;
+#if defined(__WATCOMC__) && defined(_DOS)
+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	/* DOS vars */
+
+default_t defaults[] =
+{
+/* change of order here affects mn_menu.c :
+ * see, for example, Options3Items there...
+ */
+	{ "mouse_sensitivity",	&mouseSensitivity,	5,	0, 50 },
+	{ "sfx_volume",		&snd_MaxVolume,		10,	0, 15 },
+	{ "music_volume",	&snd_MusicVolume,	10,	0, 15 },
+
+	{ "key_right",		&key_right,		KEY_RIGHTARROW,	0, 254 },
+	{ "key_left",		&key_left,		KEY_LEFTARROW,	0, 254 },
+	{ "key_up",		&key_up,		KEY_UPARROW,	0, 254 },
+	{ "key_down",		&key_down,		KEY_DOWNARROW,	0, 254 },
+	{ "key_strafeleft",	&key_strafeleft,	',',		0, 254 },
+	{ "key_straferight",	&key_straferight,	'.',		0, 254 },
+	{ "key_flyup",		&key_flyup,		KEY_PGUP,	0, 254 },
+	{ "key_flydown",	&key_flydown,		KEY_INS,	0, 254 },
+	{ "key_flycenter",	&key_flycenter,		KEY_HOME,	0, 254 },
+	{ "key_lookup",		&key_lookup,		KEY_PGDN,	0, 254 },
+	{ "key_lookdown",	&key_lookdown,		KEY_DEL,	0, 254 },
+	{ "key_lookcenter",	&key_lookcenter,	KEY_END,	0, 254 },
+	{ "key_invleft",	&key_invleft,		'[',		0, 254 },
+	{ "key_invright",	&key_invright,		']',		0, 254 },
+	{ "key_useartifact",	&key_useartifact,	KEY_ENTER,	0, 254 },
+	{ "key_fire",		&key_fire,		KEY_RCTRL,	0, 254 },
+	{ "key_use",		&key_use,		' ',		0, 254 },
+	{ "key_strafe",		&key_strafe,		KEY_RALT,	0, 254 },
+	{ "key_speed",		&key_speed,		KEY_RSHIFT,	0, 254 },
+
+	{ "use_mouse",		&usemouse,		1,	0, 1 },
+	{ "mouseb_fire",	&mousebfire,		0,	-1, 2 },
+	{ "mouseb_strafe",	&mousebstrafe,		1,	-1, 2 },
+	{ "mouseb_forward",	&mousebforward,		2,	-1, 2 },
+
+	{ "use_joystick",	&usejoystick,		0,	0, 1 },
+	{ "joyb_fire",		&joybfire,		0,	-1, 3 },
+	{ "joyb_strafe",	&joybstrafe,		1,	-1, 3 },
+	{ "joyb_use",		&joybuse,		3,	-1, 3 },
+	{ "joyb_speed",		&joybspeed,		2,	-1, 3 },
+
+	{ "screenblocks",	&screenblocks,		10,	3, 11 },
+	{ "usegamma",		&usegamma,		0,	0, 4 },
+	{ "messageson",		&messageson,		1,	0, 1 },
+	{ "mouselook",		&mouselook,		1,	0, 2 },
+	{ "alwaysrun",		&alwaysrun,		0,	0, 1 },
+
+	{ "snd_channels",	&snd_Channels,		3,	3, MAX_CHANNELS },
+#if defined(__WATCOMC__) && defined(_DOS)
+	/* the min/max values I added here are pretty much meaningless.
+	  the values used to be set by the DOS version's setup program. */
+	{ "snd_musicdevice",	&snd_DesiredMusicDevice,0,	0, NUM_SCARDS-1 },
+	{ "snd_sfxdevice",	&snd_DesiredSfxDevice,	0,	0, NUM_SCARDS-1 },
+	{ "snd_sbport",		&snd_SBport,		544,	0, 544 },
+	{ "snd_sbirq",		&snd_SBirq,		-1,	-1, 7 },
+	{ "snd_sbdma",		&snd_SBdma,		-1,	-1, 7 },
+	{ "snd_mport",		&snd_Mport,		-1,	-1, 360 }
+#endif	/* DOS vars */
+};
+
+default_str_t default_strings[] =
+{
+	{ "chatmacro0", chat_macros[0] },
+	{ "chatmacro1", chat_macros[1] },
+	{ "chatmacro2", chat_macros[2] },
+	{ "chatmacro3", chat_macros[3] },
+	{ "chatmacro4", chat_macros[4] },
+	{ "chatmacro5", chat_macros[5] },
+	{ "chatmacro6", chat_macros[6] },
+	{ "chatmacro7", chat_macros[7] },
+	{ "chatmacro8", chat_macros[8] },
+	{ "chatmacro9", chat_macros[9] }
+};
+
+static int numdefaults, numstrings;
+static char defaultfile[MAX_OSPATH];
+
+/*
+==============
+=
+= 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++)
+	{
+		v = *defaults[i].location;
+		fprintf (f, "%s\t\t%i\n", defaults[i].name, v);
+	}
+
+	for (i = 0; i < numstrings; i++)
+	{
+		fprintf (f, "%s\t\t\"%s\"\n", default_strings[i].name,
+					default_strings[i].location);
+	}
+
+	fclose (f);
+}
+
+
+/*
+==============
+=
+= M_LoadDefaults
+=
+==============
+*/
+
+void M_LoadDefaults(const char *fileName)
+{
+	int i;
+	int len;
+	FILE *f;
+	char def[80];
+	char strparm[100];
+	int parm;
+
+//
+// set everything to base values
+//
+	numdefaults = sizeof(defaults) / sizeof(defaults[0]);
+	numstrings = sizeof(default_strings) / sizeof(default_strings[0]);
+	printf("Loading default settings\n");
+	for (i = 0; i < numdefaults; i++)
+		*defaults[i].location = defaults[i].defaultvalue;
+	// Make a backup of all default strings
+	for (i = 0; i < numstrings; i++)
+	{
+		default_strings[i].defaultvalue = (char *) calloc(1, 80);
+		strcpy (default_strings[i].defaultvalue, default_strings[i].location);
+	}
+
+//
+// check for a custom default file
+//
+	i = M_CheckParm("-config");
+	if (i && i < myargc-1)
+	{
+		snprintf(defaultfile, sizeof(defaultfile), "%s%s", basePath, myargv[i + 1]);
+		printf("default file: %s\n", defaultfile);
+	}
+	else
+	{
+		snprintf(defaultfile, sizeof(defaultfile), "%s%s", basePath, fileName);
+	}
+
+//
+// read the file in, overriding any set defaults
+//
+	f = fopen(defaultfile, "r");
+	if (f)
+	{
+		while (!feof(f))
+		{
+			if (fscanf(f, "%79s %[^\n]\n", def, strparm) == 2)
+			{
+				if (strparm[0] == '"') /* string values */
+				{
+					for (i = 0; i < numstrings; i++)
+					{
+						if (!strcmp(def, default_strings[i].name))
+						{
+							len = (int)strlen(strparm) - 2;
+							if (len <= 0)
+							{
+								default_strings[i].location[0] = '\0';
+								break;
+							}
+							if (len > 79)
+							{
+								len = 79;
+							}
+							strncpy(default_strings[i].location, strparm + 1, len);
+							default_strings[i].location[len] = '\0';
+							break;
+						}
+					}
+					continue;
+				}
+
+				/* numeric values */
+				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 (parm >= defaults[i].minvalue && parm <= defaults[i].maxvalue)
+							*defaults[i].location = parm;
+						break;
+					}
+				}
+			}
+		}
+		fclose (f);
+	}
+}
+
+
+/*
+==============================================================================
+
+						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 (const char *filename, byte *data, int width, int height, byte *palette)
+{
+	int	i, length;
+	pcx_t	*pcx;
+	byte	*pack;
+
+	pcx = (pcx_t *) 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
+=
+==================
+*/
+#ifdef RENDER3D
+void M_ScreenShot (void)
+{
+	OGL_GrabScreen();
+}
+#else
+void M_ScreenShot (void)
+{
+	int	i;
+	byte	*linear;
+	char	lbmname[MAX_OSPATH], *p;
+	byte	*pal;
+
+#if defined(__WATCOMC__) && defined(_DOS)
+	extern  byte *pcscreen;
+#endif
+//
+// munge planar buffer to linear
+// 
+#if defined(__WATCOMC__) && defined(_DOS)
+	linear = pcscreen;
+#else
+	linear = screens;
+#endif
+//
+// find a file name to save it to
+//
+	snprintf (lbmname, sizeof(lbmname), "%shrtic00.pcx", basePath);
+	p = lbmname + strlen(basePath);
+	for (i = 0; i <= 99; i++)
+	{
+		p[5] = i/10 + '0';
+		p[6] = i%10 + '0';
+		if (access(lbmname, AEXIST) == -1)
+			break;	// file doesn't exist
+	}
+	if (i == 100)
+	{
+		P_SetMessage(&players[consoleplayer], "SCREEN SHOT FAILED", false);
+		return;
+	}
+
+//
+// save the pcx file
+//
+#if defined(__WATCOMC__) && defined(_DOS)
+	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);
+
+	P_SetMessage(&players[consoleplayer], "SCREEN SHOT", false);
+#if defined(__WATCOMC__) && defined(_DOS)
+	Z_Free(pal);
+#endif
+}
+#endif
+
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,57 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/games
+TARG=heretic
+CFLAGS=-Fpw
+
+OFILES = \
+	i_system.$O \
+	i_video.$O \
+	i_net.$O \
+	i_sound.$O \
+	i_cdmus.$O \
+	i_main.$O \
+	am_map.$O \
+	ct_chat.$O \
+	d_net.$O \
+	f_finale.$O \
+	g_game.$O \
+	d_main.$O \
+	info.$O \
+	in_lude.$O \
+	mn_menu.$O \
+	m_misc.$O \
+	p_ceilng.$O \
+	p_doors.$O \
+	p_enemy.$O \
+	p_floor.$O \
+	p_inter.$O \
+	p_lights.$O \
+	p_map.$O \
+	p_maputl.$O \
+	p_mobj.$O \
+	p_plats.$O \
+	p_pspr.$O \
+	p_setup.$O \
+	p_sight.$O \
+	p_spec.$O \
+	p_switch.$O \
+	p_telept.$O \
+	p_tick.$O \
+	p_user.$O \
+	r_bsp.$O \
+	r_data.$O \
+	r_draw.$O \
+	r_main.$O \
+	r_plane.$O \
+	r_segs.$O \
+	r_things.$O \
+	sb_bar.$O \
+	s_sound.$O \
+	sv_save.$O \
+	tables.$O \
+	v_video.$O \
+	w_wad.$O \
+	z_zone.$O
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/mn_menu.c
@@ -1,0 +1,2081 @@
+// MN_menu.c
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include <ctype.h>
+#include "doomdef.h"
+#include "p_local.h"
+#include "r_local.h"
+#include "soundst.h"
+#include "v_compat.h"
+
+// MACROS ------------------------------------------------------------------
+
+#define LEFT_DIR		0
+#define RIGHT_DIR		1
+#define ITEM_HEIGHT		20
+#define SMALL_ITEM_HEIGHT	9
+#define MENU_MAX_MOUSE_SENS	50
+
+#define SELECTOR_XOFFSET	(-28)
+#define SELECTOR_YOFFSET	(-1)
+#define SLOTTEXTLEN		16
+#define ASCII_CURSOR		'['
+
+#ifdef RENDER3D
+#define V_DrawPatch(x,y,p)		OGL_DrawPatch((x),(y),(p))
+#define V_DrawRawScreen(a)		OGL_DrawRawScreen((a))
+#endif
+
+// TYPES -------------------------------------------------------------------
+
+typedef enum
+{
+	ITT_EMPTY,
+	ITT_EFUNC,
+	ITT_LRFUNC,
+	ITT_SETMENU,
+	ITT_INERT,
+	ITT_SETKEY
+} ItemType_t;
+
+typedef enum
+{
+	MENU_MAIN,
+	MENU_EPISODE,
+	MENU_SKILL,
+	MENU_OPTIONS,
+	MENU_OPTIONS2,
+	MENU_OPTIONS3,
+	MENU_FILES,
+	MENU_LOAD,
+	MENU_SAVE,
+	MENU_NONE
+} MenuType_t;
+
+typedef struct
+{
+	ItemType_t type;
+	const 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;
+	int step;
+	MenuType_t prevMenu;
+} Menu_t;
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+static const char *Key2String(int key);
+static void ClearControls(int key);
+static void InitFonts(void);
+static void SetTheMenu(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 boolean SCSetKey(int option);
+static boolean SCMouselook(int option);
+static boolean SCAlwaysRun(int option);
+static void DrawMainMenu(void);
+static void DrawEpisodeMenu(void);
+static void DrawSkillMenu(void);
+static void DrawOptionsMenu(void);
+static void DrawOptions2Menu(void);
+static void DrawOptions3Menu(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);
+static void MN_LoadSlotText(void);
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+extern default_t defaults[];
+
+extern int detailLevel;
+extern int screenblocks;
+
+extern int alwaysrun;
+extern int mouselook;
+
+extern int key_right,key_left,key_up,key_down;
+extern int key_straferight,key_strafeleft;
+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;
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+boolean MenuActive;
+int InfoType;
+int messageson;	/* boolean */
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+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;
+
+#ifdef RENDER3D
+static float bgAlpha = 0;
+static float outFade = 0;
+static boolean fadingOut = false;
+static int menuDarkTicks = 15;
+static int slamInTicks = 9;
+#endif
+
+boolean askforquit;
+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 int typeofask;
+
+static int FirstKey = 0;
+static boolean askforkey = false;
+static int keyaskedfor;
+
+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,
+	ITEM_HEIGHT,
+	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,
+	ITEM_HEIGHT,
+	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,
+	ITEM_HEIGHT,
+	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 LoadgameMenu =
+{
+	70, 30,
+	DrawLoadMenu,
+	6, LoadItems,
+	0,
+	ITEM_HEIGHT,
+	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 SavegameMenu =
+{
+	70, 30,
+	DrawSaveMenu,
+	6, SaveItems,
+	0,
+	ITEM_HEIGHT,
+	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,
+	ITEM_HEIGHT,
+	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_SETMENU, "CONTROL SETUP", NULL, 0, MENU_OPTIONS3 },
+	{ ITT_LRFUNC, "MOUSELOOK : ", SCMouselook, 0, MENU_NONE },
+	{ ITT_EFUNC, "ALWAYS RUN : ", SCAlwaysRun, 0, MENU_NONE },
+	{ ITT_SETMENU, "MORE...", NULL, 0, MENU_OPTIONS2 }
+};
+
+static Menu_t OptionsMenu =
+{
+	88, 30,
+	DrawOptionsMenu,
+	7,
+	OptionsItems,
+	0,
+	ITEM_HEIGHT,
+	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,
+	ITEM_HEIGHT,
+	MENU_OPTIONS
+};
+
+static MenuItem_t Options3Items[] =
+{
+/* see defaults[] in m_misc.c for the correct option number:
+ * key_right corresponds to defaults[3], which means that we
+ * are using the (index_number - 3) here.
+ */
+	{ ITT_SETKEY, "TURN RIGHT :", SCSetKey, 0, MENU_NONE },
+	{ ITT_SETKEY, "TURN LEFT :", SCSetKey, 1, MENU_NONE },
+	{ ITT_SETKEY, "MOVE FORWARD :", SCSetKey, 2, MENU_NONE },
+	{ ITT_SETKEY, "MOVE BACK :" , SCSetKey, 3, MENU_NONE },
+	{ ITT_SETKEY, "STRAFE LEFT :", SCSetKey, 4, MENU_NONE },
+	{ ITT_SETKEY, "STRAFE RIGHT :", SCSetKey, 5, MENU_NONE },
+	{ ITT_SETKEY, "FLY UP :", SCSetKey, 6, MENU_NONE },
+	{ ITT_SETKEY, "FLY DOWN :", SCSetKey, 7, MENU_NONE },
+	{ ITT_SETKEY, "FLY CENTER :", SCSetKey, 8, MENU_NONE },
+	{ ITT_SETKEY, "LOOK UP :", SCSetKey, 9, MENU_NONE },
+	{ ITT_SETKEY, "LOOK DOWN :", SCSetKey, 10, MENU_NONE },
+	{ ITT_SETKEY, "LOOK CENTER :", SCSetKey, 11, MENU_NONE },
+	{ ITT_SETKEY, "INVENTORY LEFT :", SCSetKey, 12, MENU_NONE },
+	{ ITT_SETKEY, "INVENTORY RIGHT :", SCSetKey, 13, MENU_NONE },
+	{ ITT_SETKEY, "USE ARTIFACT :", SCSetKey, 14, MENU_NONE },
+	{ ITT_SETKEY, "FIRE :", SCSetKey, 15, MENU_NONE },
+	{ ITT_SETKEY, "USE :", SCSetKey, 16, MENU_NONE },
+	{ ITT_SETKEY, "STRAFE :", SCSetKey, 17, MENU_NONE },
+	{ ITT_SETKEY, "SPEED :", SCSetKey, 18, MENU_NONE }
+};
+
+/* Many items in Options3Items[], only 15 can be drawn on a page:
+ * So, FirstKey changes between 0 and FIRSTKEY_MAX. This menu is
+ * way too fragile. Should we adapt from Quake's M_Menu_Keys and
+ * bindnames?? */
+#define FIRSTKEY_MAX	4
+static Menu_t Options3Menu =
+{
+	70, 20,
+	DrawOptions3Menu,
+	15, /* actually 19 */
+	Options3Items,
+	0,
+	SMALL_ITEM_HEIGHT,
+	MENU_OPTIONS
+};
+
+static Menu_t *Menus[] =
+{
+	&MainMenu,
+	&EpisodeMenu,
+	&SkillMenu,
+	&OptionsMenu,
+	&Options2Menu,
+	&Options3Menu,
+	&FilesMenu,
+	&LoadgameMenu,
+	&SavegameMenu
+};
+
+static const char *mlooktext[] =
+{
+	"OFF",
+	"NORMAL",
+	"INVERSE"
+};
+
+static const char *stupidtable[] =
+{
+	"A","B","C","D","E",
+	"F","G","H","I","J",
+	"K","L","M","N","O",
+	"P","Q","R","S","T",
+	"U","V","W","X","Y",
+	"Z"
+};
+
+static const char *GammaText[] =
+{
+	TXT_GAMMA_LEVEL_OFF,
+	TXT_GAMMA_LEVEL_1,
+	TXT_GAMMA_LEVEL_2,
+	TXT_GAMMA_LEVEL_3,
+	TXT_GAMMA_LEVEL_4
+};
+
+// CODE --------------------------------------------------------------------
+
+static const char *Key2String (int key)
+{
+/* S.A.: return "[" or "]" or "\"" doesn't work
+ * because there are no lumps for these chars,
+ * therefore we have to go with "RIGHT BRACKET"
+ * and similar for much punctuation.  Probably
+ * won't work with international keyboards and
+ * dead keys, either.
+ */
+	switch (key)
+	{
+	case KEY_LEFTBRACKET:	return "LEFT BRACK";
+	case KEY_RIGHTBRACKET:	return "RIGHT BRACK";
+	case KEY_BACKQUOTE:	return "BACK QUOTE";
+	case KEY_QUOTE:		return "'";
+	case KEY_QUOTEDBL:	return "DOUBLE QUOTE";
+	case KEY_SEMICOLON:	return ";";
+	case KEY_MINUS:		return "-";
+	case KEY_PERIOD:	return ".";
+	case KEY_COMMA:		return ",";
+	case KEY_SLASH:		return "/";
+	case KEY_BACKSLASH:	return "BACKSLASH";
+	case KEY_TAB:		return "TAB";
+	case KEY_EQUALS:	return "=";
+
+	case KEY_RIGHTARROW:	return "RIGHT ARROW";
+	case KEY_LEFTARROW:	return "LEFT ARROW";
+	case KEY_DOWNARROW:	return "DOWN ARROW";
+	case KEY_UPARROW:	return "UP ARROW";
+	case KEY_ENTER:		return "ENTER";
+	case KEY_PGUP:		return "PAGE UP";
+	case KEY_PGDN:		return "PAGE DOWN";
+	case KEY_INS:		return "INSERT";
+	case KEY_HOME:		return "HOME";
+	case KEY_END:		return "END";
+	case KEY_DEL:		return "DELETE";
+	case ' ':		return "SPACE";
+	case KEY_RSHIFT:	return "SHIFT";
+	case KEY_RALT:		return "ALT";
+	case KEY_RCTRL:		return "CTRL";
+	}
+	/* Handle letter keys */
+	/* S.A.: could also be done with toupper */
+	if (key >= 'a' && key <= 'z')
+		return stupidtable[(key - 'a')];
+
+	return "?";		/* Everything else */
+}
+
+static void ClearControls (int key)
+{
+	int i;
+
+	for (i = 3; i < 24; i++)
+	{
+		if (*defaults[i].location == key)
+			*defaults[i].location = 0;
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_Init
+//
+//---------------------------------------------------------------------------
+
+void MN_Init(void)
+{
+	InitFonts();
+	MenuActive = false;
+//	messageson = 1;	// Set by defaults in .CFG
+	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(const char *text, int x, int y)
+{
+	char c;
+	patch_t *p;
+
+#ifdef RENDER3D
+	OGL_SetColorAndAlpha(1, 1, 1, 1);
+#endif
+
+	while ((c = *text++) != 0)
+	{
+		if (c < 33)
+		{
+			x += 5;
+		}
+		else
+		{
+			p = (patch_t *) W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
+#ifdef RENDER3D
+			OGL_DrawPatch_CS(x, y, FontABaseLump + c - 33);
+#else
+			V_DrawPatch(x, y, p);
+#endif
+			x += SHORT(p->width) - 1;
+		}
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC MN_TextAWidth
+//
+// Returns the pixel width of a string using font A.
+//
+//---------------------------------------------------------------------------
+
+int MN_TextAWidth(const char *text)
+{
+	char c;
+	int width;
+	patch_t *p;
+
+	width = 0;
+	while ((c = *text++) != 0)
+	{
+		if (c < 33)
+		{
+			width += 5;
+		}
+		else
+		{
+			p = (patch_t *) W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
+			width += SHORT(p->width) - 1;
+		}
+	}
+	return (width);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_DrTextB
+//
+// Draw text using font B.
+//
+//---------------------------------------------------------------------------
+
+void MN_DrTextB(const char *text, int x, int y)
+{
+	char c;
+	patch_t *p;
+
+#ifdef RENDER3D
+	OGL_SetColorAndAlpha(1, 1, 1, 1);
+#endif
+
+	while ((c = *text++) != 0)
+	{
+		if (c < 33)
+		{
+			x += 8;
+		}
+		else
+		{
+			p = (patch_t *) W_CacheLumpNum(FontBBaseLump + c - 33, PU_CACHE);
+#ifdef RENDER3D
+			OGL_DrawPatch_CS(x, y, FontBBaseLump + c - 33);
+#else
+			V_DrawPatch(x, y, p);
+#endif
+			x += SHORT(p->width) - 1;
+		}
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC MN_TextBWidth
+//
+// Returns the pixel width of a string using font B.
+//
+//---------------------------------------------------------------------------
+
+int MN_TextBWidth(const char *text)
+{
+	char c;
+	int width;
+	patch_t *p;
+
+	width = 0;
+	while ((c = *text++) != 0)
+	{
+		if (c < 33)
+		{
+			width += 5;
+		}
+		else
+		{
+			p = (patch_t *) W_CacheLumpNum(FontBBaseLump + c - 33, PU_CACHE);
+			width += SHORT(p->width) - 1;
+		}
+	}
+	return (width);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_Ticker
+//
+//---------------------------------------------------------------------------
+
+void MN_Ticker(void)
+{
+	if (MenuActive == false)
+	{
+#ifdef RENDER3D
+		if (bgAlpha > 0)
+		{
+			bgAlpha -= .5 / (float)menuDarkTicks;
+			if (bgAlpha < 0)
+				bgAlpha = 0;
+		}
+		if (fadingOut)
+		{
+			outFade += 1 / (float)slamInTicks;
+			if (outFade > 1)
+				fadingOut = false;
+		}
+#endif
+		return;
+	}
+	MenuTime++;
+}
+
+
+#ifdef RENDER3D
+static void MN_OGL_SetupState(float time)
+{
+	glMatrixMode(GL_MODELVIEW);
+	glPushMatrix();
+
+	if (time > 1 && time <= 2)
+	{
+		time = 2 - time;
+		glTranslatef(160, 100, 0);
+		glScalef(.9 + time*.1, .9 + time*.1, 1);
+		glTranslatef(-160, -100, 0);
+		glColor4f(1, 1, 1, time);
+		return;
+	}
+
+	glTranslatef(160, 100, 0);
+	glScalef(2-time, 2-time, 1);
+	glTranslatef(-160, -100, 0);
+	glColor4f(1, 1, 1, time*time);
+}
+
+static void MN_OGL_RestoreState(void)
+{
+	glMatrixMode(GL_MODELVIEW);
+	glPopMatrix();
+}
+#endif
+
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_Drawer
+//
+//---------------------------------------------------------------------------
+
+static const 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;
+	const char *selName;
+
+	if (MenuActive == false)
+	{
+#ifdef RENDER3D
+		if (bgAlpha > 0)
+		{
+			UpdateState |= I_FULLSCRN;
+			BorderNeedRefresh = true;
+		//	OGL_SetNoTexture();
+			glDisable(GL_TEXTURE_2D);
+			OGL_DrawRect(0, 0, 320, 200, 0, 0, 0, bgAlpha);
+			glEnable(GL_TEXTURE_2D);
+		}
+#endif
+		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;
+	}
+#ifdef RENDER3D
+	if (MenuActive || fadingOut)
+	{
+		int effTime = (MenuTime > menuDarkTicks) ? menuDarkTicks : MenuTime;
+		float temp = .5 * effTime / (float)menuDarkTicks;
+
+		UpdateState |= I_FULLSCRN;
+
+		if (!fadingOut)
+		{
+			if (temp > bgAlpha)
+				bgAlpha = temp;
+			effTime = (MenuTime>slamInTicks) ? slamInTicks : MenuTime;
+			temp = effTime / (float)slamInTicks;
+
+			// Draw a dark background. It makes it easier to read the menus.
+			//OGL_SetNoTexture();
+			glDisable(GL_TEXTURE_2D);
+			OGL_DrawRect(0, 0, 320, 200, 0, 0, 0, bgAlpha);
+			glEnable(GL_TEXTURE_2D);
+		}
+		else
+			temp = outFade + 1;
+		MN_OGL_SetupState(temp);
+
+		if (InfoType)
+		{
+			MN_DrawInfo();
+			MN_OGL_RestoreState();
+			return;
+		}
+	//	if (screenblocks < 10)
+	//	{
+			BorderNeedRefresh = true;
+	//	}
+		if (CurrentMenu->drawFunc != NULL)
+		{
+			CurrentMenu->drawFunc();
+		}
+		x = CurrentMenu->x;
+		y = CurrentMenu->y;
+		item = CurrentMenu->items;
+		if (item->type == ITT_SETKEY)
+			item += FirstKey;
+		for (i = 0; i < CurrentMenu->itemCount; i++)
+		{
+			switch (item->type)
+			{
+			case (ITT_EMPTY):
+				break;
+			case (ITT_SETKEY):
+				if (item->text)
+					MN_DrTextA(item->text, x, y+6);
+				break;
+			default:
+				if (item->text)
+					MN_DrTextB(item->text, x, y);
+			}
+			y += CurrentMenu->step;
+			item++;
+		}
+		y = CurrentMenu->y + (CurrentItPos * CurrentMenu->step) + SELECTOR_YOFFSET;
+		selName = (MenuTime & 16) ? "M_SLCTR1" : "M_SLCTR2";
+		OGL_DrawPatch_CS(x + SELECTOR_XOFFSET, y, W_GetNumForName(selName));
+
+		MN_OGL_RestoreState();
+	}
+#else
+	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;
+		if (item->type == ITT_SETKEY)
+			item += FirstKey;
+		for (i = 0; i < CurrentMenu->itemCount; i++)
+		{
+			switch (item->type)
+			{
+			case (ITT_EMPTY):
+				break;
+			case (ITT_SETKEY):
+				if (item->text)
+					MN_DrTextA(item->text, x, y+6);
+				break;
+			default:
+				if (item->text)
+					MN_DrTextB(item->text, x, y);
+			}
+			y += CurrentMenu->step;
+			item++;
+		}
+		y = CurrentMenu->y + (CurrentItPos * CurrentMenu->step) + SELECTOR_YOFFSET;
+		selName = (MenuTime & 16) ? "M_SLCTR1" : "M_SLCTR2";
+		V_DrawPatch(x + SELECTOR_XOFFSET, y, (patch_t *)W_CacheLumpName(selName, PU_CACHE));
+	}
+#endif
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawMainMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawMainMenu(void)
+{
+	int frame;
+
+	frame = (MenuTime / 3) % 18;
+
+#ifdef RENDER3D
+	OGL_DrawPatch_CS(88, 0, W_GetNumForName("M_HTIC") );
+	OGL_DrawPatch_CS(40, 10, SkullBaseLump + (17 - frame));
+	OGL_DrawPatch_CS(232, 10, SkullBaseLump + frame);
+#else
+	V_DrawPatch(88, 0, (patch_t *)W_CacheLumpName("M_HTIC", PU_CACHE));
+	V_DrawPatch(40, 10, (patch_t *)W_CacheLumpNum(SkullBaseLump + (17 - frame), PU_CACHE));
+	V_DrawPatch(232, 10, (patch_t *)W_CacheLumpNum(SkullBaseLump + frame, PU_CACHE));
+#endif
+}
+
+//---------------------------------------------------------------------------
+//
+// 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(&LoadgameMenu);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawSaveMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawSaveMenu(void)
+{
+	MN_DrTextB("SAVE GAME", 160 - MN_TextBWidth("SAVE GAME")/2, 10);
+	if (!slottextloaded)
+	{
+		MN_LoadSlotText();
+	}
+	DrawFileSlots(&SavegameMenu);
+}
+
+//===========================================================================
+//
+// MN_LoadSlotText
+//
+// Loads in the text message for each slot
+//===========================================================================
+
+static void MN_LoadSlotText(void)
+{
+	FILE *fp;
+	int i;
+	char name[MAX_OSPATH];
+
+	for (i = 0; i < 6; i++)
+	{
+		snprintf(name, sizeof(name), "%s%s%d.hsg", basePath, SAVEGAMENAME, i);
+		fp = fopen(name, "rb+");
+		if (!fp)
+		{
+			SlotText[i][0] = 0; // empty the string
+			SlotStatus[i] = 0;
+			continue;
+		}
+		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++)
+	{
+#ifdef RENDER3D
+		OGL_DrawPatch_CS(x, y, W_GetNumForName("M_FSLOT"));
+#else
+		V_DrawPatch(x, y, (patch_t *)W_CacheLumpName("M_FSLOT", PU_CACHE));
+#endif
+		if (SlotStatus[i])
+		{
+			MN_DrTextA(SlotText[i], x+5, y+5);
+		}
+		y += ITEM_HEIGHT;
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawOptionsMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawOptionsMenu(void)
+{
+	char num[5];
+
+	if (messageson)
+	{
+		MN_DrTextB("ON", 196, 50);
+	}
+	else
+	{
+		MN_DrTextB("OFF", 196, 50);
+	}
+
+	MN_DrTextB(mlooktext[mouselook], 208, 110);
+
+	snprintf(num, sizeof(num), "%d", mouseSensitivity);
+	MN_DrTextB(num, 265, 71);
+
+	if (alwaysrun)
+	{
+		MN_DrTextB("ON", 208, 130);
+	}
+	else
+	{
+		MN_DrTextB("OFF", 208,130);
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawOptions2Menu
+//
+//---------------------------------------------------------------------------
+
+static void DrawOptions2Menu(void)
+{
+	DrawSlider(&Options2Menu, 1, 9, screenblocks-3);
+	DrawSlider(&Options2Menu, 3, 16, snd_MaxVolume);
+	DrawSlider(&Options2Menu, 5, 16, snd_MusicVolume);
+}
+
+static void DrawOptions3Menu(void)
+{
+	int i;
+
+	for (i = 0; i < 15; i++)
+	{
+		if (askforkey && keyaskedfor == i)
+		{
+			MN_DrTextA("???", 195, (i*SMALL_ITEM_HEIGHT+26));
+		}
+		else
+		{
+			MN_DrTextA(Key2String(*(defaults[i+FirstKey+3].location)),
+				195, (i*SMALL_ITEM_HEIGHT+26));
+		}
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// 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;
+	}
+	I_Quit();
+	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)
+{
+	if (messageson)
+	{
+		messageson = 0;
+		P_SetMessage(&players[consoleplayer], "MESSAGES OFF", true);
+	}
+	else
+	{
+		messageson = 1;
+		P_SetMessage(&players[consoleplayer], "MESSAGES ON", true);
+	}
+	S_StartSound(NULL, sfx_chat);
+	return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCLoadGame
+//
+//---------------------------------------------------------------------------
+
+static boolean SCLoadGame(int option)
+{
+	if (!SlotStatus[option])
+	{ // slot's empty...don't try and load
+		return false;
+	}
+	G_LoadGame(option);
+	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;
+	player_t *player = &players[consoleplayer];
+
+	if (gamestate != GS_LEVEL || demoplayback
+		|| player->playerstate == PST_DEAD)
+	{
+		FileMenuKeySteal = false;
+		return false;
+	}
+
+	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;
+		SetTheMenu(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 < MENU_MAX_MOUSE_SENS)
+		{
+			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;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCSetKey
+//
+//---------------------------------------------------------------------------
+
+static boolean SCSetKey(int option)
+{
+	askforkey = true;
+	keyaskedfor = option;
+	if (!netgame && !demoplayback)
+	{
+		paused = true;
+	}
+	return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCMouslook(int option)
+//
+//---------------------------------------------------------------------------
+
+static boolean SCMouselook(int option)
+{
+	if (option == RIGHT_DIR)
+	{
+		if (mouselook < 2)
+			mouselook++;
+	}
+	else if (mouselook)
+		mouselook--;
+	return true;
+}
+
+static boolean SCAlwaysRun(int option)
+{
+	if (alwaysrun)
+		alwaysrun = 0;
+	else	alwaysrun = 1;
+	return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC MN_Responder
+//
+//---------------------------------------------------------------------------
+
+boolean MN_Responder(event_t *event)
+{
+	int key;
+	int i;
+	MenuItem_t *item;
+	static boolean shiftdown;
+	extern void D_StartTitle(void);
+	extern void G_CheckDemoStatus(void);
+	char *textBuffer;
+
+	if (askforkey && event->type == ev_keydown)
+	{
+		ClearControls(event->data1);
+		*defaults[keyaskedfor + 3 + FirstKey].location = event->data1;
+		askforkey = false;
+		return true;
+	}
+	if (askforkey && event->type == ev_mouse)
+	{
+		if (event->data1 & 1)
+			return true;
+		if (event->data1 & 2)
+			return true;
+		if (event->data1 & 4)
+			return true;
+		return false;
+	}
+	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)
+		{
+			if (!netgame && !demoplayback)
+			{
+				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)
+	{
+	// F12 is screenshot now. This
+	// is here for reference, only.
+		G_ScreenShot();
+		return true;
+	}
+
+	if (askforquit)
+	{
+		switch (key)
+		{
+		case KEY_ENTER:
+		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;
+					V_SetPaletteBase();
+					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;
+		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 = &SavegameMenu;
+				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 = &LoadgameMenu;
+				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: // S.A.: made F4 Controls. was Volume, before.
+			MenuActive = true;
+			FileMenuKeySteal = false;
+			MenuTime = 0;
+			CurrentMenu = &OptionsMenu;
+			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: // 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_F6: // quicksave
+			if (gamestate == GS_LEVEL && !demoplayback)
+			{
+				if (!quicksave || quicksave == -1)
+				{
+					MenuActive = true;
+					FileMenuKeySteal = false;
+					MenuTime = 0;
+					CurrentMenu = &SavegameMenu;
+					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 = &LoadgameMenu;
+					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
+		// S.A.: allowed quit to work when not in a level
+		//	if (!(gamestate == GS_LEVEL))
+		//		return true;
+			SCQuitGame(0);
+			S_StartSound(NULL, sfx_chat);
+			return true;
+		case KEY_F11: // F11 - gamma mode correction
+			usegamma++;
+			if (usegamma > 4)
+			{
+				usegamma = 0;
+			}
+#ifdef RENDER3D
+			OGL_TexReset ();
+#else
+			I_SetPalette((byte *)W_CacheLumpName("PLAYPAL", PU_CACHE));
+#endif
+			P_SetMessage(&players[consoleplayer], GammaText[usegamma], false);
+			return true;
+		case KEY_F12: // S.A.: made F12 Screenshot
+			G_ScreenShot();
+			return true;
+		}
+	}
+
+	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 (CurrentMenu->items[CurrentItPos].type == ITT_SETKEY
+					&& CurrentItPos+1 > CurrentMenu->itemCount-1)
+				{
+					if (FirstKey == FIRSTKEY_MAX)
+					{
+						CurrentItPos = 0; // End of Key menu
+						FirstKey = 0;
+					}
+					else
+					{
+						FirstKey++;
+					}
+				}
+				else if (CurrentItPos+1 > CurrentMenu->itemCount-1)
+				{
+					CurrentItPos = 0;
+				}
+				else
+				{
+					CurrentItPos++;
+				}
+			}
+			while (CurrentMenu->items[CurrentItPos].type == ITT_EMPTY);
+			S_StartSound(NULL, sfx_switch);
+			return true;
+		case KEY_UPARROW:
+			do
+			{
+				if (CurrentMenu->items[CurrentItPos].type == ITT_SETKEY && CurrentItPos == 0)
+				{
+					if (FirstKey == 0)
+					{
+						CurrentItPos = 14; // End of Key menu
+								   // 14 == 15 (max lines on a page) - 1
+						FirstKey = FIRSTKEY_MAX;
+					}
+					else
+					{
+						FirstKey--;
+					}
+				}
+				else if (CurrentItPos == 0)
+				{
+					CurrentItPos = CurrentMenu->itemCount-1;
+				}
+				else
+				{
+					CurrentItPos--;
+				}
+			}
+			while (CurrentMenu->items[CurrentItPos].type == ITT_EMPTY);
+			S_StartSound(NULL, sfx_switch);
+			return true;
+		case KEY_LEFTARROW:
+			if (item->type == ITT_LRFUNC && item->func != NULL)
+			{
+				item->func(LEFT_DIR);
+				S_StartSound(NULL, sfx_keyup);
+			}
+			return true;
+		case KEY_RIGHTARROW:
+			if (item->type == ITT_LRFUNC && item->func != NULL)
+			{
+				item->func(RIGHT_DIR);
+				S_StartSound(NULL, sfx_keyup);
+			}
+			return true;
+		case KEY_ENTER:
+			if (item->type == ITT_SETMENU)
+			{
+				SetTheMenu(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)
+						{
+							SetTheMenu(item->menu);
+						}
+					}
+				}
+				else if (item->type == ITT_SETKEY)
+				{
+					item->func(item->option);
+				}
+			}
+			S_StartSound(NULL, sfx_dorcls);
+			return true;
+		case KEY_ESCAPE:
+			MN_DeactivateMenu();
+			return true;
+		case KEY_BACKSPACE:
+			S_StartSound(NULL, sfx_switch);
+			if (CurrentMenu->prevMenu == MENU_NONE)
+			{
+				MN_DeactivateMenu();
+			}
+			else
+			{
+				SetTheMenu(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)
+				{
+					SetTheMenu(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)
+{
+	if (CurrentMenu)
+		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)
+{
+	V_SetPaletteBase();
+	V_DrawRawScreen((BYTE_REF) WR_CacheLumpNum(W_GetNumForName("TITLE")+InfoType, PU_CACHE));
+}
+
+
+//---------------------------------------------------------------------------
+//
+// PROC SetMenu
+//
+//---------------------------------------------------------------------------
+
+static void SetTheMenu(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);
+
+#ifdef RENDER3D
+	OGL_DrawPatch_CS(x-32, y, W_GetNumForName("M_SLDLT"));
+	for (x2 = x, count = width; count--; x2 += 8)
+	{
+		OGL_DrawPatch_CS(x2, y, W_GetNumForName((count & 1) ? "M_SLDMD1" : "M_SLDMD2"));
+	}
+	OGL_DrawPatch_CS(x2, y, W_GetNumForName("M_SLDRT"));
+	OGL_DrawPatch_CS(x + 4 + slot*8, y + 7, W_GetNumForName("M_SLDKB"));
+#else
+	V_DrawPatch(x-32, y, (patch_t *)W_CacheLumpName("M_SLDLT", PU_CACHE));
+	for (x2 = x, count = width; count--; x2 += 8)
+	{
+		V_DrawPatch(x2, y, (patch_t *)W_CacheLumpName((count & 1) ? "M_SLDMD1" : "M_SLDMD2", PU_CACHE));
+	}
+	V_DrawPatch(x2, y, (patch_t *)W_CacheLumpName("M_SLDRT", PU_CACHE));
+	V_DrawPatch(x + 4 + slot*8, y + 7, (patch_t *)W_CacheLumpName("M_SLDKB", PU_CACHE));
+#endif
+}
+
--- /dev/null
+++ b/mus.c
@@ -1,0 +1,458 @@
+/*
+	MUS2MIDI: MUS to MIDI Library
+
+	Copyright (C) 2014  Bret Curtis
+
+	This library is free software; you can redistribute it and/or
+	modify it under the terms of the GNU Library General Public
+	License as published by the Free Software Foundation; either
+	version 2 of the License, or (at your option) any later version.
+
+	This library is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+	Library General Public License for more details.
+
+	You should have received a copy of the GNU Library General Public
+	License along with this library; if not, write to the
+	Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+	Boston, MA  02110-1301, USA.
+*/
+
+#include <stddef.h>
+#include <stdio.h> /* fprintf */
+#include <stdlib.h>
+#include <string.h>
+#include "mus.h"
+
+#define FREQUENCY					140 /* default Hz or BPM */
+
+#if 0 /* older units: */
+#define TEMPO						0x001aa309 /* MPQN: 60000000 / 34.37Hz = 1745673 */
+#define DIVISION					0x0059 /* 89 -- used by many mus2midi converters */
+#endif
+
+#define TEMPO						0x00068A1B /* MPQN: 60000000 / 140BPM (140Hz) = 428571 */
+						/*	0x000D1436 -> MPQN: 60000000 /  70BPM  (70Hz) = 857142 */
+
+#define DIVISION					0x0101 /* 257 for 140Hz files with a 140MPQN */
+						/*	0x0088 -> 136 for  70Hz files with a 140MPQN */
+						/*	0x010B -> 267 for  70hz files with a 70MPQN  */
+						/*	0x01F9 -> 505 for 140hz files with a 70MPQN  */
+
+/* New
+ * QLS: MPQN/1000000 = 0.428571
+ * TDPS: QLS/PPQN = 0.428571/136 = 0.003151257
+ * PPQN: 136
+ *
+ * QLS: MPQN/1000000 = 0.428571
+ * TDPS: QLS/PPQN = 0.428571/257 = 0.001667591
+ * PPQN: 257
+ *
+ * QLS: MPQN/1000000 = 0.857142
+ * TDPS: QLS/PPQN = 0.857142/267 = 0.00321027
+ * PPQN: 267
+ *
+ * QLS: MPQN/1000000 = 0.857142
+ * TDPS: QLS/PPQN = 0.857142/505 = 0.001697311
+ * PPQN: 505
+ *
+ * Old
+ * QLS: MPQN/1000000 = 1.745673
+ * TDPS: QLS/PPQN = 1.745673 / 89 = 0.019614303 (seconds per tick)
+ * PPQN: (TDPS = QLS/PPQN) (0.019614303 = 1.745673/PPQN) (0.019614303*PPQN = 1.745673) (PPQN = 89.000001682)
+ */
+
+#define MUSEVENT_KEYOFF				0
+#define MUSEVENT_KEYON				1
+#define MUSEVENT_PITCHWHEEL			2
+#define MUSEVENT_CHANNELMODE		3
+#define MUSEVENT_CONTROLLERCHANGE	4
+#define MUSEVENT_END				6
+
+#define MIDI_MAXCHANNELS			16
+
+static const char MUS_ID[] = { 'M', 'U', 'S', 0x1A };
+
+static const uint8_t midimap[] =
+{/*	MIDI	Number	Description */
+	0,	/* 0	program change */
+	0,	/* 1	bank selection */
+	0x01,	/* 2	Modulation pot (frequency vibrato depth) */
+	0x07,	/* 3	Volume: 0-silent, ~100-normal, 127-loud */
+	0x0A,	/* 4	Pan (balance) pot: 0-left, 64-center (default), 127-right */
+	0x0B,	/* 5	Expression pot */
+	0x5B,	/* 6	Reverb depth */
+	0x5D,	/* 7	Chorus depth */
+	0x40,	/* 8	Sustain pedal */
+	0x43,	/* 9	Soft pedal */
+	0x78,	/* 10	All sounds off */
+	0x7B,	/* 11	All notes off */
+	0x7E,	/* 12	Mono (use numchannels + 1) */
+	0x7F,	/* 13	Poly */
+	0x79,	/* 14	reset all controllers */
+};
+
+typedef struct MUSHeader {
+	char ID[4];		/* identifier: "MUS" 0x1A */
+	uint16_t scoreLen;
+	uint16_t scoreStart;
+	uint16_t channels;	/* count of primary channels */
+	uint16_t sec_channels;	/* count of secondary channels */
+	uint16_t instrCnt;
+} MUSHeader ;
+#define MUS_HEADERSIZE 14
+
+typedef struct MidiHeaderChunk {
+	char name[4];
+	int32_t length;
+	int16_t format; /* make 0 */
+	int16_t ntracks;/* make 1 */
+	int16_t division; /* 0xe250 ?? */
+} MidiHeaderChunk;
+#define MIDI_HEADERSIZE 14
+
+typedef struct MidiTrackChunk {
+	char name[4];
+	int32_t	length;
+} MidiTrackChunk;
+#define TRK_CHUNKSIZE 8
+
+struct mus_ctx {
+	const uint8_t *src, *src_ptr;
+	uint32_t srcsize;
+	uint32_t datastart;
+	uint8_t *dst, *dst_ptr;
+	uint32_t dstsize, dstrem;
+};
+
+#define DST_CHUNK 8192
+static void resize_dst(struct mus_ctx *ctx) {
+	uint32_t pos = ctx->dst_ptr - ctx->dst;
+	ctx->dst = realloc(ctx->dst, ctx->dstsize + DST_CHUNK);
+	ctx->dstsize += DST_CHUNK;
+	ctx->dstrem += DST_CHUNK;
+	ctx->dst_ptr = ctx->dst + pos;
+}
+
+static void write1(struct mus_ctx *ctx, uint32_t val)
+{
+	if (ctx->dstrem < 1)
+		resize_dst(ctx);
+	*ctx->dst_ptr++ = val & 0xff;
+	ctx->dstrem--;
+}
+
+static void write2(struct mus_ctx *ctx, uint32_t val)
+{
+	if (ctx->dstrem < 2)
+		resize_dst(ctx);
+	*ctx->dst_ptr++ = (val>>8) & 0xff;
+	*ctx->dst_ptr++ = val & 0xff;
+	ctx->dstrem -= 2;
+}
+
+static void write4(struct mus_ctx *ctx, uint32_t val)
+{
+	if (ctx->dstrem < 4)
+		resize_dst(ctx);
+	*ctx->dst_ptr++ = (val>>24)&0xff;
+	*ctx->dst_ptr++ = (val>>16)&0xff;
+	*ctx->dst_ptr++ = (val>>8) & 0xff;
+	*ctx->dst_ptr++ = val & 0xff;
+	ctx->dstrem -= 4;
+}
+
+static void seekdst(struct mus_ctx *ctx, uint32_t pos) {
+	ctx->dst_ptr = ctx->dst + pos;
+	while (ctx->dstsize < pos)
+		resize_dst(ctx);
+	ctx->dstrem = ctx->dstsize - pos;
+}
+
+static void skipdst(struct mus_ctx *ctx, int32_t pos) {
+	size_t newpos;
+	ctx->dst_ptr += pos;
+	newpos = ctx->dst_ptr - ctx->dst;
+	while (ctx->dstsize < newpos)
+		resize_dst(ctx);
+	ctx->dstrem = ctx->dstsize - newpos;
+}
+
+static uint32_t getdstpos(struct mus_ctx *ctx) {
+	return (ctx->dst_ptr - ctx->dst);
+}
+
+/* writes a variable length integer to a buffer, and returns bytes written */
+static int32_t writevarlen(int32_t value, uint8_t *out)
+{
+	int32_t buffer, count = 0;
+
+	buffer = value & 0x7f;
+	while ((value >>= 7) > 0) {
+		buffer <<= 8;
+		buffer += 0x80;
+		buffer += (value & 0x7f);
+	}
+
+	while (1) {
+		++count;
+		*out = (uint8_t)buffer;
+		++out;
+		if (buffer & 0x80)
+			buffer >>= 8;
+		else
+			break;
+	}
+	return (count);
+}
+
+#define READ_INT16(b) ((b)[0] | ((b)[1] << 8))
+#define READ_INT32(b) ((b)[0] | ((b)[1] << 8) | ((b)[2] << 16) | ((b)[3] << 24))
+
+int mus2midi(const uint8_t *in, uint32_t insize,
+		 uint8_t **out, uint32_t *outsize,
+		 uint16_t frequency) {
+	struct mus_ctx ctx;
+	MUSHeader header;
+	const uint8_t *cur, *end;
+	uint32_t track_size_pos, begin_track_pos, current_pos;
+	int32_t delta_time; /* Delta time for midi event */
+	int temp, ret = -1;
+	int channel_volume[MIDI_MAXCHANNELS];
+	int channelMap[MIDI_MAXCHANNELS], currentChannel;
+
+	if (insize < MUS_HEADERSIZE) {
+		fprintf(stderr, "mus2midi: file too short\n");
+		return (-1);
+	}
+
+	if (!frequency)
+		frequency = FREQUENCY;
+
+	/* read the MUS header and set our location */
+	memcpy(header.ID, in, 4);
+	header.scoreLen = READ_INT16(&in[4]);
+	header.scoreStart = READ_INT16(&in[6]);
+	header.channels = READ_INT16(&in[8]);
+	header.sec_channels = READ_INT16(&in[10]);
+	header.instrCnt = READ_INT16(&in[12]);
+
+	if (memcmp(header.ID, MUS_ID, 4)) {
+		fprintf(stderr, "mus2midi: not a MUS file\n");
+		return (-1);
+	}
+	if (insize < (uint32_t)header.scoreLen + (uint32_t)header.scoreStart) {
+		fprintf(stderr, "mus2midi: file too short\n");
+		return (-1);
+	}
+	/* channel #15 should be excluded in the numchannels field: */
+	if (header.channels > MIDI_MAXCHANNELS - 1) {
+		fprintf(stderr, "mus2midi: bad MUS file\n");
+		return (-1);
+	}
+
+	memset(&ctx, 0, sizeof(struct mus_ctx));
+	ctx.src = ctx.src_ptr = in;
+	ctx.srcsize = insize;
+
+	ctx.dst = calloc(DST_CHUNK, sizeof(uint8_t));
+	ctx.dst_ptr = ctx.dst;
+	ctx.dstsize = DST_CHUNK;
+	ctx.dstrem = DST_CHUNK;
+
+	/* Map channel 15 to 9 (percussions) */
+	for (temp = 0; temp < MIDI_MAXCHANNELS; ++temp) {
+		channelMap[temp] = -1;
+		channel_volume[temp] = 0x40;
+	}
+	channelMap[15] = 9;
+
+	/* Header is 14 bytes long and add the rest as well */
+	write1(&ctx, 'M');
+	write1(&ctx, 'T');
+	write1(&ctx, 'h');
+	write1(&ctx, 'd');
+	write4(&ctx, 6);	/* length of header */
+	write2(&ctx, 0);	/* MIDI type (always 0) */
+	write2(&ctx, 1);	/* MUS files only have 1 track */
+	write2(&ctx, DIVISION);	/* division */
+
+	/* Write out track header and track length position for later */
+	begin_track_pos = getdstpos(&ctx);
+	write1(&ctx, 'M');
+	write1(&ctx, 'T');
+	write1(&ctx, 'r');
+	write1(&ctx, 'k');
+	track_size_pos = getdstpos(&ctx);
+	skipdst(&ctx, 4);
+
+	/* write tempo: microseconds per quarter note */
+	write1(&ctx, 0x00);	/* delta time */
+	write1(&ctx, 0xff);	/* sys command */
+	write2(&ctx, 0x5103);	/* command - set tempo */
+	write1(&ctx, TEMPO & 0x000000ff);
+	write1(&ctx, (TEMPO & 0x0000ff00) >> 8);
+	write1(&ctx, (TEMPO & 0x00ff0000) >> 16);
+
+	/* Percussions channel starts out at full volume */
+	write1(&ctx, 0x00);
+	write1(&ctx, 0xB9);
+	write1(&ctx, 0x07);
+	write1(&ctx, 127);
+
+	/* get current position in source, and end of position */
+	cur = in + header.scoreStart;
+	end = cur + header.scoreLen;
+
+	currentChannel = 0;
+	delta_time = 0;
+
+	/* main loop */
+	while(cur < end){
+		/*fprintf(stderr, "LOOP DEBUG: %d\r\n",iterator++);*/
+		uint8_t channel;
+		uint8_t event;
+		uint8_t temp_buffer[32];	/* temp buffer for current iterator */
+		uint8_t *out_local = temp_buffer;
+		uint8_t status, bit1, bit2, bitc = 2;
+
+		/* read in current bit */
+		event = *cur++;
+		channel = (event & 15);		/* current channel */
+
+		/* write variable length delta time */
+		out_local += writevarlen(delta_time, out_local);
+
+		/* set all channels to 127 (max) volume */
+		if (channelMap[channel] < 0) {
+			*out_local++ = 0xB0 + currentChannel;
+			*out_local++ = 0x07;
+			*out_local++ = 127;
+			*out_local++ = 0x00;
+			channelMap[channel] = currentChannel++;
+			if (currentChannel == 9)
+				++currentChannel;
+		}
+		status = channelMap[channel];
+
+		/* handle events */
+		switch ((event & 122) >> 4){
+			case MUSEVENT_KEYOFF:
+				status |=  0x80;
+				bit1 = *cur++;
+				bit2 = 0x40;
+				break;
+			case MUSEVENT_KEYON:
+				status |= 0x90;
+				bit1 = *cur & 127;
+				if (*cur++ & 128) {	/* volume bit? */
+					channel_volume[channelMap[channel]] = *cur++;
+					/* The maximum volume is 127, but it is encoded as
+					   a byte. Some songs erroneously use values higher
+					   than 127, so we have to clamp them down.
+					   https://github.com/Mindwerks/wildmidi/pull/226 */
+					if (channel_volume[channelMap[channel]] > 127) {
+					    channel_volume[channelMap[channel]] = 127;
+					}
+				}
+				bit2 = channel_volume[channelMap[channel]];
+				break;
+			case MUSEVENT_PITCHWHEEL:
+				status |= 0xE0;
+				bit1 = (*cur & 1) >> 6;
+				bit2 = (*cur++ >> 1) & 127;
+				break;
+			case MUSEVENT_CHANNELMODE:
+				status |= 0xB0;
+				if (*cur >= sizeof(midimap) / sizeof(midimap[0])) {
+					fprintf(stderr, "mus2midi: can't map %u to midi\n", *cur);
+					goto _end;
+				}
+				bit1 = midimap[*cur++];
+				bit2 = (*cur++ == 12) ? header.channels + 1 : 0x00;
+				break;
+			case MUSEVENT_CONTROLLERCHANGE:
+				if (*cur == 0) {
+					cur++;
+					status |= 0xC0;
+					bit1 = *cur++;
+					bit2 = 0; /* silence bogus warnings */
+					bitc = 1;
+				} else {
+					status |= 0xB0;
+					if (*cur >= sizeof(midimap) / sizeof(midimap[0])) {
+						fprintf(stderr, "mus2midi: can't map %u to midi\n", *cur);
+						goto _end;
+					}
+					bit1 = midimap[*cur++];
+					bit2 = *cur++;
+					/* The maximum volume is 127, but it is encoded as
+					   a byte. Some songs erroneously use values higher
+					   than 127, so we have to clamp them down.
+					   https://github.com/Mindwerks/wildmidi/pull/226 */
+					if (bit1 == 0x07 && bit2 > 127) bit2 = 127;
+				}
+				break;
+			case MUSEVENT_END:	/* End */
+				status = 0xff;
+				bit1 = 0x2f;
+				bit2 = 0x00;
+				if (cur != end) { /* should we error here or report-only? */
+					fprintf(stderr, "mus2midi: MUS buffer off by %ld bytes\n",
+										(long)(cur - end));
+				}
+				break;
+			case 5:/* Unknown */
+			case 7:/* Unknown */
+			default:/* shouldn't happen */
+				fprintf(stderr, "mus2midi: unrecognized event (%u)\n", event);
+				goto _end;
+		}
+
+		/* write it out */
+		*out_local++ = status;
+		*out_local++ = bit1;
+		if (bitc == 2)
+			*out_local++ = bit2;
+
+		/* write out our temp buffer */
+		if (out_local != temp_buffer)
+		{
+			if (ctx.dstrem < sizeof(temp_buffer))
+				resize_dst(&ctx);
+
+			memcpy(ctx.dst_ptr, temp_buffer, out_local - temp_buffer);
+			ctx.dst_ptr += out_local - temp_buffer;
+			ctx.dstrem -= out_local - temp_buffer;
+		}
+
+		if (event & 128) {
+			delta_time = 0;
+			do {
+				delta_time = (delta_time * 128 + (*cur & 127)) * (140.0 / frequency);
+			} while ((*cur++ & 128));
+		} else {
+			delta_time = 0;
+		}
+	}
+
+	/* write out track length */
+	current_pos = getdstpos(&ctx);
+	seekdst(&ctx, track_size_pos);
+	write4(&ctx, current_pos - begin_track_pos - TRK_CHUNKSIZE);
+	seekdst(&ctx, current_pos);	/* reseek to end position */
+
+	*out = ctx.dst;
+	*outsize = ctx.dstsize - ctx.dstrem;
+	ret = 0;
+
+_end:	/* cleanup */
+	if (ret < 0) {
+		free(ctx.dst);
+		*out = NULL;
+		*outsize = 0;
+	}
+
+	return (ret);
+}
--- /dev/null
+++ b/mus.h
@@ -1,0 +1,31 @@
+/*
+	MUS2MIDI: DMX (DOOM) MUS to MIDI Library Header
+
+	Copyright (C) 2014  Bret Curtis
+
+	This library is free software; you can redistribute it and/or
+	modify it under the terms of the GNU Library General Public
+	License as published by the Free Software Foundation; either
+	version 2 of the License, or (at your option) any later version.
+
+	This library is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+	Library General Public License for more details.
+
+	You should have received a copy of the GNU Library General Public
+	License along with this library; if not, write to the
+	Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+	Boston, MA  02110-1301, USA.
+*/
+
+#ifndef MUSLIB_H
+#define MUSLIB_H
+
+#include <stdint.h>
+
+int mus2midi(const uint8_t *in, uint32_t insize,
+		 uint8_t **out, uint32_t *outsize,
+		 uint16_t frequency);
+
+#endif /* MUSLIB_H */
--- /dev/null
+++ b/ogl_def.h
@@ -1,0 +1,199 @@
+//**************************************************************************
+//**
+//** ogl_def.h
+//**
+//**************************************************************************
+
+#ifndef __H2OPENGL__
+#define __H2OPENGL__
+
+#include "r_local.h"
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <GL/gl.h>
+
+/* whether to printf devel debug messages */
+#define OPENGL_DEBUGGING		0
+
+#if (OPENGL_DEBUGGING)
+#define OGL_DEBUG			printf
+#else	/* no debug msg : */
+#if defined (__GNUC__) && !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
+#define OGL_DEBUG(fmt, args...)		do {} while (0)
+#else
+#define OGL_DEBUG(fmt, ...)		do {} while (0)
+#endif
+#endif
+
+
+enum { VX, VY };	/* Vertex indices. */
+
+typedef struct	/* For dynamic lighting. */
+{
+	int		use;
+	mobj_t		*thing;
+	float		top, height;
+} lumobj_t;
+
+/* ScreenBits is currently unused. */
+extern int screenWidth, screenHeight, screenBits;
+
+void I_InitGraphics(void);
+void I_ShutdownGraphics(void);
+
+void OGL_InitRenderer(void);
+void OGL_InitData(void);
+void OGL_ResetData(void);
+
+void OGL_SwitchTo3DState(void);
+void OGL_Restore2DState(int step);	/* Step 1: matrices, 2: attributes. */
+void OGL_UseWhiteFog(int yes);
+
+float PointDist2D(float c[2]);
+
+void R_RenderSprite(vissprite_t *spr);
+
+/* 2D drawing routines. */
+void OGL_DrawPatch_CS(int x, int y, int lumpnum);
+void OGL_DrawPatch(int x, int y, int lumpnum);
+void OGL_DrawFuzzPatch(int x, int y, int lumpnum);
+void OGL_DrawAltFuzzPatch(int x, int y, int lumpnum);
+void OGL_DrawShadowedPatch(int x, int y, int lumpnum);
+void OGL_DrawRawScreen(int lump);	/* Raw screens are 320 x 200. */
+void OGL_DrawRawScreenOfs(int lump, float offx, float offy);
+void OGL_DrawLine(float x1, float y1, float x2, float y2, float r, float g, float b, float a);
+void OGL_DrawRect(float x, float y, float w, float h, float r, float g, float b, float a);
+void OGL_DrawRectTiled(int x, int y, int w, int h, int tw, int th);
+void OGL_DrawCutRectTiled(int x, int y, int w, int h, int tw, int th, int cx, int cy, int cw, int ch);
+void OGL_SetColor(int palidx);
+void OGL_SetColorAndAlpha(float r, float g, float b, float a);
+void OGL_DrawPSprite(int x, int y, float scale, int flip, int lump);
+
+/* Filters. */
+void OGL_SetFilter(int filter);
+int OGL_DrawFilter(void);
+
+void OGL_ShadeRect(int x, int y, int w, int h, float darkening);
+
+/* ogl_tex.c */
+typedef struct
+{
+	unsigned short	w, h;
+	short		offx, offy;
+	unsigned short	w2;		/* For split textures, width of the other part. */
+} texsize_t;
+
+extern texsize_t *lumptexsizes;		/* Sizes for all the lumps. */
+extern unsigned short *spriteheights;
+
+extern float		texw, texh;
+extern int		texmask;
+extern unsigned int	curtex;
+extern int		pallump;
+
+int FindNextPower2(int num);
+float NextPower2Ratio(int num);
+void OGL_TexInit(void);
+void OGL_TexReset(void);
+void OGL_ResetLumpTexData(void);
+void OGL_SetPaletteLump(const char *palname);
+void PalToRGB(byte *palidx, byte *rgb);
+void PalIdxToRGB(byte *pal, int idx, byte *rgb);
+unsigned int OGL_BindTexFlat(int lump);
+void OGL_SetFlat(int idx);
+void OGL_BindTexture(GLuint texname);
+
+/* Returns the OpenGL texture name. */
+GLuint OGL_PrepareTexture(int idx);
+GLuint OGL_PrepareFlat(int idx);	/* Returns the OpenGL name of the texture. */
+GLuint OGL_PrepareLightTexture(void);	/* The dynamic light map. */
+
+void OGL_SetTexture(int idx);
+unsigned int OGL_PrepareSky(int idx, boolean zeroMask);
+
+void OGL_SetSprite(int pnum);
+unsigned int OGL_PrepareSprite(int pnum);
+void OGL_NewSplitTex(int lump, GLuint part2name);
+GLuint OGL_GetOtherPart(int lump);
+
+/* Part is either 1 or 2. Part 0 means only the left side is loaded.
+ * No splittex is created in that case. Once a raw image is loaded
+ * as part 0 it must be deleted before the other part is loaded at the
+ * next loading.
+ */
+void OGL_SetRawImage(int lump, int part);
+void OGL_SetPatch(int lump);	/* No mipmaps are generated. */
+void OGL_SetNoTexture(void);
+
+int OGL_GetLumpTexWidth(int lump);
+int OGL_GetLumpTexHeight(int lump);
+int OGL_ValidTexHeight2(int width, int height);
+
+void OGL_UpdateTexParams(int mipmode);
+void OGL_UpdateRawScreenParams(int smoothing);
+
+
+/* ogl_scr.c */
+typedef struct _TargaHeader
+{
+	unsigned char	id_length, colormap_type, image_type;
+	unsigned short	colormap_index, colormap_length;
+	unsigned char	colormap_size;
+	unsigned short	x_origin, y_origin, width, height;
+	unsigned char	pixel_size, attributes;
+} TargaHeader;
+
+void OGL_GrabScreen(void);
+
+
+#include "m_bams.h"
+
+/* ogl_clip.c */
+typedef struct clipnode_s
+{
+	int			used;		/* 1 if the node is in use. */
+	struct clipnode_s	*prev, *next;	/* Previous and and nodes.  */
+	binangle		start, end;	/* The start and end angles (start < end). */
+} clipnode_t;
+
+extern clipnode_t *clipnodes;	/* The list of clipnodes. */
+extern clipnode_t *cliphead;	/* The head node. */
+
+void C_Init(void);
+void C_Ranger(void);
+void C_ClearRanges(void);
+void C_SafeAddRange(binangle startAngle, binangle endAngle);
+
+/* Add a segment relative to the current viewpoint. */
+void C_AddViewRelSeg(float x1, float y1, float x2, float y2);
+
+/* Check a segment relative to the current viewpoint. */
+int C_CheckViewRelSeg(float x1, float y1, float x2, float y2);
+
+/* Returns 1 if the specified angle is visible. */
+int C_IsAngleVisible(binangle bang);
+
+clipnode_t *C_AngleClippedBy(binangle bang);
+
+/* Returns 1 if the subsector might be visible. */
+int C_CheckSubsector(subsector_t *ssec);
+
+
+/* ogl_sky.c */
+
+/* Sky hemispheres. */
+#define SKYHEMI_UPPER		0x1
+#define SKYHEMI_LOWER		0x2
+#define SKYHEMI_JUST_CAP	0x4	/* Just draw the top or bottom cap. */
+
+typedef struct
+{
+	float	rgb[3];		/* The RGB values. */
+	short	set, use;	/* Is this set? Should be used? */
+} fadeout_t;
+
+void R_RenderSkyHemispheres(int hemis);
+
+#endif	/* __H2OPENGL__ */
+
--- /dev/null
+++ b/ogl_rl.h
@@ -1,0 +1,60 @@
+//**************************************************************************
+//**
+//** ogl_rl.h
+//**
+//**************************************************************************
+
+#ifndef __OGL_REND_LIST_H__
+#define __OGL_REND_LIST_H__
+
+/* Rendquad flags. */
+#define RQF_FLAT		0x1	/* This is a flat triangle. */
+#define RQF_MASKED		0x2	/* Use the special list for masked textures. */
+#define RQF_MISSING_WALL	0x4	/* Originally this surface had no texture. */
+#define RQF_SKY_MASK		0x8	/* A sky mask triangle. */
+#define RQF_SKY_MASK_WALL	0x10	/* A sky mask wall (with skyfix). */
+#define RQF_LIGHT		0x20	/* A dynamic light. */
+#define RQF_FLOOR_FACING	0x40	/* Used for flats, the quad faces upwards. */
+
+typedef struct
+{
+	float	v1[2], v2[2];		/* Two vertices. */
+	float	top;			/* Top height. */
+	union _quadTri {
+		struct _quad {
+			float bottom;		/* Bottom height. */
+			float len;		/* Length of the quad. */
+		} q;
+		float v3[2];		/* Third vertex for flats. */
+	} u;
+	float	light;			/* Light level, as in 0 = black, 1 = fullbright. */
+	float	texoffx;		/* Texture coordinates for left/top (in real texcoords). */
+	float	texoffy;
+	short	flags;			/* RQF_*. */
+	GLuint	masktex;		/* Texture name for masked textures. */
+	unsigned short texw, texh;	/* Size of the texture. */
+	float	dist[3];		/* Distances to the vertices. */
+} rendquad_t;	/* Or flat triangle. */
+
+typedef struct
+{
+	GLuint	tex;			/* The name of the texture for this list. */
+	int		numquads;	/* Number of quads in the list. */
+	int		listsize;	/* Absolute size of the list. */
+	rendquad_t *quads;		/* The list of quads. */
+} rendlist_t;
+
+
+/* ogl_rl.c */
+
+void RL_Init(void);
+void RL_ClearLists(void);
+void RL_DeleteLists(void);
+void RL_AddQuad(rendquad_t *quad, GLuint quadtex);
+void RL_AddFlatQuads(rendquad_t *base, /* GLuint */ uintptr_t quadtex,
+				int numvrts, fvertex_t *vrts, int dir);
+void RL_RenderAllLists(void);
+void SetVertexColor(float light, float dist, float alpha);
+
+#endif	/* __OGL_REND_LIST_H__ */
+
--- /dev/null
+++ b/oss.h
@@ -1,0 +1,38 @@
+/*  XMMS - Cross-platform multimedia player
+ *  Copyright (C) 1998-1999  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _SNDOSS_H
+#define _SNDOSS_H
+
+
+#include "config.h"
+#include "audio_plugin.h"
+
+
+typedef struct
+{
+	int audio_device;
+	int mixer_device;
+	int buffer_size;
+	int prebuffer;
+	int fragment_count;
+}
+OSSConfig;
+
+#endif	/* _SNDOSS_H */
+
--- /dev/null
+++ b/p_ceilng.c
@@ -1,0 +1,260 @@
+// p_ceilng.c
+
+#include "h2stdinc.h"
+#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 *)(void *)&ceiling->sector->soundorg, sfx_dormov);
+		if (res == 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 *)(void *)&ceiling->sector->soundorg, sfx_dormov);
+		if (res == 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 == 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 = (ceiling_t *) Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, NULL);
+		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/p_doors.c
@@ -1,0 +1,364 @@
+// P_doors.c
+
+#include "h2stdinc.h"
+#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 vldoor_normal:
+				door->direction = -1; // time to go back down
+				S_StartSound((mobj_t *)(void *)&door->sector->soundorg, sfx_doropn);
+				break;
+			case close30ThenOpen:
+				door->direction = 1;
+				S_StartSound((mobj_t *)(void *)&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 = vldoor_normal;
+				S_StartSound((mobj_t *)(void *)&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 == res_pastdest)
+		{
+			switch (door->type)
+			{
+			case vldoor_normal:
+			case vldoor_close:
+				door->sector->specialdata = NULL;
+				P_RemoveThinker(&door->thinker);  // unlink and free
+				S_StartSound((mobj_t *)(void *)&door->sector->soundorg, sfx_dorcls);
+				break;
+			case close30ThenOpen:
+				door->direction = 0;
+				door->topcountdown = 35*30;
+				break;
+			default:
+				break;
+			}
+		}
+		else if (res == res_crushed)
+		{
+			switch (door->type)
+			{
+			case vldoor_close: // DON'T GO BACK UP!
+				break;
+			default:
+				door->direction = 1;
+				S_StartSound((mobj_t *)(void *)&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 == res_pastdest)
+		{
+			switch (door->type)
+			{
+			case vldoor_normal:
+				door->direction = 0; // wait at top
+				door->topcountdown = door->topwait;
+				break;
+			case close30ThenOpen:
+			case vldoor_open:
+				door->sector->specialdata = NULL;
+				P_RemoveThinker (&door->thinker); // unlink and free
+				S_StopSound((mobj_t *)(void *)&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 = (vldoor_t *) Z_Malloc(sizeof(*door), PU_LEVSPEC, NULL);
+		P_AddThinker(&door->thinker);
+		sec->specialdata = door;
+		door->thinker.function = T_VerticalDoor;
+		door->sector = sec;
+		switch (type)
+		{
+		case vldoor_close:
+			door->topheight = P_FindLowestCeilingSurrounding(sec);
+			door->topheight -= 4*FRACUNIT;
+			door->direction = -1;
+			S_StartSound((mobj_t *)(void *)&door->sector->soundorg, sfx_doropn);
+			break;
+		case close30ThenOpen:
+			door->topheight = sec->ceilingheight;
+			door->direction = -1;
+			S_StartSound((mobj_t *)(void *)&door->sector->soundorg, sfx_doropn);
+			break;
+		case vldoor_normal:
+		case vldoor_open:
+			door->direction = 1;
+			door->topheight = P_FindLowestCeilingSurrounding(sec);
+			door->topheight -= 4*FRACUNIT;
+			if (door->topheight != sec->ceilingheight)
+			{
+				S_StartSound((mobj_t *)(void *)&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;
+	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;
+	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 *)(void *)&sec->soundorg, sfx_doropn);
+		//S_StartSound((mobj_t *)(void *)&sec->soundorg, sfx_dormov);
+		break;
+	default: // LOCKED DOOR SOUND
+		S_StartSound((mobj_t *)(void *)&sec->soundorg, sfx_doropn);
+		//S_StartSound((mobj_t *)(void *)&sec->soundorg, sfx_dormov);
+		break;
+	}
+
+	//
+	// new door thinker
+	//
+	door = (vldoor_t *) Z_Malloc (sizeof(*door), PU_LEVSPEC, NULL);
+	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 = vldoor_normal;
+		break;
+	case 31:
+	case 32:
+	case 33:
+	case 34:
+		door->type = vldoor_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 = (vldoor_t *) Z_Malloc(sizeof(*door), PU_LEVSPEC, NULL);
+	P_AddThinker(&door->thinker);
+	sec->specialdata = door;
+	sec->special = 0;
+	door->thinker.function = T_VerticalDoor;
+	door->sector = sec;
+	door->direction = 0;
+	door->type = vldoor_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 = (vldoor_t *) Z_Malloc(sizeof(*door), PU_LEVSPEC, NULL);
+	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/p_enemy.c
@@ -1,0 +1,2657 @@
+// P_enemy.c
+
+#include "h2stdinc.h"
+#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 mobj_t	*soundtarget;
+
+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
+//
+//----------------------------------------------------------------------------
+
+static 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
+//
+//----------------------------------------------------------------------------
+
+static 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
+//
+//----------------------------------------------------------------------------
+
+static boolean P_CheckMissileRange(mobj_t *actor)
+{
+	fixed_t dist;
+
+	if (!P_CheckSight(actor, actor->target))
+	{
+		return false;
+	}
+	if (actor->flags & MF_JUSTHIT)
+	{ // The target just hit the enemy, so fight back!
+		actor->flags &= ~MF_JUSTHIT;
+		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
+================
+*/
+
+static fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
+static fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
+
+#define MAXSPECIALCROSS		8
+extern line_t	*spechit[MAXSPECIALCROSS];
+extern int			numspechit;
+
+static 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.
+//
+//----------------------------------------------------------------------------
+
+static boolean P_TryWalk(mobj_t *actor)
+{
+	if (!P_Move(actor))
+	{
+		return false;
+	}
+	actor->movecount = P_Random() & 15;
+	return true;
+}
+
+/*
+================
+=
+= P_NewChaseDir
+=
+================
+*/
+
+static dirtype_t opposite[] =
+{
+	DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST,
+	DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST,
+	DI_NODIR
+};
+
+static dirtype_t diags[] =
+{
+	DI_NORTHWEST,
+	DI_NORTHEAST,
+	DI_SOUTHWEST,
+	DI_SOUTHEAST
+};
+
+static 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; (int)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
+
+static 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
+================
+*/
+
+static boolean P_LookForPlayers(mobj_t *actor, boolean allaround)
+{
+	int c;
+	int stop;
+	player_t *player;
+	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));
+	}
+	c = 0;
+	stop = (actor->lastlook - 1) & (MAXPLAYERS - 1);
+	for ( ; ; actor->lastlook = (actor->lastlook + 1) & (MAXPLAYERS - 1))
+	{
+		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 = (intptr_t)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 = (intptr_t)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)
+{
+	unsigned 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;
+
+	if (actor->special1 == MAX_GEN_PODS)
+	{ // Too many generated pods
+		return;
+	}
+	x = actor->x;
+	y = actor->y;
+	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 = (intptr_t)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 = 0;
+
+	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 = 0;
+
+	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);
+		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)
+{
+	unsigned 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
+static 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/p_floor.c
@@ -1,0 +1,450 @@
+// p_floor.c
+
+#include "h2stdinc.h"
+#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 res_crushed;
+				}
+				return res_pastdest;
+			}
+			else
+			{
+				lastpos = sector->floorheight;
+				sector->floorheight -= speed;
+				flag = P_ChangeSector(sector, crush);
+				if (flag == true)
+				{
+					sector->floorheight = lastpos;
+					P_ChangeSector(sector, crush);
+					return res_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 res_crushed;
+				}
+				return res_pastdest;
+			}
+			else	// COULD GET CRUSHED
+			{
+				lastpos = sector->floorheight;
+				sector->floorheight += speed;
+				flag = P_ChangeSector(sector, crush);
+				if (flag == true)
+				{
+					if (crush == true)
+						return res_crushed;
+					sector->floorheight = lastpos;
+					P_ChangeSector(sector, crush);
+					return res_crushed;
+				}
+			}
+			break;
+		}
+		break;	/* END OF THE FLOOR CASE */
+
+	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 res_crushed;
+				}
+				return res_pastdest;
+			}
+			else	// COULD GET CRUSHED
+			{
+				lastpos = sector->ceilingheight;
+				sector->ceilingheight -= speed;
+				flag = P_ChangeSector(sector, crush);
+				if (flag == true)
+				{
+					if (crush == true)
+						return res_crushed;
+					sector->ceilingheight = lastpos;
+					P_ChangeSector(sector, crush);
+					return res_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 res_crushed;
+				}
+				return res_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 res_crushed;
+				}
+				#endif
+			}
+			break;
+		}
+		break;	/* END OF THE CEILING CASE */
+	}
+
+	return res_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 *)(void *)&floor->sector->soundorg, sfx_dormov);
+	}
+
+	if (res == res_pastdest)
+	{
+		floor->sector->specialdata = NULL;
+		if (floor->type == raiseBuildStep)
+		{
+			S_StartSound((mobj_t *)(void *)&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 = (floormove_t *) Z_Malloc (sizeof(*floor), PU_LEVSPEC, NULL);
+		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 = H2MAXINT;
+				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 = (floormove_t *) Z_Malloc (sizeof(*floor), PU_LEVSPEC, NULL);
+		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 = (floormove_t *) Z_Malloc (sizeof(*floor), PU_LEVSPEC, NULL);
+				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/p_inter.c
@@ -1,0 +1,1465 @@
+
+// P_inter.c
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+
+extern int messageson;
+extern int playerkeys;
+extern vertex_t KeyPoints[];
+
+boolean ultimatemsg;
+
+#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 weapontype_t 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 weapontype_t 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
+//
+//--------------------------------------------------------------------------
+
+void P_SetMessage(player_t *player, const char *message, boolean ultmsg)
+{
+	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.
+//
+//--------------------------------------------------------------------------
+
+static 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
+//
+//---------------------------------------------------------------------------
+
+static void P_GiveKey(player_t *player, keytype_t key)
+{
+	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;
+}
+
+//==========================================================================
+//
+// P_SetDormantArtifact
+//
+// Removes the MF_SPECIAL flag and initiates the artifact pickup
+// animation.
+//
+//==========================================================================
+
+static 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
+//
+//----------------------------------------------------------------------------
+
+static 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
+//
+//---------------------------------------------------------------------------
+
+static 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
+//
+//---------------------------------------------------------------------------
+
+static 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
+//
+//---------------------------------------------------------------------------
+
+static 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
+//
+//---------------------------------------------------------------------------
+
+static 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
+//
+//---------------------------------------------------------------------------
+
+static 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
+//
+//---------------------------------------------------------------------------
+
+static void P_AutoUseHealth(player_t *player, int saveHealth)
+{
+	int i;
+	int count;
+	int normalCount;
+	int normalSlot = 0;
+	int superCount;
+	int superSlot = 0;
+
+	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 int ang;
+	int saved;
+	player_t *player;
+	fixed_t thrust;
+
+	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...
+		}
+		if (player == &players[consoleplayer])
+		{
+#if defined(__WATCOMC__) && defined(_DOS)
+			int temp = damage < 100 ? damage : 100;
+			I_Tactile(40, 10, 40 + temp*2);
+#endif	/* externdriver, DOS */
+			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/p_lights.c
@@ -1,0 +1,270 @@
+// p_lights.c
+
+#include "h2stdinc.h"
+#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 = (lightflash_t *) Z_Malloc (sizeof(*flash), PU_LEVSPEC, NULL);
+	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 = (strobe_t *) Z_Malloc (sizeof(*flash), PU_LEVSPEC, NULL);
+	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 = (glow_t *) Z_Malloc(sizeof(*g), PU_LEVSPEC, NULL);
+	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/p_local.h
@@ -1,0 +1,280 @@
+// P_local.h
+
+#ifndef __P_LOCAL__
+#define __P_LOCAL__
+
+#ifndef __R_LOCAL__
+#include "r_local.h"
+#endif
+
+#pragma pack on
+
+#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	H2MININT
+#define ONCEILINGZ	H2MAXINT
+#define FLOATRANDZ	(H2MAXINT - 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;
+
+#ifdef RENDER3D
+typedef struct
+{
+	float	x, y, dx, dy;
+} fdivline_t;
+#endif
+
+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 *ld);
+
+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;
+extern	mobj_t		*linetarget;		/* who got hit (or NULL) */
+
+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);
+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, const 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);
+
+#pragma pack off
+
+#include "p_spec.h"
+
+#endif	/* __P_LOCAL__ */
+
--- /dev/null
+++ b/p_map.c
@@ -1,0 +1,1685 @@
+// P_map.c
+
+#include "h2stdinc.h"
+#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.
+
+===============================================================================
+*/
+
+extern fixed_t		topslope, bottomslope;	/* slopes to top and bottom of target */
+
+static fixed_t		tmbbox[4];
+static mobj_t		*tmthing;
+static int		tmflags;
+static fixed_t		tmx, tmy;
+
+static mobj_t		*onmobj;	/* used for landing on pods/players */
+
+boolean			floatok;	/* if true, move would be ok if */
+					/* within tmfloorz - tmceilingz */
+
+fixed_t			tmfloorz, tmceilingz, tmdropoffz;
+
+line_t			*ceilingline;	/* keep track of the line that lowers the ceiling, */
+					/* so missiles don't explode against sky hack walls */
+
+#define MAXSPECIALCROSS		8
+line_t		*spechit[MAXSPECIALCROSS];	/* keep track of special lines as they are hit, */
+int			numspechit;	/* but don't process them until the move is proven valid */
+
+
+/*
+===============================================================================
+
+					TELEPORT MOVE
+
+===============================================================================
+*/
+
+/*
+==================
+=
+= PIT_StompThing
+=
+==================
+*/
+
+static 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
+==================
+*/
+
+static boolean PIT_CheckLine(line_t *ld)
+{
+	if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
+		|| tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
+		|| tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
+		|| tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
+	{
+		return true;
+	}
+	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
+//
+//---------------------------------------------------------------------------
+
+static 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
+//
+//---------------------------------------------------------------------------
+
+static 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
+//
+//===========================================================================
+
+static 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
+==================
+*/
+
+static 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
+
+==============================================================================
+*/
+
+static fixed_t		bestslidefrac, secondslidefrac;
+static line_t		*bestslideline, *secondslideline;
+static mobj_t		*slidemo;
+
+static fixed_t		tmxmove, tmymove;
+
+/*
+==================
+=
+= P_HitSlideLine
+=
+= Adjusts the xmove / ymove so that the next move will slide along the wall
+==================
+*/
+
+static 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
+=
+==============
+*/
+
+static 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 must 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) */
+
+fixed_t			attackrange;
+static fixed_t		aimslope;
+static int		la_damage;
+
+static mobj_t		*shootthing;
+static fixed_t		shootz;			/* height if not aiming up or down */
+							/* ???: use slope for monsters? */
+
+
+/*
+===============================================================================
+=
+= PTR_AimTraverse
+=
+= Sets linetaget and aimslope when a target is aimed at
+===============================================================================
+*/
+
+static 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))
+	{ // corpse or something
+		return true;
+	}
+	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
+=
+==============================================================================
+*/
+
+static 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
+
+==============================================================================
+*/
+
+static mobj_t		*usething;
+
+static 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
+
+==============================================================================
+*/
+
+static mobj_t		*bombsource;
+static mobj_t		*bombspot;
+static int		bombdamage;
+
+/*
+=================
+=
+= PIT_RadiusAttack
+=
+= Source is the creature that casued the explosion at spot
+=================
+*/
+
+static 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 caused 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
+==============================================================================
+*/
+
+static boolean		crushchange;
+static boolean		nofit;
+
+/*
+===============
+=
+= PIT_ChangeSector
+=
+===============
+*/
+
+static 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/p_maputl.c
@@ -1,0 +1,756 @@
+// P_maputl.c
+
+#include "h2stdinc.h"
+#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 = 0, p2 = 0;
+
+	switch (ld->slopetype)
+	{
+	case ST_HORIZONTAL:
+		p1 = tmbox[BOXTOP] > ld->v1->y;
+		p2 = tmbox[BOXBOTTOM] > ld->v1->y;
+		if (ld->dx < 0)
+		{
+			p1 ^= 1;
+			p2 ^= 1;
+		}
+		break;
+	case ST_VERTICAL:
+		p1 = tmbox[BOXRIGHT] < ld->v1->x;
+		p2 = tmbox[BOXLEFT] < ld->v1->x;
+		if (ld->dy < 0)
+		{
+			p1 ^= 1;
+			p2 ^= 1;
+		}
+		break;
+	case ST_POSITIVE:
+		p1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld);
+		p2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld);
+		break;
+	case ST_NEGATIVE:
+		p1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld);
+		p2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld);
+		break;
+	}
+
+	if (p1 == p2)
+		return p1;
+	return -1;
+}
+
+/*
+==================
+=
+= P_PointOnDivlineSide
+=
+= Returns 0 or 1
+==================
+*/
+
+int P_PointOnDivlineSide (fixed_t x, fixed_t y, divline_t *line)
+{
+	fixed_t	dx, dy;
+	fixed_t	left, right;
+
+	if (!line->dx)
+	{
+		if (x <= line->x)
+			return line->dy > 0;
+		return line->dy < 0;
+	}
+	if (!line->dy)
+	{
+		if (y <= line->y)
+			return line->dx < 0;
+		return line->dx > 0;
+	}
+
+	dx = (x - line->x);
+	dy = (y - line->y);
+
+// try to quickly decide by looking at sign bits
+	if ((line->dy ^ line->dx ^ dx ^ dy) & 0x80000000)
+	{
+		if ((line->dy ^ dx) & 0x80000000)
+			return 1;	// (left is negative)
+		return 0;
+	}
+
+	left = FixedMul (line->dy>>8, dx>>8);
+	right = FixedMul (dy>>8, line->dx>>8);
+
+	if (right < left)
+		return 0;		// front side
+	return 1;			// back side
+}
+
+
+/*
+==============
+=
+= P_MakeDivline
+=
+==============
+*/
+
+void P_MakeDivline (line_t *li, divline_t *dl)
+{
+	dl->x = li->v1->x;
+	dl->y = li->v1->y;
+	dl->dx = li->dx;
+	dl->dy = li->dy;
+}
+
+
+/*
+===============
+=
+= P_InterceptVector
+=
+= Returns the fractional intercept point along the first divline
+=
+= This is only called by the addthings and addlines traversers
+===============
+*/
+
+fixed_t P_InterceptVector (divline_t *v2, divline_t *v1)
+{
+#if 1
+	fixed_t frac, num, den;
+
+	den = FixedMul(v1->dy>>8, v2->dx) - FixedMul(v1->dx>>8, v2->dy);
+	if (den == 0)
+		return 0;
+//		I_Error ("P_InterceptVector: parallel");
+	num = FixedMul((v1->x - v2->x)>>8, v1->dy) +
+		FixedMul ((v2->y - v1->y)>>8, v1->dx);
+	frac = FixedDiv (num , den);
+
+	return frac;
+#else
+	float	frac, num, den, v1x, v1y, v1dx, v1dy, v2x, v2y, v2dx, v2dy;
+
+	v1x = (float)v1->x/FRACUNIT;
+	v1y = (float)v1->y/FRACUNIT;
+	v1dx = (float)v1->dx/FRACUNIT;
+	v1dy = (float)v1->dy/FRACUNIT;
+	v2x = (float)v2->x/FRACUNIT;
+	v2y = (float)v2->y/FRACUNIT;
+	v2dx = (float)v2->dx/FRACUNIT;
+	v2dy = (float)v2->dy/FRACUNIT;
+
+	den = v1dy*v2dx - v1dx*v2dy;
+	if (den == 0)
+		return 0;	// parallel
+	num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx;
+	frac = num / den;
+
+	return frac*FRACUNIT;
+#endif
+}
+
+/*
+==================
+=
+= P_LineOpening
+=
+= Sets opentop and openbottom to the window through a two sided line
+= OPTIMIZE: keep this precalculated
+==================
+*/
+
+fixed_t opentop, openbottom, openrange;
+fixed_t lowfloor;
+
+void P_LineOpening (line_t *ld)
+{
+	sector_t	*front, *back;
+
+	if (ld->sidenum[1] == -1)
+	{	// single sided line
+		openrange = 0;
+		return;
+	}
+
+	front = ld->frontsector;
+	back = ld->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;
+static boolean	earlyout;
+
+/*
+==================
+=
+= 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
+==================
+*/
+
+static 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
+=
+==================
+*/
+
+static 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 = NULL;		// shut up compiler warning
+
+	while (count--)
+	{
+		dist = H2MAXINT;
+		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 = H2MAXINT;
+	}
+
+	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/p_mobj.c
@@ -1,0 +1,1625 @@
+// P_mobj.c
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+#include "sounds.h"
+#include "soundst.h"
+
+// MACROS ------------------------------------------------------------------
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+void G_PlayerReborn(int player);
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+void P_SpawnMapThing(mapthing_t *mthing);
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+extern fixed_t	attackrange;
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+mobjtype_t	PuffType;
+mobj_t		*MissileMobj;
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+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
+};
+
+// CODE --------------------------------------------------------------------
+
+//==========================================================================
+//
+// 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 = 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;
+}
+
+//==========================================================================
+//
+// 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 = 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
+//
+//----------------------------------------------------------------------------
+
+static 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;
+		}
+	}
+}
+
+//----------------------------------------------------------------------------
+//
+// 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
+
+static 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
+=
+===============
+*/
+
+static 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);
+#if defined(__WATCOMC__) && defined(_DOS)
+				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
+=
+===============
+*/
+
+static 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 = (mobj_t *) 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])
+	{ // Not playing
+		return;
+	}
+	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 translation bits 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;
+	}
+}
+
+//==========================================================================
+//
+// 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 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
+//
+//---------------------------------------------------------------------------
+
+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/p_plats.c
@@ -1,0 +1,251 @@
+
+// P_plats.c
+
+#include "h2stdinc.h"
+#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 *)(void *)&plat->sector->soundorg, sfx_stnmov);
+		}
+		if (plat->type == raiseAndChange
+			|| plat->type == raiseToNearestAndChange)
+		{
+			if (!(leveltime & 7))
+			{
+				S_StartSound((mobj_t *)(void *)&plat->sector->soundorg, sfx_stnmov);
+			}
+		}
+		if (res == res_crushed && (!plat->crush))
+		{
+			plat->count = plat->wait;
+			plat->status = down;
+			S_StartSound((mobj_t *)(void *)&plat->sector->soundorg, sfx_pstart);
+		}
+		else
+		if (res == res_pastdest)
+		{
+			plat->count = plat->wait;
+			plat->status = waiting;
+			S_StartSound((mobj_t *)(void *)&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 == res_pastdest)
+		{
+			plat->count = plat->wait;
+			plat->status = waiting;
+			S_StartSound((mobj_t *)(void *)&plat->sector->soundorg, sfx_pstop);
+		}
+		else
+		{
+			if (!(leveltime & 31))
+			{
+				S_StartSound((mobj_t *)(void *)&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 *)(void *)&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 = (plat_t *) Z_Malloc( sizeof(*plat), PU_LEVSPEC, NULL);
+		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 *)(void *)&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 *)(void *)&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 *)(void *)&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 *)(void *)&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/p_pspr.c
@@ -1,0 +1,1894 @@
+// P_pspr.c
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#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
+
+// TYPES -------------------------------------------------------------------
+
+static struct
+{
+	fixed_t x;
+	fixed_t y;
+} MaceSpots[MAX_MACE_SPOTS];
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+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
+	}
+};
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+static int MaceSpotCount;
+
+static 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
+};
+
+// CODE --------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+//
+// 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.
+			((void(*)(void*,void*))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 newstate;
+
+	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])
+	{
+		newstate = wpnlev2info[player->pendingweapon].upstate;
+	}
+	else
+	{
+		newstate = wpnlev1info[player->pendingweapon].upstate;
+	}
+	player->pendingweapon = wp_nochange;
+	player->psprites[ps_weapon].sy = WEAPONBOTTOM;
+	P_SetPsprite(player, ps_weapon, newstate);
+}
+
+//---------------------------------------------------------------------------
+//
+// 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
+=
+===============
+*/
+
+static 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 = (intptr_t)linetarget;
+		}
+	}
+	S_StartSound(player->mo, sfx_lobsht);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_DeathBallImpact
+//
+//----------------------------------------------------------------------------
+
+void A_DeathBallImpact(mobj_t *ball)
+{
+	int i;
+	mobj_t *target;
+	angle_t angle=0;
+	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 = (intptr_t)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)
+{
+	unsigned 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 = (intptr_t)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/p_setup.c
@@ -1,0 +1,969 @@
+
+// P_main.c
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+#ifdef RENDER3D
+#include "ogl_def.h"
+#endif
+
+// MACROS ------------------------------------------------------------------
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+void P_SpawnMapThing(mapthing_t *mthing);
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+#ifdef RENDER3D
+static float P_AccurateDistance(fixed_t dx, fixed_t dy);
+#endif
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+mapthing_t	deathmatchstarts[10], *deathmatch_p;
+mapthing_t	playerstarts[MAXPLAYERS];
+
+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
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+// CODE --------------------------------------------------------------------
+
+
+/*
+=================
+=
+= P_LoadVertexes
+=
+=================
+*/
+
+static void P_LoadVertexes (int lump)
+{
+	void		*data;
+	int		i;
+	mapvertex_t	*ml;
+	vertex_t	*li;
+
+	numvertexes = W_LumpLength (lump) / sizeof(mapvertex_t);
+	vertexes = (vertex_t *) Z_Malloc (numvertexes*sizeof(vertex_t), PU_LEVEL, NULL);
+	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
+=
+=================
+*/
+
+static void P_LoadSegs (int lump)
+{
+	void		*data;
+	int		i;
+	mapseg_t	*ml;
+	seg_t		*li;
+	line_t		*ldef;
+	int		_linedef, side;
+
+	numsegs = W_LumpLength (lump) / sizeof(mapseg_t);
+	segs = (seg_t *) Z_Malloc (numsegs*sizeof(seg_t), PU_LEVEL, NULL);
+	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;
+
+#ifdef RENDER3D
+	// Calculate the length of the segment. We need this for
+	// the texture coordinates. -jk
+		li->len = P_AccurateDistance(li->v2->x - li->v1->x, li->v2->y - li->v1->y);
+#endif
+	}
+
+	Z_Free (data);
+}
+
+
+/*
+=================
+=
+= P_LoadSubsectors
+=
+=================
+*/
+
+static void P_LoadSubsectors (int lump)
+{
+	void			*data;
+	int			i;
+	mapsubsector_t		*ms;
+	subsector_t		*ss;
+
+	numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t);
+	subsectors = (subsector_t *) Z_Malloc (numsubsectors*sizeof(subsector_t), PU_LEVEL, NULL);
+	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
+=
+=================
+*/
+
+static void P_LoadSectors (int lump)
+{
+	void			*data;
+	int			i;
+	mapsector_t		*ms;
+	sector_t		*ss;
+
+	numsectors = W_LumpLength (lump) / sizeof(mapsector_t);
+	sectors = (sector_t *) Z_Malloc (numsectors*sizeof(sector_t), PU_LEVEL, NULL);
+	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;
+
+#ifdef RENDER3D
+		ss->flatoffx = ss->flatoffy = 0;// Flat scrolling.
+		ss->skyfix = 0;			// Set if needed.
+#endif
+	}
+	Z_Free(data);
+}
+
+
+/*
+=================
+=
+= P_LoadNodes
+=
+=================
+*/
+
+static void P_LoadNodes (int lump)
+{
+	void		*data;
+	int		i, j, k;
+	mapnode_t	*mn;
+	node_t		*no;
+
+	numnodes = W_LumpLength (lump) / sizeof(mapnode_t);
+	nodes = (node_t *) Z_Malloc (numnodes*sizeof(node_t), PU_LEVEL, NULL);
+	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
+=
+=================
+*/
+
+static void P_LoadThings(int lump)
+{
+	void		*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
+=================
+*/
+
+static void P_LoadLineDefs(int lump)
+{
+	void		*data;
+	int		i;
+	maplinedef_t	*mld;
+	line_t		*ld;
+	vertex_t	*v1, *v2;
+
+	numlines = W_LumpLength(lump) / sizeof(maplinedef_t);
+	lines = (line_t *) Z_Malloc(numlines*sizeof(line_t), PU_LEVEL, NULL);
+	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
+=
+=================
+*/
+
+static void P_LoadSideDefs (int lump)
+{
+	void		*data;
+	int		i;
+	mapsidedef_t	*msd;
+	side_t		*sd;
+
+	numsides = W_LumpLength (lump) / sizeof(mapsidedef_t);
+	sides = (side_t *) Z_Malloc (numsides*sizeof(side_t), PU_LEVEL, NULL);
+	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
+=
+=================
+*/
+
+static void P_LoadBlockMap (int lump)
+{
+	int		i, count;
+
+	blockmaplump = (short *) 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 = (mobj_t **) Z_Malloc (count, PU_LEVEL, NULL);
+	memset (blocklinks, 0, count);
+}
+
+
+/*
+=================
+=
+= P_GroupLines
+=
+= Builds sector line lists and subsector sector numbers
+= Finds block bounding boxes for sectors
+=================
+*/
+
+static 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 = (line_t **) Z_Malloc (total * sizeof(line_t *), PU_LEVEL, NULL);
+	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;
+	}
+}
+
+
+#if defined(RENDER3D)
+
+#define MAX_CC_SIDES	64
+
+static float P_AccurateDistance(fixed_t dx, fixed_t dy)
+{
+	float fx = FIX2FLT(dx), fy = FIX2FLT(dy);
+	return (float)sqrt(fx*fx + fy*fy);
+}
+
+static int __no_optimize detSideFloat(fvertex_t *pnt, fdivline_t *dline)
+{
+	/*
+	    (AY-CY)(BX-AX)-(AX-CX)(BY-AY)
+	s = -----------------------------
+			L**2
+
+	If s < 0  C is left of AB (you can just check the numerator)
+	If s > 0  C is right of AB
+	If s = 0  C is on AB
+
+	We'll return false if the point c is on the left side.
+	*/
+	float s = (dline->y - pnt->y) * dline->dx - (dline->x - pnt->x) * dline->dy;
+	if (s < 0)
+		return 0;
+	return 1;
+}
+
+// Lines start-end and fdiv must intersect.
+static float __no_optimize findIntersectionVertex(fvertex_t *start, fvertex_t *end,
+						  fdivline_t *fdiv, fvertex_t *inter)
+{
+	float ax = start->x, ay = start->y, bx = end->x, by = end->y;
+	float cx = fdiv->x, cy = fdiv->y, dx = cx + fdiv->dx, dy = cy + fdiv->dy;
+	/*
+	    (YA-YC)(XD-XC)-(XA-XC)(YD-YC)
+	r = -----------------------------  (eqn 1)
+	    (XB-XA)(YD-YC)-(YB-YA)(XD-XC)
+	*/
+	float r = ((ay - cy) * (dx-cx) - (ax-cx) * (dy-cy)) /
+		((bx - ax) * (dy - cy) - (by - ay) * (dx-cx));
+	/*
+	XI=XA+r(XB-XA)
+	YI=YA+r(YB-YA)
+	*/
+	inter->x = ax + r * (bx - ax);
+	inter->y = ay + r * (by - ay);
+	return r;
+}
+
+static void P_ConvexCarver(subsector_t *ssec, int num, divline_t *list)
+{
+	int		numclippers = num + ssec->numlines;
+	fdivline_t	*clippers = (fdivline_t *) malloc(numclippers*sizeof(fdivline_t));
+	int		i, k, numedgepoints;
+	fvertex_t	*edgepoints;
+	unsigned char	sidelist[MAX_CC_SIDES];
+
+// Convert the divlines to float, in reverse order.
+	for (i = 0; i < numclippers; i++)
+	{
+		if (i < num)
+		{
+			clippers[i].x = FIX2FLT(list[num - i - 1].x);
+			clippers[i].y = FIX2FLT(list[num - i - 1].y);
+			clippers[i].dx = FIX2FLT(list[num - i - 1].dx);
+			clippers[i].dy = FIX2FLT(list[num - i - 1].dy);
+		}
+		else
+		{
+			seg_t *seg = segs + (ssec->firstline + i - num);
+			clippers[i].x = FIX2FLT(seg->v1->x);
+			clippers[i].y = FIX2FLT(seg->v1->y);
+			clippers[i].dx = FIX2FLT(seg->v2->x - seg->v1->x);
+			clippers[i].dy = FIX2FLT(seg->v2->y - seg->v1->y);
+		}
+	}
+
+// Setup the 'worldwide' polygon.
+	numedgepoints = 4;
+	edgepoints = (fvertex_t *) malloc(numedgepoints*sizeof(fvertex_t));
+
+	edgepoints[0].x = -32768;
+	edgepoints[0].y = 32768;
+
+	edgepoints[1].x = 32768;
+	edgepoints[1].y = 32768;
+
+	edgepoints[2].x = 32768;
+	edgepoints[2].y = -32768;
+
+	edgepoints[3].x = -32768;
+	edgepoints[3].y = -32768;
+
+// We'll now clip the polygon with each of the divlines. The left side of
+// each divline is discarded.
+	for (i = 0; i < numclippers; i++)
+	{
+		fdivline_t *curclip = clippers + i;
+
+		// First we'll determine the side of each vertex.
+		// Points are allowed to be on the line.
+		for (k = 0; k < numedgepoints; k++)
+		{
+			sidelist[k] = detSideFloat(edgepoints + k, curclip);
+		}
+
+		for (k = 0; k < numedgepoints; k++)
+		{
+			int startIdx = k, endIdx = k + 1;
+
+			// Check the end index.
+			if (endIdx == numedgepoints)
+				endIdx = 0;	// Wrap-around.
+
+			// Clipping will happen when the ends are on different sides.
+			if (sidelist[startIdx] != sidelist[endIdx])
+			{
+				fvertex_t newvert;
+			// Find the intersection point of intersecting lines.
+				findIntersectionVertex(edgepoints + startIdx, edgepoints + endIdx, curclip, &newvert);
+
+			// Add the new vertex. Also modify the sidelist.
+				edgepoints = (fvertex_t *) realloc(edgepoints, (++numedgepoints)*sizeof(fvertex_t));
+				if (numedgepoints >= MAX_CC_SIDES)
+					I_Error("Too many points in carver.\n");
+
+			// Make room for the new vertex.
+				memmove(edgepoints + endIdx + 1, edgepoints + endIdx,
+					(numedgepoints - endIdx - 1)*sizeof(fvertex_t));
+				memcpy (edgepoints + endIdx, &newvert, sizeof(newvert));
+
+				memmove(sidelist + endIdx + 1, sidelist + endIdx, numedgepoints - endIdx - 1);
+				sidelist[endIdx] = 1;
+
+			// Skip over the new vertex.
+				k++;
+			}
+		}
+
+	// Now we must discard the points that are on the wrong side.
+		for (k = 0; k < numedgepoints; k++)
+		{
+			if (!sidelist[k])
+			{
+				memmove(edgepoints + k, edgepoints + k + 1, (numedgepoints-k-1)*sizeof(fvertex_t));
+				memmove(sidelist + k, sidelist + k + 1, numedgepoints - k - 1);
+				numedgepoints--;
+				k--;
+			}
+		} 
+	}
+
+	if (!numedgepoints)
+	{
+	//	I_Error("All carved away!\n");
+		printf( "All carved away: subsector %p\n", ssec);
+		ssec->numedgeverts = 0;
+		ssec->edgeverts = 0;
+		ssec->origedgeverts = 0;
+	}
+	else
+	{
+		// Screen out consecutive identical points.
+		for (i = 0; i < numedgepoints; i++)
+		{
+			int previdx = i - 1;
+			if (previdx < 0)
+				previdx = numedgepoints - 1;
+			if (edgepoints[i].x == edgepoints[previdx].x &&
+			    edgepoints[i].y == edgepoints[previdx].y)
+			{
+			// This point (i) must be removed.
+				memmove(edgepoints + i, edgepoints + i + 1,
+					sizeof(fvertex_t)*(numedgepoints - i - 1));
+				numedgepoints--;
+				i--;
+			}
+		}
+		// We need these with dynamic lights.
+		ssec->origedgeverts = (fvertex_t *) Z_Malloc(sizeof(fvertex_t)*numedgepoints, PU_LEVEL, NULL);
+		memcpy(ssec->origedgeverts, edgepoints, sizeof(fvertex_t)*numedgepoints);
+
+		// Find the center point. Do this by first finding the bounding box.
+		ssec->bbox[0].x = ssec->bbox[1].x = edgepoints[0].x;
+		ssec->bbox[0].y = ssec->bbox[1].y = edgepoints[0].y;
+		for (i = 1; i < numedgepoints; i++)
+		{
+			if (edgepoints[i].x < ssec->bbox[0].x)
+				ssec->bbox[0].x = edgepoints[i].x;
+			if (edgepoints[i].y < ssec->bbox[0].y)
+				ssec->bbox[0].y = edgepoints[i].y;
+			if (edgepoints[i].x > ssec->bbox[1].x)
+				ssec->bbox[1].x = edgepoints[i].x;
+			if (edgepoints[i].y > ssec->bbox[1].y)
+				ssec->bbox[1].y = edgepoints[i].y;
+		}
+		ssec->midpoint.x = (ssec->bbox[1].x + ssec->bbox[0].x) / 2;
+		ssec->midpoint.y = (ssec->bbox[1].y + ssec->bbox[0].y) / 2;
+
+		// Make slight adjustments to patch up those ugly, small gaps.
+		for (i = 0; i < numedgepoints; i++)
+		{
+			float dx = edgepoints[i].x - ssec->midpoint.x,
+			      dy = edgepoints[i].y - ssec->midpoint.y;
+			float dlen = (float) sqrt(dx*dx + dy*dy) * 3;
+			if (dlen)
+			{
+				edgepoints[i].x += dx / dlen;
+				edgepoints[i].y += dy / dlen;
+			}
+		}
+
+		ssec->numedgeverts = numedgepoints;
+		ssec->edgeverts = (fvertex_t *) Z_Malloc(sizeof(fvertex_t)*numedgepoints, PU_LEVEL, NULL);
+		memcpy(ssec->edgeverts, edgepoints, sizeof(fvertex_t)*numedgepoints);
+	}
+
+	// We're done, free the edgepoints memory.
+	free(clippers);
+	free(edgepoints);
+}
+
+static void P_CreateFloorsAndCeilings(int bspnode, int numdivlines, divline_t* divlines)
+{
+	node_t		*nod;
+	divline_t	*childlist, *dl;
+	int		childlistsize = numdivlines + 1;
+
+	// If this is a subsector we are dealing with, begin carving with the
+	// given list.
+	if (bspnode & NF_SUBSECTOR)
+	{
+	// We have arrived at a subsector. The divline list contains all
+	// the partition lines that carve out the subsector.
+		int ssidx = bspnode & (~NF_SUBSECTOR);
+		OGL_DEBUG("subsector %d: %d divlines\n", ssidx, numdivlines);
+	//	if (ssidx < 10)
+		P_ConvexCarver(subsectors+ssidx, numdivlines, divlines);
+
+		OGL_DEBUG("subsector %d: %d edgeverts\n", ssidx, subsectors[ssidx].numedgeverts);
+		return;	// This leaf is done.
+	}
+
+	// Get a pointer to the node.
+	nod = nodes + bspnode;
+
+	// Allocate a new list for each child.
+	childlist = (divline_t *) malloc(childlistsize*sizeof(divline_t));
+
+	// Copy the previous lines.
+	if (divlines)
+		memcpy(childlist, divlines, numdivlines*sizeof(divline_t));
+
+	dl = childlist + numdivlines;
+	dl->x = nod->x;
+	dl->y = nod->y;
+	// The right child gets the original line (LEFT side clipped).
+	dl->dx = nod->dx;
+	dl->dy = nod->dy;
+	P_CreateFloorsAndCeilings(nod->children[0], childlistsize, childlist);
+
+	// The left side. We must reverse the line, otherwise the wrong
+	// side would get clipped.
+	dl->dx = -nod->dx;
+	dl->dy = -nod->dy;
+	P_CreateFloorsAndCeilings(nod->children[1], childlistsize, childlist);
+
+	// We are finishing with this node, free the allocated list.
+	free(childlist);
+}
+
+static void P_SkyFix(void)
+{
+	int		i;
+
+	// We need to check all the linedefs.
+	for (i = 0; i < numlines; i++)
+	{
+		line_t *line = lines + i;
+		sector_t *front = line->frontsector, *back = line->backsector;
+		int fix = 0;
+		// The conditions!
+		if (!front || !back)
+			continue;
+		// Both the front and back sectors must have the sky ceiling.
+		if (front->ceilingpic != skyflatnum || back->ceilingpic != skyflatnum)
+			continue;
+		// Operate on the lower sector.
+		OGL_DEBUG("Line %d (f:%d, b:%d).\n", i, front->ceilingheight >> FRACBITS,
+							 back->ceilingheight >> FRACBITS);
+		if (front->ceilingheight < back->ceilingheight)
+		{
+			fix = (back->ceilingheight - front->ceilingheight) >> FRACBITS;
+			if (fix > front->skyfix)
+				front->skyfix = fix;
+		}
+		else if (front->ceilingheight > back->ceilingheight)
+		{
+			fix = (front->ceilingheight - back->ceilingheight) >> FRACBITS;
+			if (fix > back->skyfix)
+				back->skyfix = fix;
+		}
+	}
+}
+#endif	/* RENDER3D */
+
+//=============================================================================
+
+/*
+=================
+=
+= 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);
+
+#ifdef RENDER3D
+	OGL_ResetData();
+#endif
+
+	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);
+
+#ifdef RENDER3D
+	// We need to carve out the floor/ceiling polygons of each subsector.
+	// Walk the tree to do this.
+	//OGL_DEBUG("Floor/ceiling creation: begin at %d, ", ticcount);
+	P_CreateFloorsAndCeilings(numnodes - 1, 0, 0);
+	// Also check if the sky needs a fix.
+	P_SkyFix();
+#endif
+
+	rejectmatrix = (byte *) 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/p_sight.c
@@ -1,0 +1,334 @@
+// P_sight.c
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+
+/*
+==============================================================================
+
+P_CheckSight
+
+This uses specialized forms of the maputils routines for optimized performance
+
+==============================================================================
+*/
+
+static int		sightcounts[3];
+static fixed_t		sightzstart;		/* eye z of looker */
+
+fixed_t			topslope, bottomslope;
+				  /* slopes to top and bottom of target */
+
+
+/*
+==============
+=
+= PTR_SightTraverse
+=
+==============
+*/
+
+static 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
+=
+===================
+*/
+
+static 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
+====================
+*/
+
+static 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 = NULL;		// shut up compiler warning
+
+	while (count--)
+	{
+		dist = H2MAXINT;
+		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 = H2MAXINT;
+	}
+
+	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
+==================
+*/
+
+static 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/p_spec.c
@@ -1,0 +1,1282 @@
+
+// P_Spec.c
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#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;
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+anim_t anims[MAXANIMS];
+anim_t *lastanim;
+int *TerrainTypes;
+mobj_t LavaInflictor;
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+static int *LevelAmbientSfx[MAX_AMBIENT_SFX];
+static int *AmbSfxPtr;
+static int AmbSfxCount;
+static int AmbSfxTics;
+static int AmbSfxVolume;
+
+static int AmbSndSeqInit[] =
+{ /* Startup */
+	afxcmd_end
+};
+static int AmbSndSeq1[] =
+{ /* Scream */
+	afxcmd_play, sfx_amb1,
+	afxcmd_end
+};
+static int AmbSndSeq2[] =
+{ /* Squish */
+	afxcmd_play, sfx_amb2,
+	afxcmd_end
+};
+static 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
+};
+static 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
+};
+static 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
+};
+static 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
+};
+static int AmbSndSeq7[] =
+{ /* Growl */
+	afxcmd_play, sfx_bstsit,
+	afxcmd_end
+};
+static int AmbSndSeq8[] =
+{ /* Magic */
+	afxcmd_play, sfx_amb8,
+	afxcmd_end
+};
+static 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
+};
+static 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
+};
+
+static int *AmbientSfx[] =
+{
+	AmbSndSeq1,		/* Scream */
+	AmbSndSeq2,		/* Squish */
+	AmbSndSeq3,		/* Drops */
+	AmbSndSeq4,		/* SlowFootsteps */
+	AmbSndSeq5,		/* Heartbeat */
+	AmbSndSeq6,		/* Bells */
+	AmbSndSeq7,		/* Growl */
+	AmbSndSeq8,		/* Magic */
+	AmbSndSeq9,		/* Laughter */
+	AmbSndSeq10		/* FastFootsteps */
+};
+
+static 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 }
+};
+
+static struct
+{
+	const char	*name;
+	int		type;
+} TerrainTypeDefs[] =
+{
+	{ "FLTWAWA1", FLOOR_WATER },
+	{ "FLTFLWW1", FLOOR_WATER },
+	{ "FLTLAVA1", FLOOR_LAVA },
+	{ "FLATHUH1", FLOOR_LAVA },
+	{ "FLTSLUD1", FLOOR_SLUDGE },
+	{ "END", -1 }
+};
+
+// CODE --------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+//
+// 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 = (int *) Z_Malloc(size, PU_STATIC, NULL);
+	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
+//
+//==================================================================
+
+#define MAX_ADJOINING_SECTORS	20	/* 20 adjoining sectors max! */
+
+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[MAX_ADJOINING_SECTORS];
+
+	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;
+		if (h >= MAX_ADJOINING_SECTORS)
+		{
+			fprintf(stderr, "Sector with more than %d adjoining sectors\n",
+					 MAX_ADJOINING_SECTORS);
+			break;
+		}
+	}
+
+	//
+	// Find lowest height in list
+	//
+	if(!h)
+		return currentheight;
+
+	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 = H2MAXINT;
+
+	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;
+		}
+	}
+	switch (line->special)
+	{
+	//====================================================
+	// TRIGGERS
+	//====================================================
+	case 2: // Open Door
+		EV_DoDoor(line, vldoor_open, VDOORSPEED);
+		line->special = 0;
+		break;
+	case 3: // Close Door
+		EV_DoDoor(line, vldoor_close, VDOORSPEED);
+		line->special = 0;
+		break;
+	case 4: // Raise Door
+		EV_DoDoor(line, vldoor_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, vldoor_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, vldoor_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, vldoor_normal, VDOORSPEED);
+		break;
+	case 100: // Retrigger_Raise_Door_Turbo
+		EV_DoDoor(line, vldoor_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;
+		}
+	}
+	switch (line->special)
+	{
+	case 24: // Impact_RaiseFloor
+		EV_DoFloor(line, raiseFloor);
+		P_ChangeSwitchTexture(line, 0);
+		break;
+	case 46: // Impact_OpenDoor
+		EV_DoDoor(line, vldoor_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 swtch_top:
+					sides[buttonlist[i].line->sidenum[0]].toptexture =
+							buttonlist[i].btexture;
+					break;
+				case swtch_middle:
+					sides[buttonlist[i].line->sidenum[0]].midtexture =
+							buttonlist[i].btexture;
+						break;
+				case swtch_bottom:
+					sides[buttonlist[i].line->sidenum[0]].bottomtexture =
+							buttonlist[i].btexture;
+					break;
+				}
+				S_StartSound(buttonlist[i].soundorg, sfx_switch);
+				memset(&buttonlist[i], 0, sizeof(button_t));
+			}
+		}
+	}
+}
+
+//============================================================
+//
+//	Special Stuff that can't be categorized
+//
+//============================================================
+int EV_DoDonut(line_t *line)
+{
+	sector_t	*s1;
+	sector_t	*s2;
+	sector_t	*s3;
+	int		secnum;
+	int		rtn;
+	int		i;
+	floormove_t	*floor;
+
+	secnum = -1;
+	rtn = 0;
+	while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)
+	{
+		s1 = &sectors[secnum];
+
+		// ALREADY MOVING?  IF SO, KEEP GOING...
+		if (s1->specialdata)
+			continue;
+
+		rtn = 1;
+		s2 = getNextSector(s1->lines[0],s1);
+		for (i = 0; i < s2->linecount; i++)
+		{
+			if (!(s2->lines[i]->flags & ML_TWOSIDED) ||
+				(s2->lines[i]->backsector == s1))
+				continue;
+			s3 = s2->lines[i]->backsector;
+
+			//
+			//	Spawn rising slime
+			//
+			floor = (floormove_t *) Z_Malloc (sizeof(*floor), PU_LEVSPEC, NULL);
+			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 = (floormove_t *) Z_Malloc (sizeof(*floor), PU_LEVSPEC, NULL);
+			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;
+#if 0
+	int		episode = 1;
+	if (W_CheckNumForName("texture2") >= 0)
+		episode = 2;
+#endif
+	//
+	// 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/p_spec.h
@@ -1,0 +1,338 @@
+// P_spec.h
+
+#ifndef __P_SPEC__
+#define __P_SPEC__
+
+/* ---- 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 */
+	const char	endname[9];
+	const 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
+{
+	const char	name1[9];
+	const char	name2[9];
+	short	episode;
+} switchlist_t;
+
+typedef enum
+{
+	swtch_top,
+	swtch_middle,
+	swtch_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
+{
+	vldoor_normal,
+	close30ThenOpen,
+	vldoor_close,
+	vldoor_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
+{
+	res_ok,
+	res_crushed,
+	res_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);
+
+#endif	/* __P_SPEC__ */
+
--- /dev/null
+++ b/p_switch.c
@@ -1,0 +1,404 @@
+// p_switch.c
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+
+//==================================================================
+//
+//	CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE
+//
+//==================================================================
+
+static 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}
+};
+
+static int switchlist[MAXSWITCHES * 2];
+static int numswitches;
+
+button_t	buttonlist[MAXBUTTONS];
+
+/*
+===============
+=
+= P_InitSwitchList
+=
+= Only called at game initialization
+=
+===============
+*/
+
+void P_InitSwitchList(void)
+{
+	int		i, idx;
+	int		episode;
+
+	episode = 1;
+	if (!shareware)
+		episode = 2;
+
+	for (idx = 0, i = 0; i < MAXSWITCHES; i++)
+	{
+		if (!alphSwitchList[i].episode)
+		{
+			numswitches = idx/2;
+			switchlist[idx] = -1;
+			break;
+		}
+
+		if (alphSwitchList[i].episode <= episode)
+		{
+			switchlist[idx++] = R_TextureNumForName(alphSwitchList[i].name1);
+			switchlist[idx++] = 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 timer)
+{
+	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 = timer;
+			buttonlist[i].soundorg = (mobj_t *)(void *)&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, swtch_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, swtch_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, swtch_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, vldoor_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, vldoor_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, vldoor_open, VDOORSPEED))
+			P_ChangeSwitchTexture(line,0);
+		break;
+
+	//===============================================
+	//	BUTTONS
+	//===============================================
+	case 42: // Close Door
+		if (EV_DoDoor(line, vldoor_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, vldoor_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, vldoor_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/p_telept.c
@@ -1,0 +1,168 @@
+// P_telept.c
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+
+// MACROS ------------------------------------------------------------------
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+// CODE --------------------------------------------------------------------
+
+//==========================================================================
+//
+// 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 int 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/p_tick.c
@@ -1,0 +1,146 @@
+// P_tick.c
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+
+// MACROS ------------------------------------------------------------------
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+static void P_RunThinkers (void);
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+int leveltime;
+int TimerGame;
+thinker_t thinkercap;	/* both the head and tail of the thinker list */
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+// CODE --------------------------------------------------------------------
+
+
+//==========================================================================
+//
+// 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.
+//
+//==========================================================================
+
+
+//==========================================================================
+//
+// 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++;
+}
+
+//==========================================================================
+//
+// P_RunThinkers
+//
+//==========================================================================
+
+static 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;
+	}
+}
+
+//==========================================================================
+//
+// 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;
+}
+
--- /dev/null
+++ b/p_user.c
@@ -1,0 +1,1012 @@
+// P_user.c
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+#include "v_compat.h"	/* V_SetPaletteXXX() macros */
+
+// Macros
+
+#define MAXBOB		0x100000	/* 16 pixels of bob */
+
+// Extern Data
+
+extern int		inv_ptr;
+extern int		curpos;
+extern boolean		ultimatemsg;
+
+// Private Data
+
+static boolean		onground;
+static int		newtorch;	/* used in the torch flicker effect. */
+static int		newtorchdelta;
+
+static boolean WeaponInShareware[] =
+{
+	true,		/* Staff */
+	true,		/* Gold wand */
+	true,		/* Crossbow */
+	true,		/* Blaster */
+	false,		/* Skull rod */
+	false,		/* Phoenix rod */
+	false,		/* Mace */
+	true,		/* Gauntlets */
+	true		/* Beak */
+};
+
+// Global Functions
+
+void P_PlayerNextArtifact(player_t *player);
+
+
+//==========================================================================
+
+
+/*
+==================
+=
+= 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
+=
+==================
+*/
+
+static 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
+=
+=================
+*/
+
+static 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)
+
+static void P_DeathThink(player_t *player)
+{
+	angle_t angle, delta;
+	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])
+		{
+			V_SetPaletteBase();
+			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
+//
+//----------------------------------------------------------------------------
+
+static 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;
+
+	// 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])
+		{
+#if defined(__WATCOMC__) && defined(_DOS)
+			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
+//
+//----------------------------------------------------------------------------
+
+static 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)
+{
+	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;
+
+	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/r_bsp.c
@@ -1,0 +1,471 @@
+// R_bsp.c
+
+#include "h2stdinc.h"
+
+#ifndef RENDER3D
+
+#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_segs.c */
+
+/*
+====================
+=
+= 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
+
+static cliprange_t	solidsegs[MAXSEGS], *newend;	// newend is one past the last valid seg
+
+
+static 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
+===============================================================================
+*/
+
+static 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
+=
+======================
+*/
+
+static void R_AddLine (seg_t *line)
+{
+	int		x1, x2;
+	angle_t		angle1, angle2, span, tspan;
+
+	curline = line;
+
+// OPTIMIZE: quickly reject orthogonal back sides
+
+	angle1 = R_PointToAngle (line->v1->x, line->v1->y);
+	angle2 = R_PointToAngle (line->v2->x, line->v2->y);
+
+//
+// clip to view edges
+// OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW)
+	span = angle1 - angle2;
+	if (span >= ANG180)
+		return;		// back side
+
+	rw_angle1 = angle1;	// global angle needed by segcalc
+	angle1 -= viewangle;
+	angle2 -= viewangle;
+
+	tspan = angle1 + clipangle;
+	if (tspan > 2*clipangle)
+	{
+		tspan -= 2*clipangle;
+		if (tspan >= span)
+			return;	// totally off the left edge
+		angle1 = clipangle;
+	}
+	tspan = clipangle - angle2;
+	if (tspan > 2*clipangle)
+	{
+		tspan -= 2*clipangle;
+		if (tspan >= span)
+			return;	// totally off the left edge
+		angle2 = -clipangle;
+	}
+
+//
+// the seg is in the view range, but not necessarily visible
+//
+	angle1 = (angle1 + ANG90)>>ANGLETOFINESHIFT;
+	angle2 = (angle2 + ANG90)>>ANGLETOFINESHIFT;
+	x1 = viewangletox[angle1];
+	x2 = viewangletox[angle2];
+	if (x1 == x2)
+		return;		// does not cross a pixel
+
+	backsector = line->backsector;
+
+	if (!backsector)
+		goto clipsolid;	// single sided line
+
+	if (backsector->ceilingheight <= frontsector->floorheight ||
+	    backsector->floorheight >= frontsector->ceilingheight)
+		goto clipsolid;	// closed door
+
+	if (backsector->ceilingheight != frontsector->ceilingheight ||
+	    backsector->floorheight != frontsector->floorheight)
+		goto clippass;	// window
+
+// reject empty lines used for triggers and special events
+	if (backsector->ceilingpic == frontsector->ceilingpic &&
+	    backsector->floorpic == frontsector->floorpic &&
+	    backsector->lightlevel == frontsector->lightlevel &&
+	    curline->sidedef->midtexture == 0)
+		return;
+
+clippass:
+	R_ClipPassWallSegment (x1, x2 - 1);
+	return;
+
+clipsolid:
+	R_ClipSolidWallSegment (x1, x2 - 1);
+}
+
+//============================================================================
+
+
+/*
+===============================================================================
+=
+= R_CheckBBox
+=
+= Returns true if some part of the bbox might be visible
+=
+===============================================================================
+*/
+
+static 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}
+};
+
+static boolean R_CheckBBox (fixed_t *bspcoord)
+{
+	int		boxx, boxy, boxpos;
+	fixed_t		x1, y1, x2, y2;
+	angle_t		angle1, angle2, span, tspan;
+	cliprange_t	*start;
+	int		sx1, sx2;
+
+// find the corners of the box that define the edges from current viewpoint
+	if (viewx <= bspcoord[BOXLEFT])
+		boxx = 0;
+	else if (viewx < bspcoord[BOXRIGHT])
+		boxx = 1;
+	else
+		boxx = 2;
+
+	if (viewy >= bspcoord[BOXTOP])
+		boxy = 0;
+	else if (viewy > bspcoord[BOXBOTTOM])
+		boxy = 1;
+	else
+		boxy = 2;
+
+	boxpos = (boxy<<2) + boxx;
+	if (boxpos == 5)
+		return true;
+
+	x1 = bspcoord[checkcoord[boxpos][0]];
+	y1 = bspcoord[checkcoord[boxpos][1]];
+	x2 = bspcoord[checkcoord[boxpos][2]];
+	y2 = bspcoord[checkcoord[boxpos][3]];
+
+//
+// check clip list for an open space
+//
+	angle1 = R_PointToAngle (x1, y1) - viewangle;
+	angle2 = R_PointToAngle (x2, y2) - viewangle;
+
+	span = angle1 - angle2;
+	if (span >= ANG180)
+		return true;	// sitting on a line
+	tspan = angle1 + clipangle;
+	if (tspan > 2*clipangle)
+	{
+		tspan -= 2*clipangle;
+		if (tspan >= span)
+			return false;	// totally off the left edge
+		angle1 = clipangle;
+	}
+	tspan = clipangle - angle2;
+	if (tspan > 2*clipangle)
+	{
+		tspan -= 2*clipangle;
+		if (tspan >= span)
+			return false;	// totally off the left edge
+		angle2 = -clipangle;
+	}
+
+// find the first clippost that touches the source post (adjacent pixels are touching)
+	angle1 = (angle1 + ANG90)>>ANGLETOFINESHIFT;
+	angle2 = (angle2 + ANG90)>>ANGLETOFINESHIFT;
+	sx1 = viewangletox[angle1];
+	sx2 = viewangletox[angle2];
+	if (sx1 == sx2)
+		return false;		// does not cross a pixel
+	sx2--;
+
+	start = solidsegs;
+	while (start->last < sx2)
+		start++;
+	if (sx1 >= start->first && sx2 <= start->last)
+		return false;		// the clippost contains the new span
+
+	return true;
+}
+
+
+/*
+================
+=
+= R_Subsector
+=
+= Draw one or more segments
+================
+*/
+
+static void R_Subsector (int num)
+{
+	int		count;
+	seg_t		*line;
+	subsector_t	*sub;
+
+#ifdef RANGECHECK
+	if (num >= numsubsectors)
+		I_Error ("R_Subsector: ss %i with numss = %i", num, numsubsectors);
+#endif
+
+	sscount++;
+	sub = &subsectors[num];
+	frontsector = sub->sector;
+	count = sub->numlines;
+	line = &segs[sub->firstline];
+
+	if (frontsector->floorheight < viewz)
+	{
+		floorplane = R_FindPlane(frontsector->floorheight,
+					 frontsector->floorpic,
+					 frontsector->lightlevel,
+					 frontsector->special);
+	}
+	else
+	{
+		floorplane = NULL;
+	}
+	if (frontsector->ceilingheight > viewz ||
+	    frontsector->ceilingpic == skyflatnum)
+	{
+		ceilingplane = R_FindPlane(frontsector->ceilingheight,
+					   frontsector->ceilingpic,
+					   frontsector->lightlevel, 0);
+	}
+	else
+	{
+		ceilingplane = NULL;
+	}
+
+	R_AddSprites(frontsector);
+
+	while (count--)
+	{
+		R_AddLine (line);
+		line++;
+	}
+}
+
+
+/*
+===============================================================================
+=
+= RenderBSPNode
+=
+===============================================================================
+*/
+
+void R_RenderBSPNode (int bspnum)
+{
+	node_t		*bsp;
+	int		side;
+
+	if (bspnum & NF_SUBSECTOR)
+	{
+		if (bspnum == -1)
+			R_Subsector (0);
+		else
+			R_Subsector (bspnum & (~NF_SUBSECTOR));
+		return;
+	}
+
+	bsp = &nodes[bspnum];
+
+//
+// decide which side the view point is on
+//
+	side = R_PointOnSide (viewx, viewy, bsp);
+
+	R_RenderBSPNode (bsp->children[side]);	// recursively divide front space
+
+	if (R_CheckBBox (bsp->bbox[side^1]))	// possibly divide back space
+		R_RenderBSPNode (bsp->children[side^1]);
+}
+
+#endif	/* RENDER3D */
+
--- /dev/null
+++ b/r_data.c
@@ -1,0 +1,677 @@
+// R_data.c
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "r_local.h"
+#include "p_local.h"
+
+extern void CheckAbortStartup(void);
+
+int		firstflat, lastflat, numflats;
+int		firstpatch, lastpatch, numpatches;
+int		firstspritelump, lastspritelump, numspritelumps;
+
+int		numtextures;
+texture_t	**textures;
+
+lighttable_t	*colormaps;
+
+int		*flattranslation;	// for global animation
+int		*texturetranslation;	// for global animation
+
+fixed_t		*spritewidth;		// needed for pre rendering
+fixed_t		*spriteoffset;
+fixed_t		*spritetopoffset;
+
+fixed_t		*textureheight;		// needed for texture pegging
+
+static int	*texturewidthmask;
+static int	*texturecompositesize;
+static short	**texturecolumnlump;
+static unsigned short	**texturecolumnofs;
+static byte	**texturecomposite;
+
+
+/*
+==============================================================================
+
+						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
+=
+===================
+*/
+
+static void R_DrawColumnInCache (column_t *patch, byte *cache, int originy, int cacheheight)
+{
+	int	count, position;
+	byte	*source;
+//	byte	*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
+=
+===================
+*/
+
+static 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 = (byte *) 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 = (patch_t *) 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 *)malloc (texture->width);
+	memset (patchcount, 0, texture->width);
+	patch = texture->patches;
+
+	for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
+	{
+		realpatch = (patch_t *) 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])
+		{
+			char	name[9];
+			name[8] = 0;
+			memcpy (name, texture->name, 8);
+		//	I_Error ("R_GenerateLookup: column without a patch");
+			printf ("R_GenerateLookup: column without a patch (%s)\n", name);
+			free (patchcount);
+			return;
+		}
+		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;
+		}
+	}
+	free (patchcount);
+}
+
+
+/*
+================
+=
+= R_GetColumn
+=
+================
+*/
+
+byte *R_GetColumn (int tex, int col)
+{
+	int	lump, ofs;
+
+	col &= texturewidthmask[tex];
+	lump = texturecolumnlump[tex][col];
+	ofs = texturecolumnofs[tex][col];
+	if (lump > 0)
+		return (byte *)W_CacheLumpNum(lump, PU_CACHE) + ofs;
+	if (!texturecomposite[tex])
+		R_GenerateComposite (tex);
+	return texturecomposite[tex] + ofs;
+}
+
+
+/*
+==================
+=
+= R_InitTextures
+=
+= Initializes the texture list with the textures from the world map
+=
+==================
+*/
+
+static void R_InitTextures (void)
+{
+	maptexture_t	*mtexture;
+	texture_t	*texture;
+	mappatch_t	*mpatch;
+	texpatch_t	*patch;
+	int		i, j;
+	int		*maptex, *maptex2, *maptex1;
+	byte		*names;
+	char		name[9], *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 = (byte *) W_CacheLumpName ("PNAMES", PU_STATIC);
+	nummappatches = READ_INT32(names);
+	name_p = (char *)names + 4;
+	patchlookup = (int *)malloc(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 = (int *) W_CacheLumpName ("TEXTURE1", PU_STATIC);
+	numtextures1 = LONG(*maptex);
+	maxoff = W_LumpLength (W_GetNumForName ("TEXTURE1"));
+	directory = maptex + 1;
+
+	if (W_CheckNumForName ("TEXTURE2") != -1)
+	{
+		maptex2 = (int *) 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 = (texture_t **) Z_Malloc (numtextures*sizeof(texture_t *), PU_STATIC, NULL);
+	texturecolumnlump = (short **) Z_Malloc (numtextures*sizeof(short *), PU_STATIC, NULL);
+	texturecolumnofs = (unsigned short **) Z_Malloc (numtextures*sizeof(short *), PU_STATIC, NULL);
+	texturecomposite = (byte **) Z_Malloc (numtextures*sizeof(byte *), PU_STATIC, NULL);
+	texturecompositesize = (int *) Z_Malloc (numtextures*sizeof(int), PU_STATIC, NULL);
+	texturewidthmask = (int *) Z_Malloc (numtextures*sizeof(int), PU_STATIC, NULL);
+	textureheight = (fixed_t *) Z_Malloc (numtextures*sizeof(fixed_t), PU_STATIC, NULL);
+
+	totalwidth = 0;
+
+	for (i = 0; i < numtextures; i++, directory++)
+	{
+		IncThermo();
+		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);
+		j = SHORT(mtexture->patchcount);
+		texture = textures[i] = (texture_t *) 
+			Z_Malloc (sizeof(texture_t) + sizeof(texpatch_t)*(j - 1), PU_STATIC, NULL);
+		texture->width = SHORT(mtexture->width);
+		texture->height = SHORT(mtexture->height);
+		texture->patchcount = SHORT(mtexture->patchcount);
+		name_p = (char *)maptex + offset;
+		memcpy (texture->name, name_p, 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)
+			{
+				memcpy (name, texture->name, 8);
+				I_Error ("R_InitTextures: Missing patch in texture %s", name);
+			}
+		}
+		texturecolumnlump[i] = (short *) Z_Malloc (texture->width*2, PU_STATIC, NULL);
+		texturecolumnofs[i] = (unsigned short *) Z_Malloc (texture->width*2, PU_STATIC, NULL);
+		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 = (int *) Z_Malloc ((numtextures + 1)*sizeof(int), PU_STATIC, NULL);
+	for (i = 0; i < numtextures; i++)
+		texturetranslation[i] = i;
+
+	free (patchlookup);
+}
+
+
+/*
+================
+=
+= R_InitFlats
+=
+=================
+*/
+
+static void R_InitFlats (void)
+{
+	int		i;
+
+	firstflat = W_GetNumForName ("F_START") + 1;
+	lastflat = W_GetNumForName ("F_END") - 1;
+	numflats = lastflat - firstflat + 1;
+
+// translation table for global animation
+	flattranslation = (int *) Z_Malloc ((numflats + 1) * sizeof(int), PU_STATIC, NULL);
+	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
+=================
+*/
+
+static 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 = (fixed_t *) Z_Malloc (numspritelumps*sizeof(fixed_t), PU_STATIC, NULL);
+	spriteoffset = (fixed_t *) Z_Malloc (numspritelumps*sizeof(fixed_t), PU_STATIC, NULL);
+	spritetopoffset = (fixed_t *) Z_Malloc (numspritelumps*sizeof(fixed_t), PU_STATIC, NULL);
+
+	for (i = 0; i < numspritelumps; i++)
+	{
+		IncThermo();
+		patch = (patch_t *) 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
+=
+=================
+*/
+
+static 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 = (lighttable_t *) Z_Malloc (length, PU_STATIC, NULL);
+	colormaps = (byte *)( ((intptr_t)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();
+	tprintf("R_InitFlats\n",0);
+	R_InitFlats();
+	IncThermo();
+	tprintf("R_InitSpriteLumps ",0);
+	R_InitSpriteLumps();
+	IncThermo();
+	R_InitColormaps();
+}
+
+//=============================================================================
+
+/*
+================
+=
+= R_FlatNumForName
+=
+================
+*/
+
+int R_FlatNumForName (const 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 (const 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 (const char *name)
+{
+	int		i;
+
+	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
+=================
+*/
+
+static 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 = (char*)malloc(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 = (char*) malloc(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 = (char*)malloc(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);
+			}
+		}
+	}
+	free (flatpresent);
+	free (texturepresent);
+	free (spritepresent);
+}
+
--- /dev/null
+++ b/r_draw.c
@@ -1,0 +1,482 @@
+// R_draw.c
+
+#include "h2stdinc.h"
+
+#ifndef RENDER3D
+
+#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.
+
+*/
+
+int	viewwidth, scaledviewwidth, viewheight, viewwindowx, viewwindowy;
+
+byte	*ylookup[MAXHEIGHT];
+int	columnofs[MAXWIDTH];
+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
+
+
+void R_DrawColumn (void)
+{
+	int		count;
+	byte		*dest;
+	fixed_t		frac, fracstep;
+
+	count = dc_yh - dc_yl;
+	if (count < 0)
+		return;
+
+#ifdef RANGECHECK
+	if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT)
+		I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x);
+#endif
+
+	dest = ylookup[dc_yl] + columnofs[dc_x]; 
+
+	fracstep = dc_iscale;
+	frac = dc_texturemid + (dc_yl - centery)*fracstep;
+
+	do
+	{
+		*dest = dc_colormap[dc_source[(frac>>FRACBITS) & 127]];
+		dest += SCREENWIDTH;
+		frac += fracstep;
+	} while (count--);
+}
+
+void R_DrawColumnLow (void)
+{
+	int		count;
+	byte		*dest;
+	fixed_t		frac, fracstep;
+
+	count = dc_yh - dc_yl;
+	if (count < 0)
+		return;
+
+#ifdef RANGECHECK
+	if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT)
+		I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x);
+//	dccount++;
+#endif
+
+	dest = ylookup[dc_yl] + columnofs[dc_x];
+
+	fracstep = dc_iscale;
+	frac = dc_texturemid + (dc_yl - centery)*fracstep;
+
+	do
+	{
+		*dest = dc_colormap[dc_source[(frac>>FRACBITS) & 127]];
+		dest += SCREENWIDTH;
+		frac += fracstep;
+	} while (count--);
+}
+
+/*
+#define FUZZTABLE		50
+#define FUZZOFF		(SCREENWIDTH)
+static 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
+};
+static 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 = (byte *) W_CacheLumpName("TINTTAB", PU_STATIC);
+
+	// Allocate translation tables
+	translationtables = (byte *) Z_Malloc(256*3 + 255, PU_STATIC, NULL);
+	translationtables = (byte *)(((intptr_t)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
+
+
+void R_DrawSpan (void)
+{
+	fixed_t		xfrac, yfrac;
+	byte		*dest;
+	int		count, spot;
+
+#ifdef RANGECHECK
+	if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || (unsigned)ds_y > SCREENHEIGHT)
+		I_Error ("R_DrawSpan: %i to %i at %i", ds_x1, ds_x2, ds_y);
+//	dscount++;
+#endif
+
+	xfrac = ds_xfrac;
+	yfrac = ds_yfrac;
+
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+	count = ds_x2 - ds_x1;
+	do
+	{
+		spot = ((yfrac>>(16-6)) & (63*64)) + ((xfrac>>16) & 63);
+		*dest++ = ds_colormap[ds_source[spot]];
+		xfrac += ds_xstep;
+		yfrac += ds_ystep;
+	} while (count--);
+}
+
+void R_DrawSpanLow (void)
+{
+	fixed_t		xfrac, yfrac;
+	byte		*dest;
+	int		count, spot;
+
+#ifdef RANGECHECK
+	if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || (unsigned)ds_y > SCREENHEIGHT)
+		I_Error ("R_DrawSpan: %i to %i at %i", ds_x1, ds_x2, ds_y);
+//	dscount++;
+#endif
+
+	xfrac = ds_xfrac;
+	yfrac = ds_yfrac;
+
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+	count = ds_x2 - ds_x1;
+	do
+	{
+		spot = ((yfrac>>(16-6)) & (63*64)) + ((xfrac>>16) & 63);
+		*dest++ = ds_colormap[ds_source[spot]];
+		xfrac += ds_xstep;
+		yfrac += ds_ystep;
+	} while (count--);
+}
+
+
+/*
+================
+=
+= R_InitBuffer
+=
+=================
+*/
+
+void R_InitBuffer (int width, int height)
+{
+	int		i;
+
+	viewwindowx = (SCREENWIDTH - width) >> 1;
+	for (i = 0; i < width; i++)
+		columnofs[i] = viewwindowx + i;
+	if (width == SCREENWIDTH)
+		viewwindowy = 0;
+	else
+		viewwindowy = (SCREENHEIGHT - SBARHEIGHT - height) >> 1;
+	for (i = 0; i < height; i++)
+		ylookup[i] = screens + (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 = (byte *) W_CacheLumpName ("FLOOR04", PU_CACHE);
+	}
+	else
+	{
+		src = (byte *) W_CacheLumpName ("FLAT513", PU_CACHE);
+	}
+	dest = screens;
+
+	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, (patch_t *)W_CacheLumpName("bordt", PU_CACHE));
+		V_DrawPatch(x, viewwindowy + viewheight, (patch_t *)W_CacheLumpName("bordb", PU_CACHE));
+	}
+	for (y = viewwindowy; y < viewwindowy + viewheight; y += 16)
+	{
+		V_DrawPatch(viewwindowx - 4, y, (patch_t *)W_CacheLumpName("bordl", PU_CACHE));
+		V_DrawPatch(viewwindowx+viewwidth, y, (patch_t *)W_CacheLumpName("bordr", PU_CACHE));
+	}
+	V_DrawPatch(viewwindowx - 4, viewwindowy - 4, (patch_t *)W_CacheLumpName("bordtl", PU_CACHE));
+	V_DrawPatch(viewwindowx + viewwidth, viewwindowy - 4, (patch_t *)W_CacheLumpName("bordtr", PU_CACHE));
+	V_DrawPatch(viewwindowx + viewwidth, viewwindowy + viewheight, (patch_t *)W_CacheLumpName("bordbr", PU_CACHE));
+	V_DrawPatch(viewwindowx - 4, viewwindowy + viewheight, (patch_t *)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 = (byte *) W_CacheLumpName ("FLOOR04", PU_CACHE);
+	}
+	else
+	{
+		src = (byte *) W_CacheLumpName ("FLAT513", PU_CACHE);
+	}
+	dest = screens;
+
+	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, (patch_t *)W_CacheLumpName("bordt", PU_CACHE));
+		}
+		V_DrawPatch(viewwindowx-4, viewwindowy, (patch_t *)W_CacheLumpName("bordl", PU_CACHE));
+		V_DrawPatch(viewwindowx + viewwidth, viewwindowy, (patch_t *)W_CacheLumpName("bordr", PU_CACHE));
+		V_DrawPatch(viewwindowx - 4, viewwindowy + 16, (patch_t *)W_CacheLumpName("bordl", PU_CACHE));
+		V_DrawPatch(viewwindowx + viewwidth, viewwindowy + 16, (patch_t *)W_CacheLumpName("bordr", PU_CACHE));
+
+		V_DrawPatch(viewwindowx - 4, viewwindowy - 4, (patch_t *)W_CacheLumpName("bordtl", PU_CACHE));
+		V_DrawPatch(viewwindowx + viewwidth, viewwindowy - 4, (patch_t *)W_CacheLumpName("bordtr", PU_CACHE));
+	}
+}
+
+#endif	/* RENDER3D */
+
--- /dev/null
+++ b/r_local.h
@@ -1,0 +1,495 @@
+// 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 ---- */
+
+#pragma pack on
+
+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 */
+
+#ifdef RENDER3D
+	int	flatoffx, flatoffy;	/* Scrolling flats. */
+	int	skyfix;			/* Offset to ceiling height rendering w/sky. */
+#endif
+
+} 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;
+
+#ifdef RENDER3D
+typedef struct
+{
+	float x, y;
+} fvertex_t;
+#endif
+
+typedef struct subsector_s
+{
+	sector_t	*sector;
+	short		numlines;
+	short		firstline;
+#ifdef RENDER3D
+	/* Sorted edge vertices for rendering floors and ceilings. */
+	char		numedgeverts;
+	fvertex_t	*edgeverts;	/* A list of edge vertices. */
+	fvertex_t	*origedgeverts;	/* Unmodified, accurate edge vertices. */
+	fvertex_t	bbox[2];	/* Min and max points. */
+	fvertex_t	midpoint;	/* Center of bounding box. */
+#endif
+} 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 */
+#ifdef RENDER3D
+	float		len;		/* Length of the segment (v1 -> v2) for texture mapping. */
+#endif
+} 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;
+
+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 */
+#ifdef RENDER3D
+	boolean		masked;		/* from maptexture_t */
+#endif
+} texture_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;
+#ifdef RENDER3D
+	int		lightlevel;
+	float		v1[2], v2[2];		/* The vertices (v1 is the left one). */
+	float		secfloor, secceil;
+#else
+	lighttable_t	*colormap;
+#endif
+	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;
+
+#pragma pack off
+
+/*============================================================================*/
+
+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;
+
+#ifdef RENDER3D
+extern	float		viewpitch;
+extern	int		sbarscale;
+#endif
+
+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	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_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 (const 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/r_main.c
@@ -1,0 +1,834 @@
+// R_main.c
+
+#include "h2stdinc.h"
+
+#ifndef RENDER3D
+
+#include "doomdef.h"
+#include "r_local.h"
+
+int			viewangleoffset;
+
+#if defined(__WATCOMC__) && defined(_DOS)
+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 H2MININT to 0 to H2MAXINT.
+// 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];
+
+static 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;
+	int	t;	/* int32_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, start_map;
+	int		scale;
+
+//
+// Calculate the light levels to use for each level / distance combination
+//
+	for (i = 0; i < LIGHTLEVELS; i++)
+	{
+		start_map = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS;
+		for (j = 0; j < MAXLIGHTZ; j++)
+		{
+			scale = FixedDiv ((SCREENWIDTH/2*FRACUNIT), (j + 1)<<LIGHTZSHIFT);
+			scale >>= LIGHTSCALESHIFT;
+			level = start_map - 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.
+=
+==============
+*/
+
+static int	setblocks, setdetail;
+boolean		setsizeneeded;
+
+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, start_map;
+
+	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++)
+	{
+		start_map = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS;
+		for (j = 0; j < MAXLIGHTSCALE; j++)
+		{
+			level = start_map - 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();
+	tprintf("R_InitPointToAngle\n",0);
+	R_InitPointToAngle();
+	tprintf("R_InitTables ",0);
+	R_InitTables();
+	// viewwidth / viewheight / detailLevel are set by the defaults
+	R_SetViewSize(screenblocks, detailLevel);
+	tprintf("R_InitPlanes\n",0);
+	R_InitPlanes();
+	tprintf("R_InitLightTables ",0);
+	R_InitLightTables();
+	tprintf("R_InitSkyMap\n",0);
+	R_InitSkyMap();
+	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;
+
+	viewplayer = player;
+#if defined(__WATCOMC__) && defined(_DOS)
+	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;
+	}
+
+#if defined(__WATCOMC__) && defined(_DOS)
+	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
+}
+
+#endif	/* RENDER3D */
+
--- /dev/null
+++ b/r_plane.c
@@ -1,0 +1,450 @@
+// R_planes.c
+
+#include "h2stdinc.h"
+
+#ifndef RENDER3D
+
+#include "doomdef.h"
+#include "r_local.h"
+
+// MACROS ------------------------------------------------------------------
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+extern byte		*ylookup[MAXHEIGHT];
+extern int		columnofs[MAXWIDTH];
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+// Sky mapping
+int			skytexture;
+int			skyflatnum;
+int			skytexturemid;
+fixed_t			skyiscale;
+
+planefunction_t		floorfunc, ceilingfunc;
+
+// 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];
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+// CODE --------------------------------------------------------------------
+
+//==========================================================================
+//
+// 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
+//
+// Globals used: planeheight, ds_source, basexscale, baseyscale,
+// viewx, viewy.
+//
+//==========================================================================
+
+void R_MapPlane(int y, int x1, int x2)
+{
+	angle_t angle;
+	fixed_t distance, length;
+	unsigned idx;
+
+#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
+	{
+		idx = distance >> LIGHTZSHIFT;
+		if (idx >= MAXLIGHTZ)
+		{
+			idx = MAXLIGHTZ-1;
+		}
+		ds_colormap = planezlight[idx];
+	}
+
+	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;
+
+#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;
+		}
+		if (pl->picnum == skyflatnum)
+		{ // Sky flat
+			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 = (byte *) 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);
+	}
+}
+
+#endif	/* !RENDER3D */
+
--- /dev/null
+++ b/r_segs.c
@@ -1,0 +1,636 @@
+//**************************************************************************
+//**
+//** R_SEGS.C
+//**
+//** This version has the tall-sector-crossing-precision-bug fixed.
+//**
+//**************************************************************************
+
+#include "h2stdinc.h"
+
+#ifndef RENDER3D
+
+#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	idx;
+	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] != H2MAXSHORT)
+		{
+			if (!fixedcolormap)
+			{
+				idx = spryscale>>LIGHTSCALESHIFT;
+				if (idx >=  MAXLIGHTSCALE)
+					idx = MAXLIGHTSCALE-1;
+				dc_colormap = walllights[idx];
+			}
+
+			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] = H2MAXSHORT;
+		}
+		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	idx;
+	int		yl, yh, mid;
+	fixed_t		texturecolumn = 0;
+	int		top, bottom;
+
+	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
+			idx = rw_scale>>LIGHTSCALESHIFT;
+			if (idx >=  MAXLIGHTSCALE)
+				idx = MAXLIGHTSCALE - 1;
+			dc_colormap = walllights[idx];
+			dc_x = rw_x;
+			dc_iscale = 0xffffffffu / (unsigned)rw_scale;
+		}
+
+//
+// draw the wall tiers
+//
+		if (midtexture)
+		{	// single sided line
+			dc_yl = yl;
+			dc_yh = yh;
+			dc_texturemid = rw_midtexturemid;
+			dc_source = R_GetColumn(midtexture, texturecolumn);
+			colfunc ();
+			ceilingclip[rw_x] = viewheight;
+			floorclip[rw_x] = -1;
+		}
+		else
+		{	// two sided line
+			if (toptexture)
+			{	// top wall
+				mid = pixhigh>>HEIGHTBITS;
+				pixhigh += pixhighstep;
+				if (mid >= floorclip[rw_x])
+					mid = floorclip[rw_x] - 1;
+				if (mid >= yl)
+				{
+					dc_yl = yl;
+					dc_yh = mid;
+					dc_texturemid = rw_toptexturemid;
+					dc_source = R_GetColumn(toptexture, texturecolumn);
+					colfunc ();
+					ceilingclip[rw_x] = mid;
+				}
+				else
+					ceilingclip[rw_x] = yl - 1;
+			}
+			else
+			{	// no top wall
+				if (markceiling)
+					ceilingclip[rw_x] = yl - 1;
+			}
+
+			if (bottomtexture)
+			{	// bottom wall
+				mid = (pixlow+HEIGHTUNIT-1)>>HEIGHTBITS;
+				pixlow += pixlowstep;
+				if (mid <= ceilingclip[rw_x])
+					mid = ceilingclip[rw_x] + 1;	// no space above wall
+				if (mid <= yh)
+				{
+					dc_yl = mid;
+					dc_yh = yh;
+					dc_texturemid = rw_bottomtexturemid;
+					dc_source = R_GetColumn(bottomtexture, texturecolumn);
+					colfunc ();
+					floorclip[rw_x] = mid;
+				}
+				else
+					floorclip[rw_x] = yh + 1;
+			}
+			else
+			{	// no bottom wall
+				if (markfloor)
+					floorclip[rw_x] = yh + 1;
+			}
+
+			if (maskedtexture)
+			{	// save texturecol for backdrawing of masked mid texture
+				maskedtexturecol[rw_x] = texturecolumn;
+			}
+		}
+
+		rw_scale += rw_scalestep;
+		topfrac += topstep;
+		bottomfrac += bottomstep;
+	}
+}
+
+
+/*
+=====================
+=
+= R_StoreWallRange
+=
+= A wall segment will be drawn between start and stop pixels (inclusive)
+=
+======================
+*/
+
+void R_StoreWallRange (int start, int stop)
+{
+	fixed_t		hyp;
+	fixed_t		sineval;
+	angle_t		distangle, offsetangle;
+	fixed_t		vtop;
+	int		lightnum;
+
+	if (ds_p == &drawsegs[MAXDRAWSEGS])
+		return;		// don't overflow and crash
+
+#ifdef RANGECHECK
+	if (start >=viewwidth || start > stop)
+		I_Error ("Bad R_RenderWallRange: %i to %i", start , stop);
+#endif
+
+	sidedef = curline->sidedef;
+	linedef = curline->linedef;
+
+// mark the segment as visible for auto map
+	linedef->flags |= ML_MAPPED;
+
+//
+// calculate rw_distance for scale calculation
+//
+	rw_normalangle = curline->angle + ANG90;
+	offsetangle = abs(rw_normalangle - rw_angle1);
+	if (offsetangle > ANG90)
+		offsetangle = ANG90;
+	distangle = ANG90 - offsetangle;
+	hyp = R_PointToDist (curline->v1->x, curline->v1->y);
+	sineval = finesine[distangle>>ANGLETOFINESHIFT];
+	rw_distance = FixedMul (hyp, sineval);
+
+	ds_p->x1 = rw_x = start;
+	ds_p->x2 = stop;
+	ds_p->curline = curline;
+	rw_stopx = stop + 1;
+
+//
+// calculate scale at both ends and step
+//
+	ds_p->scale1 = rw_scale =
+		R_ScaleFromGlobalAngle (viewangle + xtoviewangle[start]);
+	if (stop > start)
+	{
+		ds_p->scale2 = R_ScaleFromGlobalAngle (viewangle + xtoviewangle[stop]);
+		ds_p->scalestep = rw_scalestep =
+			(ds_p->scale2 - rw_scale) / (stop-start);
+	}
+	else
+	{
+	//
+	// try to fix the stretched line bug
+	//
+#if 0
+		if (rw_distance < FRACUNIT/2)
+		{
+			fixed_t		xtr, ytr;
+			fixed_t		gxt, gyt;
+
+			xtr = curline->v1->x - viewx;
+			ytr = curline->v1->y - viewy;
+
+			gxt = FixedMul(xtr, viewcos);
+			gyt = -FixedMul(ytr, 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 = H2MAXINT;
+		ds_p->tsilheight = H2MININT;
+	}
+	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 = H2MAXINT;
+//			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 = H2MININT;
+//			ds_p->sprtopclip = screenheightarray;
+		}
+
+		if (backsector->ceilingheight <= frontsector->floorheight)
+		{
+			ds_p->sprbottomclip = negonearray;
+			ds_p->bsilheight = H2MAXINT;
+			ds_p->silhouette |= SIL_BOTTOM;
+		}
+		if (backsector->floorheight >= frontsector->ceilingheight)
+		{
+			ds_p->sprtopclip = screenheightarray;
+			ds_p->tsilheight = H2MININT;
+			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 = H2MININT;
+	}
+	if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM))
+	{
+		ds_p->silhouette |= SIL_BOTTOM;
+		ds_p->bsilheight = H2MAXINT;
+	}
+	ds_p++;
+}
+
+#endif	/* !RENDER3D */
+
--- /dev/null
+++ b/r_things.c
@@ -1,0 +1,1163 @@
+// R_things.c
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "r_local.h"
+#ifdef RENDER3D
+#include "ogl_def.h"
+#endif
+
+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;
+
+// constant arrays used for psprite clipping and initializing clipping
+#ifndef RENDER3D
+short			negonearray[SCREENWIDTH];
+short			screenheightarray[SCREENWIDTH];
+#endif
+
+// variables used to look up and range check thing_t sprites patches
+spritedef_t		*sprites;
+int			numsprites;
+
+#ifndef RENDER3D
+static lighttable_t	**spritelights;
+#endif
+
+static spriteframe_t	sprtemp[26];
+static int		maxframe;
+static const char	*spritename;
+
+
+/*
+===============================================================================
+
+						INITIALIZATION FUNCTIONS
+
+===============================================================================
+*/
+
+/*
+=================
+=
+= 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 (const char **namelist)
+{
+	const char	**check;
+	int		i, l, frame, rotation;
+	int		start, end;
+
+// count the number of sprite names
+	check = namelist;
+	while (*check != NULL)
+		check++;
+	numsprites = check - namelist;
+
+	if (!numsprites)
+		return;
+
+	sprites = (spritedef_t *) 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;
+
+		//
+		// scan the lumps, filling in the frames for whatever is found
+		//
+		for (l = start + 1; l < end; l++)
+		{
+			if (memcmp(lumpinfo[l].name, namelist[i], 4) == 0)
+			{
+				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 =
+			(spriteframe_t *) 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;
+
+/*
+===================
+=
+= R_InitSprites
+=
+= Called at program start
+===================
+*/
+
+void R_InitSprites (const char **namelist)
+{
+#ifndef RENDER3D
+	int		i;
+
+	for (i = 0; i < SCREENWIDTH; i++)
+	{
+		negonearray[i] = -1;
+	}
+#endif
+	R_InitSpriteDefs (namelist);
+}
+
+
+/*
+===================
+=
+= R_ClearSprites
+=
+= Called at frame start
+===================
+*/
+
+void R_ClearSprites (void)
+{
+	vissprite_p = vissprites;
+}
+
+
+/*
+===================
+=
+= R_NewVisSprite
+=
+===================
+*/
+
+static vissprite_t	overflowsprite;
+
+static vissprite_t *R_NewVisSprite (void)
+{
+	if (vissprite_p == &vissprites[MAXVISSPRITES])
+		return &overflowsprite;
+	vissprite_p++;
+	return vissprite_p - 1;
+}
+
+
+#ifndef RENDER3D
+/*
+================
+=
+= 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 = (patch_t *) 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(SHORT(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;
+}
+#endif	/* RENDER3D */
+
+
+/*
+===================
+=
+= R_ProjectSprite
+=
+= Generates a vissprite for a thing if it might be visible
+=
+===================
+*/
+
+void R_ProjectSprite (mobj_t *thing)
+{
+	fixed_t		xtr, ytr;
+	fixed_t		gxt, gyt;
+	fixed_t		tz;
+	fixed_t		xscale;
+	int		x1 = 0, x2 = 0;
+	spritedef_t	*sprdef;
+	spriteframe_t	*sprframe;
+	int		lump;
+	unsigned int	rot;
+	boolean		flip;
+#ifndef RENDER3D
+	fixed_t		tx;
+	int		idx;
+#endif
+	vissprite_t	*vis;
+	angle_t		ang;
+	fixed_t		iscale;
+#ifdef RENDER3D
+	float		v1[2], v2[2];
+	float		sinrv, cosrv, thangle;	// rv = real value
+#endif
+
+	if (thing->flags2 & MF2_DONTDRAW)
+	{ // Never make a vissprite when MF2_DONTDRAW is flagged.
+		return;
+	}
+
+//
+// transform the origin point
+//
+	xtr = thing->x - viewx;
+	ytr = thing->y - viewy;
+
+	gxt = FixedMul(xtr,viewcos);
+	gyt = -FixedMul(ytr,viewsin);
+	tz = gxt - gyt;
+
+#ifdef RENDER3D
+	if (tz < 0)
+		tz = -tz;	// Make it positive. The clipper will handle backside.
+	if (tz < FRACUNIT)
+		tz = FRACUNIT;
+#else
+	if (tz < MINZ)
+		return;		// thing is behind view plane
+#endif
+	xscale = FixedDiv(projection, tz);
+
+#ifndef RENDER3D
+	gxt = -FixedMul(xtr,viewsin);
+	gyt = FixedMul(ytr,viewcos);
+	tx = -(gyt + gxt);
+
+	if (abs(tx) > (tz<<2))
+		return;		// too far off the side
+#endif
+
+//
+// decide which patch to use for sprite reletive to player
+//
+#ifdef RANGECHECK
+	if ((unsigned int)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 int)(ANG45/2)*9) >> 29;
+		lump = sprframe->lump[rot];
+		flip = !!(sprframe->flip[rot]);
+	}
+	else
+	{	// use single rotation for all views
+		lump = sprframe->lump[0];
+		flip = !!(sprframe->flip[0]);
+	}
+
+//
+// calculate edges of the shape
+//
+#ifdef RENDER3D
+	v1[VX] = FIX2FLT(thing->x);
+	v1[VY] = FIX2FLT(thing->y);
+//	thangle = BANG2RAD(bamsAtan2((v1[VY]-FIX2FLT(viewy))*10, (v1[VX]-FIX2FLT(viewx))*10)) - PI/2;
+	thangle = BANG2RAD(bamsAtan2(FIX2FLT(ytr)*10, FIX2FLT(xtr)*10)) - PI/2;
+	sinrv = sin(thangle);
+	cosrv = cos(thangle);
+	v1[VX] -= cosrv*(spriteoffset[lump]>>FRACBITS);
+	v1[VY] -= sinrv*(spriteoffset[lump]>>FRACBITS);
+	v2[VX] = v1[VX] + cosrv*(spritewidth[lump]>>FRACBITS);
+	v2[VY] = v1[VY] + sinrv*(spritewidth[lump]>>FRACBITS);
+ 	// Check for visibility.
+	if (!C_CheckViewRelSeg(v1[VX], v1[VY], v2[VX], v2[VY]))
+	//if (!C_IsAngleVisible(RAD2BANG(thangle+PI/2)))
+		return;		// Isn't visible.
+#else
+	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
+#endif
+
+//
+// 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];
+
+#ifdef RENDER3D
+	vis->secfloor = FIX2FLT(thing->subsector->sector->floorheight);
+	vis->secceil  = FIX2FLT(thing->subsector->sector->ceilingheight);
+#endif
+
+	// 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);
+
+#ifdef RENDER3D
+	// The start and end vertices.
+	vis->v1[VX] = v1[VX];
+	vis->v1[VY] = v1[VY];
+	vis->v2[VX] = v2[VX];
+	vis->v2[VY] = v2[VY];
+#endif
+
+	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 ...
+
+#ifdef RENDER3D
+	if (thing->frame & FF_FULLBRIGHT)
+	{	// full bright
+		vis->lightlevel = -1;
+	}
+	else
+	{	// diminished light
+		vis->lightlevel = thing->subsector->sector->lightlevel;
+	}
+#else
+	if (fixedcolormap)
+		vis->colormap = fixedcolormap;	// fixed map
+	else if (thing->frame & FF_FULLBRIGHT)
+		vis->colormap = colormaps;	// full bright
+	else
+	{	// diminished light
+		idx = xscale>>(LIGHTSCALESHIFT - detailshift);
+		if (idx >= MAXLIGHTSCALE)
+			idx = MAXLIGHTSCALE-1;
+		vis->colormap = spritelights[idx];
+	}
+#endif
+}
+
+
+/*
+========================
+=
+= R_AddSprites
+=
+========================
+*/
+
+void R_AddSprites (sector_t *sec)
+{
+	if (sec->validcount == validcount)
+	{
+		return;		// already added
+	}
+	else
+	{
+		mobj_t		*thing;
+#ifndef RENDER3D
+		int		lightnum;
+
+		lightnum = (sec->lightlevel >> LIGHTSEGSHIFT) + extralight;
+		if (lightnum < 0)
+			spritelights = scalelight[0];
+		else if (lightnum >= LIGHTLEVELS)
+			spritelights = scalelight[LIGHTLEVELS-1];
+		else
+			spritelights = scalelight[lightnum];
+#endif
+
+		sec->validcount = validcount;
+
+		for (thing = sec->thinglist ; thing ; thing = thing->snext)
+			R_ProjectSprite (thing);
+	}
+}
+
+
+/*
+========================
+=
+= R_DrawPSprite
+=
+========================
+*/
+
+static 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;
+#ifndef RENDER3D
+	int		x2;
+#endif
+	spritedef_t	*sprdef;
+	spriteframe_t	*sprframe;
+	int		lump;
+	boolean		flip;
+#ifdef RENDER3D
+	float		light, alpha;
+	int		y;
+#else
+	vissprite_t	*vis, avis;
+#endif
+	int		tempangle;
+
+//
+// decide which patch to use
+//
+#ifdef RANGECHECK
+	if ( (unsigned int)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 = !!(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;
+
+#ifdef RENDER3D
+	// Set the OpenGL color & alpha.
+	light = 1;
+	alpha = 1;
+	//vis->colormap = spritelights[MAXLIGHTSCALE-1];
+	if (viewplayer->powers[pw_invulnerability] > 4*32)
+	{
+		if (viewplayer->mo->flags2 & MF2_DONTDRAW)
+		{ // don't draw the psprite
+		//	vis->mobjflags |= MF_SHADOW;
+			alpha = .333f;
+		}
+		else if (viewplayer->mo->flags & MF_SHADOW)
+		{
+		//	vis->mobjflags |= MF_ALTSHADOW;
+			alpha = .666f;
+		}
+	}
+	else if (viewplayer->powers[pw_invulnerability] & 8)
+	{
+	//	vis->mobjflags |= MF_SHADOW;
+		alpha = .333f;
+	}
+	if (fixedcolormap)
+	{
+	// Fixed color
+	//	vis->colormap = fixedcolormap;
+		light = 1;
+	}
+	else if (psp->state->frame & FF_FULLBRIGHT)
+	{
+	// Full bright
+	//	vis->colormap = colormaps;
+		light = 1;
+	}
+	else
+	{
+	// local light
+	//	vis->colormap = spritelights[MAXLIGHTSCALE-1];
+		light = viewplayer->mo->subsector->sector->lightlevel / 255.0;
+	}
+
+//
+// do some OpenGL rendering, oh yeah
+//
+	y = -(spritetopoffset[lump]>>FRACBITS) + (psp->sy>>FRACBITS);
+	if (viewheight == SCREENHEIGHT)
+	{
+		y += PSpriteSY[players[consoleplayer].readyweapon] >> FRACBITS;
+	}
+	else
+		y -= 39/2;
+ 
+	light += .1f;	// Add some extra light.
+	OGL_SetColorAndAlpha(light, light, light, alpha);
+	OGL_DrawPSprite(x1, y, 1, flip, lump);
+
+#else
+
+	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);
+#endif
+}
+
+/*
+========================
+=
+= R_DrawPlayerSprites
+=
+========================
+*/
+
+void R_DrawPlayerSprites (void)
+{
+	int		i;
+	pspdef_t	*psp;
+#ifndef RENDER3D
+	int		lightnum;
+
+//
+// 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;
+#endif
+
+//
+// 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 = H2MAXINT;
+		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;
+	}
+}
+
+
+#ifndef RENDER3D
+/*
+========================
+=
+= 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);
+}
+#endif	/* !RENDER3D */
+
+
+/*
+========================
+=
+= R_DrawMasked
+=
+========================
+*/
+
+void R_DrawMasked (void)
+{
+	vissprite_t		*spr;
+
+#ifdef RENDER3D
+	extern boolean willRenderSprites;
+
+	if (!willRenderSprites)
+		return;
+
+	R_SortVisSprites();
+
+	if (vissprite_p > vissprites)
+	{
+	// draw all vissprites back to front
+		glDepthMask(GL_FALSE);
+
+		for (spr = vsprsortedhead.next; spr != &vsprsortedhead;
+							spr = spr->next)
+		{
+			R_RenderSprite(spr);
+		}
+
+		glDepthMask(GL_TRUE);
+	}
+#else
+	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 ();
+#endif
+}
+
--- /dev/null
+++ b/s_sound.c
@@ -1,0 +1,947 @@
+//**************************************************************************
+//**
+//** S_SOUND.C:  MUSIC & SFX API
+//**
+//**************************************************************************
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"	/* P_AproxDistance() */
+#include "sounds.h"
+#include "i_sound.h"
+#include "soundst.h"
+
+// MACROS ------------------------------------------------------------------
+
+#define DEFAULT_ARCHIVEPATH	"o:\\sound\\archive\\"
+#define PRIORITY_MAX_ADJUST	10
+#define DIST_ADJUST	(MAX_SND_DIST/PRIORITY_MAX_ADJUST)
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+static boolean S_StopSoundID(int sound_id, int priority);
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+extern int	snd_MaxVolume;
+extern int	snd_MusicVolume;
+extern int	snd_Channels;
+
+#if defined(__WATCOMC__) && defined(_DOS)
+extern int	snd_SfxDevice;
+extern int	snd_MusicDevice;
+extern int	snd_DesiredSfxDevice;
+extern int	snd_DesiredMusicDevice;
+extern int	tsm_ID;
+#endif
+
+extern void	**lumpcache;
+
+extern int	startepisode;
+extern int	startmap;
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+/* 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 }
+};
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+static channel_t Channel[MAX_CHANNELS];
+static int	AmbChan;
+static int	RegisteredSong;	/* the current registered song. */
+static int	isExternalSong;
+static int	NextCleanup;
+static boolean	MusicPaused;
+static int	Mus_Song = -1;
+static int	Mus_LumpNum;
+static void	*Mus_SndPtr;
+static byte	*SoundCurve;
+
+// CODE --------------------------------------------------------------------
+
+//==========================================================================
+//
+// S_Init
+//
+//==========================================================================
+
+void S_Init(void)
+{
+	SoundCurve = (byte *) 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);
+}
+
+//==========================================================================
+//
+// S_ShutDown
+//
+//==========================================================================
+
+void S_ShutDown(void)
+{
+#if defined(__WATCOMC__) && defined(_DOS)
+	if (tsm_ID == -1)
+		return;
+#endif
+	if (RegisteredSong)
+	{
+		I_StopSong(RegisteredSong);
+		I_UnRegisterSong(RegisteredSong);
+	}
+	I_ShutdownSound();
+}
+
+//==========================================================================
+//
+// S_Start
+//
+//==========================================================================
+
+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));
+}
+
+//==========================================================================
+//
+// S_StartSong
+//
+//==========================================================================
+
+void S_StartSong(int song, boolean loop)
+{
+	int length;
+	if (song == Mus_Song)
+	{ // don't replay an old song
+		return;
+	}
+	if (RegisteredSong)
+	{
+		I_StopSong(RegisteredSong);
+		I_UnRegisterSong(RegisteredSong);
+		if (!isExternalSong)
+		{
+			Z_ChangeTag(lumpcache[Mus_LumpNum], PU_CACHE);
+#if defined(__WATCOMC__) && defined(_DOS)
+			_dpmi_unlockregion(Mus_SndPtr, lumpinfo[Mus_LumpNum].size);
+#endif
+		}
+	}
+	if (song < mus_e1m1 || song >= NUMMUSIC)
+	{
+		return;
+	}
+	isExternalSong = I_RegisterExternalSong(S_music[song].name);
+	if (isExternalSong)
+	{
+		RegisteredSong = isExternalSong;
+		I_PlaySong(RegisteredSong, loop);
+		Mus_Song = song;
+		return;
+	}
+	Mus_LumpNum = W_GetNumForName(S_music[song].name);
+	length = W_LumpLength(Mus_LumpNum);
+	Mus_SndPtr = W_CacheLumpNum(Mus_LumpNum, PU_MUSIC);
+#if defined(__WATCOMC__) && defined(_DOS)
+	_dpmi_lockregion(Mus_SndPtr, lumpinfo[Mus_LumpNum].size);
+#endif
+	RegisteredSong = I_RegisterSong(Mus_SndPtr, length);
+	I_PlaySong(RegisteredSong, loop); // 'true' denotes endless looping.
+	Mus_Song = song;
+}
+
+//==========================================================================
+//
+// S_StartSound
+//
+//==========================================================================
+
+void S_StartSound(mobj_t *origin, int sound_id)
+{
+	static int sndcount = 0;
+
+	int i;
+	int dist, vol, chan;
+	int priority;
+	int angle, sep;
+	int absx, absy;
+
+	if (sound_id == 0 || snd_MaxVolume == 0)
+		return;
+#if 0
+	if (origin == NULL)
+	{
+		origin = players[consoleplayer].mo;
+	// this can be uninitialized when we are newly
+	// started before the demos start playing !...
+	}
+#endif
+
+	// calculate the distance before other stuff so that we can throw out
+	// sounds that are beyond the hearing range.
+	if (origin)
+	{
+		absx = abs(origin->x - players[consoleplayer].mo->x);
+		absy = abs(origin->y - players[consoleplayer].mo->y);
+	}
+	else
+	{
+		absx = absy = 0;
+	}
+	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 || 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)
+	{
+		if (W_LumpLength(S_sfx[sound_id].lumpnum) <= 8)
+		{
+			char name[9];
+			strncpy(name, S_sfx[sound_id].name, 8);
+			name[8] = '\0';
+		//	I_Error("broken sound lump #%d (%s)\n",
+			fprintf(stderr, "broken sound lump #%d (%s)\n",
+					S_sfx[sound_id].lumpnum, name);
+			return;
+		}
+		S_sfx[sound_id].snd_ptr =
+			W_CacheLumpNum(S_sfx[sound_id].lumpnum, PU_SOUND);
+#if defined(__WATCOMC__) && defined(_DOS)
+		_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 || origin == players[consoleplayer].mo)
+	{
+		sep = 128;
+	}
+	else
+	{
+		angle = R_PointToAngle2(players[consoleplayer].mo->x,
+					players[consoleplayer].mo->y,
+					origin->x, origin->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++;
+	}
+}
+
+//==========================================================================
+//
+// S_StartSoundAtVolume
+//
+//==========================================================================
+void S_StartSoundAtVolume(mobj_t *origin, int sound_id, int volume)
+{
+	int i;
+
+	if (sound_id == 0 || snd_MaxVolume == 0)
+		return;
+	if (origin == NULL)
+	{
+		origin = players[displayplayer].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)
+	{
+		if (W_LumpLength(S_sfx[sound_id].lumpnum) <= 8)
+		{
+			char name[9];
+			strncpy(name, S_sfx[sound_id].name, 8);
+			name[8] = '\0';
+		//	I_Error("broken sound lump #%d (%s)\n",
+			fprintf(stderr, "broken sound lump #%d (%s)\n",
+					S_sfx[sound_id].lumpnum, name);
+			return;
+		}
+		S_sfx[sound_id].snd_ptr =
+			W_CacheLumpNum(S_sfx[sound_id].lumpnum, PU_SOUND);
+#if defined(__WATCOMC__) && defined(_DOS)
+		_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++;
+	}
+}
+
+//==========================================================================
+//
+// 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;
+			if (AmbChan == i)
+			{
+				AmbChan = -1;
+			}
+		}
+	}
+}
+
+//==========================================================================
+//
+// 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)
+{
+	I_PauseSong(RegisteredSong);
+}
+
+//==========================================================================
+//
+// S_ResumeSound
+//
+//==========================================================================
+
+void S_ResumeSound(void)
+{
+	I_ResumeSong(RegisteredSong);
+}
+
+//==========================================================================
+//
+// S_UpdateSounds
+//
+//==========================================================================
+
+void S_UpdateSounds(mobj_t *listener)
+{
+	int i, dist, vol;
+	int angle, sep;
+	int priority;
+	int absx, 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 == ZONEID)
+					{ // taken directly from the Z_ChangeTag macro
+						Z_ChangeTag2(lumpcache[S_sfx[i].lumpnum], PU_CACHE);
+#if defined(__WATCOMC__) && defined(_DOS)
+						_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;
+			}
+		}
+		// Note, heretic does this every second (gametic+35)
+		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 (AmbChan == i)
+			{
+				AmbChan = -1;
+			}
+		}
+		if (Channel[i].mo == NULL || Channel[i].sound_id == 0
+			|| Channel[i].mo == listener)
+		{
+			continue;
+		}
+		else
+		{
+			absx = abs(Channel[i].mo->x - listener->x);
+			absy = abs(Channel[i].mo->y - listener->y);
+			dist = absx+absy-(absx > absy ? absy>>1 : absx>>1);
+			dist >>= FRACBITS;
+
+			if (dist >= MAX_SND_DIST)
+			{
+				S_StopSound(Channel[i].mo);
+				continue;
+			}
+			if (dist < 0)
+			{
+				dist = 0;
+			}
+			vol = SoundCurve[dist];
+			angle = R_PointToAngle2(listener->x, listener->y,
+						Channel[i].mo->x, Channel[i].mo->y);
+			angle = (angle - viewangle)>>24;
+			sep = angle*2-128;
+			if(sep < 64)
+				sep = -sep;
+			if(sep > 192)
+				sep = 512-sep;
+			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_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].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;
+	byte *SC = (byte *)W_CacheLumpName("SNDCURVE", PU_CACHE);
+	if (!fullprocess)
+	{
+		SoundCurve[0] = ( *(SC) * (snd_MaxVolume * 8))>>7;
+	}
+	else
+	{
+		for (i = 0; i < MAX_SND_DIST; i++)
+		{
+			SoundCurve[i] = ( *(SC + i) * (snd_MaxVolume * 8))>>7;
+		}
+	}
+}
+
+//==========================================================================
+//
+// S_SetMusicVolume
+//
+//==========================================================================
+
+void S_SetMusicVolume(void)
+{
+	I_SetMusicVolume(snd_MusicVolume);
+	if (snd_MusicVolume == 0)
+	{
+		I_PauseSong(RegisteredSong);
+		MusicPaused = true;
+	}
+	else if (MusicPaused)
+	{
+		MusicPaused = false;
+		I_ResumeSong(RegisteredSong);
+	}
+}
+
--- /dev/null
+++ b/sb_bar.c
@@ -1,0 +1,1505 @@
+// SB_bar.c
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+
+#define PLAYPAL_NUM		playpalette
+#include "v_compat.h"
+
+// MACROS ------------------------------------------------------------------
+
+#ifdef RENDER3D
+#define V_DrawPatch(x,y,p)		OGL_DrawPatch((x),(y),(p))
+#define V_DrawFuzzPatch(x,y,p)		OGL_DrawFuzzPatch((x),(y),(p))
+#define V_DrawAltFuzzPatch(x,y,p)	OGL_DrawAltFuzzPatch((x),(y),(p))
+#endif
+
+#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;
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+static void DrawSoundInfo(void);
+#ifndef RENDER3D
+static void ShadeLine(int x, int y, int height, int shade);
+#endif
+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);
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+extern byte *screens;
+
+// PUBLIC DATA DECLARATIONS ------------------------------------------------
+
+boolean DebugSound;	/* Debug flag for displaying sound info */
+
+boolean inventory;
+int curpos;
+int inv_ptr;
+int ArtifactFlash;
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+static int HealthMarker;
+static int ChainWiggle;
+static player_t *CPlayer;
+static int FontBNumBase;
+static int playpalette;
+static int spinbooklump;
+static int spinflylump;
+
+static PATCH_REF PatchLTFACE;
+static PATCH_REF PatchRTFACE;
+static PATCH_REF PatchBARBACK;
+static PATCH_REF PatchCHAIN;
+static PATCH_REF PatchSTATBAR;
+static PATCH_REF PatchLIFEGEM;
+static PATCH_REF PatchLTFCTOP;
+static PATCH_REF PatchRTFCTOP;
+static PATCH_REF PatchSELECTBOX;
+static PATCH_REF PatchINumbers[10];
+static PATCH_REF PatchNEGATIVE;
+static PATCH_REF PatchSmNumbers[10];
+static PATCH_REF PatchBLACKSQ;
+static PATCH_REF PatchINVBAR;
+static PATCH_REF PatchARMCLEAR;
+static PATCH_REF PatchCHAINBACK;
+static PATCH_REF PatchINVLFGEM1;
+static PATCH_REF PatchINVLFGEM2;
+static PATCH_REF PatchINVRTGEM1;
+static PATCH_REF PatchINVRTGEM2;
+
+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
+};
+
+// CODE --------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+//
+// PROC SB_Init
+//
+//---------------------------------------------------------------------------
+
+void SB_Init(void)
+{
+	int i;
+	int startLump;
+
+	PatchLTFACE  = (PATCH_REF) WR_CacheLumpName("LTFACE", PU_STATIC);
+	PatchRTFACE  = (PATCH_REF) WR_CacheLumpName("RTFACE", PU_STATIC);
+	PatchBARBACK = (PATCH_REF) WR_CacheLumpName("BARBACK", PU_STATIC);
+	PatchINVBAR  = (PATCH_REF) WR_CacheLumpName("INVBAR", PU_STATIC);
+	PatchCHAIN   = (PATCH_REF) WR_CacheLumpName("CHAIN", PU_STATIC);
+	if (deathmatch)
+	{
+		PatchSTATBAR = (PATCH_REF) WR_CacheLumpName("STATBAR", PU_STATIC);
+	}
+	else
+	{
+		PatchSTATBAR = (PATCH_REF) WR_CacheLumpName("LIFEBAR", PU_STATIC);
+	}
+	if (!netgame)
+	{ // single player game uses red life gem
+	  PatchLIFEGEM = (PATCH_REF) WR_CacheLumpName("LIFEGEM2", PU_STATIC);
+	}
+	else
+	{
+	  PatchLIFEGEM = (PATCH_REF) WR_CacheLumpNum(W_GetNumForName("LIFEGEM0") + consoleplayer, PU_STATIC);
+	}
+	PatchLTFCTOP   = (PATCH_REF) WR_CacheLumpName("LTFCTOP", PU_STATIC);
+	PatchRTFCTOP   = (PATCH_REF) WR_CacheLumpName("RTFCTOP", PU_STATIC);
+	PatchSELECTBOX = (PATCH_REF) WR_CacheLumpName("SELECTBOX", PU_STATIC);
+	PatchINVLFGEM1 = (PATCH_REF) WR_CacheLumpName("INVGEML1", PU_STATIC);
+	PatchINVLFGEM2 = (PATCH_REF) WR_CacheLumpName("INVGEML2", PU_STATIC);
+	PatchINVRTGEM1 = (PATCH_REF) WR_CacheLumpName("INVGEMR1", PU_STATIC);
+	PatchINVRTGEM2 = (PATCH_REF) WR_CacheLumpName("INVGEMR2", PU_STATIC);
+	PatchBLACKSQ   = (PATCH_REF) WR_CacheLumpName("BLACKSQ", PU_STATIC);
+	PatchARMCLEAR  = (PATCH_REF) WR_CacheLumpName("ARMCLEAR", PU_STATIC);
+	PatchCHAINBACK = (PATCH_REF) WR_CacheLumpName("CHAINBACK", PU_STATIC);
+	startLump = W_GetNumForName("IN0");
+	for (i = 0; i < 10; i++)
+	{
+		PatchINumbers[i] = (PATCH_REF) WR_CacheLumpNum(startLump + i, PU_STATIC);
+	}
+	PatchNEGATIVE = (PATCH_REF) WR_CacheLumpName("NEGNUM", PU_STATIC);
+	FontBNumBase = W_GetNumForName("FONTB16");
+	startLump = W_GetNumForName("SMALLIN0");
+	for (i = 0; i < 10; i++)
+	{
+		PatchSmNumbers[i] = (PATCH_REF) WR_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_REF patch;
+	int oldval;
+
+	oldval = val;
+	if (val < 0)
+	{
+		if (val < -9)
+		{
+			V_DrawPatch(x + 1, y + 1, (PATCH_REF)WR_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;
+	int width;
+
+	oldval = val;
+	xpos = x;
+	if (val < 0)
+	{
+		val = 0;
+	}
+	if (val > 99)
+	{
+		patch = (patch_t *) W_CacheLumpNum(FontBNumBase + val/100, PU_CACHE);
+		width = SHORT(patch->width) / 2;
+#ifdef RENDER3D
+		OGL_DrawShadowedPatch(xpos + 6 - width, y, FontBNumBase + val/100);
+#else
+		V_DrawShadowedPatch(xpos + 6 - width, y, patch);
+#endif
+	}
+	val = val % 100;
+	xpos += 12;
+	if (val > 9 || oldval > 99)
+	{
+		patch = (patch_t *) W_CacheLumpNum(FontBNumBase + val/10, PU_CACHE);
+		width = SHORT(patch->width) / 2;
+#ifdef RENDER3D
+		OGL_DrawShadowedPatch(xpos + 6 - width, y, FontBNumBase + val/10);
+#else
+		V_DrawShadowedPatch(xpos + 6 - width, y, patch);
+#endif
+	}
+	val = val % 10;
+	xpos += 12;
+	patch = (patch_t *) W_CacheLumpNum(FontBNumBase + val, PU_CACHE);
+	width = SHORT(patch->width) / 2;
+#ifdef RENDER3D
+	OGL_DrawShadowedPatch(xpos + 6 - width, y, FontBNumBase + val/1);
+#else
+	V_DrawShadowedPatch(xpos + 6 - width, y, patch);
+#endif
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrSmallNumber
+//
+// Draws a small two digit number.
+//
+//---------------------------------------------------------------------------
+
+static void DrSmallNumber(int val, int x, int y)
+{
+	PATCH_REF 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
+//
+//---------------------------------------------------------------------------
+
+#ifndef RENDER3D
+static void ShadeLine(int x, int y, int height, int shade)
+{
+	byte *dest;
+	byte *shades;
+
+	shades = colormaps + 9*256 + shade*2*256;
+	dest = screens + y*SCREENWIDTH + x;
+	while (height--)
+	{
+		*(dest) = *(shades + *dest);
+		dest += SCREENWIDTH;
+	}
+}
+#endif
+
+//---------------------------------------------------------------------------
+//
+// PROC ShadeChain
+//
+//---------------------------------------------------------------------------
+
+static void ShadeChain(void)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+	{
+#ifndef RENDER3D
+		ShadeLine(277+ i, 190, 10,      i/2 );
+		ShadeLine(19 + i, 190, 10, 7 - (i/2));
+#else
+		OGL_ShadeRect(277+ i, 190, 1, 10, (float)(9 + i     ) / 32.0f);
+		OGL_ShadeRect(19 + i, 190, 1, 10, (float)(9 + 15 - i) / 32.0f);
+#endif
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// 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;
+		}
+		snprintf(text, sizeof(text), "%s", c->name);
+		M_ForceUppercase(text);
+		MN_DrTextA(text, xPos[x++], y);
+		snprintf(text, sizeof(text), "%d", c->mo->type);
+		MN_DrTextA(text, xPos[x++], y);
+		snprintf(text, sizeof(text), "%d", c->mo->x>>FRACBITS);
+		MN_DrTextA(text, xPos[x++], y);
+		snprintf(text, sizeof(text), "%d", c->mo->y>>FRACBITS);
+		MN_DrTextA(text, xPos[x++], y);
+		snprintf(text, sizeof(text), "%d", c->id);
+		MN_DrTextA(text, xPos[x++], y);
+		snprintf(text, sizeof(text), "%d", c->priority);
+		MN_DrTextA(text, xPos[x++], y);
+		snprintf(text, sizeof(text), "%d", c->distance);
+		MN_DrTextA(text, xPos[x++], y);
+	}
+	UpdateState |= I_FULLSCRN;
+	BorderNeedRefresh = true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SB_Drawer
+//
+//---------------------------------------------------------------------------
+
+static const 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		*/
+};
+
+static const char ammopic[][10] =
+{
+	{ "INAMGLD" },
+	{ "INAMBOW" },
+	{ "INAMBST" },
+	{ "INAMRAM" },
+	{ "INAMPNX" },
+	{ "INAMLOB" }
+};
+
+int SB_state = -1;
+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 oldarti = 0;
+static int oldartiCount = 0;
+static int oldkeys = -1;
+
+int playerkeys = 0;
+
+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
+	{
+#ifndef RENDER3D
+		if (SB_state == -1)
+		{
+#endif
+			V_DrawPatch(0, 158, PatchBARBACK);
+			if (players[consoleplayer].cheats & CF_GODMODE)
+			{
+				V_DrawPatch(16, 167, (PATCH_REF)WR_CacheLumpName("GOD1", PU_CACHE));
+				V_DrawPatch(287, 167, (PATCH_REF)WR_CacheLumpName("GOD2", PU_CACHE));
+			}
+			oldhealth = -1;
+#ifndef RENDER3D
+		}
+#endif
+		DrawCommonBar();
+		if (!inventory)
+		{
+#ifndef RENDER3D
+			if (SB_state != 0)
+			{
+#endif
+				// 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;
+#ifndef RENDER3D
+			}
+#endif
+			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, (PATCH_REF)WR_CacheLumpNum(spinflylump + 15, PU_CACHE));
+				}
+				else
+				{
+					V_DrawPatch(20, 17, (PATCH_REF)WR_CacheLumpNum(spinflylump + frame, PU_CACHE));
+					hitCenterFrame = false;
+				}
+			}
+			else
+			{
+				if (!hitCenterFrame && (frame != 15 && frame != 0))
+				{
+					V_DrawPatch(20, 17, (PATCH_REF)WR_CacheLumpNum(spinflylump + frame, PU_CACHE));
+					hitCenterFrame = false;
+				}
+				else
+				{
+					V_DrawPatch(20, 17, (PATCH_REF)WR_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, (PATCH_REF)WR_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, (PATCH_REF)WR_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;
+
+	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;
+		V_SetPaletteShift(palette);
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawCommonBar
+//
+//---------------------------------------------------------------------------
+
+void DrawCommonBar(void)
+{
+	int chainY;
+	int healthPos;
+
+	V_DrawPatch(0, 148, PatchLTFCTOP);
+	V_DrawPatch(290, 148, PatchRTFCTOP);
+
+#ifndef RENDER3D
+	if (oldhealth != HealthMarker)
+	{
+		oldhealth = HealthMarker;
+#endif
+		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();
+#ifndef RENDER3D
+		UpdateState |= I_STATBAR;
+	}
+#endif
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawMainBar
+//
+//---------------------------------------------------------------------------
+
+void DrawMainBar(void)
+{
+	int i;
+	int temp;
+
+	// Ready artifact
+	if (ArtifactFlash)
+	{
+#ifndef RENDER3D
+		V_DrawPatch(180, 161, PatchBLACKSQ);
+#endif
+		V_DrawPatch(182, 161, (PATCH_REF)WR_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)
+	{
+#ifndef RENDER3D
+		V_DrawPatch(180, 161, PatchBLACKSQ);
+#endif
+		if (CPlayer->readyArtifact > 0)
+		{
+			V_DrawPatch(179,160, (PATCH_REF)WR_CacheLumpName(patcharti[CPlayer->readyArtifact], PU_CACHE));
+			DrSmallNumber(CPlayer->inventory[inv_ptr].count, 201, 182);
+		}
+		oldarti = CPlayer->readyArtifact;
+		oldartiCount = CPlayer->inventory[inv_ptr].count;
+#ifndef RENDER3D
+		UpdateState |= I_STATBAR;
+#endif
+	}
+
+	// Frags
+	if (deathmatch)
+	{
+		temp = 0;
+		for (i = 0; i < MAXPLAYERS; i++)
+		{
+			temp += CPlayer->frags[i];
+		}
+#ifndef RENDER3D
+		if (temp != oldfrags)
+		{
+#endif
+			V_DrawPatch(57, 171, PatchARMCLEAR);
+			DrINumber(temp, 61, 170);
+#ifndef RENDER3D
+			oldfrags = temp;
+			UpdateState |= I_STATBAR;
+		}
+#endif
+	}
+	else
+	{
+		temp = HealthMarker;
+		if (temp < 0)
+		{
+			temp = 0;
+		}
+		else if (temp > 100)
+		{
+			temp = 100;
+		}
+#ifndef RENDER3D
+		if (oldlife != temp)
+		{
+			oldlife = temp;
+#endif
+			V_DrawPatch(57, 171, PatchARMCLEAR);
+			DrINumber(temp, 61, 170);
+#ifndef RENDER3D
+			UpdateState |= I_STATBAR;
+		}
+#endif
+	}
+
+	// Keys
+#ifndef RENDER3D
+	if (oldkeys != playerkeys)
+	{
+#endif
+		if (CPlayer->keys[key_yellow])
+		{
+			V_DrawPatch(153, 164, (PATCH_REF)WR_CacheLumpName("ykeyicon", PU_CACHE));
+		}
+		if (CPlayer->keys[key_green])
+		{
+			V_DrawPatch(153, 172, (PATCH_REF)WR_CacheLumpName("gkeyicon", PU_CACHE));
+		}
+		if (CPlayer->keys[key_blue])
+		{
+			V_DrawPatch(153, 180, (PATCH_REF)WR_CacheLumpName("bkeyicon", PU_CACHE));
+		}
+#ifndef RENDER3D
+		oldkeys = playerkeys;
+		UpdateState |= I_STATBAR;
+	}
+#endif
+	// Ammo
+	temp = CPlayer->ammo[wpnlev1info[CPlayer->readyweapon].ammo];
+#ifndef RENDER3D
+	if (oldammo != temp || oldweapon != CPlayer->readyweapon)
+	{
+#endif
+		V_DrawPatch(108, 161, PatchBLACKSQ);
+		if (temp && CPlayer->readyweapon > 0 && CPlayer->readyweapon < 7)
+		{
+			DrINumber(temp, 109, 162);
+			V_DrawPatch(111, 172, (PATCH_REF)WR_CacheLumpName(
+				ammopic[CPlayer->readyweapon-1], PU_CACHE));
+		}
+#ifndef RENDER3D
+		oldammo = temp;
+		oldweapon = CPlayer->readyweapon;
+		UpdateState |= I_STATBAR;
+	}
+#endif
+
+	// 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, (PATCH_REF)WR_CacheLumpName("ARTIBOX", PU_CACHE));
+		if (CPlayer->inventorySlotNum > x + i
+			&& CPlayer->inventory[x + i].type != arti_none)
+		{
+			V_DrawPatch(50 + i*31, 160, (PATCH_REF)WR_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, (PATCH_REF)WR_CacheLumpName("ARTIBOX", PU_CACHE));
+			V_DrawPatch(286, 170, (PATCH_REF)WR_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, (PATCH_REF)WR_CacheLumpName("ARTIBOX", PU_CACHE));
+			if (CPlayer->inventorySlotNum > x + i
+				&& CPlayer->inventory[x + i].type != arti_none)
+			{
+				V_DrawPatch(50 + i*31, 168, (PATCH_REF)WR_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;
+
+	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)
+{
+	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/sounds.h
@@ -1,0 +1,270 @@
+
+// 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
+{
+	const char	name[8];
+	int		p1;
+} musicinfo_t;
+
+typedef struct sfxinfo_s
+{
+	const 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;
+	int		sound_id;
+	int		handle;
+	int		pitch;
+	int		priority;
+} channel_t;
+
+typedef struct
+{
+	int		id;
+	unsigned short	priority;
+	const 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	/* __SOUNDSH__ */
+
--- /dev/null
+++ b/soundst.h
@@ -1,0 +1,24 @@
+// soundst.h
+
+#ifndef __SOUNDSTH__
+#define __SOUNDSTH__
+
+extern	int		snd_MaxVolume;
+extern	int		snd_MusicVolume;
+
+void S_Init(void);
+void S_ShutDown(void);
+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_GetChannelInfo(SoundInfo_t *s);
+void S_SetMaxVolume(boolean fullprocess);
+void S_SetMusicVolume(void);
+
+#endif	/* __SOUNDSTH__ */
+
--- /dev/null
+++ b/sv_save.c
@@ -1,0 +1,694 @@
+//**************************************************************************
+//**
+//** sv_save.c
+//**
+//**************************************************************************
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+#include "p_local.h"
+
+// MACROS ------------------------------------------------------------------
+
+#define SVG_RAM			0
+#define SVG_FILE		1
+#define SAVEGAMESIZE		0x30000
+#define SAVE_GAME_TERMINATOR	0x1d
+
+// TYPES -------------------------------------------------------------------
+
+typedef enum
+{
+	tc_end,
+	tc_mobj
+} thinkerclass_t;
+
+enum
+{
+	tc_ceiling,
+	tc_door,
+	tc_floor,
+	tc_plat,
+	tc_flash,
+	tc_strobe,
+	tc_glow,
+	tc_endspecials
+} specials_e;
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+static void ArchivePlayers(void);
+static void UnarchivePlayers(void);
+static void ArchiveWorld(void);
+static void UnarchiveWorld(void);
+static void ArchiveThinkers(void);
+static void UnarchiveThinkers(void);
+static void ArchiveSpecials(void);
+static void UnarchiveSpecials(void);
+static void OpenStreamOut(const char *fileName);
+static void CloseStreamOut(const char *fileName);
+static void StreamOutBuffer(const void *buffer, int size);
+static void StreamOutByte(byte val);
+static void StreamOutWord(unsigned short val);
+//static void StreamOutLong(unsigned int val);
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+static FILE *SavingFP;
+static int SaveGameType;
+static void *SaveBuffer;
+static byte *SavePtr;
+
+// CODE --------------------------------------------------------------------
+
+//==========================================================================
+//
+// SV_SaveGame
+//
+//==========================================================================
+
+void SV_SaveGame(int slot, const char *description)
+{
+	int i;
+	char fileName[MAX_OSPATH];
+	char verString[SAVEVERSIONSIZE];
+
+	snprintf(fileName, sizeof(fileName), "%s%s%d.hsg",
+		 basePath, SAVEGAMENAME, slot);
+	OpenStreamOut(fileName);
+	StreamOutBuffer(description, SAVESTRINGSIZE);
+	memset(verString, 0, sizeof(verString));
+	sprintf(verString, "version %i", VERSION);
+	StreamOutBuffer(verString, SAVEVERSIONSIZE);
+	StreamOutByte(gameskill);
+	StreamOutByte(gameepisode);
+	StreamOutByte(gamemap);
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		StreamOutByte(playeringame[i]);
+	}
+	StreamOutByte(leveltime>>16);
+	StreamOutByte(leveltime>>8);
+	StreamOutByte(leveltime);
+	ArchivePlayers();
+	ArchiveWorld();
+	ArchiveThinkers();
+	ArchiveSpecials();
+	CloseStreamOut(fileName);
+}
+
+//==========================================================================
+//
+// SV_LoadGame
+//
+//==========================================================================
+
+void SV_LoadGame(int slot)
+{
+	int i;
+	int a, b, c;
+	char fileName[MAX_OSPATH];
+	char vcheck[SAVEVERSIONSIZE];
+
+	snprintf(fileName, sizeof(fileName), "%s%s%d.hsg",
+			 basePath, SAVEGAMENAME, slot);
+	M_ReadFile(fileName, &SaveBuffer);
+	SavePtr = (byte *)SaveBuffer + SAVESTRINGSIZE;	// Skip the description field
+	memset(vcheck, 0, sizeof(vcheck));
+	sprintf(vcheck, "version %i", VERSION);
+	if (strcmp((char *)SavePtr, vcheck) != 0)
+	{ // Bad version
+		return;
+	}
+	SavePtr += SAVEVERSIONSIZE;
+	gameskill = *SavePtr++;
+	gameepisode = *SavePtr++;
+	gamemap = *SavePtr++;
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		playeringame[i] = *SavePtr++;
+	}
+	// Load a base level
+	G_InitNew(gameskill, gameepisode, gamemap);
+
+	// Create leveltime
+	a = *SavePtr++;
+	b = *SavePtr++;
+	c = *SavePtr++;
+	leveltime = (a<<16) + (b<<8) + c;
+
+	// De-archive all the modifications
+	UnarchivePlayers();
+	UnarchiveWorld();
+	UnarchiveThinkers();
+	UnarchiveSpecials();
+
+	if (*SavePtr != SAVE_GAME_TERMINATOR)
+	{ // Missing savegame termination marker
+		I_Error("Bad savegame");
+	}
+	Z_Free(SaveBuffer);
+}
+
+//==========================================================================
+//
+// ArchivePlayers
+//
+//==========================================================================
+
+static void ArchivePlayers(void)
+{
+	int i, 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);
+			}
+		}
+		StreamOutBuffer(&dest, sizeof(player_t));
+	}
+}
+
+//==========================================================================
+//
+// UnarchivePlayers
+//
+//==========================================================================
+
+static void UnarchivePlayers(void)
+{
+	int i, j;
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i])
+			continue;
+		memcpy(&players[i], SavePtr, sizeof(player_t));
+		SavePtr += 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];
+		}
+	}
+}
+
+//==========================================================================
+//
+// ArchiveWorld
+//
+//==========================================================================
+
+static void ArchiveWorld(void)
+{
+	int i, j;
+	sector_t *sec;
+	line_t *li;
+	side_t *si;
+
+	// Sectors
+	for (i = 0, sec = sectors; i < numsectors; i++, sec++)
+	{
+		StreamOutWord(sec->floorheight>>FRACBITS);
+		StreamOutWord(sec->ceilingheight>>FRACBITS);
+		StreamOutWord(sec->floorpic);
+		StreamOutWord(sec->ceilingpic);
+		StreamOutWord(sec->lightlevel);
+		StreamOutWord(sec->special);	// needed?
+		StreamOutWord(sec->tag);	// needed?
+	}
+
+	// Lines
+	for (i = 0, li = lines; i < numlines; i++, li++)
+	{
+		StreamOutWord(li->flags);
+		StreamOutWord(li->special);
+		StreamOutWord(li->tag);
+		for (j = 0; j < 2; j++)
+		{
+			if (li->sidenum[j] == -1)
+			{
+				continue;
+			}
+			si = &sides[li->sidenum[j]];
+			StreamOutWord(si->textureoffset>>FRACBITS);
+			StreamOutWord(si->rowoffset>>FRACBITS);
+			StreamOutWord(si->toptexture);
+			StreamOutWord(si->bottomtexture);
+			StreamOutWord(si->midtexture);
+		}
+	}
+}
+
+//==========================================================================
+//
+// UnarchiveWorld
+//
+//==========================================================================
+
+static void UnarchiveWorld(void)
+{
+	int i, j;
+	sector_t *sec;
+	line_t *li;
+	side_t *si;
+	short *get;
+
+	get = (short *)SavePtr;
+
+//
+// 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++;
+		}
+	}
+
+	SavePtr = (byte *)get;
+}
+
+//==========================================================================
+//
+// ArchiveThinkers
+//
+//==========================================================================
+
+static void ArchiveThinkers(void)
+{
+	thinker_t *th;
+	mobj_t mobj;
+
+	for (th = thinkercap.next; th != &thinkercap; th = th->next)
+	{
+		if (th->function == P_MobjThinker)
+		{
+			StreamOutByte(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);
+			}
+			StreamOutBuffer(&mobj, sizeof(mobj_t));
+			continue;
+		}
+		//I_Error("P_ArchiveThinkers: Unknown thinker function");
+	}
+
+	// Add a terminating marker
+	StreamOutByte(tc_end);
+}
+
+//==========================================================================
+//
+// UnarchiveThinkers
+//
+//==========================================================================
+
+static void 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 = *SavePtr++;
+		switch (tclass)
+		{
+		case tc_end:
+			return;			// end of list
+
+		case tc_mobj:
+			mobj = (mobj_t *) Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL);
+			memcpy (mobj, SavePtr, sizeof(*mobj));
+			SavePtr += 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);
+		}
+	}
+}
+
+//==========================================================================
+//
+// ArchiveSpecials
+//
+//==========================================================================
+
+static void 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)
+		{
+			StreamOutByte(tc_ceiling);
+			memcpy(&ceiling, th, sizeof(ceiling_t));
+			ceiling.sector = (sector_t *)(ceiling.sector - sectors);
+			StreamOutBuffer(&ceiling, sizeof(ceiling_t));
+			continue;
+		}
+		if (th->function == T_VerticalDoor)
+		{
+			StreamOutByte(tc_door);
+			memcpy(&door, th, sizeof(vldoor_t));
+			door.sector = (sector_t *)(door.sector - sectors);
+			StreamOutBuffer(&door, sizeof(vldoor_t));
+			continue;
+		}
+		if (th->function == T_MoveFloor)
+		{
+			StreamOutByte(tc_floor);
+			memcpy(&floor, th, sizeof(floormove_t));
+			floor.sector = (sector_t *)(floor.sector - sectors);
+			StreamOutBuffer(&floor, sizeof(floormove_t));
+			continue;
+		}
+		if (th->function == T_PlatRaise)
+		{
+			StreamOutByte(tc_plat);
+			memcpy(&plat, th, sizeof(plat_t));
+			plat.sector = (sector_t *)(plat.sector - sectors);
+			StreamOutBuffer(&plat, sizeof(plat_t));
+			continue;
+		}
+		if (th->function == T_LightFlash)
+		{
+			StreamOutByte(tc_flash);
+			memcpy(&flash, th, sizeof(lightflash_t));
+			flash.sector = (sector_t *)(flash.sector - sectors);
+			StreamOutBuffer(&flash, sizeof(lightflash_t));
+			continue;
+		}
+		if (th->function == T_StrobeFlash)
+		{
+			StreamOutByte(tc_strobe);
+			memcpy(&strobe, th, sizeof(strobe_t));
+			strobe.sector = (sector_t *)(strobe.sector - sectors);
+			StreamOutBuffer(&strobe, sizeof(strobe_t));
+			continue;
+		}
+		if (th->function == T_Glow)
+		{
+			StreamOutByte(tc_glow);
+			memcpy(&glow, th, sizeof(glow_t));
+			glow.sector = (sector_t *)(glow.sector - sectors);
+			StreamOutBuffer(&glow, sizeof(glow_t));
+			continue;
+		}
+	}
+	// Add a terminating marker
+	StreamOutByte(tc_endspecials);
+}
+
+//==========================================================================
+//
+// UnarchiveSpecials
+//
+//==========================================================================
+
+static void 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 = *SavePtr++;
+		switch (tclass)
+		{
+		case tc_endspecials:
+			return;		// end of list
+
+		case tc_ceiling:
+			ceiling = (ceiling_t *) Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL);
+			memcpy (ceiling, SavePtr, sizeof(*ceiling));
+			SavePtr += 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 = (vldoor_t *) Z_Malloc (sizeof(*door), PU_LEVEL, NULL);
+			memcpy (door, SavePtr, sizeof(*door));
+			SavePtr += 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 = (floormove_t *) Z_Malloc (sizeof(*floor), PU_LEVEL, NULL);
+			memcpy (floor, SavePtr, sizeof(*floor));
+			SavePtr += 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 = (plat_t *) Z_Malloc (sizeof(*plat), PU_LEVEL, NULL);
+			memcpy (plat, SavePtr, sizeof(*plat));
+			SavePtr += 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 = (lightflash_t *) Z_Malloc (sizeof(*flash), PU_LEVEL, NULL);
+			memcpy (flash, SavePtr, sizeof(*flash));
+			SavePtr += sizeof(*flash);
+			flash->sector = &sectors[(int)flash->sector];
+			flash->thinker.function = T_LightFlash;
+			P_AddThinker (&flash->thinker);
+			break;
+
+		case tc_strobe:
+			strobe = (strobe_t *) Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL);
+			memcpy (strobe, SavePtr, sizeof(*strobe));
+			SavePtr += sizeof(*strobe);
+			strobe->sector = &sectors[(int)strobe->sector];
+			strobe->thinker.function = T_StrobeFlash;
+			P_AddThinker (&strobe->thinker);
+			break;
+
+		case tc_glow:
+			glow = (glow_t *) Z_Malloc (sizeof(*glow), PU_LEVEL, NULL);
+			memcpy (glow, SavePtr, sizeof(*glow));
+			SavePtr += 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);
+		}
+	}
+}
+
+//==========================================================================
+//
+// OpenStreamOut
+//
+//==========================================================================
+
+static void OpenStreamOut(const char *fileName)
+{
+	MallocFailureOk = true;
+	SaveBuffer = Z_Malloc(SAVEGAMESIZE, PU_STATIC, NULL);
+	SavePtr = (byte *)SaveBuffer;
+	MallocFailureOk = false;
+	if (SaveBuffer == NULL)
+	{ // Not enough memory - use file save method
+		SaveGameType = SVG_FILE;
+		SavingFP = fopen(fileName, "wb");
+	}
+	else
+	{
+		SaveGameType = SVG_RAM;
+	}
+}
+
+//==========================================================================
+//
+// CloseStreamOut
+//
+//==========================================================================
+
+static void CloseStreamOut(const char *fileName)
+{
+	int length;
+
+	StreamOutByte(SAVE_GAME_TERMINATOR);
+	if (SaveGameType == SVG_RAM)
+	{
+		length = SavePtr - (byte *)SaveBuffer;
+		if (length > SAVEGAMESIZE)
+		{
+			I_Error("Savegame buffer overrun");
+		}
+		M_WriteFile(fileName, SaveBuffer, length);
+		Z_Free(SaveBuffer);
+	}
+	else
+	{ // SVG_FILE
+		fclose(SavingFP);
+	}
+}
+
+//==========================================================================
+//
+// SV_Write
+//
+//==========================================================================
+
+static void StreamOutBuffer(const void *buffer, int size)
+{
+	if (SaveGameType == SVG_RAM)
+	{
+		memcpy(SavePtr, buffer, size);
+		SavePtr += size;
+	}
+	else
+	{ // SVG_FILE
+		fwrite(buffer, size, 1, SavingFP);
+	}
+}
+
+static void StreamOutByte(byte val)
+{
+	StreamOutBuffer(&val, sizeof(byte));
+}
+
+static void StreamOutWord(unsigned short val)
+{
+	StreamOutBuffer(&val, sizeof(unsigned short));
+}
+
+/*
+static void StreamOutLong(unsigned int val)
+{
+	StreamOutBuffer(&val, sizeof(int));
+}
+*/
+
--- /dev/null
+++ b/tables.c
@@ -1,0 +1,2066 @@
+// tables.c
+
+#include "h2stdinc.h"
+#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/v_compat.h
@@ -1,0 +1,51 @@
+//**************************************************************************
+//**
+//** v_compat.h
+//**
+//**************************************************************************
+
+#ifndef __V_COMPAT_H
+#define __V_COMPAT_H
+
+#if defined(RENDER3D)
+#include "ogl_def.h"
+#endif
+
+/* SetPalette */
+
+#ifndef PLAYPAL_NUM
+#define PLAYPAL_NUM		W_GetNumForName("PLAYPAL")
+#endif
+
+#if defined(RENDER3D)
+#define V_SetPaletteBase()	OGL_SetFilter(0)
+#define V_SetPaletteShift(num)	OGL_SetFilter((num))
+#else
+#define V_SetPaletteBase()	I_SetPalette((byte *)W_CacheLumpName("PLAYPAL", PU_CACHE))
+#define V_SetPaletteShift(num)	I_SetPalette((byte *)W_CacheLumpNum(PLAYPAL_NUM, PU_CACHE) + (num)*768)
+#endif
+
+/* Minimal definitions for DrawPatch / DrawRawScreen stuff. */
+#if defined(RENDER3D)
+#define PATCH_REF		int
+#define INVALID_PATCH		0
+#define BYTE_REF		int
+#else
+#define BYTE_REF		byte*
+#define PATCH_REF		patch_t*
+#define INVALID_PATCH		NULL
+#endif
+
+/* Minimal definitions for CacheLumpName / CacheLumpNum stuff. */
+#if defined(RENDER3D)
+#define ZR_ChangeTag(a,b)
+#define WR_CacheLumpName(a,b)		W_GetNumForName((a))
+#define WR_CacheLumpNum(a,b)		(a)
+#else
+#define ZR_ChangeTag			Z_ChangeTag
+#define WR_CacheLumpName		W_CacheLumpName
+#define WR_CacheLumpNum			W_CacheLumpNum
+#endif
+
+#endif	/* __V_COMPAT_H */
+
--- /dev/null
+++ b/v_video.c
@@ -1,0 +1,296 @@
+// V_video.c
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+
+#define SC_INDEX	0x3c4
+
+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
+	}
+};
+
+#if defined(RENDER3D)
+
+void V_Init(void)
+{
+	/* OpenGL: NOTHING TO DO HERE. */
+}
+
+#else	/* RENDER3D */
+
+byte	*screens;
+int	dirtybox[4];
+
+//---------------------------------------------------------------------------
+//
+// PROC V_Init
+//
+//---------------------------------------------------------------------------
+
+void V_Init(void)
+{
+	// I_AllocLow will put screen in low dos memory on PCs.
+	screens = I_AllocLow(SCREENWIDTH*SCREENHEIGHT);
+}
+
+//---------------------------------------------------------------------------
+//
+// 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 = screens + 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 = screens + 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 = screens+y*SCREENWIDTH+x;
+	desttop2 = screens+(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(screens, raw, SCREENWIDTH*SCREENHEIGHT);
+}
+
+#endif	/* ! RENDER3D */
+
--- /dev/null
+++ b/w_wad.c
@@ -1,0 +1,440 @@
+// W_wad.c
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "h2stdinc.h"
+#include "doomdef.h"
+
+// MACROS ------------------------------------------------------------------
+
+/* compatibility with DOS/Windows */
+#ifndef O_BINARY
+# if defined(_O_BINARY)
+#  define O_BINARY	_O_BINARY
+# else
+#  define O_BINARY		0
+# endif
+#endif
+
+// 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;
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+const char *waddir;
+lumpinfo_t *lumpinfo;		// location of each lump on disk
+int numlumps;
+void **lumpcache;
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+// CODE --------------------------------------------------------------------
+
+boolean W_IsWadPresent(const char *filename)
+{
+	char path[MAX_OSPATH];
+	int handle = -1;
+
+	/* try the directory from the command line or
+	 *  from the shared data environment variable.
+	 */
+	if (waddir && *waddir)
+	{
+		snprintf (path, sizeof(path), "%s/%s", waddir, filename);
+		handle = open(path, OREAD);
+	}
+#if !defined(_NO_USERDIRS)
+	if (handle == -1)	/* Try UserDIR */
+	{
+		snprintf (path, sizeof(path), "%s%s", basePath, filename);
+		handle = open(path, OREAD);
+	}
+#endif	/* !_NO_USERDIRS */
+	if (handle == -1)	/* Now try CWD */
+	{
+		handle = open(filename, OREAD);
+	}
+	if (handle == -1)
+		return false;	/* Didn't find the file. */
+	close(handle);
+	return true;
+}
+
+//==========================================================================
+//
+// 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(const char *filename)
+{
+	wadinfo_t header;
+	lumpinfo_t *lump_p;
+	char path[MAX_OSPATH];
+	int handle, length;
+	int startlump;
+	filelump_t *fileinfo, singleinfo;
+	filelump_t *freeFileInfo;
+	int i;
+
+	handle = -1;
+	/* try the directory from the command line or
+	 *  from the shared data environment variable.
+	 */
+	if (waddir && *waddir)
+	{
+		snprintf (path, sizeof(path), "%s/%s", waddir, filename);
+		handle = open(path, OREAD);
+	}
+#if !defined(_NO_USERDIRS)
+	if (handle == -1)	/* Try UserDIR */
+	{
+		snprintf (path, sizeof(path), "%s%s", basePath, filename);
+		handle = open(path, OREAD);
+	}
+#endif	/* !_NO_USERDIRS */
+	if (handle == -1)	/* Now try CWD */
+	{
+		handle = open(filename, OREAD);
+	}
+	if (handle == -1)
+		return;		/* Didn't find the file. */
+
+	startlump = numlumps;
+	if (strcasecmp(filename + strlen(filename) - 3, "wad") != 0)
+	{ // Single lump file
+		fileinfo = &singleinfo;
+		freeFileInfo = NULL;
+		singleinfo.filepos = 0;
+		singleinfo.size = LONG(filelength(handle));
+		M_ExtractFileBase(filename, singleinfo.name);
+		numlumps++;
+	}
+	else
+	{ // WAD file
+		read(handle, &header, sizeof(header));
+		if (strncmp(header.identification, "IWAD", 4) != 0)
+		{
+			if (strncmp(header.identification, "PWAD", 4) != 0)
+			{ // Bad file id
+				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 = (filelump_t *) malloc(length);
+		if (!fileinfo)
+		{
+			I_Error("W_AddFile: fileinfo malloc failed\n");
+		}
+		freeFileInfo = fileinfo;
+		seek(handle, header.infotableofs, 0);
+		read(handle, fileinfo, length);
+		numlumps += header.numlumps;
+	}
+
+	// Fill in lumpinfo
+	lumpinfo = (lumpinfo_t *) 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++)
+	{
+		memset(lump_p->name, 0, 8);
+		lump_p->handle = handle;
+		lump_p->position = LONG(fileinfo->filepos);
+		lump_p->size = LONG(fileinfo->size);
+		strncpy(lump_p->name, fileinfo->name, 8);
+	}
+	if (freeFileInfo)
+	{
+		free(freeFileInfo);
+	}
+}
+
+//==========================================================================
+//
+// 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(const char **filenames)
+{
+	int size;
+
+	// Open all the files, load headers, and count lumps
+	numlumps = 0;
+	lumpinfo = (lumpinfo_t *) malloc(1); // Will be realloced as lumps are added
+
+	for ( ; *filenames; filenames++)
+	{
+		W_AddFile(*filenames);
+	}
+	if (!numlumps)
+	{
+		I_Error("W_InitMultipleFiles: no files found");
+	}
+
+	// Set up caching
+	size = numlumps * sizeof(*lumpcache);
+	lumpcache = (void **) 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(const char *filename)
+{
+	const 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(const char *name)
+{
+	char name8[9];
+	lumpinfo_t *lump_p;
+
+	// Make the name into two integers for easy compares
+	memset(name8, 0, sizeof(name8));
+	strncpy(name8, name, 8);
+	strupr(name8); // case insensitive
+
+	// Scan backwards so patch lump files take precedence
+	lump_p = lumpinfo + numlumps;
+	while (lump_p-- != lumpinfo)
+	{
+		if (memcmp(lump_p->name, name8, 8) == 0)
+		{
+			return (int)(lump_p - lumpinfo);
+		}
+	}
+	return -1;
+}
+
+//==========================================================================
+//
+// W_GetNumForName
+//
+// Calls W_CheckNumForName, but bombs out if not found.
+//
+//==========================================================================
+
+int	W_GetNumForName (const 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;
+	seek(l->handle, l->position, 0);
+	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);
+	}
+}
+
+//==========================================================================
+//
+// W_CacheLumpNum
+//
+//==========================================================================
+
+void *W_CacheLumpNum(int lump, int tag)
+{
+	if ((unsigned)lump >= numlumps)
+	{
+		I_Error("W_CacheLumpNum: %i >= numlumps", lump);
+	}
+	if (!lumpcache[lump])
+	{ // Need to read the lump in
+		Z_Malloc(W_LumpLength(lump), tag, &lumpcache[lump]);
+		W_ReadLump(lump, lumpcache[lump]);
+	}
+	else
+	{
+		Z_ChangeTag(lumpcache[lump], tag);
+	}
+	return lumpcache[lump];
+}
+
+//==========================================================================
+//
+// W_CacheLumpName
+//
+//==========================================================================
+
+void *W_CacheLumpName(const char *name, int tag)
+{
+	return W_CacheLumpNum(W_GetNumForName(name), tag);
+}
+
+//==========================================================================
+//
+// W_Profile
+//
+//==========================================================================
+
+// Ripped out for Heretic
+/*
+static int	info[2500][10];
+static 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/z_zone.c
@@ -1,0 +1,388 @@
+// Z_zone.c
+
+#include "h2stdinc.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
+
+==============================================================================
+*/
+
+
+typedef struct
+{
+	int		size;		// total bytes malloced, including header
+	memblock_t	blocklist;		// start / end cap for linked list
+	memblock_t	*rover;
+} memzone_t;
+
+static memzone_t	*mainzone;
+boolean MallocFailureOk;
+
+/*
+========================
+=
+= 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, *newblock, *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
+	size = (size + 7) & ~7;		// align to 8-byte boundary
+
+//
+// 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)	// scaned 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
+		newblock = (memblock_t *) ((byte *)base + size);
+		newblock->size = extra;
+		newblock->user = NULL;		// free block
+		newblock->tag = 0;
+		newblock->prev = base;
+		newblock->next = base->next;
+		newblock->next->prev = newblock;
+		base->next = newblock;
+		base->size = size;
+	}
+
+	if (user)
+	{
+		base->user = (void **)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 && (uintptr_t)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		freemem;
+
+	freemem = 0;
+	for (block = mainzone->blocklist.next ; block != &mainzone->blocklist; block = block->next)
+	{
+		if (!block->user || block->tag >= PU_PURGELEVEL)
+			freemem += block->size;
+	}
+	return freemem;
+}
+