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 = §ors[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);
+}
+
+
+
+