Initial revision
authorroot <root@eb238505-0f32-0410-b369-e4014cc52c4b>
Thu, 3 Jan 2002 02:40:50 +0000 (02:40 +0000)
committerroot <root@eb238505-0f32-0410-b369-e4014cc52c4b>
Thu, 3 Jan 2002 02:40:50 +0000 (02:40 +0000)
git-svn-id: svn://svn.icculus.org/hheretic/trunk@2 eb238505-0f32-0410-b369-e4014cc52c4b

94 files changed:
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
README [new file with mode: 0644]
acconfig.h [new file with mode: 0644]
aclocal.m4 [new file with mode: 0644]
base/am_map.c [new file with mode: 0644]
base/ct_chat.c [new file with mode: 0644]
base/d_main.c [new file with mode: 0644]
base/d_net.c [new file with mode: 0644]
base/d_netbak.c [new file with mode: 0644]
base/f_finale.c [new file with mode: 0644]
base/g_game.c [new file with mode: 0644]
base/i_cyber.c [new file with mode: 0644]
base/i_ibm.c [new file with mode: 0644]
base/i_ibm_a.asm [new file with mode: 0644]
base/i_linux.c [new file with mode: 0644]
base/i_sound.c [new file with mode: 0644]
base/in_lude.c [new file with mode: 0644]
base/info.c [new file with mode: 0644]
base/linear.asm [new file with mode: 0644]
base/m_misc.c [new file with mode: 0644]
base/mn_menu.c [new file with mode: 0644]
base/netold.c [new file with mode: 0644]
base/oldd_net.c [new file with mode: 0644]
base/oss.c [new file with mode: 0644]
base/p_ceilng.c [new file with mode: 0644]
base/p_doors.c [new file with mode: 0644]
base/p_enemy.c [new file with mode: 0644]
base/p_floor.c [new file with mode: 0644]
base/p_inter.c [new file with mode: 0644]
base/p_lights.c [new file with mode: 0644]
base/p_map.c [new file with mode: 0644]
base/p_maputl.c [new file with mode: 0644]
base/p_mobj.c [new file with mode: 0644]
base/p_plats.c [new file with mode: 0644]
base/p_pspr.c [new file with mode: 0644]
base/p_setup.c [new file with mode: 0644]
base/p_sight.c [new file with mode: 0644]
base/p_spec.c [new file with mode: 0644]
base/p_switch.c [new file with mode: 0644]
base/p_telept.c [new file with mode: 0644]
base/p_tick.c [new file with mode: 0644]
base/p_user.c [new file with mode: 0644]
base/r_bsp.c [new file with mode: 0644]
base/r_data.c [new file with mode: 0644]
base/r_draw.c [new file with mode: 0644]
base/r_main.c [new file with mode: 0644]
base/r_plane.c [new file with mode: 0644]
base/r_segs.c [new file with mode: 0644]
base/r_things.c [new file with mode: 0644]
base/sb_bar.c [new file with mode: 0644]
base/sounds.c [new file with mode: 0644]
base/tables.c [new file with mode: 0644]
base/v_video.c [new file with mode: 0644]
base/w_wad.c [new file with mode: 0644]
base/z_zone.c [new file with mode: 0644]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
include/am_data.h [new file with mode: 0644]
include/am_map.h [new file with mode: 0644]
include/audio_plugin.h [new file with mode: 0644]
include/config.h [new file with mode: 0644]
include/config.h.in [new file with mode: 0644]
include/ct_chat.h [new file with mode: 0644]
include/doomdata.h [new file with mode: 0644]
include/doomdef.h [new file with mode: 0644]
include/drcoord.h [new file with mode: 0644]
include/dstrings.h [new file with mode: 0644]
include/i_header.h [new file with mode: 0644]
include/i_sound.h [new file with mode: 0644]
include/info.h [new file with mode: 0644]
include/m_bams.h [new file with mode: 0644]
include/ogl_def.h [new file with mode: 0644]
include/ogl_font.h [new file with mode: 0644]
include/ogl_rl.h [new file with mode: 0644]
include/ogl_tex.h [new file with mode: 0644]
include/oss.h [new file with mode: 0644]
include/p_local.h [new file with mode: 0644]
include/p_spec.h [new file with mode: 0644]
include/r_local.h [new file with mode: 0644]
include/sounds.h [new file with mode: 0644]
include/soundst.h [new file with mode: 0644]
include/vgaview.h [new file with mode: 0644]
opengl/m_bams.c [new file with mode: 0644]
opengl/ogl_clip.c [new file with mode: 0644]
opengl/ogl_draw.c [new file with mode: 0644]
opengl/ogl_font.c [new file with mode: 0644]
opengl/ogl_rend.c [new file with mode: 0644]
opengl/ogl_rl.c [new file with mode: 0644]
opengl/ogl_sky.c [new file with mode: 0644]
opengl/ogl_tex.c [new file with mode: 0644]
sdl/i_sdl.c [new file with mode: 0644]
sdl/i_sdlgl.c [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..1b49141
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,140 @@
+SOFTWARE LICENSE AGREEMENT
+
+IMPORTANT - READ CAREFULLY: USE OF THIS PROGRAM IS SUBJECT TO THE SOFTWARE 
+LICENSE TERMS SET FORTH BELOW. PROGRAM INCLUDES ALL SOFTWARE INCLUDED WITH THIS
+AGREEMENT, THE ASSOCIATED MEDIA, ANY PRINTED MATERIALS, AND ANY ON-LINE OR 
+ELECTRONIC DOCUMENTATION, AND ANY AND ALL COPIES OF SUCH SOFTWARE AND MATERIALS.
+BY OPENING THIS PACKAGE, INSTALLING, AND/OR USING THE PROGRAM AND ANY SOFTWARE
+PROGRAMS INCLUDED WITHIN, YOU ACCEPT THE TERMS OF THIS LICENSE WITH ACTIVISION,
+INC. ( ACTIVISION ). 
+
+       LIMITED USE LICENSE. Subject to the conditions described below, 
+Activision grants you the non-exclusive, non-transferable, limited right and 
+license to install and use one copy of this Program solely and exclusively for 
+your personal use. All rights not specifically granted under this Agreement are 
+reserved by Activision and, as applicable, Activision's licensors. This Program 
+is licensed, not sold, for your use. Your license confers no title or ownership 
+in this Program and should not be construed as a sale of any rights in this 
+Program.
+
+LICENSE CONDITIONS. 
+You shall not:
+  Exploit this Program or any of its parts commercially.
+  Use this Program, or permit use of this Program, on more than one computer, 
+computer terminal, or workstation at the same time.
+  Make copies of this Program or any part thereof, or make copies of the 
+materials accompanying this Program.
+  Use the program, or permit use of this Program, in a network, multi-user 
+arrangement or remote access arrangement, including any online use, except as 
+otherwise explicitly provided by this Program.
+  Sell, rent, lease or license any copies of this Program, without the express 
+prior written consent of Activision.
+  Remove, disable or circumvent any proprietary notices or labels contained on 
+or within the Program.
+
+OWNERSHIP.  All title, ownership rights and intellectual property rights
+in and to this Program and any and all copies thereof (including but not 
+limited to any titles, computer code, themes, objects, characters, character 
+names, stories, dialog, catch phrases, locations, concepts, artwork, animation, 
+sounds, musical compositions, audio-visual effects, methods of operation, moral 
+rights, any related documentation, and applets incorporated into this Program) 
+are owned by Activision, affiliates of Activision or Activision's licensors. 
+This Program is protected by the copyright laws of the United States, 
+international copyright treaties and conventions and other laws. This Program 
+contains certain licensed materials and Activision's licensors may protect their
+rights in the event of any violation of this Agreement.
+
+PROGRAM UTILITIES. This Program contains certain design, programming and 
+processing utilities, tools, assets and other resources (Program Utilities) for 
+use with this Program that allow you to create customized new game levels and 
+other related game materials for personal use in connection with the Program 
+(New Game Materials). The use of the Program Utilities is subject to the 
+following additional license restrictions:
+
+You agree that, as a condition to your using the Program Utilities, you will not
+use or allow third parties to use the Program Utilities and the New Game 
+Materials created by you for any commercial purposes, including but not limited 
+to selling, renting, leasing, licensing, distributing, or otherwise transferring
+the ownership of such New Game Materials, whether on a stand alone basis or 
+packaged in combination with the New Game Materials created by others, through 
+any and all distribution channels, including, without limitation, retail sales 
+and on-line electronic distribution. You agree not to solicit, initiate or 
+encourage any proposal or offer from any person or entity to create any New 
+Game Materials for commercial distribution. You agree to promptly inform 
+Activision in writing of any instances of your receipt of any such proposal or 
+offer.
+If you decide to make available the use of the New Game Materials created by 
+you to other gamers, you agree to do so solely without charge. 
+New Game Materials may be created only if such New Game Materials can be used 
+exclusively in combination with the retail version of the Program. New Game 
+Materials may not be designed to be used as a stand-alone product.
+New Game Materials must not contain any illegal, obscene or defamatory 
+materials, materials that infringe rights of privacy and publicity of third 
+parties or (without appropriate irrevocable licenses granted specifically for 
+that purpose) any trademarks, copyright-protected works or other properties of 
+third parties.
+All New Game Materials must contain prominent identification at least in any 
+on-line description and with reasonable duration on the opening screen: (a) the 
+name and E-mail address of the New Game Materials creator(s) and (b) the words 
+THIS MATERIAL IS NOT MADE OR SUPPORTED BY ACTIVISION.
+
+WARRANTY INFORMATION.  THIS PROGRAM IS PROVIDED AS IS. ACTIVISION AND ITS 
+AFFILIATES MAKE NO WARRANTIES OF ANY KIND, WHETHER ORAL OR WRITTEN, EXPRESS OR 
+IMPLIED, INCLUDING ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+PURPOSE OR NON-INFRINGEMENT, AND NO OTHER REPRESENTATIONS OR CLAIMS OF ANY KIND 
+SHALL BE BINDING ON OR OBLIGATE ACTIVISION OR ITS AFFILIATES. 
+
+LIMITATION ON DAMAGES.  IN NO EVENT WILL ACTIVISION OR ANY AFFILIATES OF 
+ACTIVISION BE LIABLE FOR SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES RESULTING
+FROM POSSESSION, USE OR MALFUNCTION OF THE PROGRAM, INCLUDING DAMAGES TO 
+PROPERTY, LOSS OF GOODWILL, COMPUTER FAILURE OR MALFUNCTION AND, TO THE EXTENT 
+PERMITTED BY LAW, DAMAGES FOR PERSONAL INJURIES, EVEN IF ACTIVISION HAS BEEN 
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ACTIVISION'S LIABILITY SHALL NOT 
+EXCEED THE ACTUAL PRICE PAID FOR THE LICENSE TO USE THIS PROGRAM. SOME 
+STATES/COUNTRIES DO NOT ALLOW LIMITATIONS ON HOW LONG AN IMPLIED WARRANTY LASTS 
+AND/OR THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO 
+THE ABOVE LIMITAIONS AND/OR EXCLUSION OR LIMITATION OF LIABILITY MAY NOT APPLY 
+TO YOU. THIS WARRANTY GIVES YOU SPECIFIC LEGAL RIGHTS, AND YOU MAY HAVE OTHER 
+RIGHTS WHICH VARY FROM JURISDICTION TO JURISDICTION.
+
+TERMINATION. Without prejudice to any other rights of Activision, this Agreement
+will terminate automatically if you fail to comply with its terms and 
+conditions. In such event, you must destroy all copies of this Program and all 
+of its component parts.
+U.S. GOVERNMENT RESTRICTED RIGHTS. The Program and documentation have been 
+developed entirely at private expense and are provided as Commercial Computer 
+Software or restricted computer software. Use, duplication or disclosure by the 
+U.S. Government or a U.S. Government subcontractor is subject to the 
+restrictions set forth in subparagraph (c)(1)(ii) of the Rights in Technical 
+Data and Computer Software clauses in DFARS 252.227-7013 or as set forth in 
+subparagraph (c)(1) and (2) of the Commercial Computer Software Restricted 
+Rights clauses at FAR 52.227-19, as applicable. The Contractor/Manufacturer is 
+Activision, Inc., 3100 Ocean Park Boulevard, Santa Monica, California 90405.
+
+INJUNCTION. Because Activision would be irreparably damaged if the terms of this
+Agreement were not specifically enforced, you agree that Activision shall be 
+entitled, without bond, other security or proof of damages, to appropriate 
+equitable remedies with respect to breaches of this Agreement, in addition to 
+such other remedies as Activision may otherwise have under applicable laws.
+
+INDEMNITY.  You agree to indemnify, defend and hold Activision, its partners, 
+affiliates, licensors, contractors, officers, directors, employees and agents 
+harmless from all damages, losses and expenses arising directly or indirectly 
+from your acts and omissions to act in using the Product pursuant to the terms
+of this Agreement 
+
+MISCELLANEOUS. This Agreement represents the complete agreement concerning this 
+license between the parties and supersedes all prior agreements and
+representations between them. It may be amended only by a writing executed by
+both parties. If any provision of this Agreement is held to be unenforceable for
+any reason, such provision shall be reformed only to the extent necessary to 
+make it enforceable and the remaining provisions of this Agreement shall not be 
+affected. This Agreement shall be construed under California law as such law is 
+applied to agreements between California residents entered into and to be 
+performed within California, except as governed by federal law and you consent 
+to the exclusive jurisdiction of the state and federal courts in Los Angeles,
+California. 
+
+If you have any questions concerning this license, you may contact Activision 
+at 3100 Ocean Park Boulevard, Santa Monica, California 90405, (310) 255-2000,
+Attn. Business and Legal Affairs, legal@activision.com
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..646032f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,127 @@
+# Generated automatically from Makefile.in by configure.
+# This Makefile understands the following targets:
+#
+# sdl                  build SDL (software) version
+# clean:               remove all intermediate files
+
+# Basic stuff
+SHELL = /bin/sh
+
+top_srcdir = .
+srcdir = .
+prefix = /usr/local
+exec_prefix = ${prefix}
+bindir = $(exec_prefix)/bin
+infodir = $(prefix)/info
+libdir = $(prefix)/lib/gnudl
+mandir = $(prefix)/man/man1
+includedir = $(prefix)/include
+
+CC = gcc
+DEFS = -DHAVE_CONFIG_H
+CPPFLAGS =  -g -O2 -Wall
+CFLAGS = $(CPPFLAGS) 
+LDFLAGS = 
+BLIBS = -lpthread
+GLLIBS =  -L/usr/X11R6/lib -lGL -lGLU
+SDLLIBS = -L/usr/local/lib -Wl,-rpath,/usr/local/lib -lSDL -lpthread 
+SDLFLAGS = -I/usr/local/include -I/usr/local/include/SDL -D_REENTRANT
+GLHEXEN = false
+HAVE_SDL = yes
+
+# Directories
+
+TOPSRCDIR = .
+TOPOBJDIR = .
+SRCDIR    = .
+MODULE    = none
+
+INCSUBDIR = include
+
+CPPFLAGS += -I./include
+
+# Objects to build
+
+SDLOBJS = \
+       sdl/i_sdl.o
+
+GLBASE = sdl/i_sdlgl.o
+
+GLOBJS = \
+       opengl/ogl_clip.o \
+       opengl/ogl_draw.o \
+       opengl/ogl_font.o \
+       opengl/ogl_rend.o \
+       opengl/ogl_rl.o \
+       opengl/ogl_sky.o \
+       opengl/ogl_tex.o \
+       opengl/m_bams.o
+
+BASEOBJS = \
+       base/i_linux.o \
+       base/oss.o \
+       base/i_sound.o \
+       base/am_map.o \
+       base/ct_chat.o \
+       base/d_net.o \
+       base/f_finale.o \
+       base/g_game.o \
+       base/d_main.o \
+       base/info.o \
+       base/in_lude.o \
+       base/mn_menu.o \
+       base/m_misc.o \
+       base/p_ceilng.o \
+       base/p_doors.o \
+       base/p_enemy.o \
+       base/p_floor.o \
+       base/p_inter.o \
+       base/p_lights.o \
+       base/p_map.o \
+       base/p_maputl.o \
+       base/p_mobj.o \
+       base/p_plats.o \
+       base/p_pspr.o \
+       base/p_setup.o \
+       base/p_sight.o \
+       base/p_spec.o \
+       base/p_switch.o \
+       base/p_telept.o \
+       base/p_tick.o \
+       base/p_user.o \
+       base/r_bsp.o \
+       base/r_data.o \
+       base/r_draw.o \
+       base/r_main.o \
+       base/r_plane.o \
+       base/r_segs.o \
+       base/r_things.o \
+       base/sb_bar.o \
+       base/sounds.o \
+       base/tables.o \
+       base/v_video.o \
+       base/w_wad.o \
+       base/z_zone.o
+
+ifeq "$(GLHEXEN)" "true"
+opengl: $(GLBASE) $(GLOBJS) $(BASEOBJS)
+       $(CC) -o hheretic-gl $(GLBASE) $(GLOBJS) $(BASEOBJS) $(GLLIBS) $(SDLLIBS)
+else
+ifeq "$(HAVE_SDL)" "yes"
+sdl: $(SDLOBJS) $(BASEOBJS)
+       $(CC) -o hheretic-sdl $(SDLOBJS) $(BASEOBJS) $(SDLLIBS)
+endif
+endif
+
+clean:
+       $(RM) base/*.o
+       $(RM) x11/*.o
+       $(RM) svgalib/*.o
+       $(RM) opengl/*.o
+       $(RM) sdl/*.o
+
+%.o: %.c
+       $(CC) $< -o $@ -c $(CFLAGS) 
+
+%.o: %.cpp
+       $(CXX) $< -o $@ -c $(CPPFLAGS)
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..f864bb9
--- /dev/null
@@ -0,0 +1,128 @@
+# This Makefile understands the following targets:
+#
+# sdl                  build SDL (software) version
+# clean:               remove all intermediate files
+
+# Basic stuff
+SHELL = /bin/sh
+VPATH = @srcdir@
+
+top_srcdir = @top_srcdir@
+srcdir = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = $(exec_prefix)/bin
+infodir = $(prefix)/info
+libdir = $(prefix)/lib/gnudl
+mandir = $(prefix)/man/man1
+includedir = $(prefix)/include
+
+CC = @CC@
+DEFS = @DEFS@
+CPPFLAGS = @CPPFLAGS@ @CFLAGS@
+CFLAGS = $(CPPFLAGS) 
+LDFLAGS = @LDFLAGS@
+BLIBS = @BASELIBS@
+GLLIBS = @GLLIBS@ -L/usr/X11R6/lib -lGL -lGLU
+SDLLIBS = @SDL_LIBS@ 
+SDLFLAGS = @SDL_CFLAGS@
+GLHEXEN = @GLHEXEN@
+HAVE_SDL = @HAVESDL@
+
+# Directories
+
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = .
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = none
+
+INCSUBDIR = include
+
+CPPFLAGS += -I./include
+
+# Objects to build
+
+SDLOBJS = \
+       sdl/i_sdl.o
+
+GLBASE = @GLBASE@
+
+GLOBJS = \
+       opengl/ogl_clip.o \
+       opengl/ogl_draw.o \
+       opengl/ogl_font.o \
+       opengl/ogl_rend.o \
+       opengl/ogl_rl.o \
+       opengl/ogl_sky.o \
+       opengl/ogl_tex.o \
+       opengl/m_bams.o
+
+BASEOBJS = \
+       base/i_linux.o \
+       base/oss.o \
+       base/i_sound.o \
+       base/am_map.o \
+       base/ct_chat.o \
+       base/d_net.o \
+       base/f_finale.o \
+       base/g_game.o \
+       base/d_main.o \
+       base/info.o \
+       base/in_lude.o \
+       base/mn_menu.o \
+       base/m_misc.o \
+       base/p_ceilng.o \
+       base/p_doors.o \
+       base/p_enemy.o \
+       base/p_floor.o \
+       base/p_inter.o \
+       base/p_lights.o \
+       base/p_map.o \
+       base/p_maputl.o \
+       base/p_mobj.o \
+       base/p_plats.o \
+       base/p_pspr.o \
+       base/p_setup.o \
+       base/p_sight.o \
+       base/p_spec.o \
+       base/p_switch.o \
+       base/p_telept.o \
+       base/p_tick.o \
+       base/p_user.o \
+       base/r_bsp.o \
+       base/r_data.o \
+       base/r_draw.o \
+       base/r_main.o \
+       base/r_plane.o \
+       base/r_segs.o \
+       base/r_things.o \
+       base/sb_bar.o \
+       base/sounds.o \
+       base/tables.o \
+       base/v_video.o \
+       base/w_wad.o \
+       base/z_zone.o
+
+ifeq "$(GLHEXEN)" "true"
+opengl: $(GLBASE) $(GLOBJS) $(BASEOBJS)
+       $(CC) -o hheretic-gl $(GLBASE) $(GLOBJS) $(BASEOBJS) $(GLLIBS) $(SDLLIBS)
+else
+ifeq "$(HAVE_SDL)" "yes"
+sdl: $(SDLOBJS) $(BASEOBJS)
+       $(CC) -o hheretic-sdl $(SDLOBJS) $(BASEOBJS) $(SDLLIBS)
+endif
+endif
+
+clean:
+       $(RM) base/*.o
+       $(RM) x11/*.o
+       $(RM) svgalib/*.o
+       $(RM) opengl/*.o
+       $(RM) sdl/*.o
+
+%.o: %.c
+       $(CC) $< -o $@ -c $(CFLAGS) 
+
+%.o: %.cpp
+       $(CXX) $< -o $@ -c $(CPPFLAGS)
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..7c83de4
--- /dev/null
+++ b/README
@@ -0,0 +1,23 @@
+HHeretic v0.1 README
+
+Configuring
+-----------
+./configure for software.
+./configure --enable-gl for 3D accel.
+
+!!!IMPORTANT!!!
+You *MUST* do a 'make clean' between software and GL builds.
+
+HHeretic currently expects a heretic.wad in the dir it runs from.
+
+Send bugs to theoddone@quakefiles.com
+
+Known Bugs
+----------
+OpenGL:
+ - Some tall rooms can be seen through skies (ie. skies not clipped properly).
+
+Thanks
+------
+Jeff Mrochuk
+All who helped me test
diff --git a/acconfig.h b/acconfig.h
new file mode 100644 (file)
index 0000000..a50a3ff
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * This file is used by 'autoheader' to generate the list of symbols
+ * defined in the 'configure' script.
+ */
+
+/* Define if including the assassin in compile */
+#undef ASSASSIN 
+
+/* Define if building for OpenGL */
+#undef RENDER3D
+
+/* Define if building for demo wadfile */
+#undef DEMO_WAD
+
+/* Needed for something or other */
+#undef _REENTRANT
+
+/* Same as above */
+#undef NORANGECHECKING
+
+/* Define if you want configs stored in users' home directories */
+#undef USERCONFIG
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644 (file)
index 0000000..8d482fd
--- /dev/null
@@ -0,0 +1,181 @@
+dnl aclocal.m4 generated automatically by aclocal 1.4
+
+dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+dnl PARTICULAR PURPOSE.
+
+# Configure paths for SDL
+# Sam Lantinga 9/21/99
+# stolen from Manish Singh
+# stolen back from Frank Belew
+# stolen from Manish Singh
+# Shamelessly stolen from Owen Taylor
+
+dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS
+dnl
+AC_DEFUN(AM_PATH_SDL,
+[dnl 
+dnl Get the cflags and libraries from the sdl-config script
+dnl
+AC_ARG_WITH(sdl-prefix,[  --with-sdl-prefix=PFX   Prefix where SDL is installed (optional)],
+            sdl_prefix="$withval", sdl_prefix="")
+AC_ARG_WITH(sdl-exec-prefix,[  --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)],
+            sdl_exec_prefix="$withval", sdl_exec_prefix="")
+AC_ARG_ENABLE(sdltest, [  --disable-sdltest       Do not try to compile and run a test SDL program],
+                   , enable_sdltest=yes)
+
+  if test x$sdl_exec_prefix != x ; then
+     sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix"
+     if test x${SDL_CONFIG+set} != xset ; then
+        SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config
+     fi
+  fi
+  if test x$sdl_prefix != x ; then
+     sdl_args="$sdl_args --prefix=$sdl_prefix"
+     if test x${SDL_CONFIG+set} != xset ; then
+        SDL_CONFIG=$sdl_prefix/bin/sdl-config
+     fi
+  fi
+
+  AC_PATH_PROG(SDL_CONFIG, sdl-config, no)
+  min_sdl_version=ifelse([$1], ,0.11.0,$1)
+  AC_MSG_CHECKING(for SDL - version >= $min_sdl_version)
+  no_sdl=""
+  if test "$SDL_CONFIG" = "no" ; then
+    no_sdl=yes
+  else
+    SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags`
+    SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs`
+
+    sdl_major_version=`$SDL_CONFIG $sdl_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+    if test "x$enable_sdltest" = "xyes" ; then
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $SDL_CFLAGS"
+      LIBS="$LIBS $SDL_LIBS"
+dnl
+dnl Now check if the installed SDL is sufficiently new. (Also sanity
+dnl checks the results of sdl-config to some extent
+dnl
+      rm -f conf.sdltest
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "SDL.h"
+
+char*
+my_strdup (char *str)
+{
+  char *new_str;
+  
+  if (str)
+    {
+      new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char));
+      strcpy (new_str, str);
+    }
+  else
+    new_str = NULL;
+  
+  return new_str;
+}
+
+int main (int argc, char *argv[])
+{
+  int major, minor, micro;
+  char *tmp_version;
+
+  /* This hangs on some systems (?)
+  system ("touch conf.sdltest");
+  */
+  { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); }
+
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = my_strdup("$min_sdl_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string\n", "$min_sdl_version");
+     exit(1);
+   }
+
+   if (($sdl_major_version > major) ||
+      (($sdl_major_version == major) && ($sdl_minor_version > minor)) ||
+      (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro)))
+    {
+      return 0;
+    }
+  else
+    {
+      printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version);
+      printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro);
+      printf("*** best to upgrade to the required version.\n");
+      printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n");
+      printf("*** to point to the correct copy of sdl-config, and remove the file\n");
+      printf("*** config.cache before re-running configure\n");
+      return 1;
+    }
+}
+
+],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+  if test "x$no_sdl" = x ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$2], , :, [$2])     
+  else
+     AC_MSG_RESULT(no)
+     if test "$SDL_CONFIG" = "no" ; then
+       echo "*** The sdl-config script installed by SDL could not be found"
+       echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the SDL_CONFIG environment variable to the"
+       echo "*** full path to sdl-config."
+     else
+       if test -f conf.sdltest ; then
+        :
+       else
+          echo "*** Could not run SDL test program, checking why..."
+          CFLAGS="$CFLAGS $SDL_CFLAGS"
+          LIBS="$LIBS $SDL_LIBS"
+          AC_TRY_LINK([
+#include <stdio.h>
+#include "SDL.h"
+],      [ return 0; ],
+        [ echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding SDL or finding the wrong"
+          echo "*** version of SDL. If it is not finding SDL, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+         echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means SDL was incorrectly installed"
+          echo "*** or that you have moved SDL since it was installed. In the latter case, you"
+          echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ])
+          CFLAGS="$ac_save_CFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+     SDL_CFLAGS=""
+     SDL_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(SDL_CFLAGS)
+  AC_SUBST(SDL_LIBS)
+  rm -f conf.sdltest
+])
+
diff --git a/base/am_map.c b/base/am_map.c
new file mode 100644 (file)
index 0000000..2dcac74
--- /dev/null
@@ -0,0 +1,1516 @@
+
+// AM_map.c
+
+#include "doomdef.h"
+#include "p_local.h"
+#include "am_map.h"
+#include "am_data.h"
+#include <stdio.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
+
+vertex_t KeyPoints[NUMKEYS];
+
+#define NUMALIAS 3 // Number of antialiased lines.
+
+char *LevelNames[] =
+{
+       // EPISODE 1 - THE CITY OF THE DAMNED
+       "E1M1:  THE DOCKS",
+       "E1M2:  THE DUNGEONS",
+       "E1M3:  THE GATEHOUSE",
+       "E1M4:  THE GUARD TOWER",
+       "E1M5:  THE CITADEL",
+       "E1M6:  THE CATHEDRAL",
+       "E1M7:  THE CRYPTS",
+       "E1M8:  HELL'S MAW",
+       "E1M9:  THE GRAVEYARD",
+       // EPISODE 2 - HELL'S MAW
+       "E2M1:  THE CRATER",
+       "E2M2:  THE LAVA PITS",
+       "E2M3:  THE RIVER OF FIRE",
+       "E2M4:  THE ICE GROTTO",
+       "E2M5:  THE CATACOMBS",
+       "E2M6:  THE LABYRINTH",
+       "E2M7:  THE GREAT HALL",
+       "E2M8:  THE PORTALS OF CHAOS",
+       "E2M9:  THE GLACIER",
+       // EPISODE 3 - THE DOME OF D'SPARIL
+       "E3M1:  THE STOREHOUSE",
+       "E3M2:  THE CESSPOOL",
+       "E3M3:  THE CONFLUENCE",
+       "E3M4:  THE AZURE FORTRESS",
+       "E3M5:  THE OPHIDIAN LAIR",
+       "E3M6:  THE HALLS OF FEAR",
+       "E3M7:  THE CHASM",
+       "E3M8:  D'SPARIL'S KEEP",
+       "E3M9:  THE AQUIFER",
+       // EPISODE 4: THE OSSUARY
+       "E4M1:  CATAFALQUE",
+       "E4M2:  BLOCKHOUSE",
+       "E4M3:  AMBULATORY",
+       "E4M4:  SEPULCHER",
+       "E4M5:  GREAT STAIR",
+       "E4M6:  HALLS OF THE APOSTATE",
+       "E4M7:  RAMPARTS OF PERDITION",
+       "E4M8:  SHATTERED BRIDGE",
+       "E4M9:  MAUSOLEUM",
+       // EPISODE 5: THE STAGNANT DEMESNE
+       "E5M1:  OCHRE CLIFFS",
+       "E5M2:  RAPIDS",
+       "E5M3:  QUAY",
+       "E5M4:  COURTYARD",
+       "E5M5:  HYDRATYR",
+       "E5M6:  COLONNADE",
+       "E5M7:  FOETID MANSE",
+       "E5M8:  FIELD OF JUDGEMENT",
+       "E5M9:  SKEIN OF D'SPARIL"
+};
+
+static int cheating = 0;
+static int grid = 0;
+
+static int leveljuststarted = 1; // kluge until AM_LevelInit() is called
+
+boolean    automapactive = false;
+static int finit_width = SCREENWIDTH;
+static int finit_height = SCREENHEIGHT-42;
+static int f_x, f_y; // location of window on screen
+static int f_w, f_h; // size of window on screen
+static int lightlev; // used for funky strobing effect
+static byte *fb; // pseudo-frame buffer
+static int amclock;
+
+static mpoint_t m_paninc; // how far the window pans each tic (map coords)
+static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords)
+static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords)
+
+static fixed_t m_x, m_y;   // LL x,y where the window is on the map (map coords)
+static fixed_t m_x2, m_y2; // UR x,y where the window is on the map (map coords)
+
+// width/height of window on map (map coords)
+static fixed_t m_w, m_h;
+static fixed_t min_x, min_y; // based on level size
+static fixed_t max_x, max_y; // based on level size
+static fixed_t max_w, max_h; // max_x-min_x, max_y-min_y
+static fixed_t min_w, min_h; // based on player size
+static fixed_t min_scale_mtof; // used to tell when to stop zooming out
+static fixed_t max_scale_mtof; // used to tell when to stop zooming in
+
+// old stuff for recovery later
+static fixed_t old_m_w, old_m_h;
+static fixed_t old_m_x, old_m_y;
+
+// old location used by the Follower routine
+static mpoint_t f_oldloc;
+
+// used by MTOF to scale from map-to-frame-buffer coords
+static fixed_t scale_mtof = INITSCALEMTOF;
+// used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof)
+static fixed_t scale_ftom;
+
+static player_t *plr; // the player represented by an arrow
+static vertex_t oldplr;
+
+//static patch_t *marknums[10]; // numbers used for marking by the automap
+//static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are
+//static int markpointnum = 0; // next point to be assigned
+
+static int followplayer = 1; // specifies whether to follow the player around
+
+static char cheat_amap[] = { 'r','a','v','m','a','p' };
+
+static byte cheatcount=0;
+
+extern boolean viewactive;
+
+static byte antialias[NUMALIAS][8]=
+{
+       {96, 97, 98, 99, 100, 101, 102, 103},
+       {110, 109, 108, 107, 106, 105, 104, 103},
+       {75, 76, 77, 78, 79, 80, 81, 103}
+};
+/*
+static byte *aliasmax[NUMALIAS] = {
+       &antialias[0][7], &antialias[1][7], &antialias[2][7]
+};*/
+
+#ifndef RENDER3D
+static byte *maplump; // pointer to the raw data for the automap background.
+#endif
+
+static short mapystart=0; // y-value for the start of the map bitmap...used in the paralax stuff.
+static short mapxstart=0; //x-value for the bitmap.
+
+//byte screens[][SCREENWIDTH*SCREENHEIGHT];
+//void V_MarkRect (int x, int y, int width, int height);
+
+// Functions
+
+void DrawWuLine(int X0, int Y0, int X1, int Y1, byte *BaseColor,
+       int NumLevels, unsigned short IntensityBits);
+
+// Calculates the slope and slope according to the x-axis of a line
+// segment in map coordinates (with the upright y-axis n' all) so
+// that it can be used with the brain-dead drawing stuff.
+
+// Ripped out for Heretic
+/*
+void AM_getIslope(mline_t *ml, islope_t *is)
+{
+  int dx, dy;
+
+  dy = ml->a.y - ml->b.y;
+  dx = ml->b.x - ml->a.x;
+  if (!dy) is->islp = (dx<0?-MAXINT:MAXINT);
+  else is->islp = FixedDiv(dx, dy);
+  if (!dx) is->slp = (dy<0?-MAXINT:MAXINT);
+  else is->slp = FixedDiv(dy, dx);
+}
+*/
+
+void AM_activateNewScale(void)
+{
+  m_x += m_w/2;
+  m_y += m_h/2;
+  m_w = FTOM(f_w);
+  m_h = FTOM(f_h);
+  m_x -= m_w/2;
+  m_y -= m_h/2;
+  m_x2 = m_x + m_w;
+  m_y2 = m_y + m_h;
+}
+
+void AM_saveScaleAndLoc(void)
+{
+  old_m_x = m_x;
+  old_m_y = m_y;
+  old_m_w = m_w;
+  old_m_h = m_h;
+}
+
+void AM_restoreScaleAndLoc(void)
+{
+
+  m_w = old_m_w;
+  m_h = old_m_h;
+  if (!followplayer)
+  {
+    m_x = old_m_x;
+    m_y = old_m_y;
+  } else {
+    m_x = plr->mo->x - m_w/2;
+    m_y = plr->mo->y - m_h/2;
+  }
+  m_x2 = m_x + m_w;
+  m_y2 = m_y + m_h;
+
+  // Change the scaling multipliers
+  scale_mtof = FixedDiv(f_w<<FRACBITS, m_w);
+  scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
+}
+
+// adds a marker at the current location
+
+/*
+void AM_addMark(void)
+{
+  markpoints[markpointnum].x = m_x + m_w/2;
+  markpoints[markpointnum].y = m_y + m_h/2;
+  markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS;
+
+}
+*/
+void AM_findMinMaxBoundaries(void)
+{
+  int i;
+  fixed_t a, b;
+
+  min_x = min_y = MAXINT;
+  max_x = max_y = -MAXINT;
+  for (i=0;i<numvertexes;i++)
+  {
+    if (vertexes[i].x < min_x) min_x = vertexes[i].x;
+    else if (vertexes[i].x > max_x) max_x = vertexes[i].x;
+    if (vertexes[i].y < min_y) min_y = vertexes[i].y;
+    else if (vertexes[i].y > max_y) max_y = vertexes[i].y;
+  }
+  max_w = max_x - min_x;
+  max_h = max_y - min_y;
+  min_w = 2*PLAYERRADIUS;
+  min_h = 2*PLAYERRADIUS;
+
+  a = FixedDiv(f_w<<FRACBITS, max_w);
+  b = FixedDiv(f_h<<FRACBITS, max_h);
+  min_scale_mtof = a < b ? a : b;
+
+  max_scale_mtof = FixedDiv(f_h<<FRACBITS, 2*PLAYERRADIUS);
+
+}
+
+void AM_changeWindowLoc(void)
+{
+  if (m_paninc.x || m_paninc.y)
+  {
+    followplayer = 0;
+    f_oldloc.x = MAXINT;
+  }
+
+  m_x += m_paninc.x;
+  m_y += m_paninc.y;
+
+  if (m_x + m_w/2 > max_x)
+  {
+               m_x = max_x - m_w/2;
+               m_paninc.x=0;
+  }
+  else if (m_x + m_w/2 < min_x)
+  {
+               m_x = min_x - m_w/2;
+               m_paninc.x=0;
+  }
+  if (m_y + m_h/2 > max_y)
+  {
+               m_y = max_y - m_h/2;
+               m_paninc.y=0;
+  }
+  else if (m_y + m_h/2 < min_y)
+  {
+               m_y = min_y - m_h/2;
+               m_paninc.y=0;
+  }
+/*
+  mapxstart += MTOF(m_paninc.x+FRACUNIT/2);
+  mapystart -= MTOF(m_paninc.y+FRACUNIT/2);
+  if(mapxstart >= finit_width)
+               mapxstart -= finit_width;
+  if(mapxstart < 0)
+               mapxstart += finit_width;
+  if(mapystart >= finit_height)
+               mapystart -= finit_height;
+  if(mapystart < 0)
+               mapystart += finit_height;
+*/
+  m_x2 = m_x + m_w;
+  m_y2 = m_y + m_h;
+}
+
+void AM_initVariables(void)
+{
+  int pnum;
+       thinker_t *think;
+       mobj_t *mo;
+
+  //static event_t st_notify = { ev_keyup, AM_MSGENTERED };
+
+  automapactive = true;
+  fb = screen;
+
+  f_oldloc.x = MAXINT;
+  amclock = 0;
+  lightlev = 0;
+
+  m_paninc.x = m_paninc.y = 0;
+  ftom_zoommul = FRACUNIT;
+  mtof_zoommul = FRACUNIT;
+
+  m_w = FTOM(f_w);
+  m_h = FTOM(f_h);
+
+  // find player to center on initially
+  if (!playeringame[pnum = consoleplayer])
+    for (pnum=0;pnum<MAXPLAYERS;pnum++) if (playeringame[pnum]) break;
+  plr = &players[pnum];
+  oldplr.x = plr->mo->x;
+  oldplr.y = plr->mo->y;
+  m_x = plr->mo->x - m_w/2;
+  m_y = plr->mo->y - m_h/2;
+  AM_changeWindowLoc();
+
+  // for saving & restoring
+  old_m_x = m_x;
+  old_m_y = m_y;
+  old_m_w = m_w;
+  old_m_h = m_h;
+
+       // load in the location of keys, if in baby mode
+
+       memset(KeyPoints, 0, sizeof(vertex_t)*3);
+       if(gameskill == sk_baby)
+       {
+               for(think = thinkercap.next; think != &thinkercap; think = think->next)
+               {
+                       if(think->function != P_MobjThinker)
+                       { //not a mobj
+                               continue;
+                       }
+                       mo = (mobj_t *)think;
+                       if(mo->type == MT_CKEY)
+                       {
+                               KeyPoints[0].x = mo->x;
+                               KeyPoints[0].y = mo->y;
+                       }
+                       else if(mo->type == MT_AKYY)
+                       {
+                               KeyPoints[1].x = mo->x;
+                               KeyPoints[1].y = mo->y;
+                       }
+                       else if(mo->type == MT_BKYY)
+                       {
+                               KeyPoints[2].x = mo->x;
+                               KeyPoints[2].y = mo->y;
+                       }
+               }
+       }
+
+  // inform the status bar of the change
+//c  ST_Responder(&st_notify);
+}
+
+void AM_loadPics(void)
+{
+  //int i;
+  //char namebuf[9];
+/*  for (i=0;i<10;i++)
+  {
+    sprintf(namebuf, "AMMNUM%d", i);
+    marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
+  }*/
+#ifdef RENDER3D
+       maplumpnum = W_GetNumForName("AUTOPAGE");
+#else
+  maplump = W_CacheLumpName("AUTOPAGE", PU_STATIC);
+#endif
+}
+
+/*void AM_unloadPics(void)
+{
+  int i;
+  for (i=0;i<10;i++) Z_ChangeTag(marknums[i], PU_CACHE);
+
+}*/
+
+/*
+void AM_clearMarks(void)
+{
+  int i;
+  for (i=0;i<AM_NUMMARKPOINTS;i++) markpoints[i].x = -1; // means empty
+  markpointnum = 0;
+}
+*/
+
+// should be called at the start of every level
+// right now, i figure it out myself
+
+void AM_LevelInit(void)
+{
+  leveljuststarted = 0;
+
+  f_x = f_y = 0;
+  f_w = finit_width;
+  f_h = finit_height;
+       mapxstart = mapystart = 0;
+
+
+//  AM_clearMarks();
+
+  AM_findMinMaxBoundaries();
+  scale_mtof = FixedDiv(min_scale_mtof, (int) (0.7*FRACUNIT));
+  if (scale_mtof > max_scale_mtof) scale_mtof = min_scale_mtof;
+  scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
+}
+
+static boolean stopped = true;
+
+void AM_Stop (void)
+{
+  //static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED };
+
+//  AM_unloadPics();
+  automapactive = false;
+//  ST_Responder(&st_notify);
+  stopped = true;
+       BorderNeedRefresh = true;
+}
+
+void AM_Start (void)
+{
+  static int lastlevel = -1, lastepisode = -1;
+
+  if (!stopped) AM_Stop();
+  stopped = false;
+  if(gamestate != GS_LEVEL)
+  {
+               return; // don't show automap if we aren't in a game!
+  }
+  if (lastlevel != gamemap || lastepisode != gameepisode)
+  {
+    AM_LevelInit();
+    lastlevel = gamemap;
+    lastepisode = gameepisode;
+  }
+  AM_initVariables();
+  AM_loadPics();
+}
+
+// set the window scale to the maximum size
+
+void AM_minOutWindowScale(void)
+{
+  scale_mtof = min_scale_mtof;
+  scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
+  AM_activateNewScale();
+}
+
+// set the window scale to the minimum size
+
+void AM_maxOutWindowScale(void)
+{
+  scale_mtof = max_scale_mtof;
+  scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
+  AM_activateNewScale();
+}
+
+boolean AM_Responder (event_t *ev)
+{
+  int rc;
+  static int cheatstate=0;
+  static int bigstate=0;
+
+  rc = false;
+  if (!automapactive)
+  {
+    if (ev->type == ev_keydown && ev->data1 == AM_STARTKEY
+               && gamestate == GS_LEVEL)
+    {
+      AM_Start ();
+               viewactive = false;
+//      viewactive = true;
+      rc = true;
+    }
+  }
+  else if (ev->type == ev_keydown)
+  {
+
+    rc = true;
+    switch(ev->data1)
+    {
+      case AM_PANRIGHTKEY: // pan right
+       if (!followplayer) m_paninc.x = FTOM(F_PANINC);
+       else rc = false;
+       break;
+      case AM_PANLEFTKEY: // pan left
+       if (!followplayer) m_paninc.x = -FTOM(F_PANINC);
+       else rc = false;
+       break;
+      case AM_PANUPKEY: // pan up
+       if (!followplayer) m_paninc.y = FTOM(F_PANINC);
+       else rc = false;
+       break;
+      case AM_PANDOWNKEY: // pan down
+       if (!followplayer) m_paninc.y = -FTOM(F_PANINC);
+       else rc = false;
+       break;
+      case AM_ZOOMOUTKEY: // zoom out
+       mtof_zoommul = M_ZOOMOUT;
+       ftom_zoommul = M_ZOOMIN;
+       break;
+      case AM_ZOOMINKEY: // zoom in
+       mtof_zoommul = M_ZOOMIN;
+       ftom_zoommul = M_ZOOMOUT;
+       break;
+      case AM_ENDKEY:
+       bigstate = 0;
+       viewactive = true;
+       AM_Stop ();
+       break;
+      case AM_GOBIGKEY:
+       bigstate = !bigstate;
+       if (bigstate)
+       {
+         AM_saveScaleAndLoc();
+         AM_minOutWindowScale();
+       }
+       else AM_restoreScaleAndLoc();
+       break;
+      case AM_FOLLOWKEY:
+       followplayer = !followplayer;
+       f_oldloc.x = MAXINT;
+       P_SetMessage(plr, followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF, true);
+       break;
+/*
+      case AM_GRIDKEY:
+       grid = !grid;
+       plr->message = grid ? AMSTR_GRIDON : AMSTR_GRIDOFF;
+       break;
+      case AM_MARKKEY:
+       sprintf(buffer, "%s %d", AMSTR_MARKEDSPOT, markpointnum);
+       plr->message = buffer;
+       AM_addMark();
+       break;
+      case AM_CLEARMARKKEY:
+       AM_clearMarks();
+       plr->message = AMSTR_MARKSCLEARED;
+       break;
+*/
+      default:
+       cheatstate=0;
+       rc = false;
+    }
+   if(cheat_amap[cheatcount]==ev->data1 && !netgame)
+               cheatcount++;
+       else
+               cheatcount=0;
+       if(cheatcount==6)
+   {
+               cheatcount=0;
+               rc = false;
+      cheating = (cheating+1) % 3;
+    }
+  }
+
+  else if (ev->type == ev_keyup)
+  {
+    rc = false;
+    switch (ev->data1)
+    {
+      case AM_PANRIGHTKEY:
+       if (!followplayer) m_paninc.x = 0;
+       break;
+      case AM_PANLEFTKEY:
+       if (!followplayer) m_paninc.x = 0;
+       break;
+      case AM_PANUPKEY:
+       if (!followplayer) m_paninc.y = 0;
+       break;
+      case AM_PANDOWNKEY:
+       if (!followplayer) m_paninc.y = 0;
+       break;
+      case AM_ZOOMOUTKEY:
+      case AM_ZOOMINKEY:
+       mtof_zoommul = FRACUNIT;
+       ftom_zoommul = FRACUNIT;
+       break;
+    }
+  }
+
+  return rc;
+
+}
+
+void AM_changeWindowScale(void)
+{
+
+  // Change the scaling multipliers
+  scale_mtof = FixedMul(scale_mtof, mtof_zoommul);
+  scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
+
+  if (scale_mtof < min_scale_mtof) AM_minOutWindowScale();
+  else if (scale_mtof > max_scale_mtof) AM_maxOutWindowScale();
+  else AM_activateNewScale();
+}
+
+void AM_doFollowPlayer(void)
+{
+  if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y)
+  {
+//  m_x = FTOM(MTOF(plr->mo->x - m_w/2));
+//  m_y = FTOM(MTOF(plr->mo->y - m_h/2));
+//  m_x = plr->mo->x - m_w/2;
+//  m_y = plr->mo->y - m_h/2;
+    m_x = FTOM(MTOF(plr->mo->x)) - m_w/2;
+    m_y = FTOM(MTOF(plr->mo->y)) - m_h/2;
+    m_x2 = m_x + m_w;
+    m_y2 = m_y + m_h;
+
+        // do the parallax parchment scrolling.
+/*
+        dmapx = (MTOF(plr->mo->x)-MTOF(f_oldloc.x)); //fixed point
+        dmapy = (MTOF(f_oldloc.y)-MTOF(plr->mo->y));
+
+        if(f_oldloc.x == MAXINT) //to eliminate an error when the user first
+               dmapx=0;  //goes into the automap.
+        mapxstart += dmapx;
+        mapystart += dmapy;
+
+        while(mapxstart >= finit_width)
+                       mapxstart -= finit_width;
+    while(mapxstart < 0)
+                       mapxstart += finit_width;
+    while(mapystart >= finit_height)
+                       mapystart -= finit_height;
+    while(mapystart < 0)
+                       mapystart += finit_height;
+*/
+        f_oldloc.x = plr->mo->x;
+    f_oldloc.y = plr->mo->y;
+  }
+}
+
+// Ripped out for Heretic
+/*
+void AM_updateLightLev(void)
+{
+  static nexttic = 0;
+//static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 };
+  static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 };
+  static int litelevelscnt = 0;
+
+  // Change light level
+  if (amclock>nexttic)
+  {
+    lightlev = litelevels[litelevelscnt++];
+    if (litelevelscnt == sizeof(litelevels)/sizeof(int)) litelevelscnt = 0;
+    nexttic = amclock + 6 - (amclock % 6);
+  }
+}
+*/
+
+void AM_Ticker (void)
+{
+
+  if (!automapactive) return;
+
+  amclock++;
+
+  if (followplayer) AM_doFollowPlayer();
+
+  // Change the zoom if necessary
+  if (ftom_zoommul != FRACUNIT) AM_changeWindowScale();
+
+  // Change x,y location
+  if (m_paninc.x || m_paninc.y) AM_changeWindowLoc();
+  // Update light level
+// AM_updateLightLev();
+
+}
+
+void AM_clearFB(int color)
+{
+#ifndef RENDER3D
+       int i, j;
+#endif
+       int dmapx;
+       int dmapy;
+#ifdef RENDER3D
+       float scalar;
+       int lump;
+#endif
+
+       if(followplayer)
+       {
+               dmapx = (MTOF(plr->mo->x)-MTOF(oldplr.x)); //fixed point
+               dmapy = (MTOF(oldplr.y)-MTOF(plr->mo->y));
+
+               oldplr.x = plr->mo->x;
+               oldplr.y = plr->mo->y;
+//             if(f_oldloc.x == MAXINT) //to eliminate an error when the user first
+//                     dmapx=0;  //goes into the automap.
+               mapxstart += dmapx>>1;
+               mapystart += dmapy>>1;
+
+               while(mapxstart >= finit_width)
+                       mapxstart -= finit_width;
+          while(mapxstart < 0)
+                       mapxstart += finit_width;
+          while(mapystart >= finit_height)
+                       mapystart -= finit_height;
+          while(mapystart < 0)
+                       mapystart += finit_height;
+       }
+       else
+       {
+               mapxstart += (MTOF(m_paninc.x)>>1);
+               mapystart -= (MTOF(m_paninc.y)>>1);
+               if(mapxstart >= finit_width)
+                       mapxstart -= finit_width;
+               if(mapxstart < 0)
+                       mapxstart += finit_width;
+               if(mapystart >= finit_height)
+               mapystart -= finit_height;
+               if(mapystart < 0)
+               mapystart += finit_height;
+       }
+
+#ifdef RENDER3D
+       OGL_SetColorAndAlpha(1, 1, 1, 1);
+       if (shareware)
+       {
+               OGL_SetFlat(W_GetNumForName ("FLOOR04")-firstflat);
+       }
+       else
+       {
+               OGL_SetFlat(W_GetNumForName("FLAT513")-firstflat);
+       }
+       scalar = sbarscale/20.0;
+       OGL_DrawCutRectTiled(0, finit_height+4, 320, 200-finit_height-4, 64, 64,
+       160-160*scalar+1, finit_height, 320*scalar-2, 200-finit_height);
+
+       OGL_SetPatch(lump=W_GetNumForName("bordb"));
+       OGL_DrawCutRectTiled(0, finit_height, 320, 4,
+       lumptexsizes[lump].w, lumptexsizes[lump].h,
+       160-160*scalar+1, finit_height, 320*scalar-2, 4);
+
+       OGL_SetRawImage(maplumpnum, 0); // We only want the left portion.
+       OGL_DrawRectTiled( 0, 0, finit_width,
+                      /*(sbarscale<20)?200:*/ finit_height,
+       128, 128);
+#else
+       //blit the automap background to the screen.
+       j=mapystart*finit_width;
+       for(i=0;i<finit_height;i++)
+       {
+               memcpy(screen+i*finit_width, maplump+j+mapxstart, finit_width-mapxstart);
+               memcpy(screen+i*finit_width+finit_width-mapxstart, maplump+j, mapxstart);
+               j += finit_width;
+               if(j >= finit_height*finit_width)
+                       j=0;
+       }
+
+//      memcpy(screen, maplump, finit_width*finit_height);
+//  memset(fb, color, f_w*f_h);
+#endif
+}
+
+// Based on Cohen-Sutherland clipping algorithm but with a slightly
+// faster reject and precalculated slopes.  If I need the speed, will
+// hash algorithm to the common cases.
+
+boolean AM_clipMline(mline_t *ml, fline_t *fl)
+{
+  enum { LEFT=1, RIGHT=2, BOTTOM=4, TOP=8 };
+  int outcode1 = 0, outcode2 = 0, outside;
+  fpoint_t tmp;
+  int dx, dy;
+
+#define DOOUTCODE(oc, mx, my) \
+  (oc) = 0; \
+  if ((my) < 0) (oc) |= TOP; \
+  else if ((my) >= f_h) (oc) |= BOTTOM; \
+  if ((mx) < 0) (oc) |= LEFT; \
+  else if ((mx) >= f_w) (oc) |= RIGHT
+
+  // do trivial rejects and outcodes
+  if (ml->a.y > m_y2) outcode1 = TOP;
+  else if (ml->a.y < m_y) outcode1 = BOTTOM;
+  if (ml->b.y > m_y2) outcode2 = TOP;
+  else if (ml->b.y < m_y) outcode2 = BOTTOM;
+  if (outcode1 & outcode2) return false; // trivially outside
+
+  if (ml->a.x < m_x) outcode1 |= LEFT;
+  else if (ml->a.x > m_x2) outcode1 |= RIGHT;
+  if (ml->b.x < m_x) outcode2 |= LEFT;
+  else if (ml->b.x > m_x2) outcode2 |= RIGHT;
+  if (outcode1 & outcode2) return false; // trivially outside
+
+  // transform to frame-buffer coordinates.
+  fl->a.x = CXMTOF(ml->a.x);
+  fl->a.y = CYMTOF(ml->a.y);
+  fl->b.x = CXMTOF(ml->b.x);
+  fl->b.y = CYMTOF(ml->b.y);
+  DOOUTCODE(outcode1, fl->a.x, fl->a.y);
+  DOOUTCODE(outcode2, fl->b.x, fl->b.y);
+  if (outcode1 & outcode2) return false;
+
+  while (outcode1 | outcode2)
+  {
+    // may be partially inside box
+    // find an outside point
+    if (outcode1) outside = outcode1;
+    else outside = outcode2;
+    // clip to each side
+    if (outside & TOP)
+    {
+      dy = fl->a.y - fl->b.y;
+      dx = fl->b.x - fl->a.x;
+      tmp.x = fl->a.x + (dx*(fl->a.y))/dy;
+      tmp.y = 0;
+    }
+    else if (outside & BOTTOM)
+    {
+      dy = fl->a.y - fl->b.y;
+      dx = fl->b.x - fl->a.x;
+      tmp.x = fl->a.x + (dx*(fl->a.y-f_h))/dy;
+      tmp.y = f_h-1;
+    }
+    else if (outside & RIGHT)
+    {
+      dy = fl->b.y - fl->a.y;
+      dx = fl->b.x - fl->a.x;
+      tmp.y = fl->a.y + (dy*(f_w-1 - fl->a.x))/dx;
+      tmp.x = f_w-1;
+    }
+    else if (outside & LEFT)
+    {
+      dy = fl->b.y - fl->a.y;
+      dx = fl->b.x - fl->a.x;
+      tmp.y = fl->a.y + (dy*(-fl->a.x))/dx;
+      tmp.x = 0;
+    }
+    if (outside == outcode1)
+    {
+      fl->a = tmp;
+      DOOUTCODE(outcode1, fl->a.x, fl->a.y);
+    } else {
+      fl->b = tmp;
+      DOOUTCODE(outcode2, fl->b.x, fl->b.y);
+    }
+    if (outcode1 & outcode2) return false; // trivially outside
+  }
+
+  return true;
+}
+#undef DOOUTCODE
+
+// Classic Bresenham w/ whatever optimizations I need for speed
+
+void AM_drawFline(fline_t *fl, int color)
+{
+
+  register int x, y, dx, dy, sx, sy, ax, ay, d;
+  static int fuck = 0;
+
+  switch(color)
+  {
+               case WALLCOLORS:
+                       DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[0][0], 8, 3);
+                       break;
+               case FDWALLCOLORS:
+                       DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[1][0], 8, 3);
+                       break;
+               case CDWALLCOLORS:
+                       DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[2][0], 8, 3);
+                       break;
+               default:
+               {
+                                       // For debugging only
+                               if (   fl->a.x < 0 || fl->a.x >= f_w
+                               || fl->a.y < 0 || fl->a.y >= f_h
+                               || fl->b.x < 0 || fl->b.x >= f_w
+                               || fl->b.y < 0 || fl->b.y >= f_h)
+                               {
+                               fprintf(stderr, "fuck %d \r", fuck++);
+                               return;
+                               }
+
+                               #define DOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc) //the MACRO!
+
+                               dx = fl->b.x - fl->a.x;
+                               ax = 2 * (dx<0 ? -dx : dx);
+                               sx = dx<0 ? -1 : 1;
+
+                               dy = fl->b.y - fl->a.y;
+                               ay = 2 * (dy<0 ? -dy : dy);
+                               sy = dy<0 ? -1 : 1;
+
+                               x = fl->a.x;
+                               y = fl->a.y;
+
+                               if (ax > ay)
+                               {
+                               d = ay - ax/2;
+                               while (1)
+                               {
+                               DOT(x,y,color);
+                               if (x == fl->b.x) return;
+                               if (d>=0)
+                               {
+                                       y += sy;
+                                       d -= ax;
+                               }
+                               x += sx;
+                               d += ay;
+                               }
+                               } else {
+                               d = ax - ay/2;
+                               while (1)
+                               {
+                               DOT(x, y, color);
+                               if (y == fl->b.y) return;
+                               if (d >= 0)
+                               {
+                                       x += sx;
+                                       d -= ay;
+                               }
+                               y += sy;
+                               d += ax;
+                               }
+                               }
+               }
+  }
+}
+
+/* Wu antialiased line drawer.
+ * (X0,Y0),(X1,Y1) = line to draw
+ * BaseColor = color # of first color in block used for antialiasing, the
+ *          100% intensity version of the drawing color
+ * NumLevels = size of color block, with BaseColor+NumLevels-1 being the
+ *          0% intensity version of the drawing color
+ * IntensityBits = log base 2 of NumLevels; the # of bits used to describe
+ *          the intensity of the drawing color. 2**IntensityBits==NumLevels
+ */
+void PUTDOT(short xx,short yy,byte *cc, byte *cm)
+{
+       static int oldyy;
+       static int oldyyshifted;
+       byte *oldcc=cc;
+
+       if(xx < 32)
+               cc += 7-(xx>>2);
+       else if(xx > (finit_width - 32))
+               cc += 7-((finit_width-xx) >> 2);
+//     if(cc==oldcc) //make sure that we don't double fade the corners.
+//     {
+               if(yy < 32)
+                       cc += 7-(yy>>2);
+               else if(yy > (finit_height - 32))
+                       cc += 7-((finit_height-yy) >> 2);
+//     }
+       if(cc > cm && cm != NULL)
+       {
+               cc = cm;
+       }
+       else if(cc > oldcc+6) // don't let the color escape from the fade table...
+       {
+               cc=oldcc+6;
+       }
+       if(yy == oldyy+1)
+       {
+               oldyy++;
+               oldyyshifted += 320;
+       }
+       else if(yy == oldyy-1)
+       {
+               oldyy--;
+               oldyyshifted -= 320;
+       }
+       else if(yy != oldyy)
+       {
+               oldyy = yy;
+               oldyyshifted = yy*320;
+       }
+       fb[oldyyshifted+xx] = *(cc);
+//     fb[(yy)*f_w+(xx)]=*(cc);
+}
+
+void DrawWuLine(int X0, int Y0, int X1, int Y1, byte *BaseColor,
+       int NumLevels, unsigned short IntensityBits)
+{
+   unsigned short IntensityShift, ErrorAdj, ErrorAcc;
+   unsigned short ErrorAccTemp, Weighting, WeightingComplementMask;
+   short DeltaX, DeltaY, Temp, XDir;
+
+   /* Make sure the line runs top to bottom */
+   if (Y0 > Y1) {
+      Temp = Y0; Y0 = Y1; Y1 = Temp;
+      Temp = X0; X0 = X1; X1 = Temp;
+   }
+   /* Draw the initial pixel, which is always exactly intersected by
+      the line and so needs no weighting */
+   PUTDOT(X0, Y0, &BaseColor[0], NULL);
+
+   if ((DeltaX = X1 - X0) >= 0) {
+      XDir = 1;
+   } else {
+      XDir = -1;
+      DeltaX = -DeltaX; /* make DeltaX positive */
+   }
+   /* Special-case horizontal, vertical, and diagonal lines, which
+      require no weighting because they go right through the center of
+      every pixel */
+   if ((DeltaY = Y1 - Y0) == 0) {
+      /* Horizontal line */
+      while (DeltaX-- != 0) {
+         X0 += XDir;
+         PUTDOT(X0, Y0, &BaseColor[0], NULL);
+      }
+      return;
+   }
+   if (DeltaX == 0) {
+      /* Vertical line */
+      do {
+         Y0++;
+         PUTDOT(X0, Y0, &BaseColor[0], NULL);
+      } while (--DeltaY != 0);
+      return;
+   }
+       //diagonal line.
+       if (DeltaX == DeltaY) {
+      do {
+         X0 += XDir;
+         Y0++;
+         PUTDOT(X0, Y0, &BaseColor[0], NULL);
+      } while (--DeltaY != 0);
+      return;
+   }
+   /* Line is not horizontal, diagonal, or vertical */
+   ErrorAcc = 0;  /* initialize the line error accumulator to 0 */
+   /* # of bits by which to shift ErrorAcc to get intensity level */
+   IntensityShift = 16 - IntensityBits;
+   /* Mask used to flip all bits in an intensity weighting, producing the
+      result (1 - intensity weighting) */
+   WeightingComplementMask = NumLevels - 1;
+   /* Is this an X-major or Y-major line? */
+   if (DeltaY > DeltaX) {
+      /* Y-major line; calculate 16-bit fixed-point fractional part of a
+         pixel that X advances each time Y advances 1 pixel, truncating the
+         result so that we won't overrun the endpoint along the X axis */
+      ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY;
+      /* Draw all pixels other than the first and last */
+      while (--DeltaY) {
+         ErrorAccTemp = ErrorAcc;   /* remember currrent accumulated error */
+         ErrorAcc += ErrorAdj;      /* calculate error for next pixel */
+         if (ErrorAcc <= ErrorAccTemp) {
+            /* The error accumulator turned over, so advance the X coord */
+            X0 += XDir;
+         }
+         Y0++; /* Y-major, so always advance Y */
+         /* The IntensityBits most significant bits of ErrorAcc give us the
+            intensity weighting for this pixel, and the complement of the
+            weighting for the paired pixel */
+         Weighting = ErrorAcc >> IntensityShift;
+                       PUTDOT(X0, Y0, &BaseColor[Weighting], &BaseColor[7]);
+         PUTDOT(X0 + XDir, Y0,
+               &BaseColor[(Weighting ^ WeightingComplementMask)], &BaseColor[7]);
+      }
+      /* Draw the final pixel, which is always exactly intersected by the line
+         and so needs no weighting */
+      PUTDOT(X1, Y1, &BaseColor[0], NULL);
+      return;
+   }
+   /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
+      pixel that Y advances each time X advances 1 pixel, truncating the
+      result to avoid overrunning the endpoint along the X axis */
+   ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX;
+   /* Draw all pixels other than the first and last */
+   while (--DeltaX) {
+      ErrorAccTemp = ErrorAcc;   /* remember currrent accumulated error */
+      ErrorAcc += ErrorAdj;      /* calculate error for next pixel */
+      if (ErrorAcc <= ErrorAccTemp) {
+         /* The error accumulator turned over, so advance the Y coord */
+         Y0++;
+      }
+      X0 += XDir; /* X-major, so always advance X */
+      /* The IntensityBits most significant bits of ErrorAcc give us the
+         intensity weighting for this pixel, and the complement of the
+         weighting for the paired pixel */
+      Weighting = ErrorAcc >> IntensityShift;
+      PUTDOT(X0, Y0, &BaseColor[Weighting], &BaseColor[7]);
+      PUTDOT(X0, Y0 + 1,
+               &BaseColor[(Weighting ^ WeightingComplementMask)], &BaseColor[7]);
+
+   }
+   /* Draw the final pixel, which is always exactly intersected by the line
+      and so needs no weighting */
+   PUTDOT(X1, Y1, &BaseColor[0], NULL);
+}
+
+void AM_drawMline(mline_t *ml, int color)
+{
+#ifdef RENDER3D
+    byte *palette = 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 );
+#else
+  static fline_t fl;
+
+  if (AM_clipMline(ml, &fl))
+    AM_drawFline(&fl, color); // draws it on frame buffer using fb coords
+#endif
+}
+
+void AM_drawGrid(int color)
+{
+  fixed_t x, y;
+  fixed_t start, end;
+  mline_t ml;
+
+  // Figure out start of vertical gridlines
+  start = m_x;
+  if ((start-bmaporgx)%(MAPBLOCKUNITS<<FRACBITS))
+    start += (MAPBLOCKUNITS<<FRACBITS)
+      - ((start-bmaporgx)%(MAPBLOCKUNITS<<FRACBITS));
+  end = m_x + m_w;
+
+  // draw vertical gridlines
+  ml.a.y = m_y;
+  ml.b.y = m_y+m_h;
+  for (x=start; x<end; x+=(MAPBLOCKUNITS<<FRACBITS))
+  {
+    ml.a.x = x;
+    ml.b.x = x;
+    AM_drawMline(&ml, color);
+  }
+
+  // Figure out start of horizontal gridlines
+  start = m_y;
+  if ((start-bmaporgy)%(MAPBLOCKUNITS<<FRACBITS))
+    start += (MAPBLOCKUNITS<<FRACBITS)
+      - ((start-bmaporgy)%(MAPBLOCKUNITS<<FRACBITS));
+  end = m_y + m_h;
+
+  // draw horizontal gridlines
+  ml.a.x = m_x;
+  ml.b.x = m_x + m_w;
+  for (y=start; y<end; y+=(MAPBLOCKUNITS<<FRACBITS))
+  {
+    ml.a.y = y;
+    ml.b.y = y;
+    AM_drawMline(&ml, color);
+  }
+}
+
+void AM_drawWalls(void)
+{
+  int i;
+
+#ifdef RENDER3D
+    //OGL_SetNoTexture();
+    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);
+    }
+    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) {
+         AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change
+       } else if (lines[i].backsector->ceilingheight
+                  != lines[i].frontsector->ceilingheight) {
+         AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change
+       } else if (cheating) {
+         AM_drawMline(&l, TSWALLCOLORS+lightlev);
+       }
+      }
+    } else if (plr->powers[pw_allmap])
+    {
+      if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS+3);
+    }
+  }
+#endif
+}
+
+void AM_rotate(fixed_t *x, fixed_t *y, angle_t a)
+{
+  fixed_t tmpx;
+
+  tmpx = FixedMul(*x,finecosine[a>>ANGLETOFINESHIFT])
+       - FixedMul(*y,finesine[a>>ANGLETOFINESHIFT]);
+  *y   = FixedMul(*x,finesine[a>>ANGLETOFINESHIFT])
+       + FixedMul(*y,finecosine[a>>ANGLETOFINESHIFT]);
+  *x = tmpx;
+}
+
+void AM_drawLineCharacter(mline_t *lineguy, int lineguylines, fixed_t scale,
+  angle_t angle, int color, fixed_t x, fixed_t y)
+{
+  int i;
+  mline_t l;
+
+  for (i=0;i<lineguylines;i++)
+  {
+    l.a.x = lineguy[i].a.x;
+    l.a.y = lineguy[i].a.y;
+    if (scale)
+    {
+      l.a.x = FixedMul(scale, l.a.x);
+      l.a.y = FixedMul(scale, l.a.y);
+    }
+    if (angle) AM_rotate(&l.a.x, &l.a.y, angle);
+    l.a.x += x;
+    l.a.y += y;
+
+    l.b.x = lineguy[i].b.x;
+    l.b.y = lineguy[i].b.y;
+    if (scale)
+    {
+      l.b.x = FixedMul(scale, l.b.x);
+      l.b.y = FixedMul(scale, l.b.y);
+    }
+    if (angle) AM_rotate(&l.b.x, &l.b.y, angle);
+    l.b.x += x;
+    l.b.y += y;
+
+    AM_drawMline(&l, color);
+  }
+
+}
+
+void AM_drawPlayers(void)
+{
+
+  int i;
+  player_t *p;
+  static int their_colors[] = { GREENKEY, YELLOWKEY, BLOODRED, BLUEKEY };
+  int their_color = -1;
+  int color;
+
+  if (!netgame)
+  {
+       /*
+    if (cheating) AM_drawLineCharacter(cheat_player_arrow, NUMCHEATPLYRLINES, 0,
+      plr->mo->angle, WHITE, plr->mo->x, plr->mo->y);
+        */ //cheat key player pointer is the same as non-cheat pointer..
+
+               AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, plr->mo->angle,
+      WHITE, plr->mo->x, plr->mo->y);
+    return;
+  }
+
+  for (i=0;i<MAXPLAYERS;i++)
+  {
+    their_color++;
+    p = &players[i];
+       if(deathmatch && !singledemo && p != plr)
+       {
+       continue;
+       }
+    if (!playeringame[i]) continue;
+    if (p->powers[pw_invisibility]) color = 102; // *close* to the automap color
+    else color = their_colors[their_color];
+    AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, p->mo->angle,
+      color, p->mo->x, p->mo->y);
+  }
+}
+
+void AM_drawThings(int colors, int colorrange)
+{
+  int i;
+  mobj_t *t;
+
+  for (i=0;i<numsectors;i++)
+  {
+    t = sectors[i].thinglist;
+    while (t)
+    {
+      AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES,
+       16<<FRACBITS, t->angle, colors+lightlev, t->x, t->y);
+      t = t->snext;
+    }
+  }
+}
+
+/*
+void AM_drawMarks(void)
+{
+  int i, fx, fy, w, h;
+
+  for (i=0;i<AM_NUMMARKPOINTS;i++)
+  {
+    if (markpoints[i].x != -1)
+    {
+      w = SHORT(marknums[i]->width);
+      h = SHORT(marknums[i]->height);
+      fx = CXMTOF(markpoints[i].x);
+      fy = CYMTOF(markpoints[i].y);
+      if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h)
+                       V_DrawPatch(fx, fy, marknums[i]);
+    }
+  }
+}
+*/
+
+void AM_drawkeys(void)
+{
+       if(KeyPoints[0].x != 0 || KeyPoints[0].y != 0)
+       {
+               AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, YELLOWKEY,
+                       KeyPoints[0].x, KeyPoints[0].y);
+       }
+       if(KeyPoints[1].x != 0 || KeyPoints[1].y != 0)
+       {
+               AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, GREENKEY,
+                       KeyPoints[1].x, KeyPoints[1].y);
+       }
+       if(KeyPoints[2].x != 0 || KeyPoints[2].y != 0)
+       {
+               AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, BLUEKEY,
+                       KeyPoints[2].x, KeyPoints[2].y);
+       }
+}
+
+void AM_drawCrosshair(int color)
+{
+  fb[(f_w*(f_h+1))/2] = color; // single point for now
+}
+
+#ifdef RENDER3D
+void AM_OGL_SetupState()
+{
+    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);
+}
+
+void AM_OGL_RestoreState()
+{
+    glPopAttrib();
+}      
+#endif
+
+void AM_Drawer(void)
+{
+       //int highestEpisode;   // unused variable - DDOI
+
+       if (!automapactive) return;
+#ifdef RENDER3D
+    // Update the height (in case sbarscale has been changed).
+    finit_height = SCREENHEIGHT-SBARHEIGHT*sbarscale/20/*-3*/;
+#endif
+
+  UpdateState |= I_FULLSCRN;
+
+#ifdef RENDER3D
+    AM_OGL_SetupState();
+#endif
+
+  AM_clearFB(BACKGROUND);
+  if (grid) AM_drawGrid(GRIDCOLORS);
+  AM_drawWalls();
+  AM_drawPlayers();
+  if (cheating==2) AM_drawThings(THINGCOLORS, THINGRANGE);
+//  AM_drawCrosshair(XHAIRCOLORS);
+
+//  AM_drawMarks();
+       if(gameskill == sk_baby)
+       {
+               AM_drawkeys();
+       }
+
+#ifdef RENDER3D
+    AM_OGL_RestoreState();
+#endif
+
+       if((gameepisode < (ExtendedWAD ? 6 : 4)) && gamemap < 10)
+       {
+               MN_DrTextA(LevelNames[(gameepisode-1)*9+gamemap-1], 20, 145);
+       }
+//  I_Update();
+//  V_MarkRect(f_x, f_y, f_w, f_h);
+}
diff --git a/base/ct_chat.c b/base/ct_chat.c
new file mode 100644 (file)
index 0000000..534169f
--- /dev/null
@@ -0,0 +1,462 @@
+//
+// Chat mode
+//
+
+#include <string.h>
+#include <ctype.h>
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+
+#define QUEUESIZE              128
+#define MESSAGESIZE    128
+#define MESSAGELEN     265
+
+#define CT_PLR_GREEN           1
+#define CT_PLR_YELLOW  2
+#define CT_PLR_RED             3
+#define CT_PLR_BLUE            4
+#define CT_PLR_ALL             5
+
+#define CT_KEY_GREEN           'g'
+#define CT_KEY_YELLOW  'y'
+#define CT_KEY_RED             'r'
+#define CT_KEY_BLUE            'b'
+#define CT_KEY_ALL             't'
+#define CT_ESCAPE                      6
+
+// Public data
+
+void CT_Init(void);
+void CT_Drawer(void);
+boolean CT_Responder(event_t *ev);
+void CT_Ticker(void);
+char CT_dequeueChatChar(void);
+
+boolean chatmodeon;
+
+// Private data
+
+void CT_queueChatChar(char ch);
+void CT_ClearChatMessage(int player);
+void CT_AddChar(int player, char c);
+void CT_BackSpace(int player);
+
+int head;
+int tail;
+byte ChatQueue[QUEUESIZE];
+int chat_dest[MAXPLAYERS];
+char chat_msg[MAXPLAYERS][MESSAGESIZE];
+char plr_lastmsg[MAXPLAYERS][MESSAGESIZE+9]; // add in the length of the pre-string
+int msgptr[MAXPLAYERS];
+int msglen[MAXPLAYERS];
+
+boolean cheated;
+
+static int FontABaseLump;
+
+char *CT_FromPlrText[MAXPLAYERS] =
+{
+       "GREEN:  ",
+       "YELLOW:  ",
+       "RED:  ",
+       "BLUE:  "
+};
+
+char *chat_macros[10];
+
+boolean altdown;
+boolean shiftdown;
+
+
+//===========================================================================
+//
+// CT_Init
+//
+//     Initialize chat mode data
+//===========================================================================
+
+void CT_Init(void)
+{
+       int i;
+
+       head = 0; //initialize the queue index
+       tail = 0;
+       chatmodeon = false;
+       memset(ChatQueue, 0, QUEUESIZE);
+       for(i = 0; i < MAXPLAYERS; i++)
+       {
+               chat_dest[i] = 0;
+               msgptr[i] = 0;
+               memset(plr_lastmsg[i], 0, MESSAGESIZE);
+               memset(chat_msg[i], 0, MESSAGESIZE);
+       }
+       FontABaseLump = W_GetNumForName("FONTA_S")+1;
+       return;
+}
+
+//===========================================================================
+//
+// CT_Stop
+//
+//===========================================================================
+
+void CT_Stop(void)
+{
+       chatmodeon = false;
+       return;
+}
+
+//===========================================================================
+//
+// CT_Responder
+//
+//===========================================================================
+
+boolean CT_Responder(event_t *ev)
+{
+       char *macro;
+
+       int sendto;
+
+       if(!netgame)
+       {
+               return false;
+       }
+       if(ev->data1 == KEY_LALT || ev->data2 == KEY_RALT)
+       {
+               altdown = (ev->type == ev_keydown);
+               return false;
+       }
+       if(ev->data1 == KEY_RSHIFT)
+       {
+               shiftdown = (ev->type == ev_keydown);
+               return false;
+       }
+       if(ev->type != ev_keydown)
+       {
+               return false;
+       }
+       if(!chatmodeon)
+       {
+               sendto = 0;
+               if(ev->data1 == CT_KEY_ALL)
+               {
+                       sendto = CT_PLR_ALL;
+               }
+               else if(ev->data1 == CT_KEY_GREEN)
+               {
+                       sendto = CT_PLR_GREEN;
+               }
+               else if(ev->data1 == CT_KEY_YELLOW)
+               {
+                       sendto = CT_PLR_YELLOW;
+               }
+               else if(ev->data1 == CT_KEY_RED)
+               {
+                       sendto = CT_PLR_RED;
+               }
+               else if(ev->data1 == CT_KEY_BLUE)
+               {
+                       sendto = CT_PLR_BLUE;
+               }
+               if(sendto == 0 || (sendto != CT_PLR_ALL && !playeringame[sendto-1])
+                       || sendto == consoleplayer+1)
+               {
+                       return false;
+               }
+               CT_queueChatChar(sendto);
+               chatmodeon = true;
+               return true;
+       }
+       else
+       {
+               if(altdown)
+               {
+                       if(ev->data1 >= '0' && ev->data1 <= '9')
+                       {
+                               if(ev->data1 == '0')
+                               { // macro 0 comes after macro 9
+                                       ev->data1 = '9'+1;
+                               }
+                               macro = chat_macros[ev->data1-'1'];
+                               CT_queueChatChar(KEY_ENTER); //send old message
+                               CT_queueChatChar(chat_dest[consoleplayer]); // chose the dest.
+                               while(*macro)
+                               {
+                                       CT_queueChatChar(toupper(*macro++));
+                               }
+                               CT_queueChatChar(KEY_ENTER); //send it off...
+                               CT_Stop();
+                               return true;
+                       }
+               }
+               if(ev->data1 == KEY_ENTER)
+               {
+                       CT_queueChatChar(KEY_ENTER);
+                       CT_Stop();
+                       return true;
+               }
+               else if(ev->data1 == KEY_ESCAPE)
+               {
+                       CT_queueChatChar(CT_ESCAPE);
+                       CT_Stop();
+                       return true;
+               }
+               else if(ev->data1 >= 'a' && ev->data1 <= 'z')
+               {
+                       CT_queueChatChar(ev->data1-32);
+                       return true;
+               }
+               else if(shiftdown)
+               {
+                       if(ev->data1 == '1')
+                       {
+                               CT_queueChatChar('!');
+                               return true;
+                       }
+                       else if(ev->data1 == '/')
+                       {
+                               CT_queueChatChar('?');
+                               return true;
+                       }
+               }
+               else
+               {
+                       if(ev->data1 == ' ' || ev->data1 == ',' || ev->data1 == '.'
+                       || (ev->data1 >= '0' && ev->data1 <= '9') || ev->data1 == '\''
+                       || ev->data1 == KEY_BACKSPACE || ev->data1 == '-' || ev->data1 == '=')
+                       {
+                               CT_queueChatChar(ev->data1);
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+//===========================================================================
+//
+// CT_Ticker
+//
+//===========================================================================
+
+void CT_Ticker(void)
+{
+       int i;
+       int j;
+       char c;
+       int numplayers;
+
+       for(i=0; i < MAXPLAYERS; i++)
+       {
+               if(!playeringame[i])
+               {
+                       continue;
+               }
+               if((c = players[i].cmd.chatchar) != 0)
+               {
+                       if(c <= 5)
+                       {
+                               chat_dest[i] = c;
+                               continue;
+                       }
+                       else if(c == CT_ESCAPE)
+                       {
+                               CT_ClearChatMessage(i);
+                       }
+                       else if(c == KEY_ENTER)
+                       {
+                               numplayers = 0;
+                               for(j = 0; j < MAXPLAYERS; j++)
+                               {
+                                       numplayers += playeringame[j];
+                               }
+                               CT_AddChar(i, 0); // set the end of message character
+                               if(numplayers > 2)
+                               {
+                                       strcpy(plr_lastmsg[i], CT_FromPlrText[i]);
+                                       strcat(plr_lastmsg[i], chat_msg[i]);
+                               }
+                               else
+                               {
+                                       strcpy(plr_lastmsg[i], chat_msg[i]);
+                               }
+                               if(i != consoleplayer && (chat_dest[i] == consoleplayer+1
+                                       || chat_dest[i] == CT_PLR_ALL) && *chat_msg[i])
+                               {
+                                       P_SetMessage(&players[consoleplayer], plr_lastmsg[i], 
+                                               true);
+                                       S_StartSound(NULL, sfx_chat);
+                               }
+                               else if(i == consoleplayer && (*chat_msg[i]))
+                               {
+                                       if(numplayers > 1)
+                                       {
+                                               P_SetMessage(&players[consoleplayer], "-MESSAGE SENT-", 
+                                                       true);
+                                               S_StartSound(NULL, sfx_chat);
+                                       }
+                                       else
+                                       {
+                                               P_SetMessage(&players[consoleplayer],
+                                                       "THERE ARE NO OTHER PLAYERS IN THE GAME!", true);
+                                               S_StartSound(NULL, sfx_chat);
+                                       }
+                               }
+                               CT_ClearChatMessage(i);
+                       }
+                       else if(c == KEY_BACKSPACE)
+                       {
+                               CT_BackSpace(i);
+                       }
+                       else
+                       {
+                               CT_AddChar(i, c);
+                       }
+               }
+       }
+       return;
+}
+
+//===========================================================================
+//
+// CT_Drawer
+//
+//===========================================================================
+
+void CT_Drawer(void)
+{
+       int i;
+       int x;
+       patch_t *patch;
+
+       if(chatmodeon)
+       {
+               x = 25;
+               for(i = 0; i < msgptr[consoleplayer]; i++)
+               {
+                       if(chat_msg[consoleplayer][i] < 33)
+                       {
+                               x += 6;
+                       }
+                       else
+                       {
+                               patch=W_CacheLumpNum(FontABaseLump+
+                                       chat_msg[consoleplayer][i]-33, PU_CACHE);
+                               V_DrawPatch(x, 10, patch);
+                               x += patch->width;
+                       }
+               }
+               V_DrawPatch(x, 10, W_CacheLumpName("FONTA59", PU_CACHE));
+               BorderTopRefresh = true;
+               UpdateState |= I_MESSAGES;
+       }
+}
+
+//===========================================================================
+//
+// CT_queueChatChar
+//
+//===========================================================================
+
+void CT_queueChatChar(char ch)
+{
+       if(((tail+1)&(QUEUESIZE-1)) == head)
+       { // the queue is full
+               return;
+       }
+       ChatQueue[tail] = ch;
+       tail = (tail+1)&(QUEUESIZE-1);
+}
+
+//===========================================================================
+//
+// CT_dequeueChatChar
+//
+//===========================================================================
+
+char CT_dequeueChatChar(void)
+{
+       byte temp;
+
+       if(head == tail)
+       { // queue is empty
+               return 0;
+       }
+       temp = ChatQueue[head];
+       head = (head+1)&(QUEUESIZE-1);
+       return temp;
+}
+
+//===========================================================================
+//
+// CT_AddChar
+//
+//===========================================================================
+
+void CT_AddChar(int player, char c)
+{
+       patch_t *patch;
+
+       if(msgptr[player]+1 >= MESSAGESIZE || msglen[player] >= MESSAGELEN)
+       { // full.
+               return;
+       }
+       chat_msg[player][msgptr[player]] = c;
+       msgptr[player]++;
+       if(c < 33)
+       {
+               msglen[player] += 6;
+       }
+       else
+       {
+               patch = W_CacheLumpNum(FontABaseLump+c-33, PU_CACHE);
+               msglen[player] += patch->width;
+       }
+}
+
+//===========================================================================
+//
+// CT_BackSpace
+//
+//     Backs up a space, when the user hits (obviously) backspace
+//===========================================================================
+
+void CT_BackSpace(int player)
+{
+       patch_t *patch;
+       char c;
+
+       if(msgptr[player] == 0)
+       { // message is already blank
+               return;
+       }
+       msgptr[player]--;
+       c = chat_msg[player][msgptr[player]];
+       if(c < 33)
+       {
+               msglen[player] -= 6;
+       }
+       else
+       {
+               patch = W_CacheLumpNum(FontABaseLump+c-33, PU_CACHE);
+               msglen[player] -= patch->width;
+       }
+       chat_msg[player][msgptr[player]] = 0;
+}
+
+//===========================================================================
+//
+// CT_ClearChatMessage
+//
+//     Clears out the data for the chat message, but the player's message
+//             is still saved in plrmsg.
+//===========================================================================
+
+void CT_ClearChatMessage(int player)
+{
+       memset(chat_msg[player], 0, MESSAGESIZE);
+       msgptr[player] = 0;
+       msglen[player] = 0;
+}
diff --git a/base/d_main.c b/base/d_main.c
new file mode 100644 (file)
index 0000000..258ecd2
--- /dev/null
@@ -0,0 +1,1052 @@
+
+// D_main.c
+
+#ifdef __WATCOMC__
+#include <dos.h>
+#include <graph.h>
+#include <sys\types.h>
+#include <direct.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+
+#ifdef RENDER3D
+#include "ogl_def.h"
+#define W_CacheLumpName(a,b)        W_GetNumForName(a)
+#define V_DrawPatch(x,y,p)          OGL_DrawPatch(x,y,p)
+#define V_DrawRawScreen(a)          OGL_DrawRawScreen(a)
+#endif
+
+boolean shareware = false;             // true if only episode 1 present
+boolean ExtendedWAD = false;   // true if episodes 4 and 5 present
+
+boolean nomonsters;                    // checkparm of -nomonsters
+boolean respawnparm;                   // checkparm of -respawn
+boolean debugmode;                     // checkparm of -debug
+boolean ravpic;                                // checkparm of -ravpic
+boolean cdrom;                                 // true if cd-rom mode active
+boolean singletics;                    // debug flag to cancel adaptiveness
+boolean noartiskip;                    // whether shift-enter skips an artifact
+
+skill_t startskill;
+int startepisode;
+int startmap;
+boolean autostart;
+extern boolean automapactive;
+
+boolean advancedemo;
+
+FILE *debugfile;
+
+void D_CheckNetGame(void);
+void D_ProcessEvents(void);
+void G_BuildTiccmd(ticcmd_t *cmd);
+void D_DoAdvanceDemo(void);
+void D_PageDrawer (void);
+void D_AdvanceDemo (void);
+void F_Drawer(void);
+boolean F_Responder(event_t *ev);
+
+//---------------------------------------------------------------------------
+//
+// FUNC FixedDiv
+//
+//---------------------------------------------------------------------------
+
+fixed_t FixedDiv(fixed_t a, fixed_t b)
+{
+       if((abs(a)>>14) >= abs(b))
+       {
+               return((a^b)<0 ? MININT : MAXINT);
+       }
+       return(FixedDiv2(a, b));
+}
+
+/*
+===============================================================================
+
+                                                       EVENT HANDLING
+
+Events are asyncronous inputs generally generated by the game user.
+
+Events can be discarded if no responder claims them
+
+===============================================================================
+*/
+
+event_t events[MAXEVENTS];
+int eventhead;
+int eventtail;
+
+//---------------------------------------------------------------------------
+//
+// PROC D_PostEvent
+//
+// Called by the I/O functions when input is detected.
+//
+//---------------------------------------------------------------------------
+
+void D_PostEvent(event_t *ev)
+{
+       events[eventhead] = *ev;
+       eventhead = (++eventhead)&(MAXEVENTS-1);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC D_ProcessEvents
+//
+// Send all the events of the given timestamp down the responder chain.
+//
+//---------------------------------------------------------------------------
+
+void D_ProcessEvents(void)
+{
+       event_t *ev;
+
+       for(; eventtail != eventhead; eventtail = (++eventtail)&(MAXEVENTS-1))
+       {
+               ev = &events[eventtail];
+               if(F_Responder(ev))
+               {
+                       continue;
+               }
+               if(MN_Responder(ev))
+               {
+                       continue;
+               }
+               G_Responder(ev);
+       }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawMessage
+//
+//---------------------------------------------------------------------------
+
+void DrawMessage(void)
+{
+       player_t *player;
+
+       player = &players[consoleplayer];
+       if(player->messageTics <= 0 || !player->message)
+       { // No message
+               return;
+       }
+       MN_DrTextA(player->message, 160-MN_TextAWidth(player->message)/2, 1);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC D_Display
+//
+// Draw current display, possibly wiping it from the previous.
+//
+//---------------------------------------------------------------------------
+
+void R_ExecuteSetViewSize(void);
+
+extern boolean finalestage;
+
+void D_Display(void)
+{
+       extern boolean MenuActive;
+       extern boolean askforquit;
+
+       // Change the view size if needed
+       if(setsizeneeded)
+       {
+               R_ExecuteSetViewSize();
+       }
+
+//
+// do buffered drawing
+//
+       switch (gamestate)
+       {
+       case GS_LEVEL:
+               if (!gametic)
+                       break;
+               if (automapactive)
+                       AM_Drawer ();
+               else
+                       R_RenderPlayerView (&players[displayplayer]);
+               CT_Drawer();
+               UpdateState |= I_FULLVIEW;
+               SB_Drawer();
+               break;
+       case GS_INTERMISSION:
+               IN_Drawer ();
+               break;
+       case GS_FINALE:
+               F_Drawer ();
+               break;
+       case GS_DEMOSCREEN:
+               D_PageDrawer ();
+               break;
+       }
+
+       if(paused && !MenuActive && !askforquit)
+       {
+               if(!netgame)
+               {
+                       V_DrawPatch(160, viewwindowy+5, W_CacheLumpName("PAUSED",
+                               PU_CACHE));
+               }
+               else
+               {
+                       V_DrawPatch(160, 70, W_CacheLumpName("PAUSED",
+                               PU_CACHE));
+               }
+       }
+
+#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];
+               sprintf(filename, "debug%i.txt", consoleplayer);
+               debugfile = fopen(filename,"w");
+       }
+       I_InitGraphics();
+       while(1)
+       {
+               // Frame syncronous IO operations
+               I_StartFrame();
+
+               // Process one or more tics
+               if(singletics)
+               {
+                       I_StartTic();
+                       D_ProcessEvents();
+                       G_BuildTiccmd(&netcmds[consoleplayer][maketic%BACKUPTICS]);
+                       if (advancedemo)
+                               D_DoAdvanceDemo ();
+                       G_Ticker();
+                       gametic++;
+                       maketic++;
+               }
+               else
+               {
+                       // Will run at least one tic
+                       TryRunTics();
+               }
+
+               // Move positional sounds
+               S_UpdateSounds(players[consoleplayer].mo);
+               D_Display();
+       }
+}
+
+/*
+===============================================================================
+
+                                               DEMO LOOP
+
+===============================================================================
+*/
+
+int             demosequence;
+int             pagetic;
+char            *pagename;
+
+
+/*
+================
+=
+= D_PageTicker
+=
+= Handles timing for warped projection
+=
+================
+*/
+
+void D_PageTicker (void)
+{
+       if (--pagetic < 0)
+               D_AdvanceDemo ();
+}
+
+
+/*
+================
+=
+= D_PageDrawer
+=
+================
+*/
+
+extern boolean MenuActive;
+
+void D_PageDrawer(void)
+{
+       V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE));
+       if(demosequence == 1)
+       {
+               V_DrawPatch(4, 160, W_CacheLumpName("ADVISOR", PU_CACHE));
+       }
+       UpdateState |= I_FULLSCRN;
+}
+
+/*
+=================
+=
+= D_AdvanceDemo
+=
+= Called after each demo or intro demosequence finishes
+=================
+*/
+
+void D_AdvanceDemo (void)
+{
+       advancedemo = true;
+}
+
+void D_DoAdvanceDemo (void)
+{
+       players[consoleplayer].playerstate = PST_LIVE;  // don't reborn
+       advancedemo = false;
+       usergame = false;               // can't save / end game here
+       paused = false;
+       gameaction = ga_nothing;
+       demosequence = (demosequence+1)%7;
+       switch (demosequence)
+       {
+               case 0:
+                       pagetic = 210;
+                       gamestate = GS_DEMOSCREEN;
+                       pagename = "TITLE";
+                       S_StartSong(mus_titl, false);
+                       break;
+               case 1:
+                       pagetic = 140;
+                       gamestate = GS_DEMOSCREEN;
+                       pagename = "TITLE";
+                       break;
+               case 2:
+                       BorderNeedRefresh = true;
+                       UpdateState |= I_FULLSCRN;
+                       G_DeferedPlayDemo ("demo1");
+                       break;
+               case 3:
+                       pagetic = 200;
+                       gamestate = GS_DEMOSCREEN;
+                       pagename = "CREDIT";
+                       break;
+               case 4:
+                       BorderNeedRefresh = true;
+                       UpdateState |= I_FULLSCRN;
+                       G_DeferedPlayDemo ("demo2");
+                       break;
+               case 5:
+                       pagetic = 200;
+                       gamestate = GS_DEMOSCREEN;
+                       if(shareware)
+                       {
+                               pagename = "ORDER";
+                       }
+                       else
+                       {
+                               pagename = "CREDIT";
+                       }
+                       break;
+               case 6:
+                       BorderNeedRefresh = true;
+                       UpdateState |= I_FULLSCRN;
+                       G_DeferedPlayDemo ("demo3");
+                       break;
+       }
+}
+
+
+/*
+=================
+=
+= D_StartTitle
+=
+=================
+*/
+
+void D_StartTitle (void)
+{
+       gameaction = ga_nothing;
+       demosequence = -1;
+       D_AdvanceDemo ();
+}
+
+
+/*
+==============
+=
+= D_CheckRecordFrom
+=
+= -recordfrom <savegame num> <demoname>
+==============
+*/
+
+void D_CheckRecordFrom (void)
+{
+       int     p;
+       char    file[256];
+
+       p = M_CheckParm ("-recordfrom");
+       if (!p || p > myargc-2)
+               return;
+
+       if(cdrom)
+       {
+               sprintf(file, SAVEGAMENAMECD"%c.hsg",myargv[p+1][0]);
+       }
+       else
+       {
+               sprintf(file, SAVEGAMENAME"%c.hsg",myargv[p+1][0]);
+       }
+       G_LoadGame (file);
+       G_DoLoadGame ();    // load the gameskill etc info from savegame
+
+       G_RecordDemo (gameskill, 1, gameepisode, gamemap, myargv[p+2]);
+       D_DoomLoop ();  // never returns
+}
+
+/*
+===============
+=
+= D_AddFile
+=
+===============
+*/
+
+#define MAXWADFILES 20
+
+// MAPDIR should be defined as the directory that holds development maps
+// for the -wart # # command
+
+#ifdef __NeXT__
+
+#define MAPDIR "/Novell/Heretic/data/"
+
+#define SHAREWAREWADNAME "/Novell/Heretic/source/heretic1.wad"
+
+char *wadfiles[MAXWADFILES] =
+{
+       "/Novell/Heretic/source/heretic.wad",
+       "/Novell/Heretic/data/texture1.lmp",
+       "/Novell/Heretic/data/texture2.lmp",
+       "/Novell/Heretic/data/pnames.lmp"
+};
+
+#else
+
+#define MAPDIR "\\data\\"
+
+#define SHAREWAREWADNAME "heretic1.wad"
+
+char *wadfiles[MAXWADFILES] =
+{
+       "heretic.wad",
+       "texture1.lmp",
+       "texture2.lmp",
+       "pnames.lmp"
+};
+
+#endif
+
+char *basedefault = "heretic.cfg";
+
+char exrnwads[80];
+char exrnwads2[80];
+
+void wadprintf(void)
+{
+       if(debugmode)
+       {
+               return;
+       }
+       #ifdef __WATCOMC__
+       _settextposition(23, 2);
+       _setbkcolor(1);
+       _settextcolor(0);
+       _outtext(exrnwads);
+       _settextposition(24, 2);
+       _outtext(exrnwads2);
+       #endif
+}
+
+void D_AddFile(char *file)
+{
+       int numwadfiles;
+       char *new;
+//     char text[256];
+
+       for(numwadfiles = 0; wadfiles[numwadfiles]; numwadfiles++);
+       new = malloc(strlen(file)+1);
+       strcpy(new, file);
+       if(strlen(exrnwads)+strlen(file) < 78)
+       {
+               if(strlen(exrnwads))
+               {
+                       strcat(exrnwads, ", ");
+               }
+               else
+               {
+                       strcpy(exrnwads, "External Wadfiles: ");
+               }
+               strcat(exrnwads, file);
+       }
+       else if(strlen(exrnwads2)+strlen(file) < 79)
+       {
+               if(strlen(exrnwads2))
+               {
+                       strcat(exrnwads2, ", ");
+               }
+               else
+               {
+                       strcpy(exrnwads2, "     ");
+                       strcat(exrnwads, ",");
+               }
+               strcat(exrnwads2, file);
+       }
+       wadfiles[numwadfiles] = new;
+}
+
+//==========================================================
+//
+//  Startup Thermo code
+//
+//==========================================================
+#define MSG_Y       9
+//#define THERM_X 15
+//#define THERM_Y 16
+//#define THERMCOLOR  3
+#define THERM_X     14
+#define THERM_Y     14
+
+int thermMax;
+int thermCurrent;
+char    *startup;           // * to text screen
+char smsg[80];      // status bar line
+
+//
+//  Heretic startup screen shit
+//
+
+byte *hscreen;
+
+void hgotoxy(int x,int y)
+{
+       hscreen = (byte *)(0xb8000 + y*160 + x*2);
+}
+
+void hput(unsigned char c, unsigned char a)
+{
+       *hscreen++ = c;
+       *hscreen++ = a;
+}
+
+void hprintf(char *string, unsigned char a)
+{
+#ifdef __WATCOMC__
+       int i;
+
+       if(debugmode)
+       {
+               puts(string);
+               return;
+       }
+       for(i = 0; i < strlen(string); i++)
+       {
+               hput(string[i], a);
+       }
+#endif
+}
+
+void drawstatus(void)
+{
+       if(debugmode)
+       {
+               return;
+       }
+       #ifdef __WATCOMC__
+       _settextposition(25, 2);
+       _setbkcolor(1);
+       _settextcolor(15);
+       _outtext(smsg);
+       _settextposition(25, 1);
+       #endif
+}
+
+void status(char *string)
+{
+       strcat(smsg,string);
+       drawstatus();
+}
+
+void DrawThermo(void)
+{
+       #ifdef __WATCOMC__
+       unsigned char       *screen;
+       int     progress;
+       int     i;
+
+       if(debugmode)
+       {
+               return;
+       }
+#if 0
+       progress = (98*thermCurrent)/thermMax;
+       screen = (char *)0xb8000 + (THERM_Y*160 + THERM_X*2);
+       for (i = 0;i < progress/2; i++)
+       {
+               switch(i)
+               {
+                       case 4:
+                       case 9:
+                       case 14:
+                       case 19:
+                       case 29:
+                       case 34:
+                       case 39:
+                       case 44:
+                               *screen++ = 0xb3;
+                               *screen++ = (THERMCOLOR<<4)+15;
+                               break;
+                       case 24:
+                               *screen++ = 0xba;
+                               *screen++ = (THERMCOLOR<<4)+15;
+                               break;
+                       default:
+                               *screen++ = 0xdb;
+                               *screen++ = 0x40 + THERMCOLOR;
+                               break;
+               }
+       }
+       if (progress&1)
+       {
+               *screen++ = 0xdd;
+               *screen++ = 0x40 + THERMCOLOR;
+       }
+#else
+       progress = (50*thermCurrent)/thermMax+2;
+//  screen = (char *)0xb8000 + (THERM_Y*160 + THERM_X*2);
+       hgotoxy(THERM_X,THERM_Y);
+       for (i = 0; i < progress; i++)
+       {
+//      *screen++ = 0xdb;
+//      *screen++ = 0x2a;
+               hput(0xdb,0x2a);
+       }
+#endif
+       #endif
+}
+
+#ifdef __WATCOMC__
+void blitStartup(void)
+{
+       byte *textScreen;
+
+       if(debugmode)
+       {
+               return;
+       }
+
+       // Blit main screen
+       textScreen = (byte *)0xb8000;
+       memcpy(textScreen, startup, 4000);
+
+       // Print version string
+       _setbkcolor(4); // Red
+       _settextcolor(14); // Yellow
+       _settextposition(3, 47);
+       _outtext(VERSION_TEXT);
+
+       // Hide cursor
+       _settextcursor(0x2000);
+}
+#endif
+
+char tmsg[300];
+void tprintf(char *msg,int initflag)
+{
+       #ifdef __WATCOMC__
+       char    temp[80];
+       int start;
+       int add;
+       int i;
+       #endif
+
+       if(debugmode)
+       {
+               printf(msg);
+               return;
+       }
+       #ifdef __WATCOMC__
+       if (initflag)
+               tmsg[0] = 0;
+       strcat(tmsg,msg);
+       blitStartup();
+       DrawThermo();
+       _setbkcolor(4);
+       _settextcolor(15);
+       for (add = start = i = 0; i <= strlen(tmsg); i++)
+               if ((tmsg[i] == '\n') || (!tmsg[i]))
+               {
+                       memset(temp,0,80);
+                       strncpy(temp,tmsg+start,i-start);
+                       _settextposition(MSG_Y+add,40-strlen(temp)/2);
+                       _outtext(temp);
+                       start = i+1;
+                       add++;
+               }
+       _settextposition(25,1);
+       drawstatus();
+       #else
+       printf(msg);
+       #endif
+}
+
+void CheckAbortStartup(void)
+{
+#ifdef __WATCOMC__
+       extern int lastpress;
+
+       if(lastpress == 1)
+       { // Abort if escape pressed
+               CleanExit();
+       }
+#endif
+}
+
+void IncThermo(void)
+{
+       thermCurrent++;
+       DrawThermo();
+       CheckAbortStartup();
+}
+
+void InitThermo(int max)
+{
+       thermMax = max;
+       thermCurrent = 0;
+}
+
+#ifdef __WATCOMC__
+void CleanExit(void)
+{
+       union REGS regs;
+
+       I_ShutdownKeyboard();
+       regs.x.eax = 0x3;
+       int386(0x10, &regs, &regs);
+       printf("Exited from HERETIC.\n");
+       exit(1);
+}
+#endif
+
+//---------------------------------------------------------------------------
+//
+// PROC D_DoomMain
+//
+//---------------------------------------------------------------------------
+
+void D_DoomMain(void)
+{
+       int p;
+       int e;
+       int m;
+       char file[256];
+       FILE *fp;
+       boolean devMap;
+       //char *screen;
+
+       M_FindResponseFile();
+       setbuf(stdout, NULL);
+       nomonsters = M_CheckParm("-nomonsters");
+       respawnparm = M_CheckParm("-respawn");
+       ravpic = M_CheckParm("-ravpic");
+       noartiskip = M_CheckParm("-noartiskip");
+       debugmode = M_CheckParm("-debug");
+       startskill = sk_medium;
+       startepisode = 1;
+       startmap = 1;
+       autostart = false;
+
+       // wadfiles[0] is a char * to the main wad
+       fp = fopen(wadfiles[0], "rb");
+       if(fp)
+       {
+               fclose(fp);
+       }
+       else
+       { // Change to look for shareware wad
+               wadfiles[0] = SHAREWAREWADNAME;
+       }
+
+       // Check for -CDROM
+       cdrom = false;
+#ifdef __WATCOMC__
+       if(M_CheckParm("-cdrom"))
+       {
+               cdrom = true;
+               mkdir("c:\\heretic.cd");
+       }
+#endif
+
+       // -FILE [filename] [filename] ...
+       // Add files to the wad list.
+       p = M_CheckParm("-file");
+       if(p)
+       {       // the parms after p are wadfile/lump names, until end of parms
+               // or another - preceded parm
+               while(++p != myargc && myargv[p][0] != '-')
+               {
+                       D_AddFile(myargv[p]);
+               }
+       }
+
+       // -DEVMAP <episode> <map>
+       // Adds a map wad from the development directory to the wad list,
+       // and sets the start episode and the start map.
+       devMap = false;
+       p = M_CheckParm("-devmap");
+       if(p && p < myargc-2)
+       {
+               e = myargv[p+1][0];
+               m = myargv[p+2][0];
+               sprintf(file, MAPDIR"E%cM%c.wad", e, m);
+               D_AddFile(file);
+               printf("DEVMAP: Episode %c, Map %c.\n", e, m);
+               startepisode = e-'0';
+               startmap = m-'0';
+               autostart = true;
+               devMap = true;
+       }
+
+       p = M_CheckParm("-playdemo");
+       if(!p)
+       {
+               p = M_CheckParm("-timedemo");
+       }
+       if (p && p < myargc-1)
+       {
+               sprintf(file, "%s.lmp", myargv[p+1]);
+               D_AddFile(file);
+               printf("Playing demo %s.lmp.\n", myargv[p+1]);
+       }
+
+//
+// get skill / episode / map from parms
+//
+       if(M_CheckParm("-deathmatch"))
+       {
+               deathmatch = true;
+       }
+
+       p = M_CheckParm("-skill");
+       if(p && p < myargc-1)
+       {
+               startskill = myargv[p+1][0]-'1';
+               autostart = true;
+       }
+
+       p = M_CheckParm("-episode");
+       if(p && p < myargc-1)
+       {
+               startepisode = myargv[p+1][0]-'0';
+               startmap = 1;
+               autostart = true;
+       }
+
+       p = M_CheckParm("-warp");
+       if(p && p < myargc-2)
+       {
+               startepisode = myargv[p+1][0]-'0';
+               startmap = myargv[p+2][0]-'0';
+               autostart = true;
+       }
+
+//
+// init subsystems
+//
+       printf("V_Init: allocate screens.\n");
+       V_Init();
+
+       // Load defaults before initing other systems
+       printf("M_LoadDefaults: Load system defaults.\n");
+       M_LoadDefaults();
+
+       printf("Z_Init: Init zone memory allocation daemon.\n");
+       Z_Init();
+
+       printf("W_Init: Init WADfiles.\n");
+       W_InitMultipleFiles(wadfiles);
+
+       if(W_CheckNumForName("E2M1") == -1)
+       { // Can't find episode 2 maps, must be the shareware WAD
+               shareware = true;
+       }
+       else if(W_CheckNumForName("EXTENDED") != -1)
+       { // Found extended lump, must be the extended WAD
+               ExtendedWAD = true;
+       }
+
+#ifdef __WATCOMC__
+       I_StartupKeyboard();
+       I_StartupJoystick();
+#endif
+       #ifdef __WATCOMC__
+       // No use caching this if we're not going to use it - DDOI
+       startup = W_CacheLumpName("LOADING", PU_CACHE);
+       blitStartup();
+       #endif
+
+       //
+       //  Build status bar line!
+       //
+       smsg[0] = 0;
+       if (deathmatch)
+               status("DeathMatch...");
+       if (nomonsters)
+               status("No Monsters...");
+       if (respawnparm)
+               status("Respawning...");
+       if (autostart)
+       {
+               char temp[64];
+               sprintf(temp, "Warp to Episode %d, Map %d, Skill %d ",
+                       startepisode, startmap, startskill+1);
+               status(temp);
+       }
+       wadprintf(); // print the added wadfiles
+
+       tprintf("MN_Init: Init menu system.\n",1);
+       MN_Init();
+
+       CT_Init();
+
+       tprintf("R_Init: Init Heretic refresh daemon.",1);
+       hgotoxy(17,7);
+       hprintf("Loading graphics",0x3f);
+       R_Init();
+
+       tprintf("P_Init: Init Playloop state.",1);
+       hgotoxy(17,8);
+       hprintf("Init game engine.",0x3f);
+       P_Init();
+       IncThermo();
+
+       tprintf("I_Init: Setting up machine state.\n",1);
+       I_Init();
+       IncThermo();
+
+       tprintf("D_CheckNetGame: Checking network game status.\n",1);
+       hgotoxy(17,9);
+       hprintf("Checking network game status.", 0x3f);
+       D_CheckNetGame();
+       IncThermo();
+
+#ifdef __WATCOMC__
+       I_CheckExternDriver(); // Check for an external device driver
+#endif
+
+       tprintf("SB_Init: Loading patches.\n",1);
+       SB_Init();
+       IncThermo();
+
+//
+// start the apropriate game based on parms
+//
+
+       D_CheckRecordFrom();
+
+       p = M_CheckParm("-record");
+       if(p && p < myargc-1)
+       {
+               G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p+1]);
+               D_DoomLoop(); // Never returns
+       }
+
+       p = M_CheckParm("-playdemo");
+       if(p && p < myargc-1)
+       {
+               singledemo = true; // Quit after one demo
+               G_DeferedPlayDemo(myargv[p+1]);
+               D_DoomLoop(); // Never returns
+       }
+
+       p = M_CheckParm("-timedemo");
+       if(p && p < myargc-1)
+       {
+               G_TimeDemo(myargv[p+1]);
+               D_DoomLoop(); // Never returns
+       }
+
+       p = M_CheckParm("-loadgame");
+       if(p && p < myargc-1)
+       {
+               if(cdrom)
+               {
+                       sprintf(file, SAVEGAMENAMECD"%c.hsg", myargv[p+1][0]);
+               }
+               else
+               {
+                       sprintf(file, SAVEGAMENAME"%c.hsg", myargv[p+1][0]);
+               }
+               G_LoadGame(file);
+       }
+
+       // Check valid episode and map
+       if((autostart || netgame) && (devMap == false))
+       {
+               if(M_ValidEpisodeMap(startepisode, startmap) == false)
+               {
+                       startepisode = 1;
+                       startmap = 1;
+               }
+       }
+
+       if(gameaction != ga_loadgame)
+       {
+               UpdateState |= I_FULLSCRN;
+               BorderNeedRefresh = true;
+               if(autostart || netgame)
+               {
+                       G_InitNew(startskill, startepisode, startmap);
+               }
+               else
+               {
+                       D_StartTitle();
+               }
+       }
+#ifdef __WATCOMC__
+       _settextcursor(0x0607); // bring the cursor back
+#endif
+       D_DoomLoop(); // Never returns
+}
diff --git a/base/d_net.c b/base/d_net.c
new file mode 100644 (file)
index 0000000..66db4d2
--- /dev/null
@@ -0,0 +1,784 @@
+
+// d_net.c
+// This version has the fixed ticdup code
+
+#include "doomdef.h"
+
+#define NCMD_EXIT               0x80000000
+#define NCMD_RETRANSMIT 0x40000000
+#define NCMD_SETUP              0x20000000
+#define NCMD_KILL               0x10000000              // kill game
+#define NCMD_CHECKSUM   0x0fffffff
+
+
+doomcom_t               *doomcom;
+doomdata_t              *netbuffer;             // points inside doomcom
+
+
+/*
+==============================================================================
+
+                                                       NETWORKING
+
+gametic is the tic about to (or currently being) run
+maketic is the tick that hasn't had control made for it yet
+nettics[] has the maketics for all players
+
+a gametic cannot be run until nettics[] > gametic for all players
+
+==============================================================================
+*/
+
+#define RESENDCOUNT     10
+#define PL_DRONE        0x80                            // bit flag in doomdata->player
+
+ticcmd_t                localcmds[BACKUPTICS];
+
+ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
+int             nettics[MAXNETNODES];
+boolean                 nodeingame[MAXNETNODES];        // set false as nodes leave game
+boolean                 remoteresend[MAXNETNODES];      // set when local needs tics
+int                             resendto[MAXNETNODES];                  // set when remote needs tics
+int                             resendcount[MAXNETNODES];
+
+int                             nodeforplayer[MAXPLAYERS];
+
+int             maketic;
+int                             lastnettic, skiptics;
+int                             ticdup;
+int                             maxsend;        // BACKUPTICS/(2*ticdup)-1
+
+void D_ProcessEvents (void);
+void G_BuildTiccmd (ticcmd_t *cmd);
+void D_DoAdvanceDemo (void);
+
+boolean                 reboundpacket;
+doomdata_t              reboundstore;
+
+
+int     NetbufferSize (void)
+{
+       return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]);
+}
+
+unsigned NetbufferChecksum (void)
+{
+       unsigned                c;
+       int             i,l;
+
+       c = 0x1234567;
+
+#if defined(NeXT) || defined(NORMALUNIX)
+       return 0;                       // byte order problems
+#endif
+
+       l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
+       for (i=0 ; i<l ; i++)
+               c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
+
+       return c & NCMD_CHECKSUM;
+}
+
+int ExpandTics (int low)
+{
+       int     delta;
+
+       delta = low - (maketic&0xff);
+
+       if (delta >= -64 && delta <= 64)
+               return (maketic&~0xff) + low;
+       if (delta > 64)
+               return (maketic&~0xff) - 256 + low;
+       if (delta < -64)
+               return (maketic&~0xff) + 256 + low;
+
+       I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
+       return 0;
+}
+
+
+//============================================================================
+
+
+/*
+==============
+=
+= HSendPacket
+=
+==============
+*/
+
+void HSendPacket (int node, int flags)
+{
+       netbuffer->checksum = NetbufferChecksum () | flags;
+
+       if (!node)
+       {
+               reboundstore = *netbuffer;
+               reboundpacket = true;
+               return;
+       }
+
+       if (demoplayback)
+               return;
+
+       if (!netgame)
+               I_Error ("Tried to transmit to another node");
+
+       doomcom->command = CMD_SEND;
+       doomcom->remotenode = node;
+       doomcom->datalength = NetbufferSize ();
+
+if (debugfile)
+{
+       int             i;
+       int             realretrans;
+       if (netbuffer->checksum & NCMD_RETRANSMIT)
+               realretrans = ExpandTics (netbuffer->retransmitfrom);
+       else
+               realretrans = -1;
+       fprintf (debugfile,"send (%i + %i, R %i) [%i] "
+       ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
+       for (i=0 ; i<doomcom->datalength ; i++)
+               fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
+       fprintf (debugfile,"\n");
+}
+
+       I_NetCmd ();
+}
+
+/*
+==============
+=
+= HGetPacket
+=
+= Returns false if no packet is waiting
+=
+==============
+*/
+
+boolean HGetPacket (void)
+{
+       if (reboundpacket)
+       {
+               *netbuffer = reboundstore;
+               doomcom->remotenode = 0;
+               reboundpacket = false;
+               return true;
+       }
+
+       if (!netgame)
+               return false;
+       if (demoplayback)
+               return false;
+
+       doomcom->command = CMD_GET;
+       I_NetCmd ();
+       if (doomcom->remotenode == -1)
+               return false;
+
+       if (doomcom->datalength != NetbufferSize ())
+       {
+               if (debugfile)
+                       fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
+               return false;
+       }
+
+       if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
+       {
+               if (debugfile)
+                       fprintf (debugfile,"bad packet checksum\n");
+               return false;
+       }
+
+if (debugfile)
+{
+       int             realretrans;
+                       int     i;
+
+       if (netbuffer->checksum & NCMD_SETUP)
+               fprintf (debugfile,"setup packet\n");
+       else
+       {
+               if (netbuffer->checksum & NCMD_RETRANSMIT)
+                       realretrans = ExpandTics (netbuffer->retransmitfrom);
+               else
+                       realretrans = -1;
+               fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode,
+               ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
+               for (i=0 ; i<doomcom->datalength ; i++)
+                       fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
+               fprintf (debugfile,"\n");
+       }
+}
+       return true;
+}
+
+
+/*
+===================
+=
+= GetPackets
+=
+===================
+*/
+
+char    exitmsg[80];
+
+void GetPackets (void)
+{
+       int             netconsole;
+       int             netnode;
+       ticcmd_t        *src, *dest;
+       int             realend;
+       int             realstart;
+
+       while (HGetPacket ())
+       {
+               if (netbuffer->checksum & NCMD_SETUP)
+                       continue;               // extra setup packet
+
+               netconsole = netbuffer->player & ~PL_DRONE;
+               netnode = doomcom->remotenode;
+               //
+               // to save bytes, only the low byte of tic numbers are sent
+               // Figure out what the rest of the bytes are
+               //
+               realstart = ExpandTics (netbuffer->starttic);
+               realend = (realstart+netbuffer->numtics);
+
+               //
+               // check for exiting the game
+               //
+               if (netbuffer->checksum & NCMD_EXIT)
+               {
+                       if (!nodeingame[netnode])
+                               continue;
+                       nodeingame[netnode] = false;
+                       playeringame[netconsole] = false;
+                       strcpy (exitmsg, "PLAYER 1 LEFT THE GAME");
+                       exitmsg[7] += netconsole;
+                       players[consoleplayer].message = exitmsg;
+//                     if (demorecording)
+//                             G_CheckDemoStatus ();
+                       continue;
+               }
+
+               //
+               // check for a remote game kill
+               //
+               if (netbuffer->checksum & NCMD_KILL)
+                       I_Error ("Killed by network driver");
+
+               nodeforplayer[netconsole] = netnode;
+
+               //
+               // check for retransmit request
+               //
+               if ( resendcount[netnode] <= 0
+               && (netbuffer->checksum & NCMD_RETRANSMIT) )
+               {
+                       resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
+if (debugfile)
+fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
+                       resendcount[netnode] = RESENDCOUNT;
+               }
+               else
+                       resendcount[netnode]--;
+
+               //
+               // check for out of order / duplicated packet
+               //
+               if (realend == nettics[netnode])
+                       continue;
+
+               if (realend < nettics[netnode])
+               {
+if (debugfile)
+fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
+                       continue;
+               }
+
+               //
+               // check for a missed packet
+               //
+               if (realstart > nettics[netnode])
+               {
+               // stop processing until the other system resends the missed tics
+if (debugfile)
+fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, realstart, nettics[netnode]);
+                       remoteresend[netnode] = true;
+                       continue;
+               }
+
+//
+// update command store from the packet
+//
+{
+       int             start;
+
+               remoteresend[netnode] = false;
+
+               start = nettics[netnode] - realstart;
+               src = &netbuffer->cmds[start];
+
+               while (nettics[netnode] < realend)
+               {
+                       dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
+                       nettics[netnode]++;
+                       *dest = *src;
+                       src++;
+               }
+       }
+}
+
+}
+
+/*
+=============
+=
+= NetUpdate
+=
+= Builds ticcmds for console player
+= sends out a packet
+=============
+*/
+
+int      gametime;
+
+void NetUpdate (void)
+{
+       int             nowtime;
+       int             newtics;
+       int                             i,j;
+       int                             realstart;
+       int                             gameticdiv;
+
+//
+// check time
+//
+       nowtime = I_GetTime ()/ticdup;
+       newtics = nowtime - gametime;
+       gametime = nowtime;
+
+       if (newtics <= 0)                       // nothing new to update
+               goto listen;
+
+       if (skiptics <= newtics)
+       {
+               newtics -= skiptics;
+               skiptics = 0;
+       }
+       else
+       {
+               skiptics -= newtics;
+               newtics = 0;
+       }
+
+
+       netbuffer->player = consoleplayer;
+
+//
+// build new ticcmds for console player
+//
+       gameticdiv = gametic/ticdup;
+       for (i=0 ; i<newtics ; i++)
+       {
+               I_StartTic ();
+               D_ProcessEvents ();
+               if (maketic - gameticdiv >= BACKUPTICS/2-1)
+                       break;          // can't hold any more
+//printf ("mk:%i ",maketic);
+               G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
+               maketic++;
+       }
+
+
+       if (singletics)
+               return;         // singletic update is syncronous
+
+//
+// send the packet to the other nodes
+//
+       for (i=0 ; i<doomcom->numnodes ; i++)
+               if (nodeingame[i])
+               {
+                       netbuffer->starttic = realstart = resendto[i];
+                       netbuffer->numtics = maketic - realstart;
+                       if (netbuffer->numtics > BACKUPTICS)
+                               I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
+
+                       resendto[i] = maketic - doomcom->extratics;
+
+                       for (j=0 ; j< netbuffer->numtics ; j++)
+                               netbuffer->cmds[j] =
+                                       localcmds[(realstart+j)%BACKUPTICS];
+
+                       if (remoteresend[i])
+                       {
+                               netbuffer->retransmitfrom = nettics[i];
+                               HSendPacket (i, NCMD_RETRANSMIT);
+                       }
+                       else
+                       {
+                               netbuffer->retransmitfrom = 0;
+                               HSendPacket (i, 0);
+                       }
+               }
+
+//
+// listen for other packets
+//
+listen:
+
+       GetPackets ();
+}
+
+
+/*
+=====================
+=
+= CheckAbort
+=
+=====================
+*/
+
+void CheckAbort (void)
+{
+       event_t *ev;
+       int             stoptic;
+
+       stoptic = I_GetTime () + 2;
+       while (I_GetTime() < stoptic)
+               I_StartTic ();
+
+       I_StartTic ();
+       for ( ; eventtail != eventhead
+       ; eventtail = (++eventtail)&(MAXEVENTS-1) )
+       {
+               ev = &events[eventtail];
+               if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
+                       I_Error ("Network game synchronization aborted.");
+       }
+}
+
+/*
+=====================
+=
+= D_ArbitrateNetStart
+=
+=====================
+*/
+
+void D_ArbitrateNetStart (void)
+{
+       int             i;
+       boolean gotinfo[MAXNETNODES];
+
+       autostart = true;
+       memset (gotinfo,0,sizeof(gotinfo));
+
+       if (doomcom->consoleplayer)
+       {       // listen for setup info from key player
+//             mprintf ("listening for network start info...\n");
+               while (1)
+               {
+                       CheckAbort ();
+                       if (!HGetPacket ())
+                               continue;
+                       if (netbuffer->checksum & NCMD_SETUP)
+                       {
+                               if (netbuffer->player != VERSION)
+                                       I_Error ("Different DOOM versions cannot play a net game!");
+                               startskill = netbuffer->retransmitfrom & 15;
+                               deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6;
+                               nomonsters = (netbuffer->retransmitfrom & 0x20) > 0;
+                               respawnparm = (netbuffer->retransmitfrom & 0x10) > 0;
+                               //startmap = netbuffer->starttic & 0x3f;
+                               //startepisode = netbuffer->starttic >> 6;
+                               startmap = netbuffer->starttic&15;
+                               startepisode = netbuffer->starttic>>4;
+                               return;
+                       }
+               }
+       }
+       else
+       {       // key player, send the setup info
+//             mprintf ("sending network start info...\n");
+               do
+               {
+                       CheckAbort ();
+                       for (i=0 ; i<doomcom->numnodes ; i++)
+                       {
+                               netbuffer->retransmitfrom = startskill;
+                               if (deathmatch)
+                                       netbuffer->retransmitfrom |= (deathmatch<<6);
+                               if (nomonsters)
+                                       netbuffer->retransmitfrom |= 0x20;
+                               if (respawnparm)
+                                       netbuffer->retransmitfrom |= 0x10;
+                               //netbuffer->starttic = startepisode * 64 + startmap;
+                               netbuffer->starttic = (startepisode<<4)+startmap;
+                               netbuffer->player = VERSION;
+                               netbuffer->numtics = 0;
+                               HSendPacket (i, NCMD_SETUP);
+                       }
+
+#if 1
+                       for(i = 10 ; i  &&  HGetPacket(); --i)
+                       {
+ if((netbuffer->player&0x7f) < MAXNETNODES)
+                               gotinfo[netbuffer->player&0x7f] = true;
+                       }
+#else
+                       while (HGetPacket ())
+                       {
+                               gotinfo[netbuffer->player&0x7f] = true;
+                       }
+#endif
+
+                       for (i=1 ; i<doomcom->numnodes ; i++)
+                               if (!gotinfo[i])
+                                       break;
+               } while (i < doomcom->numnodes);
+       }
+}
+
+/*
+===================
+=
+= D_CheckNetGame
+=
+= Works out player numbers among the net participants
+===================
+*/
+
+extern  int                     viewangleoffset;
+
+void D_CheckNetGame (void)
+{
+       int             i;
+
+       for (i=0 ; i<MAXNETNODES ; i++)
+       {
+               nodeingame[i] = false;
+       nettics[i] = 0;
+               remoteresend[i] = false;        // set when local needs tics
+               resendto[i] = 0;                        // which tic to start sending
+       }
+
+// I_InitNetwork sets doomcom and netgame
+       I_InitNetwork ();
+       if (doomcom->id != DOOMCOM_ID)
+               I_Error ("Doomcom buffer invalid!");
+       netbuffer = &doomcom->data;
+       consoleplayer = displayplayer = doomcom->consoleplayer;
+       if (netgame)
+               D_ArbitrateNetStart ();
+//printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
+
+// read values out of doomcom
+       ticdup = doomcom->ticdup;
+       maxsend = BACKUPTICS/(2*ticdup)-1;
+       if (maxsend<1)
+               maxsend = 1;
+
+       for (i=0 ; i<doomcom->numplayers ; i++)
+               playeringame[i] = true;
+       for (i=0 ; i<doomcom->numnodes ; i++)
+               nodeingame[i] = true;
+
+//printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
+
+}
+
+/*
+==================
+=
+= D_QuitNetGame
+=
+= Called before quitting to leave a net game without hanging the
+= other players
+=
+==================
+*/
+
+void D_QuitNetGame (void)
+{
+       int             i, j;
+
+       if (debugfile)
+               fclose (debugfile);
+
+       if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
+               return;
+
+// send a bunch of packets for security
+       netbuffer->player = consoleplayer;
+       netbuffer->numtics = 0;
+       for (i=0 ; i<4 ; i++)
+       {
+               for (j=1 ; j<doomcom->numnodes ; j++)
+                       if (nodeingame[j])
+                               HSendPacket (j, NCMD_EXIT);
+               I_WaitVBL (1);
+       }
+}
+
+
+
+/*
+===============
+=
+= TryRunTics
+=
+===============
+*/
+
+int     frametics[4], frameon;
+int     frameskip[4];
+int             oldnettics;
+extern  boolean advancedemo;
+
+void TryRunTics (void)
+{
+       int             i;
+       int             lowtic;
+       int             entertic;
+       static int              oldentertics;
+       int                             realtics, availabletics;
+       int                             counts;
+       int                             numplaying;
+
+//
+// get real tics
+//
+       entertic = I_GetTime ()/ticdup;
+       realtics = entertic - oldentertics;
+       oldentertics = entertic;
+
+//
+// get available tics
+//
+       NetUpdate ();
+
+       lowtic = MAXINT;
+       numplaying = 0;
+       for (i=0 ; i<doomcom->numnodes ; i++)
+               if (nodeingame[i])
+               {
+                       numplaying++;
+                       if (nettics[i] < lowtic)
+                               lowtic = nettics[i];
+               }
+       availabletics = lowtic - gametic/ticdup;
+
+
+//
+// decide how many tics to run
+//
+       if (realtics < availabletics-1)
+               counts = realtics+1;
+       else if (realtics < availabletics)
+               counts = realtics;
+       else
+               counts = availabletics;
+       if (counts < 1)
+               counts = 1;
+
+       frameon++;
+
+if (debugfile)
+       fprintf (debugfile,"=======real: %i  avail: %i  game: %i\n",realtics, availabletics,counts);
+
+       if (!demoplayback)
+       {
+       //=============================================================================
+       //
+       //      ideally nettics[0] should be 1 - 3 tics above lowtic
+       //      if we are consistantly slower, speed up time
+       //
+               for (i=0 ; i<MAXPLAYERS ; i++)
+                       if (playeringame[i])
+                               break;
+               if (consoleplayer == i)
+               {       // the key player does not adapt
+               }
+               else
+               {
+                       if (nettics[0] <= nettics[nodeforplayer[i]])
+                       {
+                               gametime--;
+       //                      printf ("-");
+                       }
+                       frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
+                       oldnettics = nettics[0];
+                       if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
+                       {
+                               skiptics = 1;
+       //                      printf ("+");
+                       }
+               }
+       //=============================================================================
+       }       // demoplayback
+
+       //
+       // wait for new tics if needed
+       //
+               while (lowtic < gametic/ticdup + counts)
+               {
+
+                       NetUpdate ();
+                       lowtic = MAXINT;
+
+                       for (i=0 ; i<doomcom->numnodes ; i++)
+                               if (nodeingame[i] && nettics[i] < lowtic)
+                                       lowtic = nettics[i];
+
+                       if (lowtic < gametic/ticdup)
+                               I_Error ("TryRunTics: lowtic < gametic");
+
+                       // don't stay in here forever -- give the menu a chance to work
+                       if (I_GetTime ()/ticdup - entertic >= 20)
+                       {
+                               MN_Ticker ();
+                               return;
+                       }
+               }
+
+//
+// run the count * ticdup dics
+//
+       while (counts--)
+       {
+               for (i=0 ; i<ticdup ; i++)
+               {
+                       if (gametic/ticdup > lowtic)
+                               I_Error ("gametic>lowtic");
+                       if (advancedemo)
+                               D_DoAdvanceDemo ();
+                       MN_Ticker ();
+                       G_Ticker ();
+                       gametic++;
+                       //
+                       // modify command for duplicated tics
+                       //
+                       if (i != ticdup-1)
+                       {
+                               ticcmd_t        *cmd;
+                               int                     buf;
+                               int                     j;
+
+                               buf = (gametic/ticdup)%BACKUPTICS;
+                               for (j=0 ; j<MAXPLAYERS ; j++)
+                               {
+                                       cmd = &netcmds[j][buf];
+                                       cmd->chatchar = 0;
+                                       if (cmd->buttons & BT_SPECIAL)
+                                               cmd->buttons = 0;
+                               }
+                       }
+               }
+               NetUpdate ();                                   // check for new console commands
+       }
+}
diff --git a/base/d_netbak.c b/base/d_netbak.c
new file mode 100644 (file)
index 0000000..6532aa9
--- /dev/null
@@ -0,0 +1,797 @@
+
+// I_pcnet.m
+//
+// Modified 12-21-94 by Chris Rhinehart for use with multiple ticdups
+//             actually, it wasn't modified, but rather we are currently using this
+//     older version of D_NET.C, since the new one doesn't seem to work with
+//             ticdup set to greater than one.
+
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+
+#define        NCMD_EXIT               0x80000000
+#define        NCMD_RETRANSMIT 0x40000000
+#define        NCMD_SETUP              0x20000000
+#define        NCMD_CHECKSUM   0x0fffffff
+
+/*
+if more space needs to be crunched out of the protocol...
+
+1      drone
+2      player
+8      tic
+5      numtics
+
+#define        NCMD_EXIT               0x80000000
+#define        NCMD_RETRANSMIT 0x40000000                      // a retransmit will have 0 tics
+#define        NCMD_DRONE              0x20000000
+#define        NCMD_PLAYER             0x18000000
+#define        NCMD_PLAYERSHIFT        27
+#define        NCMD_TIC                0x00ff0000
+#define        NCMD_TICSHIFT   16
+#define        NCMD_NUMTICS    0x0000ff00
+#define        NCMD_NUMTICSSHIFT       8
+#define        NCMD_CHECKSUM   0x000000ff
+
+*/
+
+
+
+
+
+doomcom_t              *doomcom;
+doomdata_t             *netbuffer;             // points inside doomcom
+
+
+/*
+==============================================================================
+
+                                                       NETWORKING
+
+gametic is the tic about to (or currently being) run
+maketic is the tick that hasn't had control made for it yet
+nettics[] has the maketics for all players
+
+a gametic cannot be run until nettics[] > gametic for all players
+
+==============================================================================
+*/
+
+#define        RESENDCOUNT     10
+#define        PL_DRONE        0x80                            // bit flag in doomdata->player
+
+ticcmd_t               localcmds[BACKUPTICS];
+
+ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
+int            nettics[MAXNETNODES];
+boolean                        nodeingame[MAXNETNODES];        // set false as nodes leave game
+boolean                        remoteresend[MAXNETNODES];      // set when local needs tics
+int                            resendto[MAXNETNODES];                  // set when remote needs tics
+int                            resendcount[MAXNETNODES];
+
+int                            nodeforplayer[MAXPLAYERS];
+
+int             gametime;
+int             maketic;
+int                            lastnettic, skiptics;
+int                            ticdup;
+
+void D_ProcessEvents (void);
+void G_BuildTiccmd (ticcmd_t *cmd);
+void D_DoAdvanceDemo (void);
+
+boolean                        reboundpacket;
+doomdata_t             reboundstore;
+
+
+int    NetbufferSize (void)
+{
+       return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]);
+}
+
+unsigned NetbufferChecksum (void)
+{
+       unsigned                c;
+       int             i,l;
+
+       c = 0x1234567;
+
+       l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
+       for (i=0 ; i<l ; i++)
+               c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
+
+       return c & NCMD_CHECKSUM;
+}
+
+int ExpandTics (int low)
+{
+       int     delta;
+
+       delta = low - (maketic&0xff);
+
+       if (delta >= -64 && delta <= 64)
+               return (maketic&~0xff) + low;
+       if (delta > 64)
+               return (maketic&~0xff) - 256 + low;
+       if (delta < -64)
+               return (maketic&~0xff) + 256 + low;
+
+       I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
+       return 0;
+}
+
+
+//============================================================================
+
+
+/*
+==============
+=
+= HSendPacket
+=
+==============
+*/
+
+void HSendPacket (int node, int flags)
+{
+       netbuffer->checksum = NetbufferChecksum () | flags;
+
+       if (!node)
+       {
+               reboundstore = *netbuffer;
+               reboundpacket = true;
+               return;
+       }
+
+       if (!netgame)
+               I_Error ("Tried to transmit to another node");
+
+       doomcom->command = CMD_SEND;
+       doomcom->remotenode = node;
+       doomcom->datalength = NetbufferSize ();
+
+if (debugfile)
+{
+       int             i;
+       int             realretrans;
+       if (netbuffer->checksum & NCMD_RETRANSMIT)
+               realretrans = ExpandTics (netbuffer->retransmitfrom);
+       else
+               realretrans = -1;
+       fprintf (debugfile,"send (%i + %i, R %i) [%i] "
+       ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
+       for (i=0 ; i<doomcom->datalength ; i++)
+               fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
+       fprintf (debugfile,"\n");
+}
+
+       I_NetCmd ();
+}
+
+/*
+==============
+=
+= HGetPacket
+=
+= Returns false if no packet is waiting
+=
+==============
+*/
+
+boolean HGetPacket (void)
+{
+       if (reboundpacket)
+       {
+               *netbuffer = reboundstore;
+               doomcom->remotenode = 0;
+               reboundpacket = false;
+               return true;
+       }
+
+       if (!netgame)
+               return false;
+
+       doomcom->command = CMD_GET;
+       I_NetCmd ();
+       if (doomcom->remotenode == -1)
+               return false;
+
+       if (doomcom->datalength != NetbufferSize ())
+       {
+               if (debugfile)
+                       fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
+               return false;
+       }
+
+       if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
+       {
+               if (debugfile)
+                       fprintf (debugfile,"bad packet checksum\n");
+               return false;
+       }
+
+if (debugfile)
+{
+       int             realretrans;
+                       int     i;
+
+       if (netbuffer->checksum & NCMD_SETUP)
+               fprintf (debugfile,"setup packet\n");
+       else
+       {
+               if (netbuffer->checksum & NCMD_RETRANSMIT)
+                       realretrans = ExpandTics (netbuffer->retransmitfrom);
+               else
+                       realretrans = -1;
+               fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode,
+               ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
+               for (i=0 ; i<doomcom->datalength ; i++)
+                       fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
+               fprintf (debugfile,"\n");
+       }
+}
+       return true;
+}
+
+
+/*
+===================
+=
+= GetPackets
+=
+===================
+*/
+
+char    exitmsg[80];
+
+void GetPackets (void)
+{
+       int             netconsole;
+       int             netnode;
+       int             netdrone;
+       int             j;
+       ticcmd_t        *src, *dest;
+       int             dupedstart, dupedend;
+       int             skiptics;
+       int             realstart;
+
+       while (HGetPacket ())
+       {
+               if (netbuffer->checksum & NCMD_SETUP)
+                       continue;               // extra setup packet
+
+               netdrone = netbuffer->player & PL_DRONE;
+               netconsole = netbuffer->player & ~PL_DRONE;
+               netnode = doomcom->remotenode;
+               //
+               // to save bytes, only the low byte of tic numbers are sent
+               // Figure out what the rest of the bytes are
+               //
+               realstart = ExpandTics (netbuffer->starttic);
+               dupedstart = realstart*doomcom->ticdup;
+               dupedend = (realstart+netbuffer->numtics)*doomcom->ticdup;
+
+               //
+               // check for exiting the game
+               //
+               if (netbuffer->checksum & NCMD_EXIT)
+               {
+                       if (!nodeingame[netnode])
+                               continue;
+                       nodeingame[netnode] = false;
+                       if (!netdrone)
+                       {
+                               playeringame[netconsole] = false;
+                               strcpy (exitmsg, "PLAYER 1 HAS LEFT THE GAME");
+                               exitmsg[7] += netconsole;
+                               P_SetMessage(&players[consoleplayer], exitmsg, true);
+                               UpdateState |= I_MESSAGES;
+                               S_StartSound(NULL, sfx_chat);
+                       }
+                       continue;
+               }
+
+               //
+               // drone packets are just notifications
+               //
+               if (netdrone)
+               {
+                       nettics[netnode] = dupedend;
+                       continue;
+               }
+
+               nodeforplayer[netconsole] = netnode;
+
+               //
+               // check for retransmit request
+               //
+               if ( resendcount[netnode] <= 0
+               && (netbuffer->checksum & NCMD_RETRANSMIT) )
+               {
+                       resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
+if (debugfile)
+fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
+                       resendcount[netnode] = RESENDCOUNT;
+               }
+               else
+                       resendcount[netnode]--;
+
+               //
+               // check for out of order / duplicated packet
+               //
+               if (dupedend == nettics[netnode])
+                       continue;
+
+               if (dupedend < nettics[netnode])
+               {
+if (debugfile)
+fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
+                       continue;
+               }
+
+               //
+               // check for a missed packet
+               //
+               if (dupedstart > nettics[netnode])
+               {
+               // stop processing until the other system resends the missed tics
+if (debugfile)
+fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, dupedstart, nettics[netnode]);
+                       remoteresend[netnode] = true;
+                       continue;
+               }
+
+//
+// update command store from the packet
+//
+               remoteresend[netnode] = false;
+
+               skiptics = nettics[netnode]/doomcom->ticdup - realstart;
+               src = &netbuffer->cmds[skiptics];
+
+               while (nettics[netnode] < dupedend)
+               {
+                       for (j=0 ; j<doomcom->ticdup ; j++)
+                       {
+                               dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
+                               nettics[netnode]++;
+                               *dest = *src;
+                               src->chatchar = 0;
+                               if (src->buttons & BT_SPECIAL)
+                                       src->buttons = 0;
+                       }
+                       src++;
+               }
+       }
+}
+
+/*
+=============
+=
+= NetUpdate
+=
+= Builds ticcmds for console player
+= sends out a packet
+=============
+*/
+
+void NetUpdate (void)
+{
+       int             nowtime;
+       int             newtics;
+       int                             i,j;
+       int                             gameticdiv;
+       int                             realstart;
+
+       if (singletics)
+               return;         // singletic update is syncronous
+
+//
+// check time
+//
+       nowtime = I_GetTime ()/doomcom->ticdup;
+       newtics = nowtime - gametime;
+       gametime = nowtime;
+       if (newtics <= 0)                       // nothing new to update
+               goto listen;
+
+       if (skiptics <= newtics)
+       {
+               newtics -= skiptics;
+               skiptics = 0;
+       }
+       else
+       {
+               skiptics -= newtics;
+               newtics = 0;
+       }
+
+
+       netbuffer->player = consoleplayer;
+       if (doomcom->drone)
+               netbuffer->player |= PL_DRONE;
+
+//
+// drone packets
+//
+       if (doomcom->drone)
+       {
+               I_StartTic ();
+               D_ProcessEvents ();
+               goto sendit;
+       }
+
+//
+// build new ticcmds for console player
+//
+       gameticdiv = (gametic+doomcom->ticdup-1)/doomcom->ticdup;
+       for (i=0 ; i<newtics ; i++)
+       {
+               I_StartTic ();
+               D_ProcessEvents ();
+               if (maketic - gameticdiv >= BACKUPTICS/2 /* /doomcom->ticdup */- 1)
+               {
+                       newtics = i;
+                       break;          // can't hold any more
+               }
+//printf ("mk:%i ",maketic);
+               G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
+               maketic++;
+       }
+
+//
+// send the packet to the other nodes
+//
+sendit:
+       for (i=0 ; i<doomcom->numnodes ; i++)
+               if (nodeingame[i])
+               {
+                       if (doomcom->drone)
+                       {
+                               netbuffer->starttic = realstart = maketic + BACKUPTICS/2;
+                               netbuffer->numtics = 0;
+                       }
+                       else
+                       {
+                               netbuffer->starttic = realstart = resendto[i];
+                               netbuffer->numtics = maketic - realstart;
+                               resendto[i] = maketic - doomcom->extratics;
+                       }
+
+                       if (netbuffer->numtics > BACKUPTICS)
+                               I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
+
+                       for (j=0 ; j< netbuffer->numtics ; j++)
+                               netbuffer->cmds[j] =
+                                       localcmds[(realstart+j)%BACKUPTICS];
+
+                       if (remoteresend[i])
+                       {
+                               netbuffer->retransmitfrom = nettics[i]/doomcom->ticdup;
+                               HSendPacket (i, NCMD_RETRANSMIT);
+                       }
+                       else
+                       {
+                               netbuffer->retransmitfrom = 0;
+                               HSendPacket (i, 0);
+                       }
+               }
+
+//
+// listen for other packets
+//
+listen:
+
+       GetPackets ();
+}
+
+
+/*
+=====================
+=
+= CheckAbort
+=
+=====================
+*/
+
+void CheckAbort (void)
+{
+       event_t *ev;
+
+       I_WaitVBL(2);
+
+       I_StartTic ();
+       for ( ; eventtail != eventhead
+       ; eventtail = (++eventtail)&(MAXEVENTS-1) )
+       {
+               ev = &events[eventtail];
+               if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
+                       I_Error ("Network game synchronization aborted.");
+       }
+}
+
+/*
+=====================
+=
+= D_ArbitrateNetStart
+=
+=====================
+*/
+
+void D_ArbitrateNetStart (void)
+{
+       int             i;
+       boolean gotinfo[MAXNETNODES];
+
+       autostart = true;
+       memset (gotinfo,0,sizeof(gotinfo));
+
+       if (doomcom->consoleplayer)
+       {       // listen for setup info from key player
+               //printf ("listening for network start info...\n");
+               while (1)
+               {
+                       CheckAbort ();
+                       if (!HGetPacket ())
+                               continue;
+                       if (netbuffer->checksum & NCMD_SETUP)
+                       {
+                               if (netbuffer->player != VERSION)
+                                       I_Error ("Different HERETIC versions cannot play a net game!");
+                               startskill = netbuffer->retransmitfrom & 15;
+                               deathmatch = (netbuffer->retransmitfrom & 0x80) > 0;
+                               startmap = netbuffer->starttic & 15;
+                               startepisode = netbuffer->starttic >> 4;
+                               return;
+                       }
+               }
+       }
+       else
+       {       // key player, send the setup info
+//             printf ("sending network start info...\n");
+               do
+               {
+                       CheckAbort ();
+                       for (i=0 ; i<doomcom->numnodes ; i++)
+                       {
+                               netbuffer->retransmitfrom = startskill;
+                               if (deathmatch)
+                                       netbuffer->retransmitfrom |= 0x80;
+                               netbuffer->starttic = startepisode * 16 + startmap;
+                               netbuffer->player = VERSION;
+                               netbuffer->numtics = 0;
+                               HSendPacket (i, NCMD_SETUP);
+                       }
+
+                       while (HGetPacket ())
+                       {
+//printf ("got packet\n");
+                               gotinfo[netbuffer->player&0x7f] = true;
+                       }
+
+                       for (i=1 ; i<doomcom->numnodes ; i++)
+                               if (!gotinfo[i])
+                                       break;
+               } while (i < doomcom->numnodes);
+       }
+}
+
+/*
+===================
+=
+= D_CheckNetGame
+=
+= Works out player numbers among the net participants
+===================
+*/
+
+extern int                     viewangleoffset;
+
+void D_CheckNetGame (void)
+{
+       int             i;
+
+       for (i=0 ; i<MAXNETNODES ; i++)
+       {
+               nodeingame[i] = false;
+               nettics[i] = 0;
+               remoteresend[i] = false;        // set when local needs tics
+               resendto[i] = 0;                        // which tic to start sending
+       }
+
+// I_InitNetwork sets doomcom and netgame
+       I_InitNetwork ();
+       if (doomcom->id != DOOMCOM_ID)
+               I_Error ("Doomcom buffer invalid!");
+       netbuffer = &doomcom->data;
+       consoleplayer = displayplayer = doomcom->consoleplayer;
+       if (netgame)
+               D_ArbitrateNetStart ();
+//printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
+
+// read values out of doomcom
+       ticdup = doomcom->ticdup;
+
+       for (i=0 ; i<doomcom->numplayers ; i++)
+               playeringame[i] = true;
+       for (i=0 ; i<doomcom->numnodes ; i++)
+               nodeingame[i] = true;
+
+//printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
+
+}
+
+/*
+==================
+=
+= D_QuitNetGame
+=
+= Called before quitting to leave a net game without hanging the
+= other players
+=
+==================
+*/
+
+void D_QuitNetGame (void)
+{
+       int             i, j;
+
+       if (debugfile)
+               fclose (debugfile);
+
+       if (!netgame || !usergame || consoleplayer == -1)
+               return;
+
+// send a bunch of packets for security
+       netbuffer->player = consoleplayer;
+       if (doomcom->drone)
+               netbuffer->player |= PL_DRONE;
+       netbuffer->numtics = 0;
+       for (i=0 ; i<4 ; i++)
+       {
+               for (j=1 ; j<doomcom->numnodes ; j++)
+                       if (nodeingame[j])
+                               HSendPacket (j, NCMD_EXIT);
+               I_WaitVBL (1);
+       }
+}
+
+
+
+/*
+===============
+=
+= TryRunTics
+=
+===============
+*/
+
+int    frametics[4], frameon;
+int    frameskip[4];
+int            oldnettics;
+extern boolean advancedemo;
+
+void TryRunTics (void)
+{
+       int             i;
+       int             lowtic, nextlowest;
+       int             entertic;
+       int     static          oldentertics;
+       int                             realtics, availabletics;
+       int                             counts;
+       int                             numplaying;
+
+//
+// get real tics
+//
+       entertic = I_GetTime ();
+       realtics = entertic - oldentertics;
+       oldentertics = entertic;
+
+//
+// get available tics
+//
+       NetUpdate ();
+
+       lowtic = nextlowest = MAXINT;
+       numplaying = 0;
+       for (i=0 ; i<doomcom->numnodes ; i++)
+               if (nodeingame[i])
+               {
+                       numplaying++;
+                       if (nettics[i] < lowtic)
+                       {
+                               nextlowest = lowtic;
+                               lowtic = nettics[i];
+                       }
+                       else if (nettics[i] < nextlowest)
+                               nextlowest = nettics[i];
+               }
+       availabletics = lowtic - gametic;
+
+
+//
+// decide how many tics to run
+//
+       if (realtics < availabletics-1)
+               counts = realtics+1;
+       else if (realtics < availabletics)
+               counts = realtics;
+       else
+               counts = availabletics;
+       if (counts < 1)
+               counts = 1;
+
+       frameon++;
+
+if (debugfile)
+       fprintf (debugfile,"=======real: %i  avail: %i  game: %i\n",realtics, availabletics,counts);
+
+//=============================================================================
+//
+//     ideally nettics[0] should be 1 - 3 tics above lowtic
+//     if we are consistantly slower, speed up time
+//     drones should never hold up the other players
+//
+       for (i=0 ; i<MAXPLAYERS ; i++)
+               if (playeringame[i])
+                       break;
+       if (consoleplayer == i)
+       {       // the key player does not adapt
+       }
+       else
+       {
+               if (nettics[0] <= nettics[nodeforplayer[i]])
+               {
+                       gametime--;
+//                     printf ("-");
+               }
+               frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
+               oldnettics = nettics[0];
+               if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
+               {
+                       skiptics = 1;
+//                     printf ("+");
+               }
+       }
+//=============================================================================
+
+//
+// wait for new tics if needed
+//
+       while (lowtic < gametic + counts)
+       {
+
+               NetUpdate ();
+               lowtic = MAXINT;
+
+               for (i=0 ; i<doomcom->numnodes ; i++)
+                       if (nodeingame[i] && nettics[i] < lowtic)
+                               lowtic = nettics[i];
+
+               if (lowtic < gametic)
+                       I_Error ("TryRunTics: lowtic < gametic");
+
+               // don't stay in here forever -- give the menu a chance to work
+               if (I_GetTime () - entertic >= 20)
+               {
+                       MN_Ticker ();
+                       return;
+               }
+       }
+
+
+//
+// run the tics
+//
+       while (counts--)
+       {
+               if (advancedemo)
+                       D_DoAdvanceDemo ();
+               MN_Ticker ();
+               G_Ticker ();
+               NetUpdate ();                                   // check for new console commands
+               gametic++;
+       }
+}
diff --git a/base/f_finale.c b/base/f_finale.c
new file mode 100644 (file)
index 0000000..35e3b08
--- /dev/null
@@ -0,0 +1,438 @@
+// F_finale.c
+
+#include <ctype.h>     // for toupper()
+#include "doomdef.h"
+#include "soundst.h"
+#ifdef RENDER3D
+#include "ogl_def.h"
+#define WR_CacheLumpName(a,b)        W_GetNumForName(a)
+#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)
+#define V_DrawRawScreen(a)          OGL_DrawRawScreen(a)
+#else
+#define WR_CacheLumpName(a,b)           W_CacheLumpName(a,b)
+#endif
+
+int             finalestage;            // 0 = text, 1 = art screen
+int             finalecount;
+
+#define TEXTSPEED       3
+#define TEXTWAIT        250
+
+char    *e1text = E1TEXT;
+char    *e2text = E2TEXT;
+char    *e3text = E3TEXT;
+char    *e4text = E4TEXT;
+char    *e5text = E5TEXT;
+char    *finaletext;
+char    *finaleflat;
+
+int FontABaseLump;
+
+extern boolean automapactive;
+extern boolean viewactive;
+
+extern void D_StartTitle(void);
+
+/*
+=======================
+=
+= F_StartFinale
+=
+=======================
+*/
+
+void F_StartFinale (void)
+{
+       gameaction = ga_nothing;
+       gamestate = GS_FINALE;
+       viewactive = false;
+       automapactive = false;
+       players[consoleplayer].messageTics = 1;
+       players[consoleplayer].message = NULL;
+
+       switch(gameepisode)
+       {
+               case 1:
+                       finaleflat = "FLOOR25";
+                       finaletext = e1text;
+                       break;
+               case 2:
+                       finaleflat = "FLATHUH1";
+                       finaletext = e2text;
+                       break;
+               case 3:
+                       finaleflat = "FLTWAWA2";
+                       finaletext = e3text;
+                       break;
+               case 4:
+                       finaleflat = "FLOOR28";
+                       finaletext = e4text;
+                       break;
+               case 5:
+                       finaleflat = "FLOOR08";
+                       finaletext = e5text;
+                       break;
+       }
+
+       finalestage = 0;
+       finalecount = 0;
+       FontABaseLump = W_GetNumForName("FONTA_S")+1;
+
+//      S_ChangeMusic(mus_victor, true);
+       S_StartSong(mus_cptd, true);
+}
+
+
+
+boolean F_Responder (event_t *event)
+{
+       if(event->type != ev_keydown)
+       {
+               return false;
+       }
+       if(finalestage == 1 && gameepisode == 2)
+       { // we're showing the water pic, make any key kick to demo mode
+               finalestage++;
+               // Hmm... naughty Heretic again :/ - DDOI
+               //memset((byte *)0xa0000, 0, SCREENWIDTH*SCREENHEIGHT);
+               //memset(screen, 0, SCREENWIDTH*SCREENHEIGHT);
+               I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE));
+               return true;
+       }
+       return false;
+}
+
+
+/*
+=======================
+=
+= F_Ticker
+=
+=======================
+*/
+
+void F_Ticker (void)
+{
+       finalecount++;
+       if (!finalestage && finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT)
+       {
+               finalecount = 0;
+               if(!finalestage)
+               {
+                       finalestage = 1;
+               }
+
+//              wipegamestate = -1;             // force a wipe
+/*
+               if (gameepisode == 3)
+                       S_StartMusic (mus_bunny);
+*/
+       }
+}
+
+
+/*
+=======================
+=
+= F_TextWrite
+=
+=======================
+*/
+
+//#include "HU_stuff.h"
+//extern        patch_t *hu_font[HU_FONTSIZE];
+
+void F_TextWrite (void)
+{
+#ifndef RENDER3D
+       byte    *src, *dest;
+       int             x,y;
+#endif
+       int             count;
+       char    *ch;
+       int             c;
+       int             cx, cy;
+       patch_t *w;
+
+//
+// erase the entire screen to a tiled background
+//
+#ifndef RENDER3D
+       src = W_CacheLumpName(finaleflat, PU_CACHE);
+       dest = screen;
+       for (y=0 ; y<SCREENHEIGHT ; y++)
+       {
+               for (x=0 ; x<SCREENWIDTH/64 ; x++)
+               {
+                       memcpy (dest, src+((y&63)<<6), 64);
+                       dest += 64;
+               }
+               if (SCREENWIDTH&63)
+               {
+                       memcpy (dest, src+((y&63)<<6), SCREENWIDTH&63);
+                       dest += (SCREENWIDTH&63);
+               }
+       }
+#else
+       OGL_SetFlat (R_FlatNumForName(finaleflat));
+       OGL_DrawRectTiled(0,0,SCREENWIDTH,SCREENHEIGHT,64,64);
+#endif
+
+//      V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
+
+//
+// draw some of the text onto the screen
+//
+       cx = 20;
+       cy = 5;
+       ch = finaletext;
+
+       count = (finalecount - 10)/TEXTSPEED;
+       if (count < 0)
+               count = 0;
+       for ( ; count ; count-- )
+       {
+               c = *ch++;
+               if (!c)
+                       break;
+               if (c == '\n')
+               {
+                       cx = 20;
+                       cy += 9;
+                       continue;
+               }
+
+               c = toupper(c);
+               if (c < 33)
+               {
+                       cx += 5;
+                       continue;
+               }
+
+               w = W_CacheLumpNum(FontABaseLump+c-33, PU_CACHE);
+               if (cx+w->width > SCREENWIDTH)
+                       break;
+#ifdef RENDER3D
+               OGL_DrawPatch (cx,cy,FontABaseLump+c-33);
+#else
+               V_DrawPatch(cx, cy, w);
+#endif
+               cx += w->width;
+       }
+
+}
+
+void F_DrawPatchCol (int x, patch_t *patch, int col)
+{
+       column_t        *column;
+       byte            *source, *dest, *desttop;
+       int                     count;
+
+       column = (column_t *)((byte *)patch + LONG(patch->columnofs[col]));
+       desttop = screen+x;
+
+// step through the posts in a column
+
+       while (column->topdelta != 0xff )
+       {
+               source = (byte *)column + 3;
+               dest = desttop + column->topdelta*SCREENWIDTH;
+               count = column->length;
+
+               while (count--)
+               {
+                       *dest = *source++;
+                       dest += SCREENWIDTH;
+               }
+               column = (column_t *)(  (byte *)column + column->length+ 4 );
+       }
+}
+
+/*
+==================
+=
+= F_DemonScroll
+=
+==================
+*/
+
+void F_DemonScroll(void)
+{
+       byte *p1, *p2;
+       static int yval = 0;
+       static int nextscroll = 0;
+
+       if(finalecount < nextscroll)
+       {
+               return;
+       }
+       p1 = W_CacheLumpName("FINAL1", PU_LEVEL);
+       p2 = W_CacheLumpName("FINAL2", PU_LEVEL);
+       if(finalecount < 70)
+       {
+               memcpy(screen, p1, SCREENHEIGHT*SCREENWIDTH);
+               nextscroll = finalecount;
+               return;
+       }
+       if(yval < 64000)
+       {
+               memcpy(screen, p2+SCREENHEIGHT*SCREENWIDTH-yval, yval);
+               memcpy(screen+yval, p1, SCREENHEIGHT*SCREENWIDTH-yval);
+               yval += SCREENWIDTH;
+               nextscroll = finalecount+3;
+       }
+       else
+       { //else, we'll just sit here and wait, for now
+               memcpy(screen, p2, SCREENWIDTH*SCREENHEIGHT);
+       }
+}
+
+/*
+==================
+=
+= F_DrawUnderwater
+=
+==================
+*/
+
+void F_DrawUnderwater(void)
+{
+#ifndef RENDER3D
+       static boolean underwawa;
+#endif
+       extern boolean MenuActive;
+       extern boolean askforquit;
+
+       switch(finalestage)
+       {
+               case 1:
+#ifndef RENDER3D       
+                       if(!underwawa)
+                       {
+                               underwawa = true;
+                               // Naughty Heretic :/ - DDOI
+                               //memset((byte *)0xa0000, 0, SCREENWIDTH*SCREENHEIGHT);
+                               I_SetPalette(W_CacheLumpName("E2PAL", PU_CACHE));
+                               memcpy(screen, W_CacheLumpName("E2END", PU_CACHE),
+                                       SCREENWIDTH*SCREENHEIGHT);
+                       }
+#endif
+                       paused = false;
+                       MenuActive = false;
+                       askforquit = false;
+
+                       break;
+               case 2:
+#ifndef RENDER3D               
+                       memcpy(screen, W_CacheLumpName("TITLE", PU_CACHE),
+                               SCREENWIDTH*SCREENHEIGHT);
+#else
+                       OGL_DrawRawScreen(W_GetNumForName("TITLE"));
+#endif
+                       //D_StartTitle(); // go to intro/demo mode.
+       }
+}
+
+
+#if 0
+/*
+==================
+=
+= F_BunnyScroll
+=
+==================
+*/
+
+void F_BunnyScroll (void)
+{
+       int                     scrolled, x;
+       patch_t         *p1, *p2;
+       char            name[10];
+       int                     stage;
+       static int      laststage;
+
+       p1 = W_CacheLumpName ("PFUB2", PU_LEVEL);
+       p2 = W_CacheLumpName ("PFUB1", PU_LEVEL);
+
+       V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
+
+       scrolled = 320 - (finalecount-230)/2;
+       if (scrolled > 320)
+               scrolled = 320;
+       if (scrolled < 0)
+               scrolled = 0;
+
+       for ( x=0 ; x<SCREENWIDTH ; x++)
+       {
+               if (x+scrolled < 320)
+                       F_DrawPatchCol (x, p1, x+scrolled);
+               else
+                       F_DrawPatchCol (x, p2, x+scrolled - 320);
+       }
+
+       if (finalecount < 1130)
+               return;
+       if (finalecount < 1180)
+       {
+               V_DrawPatch ((SCREENWIDTH-13*8)/2, (SCREENHEIGHT-8*8)/2,0, W_CacheLumpName ("END0",PU_CACHE));
+               laststage = 0;
+               return;
+       }
+
+       stage = (finalecount-1180) / 5;
+       if (stage > 6)
+               stage = 6;
+       if (stage > laststage)
+       {
+               S_StartSound (NULL, sfx_pistol);
+               laststage = stage;
+       }
+
+       sprintf (name,"END%i",stage);
+       V_DrawPatch ((SCREENWIDTH-13*8)/2, (SCREENHEIGHT-8*8)/2, W_CacheLumpName (name,PU_CACHE));
+}
+#endif
+
+/*
+=======================
+=
+= F_Drawer
+=
+=======================
+*/
+
+void F_Drawer(void)
+{
+       UpdateState |= I_FULLSCRN;
+       if (!finalestage)
+               F_TextWrite ();
+       else
+       {
+               switch (gameepisode)
+               {
+                       case 1:
+                               if(shareware)
+                               {
+                                       V_DrawRawScreen(WR_CacheLumpName("ORDER", PU_CACHE));
+                               }
+                               else
+                               {
+                                       V_DrawRawScreen(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(WR_CacheLumpName("CREDIT", PU_CACHE));
+                               break;
+               }
+       }
+}
diff --git a/base/g_game.c b/base/g_game.c
new file mode 100644 (file)
index 0000000..e8c1aac
--- /dev/null
@@ -0,0 +1,1950 @@
+
+// G_game.c
+
+#include <stdio.h>
+#include <string.h>
+#include "doomdef.h"
+#include "p_local.h"
+#include "soundst.h"
+
+// Macros
+
+#define SVG_RAM 0
+#define SVG_FILE 1
+#define SAVE_GAME_TERMINATOR 0x1d
+#define AM_STARTKEY     9
+
+// Functions
+
+boolean G_CheckDemoStatus (void);
+void G_ReadDemoTiccmd (ticcmd_t *cmd);
+void G_WriteDemoTiccmd (ticcmd_t *cmd);
+void G_PlayerReborn (int player);
+void G_InitNew (skill_t skill, int episode, int map);
+
+void G_DoReborn (int playernum);
+
+void G_DoLoadLevel (void);
+void G_DoNewGame (void);
+void G_DoLoadGame (void);
+void G_DoPlayDemo (void);
+void G_DoCompleted (void);
+void G_DoVictory (void);
+void G_DoWorldDone (void);
+void G_DoSaveGame (void);
+
+void D_PageTicker(void);
+void D_AdvanceDemo(void);
+
+struct
+{
+       mobjtype_t type;
+       int speed[2];
+} MonsterMissileInfo[] =
+{
+       { MT_IMPBALL, {10, 20} },
+       { MT_MUMMYFX1, {9, 18} },
+       { MT_KNIGHTAXE, {9, 18} },
+       { MT_REDAXE, {9, 18} },
+       { MT_BEASTBALL, {12, 20} },
+       { MT_WIZFX1, {18, 24} },
+       { MT_SNAKEPRO_A, {14, 20} },
+       { MT_SNAKEPRO_B, {14, 20} },
+       { MT_HEADFX1, {13, 20} },
+       { MT_HEADFX3, {10, 18} },
+       { MT_MNTRFX1, {20, 26} },
+       { MT_MNTRFX2, {14, 20} },
+       { MT_SRCRFX1, {20, 28} },
+       { MT_SOR2FX1, {20, 28} },
+       { -1, {-1, -1} } // Terminator
+};
+
+FILE *SaveGameFP;
+int SaveGameType;
+
+gameaction_t    gameaction;
+gamestate_t     gamestate;
+skill_t         gameskill;
+boolean         respawnmonsters;
+int             gameepisode;
+int             gamemap;
+int                              prevmap;
+
+boolean         paused;
+boolean         sendpause;              // send a pause event next tic
+boolean         sendsave;               // send a save event next tic
+boolean         usergame;               // ok to save / end game
+
+boolean         timingdemo;             // if true, exit with report on completion
+int             starttime;              // for comparative timing purposes
+
+boolean         viewactive;
+
+boolean         deathmatch;             // only if started as net death
+boolean         netgame;                // only true if packets are broadcast
+boolean         playeringame[MAXPLAYERS];
+player_t        players[MAXPLAYERS];
+
+int             consoleplayer;          // player taking events and displaying
+int             displayplayer;          // view being displayed
+int             gametic;
+int             levelstarttic;          // gametic at level start
+int             totalkills, totalitems, totalsecret;    // for intermission
+
+char            demoname[32];
+boolean         demorecording;
+boolean         demoplayback;
+byte            *demobuffer, *demo_p;
+boolean         singledemo;             // quit after playing a demo from cmdline
+
+boolean         precache = true;        // if true, load all graphics at start
+
+short            consistancy[MAXPLAYERS][BACKUPTICS];
+
+byte            *savebuffer, *save_p;
+
+
+//
+// controls (have defaults)
+//
+int             key_right, key_left, key_up, key_down;
+int             key_strafeleft, key_straferight;
+int             key_fire, key_use, key_strafe, key_speed;
+int                             key_flyup, key_flydown, key_flycenter;
+int                             key_lookup, key_lookdown, key_lookcenter;
+int                             key_invleft, key_invright, key_useartifact;
+
+int             mousebfire;
+int             mousebstrafe;
+int             mousebforward;
+
+int             joybfire;
+int             joybstrafe;
+int             joybuse;
+int             joybspeed;
+
+
+
+#define MAXPLMOVE       0x32
+
+fixed_t         forwardmove[2] = {0x19, 0x32};
+fixed_t         sidemove[2] = {0x18, 0x28};
+fixed_t         angleturn[3] = {640, 1280, 320};     // + slow turn
+#define SLOWTURNTICS    6
+
+#define NUMKEYS 256
+boolean         gamekeydown[NUMKEYS];
+int             turnheld;                   // for accelerative turning
+int                              lookheld;
+
+
+boolean         mousearray[4];
+boolean         *mousebuttons = &mousearray[1];
+       // allow [-1]
+int             mousex, mousey;             // mouse values are used once
+int             dclicktime, dclickstate, dclicks;
+int             dclicktime2, dclickstate2, dclicks2;
+
+int             joyxmove, joyymove;         // joystick values are repeated
+boolean         joyarray[5];
+boolean         *joybuttons = &joyarray[1];     // allow [-1]
+
+int     savegameslot;
+char    savedescription[32];
+
+int inventoryTics;
+
+#ifdef __WATCOMC__
+extern externdata_t *i_ExternData;
+#endif
+
+//=============================================================================
+// Not used - ripped out for Heretic
+/*
+int G_CmdChecksum(ticcmd_t *cmd)
+{
+       int     i;
+       int sum;
+
+       sum = 0;
+       for(i = 0; i < sizeof(*cmd)/4-1; i++)
+       {
+               sum += ((int *)cmd)[i];
+       }
+       return(sum);
+}
+*/
+
+/*
+====================
+=
+= G_BuildTiccmd
+=
+= Builds a ticcmd from all of the available inputs or reads it from the
+= demo buffer.
+= If recording a demo, write it out
+====================
+*/
+
+extern boolean inventory;
+extern int curpos;
+extern int inv_ptr;
+
+extern  int             isCyberPresent;     // is CyberMan present?
+boolean usearti = true;
+void I_ReadCyberCmd (ticcmd_t *cmd);
+
+void G_BuildTiccmd (ticcmd_t *cmd)
+{
+       int             i;
+       boolean         strafe, bstrafe;
+       int             speed, tspeed, lspeed;
+       int             forward, side;
+       int look, arti;
+       int flyheight;
+
+       extern boolean noartiskip;
+
+#ifdef __WATCOMC__
+       int angleDelta;
+       static int oldAngle;
+       extern int newViewAngleOff;
+       static int externInvKey;
+       extern boolean automapactive;
+       event_t ev;
+#endif
+
+
+       memset (cmd,0,sizeof(*cmd));
+       //cmd->consistancy =
+       //      consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS];
+       cmd->consistancy =
+               consistancy[consoleplayer][maketic%BACKUPTICS];
+
+//printf ("cons: %i\n",cmd->consistancy);
+
+       strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
+               || joybuttons[joybstrafe];
+       speed = gamekeydown[key_speed] || joybuttons[joybspeed]
+               || joybuttons[joybspeed];
+#ifdef __WATCOMC__
+       if(useexterndriver)
+       {
+               speed |= (i_ExternData->buttons&EBT_SPEED);
+               strafe |= (i_ExternData->buttons&EBT_STRAFE);
+       }
+#endif
+
+       forward = side = look = arti = flyheight = 0;
+
+//
+// use two stage accelerative turning on the keyboard and joystick
+//
+       if (joyxmove < 0 || joyxmove > 0
+       || gamekeydown[key_right] || gamekeydown[key_left])
+               turnheld += ticdup;
+       else
+               turnheld = 0;
+       if (turnheld < SLOWTURNTICS)
+               tspeed = 2;             // slow turn
+       else
+               tspeed = speed;
+
+       if(gamekeydown[key_lookdown] || gamekeydown[key_lookup])
+       {
+               lookheld += ticdup;
+       }
+       else
+       {
+               lookheld = 0;
+       }
+       if(lookheld < SLOWTURNTICS)
+       {
+               lspeed = 1;
+       }
+       else
+       {
+               lspeed = 2;
+       }
+
+//
+// let movement keys cancel each other out
+//
+       if(strafe)
+       {
+               if (gamekeydown[key_right])
+                       side += sidemove[speed];
+               if (gamekeydown[key_left])
+                       side -= sidemove[speed];
+               if (joyxmove > 0)
+                       side += sidemove[speed];
+               if (joyxmove < 0)
+                       side -= sidemove[speed];
+       }
+       else
+       {
+               if (gamekeydown[key_right])
+                       cmd->angleturn -= angleturn[tspeed];
+               if (gamekeydown[key_left])
+                       cmd->angleturn += angleturn[tspeed];
+               if (joyxmove > 0)
+                       cmd->angleturn -= angleturn[tspeed];
+               if (joyxmove < 0)
+                       cmd->angleturn += angleturn[tspeed];
+       }
+
+       if (gamekeydown[key_up])
+               forward += forwardmove[speed];
+       if (gamekeydown[key_down])
+               forward -= forwardmove[speed];
+       if (joyymove < 0)
+               forward += forwardmove[speed];
+       if (joyymove > 0)
+               forward -= forwardmove[speed];
+       if (gamekeydown[key_straferight])
+               side += sidemove[speed];
+       if (gamekeydown[key_strafeleft])
+               side -= sidemove[speed];
+
+       // Look up/down/center keys
+       if(gamekeydown[key_lookup])
+       {
+               look = lspeed;
+       }
+       if(gamekeydown[key_lookdown])
+       {
+               look = -lspeed;
+       }
+#ifdef __WATCOMC__
+       if(gamekeydown[key_lookcenter] && !useexterndriver)
+       {
+               look = TOCENTER;
+       }
+#else
+       if(gamekeydown[key_lookcenter])
+       {
+               look = TOCENTER;
+       }
+#endif
+
+#ifdef __WATCOMC__
+       if(useexterndriver && look != TOCENTER && (gamestate == GS_LEVEL ||
+               gamestate == GS_INTERMISSION))
+       {
+               if(i_ExternData->moveForward)
+               {
+                       forward += i_ExternData->moveForward;
+                       if(speed)
+                       {
+                               forward <<= 1;
+                       }
+               }
+               if(i_ExternData->angleTurn)
+               {
+                       if(strafe)
+                       {
+                               side += i_ExternData->angleTurn;
+                       }
+                       else
+                       {
+                               cmd->angleturn += i_ExternData->angleTurn;
+                       }
+               }
+               if(i_ExternData->moveSideways)
+               {
+                       side += i_ExternData->moveSideways;
+                       if(speed)
+                       {
+                               side <<= 1;
+                       }
+               }
+               if(i_ExternData->buttons&EBT_CENTERVIEW)
+               {
+                       look = TOCENTER;
+                       oldAngle = 0;
+               }
+               else if(i_ExternData->pitch)
+               {
+                       angleDelta = i_ExternData->pitch-oldAngle;
+                       if(abs(angleDelta) < 35)
+                       {
+                               look = angleDelta/5;
+                       }
+                       else
+                       {
+                               look = 7*(angleDelta > 0 ? 1 : -1);
+                       }
+                       if(look == TOCENTER)
+                       {
+                               look++;
+                       }
+                       oldAngle += look*5;
+               }
+               if(i_ExternData->flyDirection)
+               {
+                       if(i_ExternData->flyDirection > 0)
+                       {
+                               flyheight = 5;
+                       }
+                       else
+                       {
+                               flyheight = -5;
+                       }
+               }
+               if(abs(newViewAngleOff-i_ExternData->angleHead) < 3000)
+               {
+                       newViewAngleOff = i_ExternData->angleHead;
+               }
+               if(i_ExternData->buttons&EBT_FIRE)
+               {
+                       cmd->buttons |= BT_ATTACK;
+               }
+               if(i_ExternData->buttons&EBT_OPENDOOR)
+               {
+                       cmd->buttons |= BT_USE;
+               }
+               if(i_ExternData->buttons&EBT_PAUSE)
+               {
+                       cmd->buttons = BT_SPECIAL | BTS_PAUSE;
+                       i_ExternData->buttons &= ~EBT_PAUSE;
+               }
+               if(externInvKey&EBT_USEARTIFACT)
+               {
+                       ev.type = ev_keyup;
+                       ev.data1 = key_useartifact;
+                       D_PostEvent(&ev);
+                       externInvKey &= ~EBT_USEARTIFACT;
+               }
+               else if(i_ExternData->buttons&EBT_USEARTIFACT)
+               {
+                       externInvKey |= EBT_USEARTIFACT;
+                       ev.type = ev_keydown;
+                       ev.data1 = key_useartifact;
+                       D_PostEvent(&ev);
+               }
+               if(externInvKey&EBT_INVENTORYRIGHT)
+               {
+                       ev.type = ev_keyup;
+                       ev.data1 = key_invright;
+                       D_PostEvent(&ev);
+                       externInvKey &= ~EBT_INVENTORYRIGHT;
+               }
+               else if(i_ExternData->buttons&EBT_INVENTORYRIGHT)
+               {
+                       externInvKey |= EBT_INVENTORYRIGHT;
+                       ev.type = ev_keydown;
+                       ev.data1 = key_invright;
+                       D_PostEvent(&ev);
+               }
+               if(externInvKey&EBT_INVENTORYLEFT)
+               {
+                       ev.type = ev_keyup;
+                       ev.data1 = key_invleft;
+                       D_PostEvent(&ev);
+                       externInvKey &= ~EBT_INVENTORYLEFT;
+               }
+               else if(i_ExternData->buttons&EBT_INVENTORYLEFT)
+               {
+                       externInvKey |= EBT_INVENTORYLEFT;
+                       ev.type = ev_keydown;
+                       ev.data1 = key_invleft;
+                       D_PostEvent(&ev);
+               }
+               if(i_ExternData->buttons&EBT_FLYDROP)
+               {
+                       flyheight = TOCENTER;
+               }
+               if(gamestate == GS_LEVEL)
+               {
+                       if(externInvKey&EBT_MAP)
+                       { // AutoMap
+                               ev.type = ev_keyup;
+                               ev.data1 = AM_STARTKEY;
+                               D_PostEvent(&ev);
+                               externInvKey &= ~EBT_MAP;
+                       }
+                       else if(i_ExternData->buttons&EBT_MAP)
+                       {
+                               externInvKey |= EBT_MAP;
+                               ev.type = ev_keydown;
+                               ev.data1 = AM_STARTKEY;
+                               D_PostEvent(&ev);
+                       }
+               }
+#if 0
+               if((i = (i_ExternData->buttons>>EBT_WEAPONSHIFT)&EBT_WEAPONMASK) != 0)
+               {
+                       cmd->buttons |= BT_CHANGE;
+                       cmd->buttons |= (i-1)<<BT_WEAPONSHIFT;
+               }
+#endif
+               if(i_ExternData->buttons&EBT_WEAPONCYCLE)
+               {
+                       int curWeapon;
+                       player_t *pl;
+
+                       pl = &players[consoleplayer];
+                       curWeapon = pl->readyweapon;
+                       for(curWeapon = (curWeapon+1)&7; curWeapon != pl->readyweapon;
+                               curWeapon = (curWeapon+1)&7)
+                       {
+                               if(pl->weaponowned[curWeapon])
+                               {
+                                       if(curWeapon >= wp_goldwand && curWeapon <= wp_mace &&
+                                               !pl->ammo[wpnlev1info[curWeapon].ammo])
+                                       { // weapon that requires ammo is empty
+                                               continue;
+                                       }
+                                       break;
+                               }
+                       }
+                       cmd->buttons |= BT_CHANGE;
+                       cmd->buttons |= curWeapon<<BT_WEAPONSHIFT;
+               }
+       }
+#endif
+
+       // Fly up/down/drop keys
+       if(gamekeydown[key_flyup])
+       {
+               flyheight = 5; // note that the actual flyheight will be twice this
+       }
+       if(gamekeydown[key_flydown])
+       {
+               flyheight = -5;
+       }
+       if(gamekeydown[key_flycenter])
+       {
+               flyheight = TOCENTER;
+#ifdef __WATCOMC__
+               if(!useexterndriver)
+               {
+                       look = TOCENTER;
+               }
+#else
+               look = TOCENTER;
+#endif
+       }
+
+       // Use artifact key
+       if(gamekeydown[key_useartifact])
+       {
+               if(gamekeydown[key_speed] && !noartiskip)
+               {
+                       if(players[consoleplayer].inventory[inv_ptr].type != arti_none)
+                       {
+                               gamekeydown[key_useartifact] = false;
+                               cmd->arti = 0xff; // skip artifact code
+                       }
+               }
+               else
+               {
+                       if(inventory)
+                       {
+                               players[consoleplayer].readyArtifact =
+                                       players[consoleplayer].inventory[inv_ptr].type;
+                               inventory = false;
+                               cmd->arti = 0;
+                               usearti = false;
+                       }
+                       else if(usearti)
+                       {
+                               cmd->arti = players[consoleplayer].inventory[inv_ptr].type;
+                               usearti = false;
+                       }
+               }
+       }
+       if(gamekeydown[127] && !cmd->arti
+               && !players[consoleplayer].powers[pw_weaponlevel2])
+       {
+               gamekeydown[127] = false;
+               cmd->arti = arti_tomeofpower;
+       }
+
+//
+// buttons
+//
+       cmd->chatchar = CT_dequeueChatChar();
+
+       if (gamekeydown[key_fire] || mousebuttons[mousebfire]
+               || joybuttons[joybfire])
+               cmd->buttons |= BT_ATTACK;
+
+       if (gamekeydown[key_use] || joybuttons[joybuse] )
+       {
+               cmd->buttons |= BT_USE;
+               dclicks = 0;                    // clear double clicks if hit use button
+       }
+
+       for(i = 0; i < NUMWEAPONS-2; i++)
+       {
+               if(gamekeydown['1'+i])
+               {
+                       cmd->buttons |= BT_CHANGE;
+                       cmd->buttons |= i<<BT_WEAPONSHIFT;
+                       break;
+               }
+       }
+
+//
+// mouse
+//
+       if (mousebuttons[mousebforward])
+       {
+               forward += forwardmove[speed];
+       }
+
+//
+// forward double click
+//
+       if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 )
+       {
+               dclickstate = mousebuttons[mousebforward];
+               if (dclickstate)
+                       dclicks++;
+               if (dclicks == 2)
+               {
+                       cmd->buttons |= BT_USE;
+                       dclicks = 0;
+               }
+               else
+                       dclicktime = 0;
+       }
+       else
+       {
+               dclicktime += ticdup;
+               if (dclicktime > 20)
+               {
+                       dclicks = 0;
+                       dclickstate = 0;
+               }
+       }
+
+//
+// strafe double click
+//
+       bstrafe = mousebuttons[mousebstrafe]
+|| joybuttons[joybstrafe];
+       if (bstrafe != dclickstate2 && dclicktime2 > 1 )
+       {
+               dclickstate2 = bstrafe;
+               if (dclickstate2)
+                       dclicks2++;
+               if (dclicks2 == 2)
+               {
+                       cmd->buttons |= BT_USE;
+                       dclicks2 = 0;
+               }
+               else
+                       dclicktime2 = 0;
+       }
+       else
+       {
+               dclicktime2 += ticdup;
+               if (dclicktime2 > 20)
+               {
+                       dclicks2 = 0;
+                       dclickstate2 = 0;
+               }
+       }
+
+       if (strafe)
+       {
+               side += mousex*2;
+       }
+       else
+       {
+               cmd->angleturn -= mousex*0x8;
+       }
+       forward += mousey;
+       mousex = mousey = 0;
+
+       if (forward > MAXPLMOVE)
+               forward = MAXPLMOVE;
+       else if (forward < -MAXPLMOVE)
+               forward = -MAXPLMOVE;
+       if (side > MAXPLMOVE)
+               side = MAXPLMOVE;
+       else if (side < -MAXPLMOVE)
+               side = -MAXPLMOVE;
+
+       cmd->forwardmove += forward;
+       cmd->sidemove += side;
+       if(players[consoleplayer].playerstate == PST_LIVE)
+       {
+               if(look < 0)
+               {
+                       look += 16;
+               }
+               cmd->lookfly = look;
+       }
+       if(flyheight < 0)
+       {
+               flyheight += 16;
+       }
+       cmd->lookfly |= flyheight<<4;
+
+//
+// special buttons
+//
+       if (sendpause)
+       {
+               sendpause = false;
+               cmd->buttons = BT_SPECIAL | BTS_PAUSE;
+       }
+
+       if (sendsave)
+       {
+               sendsave = false;
+               cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT);
+       }
+
+}
+
+
+/*
+==============
+=
+= G_DoLoadLevel
+=
+==============
+*/
+
+void G_DoLoadLevel (void)
+{
+       int             i;
+
+       levelstarttic = gametic;        // for time calculation
+       gamestate = GS_LEVEL;
+       for (i=0 ; i<MAXPLAYERS ; i++)
+       {
+               if (playeringame[i] && players[i].playerstate == PST_DEAD)
+                       players[i].playerstate = PST_REBORN;
+               memset (players[i].frags,0,sizeof(players[i].frags));
+       }
+
+       P_SetupLevel (gameepisode, gamemap, 0, gameskill);
+       displayplayer = consoleplayer;      // view the guy you are playing
+       starttime = I_GetTime ();
+       gameaction = ga_nothing;
+       Z_CheckHeap ();
+
+//
+// clear cmd building stuff
+//
+
+       memset (gamekeydown, 0, sizeof(gamekeydown));
+       joyxmove = joyymove = 0;
+       mousex = mousey = 0;
+       sendpause = sendsave = paused = false;
+       memset (mousebuttons, 0, sizeof(mousebuttons));
+       memset (joybuttons, 0, sizeof(joybuttons));
+}
+
+
+/*
+===============================================================================
+=
+= G_Responder
+=
+= get info needed to make ticcmd_ts for the players
+=
+===============================================================================
+*/
+
+boolean G_Responder(event_t *ev)
+{
+       player_t *plr;
+       extern boolean MenuActive;
+
+       plr = &players[consoleplayer];
+       if(ev->type == ev_keyup && ev->data1 == key_useartifact)
+       { // flag to denote that it's okay to use an artifact
+               if(!inventory)
+               {
+                       plr->readyArtifact = plr->inventory[inv_ptr].type;
+               }
+               usearti = true;
+       }
+
+       // Check for spy mode player cycle
+       if(gamestate == GS_LEVEL && ev->type == ev_keydown
+               && ev->data1 == KEY_F12 && !deathmatch)
+       { // Cycle the display player
+               do
+               {
+                       displayplayer++;
+                       if(displayplayer == MAXPLAYERS)
+                       {
+                               displayplayer = 0;
+                       }
+               } while(!playeringame[displayplayer]
+                       && displayplayer != consoleplayer);
+               return(true);
+       }
+
+       if(gamestate == GS_LEVEL)
+       {
+               if(CT_Responder(ev))
+               { // Chat ate the event
+                       return(true);
+               }
+               if(SB_Responder(ev))
+               { // Status bar ate the event
+                       return(true);
+               }
+               if(AM_Responder(ev))
+               { // Automap ate the event
+                       return(true);
+               }
+       }
+
+       switch(ev->type)
+       {
+               case ev_keydown:
+                       if(ev->data1 == key_invleft)
+                       {
+                               inventoryTics = 5*35;
+                               if(!inventory)
+                               {
+                                       inventory = true;
+                                       break;
+                               }
+                               inv_ptr--;
+                               if(inv_ptr < 0)
+                               {
+                                       inv_ptr = 0;
+                               }
+                               else
+                               {
+                                       curpos--;
+                                       if(curpos < 0)
+                                       {
+                                               curpos = 0;
+                                       }
+                               }
+                               return(true);
+                       }
+                       if(ev->data1 == key_invright)
+                       {
+                               inventoryTics = 5*35;
+                               if(!inventory)
+                               {
+                                       inventory = true;
+                                       break;
+                               }
+                               inv_ptr++;
+                               if(inv_ptr >= plr->inventorySlotNum)
+                               {
+                                       inv_ptr--;
+                                       if(inv_ptr < 0)
+                                               inv_ptr = 0;
+                               }
+                               else
+                               {
+                                       curpos++;
+                                       if(curpos > 6)
+                                       {
+                                               curpos = 6;
+                                       }
+                               }
+                               return(true);
+                       }
+                       if(ev->data1 == KEY_PAUSE && !MenuActive)
+                       {
+                               sendpause = true;
+                               return(true);
+                       }
+                       if(ev->data1 < NUMKEYS)
+                       {
+                               gamekeydown[ev->data1] = true;
+                       }
+                       return(true); // eat key down events
+
+               case ev_keyup:
+                       if(ev->data1 < NUMKEYS)
+                       {
+                               gamekeydown[ev->data1] = false;
+                       }
+                       return(false); // always let key up events filter down
+
+               case ev_mouse:
+                       mousebuttons[0] = ev->data1&1;
+                       mousebuttons[1] = ev->data1&2;
+                       mousebuttons[2] = ev->data1&4;
+                       mousex = ev->data2*(mouseSensitivity+5)/10;
+                       mousey = ev->data3*(mouseSensitivity+5)/10;
+                       return(true); // eat events
+
+               case ev_joystick:
+                       joybuttons[0] = ev->data1&1;
+                       joybuttons[1] = ev->data1&2;
+                       joybuttons[2] = ev->data1&4;
+                       joybuttons[3] = ev->data1&8;
+                       joyxmove = ev->data2;
+                       joyymove = ev->data3;
+                       return(true); // eat events
+
+               default:
+                       break;
+       }
+       return(false);
+}
+
+/*
+===============================================================================
+=
+= G_Ticker
+=
+===============================================================================
+*/
+
+void G_Ticker (void)
+{
+       int                     i, buf;
+       ticcmd_t        *cmd = 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
+//
+//
+// do main actions
+//
+       switch (gamestate)
+       {
+               case GS_LEVEL:
+                       P_Ticker ();
+                       SB_Ticker ();
+                       AM_Ticker ();
+                       CT_Ticker();
+                       break;
+               case GS_INTERMISSION:
+                       IN_Ticker ();
+                       break;
+               case GS_FINALE:
+                       F_Ticker();
+                       break;
+               case GS_DEMOSCREEN:
+                       D_PageTicker ();
+                       break;
+       }
+}
+
+
+/*
+==============================================================================
+
+                                               PLAYER STRUCTURE FUNCTIONS
+
+also see P_SpawnPlayer in P_Things
+==============================================================================
+*/
+
+/*
+====================
+=
+= G_InitPlayer
+=
+= Called at the start
+= Called by the game initialization functions
+====================
+*/
+
+void G_InitPlayer (int player)
+{
+       player_t        *p;
+
+// set up the saved info
+       p = &players[player];
+
+// clear everything else to defaults
+       G_PlayerReborn (player);
+
+}
+
+
+/*
+====================
+=
+= G_PlayerFinishLevel
+=
+= Can when a player completes a level
+====================
+*/
+extern int curpos;
+extern int inv_ptr;
+extern int playerkeys;
+
+void G_PlayerFinishLevel(int player)
+{
+       player_t *p;
+       int i;
+
+/*      // BIG HACK
+       inv_ptr = 0;
+       curpos = 0;
+*/
+       // END HACK
+       p = &players[player];
+       for(i=0; i<p->inventorySlotNum; i++)
+       {
+               p->inventory[i].count = 1;
+       }
+       p->artifactCount = p->inventorySlotNum;
+
+       if(!deathmatch)
+       {
+               for(i = 0; i < 16; i++)
+               {
+                       P_PlayerUseArtifact(p, arti_fly);
+               }
+       }
+       memset(p->powers, 0, sizeof(p->powers));
+       memset(p->keys, 0, sizeof(p->keys));
+       playerkeys = 0;
+//      memset(p->inventory, 0, sizeof(p->inventory));
+       if(p->chickenTics)
+       {
+               p->readyweapon = p->mo->special1; // Restore weapon
+               p->chickenTics = 0;
+       }
+       p->messageTics = 0;
+       p->lookdir = 0;
+       p->mo->flags &= ~MF_SHADOW; // Remove invisibility
+       p->extralight = 0; // Remove weapon flashes
+       p->fixedcolormap = 0; // Remove torch
+       p->damagecount = 0; // No palette changes
+       p->bonuscount = 0;
+       p->rain1 = NULL;
+       p->rain2 = NULL;
+       if(p == &players[consoleplayer])
+       {
+               SB_state = -1; // refresh the status bar
+       }
+}
+
+/*
+====================
+=
+= G_PlayerReborn
+=
+= Called after a player dies
+= almost everything is cleared and initialized
+====================
+*/
+
+void G_PlayerReborn(int player)
+{
+       player_t *p;
+       int i;
+       int frags[MAXPLAYERS];
+       int killcount, itemcount, secretcount;
+       boolean secret;
+
+       secret = false;
+       memcpy(frags, players[player].frags, sizeof(frags));
+       killcount = players[player].killcount;
+       itemcount = players[player].itemcount;
+       secretcount = players[player].secretcount;
+
+       p = &players[player];
+       if(p->didsecret)
+       {
+               secret = true;
+       }
+       memset(p, 0, sizeof(*p));
+
+       memcpy(players[player].frags, frags, sizeof(players[player].frags));
+       players[player].killcount = killcount;
+       players[player].itemcount = itemcount;
+       players[player].secretcount = secretcount;
+
+       p->usedown = p->attackdown = true; // don't do anything immediately
+       p->playerstate = PST_LIVE;
+       p->health = MAXHEALTH;
+       p->readyweapon = p->pendingweapon = wp_goldwand;
+       p->weaponowned[wp_staff] = true;
+       p->weaponowned[wp_goldwand] = true;
+       p->messageTics = 0;
+       p->lookdir = 0;
+       p->ammo[am_goldwand] = 50;
+       for(i = 0; i < NUMAMMO; i++)
+       {
+               p->maxammo[i] = maxammo[i];
+       }
+       if(gamemap == 9 || secret)
+       {
+               p->didsecret = true;
+       }
+       if(p == &players[consoleplayer])
+       {
+               SB_state = -1; // refresh the status bar
+               inv_ptr = 0; // reset the inventory pointer
+               curpos = 0;
+       }
+}
+
+/*
+====================
+=
+= G_CheckSpot
+=
+= Returns false if the player cannot be respawned at the given mapthing_t spot
+= because something is occupying it
+====================
+*/
+
+void P_SpawnPlayer (mapthing_t *mthing);
+
+boolean G_CheckSpot (int playernum, mapthing_t *mthing)
+{
+       fixed_t         x,y;
+       subsector_t *ss;
+       unsigned        an;
+       mobj_t      *mo;
+
+       x = mthing->x << FRACBITS;
+       y = mthing->y << FRACBITS;
+
+       players[playernum].mo->flags2 &= ~MF2_PASSMOBJ;
+       if (!P_CheckPosition (players[playernum].mo, x, y) )
+       {
+               players[playernum].mo->flags2 |= MF2_PASSMOBJ;
+               return false;
+       }
+       players[playernum].mo->flags2 |= MF2_PASSMOBJ;
+
+// spawn a teleport fog
+       ss = R_PointInSubsector (x,y);
+       an = ( ANG45 * (mthing->angle/45) ) >> ANGLETOFINESHIFT;
+
+       mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an]
+       , ss->sector->floorheight+TELEFOGHEIGHT
+, MT_TFOG);
+
+       if (players[consoleplayer].viewz != 1)
+               S_StartSound (mo, sfx_telept);  // don't start sound on first frame
+
+       return true;
+}
+
+/*
+====================
+=
+= G_DeathMatchSpawnPlayer
+=
+= Spawns a player at one of the random death match spots
+= called at level load and each death
+====================
+*/
+
+void G_DeathMatchSpawnPlayer (int playernum)
+{
+       int             i,j;
+       int             selections;
+
+       selections = deathmatch_p - deathmatchstarts;
+       if (selections < 4)
+               I_Error ("Only %i deathmatch spots, 4 required", selections);
+
+       for (j=0 ; j<20 ; j++)
+       {
+               i = P_Random() % selections;
+               if (G_CheckSpot (playernum, &deathmatchstarts[i]) )
+               {
+                       deathmatchstarts[i].type = playernum+1;
+                       P_SpawnPlayer (&deathmatchstarts[i]);
+                       return;
+               }
+       }
+
+// no good spot, so the player will probably get stuck
+       P_SpawnPlayer (&playerstarts[playernum]);
+}
+
+/*
+====================
+=
+= G_DoReborn
+=
+====================
+*/
+
+void G_DoReborn (int playernum)
+{
+       int                             i;
+
+       if (G_CheckDemoStatus ())
+               return;
+       if (!netgame)
+               gameaction = ga_loadlevel;                      // reload the level from scratch
+       else
+       {       // respawn at the start
+               players[playernum].mo->player = NULL;   // dissasociate the corpse
+
+               // spawn at random spot if in death match
+               if (deathmatch)
+               {
+                       G_DeathMatchSpawnPlayer (playernum);
+                       return;
+               }
+
+               if (G_CheckSpot (playernum, &playerstarts[playernum]) )
+               {
+                       P_SpawnPlayer (&playerstarts[playernum]);
+                       return;
+               }
+               // try to spawn at one of the other players spots
+               for (i=0 ; i<MAXPLAYERS ; i++)
+                       if (G_CheckSpot (playernum, &playerstarts[i]) )
+                       {
+                               playerstarts[i].type = playernum+1;             // fake as other player
+                               P_SpawnPlayer (&playerstarts[i]);
+                               playerstarts[i].type = i+1;                             // restore
+                               return;
+                       }
+               // he's going to be inside something.  Too bad.
+               P_SpawnPlayer (&playerstarts[playernum]);
+       }
+}
+
+
+void G_ScreenShot (void)
+{
+       gameaction = ga_screenshot;
+}
+
+
+/*
+====================
+=
+= G_DoCompleted
+=
+====================
+*/
+
+boolean         secretexit;
+
+void G_ExitLevel (void)
+{
+       secretexit = false;
+       gameaction = ga_completed;
+}
+
+void G_SecretExitLevel (void)
+{
+       secretexit = true;
+       gameaction = ga_completed;
+}
+
+void G_DoCompleted(void)
+{
+       int i;
+       static int afterSecret[5] = { 7, 5, 5, 5, 4 };
+
+       gameaction = ga_nothing;
+       if(G_CheckDemoStatus())
+       {
+               return;
+       }
+       for(i = 0; i < MAXPLAYERS; i++)
+       {
+               if(playeringame[i])
+               {
+                       G_PlayerFinishLevel(i);
+               }
+       }
+       prevmap = gamemap;
+       if(secretexit == true)
+       {
+               gamemap = 9;
+       }
+       else if(gamemap == 9)
+       { // Finished secret level
+               gamemap = afterSecret[gameepisode-1];
+       }
+       else if(gamemap == 8)
+       {
+               gameaction = ga_victory;
+               return;
+       }
+       else
+       {
+               gamemap++;
+       }
+       gamestate = GS_INTERMISSION;
+       IN_Start();
+}
+
+//============================================================================
+//
+// G_WorldDone
+//
+//============================================================================
+
+void G_WorldDone(void)
+{
+       gameaction = ga_worlddone;
+}
+
+//============================================================================
+//
+// G_DoWorldDone
+//
+//============================================================================
+
+void G_DoWorldDone(void)
+{
+       gamestate = GS_LEVEL;
+       G_DoLoadLevel();
+       gameaction = ga_nothing;
+       viewactive = true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC G_LoadGame
+//
+// Can be called by the startup code or the menu task.
+//
+//---------------------------------------------------------------------------
+
+char savename[256];
+
+void G_LoadGame(char *name)
+{
+       strcpy(savename, name);
+       gameaction = ga_loadgame;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC G_DoLoadGame
+//
+// Called by G_Ticker based on gameaction.
+//
+//---------------------------------------------------------------------------
+
+#define VERSIONSIZE 16
+
+void G_DoLoadGame(void)
+{
+       int length;
+       int i;
+       int a, b, c;
+       char vcheck[VERSIONSIZE];
+
+       gameaction = ga_nothing;
+
+       length = M_ReadFile(savename, &savebuffer);
+       save_p = savebuffer+SAVESTRINGSIZE;
+       // Skip the description field
+       memset(vcheck, 0, sizeof(vcheck));
+       sprintf(vcheck, "version %i", VERSION);
+       if (strcmp (save_p, vcheck))
+       { // Bad version
+               return;
+       }
+       save_p += VERSIONSIZE;
+       gameskill = *save_p++;
+       gameepisode = *save_p++;
+       gamemap = *save_p++;
+       for(i = 0; i < MAXPLAYERS; i++)
+       {
+               playeringame[i] = *save_p++;
+       }
+       // Load a base level
+       G_InitNew(gameskill, gameepisode, gamemap);
+
+       // Create leveltime
+       a = *save_p++;
+       b = *save_p++;
+       c = *save_p++;
+       leveltime = (a<<16)+(b<<8)+c;
+
+       // De-archive all the modifications
+       P_UnArchivePlayers();
+       P_UnArchiveWorld();
+       P_UnArchiveThinkers();
+       P_UnArchiveSpecials();
+
+       if(*save_p != SAVE_GAME_TERMINATOR)
+       { // Missing savegame termination marker
+               I_Error("Bad savegame");
+       }
+       Z_Free(savebuffer);
+}
+
+
+/*
+====================
+=
+= G_InitNew
+=
+= Can be called by the startup code or the menu task
+= consoleplayer, displayplayer, playeringame[] should be set
+====================
+*/
+
+skill_t d_skill;
+int     d_episode;
+int     d_map;
+
+void G_DeferedInitNew (skill_t skill, int episode, int map)
+{
+       d_skill = skill;
+       d_episode = episode;
+       d_map = map;
+       gameaction = ga_newgame;
+}
+
+void G_DoNewGame (void)
+{
+       G_InitNew (d_skill, d_episode, d_map);
+       gameaction = ga_nothing;
+}
+
+extern  int                     skytexture;
+
+void G_InitNew(skill_t skill, int episode, int map)
+{
+       int i;
+       int speed;
+       static char *skyLumpNames[5] =
+       {
+               "SKY1", "SKY2", "SKY3", "SKY1", "SKY3"
+       };
+
+       if(paused)
+       {
+               paused = false;
+               S_ResumeSound();
+       }
+       if(skill < sk_baby)
+               skill = sk_baby;
+       if(skill > sk_nightmare)
+               skill = sk_nightmare;
+       if(episode < 1)
+               episode = 1;
+       // Up to 9 episodes for testing
+       if(episode > 9)
+               episode = 9;
+       if(map < 1)
+               map = 1;
+       if(map > 9)
+               map = 9;
+       M_ClearRandom();
+       if(respawnparm)
+       {
+               respawnmonsters = true;
+       }
+       else
+       {
+               respawnmonsters = false;
+       }
+       // Set monster missile speeds
+       speed = skill == sk_nightmare;
+       for(i = 0; MonsterMissileInfo[i].type != -1; i++)
+       {
+               mobjinfo[MonsterMissileInfo[i].type].speed
+                       = MonsterMissileInfo[i].speed[speed]<<FRACBITS;
+       }
+       // Force players to be initialized upon first level load
+       for(i = 0; i < MAXPLAYERS; i++)
+       {
+               players[i].playerstate = PST_REBORN;
+               players[i].didsecret = false;
+       }
+       // Set up a bunch of globals
+       usergame = true; // will be set false if a demo
+       paused = false;
+       demorecording = false;
+       demoplayback = false;
+       viewactive = true;
+       gameepisode = episode;
+       gamemap = map;
+       gameskill = skill;
+       viewactive = true;
+       BorderNeedRefresh = true;
+
+       // Set the sky map
+       if(episode > 5)
+       {
+               skytexture = R_TextureNumForName("SKY1");
+       }
+       else
+       {
+               skytexture = R_TextureNumForName(skyLumpNames[episode-1]);
+       }
+
+//
+// give one null ticcmd_t
+//
+#if 0
+       gametic = 0;
+       maketic = 1;
+       for (i=0 ; i<MAXPLAYERS ; i++)
+               nettics[i] = 1;                 // one null event for this gametic
+       memset (localcmds,0,sizeof(localcmds));
+       memset (netcmds,0,sizeof(netcmds));
+#endif
+       G_DoLoadLevel();
+}
+
+
+/*
+===============================================================================
+
+                                                       DEMO RECORDING
+
+===============================================================================
+*/
+
+#define DEMOMARKER      0x80
+
+void G_ReadDemoTiccmd (ticcmd_t *cmd)
+{
+       if (*demo_p == DEMOMARKER)
+       {       // end of demo data stream
+               G_CheckDemoStatus ();
+               return;
+       }
+       cmd->forwardmove = ((signed char)*demo_p++);
+       cmd->sidemove = ((signed char)*demo_p++);
+       cmd->angleturn = ((unsigned char)*demo_p++)<<8;
+       cmd->buttons = (unsigned char)*demo_p++;
+       cmd->lookfly = (unsigned char)*demo_p++;
+       cmd->arti = (unsigned char)*demo_p++;
+}
+
+void G_WriteDemoTiccmd (ticcmd_t *cmd)
+{
+       if (gamekeydown['q'])           // press q to end demo recording
+               G_CheckDemoStatus ();
+       *demo_p++ = cmd->forwardmove;
+       *demo_p++ = cmd->sidemove;
+       *demo_p++ = cmd->angleturn>>8;
+       *demo_p++ = cmd->buttons;
+       *demo_p++ = cmd->lookfly;
+       *demo_p++ = cmd->arti;
+       demo_p -= 6;
+       G_ReadDemoTiccmd (cmd);         // make SURE it is exactly the same
+}
+
+
+
+/*
+===================
+=
+= G_RecordDemo
+=
+===================
+*/
+
+void G_RecordDemo (skill_t skill, int numplayers, int episode, int map, char *name)
+{
+       int             i;
+
+       G_InitNew (skill, episode, map);
+       usergame = false;
+       strcpy (demoname, name);
+       strcat (demoname, ".lmp");
+       demobuffer = demo_p = Z_Malloc (0x20000,PU_STATIC,NULL);
+       *demo_p++ = skill;
+       *demo_p++ = episode;
+       *demo_p++ = map;
+
+       for (i=0 ; i<MAXPLAYERS ; i++)
+               *demo_p++ = playeringame[i];
+
+       demorecording = true;
+}
+
+
+/*
+===================
+=
+= G_PlayDemo
+=
+===================
+*/
+
+char    *defdemoname;
+
+void G_DeferedPlayDemo (char *name)
+{
+       defdemoname = name;
+       gameaction = ga_playdemo;
+}
+
+void G_DoPlayDemo (void)
+{
+       skill_t skill;
+       int             i, episode, map;
+
+       gameaction = ga_nothing;
+       demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC);
+       skill = *demo_p++;
+       episode = *demo_p++;
+       map = *demo_p++;
+
+       for (i=0 ; i<MAXPLAYERS ; i++)
+               playeringame[i] = *demo_p++;
+
+       precache = false;               // don't spend a lot of time in loadlevel
+       G_InitNew (skill, episode, map);
+       precache = true;
+       usergame = false;
+       demoplayback = true;
+}
+
+
+/*
+===================
+=
+= G_TimeDemo
+=
+===================
+*/
+
+void G_TimeDemo (char *name)
+{
+       skill_t skill;
+       int             episode, map;
+
+       demobuffer = demo_p = W_CacheLumpName (name, PU_STATIC);
+       skill = *demo_p++;
+       episode = *demo_p++;
+       map = *demo_p++;
+       G_InitNew (skill, episode, map);
+       usergame = false;
+       demoplayback = true;
+       timingdemo = true;
+       singletics = true;
+}
+
+
+/*
+===================
+=
+= G_CheckDemoStatus
+=
+= Called after a death or level completion to allow demos to be cleaned up
+= Returns true if a new demo loop action will take place
+===================
+*/
+
+boolean G_CheckDemoStatus (void)
+{
+       int             endtime;
+
+       if (timingdemo)
+       {
+               endtime = I_GetTime ();
+               I_Error ("timed %i gametics in %i realtics",gametic
+               , endtime-starttime);
+       }
+
+       if (demoplayback)
+       {
+               if (singledemo)
+                       I_Quit ();
+
+               Z_ChangeTag (demobuffer, PU_CACHE);
+               demoplayback = false;
+               D_AdvanceDemo ();
+               return true;
+       }
+
+       if (demorecording)
+       {
+               *demo_p++ = DEMOMARKER;
+               M_WriteFile (demoname, demobuffer, demo_p - demobuffer);
+               Z_Free (demobuffer);
+               demorecording = false;
+               I_Error ("Demo %s recorded",demoname);
+       }
+
+       return false;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+
+//==========================================================================
+//
+// G_SaveGame
+//
+// Called by the menu task.  <description> is a 24 byte text string.
+//
+//==========================================================================
+
+void G_SaveGame(int slot, char *description)
+{
+       savegameslot = slot;
+       strcpy(savedescription, description);
+       sendsave = true;
+}
+
+//==========================================================================
+//
+// G_DoSaveGame
+//
+// Called by G_Ticker based on gameaction.
+//
+//==========================================================================
+
+void G_DoSaveGame(void)
+{
+       int i;
+       char name[100];
+       char verString[VERSIONSIZE];
+       char *description;
+
+       if(cdrom)
+       {
+               sprintf(name, SAVEGAMENAMECD"%d.hsg", savegameslot);
+       }
+       else
+       {
+               sprintf(name, SAVEGAMENAME"%d.hsg", savegameslot);
+       }
+       description = savedescription;
+
+       SV_Open(name);
+       SV_Write(description, SAVESTRINGSIZE);
+       memset(verString, 0, sizeof(verString));
+       sprintf(verString, "version %i", VERSION);
+       SV_Write(verString, VERSIONSIZE);
+       SV_WriteByte(gameskill);
+       SV_WriteByte(gameepisode);
+       SV_WriteByte(gamemap);
+       for(i = 0; i < MAXPLAYERS; i++)
+       {
+               SV_WriteByte(playeringame[i]);
+       }
+       SV_WriteByte(leveltime>>16);
+       SV_WriteByte(leveltime>>8);
+       SV_WriteByte(leveltime);
+       P_ArchivePlayers();
+       P_ArchiveWorld();
+       P_ArchiveThinkers();
+       P_ArchiveSpecials();
+       SV_Close(name);
+
+       gameaction = ga_nothing;
+       savedescription[0] = 0;
+       P_SetMessage(&players[consoleplayer], TXT_GAMESAVED, true);
+}
+
+//==========================================================================
+//
+// SV_Open
+//
+//==========================================================================
+
+void SV_Open(char *fileName)
+{
+       MallocFailureOk = true;
+       save_p = savebuffer = Z_Malloc(SAVEGAMESIZE, PU_STATIC, NULL);
+       MallocFailureOk = false;
+       if(savebuffer == NULL)
+       { // Not enough memory - use file save method
+               SaveGameType = SVG_FILE;
+               SaveGameFP = fopen(fileName, "wb");
+       }
+       else
+       {
+               SaveGameType = SVG_RAM;
+       }
+}
+
+//==========================================================================
+//
+// SV_Close
+//
+//==========================================================================
+
+void SV_Close(char *fileName)
+{
+       int length;
+
+       SV_WriteByte(SAVE_GAME_TERMINATOR);
+       if(SaveGameType == SVG_RAM)
+       {
+               length = save_p-savebuffer;
+               if(length > SAVEGAMESIZE)
+               {
+                       I_Error("Savegame buffer overrun");
+               }
+               M_WriteFile(fileName, savebuffer, length);
+               Z_Free(savebuffer);
+       }
+       else
+       { // SVG_FILE
+               fclose(SaveGameFP);
+       }
+}
+
+//==========================================================================
+//
+// SV_Write
+//
+//==========================================================================
+
+void SV_Write(void *buffer, int size)
+{
+       if(SaveGameType == SVG_RAM)
+       {
+               memcpy(save_p, buffer, size);
+               save_p += size;
+       }
+       else
+       { // SVG_FILE
+               fwrite(buffer, size, 1, SaveGameFP);
+       }
+}
+
+void SV_WriteByte(byte val)
+{
+       SV_Write(&val, sizeof(byte));
+}
+
+void SV_WriteWord(unsigned short val)
+{
+       SV_Write(&val, sizeof(unsigned short));
+}
+
+void SV_WriteLong(unsigned int val)
+{
+       SV_Write(&val, sizeof(int));
+}
diff --git a/base/i_cyber.c b/base/i_cyber.c
new file mode 100644 (file)
index 0000000..ebeb2cb
--- /dev/null
@@ -0,0 +1,259 @@
+// I_cyber.c
+
+#include <dos.h>
+#include <stdlib.h>
+
+
+/*
+====================================================
+
+Doom control structure
+
+The keybaord and joystick will add to the values set by the cyberman,
+to a maximum of 0x19000 for forwardmove and sidemove.  Angleturn is
+not bounded at all.
+
+parm                    normal          fast
+-----           ------          ----
+forwardmove             0xc800          0x19000
+sidemove                0xc000          0x14000
+angleturn               0x2800000       0x5000000
+
+The keyboard and joystick have a 1/3 second slow turn of 0x1400000 under
+normal speed to help aiming.
+
+
+
+====================================================
+*/
+
+typedef struct
+{
+       char            forwardmove;            // *2048 for move
+       char            sidemove;                       // *2048 for move
+       short           angleturn;                      // <<16 for angle delta
+       short           consistancy;            // checks for net game
+       unsigned char            chatchar;
+       unsigned char           buttons;
+} ticcmd_t;
+
+#define BT_ATTACK               1
+#define BT_USE                  2
+#define BT_CHANGE               4                       // if true, the next 3 bits hold weapon num
+#define BT_WEAPONMASK   (8+16+32)
+#define BT_WEAPONSHIFT  3
+
+
+
+//==================================================
+//
+// CyberMan detection and usage info
+//
+//==================================================
+#define DPMI_INT        0x31
+#define MOUSE_INT       0x33
+
+#define DOSMEMSIZE      64      // enough for any SWIFT structure
+
+typedef struct {
+   short        x;
+   short        y;
+   short        z;
+   short        pitch;
+   short        roll;
+   short        yaw;
+   short        buttons;
+} SWIFT_3DStatus;
+
+// DPMI real mode interrupt structure
+static struct rminfo {
+       long EDI;
+       long ESI;
+       long EBP;
+       long reserved_by_system;
+       long EBX;
+       long EDX;
+       long ECX;
+       long EAX;
+       short flags;
+       short ES,DS,FS,GS,IP,CS,SP,SS;
+} RMI;
+
+typedef struct {
+   unsigned char        deviceType;
+   unsigned char        majorVersion;
+   unsigned char        minorVersion;
+   unsigned char        absRelFlags;
+   unsigned char        centeringFlags;
+   unsigned char        reserved[5];
+} StaticDeviceData;
+
+// values for deviceType:
+#define DEVTYPE_CYBERMAN        1
+
+short                   selector;
+unsigned short  segment;                // segment of DOS memory block
+SWIFT_3DStatus  *cyberstat;
+int                             isCyberPresent;         // is CyberMan present?
+
+
+static  union REGS regs;
+static  struct SREGS sregs;
+
+
+extern  int mousepresent;
+
+//===========================================================
+//
+// I_StartupCyberMan
+//
+// If a cyberman is present, init it and set isCyberPresent to 1
+//===========================================================
+void I_StartupCyberMan(void)
+{
+   StaticDeviceData *pbuf;
+   int success = 0;
+
+   isCyberPresent = 0;
+
+   cyberstat = (SWIFT_3DStatus *)I_AllocLow (DOSMEMSIZE);
+   segment = (int)cyberstat>>4;
+
+   pbuf = (StaticDeviceData *)cyberstat;
+   memset(pbuf, 0, sizeof (StaticDeviceData));
+
+   // Use DPMI call 300h to issue mouse interrupt
+   memset(&RMI, 0, sizeof(RMI));
+   RMI.EAX = 0x53C1;            // SWIFT: Get Static Device Data
+   RMI.ES = segment;
+   RMI.EDX = 0;
+   memset(&sregs, 0, sizeof (sregs));
+   regs.w.ax = 0x0300;          // DPMI: simulate interrupt
+   regs.w.bx = MOUSE_INT;
+   regs.w.cx = 0;
+   regs.x.edi = FP_OFF(&RMI);
+   sregs.es = FP_SEG(&RMI);
+   int386x( DPMI_INT, &regs, &regs, &sregs );
+
+   if ((short)RMI.EAX != 1)
+   {
+         // SWIFT functions not present
+         tprintf("CyberMan: Wrong mouse driver - no SWIFT support (AX=%04x).\n",
+                        (unsigned)(short)RMI.EAX);
+   }
+   else
+   if (pbuf->deviceType != DEVTYPE_CYBERMAN)
+   {
+         // no SWIFT device, or not CyberMan
+         if (pbuf->deviceType == 0)
+         {
+                tprintf("CyberMan: no SWIFT device connected.\n");
+         }
+         else
+         {
+                tprintf("CyberMan: SWIFT device is not a CyberMan! (type=%d)\n",
+                               pbuf->deviceType);
+         }
+   }
+   else
+   {
+         tprintf("CyberMan: CyberMan %d.%02d connected.\n",
+                        pbuf->majorVersion, pbuf->minorVersion);
+         isCyberPresent = 1;
+         mousepresent = 0;
+   }
+}
+
+
+
+/*
+===============
+=
+= I_ReadCyberCmds
+=
+===============
+*/
+
+
+int             oldpos;
+
+void I_ReadCyberCmd (ticcmd_t *cmd)
+{
+       int             delta;
+
+       // Use DPMI call 300h to issue mouse interrupt
+       memset(&RMI, 0, sizeof(RMI));
+       RMI.EAX = 0x5301;            // SWIFT: Get Position and Buttons
+       RMI.ES = segment;
+       RMI.EDX = 0;
+       memset(&sregs, 0, sizeof (sregs));
+       regs.w.ax = 0x0300;          // DPMI: simulate interrupt
+       regs.w.bx = MOUSE_INT;
+       regs.w.cx = 0;
+       regs.x.edi = FP_OFF(&RMI);
+       sregs.es = FP_SEG(&RMI);
+       int386x( DPMI_INT, &regs, &regs, &sregs );
+
+       if (cyberstat->y < -7900)
+               cmd->forwardmove = 0xc800/2048;
+       else if (cyberstat->y > 7900)
+               cmd->forwardmove = -0xc800/2048;
+
+       if (cyberstat->buttons & 4)
+               cmd->buttons |= BT_ATTACK;
+       if (cyberstat->buttons & 2)
+               cmd->buttons |= BT_USE;
+
+       delta = cyberstat->x - oldpos;
+       oldpos = cyberstat->x;
+
+       if (cyberstat->buttons & 1)
+       {       // strafe
+               if (cyberstat->x < -7900)
+                       cmd->sidemove = -0xc800/2048;
+               else if (cyberstat->x > 7900)
+                       cmd->sidemove = 0xc800/2048;
+               else
+                       cmd->sidemove = delta*40/2048;
+       }
+       else
+       {
+               if (cyberstat->x < -7900)
+                       cmd->angleturn = 0x280;
+               else if (cyberstat->x > 7900)
+                       cmd->angleturn = -0x280;
+               else
+                       cmd->angleturn = -delta*0xa/16;
+
+       }
+
+}
+
+
+void I_Tactile (int on, int off, int total)
+{
+       if (!isCyberPresent)
+               return;
+
+       on /= 5;
+       off /= 5;
+       total /= 40;
+       if (on > 255)
+               on = 255;
+       if (off > 255)
+               off = 255;
+       if (total > 255)
+               total = 255;
+
+       memset(&RMI, 0, sizeof(RMI));
+       RMI.EAX = 0x5330;            // SWIFT: Get Position and Buttons
+       RMI.EBX = on*256+off;
+       RMI.ECX = total;
+       memset(&sregs, 0, sizeof (sregs));
+       regs.w.ax = 0x0300;          // DPMI: simulate interrupt
+       regs.w.bx = MOUSE_INT;
+       regs.w.cx = 0;
+       regs.x.edi = FP_OFF(&RMI);
+       sregs.es = FP_SEG(&RMI);
+       int386x( DPMI_INT, &regs, &regs, &sregs );
+}
diff --git a/base/i_ibm.c b/base/i_ibm.c
new file mode 100644 (file)
index 0000000..10981c1
--- /dev/null
@@ -0,0 +1,2204 @@
+
+// I_IBM.C
+
+#include <dos.h>
+#include <conio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <graph.h>
+#include "doomdef.h"
+#include "r_local.h"
+#include "sounds.h"
+#include "i_sound.h"
+#include "dmx.h"
+
+// Macros
+
+#define DPMI_INT 0x31
+//#define NOKBD
+//#define NOTIMER
+
+// Public Data
+
+int DisplayTicker = 0;
+
+// Code
+
+void main(int argc, char **argv)
+{
+       myargc = argc;
+       myargv = argv;
+       D_DoomMain();
+}
+
+void I_StartupNet (void);
+void I_ShutdownNet (void);
+void I_ReadExternDriver(void);
+
+typedef struct
+{
+       unsigned        edi, esi, ebp, reserved, ebx, edx, ecx, eax;
+       unsigned short  flags, es, ds, fs, gs, ip, cs, sp, ss;
+} dpmiregs_t;
+
+extern  dpmiregs_t      dpmiregs;
+
+void I_ReadMouse (void);
+void I_InitDiskFlash (void);
+
+extern  int     usemouse, usejoystick;
+
+extern void **lumpcache;
+
+/*
+===============================================================================
+
+               MUSIC & SFX API
+
+===============================================================================
+*/
+
+static channel_t channel[MAX_CHANNELS];
+
+static int rs; //the current registered song.
+int mus_song = -1;
+int mus_lumpnum;
+void *mus_sndptr;
+byte *soundCurve;
+
+extern sfxinfo_t S_sfx[];
+extern musicinfo_t S_music[];
+
+extern int snd_DesiredMusicDevice;
+extern int snd_DesiredSfxDevice;
+extern int snd_MaxVolume;
+extern int snd_MusicVolume;
+extern int snd_Channels;
+
+extern int startepisode;
+extern int startmap;
+
+int AmbChan;
+
+void S_Start(void)
+{
+       int i;
+
+       S_StartSong((gameepisode-1)*9 + gamemap-1, true);
+
+       //stop all sounds
+       for(i=0; i < snd_Channels; i++)
+       {
+               if(channel[i].handle)
+               {
+                       S_StopSound(channel[i].mo);
+               }
+       }
+       memset(channel, 0, 8*sizeof(channel_t));
+}
+
+void S_StartSong(int song, boolean loop)
+{
+       if(song == mus_song)
+       { // don't replay an old song
+               return;
+       }
+       if(rs)
+       {
+               I_StopSong(rs);
+               I_UnRegisterSong(rs);
+               Z_ChangeTag(lumpcache[mus_lumpnum], PU_CACHE);
+               #ifdef __WATCOMC__
+                       _dpmi_unlockregion(mus_sndptr, lumpinfo[mus_lumpnum].size);
+               #endif
+       }
+       if(song < mus_e1m1 || song > NUMMUSIC)
+       {
+               return;
+       }
+       mus_lumpnum = W_GetNumForName(S_music[song].name);
+       mus_sndptr = W_CacheLumpNum(mus_lumpnum, PU_MUSIC);
+       #ifdef __WATCOMC__
+               _dpmi_lockregion(mus_sndptr, lumpinfo[mus_lumpnum].size);
+       #endif
+       rs = I_RegisterSong(mus_sndptr);
+       I_PlaySong(rs, loop); //'true' denotes endless looping.
+       mus_song = song;
+}
+
+void S_StartSound(mobj_t *origin, int sound_id)
+{
+       int dist, vol;
+       int i;
+       int sound;
+       int priority;
+       int sep;
+       int angle;
+       int absx;
+       int absy;
+
+       static int sndcount = 0;
+       int chan;
+
+       if(sound_id==0 || snd_MaxVolume == 0)
+               return;
+       if(origin == NULL)
+       {
+               origin = players[consoleplayer].mo;
+       }
+
+// calculate the distance before other stuff so that we can throw out
+// sounds that are beyond the hearing range.
+       absx = abs(origin->x-players[consoleplayer].mo->x);
+       absy = abs(origin->y-players[consoleplayer].mo->y);
+       dist = absx+absy-(absx > absy ? absy>>1 : absx>>1);
+       dist >>= FRACBITS;
+//  dist = P_AproxDistance(origin->x-viewx, origin->y-viewy)>>FRACBITS;
+
+       if(dist >= MAX_SND_DIST)
+       {
+//      dist = MAX_SND_DIST - 1;
+         return; //sound is beyond the hearing range...
+       }
+       if(dist < 0)
+       {
+               dist = 0;
+       }
+       priority = S_sfx[sound_id].priority;
+       priority *= (10 - (dist/160));
+       if(!S_StopSoundID(sound_id, priority))
+       {
+               return; // other sounds have greater priority
+       }
+       for(i=0; i<snd_Channels; i++)
+       {
+               if(origin->player)
+               {
+                       i = snd_Channels;
+                       break; // let the player have more than one sound.
+               }
+               if(origin == channel[i].mo)
+               { // only allow other mobjs one sound
+                       S_StopSound(channel[i].mo);
+                       break;
+               }
+       }
+       if(i >= snd_Channels)
+       {
+               if(sound_id >= sfx_wind)
+               {
+                       if(AmbChan != -1 && S_sfx[sound_id].priority <=
+                               S_sfx[channel[AmbChan].sound_id].priority)
+                       {
+                               return; //ambient channel already in use
+                       }
+                       else
+                       {
+                               AmbChan = -1;
+                       }
+               }
+               for(i=0; i<snd_Channels; i++)
+               {
+                       if(channel[i].mo == NULL)
+                       {
+                               break;
+                       }
+               }
+               if(i >= snd_Channels)
+               {
+                       //look for a lower priority sound to replace.
+                       sndcount++;
+                       if(sndcount >= snd_Channels)
+                       {
+                               sndcount = 0;
+                       }
+                       for(chan=0; chan < snd_Channels; chan++)
+                       {
+                               i = (sndcount+chan)%snd_Channels;
+                               if(priority >= channel[i].priority)
+                               {
+                                       chan = -1; //denote that sound should be replaced.
+                                       break;
+                               }
+                       }
+                       if(chan != -1)
+                       {
+                               return; //no free channels.
+                       }
+                       else //replace the lower priority sound.
+                       {
+                               if(channel[i].handle)
+                               {
+                                       if(I_SoundIsPlaying(channel[i].handle))
+                                       {
+                                               I_StopSound(channel[i].handle);
+                                       }
+                                       if(S_sfx[channel[i].sound_id].usefulness > 0)
+                                       {
+                                               S_sfx[channel[i].sound_id].usefulness--;
+                                       }
+
+                                       if(AmbChan == i)
+                                       {
+                                               AmbChan = -1;
+                                       }
+                               }
+                       }
+               }
+       }
+       if(S_sfx[sound_id].lumpnum == 0)
+       {
+               S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]);
+       }
+       if(S_sfx[sound_id].snd_ptr == NULL)
+       {
+               S_sfx[sound_id].snd_ptr = W_CacheLumpNum(S_sfx[sound_id].lumpnum,
+                       PU_SOUND);
+               #ifdef __WATCOMC__
+               _dpmi_lockregion(S_sfx[sound_id].snd_ptr,
+                       lumpinfo[S_sfx[sound_id].lumpnum].size);
+               #endif
+       }
+
+       // calculate the volume based upon the distance from the sound origin.
+//      vol = (snd_MaxVolume*16 + dist*(-snd_MaxVolume*16)/MAX_SND_DIST)>>9;
+       vol = soundCurve[dist];
+
+       if(origin == players[consoleplayer].mo)
+       {
+               sep = 128;
+       }
+       else
+       {
+               angle = R_PointToAngle2(players[consoleplayer].mo->x,
+                       players[consoleplayer].mo->y, channel[i].mo->x, channel[i].mo->y);
+               angle = (angle-viewangle)>>24;
+               sep = angle*2-128;
+               if(sep < 64)
+                       sep = -sep;
+               if(sep > 192)
+                       sep = 512-sep;
+       }
+
+       channel[i].pitch = (byte)(127+(M_Random()&7)-(M_Random()&7));
+       channel[i].handle = I_StartSound(sound_id, S_sfx[sound_id].snd_ptr, vol, sep, channel[i].pitch, 0);
+       channel[i].mo = origin;
+       channel[i].sound_id = sound_id;
+       channel[i].priority = priority;
+       if(sound_id >= sfx_wind)
+       {
+               AmbChan = i;
+       }
+       if(S_sfx[sound_id].usefulness == -1)
+       {
+               S_sfx[sound_id].usefulness = 1;
+       }
+       else
+       {
+               S_sfx[sound_id].usefulness++;
+       }
+}
+
+void S_StartSoundAtVolume(mobj_t *origin, int sound_id, int volume)
+{
+       int dist;
+       int i;
+       int sep;
+
+       static int sndcount;
+       int chan;
+
+       if(sound_id == 0 || snd_MaxVolume == 0)
+               return;
+       if(origin == NULL)
+       {
+               origin = players[consoleplayer].mo;
+       }
+
+       if(volume == 0)
+       {
+               return;
+       }
+       volume = (volume*(snd_MaxVolume+1)*8)>>7;
+
+// no priority checking, as ambient sounds would be the LOWEST.
+       for(i=0; i<snd_Channels; i++)
+       {
+               if(channel[i].mo == NULL)
+               {
+                       break;
+               }
+       }
+       if(i >= snd_Channels)
+       {
+               return;
+       }
+       if(S_sfx[sound_id].lumpnum == 0)
+       {
+               S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]);
+       }
+       if(S_sfx[sound_id].snd_ptr == NULL)
+       {
+               S_sfx[sound_id].snd_ptr = W_CacheLumpNum(S_sfx[sound_id].lumpnum,
+                       PU_SOUND);
+               #ifdef __WATCOMC__
+               _dpmi_lockregion(S_sfx[sound_id].snd_ptr,
+                       lumpinfo[S_sfx[sound_id].lumpnum].size);
+               #endif
+       }
+       channel[i].pitch = (byte)(127-(M_Random()&3)+(M_Random()&3));
+       channel[i].handle = I_StartSound(sound_id, S_sfx[sound_id].snd_ptr, volume, 128, channel[i].pitch, 0);
+       channel[i].mo = origin;
+       channel[i].sound_id = sound_id;
+       channel[i].priority = 1; //super low priority.
+       if(S_sfx[sound_id].usefulness == -1)
+       {
+               S_sfx[sound_id].usefulness = 1;
+       }
+       else
+       {
+               S_sfx[sound_id].usefulness++;
+       }
+}
+
+boolean S_StopSoundID(int sound_id, int priority)
+{
+       int i;
+       int lp; //least priority
+       int found;
+
+       if(S_sfx[sound_id].numchannels == -1)
+       {
+               return(true);
+       }
+       lp = -1; //denote the argument sound_id
+       found = 0;
+       for(i=0; i<snd_Channels; i++)
+       {
+               if(channel[i].sound_id == sound_id && channel[i].mo)
+               {
+                       found++; //found one.  Now, should we replace it??
+                       if(priority >= channel[i].priority)
+                       { // if we're gonna kill one, then this'll be it
+                               lp = i;
+                               priority = channel[i].priority;
+                       }
+               }
+       }
+       if(found < S_sfx[sound_id].numchannels)
+       {
+               return(true);
+       }
+       else if(lp == -1)
+       {
+               return(false); // don't replace any sounds
+       }
+       if(channel[lp].handle)
+       {
+               if(I_SoundIsPlaying(channel[lp].handle))
+               {
+                       I_StopSound(channel[lp].handle);
+               }
+               if(S_sfx[channel[i].sound_id].usefulness > 0)
+               {
+                       S_sfx[channel[i].sound_id].usefulness--;
+               }
+               channel[lp].mo = NULL;
+       }
+       return(true);
+}
+
+void S_StopSound(mobj_t *origin)
+{
+       int i;
+
+       for(i=0;i<snd_Channels;i++)
+       {
+               if(channel[i].mo == origin)
+               {
+                       I_StopSound(channel[i].handle);
+                       if(S_sfx[channel[i].sound_id].usefulness > 0)
+                       {
+                               S_sfx[channel[i].sound_id].usefulness--;
+                       }
+                       channel[i].handle = 0;
+                       channel[i].mo = NULL;
+                       if(AmbChan == i)
+                       {
+                               AmbChan = -1;
+                       }
+               }
+       }
+}
+
+void S_SoundLink(mobj_t *oldactor, mobj_t *newactor)
+{
+       int i;
+
+       for(i=0;i<snd_Channels;i++)
+       {
+               if(channel[i].mo == oldactor)
+                       channel[i].mo = newactor;
+       }
+}
+
+void S_PauseSound(void)
+{
+       I_PauseSong(rs);
+}
+
+void S_ResumeSound(void)
+{
+       I_ResumeSong(rs);
+}
+
+static int nextcleanup;
+
+void S_UpdateSounds(mobj_t *listener)
+{
+       int i, dist, vol;
+       int angle;
+       int sep;
+       int priority;
+       int absx;
+       int absy;
+
+       listener = players[consoleplayer].mo;
+       if(snd_MaxVolume == 0)
+       {
+               return;
+       }
+       if(nextcleanup < gametic)
+       {
+               for(i=0; i < NUMSFX; i++)
+               {
+                       if(S_sfx[i].usefulness == 0 && S_sfx[i].snd_ptr)
+                       {
+                               if(lumpcache[S_sfx[i].lumpnum])
+                               {
+                                       if(((memblock_t *)((byte *)(lumpcache[S_sfx[i].lumpnum])-
+                                               sizeof(memblock_t)))->id == 0x1d4a11)
+                                       { // taken directly from the Z_ChangeTag macro
+                                               Z_ChangeTag2(lumpcache[S_sfx[i].lumpnum], PU_CACHE);
+                                               #ifdef __WATCOMC__
+                                                       _dpmi_unlockregion(S_sfx[i].snd_ptr, lumpinfo[S_sfx[i].lumpnum].size);
+                                               #endif
+                                       }
+                               }
+                               S_sfx[i].usefulness = -1;
+                               S_sfx[i].snd_ptr = NULL;
+                       }
+               }
+               nextcleanup = gametic+35; //CLEANUP DEBUG cleans every second
+       }
+       for(i=0;i<snd_Channels;i++)
+       {
+               if(!channel[i].handle || S_sfx[channel[i].sound_id].usefulness == -1)
+               {
+                       continue;
+               }
+               if(!I_SoundIsPlaying(channel[i].handle))
+               {
+                       if(S_sfx[channel[i].sound_id].usefulness > 0)
+                       {
+                               S_sfx[channel[i].sound_id].usefulness--;
+                       }
+                       channel[i].handle = 0;
+                       channel[i].mo = NULL;
+                       channel[i].sound_id = 0;
+                       if(AmbChan == i)
+                       {
+                               AmbChan = -1;
+                       }
+               }
+               if(channel[i].mo == NULL || channel[i].sound_id == 0
+                       || channel[i].mo == players[consoleplayer].mo)
+               {
+                       continue;
+               }
+               else
+               {
+                       absx = abs(channel[i].mo->x-players[consoleplayer].mo->x);
+                       absy = abs(channel[i].mo->y-players[consoleplayer].mo->y);
+                       dist = absx+absy-(absx > absy ? absy>>1 : absx>>1);
+                       dist >>= FRACBITS;
+//          dist = P_AproxDistance(channel[i].mo->x-listener->x, channel[i].mo->y-listener->y)>>FRACBITS;
+
+                       if(dist >= MAX_SND_DIST)
+                       {
+                               S_StopSound(channel[i].mo);
+                               continue;
+                       }
+                       if(dist < 0)
+                               dist = 0;
+
+// calculate the volume based upon the distance from the sound origin.
+//          vol = (*((byte *)W_CacheLumpName("SNDCURVE", PU_CACHE)+dist)*(snd_MaxVolume*8))>>7;
+                       vol = soundCurve[dist];
+
+                       angle = R_PointToAngle2(players[consoleplayer].mo->x,
+                               players[consoleplayer].mo->y, channel[i].mo->x, channel[i].mo->y);
+                       angle = (angle-viewangle)>>24;
+                       sep = angle*2-128;
+                       if(sep < 64)
+                               sep = -sep;
+                       if(sep > 192)
+                               sep = 512-sep;
+                       I_UpdateSoundParams(channel[i].handle, vol, sep, channel[i].pitch);
+                       priority = S_sfx[channel[i].sound_id].priority;
+                       priority *= (10 - (dist>>8));
+                       channel[i].priority = priority;
+               }
+       }
+}
+
+void S_Init(void)
+{
+       soundCurve = Z_Malloc(MAX_SND_DIST, PU_STATIC, NULL);
+       I_StartupSound();
+       if(snd_Channels > 8)
+       {
+               snd_Channels = 8;
+       }
+       I_SetChannels(snd_Channels);
+       I_SetMusicVolume(snd_MusicVolume);
+       S_SetMaxVolume(true);
+}
+
+void S_GetChannelInfo(SoundInfo_t *s)
+{
+       int i;
+       ChanInfo_t *c;
+
+       s->channelCount = snd_Channels;
+       s->musicVolume = snd_MusicVolume;
+       s->soundVolume = snd_MaxVolume;
+       for(i = 0; i < snd_Channels; i++)
+       {
+               c = &s->chan[i];
+               c->id = channel[i].sound_id;
+               c->priority = channel[i].priority;
+               c->name = S_sfx[c->id].name;
+               c->mo = channel[i].mo;
+               c->distance = P_AproxDistance(c->mo->x-viewx, c->mo->y-viewy)
+                       >>FRACBITS;
+       }
+}
+
+void S_SetMaxVolume(boolean fullprocess)
+{
+       int i;
+
+       if(!fullprocess)
+       {
+               soundCurve[0] = (*((byte *)W_CacheLumpName("SNDCURVE", PU_CACHE))*(snd_MaxVolume*8))>>7;
+       }
+       else
+       {
+               for(i = 0; i < MAX_SND_DIST; i++)
+               {
+                       soundCurve[i] = (*((byte *)W_CacheLumpName("SNDCURVE", PU_CACHE)+i)*(snd_MaxVolume*8))>>7;
+               }
+       }
+}
+
+static boolean musicPaused;
+void S_SetMusicVolume(void)
+{
+       I_SetMusicVolume(snd_MusicVolume);
+       if(snd_MusicVolume == 0)
+       {
+               I_PauseSong(rs);
+               musicPaused = true;
+       }
+       else if(musicPaused)
+       {
+               musicPaused = false;
+               I_ResumeSong(rs);
+       }
+}
+
+void S_ShutDown(void)
+{
+       extern int tsm_ID;
+       if(tsm_ID != -1)
+       {
+               I_StopSong(rs);
+               I_UnRegisterSong(rs);
+               I_ShutdownSound();
+       }
+}
+
+/*
+=============================================================================
+
+                                                       CONSTANTS
+
+=============================================================================
+*/
+
+#define SC_INDEX                0x3C4
+#define SC_RESET                0
+#define SC_CLOCK                1
+#define SC_MAPMASK              2
+#define SC_CHARMAP              3
+#define SC_MEMMODE              4
+
+#define CRTC_INDEX              0x3D4
+#define CRTC_H_TOTAL    0
+#define CRTC_H_DISPEND  1
+#define CRTC_H_BLANK    2
+#define CRTC_H_ENDBLANK 3
+#define CRTC_H_RETRACE  4
+#define CRTC_H_ENDRETRACE 5
+#define CRTC_V_TOTAL    6
+#define CRTC_OVERFLOW   7
+#define CRTC_ROWSCAN    8
+#define CRTC_MAXSCANLINE 9
+#define CRTC_CURSORSTART 10
+#define CRTC_CURSOREND  11
+#define CRTC_STARTHIGH  12
+#define CRTC_STARTLOW   13
+#define CRTC_CURSORHIGH 14
+#define CRTC_CURSORLOW  15
+#define CRTC_V_RETRACE  16
+#define CRTC_V_ENDRETRACE 17
+#define CRTC_V_DISPEND  18
+#define CRTC_OFFSET             19
+#define CRTC_UNDERLINE  20
+#define CRTC_V_BLANK    21
+#define CRTC_V_ENDBLANK 22
+#define CRTC_MODE               23
+#define CRTC_LINECOMPARE 24
+
+
+#define GC_INDEX                0x3CE
+#define GC_SETRESET             0
+#define GC_ENABLESETRESET 1
+#define GC_COLORCOMPARE 2
+#define GC_DATAROTATE   3
+#define GC_READMAP              4
+#define GC_MODE                 5
+#define GC_MISCELLANEOUS 6
+#define GC_COLORDONTCARE 7
+#define GC_BITMASK              8
+
+#define ATR_INDEX               0x3c0
+#define ATR_MODE                16
+#define ATR_OVERSCAN    17
+#define ATR_COLORPLANEENABLE 18
+#define ATR_PELPAN              19
+#define ATR_COLORSELECT 20
+
+#define STATUS_REGISTER_1    0x3da
+
+#define PEL_WRITE_ADR   0x3c8
+#define PEL_READ_ADR    0x3c7
+#define PEL_DATA                0x3c9
+#define PEL_MASK                0x3c6
+
+boolean grmode;
+
+//==================================================
+//
+// joystick vars
+//
+//==================================================
+
+boolean         joystickpresent;
+extern  unsigned        joystickx, joysticky;
+boolean I_ReadJoystick (void);          // returns false if not connected
+
+
+//==================================================
+
+#define VBLCOUNTER              34000           // hardware tics to a frame
+
+
+#define TIMERINT 8
+#define KEYBOARDINT 9
+
+#define CRTCOFF (_inbyte(STATUS_REGISTER_1)&1)
+#define CLI     _disable()
+#define STI     _enable()
+
+#define _outbyte(x,y) (outp(x,y))
+#define _outhword(x,y) (outpw(x,y))
+
+#define _inbyte(x) (inp(x))
+#define _inhword(x) (inpw(x))
+
+#define MOUSEB1 1
+#define MOUSEB2 2
+#define MOUSEB3 4
+
+boolean mousepresent;
+//static  int tsm_ID = -1; // tsm init flag
+
+//===============================
+
+int             ticcount;
+
+// REGS stuff used for int calls
+union REGS regs;
+struct SREGS segregs;
+
+boolean novideo; // if true, stay in text mode for debugging
+
+#define KBDQUESIZE 32
+byte keyboardque[KBDQUESIZE];
+int kbdtail, kbdhead;
+
+#define KEY_LSHIFT      0xfe
+
+#define KEY_INS         (0x80+0x52)
+#define KEY_DEL         (0x80+0x53)
+#define KEY_PGUP        (0x80+0x49)
+#define KEY_PGDN        (0x80+0x51)
+#define KEY_HOME        (0x80+0x47)
+#define KEY_END         (0x80+0x4f)
+
+#define SC_RSHIFT       0x36
+#define SC_LSHIFT       0x2a
+
+byte        scantokey[128] =
+                                       {
+//  0           1       2       3       4       5       6       7
+//  8           9       A       B       C       D       E       F
+       0  ,    27,     '1',    '2',    '3',    '4',    '5',    '6',
+       '7',    '8',    '9',    '0',    '-',    '=',    KEY_BACKSPACE, 9, // 0
+       'q',    'w',    'e',    'r',    't',    'y',    'u',    'i',
+       'o',    'p',    '[',    ']',    13 ,    KEY_RCTRL,'a',  's',      // 1
+       'd',    'f',    'g',    'h',    'j',    'k',    'l',    ';',
+       39 ,    '`',    KEY_LSHIFT,92,  'z',    'x',    'c',    'v',      // 2
+       'b',    'n',    'm',    ',',    '.',    '/',    KEY_RSHIFT,'*',
+       KEY_RALT,' ',   0  ,    KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,   // 3
+       KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,0  ,    0  , KEY_HOME,
+       KEY_UPARROW,KEY_PGUP,'-',KEY_LEFTARROW,'5',KEY_RIGHTARROW,'+',KEY_END, //4
+       KEY_DOWNARROW,KEY_PGDN,KEY_INS,KEY_DEL,0,0,             0,              KEY_F11,
+       KEY_F12,0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 5
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 6
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0         // 7
+                                       };
+
+//==========================================================================
+
+//--------------------------------------------------------------------------
+//
+// FUNC I_GetTime
+//
+// Returns time in 1/35th second tics.
+//
+//--------------------------------------------------------------------------
+
+int I_GetTime (void)
+{
+#ifdef NOTIMER
+       ticcount++;
+#endif
+       return(ticcount);
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_ColorBorder
+//
+//--------------------------------------------------------------------------
+
+void I_ColorBorder(void)
+{
+       int i;
+
+       I_WaitVBL(1);
+       _outbyte(PEL_WRITE_ADR, 0);
+       for(i = 0; i < 3; i++)
+       {
+               _outbyte(PEL_DATA, 63);
+       }
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_UnColorBorder
+//
+//--------------------------------------------------------------------------
+
+void I_UnColorBorder(void)
+{
+       int i;
+
+       I_WaitVBL(1);
+       _outbyte(PEL_WRITE_ADR, 0);
+       for(i = 0; i < 3; i++)
+       {
+               _outbyte(PEL_DATA, 0);
+       }
+}
+
+/*
+============================================================================
+
+                                                               USER INPUT
+
+============================================================================
+*/
+
+//--------------------------------------------------------------------------
+//
+// PROC I_WaitVBL
+//
+//--------------------------------------------------------------------------
+
+void I_WaitVBL(int vbls)
+{
+       int i;
+       int old;
+       int stat;
+
+       if(novideo)
+       {
+               return;
+       }
+       while(vbls--)
+       {
+               do
+               {
+                       stat = inp(STATUS_REGISTER_1);
+                       if(stat&8)
+                       {
+                               break;
+                       }
+               } while(1);
+               do
+               {
+                       stat = inp(STATUS_REGISTER_1);
+                       if((stat&8) == 0)
+                       {
+                               break;
+                       }
+               } while(1);
+       }
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_SetPalette
+//
+// Palette source must use 8 bit RGB elements.
+//
+//--------------------------------------------------------------------------
+
+void I_SetPalette(byte *palette)
+{
+       int i;
+
+       if(novideo)
+       {
+               return;
+       }
+       I_WaitVBL(1);
+       _outbyte(PEL_WRITE_ADR, 0);
+       for(i = 0; i < 768; i++)
+       {
+               _outbyte(PEL_DATA, (gammatable[usegamma][*palette++])>>2);
+       }
+}
+
+/*
+============================================================================
+
+                                                       GRAPHICS MODE
+
+============================================================================
+*/
+
+byte *pcscreen, *destscreen, *destview;
+
+
+/*
+==============
+=
+= I_Update
+=
+==============
+*/
+
+int UpdateState;
+extern int screenblocks;
+
+void I_Update (void)
+{
+       int i;
+       byte *dest;
+       int tics;
+       static int lasttic;
+
+//
+// blit screen to video
+//
+       if(DisplayTicker)
+       {
+               if(screenblocks > 9 || UpdateState&(I_FULLSCRN|I_MESSAGES))
+               {
+                       dest = (byte *)screen;
+               }
+               else
+               {
+                       dest = (byte *)pcscreen;
+               }
+               tics = ticcount-lasttic;
+               lasttic = ticcount;
+               if(tics > 20)
+               {
+                       tics = 20;
+               }
+               for(i = 0; i < tics; i++)
+               {
+                       *dest = 0xff;
+                       dest += 2;
+               }
+               for(i = tics; i < 20; i++)
+               {
+                       *dest = 0x00;
+                       dest += 2;
+               }
+       }
+       if(UpdateState == I_NOUPDATE)
+       {
+               return;
+       }
+       if(UpdateState&I_FULLSCRN)
+       {
+               memcpy(pcscreen, screen, SCREENWIDTH*SCREENHEIGHT);
+               UpdateState = I_NOUPDATE; // clear out all draw types
+       }
+       if(UpdateState&I_FULLVIEW)
+       {
+               if(UpdateState&I_MESSAGES && screenblocks > 7)
+               {
+                       for(i = 0; i <
+                               (viewwindowy+viewheight)*SCREENWIDTH; i += SCREENWIDTH)
+                       {
+                               memcpy(pcscreen+i, screen+i, SCREENWIDTH);
+                       }
+                       UpdateState &= ~(I_FULLVIEW|I_MESSAGES);
+               }
+               else
+               {
+                       for(i = viewwindowy*SCREENWIDTH+viewwindowx; i <
+                               (viewwindowy+viewheight)*SCREENWIDTH; i += SCREENWIDTH)
+                       {
+                               memcpy(pcscreen+i, screen+i, viewwidth);
+                       }
+                       UpdateState &= ~I_FULLVIEW;
+               }
+       }
+       if(UpdateState&I_STATBAR)
+       {
+               memcpy(pcscreen+SCREENWIDTH*(SCREENHEIGHT-SBARHEIGHT),
+                       screen+SCREENWIDTH*(SCREENHEIGHT-SBARHEIGHT),
+                       SCREENWIDTH*SBARHEIGHT);
+               UpdateState &= ~I_STATBAR;
+       }
+       if(UpdateState&I_MESSAGES)
+       {
+               memcpy(pcscreen, screen, SCREENWIDTH*28);
+               UpdateState &= ~I_MESSAGES;
+       }
+
+//  memcpy(pcscreen, screen, SCREENHEIGHT*SCREENWIDTH);
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_InitGraphics
+//
+//--------------------------------------------------------------------------
+
+void I_InitGraphics(void)
+{
+       if(novideo)
+       {
+               return;
+       }
+       grmode = true;
+       regs.w.ax = 0x13;
+       int386(0x10, (const union REGS *)&regs, &regs);
+       pcscreen = destscreen = (byte *)0xa0000;
+       I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE));
+       I_InitDiskFlash();
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_ShutdownGraphics
+//
+//--------------------------------------------------------------------------
+
+void I_ShutdownGraphics(void)
+{
+
+       if(*(byte *)0x449 == 0x13) // don't reset mode if it didn't get set
+       {
+               regs.w.ax = 3;
+               int386(0x