foxygit / doom Log in
commit 516a7028994df6718289a7e3db4d07a45c95466b
Merge: a15ba757 a9996b41
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Sat Feb 12 18:42:10 2011 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Sat Feb 12 18:42:10 2011 +0000

    Merge from raven-branch. FEATURE_MULTIPLAYER has been disabled
    temporarily until the netgame changes on raven-branch are finished.

    Subversion-branch: /branches/strife-branch
    Subversion-revision: 2259

 ChangeLog                                       |  377 ++++
 Makefile.am                                     |    1 +
 NEWS                                            |  100 +-
 NOT-BUGS                                        |  135 ++
 README                                          |   10 +-
 codeblocks/config.h                             |    6 +-
 codeblocks/game-res.rc                          |   10 +-
 codeblocks/server.cbp                           |    4 +
 codeblocks/setup-res.rc                         |    8 +-
 configure.in                                    |    2 +-
 data/convert-icon                               |    7 +-
 man/chocolate-server.6                          |    6 +
 man/docgen                                      |   29 +-
 msvc/config.h                                   |    6 +-
 msvc/win32.rc                                   |   10 +-
 opl/opl_sdl.c                                   |    4 +-
 pkg/Makefile.am                                 |    3 +-
 pkg/config.make.in                              |    1 +
 pkg/osx/LauncherManager.h                       |    1 -
 pkg/osx/LauncherManager.m                       |    1 -
 pkg/osx/Resources/launcher.nib/classes.nib      |   47 -
 pkg/osx/Resources/launcher.nib/designable.nib   | 2679 +++++++++++++++++++++++
 pkg/osx/Resources/launcher.nib/info.nib         |   23 -
 pkg/osx/Resources/launcher.nib/keyedobjects.nib |  Bin 19616 -> 24936 bytes
 pkg/wince/wince-cabgen                          |   40 +-
 rpm.spec.in                                     |    1 +
 src/Makefile.am                                 |    7 +-
 src/d_iwad.c                                    |    3 +-
 src/d_mode.h                                    |    1 +
 src/deh_io.c                                    |  141 +-
 src/deh_io.h                                    |    1 +
 src/deh_main.c                                  |   49 +-
 src/deh_main.h                                  |    2 +
 src/doom/d_main.c                               |  191 +-
 src/doom/d_net.c                                |  770 +++++--
 src/doom/d_net.h                                |    1 -
 src/doom/doomstat.h                             |    3 +-
 src/doom/dstrings.h                             |    9 -
 src/doom/g_game.c                               |   33 +-
 src/doom/m_menu.c                               |   10 +-
 src/doom/p_map.c                                |    2 +-
 src/doom/p_setup.c                              |   12 +-
 src/doom/p_spec.c                               |    9 +-
 src/doom/r_data.c                               |   20 +-
 src/doomfeatures.h                              |    3 +-
 src/i_sdlsound.c                                |   41 +-
 src/i_system.c                                  |    8 +-
 src/i_video.c                                   |  235 +-
 src/i_video.h                                   |    1 +
 src/m_argv.c                                    |   15 +-
 src/m_argv.h                                    |    4 +
 src/m_config.c                                  |   28 +-
 src/net_client.c                                |  257 +--
 src/net_client.h                                |    6 +-
 src/net_dedicated.c                             |    2 +-
 src/net_defs.h                                  |   33 +
 src/net_gui.c                                   |   18 +-
 src/net_gui.h                                   |    2 +-
 src/net_loop.c                                  |   22 +-
 src/net_query.c                                 |  691 ++++--
 src/net_query.h                                 |   15 +-
 src/net_sdl.c                                   |    4 +-
 src/net_server.c                                |  128 +-
 src/net_server.h                                |    4 +
 src/net_structrw.c                              |   22 +-
 src/net_structrw.h                              |    3 +
 src/setup/display.c                             |  304 ++-
 src/setup/execute.c                             |   55 +-
 src/setup/execute.h                             |    2 +-
 src/setup/joystick.c                            |    4 +-
 src/setup/mainmenu.c                            |   31 +-
 src/setup/multiplayer.c                         |  148 +-
 src/setup/multiplayer.h                         |    1 +
 src/setup/txt_joybinput.c                       |    1 +
 src/setup/txt_keyinput.c                        |    1 +
 src/setup/txt_mouseinput.c                      |    1 +
 src/strife/d_main.c                             |  105 +-
 src/strife/d_net.c                              |   27 +-
 src/strife/doomstat.h                           |    1 -
 src/strife/dstrings.h                           |    9 -
 src/strife/g_game.c                             |   19 +-
 src/strife/m_menu.c                             |    8 +-
 src/strife/p_map.c                              |    2 +-
 src/strife/p_spec.c                             |    7 +-
 src/strife/r_data.c                             |   20 +-
 src/w_main.c                                    |   12 +-
 textscreen/examples/guitest.c                   |   13 +-
 textscreen/txt_button.c                         |    1 +
 textscreen/txt_checkbox.c                       |    9 +-
 textscreen/txt_dropdown.c                       |    1 +
 textscreen/txt_inputbox.c                       |    2 +
 textscreen/txt_label.c                          |    2 +-
 textscreen/txt_radiobutton.c                    |    9 +-
 textscreen/txt_scrollpane.c                     |   20 +-
 textscreen/txt_sdl.c                            |   37 +-
 textscreen/txt_separator.c                      |    2 +-
 textscreen/txt_spinctrl.c                       |    1 +
 textscreen/txt_strut.c                          |    2 +-
 textscreen/txt_table.c                          |   51 +-
 textscreen/txt_widget.c                         |   27 +-
 textscreen/txt_widget.h                         |   14 +-
 textscreen/txt_window.c                         |   39 +-
 textscreen/txt_window_action.c                  |    1 +
 103 files changed, 6095 insertions(+), 1211 deletions(-)

diff --cc src/doomfeatures.h
index c5e6067a,46e26e52..1c994b70
--- a/src/doomfeatures.h
+++ b/src/doomfeatures.h
@@@ -36,8 -36,8 +36,9 @@@
  #define FEATURE_DEHACKED 1

  // Enables multiplayer support (network games)
++// STRIFE-TODO: multiplayer is currently broken ...

- //#define FEATURE_MULTIPLAYER 1
 -#define FEATURE_MULTIPLAYER 1
++// #define FEATURE_MULTIPLAYER 1

  // Enables sound output

diff --cc src/i_video.c
index 8c2cfeac,9d70e88e..6bf2f179
--- a/src/i_video.c
+++ b/src/i_video.c
@@@ -1760,12 -1881,16 +1881,18 @@@ static void SetVideoMode(screen_mode_t
      }
      else
      {
+         // In windowed mode, the window can be resized while the game is
+         // running.  This feature is disabled on OS X, as it adds an ugly
+         // scroll handle to the corner of the screen.
+
+ #ifndef __MACOSX__
          flags |= SDL_RESIZABLE;
+ #endif
 +        // villsa - center window
 +        SDL_putenv("SDL_VIDEO_CENTERED=1");
      }

-     screen = SDL_SetVideoMode(w, h, 8, flags);
+     screen = SDL_SetVideoMode(w, h, screen_bpp, flags);

      if (screen == NULL)
      {
diff --cc src/strife/d_main.c
index 4260ca7d,00000000..95b1b643
mode 100644,000000..100644
--- a/src/strife/d_main.c
+++ b/src/strife/d_main.c
@@@ -1,2002 -1,0 +1,1997 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	DOOM main program (D_DoomMain) and game loop (D_DoomLoop),
 +//	plus functions to determine game mode (shareware, registered),
 +//	parse command line parameters, configure game parameters (turbo),
 +//	and call the startup functions.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#include <ctype.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "config.h"
 +#include "deh_main.h"
 +#include "doomdef.h"
 +#include "doomstat.h"
 +
 +#include "dstrings.h"
 +#include "doomfeatures.h"
 +#include "sounds.h"
 +
 +#include "d_iwad.h"
 +
 +#include "z_zone.h"
 +#include "w_main.h"
 +#include "w_wad.h"
 +#include "s_sound.h"
 +#include "v_video.h"
 +
 +#include "f_finale.h"
 +#include "f_wipe.h"
 +
 +#include "m_argv.h"
 +#include "m_config.h"
 +#include "m_controls.h"
 +#include "m_misc.h"
 +#include "m_menu.h"
 +#include "m_saves.h" // haleyjd [STRIFE]
 +#include "p_saveg.h"
 +
 +#include "i_endoom.h"
 +#include "i_joystick.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "i_swap.h"
 +
 +#include "g_game.h"
 +
 +#include "hu_stuff.h"
 +#include "wi_stuff.h"
 +#include "st_stuff.h"
 +#include "am_map.h"
 +#include "net_client.h"
 +#include "net_dedicated.h"
 +#include "net_query.h"
 +
 +#include "p_setup.h"
 +#include "r_local.h"
 +
 +#include "d_main.h"
 +
 +//
 +// D-DoomLoop()
 +// Not a globally visible function,
 +//  just included for source reference,
 +//  called by D_DoomMain, never exits.
 +// Manages timing and IO,
 +//  calls all ?_Responder, ?_Ticker, and ?_Drawer,
 +//  calls I_GetTime, I_StartFrame, and I_StartTic
 +//
 +void D_DoomLoop (void);
 +
 +static boolean D_AddFile(char *filename);
 +
 +// Location where savegames are stored
 +
 +char *          savegamedir;
 +
 +// location of IWAD and WAD files
 +
 +char *          iwadfile;
 +
 +
 +boolean         devparm;        // started game with -devparm
 +boolean         nomonsters;     // checkparm of -nomonsters
 +boolean         respawnparm;    // checkparm of -respawn
 +boolean         fastparm;       // checkparm of -fast
 +
 +boolean         showintro = true;   // [STRIFE] checkparm of -nograph, disables intro
 +boolean         singletics = false; // debug flag to cancel adaptiveness
 +
 +
 +//extern int soundVolume;
 +//extern  int	sfxVolume;
 +//extern  int	musicVolume;
 +
 +extern  boolean	inhelpscreens;
 +
 +skill_t		startskill;
 +int             startepisode;
 +int		startmap;
 +boolean		autostart;
 +int             startloadgame;
 +
- FILE*		debugfile;
-
 +boolean		advancedemo;
 +
 +// villsa [STRIFE] workparm variable (similar to devparm?)
 +boolean         workparm = false;
 +
 +// villsa [STRIFE] stonecold cheat variable
 +boolean         stonecold = false;
 +
 +// haleyjd 09/11/10: [STRIFE] Game type variables
 +boolean         isregistered;
 +boolean         isdemoversion;
 +
 +// Store demo, do not accept any inputs
 +// haleyjd [STRIFE] Unused.
 +//boolean         storedemo;
 +
 +
 +char		wadfile[1024];          // primary wad file
 +char		mapdir[1024];           // directory of development maps
 +
 +int             show_endoom = 1;
 +
 +
 +void D_CheckNetGame (void);
 +void D_ProcessEvents (void);
 +void G_BuildTiccmd (ticcmd_t* cmd);
 +void D_DoAdvanceDemo (void);
 +
 +
 +//
 +// D_ProcessEvents
 +// Send all the events of the given timestamp down the responder chain
 +//
 +void D_ProcessEvents (void)
 +{
 +    event_t*    ev;
 +
 +    // haleyjd 08/22/2010: [STRIFE] there is no such thing as a "store demo"
 +    // version of Strife
 +
 +    // IF STORE DEMO, DO NOT ACCEPT INPUT
 +    //if (storedemo)
 +    //    return;
 +
 +    while ((ev = D_PopEvent()) != NULL)
 +    {
 +        if (M_Responder (ev))
 +            continue;               // menu ate the event
 +        G_Responder (ev);
 +    }
 +}
 +
 +
 +
 +
 +//
 +// D_Display
 +//  draw current display, possibly wiping it from the previous
 +//
 +// wipegamestate can be set to -1 to force a wipe on the next draw
 +//
 +// haleyjd 08/23/10: [STRIFE]:
 +// * Changes to eliminate intermission and change timing of screenwipe
 +// * 20100901: Added ST_DrawExternal and popupactivestate static variable
 +// * 20110206: Start wipegamestate at GS_UNKNOWN (STRIFE-TODO: rename?)
 +//
 +gamestate_t     wipegamestate = GS_UNKNOWN;
 +extern  boolean setsizeneeded;
 +extern  int             showMessages;
 +void R_ExecuteSetViewSize (void);
 +
 +void D_Display (void)
 +{
 +    static  boolean             viewactivestate = false;
 +    static  boolean             menuactivestate = false;
 +    static  boolean             inhelpscreensstate = false;
 +    static  boolean             popupactivestate = false; // [STRIFE]
 +    static  boolean             fullscreen = false;
 +    static  gamestate_t         oldgamestate = -1;
 +    static  int                 borderdrawcount;
 +    int                         nowtime;
 +    int                         tics;
 +    int                         wipestart;
 +    int                         y;
 +    boolean                     done;
 +    boolean                     wipe;
 +    boolean                     redrawsbar;
 +
 +    if (nodrawers)
 +        return;                    // for comparative timing / profiling
 +
 +    redrawsbar = false;
 +
 +    // change the view size if needed
 +    if (setsizeneeded)
 +    {
 +        R_ExecuteSetViewSize ();
 +        oldgamestate = -1;                      // force background redraw
 +        borderdrawcount = 3;
 +    }
 +
 +    // save the current screen if about to wipe
 +    if (gamestate != wipegamestate)
 +    {
 +        wipe = true;
 +        wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);
 +    }
 +    else
 +        wipe = false;
 +
 +    if (gamestate == GS_LEVEL && gametic)
 +        HU_Erase();
 +
 +    // do buffered drawing
 +    switch (gamestate)
 +    {
 +    case GS_LEVEL:
 +        if (!gametic)
 +            break;
 +        if (automapactive)
 +            AM_Drawer ();
 +        if (wipe || (viewheight != 200 && fullscreen) )
 +            redrawsbar = true;
 +        // haleyjd 08/29/10: [STRIFE] Always redraw sbar if menu is/was active
 +        if (menuactivestate || (inhelpscreensstate && !inhelpscreens))
 +            redrawsbar = true;              // just put away the help screen
 +        ST_Drawer (viewheight == 200, redrawsbar );
 +        fullscreen = viewheight == 200;
 +        break;
 +
 +     // haleyjd 08/23/2010: [STRIFE] No intermission
 +     /*
 +     case GS_INTERMISSION:
 +         WI_Drawer ();
 +         break;
 +     */
 +
 +    case GS_FINALE:
 +        F_Drawer ();
 +        break;
 +
 +    case GS_DEMOSCREEN:
 +        D_PageDrawer ();
 +        break;
 +
 +    default:
 +        break;
 +    }
 +
 +    // draw buffered stuff to screen
 +    I_UpdateNoBlit ();
 +
 +    // draw the view directly
 +    if (gamestate == GS_LEVEL && !automapactive && gametic)
 +        R_RenderPlayerView (&players[displayplayer]);
 +
 +    if (gamestate == GS_LEVEL && gametic)
 +    {
 +        HU_Drawer ();
 +        if(ST_DrawExternal())
 +            popupactivestate = true;
 +        else if(popupactivestate)
 +        {
 +            popupactivestate = false;
 +            menuactivestate = 1;
 +        }
 +    }
 +
 +    // clean up border stuff
 +    if (gamestate != oldgamestate && gamestate != GS_LEVEL)
 +        I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE));
 +
 +    // see if the border needs to be initially drawn
 +    if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL)
 +    {
 +        viewactivestate = false;        // view was not active
 +        R_FillBackScreen ();    // draw the pattern into the back screen
 +    }
 +
 +    // see if the border needs to be updated to the screen
 +    if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != 320)
 +    {
 +        if (menuactive || menuactivestate || !viewactivestate)
 +        {
 +            borderdrawcount = 3;
 +            popupactivestate = false;
 +        }
 +        if (borderdrawcount)
 +        {
 +            R_DrawViewBorder ();    // erase old menu stuff
 +            borderdrawcount--;
 +        }
 +
 +    }
 +
 +    if (testcontrols)
 +    {
 +        // Box showing current mouse speed
 +
 +        G_DrawMouseSpeedBox();
 +    }
 +
 +    menuactivestate = menuactive;
 +    viewactivestate = viewactive;
 +    inhelpscreensstate = inhelpscreens;
 +    oldgamestate = wipegamestate = gamestate;
 +
 +    // draw pause pic
 +    if (paused)
 +    {
 +        if (automapactive)
 +            y = 4;
 +        else
 +            y = viewwindowy+4;
 +        V_DrawPatchDirect(viewwindowx + (scaledviewwidth - 68) / 2, y,
 +                          W_CacheLumpName (DEH_String("M_PAUSE"), PU_CACHE));
 +    }
 +
 +
 +    // menus go directly to the screen
 +    M_Drawer ();          // menu is drawn even on top of everything
 +    NetUpdate ();         // send out any new accumulation
 +
 +
 +    // normal update
 +    if (!wipe)
 +    {
 +        I_FinishUpdate ();              // page flip or blit buffer
 +        return;
 +    }
 +
 +    // wipe update
 +    wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);
 +
 +    wipestart = I_GetTime () - 1;
 +
 +    do
 +    {
 +        do
 +        {
 +            nowtime = I_GetTime ();
 +            tics = nowtime - wipestart;
 +            I_Sleep(1);
 +        } while (tics < 3); // haleyjd 08/23/2010: [STRIFE] Changed from == 0 to < 3
 +
 +        // haleyjd 08/26/10: [STRIFE] Changed to use ColorXForm wipe.
 +        wipestart = nowtime;
 +        done = wipe_ScreenWipe(wipe_ColorXForm
 +                               , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics);
 +        I_UpdateNoBlit ();
 +        M_Drawer ();                            // menu is drawn even on top of wipes
 +        I_FinishUpdate ();                      // page flip or blit buffer
 +    } while (!done);
 +}
 +
 +//
 +// Add configuration file variable bindings.
 +//
 +
 +void D_BindVariables(void)
 +{
 +    int i;
 +
 +    M_ApplyPlatformDefaults();
 +
 +    I_BindVideoVariables();
 +    I_BindJoystickVariables();
 +    I_BindSoundVariables();
 +
 +    M_BindBaseControls();
 +    M_BindWeaponControls();
 +    M_BindMapControls();
 +    M_BindMenuControls();
 +    M_BindStrifeControls(); // haleyjd 09/01/10: [STRIFE]
 +    M_BindChatControls(MAXPLAYERS);
 +
 +    key_multi_msgplayer[0] = HUSTR_KEYGREEN;
 +    key_multi_msgplayer[1] = HUSTR_KEYINDIGO;
 +    key_multi_msgplayer[2] = HUSTR_KEYBROWN;
 +    key_multi_msgplayer[3] = HUSTR_KEYRED;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    NET_BindVariables();
 +#endif
 +
 +    // haleyjd 08/29/10: [STRIFE]
 +    // * Added voice volume
 +    // * Added back flat
 +    M_BindVariable("mouse_sensitivity",      &mouseSensitivity);
 +    M_BindVariable("sfx_volume",             &sfxVolume);
 +    M_BindVariable("music_volume",           &musicVolume);
 +    M_BindVariable("voice_volume",           &voiceVolume);
 +    M_BindVariable("show_messages",          &showMessages);
 +    M_BindVariable("screenblocks",           &screenblocks);
 +    M_BindVariable("detaillevel",            &detailLevel);
 +    M_BindVariable("snd_channels",           &snd_channels);
 +    M_BindVariable("vanilla_savegame_limit", &vanilla_savegame_limit);
 +    M_BindVariable("vanilla_demo_limit",     &vanilla_demo_limit);
 +    M_BindVariable("show_endoom",            &show_endoom);
 +    M_BindVariable("back_flat",              &back_flat);
 +
 +    // Multiplayer chat macros
 +
 +    for (i=0; i<10; ++i)
 +    {
 +        char buf[12];
 +
 +        sprintf(buf, "chatmacro%i", i);
 +        M_BindVariable(buf, &chat_macros[i]);
 +    }
 +}
 +
 +//
 +// D_GrabMouseCallback
 +//
 +// Called to determine whether to grab the mouse pointer
 +//
 +
 +boolean D_GrabMouseCallback(void)
 +{
 +    // Drone players don't need mouse focus
 +
 +    if (drone)
 +        return false;
 +
 +    // when menu is active or game is paused, release the mouse
 +
 +    if (menuactive || paused)
 +        return false;
 +
 +    // only grab mouse when playing levels (but not demos)
 +
 +    return (gamestate == GS_LEVEL) && !demoplayback;
 +}
 +
 +//
 +//  D_DoomLoop
 +//
 +//  haleyjd 08/23/10: [STRIFE] Verified unmodified.
 +//
 +extern  boolean         demorecording;
 +
 +void D_DoomLoop (void)
 +{
 +    if (demorecording)
 +        G_BeginRecording ();
 +
-     if (M_CheckParm ("-debugfile"))
-     {
-         char    filename[20];
-         sprintf (filename,"debug%i.txt",consoleplayer);
-         printf ("debug output to: %s\n",filename);
-         debugfile = fopen (filename,"w");
-     }
-
 +    TryRunTics();
 +
 +    if(!showintro) // [STRIFE]
 +    {
 +        I_SetWindowTitle(gamedescription);
 +        I_InitGraphics();
 +    }
 +    I_EnableLoadingDisk();
 +    I_SetGrabMouseCallback(D_GrabMouseCallback);
 +
 +    V_RestoreBuffer();
 +    R_ExecuteSetViewSize();
 +
 +    D_StartGameLoop();
 +
 +    if (testcontrols)
 +    {
 +        wipegamestate = gamestate;
 +    }
 +
 +    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 ();
 +            M_Ticker ();
 +            G_Ticker ();
 +            gametic++;
 +            maketic++;
 +        }
 +        else
 +        {
 +            TryRunTics (); // will run at least one tic
 +        }
 +
 +        S_UpdateSounds (players[consoleplayer].mo);// move positional sounds
 +
 +        // Update display, next frame, with current state.
 +        if (screenvisible)
 +            D_Display ();
 +    }
 +}
 +
 +
 +
 +//
 +//  DEMO LOOP
 +//
 +int             demosequence;
 +int             pagetic;
 +char                    *pagename;
 +
 +
 +//
 +// D_PageTicker
 +// Handles timing for warped projection
 +//
 +// haleyjd 08/22/2010: [STRIFE] verified unmodified
 +//
 +void D_PageTicker (void)
 +{
 +    if (--pagetic < 0)
 +        D_AdvanceDemo ();
 +}
 +
 +
 +
 +//
 +// D_PageDrawer
 +//
 +// haleyjd 08/22/2010: [STRIFE] verified unmodified
 +//
 +void D_PageDrawer (void)
 +{
 +    V_DrawPatch (0, 0, W_CacheLumpName(pagename, PU_CACHE));
 +}
 +
 +
 +//
 +// D_AdvanceDemo
 +// Called after each demo or intro demosequence finishes
 +//
 +// haleyjd 08/22/2010: [STRIFE] verified unmodified
 +//
 +void D_AdvanceDemo (void)
 +{
 +    advancedemo = true;
 +}
 +
 +
 +//
 +// This cycles through the demo sequences.
 +// FIXME - version dependend demo numbers?
 +//
 +// [STRIFE] Modified for the opening slideshow and the exit screen
 +//
 +void D_DoAdvanceDemo (void)
 +{
 +    players[consoleplayer].playerstate = PST_LIVE;  // not reborn
 +    advancedemo = false;
 +    usergame = false;               // no save / end game here
 +    paused = false;
 +    gameaction = ga_nothing;
 +
 +    // villsa 09/12/10: [STRIFE] converted pagetics to ticrate
 +    switch (demosequence)
 +    {
 +    case -5: // exit the game
 +        I_Quit();
 +        return;
 +    case -4: // show exit screen
 +        menuactive = false;
 +        pagetic = 3*TICRATE;
 +        gamestate = GS_DEMOSCREEN;
 +        pagename = DEH_String("PANEL7");
 +        S_StartMusic(mus_fast);
 +        if(isdemoversion)
 +            demosequence = -3; // show Velocity logo
 +        else
 +            demosequence = -5; // exit
 +        return;
 +    case -3: // show Velocity logo for demo version
 +        pagetic = 6*TICRATE;
 +        gamestate = GS_DEMOSCREEN;
 +        pagename = DEH_String("vellogo");
 +        demosequence = -5; // exit
 +        return;
 +    case -2: // title screen
 +        pagetic = 6*TICRATE;
 +        gamestate = GS_DEMOSCREEN;
 +        pagename = DEH_String("TITLEPIC");
 +        S_StartMusic(mus_logo);
 +        demosequence = -1; // start intro cinematic
 +        return;
 +    case -1: // start of intro cinematic
 +        pagetic = 10;
 +        gamestate = GS_DEMOSCREEN;
 +        pagename = DEH_String("PANEL0");
 +        S_StartSound(NULL, sfx_rb2act);
 +        wipegamestate = -1;
 +        break;
 +    case 0: // Rogue logo
 +        pagetic = 4*TICRATE;
 +        gamestate = GS_DEMOSCREEN;
 +        pagename = DEH_String("RGELOGO");
 +        wipegamestate = -1;
 +        break;
 +    case 1:
 +        pagetic = 7*TICRATE;              // The comet struck our planet without
 +        gamestate = GS_DEMOSCREEN;        // warning.We lost our paradise in a
 +        pagename = DEH_String("PANEL1");  // single, violent stroke.
 +        I_StartVoice(DEH_String("pro1"));
 +        S_StartMusic(mus_intro);
 +        break;
 +    case 2:
 +        pagetic = 9*TICRATE;              // The impact released a virus which
 +        gamestate = GS_DEMOSCREEN;        // swept through the land and killed
 +        pagename = DEH_String("PANEL2");  // millions. They turned out to be
 +        I_StartVoice(DEH_String("pro2")); // the lucky ones...
 +        break;
 +    case 3:
 +        pagetic = 12*TICRATE;             // For those that did not die became
 +        gamestate = GS_DEMOSCREEN;        // mutations of humanity. Some became
 +        pagename = DEH_String("PANEL3");  // fanatics who heard the voice of a
 +        I_StartVoice(DEH_String("pro3")); // malignant God in their heads, and
 +        break;                            // called themselves the Order.
 +    case 4:
 +        pagetic = 11*TICRATE;             // Those of us who were deaf to this
 +        pagename = DEH_String("PANEL4");  // voice suffer horribly and are
 +        gamestate = GS_DEMOSCREEN;        // forced to serve these ruthless
 +        I_StartVoice(DEH_String("pro4")); // psychotics, who wield weapons more
 +        break;                            // powerful than anything we can muster.
 +    case 5:
 +        pagetic = 10*TICRATE;             // They destroy our women and children,
 +        gamestate = GS_DEMOSCREEN;        // so that we must hide them underground,
 +        pagename = DEH_String("PANEL5");  // and live like animals in constant
 +        I_StartVoice(DEH_String("pro5")); // fear for our lives.
 +        break;
 +    case 6:                               // But there are whispers of discontent.
 +        pagetic = 16*TICRATE;             // If we organize, can we defeat our
 +        gamestate = GS_DEMOSCREEN;        // masters? Weapons are being stolen,
 +        pagename = DEH_String("PANEL6");  // soldiers are being trained. A
 +        I_StartVoice(DEH_String("pro6")); // Movement is born! Born of lifelong
 +        break;                            // STRIFE!
 +    case 7: // titlepic again - unused...
 +        pagetic = 9*TICRATE;
 +        gamestate = GS_DEMOSCREEN;
 +        pagename = DEH_String("TITLEPIC");
 +        wipegamestate = -1;
 +        break;
 +    case 8: // demo
 +        ClearTmp();
 +        pagetic = 9*TICRATE;
 +        G_DeferedPlayDemo(DEH_String("demo1"));
 +        break;
 +    case 9: // velocity logo? - unused...
 +        pagetic = 6*TICRATE;
 +        gamestate = GS_DEMOSCREEN;
 +        pagename = DEH_String("vellogo");
 +        wipegamestate = -1;
 +        break;
 +    case 10: // credits
 +        gamestate = GS_DEMOSCREEN;
 +        pagetic = 12*TICRATE;
 +        pagename = DEH_String("CREDIT");
 +        wipegamestate = -1;
 +        break;
 +    default:
 +        break;
 +    }
 +
 +    ++demosequence;
 +
 +    if(demosequence > 11)
 +        demosequence = -2;
 +    if(demosequence == 7 || demosequence == 9)
 +        ++demosequence;
 +}
 +
 +
 +
 +//
 +// D_StartTitle
 +//
 +// [STRIFE]
 +// haleyjd 09/11/10: Small modifications for new demo sequence.
 +//
 +void D_StartTitle (void)
 +{
 +    gamestate = GS_DEMOSCREEN;
 +    gameaction = ga_nothing;
 +    demosequence = -2;
 +    D_AdvanceDemo ();
 +}
 +
 +//
 +// D_QuitGame
 +//
 +// [STRIFE] New function
 +// haleyjd 09/11/10: Sets up the quit game snippet powered by the
 +// demo sequence.
 +//
 +void D_QuitGame(void)
 +{
 +    gameaction = ga_nothing;
 +    demosequence = -4;
 +    D_AdvanceDemo();
 +}
 +
 +// Strings for dehacked replacements of the startup banner
 +//
 +// These are from the original source: some of them are perhaps
 +// not used in any dehacked patches
 +
- static char *banners[] =
++static char *banners[] =
 +{
 +    // strife1.wad:
 +
 +    "                      "
 +    "STRIFE:  Quest for the Sigil v1.31"
 +    "                                 "
 +};
 +
 +//
 +// Get game name: if the startup banner has been replaced, use that.
 +// Otherwise, use the name given
 +//
 +
 +static char *GetGameName(char *gamename)
 +{
 +    size_t i;
 +    char *deh_sub;
 +
 +    for (i=0; i<arrlen(banners); ++i)
 +    {
 +        // Has the banner been replaced?
 +
 +        deh_sub = DEH_String(banners[i]);
 +
 +        if (deh_sub != banners[i])
 +        {
 +            // Has been replaced
 +            // We need to expand via printf to include the Doom version
 +            // number
 +            // We also need to cut off spaces to get the basic name
 +
 +            gamename = Z_Malloc(strlen(deh_sub) + 10, PU_STATIC, 0);
 +            sprintf(gamename, deh_sub, STRIFE_VERSION / 100, STRIFE_VERSION % 100);
 +
 +            while (gamename[0] != '\0' && isspace(gamename[0]))
 +                strcpy(gamename, gamename+1);
 +
 +            while (gamename[0] != '\0' && isspace(gamename[strlen(gamename)-1]))
 +                gamename[strlen(gamename) - 1] = '\0';
 +
 +            return gamename;
 +        }
 +    }
 +
 +    return gamename;
 +}
 +
 +//
 +// Find out what version of Doom is playing.
 +//
 +
 +void D_IdentifyVersion(void)
 +{
 +    // gamemission is set up by the D_FindIWAD function.  But if
 +    // we specify '-iwad', we have to identify using
 +    // IdentifyIWADByName.  However, if the iwad does not match
 +    // any known IWAD name, we may have a dilemma.  Try to
 +    // identify by its contents.
 +
 +    // STRIFE-TODO: some elaborate checks? for now we assume...
 +    // The logic in strife1.exe is simple:
 +    // * if strife1.wad is found, set isregistered = true
 +    // * if strife0.wad is found, set isdemoversion = true
 +
 +    // Make sure gamemode is set up correctly
 +    gamemode = commercial;
 +    gamemission = strife;
 +    isregistered = true;
 +
 +    // Load voices.wad
 +    if(isregistered)
 +    {
 +        char *name = D_FindWADByName("voices.wad");
 +
 +        if(!name) // not found?
 +        {
 +            int p;
 +
 +            // haleyjd STRIFE-FIXME: Temporary?
 +            // If -iwad was used, check and see if voices.wad exists on the
 +            // same filepath.
 +            if((p = M_CheckParm("-iwad")) && p < myargc - 1)
 +            {
 +                char   *iwad     = myargv[p + 1];
 +                size_t  len      = strlen(iwad) + 24;
 +                char   *filename = malloc(len);
 +                char    sepchar;
 +
 +                // how the heck is Choco surviving without this routine?
 +                sepchar = M_GetFilePath(iwad, filename, len);
 +                filename[strlen(filename)] = sepchar;
 +                strcat(filename, "voices.wad");
 +
 +                if(!M_FileExists(filename))
 +                    disable_voices = 1;
 +                else
 +                    name = filename; // STRIFE-FIXME: memory leak!!
 +            }
 +            else
 +                disable_voices = 1;
 +        }
 +
 +        if(disable_voices) // voices disabled?
 +        {
 +            if(devparm)
 +                 printf("Voices disabled\n");
 +            return;
 +        }
 +
 +        D_AddFile(name);
 +    }
 +}
 +
 +#if 0
 +//
 +// DoTimeBomb
 +//
 +// haleyjd 08/23/2010: [STRIFE] New function
 +// Code with no xrefs; probably left over from a private alpha or beta.
 +// Translated here because it explains what the SERIAL lump was meant to do.
 +//
 +void DoTimeBomb(void)
 +{
 +    dosdate_t date;
 +    char *serial;
 +    int serialnum;
 +    int serial_year;
 +    int serial_month;
 +
 +    serial = W_CacheLumpName("serial", PU_CACHE);
 +    serialnum = atoi(serial);
 +
 +    // Rogue, much like Governor Mourel, were lousy liars. These deceptive
 +    // error messages are pretty low :P
 +    dos_getdate(&date);
 +    if(date.year > 1996 || date.day > 15 && date.month > 4)
 +        I_Error("Data error! Corrupted WAD File!");
 +    serial_year = serialnum / 10000;
 +    serial_month = serialnum / 100 - 100 * serial_year;
 +    if(date.year < serial_year ||
 +       date.day < serialnum - 100 * serial_month - 10000 * serial_year &&
 +       date.month < serial_month)
 +       I_Error("Bad wadfile");
 +}
 +#endif
 +
 +// Set the gamedescription string
 +
 +void D_SetGameDescription(void)
 +{
 +    gamedescription = GetGameName("Strife: Quest for the Sigil");
 +}
 +
 +static void SetSaveGameDir(char *iwad_filename)
 +{
 +    char *sep;
 +    char *basefile;
 +
 +    // Extract the base filename
 +
 +    sep = strrchr(iwad_filename, DIR_SEPARATOR);
 +
 +    if (sep == NULL)
 +    {
 +        basefile = iwad_filename;
 +    }
 +    else
 +    {
 +        basefile = sep + 1;
 +    }
 +
 +    // ~/.chocolate-doom/savegames/
 +
 +    savegamedir = Z_Malloc(strlen(configdir) + 30, PU_STATIC, 0);
 +    sprintf(savegamedir, "%ssavegames%c", configdir,
 +                         DIR_SEPARATOR);
 +
 +    M_MakeDirectory(savegamedir);
 +
 +    // eg. ~/.chocolate-doom/savegames/doom2.wad/
 +
 +    sprintf(savegamedir + strlen(savegamedir), "%s%c",
 +            basefile, DIR_SEPARATOR);
 +
 +    M_MakeDirectory(savegamedir);
 +
 +    // haleyjd 20110210: Create Strife hub save folders
 +    M_CreateSaveDirs(savegamedir);
 +}
 +
 +// Check if the IWAD file is the Chex Quest IWAD.
 +// Returns true if this is chex.wad.
 +// haleyjd 08/23/10: there is no Chex Quest based on Strife,
 +// though I must admit that makes an intriguing idea....
 +/*
 +static boolean CheckChex(char *iwadname)
 +{
 +    char *chex_iwadname = "chex.wad";
 +
 +    return (strlen(iwadname) > strlen(chex_iwadname)
 +     && !strcasecmp(iwadname + strlen(iwadname) - strlen(chex_iwadname),
 +                    chex_iwadname));
 +}
 +*/
 +
 +//      print title for every printed line
 +char            title[128];
 +
 +static boolean D_AddFile(char *filename)
 +{
 +    wad_file_t *handle;
 +
 +    printf(" adding %s\n", filename);
 +    handle = W_AddFile(filename);
 +
 +    return handle != NULL;
 +}
 +
 +// Copyright message banners
 +// Some dehacked mods replace these.  These are only displayed if they are
 +// replaced by dehacked.
 +// haleyjd 08/22/2010: [STRIFE] altered to match strings from binary
 +static char *copyright_banners[] =
 +{
 +    "===========================================================================\n"
 +    "ATTENTION:  This version of STRIFE has extra files added to it.\n"
 +    "        You will not receive technical support for modified games.\n"
 +    "===========================================================================\n",
 +
 +    "===========================================================================\n"
 +    "             This version is NOT SHAREWARE, do not distribute!\n"
 +    "         Please report software piracy to the SPA: 1-800-388-PIR8\n"
 +    "===========================================================================\n",
 +
 +    "===========================================================================\n"
 +    "                                Shareware!\n"
 +    "===========================================================================\n"
 +};
 +
 +// Prints a message only if it has been modified by dehacked.
 +
 +void PrintDehackedBanners(void)
 +{
 +    size_t i;
 +
 +    for (i=0; i<arrlen(copyright_banners); ++i)
 +    {
 +        char *deh_s;
 +
 +        deh_s = DEH_String(copyright_banners[i]);
 +
 +        if (deh_s != copyright_banners[i])
 +        {
 +            printf("%s", deh_s);
 +
 +            // Make sure the modified banner always ends in a newline character.
 +            // If it doesn't, add a newline.  This fixes av.wad.
 +
 +            if (deh_s[strlen(deh_s) - 1] != '\n')
 +            {
 +                printf("\n");
 +            }
 +        }
 +    }
 +}
 +
 +static struct
 +{
 +    char *description;
 +    char *cmdline;
 +    GameVersion_t version;
 +} gameversions[] = {
 +    {"Doom 1.9",             "1.9",        exe_doom_1_9},
 +    {"Ultimate Doom",        "ultimate",   exe_ultimate},
 +    {"Final Doom",           "final",      exe_final},
 +    {"Chex Quest",           "chex",       exe_chex},
 +    { NULL,                  NULL,         0},
 +};
 +
 +// Initialize the game version
 +
 +static void InitGameVersion(void)
 +{
 +    int p;
 +    int i;
 +
 +    //!
 +    // @arg <version>
 +    // @category compat
 +    //
 +    // Emulate a specific version of Doom.  Valid values are "1.9",
 +    // "ultimate" and "final".
 +    //
 +
-     p = M_CheckParm("-gameversion");
++    p = M_CheckParmWithArgs("-gameversion", 1);
 +
-     if (p > 0)
++    if (p)
 +    {
 +        for (i=0; gameversions[i].description != NULL; ++i)
 +        {
 +            if (!strcmp(myargv[p+1], gameversions[i].cmdline))
 +            {
 +                gameversion = gameversions[i].version;
 +                break;
 +            }
 +        }
 +
 +        if (gameversions[i].description == NULL)
 +        {
 +            printf("Supported game versions:\n");
 +
 +            for (i=0; gameversions[i].description != NULL; ++i)
 +            {
 +                printf("\t%s (%s)\n", gameversions[i].cmdline,
 +                        gameversions[i].description);
 +            }
 +
 +            I_Error("Unknown game version '%s'", myargv[p+1]);
 +        }
 +    }
 +    else
 +    {
 +        // Determine automatically
 +
 +        // haleyjd 08/23/10: Removed Chex mode, as it is irrelevant to Strife
 +        if (gamemode == shareware || gamemode == registered)
 +        {
 +            // original
 +
 +            gameversion = exe_doom_1_9;
 +        }
 +        else if (gamemode == retail)
 +        {
 +            gameversion = exe_ultimate;
 +        }
 +        else if (gamemode == commercial)
 +        {
 +            if (gamemission == doom2)
 +            {
 +                gameversion = exe_doom_1_9;
 +            }
 +            else
 +            {
 +                // Final Doom: tnt or plutonia
 +
 +                gameversion = exe_final;
 +            }
 +        }
 +    }
 +
 +    // The original exe does not support retail - 4th episode not supported
 +
 +    if (gameversion < exe_ultimate && gamemode == retail)
 +    {
 +        gamemode = registered;
 +    }
 +
 +    // EXEs prior to the Final Doom exes do not support Final Doom.
 +
 +    if (gameversion < exe_final && gamemode == commercial)
 +    {
 +        gamemission = doom2;
 +    }
 +}
 +
 +void PrintGameVersion(void)
 +{
 +    int i;
 +
 +    for (i=0; gameversions[i].description != NULL; ++i)
 +    {
 +        if (gameversions[i].version == gameversion)
 +        {
 +            printf("Emulating the behavior of the "
 +                   "'%s' executable.\n", gameversions[i].description);
 +            break;
 +        }
 +    }
 +}
 +
 +// Load the Chex Quest dehacked file, if we are in Chex mode.
 +// haleyjd 08/23/2010: Removed, as irrelevant to Strife.
 +/*
 +static void LoadChexDeh(void)
 +{
 +    char *chex_deh;
 +
 +    if (gameversion == exe_chex)
 +    {
 +        chex_deh = D_FindWADByName("chex.deh");
 +
 +        if (chex_deh == NULL)
 +        {
 +            I_Error("Unable to find Chex Quest dehacked file (chex.deh).\n"
 +                    "The dehacked file is required in order to emulate\n"
 +                    "chex.exe correctly.  It can be found in your nearest\n"
 +                    "/idgames repository mirror at:\n\n"
 +                    "   utils/exe_edit/patches/chexdeh.zip");
 +        }
 +
 +        if (!DEH_LoadFile(chex_deh))
 +        {
 +            I_Error("Failed to load chex.deh needed for emulating chex.exe.");
 +        }
 +    }
 +}
 +*/
 +
 +// Function called at exit to display the ENDOOM screen
 +
 +static void D_Endoom(void)
 +{
 +    byte *endoom;
 +
 +    // Don't show ENDOOM if we have it disabled, or we're running
 +    // in screensaver or control test mode.
 +
 +    if (!show_endoom || screensaver_mode || M_CheckParm("-testcontrols") > 0)
 +    {
 +        return;
 +    }
 +
 +    // haleyjd 08/27/10: [STRIFE] ENDOOM -> ENDSTRF
 +    endoom = W_CacheLumpName(DEH_String("ENDSTRF"), PU_STATIC);
 +
 +    I_Endoom(endoom);
 +}
 +
 +//=============================================================================
 +//
 +// haleyjd: Chocolate Strife Specifics
 +//
 +// None of the code in here is from the original executable, but is needed for
 +// other reasons.
 +
 +//
 +// D_PatchClipCallback
 +//
 +// haleyjd 08/28/10: Clip patches to the framebuffer without errors.
 +// Returns false if V_DrawPatch should return without drawing.
 +//
 +boolean D_PatchClipCallback(patch_t *patch, int x, int y)
 +{
 +    // note that offsets were already accounted for in V_DrawPatch
 +    return (x >= 0 && y >= 0
 +            && x + SHORT(patch->width) <= SCREENWIDTH
 +            && y + SHORT(patch->height) <= SCREENHEIGHT);
 +}
 +
 +//
 +// D_InitChocoStrife
 +//
 +// haleyjd 08/28/10: Take care of some Strife-specific initialization
 +// that is necessitated by Chocolate Doom issues, such as setting global
 +// callbacks.
 +//
 +static void D_InitChocoStrife(void)
 +{
 +    // set the V_DrawPatch clipping callback
 +    V_SetPatchClipCallback(D_PatchClipCallback);
 +}
 +
 +
 +//
 +// STRIFE Graphical Intro Sequence
 +//
 +
 +#define MAXINTROPROGRESS 69
 +
 +static int introprogress;        // track the progress of the intro
 +
 +static byte *rawgfx_startup0;    // raw linear gfx for intro
 +static byte *rawgfx_startp[4];
 +static byte *rawgfx_startlz[2];
 +static byte *rawgfx_startbot;
 +
 +//
 +// D_IntroBackground
 +//
 +// [STRIFE] New function
 +// haleyjd 20110206: Strife only drew this once, but for supporting double-
 +// buffered or page-flipped surfaces it is best to redraw the entire screen
 +// every frame.
 +//
 +static void D_IntroBackground(void)
 +{
 +    patch_t *panel0;
 +
 +    if(!showintro)
 +        return;
 +
 +    // Slam up PANEL0 to fill the background entirely (wasn't needed in vanilla)
 +    panel0 = W_CacheLumpName("PANEL0", PU_CACHE);
 +    V_DrawPatch(0, 0, panel0);
 +
 +    // Strife cleared the screen somewhere in the low-level code between the
 +    // intro and the titlescreen, so this is to take care of that and get
 +    // proper fade-in behavior on the titlescreen
 +    if(introprogress >= MAXINTROPROGRESS)
 +    {
 +        I_FinishUpdate();
 +        return;
 +    }
 +
 +    // Draw a 95-pixel rect from STARTUP0 starting at y=57 to (0,41) on the
 +    // screen (this was a memcpy directly to 0xA3340 in low DOS memory)
 +    V_DrawBlock(0, 41, 320, 95, rawgfx_startup0 + (320*57));
 +}
 +
 +//
 +// D_InitIntroSequence
 +//
 +// [STRIFE] New function
 +// haleyjd 20110206: Initialize the graphical introduction sequence
 +//
 +static void D_InitIntroSequence(void)
 +{
 +    if(showintro)
 +    {
 +        // In vanilla Strife, Mode 13h was initialized directly in D_DoomMain.
 +        // We have to be a little more courteous of the low-level code here.
 +        I_SetWindowTitle(gamedescription);
 +        I_InitGraphics();
 +        V_RestoreBuffer(); // make the V_ routines work
 +
 +        // Load all graphics
 +        rawgfx_startup0   = W_CacheLumpName("STARTUP0", PU_STATIC);
 +        rawgfx_startp[0]  = W_CacheLumpName("STRTPA1",  PU_STATIC);
 +        rawgfx_startp[1]  = W_CacheLumpName("STRTPB1",  PU_STATIC);
 +        rawgfx_startp[2]  = W_CacheLumpName("STRTPC1",  PU_STATIC);
 +        rawgfx_startp[3]  = W_CacheLumpName("STRTPD1",  PU_STATIC);
 +        rawgfx_startlz[0] = W_CacheLumpName("STRTLZ1",  PU_STATIC);
 +        rawgfx_startlz[1] = W_CacheLumpName("STRTLZ2",  PU_STATIC);
 +        rawgfx_startbot   = W_CacheLumpName("STRTBOT",  PU_STATIC);
 +
 +        // Draw the background
 +        D_IntroBackground();
 +    }
 +    /*
 +    // STRIFE-FIXME: This was actually displayed on a special textmode
 +    // screen with ANSI color codes...
 +    else
 +    {
 +        puts(DEH_String("Conversation ON"));
 +        puts(DEH_String("Rogue Entertainment"));
 +        puts(DEH_String("and"));
 +        puts(DEH_String("Velocity Games"));
 +        puts(DEH_String("present"));
 +        puts(DEH_String("S T R I F E"));
 +        puts(DEH_String("Loading..."));
 +    }
 +    */
 +}
 +
 +//
 +// D_DrawIntroSequence
 +//
 +// [STRIFE] New function
 +// haleyjd 20110206: Refresh the intro sequence
 +//
 +static void D_DrawIntroSequence(void)
 +{
 +    int laserpos;
 +    int robotpos;
 +
 +    if(!showintro)
 +        return;
 +
 +    D_IntroBackground(); // haleyjd: refresh the background
 +
 +    // Laser position
 +    laserpos = (200 * introprogress / MAXINTROPROGRESS) + 60;
 +
 +    // BUG: (?) Due to this clip, the laser never even comes close to
 +    // touching the peasant; confirmed with vanilla. This MAY have been
 +    // intentional, for effect, however, since no death frames are shown
 +    // either... kind of a black-out death.
 +    if(laserpos > 200)
 +        laserpos = 200;
 +
 +    // Draw the laser
 +    // Blitted 16 bytes for 16 rows starting at 705280 + laserpos
 +    // (705280 - 0xA0000) / 320 == 156
 +    V_DrawBlock(laserpos, 156, 16, 16, rawgfx_startlz[laserpos % 2]);
 +
 +    // Robot position
 +    robotpos = laserpos % 5 - 2;
 +
 +    // Draw the robot
 +    // Blitted 48 bytes for 48 rows starting at 699534 + (320*robotpos)
 +    // 699534 - 0xA0000 == 44174, which % 320 == 14, / 320 == 138
 +    V_DrawBlock(14, 138 + robotpos, 48, 48, rawgfx_startbot);
 +
 +    // Draw the peasant
 +    // Blitted 32 bytes for 64 rows starting at 699142
 +    // 699142 - 0xA0000 == 43782, which % 320 == 262, / 320 == 136
 +    V_DrawBlock(262, 136, 32, 64, rawgfx_startp[laserpos % 4]);
 +
 +    I_FinishUpdate();
 +}
 +
 +//
 +// D_IntroTick
 +//
 +// Advance the intro sequence
 +//
 +void D_IntroTick(void)
 +{
 +    if(devparm)
 +        return;
 +
 +    ++introprogress;
 +    if(introprogress >= MAXINTROPROGRESS)
 +    {
 +        D_IntroBackground(); // haleyjd: clear the bg anyway
 +        S_StartSound(NULL, sfx_psdtha);
 +    }
 +    else
 +        D_DrawIntroSequence();
 +}
 +
 +//
 +// End Chocolate Strife Specifics
 +//
 +//=============================================================================
 +
 +//
 +// D_DoomMain
 +//
 +void D_DoomMain (void)
 +{
 +    int             p;
 +    char            file[256];
 +    char            demolumpname[9];
 +
 +    I_AtExit(D_Endoom, false);
 +
 +    M_FindResponseFile ();
 +
 +    // haleyjd 20110206 [STRIFE]: -nograph parameter
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Disable graphical introduction sequence
 +    //
 +
 +    if (M_CheckParm("-nograph") > 0)
 +        showintro = false;
 +
 +    // haleyjd 20110206: Moved up -devparm for max visibility
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Developer mode.  F1 saves a screenshot in the current working
 +    // directory.
 +    //
 +
 +    devparm = M_CheckParm ("-devparm");
 +
 +    // print banner
 +
 +    I_PrintBanner(PACKAGE_STRING);
 +
 +    //DEH_printf("Z_Init: Init zone memory allocation daemon. \n"); [STRIFE] removed
 +    Z_Init ();
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    //!
 +    // @category net
 +    //
 +    // Start a dedicated server, routing packets but not participating
 +    // in the game itself.
 +    //
 +
 +    if (M_CheckParm("-dedicated") > 0)
 +    {
 +        printf("Dedicated server mode.\n");
 +        NET_DedicatedServer();
 +
 +        // Never returns
 +    }
 +
++    //!
++    // @category net
++    //
++    // Query the Internet master server for a global list of active
++    // servers.
++    //
++
++    if (M_CheckParm("-search"))
++    {
++        printf("\nSearching for servers on Internet ...\n");
++        p = NET_MasterQuery(NET_QueryPrintCallback, NULL);
++        printf("\n%i server(s) found.\n", p);
++        exit(0);
++    }
++
 +    //!
 +    // @arg <address>
 +    // @category net
 +    //
 +    // Query the status of the server running on the given IP
 +    // address.
 +    //
 +
-     p = M_CheckParm("-query");
++    p = M_CheckParmWithArgs("-query", 1);
 +
-     if (p > 0)
++    if (p)
 +    {
 +        NET_QueryAddress(myargv[p+1]);
++        exit(0);
 +    }
 +
 +    //!
 +    // @category net
 +    //
 +    // Search the local LAN for running servers.
 +    //
 +
-     if (M_CheckParm("-search"))
-         NET_LANQuery();
++    if (M_CheckParm("-localsearch"))
++    {
++        printf("\nSearching for servers on local LAN ...\n");
++        p = NET_LANQuery(NET_QueryPrintCallback, NULL);
++        printf("\n%i server(s) found.\n", p);
++        exit(0);
++    }
 +
 +#endif
 +
 +#ifdef FEATURE_DEHACKED
 +    if(devparm)
 +        printf("DEH_Init: Init Dehacked support.\n");
 +    DEH_Init();
 +#endif
 +
 +    iwadfile = D_FindIWAD(IWAD_MASK_STRIFE, &gamemission);
 +
 +    // None found?
 +
 +    if (iwadfile == NULL)
 +    {
 +        I_Error("Game mode indeterminate.  No IWAD file was found.  Try\n"
 +                "specifying one with the '-iwad' command line parameter.\n");
 +    }
 +
 +    modifiedgame = false;
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Disable monsters.
 +    //
 +
 +    nomonsters = M_CheckParm ("-nomonsters");
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Monsters respawn after being killed.
 +    //
 +
 +    respawnparm = M_CheckParm ("-respawn");
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Monsters move faster.
 +    //
 +
 +    fastparm = M_CheckParm ("-fast");
 +
 +    I_DisplayFPSDots(devparm);
 +
 +    // haleyjd 20110206 [STRIFE]: -devparm implies -nograph
 +    if(devparm)
 +        showintro = false;
 +
 +    //!
 +    // @category net
 +    // @vanilla
 +    //
 +    // Start a deathmatch game.
 +    //
 +
 +    if (M_CheckParm ("-deathmatch"))
 +        deathmatch = 1;
 +
 +    //!
 +    // @category net
 +    // @vanilla
 +    //
 +    // Start a deathmatch 2.0 game.  Weapons do not stay in place and
 +    // all items respawn after 30 seconds.
 +    //
 +
 +    if (M_CheckParm ("-altdeath"))
 +        deathmatch = 2;
 +
 +    if (devparm)
 +        DEH_printf(D_DEVSTR);
 +
 +    // find which dir to use for config files
 +
 +#ifdef _WIN32
 +
 +    //!
 +    // @platform windows
 +    // @vanilla
 +    //
 +    // Save configuration data and savegames in c:\doomdata,
 +    // allowing play from CD.
 +    //
 +
 +    if (M_CheckParm("-cdrom") > 0)
 +    {
 +        printf(D_CDROM);
 +
 +        // haleyjd 08/22/2010: [STRIFE] Use strife.cd folder for -cdrom
 +        M_SetConfigDir("c:\\strife.cd\\");
 +    }
 +    else
 +#endif
 +    {
 +        // Auto-detect the configuration dir.
 +
 +        M_SetConfigDir(NULL);
 +    }
 +
 +    //!
 +    // @arg <x>
 +    // @vanilla
 +    //
 +    // Turbo mode.  The player's speed is multiplied by x%.  If unspecified,
 +    // x defaults to 200.  Values are rounded up to 10 and down to 400.
 +    //
 +
 +    if ( (p=M_CheckParm ("-turbo")) )
 +    {
 +        int     scale = 200;
 +        extern int forwardmove[2];
 +        extern int sidemove[2];
 +
 +        if (p<myargc-1)
 +            scale = atoi (myargv[p+1]);
 +        if (scale < 10)
 +            scale = 10;
 +        if (scale > 400)
 +            scale = 400;
 +        DEH_printf("turbo scale: %i%%\n", scale);
 +        forwardmove[0] = forwardmove[0]*scale/100;
 +        forwardmove[1] = forwardmove[1]*scale/100;
 +        sidemove[0] = sidemove[0]*scale/100;
 +        sidemove[1] = sidemove[1]*scale/100;
 +    }
 +
 +    // init subsystems
 +    // DEH_printf("V_Init: allocate screens.\n"); [STRIFE] removed
 +    V_Init ();
 +
 +    // Load configuration files before initialising other subsystems.
 +    // haleyjd 08/22/2010: [STRIFE] - use strife.cfg
 +    // DEH_printf("M_LoadDefaults: Load system defaults.\n"); [STRIFE] removed
 +    M_SetConfigFilenames("strife.cfg", PROGRAM_PREFIX "strife.cfg");
 +    D_BindVariables();
 +    M_LoadDefaults();
 +
 +    // Save configuration at exit.
 +    I_AtExit(M_SaveDefaults, false);
 +
 +    if(devparm) // [STRIFE] Devparm only
 +        DEH_printf("W_Init: Init WADfiles.\n");
 +    D_AddFile(iwadfile);
 +    modifiedgame = W_ParseCommandLine();
 +
 +    // [STRIFE] serial number output
 +    if(devparm)
 +    {
 +        char msgbuf[80];
 +        char *serial  = W_CacheLumpName("SERIAL", PU_CACHE);
 +        int serialnum = atoi(serial);
 +
 +        DEH_snprintf(msgbuf, sizeof(msgbuf), "Wad Serial Number: %d:", serialnum);
 +        printf("%s\n", msgbuf);
 +    }
 +
 +    // add any files specified on the command line with -file wadfile
 +    // to the wad list
 +    //
-     // convenience hack to allow -wart e m to add a wad file
-     // prepend a tilde to the filename so wadfile will be reloadable
-     p = M_CheckParm ("-wart");
-     if (p)
-     {
-         myargv[p][4] = 'p';     // big hack, change to -warp
 +
-         // Map name handling.
-         // [STRIFE]: looks for f:/st/data
-         p = atoi (myargv[p+1]);
-         if (p<10)
-             sprintf (file,"~f:/st/data/map0%i.wad", p);
-         else
-             sprintf (file,"~f:/st/data/map%i.wad", p);
-
-         D_AddFile (file);
-     }
++    // Debug:
++//    W_PrintDirectory();
 +
 +    //!
 +    // @arg <demo>
 +    // @category demo
 +    // @vanilla
 +    //
 +    // Play back the demo named demo.lmp.
 +    //
 +
-     p = M_CheckParm ("-playdemo");
++    p = M_CheckParmWithArgs ("-playdemo", 1);
 +
 +    if (!p)
 +    {
 +        //!
 +        // @arg <demo>
 +        // @category demo
 +        // @vanilla
 +        //
 +        // Play back the demo named demo.lmp, determining the framerate
 +        // of the screen.
 +        //
-         p = M_CheckParm ("-timedemo");
++	p = M_CheckParmWithArgs("-timedemo", 1);
 +
 +    }
 +
-     if (p && p < myargc-1)
++    if (p)
 +    {
 +        if (!strcasecmp(myargv[p+1] + strlen(myargv[p+1]) - 4, ".lmp"))
 +        {
 +            strcpy(file, myargv[p + 1]);
 +        }
 +        else
 +        {
 +            sprintf (file,"%s.lmp", myargv[p+1]);
 +        }
 +
 +        if (D_AddFile (file))
 +        {
 +            strncpy(demolumpname, lumpinfo[numlumps - 1].name, 8);
 +            demolumpname[8] = '\0';
 +
 +            printf("Playing demo %s.\n", file);
 +        }
 +        else
 +        {
 +            // If file failed to load, still continue trying to play
 +            // the demo in the same way as Vanilla Doom.  This makes
 +            // tricks like "-playdemo demo1" possible.
 +
 +            strncpy(demolumpname, myargv[p + 1], 8);
 +            demolumpname[8] = '\0';
 +        }
 +
 +    }
 +
 +    I_AtExit((atexit_func_t) G_CheckDemoStatus, true);
 +
 +    // Generate the WAD hash table.  Speed things up a bit.
 +
 +    W_GenerateHashTable();
 +
 +    D_IdentifyVersion();
 +    InitGameVersion();
 +    //LoadChexDeh(); - haleyjd: removed, as irrelevant to Strife
 +    D_SetGameDescription();
 +    SetSaveGameDir(iwadfile);
 +
 +    // haleyjd 20110206 [STRIFE] Startup the introduction sequence
 +    D_InitIntroSequence();
 +    D_IntroTick();
 +
 +    // Check for -file in shareware
 +    if (modifiedgame)
 +    {
 +        // These are the lumps that will be checked in IWAD,
 +        // if any one is not present, execution will be aborted.
 +        // haleyjd 08/22/2010: [STRIFE] Check for Strife lumps.
 +        char name[3][8]=
 +        {
 +            "map23", "map30", "ROB3E1"
 +        };
 +        int i;
 +
 +        // haleyjd 08/22/2010: [STRIFE] Changed string to match binary
 +        // STRIFE-FIXME: Needs to test isdemoversion variable
 +        if ( gamemode == shareware)
 +            I_Error(DEH_String("\nYou cannot -file with the demo "
 +                               "version. You must buy the real game!"));
 +
 +        // Check for fake IWAD with right name,
 +        // but w/o all the lumps of the registered version.
 +        // STRIFE-FIXME: Needs to test isregistered variable
 +        if (gamemode == registered)
 +            for (i = 0; i < 3; i++)
 +                if (W_CheckNumForName(name[i])<0)
 +                    I_Error(DEH_String("\nThis is not the registered version."));
 +    }
 +
 +    D_IntroTick(); // [STRIFE]
 +
 +    // get skill / episode / map from parms
 +    startskill = sk_medium;
 +    startepisode = 1;
 +    startmap = 1;
 +    autostart = false;
 +
 +    //!
 +    // @arg <skill>
 +    // @vanilla
 +    //
 +    // Set the game skill, 1-5 (1: easiest, 5: hardest).  A skill of
 +    // 0 disables all monsters.
 +    //
 +
-     p = M_CheckParm ("-skill");
++    p = M_CheckParmWithArgs("-skill", 1);
 +
-     if (p && p < myargc-1)
++    if (p)
 +    {
 +        startskill = myargv[p+1][0]-'1';
 +        autostart = true;
 +    }
 +
 +    //!
 +    // @arg <n>
 +    // @vanilla
 +    //
 +    // Start playing on episode n (1-4)
 +    //
 +
-     p = M_CheckParm ("-episode");
++    p = M_CheckParmWithArgs("-episode", 1);
 +
-     if (p && p < myargc-1)
++    if (p)
 +    {
 +        startepisode = myargv[p+1][0]-'0';
 +        startmap = 1;
 +        autostart = true;
 +    }
 +
 +    timelimit = 0;
 +
 +    //!
 +    // @arg <n>
 +    // @category net
 +    // @vanilla
 +    //
 +    // For multiplayer games: exit each level after n minutes.
 +    //
 +
-     p = M_CheckParm ("-timer");
++    p = M_CheckParmWithArgs("-timer", 1);
 +
-     if (p && p < myargc-1 && deathmatch)
++    if (p)
 +    {
 +        timelimit = atoi(myargv[p+1]);
 +        printf("timer: %i\n", timelimit);
 +    }
 +
 +    //!
 +    // @category net
 +    // @vanilla
 +    //
 +    // Austin Virtual Gaming: end levels after 20 minutes.
 +    //
 +
 +    p = M_CheckParm ("-avg");
 +
-     if (p && p < myargc-1 && deathmatch)
++    if (p)
 +    {
-         DEH_printf("Austin Virtual Gaming: Levels will end "
-                        "after 20 minutes\n");
 +        timelimit = 20;
 +    }
 +
 +    //!
 +    // @arg [<x> <y> | <xy>]
 +    // @vanilla
 +    //
 +    // Start a game immediately, warping to ExMy (Doom 1) or MAPxy
 +    // (Doom 2)
 +    //
 +
-     p = M_CheckParm ("-warp");
++    p = M_CheckParmWithArgs("-warp", 1);
 +
-     if (p && p < myargc-1)
++    if (p)
 +    {
 +        if (gamemode == commercial)
 +            startmap = atoi (myargv[p+1]);
 +        else
 +        {
 +            startepisode = myargv[p+1][0]-'0';
 +
 +            if (p + 2 < myargc)
 +            {
 +                startmap = myargv[p+2][0]-'0';
 +            }
 +            else
 +            {
 +                startmap = 1;
 +            }
 +        }
 +        autostart = true;
 +    }
 +
 +    // Undocumented:
 +    // Invoked by setup to test the controls.
 +
 +    p = M_CheckParm("-testcontrols");
 +
 +    if (p > 0)
 +    {
 +        startepisode = 1;
 +        startmap = 1;
 +        autostart = true;
 +        testcontrols = true;
 +    }
 +
 +    // Check for load game parameter
 +    // We do this here and save the slot number, so that the network code
 +    // can override it or send the load slot to other players.
 +
 +    //!
 +    // @arg <s>
 +    // @vanilla
 +    //
 +    // Load the game in slot s.
 +    //
 +
-     p = M_CheckParm ("-loadgame");
++    p = M_CheckParmWithArgs("-loadgame", 1);
 +
-     if (p && p < myargc-1)
++    if (p)
 +    {
 +        startloadgame = atoi(myargv[p+1]);
 +    }
 +    else
 +    {
 +        // Not loading a game
 +        startloadgame = -1;
 +    }
 +
 +    if (W_CheckNumForName("SS_START") >= 0
 +     || W_CheckNumForName("FF_END") >= 0)
 +    {
 +        I_PrintDivider();
 +        printf(" WARNING: The loaded WAD file contains modified sprites or\n"
 +               " floor textures.  You may want to use the '-merge' command\n"
 +               " line option instead of '-file'.\n");
 +    }
 +
 +    I_PrintStartupBanner(gamedescription);
 +    PrintDehackedBanners();
 +
 +    // haleyjd 08/28/10: Init Choco Strife stuff.
 +    D_InitChocoStrife();
 +
 +    // haleyjd 08/22/2010: [STRIFE] Modified string to match binary
 +    if(devparm) // [STRIFE]
 +       DEH_printf("R_Init: Loading Graphics - ");
 +    R_Init ();
 +    D_IntroTick(); // [STRIFE]
 +
 +    if(devparm) // [STRIFE]
 +        DEH_printf("\nP_Init: Init Playloop state.\n");
 +    P_Init ();
 +    D_IntroTick(); // [STRIFE]
 +
 +    if(devparm) // [STRIFE]
 +        DEH_printf("I_Init: Setting up machine state.\n");
 +    I_CheckIsScreensaver();
 +    I_InitTimer();
 +    I_InitJoystick();
 +    D_IntroTick(); // [STRIFE]
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    if(devparm) // [STRIFE]
 +        printf ("NET_Init: Init network subsystem.\n");
 +    NET_Init ();
 +#endif
 +    D_IntroTick(); // [STRIFE]
 +
 +    if(devparm) // [STRIFE]
 +        DEH_printf("M_Init: Init Menu.\n");
 +    M_Init ();
 +    D_IntroTick(); // [STRIFE]
 +
 +    if(devparm) // [STRIFE]
 +        DEH_printf("S_Init: Setting up sound.\n");
 +    S_Init (sfxVolume * 8, musicVolume * 8, voiceVolume * 8); // [STRIFE]: voice
 +    D_IntroTick(); // [STRIFE]
 +
 +    if(devparm) // [STRIFE]
 +        DEH_printf("D_CheckNetGame: Checking network game status.\n");
 +    D_CheckNetGame ();
 +
 +    PrintGameVersion();
 +
 +    if(devparm)
 +        DEH_printf("HU_Init: Setting up heads up display.\n");
 +    HU_Init ();
 +    D_IntroTick(); // [STRIFE]
 +
 +    if(devparm)
 +        DEH_printf("ST_Init: Init status bar.\n");
 +    ST_Init ();
 +    D_IntroTick(); // [STRIFE]
 +
 +    // haleyjd [STRIFE] -statcopy used to be here...
 +    D_IntroTick();
 +
 +    // If Doom II without a MAP01 lump, this is a store demo.
 +    // Moved this here so that MAP01 isn't constantly looked up
 +    // in the main loop.
 +    // haleyjd 08/23/2010: [STRIFE] There is no storedemo version of Strife
 +    /*
 +    if (gamemode == commercial && W_CheckNumForName("map01") < 0)
 +        storedemo = true;
 +    */
 +
 +    //!
 +    // @arg <x>
 +    // @category demo
 +    // @vanilla
 +    //
 +    // Record a demo named x.lmp.
 +    //
 +
-     p = M_CheckParm ("-record");
++    p = M_CheckParmWithArgs("-record", 1);
 +
-     if (p && p < myargc-1)
++    if (p)
 +    {
 +        G_RecordDemo (myargv[p+1]);
 +        autostart = true;
 +    }
 +    D_IntroTick(); // [STRIFE]
 +
-     p = M_CheckParm ("-playdemo");
-     if (p && p < myargc-1)
++    p = M_CheckParmWithArgs("-playdemo", 1);
++    if (p)
 +    {
 +        singledemo = true;              // quit after one demo
 +        G_DeferedPlayDemo (demolumpname);
 +        D_DoomLoop ();  // never returns
 +    }
 +    D_IntroTick(); // [STRIFE]
 +
-     p = M_CheckParm ("-timedemo");
-     if (p && p < myargc-1)
++    p = M_CheckParmWithArgs("-timedemo", 1);
++    if (p)
 +    {
 +        G_TimeDemo (demolumpname);
 +        D_DoomLoop ();  // never returns
 +    }
 +    D_IntroTick(); // [STRIFE]
 +
 +    if (startloadgame >= 0)
 +    {
 +        // [STRIFE]: different, for hubs
 +        M_LoadSelect(startloadgame);
 +    }
 +    D_IntroTick(); // [STRIFE]
 +
 +    if (gameaction != ga_loadgame )
 +    {
 +        if (autostart || netgame)
 +            G_InitNew (startskill, startmap);
 +        else
 +            D_StartTitle ();                // start up intro loop
 +    }
 +
 +    D_DoomLoop ();  // never returns
 +}
 +
diff --cc src/strife/d_net.c
index 3480b0ad,00000000..7e1644e8
mode 100644,000000..100644
--- a/src/strife/d_net.c
+++ b/src/strife/d_net.c
@@@ -1,628 -1,0 +1,635 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	DOOM Network game communication and protocol,
 +//	all OS independend parts.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +
 +#include "doomfeatures.h"
 +
 +#include "d_main.h"
 +#include "m_argv.h"
 +#include "m_menu.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "g_game.h"
 +#include "doomdef.h"
 +#include "doomstat.h"
 +
 +#include "deh_main.h"
 +
 +#include "net_client.h"
 +#include "net_gui.h"
 +#include "net_io.h"
 +#include "net_query.h"
 +#include "net_server.h"
 +#include "net_sdl.h"
 +#include "net_loop.h"
 +
-
 +//
 +// 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
 +//
 +
 +ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
 +int             nettics[MAXPLAYERS];
 +
 +int             maketic;
 +
 +// Used for original sync code.
 +
 +int		lastnettic;
 +int             skiptics = 0;
 +
 +// Reduce the bandwidth needed by sampling game input less and transmitting
 +// less.  If ticdup is 2, sample half normal, 3 = one third normal, etc.
 +
 +int		ticdup;
 +
 +// Send this many extra (backup) tics in each packet.
 +
 +int             extratics;
 +
 +// Amount to offset the timer for game sync.
 +
 +fixed_t         offsetms;
 +
 +// Use new client syncronisation code
 +
 +boolean         net_cl_new_sync = true;
 +
 +// Connected but not participating in the game (observer)
 +
 +boolean drone = false;
 +
 +// 35 fps clock adjusted by offsetms milliseconds
 +
 +static int GetAdjustedTime(void)
 +{
 +    int time_ms;
 +
 +    time_ms = I_GetTimeMS();
 +
 +    if (net_cl_new_sync)
 +    {
 +	// Use the adjustments from net_client.c only if we are
 +	// using the new sync mode.
 +
 +        time_ms += (offsetms / FRACUNIT);
 +    }
 +
 +    return (time_ms * TICRATE) / 1000;
 +}
 +
 +//
 +// NetUpdate
 +// Builds ticcmds for console player,
 +// sends out a packet
 +//
 +int      lasttime;
 +
 +void NetUpdate (void)
 +{
 +    int nowtime;
 +    int newtics;
 +    int	i;
 +    int	gameticdiv;
 +
 +    // If we are running with singletics (timing a demo), this
 +    // is all done separately.
 +
 +    if (singletics)
 +        return;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +
 +    // Run network subsystems
 +
 +    NET_CL_Run();
 +    NET_SV_Run();
 +
 +#endif
 +
 +    // check time
 +    nowtime = GetAdjustedTime() / ticdup;
 +    newtics = nowtime - lasttime;
 +
 +    lasttime = nowtime;
 +
 +    if (skiptics <= newtics)
 +    {
 +        newtics -= skiptics;
 +        skiptics = 0;
 +    }
 +    else
 +    {
 +        skiptics -= newtics;
 +        newtics = 0;
 +    }
 +
 +    // build new ticcmds for console player
 +    gameticdiv = gametic/ticdup;
 +
 +    for (i=0 ; i<newtics ; i++)
 +    {
 +        ticcmd_t cmd;
 +
 +	I_StartTic ();
 +	D_ProcessEvents ();
 +
 +        // Always run the menu
 +
 +        M_Ticker ();
 +
 +        if (drone)
 +        {
 +            // In drone mode, do not generate any ticcmds.
 +
 +            continue;
 +        }
 +
 +        if (net_cl_new_sync)
 +        {
 +           // If playing single player, do not allow tics to buffer
 +           // up very far
 +
 +           if ((!netgame || demoplayback) && maketic - gameticdiv > 2)
 +               break;
 +
 +           // Never go more than ~200ms ahead
 +
 +           if (maketic - gameticdiv > 8)
 +               break;
 +        }
 +	else
 +	{
 +           if (maketic - gameticdiv >= 5)
 +               break;
 +	}
 +
 +	//printf ("mk:%i ",maketic);
 +	G_BuildTiccmd(&cmd);
 +
 +#ifdef FEATURE_MULTIPLAYER
 +
 +        if (netgame && !demoplayback)
 +        {
 +            NET_CL_SendTiccmd(&cmd, maketic);
 +        }
 +
 +#endif
 +        netcmds[consoleplayer][maketic % BACKUPTICS] = cmd;
 +
 +	++maketic;
 +        nettics[consoleplayer] = maketic;
 +    }
 +}
 +
 +//
 +// Start game loop
 +//
 +// Called after the screen is set but before the game starts running.
 +//
 +
 +void D_StartGameLoop(void)
 +{
 +    lasttime = GetAdjustedTime() / ticdup;
 +}
 +
 +
 +//
 +// D_CheckNetGame
 +// Works out player numbers among the net participants
 +//
 +extern	int			viewangleoffset;
 +
 +void D_CheckNetGame (void)
 +{
 +    int i;
 +    int num_players;
 +
 +    // Call D_QuitNetGame on exit
 +
 +    I_AtExit(D_QuitNetGame, true);
 +
 +    // default values for single player
 +
 +    consoleplayer = 0;
 +    netgame = false;
 +    ticdup = 1;
 +    extratics = 1;
 +    lowres_turn = false;
 +    offsetms = 0;
 +
 +    for (i=0; i<MAXPLAYERS; i++)
 +    {
 +        playeringame[i] = false;
 +        nettics[i] = 0;
 +    }
 +
 +    playeringame[0] = true;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +
 +    {
 +        net_addr_t *addr = NULL;
 +
 +        //!
 +        // @category net
 +        //
 +        // Start a multiplayer server, listening for connections.
 +        //
 +
 +        if (M_CheckParm("-server") > 0)
 +        {
 +            NET_SV_Init();
 +            NET_SV_AddModule(&net_loop_server_module);
 +            NET_SV_AddModule(&net_sdl_module);
 +
 +            net_loop_client_module.InitClient();
 +            addr = net_loop_client_module.ResolveAddress(NULL);
 +        }
 +        else
 +        {
 +            //!
 +            // @category net
 +            //
 +            // Automatically search the local LAN for a multiplayer
 +            // server and join it.
 +            //
 +
 +            i = M_CheckParm("-autojoin");
 +
 +            if (i > 0)
 +            {
 +                addr = NET_FindLANServer();
++                NET_SV_RegisterWithMaster();
 +
 +                if (addr == NULL)
 +                {
 +                    I_Error("No server found on local LAN");
 +                }
 +            }
 +
 +            //!
 +            // @arg <address>
 +            // @category net
 +            //
 +            // Connect to a multiplayer server running on the given
 +            // address.
 +            //
 +
-             i = M_CheckParm("-connect");
++            i = M_CheckParmWithArgs("-connect", 1);
 +
 +            if (i > 0)
 +            {
 +                net_sdl_module.InitClient();
 +                addr = net_sdl_module.ResolveAddress(myargv[i+1]);
 +
 +                if (addr == NULL)
 +                {
 +                    I_Error("Unable to resolve '%s'\n", myargv[i+1]);
 +                }
 +            }
 +        }
 +
 +        if (addr != NULL)
 +        {
 +            if (M_CheckParm("-drone") > 0)
 +            {
 +                drone = true;
 +            }
 +
 +            //!
 +            // @category net
 +            //
 +            // Run as the left screen in three screen mode.
 +            //
 +
 +            if (M_CheckParm("-left") > 0)
 +            {
 +                viewangleoffset = ANG90;
 +                drone = true;
 +            }
 +
 +            //!
 +            // @category net
 +            //
 +            // Run as the right screen in three screen mode.
 +            //
 +
 +            if (M_CheckParm("-right") > 0)
 +            {
 +                viewangleoffset = ANG270;
 +                drone = true;
 +            }
 +
 +            if (!NET_CL_Connect(addr))
 +            {
 +                I_Error("D_CheckNetGame: Failed to connect to %s\n",
 +                        NET_AddrToString(addr));
 +            }
 +
 +            printf("D_CheckNetGame: Connected to %s\n", NET_AddrToString(addr));
 +
 +            NET_WaitForStart();
 +        }
 +    }
 +
 +#endif
 +
 +    num_players = 0;
 +
 +    for (i=0; i<MAXPLAYERS; ++i)
 +    {
 +        if (playeringame[i])
 +            ++num_players;
 +    }
 +
 +    DEH_printf("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n",
 +               startskill, deathmatch, startmap, startepisode);
 +
 +    DEH_printf("player %i of %i (%i nodes)\n",
 +               consoleplayer+1, num_players, num_players);
 +
 +    // Show players here; the server might have specified a time limit
 +
-     if (timelimit > 0)
++    if (timelimit > 0 && deathmatch)
 +    {
- 	DEH_printf("Levels will end after %d minute", timelimit);
- 	if (timelimit > 1)
- 	    printf("s");
- 	printf(".\n");
++        // Gross hack to work like Vanilla:
++
++        if (timelimit == 20 && M_CheckParm("-avg"))
++        {
++            DEH_printf("Austin Virtual Gaming: Levels will end "
++                           "after 20 minutes\n");
++        }
++        else
++        {
++            DEH_printf("Levels will end after %d minute", timelimit);
++            if (timelimit > 1)
++                printf("s");
++            printf(".\n");
++        }
 +    }
 +}
 +
 +
 +//
 +// D_QuitNetGame
 +// Called before quitting to leave a net game
 +// without hanging the other players
 +//
 +void D_QuitNetGame (void)
 +{
-     if (debugfile)
- 	fclose (debugfile);
-
 +#ifdef FEATURE_MULTIPLAYER
 +
 +    NET_SV_Shutdown();
 +    NET_CL_Disconnect();
 +
 +#endif
 +
 +}
 +
 +// Returns true if there are currently any players in the game.
 +
 +static boolean PlayersInGame(void)
 +{
 +    int i;
 +
 +    for (i=0; i<MAXPLAYERS; ++i)
 +    {
 +        if (playeringame[i])
 +        {
 +            return true;
 +        }
 +    }
 +
 +    return false;
 +}
 +
 +static int GetLowTic(void)
 +{
 +    int lowtic;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    if (net_client_connected)
 +    {
 +        int i;
 +
 +        lowtic = INT_MAX;
 +
 +        for (i=0; i<MAXPLAYERS; ++i)
 +        {
 +            if (playeringame[i])
 +            {
 +                if (nettics[i] < lowtic)
 +                    lowtic = nettics[i];
 +            }
 +        }
 +    }
 +    else
 +#endif
 +    {
 +        lowtic = maketic;
 +    }
 +
 +    return lowtic;
 +}
 +
 +//
 +// TryRunTics
 +//
 +int	oldnettics;
 +int	frametics[4];
 +int	frameon;
 +int	frameskip[4];
 +int	oldnettics;
 +
 +extern	boolean	advancedemo;
 +
 +void TryRunTics (void)
 +{
 +    int	i;
 +    int	lowtic;
 +    int	entertic;
 +    static int oldentertics;
 +    int realtics;
 +    int	availabletics;
 +    int	counts;
 +
 +    // get real tics
 +    entertic = I_GetTime() / ticdup;
 +    realtics = entertic - oldentertics;
 +    oldentertics = entertic;
 +
 +    // get available tics
 +    NetUpdate ();
 +
 +    lowtic = GetLowTic();
 +
 +    availabletics = lowtic - gametic/ticdup;
 +
 +    // decide how many tics to run
 +
 +    if (net_cl_new_sync)
 +    {
 +	counts = availabletics;
 +    }
 +    else
 +    {
 +        // 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 (!demoplayback)
 +        {
 +	    int keyplayer = -1;
 +
 +            // ideally maketic should be 1 - 3 tics above lowtic
 +            // if we are consistantly slower, speed up time
 +
 +            for (i=0 ; i<MAXPLAYERS ; i++)
 +	    {
 +                if (playeringame[i])
 +		{
 +		    keyplayer = i;
 +                    break;
 +		}
 +	    }
 +
 +	    if (keyplayer < 0)
 +	    {
 +		// If there are no players, we can never advance anyway
 +
 +		return;
 +	    }
 +
 +            if (consoleplayer == keyplayer)
 +            {
 +                // the key player does not adapt
 +            }
 +            else
 +            {
 +                if (maketic <= nettics[keyplayer])
 +                {
 +                    lasttime--;
 +                    // printf ("-");
 +                }
 +
 +                frameskip[frameon & 3] = (oldnettics > nettics[keyplayer]);
 +                oldnettics = maketic;
 +
 +                if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
 +                {
 +                    skiptics = 1;
 +                    // printf ("+");
 +                }
 +            }
 +        }
 +    }
 +
 +    if (counts < 1)
 +	counts = 1;
 +
 +    // wait for new tics if needed
 +
 +    while (!PlayersInGame() || lowtic < gametic/ticdup + counts)
 +    {
 +	NetUpdate ();
 +
 +        lowtic = GetLowTic();
 +
 +	if (lowtic < gametic/ticdup)
 +	    I_Error ("TryRunTics: lowtic < gametic");
 +
 +        // Don't stay in this loop forever.  The menu is still running,
 +        // so return to update the screen
 +
 +	if (I_GetTime() / ticdup - entertic > 0)
 +	{
 +	    return;
 +	}
 +
 +        I_Sleep(1);
 +    }
 +
 +    // run the count * ticdup dics
 +    while (counts--)
 +    {
 +	for (i=0 ; i<ticdup ; i++)
 +	{
 +            // check that there are players in the game.  if not, we cannot
 +            // run a tic.
 +
 +            if (!PlayersInGame())
 +            {
 +                return;
 +            }
 +
 +	    if (gametic/ticdup > lowtic)
 +		I_Error ("gametic>lowtic");
 +	    if (advancedemo)
 +		D_DoAdvanceDemo ();
 +
 +	    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 --cc src/strife/doomstat.h
index ae513f78,00000000..8cb562b2
mode 100644,000000..100644
--- a/src/strife/doomstat.h
+++ b/src/strife/doomstat.h
@@@ -1,306 -1,0 +1,305 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//   All the global variables that store the internal state.
 +//   Theoretically speaking, the internal state of the engine
 +//    should be found by looking at the variables collected
 +//    here, and every relevant module will have to include
 +//    this header file.
 +//   In practice, things are a bit messy.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#ifndef __D_STATE__
 +#define __D_STATE__
 +
 +// We need globally shared data structures,
 +//  for defining the global state variables.
 +#include "doomdata.h"
 +#include "d_net.h"
 +
 +// We need the playr data structure as well.
 +#include "d_player.h"
 +
 +// Game mode/mission
 +#include "d_mode.h"
 +
 +#include "net_defs.h"
 +
 +
 +
 +// ------------------------
 +// Command line parameters.
 +//
 +extern  boolean	nomonsters;	// checkparm of -nomonsters
 +extern  boolean	respawnparm;	// checkparm of -respawn
 +extern  boolean	fastparm;	// checkparm of -fast
 +
 +extern  boolean	devparm;	// DEBUG: launched with -devparm
 +
 +
 +// -----------------------------------------------------
 +// Game Mode - identify IWAD as shareware, retail etc.
 +//
 +extern GameMode_t	gamemode;
 +extern GameMission_t	gamemission;
 +extern GameVersion_t    gameversion;
 +extern char            *gamedescription;
 +
 +// Set if homebrew PWAD stuff has been added.
 +extern  boolean	modifiedgame;
 +
 +
 +// -------------------------------------------
 +// Selected skill type, map etc.
 +//
 +
 +// Defaults for menu, methinks.
 +extern  skill_t		startskill;
 +extern  int             startepisode;
 +extern	int		startmap;
 +
 +// Savegame slot to load on startup.  This is the value provided to
 +// the -loadgame option.  If this has not been provided, this is -1.
 +
 +extern  int             startloadgame;
 +
 +extern  boolean		autostart;
 +
 +// Selected by user.
 +extern  skill_t         gameskill;
 +extern  int		gameepisode;
 +extern  int		gamemap;
 +
 +// If non-zero, exit the level after this number of minutes
 +extern  int             timelimit;
 +
 +// Nightmare mode flag, single player.
 +extern  boolean         respawnmonsters;
 +
 +// Netgame? Only true if >1 player.
 +extern  boolean	netgame;
 +
 +// Flag: true only if started as net deathmatch.
 +// An enum might handle altdeath/cooperative better.
 +extern  boolean	deathmatch;
 +
 +// -------------------------
 +// Internal parameters for sound rendering.
 +// These have been taken from the DOS version,
 +//  but are not (yet) supported with Linux
 +//  (e.g. no sound volume adjustment with menu.
 +
 +// From m_menu.c:
 +//  Sound FX volume has default, 0 - 15
 +//  Music volume has default, 0 - 15
 +// These are multiplied by 8.
 +extern int sfxVolume;
 +extern int musicVolume;
 +extern int voiceVolume; // haleyjd 08/29/10: [STRIFE]
 +
 +// Current music/sfx card - index useless
 +//  w/o a reference LUT in a sound module.
 +// Ideally, this would use indices found
 +//  in: /usr/include/linux/soundcard.h
 +extern int snd_MusicDevice;
 +extern int snd_SfxDevice;
 +// Config file? Same disclaimer as above.
 +extern int snd_DesiredMusicDevice;
 +extern int snd_DesiredSfxDevice;
 +
 +
 +// -------------------------
 +// Status flags for refresh.
 +//
 +
 +// Depending on view size - no status bar?
 +// Note that there is no way to disable the
 +//  status bar explicitely.
 +extern  boolean statusbaractive;
 +
 +extern  boolean automapactive;  // In AutoMap mode?
 +extern  boolean	menuactive;     // Menu overlayed?
 +extern  boolean menupause;      // haleyjd 08/29/10: [STRIFE]
 +extern  int     menupausetime;  // haleyjd 09/04/10: [STRIFE]
 +extern  boolean menuindialog;   // haleyjd: ditto
 +extern  boolean	paused;         // Game Pause?
 +
 +
 +extern  boolean		viewactive;
 +
 +extern  boolean		nodrawers;
 +
 +extern	int		viewwindowx;
 +extern	int		viewwindowy;
 +extern	int		viewheight;
 +extern	int		viewwidth;
 +extern	int		scaledviewwidth;
 +
 +extern  boolean         testcontrols;
 +
 +
 +
 +
 +// This one is related to the 3-screen display mode.
 +// ANG90 = left side, ANG270 = right
 +extern  int	viewangleoffset;
 +
 +// Player taking events, and displaying.
 +extern  int	consoleplayer;
 +extern  int	displayplayer;
 +
 +
 +// -------------------------------------
 +// Scores, rating.
 +// Statistics on a given map, for intermission.
 +//
 +extern  int	totalkills;
 +//extern	int	totalitems; [STRIFE] unused
 +extern	int	totalsecret;
 +
 +// Timer, for scores.
 +extern  int	levelstarttic;	// gametic at level start
 +extern  int	leveltime;	// tics in game play for par
 +
 +
 +
 +// --------------------------------------
 +// DEMO playback/recording related stuff.
 +// No demo, there is a human player in charge?
 +// Disable save/end game?
 +extern  boolean	usergame;
 +
 +//?
 +extern  boolean	demoplayback;
 +extern  boolean	demorecording;
 +extern  int     mouse_fire_countdown;   // villsa [STRIFE]
 +
 +// Round angleturn in ticcmds to the nearest 256.  This is used when
 +// recording Vanilla demos in netgames.
 +
 +extern boolean lowres_turn;
 +
 +// Quit after playing a demo from cmdline.
 +extern  boolean		singledemo;
 +
 +
 +
 +
 +//?
 +extern  gamestate_t     gamestate;
 +
 +
 +
 +
 +
 +
 +//-----------------------------
 +// Internal parameters, fixed.
 +// These are set by the engine, and not changed
 +//  according to user inputs. Partly load from
 +//  WAD, partly set at startup time.
 +
 +
 +
 +extern	int		gametic;
 +
 +
 +// Bookkeeping on players - state.
 +extern	player_t	players[MAXPLAYERS];
 +
 +// Alive? Disconnected?
 +extern  boolean		playeringame[MAXPLAYERS];
 +
 +
 +// Player spawn spots for deathmatch.
 +#define MAX_DM_STARTS   10
 +extern  mapthing_t      deathmatchstarts[MAX_DM_STARTS];
 +extern  mapthing_t*	deathmatch_p;
 +
 +// Player spawn spots.
 +extern  mapthing_t      playerstarts[MAXPLAYERS];
 +
 +// haleyjd 08/24/10: [STRIFE] rift spots
 +#define MAXRIFTSPOTS 16
 +extern  mapthing_t      riftSpots[MAXRIFTSPOTS];
 +
 +// Intermission stats.
 +// Parameters for world map / intermission.
 +extern  wbstartstruct_t		wminfo;
 +
 +
 +// LUT of ammunition limits for each kind.
 +// This doubles with BackPack powerup item.
 +extern  int		maxammo[NUMAMMO];
 +
 +
 +
 +
 +
 +//-----------------------------------------
 +// Internal parameters, used for engine.
 +//
 +
 +// File handling stuff.
 +extern  char *          savegamedir;
 +extern	char		basedefault[1024];
- extern  FILE*		debugfile;
 +
 +// if true, load all graphics at level load
 +extern  boolean         precache;
 +
 +
 +// wipegamestate can be set to -1
 +//  to force a wipe on the next draw
 +extern  gamestate_t     wipegamestate;
 +
 +extern  int             mouseSensitivity;
 +//?
 +// debug flag to cancel adaptiveness
 +extern  boolean         singletics;
 +
 +//extern  int             bodyqueslot; [STRIFE] unused
 +
 +
 +
 +// Needed to store the number of the dummy sky flat.
 +// Used for rendering,
 +//  as well as tracking projectiles etc.
 +extern int		skyflatnum;
 +
 +
 +
 +// Netgame stuff (buffers and pointers, i.e. indices).
 +
 +
 +extern	int		rndindex;
 +
 +extern	int		maketic;
 +extern  int             nettics[MAXPLAYERS];
 +
 +extern  ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
 +extern	int		ticdup;
 +
 +
 +
 +#endif
diff --cc src/strife/dstrings.h
index bdc6b2ce,00000000..d47fc1af
mode 100644,000000..100644
--- a/src/strife/dstrings.h
+++ b/src/strife/dstrings.h
@@@ -1,58 -1,0 +1,49 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +//
 +// DESCRIPTION:
 +//	DOOM strings, by language.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#ifndef __DSTRINGS__
 +#define __DSTRINGS__
 +
 +
 +// All important printed strings.
 +
 +#include "d_englsh.h"
 +
 +// Misc. other strings.
 +#define SAVEGAMENAME	"doomsav"
 +
 +
- //
- // File locations,
- //  relative to current position.
- // Path names are OS-sensitive.
- //
- #define DEVMAPS "devmaps"
- #define DEVDATA "devdata"
-
-
 +// QuitDOOM messages
 +// 8 per each game type
 +#define NUM_QUITMESSAGES   8
 +
 +extern char *doom1_endmsg[];
 +extern char *doom2_endmsg[];
 +
 +
 +#endif
diff --cc src/strife/g_game.c
index b8496a5b,00000000..c9d49c18
mode 100644,000000..100644
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@@ -1,2563 -1,0 +1,2564 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:  none
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <string.h>
 +#include <stdlib.h>
 +#include <math.h>
 +
 +#include "doomdef.h"
 +#include "doomkeys.h"
 +#include "doomstat.h"
 +
 +#include "deh_main.h"
 +#include "deh_misc.h"
 +#include "z_zone.h"
 +#include "f_finale.h"
 +#include "m_argv.h"
 +#include "m_controls.h"
 +#include "m_misc.h"
 +#include "m_menu.h"
 +#include "m_saves.h" // STRIFE
 +#include "m_random.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "p_setup.h"
 +#include "p_saveg.h"
 +#include "p_tick.h"
 +#include "d_main.h"
 +#include "wi_stuff.h"
 +#include "hu_stuff.h"
 +#include "st_stuff.h"
 +#include "am_map.h"
 +
 +// Needs access to LFB.
 +#include "v_video.h"
 +
 +#include "w_wad.h"
 +
 +#include "p_local.h"
 +
 +#include "s_sound.h"
 +
 +// Data.
 +#include "dstrings.h"
 +#include "sounds.h"
 +
 +// SKY handling - still the wrong place.
 +#include "r_data.h"
 +#include "r_sky.h"
 +
 +#include "p_dialog.h"   // villsa [STRIFE]
 +
 +#include "g_game.h"
 +
 +
 +#define SAVEGAMESIZE	0x2c000
 +
 +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 map);
 +
 +void	G_DoReborn (int playernum);
 +
 +void	G_DoLoadLevel (void);
 +void	G_DoNewGame (void);
 +void	G_DoLoadGame (boolean userload);
 +void	G_DoPlayDemo (void);
 +void	G_DoCompleted (void);
 +void	G_DoVictory (void);
 +void	G_DoWorldDone (void);
 +void	G_DoSaveGame (char *path);
 +
 +// Gamestate the last time G_Ticker was called.
 +
 +gamestate_t     oldgamestate;
 +
 +gameaction_t    gameaction;
 +gamestate_t     gamestate;
 +skill_t         gameskill = 2; // [STRIFE] Default value set to 2.
 +boolean         respawnmonsters;
 +//int             gameepisode;
 +int             gamemap;
 +
 +// haleyjd 08/24/10: [STRIFE] New variables
 +int             destmap;   // current destination map when exiting
 +int             riftdest;  // destination spot for player
 +angle_t         riftangle; // player angle saved during exit
 +
 +// If non-zero, exit the level after this number of minutes.
 +
 +int             timelimit;
 +
 +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
 +boolean         nodrawers;              // for comparative timing purposes
 +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];
 +
 +boolean         turbodetected[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];
++char           *demoname;
 +boolean         demorecording;
 +boolean         longtics;               // cph's doom 1.91 longtics hack
 +boolean         lowres_turn;            // low resolution turning for longtics
 +boolean         demoplayback;
 +boolean		netdemo;
 +byte*		demobuffer;
 +byte*		demo_p;
 +byte*		demoend;
 +boolean         singledemo;             // quit after playing a demo from cmdline
 +
 +boolean         precache = true;        // if true, load all graphics at start
 +
 +boolean         testcontrols = false;    // Invoked by setup to test controls
 +
 +wbstartstruct_t wminfo;                 // parms for world map / intermission
 +
 +byte            consistancy[MAXPLAYERS][BACKUPTICS];
 +
 +#define MAXPLMOVE		(forwardmove[1])
 +
 +#define TURBOTHRESHOLD	0x32
 +
 +fixed_t         forwardmove[2] = {0x19, 0x32};
 +fixed_t         sidemove[2] = {0x18, 0x28};
 +fixed_t         angleturn[3] = {640, 1280, 320};    // + slow turn
 +
 +int mouse_fire_countdown = 0;    // villsa [STRIFE]
 +
 +static int *weapon_keys[] = {
 +    &key_weapon1,
 +    &key_weapon2,
 +    &key_weapon3,
 +    &key_weapon4,
 +    &key_weapon5,
 +    &key_weapon6,
 +    &key_weapon7,
 +    &key_weapon8
 +};
 +
 +// Set to -1 or +1 to switch to the previous or next weapon.
 +
 +static int next_weapon = 0;
 +
 +// Used for prev/next weapon keys.
 +// STRIFE-TODO: Check this table makes sense.
 +
 +static const struct
 +{
 +    weapontype_t weapon;
 +    weapontype_t weapon_num;
 +} weapon_order_table[] = {
 +    { wp_fist,                  wp_fist },
 +    { wp_elecbow,               wp_elecbow },
 +    { wp_poisonbow,             wp_elecbow },
 +    { wp_rifle,                 wp_rifle },
 +    { wp_missile,               wp_missile },
 +    { wp_hegrenade,             wp_hegrenade },
 +    { wp_wpgrenade,             wp_hegrenade },
 +    { wp_flame,                 wp_flame },
 +    { wp_mauler,                wp_mauler },
 +    { wp_torpedo,               wp_mauler },
 +    { wp_sigil,                 wp_sigil },
 +};
 +
 +#define SLOWTURNTICS	6
 +
 +#define NUMKEYS		256
 +#define MAX_JOY_BUTTONS 20
 +
 +static boolean  gamekeydown[NUMKEYS];
 +static int      turnheld;		// for accelerative turning
 +
 +static boolean  mousearray[MAX_MOUSE_BUTTONS + 1];
 +static boolean *mousebuttons = &mousearray[1];  // allow [-1]
 +
 +// mouse values are used once
 +int             mousex;
 +int             mousey;
 +
 +static int      dclicktime;
 +static boolean  dclickstate;
 +static int      dclicks;
 +static int      dclicktime2;
 +static boolean  dclickstate2;
 +static int      dclicks2;
 +
 +// joystick values are repeated
 +static int      joyxmove;
 +static int      joyymove;
 +static boolean  joyarray[MAX_JOY_BUTTONS + 1];
 +static boolean *joybuttons = &joyarray[1];		// allow [-1]
 +
 +static int      savegameslot = 6; // [STRIFE] initialized to 6
 +static char     savedescription[32];
 +
 +static int      testcontrols_mousespeed;
 +
 +#define	BODYQUESIZE	32
 +
 +mobj_t*		bodyque[BODYQUESIZE];
 +//int       bodyqueslot; [STRIFE] unused
 +
 +int             vanilla_savegame_limit = 1;
 +int             vanilla_demo_limit = 1;
 +
 +
 +#define MOUSE_SPEED_BOX_WIDTH 16
 +#define COLOR_RED    0xb0
 +#define COLOR_BLACK  0x00
 +#define COLOR_WHITE  0x04
 +#define COLOR_YELLOW 0xe7
 +
 +void G_DrawMouseSpeedBox(void)
 +{
 +    extern int usemouse;
 +    int i;
 +    int box_x, box_y;
 +    int original_speed;
 +    int x, y;
 +    int redline_x;
 +    int linelen;
 +    char *lumpname;
 +    int color;
 +
 +    // If the mouse is turned off or acceleration is turned off, don't
 +    // draw the box at all.
 +
 +    if (!usemouse || fabs(mouse_acceleration - 1) < 0.01)
 +    {
 +        return;
 +    }
 +
 +    // Calculate box position
 +
 +    box_x = SCREENWIDTH - MOUSE_SPEED_BOX_WIDTH * 8;
 +    box_y = SCREENHEIGHT - 9;
 +
 +    // Draw the box.
 +
 +    x = box_x;
 +
 +    for (i=0; i<MOUSE_SPEED_BOX_WIDTH; ++i)
 +    {
 +        if (i == 0)
 +        {
 +            lumpname = "M_LSLEFT";
 +        }
 +        else if (i == MOUSE_SPEED_BOX_WIDTH - 1)
 +        {
 +            lumpname = "M_LSRGHT";
 +        }
 +        else
 +        {
 +            lumpname = "M_LSCNTR";
 +        }
 +
 +        V_DrawPatchDirect(x, box_y,
 +                          W_CacheLumpName(DEH_String(lumpname), PU_CACHE));
 +        x += 8;
 +    }
 +
 +    // Calculate the position of the red line.  This is 1/3 of the way
 +    // along the box.
 +
 +    redline_x = (MOUSE_SPEED_BOX_WIDTH / 3) * 8;
 +
 +    // Undo acceleration and get back the original mouse speed
 +
 +    if (testcontrols_mousespeed < mouse_threshold)
 +    {
 +        original_speed = testcontrols_mousespeed;
 +    }
 +    else
 +    {
 +        original_speed = testcontrols_mousespeed - mouse_threshold;
 +        original_speed = (int) (original_speed / mouse_acceleration);
 +        original_speed += mouse_threshold;
 +    }
 +
 +    // Calculate line length
 +
 +    linelen = (original_speed * redline_x) / mouse_threshold;
 +
 +    // Draw horizontal "thermometer"
 +
 +    for (x=0; x<(MOUSE_SPEED_BOX_WIDTH - 1) * 8; ++x)
 +    {
 +        if (x < linelen)
 +        {
 +            if (x < redline_x)
 +            {
 +                color = COLOR_WHITE;
 +            }
 +            else
 +            {
 +                color = COLOR_YELLOW;
 +            }
 +        }
 +        else
 +        {
 +            color = COLOR_BLACK;
 +        }
 +
 +        I_VideoBuffer[(box_y - 4) * SCREENWIDTH + box_x + x + 1] = color;
 +    }
 +
 +    // Draw red line
 +
 +    for (y=box_y - 8; y<box_y; ++y)
 +    {
 +        I_VideoBuffer[y * SCREENWIDTH + box_x + redline_x] = COLOR_RED;
 +    }
 +}
 +
 +int G_CmdChecksum (ticcmd_t* cmd)
 +{
 +    size_t		i;
 +    int		sum = 0;
 +
 +    for (i=0 ; i< sizeof(*cmd)/4 - 1 ; i++)
 +	sum += ((int *)cmd)[i];
 +
 +    return sum;
 +}
 +
 +static boolean WeaponSelectable(weapontype_t weapon)
 +{
 +    // Can't select a weapon if we don't own it.
 +
 +    if (!players[consoleplayer].weaponowned[weapon])
 +    {
 +        return false;
 +    }
 +
 +    // STRIFE-TODO: Special weapon cycling rules?
 +
 +    return true;
 +}
 +
 +static int G_NextWeapon(int direction)
 +{
 +    weapontype_t weapon;
 +    int i;
 +
 +    // Find index in the table.
 +
 +    if (players[consoleplayer].pendingweapon == wp_nochange)
 +    {
 +        weapon = players[consoleplayer].readyweapon;
 +    }
 +    else
 +    {
 +        weapon = players[consoleplayer].pendingweapon;
 +    }
 +
 +    for (i=0; i<arrlen(weapon_order_table); ++i)
 +    {
 +        if (weapon_order_table[i].weapon == weapon)
 +        {
 +            break;
 +        }
 +    }
 +
 +    // Switch weapon.
 +
 +    do
 +    {
 +        i += direction;
 +        i = (i + arrlen(weapon_order_table)) % arrlen(weapon_order_table);
 +    } while (!WeaponSelectable(weapon_order_table[i].weapon));
 +
 +    return weapon_order_table[i].weapon_num;
 +}
 +
 +//
 +// 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
 +//
 +void G_BuildTiccmd (ticcmd_t* cmd)
 +{
 +    int		i;
 +    boolean	strafe;
 +    boolean	bstrafe;
 +    int		speed;
 +    int		tspeed;
 +    int		forward;
 +    int		side;
 +
 +    memset(cmd, 0, sizeof(ticcmd_t));
 +
 +    cmd->consistancy =
 +        consistancy[consoleplayer][maketic%BACKUPTICS];
 +
 +    // villsa [STRIFE] look up key
 +    if(gamekeydown[key_lookup])
 +        cmd->buttons2 |= BT2_LOOKUP;
 +
 +    // villsa [STRIFE] look down key
 +    if(gamekeydown[key_lookdown])
 +        cmd->buttons2 |= BT2_LOOKDOWN;
 +
 +    // villsa [STRIFE] inventory use key
 +    if(gamekeydown[key_invuse])
 +    {
 +        player_t* player = &players[consoleplayer];
 +        if(player->numinventory > 0)
 +        {
 +            cmd->buttons2 |= BT2_INVUSE;
 +            cmd->inventory = player->inventory[player->inventorycursor].sprite;
 +        }
 +    }
 +
 +    // villsa [STRIFE] inventory drop key
 +    if(gamekeydown[key_invdrop])
 +    {
 +        player_t* player = &players[consoleplayer];
 +        if(player->numinventory > 0)
 +        {
 +            cmd->buttons2 |= BT2_INVDROP;
 +            cmd->inventory = player->inventory[player->inventorycursor].sprite;
 +        }
 +    }
 +
 +    // villsa [STRIFE] use medkit
 +    if(gamekeydown[key_usehealth])
 +        cmd->buttons2 |= BT2_HEALTH;
 +
 +
 +
 +    strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
 +        || joybuttons[joybstrafe];
 +
 +    // fraggle: support the old "joyb_speed = 31" hack which
 +    // allowed an autorun effect
 +
 +    speed = key_speed >= NUMKEYS
 +         || joybspeed >= MAX_JOY_BUTTONS
 +         || gamekeydown[key_speed]
 +         || joybuttons[joybspeed];
 +
 +    forward = side = 0;
 +
 +    // villsa [STRIFE] running causes centerview to occur
 +    if(speed)
 +        cmd->buttons2 |= BT2_CENTERVIEW;
 +
 +    // villsa [STRIFE] disable running if low on health
 +    if (players[consoleplayer].health <= 15)
 +        speed = 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;
 +
 +    // let movement keys cancel each other out
 +    if (strafe)
 +    {
 +        if (gamekeydown[key_right])
 +        {
 +            // fprintf(stderr, "strafe right\n");
 +            side += sidemove[speed];
 +        }
 +        if (gamekeydown[key_left])
 +        {
 +            //	fprintf(stderr, "strafe left\n");
 +            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])
 +    {
 +        // fprintf(stderr, "up\n");
 +        forward += forwardmove[speed];
 +    }
 +    if (gamekeydown[key_down])
 +    {
 +        // fprintf(stderr, "down\n");
 +        forward -= forwardmove[speed];
 +    }
 +
 +    if (joyymove < 0)
 +        forward += forwardmove[speed];
 +    if (joyymove > 0)
 +        forward -= forwardmove[speed];
 +
 +    if (gamekeydown[key_strafeleft]
 +     || joybuttons[joybstrafeleft]
 +     || mousebuttons[mousebstrafeleft])
 +    {
 +        side -= sidemove[speed];
 +    }
 +
 +    if (gamekeydown[key_straferight]
 +     || joybuttons[joybstraferight]
 +     || mousebuttons[mousebstraferight])
 +    {
 +        side += sidemove[speed];
 +    }
 +
 +    // buttons
 +    cmd->chatchar = HU_dequeueChatChar();
 +
 +    // villsa [STRIFE] TODO - add mouse button support for jump
 +    if(gamekeydown[key_jump] /*|| mousebuttons[mousebjump]*/)
 +        cmd->buttons2 |= BT2_JUMP;
 +
 +    // villsa [STRIFE]: Moved mousebuttons[mousebfire] to below
 +    if (gamekeydown[key_fire] || joybuttons[joybfire])
 +        cmd->buttons |= BT_ATTACK;
 +
 +    // villsa [STRIFE]
 +    if(mousebuttons[mousebfire])
 +    {
 +         if(mouse_fire_countdown <= 0)
 +             cmd->buttons |= BT_ATTACK;
 +         else
 +             --mouse_fire_countdown;
 +    }
 +
 +    if (gamekeydown[key_use]
 +     || joybuttons[joybuse]
 +     || mousebuttons[mousebuse])
 +    {
 +        cmd->buttons |= BT_USE;
 +        // clear double clicks if hit use button
 +        dclicks = 0;
 +    }
 +
 +    // If the previous or next weapon button is pressed, the
 +    // next_weapon variable is set to change weapons when
 +    // we generate a ticcmd.  Choose a new weapon.
 +
 +    if (next_weapon != 0)
 +    {
 +        i = G_NextWeapon(next_weapon);
 +        cmd->buttons |= BT_CHANGE;
 +        cmd->buttons |= i << BT_WEAPONSHIFT;
 +        next_weapon = 0;
 +    }
 +    else
 +    {
 +        // Check weapon keys.
 +
 +        for (i=0; i<arrlen(weapon_keys); ++i)
 +        {
 +            int key = *weapon_keys[i];
 +
 +            if (gamekeydown[key])
 +            {
 +                cmd->buttons |= BT_CHANGE;
 +                cmd->buttons |= i<<BT_WEAPONSHIFT;
 +                break;
 +            }
 +        }
 +    }
 +
 +    // mouse
 +    if (mousebuttons[mousebforward])
 +    {
 +        forward += forwardmove[speed];
 +    }
 +    if (mousebuttons[mousebbackward])
 +    {
 +        forward -= forwardmove[speed];
 +    }
 +
 +    if (dclick_use)
 +    {
 +        // forward double click
 +        if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 )
 +        {
 +            dclickstate = mousebuttons[mousebforward];
 +            if (dclickstate)
 +                dclicks++;
 +            if (dclicks == 2)
 +            {
 +                cmd->buttons |= BT_USE;
 +                dclicks = 0;
 +            }
 +            else
 +                dclicktime = 0;
 +        }
 +        else
 +        {
 +            dclicktime += ticdup;
 +            if (dclicktime > 20)
 +            {
 +                dclicks = 0;
 +                dclickstate = 0;
 +            }
 +        }
 +
 +        // strafe double click
 +        bstrafe =
 +            mousebuttons[mousebstrafe]
 +            || joybuttons[joybstrafe];
 +        if (bstrafe != dclickstate2 && dclicktime2 > 1 )
 +        {
 +            dclickstate2 = bstrafe;
 +            if (dclickstate2)
 +                dclicks2++;
 +            if (dclicks2 == 2)
 +            {
 +                cmd->buttons |= BT_USE;
 +                dclicks2 = 0;
 +            }
 +            else
 +                dclicktime2 = 0;
 +        }
 +        else
 +        {
 +            dclicktime2 += ticdup;
 +            if (dclicktime2 > 20)
 +            {
 +                dclicks2 = 0;
 +                dclickstate2 = 0;
 +            }
 +        }
 +    }
 +
 +    forward += mousey;
 +
 +    if (strafe)
 +        side += mousex*2;
 +    else
 +        cmd->angleturn -= mousex*0x8;
 +
 +    if (mousex == 0)
 +    {
 +        // No movement in the previous frame
 +
 +        testcontrols_mousespeed = 0;
 +    }
 +
 +    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;
 +
 +    // 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);
 +    }
 +
 +    // low-res turning
 +
 +    if (lowres_turn)
 +    {
 +        static signed short carry = 0;
 +        signed short desired_angleturn;
 +
 +        desired_angleturn = cmd->angleturn + carry;
 +
 +        // round angleturn to the nearest 256 unit boundary
 +        // for recording demos with single byte values for turn
 +
 +        cmd->angleturn = (desired_angleturn + 128) & 0xff00;
 +
 +        // Carry forward the error from the reduced resolution to the
 +        // next tic, so that successive small movements can accumulate.
 +
 +        carry = desired_angleturn - cmd->angleturn;
 +    }
 +}
 +
 +
 +//
 +// G_DoLoadLevel
 +//
 +extern  gamestate_t     wipegamestate;
 +
 +void G_DoLoadLevel (void)
 +{
 +    int             i;
 +
 +    // haleyjd 10/03/10: [STRIFE] This is not done here.
 +    //skyflatnum = R_FlatNumForName(DEH_String(SKYFLATNAME));
 +
 +    levelstarttic = gametic;        // for time calculation
 +
 +    if (wipegamestate == GS_LEVEL)
 +        wipegamestate = -1;             // force a wipe
 +
 +    gamestate = GS_LEVEL;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +        turbodetected[i] = false;
 +
 +        // haleyjd 20110204 [STRIFE]: PST_REBORN if players[i].health <= 0
 +        if (playeringame[i] && (players[i].playerstate == PST_DEAD || players[i].health <= 0))
 +            players[i].playerstate = PST_REBORN;
 +        memset (players[i].frags,0,sizeof(players[i].frags));
 +    }
 +
 +    P_SetupLevel (gamemap, 0, gameskill);
 +    displayplayer = consoleplayer;      // view the guy you are playing
 +    starttime = I_GetTime(); // haleyjd 20110204 [STRIFE]
 +    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));
 +
 +    if (testcontrols)
 +    {
 +        players[consoleplayer].message = "Press escape to quit.";
 +    }
 +
 +    P_DialogLoad(); // villsa [STRIFE]
 +}
 +
 +static void SetJoyButtons(unsigned int buttons_mask)
 +{
 +    int i;
 +
 +    for (i=0; i<MAX_JOY_BUTTONS; ++i)
 +    {
 +        int button_on = (buttons_mask & (1 << i)) != 0;
 +
 +        // Detect button press:
 +
 +        if (!joybuttons[i] && button_on)
 +        {
 +            // Weapon cycling:
 +
 +            if (i == joybprevweapon)
 +            {
 +                next_weapon = -1;
 +            }
 +            else if (i == joybnextweapon)
 +            {
 +                next_weapon = 1;
 +            }
 +        }
 +
 +        joybuttons[i] = button_on;
 +    }
 +}
 +
 +static void SetMouseButtons(unsigned int buttons_mask)
 +{
 +    int i;
 +
 +    for (i=0; i<MAX_MOUSE_BUTTONS; ++i)
 +    {
 +        unsigned int button_on = (buttons_mask & (1 << i)) != 0;
 +
 +        // Detect button press:
 +
 +        if (!mousebuttons[i] && button_on)
 +        {
 +            if (i == mousebprevweapon)
 +            {
 +                next_weapon = -1;
 +            }
 +            else if (i == mousebnextweapon)
 +            {
 +                next_weapon = 1;
 +            }
 +        }
 +
 +	mousebuttons[i] = button_on;
 +    }
 +}
 +
 +//
 +// G_Responder
 +// Get info needed to make ticcmd_ts for the players.
 +//
 +boolean G_Responder (event_t* ev)
 +{
 +    // allow spy mode changes even during the demo
 +    if (gamestate == GS_LEVEL && ev->type == ev_keydown
 +        && ev->data1 == key_spy && (singledemo || !gameskill) ) // [STRIFE]: o_O
 +    {
 +        // spy mode
 +        do
 +        {
 +            displayplayer++;
 +            if (displayplayer == MAXPLAYERS)
 +                displayplayer = 0;
 +        } while (!playeringame[displayplayer] && displayplayer != consoleplayer);
 +        return true;
 +    }
 +
 +    // any other key pops up menu if in demos
 +    if (gameaction == ga_nothing && !singledemo &&
 +        (demoplayback || gamestate == GS_DEMOSCREEN)
 +        )
 +    {
 +        if (ev->type == ev_keydown ||
 +            (ev->type == ev_mouse && ev->data1) ||
 +            (ev->type == ev_joystick && ev->data1) )
 +        {
 +            if(devparm && ev->data1 == 'g')
 +                D_PageTicker(); // [STRIFE]: wat? o_O
 +            else
 +                M_StartControlPanel ();
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +    if (gamestate == GS_LEVEL)
 +    {
 +#if 0
 +        if (devparm && ev->type == ev_keydown && ev->data1 == ';')
 +        {
 +            G_DeathMatchSpawnPlayer (0);
 +            return true;
 +        }
 +#endif
 +        if (HU_Responder (ev))
 +            return true;	// chat ate the event
 +        if (ST_Responder (ev))
 +            return true;	// status window ate it
 +        if (AM_Responder (ev))
 +            return true;	// automap ate it
 +    }
 +
 +    if (gamestate == GS_FINALE)
 +    {
 +        if (F_Responder (ev))
 +            return true;	// finale ate the event
 +    }
 +
 +    if (testcontrols && ev->type == ev_mouse)
 +    {
 +        // If we are invoked by setup to test the controls, save the
 +        // mouse speed so that we can display it on-screen.
 +        // Perform a low pass filter on this so that the thermometer
 +        // appears to move smoothly.
 +
 +        testcontrols_mousespeed = abs(ev->data2);
 +    }
 +
 +    // If the next/previous weapon keys are pressed, set the next_weapon
 +    // variable to change weapons when the next ticcmd is generated.
 +
 +    if (ev->type == ev_keydown && ev->data1 == key_prevweapon)
 +    {
 +        next_weapon = -1;
 +    }
 +    else if (ev->type == ev_keydown && ev->data1 == key_nextweapon)
 +    {
 +        next_weapon = 1;
 +    }
 +
 +    switch (ev->type)
 +    {
 +    case ev_keydown:
 +        if (ev->data1 == key_pause)
 +        {
 +            sendpause = true;
 +        }
 +        else 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:
 +        SetMouseButtons(ev->data1);
 +        mousex = ev->data2*(mouseSensitivity+5)/10;
 +        mousey = ev->data3*(mouseSensitivity+5)/10;
 +        return true;    // eat events
 +
 +    case ev_joystick:
 +        SetJoyButtons(ev->data1);
 +        joyxmove = ev->data2;
 +        joyymove = ev->data3;
 +        return true;    // eat events
 +
 +    default:
 +        break;
 +    }
 +
 +    return false;
 +}
 +
 +//
 +// G_Ticker
 +// Make ticcmd_ts for the players.
 +//
 +void G_Ticker (void)
 +{
 +    int         i;
 +    int         buf;
 +    ticcmd_t*   cmd;
 +
 +    // do player reborns if needed
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +        if (playeringame[i] && players[i].playerstate == PST_REBORN)
 +            G_DoReborn (i);
 +
 +    // do things to change the game state
 +    while (gameaction != ga_nothing)
 +    {
 +        switch (gameaction)
 +        {
 +        case ga_loadlevel:
 +            G_DoLoadLevel ();
 +            break;
 +        case ga_newgame:
 +            G_DoNewGame ();
 +            break;
 +        case ga_loadgame:
 +            G_DoLoadGame(true);
 +            M_SaveMoveHereToMap(); // [STRIFE]
 +            M_ReadMisObj();
 +            break;
 +        case ga_savegame:
 +            M_SaveMoveMapToHere(); // [STRIFE]
 +            M_SaveMisObj(savepath);
 +            G_DoSaveGame(savepath);
 +            break;
 +        case ga_playdemo:
 +            G_DoPlayDemo ();
 +            break;
 +        case ga_completed:
 +            G_DoCompleted ();
 +            break;
 +        case ga_victory:
 +            F_StartFinale ();
 +            break;
 +        case ga_worlddone:
 +            G_DoWorldDone ();
 +            break;
 +        case ga_screenshot:
 +            V_ScreenShot("STRIFE%02i.pcx"); // [STRIFE] file name, message
 +            players[consoleplayer].message = DEH_String("STRIFE  by Rogue entertainment");
 +            gameaction = ga_nothing;
 +            break;
 +        case ga_nothing:
 +            break;
 +        }
 +    }
 +
 +    // get commands, check consistancy,
 +    // and build new consistancy check
 +    buf = (gametic/ticdup)%BACKUPTICS;
 +
 +    // STRIFE-TODO: pnameprefixes bullcrap
 +
 +    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);
 +
 +            // check for turbo cheats
 +
 +            // check ~ 4 seconds whether to display the turbo message.
 +            // store if the turbo threshold was exceeded in any tics
 +            // over the past 4 seconds.  offset the checking period
 +            // for each player so messages are not displayed at the
 +            // same time.
 +
 +            if (cmd->forwardmove > TURBOTHRESHOLD)
 +            {
 +                turbodetected[i] = true;
 +            }
 +
 +            if ((gametic & 31) == 0
 +                && ((gametic >> 5) % MAXPLAYERS) == i
 +                && turbodetected[i])
 +            {
 +                static char turbomessage[80];
 +                extern char *player_names[4];
 +                sprintf (turbomessage, "%s is turbo!",player_names[i]);
 +                players[consoleplayer].message = turbomessage;
 +                turbodetected[i] = false;
 +            }
 +
 +            if (netgame && !netdemo && !(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 (!character_name[0]) // [STRIFE]
 +                        strcpy (character_name, "NET GAME");
 +                    savegameslot =
 +                        (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
 +                    gameaction = ga_savegame;
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +
 +    // Have we just finished displaying an intermission screen?
 +    // haleyjd 08/23/10: [STRIFE] No intermission.
 +    /*
 +    if (oldgamestate == GS_INTERMISSION && gamestate != GS_INTERMISSION)
 +    {
 +        WI_End();
 +    }
 +    */
 +
 +    oldgamestate = gamestate;
 +
 +    // do main actions
 +    switch (gamestate)
 +    {
 +    case GS_LEVEL:
 +        P_Ticker ();
 +        ST_Ticker ();
 +        AM_Ticker ();
 +        HU_Ticker ();
 +        break;
 +
 +        // haleyjd 08/23/10: [STRIFE] No intermission.
 +        /*
 +    case GS_INTERMISSION:
 +        WI_Ticker ();
 +        break;
 +        */
 +    case GS_UNKNOWN: // STRIFE-TODO: What is this? is it ever used??
 +        F_WaitTicker();
 +        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.
 +//
 +// [STRIFE] No such function.
 +/*
 +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.
 +//
 +// [STRIFE] No such function. The equivalent to this logic was moved into
 +// G_DoCompleted.
 +/*
 +void G_PlayerFinishLevel (int player)
 +{
 +    player_t*	p;
 +
 +    p = &players[player];
 +
 +    memset (p->powers, 0, sizeof (p->powers));
 +    memset (p->cards, 0, sizeof (p->cards));
 +    p->mo->flags &= ~MF_SHADOW;		// cancel invisibility
 +    p->extralight = 0;			// cancel gun flashes
 +    p->fixedcolormap = 0;		// cancel ir gogles
 +    p->damagecount = 0;			// no palette changes
 +    p->bonuscount = 0;
 +}
 +*/
 +
 +//
 +// G_PlayerReborn
 +// Called after a player dies
 +// almost everything is cleared and initialized
 +//
 +// [STRIFE] Small changes for allegiance, inventory, health auto-use, and
 +// mission objective.
 +//
 +void G_PlayerReborn (int player)
 +{
 +    player_t*   p;
 +    int         i;
 +    int         frags[MAXPLAYERS];
 +    int         killcount;
 +    int         allegiance;
 +
 +    killcount = players[player].killcount;
 +    allegiance = players[player].allegiance; // [STRIFE]
 +
 +    memcpy(frags,players[player].frags,sizeof(frags));
 +
 +    p = &players[player];
 +    memset (p, 0, sizeof(*p));
 +
 +    memcpy(p->frags, frags, sizeof(p->frags));
 +
 +    p->usedown               = true;                 // don't do anything immediately
 +    p->attackdown            = true;
 +    p->inventorydown         = true;                 // villsa [STRIFE]
 +    p->playerstate           = PST_LIVE;
 +    p->health                = deh_initial_health;   // Use dehacked value
 +    p->readyweapon           = wp_fist;              // villsa [STRIFE] default to fists
 +    p->pendingweapon         = wp_fist;              // villsa [STRIFE] default to fists
 +    p->weaponowned[wp_fist]  = true;                 // villsa [STRIFE] default to fists
 +    p->cheats               |= CF_AUTOHEALTH;        // villsa [STRIFE]
 +    p->killcount             = killcount;
 +    p->allegiance            = allegiance;           // villsa [STRIFE]
 +    p->centerview            = true;                 // villsa [STRIFE]
 +
 +    for(i = 0; i < NUMAMMO; i++)
 +        p->maxammo[i] = maxammo[i];
 +
 +    // [STRIFE] clear inventory
 +    for(i = 0; i < 32; i++)
 +        p->inventory[i].type = NUMMOBJTYPES;
 +
 +    // villsa [STRIFE]: Default objective
 +    strncpy(mission_objective, DEH_String("Find help"), OBJECTIVE_LEN);
 +}
 +
 +//
 +// G_CheckSpot
 +// Returns false if the player cannot be respawned
 +// at the given mapthing_t spot
 +// because something is occupying it
 +//
 +// [STRIFE] Changed to eliminate body queue and an odd error message was added.
 +//
 +void P_SpawnPlayer (mapthing_t* mthing);
 +
 +boolean
 +G_CheckSpot
 +( int		playernum,
 +  mapthing_t*	mthing )
 +{
 +    fixed_t             x;
 +    fixed_t             y;
 +    subsector_t*        ss;
 +    unsigned            an;
 +    mobj_t*             mo;
 +    int                 i;
 +
 +    if (!players[playernum].mo)
 +    {
 +        // [STRIFE] weird error message added here:
 +        if(leveltime > 0)
 +            players[playernum].message = DEH_String("you didn't have a body!");
 +
 +        // first spawn of level, before corpses
 +        for (i=0 ; i<playernum ; i++)
 +            if (players[i].mo->x == mthing->x << FRACBITS
 +                && players[i].mo->y == mthing->y << FRACBITS)
 +                return false;
 +        return true;
 +    }
 +
 +    x = mthing->x << FRACBITS;
 +    y = mthing->y << FRACBITS;
 +
 +    if (!P_CheckPosition (players[playernum].mo, x, y) )
 +        return false;
 +
 +    // flush an old corpse if needed
 +    // [STRIFE] player corpses remove themselves after a short time, so
 +    // evidently this wasn't needed.
 +    /*
 +    if (bodyqueslot >= BODYQUESIZE)
 +        P_RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]);
 +    bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo;
 +    bodyqueslot++;
 +    */
 +
 +    // spawn a teleport fog
 +    ss = R_PointInSubsector (x,y);
 +    an = ( ANG45 * (((unsigned int) mthing->angle)/45) ) >> ANGLETOFINESHIFT;
 +
 +    mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an]
 +                      , ss->sector->floorheight
 +                      , MT_TFOG);
 +
 +    if (players[consoleplayer].viewz != 1)
 +        S_StartSound (mo, sfx_telept);	// don't start sound on first frame
 +
 +    return true;
 +}
 +
 +
 +//
 +// G_DeathMatchSpawnPlayer
 +// Spawns a player at one of the random death match spots
 +// called at level load and each death
 +//
 +// [STRIFE] Verified unmodified
 +//
 +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_LoadPath
 +//
 +// haleyjd 10/03/10: [STRIFE] New function
 +// Sets loadpath based on the map and "savepathtemp"
 +//
 +void G_LoadPath(int map)
 +{
 +    char mapbuf[33];
 +
 +    memset(mapbuf, 0, sizeof(mapbuf));
 +    sprintf(mapbuf, "%d", map);
 +
 +    // haleyjd: free if already set, and use M_SafeFilePath
 +    if(loadpath)
 +        Z_Free(loadpath);
 +    loadpath = M_SafeFilePath(savepathtemp, mapbuf);
 +}
 +
 +//
 +// G_DoReborn
 +//
 +void G_DoReborn (int playernum)
 +{
 +    int                             i;
 +
 +    if (!netgame)
 +    {
 +        // reload the level from scratch
 +        // [STRIFE] Reborn level load
 +        G_LoadPath(gamemap);
 +        gameaction = ga_loadgame;
 +    }
 +    else
 +    {
 +        // respawn at the start
 +
 +        // first dissasociate the corpse
 +        // [STRIFE] Checks for NULL first
 +        if(players[playernum].mo)
 +            players[playernum].mo->player = NULL;
 +
 +        // spawn at random spot if in death match
 +        if (deathmatch)
 +        {
 +            G_DeathMatchSpawnPlayer (playernum);
 +            return;
 +        }
 +
 +        if (G_CheckSpot (playernum, &playerstarts[playernum]) )
 +        {
 +            P_SpawnPlayer (&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]);
 +    }
 +}
 +
 +//
 +// G_ScreenShot
 +//
 +// [STRIFE] Verified unmodified
 +//
 +void G_ScreenShot (void)
 +{
 +    gameaction = ga_screenshot;
 +}
 +
 +// haleyjd 08/23/2010: [STRIFE] Removed par times.
 +
 +//
 +// G_DoCompleted
 +//
 +//boolean         secretexit;
 +extern char*	pagename;
 +
 +//
 +// G_RiftExitLevel
 +//
 +// haleyjd 08/24/10: [STRIFE] New function
 +// * Called from some exit linedefs to exit to a specific riftspot in the
 +//   given destination map.
 +//
 +void G_RiftExitLevel(int map, int spot, angle_t angle)
 +{
 +    gameaction = ga_completed;
 +
 +    // special handling for post-Sigil map changes
 +    if(players[0].weaponowned[wp_sigil])
 +    {
 +        if(map == 3) // Front Base -> Abandoned Front Base
 +            map = 30;
 +        if(map == 7) // Castle -> New Front Base
 +            map = 10;
 +    }
 +
 +    // no rifting in deathmatch games
 +    if(deathmatch)
 +        spot = 0;
 +
 +    riftangle = angle;
 +    riftdest  = spot;
 +    destmap   = map;
 +}
 +
 +//
 +// G_Exit2
 +//
 +// haleyjd 10/03/10: [STRIFE] New function.
 +// No xrefs to this, doesn't seem to be used. Could have gotten inlined
 +// somewhere but I haven't seen it.
 +//
 +void G_Exit2(int dest, angle_t angle)
 +{
 +    riftdest = dest;
 +    gameaction = ga_completed;
 +    riftangle = angle;
 +    destmap = gamemap;
 +}
 +
 +//
 +// G_ExitLevel
 +//
 +// haleyjd 08/24/10: [STRIFE]:
 +// * Default to next map in numeric order; init destmap and riftdest.
 +//
 +void G_ExitLevel (int dest)
 +{
 +    if(dest == 0)
 +        dest = gamemap + 1;
 +    destmap = dest;
 +    riftdest = 0;
 +    gameaction = ga_completed;
 +}
 +
 +/*
 +// haleyjd 08/23/2010: [STRIFE] No secret exits in Strife.
 +// Here's for the german edition.
 +void G_SecretExitLevel (void)
 +{
 +    // IF NO WOLF3D LEVELS, NO SECRET EXIT!
 +    if ( (gamemode == commercial)
 +        && (W_CheckNumForName("map31")<0))
 +        secretexit = false;
 +    else
 +        secretexit = true;
 +    gameaction = ga_completed;
 +}
 +*/
 +
 +//
 +// G_StartFinale
 +//
 +// haleyjd 09/21/10: [STRIFE] New function.
 +// This replaced G_SecretExitLevel in Strife. I don't know that it's actually
 +// used anywhere in the game, but it *is* usable in mods via linetype 124,
 +// W1 Start Finale.
 +//
 +void G_StartFinale(void)
 +{
 +    gameaction = ga_victory;
 +}
 +
 +//
 +// G_DoCompleted
 +//
 +// haleyjd 08/23/10: [STRIFE]:
 +// * Removed G_PlayerFinishLevel and just sets some powerup states.
 +// * Removed Chex, as not relevant to Strife.
 +// * Removed DOOM level transfer logic
 +// * Removed intermission code.
 +// * Added setting gameaction to ga_worlddone.
 +//
 +void G_DoCompleted (void)
 +{
 +    int i;
 +
 +    // deal with powerup states
 +    for(i = 0; i < MAXPLAYERS; i++)
 +    {
 +        if(playeringame[i])
 +        {
 +            // STRIFE-TODO: not quite sure why it does this
 +            if(destmap < 40)
 +                players[i].powers[pw_allmap] = players[i].mapstate[destmap];
 +
 +            // Shadowarmor doesn't persist between maps in netgames
 +            if(netgame)
 +                players[i].powers[pw_invisibility] = 0;
 +        }
 +    }
 +
 +    stonecold = false;  // villsa [STRIFE]
 +
 +    if (automapactive)
 +        AM_Stop ();
 +
 +    // [STRIFE] HUB SAVE
 +    if(!deathmatch)
 +        G_DoSaveGame(savepathtemp);
 +
 +    gameaction = ga_worlddone;
 +}
 +
 +
 +// haleyjd 08/24/10: [STRIFE] No secret exits.
 +/*
 +//
 +// G_WorldDone
 +//
 +void G_WorldDone (void)
 +{
 +    gameaction = ga_worlddone;
 +
 +    if (secretexit)
 +        players[consoleplayer].didsecret = true;
 +
 +    if ( gamemode == commercial )
 +    {
 +	switch (gamemap)
 +	{
 +	  case 15:
 +	  case 31:
 +	    if (!secretexit)
 +		break;
 +	  case 6:
 +	  case 11:
 +	  case 20:
 +	  case 30:
 +	    F_StartFinale ();
 +	    break;
 +	}
 +    }
 +}
 +*/
 +
 +//
 +// G_RiftPlayer
 +//
 +// haleyjd 08/24/10: [STRIFE] New function
 +// Teleports the player to the appropriate rift spot.
 +//
 +void G_RiftPlayer(void)
 +{
 +    if(riftdest)
 +    {
 +        P_TeleportMove(players[0].mo,
 +                       riftSpots[riftdest - 1].x << FRACBITS,
 +                       riftSpots[riftdest - 1].y << FRACBITS);
 +        players[0].mo->angle  = riftangle;
 +        players[0].mo->health = players[0].health;
 +    }
 +}
 +
 +//
 +// G_RiftCheat
 +//
 +// haleyjd 08/24/10: [STRIFE] New function
 +// Called from the cheat code to jump to a rift spot.
 +//
 +boolean G_RiftCheat(int riftSpotNum)
 +{
 +    return P_TeleportMove(players[0].mo,
 +                          riftSpots[riftSpotNum - 1].x << FRACBITS,
 +                          riftSpots[riftSpotNum - 1].y << FRACBITS);
 +}
 +
 +//
 +// G_DoWorldDone
 +//
 +// haleyjd 08/24/10: [STRIFE] Added destmap -> gamemap set.
 +//
 +void G_DoWorldDone (void)
 +{
 +    int temp_leveltime = leveltime;
 +    boolean temp_shadow = false;
 +    boolean temp_mvis   = false;
 +
 +    gamestate = GS_LEVEL;
 +    gamemap = destmap;
 +
 +    // [STRIFE] HUB LOAD
 +    G_LoadPath(destmap);
 +    if (!deathmatch)
 +    {
 +        // Remember Shadowarmor across hub loads
 +        if(players[0].mo->flags & MF_SHADOW)
 +            temp_shadow = true;
 +        if(players[0].mo->flags & MF_MVIS)
 +            temp_mvis = true;
 +    }
 +    G_DoLoadGame(false);
 +
 +    // [STRIFE] leveltime carries over between maps
 +    leveltime = temp_leveltime;
 +
 +    if(!deathmatch)
 +    {
 +        // [STRIFE]: transfer saved powerups
 +        players[0].mo->flags &= ~(MF_SHADOW|MF_MVIS);
 +        if(temp_shadow)
 +            players[0].mo->flags |= MF_SHADOW;
 +        if(temp_mvis)
 +            players[0].mo->flags |= MF_MVIS;
 +
 +        // [STRIFE] HUB SAVE
 +        G_RiftPlayer();
 +        G_DoSaveGame(savepathtemp);
 +        M_SaveMisObj(savepathtemp);
 +    }
 +
 +    gameaction = ga_nothing;
 +    viewactive = true;
 +}
 +
 +//
 +// G_DoWorldDone2
 +//
 +// haleyjd 10/03/10: [STRIFE] New function. No xrefs; unused.
 +//
 +void G_DoWorldDone2(void)
 +{
 +    gamestate = GS_LEVEL;
 +    gameaction = ga_nothing;
 +    viewactive = true;
 +}
 +
 +//
 +// G_ReadCurrent
 +//
 +// haleyjd 10/03/10: [STRIFE] New function.
 +// Reads the "CURRENT" file from the given path and then sets it to
 +// gamemap.
 +//
 +void G_ReadCurrent(const char *path)
 +{
 +    char *temppath = NULL;
 +    byte *buffer = NULL;
 +
 +    temppath = M_SafeFilePath(path, "\\current");
 +
 +    if(M_ReadFile(temppath, &buffer) <= 0)
 +        gameaction = ga_newgame;
 +    else
 +    {
 +        // haleyjd 20110211: do endian-correct read
 +        gamemap = (((int)buffer[0])       |
 +                   ((int)buffer[1] <<  8) |
 +                   ((int)buffer[2] << 16) |
 +                   ((int)buffer[3] << 24));
 +        gameaction = ga_loadgame;
 +        Z_Free(buffer);
 +    }
 +
 +    Z_Free(temppath);
 +
 +    G_LoadPath(gamemap);
 +}
 +
 +//
 +// G_InitFromSavegame
 +// Can be called by the startup code or the menu task.
 +//
 +extern boolean setsizeneeded;
 +void R_ExecuteSetViewSize (void);
 +
 +char	savename[256];
 +
 +// [STRIFE]: No such function, at least in v1.2
 +// STRIFE-TODO: Does this come back in v1.31?
 +/*
 +void G_LoadGame (char* name)
 +{
 +    strcpy (savename, name);
 +    gameaction = ga_loadgame;
 +}
 +*/
 +
 +// haleyjd 09/28/10: [STRIFE] VERSIONSIZE == 8
 +#define VERSIONSIZE             8
 +
 +void G_DoLoadGame (boolean userload)
 +{
 +    int savedleveltime;
 +
 +    gameaction = ga_nothing;
 +
 +    save_stream = fopen(loadpath, "rb");
 +
 +    // [STRIFE] If the file does not exist, G_DoLoadLevel is called.
 +    if (save_stream == NULL)
 +    {
 +        G_DoLoadLevel();
 +        return;
 +    }
 +
 +    savegame_error = false;
 +
 +    if (!P_ReadSaveGameHeader())
 +    {
 +        fclose(save_stream);
 +        return;
 +    }
 +
 +    // haleyjd: A comment would be good here, fraggle...
 +    // Evidently this is a Choco-ism, necessitated by reading the savegame
 +    // header *before* calling G_DoLoadLevel.
 +    savedleveltime = leveltime;
 +
 +    // load a base level
 +
 +    // STRIFE-TODO: ????
 +    if(userload)
 +        G_InitNew(gameskill, gamemap);
 +    else
 +        G_DoLoadLevel();
 +
 +    leveltime = savedleveltime;
 +
 +    // dearchive all the modifications
 +    // [STRIFE] some portions of player_t are not overwritten when loading
 +    //   between hub levels
 +    P_UnArchivePlayers (userload);
 +    P_UnArchiveWorld ();
 +    P_UnArchiveThinkers ();
 +    P_UnArchiveSpecials ();
 +
 +    if (!P_ReadSaveGameEOF())
 +        I_Error ("Bad savegame");
 +
 +    fclose(save_stream);
 +
 +    if (setsizeneeded)
 +        R_ExecuteSetViewSize ();
 +
 +    // draw the pattern into the back screen
 +    R_FillBackScreen ();
 +}
 +
 +//
 +// G_WriteSaveName
 +//
 +// haleyjd 2010103: [STRIFE] New function
 +//
 +// Writes the character name to the NAME file.
 +//
 +boolean G_WriteSaveName(int slot, const char *charname)
 +{
 +    //char savedir[16];
 +    char *tmpname;
 +    boolean retval;
 +
 +    savegameslot = slot;
 +
 +    // haleyjd: removed special -cdrom treatment, as I believe it is taken
 +    // care of automatically via using Choco's savegamedir setting.
 +
 +    // haleyjd: free previous path, if any, and allocate new one using
 +    // M_SafeFilePath routine, which isn't limited to 128 characters.
 +    if(savepathtemp)
 +        Z_Free(savepathtemp);
 +    savepathtemp = M_SafeFilePath(savegamedir, "strfsav6.ssg");
 +
 +    // haleyjd: as above.
 +    if(savepath)
 +        Z_Free(savepath);
 +    savepath = M_SafeFilePath(savegamedir, M_MakeStrifeSaveDir(savegameslot, ""));
 +
 +    // haleyjd: memset full character_name for safety
 +    memset(character_name, 0, CHARACTER_NAME_LEN);
 +    strcpy(character_name, charname);
 +
 +    // haleyjd: use M_SafeFilePath
 +    tmpname = M_SafeFilePath(savepathtemp, "name");
 +
 +    // Write the "name" file under the directory
 +    retval = M_WriteFile(tmpname, character_name, 32);
 +
 +    Z_Free(tmpname);
 +
 +    return retval;
 +}
 +
 +//
 +// G_SaveGame
 +// Called by the menu task.
 +// Description is a 24 byte text string
 +//
 +// [STRIFE] No such function, at least in v1.2
 +// STRIFE-TODO: Does this make a comeback in v1.31?
 +/*
 +void
 +G_SaveGame
 +( int	slot,
 +  char*	description )
 +{
 +    savegameslot = slot;
 +    strcpy (savedescription, description);
 +    sendsave = true;
 +}
 +*/
 +
 +void G_DoSaveGame (char *path)
 +{
 +    char *current_path;
 +    char *savegame_file;
 +    char *temp_savegame_file;
 +    byte gamemapbytes[4];
 +    char gamemapstr[33];
 +
 +    temp_savegame_file = P_TempSaveGameFile();
 +
 +    // [STRIFE] custom save file path logic
 +    memset(gamemapstr, 0, sizeof(gamemapstr));
 +    sprintf(gamemapstr, "%d", gamemap);
 +    savegame_file = M_SafeFilePath(path, gamemapstr);
 +
 +    // [STRIFE] write the "current" file, which tells which hub map
 +    //   the save slot is currently on.
 +    current_path = M_SafeFilePath(path, "current");
 +    // haleyjd: endian-agnostic IO
 +    gamemapbytes[0] = (byte)( gamemap        & 0xff);
 +    gamemapbytes[1] = (byte)((gamemap >>  8) & 0xff);
 +    gamemapbytes[2] = (byte)((gamemap >> 16) & 0xff);
 +    gamemapbytes[3] = (byte)((gamemap >> 24) & 0xff);
 +    M_WriteFile(current_path, gamemapbytes, 4);
 +    Z_Free(current_path);
 +
 +    // Open the savegame file for writing.  We write to a temporary file
 +    // and then rename it at the end if it was successfully written.
 +    // This prevents an existing savegame from being overwritten by
 +    // a corrupted one, or if a savegame buffer overrun occurs.
 +
 +    save_stream = fopen(temp_savegame_file, "wb");
 +
 +    if (save_stream == NULL)
 +    {
 +        return;
 +    }
 +
 +    savegame_error = false;
 +
 +    P_WriteSaveGameHeader(savedescription);
 +
 +    P_ArchivePlayers ();
 +    P_ArchiveWorld ();
 +    P_ArchiveThinkers ();
 +    P_ArchiveSpecials ();
 +
 +    P_WriteSaveGameEOF();
 +
 +    // Enforce the same savegame size limit as in Vanilla Doom,
 +    // except if the vanilla_savegame_limit setting is turned off.
 +    // [STRIFE]: Verified subject to same limit.
 +
 +    if (vanilla_savegame_limit && ftell(save_stream) > SAVEGAMESIZE)
 +    {
 +        I_Error ("Savegame buffer overrun");
 +    }
 +
 +    // Finish up, close the savegame file.
 +
 +    fclose(save_stream);
 +
 +    // Now rename the temporary savegame file to the actual savegame
 +    // file, overwriting the old savegame if there was one there.
 +
 +    remove(savegame_file);
 +    rename(temp_savegame_file, savegame_file);
 +
 +    // haleyjd: free the savegame_file path
 +    Z_Free(savegame_file);
 +
 +    gameaction = ga_nothing;
 +    //strcpy(savedescription, "");
 +
 +    // [STRIFE]: custom message logic
 +    if(!strcmp(path, savepath))
 +    {
 +        sprintf(savename, "%s saved.", character_name);
 +        players[consoleplayer].message = savename;
 +    }
 +
 +    // draw the pattern into the back screen
 +    R_FillBackScreen ();
 +}
 +
 +
 +//
 +skill_t d_skill;
 +//int     d_episode; [STRIFE] No such thing as episodes in Strife
 +int     d_map;
 +
 +//
 +// G_DeferedInitNew
 +//
 +// Can be called by the startup code or the menu task,
 +// consoleplayer, displayplayer, playeringame[] should be set.
 +//
 +// haleyjd 09/22/10: [STRIFE] Removed episode parameter
 +//
 +void G_DeferedInitNew(skill_t skill, int map)
 +{
 +    d_skill = skill;
 +    d_map = map;
 +    gameaction = ga_newgame;
 +}
 +
 +//
 +// G_DoNewGame
 +//
 +// [STRIFE] Code added to turn off the stonecold effect.
 +//   Someone also removed the nomonsters reset...
 +//
 +void G_DoNewGame (void)
 +{
 +    demoplayback = false;
 +    netdemo = false;
 +    netgame = false;
 +    deathmatch = false;
 +    playeringame[1] = playeringame[2] = playeringame[3] = 0;
 +    respawnparm = false;
 +    fastparm = false;
 +    stonecold = false;      // villsa [STRIFE]
 +    //nomonsters = false;   [STRIFE] not set here!?!
 +    consoleplayer = 0;
 +    G_InitNew (d_skill, d_map);
 +    gameaction = ga_nothing;
 +}
 +
 +// The sky texture to be used instead of the F_SKY1 dummy.
 +extern  int	skytexture;
 +
 +//
 +// G_InitNew
 +//
 +// haleyjd 08/24/10: [STRIFE]:
 +// * Added riftdest initialization
 +// * Removed episode parameter
 +//
 +void
 +G_InitNew
 +( skill_t       skill,
 +  int           map )
 +{
 +    char *skytexturename;
 +    int             i;
 +
 +    if (paused)
 +    {
 +        paused = false;
 +        S_ResumeSound ();
 +    }
 +
 +    if (skill > sk_nightmare)
 +        skill = sk_nightmare;
 +
 +    // [STRIFE] Removed episode nonsense and gamemap clipping
 +
 +    M_ClearRandom ();
 +
 +    if (skill == sk_nightmare || respawnparm )
 +        respawnmonsters = true;
 +    else
 +        respawnmonsters = false;
 +
 +    // [STRIFE] Strife skill level mobjinfo/states tweaking
 +    // BUG: None of this code runs properly when loading save games, so
 +    // basically it's impossible to play any skill level properly unless
 +    // you never quit and reload from the command line.
 +    if(!skill && gameskill)
 +    {
 +        // Setting to Baby skill... make things easier.
 +
 +        // Acolytes walk, attack, and feel pain slower
 +        for(i = S_AGRD_13; i <= S_AGRD_23; i++)
 +            states[i].tics *= 2;
 +
 +        // Reavers attack slower
 +        for(i = S_ROB1_10; i <= S_ROB1_15; i++)
 +            states[i].tics *= 2;
 +
 +        // Turrets attack slower
 +        for(i = S_TURT_02; i <= S_TURT_03; i++)
 +            states[i].tics *= 2;
 +
 +        // Crusaders attack and feel pain slower
 +        for(i = S_ROB2_09; i <= S_ROB2_19; i++)
 +            states[i].tics *= 2;
 +
 +        // Stalkers think, walk, and attack slower
 +        for(i = S_SPID_03; i <= S_SPID_10; i++)
 +            states[i].tics *= 2;
 +
 +        // The Bishop's homing missiles are faster (what?? BUG?)
 +        mobjinfo[MT_SEEKMISSILE].speed *= 2;
 +    }
 +    if(skill && !gameskill)
 +    {
 +        // Setting a higher skill when previously on baby... make things normal
 +
 +        // Acolytes
 +        for(i = S_AGRD_13; i <= S_AGRD_23; i++)
 +            states[i].tics >>= 1;
 +
 +        // Reavers
 +        for(i = S_ROB1_10; i <= S_ROB1_15; i++)
 +            states[i].tics >>= 1;
 +
 +        // Turrets
 +        for(i = S_TURT_02; i <= S_TURT_03; i++)
 +            states[i].tics >>= 1;
 +
 +        // Crusaders
 +        for(i = S_ROB2_09; i <= S_ROB2_19; i++)
 +            states[i].tics >>= 1;
 +
 +        // Stalkers
 +        for(i = S_SPID_03; i <= S_SPID_10; i++)
 +            states[i].tics >>= 1;
 +
 +        // The Bishop's homing missiles - again, seemingly backward.
 +        mobjinfo[MT_SEEKMISSILE].speed >>= 1;
 +    }
 +    if(fastparm || (skill == sk_nightmare && skill != gameskill))
 +    {
 +        // BLOODBATH! Make some things super-aggressive.
 +
 +        // Acolytes walk, attack, and feel pain twice as fast
 +        // (This makes just getting out of the first room almost impossible)
 +        for(i = S_AGRD_13; i <= S_AGRD_23; i++)
 +            states[i].tics >>= 1;
 +
 +        // Bishop's homing missiles again get SLOWER and not faster o_O
 +        mobjinfo[MT_SEEKMISSILE].speed >>= 1;
 +    }
 +    else if(skill != sk_nightmare && gameskill == sk_nightmare)
 +    {
 +        // Setting back to an ordinary skill after being on Bloodbath?
 +        // Put stuff back to normal.
 +
 +        // Acolytes
 +        for(i = S_AGRD_13; i <= S_AGRD_23; i++)
 +            states[i].tics *= 2;
 +
 +        // Bishop's homing missiles
 +        mobjinfo[MT_SEEKMISSILE].speed *= 2;
 +    }
 +
 +    // force players to be initialized upon first level load
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +        players[i].playerstate = PST_REBORN;
 +
 +    usergame = true;                // will be set false if a demo
 +    paused = false;
 +    demoplayback = false;
 +    automapactive = false;
 +    viewactive = true;
 +    //gameepisode = episode; [STRIFE] no episodes
 +    gamemap = map;
 +    gameskill = skill;
 +    riftdest = 0; // haleyjd 08/24/10: [STRIFE] init riftdest to zero on new game
 +
 +    // Set the sky to use.
 +    //
 +    // Note: This IS broken, but it is how Vanilla Doom behaves.
 +    // See http://doom.wikia.com/wiki/Sky_never_changes_in_Doom_II.
 +    //
 +    // Because we set the sky here at the start of a game, not at the
 +    // start of a level, the sky texture never changes unless we
 +    // restore from a saved game.  This was fixed before the Doom
 +    // source release, but this IS the way Vanilla DOS Doom behaves.
 +
 +    // [STRIFE] Strife skies (of which there are but two)
 +    if(gamemap >= 9 && gamemap < 32)
 +        skytexturename = "skymnt01";
 +    else
 +        skytexturename = "skymnt02";
 +
 +    skytexturename = DEH_String(skytexturename);
 +
 +    skytexture = R_TextureNumForName(skytexturename);
 +
 +    // [STRIFE] HUBS
 +    G_LoadPath(gamemap);
 +    G_DoLoadLevel();
 +}
 +
 +
 +//
 +// DEMO RECORDING
 +//
 +#define DEMOMARKER		0x80
 +
 +//
 +// G_ReadDemoTiccmd
 +//
 +// [STRIFE] Modified for Strife ticcmd_t
 +//
 +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->buttons2 = (unsigned char)*demo_p++; // [STRIFE]
 +    cmd->inventory = (int)*demo_p++;          // [STRIFE]
 +}
 +
 +// Increase the size of the demo buffer to allow unlimited demos
 +
 +static void IncreaseDemoBuffer(void)
 +{
 +    int current_length;
 +    byte *new_demobuffer;
 +    byte *new_demop;
 +    int new_length;
 +
 +    // Find the current size
 +
 +    current_length = demoend - demobuffer;
 +
 +    // Generate a new buffer twice the size
 +    new_length = current_length * 2;
 +
 +    new_demobuffer = Z_Malloc(new_length, PU_STATIC, 0);
 +    new_demop = new_demobuffer + (demo_p - demobuffer);
 +
 +    // Copy over the old data
 +
 +    memcpy(new_demobuffer, demobuffer, current_length);
 +
 +    // Free the old buffer and point the demo pointers at the new buffer.
 +
 +    Z_Free(demobuffer);
 +
 +    demobuffer = new_demobuffer;
 +    demo_p = new_demop;
 +    demoend = demobuffer + new_length;
 +}
 +
 +//
 +// G_WriteDemoTiccmd
 +//
 +// [STRIFE] Modified for Strife ticcmd_t.
 +//
 +void G_WriteDemoTiccmd (ticcmd_t* cmd)
 +{
 +    byte *demo_start;
 +
 +    if (gamekeydown[key_demo_quit])           // press q to end demo recording
 +        G_CheckDemoStatus ();
 +
 +    demo_start = demo_p;
 +
 +    *demo_p++ = cmd->forwardmove;
 +    *demo_p++ = cmd->sidemove;
 +    *demo_p++ = cmd->angleturn >> 8;
 +    *demo_p++ = cmd->buttons;
 +    *demo_p++ = cmd->buttons2;                 // [STRIFE]
 +    *demo_p++ = (byte)(cmd->inventory & 0xff); // [STRIFE]
 +
 +    // reset demo pointer back
 +    demo_p = demo_start;
 +
 +    if (demo_p > demoend - 16)
 +    {
 +        if (vanilla_demo_limit)
 +        {
 +            // no more space
 +            G_CheckDemoStatus ();
 +            return;
 +        }
 +        else
 +        {
 +            // Vanilla demo limit disabled: unlimited
 +            // demo lengths!
 +
 +            IncreaseDemoBuffer();
 +        }
 +    }
 +
 +    G_ReadDemoTiccmd (cmd);         // make SURE it is exactly the same
 +}
 +
 +
 +
 +//
 +// G_RecordDemo
 +//
 +// [STRIFE] Verified unmodified
 +//
 +void G_RecordDemo (char* name)
 +{
 +    int             i;
 +    int             maxsize;
 +
 +    usergame = false;
-     strcpy (demoname, name);
-     strcat (demoname, ".lmp");
++    demoname = Z_Malloc(strlen(name) + 5, PU_STATIC, NULL);
++    sprintf(demoname, "%s.lmp", name);
 +    maxsize = 0x20000;
 +
 +    //!
 +    // @arg <size>
 +    // @category demo
 +    // @vanilla
 +    //
 +    // Specify the demo buffer size (KiB)
 +    //
 +
-     i = M_CheckParm ("-maxdemo");
-     if (i && i<myargc-1)
++    i = M_CheckParmWithArgs("-maxdemo", 1);
++    if (i)
 +        maxsize = atoi(myargv[i+1])*1024;
 +    demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL);
 +    demoend = demobuffer + maxsize;
 +
 +    demorecording = true;
 +}
 +
 +
 +void G_BeginRecording (void)
 +{
 +    int             i;
 +
 +    //!
 +    // @category demo
 +    //
 +    // Record a high resolution "Doom 1.91" demo.
 +    //
 +
 +    // STRIFE-TODO: if somebody makes a "Strife Plus", we could add this.
 +    /*
 +    longtics = M_CheckParm("-longtics") != 0;
 +    */
 +    longtics = false;
 +
 +    // If not recording a longtics demo, record in low res
 +    lowres_turn = !longtics;
 +
 +    demo_p = demobuffer;
 +
 +    // Save the right version code for this demo
 +    *demo_p++ = STRIFE_VERSION;
 +
 +    *demo_p++ = gameskill;
 +    //*demo_p++ = gameepisode; [STRIFE] Doesn't have episodes.
 +    *demo_p++ = gamemap;
 +    *demo_p++ = deathmatch;
 +    *demo_p++ = respawnparm;
 +    *demo_p++ = fastparm;
 +    *demo_p++ = nomonsters;
 +    *demo_p++ = consoleplayer;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +        *demo_p++ = playeringame[i];
 +
 +    // STRIFE-TODO: TEMPORARY: fill out the rest of playeringame until
 +    // MAXPLAYERS == 8
 +    for (; i < 8; i++)
 +        *demo_p++ = 0;
 +}
 +
 +
 +//
 +// G_PlayDemo
 +//
 +
 +char*	defdemoname;
 +
 +//
 +// G_DeferedPlayDemo
 +//
 +// [STRIFE] Verified unmodified
 +//
 +void G_DeferedPlayDemo (char* name)
 +{
 +    defdemoname = name;
 +    gameaction = ga_playdemo;
 +}
 +
 +// Generate a string describing a demo version
 +// [STRIFE] Modified to handle the one and only Strife demo version.
 +static char *DemoVersionDescription(int version)
 +{
 +    static char resultbuf[16];
 +
 +    // [STRIFE] All versions of Strife 1.1 and later use 101 as their
 +    // internal version number. Brilliant, huh? So we can't discern much
 +    // here.
 +
 +    switch (version)
 +    {
 +    case 100:
 +        return "v1.0"; // v1.0 would be the ancient demo version
 +    default:
 +        break;
 +    }
 +
 +    // Unknown version. Who knows?
 +    sprintf(resultbuf, "%i.%i (unknown)", version / 100, version % 100);
 +
 +    return resultbuf;
 +}
 +
 +//
 +// G_DoPlayDemo
 +//
 +// [STRIFE] Modified for Strife demo format.
 +//
 +void G_DoPlayDemo (void)
 +{
 +    skill_t skill;
 +    int     i, map;
 +    int     demoversion;
 +
 +    gameaction = ga_nothing;
 +    demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC);
 +
 +    demoversion = *demo_p++;
 +
 +    if (demoversion == STRIFE_VERSION)
 +    {
 +        longtics = false;
 +    }
 +    /* STRIFE-TODO: Not until/unless somebody makes a Strife-Plus :P
 +    else if (demoversion == DOOM_191_VERSION)
 +    {
 +        // demo recorded with cph's modified "v1.91" doom exe
 +        longtics = true;
 +    }
 +    */
 +    else
 +    {
 +        char *message = "Demo is from a different game version!\n"
 +                        "(read %i, should be %i)\n"
 +                        "\n"
 +                        "*** You may need to upgrade your version "
 +                            "of Strife to v1.1 or later. ***\n"
 +                        "    See: http://doomworld.com/files/patches.shtml\n"
 +                        "    This appears to be %s.";
 +
 +        I_Error(message, demoversion, STRIFE_VERSION,
 +                         DemoVersionDescription(demoversion));
 +    }
 +
 +    skill = *demo_p++;
 +    //episode = *demo_p++; [STRIFE] No episodes
 +    map = *demo_p++;
 +    deathmatch = *demo_p++;
 +    respawnparm = *demo_p++;
 +    fastparm = *demo_p++;
 +    nomonsters = *demo_p++;
 +    consoleplayer = *demo_p++;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +        playeringame[i] = *demo_p++;
 +
 +    // STRIFE-TODO: read out the rest of playeringame until
 +    // MAXPLAYERS is bumped to 8
 +    for(; i < 8; i++)
 +        ++demo_p;
 +
 +    //!
 +    // @category demo
 +    //
 +    // Play back a demo recorded in a netgame with a single player.
 +    //
 +
-     if (playeringame[1] || M_CheckParm("-netdemo") > 0)
-     {
-         netgame = true;
-         netdemo = true;
++    if (playeringame[1] || M_CheckParm("-solo-net") > 0
++                        || M_CheckParm("-netdemo") > 0)
++    {
++	netgame = true;
++	netdemo = true;
 +    }
 +
 +    // don't spend a lot of time in loadlevel
 +    precache = false;
 +    G_InitNew(skill, map);
 +    precache = true;
 +
 +    // [STRIFE] not here...
 +    //starttime = I_GetTime ();
 +
 +    usergame = false;
 +    demoplayback = true;
 +}
 +
 +//
 +// G_TimeDemo
 +//
 +// [STRIFE] Verified unmodified
 +//
 +void G_TimeDemo (char* name)
 +{
 +    //!
 +    // @vanilla
 +    //
 +    // Disable rendering the screen entirely.
 +    //
 +
 +    nodrawers = M_CheckParm ("-nodraw");
 +
 +    // haleyjd: STRIFE-TODO: where's -noblit?
 +
 +    timingdemo = true;
 +    singletics = true;
 +
 +    defdemoname = name;
 +    gameaction = ga_playdemo;
 +}
 +
 +
 +/*
 +===================
 +=
 += 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
 +===================
 +*/
 +//
 +// [STRIFE] Verified unmodified
 +//
 +boolean G_CheckDemoStatus (void)
 +{
 +    int             endtime;
 +
 +    if (timingdemo)
 +    {
 +        float fps;
 +        int realtics;
 +
 +        endtime = I_GetTime ();
 +        realtics = endtime - starttime;
 +        fps = ((float) gametic * TICRATE) / realtics;
 +
 +        // Prevent recursive calls
 +        timingdemo = false;
 +        demoplayback = false;
 +
 +        I_Error ("timed %i gametics in %i realtics (%f fps)",
 +                 gametic, realtics, fps);
 +    }
 +
 +    if (demoplayback)
 +    {
 +        W_ReleaseLumpName(defdemoname);
 +        demoplayback = false;
 +        netdemo = false;
 +        netgame = false;
 +        deathmatch = false;
 +        playeringame[1] = playeringame[2] = playeringame[3] = 0;
 +        respawnparm = false;
 +        fastparm = false;
 +        nomonsters = false;
 +        consoleplayer = 0;
 +
 +        if (singledemo)
 +            I_Quit ();
 +        else
 +            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;
 +}
 +
 +
 +
diff --cc src/strife/m_menu.c
index 92564e96,00000000..4d16e3e8
mode 100644,000000..100644
--- a/src/strife/m_menu.c
+++ b/src/strife/m_menu.c
@@@ -1,2314 -1,0 +1,2314 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	DOOM selection menu, options, episode etc.
 +//	Sliders and icons. Kinda widget stuff.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#include <stdlib.h>
 +#include <ctype.h>
 +
 +
 +#include "doomdef.h"
 +#include "doomkeys.h"
 +#include "dstrings.h"
 +
 +#include "d_main.h"
 +#include "deh_main.h"
 +
 +#include "i_swap.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "z_zone.h"
 +#include "v_video.h"
 +#include "w_wad.h"
 +
 +#include "r_local.h"
 +
 +
 +#include "hu_stuff.h"
 +
 +#include "g_game.h"
 +
 +#include "m_argv.h"
 +#include "m_controls.h"
 +#include "m_saves.h"    // [STRIFE]
 +#include "p_saveg.h"
 +
 +#include "s_sound.h"
 +
 +#include "doomstat.h"
 +
 +// Data.
 +#include "sounds.h"
 +
 +#include "m_menu.h"
 +#include "p_dialog.h"
 +
 +
 +extern void M_QuitStrife(int);
 +
 +extern patch_t*         hu_font[HU_FONTSIZE];
 +extern boolean          message_dontfuckwithme;
 +
 +extern boolean          chat_on;        // in heads-up code
 +extern boolean          sendsave;       // [STRIFE]
 +
 +//
 +// defaulted values
 +//
 +int			mouseSensitivity = 5;
 +
 +// Show messages has default, 0 = off, 1 = on
 +int			showMessages = 1;
 +
 +
 +// Blocky mode, has default, 0 = high, 1 = normal
 +int			detailLevel = 0;
 +int			screenblocks = 9;
 +
 +// temp for screenblocks (0-9)
 +int			screenSize;
 +
 +// -1 = no quicksave slot picked!
 +int			quickSaveSlot;
 +
 + // 1 = message to be printed
 +int			messageToPrint;
 +// ...and here is the message string!
 +char*			messageString;
 +
 +// message x & y
 +int			messx;
 +int			messy;
 +int			messageLastMenuActive;
 +
 +// timed message = no input from user
 +boolean			messageNeedsInput;
 +
 +void    (*messageRoutine)(int response);
 +
 +char gammamsg[5][26] =
 +{
 +    GAMMALVL0,
 +    GAMMALVL1,
 +    GAMMALVL2,
 +    GAMMALVL3,
 +    GAMMALVL4
 +};
 +
 +// we are going to be entering a savegame string
 +int			saveStringEnter;
 +int             	saveSlot;	// which slot to save in
 +int			saveCharIndex;	// which char we're editing
 +// old save description before edit
 +char			saveOldString[SAVESTRINGSIZE];
 +
 +boolean                 inhelpscreens;
 +boolean                 menuactive;
 +boolean                 menupause;     // haleyjd 08/29/10: [STRIFE] New global
 +int                     menupausetime; // haleyjd 09/04/10: [STRIFE] New global
 +boolean                 menuindialog;  // haleyjd 09/04/10: ditto
 +
 +// haleyjd 08/27/10: [STRIFE] SKULLXOFF == -28, LINEHEIGHT == 19
 +#define CURSORXOFF		-28
 +#define LINEHEIGHT		19
 +
 +extern boolean		sendpause;
 +char			savegamestrings[10][SAVESTRINGSIZE];
 +
 +char	endstring[160];
 +
 +// haleyjd 09/04/10: [STRIFE] Moved menuitem / menu structures into header
 +// because they are needed externally by the dialog engine.
 +
 +// haleyjd 08/27/10: [STRIFE] skull* stuff changed to cursor* stuff
 +short		itemOn;			// menu item skull is on
 +short		cursorAnimCounter;	// skull animation counter
 +short		whichCursor;		// which skull to draw
 +
 +// graphic name of cursors
 +// haleyjd 08/27/10: [STRIFE] M_SKULL* -> M_CURS*
 +char    *cursorName[8] = {"M_CURS1", "M_CURS2", "M_CURS3", "M_CURS4",
 +                          "M_CURS5", "M_CURS6", "M_CURS7", "M_CURS8" };
 +
 +// haleyjd 20110210 [STRIFE]: skill level for menus
 +int menuskill;
 +
 +// current menudef
 +menu_t*	currentMenu;
 +
 +//
 +// PROTOTYPES
 +//
 +void M_NewGame(int choice);
 +void M_Episode(int choice);
 +void M_ChooseSkill(int choice);
 +void M_LoadGame(int choice);
 +void M_SaveGame(int choice);
 +void M_Options(int choice);
 +void M_EndGame(int choice);
 +void M_ReadThis(int choice);
 +void M_ReadThis2(int choice);
 +void M_ReadThis3(int choice); // [STRIFE]
 +void M_QuitStrife(int choice);
 +
 +void M_ChangeMessages(int choice);
 +void M_ChangeSensitivity(int choice);
 +void M_SfxVol(int choice);
 +void M_VoiceVol(int choice); // [STRIFE]
 +void M_MusicVol(int choice);
 +void M_SizeDisplay(int choice);
 +void M_StartGame(int choice);
 +void M_Sound(int choice);
 +
 +//void M_FinishReadThis(int choice); - [STRIFE] unused
 +void M_LoadSelect(int choice);
 +void M_SaveSelect(int choice);
 +void M_ReadSaveStrings(void);
 +void M_QuickSave(void);
 +void M_QuickLoad(void);
 +
 +void M_DrawMainMenu(void);
 +void M_DrawReadThis1(void);
 +void M_DrawReadThis2(void);
 +void M_DrawReadThis3(void); // [STRIFE]
 +void M_DrawNewGame(void);
 +void M_DrawEpisode(void);
 +void M_DrawOptions(void);
 +void M_DrawSound(void);
 +void M_DrawLoad(void);
 +void M_DrawSave(void);
 +
 +void M_DrawSaveLoadBorder(int x,int y);
 +void M_SetupNextMenu(menu_t *menudef);
 +void M_DrawThermo(int x,int y,int thermWidth,int thermDot);
 +void M_DrawEmptyCell(menu_t *menu,int item);
 +void M_DrawSelCell(menu_t *menu,int item);
 +int  M_StringWidth(char *string);
 +int  M_StringHeight(char *string);
 +void M_StartControlPanel(void);
 +void M_StartMessage(char *string,void *routine,boolean input);
 +void M_StopMessage(void);
 +void M_ClearMenus (int choice);
 +
 +
 +
 +
 +//
 +// DOOM MENU
 +//
 +enum
 +{
 +    newgame = 0,
 +    options,
 +    loadgame,
 +    savegame,
 +    readthis,
 +    quitdoom,
 +    main_end
 +} main_e;
 +
 +menuitem_t MainMenu[]=
 +{
 +    {1,"M_NGAME",M_NewGame,'n'},
 +    {1,"M_OPTION",M_Options,'o'},
 +    {1,"M_LOADG",M_LoadGame,'l'},
 +    {1,"M_SAVEG",M_SaveGame,'s'},
 +    // Another hickup with Special edition.
 +    {1,"M_RDTHIS",M_ReadThis,'h'}, // haleyjd 08/28/10: 'r' -> 'h'
 +    {1,"M_QUITG",M_QuitStrife,'q'}
 +};
 +
 +menu_t  MainDef =
 +{
 +    main_end,
 +    NULL,
 +    MainMenu,
 +    M_DrawMainMenu,
 +    97,45, // haleyjd 08/28/10: [STRIFE] changed y coord
 +    0
 +};
 +
 +
 +//
 +// EPISODE SELECT
 +//
 +/*
 +enum
 +{
 +    ep1,
 +    ep2,
 +    ep3,
 +    ep4,
 +    ep_end
 +} episodes_e;
 +
 +menuitem_t EpisodeMenu[]=
 +{
 +    {1,"M_EPI1", M_Episode,'k'},
 +    {1,"M_EPI2", M_Episode,'t'},
 +    {1,"M_EPI3", M_Episode,'i'},
 +    {1,"M_EPI4", M_Episode,'t'}
 +};
 +
 +menu_t  EpiDef =
 +{
 +    ep_end,		// # of menu items
 +    &MainDef,		// previous menu
 +    EpisodeMenu,	// menuitem_t ->
 +    M_DrawEpisode,	// drawing routine ->
 +    48,63,              // x,y
 +    ep1			// lastOn
 +};
 +*/
 +
 +//
 +// NEW GAME
 +//
 +enum
 +{
 +    killthings,
 +    toorough,
 +    hurtme,
 +    violence,
 +    nightmare,
 +    newg_end
 +} newgame_e;
 +
 +menuitem_t NewGameMenu[]=
 +{
 +    // haleyjd 08/28/10: [STRIFE] changed all shortcut letters
 +    {1,"M_JKILL",   M_ChooseSkill, 't'},
 +    {1,"M_ROUGH",   M_ChooseSkill, 'r'},
 +    {1,"M_HURT",    M_ChooseSkill, 'v'},
 +    {1,"M_ULTRA",   M_ChooseSkill, 'e'},
 +    {1,"M_NMARE",   M_ChooseSkill, 'b'}
 +};
 +
 +menu_t  NewDef =
 +{
 +    newg_end,           // # of menu items
 +    &MainDef,           // previous menu - haleyjd [STRIFE] changed to MainDef
 +    NewGameMenu,        // menuitem_t ->
 +    M_DrawNewGame,      // drawing routine ->
 +    48,63,              // x,y
 +    toorough            // lastOn - haleyjd [STRIFE]: default to skill 1
 +};
 +
 +//
 +// OPTIONS MENU
 +//
 +enum
 +{
 +    // haleyjd 08/28/10: [STRIFE] Removed messages, mouse sens., detail.
 +    endgame,
 +    scrnsize,
 +    option_empty1,
 +    soundvol,
 +    opt_end
 +} options_e;
 +
 +menuitem_t OptionsMenu[]=
 +{
 +    // haleyjd 08/28/10: [STRIFE] Removed messages, mouse sens., detail.
 +    {1,"M_ENDGAM",	M_EndGame,'e'},
 +    {2,"M_SCRNSZ",	M_SizeDisplay,'s'},
 +    {-1,"",0,'\0'},
 +    {1,"M_SVOL",	M_Sound,'s'}
 +};
 +
 +menu_t  OptionsDef =
 +{
 +    opt_end,
 +    &MainDef,
 +    OptionsMenu,
 +    M_DrawOptions,
 +    60,37,
 +    0
 +};
 +
 +//
 +// Read This! MENU 1 & 2 & [STRIFE] 3
 +//
 +enum
 +{
 +    rdthsempty1,
 +    read1_end
 +} read_e;
 +
 +menuitem_t ReadMenu1[] =
 +{
 +    {1,"",M_ReadThis2,0}
 +};
 +
 +menu_t  ReadDef1 =
 +{
 +    read1_end,
 +    &MainDef,
 +    ReadMenu1,
 +    M_DrawReadThis1,
 +    280,185,
 +    0
 +};
 +
 +enum
 +{
 +    rdthsempty2,
 +    read2_end
 +} read_e2;
 +
 +menuitem_t ReadMenu2[]=
 +{
 +    {1,"",M_ReadThis3,0} // haleyjd 08/28/10: [STRIFE] Go to ReadThis3
 +};
 +
 +menu_t  ReadDef2 =
 +{
 +    read2_end,
 +    &ReadDef1,
 +    ReadMenu2,
 +    M_DrawReadThis2,
 +    250,185, // haleyjd 08/28/10: [STRIFE] changed coords
 +    0
 +};
 +
 +// haleyjd 08/28/10: Added Read This! menu 3
 +enum
 +{
 +    rdthsempty3,
 +    read3_end
 +} read_e3;
 +
 +menuitem_t ReadMenu3[]=
 +{
 +    {1,"",M_ClearMenus,0}
 +};
 +
 +menu_t  ReadDef3 =
 +{
 +    read3_end,
 +    &ReadDef2,
 +    ReadMenu3,
 +    M_DrawReadThis3,
 +    250, 185,
 +    0
 +};
 +
 +//
 +// SOUND VOLUME MENU
 +//
 +enum
 +{
 +    sfx_vol,
 +    sfx_empty1,
 +    music_vol,
 +    sfx_empty2,
 +    voice_vol,
 +    sfx_empty3,
 +    sfx_mouse,
 +    sfx_empty4,
 +    sound_end
 +} sound_e;
 +
 +// haleyjd 08/29/10:
 +// [STRIFE]
 +// * Added voice volume
 +// * Moved mouse sensitivity here (who knows why...)
 +menuitem_t SoundMenu[]=
 +{
 +    {2,"M_SFXVOL",M_SfxVol,'s'},
 +    {-1,"",0,'\0'},
 +    {2,"M_MUSVOL",M_MusicVol,'m'},
 +    {-1,"",0,'\0'},
 +    {2,"M_VOIVOL",M_VoiceVol,'v'},
 +    {-1,"",0,'\0'},
 +    {2,"M_MSENS",M_ChangeSensitivity,'m'},
 +    {-1,"",0,'\0'}
 +};
 +
 +menu_t  SoundDef =
 +{
 +    sound_end,
 +    &OptionsDef,
 +    SoundMenu,
 +    M_DrawSound,
 +    80,35,       // [STRIFE] changed y coord 64 -> 35
 +    0
 +};
 +
 +//
 +// LOAD GAME MENU
 +//
 +enum
 +{
 +    load1,
 +    load2,
 +    load3,
 +    load4,
 +    load5,
 +    load6,
 +    load_end
 +} load_e;
 +
 +menuitem_t LoadMenu[]=
 +{
 +    {1,"", M_LoadSelect,'1'},
 +    {1,"", M_LoadSelect,'2'},
 +    {1,"", M_LoadSelect,'3'},
 +    {1,"", M_LoadSelect,'4'},
 +    {1,"", M_LoadSelect,'5'},
 +    {1,"", M_LoadSelect,'6'}
 +};
 +
 +menu_t  LoadDef =
 +{
 +    load_end,
 +    &MainDef,
 +    LoadMenu,
 +    M_DrawLoad,
 +    80,54,
 +    0
 +};
 +
 +//
 +// SAVE GAME MENU
 +//
 +menuitem_t SaveMenu[]=
 +{
 +    {1,"", M_SaveSelect,'1'},
 +    {1,"", M_SaveSelect,'2'},
 +    {1,"", M_SaveSelect,'3'},
 +    {1,"", M_SaveSelect,'4'},
 +    {1,"", M_SaveSelect,'5'},
 +    {1,"", M_SaveSelect,'6'}
 +};
 +
 +menu_t  SaveDef =
 +{
 +    load_end,
 +    &MainDef,
 +    SaveMenu,
 +    M_DrawSave,
 +    80,54,
 +    0
 +};
 +
 +void M_DrawNameChar(void);
 +
 +//
 +// NAME CHARACTER MENU
 +//
 +// [STRIFE]
 +// haleyjd 20110210: New "Name Your Character" Menu
 +//
 +menu_t NameCharDef =
 +{
 +    load_end,
 +    &NewDef,
 +    SaveMenu,
 +    M_DrawNameChar,
 +    80,54,
 +    0
 +};
 +
 +
 +//
 +// M_ReadSaveStrings
 +//  read the strings from the savegame files
 +//
 +// [STRIFE]
 +// haleyjd 20110210: Rewritten to read "name" file in each slot directory
 +//
 +void M_ReadSaveStrings(void)
 +{
 +    FILE *handle;
 +    int   count;
 +    int   i;
 +    char *fname = NULL;
 +
 +    for(i = 0; i < load_end; i++)
 +    {
 +        if(fname)
 +            Z_Free(fname);
 +        fname = M_SafeFilePath(savegamedir, M_MakeStrifeSaveDir(i, "\\name"));
 +
 +        handle = fopen(fname, "rb");
 +        if(handle == NULL)
 +        {
 +            strcpy(savegamestrings[i], EMPTYSTRING);
 +            LoadMenu[i].status = 0;
 +            continue;
 +        }
 +        count = fread(savegamestrings[i], 1, SAVESTRINGSIZE, handle);
 +        fclose(handle);
 +        LoadMenu[i].status = 1;
 +    }
 +
 +    if(fname)
 +        Z_Free(fname);
 +}
 +
 +//
 +// M_DrawNameChar
 +//
 +// haleyjd 09/22/10: [STRIFE] New function
 +// Handler for drawing the "Name Your Character" menu.
 +//
 +void M_DrawNameChar(void)
 +{
 +    int i;
 +
 +    M_WriteText(72, 28, DEH_String("Name Your Character"));
 +
 +    for (i = 0;i < load_end; i++)
 +    {
 +        M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
 +        M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
 +    }
 +
 +    if (saveStringEnter)
 +    {
 +        i = M_StringWidth(savegamestrings[quickSaveSlot]);
 +        M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*quickSaveSlot,"_");
 +    }
 +}
 +
 +//
 +// M_DoNameChar
 +//
 +// haleyjd 09/22/10: [STRIFE] New function
 +// Handler for items in the "Name Your Character" menu.
 +//
 +void M_DoNameChar(int choice)
 +{
 +    int map;
 +
 +    sendsave = 1;
 +    ClearTmp();
 +    G_WriteSaveName(choice, savegamestrings[choice]);
 +    quickSaveSlot = choice;  // VERIFY VARIABLE
 +    SaveDef.lastOn = choice;
 +    ClearSlot();
 +    FromCurr();
 +
 +    if(isdemoversion)
 +        map = 33;
 +    else
 +        map = 2;
 +
 +    G_DeferedInitNew(menuskill, map);
 +    M_ClearMenus(0);
 +}
 +
 +//
 +// M_LoadGame & Cie.
 +//
 +void M_DrawLoad(void)
 +{
 +    int             i;
 +
 +    V_DrawPatchDirect(72, 28,
 +                      W_CacheLumpName(DEH_String("M_LOADG"), PU_CACHE));
 +
 +    for (i = 0;i < load_end; i++)
 +    {
 +        M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
 +        M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
 +    }
 +}
 +
 +
 +
 +//
 +// Draw border for the savegame description
 +//
 +void M_DrawSaveLoadBorder(int x,int y)
 +{
 +    int             i;
 +
 +    V_DrawPatchDirect(x - 8, y + 7,
 +                      W_CacheLumpName(DEH_String("M_LSLEFT"), PU_CACHE));
 +
 +    for (i = 0;i < 24;i++)
 +    {
 +        V_DrawPatchDirect(x, y + 7,
 +                          W_CacheLumpName(DEH_String("M_LSCNTR"), PU_CACHE));
 +        x += 8;
 +    }
 +
 +    V_DrawPatchDirect(x, y + 7,
 +                      W_CacheLumpName(DEH_String("M_LSRGHT"), PU_CACHE));
 +}
 +
 +
 +
 +//
 +// User wants to load this game
 +//
 +void M_LoadSelect(int choice)
 +{
 +    /*
 +    char    name[256];
 +
 +    strcpy(name, P_SaveGameFile(choice));
 +
 +    G_LoadGame (name);
 +    M_ClearMenus (0);
 +    */
 +
 +    // [STRIFE] (v1.2) - TODO: v1.31
 +    char *name = NULL;
 +
 +    G_WriteSaveName(choice, savegamestrings[choice]);
 +    ToCurr();
 +
 +    name = M_SafeFilePath(savegamedir, M_MakeStrifeSaveDir(choice, ""));
 +
 +    G_ReadCurrent(name);
 +    quickSaveSlot = choice;
 +    M_ClearMenus(0);
 +
 +    Z_Free(name);
 +}
 +
 +//
 +// Selected from DOOM menu
 +//
 +// [STRIFE] Verified unmodified (v1.2) - TODO: v1.31
 +//
 +void M_LoadGame (int choice)
 +{
 +    if (netgame)
 +    {
 +        M_StartMessage(DEH_String(LOADNET), NULL, false);
 +        return;
 +    }
 +
 +    M_SetupNextMenu(&LoadDef);
 +    M_ReadSaveStrings();
 +}
 +
 +
 +//
 +//  M_SaveGame & Cie.
 +//
 +void M_DrawSave(void)
 +{
 +    int             i;
 +
 +    V_DrawPatchDirect(72, 28, W_CacheLumpName(DEH_String("M_SAVEG"), PU_CACHE));
 +    for (i = 0;i < load_end; i++)
 +    {
 +        M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
 +        M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
 +    }
 +
 +    if (saveStringEnter)
 +    {
 +        i = M_StringWidth(savegamestrings[quickSaveSlot]);
 +        M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*quickSaveSlot,"_");
 +    }
 +}
 +
 +//
 +// M_Responder calls this when user is finished
 +//
 +void M_DoSave(int slot)
 +{
 +    /*
 +    G_SaveGame (slot,savegamestrings[slot]);
 +    M_ClearMenus (0);
 +
 +    // PICK QUICKSAVE SLOT YET?
 +    if (quickSaveSlot == -2)
 +        quickSaveSlot = slot;
 +    */
 +    // [STRIFE] (v1.2) - TODO: v1.31
 +    if(slot >= 0)
 +    {
 +        sendsave = 1;
 +        G_WriteSaveName(slot, savegamestrings[slot]);
 +        M_ClearMenus(0);
 +        quickSaveSlot = slot;
 +        FromCurr();
 +    }
 +    else
 +        M_StartMessage(DEH_String(QSAVESPOT), NULL, false);
 +}
 +
 +//
 +// User wants to save. Start string input for M_Responder
 +//
 +void M_SaveSelect(int choice)
 +{
 +    // we are going to be intercepting all chars
 +    saveStringEnter = 1;
 +
 +    // [STRIFE] (v1.2) - TODO: 1.31
 +    quickSaveSlot = choice;
 +    //saveSlot = choice;
 +
 +    strcpy(saveOldString,savegamestrings[choice]);
 +    if (!strcmp(savegamestrings[choice],EMPTYSTRING))
 +        savegamestrings[choice][0] = 0;
 +    saveCharIndex = strlen(savegamestrings[choice]);
 +}
 +
 +//
 +// Selected from DOOM menu
 +//
 +void M_SaveGame (int choice)
 +{
 +    // [STRIFE] (v1.2) - TODO: v1.31
 +    if (netgame)
 +    {
 +        // haleyjd 20110211: Hooray for Rogue's awesome multiplayer support...
 +        M_StartMessage(DEH_String("You can't save a netgame"), NULL, false);
 +        return;
 +    }
 +    if (!usergame)
 +    {
 +        M_StartMessage(DEH_String(SAVEDEAD),NULL,false);
 +        return;
 +    }
 +
 +    if (gamestate != GS_LEVEL)
 +        return;
 +
 +    // [STRIFE] (v1.2) - TODO: v1.31
 +    //M_SetupNextMenu(&SaveDef);
 +    M_ReadSaveStrings();
 +    M_DoSave(quickSaveSlot);
 +}
 +
 +
 +
 +//
 +//      M_QuickSave
 +//
 +char    tempstring[80];
 +
 +void M_QuickSaveResponse(int key)
 +{
 +    if (key == key_menu_confirm)
 +    {
 +        M_DoSave(quickSaveSlot);
 +        S_StartSound(NULL, sfx_mtalht); // [STRIFE] sound
 +    }
 +}
 +
 +void M_QuickSave(void)
 +{
 +    if (netgame)
 +    {
 +        // haleyjd 20110211 [STRIFE]: More fun...
 +        M_StartMessage(DEH_String("You can't save a netgame"), NULL, false);
 +        return;
 +    }
 +
 +    if (!usergame)
 +    {
 +        S_StartSound(NULL, sfx_oof);
 +        return;
 +    }
 +
 +    if (gamestate != GS_LEVEL)
 +        return;
 +
 +    if (quickSaveSlot < 0)
 +    {
 +        M_StartControlPanel();
 +        M_ReadSaveStrings();
 +        M_SetupNextMenu(&SaveDef);
 +        quickSaveSlot = -2;	// means to pick a slot now
 +        return;
 +    }
 +    DEH_snprintf(tempstring, 80, QSPROMPT, savegamestrings[quickSaveSlot]);
 +    M_StartMessage(tempstring,M_QuickSaveResponse,true);
 +}
 +
 +
 +
 +//
 +// M_QuickLoadResponse
 +//
 +void M_QuickLoadResponse(int key)
 +{
 +    if (key == key_menu_confirm)
 +    {
 +        M_LoadSelect(quickSaveSlot);
 +        S_StartSound(NULL, sfx_mtalht); // [STRIFE] sound
 +    }
 +}
 +
 +//
 +// M_QuickLoad
 +//
 +// [STRIFE] Verified unmodified (v1.2) - TODO: v1.31
 +//
 +void M_QuickLoad(void)
 +{
 +    if (netgame)
 +    {
 +        M_StartMessage(DEH_String(QLOADNET),NULL,false);
 +        return;
 +    }
 +
 +    if (quickSaveSlot < 0)
 +    {
 +        M_StartMessage(DEH_String(QSAVESPOT),NULL,false);
 +        return;
 +    }
 +    DEH_snprintf(tempstring, 80, QLPROMPT, savegamestrings[quickSaveSlot]);
 +    M_StartMessage(tempstring,M_QuickLoadResponse,true);
 +}
 +
 +
 +
 +
 +//
 +// Read This Menus
 +// Had a "quick hack to fix romero bug"
 +// haleyjd 08/28/10: [STRIFE] Draw HELP1, unconditionally.
 +//
 +void M_DrawReadThis1(void)
 +{
 +    inhelpscreens = true;
 +
 +    V_DrawPatchDirect (0, 0, W_CacheLumpName(DEH_String("HELP1"), PU_CACHE));
 +}
 +
 +
 +
 +//
 +// Read This Menus
 +// haleyjd 08/28/10: [STRIFE] Not optional, draws HELP2
 +//
 +void M_DrawReadThis2(void)
 +{
 +    inhelpscreens = true;
 +
 +    V_DrawPatchDirect(0, 0, W_CacheLumpName(DEH_String("HELP2"), PU_CACHE));
 +}
 +
 +
 +//
 +// Read This Menus
 +// haleyjd 08/28/10: [STRIFE] New function to draw HELP3.
 +//
 +void M_DrawReadThis3(void)
 +{
 +    inhelpscreens = true;
 +
 +    V_DrawPatchDirect(0, 0, W_CacheLumpName(DEH_String("HELP3"), PU_CACHE));
 +}
 +
 +//
 +// Change Sfx & Music volumes
 +//
 +// haleyjd 08/29/10: [STRIFE]
 +// * Changed title graphic coordinates
 +// * Added voice volume and sensitivity sliders
 +//
 +void M_DrawSound(void)
 +{
 +    V_DrawPatchDirect (100, 10, W_CacheLumpName(DEH_String("M_SVOL"), PU_CACHE));
 +
 +    M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),
 +                 16,sfxVolume);
 +
 +    M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),
 +                 16,musicVolume);
 +
 +    M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(voice_vol+1),
 +                 16,voiceVolume);
 +
 +    M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_mouse+1),
 +                 16,mouseSensitivity);
 +}
 +
 +void M_Sound(int choice)
 +{
 +    M_SetupNextMenu(&SoundDef);
 +}
 +
 +void M_SfxVol(int choice)
 +{
 +    switch(choice)
 +    {
 +    case 0:
 +        if (sfxVolume)
 +            sfxVolume--;
 +        break;
 +    case 1:
 +        if (sfxVolume < 15)
 +            sfxVolume++;
 +        break;
 +    }
 +
 +    S_SetSfxVolume(sfxVolume * 8);
 +}
 +
 +//
 +// M_VoiceVol
 +//
 +// haleyjd 08/29/10: [STRIFE] New function
 +// Sets voice volume level.
 +//
 +void M_VoiceVol(int choice)
 +{
 +    switch(choice)
 +    {
 +    case 0:
 +        if (voiceVolume)
 +            voiceVolume--;
 +        break;
 +    case 1:
 +        if (voiceVolume < 15)
 +            voiceVolume++;
 +        break;
 +    }
 +
 +    // STRIFE-TODO: Voice volume setting
 +    //S_SetVoiceVolume(voiceVolume * 8);
 +}
 +
 +void M_MusicVol(int choice)
 +{
 +    switch(choice)
 +    {
 +    case 0:
 +        if (musicVolume)
 +            musicVolume--;
 +        break;
 +    case 1:
 +        if (musicVolume < 15)
 +            musicVolume++;
 +        break;
 +    }
 +
 +    S_SetMusicVolume(musicVolume * 8);
 +}
 +
 +
 +
 +
 +//
 +// M_DrawMainMenu
 +//
 +// haleyjd 08/27/10: [STRIFE] Changed x coordinate; M_DOOM -> M_STRIFE
 +//
 +void M_DrawMainMenu(void)
 +{
 +    V_DrawPatchDirect(84, 2,
 +                      W_CacheLumpName(DEH_String("M_STRIFE"), PU_CACHE));
 +}
 +
 +
 +
 +
 +//
 +// M_NewGame
 +//
 +// haleyjd 08/31/10: [STRIFE] Changed M_NEWG -> M_NGAME
 +//
 +void M_DrawNewGame(void)
 +{
 +    V_DrawPatchDirect(96, 14, W_CacheLumpName(DEH_String("M_NGAME"), PU_CACHE));
 +    V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_SKILL"), PU_CACHE));
 +}
 +
 +void M_NewGame(int choice)
 +{
 +    if (netgame && !demoplayback)
 +    {
 +        M_StartMessage(DEH_String(NEWGAME),NULL,false);
 +        return;
 +    }
 +    // haleyjd 09/07/10: [STRIFE] Removed Chex Quest and DOOM gamemodes
 +    M_SetupNextMenu(&NewDef);
 +}
 +
 +
 +//
 +//      M_Episode
 +//
 +int     epi;
 +
 +void M_DrawEpisode(void)
 +{
 +    V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_EPISOD"), PU_CACHE));
 +}
 +
 +/*
 +// haleyjd: [STRIFE] Unused
 +void M_VerifyNightmare(int key)
 +{
 +    if (key != key_menu_confirm)
 +        return;
 +
 +    G_DeferedInitNew(nightmare, 1);
 +    M_ClearMenus (0);
 +}
 +*/
 +
 +void M_ChooseSkill(int choice)
 +{
 +    // haleyjd 09/07/10: Removed nightmare confirmation
 +    // [STRIFE]: start "Name Your Character" menu
 +    menuskill = choice;
 +    currentMenu = &NameCharDef;
 +    itemOn = NameCharDef.lastOn;
 +    M_ReadSaveStrings();
 +}
 +
 +/*
 +// haleyjd [STRIFE] Unused
 +void M_Episode(int choice)
 +{
 +    if ( (gamemode == shareware)
 +	 && choice)
 +    {
 +	M_StartMessage(DEH_String(SWSTRING),NULL,false);
 +	M_SetupNextMenu(&ReadDef1);
 +	return;
 +    }
 +
 +    // Yet another hack...
 +    if ( (gamemode == registered)
 +	 && (choice > 2))
 +    {
 +      fprintf( stderr,
 +	       "M_Episode: 4th episode requires UltimateDOOM\n");
 +      choice = 0;
 +    }
 +
 +    epi = choice;
 +    M_SetupNextMenu(&NewDef);
 +}
 +*/
 +
 +
 +//
 +// M_Options
 +//
 +char    detailNames[2][9]	= {"M_GDHIGH","M_GDLOW"};
 +char	msgNames[2][9]		= {"M_MSGOFF","M_MSGON"};
 +
 +
 +void M_DrawOptions(void)
 +{
 +    // haleyjd 08/27/10: [STRIFE] M_OPTTTL -> M_OPTION
 +    V_DrawPatchDirect(108, 15,
 +                      W_CacheLumpName(DEH_String("M_OPTION"), PU_CACHE));
 +
 +    // haleyjd 08/26/10: [STRIFE] Removed messages, sensitivity, detail.
 +
 +    M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),
 +                 9,screenSize);
 +}
 +
 +void M_Options(int choice)
 +{
 +    M_SetupNextMenu(&OptionsDef);
 +}
 +
 +//
 +// M_AutoUseHealth
 +//
 +// [STRIFE] New function
 +// haleyjd 20110211: toggle autouse health state
 +//
 +void M_AutoUseHealth(void)
 +{
 +    if(!netgame && usergame)
 +    {
 +        players[consoleplayer].cheats ^= CF_AUTOHEALTH;
 +
 +        if(players[consoleplayer].cheats & CF_AUTOHEALTH)
 +            players[consoleplayer].message = DEH_String("Auto use health ON");
 +        else
 +            players[consoleplayer].message = DEH_String("Auto use health OFF");
 +    }
 +}
 +
 +//
 +// M_ChangeShowText
 +//
 +// [STRIFE] New function
 +//
 +void M_ChangeShowText(void)
 +{
 +    dialogshowtext ^= true;
 +
 +    if(dialogshowtext)
 +        players[consoleplayer].message = DEH_String("Conversation Text On");
 +    else
 +        players[consoleplayer].message = DEH_String("Conversation Text Off");
 +}
 +
 +//
 +//      Toggle messages on/off
 +//
 +void M_ChangeMessages(int choice)
 +{
 +    // warning: unused parameter `int choice'
 +    choice = 0;
 +    showMessages = 1 - showMessages;
 +
 +    if (!showMessages)
 +        players[consoleplayer].message = DEH_String(MSGOFF);
 +    else
 +        players[consoleplayer].message = DEH_String(MSGON);
 +
 +    message_dontfuckwithme = true;
 +}
 +
 +
 +//
 +// M_EndGame
 +//
 +void M_EndGameResponse(int key)
 +{
 +    if (key != key_menu_confirm)
 +        return;
 +
 +    currentMenu->lastOn = itemOn;
 +    M_ClearMenus (0);
 +    D_StartTitle ();
 +}
 +
 +void M_EndGame(int choice)
 +{
 +    choice = 0;
 +    if (!usergame)
 +    {
 +        S_StartSound(NULL,sfx_oof);
 +        return;
 +    }
 +
 +    if (netgame)
 +    {
 +        M_StartMessage(DEH_String(NETEND),NULL,false);
 +        return;
 +    }
 +
 +    M_StartMessage(DEH_String(ENDGAME),M_EndGameResponse,true);
 +}
 +
 +
 +
 +
 +//
 +// M_ReadThis
 +//
 +void M_ReadThis(int choice)
 +{
 +    choice = 0;
 +    M_SetupNextMenu(&ReadDef1);
 +}
 +
 +//
 +// M_ReadThis2
 +//
 +// haleyjd 08/28/10: [STRIFE] Eliminated DOOM stuff.
 +//
 +void M_ReadThis2(int choice)
 +{
 +    choice = 0;
 +    M_SetupNextMenu(&ReadDef2);
 +}
 +
 +//
 +// M_ReadThis3
 +//
 +// haleyjd 08/28/10: [STRIFE] New function.
 +//
 +void M_ReadThis3(int choice)
 +{
 +    choice = 0;
 +    M_SetupNextMenu(&ReadDef3);
 +}
 +
 +/*
 +// haleyjd 08/28/10: [STRIFE] Not used.
 +void M_FinishReadThis(int choice)
 +{
 +    choice = 0;
 +    M_SetupNextMenu(&MainDef);
 +}
 +*/
 +
 +
 +//
 +// M_QuitResponse
 +//
 +// haleyjd 09/11/10: [STRIFE] Modifications to start up endgame
 +// demosequence.
 +//
 +void M_QuitResponse(int key)
 +{
 +    char buffer[20];
 +
 +    if (key != key_menu_confirm)
 +        return;
 +
 +    if(netgame)
 +        I_Quit();
 +    else
 +    {
 +        DEH_snprintf(buffer, sizeof(buffer), "qfmrm%i", gametic % 8 + 1);
 +        I_StartVoice(buffer);
 +        D_QuitGame();
 +    }
 +}
 +
 +/*
 +// haleyjd 09/11/10: [STRIFE] Unused
 +static char *M_SelectEndMessage(void)
 +{
 +}
 +*/
 +
 +//
 +// M_QuitStrife
 +//
 +// [STRIFE] Renamed from M_QuitDOOM
 +// haleyjd 09/11/10: No randomized text message; that's taken care of
 +// by the randomized voice message after confirmation.
 +//
 +void M_QuitStrife(int choice)
 +{
 +    DEH_snprintf(endstring, sizeof(endstring),
 +                 "Do you really want to leave?\n\n" DOSY);
 +
 +    M_StartMessage(endstring, M_QuitResponse, true);
 +}
 +
 +
 +
 +
 +void M_ChangeSensitivity(int choice)
 +{
 +    switch(choice)
 +    {
 +    case 0:
 +        if (mouseSensitivity)
 +            mouseSensitivity--;
 +        break;
 +    case 1:
 +        if (mouseSensitivity < 9)
 +            mouseSensitivity++;
 +        break;
 +    }
 +}
 +
 +/*
 +// haleyjd [STRIFE] Unused
 +void M_ChangeDetail(int choice)
 +{
 +    choice = 0;
 +    detailLevel = 1 - detailLevel;
 +
 +    R_SetViewSize (screenblocks, detailLevel);
 +
 +    if (!detailLevel)
 +	players[consoleplayer].message = DEH_String(DETAILHI);
 +    else
 +	players[consoleplayer].message = DEH_String(DETAILLO);
 +}
 +*/
 +
 +// [STRIFE] Verified unmodified
 +void M_SizeDisplay(int choice)
 +{
 +    switch(choice)
 +    {
 +    case 0:
 +        if (screenSize > 0)
 +        {
 +            screenblocks--;
 +            screenSize--;
 +        }
 +        break;
 +    case 1:
 +        if (screenSize < 8)
 +        {
 +            screenblocks++;
 +            screenSize++;
 +        }
 +        break;
 +    }
 +
 +    R_SetViewSize (screenblocks, detailLevel);
 +}
 +
 +
 +
 +
 +//
 +//      Menu Functions
 +//
 +
 +//
 +// M_DrawThermo
 +//
 +// haleyjd 08/28/10: [STRIFE] Changes to some patch coordinates.
 +//
 +void
 +M_DrawThermo
 +( int	x,
 +  int	y,
 +  int	thermWidth,
 +  int	thermDot )
 +{
 +    int         xx;
 +    int         yy; // [STRIFE] Needs a temp y coordinate variable
 +    int         i;
 +
 +    xx = x;
 +    yy = y + 6; // [STRIFE] +6 to y coordinate
 +    V_DrawPatchDirect(xx, yy, W_CacheLumpName(DEH_String("M_THERML"), PU_CACHE));
 +    xx += 8;
 +    for (i=0;i<thermWidth;i++)
 +    {
 +        V_DrawPatchDirect(xx, yy, W_CacheLumpName(DEH_String("M_THERMM"), PU_CACHE));
 +        xx += 8;
 +    }
 +    V_DrawPatchDirect(xx, yy, W_CacheLumpName(DEH_String("M_THERMR"), PU_CACHE));
 +
 +    // [STRIFE] +2 to initial y coordinate
 +    V_DrawPatchDirect((x + 8) + thermDot * 8, y + 2,
 +                      W_CacheLumpName(DEH_String("M_THERMO"), PU_CACHE));
 +}
 +
 +
 +// haleyjd: These are from DOOM v0.5 and the prebeta! They drew those ugly red &
 +// blue checkboxes... preserved for historical interest, as not in Strife.
 +void
 +M_DrawEmptyCell
 +( menu_t*	menu,
 +  int		item )
 +{
 +    V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1,
 +                      W_CacheLumpName(DEH_String("M_CELL1"), PU_CACHE));
 +}
 +
 +void
 +M_DrawSelCell
 +( menu_t*	menu,
 +  int		item )
 +{
 +    V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1,
 +                      W_CacheLumpName(DEH_String("M_CELL2"), PU_CACHE));
 +}
 +
 +
 +void
 +M_StartMessage
 +( char*		string,
 +  void*		routine,
 +  boolean	input )
 +{
 +    messageLastMenuActive = menuactive;
 +    messageToPrint = 1;
 +    messageString = string;
 +    messageRoutine = routine;
 +    messageNeedsInput = input;
 +    menuactive = true;
 +    return;
 +}
 +
 +
 +
 +void M_StopMessage(void)
 +{
 +    menuactive = messageLastMenuActive;
 +    messageToPrint = 0;
 +}
 +
 +
 +
 +//
 +// Find string width from hu_font chars
 +//
 +int M_StringWidth(char* string)
 +{
 +    size_t             i;
 +    int             w = 0;
 +    int             c;
 +
 +    for (i = 0;i < strlen(string);i++)
 +    {
 +        c = toupper(string[i]) - HU_FONTSTART;
 +        if (c < 0 || c >= HU_FONTSIZE)
 +            w += 4;
 +        else
 +            w += SHORT (hu_font[c]->width);
 +    }
 +
 +    return w;
 +}
 +
 +
 +
 +//
 +//      Find string height from hu_font chars
 +//
 +int M_StringHeight(char* string)
 +{
 +    size_t             i;
 +    int             h;
 +    int             height = SHORT(hu_font[0]->height);
 +
 +    h = height;
 +    for (i = 0;i < strlen(string);i++)
 +        if (string[i] == '\n')
 +            h += height;
 +
 +    return h;
 +}
 +
 +
 +//
 +// M_WriteText
 +//
 +// Write a string using the hu_font
 +// haleyjd 09/04/10: [STRIFE]
 +// * Rogue made a lot of changes to this for the dialog system.
 +//
 +int
 +M_WriteText
 +( int           x,
 +  int           y,
 +  const char*   string) // haleyjd: made const for safety w/dialog engine
 +{
 +    int	        w;
 +    const char* ch;
 +    int         c;
 +    int         cx;
 +    int         cy;
 +
 +    ch = string;
 +    cx = x;
 +    cy = y;
 +
 +    while(1)
 +    {
 +        c = *ch++;
 +        if (!c)
 +            break;
 +
 +        // haleyjd 09/04/10: [STRIFE] Don't draw spaces at the start of lines.
 +        if(c == ' ' && cx == x)
 +            continue;
 +
 +        if (c == '\n')
 +        {
 +            cx = x;
 +            cy += 11; // haleyjd 09/04/10: [STRIFE]: Changed 12 -> 11
 +            continue;
 +        }
 +
 +        c = toupper(c) - HU_FONTSTART;
 +        if (c < 0 || c>= HU_FONTSIZE)
 +        {
 +            cx += 4;
 +            continue;
 +        }
 +
 +        w = SHORT (hu_font[c]->width);
 +
 +        // haleyjd 09/04/10: [STRIFE] Different linebreak handling
 +        if (cx + w > SCREENWIDTH - 20)
 +        {
 +            cx = x;
 +            cy += 11;
 +            --ch;
 +        }
 +        else
 +        {
 +            V_DrawPatchDirect(cx, cy, hu_font[c]);
 +            cx += w;
 +        }
 +    }
 +
 +    // [STRIFE] Return final y coordinate.
 +    return cy + 12;
 +}
 +
 +//
 +// M_DialogDimMsg
 +//
 +// [STRIFE] New function
 +// haleyjd 09/04/10: Painstakingly transformed from the assembly code, as the
 +// decompiler could not touch it. Redimensions a string to fit on screen, leaving
 +// at least a 20 pixel margin on the right side. The string passed in must be
 +// writable.
 +//
 +void M_DialogDimMsg(int x, int y, char *str, boolean useyfont)
 +{
 +    int rightbound = (SCREENWIDTH - 20) - x;
 +    patch_t **fontarray;  // ebp
 +    int linewidth = 0;    // esi
 +    int i = 0;            // edx
 +    char *message = str;  // edi
 +    char  bl;             // bl
 +
 +    if(useyfont)
 +       fontarray = yfont;
 +    else
 +       fontarray = hu_font;
 +
 +    bl = toupper(*message);
 +
 +    if(!bl)
 +        return;
 +
 +    // outer loop - run to end of string
 +    do
 +    {
 +        if(bl != '\n')
 +        {
 +            int charwidth; // eax
 +            int tempwidth; // ecx
 +
 +            if(bl < HU_FONTSTART || bl > HU_FONTEND)
 +                charwidth = 4;
 +            else
 +                charwidth = SHORT(fontarray[bl - HU_FONTSTART]->width);
 +
 +            tempwidth = linewidth + charwidth;
 +
 +            // Test if the line still fits within the boundary...
 +            if(tempwidth >= rightbound)
 +            {
 +                // Doesn't fit...
 +                char *tempptr = &message[i]; // ebx
 +                char  al;                    // al
 +
 +                // inner loop - run backward til a space (or the start of the
 +                // string) is found, subtracting width off the current line.
 +                // BUG: shouldn't we stop at a previous '\n' too?
 +                while(*tempptr != ' ' && i > 0)
 +                {
 +                    tempptr--;
 +                    // BUG: they didn't add the first char to linewidth yet...
 +                    linewidth -= charwidth;
 +                    i--;
 +                    al = toupper(*tempptr);
 +                    if(al < HU_FONTSTART || al > HU_FONTEND)
 +                        charwidth = 4;
 +                    else
 +                        charwidth = SHORT(fontarray[al - HU_FONTSTART]->width);
 +                }
 +                // Replace the space with a linebreak.
 +                // BUG: what if i is zero? ... infinite loop time!
 +                message[i] = '\n';
 +                linewidth = 0;
 +            }
 +            else
 +            {
 +                // The line does fit.
 +                // Spaces at the start of a line don't count though.
 +                if(!(bl == ' ' && linewidth == 0))
 +                    linewidth += charwidth;
 +            }
 +        }
 +        else
 +            linewidth = 0; // '\n' seen, so reset the line width
 +    }
 +    while((bl = toupper(message[++i])) != 0); // step to the next character
 +}
 +
 +
 +//
 +// CONTROL PANEL
 +//
 +
 +//
 +// M_Responder
 +//
 +boolean M_Responder (event_t* ev)
 +{
 +    int             ch;
 +    int             key;
 +    int             i;
 +    static  int     joywait = 0;
 +    static  int     mousewait = 0;
 +    static  int     mousey = 0;
 +    static  int     lasty = 0;
 +    static  int     mousex = 0;
 +    static  int     lastx = 0;
 +
 +    // "close" button pressed on window?
 +    if (ev->type == ev_quit)
 +    {
 +        S_StartSound(NULL, sfx_swtchn);
 +        M_QuitStrife(0);
 +        return true;
 +    }
 +
 +    // key is the key pressed, ch is the actual character typed
 +
 +    ch = 0;
 +    key = -1;
 +
 +    if (ev->type == ev_joystick && joywait < I_GetTime())
 +    {
-         if (ev->data3 == -1)
++        if (ev->data3 < 0)
 +        {
 +            key = key_menu_up;
 +            joywait = I_GetTime() + 5;
 +        }
-         else if (ev->data3 == 1)
++        else if (ev->data3 > 0)
 +        {
 +            key = key_menu_down;
 +            joywait = I_GetTime() + 5;
 +        }
 +
-         if (ev->data2 == -1)
++        if (ev->data2 < 0)
 +        {
 +            key = key_menu_left;
 +            joywait = I_GetTime() + 2;
 +        }
-         else if (ev->data2 == 1)
++        else if (ev->data2 > 0)
 +        {
 +            key = key_menu_right;
 +            joywait = I_GetTime() + 2;
 +        }
 +
 +        if (ev->data1&1)
 +        {
 +            key = key_menu_forward;
 +            joywait = I_GetTime() + 5;
 +        }
 +        if (ev->data1&2)
 +        {
 +            key = key_menu_back;
 +            joywait = I_GetTime() + 5;
 +        }
 +    }
 +    else
 +    {
 +        if (ev->type == ev_mouse && mousewait < I_GetTime())
 +        {
 +            mousey += ev->data3;
 +            if (mousey < lasty-30)
 +            {
 +                key = key_menu_down;
 +                mousewait = I_GetTime() + 5;
 +                mousey = lasty -= 30;
 +            }
 +            else if (mousey > lasty+30)
 +            {
 +                key = key_menu_up;
 +                mousewait = I_GetTime() + 5;
 +                mousey = lasty += 30;
 +            }
 +
 +            mousex += ev->data2;
 +            if (mousex < lastx-30)
 +            {
 +                key = key_menu_left;
 +                mousewait = I_GetTime() + 5;
 +                mousex = lastx -= 30;
 +            }
 +            else if (mousex > lastx+30)
 +            {
 +                key = key_menu_right;
 +                mousewait = I_GetTime() + 5;
 +                mousex = lastx += 30;
 +            }
 +
 +            if (ev->data1&1)
 +            {
 +                key = key_menu_forward;
 +                mousewait = I_GetTime() + 15;
 +                mouse_fire_countdown = 5;   // villsa [STRIFE]
 +            }
 +
 +            if (ev->data1&2)
 +            {
 +                key = key_menu_back;
 +                mousewait = I_GetTime() + 15;
 +            }
 +        }
 +        else
 +        {
 +            if (ev->type == ev_keydown)
 +            {
 +                key = ev->data1;
 +                ch = ev->data2;
 +            }
 +        }
 +    }
 +
 +    if (key == -1)
 +        return false;
 +
 +    // In testcontrols mode, none of the function keys should do anything
 +    // - the only key is escape to quit.
 +
 +    if (testcontrols)
 +    {
 +        if (key == key_menu_activate || key == key_menu_quit)
 +        {
 +            I_Quit();
 +            return true;
 +        }
 +
 +        return false;
 +    }
 +
 +    // Save Game string input
 +    if (saveStringEnter)
 +    {
 +        switch(key)
 +        {
 +        case KEY_BACKSPACE:
 +            if (saveCharIndex > 0)
 +            {
 +                saveCharIndex--;
 +                savegamestrings[quickSaveSlot][saveCharIndex] = 0;
 +            }
 +            break;
 +
 +        case KEY_ESCAPE:
 +            saveStringEnter = 0;
 +            strcpy(&savegamestrings[quickSaveSlot][0],saveOldString);
 +            break;
 +
 +        case KEY_ENTER:
 +            // [STRIFE] v1.2 - TODO: v1.31
 +            saveStringEnter = 0;
 +            if (savegamestrings[quickSaveSlot][0])
 +                M_DoNameChar(quickSaveSlot);
 +            break;
 +
 +        default:
 +            // Entering a character - use the 'ch' value, not the key
 +
 +            ch = toupper(ch);
 +
 +            if (ch != ' '
 +                && (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE))
 +            {
 +                break;
 +            }
 +
 +            if (ch >= 32 && ch <= 127 &&
 +                saveCharIndex < SAVESTRINGSIZE-1 &&
 +                M_StringWidth(savegamestrings[quickSaveSlot]) <
 +                (SAVESTRINGSIZE-2)*8)
 +            {
 +                savegamestrings[quickSaveSlot][saveCharIndex++] = ch;
 +                savegamestrings[quickSaveSlot][saveCharIndex] = 0;
 +            }
 +            break;
 +        }
 +        return true;
 +    }
 +
 +    // Take care of any messages that need input
 +    if (messageToPrint)
 +    {
 +        if (messageNeedsInput)
 +        {
 +            if (key != ' ' && key != KEY_ESCAPE
 +                && key != key_menu_confirm && key != key_menu_abort)
 +            {
 +                return false;
 +            }
 +        }
 +
 +        menuactive = messageLastMenuActive;
 +        messageToPrint = 0;
 +        if (messageRoutine)
 +            messageRoutine(key);
 +
 +        menupause = false;                // [STRIFE] unpause
 +        menuactive = false;
 +        S_StartSound(NULL, sfx_mtalht);   // [STRIFE] sound
 +        return true;
 +    }
 +
 +    // [STRIFE]:
 +    // * In v1.2 this is moved to F9 (quickload)
 +    // * In v1.31 it is moved to a different button and quicksave
 +    //   functionality is restored separate from normal saving (STRIFE-TODO)
 +    /*
 +    if (devparm && key == key_menu_help)
 +    {
 +        G_ScreenShot ();
 +        return true;
 +    }
 +    */
 +
 +    // F-Keys
 +    if (!menuactive)
 +    {
 +        if (key == key_menu_decscreen)      // Screen size down
 +        {
 +            if (automapactive || chat_on)
 +                return false;
 +            M_SizeDisplay(0);
 +            S_StartSound(NULL, sfx_stnmov);
 +            return true;
 +        }
 +        else if (key == key_menu_incscreen) // Screen size up
 +        {
 +            if (automapactive || chat_on)
 +                return false;
 +            M_SizeDisplay(1);
 +            S_StartSound(NULL, sfx_stnmov);
 +            return true;
 +        }
 +        else if (key == key_menu_help)     // Help key
 +        {
 +            M_StartControlPanel ();
 +            // haleyjd 08/29/10: [STRIFE] always ReadDef1
 +            currentMenu = &ReadDef1;
 +
 +            itemOn = 0;
 +            S_StartSound(NULL, sfx_swtchn);
 +            return true;
 +        }
 +        else if (key == key_menu_save)     // Save
 +        {
 +            /*
 +            M_StartControlPanel();
 +            S_StartSound(NULL, sfx_swtchn);
 +            M_SaveGame(0);
 +            */
 +            // [STRIFE] (v1.2) - TODO: v1.31
 +            if(netgame || players[consoleplayer].health <= 0 ||
 +                players[consoleplayer].cheats & CF_ONFIRE)
 +            {
 +                S_StartSound(NULL, sfx_oof);
 +            }
 +            else
 +            {
 +                M_StartControlPanel();
 +                S_StartSound(NULL, sfx_swtchn);
 +                M_SaveGame(0);
 +            }
 +            return true;
 +        }
 +        else if (key == key_menu_load)     // Load
 +        {
 +            /*
 +            M_StartControlPanel();
 +            S_StartSound(NULL, sfx_swtchn);
 +            M_LoadGame(0);
 +            */
 +            // [STRIFE] (v1.2) - TODO: v1.31
 +            S_StartSound(NULL, sfx_swtchn);
 +            M_QuickLoad();
 +            return true;
 +        }
 +        else if (key == key_menu_volume)   // Sound Volume
 +        {
 +            M_StartControlPanel ();
 +            currentMenu = &SoundDef;
 +            itemOn = sfx_vol;
 +            S_StartSound(NULL, sfx_swtchn);
 +            return true;
 +        }
 +        else if (key == key_menu_detail)   // Detail toggle
 +        {
 +            //M_ChangeDetail(0);
 +            M_AutoUseHealth(); // [STRIFE]
 +            S_StartSound(NULL,sfx_swtchn);
 +            return true;
 +        }
 +        // STRIFE-TODO: autouse health toggle
 +        else if (key == key_menu_qsave)    // Quicksave
 +        {
 +            //S_StartSound(NULL, sfx_swtchn);
 +            //M_QuickSave();
 +            // [STRIFE] (v1.2) - TODO: v1.31
 +            if(netgame || players[consoleplayer].health <= 0 ||
 +               players[consoleplayer].cheats & CF_ONFIRE)
 +            {
 +                S_StartSound(NULL, sfx_oof);
 +            }
 +            else
 +            {
 +                S_StartSound(NULL, sfx_swtchn);
 +                M_QuickSave();
 +            }
 +            return true;
 +        }
 +        else if (key == key_menu_endgame)  // End game
 +        {
 +            S_StartSound(NULL, sfx_swtchn);
 +            M_EndGame(0);
 +            return true;
 +        }
 +        else if (key == key_menu_messages) // Toggle messages
 +        {
 +            //M_ChangeMessages(0);
 +            M_ChangeShowText(); // [STRIFE]
 +            S_StartSound(NULL, sfx_swtchn);
 +            return true;
 +        }
 +        else if (key == key_menu_qload)    // Quickload
 +        {
 +            // [STRIFE]
 +            // * v1.2: takes a screenshot
 +            // * v1.31: does quickload again... (STRIFE-TODO)
 +            /*
 +            S_StartSound(NULL, sfx_swtchn);
 +            M_QuickLoad();
 +            */
 +            G_ScreenShot();
 +            return true;
 +        }
 +        else if (key == key_menu_quit)     // Quit DOOM
 +        {
 +            S_StartSound(NULL, sfx_swtchn);
 +            M_QuitStrife(0);
 +            return true;
 +        }
 +        else if (key == key_menu_gamma)    // gamma toggle
 +        {
 +            usegamma++;
 +            if (usegamma > 4)
 +                usegamma = 0;
 +            players[consoleplayer].message = DEH_String(gammamsg[usegamma]);
 +            I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE));
 +            return true;
 +        }
 +    }
 +
 +    // Pop-up menu?
 +    if (!menuactive)
 +    {
 +        if (key == key_menu_activate)
 +        {
 +            M_StartControlPanel ();
 +            S_StartSound(NULL, sfx_swtchn);
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +
 +    // Keys usable within menu
 +
 +    if (key == key_menu_down)
 +    {
 +        // Move down to next item
 +
 +        do
 +        {
 +            if (itemOn+1 > currentMenu->numitems-1)
 +                itemOn = 0;
 +            else itemOn++;
 +            S_StartSound(NULL, sfx_pstop);
 +        } while(currentMenu->menuitems[itemOn].status==-1);
 +
 +        return true;
 +    }
 +    else if (key == key_menu_up)
 +    {
 +        // Move back up to previous item
 +
 +        do
 +        {
 +            if (!itemOn)
 +                itemOn = currentMenu->numitems-1;
 +            else itemOn--;
 +            S_StartSound(NULL, sfx_pstop);
 +        } while(currentMenu->menuitems[itemOn].status==-1);
 +
 +        return true;
 +    }
 +    else if (key == key_menu_left)
 +    {
 +        // Slide slider left
 +
 +        if (currentMenu->menuitems[itemOn].routine &&
 +            currentMenu->menuitems[itemOn].status == 2)
 +        {
 +            S_StartSound(NULL, sfx_stnmov);
 +            currentMenu->menuitems[itemOn].routine(0);
 +        }
 +        return true;
 +    }
 +    else if (key == key_menu_right)
 +    {
 +        // Slide slider right
 +
 +        if (currentMenu->menuitems[itemOn].routine &&
 +            currentMenu->menuitems[itemOn].status == 2)
 +        {
 +            S_StartSound(NULL, sfx_stnmov);
 +            currentMenu->menuitems[itemOn].routine(1);
 +        }
 +        return true;
 +    }
 +    else if (key == key_menu_forward)
 +    {
 +        // Activate menu item
 +
 +        if (currentMenu->menuitems[itemOn].routine &&
 +            currentMenu->menuitems[itemOn].status)
 +        {
 +            currentMenu->lastOn = itemOn;
 +            if (currentMenu->menuitems[itemOn].status == 2)
 +            {
 +                currentMenu->menuitems[itemOn].routine(1);      // right arrow
 +                S_StartSound(NULL, sfx_stnmov);
 +            }
 +            else
 +            {
 +                currentMenu->menuitems[itemOn].routine(itemOn);
 +                //S_StartSound(NULL, sfx_swish); [STRIFE] No sound is played here.
 +            }
 +        }
 +        return true;
 +    }
 +    else if (key == key_menu_activate)
 +    {
 +        // Deactivate menu
 +        if(menuindialog) // [STRIFE] - Get out of dialog engine semi-gracefully
 +            P_DialogDoChoice(-1);
 +
 +        currentMenu->lastOn = itemOn;
 +        M_ClearMenus (0);
 +        S_StartSound(NULL, sfx_mtalht);   // villsa [STRIFE] TODO - fix sounds
 +        return true;
 +    }
 +    else if (key == key_menu_back)
 +    {
 +        // Go back to previous menu
 +
 +        currentMenu->lastOn = itemOn;
 +        if (currentMenu->prevMenu)
 +        {
 +            currentMenu = currentMenu->prevMenu;
 +            itemOn = currentMenu->lastOn;
 +            S_StartSound(NULL, sfx_swtchn);
 +        }
 +        return true;
 +    }
 +    else if (ch != 0)
 +    {
 +        // Keyboard shortcut?
 +
 +        for (i = itemOn+1;i < currentMenu->numitems;i++)
 +        {
 +            if (currentMenu->menuitems[i].alphaKey == ch)
 +            {
 +                itemOn = i;
 +                S_StartSound(NULL, sfx_pstop);
 +                return true;
 +            }
 +        }
 +
 +        for (i = 0;i <= itemOn;i++)
 +        {
 +            if (currentMenu->menuitems[i].alphaKey == ch)
 +            {
 +                itemOn = i;
 +                S_StartSound(NULL, sfx_pstop);
 +                return true;
 +            }
 +        }
 +    }
 +
 +    return false;
 +}
 +
 +
 +
 +//
 +// M_StartControlPanel
 +//
 +void M_StartControlPanel (void)
 +{
 +    // intro might call this repeatedly
 +    if (menuactive)
 +        return;
 +
 +    menuactive = 1;
 +    menupause = true;
 +    currentMenu = &MainDef;         // JDC
 +    itemOn = currentMenu->lastOn;   // JDC
 +}
 +
 +
 +//
 +// M_Drawer
 +// Called after the view has been rendered,
 +// but before it has been blitted.
 +//
 +void M_Drawer (void)
 +{
 +    static short	x;
 +    static short	y;
 +    unsigned int	i;
 +    unsigned int	max;
 +    char		string[80];
 +    char               *name;
 +    int			start;
 +
 +    inhelpscreens = false;
 +
 +    // Horiz. & Vertically center string and print it.
 +    if (messageToPrint)
 +    {
 +        start = 0;
 +        y = 100 - M_StringHeight(messageString) / 2;
 +        while (messageString[start] != '\0')
 +        {
 +            int foundnewline = 0;
 +
 +            for (i = 0; i < strlen(messageString + start); i++)
 +                if (messageString[start + i] == '\n')
 +                {
 +                    memset(string, 0, sizeof(string));
 +                    strncpy(string, messageString + start, i);
 +                    foundnewline = 1;
 +                    start += i + 1;
 +                    break;
 +                }
 +
 +                if (!foundnewline)
 +                {
 +                    strcpy(string, messageString + start);
 +                    start += strlen(string);
 +                }
 +
 +                x = 160 - M_StringWidth(string) / 2;
 +                M_WriteText(x, y, string);
 +                y += SHORT(hu_font[0]->height);
 +        }
 +
 +        return;
 +    }
 +
 +    if (!menuactive)
 +        return;
 +
 +    if (currentMenu->routine)
 +        currentMenu->routine();         // call Draw routine
 +
 +    // DRAW MENU
 +    x = currentMenu->x;
 +    y = currentMenu->y;
 +    max = currentMenu->numitems;
 +
 +    for (i=0;i<max;i++)
 +    {
 +        name = DEH_String(currentMenu->menuitems[i].name);
 +
 +        if (name[0])
 +        {
 +            V_DrawPatchDirect (x, y, W_CacheLumpName(name, PU_CACHE));
 +        }
 +        y += LINEHEIGHT;
 +    }
 +
 +
 +    // haleyjd 08/27/10: [STRIFE] Adjust to draw spinning Sigil
 +    // DRAW SIGIL
 +    V_DrawPatchDirect(x + CURSORXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,
 +                      W_CacheLumpName(DEH_String(cursorName[whichCursor]),
 +                                      PU_CACHE));
 +
 +}
 +
 +
 +//
 +// M_ClearMenus
 +//
 +// haleyjd 08/28/10: [STRIFE] Added an int param so this can be called by menus.
 +//         09/08/10: Added menupause.
 +//
 +void M_ClearMenus (int choice)
 +{
 +    choice = 0;     // haleyjd: for no warning; not from decompilation.
 +    menuactive = 0;
 +    menupause = 0;
 +}
 +
 +
 +
 +
 +//
 +// M_SetupNextMenu
 +//
 +void M_SetupNextMenu(menu_t *menudef)
 +{
 +    currentMenu = menudef;
 +    itemOn = currentMenu->lastOn;
 +}
 +
 +
 +//
 +// M_Ticker
 +//
 +// haleyjd 08/27/10: [STRIFE] Rewritten for Sigil cursor
 +//
 +void M_Ticker (void)
 +{
 +    if (--cursorAnimCounter <= 0)
 +    {
 +        whichCursor = (whichCursor + 1) % 8;
 +        cursorAnimCounter = 5;
 +    }
 +}
 +
 +
 +//
 +// M_Init
 +//
 +// haleyjd 08/27/10: [STRIFE] Removed DOOM gamemode stuff
 +//
 +void M_Init (void)
 +{
 +    currentMenu = &MainDef;
 +    menuactive = 0;
 +    itemOn = currentMenu->lastOn;
 +    whichCursor = 0;
 +    cursorAnimCounter = 10;
 +    screenSize = screenblocks - 3;
 +    messageToPrint = 0;
 +    messageString = NULL;
 +    messageLastMenuActive = menuactive; // STRIFE-FIXME: assigns 0 here...
 +    quickSaveSlot = -1;
 +
 +    // [STRIFE]: Initialize savegame paths and clear temporary directory
 +    G_WriteSaveName(5, "ME");
 +    ClearTmp();
 +
 +    // Here we could catch other version dependencies,
 +    //  like HELP1/2, and four episodes.
 +}
 +
diff --cc src/strife/p_map.c
index 6a1d526d,00000000..c557f968
mode 100644,000000..100644
--- a/src/strife/p_map.c
+++ b/src/strife/p_map.c
@@@ -1,1668 -1,0 +1,1668 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard, Andrey Budko
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	Movement, collision handling.
 +//	Shooting and aiming.
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "deh_misc.h"
 +
 +#include "m_bbox.h"
 +#include "m_random.h"
 +#include "i_system.h"
 +
 +#include "doomdef.h"
 +#include "m_argv.h"
 +#include "m_misc.h"
 +#include "p_local.h"
 +
 +#include "s_sound.h"
 +
 +// State.
 +#include "doomstat.h"
 +#include "r_state.h"
 +// Data.
 +#include "sounds.h"
 +
 +// Spechit overrun magic value.
 +//
 +// This is the value used by PrBoom-plus.  I think the value below is
 +// actually better and works with more demos.  However, I think
 +// it's better for the spechits emulation to be compatible with
 +// PrBoom-plus, at least so that the big spechits emulation list
 +// on Doomworld can also be used with Chocolate Doom.
 +
 +#define DEFAULT_SPECHIT_MAGIC 0x01C09C98
 +
 +// This is from a post by myk on the Doomworld forums,
 +// outputted from entryway's spechit_magic generator for
 +// s205n546.lmp.  The _exact_ value of this isn't too
 +// important; as long as it is in the right general
 +// range, it will usually work.  Otherwise, we can use
 +// the generator (hacked doom2.exe) and provide it
 +// with -spechit.
 +
 +//#define DEFAULT_SPECHIT_MAGIC 0x84f968e8
 +
 +
 +fixed_t     tmbbox[4];
 +mobj_t*     tmthing;
 +int         tmflags;
 +fixed_t     tmx;
 +fixed_t     tmy;
 +
 +
 +// If "floatok" true, move would be ok
 +// if within "tmfloorz - tmceilingz".
 +boolean     floatok;
 +
 +fixed_t     tmfloorz;
 +fixed_t     tmceilingz;
 +fixed_t     tmdropoffz;
 +
 +// keep track of the line that lowers the ceiling,
 +// so missiles don't explode against sky hack walls
 +line_t*     ceilingline;
 +
 +// haleyjd 20110203: [STRIFE] New global
 +// "blockingline" tracks the linedef responsible for blocking motion of an mobj
 +// for purposes of doing impact special activation by missiles. Suspiciously
 +// similar to the solution used by Raven in Heretic and Hexen.
 +line_t     *blockingline;
 +
 +// keep track of special lines as they are hit,
 +// but don't process them until the move is proven valid
 +
 +// fraggle: I have increased the size of this buffer.  In the original Doom,
 +// overrunning past this limit caused other bits of memory to be overwritten,
 +// affecting demo playback.  However, in doing so, the limit was still
 +// exceeded.  So we have to support more than 8 specials.
 +//
 +// We keep the original limit, to detect what variables in memory were
 +// overwritten (see SpechitOverrun())
 +
 +#define MAXSPECIALCROSS 		20
 +#define MAXSPECIALCROSS_ORIGINAL	8
 +
 +line_t*		spechit[MAXSPECIALCROSS];
 +int		numspechit;
 +
 +
 +
 +//
 +// TELEPORT MOVE
 +//
 +
 +//
 +// PIT_StompThing
 +//
 +// [STRIFE] haleyjd 09/15/10: Modified so monsters can telestomp.
 +//
 +boolean PIT_StompThing (mobj_t* thing)
 +{
 +    fixed_t blockdist;
 +
 +    if (!(thing->flags & MF_SHOOTABLE) )
 +        return true;
 +
 +    blockdist = thing->radius + tmthing->radius;
 +
 +    if ( abs(thing->x - tmx) >= blockdist
 +        || abs(thing->y - tmy) >= blockdist )
 +    {
 +        // didn't hit it
 +        return true;
 +    }
 +
 +    // don't clip against self
 +    if (thing == tmthing)
 +        return true;
 +
 +    // [STRIFE] monsters DO stomp things, on all levels
 +    // Basically, one thing involved must be a player.
 +    // Monsters can telefrag players, and players can telefrag monsters, but
 +    // monsters cannot telefrag other monsters.
 +    if (!(tmthing->player || thing->player))
 +        return false;
 +
 +    P_DamageMobj (thing, tmthing, tmthing, 10000);
 +
 +    return true;
 +}
 +
 +
 +//
 +// P_TeleportMove
 +//
 +// [STRIFE]
 +// haleyjd 09/15/10: Modified to set thing z position.
 +//
 +boolean P_TeleportMove(mobj_t*  thing, fixed_t  x, fixed_t  y)
 +{
 +    int                 xl;
 +    int                 xh;
 +    int                 yl;
 +    int                 yh;
 +    int                 bx;
 +    int                 by;
 +
 +    subsector_t*        newsubsec;
 +
 +    // kill anything occupying the position
 +    tmthing = thing;
 +    tmflags = thing->flags;
 +
 +    tmx = x;
 +    tmy = y;
 +
 +    tmbbox[BOXTOP] = y + tmthing->radius;
 +    tmbbox[BOXBOTTOM] = y - tmthing->radius;
 +    tmbbox[BOXRIGHT] = x + tmthing->radius;
 +    tmbbox[BOXLEFT] = x - tmthing->radius;
 +
 +    newsubsec = R_PointInSubsector (x,y);
 +    ceilingline = NULL;
 +
 +    // The base floor/ceiling is from the subsector
 +    // that contains the point.
 +    // Any contacted lines the step closer together
 +    // will adjust them.
 +    tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
 +    tmceilingz = newsubsec->sector->ceilingheight;
 +
 +    validcount++;
 +    numspechit = 0;
 +
 +    // stomp on any things contacted
 +    xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
 +    xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
 +    yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
 +    yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
 +
 +    for (bx=xl ; bx<=xh ; bx++)
 +        for (by=yl ; by<=yh ; by++)
 +            if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
 +                return false;
 +
 +    // the move is ok,
 +    // so link the thing into its new position
 +    P_UnsetThingPosition (thing);
 +
 +    thing->floorz = tmfloorz;
 +    thing->ceilingz = tmceilingz;
 +    thing->x = x;
 +    thing->y = y;
 +    thing->z = tmfloorz; // haleyjd 09/15/10: [STRIFE] Rogue added a z-set here
 +
 +    P_SetThingPosition (thing);
 +
 +    return true;
 +}
 +
 +
 +//
 +// MOVEMENT ITERATOR FUNCTIONS
 +//
 +
 +static void SpechitOverrun(line_t *ld);
 +
 +//
 +// PIT_CheckLine
 +// Adjusts tmfloorz and tmceilingz as lines are contacted
 +//
 +boolean PIT_CheckLine (line_t* ld)
 +{
 +    if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
 +        || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
 +        || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
 +        || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
 +        return true;
 +
 +    if (P_BoxOnLineSide (tmbbox, ld) != -1)
 +        return true;
 +
 +    // A line has been hit
 +
 +    // The moving thing's destination position will cross
 +    // the given line.
 +    // If this should not be allowed, return false.
 +    // If the line is special, keep track of it
 +    // to process later if the move is proven ok.
 +    // NOTE: specials are NOT sorted by order,
 +    // so two special lines that are only 8 pixels apart
 +    // could be crossed in either order.
 +
 +    if (!ld->backsector)
 +        return false;           // one sided line
 +
 +    if (!(tmthing->flags & MF_MISSILE) )
 +    {
 +        // villsa [STRIFE] include jumpover flag
 +        if ( ld->flags & ML_BLOCKING &&
 +            (!(ld->flags & ML_JUMPOVER) || tmfloorz + (32*FRACUNIT) > tmthing->z) )
 +            return false;	// explicitly blocking everything
 +
 +        // villsa [STRIFE] exclude floaters from blockmonster lines
 +        if ( !tmthing->player && (ld->flags & ML_BLOCKMONSTERS) &&
 +            !(tmthing->flags & MF_FLOAT))
 +            return false;	// block monsters only
 +
 +        // villsa [STRIFE]
 +        if ( ld->flags & ML_BLOCKFLOATERS && tmthing->flags & MF_FLOAT )
 +            return false;   // block floaters only
 +    }
 +
 +    // set openrange, opentop, openbottom
 +    P_LineOpening (ld);
 +
 +    // adjust floor / ceiling heights
 +    if (opentop < tmceilingz)
 +    {
 +        tmceilingz = opentop;
 +        ceilingline = ld;
 +    }
 +
 +    if (openbottom > tmfloorz)
 +        tmfloorz = openbottom;
 +
 +    if (lowfloor < tmdropoffz)
 +        tmdropoffz = lowfloor;
 +
 +    // if contacted a special line, add it to the list
 +    if (ld->special)
 +    {
 +        spechit[numspechit] = ld;
 +        numspechit++;
 +
 +        // fraggle: spechits overrun emulation code from prboom-plus
 +        if (numspechit > MAXSPECIALCROSS_ORIGINAL)
 +        {
 +            SpechitOverrun(ld);
 +        }
 +    }
 +
 +    return true;
 +}
 +
 +//
 +// PIT_CheckThing
 +//
 +boolean PIT_CheckThing (mobj_t* thing)
 +{
 +    fixed_t             blockdist;
 +    boolean             solid;
 +    int                 damage;
 +
 +    if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) ))
 +        return true;
 +
 +    // don't clip against self
 +    if (thing == tmthing)
 +        return true;
 +
 +    blockdist = thing->radius + tmthing->radius;
 +
 +    if ( abs(thing->x - tmx) >= blockdist
 +        || abs(thing->y - tmy) >= blockdist )
 +    {
 +        // didn't hit it
 +        return true;
 +    }
 +
 +    // villsa [STRIFE] see if it went over / under
 +    if(thing->height + thing->z < tmthing->z)
 +        return true;    // overhead
 +
 +    // villsa [STRIFE] see if it went over / under
 +    if (tmthing->z + tmthing->height < thing->z)
 +        return true;    // underneath
 +
 +    // villsa [STRIFE] unused
 +    // check for skulls slamming into things
 +    /*if (tmthing->flags & MF_SKULLFLY)
 +    {
 +        damage = ((P_Random()%8)+1)*tmthing->info->damage;
 +
 +        P_DamageMobj (thing, tmthing, tmthing, damage);
 +
 +        tmthing->flags &= ~MF_SKULLFLY;
 +        tmthing->momx = tmthing->momy = tmthing->momz = 0;
 +
 +        P_SetMobjState (tmthing, tmthing->info->spawnstate);
 +
 +        return false;           // stop moving
 +    }*/
 +
 +    // missiles can hit other things
 +    if (tmthing->flags & MF_MISSILE)
 +    {
 +        // villsa [STRIFE] code to check over/under clipping moved to the beginning of the
 +        // function, so that it applies to everything.
 +
 +        // villsa [STRIFE] updated to strife version
 +        if (tmthing->target && (tmthing->target->type == thing->type))
 +        {
 +            // Don't hit same species as originator.
 +            if (thing == tmthing->target)
 +                return true;
 +
 +            // sdh: Add deh_species_infighting here.  We can override the
 +            // "monsters of the same species cant hurt each other" behavior
 +            // through dehacked patches
 +
 +            if (thing->type != MT_PLAYER && !deh_species_infighting)
 +            {
 +                // Explode, but do no damage.
 +                // Let players missile other players.
 +                return false;
 +            }
 +        }
 +
 +        if (!(thing->flags & MF_SHOOTABLE))
 +        {
 +            // didn't do any damage
 +            return !(thing->flags & MF_SOLID);
 +        }
 +
 +        // haleyjd 09/01/10: [STRIFE] Spectral check:
 +        // Missiles cannot hit SPECTRAL entities unless the missiles are also
 +        // flagged as SPECTRAL.
 +        if (thing->flags & MF_SPECTRAL && !(tmthing->flags & MF_SPECTRAL))
 +            return true; // keep going
 +
 +        // damage / explode
 +        // haleyjd 09/01/10: [STRIFE] Modified missile damage formula
 +        damage = ((P_Random()&3)+1)*tmthing->info->damage;
 +        P_DamageMobj (thing, tmthing, tmthing->target, damage);
 +
 +        // don't traverse any more
 +        return false;
 +    }
 +
 +    // check for special pickup
 +    if (thing->flags & MF_SPECIAL)
 +    {
 +        solid = thing->flags&MF_SOLID;
 +        if (tmthing->player) // villsa [STRIFE] no longer checks MF_PICKUP flag
 +        {
 +            // can remove thing
 +            P_TouchSpecialThing (thing, tmthing);
 +        }
 +        return !solid;
 +    }
 +
 +    return !(thing->flags & MF_SOLID);
 +}
 +
 +
 +//
 +// MOVEMENT CLIPPING
 +//
 +
 +//
 +// P_CheckPosition
 +// This is purely informative, nothing is modified
 +// (except things picked up).
 +//
 +// in:
 +//  a mobj_t (can be valid or invalid)
 +//  a position to be checked
 +//   (doesn't need to be related to the mobj_t->x,y)
 +//
 +// during:
 +//  special things are touched if MF_PICKUP
 +//  early out on solid lines?
 +//
 +// out:
 +//  newsubsec
 +//  floorz
 +//  ceilingz
 +//  tmdropoffz
 +//   the lowest point contacted
 +//   (monsters won't move to a dropoff)
 +//  speciallines[]
 +//  numspeciallines
 +//
 +// haleyjd 20110203:
 +// [STRIFE] Modified to clear blockingline in advance of P_BlockLinesIterator
 +//
 +boolean
 +P_CheckPosition
 +( mobj_t*   thing,
 +  fixed_t   x,
 +  fixed_t   y )
 +{
 +    int             xl;
 +    int             xh;
 +    int             yl;
 +    int             yh;
 +    int             bx;
 +    int             by;
 +    subsector_t*    newsubsec;
 +
 +    tmthing = thing;
 +    tmflags = thing->flags;
 +
 +    tmx = x;
 +    tmy = y;
 +
 +    tmbbox[BOXTOP] = y + tmthing->radius;
 +    tmbbox[BOXBOTTOM] = y - tmthing->radius;
 +    tmbbox[BOXRIGHT] = x + tmthing->radius;
 +    tmbbox[BOXLEFT] = x - tmthing->radius;
 +
 +    newsubsec = R_PointInSubsector (x,y);
 +
 +    // [STRIFE] clear blockingline (see P_XYMovement, P_BlockLinesIterator)
 +    blockingline = NULL;
 +    ceilingline  = NULL;
 +
 +    // The base floor / ceiling is from the subsector
 +    // that contains the point.
 +    // Any contacted lines the step closer together
 +    // will adjust them.
 +    tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
 +    tmceilingz = newsubsec->sector->ceilingheight;
 +
 +    validcount++;
 +    numspechit = 0;
 +
 +    if ( tmflags & MF_NOCLIP )
 +        return true;
 +
 +    // Check things first, possibly picking things up.
 +    // The bounding box is extended by MAXRADIUS
 +    // because mobj_ts are grouped into mapblocks
 +    // based on their origin point, and can overlap
 +    // into adjacent blocks by up to MAXRADIUS units.
 +    xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
 +    xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
 +    yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
 +    yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
 +
 +    for (bx=xl ; bx<=xh ; bx++)
 +        for (by=yl ; by<=yh ; by++)
 +            if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
 +                return false;
 +
 +    // check lines
 +    xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
 +    xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
 +    yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
 +    yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
 +
 +    for (bx=xl ; bx<=xh ; bx++)
 +        for (by=yl ; by<=yh ; by++)
 +            if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
 +                return false;
 +
 +    return true;
 +}
 +
 +
 +//
 +// P_TryMove
 +// Attempt to move to a new position,
 +// crossing special lines unless MF_TELEPORT is set.
 +//
 +boolean
 +P_TryMove
 +( mobj_t*	thing,
 +  fixed_t	x,
 +  fixed_t	y )
 +{
 +    fixed_t oldx;
 +    fixed_t oldy;
 +    int     side;
 +    int     oldside;
 +    line_t* ld;
 +
 +    floatok = false;
 +    if (!P_CheckPosition (thing, x, y))
 +        return false;       // solid wall or thing
 +
 +    if ( !(thing->flags & MF_NOCLIP) )
 +    {
 +        if (tmceilingz - tmfloorz < thing->height)
 +            return false;   // doesn't fit
 +
 +        floatok = true;
 +
 +        // villsa [STRIFE] Removed MF_TELEPORT
 +        if (tmceilingz - thing->z < thing->height)
 +            return false;       // mobj must lower itself to fit
 +
 +        // villsa [STRIFE] non-robots are limited to 16 unit step height
 +        if ((thing->flags & MF_NOBLOOD) == 0 && tmfloorz - thing->z > (16*FRACUNIT))
 +            return false;
 +        if (tmfloorz - thing->z > 24*FRACUNIT)
 +            return false;       // too big a step up
 +
 +        // villsa [STRIFE] special case for missiles
 +        if((thing->flags & MF_MISSILE) && tmfloorz - thing->z > 4*FRACUNIT)
 +            return false;
 +
 +        // haleyjd 20110204 [STRIFE]: dropoff height changed 24 -> 32
 +        if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT))
 +            && tmfloorz - tmdropoffz > 32*FRACUNIT )
 +            return false;       // don't stand over a dropoff
 +    }
 +
 +    // the move is ok,
 +    // so link the thing into its new position
 +    P_UnsetThingPosition (thing);
 +
 +    oldx = thing->x;
 +    oldy = thing->y;
 +    thing->floorz = tmfloorz;
 +    thing->ceilingz = tmceilingz;
 +    thing->x = x;
 +    thing->y = y;
 +
 +    P_SetThingPosition (thing);
 +
 +    // if any special lines were hit, do the effect
 +    if (! (thing->flags&MF_NOCLIP) ) // villsa [STRIFE] MF_TELEPORT not used
 +    {
 +        while (numspechit--)
 +        {
 +            // see if the line was crossed
 +            ld = spechit[numspechit];
 +            side = P_PointOnLineSide (thing->x, thing->y, ld);
 +            oldside = P_PointOnLineSide (oldx, oldy, ld);
 +            if (side != oldside)
 +            {
 +                if (ld->special)
 +                    P_CrossSpecialLine (ld-lines, oldside, thing);
 +            }
 +        }
 +    }
 +
 +    return true;
 +}
 +
 +//
 +// P_CheckPositionZ
 +//
 +// villsa [STRIFE] new function
 +// Check colliding things on top of one another; ie., 3D Object Clipping
 +//
 +boolean P_CheckPositionZ(mobj_t* thing, fixed_t height)
 +{
 +    fixed_t         x;
 +    fixed_t         y;
 +    fixed_t         z;
 +    int             xl;
 +    int             xh;
 +    int             yl;
 +    int             yh;
 +    int             bx;
 +    int             by;
 +    subsector_t*    newsubsec;
 +
 +    x = thing->x;
 +    y = thing->y;
 +    z = thing->z;
 +
 +    thing->z = height;
 +
 +    tmthing = thing;
 +    tmflags = thing->flags;
 +    tmx = x;
 +    tmy = y;
 +
 +    tmbbox[BOXTOP] = y + tmthing->radius;
 +    tmbbox[BOXBOTTOM] = y - tmthing->radius;
 +    tmbbox[BOXRIGHT] = x + tmthing->radius;
 +    tmbbox[BOXLEFT] = x - tmthing->radius;
 +
 +    ceilingline = 0;
 +    newsubsec = thing->subsector;
 +
 +    // The base floor / ceiling is from the subsector
 +    // that contains the point.
 +    // Any contacted lines the step closer together
 +    // will adjust them.
 +
 +    tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
 +    tmceilingz = newsubsec->sector->ceilingheight;
 +
 +    if(tmflags & MF_NOCLIP)
 +        return true;
 +
 +    // Check things first, possibly picking things up.
 +    // The bounding box is extended by MAXRADIUS
 +    // because mobj_ts are grouped into mapblocks
 +    // based on their origin point, and can overlap
 +    // into adjacent blocks by up to MAXRADIUS units.
 +
 +    xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
 +    xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
 +    yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
 +    yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
 +
 +    for(bx = xl; bx <= xh; bx++)
 +    {
 +        for(by = yl; by <= yh; by++)
 +        {
 +            if(!P_BlockThingsIterator(bx, by, PIT_CheckThing))
 +            {
 +                tmthing->z = z;
 +                return false;
 +            }
 +        }
 +    }
 +
 +    return true;
 +}
 +
 +
 +//
 +// P_ThingHeightClip
 +// Takes a valid thing and adjusts the thing->floorz,
 +// thing->ceilingz, and possibly thing->z.
 +// This is called for all nearby monsters
 +// whenever a sector changes height.
 +// If the thing doesn't fit,
 +// the z will be set to the lowest value
 +// and false will be returned.
 +//
 +// [STRIFE] Verified unmodified
 +//
 +boolean P_ThingHeightClip (mobj_t* thing)
 +{
 +    boolean     onfloor;
 +
 +    onfloor = (thing->z == thing->floorz);
 +
 +    P_CheckPosition (thing, thing->x, thing->y);
 +    // what about stranding a monster partially off an edge?
 +
 +    thing->floorz = tmfloorz;
 +    thing->ceilingz = tmceilingz;
 +
 +    if (onfloor)
 +    {
 +        // walking monsters rise and fall with the floor
 +        thing->z = thing->floorz;
 +    }
 +    else
 +    {
 +        // don't adjust a floating monster unless forced to
 +        if (thing->z+thing->height > thing->ceilingz)
 +            thing->z = thing->ceilingz - thing->height;
 +    }
 +
 +    if (thing->ceilingz - thing->floorz < thing->height)
 +        return false;
 +
 +    return true;
 +}
 +
 +
 +
 +//
 +// SLIDE MOVE
 +// Allows the player to slide along any angled walls.
 +//
 +fixed_t		bestslidefrac;
 +fixed_t		secondslidefrac;
 +
 +line_t*		bestslideline;
 +line_t*		secondslideline;
 +
 +mobj_t*		slidemo;
 +
 +fixed_t		tmxmove;
 +fixed_t		tmymove;
 +
 +
 +
 +//
 +// P_HitSlideLine
 +// Adjusts the xmove / ymove
 +// so that the next move will slide along the wall.
 +//
 +// [STRIFE] Verified unmodified
 +//
 +void P_HitSlideLine (line_t* ld)
 +{
 +    int         side;
 +
 +    angle_t     lineangle;
 +    angle_t     moveangle;
 +    angle_t     deltaangle;
 +
 +    fixed_t     movelen;
 +    fixed_t     newlen;
 +
 +
 +    if (ld->slopetype == ST_HORIZONTAL)
 +    {
 +        tmymove = 0;
 +        return;
 +    }
 +
 +    if (ld->slopetype == ST_VERTICAL)
 +    {
 +        tmxmove = 0;
 +        return;
 +    }
 +
 +    side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);
 +
 +    lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
 +
 +    if (side == 1)
 +        lineangle += ANG180;
 +
 +    moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);
 +    deltaangle = moveangle-lineangle;
 +
 +    if (deltaangle > ANG180)
 +        deltaangle += ANG180;
 +    //  I_Error ("SlideLine: ang>ANG180");
 +
 +    lineangle >>= ANGLETOFINESHIFT;
 +    deltaangle >>= ANGLETOFINESHIFT;
 +
 +    movelen = P_AproxDistance (tmxmove, tmymove);
 +    newlen = FixedMul (movelen, finecosine[deltaangle]);
 +
 +    tmxmove = FixedMul (newlen, finecosine[lineangle]);
 +    tmymove = FixedMul (newlen, finesine[lineangle]);
 +}
 +
 +
 +//
 +// PTR_SlideTraverse
 +//
 +// [STRIFE] Modified for smaller step-up height
 +//
 +boolean PTR_SlideTraverse (intercept_t* in)
 +{
 +    line_t* li;
 +
 +    if (!in->isaline)
 +        I_Error ("PTR_SlideTraverse: not a line?");
 +
 +    li = in->d.line;
 +
 +    if ( ! (li->flags & ML_TWOSIDED) )
 +    {
 +        if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
 +        {
 +            // don't hit the back side
 +            return true;
 +        }
 +        goto isblocking;
 +    }
 +
 +    // set openrange, opentop, openbottom
 +    P_LineOpening (li);
 +
 +    if (openrange < slidemo->height)
 +        goto isblocking;        // doesn't fit
 +
 +    if (opentop - slidemo->z < slidemo->height)
 +        goto isblocking;        // mobj is too high
 +
 +    // villsa [STRIFE] change from 24 to 16
 +    if (openbottom - slidemo->z > 16*FRACUNIT )
 +        goto isblocking;        // too big a step up
 +
 +    // this line doesn't block movement
 +    return true;
 +
 +    // the line does block movement,
 +    // see if it is closer than best so far
 +isblocking:
 +    if (in->frac < bestslidefrac)
 +    {
 +        secondslidefrac = bestslidefrac;
 +        secondslideline = bestslideline;
 +        bestslidefrac = in->frac;
 +        bestslideline = li;
 +    }
 +
 +    return false;   // stop
 +}
 +
 +
 +
 +//
 +// P_SlideMove
 +// The momx / momy move is bad, so try to slide
 +// along a wall.
 +// Find the first line hit, move flush to it,
 +// and slide along it
 +//
 +// This is a kludgy mess.
 +//
 +// [STRIFE] Verified unmodified
 +//
 +void P_SlideMove (mobj_t* mo)
 +{
 +    fixed_t     leadx;
 +    fixed_t     leady;
 +    fixed_t     trailx;
 +    fixed_t     traily;
 +    fixed_t     newx;
 +    fixed_t     newy;
 +    int         hitcount;
 +
 +    slidemo = mo;
 +    hitcount = 0;
 +
 +retry:
 +    if (++hitcount == 3)
 +        goto stairstep;     // don't loop forever
 +
 +    // trace along the three leading corners
 +    if (mo->momx > 0)
 +    {
 +        leadx = mo->x + mo->radius;
 +        trailx = mo->x - mo->radius;
 +    }
 +    else
 +    {
 +        leadx = mo->x - mo->radius;
 +        trailx = mo->x + mo->radius;
 +    }
 +
 +    if (mo->momy > 0)
 +    {
 +        leady = mo->y + mo->radius;
 +        traily = mo->y - mo->radius;
 +    }
 +    else
 +    {
 +        leady = mo->y - mo->radius;
 +        traily = mo->y + mo->radius;
 +    }
 +
 +    bestslidefrac = FRACUNIT+1;
 +
 +    P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy,
 +                     PT_ADDLINES, PTR_SlideTraverse );
 +    P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy,
 +                     PT_ADDLINES, PTR_SlideTraverse );
 +    P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy,
 +                     PT_ADDLINES, PTR_SlideTraverse );
 +
 +    // move up to the wall
 +    if (bestslidefrac == FRACUNIT+1)
 +    {
 +        // the move most have hit the middle, so stairstep
 +stairstep:
 +        if (!P_TryMove (mo, mo->x, mo->y + mo->momy))
 +            P_TryMove (mo, mo->x + mo->momx, mo->y);
 +        return;
 +    }
 +
 +    // fudge a bit to make sure it doesn't hit
 +    bestslidefrac -= 0x800;
 +    if (bestslidefrac > 0)
 +    {
 +        newx = FixedMul (mo->momx, bestslidefrac);
 +        newy = FixedMul (mo->momy, bestslidefrac);
 +
 +        if (!P_TryMove (mo, mo->x+newx, mo->y+newy))
 +            goto stairstep;
 +    }
 +
 +    // Now continue along the wall.
 +    // First calculate remainder.
 +    bestslidefrac = FRACUNIT-(bestslidefrac+0x800);
 +
 +    if (bestslidefrac > FRACUNIT)
 +        bestslidefrac = FRACUNIT;
 +
 +    if (bestslidefrac <= 0)
 +        return;
 +
 +    tmxmove = FixedMul (mo->momx, bestslidefrac);
 +    tmymove = FixedMul (mo->momy, bestslidefrac);
 +
 +    P_HitSlideLine (bestslideline); // clip the moves
 +
 +    mo->momx = tmxmove;
 +    mo->momy = tmymove;
 +
 +    if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove))
 +    {
 +        goto retry;
 +    }
 +}
 +
 +
 +//
 +// P_LineAttack
 +//
 +mobj_t*		linetarget;	// who got hit (or NULL)
 +mobj_t*		shootthing;
 +
 +// Height if not aiming up or down
 +// ???: use slope for monsters?
 +fixed_t		shootz;
 +
 +int		la_damage;
 +fixed_t		attackrange;
 +
 +fixed_t		aimslope;
 +
 +// slopes to top and bottom of target
 +extern fixed_t	topslope;
 +extern fixed_t	bottomslope;
 +
 +
 +//
 +// PTR_AimTraverse
 +// Sets linetaget and aimslope when a target is aimed at.
 +//
 +// [STRIFE] Verified unmodified
 +//
 +boolean
 +PTR_AimTraverse (intercept_t* in)
 +{
 +    line_t*     li;
 +    mobj_t*     th;
 +    fixed_t     slope;
 +    fixed_t     thingtopslope;
 +    fixed_t     thingbottomslope;
 +    fixed_t     dist;
 +
 +    if (in->isaline)
 +    {
 +        li = in->d.line;
 +
 +        if ( !(li->flags & ML_TWOSIDED) )
 +            return false;       // stop
 +
 +        // Crosses a two sided line.
 +        // A two sided line will restrict
 +        // the possible target ranges.
 +        P_LineOpening (li);
 +
 +        if (openbottom >= opentop)
 +            return false;       // stop
 +
 +        dist = FixedMul (attackrange, in->frac);
 +
 +        // Return false if there is no back sector.  This should never
 +        // be the case if the line is two-sided; however, some WADs
 +        // (eg. ottawau.wad) use this as an "impassible glass" trick
 +        // and rely on Vanilla Doom's (unintentional) support for this.
 +
 +        if (li->backsector == NULL)
 +        {
 +            return false;
 +        }
 +
 +        if (li->frontsector->floorheight != li->backsector->floorheight)
 +        {
 +            slope = FixedDiv (openbottom - shootz , dist);
 +            if (slope > bottomslope)
 +                bottomslope = slope;
 +        }
 +
 +        if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
 +        {
 +            slope = FixedDiv (opentop - shootz , dist);
 +            if (slope < topslope)
 +                topslope = slope;
 +        }
 +
 +        if (topslope <= bottomslope)
 +            return false;       // stop
 +
 +        return true;            // shot continues
 +    }
 +
 +    // shoot a thing
 +    th = in->d.thing;
 +    if (th == shootthing)
 +        return true;            // can't shoot self
 +
 +    if (!(th->flags&MF_SHOOTABLE))
 +        return true;            // corpse or something
 +
 +    // check angles to see if the thing can be aimed at
 +    dist = FixedMul (attackrange, in->frac);
 +    thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
 +
 +    if (thingtopslope < bottomslope)
 +        return true;            // shot over the thing
 +
 +    thingbottomslope = FixedDiv (th->z - shootz, dist);
 +
 +    if (thingbottomslope > topslope)
 +        return true;            // shot under the thing
 +
 +    // this thing can be hit!
 +    if (thingtopslope > topslope)
 +        thingtopslope = topslope;
 +
 +    if (thingbottomslope < bottomslope)
 +        thingbottomslope = bottomslope;
 +
 +    aimslope = (thingtopslope+thingbottomslope)/2;
 +    linetarget = th;
 +
 +    return false;               // don't go any farther
 +}
 +
 +
 +//
 +// PTR_ShootTraverse
 +//
 +// [STRIFE] Changes for Spectres and Mauler puff/damage inflictor
 +//
 +boolean PTR_ShootTraverse (intercept_t* in)
 +{
 +    fixed_t             x;
 +    fixed_t             y;
 +    fixed_t             z;
 +    fixed_t             frac;
 +
 +    line_t*             li;
 +
 +    mobj_t*             th;
 +    mobj_t*             th2;    // villsa [STRIFE]
 +
 +    fixed_t             slope;
 +    fixed_t             dist;
 +    fixed_t             thingtopslope;
 +    fixed_t             thingbottomslope;
 +
 +    if (in->isaline)
 +    {
 +        li = in->d.line;
 +
 +        if (li->special)
 +            P_ShootSpecialLine (shootthing, li);
 +
 +        if ( !(li->flags & ML_TWOSIDED) )
 +            goto hitline;
 +
 +        // crosses a two sided line
 +        P_LineOpening (li);
 +
 +        dist = FixedMul (attackrange, in->frac);
 +
 +        // Check if backsector is NULL.  See comment in PTR_AimTraverse.
 +
 +        if (li->backsector == NULL)
 +        {
 +            goto hitline;
 +        }
 +
 +        if (li->frontsector->floorheight != li->backsector->floorheight)
 +        {
 +            slope = FixedDiv (openbottom - shootz , dist);
 +            if (slope > aimslope)
 +                goto hitline;
 +        }
 +
 +        if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
 +        {
 +            slope = FixedDiv (opentop - shootz , dist);
 +            if (slope < aimslope)
 +                goto hitline;
 +        }
 +
 +        // shot continues
 +        return true;
 +
 +
 +        // hit line
 +hitline:
 +        // position a bit closer
 +        frac = in->frac - FixedDiv (4*FRACUNIT,attackrange);
 +        x = trace.x + FixedMul (trace.dx, frac);
 +        y = trace.y + FixedMul (trace.dy, frac);
 +        z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
 +
 +        if (li->frontsector->ceilingpic == skyflatnum)
 +        {
 +            // don't shoot the sky!
 +            if (z > li->frontsector->ceilingheight)
 +                return false;
 +
 +            // it's a sky hack wall
 +            if	(li->backsector && li->backsector->ceilingpic == skyflatnum)
 +                return false;
 +        }
 +
 +        // villsa [STRIFE]
 +        if(la_damage > 0)
 +        {
 +            // villsa [STRIFE] Test against Mauler attack range
 +            if(attackrange != 2112*FRACUNIT)
 +                P_SpawnPuff(x, y, z); // Spawn bullet puffs.
 +            else
 +                P_SpawnMobj(x, y, z, MT_STRIFEPUFF3);
 +        }
 +
 +        // don't go any farther
 +        return false;
 +    }
 +
 +    // shoot a thing
 +    th = in->d.thing;
 +    if (th == shootthing)
 +        return true;        // can't shoot self
 +
 +    if (!(th->flags&MF_SHOOTABLE))
 +        return true;        // corpse or something
 +
 +    // haleyjd 09/18/10: [STRIFE] Corrected - not MVIS, but SPECTRAL.
 +    if(th->flags & MF_SPECTRAL)
 +        return true;        // is a spectral entity
 +
 +    // check angles to see if the thing can be aimed at
 +    dist = FixedMul (attackrange, in->frac);
 +    thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
 +
 +    if (thingtopslope < aimslope)
 +        return true;        // shot over the thing
 +
 +    thingbottomslope = FixedDiv (th->z - shootz, dist);
 +
 +    if (thingbottomslope > aimslope)
 +        return true;        // shot under the thing
 +
 +    // hit thing
 +    // position a bit closer
 +    frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);
 +
 +    x = trace.x + FixedMul (trace.dx, frac);
 +    y = trace.y + FixedMul (trace.dy, frac);
 +    z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
 +
 +    // villsa [STRIFE] Check for Mauler attack range
 +    if(attackrange == 2112*FRACUNIT)
 +    {
 +        th2 = P_SpawnMobj(x, y, z, MT_STRIFEPUFF3);
 +        th2->momz = -FRACUNIT;
 +        P_DamageMobj(th, th2, shootthing, la_damage);
 +        return false;
 +    }
 +
 +    // villsa [STRIFE] disabled check for damage
 +    //if (la_damage)
 +    P_DamageMobj (th, shootthing, shootthing, la_damage);
 +
 +    // Spawn bullet puffs or blod spots,
 +    // depending on target type.
 +    if (in->d.thing->flags & MF_NOBLOOD)
 +        P_SpawnSparkPuff(x, y, z);  // villsa [STRIFE] call spark puff function instead
 +    else
 +        P_SpawnBlood (x,y,z, la_damage);
 +
 +    // don't go any farther
 +    return false;
 +
 +}
 +
 +
 +//
 +// P_AimLineAttack
 +//
 +// [STRIFE] Modified to support player->pitch
 +//
 +fixed_t
 +P_AimLineAttack
 +( mobj_t*	t1,
 +  angle_t	angle,
 +  fixed_t	distance )
 +{
 +    fixed_t     x2;
 +    fixed_t     y2;
 +
 +    t1 = P_SubstNullMobj(t1);
 +
 +    angle >>= ANGLETOFINESHIFT;
 +    shootthing = t1;
 +
 +    x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
 +    y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
 +    shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
 +
 +    // can't shoot outside view angles
 +    topslope = 100*FRACUNIT/160;
 +    bottomslope = -100*FRACUNIT/160;
 +
 +    attackrange = distance;
 +    linetarget = NULL;
 +
 +    P_PathTraverse ( t1->x, t1->y,
 +                     x2, y2,
 +                     PT_ADDLINES|PT_ADDTHINGS,
 +                     PTR_AimTraverse );
 +
 +    if (linetarget)
 +        return aimslope;
 +    else    // villsa [STRIFE] checks for player pitch
 +    {
 +        if(t1->player)
 +            return (t1->player->pitch << FRACBITS) / 160;
 +    }
 +
 +    return 0;
 +}
 +
 +
 +//
 +// P_LineAttack
 +// If damage == 0, it is just a test trace
 +// that will leave linetarget set.
 +//
 +// [STRIFE] Modified to check lines only if damage <= 0 (see P_RadiusAttack)
 +//
 +void
 +P_LineAttack
 +( mobj_t*       t1,
 +  angle_t       angle,
 +  fixed_t       distance,
 +  fixed_t       slope,
 +  int	        damage )
 +{
 +    fixed_t     x2;
 +    fixed_t     y2;
 +    int         traverseflags;
 +
 +    angle >>= ANGLETOFINESHIFT;
 +    shootthing = t1;
 +    la_damage = damage;
 +    x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
 +    y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
 +    shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
 +    attackrange = distance;
 +    aimslope = slope;
 +
 +    // villsa [STRIFE] test lines only if damage is <= 0
 +    if(damage >= 1)
 +        traverseflags = (PT_ADDLINES|PT_ADDTHINGS);
 +    else
 +        traverseflags = PT_ADDLINES;
 +
 +    P_PathTraverse(t1->x, t1->y,
 +                   x2, y2,
 +                   traverseflags,
 +                   PTR_ShootTraverse);
 +}
 +
 +
 +
 +//
 +// USE LINES
 +//
 +// [STRIFE] Verified unmodified
 +//
 +mobj_t*     usething;
 +
 +boolean PTR_UseTraverse (intercept_t* in)
 +{
 +    int     side;
 +
 +    if (!in->d.line->special)
 +    {
 +        P_LineOpening (in->d.line);
 +        if (openrange <= 0)
 +        {
 +            S_StartSound (usething, sfx_noway);
 +
 +            // can't use through a wall
 +            return false;
 +        }
 +        // not a special line, but keep checking
 +        return true ;
 +    }
 +
 +    side = 0;
 +    if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
 +        side = 1;
 +
 +    //  return false;       // don't use back side
 +
 +    P_UseSpecialLine (usething, in->d.line, side);
 +
 +    // can't use for than one special line in a row
 +    return false;
 +}
 +
 +
 +//
 +// P_UseLines
 +// Looks for special lines in front of the player to activate.
 +//
 +// [STRIFE] Verified unmodified
 +//
 +void P_UseLines (player_t*	player)
 +{
 +    int     angle;
 +    fixed_t x1;
 +    fixed_t y1;
 +    fixed_t x2;
 +    fixed_t y2;
 +
 +    usething = player->mo;
 +
 +    angle = player->mo->angle >> ANGLETOFINESHIFT;
 +
 +    x1 = player->mo->x;
 +    y1 = player->mo->y;
 +    x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
 +    y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
 +
 +    P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
 +}
 +
 +
 +//
 +// RADIUS ATTACK
 +//
 +mobj_t*     bombsource;
 +mobj_t*     bombspot;
 +int         bombdamage;
 +
 +
 +//
 +// PIT_RadiusAttack
 +// "bombsource" is the creature
 +// that caused the explosion at "bombspot".
 +//
 +// [STRIFE] Modified for Spectral and Inquisitor exclusions
 +//
 +boolean PIT_RadiusAttack (mobj_t* thing)
 +{
 +    fixed_t dx;
 +    fixed_t dy;
 +    fixed_t dist;
 +
 +    if (!(thing->flags & MF_SHOOTABLE))
 +        return true;
 +
 +    // haleyjd 10/04/10: Spectrals are not damaged by blast radii
 +    if(thing->flags & MF_SPECTRAL)
 +        return true;
 +
 +    // Boss spider and cyborg
 +    // take no damage from concussion.
 +    // villsa [STRIFE] unused
 +    // - haleyjd: INQUISITOR
 +
 +    if(thing->type == MT_INQUISITOR)
 +        return true;
 +
 +    dx = abs(thing->x - bombspot->x);
 +    dy = abs(thing->y - bombspot->y);
 +
 +    dist = dx>dy ? dx : dy;
 +    dist = (dist - thing->radius) >> FRACBITS;
 +
 +    if (dist < 0)
 +        dist = 0;
 +
 +    if (dist >= bombdamage)
 +        return true;        // out of range
 +
 +    if ( P_CheckSight (thing, bombspot) )
 +    {
 +        // must be in direct path
 +        P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist);
 +    }
 +
 +    return true;
 +}
 +
 +
 +//
 +// P_RadiusAttack
 +// Source is the creature that caused the explosion at spot.
 +//
 +// [STRIFE] Modified to emit "test" tracers which can shatter glass screens
 +//          and windows.
 +//
 +void
 +P_RadiusAttack
 +( mobj_t*       spot,
 +  mobj_t*       source,
 +  int           damage )
 +{
 +    int         x;
 +    int         y;
 +
 +    int         xl;
 +    int         xh;
 +    int         yl;
 +    int         yh;
 +
 +    fixed_t     dist;
 +
 +    dist = (damage+MAXRADIUS)<<FRACBITS;
 +    yh = (spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
 +    yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
 +    xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
 +    xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;
 +    bombspot = spot;
 +    bombsource = source;
 +    bombdamage = damage;
 +
 +    for (y=yl ; y<=yh ; y++)
 +        for (x=xl ; x<=xh ; x++)
 +            P_BlockThingsIterator (x, y, PIT_RadiusAttack );
 +
 +    // villsa [STRIFE] Send out 0 damage tracers to shatter nearby glass.
 +    spot->z += 32*FRACUNIT;
 +    P_LineAttack(spot, 0,      dist, 1, 0);
 +    P_LineAttack(spot, ANG90,  dist, 1, 0);
 +    P_LineAttack(spot, ANG180, dist, 1, 0);
 +    P_LineAttack(spot, ANG270, dist, 1, 0);
 +    spot->z -= 32*FRACUNIT;
 +}
 +
 +
 +
 +//
 +// SECTOR HEIGHT CHANGING
 +// After modifying a sectors floor or ceiling height,
 +// call this routine to adjust the positions
 +// of all things that touch the sector.
 +//
 +// If anything doesn't fit anymore, true will be returned.
 +// If crunch is true, they will take damage
 +//  as they are being crushed.
 +// If Crunch is false, you should set the sector height back
 +//  the way it was and call P_ChangeSector again
 +//  to undo the changes.
 +//
 +boolean		crushchange;
 +boolean		nofit;
 +
 +
 +//
 +// PIT_ChangeSector
 +//
 +// [STRIFE] Changes to crushing behavior
 +//
 +boolean PIT_ChangeSector (mobj_t*   thing)
 +{
 +    mobj_t*	mo;
 +
 +    if (P_ThingHeightClip (thing))
 +    {
 +        // keep checking
 +        return true;
 +    }
 +
 +    // crunch bodies to giblets
 +    if (thing->health <= 0)
 +    {
 +        // villsa [STRIFE] do something with the player
 +        if(thing->player && thing->subsector->sector->specialdata)
 +        {
 +            nofit = true;
 +            return false;
 +        }
 +        //P_SetMobjState (thing, S_GIBS);   // villsa [STRIFE] unused
 +
 +        A_BodyParts(thing); // villsa [STRIFE] spit out meat/junk stuff
 +        thing->flags &= ~MF_SOLID;
 +        thing->height = 0;
 +        thing->radius = 0;
 +
 +        // keep checking
 +        return true;
 +    }
 +
 +    // crunch dropped items
 +    if (thing->flags & MF_DROPPED)
 +    {
 +        P_RemoveMobj (thing);
 +
 +        // keep checking
 +        return true;
 +    }
 +
 +    if (! (thing->flags & MF_SHOOTABLE) )
 +    {
 +        // assume it is bloody gibs or something
 +        return true;
 +    }
 +
 +    nofit = true;
 +
 +    if (crushchange && !(leveltime&3) )
 +    {
 +        int t;
 +        S_StartSound(thing, sfx_pcrush);   // villsa [STRIFE]
 +        P_DamageMobj(thing,NULL,NULL,10);
 +
 +        // spray blood in a random direction
 +        mo = P_SpawnMobj (thing->x,
 +                          thing->y,
 +                          thing->z + thing->height/2, MT_BLOOD_DEATH);
 +
 +        t = P_Random();
 +        mo->momx = (t - P_Random ()) << 12;
 +        t = P_Random();
 +        mo->momy = (t - P_Random ()) << 12;
 +    }
 +
 +    // keep checking (crush other things)
 +    return true;
 +}
 +
 +
 +
 +//
 +// P_ChangeSector
 +//
 +// [STRIFE] Verified unmodified
 +//
 +boolean
 +P_ChangeSector
 +( sector_t* sector,
 +  boolean   crunch )
 +{
 +    int     x;
 +    int     y;
 +
 +    nofit = false;
 +    crushchange = crunch;
 +
 +    // re-check heights for all things near the moving sector
 +    for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
 +        for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
 +            P_BlockThingsIterator (x, y, PIT_ChangeSector);
 +
 +    return nofit;
 +}
 +
 +// Code to emulate the behavior of Vanilla Doom when encountering an overrun
 +// of the spechit array.  This is by Andrey Budko (e6y) and comes from his
 +// PrBoom plus port.  A big thanks to Andrey for this.
 +
 +static void SpechitOverrun(line_t *ld)
 +{
 +    static unsigned int baseaddr = 0;
 +    unsigned int addr;
 +
 +    if (baseaddr == 0)
 +    {
 +        int p;
 +
 +        // This is the first time we have had an overrun.  Work out
 +        // what base address we are going to use.
 +        // Allow a spechit value to be specified on the command line.
 +
 +        //!
 +        // @category compat
 +        // @arg <n>
 +        //
 +        // Use the specified magic value when emulating spechit overruns.
 +        //
 +
-         p = M_CheckParm("-spechit");
++        p = M_CheckParmWithArgs("-spechit", 1);
 +
 +        if (p > 0)
 +        {
 +            M_StrToInt(myargv[p+1], (int *) &baseaddr);
 +        }
 +        else
 +        {
 +            baseaddr = DEFAULT_SPECHIT_MAGIC;
 +        }
 +    }
 +
 +    // Calculate address used in doom2.exe
 +
 +    addr = baseaddr + (ld - lines) * 0x3E;
 +
 +    switch(numspechit)
 +    {
 +        case 9:
 +        case 10:
 +        case 11:
 +        case 12:
 +            tmbbox[numspechit-9] = addr;
 +            break;
 +        case 13:
 +            nofit = addr; // haleyjd 20110204: nofit/crushchange are in opposite
 +            break;        // order in the Strife binary.
 +        case 14:
 +            crushchange = addr;
 +            break;
 +        default:
 +            fprintf(stderr, "SpechitOverrun: Warning: unable to emulate"
 +                            "an overrun where numspechit=%i\n",
 +                            numspechit);
 +            break;
 +    }
 +}
 +
diff --cc src/strife/p_spec.c
index af070d55,00000000..e26d9144
mode 100644,000000..100644
--- a/src/strife/p_spec.c
+++ b/src/strife/p_spec.c
@@@ -1,2003 -1,0 +1,2002 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	Implements special effects:
 +//	Texture animation, height or lighting changes
 +//	 according to adjacent sectors, respective
 +//	 utility functions, etc.
 +//	Line Tag handling. Line and Sector triggers.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#include <stdlib.h>
 +
 +#include "doomdef.h"
 +#include "doomstat.h"
 +
 +#include "deh_main.h"
 +#include "i_system.h"
 +#include "z_zone.h"
 +#include "m_argv.h"
 +#include "m_misc.h"
 +#include "m_random.h"
 +#include "w_wad.h"
 +
 +#include "r_local.h"
 +#include "p_local.h"
 +
 +#include "g_game.h"
 +
 +#include "s_sound.h"
 +
 +// State.
 +#include "r_state.h"
 +
 +// Data.
 +#include "sounds.h"
 +
 +// [STRIFE]
 +#include "hu_stuff.h"
 +#include "p_dialog.h"
 +
 +
 +//
 +// Animating textures and planes
 +// There is another anim_t used in wi_stuff, unrelated.
 +//
 +typedef struct
 +{
 +    boolean	istexture;
 +    int		picnum;
 +    int		basepic;
 +    int		numpics;
 +    int		speed;
 +
 +} anim_t;
 +
 +//
 +//      source animation definition
 +//
 +typedef struct
 +{
 +    int 	istexture;	// if false, it is a flat
 +    char	endname[9];
 +    char	startname[9];
 +    int		speed;
 +} animdef_t;
 +
 +
 +// haleyjd 08/30/10: [STRIFE] MAXANIMS raised from 32 to 40
 +#define MAXANIMS                40
 +
 +//
 +// P_InitPicAnims
 +//
 +
 +// Floor/ceiling animation sequences,
 +//  defined by first and last frame,
 +//  i.e. the flat (64x64 tile) name to
 +//  be used.
 +// The full animation sequence is given
 +//  using all the flats between the start
 +//  and end entry, in the order found in
 +//  the WAD file.
 +//
 +// haleyjd 08/29/10: [STRIFE] Changed animdefs.
 +//
 +animdef_t               animdefs[] =
 +{
 +    { false, "F_SCANR8", "F_SCANR5",  4},
 +    { false, "F_WATR03", "F_WATR01",  8},
 +    { false, "F_PWATR3", "F_PWATR1", 11},
 +    { false, "F_SCANR4", "F_SCANR1",  4},
 +    { true,  "SCAN08",   "SCAN05",    4},
 +    { true,  "SWTRMG03", "SWTRMG01",  4},
 +    { true,  "SCAN04",   "SCAN01",    4},
 +    { true,  "COMP04",   "COMP01",    4},
 +    { true,  "COMP08",   "COMP05",    6},
 +    { true,  "COMP12",   "COMP09",   11},
 +    { true,  "COMP16",   "COMP13",   12},
 +    { true,  "COMP20",   "COMP17",   12},
 +    { true,  "COMP24",   "COMP21",   12},
 +    { true,  "COMP28",   "COMP25",   12},
 +    { true,  "COMP32",   "COMP29",   12},
 +    { true,  "COMP37",   "COMP33",   12},
 +    { true,  "COMP41",   "COMP38",   12},
 +    { true,  "COMP49",   "COMP42",   10},
 +    { true,  "BRKGRY16", "BRKGRY13", 10},
 +    { true,  "BRNSCN04", "BRNSCN01", 10},
 +    { true,  "CONCRT12", "CONCRT09", 11},
 +    { true,  "CONCRT25", "CONCRT22", 11},
 +    { true,  "WALPMP02", "WALPMP01", 16},
 +    { true,  "WALTEK17", "WALTEK16",  8},
 +    { true,  "FORCE04",  "FORCE01",   4},
 +    { true,  "FORCE08",  "FORCE05",   4},
 +    { true,  "FAN02",    "FAN01",     4},
 +    { false, "F_VWATR3", "P_VWATR1",  4},
 +    { false, "F_HWATR3", "F_HWATR1",  4},
 +    { false, "F_TELE2",  "F_TELE1",   4},
 +    { false, "F_FAN2",   "F_FAN1",    4},
 +    { false, "F_CONVY2", "F_CONVY1",  4},
 +    { false, "F_RDALN4", "F_RDALN1",  4},
 +    { -1,    "",         "",          0},
 +};
 +
 +anim_t  anims[MAXANIMS];
 +anim_t* lastanim;
 +
 +//
 +//      Animating line specials
 +//
 +// haleyjd 08/29/10: [STRIFE] MAXLINEANIMS raised from 64 to 96
 +#define MAXLINEANIMS            96
 +
 +extern  short   numlinespecials;
 +extern  line_t* linespeciallist[MAXLINEANIMS];
 +
 +
 +
 +void P_InitPicAnims (void)
 +{
 +    int		i;
 +
 +
 +    //	Init animation
 +    lastanim = anims;
 +    for (i=0 ; animdefs[i].istexture != -1 ; i++)
 +    {
 +        char *startname, *endname;
 +
 +        startname = DEH_String(animdefs[i].startname);
 +        endname = DEH_String(animdefs[i].endname);
 +
 +        if (animdefs[i].istexture)
 +        {
 +            // different episode ?
 +            if (R_CheckTextureNumForName(startname) == -1)
 +                continue;
 +
 +            lastanim->picnum = R_TextureNumForName(endname);
 +            lastanim->basepic = R_TextureNumForName(startname);
 +        }
 +        else
 +        {
 +            if (W_CheckNumForName(startname) == -1)
 +                continue;
 +
 +            lastanim->picnum = R_FlatNumForName(endname);
 +            lastanim->basepic = R_FlatNumForName(startname);
 +        }
 +
 +        lastanim->istexture = animdefs[i].istexture;
 +        lastanim->numpics = lastanim->picnum - lastanim->basepic + 1;
 +
 +        if (lastanim->numpics < 2)
 +            I_Error ("P_InitPicAnims: bad cycle from %s to %s",
 +            startname, endname);
 +
 +        lastanim->speed = animdefs[i].speed;
 +        lastanim++;
 +    }
 +
 +}
 +
 +// villsa [STRIFE] terrain type definitions
 +typedef struct
 +{
 +    char*   flat;
 +    int     type;
 +    int     num;
 +} terraintype_t;
 +
 +terraintype_t   terraintypes[] =
 +{
 +    {   "F_WATR03", FLOOR_WATER,    -1  },
 +    {   "F_WATR02", FLOOR_WATER,    -1  },
 +    {   "F_WATR01", FLOOR_WATER,    -1  },
 +    {   "F_VWATR3", FLOOR_WATER,    -1  },
 +    {   "F_VWATR2", FLOOR_WATER,    -1  },
 +    {   "P_VWATR1", FLOOR_WATER,    -1  },
 +    {   "F_HWATR3", FLOOR_WATER,    -1  },
 +    {   "F_HWATR2", FLOOR_WATER,    -1  },
 +    {   "F_HWATR1", FLOOR_WATER,    -1  },
 +    {   "F_PWATR3", FLOOR_SLIME,    -1  },
 +    {   "F_PWATR2", FLOOR_SLIME,    -1  },
 +    {   "F_PWATR1", FLOOR_SLIME,    -1  },
 +    {   "END",      FLOOR_END,      -1  },
 +};
 +
 +//
 +// P_GetTerrainType
 +// villsa [STRIFE] new function
 +//
 +
 +terraintype_e P_GetTerrainType(mobj_t* mobj)
 +{
 +    int i = 0;
 +    subsector_t* ss = mobj->subsector;
 +
 +    if(mobj->z <= ss->sector->floorheight &&
 +        terraintypes[0].type != FLOOR_END)
 +    {
 +        while(ss->sector->floorpic != terraintypes[i].num)
 +        {
 +            if(terraintypes[i+1].type == FLOOR_END)
 +                return FLOOR_SOLID;
 +
 +            i++;
 +        }
 +
 +        return terraintypes[i].type;
 +    }
 +
 +    return FLOOR_SOLID;
 +}
 +
 +//
 +// P_InitTerrainTypes
 +// villsa [STRIFE] new function
 +// Initialize terrain types
 +//
 +
 +void P_InitTerrainTypes(void)
 +{
 +    int pic = 0;
 +    int i = 0;
 +
 +    if(terraintypes[0].type != FLOOR_END)
 +    {
 +        while(terraintypes[i].type != FLOOR_END)
 +        {
 +            terraintypes[i].num = R_FlatNumForName(terraintypes[i].flat);
 +            i++;
 +        }
 +    }
 +}
 +
 +
 +
 +//
 +// UTILITIES
 +//
 +
 +
 +
 +//
 +// getSide()
 +// Will return a side_t*
 +//  given the number of the current sector,
 +//  the line number, and the side (0/1) that you want.
 +//
 +side_t*
 +getSide
 +( int		currentSector,
 +  int		line,
 +  int		side )
 +{
 +    return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ];
 +}
 +
 +
 +//
 +// getSector()
 +// Will return a sector_t*
 +//  given the number of the current sector,
 +//  the line number and the side (0/1) that you want.
 +//
 +sector_t*
 +getSector
 +( int		currentSector,
 +  int		line,
 +  int		side )
 +{
 +    return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector;
 +}
 +
 +
 +//
 +// twoSided()
 +// Given the sector number and the line number,
 +//  it will tell you whether the line is two-sided or not.
 +//
 +int
 +twoSided
 +( int	sector,
 +  int	line )
 +{
 +    return (sectors[sector].lines[line])->flags & ML_TWOSIDED;
 +}
 +
 +
 +
 +
 +//
 +// getNextSector()
 +// Return sector_t * of sector next to current.
 +// NULL if not two-sided line
 +//
 +sector_t*
 +getNextSector
 +( line_t*	line,
 +  sector_t*	sec )
 +{
 +    if (!(line->flags & ML_TWOSIDED))
 +	return NULL;
 +
 +    if (line->frontsector == sec)
 +	return line->backsector;
 +
 +    return line->frontsector;
 +}
 +
 +
 +
 +//
 +// P_FindLowestFloorSurrounding()
 +// FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS
 +//
 +fixed_t	P_FindLowestFloorSurrounding(sector_t* sec)
 +{
 +    int			i;
 +    line_t*		check;
 +    sector_t*		other;
 +    fixed_t		floor = sec->floorheight;
 +
 +    for (i=0 ;i < sec->linecount ; i++)
 +    {
 +	check = sec->lines[i];
 +	other = getNextSector(check,sec);
 +
 +	if (!other)
 +	    continue;
 +
 +	if (other->floorheight < floor)
 +	    floor = other->floorheight;
 +    }
 +    return floor;
 +}
 +
 +
 +
 +//
 +// P_FindHighestFloorSurrounding()
 +// FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS
 +//
 +fixed_t	P_FindHighestFloorSurrounding(sector_t *sec)
 +{
 +    int			i;
 +    line_t*		check;
 +    sector_t*		other;
 +    fixed_t		floor = -500*FRACUNIT;
 +
 +    for (i=0 ;i < sec->linecount ; i++)
 +    {
 +	check = sec->lines[i];
 +	other = getNextSector(check,sec);
 +
 +	if (!other)
 +	    continue;
 +
 +	if (other->floorheight > floor)
 +	    floor = other->floorheight;
 +    }
 +    return floor;
 +}
 +
 +
 +
 +//
 +// P_FindNextHighestFloor
 +// FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS
 +// Note: this should be doable w/o a fixed array.
 +
 +// Thanks to entryway for the Vanilla overflow emulation.
 +
 +// 20 adjoining sectors max!
 +#define MAX_ADJOINING_SECTORS     20
 +
 +fixed_t
 +P_FindNextHighestFloor
 +( sector_t* sec,
 +  int       currentheight )
 +{
 +    int         i;
 +    int         h;
 +    int         min;
 +    line_t*     check;
 +    sector_t*   other;
 +    fixed_t     height = currentheight;
 +    fixed_t     heightlist[MAX_ADJOINING_SECTORS + 2];
 +
 +    for (i=0, h=0; i < sec->linecount; i++)
 +    {
 +        check = sec->lines[i];
 +        other = getNextSector(check,sec);
 +
 +        if (!other)
 +            continue;
 +
 +        if (other->floorheight > height)
 +        {
 +            // Emulation of memory (stack) overflow
 +            if (h == MAX_ADJOINING_SECTORS + 1)
 +            {
 +                height = other->floorheight;
 +            }
 +            else if (h == MAX_ADJOINING_SECTORS + 2)
 +            {
 +                // Fatal overflow: game crashes at 22 textures
 +                I_Error("Sector with more than 22 adjoining sectors. "
 +                        "Vanilla will crash here");
 +            }
 +
 +            heightlist[h++] = other->floorheight;
 +        }
 +    }
 +
 +    // Find lowest height in list
 +    if (!h)
 +    {
 +        return currentheight;
 +    }
 +
 +    min = heightlist[0];
 +
 +    // Range checking?
 +    for (i = 1; i < h; i++)
 +    {
 +        if (heightlist[i] < min)
 +        {
 +            min = heightlist[i];
 +        }
 +    }
 +
 +    return min;
 +}
 +
 +//
 +// FIND LOWEST CEILING IN THE SURROUNDING SECTORS
 +//
 +fixed_t
 +P_FindLowestCeilingSurrounding(sector_t* sec)
 +{
 +    int			i;
 +    line_t*		check;
 +    sector_t*		other;
 +    fixed_t		height = INT_MAX;
 +
 +    for (i=0 ;i < sec->linecount ; i++)
 +    {
 +	check = sec->lines[i];
 +	other = getNextSector(check,sec);
 +
 +	if (!other)
 +	    continue;
 +
 +	if (other->ceilingheight < height)
 +	    height = other->ceilingheight;
 +    }
 +    return height;
 +}
 +
 +
 +//
 +// FIND HIGHEST CEILING IN THE SURROUNDING SECTORS
 +//
 +fixed_t	P_FindHighestCeilingSurrounding(sector_t* sec)
 +{
 +    int		i;
 +    line_t*	check;
 +    sector_t*	other;
 +    fixed_t	height = 0;
 +
 +    for (i=0 ;i < sec->linecount ; i++)
 +    {
 +	check = sec->lines[i];
 +	other = getNextSector(check,sec);
 +
 +	if (!other)
 +	    continue;
 +
 +	if (other->ceilingheight > height)
 +	    height = other->ceilingheight;
 +    }
 +    return height;
 +}
 +
 +
 +
 +//
 +// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO
 +//
 +int
 +P_FindSectorFromLineTag
 +( line_t*	line,
 +  int		start )
 +{
 +    int	i;
 +
 +    for (i=start+1;i<numsectors;i++)
 +	if (sectors[i].tag == line->tag)
 +	    return i;
 +
 +    return -1;
 +}
 +
 +
 +
 +
 +//
 +// Find minimum light from an adjacent sector
 +//
 +int
 +P_FindMinSurroundingLight
 +( sector_t*	sector,
 +  int		max )
 +{
 +    int		i;
 +    int		min;
 +    line_t*	line;
 +    sector_t*	check;
 +
 +    min = max;
 +    for (i=0 ; i < sector->linecount ; i++)
 +    {
 +	line = sector->lines[i];
 +	check = getNextSector(line,sector);
 +
 +	if (!check)
 +	    continue;
 +
 +	if (check->lightlevel < min)
 +	    min = check->lightlevel;
 +    }
 +    return min;
 +}
 +
 +
 +
 +//
 +// EVENTS
 +// Events are operations triggered by using, crossing,
 +// or shooting special lines, or by timed thinkers.
 +//
 +
 +// [STRIFE]
 +static char crosslinestr[90];
 +
 +//
 +// P_CrossSpecialLine - TRIGGER
 +// Called every time a thing origin is about
 +//  to cross a line with a non 0 special.
 +//
 +void
 +P_CrossSpecialLine
 +( int           linenum,
 +  int           side,
 +  mobj_t*       thing )
 +{
 +    line_t*     line;
 +    side_t*     sidedef; // [STRIFE]
 +    int         flag;    // [STRIFE]
 +    int         ok;
 +
 +    line = &lines[linenum];
 +
 +    // haleyjd 09/21/10: corpses and missiles cannot activate any cross-over
 +    // line types, *except* 182 (which is for the sake of missiles).
 +    if((thing->flags & (MF_MISSILE|MF_CORPSE)) && line->special != 182)
 +        return;
 +
 +    //	Triggers that other things can activate
 +    if (!thing->player)
 +    {
 +        // Things that should NOT trigger specials...
 +        // villsa [STRIFE] unused
 +        //   haleyjd: removed dead switch. Strife only excludes missiles and
 +        //   corpses, which is handled above.
 +
 +        ok = 0;
 +
 +        // [STRIFE] Added several line types. Removed none.
 +        switch(line->special)
 +        {
 +        case 97:        // TELEPORT RETRIGGER
 +        case 185:       // haleyjd: STRIFE-TODO: Identify type
 +        case 195:       // haleyjd: STRIFE-TODO: Identify type
 +        case 231:       // haleyjd: STRIFE-TODO: Identify type
 +        case 125:       // TELEPORT MONSTERONLY TRIGGER
 +        case 126:       // TELEPORT MONSTERONLY RETRIGGER
 +        case 182:       // haleyjd: [STRIFE] Break glass - it's a W1 type too!
 +        case 10:        // PLAT DOWN-WAIT-UP-STAY TRIGGER
 +        case 39:        // TELEPORT TRIGGER
 +        case 88:        // PLAT DOWN-WAIT-UP-STAY RETRIGGER
 +        case 4:         // RAISE DOOR
 +            ok = 1;
 +            break;
 +        }
 +        if (!ok)
 +            return;
 +    }
 +
 +
 +    // Note: could use some const's here.
 +    switch (line->special)
 +    {
 +    //
 +    // TRIGGERS.
 +    // All from here to RETRIGGERS.
 +    //
 +    case 230:
 +        // haleyjd 09/21/10: [STRIFE] W1 Open Door if Quest
 +        sidedef = &sides[line->sidenum[0]];
 +        flag = (sidedef->rowoffset >> FRACBITS) - 1;
 +
 +        if(!(thing->player->questflags & (1 << flag)))
 +            break;
 +        // fall-through:
 +    case 2:
 +        // Open Door - [STRIFE] Verified unmodified.
 +        EV_DoDoor(line,open);
 +        line->special = 0;
 +        break;
 +
 +    case 227:
 +        // haleyjd 09/21/10: [STRIFE] W1 Close Door if Quest
 +        sidedef = &sides[line->sidenum[0]];
 +        flag = (sidedef->rowoffset >> FRACBITS) - 1;
 +
 +        if(!(thing->player->questflags & (1 << flag)))
 +            break;
 +        // fall-through:
 +    case 3:
 +        // Close Door - [STRIFE] Verified unmodified.
 +        EV_DoDoor(line,close);
 +        line->special = 0;
 +        break;
 +
 +    case 4:
 +        // Raise Door - [STRIFE] Verified unmodified.
 +        EV_DoDoor(line,normal);
 +        line->special = 0;
 +        break;
 +
 +    case 5:
 +        // Raise Floor - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,raiseFloor);
 +        line->special = 0;
 +        break;
 +
 +    case 6:
 +        // Fast Ceiling Crush & Raise - [STRIFE] Verified unmodified.
 +        EV_DoCeiling(line,fastCrushAndRaise);
 +        line->special = 0;
 +        break;
 +
 +    case 8:
 +        // Build Stairs - [STRIFE] Verified unmodified.
 +        EV_BuildStairs(line,build8);
 +        line->special = 0;
 +        break;
 +
 +    case 10:
 +        // PlatDownWaitUp - [STRIFE] Verified unmodified.
 +        EV_DoPlat(line,downWaitUpStay,0);
 +        line->special = 0;
 +        break;
 +
 +    case 12:
 +        // Light Turn On - brightest near - [STRIFE] Verified unmodified.
 +        EV_LightTurnOn(line,0);
 +        line->special = 0;
 +        break;
 +
 +    case 13:
 +        // Light Turn On 255 - [STRIFE] Verified unmodified.
 +        EV_LightTurnOn(line,255);
 +        line->special = 0;
 +        break;
 +
 +    case 16:
 +        // Close Door 30 - [STRIFE] Verified unmodified.
 +        EV_DoDoor(line,close30ThenOpen);
 +        line->special = 0;
 +        break;
 +
 +    case 17:
 +        // Start Light Strobing - [STRIFE] Verified unmodified.
 +        EV_StartLightStrobing(line);
 +        line->special = 0;
 +        break;
 +
 +    case 19:
 +        // Lower Floor - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,lowerFloor);
 +        line->special = 0;
 +        break;
 +
 +    case 22:
 +        // villsa [STRIFE] Verified unmodified.
 +        // Raise floor to nearest height and change texture
 +        EV_DoPlat(line,raiseToNearestAndChange,0);
 +        line->special = 0;
 +        break;
 +
 +    case 25:
 +        // Ceiling Crush and Raise - [STRIFE] Verified unmodified.
 +        EV_DoCeiling(line,crushAndRaise);
 +        line->special = 0;
 +        break;
 +
 +    case 30:
 +        // Raise floor to shortest texture height - [STRIFE] Verified unmodified.
 +        //  on either side of lines.
 +        EV_DoFloor(line,raiseToTexture);
 +        line->special = 0;
 +        break;
 +
 +    case 35:
 +        // Lights Very Dark - [STRIFE] Verified unmodified.
 +        EV_LightTurnOn(line,35);
 +        line->special = 0;
 +        break;
 +
 +    case 36:
 +        // Lower Floor (TURBO) - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,turboLower);
 +        line->special = 0;
 +        break;
 +
 +    case 37:
 +        // LowerAndChange - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,lowerAndChange);
 +        line->special = 0;
 +        break;
 +
 +    case 193:
 +        // haleyjd 09/21/10: [STRIFE] W1 Floor Lower to Lowest if Quest
 +        sidedef = &sides[line->sidenum[0]];
 +        flag = (sidedef->rowoffset >> FRACBITS) - 1; // note is fixed_t
 +
 +        // must have the questflag indicated in the line's y offset
 +        if(!(thing->player->questflags & (1 << flag)))
 +            break;
 +        // fall-through:
 +    case 38:
 +        // Lower Floor To Lowest - [STRIFE] Verified unmodified.
 +        EV_DoFloor( line, lowerFloorToLowest );
 +        line->special = 0;
 +        break;
 +
 +    case 39:
 +        // TELEPORT! - [STRIFE] Verified unmodified (except for 0 flags param)
 +        EV_Teleport( line, side, thing, TF_NORMAL );
 +        line->special = 0;
 +        break;
 +
 +        /*case 40:
 +        // RaiseCeilingLowerFloor
 +        EV_DoCeiling( line, raiseToHighest );
 +        EV_DoFloor( line, lowerFloorToLowest );
 +        line->special = 0;
 +        break;*/
 +
 +    case 44:
 +        // Ceiling Crush - [STRIFE] Verified unmodified.
 +        EV_DoCeiling( line, lowerAndCrush );
 +        line->special = 0;
 +        break;
 +
 +    case 52:
 +        // EXIT! - haleyjd 09/21/10: [STRIFE] Exit to level tag/100
 +        G_ExitLevel (line->tag / 100);
 +        break;
 +
 +    case 53:
 +        // Perpetual Platform Raise - [STRIFE] Verified unmodified.
 +        EV_DoPlat(line,perpetualRaise,0);
 +        line->special = 0;
 +        break;
 +
 +    case 54:
 +        // Platform Stop - [STRIFE] Verified unmodified.
 +        EV_StopPlat(line);
 +        line->special = 0;
 +        break;
 +
 +    case 56:
 +        // Raise Floor Crush - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,raiseFloorCrush);
 +        line->special = 0;
 +        break;
 +
 +    case 57:
 +        // Ceiling Crush Stop - [STRIFE] Verified unmodified.
 +        EV_CeilingCrushStop(line);
 +        line->special = 0;
 +        break;
 +
 +    case 58:
 +        // [STRIFE] raiseFloor24 was modified into raiseFloor64
 +        // Raise Floor 64
 +        EV_DoFloor(line,raiseFloor64);
 +        line->special = 0;
 +        break;
 +
 +    case 59:
 +        // Raise Floor 24 And Change - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,raiseFloor24AndChange);
 +        line->special = 0;
 +        break;
 +
 +    case 104:
 +        // Turn lights off in sector(tag) - [STRIFE] Verified unmodified.
 +        EV_TurnTagLightsOff(line);
 +        line->special = 0;
 +        break;
 +
 +    case 108:
 +        // Blazing Door Raise (faster than TURBO!) - [STRIFE] Verified unmodified.
 +        EV_DoDoor (line,blazeRaise);
 +        line->special = 0;
 +        break;
 +
 +    case 109:
 +        // Blazing Door Open (faster than TURBO!) - [STRIFE] Verified unmodified.
 +        EV_DoDoor (line,blazeOpen);
 +        line->special = 0;
 +        break;
 +
 +    case 100:
 +        // Build Stairs Turbo 16 - [STRIFE] Verified unmodified.
 +        EV_BuildStairs(line,turbo16);
 +        line->special = 0;
 +        break;
 +
 +    case 197:
 +        // haleyjd 09/21/10: [STRIFE] Blazing Door Close if Has Sigil B
 +        if(thing->player->sigiltype <= 0)
 +            break;
 +        // fall-through:
 +    case 110:
 +        // Blazing Door Close (faster than TURBO!) - [STRIFE] Verified unmodified.
 +        EV_DoDoor (line,blazeClose);
 +        line->special = 0;
 +        break;
 +
 +    case 119:
 +        // Raise floor to nearest surr. floor - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,raiseFloorToNearest);
 +        line->special = 0;
 +        break;
 +
 +    case 121:
 +        // villsa [STRIFE] Verified unmodified.
 +        // Blazing PlatDownWaitUpStay
 +        EV_DoPlat(line,blazeDWUS,0);
 +        line->special = 0;
 +        break;
 +
 +    case 124:
 +        // haleyjd 09/21/10: [STRIFE] W1 Start Finale
 +        // Altered from G_SecretExitLevel.
 +        G_StartFinale();
 +        break;
 +
 +    case 125:
 +        // TELEPORT MonsterONLY - [STRIFE] Verified unmodified
 +        //    (except for 0 flags parameter)
 +        if (!thing->player)
 +        {
 +            EV_Teleport( line, side, thing, TF_NORMAL );
 +            line->special = 0;
 +        }
 +        break;
 +
 +    case 130:
 +        // Raise Floor Turbo - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,raiseFloorTurbo);
 +        line->special = 0;
 +        break;
 +
 +    case 141:
 +        // Silent Ceiling Crush & Raise - [STRIFE] Verified unmodified.
 +        EV_DoCeiling(line,silentCrushAndRaise);
 +        line->special = 0;
 +        break;
 +
 +    case 174:
 +        // villsa [STRIFE] Split Open
 +        EV_DoDoor(line, splitOpen);
 +        line->special = 0;
 +        break;
 +
 +    case 183:
 +        // villsa [STRIFE] Split Raise Nearest
 +        EV_DoDoor(line, splitRaiseNearest);
 +        line->special = 0;
 +        break;
 +
 +    case 178:
 +        // haleyjd 09/24/10: [STRIFE] W1 Build Stairs Down 16
 +        EV_BuildStairs(line, buildDown16);
 +        line->special = 0;
 +        break;
 +
 +    case 179:
 +        // haleyjd 09/25/10: [STRIFE] W1 Ceiling Lower to Floor
 +        EV_DoCeiling(line, lowerToFloor);
 +        line->special = 0;
 +        break;
 +
 +    case 182:
 +        // haleyjd 09/21/10: [STRIFE] Break Glass
 +        // 182 is a unique linetype in that it is both a G1 and a W1 linetype,
 +        // but only missiles may activate it as a W1 type.
 +        if(thing->flags & MF_MISSILE)
 +            P_ChangeSwitchTexture(line, 1); // why 1? it will be cleared anyway.
 +        break;
 +
 +    case 187:
 +        // haleyjd 09/21/10: [STRIFE] W1 Clear Force Fields if Quest
 +        sidedef = &sides[line->sidenum[0]];
 +        flag = (sidedef->rowoffset >> FRACBITS) - 1; // note is fixed_t
 +
 +        // must have the questflag indicated in the line's y offset
 +        if(!(thing->player->questflags & (1 << flag)))
 +            break;
 +
 +        // Do it!
 +        EV_ClearForceFields(line);
 +        line->special = 0;
 +        break;
 +
 +    case 188:
 +        // haleyjd 09/21/10: [STRIFE] W1 Open Door if Quest 16 (Gate Mechanism
 +        // Destroyed)
 +        if(!(thing->player->questflags & QF_QUEST16))
 +            break;
 +        EV_DoDoor(line, open);
 +        line->special = 0;
 +        break;
 +
 +    case 196:
 +        // haleyjd 09/26/10: [STRIFE] W1 Floor Lower to Lowest if Sigil Type > 0
 +        if(thing->player->sigiltype > 0)
 +        {
 +            EV_DoFloor(line, lowerFloorToLowest);
 +            line->special = 0;
 +        }
 +        break;
 +
 +    case 200:
 +        // haleyjd 09/21/10: [STRIFE] W1 Open Door if Sigil Owned
 +        if(!(thing->player->weaponowned[wp_sigil]))
 +            break;
 +        EV_DoDoor(line, open);
 +        line->special = 0;
 +        break;
 +
 +    case 201:
 +        // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective (First Side Only)
 +        if(side == 1)
 +            break;
 +        // fall-through:
 +    case 202:
 +        // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective (Tag = VOC/LOG #)
 +        // must be consoleplayer
 +        if(thing->player != &players[consoleplayer])
 +            break;
 +
 +        // must have comm unit
 +        if(!(thing->player->powers[pw_communicator]))
 +            break;
 +
 +        // load voice
 +        DEH_snprintf(crosslinestr, sizeof(crosslinestr), "voc%i", line->tag);
 +        I_StartVoice(crosslinestr);
 +
 +        // load objective
 +        DEH_snprintf(crosslinestr, sizeof(crosslinestr), "log%i", line->tag);
 +        GiveObjective(crosslinestr, 0);
 +
 +        // Put up a message
 +        thing->player->message = DEH_String("Incoming Message...");
 +        line->special = 0;
 +        break;
 +
 +    case 210:
 +        // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective if Flamethrower????
 +        // I don't think this is actually used anywhere o_O
 +        // must be player 1...
 +        if(thing->player != &players[0])
 +            break;
 +
 +        // must have comm unit
 +        if(!(thing->player->powers[pw_communicator]))
 +            break;
 +
 +        // must have... the flamethrower?!
 +        if(!(thing->player->weaponowned[wp_flame]))
 +            break;
 +
 +        // load voice
 +        DEH_snprintf(crosslinestr, sizeof(crosslinestr), "voc%i", line->tag);
 +        I_StartVoice(crosslinestr);
 +
 +        // load objective
 +        DEH_snprintf(crosslinestr, sizeof(crosslinestr), "log%i", line->tag);
 +        GiveObjective(crosslinestr, 0);
 +
 +        // Put up a message
 +        thing->player->message = DEH_String("Incoming Message from BlackBird...");
 +        line->special = 0;
 +        break;
 +
 +    case 212:
 +        // haleyjd 09/25/10: [STRIFE] W1 Floor Lower to Lowest if Have Flamethrower
 +        if(thing->player->weaponowned[wp_flame])
 +        {
 +            EV_DoFloor(line, lowerFloorToLowest);
 +            line->special = 0;
 +        }
 +        break;
 +
 +    case 215:
 +        // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective if Quest (Tag/100, Tag%100)
 +        // must be player 1...
 +        if(thing->player != &players[0])
 +            break;
 +
 +        // must have comm unit
 +        if(!(thing->player->powers[pw_communicator]))
 +            break;
 +
 +        if(line->tag != 0)
 +        {
 +            // test for questflag
 +            if(!(thing->player->questflags & (1 << (line->tag % 100 - 1))))
 +                break;
 +        }
 +
 +        // start voice
 +        DEH_snprintf(crosslinestr, sizeof(crosslinestr), "voc%i", line->tag/100);
 +        I_StartVoice(crosslinestr);
 +
 +        // give objective
 +        DEH_snprintf(crosslinestr, sizeof(crosslinestr), "log%i", line->tag/100);
 +        GiveObjective(crosslinestr, 0);
 +
 +        // Put up a message
 +        thing->player->message = DEH_String("Incoming Message from BlackBird...");
 +        line->special = 0;
 +        break;
 +
 +    case 204:
 +        // haleyjd 09/21/10: [STRIFE] W1 Change Music (unused!)
 +        if(thing->player != &players[0])
 +            break;
 +        S_ChangeMusic(line->tag, 1);
 +        line->special = 0;
 +        break;
 +
 +    case 228:
 +        // haleyjd 09/21/10: [STRIFE] W1 Entity Voice?
 +        if(!(thing->player->questflags & QF_QUEST24)) // Not killed Macil???
 +            break; // STRIFE-TODO: verify...
 +
 +        if(!(thing->player->questflags & QF_QUEST28)) // ????? STRIFE-TODO
 +            I_StartVoice(DEH_String("voc128"));
 +        else
 +            I_StartVoice(DEH_String("voc130"));
 +
 +        line->special = 0;
 +        break;
 +
 +        //
 +        // RETRIGGERS.  All from here till end.
 +        //
 +    case 72:
 +        // Ceiling Crush - [STRIFE] Verified unmodified.
 +        EV_DoCeiling( line, lowerAndCrush );
 +        break;
 +
 +    case 73:
 +        // Ceiling Crush and Raise - [STRIFE] Verified unmodified.
 +        EV_DoCeiling(line,crushAndRaise);
 +        break;
 +
 +    case 74:
 +        // Ceiling Crush Stop - [STRIFE] Verified unmodified.
 +        EV_CeilingCrushStop(line);
 +        break;
 +
 +    case 75:
 +        // Close Door - [STRIFE] Verified unmodified.
 +        EV_DoDoor(line,close);
 +        break;
 +
 +    case 76:
 +        // Close Door 30 - [STRIFE] Verified unmodified.
 +        EV_DoDoor(line,close30ThenOpen);
 +        break;
 +
 +    case 77:
 +        // Fast Ceiling Crush & Raise - [STRIFE] Verified unmodified.
 +        EV_DoCeiling(line,fastCrushAndRaise);
 +        break;
 +
 +    case 79:
 +        // Lights Very Dark - [STRIFE] Verified unmodified.
 +        EV_LightTurnOn(line,35);
 +        break;
 +
 +    case 80:
 +        // Light Turn On - brightest near - [STRIFE] Verified unmodified.
 +        EV_LightTurnOn(line,0);
 +        break;
 +
 +    case 81:
 +        // Light Turn On 255 - [STRIFE] Verified unmodified.
 +        EV_LightTurnOn(line,255);
 +        break;
 +
 +    case 82:
 +        // Lower Floor To Lowest - [STRIFE] Verified unmodified.
 +        EV_DoFloor( line, lowerFloorToLowest );
 +        break;
 +
 +    case 83:
 +        // Lower Floor - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,lowerFloor);
 +        break;
 +
 +    case 84:
 +        // LowerAndChange - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,lowerAndChange);
 +        break;
 +
 +    case 86:
 +        // Open Door - [STRIFE] Verified unmodified.
 +        EV_DoDoor(line,open);
 +        break;
 +
 +    case 87:
 +        // Perpetual Platform Raise - [STRIFE] Verified unmodified.
 +        EV_DoPlat(line,perpetualRaise,0);
 +        break;
 +
 +    case 88:
 +        // PlatDownWaitUp - [STRIFE] Verified unmodified.
 +        EV_DoPlat(line,downWaitUpStay,0);
 +        break;
 +
 +    case 89:
 +        // Platform Stop - [STRIFE] Verified unmodified.
 +        EV_StopPlat(line);
 +        break;
 +
 +    case 216:
 +        // haleyjd 09/21/10: [STRIFE] WR Raise Door if Quest
 +        sidedef = &sides[line->sidenum[0]];
 +        flag = (sidedef->rowoffset >> FRACBITS) - 1; // note is fixed_t.
 +
 +        if(!(thing->player->questflags & (1 << flag)))
 +            break;
 +        // fall-through:
 +    case 90:
 +        // Raise Door - [STRIFE] Verified unmodified.
 +        EV_DoDoor(line,normal);
 +        break;
 +
 +    case 91:
 +        // Raise Floor - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,raiseFloor);
 +        break;
 +
 +    case 92:
 +        // [STRIFE] raiseFloor24 changed to raiseFloor64
 +        // Raise Floor 64
 +        EV_DoFloor(line,raiseFloor64);
 +        break;
 +
 +    case 93:
 +        // Raise Floor 24 And Change - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,raiseFloor24AndChange);
 +        break;
 +
 +    case 94:
 +        // Raise Floor Crush - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,raiseFloorCrush);
 +        break;
 +
 +    case 95:
 +        // villsa [STRIFE] Verified unmodified.
 +        // Raise floor to nearest height
 +        // and change texture.
 +        EV_DoPlat(line,raiseToNearestAndChange,0);
 +        break;
 +
 +    case 96:
 +        // Raise floor to shortest texture height - [STRIFE] Verified unmodified.
 +        // on either side of lines.
 +        EV_DoFloor(line,raiseToTexture);
 +        break;
 +
 +    case 97:
 +        // TELEPORT! - [STRIFE] Verified unmodified (except for 0 flags param)
 +        EV_Teleport( line, side, thing, TF_NORMAL );
 +        break;
 +
 +    case 98:
 +        // Lower Floor (TURBO) - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,turboLower);
 +        break;
 +
 +    case 105:
 +        // Blazing Door Raise (faster than TURBO!) - [STRIFE] Verified unmodified.
 +        EV_DoDoor (line,blazeRaise);
 +        break;
 +
 +    case 106:
 +        // Blazing Door Open (faster than TURBO!) - [STRIFE] Verified unmodified.
 +        EV_DoDoor (line,blazeOpen);
 +        break;
 +
 +    case 107:
 +        // Blazing Door Close (faster than TURBO!) - [STRIFE] Verified unmodified.
 +        EV_DoDoor (line,blazeClose);
 +        break;
 +
 +    case 120:
 +        // villsa [STRIFE] Verified unmodified.
 +        // Blazing PlatDownWaitUpStay.
 +        EV_DoPlat(line,blazeDWUS,0);
 +        break;
 +
 +    case 126:
 +        // TELEPORT MonsterONLY. - [STRIFE] Verified unmodified (except for 0 flags param)
 +        if (!thing->player)
 +            EV_Teleport( line, side, thing, TF_NORMAL );
 +        break;
 +
 +    case 128:
 +        // Raise To Nearest Floor - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,raiseFloorToNearest);
 +        break;
 +
 +    case 129:
 +        // Raise Floor Turbo - [STRIFE] Verified unmodified.
 +        EV_DoFloor(line,raiseFloorTurbo);
 +        break;
 +
 +    case 186:
 +        // haleyjd [STRIFE] Exit Level to Spot, First Side Only
 +        if(side == 1)
 +            break;
 +        // fall-through:
 +    case 145:
 +        // haleyjd [STRIFE] Exit Level to Spot
 +        thing->momx = thing->momy = thing->momz = 0;
 +        {
 +            int map  = line->tag / 100;
 +            int spot = line->tag % 100;
 +
 +            if(thing->player->weaponowned[wp_sigil])
 +            {
 +                if(map == 3)
 +                    map = 30;
 +                else if(map == 7)
 +                    map = 10;
 +            }
 +
 +            DEH_snprintf(crosslinestr, sizeof(crosslinestr),
 +                         "Entering%s",
 +                         DEH_String(mapnames[map - 1]) + 8);
 +            thing->player->message = crosslinestr;
 +
 +            if(netgame && deathmatch)
 +            {
 +                if(levelTimer && levelTimeCount != 0)
 +                {
 +                    DEH_snprintf(crosslinestr, sizeof(crosslinestr),
 +                                 "%d min left",
 +                                 (levelTimeCount/TICRATE)/60);
 +                    break;
 +                }
 +
 +                // raise switch from floor
 +                EV_DoFloor(line, raiseFloor64);
 +            }
 +            else
 +            {
 +                // normal single-player exit
 +
 +                // BUG: Here is the opening for a flaming player to cross past
 +                // the exit line and hit a deathmatch switch ;) It's not so much
 +                // that this is incorrect, as that they forgot to add such a
 +                // check to the other kind of exit lines too ;)
 +                if(thing->player->health <= 0)
 +                    break;
 +
 +                G_RiftExitLevel(map, spot, thing->angle);
 +            }
 +        }
 +        break;
 +
 +    case 175:
 +        // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if < 16 Above Floor
 +        if(thing->z < thing->floorz + 16 * FRACUNIT)
 +            P_NoiseAlert(thing->player->mo, thing->player->mo);
 +        break;
 +
 +    case 198:
 +        // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if No Guard Uniform
 +        if(P_PlayerHasItem(thing->player, MT_QUEST_GUARD_UNIFORM))
 +            break;
 +        // fall-through:
 +    case 150:
 +        // haleyjd 09/21/10: [STRIFE] WR Raise Alarm
 +        P_NoiseAlert(thing->player->mo, thing->player->mo);
 +        break;
 +
 +    case 208:
 +        // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if Have Flamethrower
 +        // O_o - this is definitely unused. Was an entire flamethrower quest
 +        // cut out of the game before release?
 +        if(thing->player->weaponowned[wp_flame])
 +            P_NoiseAlert(thing->player->mo, thing->player->mo);
 +        break;
 +
 +    case 206:
 +        // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if Have Chalice
 +        // This *is* used, inside the Tavern in Tarnhill. Oddly there is also
 +        // one just randomly placed outside the entrance to the Power Station.
 +        if(P_PlayerHasItem(thing->player, MT_INV_CHALICE))
 +            P_NoiseAlert(thing->player->mo, thing->player->mo);
 +        break;
 +
 +    case 184:
 +        // villsa [STRIFE] plat up wait down stay
 +        if(EV_DoPlat(line, upWaitDownStay, 0))
 +            P_ChangeSwitchTexture(line, 1); // In P_CrossSpecialLine? Copypasta error?
 +        break;
 +
 +    case 185:
 +        // haleyjd 09/21/10: [STRIFE] Silent Teleport (used for Converter)
 +        EV_Teleport(line, side, thing, TF_FULLSILENCE);
 +        break;
 +
 +    case 195:
 +        // haleyjd 09/21/10: [STRIFE] Silent Teleport and Change Zombie
 +        EV_Teleport(line, side, thing, TF_FULLSILENCE);
 +        P_SetMobjState(thing, S_AGRD_00); // 419
 +        break;
 +
 +    case 203:
 +        // haleyjd 09/21/10: [STRIFE] WR Change Music
 +        if(thing->player != &players[0])
 +            break;
 +        S_ChangeMusic(line->tag, 1);
 +        break;
 +
 +    case 231:
 +        // haleyjd 09/21/10: [STRIFE] WR Teleport (Silent at Source)
 +        EV_Teleport(line, side, thing, TF_SRCSILENCE);
 +        break;
 +
 +        // haleyjd 09/21/10: Moved one-time-use lines up above with the others.
 +    }
 +}
 +
 +
 +
 +//
 +// P_ShootSpecialLine - IMPACT SPECIALS
 +// Called when a thing shoots a special line.
 +//
 +void
 +P_ShootSpecialLine
 +( mobj_t*       thing,
 +  line_t*       line )
 +{
 +    int         ok;
 +
 +    //	Impacts that other things can activate.
 +    if (!thing->player)
 +    {
 +        ok = 0;
 +        switch(line->special)
 +        {
 +        case 46:  // OPEN DOOR IMPACT
 +        case 182: // villsa [STRIFE] for windows
 +            ok = 1;
 +            break;
 +        }
 +        if (!ok)
 +            return;
 +    }
 +
 +    switch(line->special)
 +    {
 +    case 24:
 +        // RAISE FLOOR - [STRIFE] Verified unmodified
 +        EV_DoFloor(line,raiseFloor);
 +        P_ChangeSwitchTexture(line,0);
 +        break;
 +
 +    case 46:
 +        // OPEN DOOR - [STRIFE] Verified unmodified.
 +        EV_DoDoor(line,open);
 +        P_ChangeSwitchTexture(line,1);
 +        break;
 +
 +    case 47:
 +        // villsa [STRIFE] Verified unmodified.
 +        // RAISE FLOOR NEAR AND CHANGE
 +        EV_DoPlat(line,raiseToNearestAndChange,0);
 +        P_ChangeSwitchTexture(line,0);
 +        break;
 +
 +    case 180:
 +        // haleyjd 09/22/10: [STRIFE] G1 Raise Floor 512 & Change
 +        EV_DoFloor(line, raiseFloor512AndChange);
 +        P_ChangeSwitchTexture(line, 0);
 +        break;
 +
 +    case 182:
 +        // villsa [STRIFE] G1 Break Glass
 +        //   haleyjd: note that 182 is also a W1 type in P_CrossSpecialLine, but
 +        //   can only be activated in that manner by an MF_MISSILE object.
 +        P_ChangeSwitchTexture(line, 0);
 +        break;
 +    }
 +}
 +
 +
 +
 +//
 +// P_PlayerInSpecialSector
 +// Called every tic frame
 +//  that the player origin is in a special sector
 +//
 +// [STRIFE] Modified for new sector types and changes to old ones.
 +//
 +void P_PlayerInSpecialSector (player_t* player)
 +{
 +    sector_t*	sector;
 +
 +    sector = player->mo->subsector->sector;
 +
 +    // Falling, not all the way down yet?
 +    if (player->mo->z != sector->floorheight)
 +        return;
 +
 +    // Has hitten ground.
 +    switch (sector->special)
 +    {
 +    case 5:
 +        // HELLSLIME DAMAGE
 +        // [STRIFE] +2 to nukagecount
 +        if(!player->powers[pw_ironfeet])
 +            player->nukagecount += 2;
 +        break;
 +
 +    case 16:
 +        // [STRIFE] +4 to nukagecount
 +        if(!player->powers[pw_ironfeet])
 +            player->nukagecount += 4;
 +        break;
 +
 +    case 4:
 +    case 7:
 +        // [STRIFE] Immediate 5 damage every 31 tics
 +        if(!player->powers[pw_ironfeet])
 +            if(!(leveltime & 0x1f))
 +                P_DamageMobj(player->mo, NULL, NULL, 5);
 +        break;
 +
 +    case 9:
 +        // SECRET SECTOR
 +        //player->secretcount++; [STRIFE] Don't have a secret count.
 +        sector->special = 0;
 +        if(player - players == consoleplayer)
 +            S_StartSound(NULL, sfx_yeah);
 +        break;
 +
 +    case 11:
 +        // EXIT SUPER DAMAGE! (for E1M8 finale)
 +        player->cheats &= ~CF_GODMODE;
 +
 +        if (!(leveltime&0x1f))
 +            P_DamageMobj (player->mo, NULL, NULL, 20);
 +
 +        if (player->health <= 10)
 +            G_ExitLevel(0);
 +        break;
 +
 +    case 15:
 +        // haleyjd 08/30/10: [STRIFE] "Instant" Death sector
 +        P_DamageMobj(player->mo, NULL, NULL, 999);
 +        break;
 +
 +
 +    case 18:
 +        // haleyjd 08/30/10: [STRIFE] Water current
 +        {
 +            int tagval = sector->tag - 100;
 +            fixed_t force;
 +            angle_t angle;
 +
 +            if(player->cheats & CF_NOCLIP)
 +                return;
 +
 +            force = (tagval % 10) << 12;
 +            angle = (tagval / 10) << 29;
 +
 +            P_Thrust(player, angle, force);
 +        }
 +        break;
 +
 +    default:
 +        I_Error ("P_PlayerInSpecialSector: "
 +                 "unknown special %i",
 +                 sector->special);
 +        break;
 +    };
 +}
 +
 +
 +
 +
 +//
 +// P_UpdateSpecials
 +// Animate planes, scroll walls, etc.
 +//
 +// [STRIFE] Modifications to support multiple scrolling line types.
 +//
 +boolean         levelTimer;
 +int             levelTimeCount;
 +
 +void P_UpdateSpecials (void)
 +{
 +    anim_t*     anim;
 +    int         pic;
 +    int         i;
 +    line_t*     line;
 +
 +
 +    //  LEVEL TIMER
 +    if (levelTimer == true)
 +    {
 +        if(levelTimeCount) // [STRIFE] Does not allow to go negative
 +            levelTimeCount--;
 +
 +        /*
 +        // [STRIFE] Not done here. Exit lines check this manually instead.
 +        if (!levelTimeCount)
 +            G_ExitLevel(0);
 +        */
 +    }
 +
 +    //  ANIMATE FLATS AND TEXTURES GLOBALLY
 +    for (anim = anims ; anim < lastanim ; anim++)
 +    {
 +        for (i=anim->basepic ; i<anim->basepic+anim->numpics ; i++)
 +        {
 +            pic = anim->basepic + ( (leveltime/anim->speed + i)%anim->numpics );
 +            if (anim->istexture)
 +                texturetranslation[i] = pic;
 +            else
 +                flattranslation[i] = pic;
 +        }
 +    }
 +
 +
 +    //  ANIMATE LINE SPECIALS
 +    for (i = 0; i < numlinespecials; i++)
 +    {
 +        line = linespeciallist[i];
 +        switch(line->special)
 +        {
 +        case 48:
 +            // EFFECT FIRSTCOL SCROLL +
 +            sides[line->sidenum[0]].textureoffset += FRACUNIT;
 +            break;
 +
 +        case 142:
 +            // haleyjd 09/25/10 [STRIFE] Scroll Up Slow
 +            sides[line->sidenum[0]].rowoffset += FRACUNIT;
 +            break;
 +
 +        case 143:
 +            // haleyjd 09/25/10 [STRIFE] Scroll Down Fast (3 Units/Tic)
 +            sides[line->sidenum[0]].rowoffset -= 3*FRACUNIT;
 +            break;
 +
 +        case 149:
 +            // haleyjd 09/25/10 [STRIFE] Scroll Down Slow
 +            sides[line->sidenum[0]].rowoffset -= FRACUNIT;
 +            break;
 +        }
 +    }
 +
 +
 +    //  DO BUTTONS
 +    for (i = 0; i < MAXBUTTONS; i++)
 +        if (buttonlist[i].btimer)
 +        {
 +            buttonlist[i].btimer--;
 +            if (!buttonlist[i].btimer)
 +            {
 +                switch(buttonlist[i].where)
 +                {
 +                case top:
 +                    sides[buttonlist[i].line->sidenum[0]].toptexture =
 +                        buttonlist[i].btexture;
 +                    break;
 +
 +                case middle:
 +                    sides[buttonlist[i].line->sidenum[0]].midtexture =
 +                        buttonlist[i].btexture;
 +                    break;
 +
 +                case bottom:
 +                    sides[buttonlist[i].line->sidenum[0]].bottomtexture =
 +                        buttonlist[i].btexture;
 +                    break;
 +                }
 +                S_StartSound(&buttonlist[i].soundorg,sfx_swtchn);
 +                memset(&buttonlist[i],0,sizeof(button_t));
 +            }
 +        }
 +}
 +
 +
 +//
 +// Donut overrun emulation
 +//
 +// Derived from the code from PrBoom+.  Thanks go to Andrey Budko (entryway)
 +// as usual :-)
 +//
 +
 +#define DONUT_FLOORHEIGHT_DEFAULT 0x00000000
 +#define DONUT_FLOORPIC_DEFAULT 0x16
 +
 +static void DonutOverrun(fixed_t *s3_floorheight, short *s3_floorpic,
 +                         line_t *line, sector_t *pillar_sector)
 +{
 +    static int first = 1;
 +    static int tmp_s3_floorheight;
 +    static int tmp_s3_floorpic;
 +
 +    extern int numflats;
 +
 +    if (first)
 +    {
 +        int p;
 +
 +        // This is the first time we have had an overrun.
 +        first = 0;
 +
 +        // Default values
 +        tmp_s3_floorheight = DONUT_FLOORHEIGHT_DEFAULT;
 +        tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT;
 +
 +        //!
 +        // @category compat
 +        // @arg <x> <y>
 +        //
 +        // Use the specified magic values when emulating behavior caused
 +        // by memory overruns from improperly constructed donuts.
 +        // In Vanilla Doom this can differ depending on the operating
 +        // system.  The default (if this option is not specified) is to
 +        // emulate the behavior when running under Windows 98.
 +
-         p = M_CheckParm("-donut");
++        p = M_CheckParmWithArgs("-donut", 2);
 +
-         if (p > 0 && p < myargc - 2)
++        if (p > 0)
 +        {
 +            // Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008
 +            //
 +            // C:\>debug
 +            // -d 0:0
 +            //
 +            // DOS 6.22:
 +            // 0000:0000    (57 92 19 00) F4 06 70 00-(16 00)
 +            // DOS 7.1:
 +            // 0000:0000    (9E 0F C9 00) 65 04 70 00-(16 00)
 +            // Win98:
 +            // 0000:0000    (00 00 00 00) 65 04 70 00-(16 00)
 +            // DOSBox under XP:
 +            // 0000:0000    (00 00 00 F1) ?? ?? ?? 00-(07 00)
 +
 +            M_StrToInt(myargv[p + 1], &tmp_s3_floorheight);
 +            M_StrToInt(myargv[p + 2], &tmp_s3_floorpic);
 +
 +            if (tmp_s3_floorpic >= numflats)
 +            {
 +                fprintf(stderr,
 +                        "DonutOverrun: The second parameter for \"-donut\" "
 +                        "switch should be greater than 0 and less than number "
 +                        "of flats (%d). Using default value (%d) instead. \n",
 +                        numflats, DONUT_FLOORPIC_DEFAULT);
 +                tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT;
 +            }
 +        }
 +    }
 +
 +    /*
 +    fprintf(stderr,
 +            "Linedef: %d; Sector: %d; "
 +            "New floor height: %d; New floor pic: %d\n",
 +            line->iLineID, pillar_sector->iSectorID,
 +            tmp_s3_floorheight >> 16, tmp_s3_floorpic);
 +     */
 +
 +    *s3_floorheight = (fixed_t) tmp_s3_floorheight;
 +    *s3_floorpic = (short) tmp_s3_floorpic;
 +}
 +
 +
 +//
 +// Special Stuff that can not be categorized
 +//
 +int EV_DoDonut(line_t*	line)
 +{
 +    sector_t*		s1;
 +    sector_t*		s2;
 +    sector_t*		s3;
 +    int			secnum;
 +    int			rtn;
 +    int			i;
 +    floormove_t*	floor;
 +    fixed_t s3_floorheight;
 +    short s3_floorpic;
 +
 +    secnum = -1;
 +    rtn = 0;
 +    while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)
 +    {
 +	s1 = &sectors[secnum];
 +
 +	// ALREADY MOVING?  IF SO, KEEP GOING...
 +	if (s1->specialdata)
 +	    continue;
 +
 +	rtn = 1;
 +	s2 = getNextSector(s1->lines[0],s1);
 +
 +        // Vanilla Doom does not check if the linedef is one sided.  The
 +        // game does not crash, but reads invalid memory and causes the
 +        // sector floor to move "down" to some unknown height.
 +        // DOSbox prints a warning about an invalid memory access.
 +        //
 +        // I'm not sure exactly what invalid memory is being read.  This
 +        // isn't something that should be done, anyway.
 +        // Just print a warning and return.
 +
 +        if (s2 == NULL)
 +        {
 +            fprintf(stderr,
 +                    "EV_DoDonut: linedef had no second sidedef! "
 +                    "Unexpected behavior may occur in Vanilla Doom. \n");
 +	    break;
 +        }
 +
 +	for (i = 0; i < s2->linecount; i++)
 +	{
 +	    s3 = s2->lines[i]->backsector;
 +
 +	    if (s3 == s1)
 +		continue;
 +
 +            if (s3 == NULL)
 +            {
 +                // e6y
 +                // s3 is NULL, so
 +                // s3->floorheight is an int at 0000:0000
 +                // s3->floorpic is a short at 0000:0008
 +                // Trying to emulate
 +
 +                fprintf(stderr,
 +                        "EV_DoDonut: WARNING: emulating buffer overrun due to "
 +                        "NULL back sector. "
 +                        "Unexpected behavior may occur in Vanilla Doom.\n");
 +
 +                DonutOverrun(&s3_floorheight, &s3_floorpic, line, s1);
 +            }
 +            else
 +            {
 +                s3_floorheight = s3->floorheight;
 +                s3_floorpic = s3->floorpic;
 +            }
 +
 +	    //	Spawn rising slime
 +	    floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);
 +	    P_AddThinker (&floor->thinker);
 +	    s2->specialdata = floor;
 +	    floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;
 +	    floor->type = donutRaise;
 +	    floor->crush = false;
 +	    floor->direction = 1;
 +	    floor->sector = s2;
 +	    floor->speed = FLOORSPEED / 2;
 +	    floor->texture = s3_floorpic;
 +	    floor->newspecial = 0;
 +	    floor->floordestheight = s3_floorheight;
 +
 +	    //	Spawn lowering donut-hole
 +	    floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);
 +	    P_AddThinker (&floor->thinker);
 +	    s1->specialdata = floor;
 +	    floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;
 +	    floor->type = lowerFloor;
 +	    floor->crush = false;
 +	    floor->direction = -1;
 +	    floor->sector = s1;
 +	    floor->speed = FLOORSPEED / 2;
 +	    floor->floordestheight = s3_floorheight;
 +	    break;
 +	}
 +    }
 +    return rtn;
 +}
 +
 +
 +
 +//
 +// SPECIAL SPAWNING
 +//
 +
 +//
 +// P_SpawnSpecials
 +// After the map has been loaded, scan for specials
 +//  that spawn thinkers
 +//
 +short		numlinespecials;
 +line_t*		linespeciallist[MAXLINEANIMS];
 +
 +
 +// Parses command line parameters.
 +//
 +// haleyjd 09/25/10: [STRIFE] Modifications for more scrolling line types and
 +// for initialization of sliding door resources.
 +//
 +void P_SpawnSpecials (void)
 +{
 +    sector_t*   sector;
 +    int         i;
 +    int         episode;
 +
 +    episode = 1;
 +    if (W_CheckNumForName(DEH_String("texture2")) >= 0)
 +        episode = 2;
 +
-
 +    // See if -TIMER was specified.
 +
-     if (timelimit > 0)
++    if (timelimit > 0 && deathmatch)
 +    {
 +        levelTimer = true;
 +        levelTimeCount = timelimit * 60 * TICRATE;
 +    }
 +    else
 +    {
 +        levelTimer = false;
 +    }
 +
 +    //	Init special SECTORs - [STRIFE] Verified unmodified.
 +    sector = sectors;
 +    for (i=0 ; i<numsectors ; i++, sector++)
 +    {
 +        if (!sector->special)
 +            continue;
 +
 +        switch (sector->special)
 +        {
 +        case 1:
 +            // FLICKERING LIGHTS
 +            P_SpawnLightFlash (sector);
 +            break;
 +
 +        case 2:
 +            // STROBE FAST
 +            P_SpawnStrobeFlash(sector,FASTDARK,0);
 +            break;
 +
 +        case 3:
 +            // STROBE SLOW
 +            P_SpawnStrobeFlash(sector,SLOWDARK,0);
 +            break;
 +
 +        case 4:
 +            // STROBE FAST/DEATH SLIME
 +            P_SpawnStrobeFlash(sector,FASTDARK,0);
 +            sector->special = 4;
 +            break;
 +
 +        case 8:
 +            // GLOWING LIGHT
 +            P_SpawnGlowingLight(sector);
 +            break;
 +        case 9:
 +            // SECRET SECTOR
 +            totalsecret++;
 +            break;
 +
 +        case 10:
 +            // DOOR CLOSE IN 30 SECONDS
 +            P_SpawnDoorCloseIn30 (sector);
 +            break;
 +
 +        case 12:
 +            // SYNC STROBE SLOW
 +            P_SpawnStrobeFlash (sector, SLOWDARK, 1);
 +            break;
 +
 +        case 13:
 +            // SYNC STROBE FAST
 +            P_SpawnStrobeFlash (sector, FASTDARK, 1);
 +            break;
 +
 +        case 14:
 +            // DOOR RAISE IN 5 MINUTES
 +            P_SpawnDoorRaiseIn5Mins (sector, i);
 +            break;
 +
 +        case 17:
 +            P_SpawnFireFlicker(sector);
 +            break;
 +        }
 +    }
 +
 +
 +    //	Init line EFFECTs
 +    numlinespecials = 0;
 +    for (i = 0;i < numlines; i++)
 +    {
 +        switch(lines[i].special)
 +        {
 +        case 48:  // EFFECT FIRSTCOL SCROLL+
 +        case 142: // [STRIFE] TODO: verify scroll types
 +        case 143:
 +        case 149:
 +            linespeciallist[numlinespecials] = &lines[i];
 +            numlinespecials++;
 +            break;
 +        }
 +    }
 +
 +    //	Init other misc stuff
 +    for (i = 0;i < MAXCEILINGS;i++)
 +        activeceilings[i] = NULL;
 +
 +    for (i = 0;i < MAXPLATS;i++)
 +        activeplats[i] = NULL;
 +
 +    for (i = 0;i < MAXBUTTONS;i++)
 +        memset(&buttonlist[i],0,sizeof(button_t));
 +
 +    // villsa [STRIFE]
 +    P_InitSlidingDoorFrames();
 +}
diff --cc src/strife/r_data.c
index 043df348,00000000..46138c85
mode 100644,000000..100644
--- a/src/strife/r_data.c
+++ b/src/strife/r_data.c
@@@ -1,1020 -1,0 +1,1034 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	Preparation of data for rendering,
 +//	generation of lookups, caching, retrieval by name.
 +//
 +//-----------------------------------------------------------------------------
 +
 +#include <stdio.h>
 +
 +#include "d_main.h"
 +#include "deh_main.h"
 +#include "i_swap.h"
 +#include "i_system.h"
 +#include "z_zone.h"
 +#include "w_wad.h"
 +#include "doomdef.h"
 +#include "r_local.h"
 +#include "p_local.h"
 +#include "doomstat.h"
 +#include "r_sky.h"
 +#include "r_data.h"
 +#include "sounds.h" // villsa [STRIFE]
 +
 +//
 +// Graphics.
 +// DOOM graphics for walls and sprites
 +// is stored in vertical runs of opaque pixels (posts).
 +// A column is composed of zero or more posts,
 +// a patch or sprite is composed of zero or more columns.
 +//
 +
 +
 +
 +//
 +// Texture definition.
 +// Each texture is composed of one or more patches,
 +// with patches being lumps stored in the WAD.
 +// The lumps are referenced by number, and patched
 +// into the rectangular texture space using origin
 +// and possibly other attributes.
 +//
 +typedef struct
 +{
 +    short	originx;
 +    short	originy;
 +    short	patch;
 +    //short	stepdir;    // villsa [STRIFE] removed
 +    //short	colormap;   // villsa [STRIFE] removed
 +} PACKEDATTR mappatch_t;
 +
 +
 +//
 +// Texture definition.
 +// A DOOM wall texture is a list of patches
 +// which are to be combined in a predefined order.
 +//
 +typedef struct
 +{
 +    char		name[8];
 +    int			masked;
 +    short		width;
 +    short		height;
 +    //int               obsolete;   // villsa [STRIFE] removed
 +    short		patchcount;
 +    mappatch_t	patches[1];
 +} PACKEDATTR maptexture_t;
 +
 +
 +// A single patch from a texture definition,
 +//  basically a rectangular area within
 +//  the texture rectangle.
 +typedef struct
 +{
 +    // Block origin (allways UL),
 +    // which has allready accounted
 +    // for the internal origin of the patch.
 +    short	originx;
 +    short	originy;
 +    int		patch;
 +} texpatch_t;
 +
 +
 +// A maptexturedef_t describes a rectangular texture,
 +//  which is composed of one or more mappatch_t structures
 +//  that arrange graphic patches.
 +
 +typedef struct texture_s texture_t;
 +
 +struct texture_s
 +{
 +    // Keep name for switch changing, etc.
 +    char	name[8];
 +    short	width;
 +    short	height;
 +
 +    // Index in textures list
 +
 +    int         index;
 +
 +    // Next in hash table chain
 +
 +    texture_t  *next;
 +
 +    // All the patches[patchcount]
 +    //  are drawn back to front into the cached texture.
 +    short	patchcount;
 +    texpatch_t	patches[1];
 +};
 +
 +
 +
 +int		firstflat;
 +int		lastflat;
 +int		numflats;
 +
 +int		firstpatch;
 +int		lastpatch;
 +int		numpatches;
 +
 +int		firstspritelump;
 +int		lastspritelump;
 +int		numspritelumps;
 +
 +int		numtextures;
 +texture_t**	textures;
 +texture_t**     textures_hashtable;
 +
 +
 +int*			texturewidthmask;
 +// needed for texture pegging
 +fixed_t*		textureheight;
 +int*			texturecompositesize;
 +short**			texturecolumnlump;
 +unsigned short**	texturecolumnofs;
 +byte**			texturecomposite;
 +
 +// for global animation
 +int*		flattranslation;
 +int*		texturetranslation;
 +
 +// needed for pre rendering
 +fixed_t*	spritewidth;
 +fixed_t*	spriteoffset;
 +fixed_t*	spritetopoffset;
 +
 +lighttable_t	*colormaps;
 +
 +
 +//
 +// MAPTEXTURE_T CACHING
 +// When a texture is first needed,
 +//  it counts the number of composite columns
 +//  required in the texture and allocates space
 +//  for a column directory and any new columns.
 +// The directory will simply point inside other patches
 +//  if there is only one patch in a given column,
 +//  but any columns with multiple patches
 +//  will have new column_ts generated.
 +//
 +
 +
 +
 +//
 +// R_DrawColumnInCache
 +// Clip and draw a column
 +//  from a patch into a cached post.
 +//
 +void
 +R_DrawColumnInCache
 +( column_t*	patch,
 +  byte*		cache,
 +  int		originy,
 +  int		cacheheight )
 +{
 +    int		count;
 +    int		position;
 +    byte*	source;
 +    byte*	dest;
 +
 +    dest = (byte *)cache + 3;
 +
 +    while (patch->topdelta != 0xff)
 +    {
 +	source = (byte *)patch + 3;
 +	count = patch->length;
 +	position = originy + patch->topdelta;
 +
 +	if (position < 0)
 +	{
 +	    count += position;
 +	    position = 0;
 +	}
 +
 +	if (position + count > cacheheight)
 +	    count = cacheheight - position;
 +
 +	if (count > 0)
 +	    memcpy (cache + position, source, count);
 +
 +	patch = (column_t *)(  (byte *)patch + patch->length + 4);
 +    }
 +}
 +
 +
 +
 +//
 +// R_GenerateComposite
 +// Using the texture definition,
 +//  the composite texture is created from the patches,
 +//  and each column is cached.
 +//
 +void R_GenerateComposite (int texnum)
 +{
 +    byte*		block;
 +    texture_t*		texture;
 +    texpatch_t*		patch;
 +    patch_t*		realpatch;
 +    int			x;
 +    int			x1;
 +    int			x2;
 +    int			i;
 +    column_t*		patchcol;
 +    short*		collump;
 +    unsigned short*	colofs;
 +
 +    texture = textures[texnum];
 +
 +    block = Z_Malloc (texturecompositesize[texnum],
 +		      PU_STATIC,
 +		      &texturecomposite[texnum]);
 +
 +    collump = texturecolumnlump[texnum];
 +    colofs = texturecolumnofs[texnum];
 +
 +    // Composite the columns together.
 +    patch = texture->patches;
 +
 +    for (i=0 , patch = texture->patches;
 +	 i<texture->patchcount;
 +	 i++, patch++)
 +    {
 +	realpatch = W_CacheLumpNum (patch->patch, PU_CACHE);
 +	x1 = patch->originx;
 +	x2 = x1 + SHORT(realpatch->width);
 +
 +	if (x1<0)
 +	    x = 0;
 +	else
 +	    x = x1;
 +
 +	if (x2 > texture->width)
 +	    x2 = texture->width;
 +
 +	for ( ; x<x2 ; x++)
 +	{
 +	    // Column does not have multiple patches?
 +	    if (collump[x] >= 0)
 +		continue;
 +
 +	    patchcol = (column_t *)((byte *)realpatch
 +				    + LONG(realpatch->columnofs[x-x1]));
 +	    R_DrawColumnInCache (patchcol,
 +				 block + colofs[x],
 +				 patch->originy,
 +				 texture->height);
 +	}
 +
 +    }
 +
 +    // Now that the texture has been built in column cache,
 +    //  it is purgable from zone memory.
 +    Z_ChangeTag (block, PU_CACHE);
 +}
 +
 +
 +
 +//
 +// R_GenerateLookup
 +//
 +void R_GenerateLookup (int texnum)
 +{
 +    texture_t*		texture;
 +    byte*		patchcount;	// patchcount[texture->width]
 +    texpatch_t*		patch;
 +    patch_t*		realpatch;
 +    int			x;
 +    int			x1;
 +    int			x2;
 +    int			i;
 +    short*		collump;
 +    unsigned short*	colofs;
 +
 +    texture = textures[texnum];
 +
 +    // Composited texture not created yet.
 +    texturecomposite[texnum] = 0;
 +
 +    texturecompositesize[texnum] = 0;
 +    collump = texturecolumnlump[texnum];
 +    colofs = texturecolumnofs[texnum];
 +
 +    // Now count the number of columns
 +    //  that are covered by more than one patch.
 +    // Fill in the lump / offset, so columns
 +    //  with only a single patch are all done.
 +    patchcount = (byte *) Z_Malloc(texture->width, PU_STATIC, &patchcount);
 +    memset (patchcount, 0, texture->width);
 +    patch = texture->patches;
 +
 +    for (i=0 , patch = texture->patches;
 +	 i<texture->patchcount;
 +	 i++, patch++)
 +    {
 +	realpatch = W_CacheLumpNum (patch->patch, PU_CACHE);
 +	x1 = patch->originx;
 +	x2 = x1 + SHORT(realpatch->width);
 +
 +	if (x1 < 0)
 +	    x = 0;
 +	else
 +	    x = x1;
 +
 +	if (x2 > texture->width)
 +	    x2 = texture->width;
 +	for ( ; x<x2 ; x++)
 +	{
 +	    patchcount[x]++;
 +	    collump[x] = patch->patch;
 +	    colofs[x] = LONG(realpatch->columnofs[x-x1])+3;
 +	}
 +    }
 +
 +    for (x=0 ; x<texture->width ; x++)
 +    {
 +	if (!patchcount[x])
 +	{
 +	    printf ("R_GenerateLookup: column without a patch (%s)\n",
 +		    texture->name);
 +	    return;
 +	}
 +	// I_Error ("R_GenerateLookup: column without a patch");
 +
 +	if (patchcount[x] > 1)
 +	{
 +	    // Use the cached block.
 +	    collump[x] = -1;
 +	    colofs[x] = texturecompositesize[texnum];
 +
 +	    if (texturecompositesize[texnum] > 0x10000-texture->height)
 +	    {
 +		I_Error ("R_GenerateLookup: texture %i is >64k",
 +			 texnum);
 +	    }
 +
 +	    texturecompositesize[texnum] += texture->height;
 +	}
 +    }
 +
 +    Z_Free(patchcount);
 +}
 +
 +
 +
 +
 +//
 +// R_GetColumn
 +//
 +byte*
 +R_GetColumn
 +( int		tex,
 +  int		col )
 +{
 +    int		lump;
 +    int		ofs;
 +
 +    col &= texturewidthmask[tex];
 +    lump = texturecolumnlump[tex][col];
 +    ofs = texturecolumnofs[tex][col];
 +
 +    if (lump > 0)
 +	return (byte *)W_CacheLumpNum(lump,PU_CACHE)+ofs;
 +
 +    if (!texturecomposite[tex])
 +	R_GenerateComposite (tex);
 +
 +    return texturecomposite[tex] + ofs;
 +}
 +
 +
 +static void GenerateTextureHashTable(void)
 +{
++    texture_t **rover;
 +    int i;
 +    int key;
 +
 +    textures_hashtable
 +            = Z_Malloc(sizeof(texture_t *) * numtextures, PU_STATIC, 0);
 +
 +    memset(textures_hashtable, 0, sizeof(texture_t *) * numtextures);
 +
 +    // Add all textures to hash table
 +
 +    for (i=0; i<numtextures; ++i)
 +    {
 +        // Store index
 +
 +        textures[i]->index = i;
 +
-         // Hook into hash table
++        // Vanilla Doom does a linear search of the texures array
++        // and stops at the first entry it finds.  If there are two
++        // entries with the same name, the first one in the array
++        // wins. The new entry must therefore be added at the end
++        // of the hash chain, so that earlier entries win.
 +
 +        key = W_LumpNameHash(textures[i]->name) % numtextures;
 +
-         textures[i]->next = textures_hashtable[key];
-         textures_hashtable[key] = textures[i];
++        rover = &textures_hashtable[key];
++
++        while (*rover != NULL)
++        {
++            rover = &(*rover)->next;
++        }
++
++        // Hook into hash table
++
++        textures[i]->next = NULL;
++        *rover = textures[i];
 +    }
 +}
 +
 +
 +//
 +// R_InitTextures
 +// Initializes the texture list
 +//  with the textures from the world map.
 +//
 +void R_InitTextures (void)
 +{
 +    maptexture_t*	mtexture;
 +    texture_t*		texture;
 +    mappatch_t*		mpatch;
 +    texpatch_t*		patch;
 +
 +    int			i;
 +    int			j;
 +
 +    int*		maptex;
 +    int*		maptex2;
 +    int*		maptex1;
 +
 +    char		name[9];
 +    char*		names;
 +    char*		name_p;
 +
 +    int*		patchlookup;
 +
 +    int			totalwidth;
 +    int			nummappatches;
 +    int			offset;
 +    int			maxoff;
 +    int			maxoff2;
 +    int			numtextures1;
 +    int			numtextures2;
 +
 +    int*		directory;
 +
 +    int			temp1;
 +    int			temp2;
 +    int			temp3;
 +
 +
 +    // Load the patch names from pnames.lmp.
 +    name[8] = 0;
 +    names = W_CacheLumpName (DEH_String("PNAMES"), PU_STATIC);
 +    nummappatches = LONG ( *((int *)names) );
 +    name_p = names+4;
 +    patchlookup = Z_Malloc(nummappatches*sizeof(*patchlookup), PU_STATIC, NULL);
 +
 +    for (i=0 ; i<nummappatches ; i++)
 +    {
 +        strncpy (name,name_p+i*8, 8);
 +        patchlookup[i] = W_CheckNumForName (name);
 +    }
 +    W_ReleaseLumpName(DEH_String("PNAMES"));
 +
 +    // Load the map texture definitions from textures.lmp.
 +    // The data is contained in one or two lumps,
 +    //  TEXTURE1 for shareware, plus TEXTURE2 for commercial.
 +    maptex = maptex1 = W_CacheLumpName (DEH_String("TEXTURE1"), PU_STATIC);
 +    numtextures1 = LONG(*maptex);
 +    maxoff = W_LumpLength (W_GetNumForName (DEH_String("TEXTURE1")));
 +    directory = maptex+1;
 +
 +    if (W_CheckNumForName (DEH_String("TEXTURE2")) != -1)
 +    {
 +        maptex2 = W_CacheLumpName (DEH_String("TEXTURE2"), PU_STATIC);
 +        numtextures2 = LONG(*maptex2);
 +        maxoff2 = W_LumpLength (W_GetNumForName (DEH_String("TEXTURE2")));
 +    }
 +    else
 +    {
 +        maptex2 = NULL;
 +        numtextures2 = 0;
 +        maxoff2 = 0;
 +    }
 +    numtextures = numtextures1 + numtextures2;
 +
 +    textures = Z_Malloc (numtextures * sizeof(*textures), PU_STATIC, 0);
 +    texturecolumnlump = Z_Malloc (numtextures * sizeof(*texturecolumnlump), PU_STATIC, 0);
 +    texturecolumnofs = Z_Malloc (numtextures * sizeof(*texturecolumnofs), PU_STATIC, 0);
 +    texturecomposite = Z_Malloc (numtextures * sizeof(*texturecomposite), PU_STATIC, 0);
 +    texturecompositesize = Z_Malloc (numtextures * sizeof(*texturecompositesize), PU_STATIC, 0);
 +    texturewidthmask = Z_Malloc (numtextures * sizeof(*texturewidthmask), PU_STATIC, 0);
 +    textureheight = Z_Malloc (numtextures * sizeof(*textureheight), PU_STATIC, 0);
 +
 +    totalwidth = 0;
 +
 +    //	Really complex printing shit...
 +    temp1 = W_GetNumForName (DEH_String("S_START"));  // P_???????
 +    temp2 = W_GetNumForName (DEH_String("S_END")) - 1;
 +    temp3 = ((temp2-temp1+63)/64) + ((numtextures+63)/64);
 +
 +    // If stdout is a real console, use the classic vanilla "filling
 +    // up the box" effect, which uses backspace to "step back" inside
 +    // the box.  If stdout is a file, don't draw the box.
 +
 +    // haleyjd 20110206 [STRIFE]: box is in devparm only
 +    if (devparm && I_ConsoleStdout())
 +    {
 +        printf("[");
 +        for (i = 0; i < temp3 + 9; i++)
 +            printf(" ");
 +        printf("]");
 +        for (i = 0; i < temp3 + 10; i++)
 +            printf("\b");
 +    }
 +
 +    for (i=0 ; i<numtextures ; i++, directory++)
 +    {
 +        if (!(i&63))
 +        {
 +            // [STRIFE]: tick intro if not in devparm
 +            if(devparm)
 +                printf (".");
 +            else
 +                D_IntroTick();
 +        }
 +
 +        if (i == numtextures1)
 +        {
 +            // Start looking in second texture file.
 +            maptex = maptex2;
 +            maxoff = maxoff2;
 +            directory = maptex+1;
 +        }
 +
 +        offset = LONG(*directory);
 +
 +        if (offset > maxoff)
 +            I_Error ("R_InitTextures: bad texture directory");
 +
 +        mtexture = (maptexture_t *) ( (byte *)maptex + offset);
 +
 +        texture = textures[i] =
 +            Z_Malloc (sizeof(texture_t)
 +                      + sizeof(texpatch_t)*(SHORT(mtexture->patchcount)-1),
 +                      PU_STATIC, 0);
 +
 +        texture->width = SHORT(mtexture->width);
 +        texture->height = SHORT(mtexture->height);
 +        texture->patchcount = SHORT(mtexture->patchcount);
 +
 +        memcpy (texture->name, mtexture->name, sizeof(texture->name));
 +        mpatch = &mtexture->patches[0];
 +        patch = &texture->patches[0];
 +
 +        for (j=0 ; j<texture->patchcount ; j++, mpatch++, patch++)
 +        {
 +            patch->originx = SHORT(mpatch->originx);
 +            patch->originy = SHORT(mpatch->originy);
 +            patch->patch = patchlookup[SHORT(mpatch->patch)];
 +            if (patch->patch == -1)
 +            {
 +                I_Error ("R_InitTextures: Missing patch in texture %s",
 +                         texture->name);
 +            }
 +        }
 +        texturecolumnlump[i] = Z_Malloc (texture->width*sizeof(**texturecolumnlump), PU_STATIC,0);
 +        texturecolumnofs[i] = Z_Malloc (texture->width*sizeof(**texturecolumnofs), PU_STATIC,0);
 +
 +        j = 1;
 +        while (j*2 <= texture->width)
 +            j<<=1;
 +
 +        texturewidthmask[i] = j-1;
 +        textureheight[i] = texture->height<<FRACBITS;
 +
 +        totalwidth += texture->width;
 +    }
 +
 +    Z_Free(patchlookup);
 +
 +    W_ReleaseLumpName(DEH_String("TEXTURE1"));
 +    if (maptex2)
 +        W_ReleaseLumpName(DEH_String("TEXTURE2"));
 +
 +    // Precalculate whatever possible.
 +
 +    for (i=0 ; i<numtextures ; i++)
 +    {
 +        // [STRIFE]: tick intro
 +        if(!(i & 63))
 +            D_IntroTick();
 +        R_GenerateLookup (i);
 +    }
 +
 +    // Create translation table for global animation.
 +    texturetranslation = Z_Malloc ((numtextures+1)*sizeof(*texturetranslation), PU_STATIC, 0);
 +
 +    for (i=0 ; i<numtextures ; i++)
 +        texturetranslation[i] = i;
 +
 +    GenerateTextureHashTable();
 +}
 +
 +
 +
 +//
 +// R_InitFlats
 +//
 +void R_InitFlats (void)
 +{
 +    int		i;
 +
 +    firstflat = W_GetNumForName (DEH_String("F_START")) + 1;
 +    lastflat = W_GetNumForName (DEH_String("F_END")) - 1;
 +    numflats = lastflat - firstflat + 1;
 +
 +    // Create translation table for global animation.
 +    flattranslation = Z_Malloc ((numflats+1)*sizeof(*flattranslation), PU_STATIC, 0);
 +
 +    for (i=0 ; i<numflats ; i++)
 +	flattranslation[i] = i;
 +}
 +
 +
 +//
 +// R_InitSpriteLumps
 +// Finds the width and hoffset of all sprites in the wad,
 +//  so the sprite does not need to be cached completely
 +//  just for having the header info ready during rendering.
 +//
 +void R_InitSpriteLumps (void)
 +{
 +    int		i;
 +    patch_t	*patch;
 +
 +    firstspritelump = W_GetNumForName (DEH_String("S_START")) + 1;
 +    lastspritelump = W_GetNumForName (DEH_String("S_END")) - 1;
 +
 +    numspritelumps = lastspritelump - firstspritelump + 1;
 +    spritewidth = Z_Malloc (numspritelumps*sizeof(*spritewidth), PU_STATIC, 0);
 +    spriteoffset = Z_Malloc (numspritelumps*sizeof(*spriteoffset), PU_STATIC, 0);
 +    spritetopoffset = Z_Malloc (numspritelumps*sizeof(*spritetopoffset), PU_STATIC, 0);
 +
 +    for (i=0 ; i< numspritelumps ; i++)
 +    {
 +        if(!(i&63))
 +        {
 +            // [STRIFE] tick intro if not in devparm
 +            if(devparm)
 +                printf (".");
 +            else
 +                D_IntroTick();
 +        }
 +
 +        patch = W_CacheLumpNum (firstspritelump+i, PU_CACHE);
 +        spritewidth[i] = SHORT(patch->width)<<FRACBITS;
 +        spriteoffset[i] = SHORT(patch->leftoffset)<<FRACBITS;
 +        spritetopoffset[i] = SHORT(patch->topoffset)<<FRACBITS;
 +    }
 +}
 +
 +
 +
 +//
 +// R_InitColormaps
 +//
 +void R_InitColormaps (void)
 +{
 +    int	lump, length;
 +
 +    // Load in the light tables,
 +    //  256 byte align tables.
 +    lump = W_GetNumForName(DEH_String("COLORMAP"));
 +    length = W_LumpLength (lump);
 +    colormaps = W_CacheLumpNum(lump, PU_STATIC);
 +}
 +
 +
 +
 +//
 +// R_InitData
 +// Locates all the lumps
 +//  that will be used by all views
 +// Must be called after W_Init.
 +//
 +void R_InitData (void)
 +{
 +    R_InitTextures ();
 +    if(devparm)
 +        printf (".");
 +    else
 +        D_IntroTick(); // [STRIFE] tick intro
 +
 +    R_InitFlats ();
 +    if(devparm)
 +        printf (".");
 +    else
 +        D_IntroTick();
 +
 +    R_InitSpriteLumps ();
 +    if(devparm)
 +        printf (".");
 +    else
 +        D_IntroTick();
 +
 +    R_InitColormaps ();
 +}
 +
 +
 +
 +//
 +// R_FlatNumForName
 +// Retrieval, get a flat number for a flat name.
 +//
 +int R_FlatNumForName (char* name)
 +{
 +    int		i;
 +    char	namet[9];
 +
 +    i = W_CheckNumForName (name);
 +
 +    if (i == -1)
 +    {
 +	namet[8] = 0;
 +	memcpy (namet, name,8);
 +	I_Error ("R_FlatNumForName: %s not found",namet);
 +    }
 +    return i - firstflat;
 +}
 +
 +
 +
 +
 +//
 +// R_CheckTextureNumForName
 +// Check whether texture is available.
 +// Filter out NoTexture indicator.
 +//
 +int	R_CheckTextureNumForName (char *name)
 +{
 +    texture_t *texture;
 +    int key;
 +
 +    // "NoTexture" marker.
 +    if (name[0] == '-')
 +	return 0;
 +
 +    key = W_LumpNameHash(name) % numtextures;
 +
 +    texture=textures_hashtable[key];
 +
 +    while (texture != NULL)
 +    {
 +	if (!strncasecmp (texture->name, name, 8) )
 +	    return texture->index;
 +
 +        texture = texture->next;
 +    }
 +
 +    return -1;
 +}
 +
 +
 +
 +//
 +// R_TextureNumForName
 +// Calls R_CheckTextureNumForName,
 +//  aborts with error message.
 +//
 +int	R_TextureNumForName (char* name)
 +{
 +    int		i;
 +
 +    i = R_CheckTextureNumForName (name);
 +
 +    if (i==-1)
 +    {
 +	I_Error ("R_TextureNumForName: %s not found",
 +		 name);
 +    }
 +    return i;
 +}
 +
 +//
 +// R_SoundNumForDoor
 +//
 +// villsa [STRIFE] - new function
 +// Set sounds associated with door though why
 +// on earth is this function placed here?
 +//
 +void R_SoundNumForDoor(vldoor_t* door)
 +{
 +    int         i;
 +    sector_t    *sector;
 +    line_t      *line;
 +    texture_t   *texture;
 +    char        name[8];
 +    char        c1;
 +    char        c2;
 +
 +    // set default sounds
 +    door->opensound = sfx_drsmto;
 +    door->closesound = sfx_drsmtc;
 +
 +    for(sector = door->sector, i = 0; i < sector->linecount; i++)
 +    {
 +        line = sector->lines[i];
 +
 +        if(!(line->flags & ML_TWOSIDED))
 +            continue;
 +
 +        texture = textures[sides[line->sidenum[0]].toptexture];
 +        memcpy(name, texture->name, 8);
 +        //memcpy(&v6, texture->index, 0);   // [STRIFE] todo - WHAT?!
 +
 +        if(strncmp(name, "DOR", 3))
 +            continue;
 +
 +        c1 = name[3];
 +        c2 = name[4];
 +
 +        // S type
 +        if(c1 == 'S')
 +        {
 +            door->opensound = sfx_drston;
 +            door->closesound = sfx_drston;
 +            return;
 +        }
 +
 +        // M type
 +        if(c1 == 'M')
 +        {
 +            // L subtype
 +            if(c2 == 'L')
 +            {
 +                door->opensound = sfx_drlmto;
 +                door->closesound = sfx_drlmtc;
 +            }
 +            // S subtype
 +            else if(c2 == 'S')
 +            {
 +                door->opensound = sfx_drsmto;
 +                door->closesound = sfx_drsmtc;
 +            }
 +            return;
 +        }
 +        // W type
 +        else if(c1 == 'W')
 +        {
 +            // L subtype
 +            if(c2 == 'L')
 +            {
 +                door->opensound = sfx_drlwud;
 +                door->closesound = sfx_drlwud;
 +
 +            }
 +            // S subtype
 +            else if(c2 == 'S')
 +            {
 +                door->opensound = sfx_drswud;
 +                door->closesound = sfx_drswud;
 +
 +            }
 +            return;
 +        }
 +    }
 +}
 +
 +
 +
 +
 +//
 +// R_PrecacheLevel
 +// Preloads all relevant graphics for the level.
 +//
 +int		flatmemory;
 +int		texturememory;
 +int		spritememory;
 +
 +void R_PrecacheLevel (void)
 +{
 +    char*		flatpresent;
 +    char*		texturepresent;
 +    char*		spritepresent;
 +
 +    int			i;
 +    int			j;
 +    int			k;
 +    int			lump;
 +
 +    texture_t*		texture;
 +    thinker_t*		th;
 +    spriteframe_t*	sf;
 +
 +    if (demoplayback)
 +	return;
 +
 +    // Precache flats.
 +    flatpresent = Z_Malloc(numflats, PU_STATIC, NULL);
 +    memset (flatpresent,0,numflats);
 +
 +    for (i=0 ; i<numsectors ; i++)
 +    {
 +	flatpresent[sectors[i].floorpic] = 1;
 +	flatpresent[sectors[i].ceilingpic] = 1;
 +    }
 +
 +    flatmemory = 0;
 +
 +    for (i=0 ; i<numflats ; i++)
 +    {
 +	if (flatpresent[i])
 +	{
 +	    lump = firstflat + i;
 +	    flatmemory += lumpinfo[lump].size;
 +	    W_CacheLumpNum(lump, PU_CACHE);
 +	}
 +    }
 +
 +    Z_Free(flatpresent);
 +
 +    // Precache textures.
 +    texturepresent = Z_Malloc(numtextures, PU_STATIC, NULL);
 +    memset (texturepresent,0, numtextures);
 +
 +    for (i=0 ; i<numsides ; i++)
 +    {
 +	texturepresent[sides[i].toptexture] = 1;
 +	texturepresent[sides[i].midtexture] = 1;
 +	texturepresent[sides[i].bottomtexture] = 1;
 +    }
 +
 +    // Sky texture is always present.
 +    // Note that F_SKY1 is the name used to
 +    //  indicate a sky floor/ceiling as a flat,
 +    //  while the sky texture is stored like
 +    //  a wall texture, with an episode dependend
 +    //  name.
 +    texturepresent[skytexture] = 1;
 +
 +    texturememory = 0;
 +    for (i=0 ; i<numtextures ; i++)
 +    {
 +	if (!texturepresent[i])
 +	    continue;
 +
 +	texture = textures[i];
 +
 +	for (j=0 ; j<texture->patchcount ; j++)
 +	{
 +	    lump = texture->patches[j].patch;
 +	    texturememory += lumpinfo[lump].size;
 +	    W_CacheLumpNum(lump , PU_CACHE);
 +	}
 +    }
 +
 +    Z_Free(texturepresent);
 +
 +    // Precache sprites.
 +    spritepresent = Z_Malloc(numsprites, PU_STATIC, NULL);
 +    memset (spritepresent,0, numsprites);
 +
 +    for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
 +    {
 +	if (th->function.acp1 == (actionf_p1)P_MobjThinker)
 +	    spritepresent[((mobj_t *)th)->sprite] = 1;
 +    }
 +
 +    spritememory = 0;
 +    for (i=0 ; i<numsprites ; i++)
 +    {
 +	if (!spritepresent[i])
 +	    continue;
 +
 +	for (j=0 ; j<sprites[i].numframes ; j++)
 +	{
 +	    sf = &sprites[i].spriteframes[j];
 +	    for (k=0 ; k<8 ; k++)
 +	    {
 +		lump = firstspritelump + sf->lump[k];
 +		spritememory += lumpinfo[lump].size;
 +		W_CacheLumpNum(lump , PU_CACHE);
 +	    }
 +	}
 +    }
 +
 +    Z_Free(spritepresent);
 +}
 +
 +
 +
 +