diff --git a/CMakeLists.txt b/CMakeLists.txt
index c85f23743..ec96b7030 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.0)
project(SRB2
- VERSION 2.1.20
+ VERSION 2.1.21
LANGUAGES C)
if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})
diff --git a/SRB2.cbp b/SRB2.cbp
index 5aa623fa8..2a1eb87b8 100644
--- a/SRB2.cbp
+++ b/SRB2.cbp
@@ -1549,6 +1549,9 @@ HW3SOUND for 3D hardware sound support
+
+
+
diff --git a/appveyor.yml b/appveyor.yml
index 69913cfc8..061613c4d 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 2.1.20.{branch}-{build}
+version: 2.1.21.{branch}-{build}
os: MinGW
environment:
diff --git a/bin/Resources/i686/exchndl.dll b/bin/Resources/i686/exchndl.dll
deleted file mode 100644
index d836a6762..000000000
Binary files a/bin/Resources/i686/exchndl.dll and /dev/null differ
diff --git a/libs/DLL-README.txt b/libs/DLL-README.txt
new file mode 100644
index 000000000..058ec0685
--- /dev/null
+++ b/libs/DLL-README.txt
@@ -0,0 +1,43 @@
+# SRB2 - Which DLLs do I need to bundle?
+
+Updated 12/4/2018 (v2.1.21)
+
+Here are the required DLLs, per build. For each architecture, copy all the binaries from these folders:
+
+* libs\dll-binaries\[i686/x86_64]
+* libs\SDL2\[i686/x86_64]...\bin
+* libs\SDL2_mixer\[i686/x86_64]...\bin
+
+and don't forget to build r_opengl.dll for srb2dd.
+
+## srb2win, 32-bit
+
+* libs\dll-binaries\i686\exchndl.dll
+* libs\dll-binaries\i686\libgme.dll
+* libs\dll-binaries\i686\mgwhelp.dll (depend for exchndl.dll)
+* libs\SDL2\i686-w64-mingw32\bin\SDL2.dll
+* libs\SDL2_mixer\i686-w64-mingw32\bin\*.dll (get everything)
+
+## srb2win, 64-bit
+
+* libs\dll-binaries\x86_64\exchndl.dll
+* libs\dll-binaries\x86_64\libgme.dll
+* libs\dll-binaries\x86_64\mgwhelp.dll (depend for exchndl.dll)
+* libs\SDL2\x86_64-w64-mingw32\bin\SDL2.dll
+* libs\SDL2_mixer\x86_64-w64-mingw32\bin\*.dll (get everything)
+
+## srb2dd, 32-bit
+
+* libs\dll-binaries\i686\exchndl.dll
+* libs\dll-binaries\i686\fmodex.dll
+* libs\dll-binaries\i686\libgme.dll
+* libs\dll-binaries\i686\mgwhelp.dll (depend for exchndl.dll)
+* r_opengl.dll (build this from make)
+
+## srb2dd, 64-bit
+
+* libs\dll-binaries\x86_64\exchndl.dll
+* libs\dll-binaries\x86_64\fmodex.dll
+* libs\dll-binaries\x86_64\libgme.dll
+* libs\dll-binaries\x86_64\mgwhelp.dll (depend for exchndl.dll)
+* r_opengl.dll (build this from make)
diff --git a/bin/Resources/i686/fmod.dll b/libs/dll-binaries/i686/Old/fmod.dll
similarity index 100%
rename from bin/Resources/i686/fmod.dll
rename to libs/dll-binaries/i686/Old/fmod.dll
diff --git a/bin/Resources/i686/fmodexL.dll b/libs/dll-binaries/i686/Old/fmodexL.dll
similarity index 100%
rename from bin/Resources/i686/fmodexL.dll
rename to libs/dll-binaries/i686/Old/fmodexL.dll
diff --git a/bin/Resources/i686/libgcc_s_dw2-1.dll b/libs/dll-binaries/i686/Old/libgcc_s_dw2-1.dll
similarity index 100%
rename from bin/Resources/i686/libgcc_s_dw2-1.dll
rename to libs/dll-binaries/i686/Old/libgcc_s_dw2-1.dll
diff --git a/bin/Resources/i686/libintl-8.dll b/libs/dll-binaries/i686/Old/libintl-8.dll
similarity index 100%
rename from bin/Resources/i686/libintl-8.dll
rename to libs/dll-binaries/i686/Old/libintl-8.dll
diff --git a/libs/dll-binaries/i686/exchndl.dll b/libs/dll-binaries/i686/exchndl.dll
new file mode 100644
index 000000000..d6beb764a
Binary files /dev/null and b/libs/dll-binaries/i686/exchndl.dll differ
diff --git a/bin/Resources/i686/fmodex.dll b/libs/dll-binaries/i686/fmodex.dll
similarity index 100%
rename from bin/Resources/i686/fmodex.dll
rename to libs/dll-binaries/i686/fmodex.dll
diff --git a/bin/Resources/i686/libgme.dll b/libs/dll-binaries/i686/libgme.dll
similarity index 100%
rename from bin/Resources/i686/libgme.dll
rename to libs/dll-binaries/i686/libgme.dll
diff --git a/libs/dll-binaries/i686/mgwhelp.dll b/libs/dll-binaries/i686/mgwhelp.dll
new file mode 100644
index 000000000..3cf97424d
Binary files /dev/null and b/libs/dll-binaries/i686/mgwhelp.dll differ
diff --git a/bin/Resources/x86_64/fmod64.dll b/libs/dll-binaries/x86_64/Old/fmod64.dll
similarity index 100%
rename from bin/Resources/x86_64/fmod64.dll
rename to libs/dll-binaries/x86_64/Old/fmod64.dll
diff --git a/bin/Resources/x86_64/fmodexL64.dll b/libs/dll-binaries/x86_64/Old/fmodexL64.dll
similarity index 100%
rename from bin/Resources/x86_64/fmodexL64.dll
rename to libs/dll-binaries/x86_64/Old/fmodexL64.dll
diff --git a/libs/dll-binaries/x86_64/exchndl.dll b/libs/dll-binaries/x86_64/exchndl.dll
new file mode 100644
index 000000000..747d7a3d5
Binary files /dev/null and b/libs/dll-binaries/x86_64/exchndl.dll differ
diff --git a/bin/Resources/x86_64/fmodex64.dll b/libs/dll-binaries/x86_64/fmodex64.dll
similarity index 100%
rename from bin/Resources/x86_64/fmodex64.dll
rename to libs/dll-binaries/x86_64/fmodex64.dll
diff --git a/bin/Resources/x86_64/libgme.dll b/libs/dll-binaries/x86_64/libgme.dll
similarity index 100%
rename from bin/Resources/x86_64/libgme.dll
rename to libs/dll-binaries/x86_64/libgme.dll
diff --git a/libs/dll-binaries/x86_64/mgwhelp.dll b/libs/dll-binaries/x86_64/mgwhelp.dll
new file mode 100644
index 000000000..4e30e140e
Binary files /dev/null and b/libs/dll-binaries/x86_64/mgwhelp.dll differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 99f2beebe..4a9ef5ba8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -237,6 +237,7 @@ if(${SRB2_CONFIG_HAVE_BLUA})
add_definitions(-DHAVE_BLUA)
set(SRB2_LUA_SOURCES
lua_baselib.c
+ lua_blockmaplib.c
lua_consolelib.c
lua_hooklib.c
lua_hudlib.c
diff --git a/src/blua/Makefile.cfg b/src/blua/Makefile.cfg
index e3fb3df46..b1131eaca 100644
--- a/src/blua/Makefile.cfg
+++ b/src/blua/Makefile.cfg
@@ -39,6 +39,7 @@ OBJS:=$(OBJS) \
$(OBJDIR)/lvm.o \
$(OBJDIR)/lua_script.o \
$(OBJDIR)/lua_baselib.o \
+ $(OBJDIR)/lua_blockmaplib.o \
$(OBJDIR)/lua_mathlib.o \
$(OBJDIR)/lua_hooklib.o \
$(OBJDIR)/lua_consolelib.o \
diff --git a/src/command.c b/src/command.c
index 4a65a0aa6..16f18749b 100644
--- a/src/command.c
+++ b/src/command.c
@@ -32,6 +32,7 @@
#include "hu_stuff.h"
#include "p_setup.h"
#include "lua_script.h"
+#include "d_netfil.h" // findfile
//========
// protos.
@@ -49,6 +50,7 @@ static void COM_Wait_f(void);
static void COM_Help_f(void);
static void COM_Toggle_f(void);
+static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr);
static boolean CV_Command(void);
static consvar_t *CV_FindVar(const char *name);
static const char *CV_StringValue(const char *var_name);
@@ -62,7 +64,19 @@ CV_PossibleValue_t CV_YesNo[] = {{0, "No"}, {1, "Yes"}, {0, NULL}};
CV_PossibleValue_t CV_Unsigned[] = {{0, "MIN"}, {999999999, "MAX"}, {0, NULL}};
CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}};
+// Filter consvars by MODVERSION
+// First implementation is 26 (2.1.21), so earlier configs default at 25 (2.1.20)
+// Also set CV_HIDEN during runtime, after config is loaded
+consvar_t cv_execversion = {"execversion","25",0,CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// for default joyaxis detection
+static boolean joyaxis_default = false;
+static boolean joyaxis2_default = false;
+static INT32 joyaxis_count = 0;
+static INT32 joyaxis2_count = 0;
+
#define COM_BUF_SIZE 8192 // command buffer size
+#define MAX_ALIAS_RECURSION 100 // max recursion allowed for aliases
static INT32 com_wait; // one command per frame (for cmd sequences)
@@ -485,6 +499,7 @@ static void COM_ExecuteString(char *ptext)
{
xcommand_t *cmd;
cmdalias_t *a;
+ static INT32 recursion = 0; // detects recursion and stops it if it goes too far
COM_TokenizeString(ptext);
@@ -497,6 +512,7 @@ static void COM_ExecuteString(char *ptext)
{
if (!stricmp(com_argv[0], cmd->name)) //case insensitive now that we have lower and uppercase!
{
+ recursion = 0;
cmd->function();
return;
}
@@ -507,11 +523,20 @@ static void COM_ExecuteString(char *ptext)
{
if (!stricmp(com_argv[0], a->name))
{
+ if (recursion > MAX_ALIAS_RECURSION)
+ {
+ CONS_Alert(CONS_WARNING, M_GetText("Alias recursion cycle detected!\n"));
+ recursion = 0;
+ return;
+ }
+ recursion++;
COM_BufInsertText(a->value);
return;
}
}
+ recursion = 0;
+
// check cvars
// Hurdler: added at Ebola's request ;)
// (don't flood the console in software mode with bad gr_xxx command)
@@ -617,6 +642,7 @@ static void COM_CEchoDuration_f(void)
static void COM_Exec_f(void)
{
UINT8 *buf = NULL;
+ char filename[256];
if (COM_Argc() < 2 || COM_Argc() > 3)
{
@@ -625,13 +651,23 @@ static void COM_Exec_f(void)
}
// load file
+ // Try with Argv passed verbatim first, for back compat
FIL_ReadFile(COM_Argv(1), &buf);
if (!buf)
{
- if (!COM_CheckParm("-noerror"))
- CONS_Printf(M_GetText("couldn't execute file %s\n"), COM_Argv(1));
- return;
+ // Now try by searching the file path
+ // filename is modified with the full found path
+ strcpy(filename, COM_Argv(1));
+ if (findfile(filename, NULL, true) != FS_NOTFOUND)
+ FIL_ReadFile(filename, &buf);
+
+ if (!buf)
+ {
+ if (!COM_CheckParm("-noerror"))
+ CONS_Printf(M_GetText("couldn't execute file %s\n"), COM_Argv(1));
+ return;
+ }
}
if (!COM_CheckParm("-silent"))
@@ -1220,7 +1256,7 @@ static void Got_NetVar(UINT8 **p, INT32 playernum)
char *svalue;
UINT8 stealth = false;
- if (playernum != serverplayer && playernum != adminplayer && !serverloading)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum) && !serverloading)
{
// not from server or remote admin, must be hacked/buggy client
CONS_Alert(CONS_WARNING, M_GetText("Illegal netvar command received from %s\n"), player_names[playernum]);
@@ -1349,7 +1385,7 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
// send the value of the variable
XBOXSTATIC UINT8 buf[128];
UINT8 *p = buf;
- if (!(server || (adminplayer == consoleplayer)))
+ if (!(server || (IsPlayerAdmin(consoleplayer))))
{
CONS_Printf(M_GetText("Only the server or admin can change: %s %s\n"), var->name, var->string);
return;
@@ -1556,6 +1592,200 @@ void CV_AddValue(consvar_t *var, INT32 increment)
var->changed = 1; // user has changed it now
}
+void CV_InitFilterVar(void)
+{
+ joyaxis_default = joyaxis2_default = true;
+ joyaxis_count = joyaxis2_count = 0;
+}
+
+static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr)
+{
+ // If ALL axis settings are previous defaults, set them to the new defaults
+ // MODVERSION < 26 (2.1.21)
+
+ if (joyaxis_default)
+ {
+#if !defined (_WII) && !defined (WMINPUT)
+ if (!stricmp(v->name, "joyaxis_turn"))
+ {
+ if (joyaxis_count > 6) return false;
+ // we're currently setting the new defaults, don't interfere
+ else if (joyaxis_count == 6) return true;
+
+ if (!stricmp(valstr, "X-Axis")) joyaxis_count++;
+ else joyaxis_default = false;
+ }
+#if !defined (PSP)
+ if (!stricmp(v->name, "joyaxis_move"))
+ {
+ if (joyaxis_count > 6) return false;
+ else if (joyaxis_count == 6) return true;
+
+ if (!stricmp(valstr, "Y-Axis")) joyaxis_count++;
+ else joyaxis_default = false;
+ }
+#endif
+#if !defined (_arch_dreamcast) && !defined (_XBOX) && !defined (PSP)
+ if (!stricmp(v->name, "joyaxis_side"))
+ {
+ if (joyaxis_count > 6) return false;
+ else if (joyaxis_count == 6) return true;
+
+ if (!stricmp(valstr, "Z-Axis")) joyaxis_count++;
+ else joyaxis_default = false;
+ }
+#endif
+#if !defined (_XBOX) && !defined (PSP)
+ if (!stricmp(v->name, "joyaxis_look"))
+ {
+ if (joyaxis_count > 6) return false;
+ else if (joyaxis_count == 6) return true;
+
+ if (!stricmp(valstr, "None")) joyaxis_count++;
+ else joyaxis_default = false;
+ }
+#endif
+ if (!stricmp(v->name, "joyaxis_fire")
+ || !stricmp(v->name, "joyaxis_firenormal"))
+ {
+ if (joyaxis_count > 6) return false;
+ else if (joyaxis_count == 6) return true;
+
+ if (!stricmp(valstr, "None")) joyaxis_count++;
+ else joyaxis_default = false;
+ }
+#endif
+ // reset all axis settings to defaults
+ if (joyaxis_count == 6)
+ {
+ COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis.name, cv_turnaxis.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis.name, cv_moveaxis.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis.name, cv_sideaxis.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis.name, cv_lookaxis.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis.name, cv_fireaxis.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis.name, cv_firenaxis.defaultvalue));
+ joyaxis_count++;
+ return false;
+ }
+ }
+
+ if (joyaxis2_default)
+ {
+#if !defined (_WII) && !defined (WMINPUT)
+ if (!stricmp(v->name, "joyaxis2_turn"))
+ {
+ if (joyaxis2_count > 6) return false;
+ // we're currently setting the new defaults, don't interfere
+ else if (joyaxis2_count == 6) return true;
+
+ if (!stricmp(valstr, "X-Axis")) joyaxis2_count++;
+ else joyaxis2_default = false;
+ }
+// #if !defined (PSP)
+ if (!stricmp(v->name, "joyaxis2_move"))
+ {
+ if (joyaxis2_count > 6) return false;
+ else if (joyaxis2_count == 6) return true;
+
+ if (!stricmp(valstr, "Y-Axis")) joyaxis2_count++;
+ else joyaxis2_default = false;
+ }
+// #endif
+#if !defined (_arch_dreamcast) && !defined (_XBOX) && !defined (PSP)
+ if (!stricmp(v->name, "joyaxis2_side"))
+ {
+ if (joyaxis2_count > 6) return false;
+ else if (joyaxis2_count == 6) return true;
+
+ if (!stricmp(valstr, "Z-Axis")) joyaxis2_count++;
+ else joyaxis2_default = false;
+ }
+#endif
+#if !defined (_XBOX) // && !defined (PSP)
+ if (!stricmp(v->name, "joyaxis2_look"))
+ {
+ if (joyaxis2_count > 6) return false;
+ else if (joyaxis2_count == 6) return true;
+
+ if (!stricmp(valstr, "None")) joyaxis2_count++;
+ else joyaxis2_default = false;
+ }
+#endif
+ if (!stricmp(v->name, "joyaxis2_fire")
+ || !stricmp(v->name, "joyaxis2_firenormal"))
+ {
+ if (joyaxis2_count > 6) return false;
+ else if (joyaxis2_count == 6) return true;
+
+ if (!stricmp(valstr, "None")) joyaxis2_count++;
+ else joyaxis2_default = false;
+ }
+#endif
+
+ // reset all axis settings to defaults
+ if (joyaxis2_count == 6)
+ {
+ COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis2.name, cv_turnaxis2.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis2.name, cv_moveaxis2.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis2.name, cv_sideaxis2.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis2.name, cv_lookaxis2.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis2.name, cv_fireaxis2.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis2.name, cv_firenaxis2.defaultvalue));
+ joyaxis2_count++;
+ return false;
+ }
+ }
+
+ // we haven't reached our counts yet, or we're not default
+ return true;
+}
+
+static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr)
+{
+ // True means allow the CV change, False means block it
+
+ // We only care about CV_SAVE because this filters the user's config files
+ // We do this same check in CV_Command
+ if (!(v->flags & CV_SAVE))
+ return true;
+
+ // We go by MODVERSION here
+ if (cv_execversion.value < 26) // 26 = 2.1.21
+ {
+ // MOUSE SETTINGS
+ // alwaysfreelook split between first and third person (chasefreelook)
+ // mousemove was on by default, which invalidates the current approach
+ if (!stricmp(v->name, "alwaysmlook")
+ || !stricmp(v->name, "alwaysmlook2")
+ || !stricmp(v->name, "mousemove")
+ || !stricmp(v->name, "mousemove2"))
+ return false;
+
+ // mousesens was changed from 35 to 20 due to oversensitivity
+ if ((!stricmp(v->name, "mousesens")
+ || !stricmp(v->name, "mousesens2")
+ || !stricmp(v->name, "mouseysens")
+ || !stricmp(v->name, "mouseysens2"))
+ && atoi(valstr) == 35)
+ return false;
+
+ // JOYSTICK DEFAULTS
+ // use_joystick was changed from 0 to 1 to automatically use a joystick if available
+#if defined(HAVE_SDL) || defined(_WINDOWS)
+ if ((!stricmp(v->name, "use_joystick")
+ || !stricmp(v->name, "use_joystick2"))
+ && atoi(valstr) == 0)
+ return false;
+#endif
+
+ // axis defaults were changed to be friendly to 360 controllers
+ // if ALL axis settings are defaults, then change them to new values
+ if (!CV_FilterJoyAxisVars(v, valstr))
+ return false;
+ }
+ return true;
+}
+
/** Displays or changes a variable from the console.
* Since the user is presumed to have been directly responsible
* for this change, the variable is marked as changed this game.
@@ -1580,8 +1810,11 @@ static boolean CV_Command(void)
return true;
}
- CV_Set(v, COM_Argv(1));
- v->changed = 1; // now it's been changed by (presumably) the user
+ if (!(v->flags & CV_SAVE) || CV_FilterVarByVersion(v, COM_Argv(1)))
+ {
+ CV_Set(v, COM_Argv(1));
+ v->changed = 1; // now it's been changed by (presumably) the user
+ }
return true;
}
diff --git a/src/command.h b/src/command.h
index 7420c2103..8dee1174c 100644
--- a/src/command.h
+++ b/src/command.h
@@ -125,6 +125,12 @@ extern CV_PossibleValue_t CV_OnOff[];
extern CV_PossibleValue_t CV_YesNo[];
extern CV_PossibleValue_t CV_Unsigned[];
extern CV_PossibleValue_t CV_Natural[];
+
+// Filter consvars by version
+extern consvar_t cv_execversion;
+
+void CV_InitFilterVar(void);
+
// register a variable for use at the console
void CV_RegisterVar(consvar_t *variable);
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 073c0d363..cd8367af8 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -25,6 +25,7 @@
#include "g_game.h"
#include "hu_stuff.h"
#include "keys.h"
+#include "g_input.h" // JOY1
#include "m_menu.h"
#include "console.h"
#include "d_netfil.h"
@@ -1365,15 +1366,18 @@ static boolean SV_SendServerConfig(INT32 node)
netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
netbuffer->u.servercfg.gametype = (UINT8)gametype;
netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
- netbuffer->u.servercfg.adminplayer = (SINT8)adminplayer;
// we fill these structs with FFs so that any players not in game get sent as 0xFFFF
// which is nice and easy for us to detect
memset(netbuffer->u.servercfg.playerskins, 0xFF, sizeof(netbuffer->u.servercfg.playerskins));
memset(netbuffer->u.servercfg.playercolor, 0xFF, sizeof(netbuffer->u.servercfg.playercolor));
+ memset(netbuffer->u.servercfg.adminplayers, -1, sizeof(netbuffer->u.servercfg.adminplayers));
+
for (i = 0; i < MAXPLAYERS; i++)
{
+ netbuffer->u.servercfg.adminplayers[i] = (SINT8)adminplayers[i];
+
if (!playeringame[i])
continue;
netbuffer->u.servercfg.playerskins[i] = (UINT8)players[i].skin;
@@ -1959,7 +1963,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
I_OsPolling();
key = I_GetKey();
- if (key == KEY_ESCAPE)
+ if (key == KEY_ESCAPE || key == KEY_JOY1+1)
{
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
@@ -2042,7 +2046,7 @@ static void CL_ConnectToServer(boolean viams)
G_SetGamestate(GS_WAITINGPLAYERS);
wipegamestate = GS_WAITINGPLAYERS;
- adminplayer = -1;
+ ClearAdminPlayers();
pnumnodes = 1;
oldtic = I_GetTime() - 1;
#ifndef NONET
@@ -2354,7 +2358,7 @@ void CL_ClearPlayer(INT32 playernum)
//
// Removes a player from the current game
//
-static void CL_RemovePlayer(INT32 playernum)
+static void CL_RemovePlayer(INT32 playernum, INT32 reason)
{
// Sanity check: exceptional cases (i.e. c-fails) can cause multiple
// kick commands to be issued for the same player.
@@ -2409,6 +2413,10 @@ static void CL_RemovePlayer(INT32 playernum)
}
}
+#ifdef HAVE_BLUA
+ LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting
+#endif
+
// Reset player data
CL_ClearPlayer(playernum);
@@ -2421,8 +2429,10 @@ static void CL_RemovePlayer(INT32 playernum)
// Reset the name
sprintf(player_names[playernum], "Player %d", playernum+1);
- if (playernum == adminplayer)
- adminplayer = -1; // don't stay admin after you're gone
+ if (IsPlayerAdmin(playernum))
+ {
+ RemoveAdminPlayer(playernum); // don't stay admin after you're gone
+ }
if (playernum == displayplayer)
displayplayer = consoleplayer; // don't look through someone's view who isn't there
@@ -2540,7 +2550,7 @@ static void Command_Nodes(void)
if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL)
CONS_Printf(" - %s", address);
- if (i == adminplayer)
+ if (IsPlayerAdmin(i))
CONS_Printf(M_GetText(" (verified admin)"));
if (players[i].spectator)
@@ -2565,7 +2575,7 @@ static void Command_Ban(void)
return;
}
- if (server || adminplayer == consoleplayer)
+ if (server || IsPlayerAdmin(consoleplayer))
{
XBOXSTATIC UINT8 buf[3 + MAX_REASONLENGTH];
UINT8 *p = buf;
@@ -2631,7 +2641,7 @@ static void Command_Kick(void)
return;
}
- if (server || adminplayer == consoleplayer)
+ if (server || IsPlayerAdmin(consoleplayer))
{
XBOXSTATIC UINT8 buf[3 + MAX_REASONLENGTH];
UINT8 *p = buf;
@@ -2640,13 +2650,16 @@ static void Command_Kick(void)
if (pn == -1 || pn == 0)
return;
- // Special case if we are trying to kick a player who is downloading the game state:
- // trigger a timeout instead of kicking them, because a kick would only
- // take effect after they have finished downloading
- if (sendingsavegame[playernode[pn]])
+ if (server)
{
- Net_ConnectionTimeout(playernode[pn]);
- return;
+ // Special case if we are trying to kick a player who is downloading the game state:
+ // trigger a timeout instead of kicking them, because a kick would only
+ // take effect after they have finished downloading
+ if (sendingsavegame[playernode[pn]])
+ {
+ Net_ConnectionTimeout(playernode[pn]);
+ return;
+ }
}
WRITESINT8(p, pn);
@@ -2684,11 +2697,12 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
INT32 pnum, msg;
XBOXSTATIC char buf[3 + MAX_REASONLENGTH];
char *reason = buf;
+ kickreason_t kickreason = KR_KICK;
pnum = READUINT8(*p);
msg = READUINT8(*p);
- if (pnum == serverplayer && playernum == adminplayer)
+ if (pnum == serverplayer && IsPlayerAdmin(playernum))
{
CONS_Printf(M_GetText("Server is being shut down remotely. Goodbye!\n"));
@@ -2699,7 +2713,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
}
// Is playernum authorized to make this kick?
- if (playernum != serverplayer && playernum != adminplayer
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum)
&& !(playerpernode[playernode[playernum]] == 2
&& nodetoplayer2[playernode[playernum]] == pnum))
{
@@ -2766,14 +2780,17 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
{
case KICK_MSG_GO_AWAY:
CONS_Printf(M_GetText("has been kicked (Go away)\n"));
+ kickreason = KR_KICK;
break;
#ifdef NEWPING
case KICK_MSG_PING_HIGH:
CONS_Printf(M_GetText("left the game (Broke ping limit)\n"));
+ kickreason = KR_PINGLIMIT;
break;
#endif
case KICK_MSG_CON_FAIL:
CONS_Printf(M_GetText("left the game (Synch failure)\n"));
+ kickreason = KR_SYNCH;
if (M_CheckParm("-consisdump")) // Helps debugging some problems
{
@@ -2810,21 +2827,26 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
break;
case KICK_MSG_TIMEOUT:
CONS_Printf(M_GetText("left the game (Connection timeout)\n"));
+ kickreason = KR_TIMEOUT;
break;
case KICK_MSG_PLAYER_QUIT:
if (netgame) // not splitscreen/bots
CONS_Printf(M_GetText("left the game\n"));
+ kickreason = KR_LEAVE;
break;
case KICK_MSG_BANNED:
CONS_Printf(M_GetText("has been banned (Don't come back)\n"));
+ kickreason = KR_BAN;
break;
case KICK_MSG_CUSTOM_KICK:
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
CONS_Printf(M_GetText("has been kicked (%s)\n"), reason);
+ kickreason = KR_KICK;
break;
case KICK_MSG_CUSTOM_BAN:
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
CONS_Printf(M_GetText("has been banned (%s)\n"), reason);
+ kickreason = KR_BAN;
break;
}
@@ -2852,7 +2874,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
}
else
- CL_RemovePlayer(pnum);
+ CL_RemovePlayer(pnum, kickreason);
}
consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
@@ -2962,6 +2984,7 @@ void SV_ResetServer(void)
playeringame[i] = false;
playernode[i] = UINT8_MAX;
sprintf(player_names[i], "Player %d", i + 1);
+ adminplayers[i] = -1; // Populate the entire adminplayers array with -1.
}
mynode = 0;
@@ -3036,7 +3059,7 @@ void D_QuitNetGame(void)
}
D_CloseConnection();
- adminplayer = -1;
+ ClearAdminPlayers();
DEBFILE("===========================================================================\n"
" Log finish\n"
@@ -3067,7 +3090,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
INT16 node, newplayernum;
boolean splitscreenplayer;
- if (playernum != serverplayer && playernum != adminplayer)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
// protect against hacked/buggy client
CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]);
@@ -3592,7 +3615,8 @@ static void HandlePacketFromAwayNode(SINT8 node)
maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
gametype = netbuffer->u.servercfg.gametype;
modifiedgame = netbuffer->u.servercfg.modifiedgame;
- adminplayer = netbuffer->u.servercfg.adminplayer;
+ for (j = 0; j < MAXPLAYERS; j++)
+ adminplayers[j] = netbuffer->u.servercfg.adminplayers[j];
memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
}
@@ -4461,6 +4485,7 @@ static void Local_Maketic(INT32 realtics)
void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle)
{
tic_t tic;
+ UINT8 numadjust = 0;
(void)x;
(void)y;
@@ -4470,7 +4495,21 @@ void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle)
// spawning, but will be applied afterwards.
for (tic = server ? maketic : (neededtic - 1); tic >= gametic; tic--)
+ {
+ if (numadjust++ == BACKUPTICS)
+ {
+ DEBFILE(va("SV_SpawnPlayer: All netcmds for player %d adjusted!\n", playernum));
+ // We already adjusted them all, waste of time doing the same thing over and over
+ // This shouldn't happen normally though, either gametic was 0 (which is handled now anyway)
+ // or maketic >= gametic + BACKUPTICS
+ // -- Monster Iestyn 16/01/18
+ break;
+ }
netcmds[tic%BACKUPTICS][playernum].angleturn = (INT16)((angle>>16) | TICCMD_RECEIVED);
+
+ if (!tic) // failsafe for gametic == 0 -- Monster Iestyn 16/01/18
+ break;
+ }
}
// create missed tic
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 5d0861276..db1e85fd4 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -282,7 +282,7 @@ typedef struct
UINT8 gametype;
UINT8 modifiedgame;
- SINT8 adminplayer; // Needs to be signed
+ SINT8 adminplayers[MAXPLAYERS]; // Needs to be signed
char server_context[8]; // Unique context id, generated at server startup.
@@ -445,6 +445,17 @@ extern consvar_t cv_playbackspeed;
#define KICK_MSG_CUSTOM_KICK 7
#define KICK_MSG_CUSTOM_BAN 8
+typedef enum
+{
+ KR_KICK = 1, //Kicked by server
+ KR_PINGLIMIT = 2, //Broke Ping Limit
+ KR_SYNCH = 3, //Synch Failure
+ KR_TIMEOUT = 4, //Connection Timeout
+ KR_BAN = 5, //Banned by server
+ KR_LEAVE = 6, //Quit the game
+
+} kickreason_t;
+
extern boolean server;
#define client (!server)
extern boolean dedicated; // For dedicated server
diff --git a/src/d_net.c b/src/d_net.c
index 82c60e4ae..cdd63ea32 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -27,6 +27,7 @@
#include "d_clisrv.h"
#include "z_zone.h"
#include "i_tcp.h"
+#include "d_main.h" // srb2home
//
// NETWORKING
@@ -1374,12 +1375,12 @@ boolean D_CheckNetGame(void)
{
k++;
sprintf(filename, "debug%d.txt", k);
- debugfile = fopen(filename, "w");
+ debugfile = fopen(va("%s" PATHSEP "%s", srb2home, filename), "w");
}
if (debugfile)
- CONS_Printf(M_GetText("debug output to: %s\n"), filename);
+ CONS_Printf(M_GetText("debug output to: %s\n"), va("%s" PATHSEP "%s", srb2home, filename));
else
- CONS_Alert(CONS_WARNING, M_GetText("cannot debug output to file %s!\n"), filename);
+ CONS_Alert(CONS_WARNING, M_GetText("cannot debug output to file %s!\n"), va("%s" PATHSEP "%s", srb2home, filename));
}
#endif
#endif
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index a452e0b72..a3d3272b5 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -145,7 +145,9 @@ static void Command_Changepassword_f(void);
static void Command_Login_f(void);
static void Got_Login(UINT8 **cp, INT32 playernum);
static void Got_Verification(UINT8 **cp, INT32 playernum);
+static void Got_Removal(UINT8 **cp, INT32 playernum);
static void Command_Verify_f(void);
+static void Command_RemoveAdmin_f(void);
static void Command_MotD_f(void);
static void Got_MotD_f(UINT8 **cp, INT32 playernum);
@@ -241,7 +243,7 @@ INT32 cv_debug;
consvar_t cv_usemouse = {"use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_usemouse2 = {"use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2, 0, NULL, NULL, 0, 0, NULL};
-#if defined (DC) || defined (_XBOX) || defined (WMINPUT) || defined (_WII) //joystick 1 and 2
+#if defined (DC) || defined (_XBOX) || defined (WMINPUT) || defined (_WII) || defined(HAVE_SDL) || defined(_WINDOWS) //joystick 1 and 2
consvar_t cv_usejoystick = {"use_joystick", "1", CV_SAVE|CV_CALL, usejoystick_cons_t,
I_InitJoystick, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_usejoystick2 = {"use_joystick2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t,
@@ -318,7 +320,6 @@ consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL,
consvar_t cv_rollingdemos = {"rollingdemos", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_timetic = {"timerres", "Normal", CV_SAVE, timetic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // use tics in display
-
static CV_PossibleValue_t pointlimit_cons_t[] = {{0, "MIN"}, {999999990, "MAX"}, {0, NULL}};
consvar_t cv_pointlimit = {"pointlimit", "0", CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t,
PointLimit_OnChange, 0, NULL, NULL, 0, 0, NULL};
@@ -366,7 +367,7 @@ consvar_t cv_sleep = {"cpusleep", "-1", CV_SAVE, sleeping_cons_t, NULL, -1, NULL
INT16 gametype = GT_COOP;
boolean splitscreen = false;
boolean circuitmap = false;
-INT32 adminplayer = -1;
+INT32 adminplayers[MAXPLAYERS];
/// \warning Keep this up-to-date if you add/remove/rename net text commands
const char *netxcmdnames[MAXNETXCMD - 1] =
@@ -428,8 +429,10 @@ void D_RegisterServerCommands(void)
COM_AddCommand("password", Command_Changepassword_f);
RegisterNetXCmd(XD_LOGIN, Got_Login);
COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin
- COM_AddCommand("verify", Command_Verify_f);
+ COM_AddCommand("promote", Command_Verify_f);
RegisterNetXCmd(XD_VERIFIED, Got_Verification);
+ COM_AddCommand("demote", Command_RemoveAdmin_f);
+ RegisterNetXCmd(XD_DEMOTED, Got_Removal);
COM_AddCommand("motd", Command_MotD_f);
RegisterNetXCmd(XD_SETMOTD, Got_MotD_f); // For remote admin
@@ -675,6 +678,8 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_crosshair2);
CV_RegisterVar(&cv_alwaysfreelook);
CV_RegisterVar(&cv_alwaysfreelook2);
+ CV_RegisterVar(&cv_chasefreelook);
+ CV_RegisterVar(&cv_chasefreelook2);
// g_input.c
CV_RegisterVar(&cv_sideaxis);
@@ -1014,7 +1019,7 @@ UINT8 CanChangeSkin(INT32 playernum)
return true;
// Force skin in effect.
- if (client && (cv_forceskin.value != -1) && !(adminplayer == playernum && serverplayer == -1))
+ if (client && (cv_forceskin.value != -1) && !(IsPlayerAdmin(playernum) && serverplayer == -1))
return false;
// Can change skin in intermission and whatnot.
@@ -1165,7 +1170,7 @@ static void SendNameAndColor(void)
snacpending++;
// Don't change name if muted
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
+ if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
else // Cleanup name if changing it
CleanupPlayerName(consoleplayer, cv_playername.zstring);
@@ -1452,7 +1457,12 @@ static void Command_Playdemo_f(void)
CONS_Printf(M_GetText("Playing back demo '%s'.\n"), name);
- G_DoPlayDemo(name);
+ // Internal if no extension, external if one exists
+ // If external, convert the file name to a path in SRB2's home directory
+ if (FIL_CheckExtension(name))
+ G_DoPlayDemo(va("%s"PATHSEP"%s", srb2home, name));
+ else
+ G_DoPlayDemo(name);
}
static void Command_Timedemo_f(void)
@@ -1572,7 +1582,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
mapchangepending = 0;
// spawn the server if needed
// reset players if there is a new one
- if (!(adminplayer == consoleplayer))
+ if (!IsPlayerAdmin(consoleplayer))
{
if (SV_SpawnServer())
buf[0] &= ~(1<<1);
@@ -1630,7 +1640,7 @@ static void Command_Map_f(void)
return;
}
- if (client && !(adminplayer == consoleplayer))
+ if (client && !IsPlayerAdmin(consoleplayer))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -1758,8 +1768,11 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
UINT8 flags;
INT32 resetplayer = 1, lastgametype;
UINT8 skipprecutscene, FLS;
+#ifdef HAVE_BLUA
+ INT16 mapnumber;
+#endif
- if (playernum != serverplayer && playernum != adminplayer)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal map change received from %s\n"), player_names[playernum]);
if (server)
@@ -1819,7 +1832,8 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
}
#ifdef HAVE_BLUA
- LUAh_MapChange();
+ mapnumber = M_MapNumber(mapname[3], mapname[4]);
+ LUAh_MapChange(mapnumber);
#endif
G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene);
@@ -1860,7 +1874,7 @@ static void Command_Pause(void)
else
WRITEUINT8(cp, 0);
- if (cv_pause.value || server || (adminplayer == consoleplayer))
+ if (cv_pause.value || server || (IsPlayerAdmin(consoleplayer)))
{
if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
{
@@ -1878,7 +1892,7 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
UINT8 dedicatedpause = false;
const char *playername;
- if (netgame && !cv_pause.value && playernum != serverplayer && playernum != adminplayer)
+ if (netgame && !cv_pause.value && playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal pause command received from %s\n"), player_names[playernum]);
if (server)
@@ -2007,7 +2021,7 @@ static void Got_RandomSeed(UINT8 **cp, INT32 playernum)
*/
static void Command_Clearscores_f(void)
{
- if (!(server || (adminplayer == consoleplayer)))
+ if (!(server || (IsPlayerAdmin(consoleplayer))))
return;
SendNetXCmd(XD_CLEARSCORES, NULL, 1);
@@ -2027,7 +2041,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum)
INT32 i;
(void)cp;
- if (playernum != serverplayer && playernum != adminplayer)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal clear scores command received from %s\n"), player_names[playernum]);
if (server)
@@ -2248,7 +2262,7 @@ static void Command_ServerTeamChange_f(void)
UINT16 usvalue;
NetPacket.value.l = NetPacket.value.b = 0;
- if (!(server || (adminplayer == consoleplayer)))
+ if (!(server || (IsPlayerAdmin(consoleplayer))))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -2395,7 +2409,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
if (NetPacket.packet.verification) // Special marker that the server sent the request
{
- if (playernum != serverplayer && (playernum != adminplayer))
+ if (playernum != serverplayer && (!IsPlayerAdmin(playernum)))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
if (server)
@@ -2434,7 +2448,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
}
else
{
- if (playernum != serverplayer && (playernum != adminplayer))
+ if (playernum != serverplayer && (!IsPlayerAdmin(playernum)))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
if (server)
@@ -2703,6 +2717,12 @@ static void Command_Login_f(void)
XBOXSTATIC UINT8 finalmd5[16];
const char *pw;
+ if (!netgame)
+ {
+ CONS_Printf(M_GetText("This only works in a netgame.\n"));
+ return;
+ }
+
// If the server uses login, it will effectively just remove admin privileges
// from whoever has them. This is good.
if (COM_Argc() != 2)
@@ -2751,13 +2771,56 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
if (!memcmp(sentmd5, finalmd5, 16))
{
CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[playernum]);
- COM_BufInsertText(va("verify %d\n", playernum)); // do this immediately
+ COM_BufInsertText(va("promote %d\n", playernum)); // do this immediately
}
else
CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[playernum]);
#endif
}
+boolean IsPlayerAdmin(INT32 playernum)
+{
+ INT32 i;
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playernum == adminplayers[i])
+ return true;
+
+ return false;
+}
+
+void SetAdminPlayer(INT32 playernum)
+{
+ INT32 i;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playernum == adminplayers[i])
+ return; // Player is already admin
+
+ if (adminplayers[i] == -1)
+ {
+ adminplayers[i] = playernum; // Set the player to a free spot
+ break; // End the loop now. If it keeps going, the same player might get assigned to two slots.
+ }
+
+
+ }
+}
+
+void ClearAdminPlayers(void)
+{
+ INT32 i;
+ for (i = 0; i < MAXPLAYERS; i++)
+ adminplayers[i] = -1;
+}
+
+void RemoveAdminPlayer(INT32 playernum)
+{
+ INT32 i;
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playernum == adminplayers[i])
+ adminplayers[i] = -1;
+}
+
static void Command_Verify_f(void)
{
XBOXSTATIC char buf[8]; // Should be plenty
@@ -2770,9 +2833,15 @@ static void Command_Verify_f(void)
return;
}
+ if (!netgame)
+ {
+ CONS_Printf(M_GetText("This only works in a netgame.\n"));
+ return;
+ }
+
if (COM_Argc() != 2)
{
- CONS_Printf(M_GetText("verify : give admin privileges to a node\n"));
+ CONS_Printf(M_GetText("promote : give admin privileges to a node\n"));
return;
}
@@ -2806,7 +2875,7 @@ static void Got_Verification(UINT8 **cp, INT32 playernum)
return;
}
- adminplayer = num;
+ SetAdminPlayer(num);
if (num != consoleplayer)
return;
@@ -2814,6 +2883,62 @@ static void Got_Verification(UINT8 **cp, INT32 playernum)
CONS_Printf(M_GetText("You are now a server administrator.\n"));
}
+static void Command_RemoveAdmin_f(void)
+{
+ XBOXSTATIC char buf[8]; // Should be plenty
+ char *temp;
+ INT32 playernum;
+
+ if (client)
+ {
+ CONS_Printf(M_GetText("Only the server can use this.\n"));
+ return;
+ }
+
+ if (COM_Argc() != 2)
+ {
+ CONS_Printf(M_GetText("demote : remove admin privileges from a node\n"));
+ return;
+ }
+
+ strlcpy(buf, COM_Argv(1), sizeof(buf));
+
+ playernum = atoi(buf);
+
+ temp = buf;
+
+ WRITEUINT8(temp, playernum);
+
+ if (playeringame[playernum])
+ SendNetXCmd(XD_DEMOTED, buf, 1);
+}
+
+static void Got_Removal(UINT8 **cp, INT32 playernum)
+{
+ INT16 num = READUINT8(*cp);
+
+ if (playernum != serverplayer) // it's not from the server (hacker or bug)
+ {
+ CONS_Alert(CONS_WARNING, M_GetText("Illegal demotion received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]);
+ if (server)
+ {
+ XBOXSTATIC UINT8 buf[2];
+
+ buf[0] = (UINT8)playernum;
+ buf[1] = KICK_MSG_CON_FAIL;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ }
+ return;
+ }
+
+ RemoveAdminPlayer(num);
+
+ if (num != consoleplayer)
+ return;
+
+ CONS_Printf(M_GetText("You are no longer a server administrator.\n"));
+}
+
static void Command_MotD_f(void)
{
size_t i, j;
@@ -2825,7 +2950,7 @@ static void Command_MotD_f(void)
return;
}
- if (!(server || (adminplayer == consoleplayer)))
+ if (!(server || (IsPlayerAdmin(consoleplayer))))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -2849,7 +2974,7 @@ static void Command_MotD_f(void)
}
if ((netgame || multiplayer) && client)
- SendNetXCmd(XD_SETMOTD, mymotd, sizeof(motd));
+ SendNetXCmd(XD_SETMOTD, mymotd, i); // send the actual size of the motd string, not the full buffer's size
else
{
strcpy(motd, mymotd);
@@ -2872,7 +2997,7 @@ static void Got_MotD_f(UINT8 **cp, INT32 playernum)
if (!isprint(mymotd[i]) || mymotd[i] == ';')
kick = true;
- if ((playernum != serverplayer && playernum != adminplayer) || kick)
+ if ((playernum != serverplayer && !IsPlayerAdmin(playernum)) || kick)
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal motd change received from %s\n"), player_names[playernum]);
if (server)
@@ -2909,7 +3034,7 @@ static void Command_RunSOC(void)
else
fn = COM_Argv(1);
- if (netgame && !(server || consoleplayer == adminplayer))
+ if (netgame && !(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -2935,7 +3060,7 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum)
char filename[256];
filestatus_t ncs = FS_NOTFOUND;
- if (playernum != serverplayer && playernum != adminplayer)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal runsoc command received from %s\n"), player_names[playernum]);
if (server)
@@ -3006,7 +3131,7 @@ static void Command_Addfile(void)
if (!musiconly)
{
// ... But only so long as they contain nothing more then music and sprites.
- if (netgame && !(server || adminplayer == consoleplayer))
+ if (netgame && !(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -3017,7 +3142,7 @@ static void Command_Addfile(void)
// Add file on your client directly if it is trivial, or you aren't in a netgame.
if (!(netgame || multiplayer) || musiconly)
{
- P_AddWadFile(fn, NULL);
+ P_AddWadFile(fn);
return;
}
@@ -3068,7 +3193,7 @@ static void Command_Addfile(void)
WRITEMEM(buf_p, md5sum, 16);
}
- if (adminplayer == consoleplayer) // Request to add file
+ if (IsPlayerAdmin(consoleplayer) && (!server)) // Request to add file
SendNetXCmd(XD_REQADDFILE, buf, buf_p - buf);
else
SendNetXCmd(XD_ADDFILE, buf, buf_p - buf);
@@ -3117,7 +3242,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
UINT8 md5sum[16];
boolean kick = false;
boolean toomany = false;
- INT32 i;
+ INT32 i,j;
size_t packetsize = 0;
serverinfo_pak *dummycheck = NULL;
@@ -3136,7 +3261,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
if (!isprint(filename[i]) || filename[i] == ';')
kick = true;
- if ((playernum != serverplayer && playernum != adminplayer) || kick)
+ if ((playernum != serverplayer && !IsPlayerAdmin(playernum)) || kick)
{
XBOXSTATIC UINT8 buf[2];
@@ -3170,8 +3295,9 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
CONS_Printf("%s",message);
- if (adminplayer)
- COM_BufAddText(va("sayto %d %s", adminplayer, message));
+ for (j = 0; j < MAXPLAYERS; j++)
+ if (adminplayers[j])
+ COM_BufAddText(va("sayto %d %s", adminplayers[j], message));
return;
}
@@ -3231,7 +3357,7 @@ static void Got_Addfilecmd(UINT8 **cp, INT32 playernum)
ncs = findfile(filename,md5sum,true);
- if (ncs != FS_FOUND || !P_AddWadFile(filename, NULL))
+ if (ncs != FS_FOUND || !P_AddWadFile(filename))
{
Command_ExitGame_f();
if (ncs == FS_FOUND)
@@ -3286,10 +3412,51 @@ static void Command_ListWADS_f(void)
static void Command_Version_f(void)
{
#ifdef DEVELOP
- CONS_Printf("Sonic Robo Blast 2 %s-%s (%s %s)\n", compbranch, comprevision, compdate, comptime);
+ CONS_Printf("Sonic Robo Blast 2 %s-%s (%s %s) ", compbranch, comprevision, compdate, comptime);
#else
- CONS_Printf("Sonic Robo Blast 2 %s (%s %s %s)\n", VERSIONSTRING, compdate, comptime, comprevision);
+ CONS_Printf("Sonic Robo Blast 2 %s (%s %s %s) ", VERSIONSTRING, compdate, comptime, comprevision);
#endif
+
+ // Base library
+#if defined( HAVE_SDL)
+ CONS_Printf("SDL ");
+#elif defined(_WINDOWS)
+ CONS_Printf("DD ");
+#endif
+
+ // OS
+ // Would be nice to use SDL_GetPlatform for this
+#if defined (_WIN32) || defined (_WIN64)
+ CONS_Printf("Windows ");
+#elif defined(__linux__)
+ CONS_Printf("Linux ");
+#elif defined(MACOSX)
+ CONS_Printf("macOS" );
+#elif defined(UNIXCOMMON)
+ CONS_Printf("Unix (Common) ");
+#else
+ CONS_Printf("Other OS ");
+#endif
+
+ // Bitness
+ if (sizeof(void*) == 4)
+ CONS_Printf("32-bit ");
+ else if (sizeof(void*) == 8)
+ CONS_Printf("64-bit ");
+ else // 16-bit? 128-bit?
+ CONS_Printf("Bits Unknown ");
+
+ // No ASM?
+#ifdef NOASM
+ CONS_Printf("\x85" "NOASM " "\x80");
+#endif
+
+ // Debug build
+#ifdef _DEBUG
+ CONS_Printf("\x85" "DEBUG " "\x80");
+#endif
+
+ CONS_Printf("\n");
}
#ifdef UPDATE_ALERT
@@ -3568,7 +3735,7 @@ void D_GameTypeChanged(INT32 lastgametype)
if (playeringame[i])
players[i].ctfteam = 0;
- if (server || (adminplayer == consoleplayer))
+ if (server || (IsPlayerAdmin(consoleplayer)))
{
CV_StealthSetValue(&cv_teamscramble, 0);
teamscramble = 0;
@@ -3651,7 +3818,7 @@ static void TeamScramble_OnChange(void)
if (!cv_teamscramble.value)
teamscramble = 0;
- if (!G_GametypeHasTeams() && (server || consoleplayer == adminplayer))
+ if (!G_GametypeHasTeams() && (server || IsPlayerAdmin(consoleplayer)))
{
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
CV_StealthSetValue(&cv_teamscramble, 0);
@@ -3830,7 +3997,7 @@ static void Command_ExitLevel_f(void)
{
if (!(netgame || (multiplayer && gametype != GT_COOP)) && !cv_debug)
CONS_Printf(M_GetText("This only works in a netgame.\n"));
- else if (!(server || (adminplayer == consoleplayer)))
+ else if (!(server || (IsPlayerAdmin(consoleplayer))))
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
else if (gamestate != GS_LEVEL || demoplayback)
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
@@ -3846,7 +4013,7 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
if (gameaction == ga_completed)
return;
- if (playernum != serverplayer && playernum != adminplayer)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal exitlevel command received from %s\n"), player_names[playernum]);
if (server)
@@ -3954,7 +4121,7 @@ static void Command_Cheats_f(void)
{
if (COM_CheckParm("off"))
{
- if (!(server || (adminplayer == consoleplayer)))
+ if (!(server || (IsPlayerAdmin(consoleplayer))))
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
else
CV_ResetCheatNetVars();
@@ -3964,7 +4131,7 @@ static void Command_Cheats_f(void)
if (CV_CheatsEnabled())
{
CONS_Printf(M_GetText("At least one CHEAT-marked variable has been changed -- Cheats are enabled.\n"));
- if (server || (adminplayer == consoleplayer))
+ if (server || (IsPlayerAdmin(consoleplayer)))
CONS_Printf(M_GetText("Type CHEATS OFF to reset all cheat variables to default.\n"));
}
else
@@ -4033,7 +4200,7 @@ static void Command_Archivetest_f(void)
*/
static void ForceSkin_OnChange(void)
{
- if ((server || adminplayer == consoleplayer) && (cv_forceskin.value < -1 || cv_forceskin.value >= numskins))
+ if ((server || IsPlayerAdmin(consoleplayer)) && (cv_forceskin.value < -1 || cv_forceskin.value >= numskins))
{
if (cv_forceskin.value == -2)
CV_SetValue(&cv_forceskin, numskins-1);
@@ -4063,7 +4230,7 @@ static void ForceSkin_OnChange(void)
//Allows the player's name to be changed if cv_mute is off.
static void Name_OnChange(void)
{
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
+ if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
@@ -4186,7 +4353,7 @@ static void Color2_OnChange(void)
*/
static void Mute_OnChange(void)
{
- if (server || (adminplayer == consoleplayer))
+ if (server || (IsPlayerAdmin(consoleplayer)))
return;
if (cv_mute.value)
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index 09bde4e5b..b82065c82 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -133,9 +133,10 @@ typedef enum
XD_DELFILE, // 18
XD_SETMOTD, // 19
XD_SUICIDE, // 20
+ XD_DEMOTED, // 21
#ifdef HAVE_BLUA
- XD_LUACMD, // 21
- XD_LUAVAR, // 22
+ XD_LUACMD, // 22
+ XD_LUAVAR, // 23
#endif
MAXNETXCMD
} netxcmd_t;
@@ -190,6 +191,10 @@ void Command_ExitGame_f(void);
void Command_Retry_f(void);
void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pultmode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect);
+boolean IsPlayerAdmin(INT32 playernum);
+void SetAdminPlayer(INT32 playernum);
+void ClearAdminPlayers(void);
+void RemoveAdminPlayer(INT32 playernum);
void ItemFinder_OnChange(void);
void D_SetPassword(const char *pw);
diff --git a/src/d_netfil.c b/src/d_netfil.c
index 1cc789ee7..deb04fbe1 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -425,7 +425,7 @@ void CL_LoadServerFiles(void)
continue; // Already loaded
else if (fileneeded[i].status == FS_FOUND)
{
- P_AddWadFile(fileneeded[i].filename, NULL);
+ P_AddWadFile(fileneeded[i].filename);
G_SetGameModified(true);
fileneeded[i].status = FS_OPEN;
}
diff --git a/src/dehacked.c b/src/dehacked.c
index 7f549ce8e..db1d6eed8 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -32,6 +32,7 @@
#include "fastcmp.h"
#include "lua_script.h"
#include "lua_hook.h"
+#include "d_clisrv.h"
#include "m_cond.h"
@@ -1035,7 +1036,10 @@ static void readlevelheader(MYFILE *f, INT32 num)
// Get the part before the " = "
tmp = strchr(s, '=');
- *(tmp-1) = '\0';
+ if (tmp)
+ *(tmp-1) = '\0';
+ else
+ break;
strupr(word);
// Now get the part after
@@ -1616,7 +1620,10 @@ static void readhuditem(MYFILE *f, INT32 num)
// Get the part before the " = "
tmp = strchr(s, '=');
- *(tmp-1) = '\0';
+ if (tmp)
+ *(tmp-1) = '\0';
+ else
+ break;
strupr(word);
// Now get the part after
@@ -1860,7 +1867,6 @@ static void readframe(MYFILE *f, INT32 num)
char *word1;
char *word2 = NULL;
char *tmp;
- INT32 j;
do
{
@@ -1875,16 +1881,6 @@ static void readframe(MYFILE *f, INT32 num)
if (s == tmp)
continue; // Skip comment lines, but don't break.
- for (j = 0; s[j] != '\n'; j++)
- {
- if (s[j] == '=')
- {
- j += 2;
- j = atoi(&s[j]);
- break;
- }
- }
-
word1 = strtok(s, " ");
if (word1)
strupr(word1);
@@ -2129,7 +2125,10 @@ static void reademblemdata(MYFILE *f, INT32 num)
// Get the part before the " = "
tmp = strchr(s, '=');
- *(tmp-1) = '\0';
+ if (tmp)
+ *(tmp-1) = '\0';
+ else
+ break;
strupr(word);
// Now get the part after
@@ -2269,7 +2268,10 @@ static void readextraemblemdata(MYFILE *f, INT32 num)
// Get the part before the " = "
tmp = strchr(s, '=');
- *(tmp-1) = '\0';
+ if (tmp)
+ *(tmp-1) = '\0';
+ else
+ break;
strupr(word);
// Now get the part after
@@ -2353,7 +2355,10 @@ static void readunlockable(MYFILE *f, INT32 num)
// Get the part before the " = "
tmp = strchr(s, '=');
- *(tmp-1) = '\0';
+ if (tmp)
+ *(tmp-1) = '\0';
+ else
+ break;
strupr(word);
// Now get the part after
@@ -2640,7 +2645,10 @@ static void readconditionset(MYFILE *f, UINT8 setnum)
// Get the part before the " = "
tmp = strchr(s, '=');
- *(tmp-1) = '\0';
+ if (tmp)
+ *(tmp-1) = '\0';
+ else
+ break;
strupr(word);
// Now get the part after
@@ -2885,7 +2893,10 @@ static void readmaincfg(MYFILE *f)
// Get the part before the " = "
tmp = strchr(s, '=');
- *(tmp-1) = '\0';
+ if (tmp)
+ *(tmp-1) = '\0';
+ else
+ break;
strupr(word);
// Now get the part after
@@ -3069,7 +3080,7 @@ static void readmaincfg(MYFILE *f)
strncpy(timeattackfolder, gamedatafilename, min(filenamelen, sizeof (timeattackfolder)));
timeattackfolder[min(filenamelen, sizeof (timeattackfolder) - 1)] = '\0';
- strncpy(savegamename, timeattackfolder, strlen(timeattackfolder));
+ strcpy(savegamename, timeattackfolder);
strlcat(savegamename, "%u.ssg", sizeof(savegamename));
// can't use sprintf since there is %u in savegamename
strcatbf(savegamename, srb2home, PATHSEP);
@@ -3124,7 +3135,10 @@ static void readwipes(MYFILE *f)
// Get the part before the " = "
tmp = strchr(s, '=');
- *(tmp-1) = '\0';
+ if (tmp)
+ *(tmp-1) = '\0';
+ else
+ break;
strupr(word);
// Now get the part after
@@ -7272,6 +7286,14 @@ struct {
{"FF_COLORMAPONLY",FF_COLORMAPONLY}, ///< Only copy the colormap, not the lightlevel
{"FF_GOOWATER",FF_GOOWATER}, ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop.
+#ifdef ESLOPE
+ // Slope flags
+ {"SL_NOPHYSICS",SL_NOPHYSICS}, // Don't do momentum adjustment with this slope
+ {"SL_NODYNAMIC",SL_NODYNAMIC}, // Slope will never need to move during the level, so don't fuss with recalculating it
+ {"SL_ANCHORVERTEX",SL_ANCHORVERTEX},// Slope is using a Slope Vertex Thing to anchor its position
+ {"SL_VERTEXSLOPE",SL_VERTEXSLOPE}, // Slope is built from three Slope Vertex Things
+#endif
+
// Angles
{"ANG1",ANG1},
{"ANG2",ANG2},
@@ -7395,6 +7417,14 @@ struct {
{"V_CHARCOLORSHIFT",V_CHARCOLORSHIFT},
{"V_ALPHASHIFT",V_ALPHASHIFT},
+
+ //Kick Reasons
+ {"KR_KICK",KR_KICK},
+ {"KR_PINGLIMIT",KR_PINGLIMIT},
+ {"KR_SYNCH",KR_SYNCH},
+ {"KR_TIMEOUT",KR_TIMEOUT},
+ {"KR_BAN",KR_BAN},
+ {"KR_LEAVE",KR_LEAVE},
#endif
{NULL,0}
@@ -8204,6 +8234,9 @@ static inline int lib_getenum(lua_State *L)
} else if (fastcmp(word,"maptol")) {
lua_pushinteger(L, maptol);
return 1;
+ } else if (fastcmp(word,"ultimatemode")) {
+ lua_pushboolean(L, ultimatemode != 0);
+ return 1;
} else if (fastcmp(word,"mariomode")) {
lua_pushboolean(L, mariomode != 0);
return 1;
@@ -8269,10 +8302,10 @@ static inline int lib_getenum(lua_State *L)
return 0;
LUA_PushUserdata(L, &players[serverplayer], META_PLAYER);
return 1;
- } else if (fastcmp(word,"admin")) {
- if (!playeringame[adminplayer] || adminplayer == serverplayer)
+ } else if (fastcmp(word,"admin")) { // BACKWARDS COMPATIBILITY HACK: This was replaced with IsPlayerAdmin(), but some 2.1 Lua scripts still use the admin variable. It now points to the first admin player in the array.
+ if (!playeringame[adminplayers[0]] || IsPlayerAdmin(serverplayer))
return 0;
- LUA_PushUserdata(L, &players[adminplayer], META_PLAYER);
+ LUA_PushUserdata(L, &players[adminplayers[0]], META_PLAYER);
return 1;
} else if (fastcmp(word,"emeralds")) {
lua_pushinteger(L, emeralds);
diff --git a/src/doomdef.h b/src/doomdef.h
index 470fa20bb..796221c91 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -150,9 +150,9 @@ extern FILE *logstream;
// we use comprevision and compbranch instead.
#else
#define VERSION 201 // Game version
-#define SUBVERSION 20 // more precise version number
-#define VERSIONSTRING "v2.1.20"
-#define VERSIONSTRINGW L"v2.1.20"
+#define SUBVERSION 21 // more precise version number
+#define VERSIONSTRING "v2.1.21"
+#define VERSIONSTRINGW L"v2.1.21"
// Hey! If you change this, add 1 to the MODVERSION below!
// Otherwise we can't force updates!
#endif
@@ -214,7 +214,7 @@ extern FILE *logstream;
// it's only for detection of the version the player is using so the MS can alert them of an update.
// Only set it higher, not lower, obviously.
// Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1".
-#define MODVERSION 25
+#define MODVERSION 26
// =========================================================================
diff --git a/src/doomstat.h b/src/doomstat.h
index 52da2ae2c..7b4aa2644 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -476,7 +476,8 @@ extern consvar_t cv_timetic; // display high resolution timer
extern consvar_t cv_forceskin; // force clients to use the server's skin
extern consvar_t cv_downloading; // allow clients to downloading WADs.
extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
-extern INT32 adminplayer, serverplayer;
+extern INT32 serverplayer;
+extern INT32 adminplayers[MAXPLAYERS];
/// \note put these in d_clisrv outright?
diff --git a/src/f_finale.c b/src/f_finale.c
index 7662ac061..484a0afe6 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -1739,7 +1739,7 @@ static void F_AdvanceToNextScene(void)
void F_EndCutScene(void)
{
- cutsceneover = true; // do this first, just in case Y_EndGame or something wants to turn it back false later
+ cutsceneover = true; // do this first, just in case G_EndGame or something wants to turn it back false later
if (runningprecutscene)
{
if (server)
@@ -1754,7 +1754,7 @@ void F_EndCutScene(void)
else if (nextmap < 1100-1)
G_NextLevel();
else
- Y_EndGame();
+ G_EndGame();
}
}
@@ -1858,7 +1858,7 @@ void F_CutsceneTicker(void)
for (i = 0; i < MAXPLAYERS; i++)
{
- if (netgame && i != serverplayer && i != adminplayer)
+ if (netgame && i != serverplayer && !IsPlayerAdmin(i))
continue;
if (players[i].cmd.buttons & BT_USE)
diff --git a/src/g_game.c b/src/g_game.c
index 4a7719fa0..5cc78d4b6 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -350,11 +350,13 @@ static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"},
consvar_t cv_crosshair = {"crosshair", "Cross", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_crosshair2 = {"crosshair2", "Cross", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_invertmouse = {"invertmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_alwaysfreelook = {"alwaysmlook", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_invertmouse2 = {"invertmouse2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_alwaysfreelook2 = {"alwaysmlook2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_mousemove = {"mousemove", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_mousemove2 = {"mousemove2", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_alwaysfreelook = {"alwaysmlook", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_alwaysfreelook2 = {"alwaysmlook2", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_chasefreelook = {"chasemlook", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_chasefreelook2 = {"chasemlook2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mousemove = {"mousemove", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mousemove2 = {"mousemove2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_analog = {"analog", "Off", CV_CALL, CV_OnOff, Analog_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_analog2 = {"analog2", "Off", CV_CALL, CV_OnOff, Analog2_OnChange, 0, NULL, NULL, 0, 0, NULL};
#ifdef DC
@@ -385,7 +387,7 @@ consvar_t cv_lookaxis = {"joyaxis_look", "RStick.Y", CV_SAVE, joyaxis_cons_t, NU
consvar_t cv_fireaxis = {"joyaxis_fire", "LAnalog", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_firenaxis = {"joyaxis_firenormal", "RAnalog", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#else
-consvar_t cv_turnaxis = {"joyaxis_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_turnaxis = {"joyaxis_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#ifdef PSP
consvar_t cv_moveaxis = {"joyaxis_move", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#else
@@ -399,17 +401,17 @@ consvar_t cv_lookaxis = {"joyaxis_look", "Alt Y-Axis", CV_SAVE, joyaxis_cons_t,
#elif defined (PSP)
consvar_t cv_sideaxis = {"joyaxis_side", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#else
-consvar_t cv_sideaxis = {"joyaxis_side", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_sideaxis = {"joyaxis_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
#ifndef _XBOX
#ifdef PSP
consvar_t cv_lookaxis = {"joyaxis_look", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#else
-consvar_t cv_lookaxis = {"joyaxis_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_lookaxis = {"joyaxis_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
#endif
-consvar_t cv_fireaxis = {"joyaxis_fire", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_firenaxis = {"joyaxis_firenormal", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_fireaxis = {"joyaxis_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_firenaxis = {"joyaxis_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
#if defined (_WII) || defined (WMINPUT)
@@ -420,7 +422,7 @@ consvar_t cv_lookaxis2 = {"joyaxis2_look", "RStick.Y", CV_SAVE, joyaxis_cons_t,
consvar_t cv_fireaxis2 = {"joyaxis2_fire", "LAnalog", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_firenaxis2 = {"joyaxis2_firenormal", "RAnalog", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#else
-consvar_t cv_turnaxis2 = {"joyaxis2_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_turnaxis2 = {"joyaxis2_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_moveaxis2 = {"joyaxis2_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#ifdef _arch_dreamcast
consvar_t cv_sideaxis2 = {"joyaxis2_side", "Triggers", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -430,13 +432,13 @@ consvar_t cv_lookaxis2 = {"joyaxis2_look", "Alt Y-Axis", CV_SAVE, joyaxis_cons_t
#elif defined (_PSP)
consvar_t cv_sideaxis2 = {"joyaxis2_side", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#else
-consvar_t cv_sideaxis2 = {"joyaxis2_side", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_sideaxis2 = {"joyaxis2_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
#ifndef _XBOX
-consvar_t cv_lookaxis2 = {"joyaxis2_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_lookaxis2 = {"joyaxis2_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
-consvar_t cv_fireaxis2 = {"joyaxis2_fire", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_firenaxis2 = {"joyaxis2_firenormal", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_fireaxis2 = {"joyaxis2_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_firenaxis2 = {"joyaxis2_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
@@ -958,13 +960,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
INT32 tspeed, forward, side, axis, i;
const INT32 speed = 1;
// these ones used for multiple conditions
- boolean turnleft, turnright, mouseaiming, analogjoystickmove, gamepadjoystickmove;
+ boolean turnleft, turnright, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming;
player_t *player = &players[consoleplayer];
camera_t *thiscam = &camera;
static INT32 turnheld; // for accelerative turning
static boolean keyboard_look; // true if lookup/down using keyboard
static boolean resetdown; // don't cam reset every frame
+ static boolean joyaiming; // check the last frame's value if we need to reset the camera
G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver
@@ -979,10 +982,18 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
turnright = PLAYER1INPUTDOWN(gc_turnright);
turnleft = PLAYER1INPUTDOWN(gc_turnleft);
- mouseaiming = (PLAYER1INPUTDOWN(gc_mouseaiming)) ^ cv_alwaysfreelook.value;
+ mouseaiming = (PLAYER1INPUTDOWN(gc_mouseaiming)) ^
+ (cv_chasecam.value ? cv_chasefreelook.value : cv_alwaysfreelook.value);
analogjoystickmove = cv_usejoystick.value && !Joystick.bGamepadStyle;
gamepadjoystickmove = cv_usejoystick.value && Joystick.bGamepadStyle;
+ thisjoyaiming = (cv_chasecam.value) ? cv_chasefreelook.value : cv_alwaysfreelook.value;
+
+ // Reset the vertical look if we're no longer joyaiming
+ if (!thisjoyaiming && joyaiming)
+ localaiming = 0;
+ joyaiming = thisjoyaiming;
+
axis = JoyAxis(AXISTURN);
if (gamepadjoystickmove && axis != 0)
{
@@ -1158,11 +1169,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
}
axis = JoyAxis(AXISLOOK);
- if (analogjoystickmove && axis != 0 && cv_lookaxis.value != 0)
+ if (analogjoystickmove && joyaiming && axis != 0 && cv_lookaxis.value != 0)
localaiming += (axis<<16) * screen_invert;
// spring back if not using keyboard neither mouselookin'
- if (!keyboard_look && cv_lookaxis.value == 0 && !mouseaiming)
+ if (!keyboard_look && cv_lookaxis.value == 0 && !joyaiming && !mouseaiming)
localaiming = 0;
if (PLAYER1INPUTDOWN(gc_lookup) || (gamepadjoystickmove && axis < 0))
@@ -1249,13 +1260,14 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
INT32 tspeed, forward, side, axis, i;
const INT32 speed = 1;
// these ones used for multiple conditions
- boolean turnleft, turnright, mouseaiming, analogjoystickmove, gamepadjoystickmove;
+ boolean turnleft, turnright, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming;
player_t *player = &players[secondarydisplayplayer];
camera_t *thiscam = (player->bot == 2 ? &camera : &camera2);
static INT32 turnheld; // for accelerative turning
static boolean keyboard_look; // true if lookup/down using keyboard
static boolean resetdown; // don't cam reset every frame
+ static boolean joyaiming; // check the last frame's value if we need to reset the camera
G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver
@@ -1270,10 +1282,18 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
turnright = PLAYER2INPUTDOWN(gc_turnright);
turnleft = PLAYER2INPUTDOWN(gc_turnleft);
- mouseaiming = (PLAYER2INPUTDOWN(gc_mouseaiming)) ^ cv_alwaysfreelook2.value;
+ mouseaiming = (PLAYER2INPUTDOWN(gc_mouseaiming)) ^
+ (cv_chasecam2.value ? cv_chasefreelook2.value : cv_alwaysfreelook2.value);
analogjoystickmove = cv_usejoystick2.value && !Joystick2.bGamepadStyle;
gamepadjoystickmove = cv_usejoystick2.value && Joystick2.bGamepadStyle;
+ thisjoyaiming = (cv_chasecam2.value) ? cv_chasefreelook2.value : cv_alwaysfreelook2.value;
+
+ // Reset the vertical look if we're no longer joyaiming
+ if (!thisjoyaiming && joyaiming)
+ localaiming2 = 0;
+ joyaiming = thisjoyaiming;
+
axis = Joy2Axis(AXISTURN);
if (gamepadjoystickmove && axis != 0)
{
@@ -1446,11 +1466,11 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
}
axis = Joy2Axis(AXISLOOK);
- if (analogjoystickmove && axis != 0 && cv_lookaxis2.value != 0)
+ if (analogjoystickmove && joyaiming && axis != 0 && cv_lookaxis2.value != 0)
localaiming2 += (axis<<16) * screen_invert;
// spring back if not using keyboard neither mouselookin'
- if (!keyboard_look && cv_lookaxis2.value == 0 && !mouseaiming)
+ if (!keyboard_look && cv_lookaxis2.value == 0 && !joyaiming && !mouseaiming)
localaiming2 = 0;
if (PLAYER2INPUTDOWN(gc_lookup) || (gamepadjoystickmove && axis < 0))
@@ -1673,7 +1693,8 @@ static INT32 camtoggledelay, camtoggledelay2 = 0;
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_F12)
+ if (gamestate == GS_LEVEL && ev->type == ev_keydown
+ && (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1]))
{
if (splitscreen || !netgame)
displayplayer = consoleplayer;
@@ -1814,7 +1835,8 @@ boolean G_Responder(event_t *ev)
{
case ev_keydown:
if (ev->data1 == gamecontrol[gc_pause][0]
- || ev->data1 == gamecontrol[gc_pause][1])
+ || ev->data1 == gamecontrol[gc_pause][1]
+ || ev->data1 == KEY_PAUSE)
{
if (!pausedelay)
{
@@ -2547,14 +2569,12 @@ void G_DoReborn(INT32 playernum)
}
}
else
-#ifdef HAVE_BLUA
{
- LUAh_MapChange();
+#ifdef HAVE_BLUA
+ LUAh_MapChange(gamemap);
#endif
G_DoLoadLevel(true);
-#ifdef HAVE_BLUA
}
-#endif
}
else
{
@@ -2605,7 +2625,7 @@ void G_ExitLevel(void)
CONS_Printf(M_GetText("The round has ended.\n"));
// Remove CEcho text on round end.
- HU_DoCEcho("");
+ HU_ClearCEcho();
}
}
@@ -2901,7 +2921,7 @@ void G_AfterIntermission(void)
if (nextmap < 1100-1)
G_NextLevel();
else
- Y_EndGame();
+ G_EndGame();
}
}
@@ -2987,6 +3007,38 @@ static void G_DoContinued(void)
gameaction = ga_nothing;
}
+//
+// G_EndGame (formerly Y_EndGame)
+// Frankly this function fits better in g_game.c than it does in y_inter.c
+//
+// ...Gee, (why) end the game?
+// Because G_AfterIntermission and F_EndCutscene would
+// both do this exact same thing *in different ways* otherwise,
+// which made it so that you could only unlock Ultimate mode
+// if you had a cutscene after the final level and crap like that.
+// This function simplifies it so only one place has to be updated
+// when something new is added.
+void G_EndGame(void)
+{
+ // Only do evaluation and credits in coop games.
+ if (gametype == GT_COOP)
+ {
+ if (nextmap == 1102-1) // end game with credits
+ {
+ F_StartCredits();
+ return;
+ }
+ if (nextmap == 1101-1) // end game with evaluation
+ {
+ F_StartGameEvaluation();
+ return;
+ }
+ }
+
+ // 1100 or competitive multiplayer, so go back to title screen.
+ D_StartTitle();
+}
+
//
// G_LoadGameSettings
//
@@ -5091,7 +5143,7 @@ void G_DoPlayDemo(char *defdemoname)
demo_start = false;
#ifdef HAVE_BLUA
- LUAh_MapChange();
+ LUAh_MapChange(gamemap);
#endif
displayplayer = consoleplayer = 0;
memset(playeringame,0,sizeof(playeringame));
diff --git a/src/g_game.h b/src/g_game.h
index e33d13661..891e7b3ee 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -55,8 +55,8 @@ extern INT16 rw_maximums[NUM_WEAPONS];
// used in game menu
extern consvar_t cv_crosshair, cv_crosshair2;
-extern consvar_t cv_invertmouse, cv_alwaysfreelook, cv_mousemove;
-extern consvar_t cv_invertmouse2, cv_alwaysfreelook2, cv_mousemove2;
+extern consvar_t cv_invertmouse, cv_alwaysfreelook, cv_chasefreelook, cv_mousemove;
+extern consvar_t cv_invertmouse2, cv_alwaysfreelook2, cv_chasefreelook2, cv_mousemove2;
extern consvar_t cv_useranalog, cv_useranalog2;
extern consvar_t cv_analog, cv_analog2;
extern consvar_t cv_sideaxis,cv_turnaxis,cv_moveaxis,cv_lookaxis,cv_fireaxis,cv_firenaxis;
@@ -174,6 +174,7 @@ void G_NextLevel(void);
void G_Continue(void);
void G_UseContinue(void);
void G_AfterIntermission(void);
+void G_EndGame(void); // moved from y_inter.c/h and renamed
void G_Ticker(boolean run);
boolean G_Responder(event_t *ev);
diff --git a/src/g_input.c b/src/g_input.c
index 09ea336ac..44d9f2b28 100644
--- a/src/g_input.c
+++ b/src/g_input.c
@@ -25,10 +25,10 @@ static CV_PossibleValue_t mousesens_cons_t[] = {{1, "MIN"}, {MAXMOUSESENSITIVITY
static CV_PossibleValue_t onecontrolperkey_cons_t[] = {{1, "One"}, {2, "Several"}, {0, NULL}};
// mouse values are used once
-consvar_t cv_mousesens = {"mousesens", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_mousesens2 = {"mousesens2", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_mouseysens = {"mouseysens", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_mouseysens2 = {"mouseysens2", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mousesens = {"mousesens", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mousesens2 = {"mousesens2", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mouseysens = {"mouseysens", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mouseysens2 = {"mouseysens2", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_controlperkey = {"controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
INT32 mousex, mousey;
@@ -990,6 +990,10 @@ static const char *gamecontrolname[num_gamecontrols] =
"jump",
"console",
"pause",
+ "systemmenu",
+ "screenshot",
+ "recordgif",
+ "viewpoint",
"custom1",
"custom2",
"custom3",
@@ -1154,10 +1158,8 @@ void G_Controldefault(void)
#else
void G_Controldefault(void)
{
- gamecontrol[gc_forward ][0] = KEY_UPARROW;
- gamecontrol[gc_forward ][1] = 'w';
- gamecontrol[gc_backward ][0] = KEY_DOWNARROW;
- gamecontrol[gc_backward ][1] = 's';
+ gamecontrol[gc_forward ][0] = 'w';
+ gamecontrol[gc_backward ][0] = 's';
gamecontrol[gc_strafeleft ][0] = 'a';
gamecontrol[gc_straferight][0] = 'd';
gamecontrol[gc_turnleft ][0] = KEY_LEFTARROW;
@@ -1178,21 +1180,24 @@ void G_Controldefault(void)
gamecontrol[gc_fire ][1] = KEY_MOUSE1+0;
gamecontrol[gc_firenormal ][0] = 'c';
gamecontrol[gc_tossflag ][0] = '\'';
- gamecontrol[gc_use ][0] = 'x';
+ gamecontrol[gc_use ][0] = KEY_LSHIFT;
gamecontrol[gc_camtoggle ][0] = 'v';
gamecontrol[gc_camleft ][0] = '[';
gamecontrol[gc_camright ][0] = ']';
gamecontrol[gc_camreset ][0] = 'r';
- gamecontrol[gc_lookup ][0] = KEY_PGUP;
- gamecontrol[gc_lookdown ][0] = KEY_PGDN;
+ gamecontrol[gc_lookup ][0] = KEY_UPARROW;
+ gamecontrol[gc_lookdown ][0] = KEY_DOWNARROW;
gamecontrol[gc_centerview ][0] = KEY_END;
gamecontrol[gc_talkkey ][0] = 't';
gamecontrol[gc_teamkey ][0] = 'y';
gamecontrol[gc_scores ][0] = KEY_TAB;
- gamecontrol[gc_jump ][0] = 'z';
- gamecontrol[gc_jump ][1] = KEY_MOUSE1+1;
+ gamecontrol[gc_jump ][0] = KEY_SPACE;
gamecontrol[gc_console ][0] = KEY_CONSOLE;
- gamecontrol[gc_pause ][0] = KEY_PAUSE;
+ gamecontrol[gc_pause ][0] = 'p';
+ gamecontrol[gc_screenshot ][0] = KEY_F8;
+ gamecontrol[gc_recordgif ][0] = KEY_F9;
+ gamecontrol[gc_viewpoint ][0] = KEY_F12;
+ gamecontrol[gc_systemmenu ][0] = KEY_JOY1+7; // Start
#ifdef WMINPUT
gamecontrol[gc_forward ][0] = KEY_JOY1+02; //UP
gamecontrol[gc_backward ][0] = KEY_JOY1+03; //DOWN
@@ -1330,11 +1335,31 @@ static void setcontrol(INT32 (*gc)[2], INT32 na)
return;
}
keynum = G_KeyStringtoNum(COM_Argv(2));
+
+ if (keynum == KEY_PAUSE) // fail silently; pause is hardcoded
+ {
+ if (na == 4)
+ {
+ na--;
+ keynum = G_KeyStringtoNum(COM_Argv(3));
+ if (keynum == KEY_PAUSE)
+ return;
+ }
+ else
+ return;
+ }
+
G_CheckDoubleUsage(keynum);
gc[numctrl][0] = keynum;
if (na == 4)
- gc[numctrl][1] = G_KeyStringtoNum(COM_Argv(3));
+ {
+ keynum = G_KeyStringtoNum(COM_Argv(3));
+ if (keynum != KEY_PAUSE)
+ gc[numctrl][1] = keynum;
+ else
+ gc[numctrl][1] = 0;
+ }
else
gc[numctrl][1] = 0;
}
diff --git a/src/g_input.h b/src/g_input.h
index 6dc99ac55..557fb3bf3 100644
--- a/src/g_input.h
+++ b/src/g_input.h
@@ -118,6 +118,10 @@ typedef enum
gc_jump,
gc_console,
gc_pause,
+ gc_systemmenu,
+ gc_screenshot,
+ gc_recordgif,
+ gc_viewpoint,
gc_custom1, // Lua scriptable
gc_custom2, // Lua scriptable
gc_custom3, // Lua scriptable
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index b735f91b9..c6f95613f 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -347,14 +347,14 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
numwords = COM_Argc() - usedargs;
I_Assert(numwords > 0);
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
+ if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Alert(CONS_NOTICE, M_GetText("The chat is muted. You can't say anything at the moment.\n"));
return;
}
// Only servers/admins can CSAY.
- if(!server && adminplayer != consoleplayer)
+ if(!server && IsPlayerAdmin(consoleplayer))
flags &= ~HU_CSAY;
// We handle HU_SERVER_SAY, not the caller.
@@ -448,7 +448,7 @@ static void Command_CSay_f(void)
return;
}
- if(!server && adminplayer != consoleplayer)
+ if(!server && !IsPlayerAdmin(consoleplayer))
{
CONS_Alert(CONS_NOTICE, M_GetText("Only servers and admins can use csay.\n"));
return;
@@ -477,7 +477,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
msg = (char *)*p;
SKIPSTRING(*p);
- if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && playernum != adminplayer)
+ if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, cv_mute.value ?
M_GetText("Illegal say command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"),
@@ -575,7 +575,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
// Give admins and remote admins their symbols.
if (playernum == serverplayer)
tempchar = (char *)Z_Calloc(strlen(cstart) + strlen(adminchar) + 1, PU_STATIC, NULL);
- else if (playernum == adminplayer)
+ else if (IsPlayerAdmin(playernum))
tempchar = (char *)Z_Calloc(strlen(cstart) + strlen(remotechar) + 1, PU_STATIC, NULL);
if (tempchar)
{
@@ -710,7 +710,7 @@ static void HU_queueChatChar(char c)
} while (c);
// last minute mute check
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
+ if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Alert(CONS_NOTICE, M_GetText("The chat is muted. You can't say anything at the moment.\n"));
return;
@@ -768,9 +768,9 @@ boolean HU_Responder(event_t *ev)
{
// enter chat mode
if ((ev->data1 == gamecontrol[gc_talkkey][0] || ev->data1 == gamecontrol[gc_talkkey][1])
- && netgame && (!cv_mute.value || server || (adminplayer == consoleplayer)))
+ && netgame && (!cv_mute.value || server || IsPlayerAdmin(consoleplayer)))
{
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
+ if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
return false;
chat_on = true;
w_chat[0] = 0;
@@ -778,9 +778,9 @@ boolean HU_Responder(event_t *ev)
return true;
}
if ((ev->data1 == gamecontrol[gc_teamkey][0] || ev->data1 == gamecontrol[gc_teamkey][1])
- && netgame && (!cv_mute.value || server || (adminplayer == consoleplayer)))
+ && netgame && (!cv_mute.value || server || (IsPlayerAdmin(consoleplayer))))
{
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
+ if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
return false;
chat_on = true;
w_chat[0] = 0;
diff --git a/src/i_tcp.c b/src/i_tcp.c
index 279081e72..739355ccf 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -713,14 +713,29 @@ static boolean SOCK_CanGet(void)
#endif
#ifndef NONET
-static void SOCK_Send(void)
+static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr)
{
- ssize_t c = ERRSOCKET;
socklen_t d4 = (socklen_t)sizeof(struct sockaddr_in);
#ifdef HAVE_IPV6
socklen_t d6 = (socklen_t)sizeof(struct sockaddr_in6);
#endif
socklen_t d, da = (socklen_t)sizeof(mysockaddr_t);
+
+ switch (sockaddr->any.sa_family)
+ {
+ case AF_INET: d = d4; break;
+#ifdef HAVE_IPV6
+ case AF_INET6: d = d6; break;
+#endif
+ default: d = da; break;
+ }
+
+ return sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d);
+}
+
+static void SOCK_Send(void)
+{
+ ssize_t c = ERRSOCKET;
size_t i, j;
if (!nodeconnected[doomcom->remotenode])
@@ -733,19 +748,7 @@ static void SOCK_Send(void)
for (j = 0; j < broadcastaddresses; j++)
{
if (myfamily[i] == broadcastaddress[j].any.sa_family)
- {
- if (broadcastaddress[i].any.sa_family == AF_INET)
- d = d4;
-#ifdef HAVE_IPV6
- else if (broadcastaddress[i].any.sa_family == AF_INET6)
- d = d6;
-#endif
- else
- d = da;
-
- c = sendto(mysockets[i], (char *)&doomcom->data, doomcom->datalength, 0,
- &broadcastaddress[j].any, d);
- }
+ SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]);
}
}
return;
@@ -755,35 +758,13 @@ static void SOCK_Send(void)
for (i = 0; i < mysocketses; i++)
{
if (myfamily[i] == clientaddress[doomcom->remotenode].any.sa_family)
- {
- if (clientaddress[doomcom->remotenode].any.sa_family == AF_INET)
- d = d4;
-#ifdef HAVE_IPV6
- else if (clientaddress[doomcom->remotenode].any.sa_family == AF_INET6)
- d = d6;
-#endif
- else
- d = da;
-
- sendto(mysockets[i], (char *)&doomcom->data, doomcom->datalength, 0,
- &clientaddress[doomcom->remotenode].any, d);
- }
+ SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]);
}
return;
}
else
{
- if (clientaddress[doomcom->remotenode].any.sa_family == AF_INET)
- d = d4;
-#ifdef HAVE_IPV6
- else if (clientaddress[doomcom->remotenode].any.sa_family == AF_INET6)
- d = d6;
-#endif
- else
- d = da;
-
- c = sendto(nodesocket[doomcom->remotenode], (char *)&doomcom->data, doomcom->datalength, 0,
- &clientaddress[doomcom->remotenode].any, d);
+ c = SOCK_SendToAddr(nodesocket[doomcom->remotenode], &clientaddress[doomcom->remotenode]);
}
if (c == ERRSOCKET)
@@ -1075,7 +1056,7 @@ static boolean UDP_Socket(void)
if (gaie == 0)
{
runp = ai;
- while (runp != NULL)
+ while (runp != NULL && s < MAXNETNODES+1)
{
memcpy(&clientaddress[s], runp->ai_addr, runp->ai_addrlen);
s++;
@@ -1090,12 +1071,15 @@ static boolean UDP_Socket(void)
clientaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); //GetLocalAddress(); // my own ip
s++;
}
+
+ s = 0;
+
// setup broadcast adress to BROADCASTADDR entry
gaie = I_getaddrinfo("255.255.255.255", "0", &hints, &ai);
if (gaie == 0)
{
runp = ai;
- while (runp != NULL)
+ while (runp != NULL && s < MAXNETNODES+1)
{
memcpy(&broadcastaddress[s], runp->ai_addr, runp->ai_addrlen);
s++;
@@ -1118,7 +1102,7 @@ static boolean UDP_Socket(void)
if (gaie == 0)
{
runp = ai;
- while (runp != NULL)
+ while (runp != NULL && s < MAXNETNODES+1)
{
memcpy(&broadcastaddress[s], runp->ai_addr, runp->ai_addrlen);
s++;
diff --git a/src/info.c b/src/info.c
index 84e9cbdc4..08470d4cd 100644
--- a/src/info.c
+++ b/src/info.c
@@ -12733,7 +12733,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
10, // mass
0, // damage
sfx_None, // activesound
- MF_NOTHINK|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
+ MF_SCENERY|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
diff --git a/src/locale/en.po b/src/locale/en.po
index 2665082a2..ce4510802 100644
--- a/src/locale/en.po
+++ b/src/locale/en.po
@@ -3629,7 +3629,7 @@ msgid "another castle!"
msgstr ""
#: st_stuff.c:2328 st_stuff.c:2334 st_stuff.c:2356
-msgid "Press F12 to watch another player."
+msgid "Press Viewpoint Key to watch a player."
msgstr ""
#: st_stuff.c:2333
diff --git a/src/locale/srb2.pot b/src/locale/srb2.pot
index 37d14f8b7..c61cbcc97 100644
--- a/src/locale/srb2.pot
+++ b/src/locale/srb2.pot
@@ -3820,7 +3820,7 @@ msgid "another castle!"
msgstr ""
#: st_stuff.c:2092 st_stuff.c:2098 st_stuff.c:2120
-msgid "Press F12 to watch another player."
+msgid "Press Viewpoint Key to watch a player."
msgstr ""
#: st_stuff.c:2097
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index c07d104c2..969987762 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -14,12 +14,18 @@
#ifdef HAVE_BLUA
#include "p_local.h"
#include "p_setup.h" // So we can have P_SetupLevelSky
+#ifdef ESLOPE
+#include "p_slopes.h" // P_GetZAt
+#endif
#include "z_zone.h"
#include "r_main.h"
#include "r_things.h"
#include "m_random.h"
#include "s_sound.h"
#include "g_game.h"
+#include "hu_stuff.h"
+#include "console.h"
+#include "d_netcmd.h" // IsPlayerAdmin
#include "lua_script.h"
#include "lua_libs.h"
@@ -93,6 +99,16 @@ static int lib_evalMath(lua_State *L)
return 1;
}
+static int lib_isPlayerAdmin(lua_State *L)
+{
+ player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+ //HUDSAFE
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ lua_pushboolean(L, IsPlayerAdmin(player-players));
+ return 1;
+}
+
// M_RANDOM
//////////////
@@ -1547,6 +1563,24 @@ static int lib_evCrumbleChain(lua_State *L)
return 0;
}
+#ifdef ESLOPE
+// P_SLOPES
+////////////
+
+static int lib_pGetZAt(lua_State *L)
+{
+ pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
+ fixed_t x = luaL_checkfixed(L, 2);
+ fixed_t y = luaL_checkfixed(L, 3);
+ //HUDSAFE
+ if (!slope)
+ return LUA_ErrInvalid(L, "pslope_t");
+
+ lua_pushfixed(L, P_GetZAt(slope, x, y));
+ return 1;
+}
+#endif
+
// R_DEFS
////////////
@@ -1983,6 +2017,7 @@ static int lib_gTicsToMilliseconds(lua_State *L)
static luaL_Reg lib[] = {
{"print", lib_print},
{"EvalMath", lib_evalMath},
+ {"IsPlayerAdmin", lib_isPlayerAdmin},
// m_random
{"P_RandomFixed",lib_pRandomFixed},
@@ -2113,6 +2148,11 @@ static luaL_Reg lib[] = {
{"P_StartQuake",lib_pStartQuake},
{"EV_CrumbleChain",lib_evCrumbleChain},
+#ifdef ESLOPE
+ // p_slopes
+ {"P_GetZAt",lib_pGetZAt},
+#endif
+
// r_defs
{"R_PointToAngle",lib_rPointToAngle},
{"R_PointToAngle2",lib_rPointToAngle2},
diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c
new file mode 100644
index 000000000..dabbdd9f6
--- /dev/null
+++ b/src/lua_blockmaplib.c
@@ -0,0 +1,266 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2016 by Iestyn "Monster Iestyn" Jealous.
+// Copyright (C) 2016 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file lua_blockmaplib.c
+/// \brief blockmap library for Lua scripting
+
+#include "doomdef.h"
+#ifdef HAVE_BLUA
+#include "p_local.h"
+#include "r_main.h" // validcount
+#include "lua_script.h"
+#include "lua_libs.h"
+//#include "lua_hud.h" // hud_running errors
+
+static const char *const search_opt[] = {
+ "objects",
+ "lines",
+ NULL};
+
+// a quickly-made function pointer typedef used by lib_searchBlockmap...
+// return values:
+// 0 - normal, no interruptions
+// 1 - stop search through current block
+// 2 - stop search completely
+typedef UINT8 (*blockmap_func)(lua_State *, INT32, INT32, mobj_t *);
+
+static boolean blockfuncerror = false; // errors should only print once per search blockmap call
+
+// Helper function for "objects" search
+static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *thing)
+{
+ mobj_t *mobj, *bnext = NULL;
+
+ if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+ return 0;
+
+ // Check interaction with the objects in the blockmap.
+ for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext)
+ {
+ P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
+ if (mobj == thing)
+ continue; // our thing just found itself, so move on
+ lua_pushvalue(L, 1); // push function
+ LUA_PushUserdata(L, thing, META_MOBJ);
+ LUA_PushUserdata(L, mobj, META_MOBJ);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!blockfuncerror || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ blockfuncerror = true;
+ return 0; // *shrugs*
+ }
+ if (!lua_isnil(gL, -1))
+ { // if nil, continue
+ if (lua_toboolean(gL, -1))
+ return 2; // stop whole search
+ else
+ return 1; // stop block search
+ }
+ lua_pop(gL, 1);
+ if (P_MobjWasRemoved(thing) // func just popped our thing, cannot continue.
+ || (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
+ {
+ P_SetTarget(&bnext, NULL);
+ return (P_MobjWasRemoved(thing)) ? 2 : 1;
+ }
+ }
+ return 0;
+}
+
+// Helper function for "lines" search
+static UINT8 lib_searchBlockmap_Lines(lua_State *L, INT32 x, INT32 y, mobj_t *thing)
+{
+ INT32 offset;
+ const INT32 *list; // Big blockmap
+#ifdef POLYOBJECTS
+ polymaplink_t *plink; // haleyjd 02/22/06
+#endif
+ line_t *ld;
+
+ if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+ return 0;
+
+ offset = y*bmapwidth + x;
+
+#ifdef POLYOBJECTS
+ // haleyjd 02/22/06: consider polyobject lines
+ plink = polyblocklinks[offset];
+
+ while (plink)
+ {
+ polyobj_t *po = plink->po;
+
+ if (po->validcount != validcount) // if polyobj hasn't been checked
+ {
+ size_t i;
+ po->validcount = validcount;
+
+ for (i = 0; i < po->numLines; ++i)
+ {
+ if (po->lines[i]->validcount == validcount) // line has been checked
+ continue;
+ po->lines[i]->validcount = validcount;
+
+ lua_pushvalue(L, 1);
+ LUA_PushUserdata(L, thing, META_MOBJ);
+ LUA_PushUserdata(L, po->lines[i], META_LINE);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!blockfuncerror || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ blockfuncerror = true;
+ return 0; // *shrugs*
+ }
+ if (!lua_isnil(gL, -1))
+ { // if nil, continue
+ if (lua_toboolean(gL, -1))
+ return 2; // stop whole search
+ else
+ return 1; // stop block search
+ }
+ lua_pop(gL, 1);
+ if (P_MobjWasRemoved(thing))
+ return 2;
+ }
+ }
+ plink = (polymaplink_t *)(plink->link.next);
+ }
+#endif
+
+ offset = *(blockmap + offset); // offset = blockmap[y*bmapwidth+x];
+
+ // First index is really empty, so +1 it.
+ for (list = blockmaplump + offset + 1; *list != -1; list++)
+ {
+ ld = &lines[*list];
+
+ if (ld->validcount == validcount)
+ continue; // Line has already been checked.
+
+ ld->validcount = validcount;
+
+ lua_pushvalue(L, 1);
+ LUA_PushUserdata(L, thing, META_MOBJ);
+ LUA_PushUserdata(L, ld, META_LINE);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!blockfuncerror || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ blockfuncerror = true;
+ return 0; // *shrugs*
+ }
+ if (!lua_isnil(gL, -1))
+ { // if nil, continue
+ if (lua_toboolean(gL, -1))
+ return 2; // stop whole search
+ else
+ return 1; // stop block search
+ }
+ lua_pop(gL, 1);
+ if (P_MobjWasRemoved(thing))
+ return 2;
+ }
+ return 0; // Everything was checked.
+}
+
+// The searchBlockmap function
+// arguments: searchBlockmap(searchtype, function, mobj, [x1, x2, y1, y2])
+// return value:
+// true = search completely uninteruppted,
+// false = searching of at least one block stopped mid-way (including if the whole search was stopped)
+static int lib_searchBlockmap(lua_State *L)
+{
+ int searchtype = luaL_checkoption(L, 1, "objects", search_opt);
+ int n;
+ mobj_t *mobj;
+ INT32 xl, xh, yl, yh, bx, by;
+ fixed_t x1, x2, y1, y2;
+ boolean retval = true;
+ UINT8 funcret = 0;
+ blockmap_func searchFunc;
+
+ lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2]
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+
+ switch (searchtype)
+ {
+ case 0: // "objects"
+ default:
+ searchFunc = lib_searchBlockmap_Objects;
+ break;
+ case 1: // "lines"
+ searchFunc = lib_searchBlockmap_Lines;
+ break;
+ }
+
+ // the mobj we are searching around, the "calling" mobj we could say
+ mobj = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
+ if (!mobj)
+ return LUA_ErrInvalid(L, "mobj_t");
+
+ n = lua_gettop(L);
+
+ if (n > 2) // specific x/y ranges have been supplied
+ {
+ if (n < 6)
+ return luaL_error(L, "arguments 4 to 6 not all given (expected 4 fixed-point integers)");
+
+ x1 = luaL_checkfixed(L, 3);
+ x2 = luaL_checkfixed(L, 4);
+ y1 = luaL_checkfixed(L, 5);
+ y2 = luaL_checkfixed(L, 6);
+ }
+ else // mobj and function only - search around mobj's radius by default
+ {
+ fixed_t radius = mobj->radius + MAXRADIUS;
+ x1 = mobj->x - radius;
+ x2 = mobj->x + radius;
+ y1 = mobj->y - radius;
+ y2 = mobj->y + radius;
+ }
+ lua_settop(L, 2); // pop everything except function, mobj
+
+ xl = (unsigned)(x1 - bmaporgx)>>MAPBLOCKSHIFT;
+ xh = (unsigned)(x2 - bmaporgx)>>MAPBLOCKSHIFT;
+ yl = (unsigned)(y1 - bmaporgy)>>MAPBLOCKSHIFT;
+ yh = (unsigned)(y2 - bmaporgy)>>MAPBLOCKSHIFT;
+
+ BMBOUNDFIX(xl, xh, yl, yh);
+
+ blockfuncerror = false; // reset
+ validcount++;
+ for (bx = xl; bx <= xh; bx++)
+ for (by = yl; by <= yh; by++)
+ {
+ funcret = searchFunc(L, bx, by, mobj);
+ // return value of searchFunc determines searchFunc's return value and/or when to stop
+ if (funcret == 2){ // stop whole search
+ lua_pushboolean(L, false); // return false
+ return 1;
+ }
+ else if (funcret == 1) // search was interrupted for this block
+ retval = false; // this changes the return value, but doesn't stop the whole search
+ // else don't do anything, continue as normal
+ if (P_MobjWasRemoved(mobj)){ // ...unless the original object was removed
+ lua_pushboolean(L, false); // in which case we have to stop now regardless
+ return 1;
+ }
+ }
+ lua_pushboolean(L, retval);
+ return 1;
+}
+
+int LUA_BlockmapLib(lua_State *L)
+{
+ lua_register(L, "searchBlockmap", lib_searchBlockmap);
+ return 0;
+}
+
+#endif
diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index 3f1fea5b1..dced4e43c 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -55,7 +55,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum)
lua_pop(gL, 1); // pop flags
// requires server/admin and the player is not one of them
- if ((flags & 1) && playernum != serverplayer && playernum != adminplayer)
+ if ((flags & 1) && playernum != serverplayer && !IsPlayerAdmin(playernum))
goto deny;
lua_rawgeti(gL, -1, 1); // push function from command info table
@@ -133,7 +133,7 @@ void COM_Lua_f(void)
UINT8 argc;
lua_pop(gL, 1); // pop command info table
- if (flags & 1 && !server && adminplayer != playernum) // flag 1: only server/admin can use this command.
+ if (flags & 1 && !server && !IsPlayerAdmin(playernum)) // flag 1: only server/admin can use this command.
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
diff --git a/src/lua_hook.h b/src/lua_hook.h
index db32c42ba..252960edf 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -43,12 +43,13 @@ enum hook {
hook_PlayerMsg,
hook_HurtMsg,
hook_PlayerSpawn,
+ hook_PlayerQuit,
hook_MAX // last hook
};
extern const char *const hookNames[];
-void LUAh_MapChange(void); // Hook for map change (before load)
+void LUAh_MapChange(INT16 mapnumber); // Hook for map change (before load)
void LUAh_MapLoad(void); // Hook for map load
void LUAh_PlayerJoin(int playernum); // Hook for Got_AddPlayer
void LUAh_ThinkFrame(void); // Hook for frame (after mobj and player thinkers)
@@ -77,5 +78,6 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook
boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg); // Hook for chat messages
boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source); // Hook for hurt messages
#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer
+void LUAh_PlayerQuit(player_t *plr, int reason); // Hook for player quitting
#endif
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 2a98a82e5..7e544ae99 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -54,6 +54,7 @@ const char *const hookNames[hook_MAX+1] = {
"PlayerMsg",
"HurtMsg",
"PlayerSpawn",
+ "PlayerQuit",
NULL
};
@@ -314,14 +315,14 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which)
}
// Hook for map change (before load)
-void LUAh_MapChange(void)
+void LUAh_MapChange(INT16 mapnumber)
{
hook_p hookp;
if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8))))
return;
lua_settop(gL, 0);
- lua_pushinteger(gL, gamemap);
+ lua_pushinteger(gL, mapnumber);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_MapChange)
@@ -1074,4 +1075,30 @@ void LUAh_NetArchiveHook(lua_CFunction archFunc)
// stack: tables
}
+void LUAh_PlayerQuit(player_t *plr, int reason)
+{
+ hook_p hookp;
+ if (!gL || !(hooksAvailable[hook_PlayerQuit/8] & (1<<(hook_PlayerQuit%8))))
+ return;
+
+ lua_settop(gL, 0);
+
+ for (hookp = roothook; hookp; hookp = hookp->next)
+ if (hookp->type == hook_PlayerQuit)
+ {
+ if (lua_gettop(gL) == 0)
+ {
+ LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit
+ lua_pushinteger(gL, reason); // Reason for quitting
+ }
+ lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+ lua_gettable(gL, LUA_REGISTRYINDEX);
+ lua_pushvalue(gL, -3);
+ lua_pushvalue(gL, -3);
+ LUA_Call(gL, 2);
+ }
+
+ lua_settop(gL, 0);
+}
+
#endif
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index ee470d50c..b16125395 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -560,6 +560,15 @@ static int libd_renderer(lua_State *L)
return 1;
}
+// 30/10/18 Lat': Get cv_translucenthud's value for HUD rendering as a normal V_xxTRANS int
+// Could as well be thrown in global vars for ease of access but I guess it makes sense for it to be a HUD fn
+static int libd_getlocaltransflag(lua_State *L)
+{
+ HUDONLY
+ lua_pushinteger(L, (10-cv_translucenthud.value)*V_10TRANS); // A bit weird that it's called "translucenthud" yet 10 is fully opaque :V
+ return 1;
+}
+
static luaL_Reg lib_draw[] = {
{"patchExists", libd_patchExists},
{"cachePatch", libd_cachePatch},
@@ -576,6 +585,7 @@ static luaL_Reg lib_draw[] = {
{"dupx", libd_dupx},
{"dupy", libd_dupy},
{"renderer", libd_renderer},
+ {"localTransFlag", libd_getlocaltransflag},
{NULL, NULL}
};
@@ -599,6 +609,19 @@ static int lib_huddisable(lua_State *L)
return 0;
}
+// 30/10/18: Lat': How come this wasn't here before?
+static int lib_hudenabled(lua_State *L)
+{
+ enum hud option = luaL_checkoption(L, 1, NULL, hud_disable_options);
+ if (hud_enabled[option/8] & (1<<(option%8)))
+ lua_pushboolean(L, true);
+ else
+ lua_pushboolean(L, false);
+
+ return 1;
+}
+
+
// add a HUD element for rendering
static int lib_hudadd(lua_State *L)
{
@@ -623,6 +646,7 @@ static int lib_hudadd(lua_State *L)
static luaL_Reg lib_hud[] = {
{"enable", lib_hudenable},
{"disable", lib_huddisable},
+ {"enabled", lib_hudenabled},
{"add", lib_hudadd},
{NULL, NULL}
};
diff --git a/src/lua_libs.h b/src/lua_libs.h
index fe3d82200..9c6050bea 100644
--- a/src/lua_libs.h
+++ b/src/lua_libs.h
@@ -38,6 +38,11 @@ extern lua_State *gL;
#define META_SUBSECTOR "SUBSECTOR_T*"
#define META_SECTOR "SECTOR_T*"
#define META_FFLOOR "FFLOOR_T*"
+#ifdef ESLOPE
+#define META_SLOPE "PSLOPE_T*"
+#define META_VECTOR2 "VECTOR2_T"
+#define META_VECTOR3 "VECTOR3_T"
+#endif
#define META_MAPHEADER "MAPHEADER_T*"
#define META_CVAR "CONSVAR_T*"
@@ -64,6 +69,7 @@ int LUA_PlayerLib(lua_State *L);
int LUA_SkinLib(lua_State *L);
int LUA_ThinkerLib(lua_State *L);
int LUA_MapLib(lua_State *L);
+int LUA_BlockmapLib(lua_State *L);
int LUA_HudLib(lua_State *L);
#endif
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 5c1763862..efe9e6f4c 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -16,6 +16,10 @@
#include "p_local.h"
#include "p_setup.h"
#include "z_zone.h"
+#ifdef ESLOPE
+#include "p_slopes.h"
+#endif
+#include "r_main.h"
#include "lua_script.h"
#include "lua_libs.h"
@@ -38,7 +42,13 @@ enum sector_e {
sector_heightsec,
sector_camsec,
sector_lines,
+#ifdef ESLOPE
+ sector_ffloors,
+ sector_fslope,
+ sector_cslope
+#else
sector_ffloors
+#endif
};
static const char *const sector_opt[] = {
@@ -55,6 +65,10 @@ static const char *const sector_opt[] = {
"camsec",
"lines",
"ffloors",
+#ifdef ESLOPE
+ "f_slope",
+ "c_slope",
+#endif
NULL};
enum subsector_e {
@@ -160,6 +174,10 @@ enum ffloor_e {
ffloor_toplightlevel,
ffloor_bottomheight,
ffloor_bottompic,
+#ifdef ESLOPE
+ ffloor_tslope,
+ ffloor_bslope,
+#endif
ffloor_sector,
ffloor_flags,
ffloor_master,
@@ -176,6 +194,10 @@ static const char *const ffloor_opt[] = {
"toplightlevel",
"bottomheight",
"bottompic",
+#ifdef ESLOPE
+ "t_slope",
+ "b_slope",
+#endif
"sector", // secnum pushed as control sector userdata
"flags",
"master", // control linedef
@@ -185,6 +207,47 @@ static const char *const ffloor_opt[] = {
"alpha",
NULL};
+#ifdef ESLOPE
+enum slope_e {
+ slope_valid = 0,
+ slope_o,
+ slope_d,
+ slope_zdelta,
+ slope_normal,
+ slope_zangle,
+ slope_xydirection,
+ slope_sourceline,
+ slope_refpos,
+ slope_flags
+};
+
+static const char *const slope_opt[] = {
+ "valid",
+ "o",
+ "d",
+ "zdelta",
+ "normal",
+ "zangle",
+ "xydirection",
+ "sourceline",
+ "refpos",
+ "flags",
+ NULL};
+
+// shared by both vector2_t and vector3_t
+enum vector_e {
+ vector_x = 0,
+ vector_y,
+ vector_z
+};
+
+static const char *const vector_opt[] = {
+ "x",
+ "y",
+ "z",
+ NULL};
+#endif
+
static const char *const array_opt[] ={"iterate",NULL};
static const char *const valid_opt[] ={"valid",NULL};
@@ -327,6 +390,7 @@ static int sector_get(lua_State *L)
{
sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
enum sector_e field = luaL_checkoption(L, 2, sector_opt[0], sector_opt);
+ INT16 i;
if (!sector)
{
@@ -349,11 +413,23 @@ static int sector_get(lua_State *L)
lua_pushfixed(L, sector->ceilingheight);
return 1;
case sector_floorpic: // floorpic
- lua_pushlstring(L, levelflats[sector->floorpic].name, 8);
+ {
+ levelflat_t *levelflat = &levelflats[sector->floorpic];
+ for (i = 0; i < 8; i++)
+ if (!levelflat->name[i])
+ break;
+ lua_pushlstring(L, levelflat->name, i);
return 1;
+ }
case sector_ceilingpic: // ceilingpic
- lua_pushlstring(L, levelflats[sector->ceilingpic].name, 8);
+ {
+ levelflat_t *levelflat = &levelflats[sector->ceilingpic];
+ for (i = 0; i < 8; i++)
+ if (!levelflat->name[i])
+ break;
+ lua_pushlstring(L, levelflat->name, i);
return 1;
+ }
case sector_lightlevel:
lua_pushinteger(L, sector->lightlevel);
return 1;
@@ -386,6 +462,14 @@ static int sector_get(lua_State *L)
LUA_PushUserdata(L, sector->ffloors, META_FFLOOR);
lua_pushcclosure(L, sector_iterate, 2); // push lib_iterateFFloors and sector->ffloors as upvalues for the function
return 1;
+#ifdef ESLOPE
+ case sector_fslope: // f_slope
+ LUA_PushUserdata(L, sector->f_slope, META_SLOPE);
+ return 1;
+ case sector_cslope: // c_slope
+ LUA_PushUserdata(L, sector->c_slope, META_SLOPE);
+ return 1;
+#endif
}
return 0;
}
@@ -408,6 +492,10 @@ static int sector_set(lua_State *L)
case sector_heightsec: // heightsec
case sector_camsec: // camsec
case sector_ffloors: // ffloors
+#ifdef ESLOPE
+ case sector_fslope: // f_slope
+ case sector_cslope: // c_slope
+#endif
default:
return luaL_error(L, "sector_t field " LUA_QS " cannot be set.", sector_opt[field]);
case sector_floorheight: { // floorheight
@@ -1042,6 +1130,14 @@ static int ffloor_get(lua_State *L)
lua_pushlstring(L, levelflat->name, 8);
return 1;
}
+#ifdef ESLOPE
+ case ffloor_tslope:
+ LUA_PushUserdata(L, *ffloor->t_slope, META_SLOPE);
+ return 1;
+ case ffloor_bslope:
+ LUA_PushUserdata(L, *ffloor->b_slope, META_SLOPE);
+ return 1;
+#endif
case ffloor_sector:
LUA_PushUserdata(L, §ors[ffloor->secnum], META_SECTOR);
return 1;
@@ -1081,6 +1177,10 @@ static int ffloor_set(lua_State *L)
switch(field)
{
case ffloor_valid: // valid
+#ifdef ESLOPE
+ case ffloor_tslope: // t_slope
+ case ffloor_bslope: // b_slope
+#endif
case ffloor_sector: // sector
case ffloor_master: // master
case ffloor_target: // target
@@ -1141,6 +1241,181 @@ static int ffloor_set(lua_State *L)
return 0;
}
+#ifdef ESLOPE
+static int slope_get(lua_State *L)
+{
+ pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
+ enum slope_e field = luaL_checkoption(L, 2, slope_opt[0], slope_opt);
+
+ if (!slope)
+ {
+ if (field == slope_valid) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+ return luaL_error(L, "accessed pslope_t doesn't exist anymore.");
+ }
+
+ switch(field)
+ {
+ case slope_valid: // valid
+ lua_pushboolean(L, 1);
+ return 1;
+ case slope_o: // o
+ LUA_PushUserdata(L, &slope->o, META_VECTOR3);
+ return 1;
+ case slope_d: // d
+ LUA_PushUserdata(L, &slope->d, META_VECTOR2);
+ return 1;
+ case slope_zdelta: // zdelta
+ lua_pushfixed(L, slope->zdelta);
+ return 1;
+ case slope_normal: // normal
+ LUA_PushUserdata(L, &slope->normal, META_VECTOR3);
+ return 1;
+ case slope_zangle: // zangle
+ lua_pushangle(L, slope->zangle);
+ return 1;
+ case slope_xydirection: // xydirection
+ lua_pushangle(L, slope->xydirection);
+ return 1;
+ case slope_sourceline: // source linedef
+ LUA_PushUserdata(L, slope->sourceline, META_LINE);
+ return 1;
+ case slope_refpos: // refpos
+ lua_pushinteger(L, slope->refpos);
+ return 1;
+ case slope_flags: // flags
+ lua_pushinteger(L, slope->flags);
+ return 1;
+ }
+ return 0;
+}
+
+static int slope_set(lua_State *L)
+{
+ pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
+ enum slope_e field = luaL_checkoption(L, 2, slope_opt[0], slope_opt);
+
+ if (!slope)
+ return luaL_error(L, "accessed pslope_t doesn't exist anymore.");
+
+ if (hud_running)
+ return luaL_error(L, "Do not alter pslope_t in HUD rendering code!");
+
+ switch(field) // todo: reorganize this shit
+ {
+ case slope_valid: // valid
+ case slope_sourceline: // sourceline
+ case slope_d: // d
+ case slope_flags: // flags
+ case slope_normal: // normal
+ case slope_refpos: // refpos
+ default:
+ return luaL_error(L, "pslope_t field " LUA_QS " cannot be set.", slope_opt[field]);
+ case slope_o: { // o
+ luaL_checktype(L, 3, LUA_TTABLE);
+
+ lua_getfield(L, 3, "x");
+ if (lua_isnil(L, -1))
+ {
+ lua_pop(L, 1);
+ lua_rawgeti(L, 3, 1);
+ }
+ if (!lua_isnil(L, -1))
+ slope->o.x = luaL_checkfixed(L, -1);
+ else
+ slope->o.x = 0;
+ lua_pop(L, 1);
+
+ lua_getfield(L, 3, "y");
+ if (lua_isnil(L, -1))
+ {
+ lua_pop(L, 1);
+ lua_rawgeti(L, 3, 2);
+ }
+ if (!lua_isnil(L, -1))
+ slope->o.y = luaL_checkfixed(L, -1);
+ else
+ slope->o.y = 0;
+ lua_pop(L, 1);
+
+ lua_getfield(L, 3, "z");
+ if (lua_isnil(L, -1))
+ {
+ lua_pop(L, 1);
+ lua_rawgeti(L, 3, 3);
+ }
+ if (!lua_isnil(L, -1))
+ slope->o.z = luaL_checkfixed(L, -1);
+ else
+ slope->o.z = 0;
+ lua_pop(L, 1);
+ break;
+ }
+ case slope_zdelta: { // zdelta, this is temp until i figure out wtf to do
+ slope->zdelta = luaL_checkfixed(L, 3);
+ slope->zangle = R_PointToAngle2(0, 0, FRACUNIT, -slope->zdelta);
+ P_CalculateSlopeNormal(slope);
+ break;
+ }
+ case slope_zangle: { // zangle
+ angle_t zangle = luaL_checkangle(L, 3);
+ if (zangle == ANGLE_90 || zangle == ANGLE_270)
+ return luaL_error(L, "invalid zangle for slope!");
+ slope->zangle = zangle;
+ slope->zdelta = -FINETANGENT(((slope->zangle+ANGLE_90)>>ANGLETOFINESHIFT) & 4095);
+ P_CalculateSlopeNormal(slope);
+ break;
+ }
+ case slope_xydirection: // xydirection
+ slope->xydirection = luaL_checkangle(L, 3);
+ slope->d.x = -FINECOSINE((slope->xydirection>>ANGLETOFINESHIFT) & FINEMASK);
+ slope->d.y = -FINESINE((slope->xydirection>>ANGLETOFINESHIFT) & FINEMASK);
+ P_CalculateSlopeNormal(slope);
+ break;
+ }
+ return 0;
+}
+
+static int vector2_get(lua_State *L)
+{
+ vector2_t *vec = *((vector2_t **)luaL_checkudata(L, 1, META_VECTOR2));
+ enum vector_e field = luaL_checkoption(L, 2, vector_opt[0], vector_opt);
+
+ if (!vec)
+ return luaL_error(L, "accessed vector2_t doesn't exist anymore.");
+
+ switch(field)
+ {
+ case vector_x: lua_pushfixed(L, vec->x); return 1;
+ case vector_y: lua_pushfixed(L, vec->y); return 1;
+ default: break;
+ }
+
+ return 0;
+}
+
+static int vector3_get(lua_State *L)
+{
+ vector3_t *vec = *((vector3_t **)luaL_checkudata(L, 1, META_VECTOR3));
+ enum vector_e field = luaL_checkoption(L, 2, vector_opt[0], vector_opt);
+
+ if (!vec)
+ return luaL_error(L, "accessed vector3_t doesn't exist anymore.");
+
+ switch(field)
+ {
+ case vector_x: lua_pushfixed(L, vec->x); return 1;
+ case vector_y: lua_pushfixed(L, vec->y); return 1;
+ case vector_z: lua_pushfixed(L, vec->z); return 1;
+ default: break;
+ }
+
+ return 0;
+}
+#endif
+
static int lib_getMapheaderinfo(lua_State *L)
{
// i -> mapheaderinfo[i-1]
@@ -1317,6 +1592,26 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
+#ifdef ESLOPE
+ luaL_newmetatable(L, META_SLOPE);
+ lua_pushcfunction(L, slope_get);
+ lua_setfield(L, -2, "__index");
+
+ lua_pushcfunction(L, slope_set);
+ lua_setfield(L, -2, "__newindex");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, META_VECTOR2);
+ lua_pushcfunction(L, vector2_get);
+ lua_setfield(L, -2, "__index");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, META_VECTOR3);
+ lua_pushcfunction(L, vector3_get);
+ lua_setfield(L, -2, "__index");
+ lua_pop(L, 1);
+#endif
+
luaL_newmetatable(L, META_MAPHEADER);
lua_pushcfunction(L, mapheaderinfo_get);
lua_setfield(L, -2, "__index");
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 4cd952f8d..aca501221 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -80,7 +80,12 @@ enum mobj_e {
mobj_extravalue1,
mobj_extravalue2,
mobj_cusval,
+#ifdef ESLOPE
+ mobj_cvmem,
+ mobj_standingslope
+#else
mobj_cvmem
+#endif
};
static const char *const mobj_opt[] = {
@@ -140,6 +145,9 @@ static const char *const mobj_opt[] = {
"extravalue2",
"cusval",
"cvmem",
+#ifdef ESLOPE
+ "standingslope",
+#endif
NULL};
#define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field])
@@ -343,6 +351,11 @@ static int mobj_get(lua_State *L)
case mobj_cvmem:
lua_pushinteger(L, mo->cvmem);
break;
+#ifdef ESLOPE
+ case mobj_standingslope:
+ LUA_PushUserdata(L, mo->standingslope, META_SLOPE);
+ break;
+#endif
default: // extra custom variables in Lua memory
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
@@ -634,6 +647,10 @@ static int mobj_set(lua_State *L)
case mobj_cvmem:
mo->cvmem = luaL_checkinteger(L, 3);
break;
+#ifdef ESLOPE
+ case mobj_standingslope:
+ return NOSET;
+#endif
default:
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
diff --git a/src/lua_script.c b/src/lua_script.c
index 0ee65c83b..edf8ac3c5 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -22,6 +22,9 @@
#include "byteptr.h"
#include "p_saveg.h"
#include "p_local.h"
+#ifdef ESLOPE
+#include "p_slopes.h" // for P_SlopeById
+#endif
#ifdef LUA_ALLOW_BYTECODE
#include "d_netfil.h" // for LUA_DumpFile
#endif
@@ -48,6 +51,7 @@ static lua_CFunction liblist[] = {
LUA_SkinLib, // skin_t, skins[]
LUA_ThinkerLib, // thinker_t
LUA_MapLib, // line_t, side_t, sector_t, subsector_t
+ LUA_BlockmapLib, // blockmap stuff
LUA_HudLib, // HUD stuff
NULL
};
@@ -170,6 +174,7 @@ static inline void LUA_LoadFile(MYFILE *f, char *name)
LUA_ClearState();
lua_pushinteger(gL, f->wad);
lua_setfield(gL, LUA_REGISTRYINDEX, "WAD");
+
if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, 0, 0)) {
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1));
lua_pop(gL,1);
@@ -189,17 +194,23 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump)
W_ReadLumpPwad(wad, lump, f.data);
f.curpos = f.data;
- len = strlen(wadfiles[wad]->filename);
- name = malloc(len+10);
- strcpy(name, wadfiles[wad]->filename);
- if (!fasticmp(&name[len - 4], ".lua")) {
- // If it's not a .lua file, copy the lump name in too.
- name[len] = '|';
- M_Memcpy(name+len+1, wadfiles[wad]->lumpinfo[lump].name, 8);
- name[len+9] = '\0';
+ len = strlen(wadfiles[wad]->filename); // length of file name
+
+ if (wadfiles[wad]->type == RET_LUA)
+ {
+ name = malloc(len+1);
+ strcpy(name, wadfiles[wad]->filename);
+ }
+ else // If it's not a .lua file, copy the lump name in too.
+ {
+ lumpinfo_t *lump_p = &wadfiles[wad]->lumpinfo[lump];
+ len += 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
+ name = malloc(len+1);
+ sprintf(name, "%s|%s", wadfiles[wad]->filename, lump_p->name2);
+ name[len] = '\0';
}
- LUA_LoadFile(&f, name);
+ LUA_LoadFile(&f, name); // actually load file!
free(name);
Z_Free(f.data);
@@ -457,6 +468,9 @@ enum
ARCH_SIDE,
ARCH_SUBSECTOR,
ARCH_SECTOR,
+#ifdef ESLOPE
+ ARCH_SLOPE,
+#endif
ARCH_MAPHEADER,
ARCH_TEND=0xFF,
@@ -476,6 +490,9 @@ static const struct {
{META_SIDE, ARCH_SIDE},
{META_SUBSECTOR,ARCH_SUBSECTOR},
{META_SECTOR, ARCH_SECTOR},
+#ifdef ESLOPE
+ {META_SLOPE, ARCH_SLOPE},
+#endif
{META_MAPHEADER, ARCH_MAPHEADER},
{NULL, ARCH_NULL}
};
@@ -528,9 +545,23 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
break;
}
case LUA_TSTRING:
+ {
+ UINT16 len = (UINT16)lua_objlen(gL, myindex); // get length of string, including embedded zeros
+ const char *s = lua_tostring(gL, myindex);
+ UINT16 i = 0;
WRITEUINT8(save_p, ARCH_STRING);
- WRITESTRING(save_p, lua_tostring(gL, myindex));
+ // if you're wondering why we're writing a string to save_p this way,
+ // it turns out that Lua can have embedded zeros ('\0') in the strings,
+ // so we can't use WRITESTRING as that cuts off when it finds a '\0'.
+ // Saving the size of the string also allows us to get the size of the string on the other end,
+ // fixing the awful crashes previously encountered for reading strings longer than 1024
+ // (yes I know that's kind of a stupid thing to care about, but it'd be evil to trim or ignore them?)
+ // -- Monster Iestyn 05/08/18
+ WRITEUINT16(save_p, len); // save size of string
+ while (i < len)
+ WRITECHAR(save_p, s[i++]); // write chars individually, including the embedded zeros
break;
+ }
case LUA_TTABLE:
{
boolean found = false;
@@ -666,6 +697,19 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
}
break;
}
+#ifdef ESLOPE
+ case ARCH_SLOPE:
+ {
+ pslope_t *slope = *((pslope_t **)lua_touserdata(gL, myindex));
+ if (!slope)
+ WRITEUINT8(save_p, ARCH_NULL);
+ else {
+ WRITEUINT8(save_p, ARCH_SLOPE);
+ WRITEUINT16(save_p, slope->id);
+ }
+ break;
+ }
+#endif
case ARCH_MAPHEADER:
{
mapheader_t *header = *((mapheader_t **)lua_touserdata(gL, myindex));
@@ -769,11 +813,19 @@ static void ArchiveTables(void)
lua_pushnil(gL);
while (lua_next(gL, -2))
{
- ArchiveValue(TABLESINDEX, -2); // key should be either a number or a string, ArchiveValue can handle this.
+ // Write key
+ e = ArchiveValue(TABLESINDEX, -2); // key should be either a number or a string, ArchiveValue can handle this.
+ if (e == 2) // invalid key type (function, thread, lightuserdata, or anything we don't recognise)
+ {
+ lua_pushvalue(gL, -2);
+ CONS_Alert(CONS_ERROR, "Index '%s' (%s) of table %d could not be archived!\n", lua_tostring(gL, -1), luaL_typename(gL, -1), i);
+ lua_pop(gL, 1);
+ }
+ // Write value
e = ArchiveValue(TABLESINDEX, -1);
if (e == 1)
n++; // the table contained a new table we'll have to archive. :(
- else if (e == 2)
+ else if (e == 2) // invalid value type
{
lua_pushvalue(gL, -2);
CONS_Alert(CONS_ERROR, "Type of value for table %d entry '%s' (%s) could not be archived!\n", i, lua_tostring(gL, -1), luaL_typename(gL, -1));
@@ -803,9 +855,19 @@ static UINT8 UnArchiveValue(int TABLESINDEX)
break;
case ARCH_STRING:
{
- char value[1024];
- READSTRING(save_p, value);
- lua_pushstring(gL, value);
+ UINT16 len = READUINT16(save_p); // length of string, including embedded zeros
+ char *value;
+ UINT16 i = 0;
+ // See my comments in the ArchiveValue function;
+ // it's much the same for reading strings as writing them!
+ // (i.e. we can't use READSTRING either)
+ // -- Monster Iestyn 05/08/18
+ value = malloc(len); // make temp buffer of size len
+ // now read the actual string
+ while (i < len)
+ value[i++] = READCHAR(save_p); // read chars individually, including the embedded zeros
+ lua_pushlstring(gL, value, len); // push the string (note: this function supports embedded zeros)
+ free(value); // free the buffer
break;
}
case ARCH_TABLE:
@@ -852,6 +914,11 @@ static UINT8 UnArchiveValue(int TABLESINDEX)
case ARCH_SECTOR:
LUA_PushUserdata(gL, §ors[READUINT16(save_p)], META_SECTOR);
break;
+#ifdef ESLOPE
+ case ARCH_SLOPE:
+ LUA_PushUserdata(gL, P_SlopeById(READUINT16(save_p)), META_SLOPE);
+ break;
+#endif
case ARCH_MAPHEADER:
LUA_PushUserdata(gL, mapheaderinfo[READUINT16(save_p)], META_MAPHEADER);
break;
@@ -915,11 +982,17 @@ static void UnArchiveTables(void)
lua_rawgeti(gL, TABLESINDEX, i);
while (true)
{
- if (UnArchiveValue(TABLESINDEX) == 1)
+ if (UnArchiveValue(TABLESINDEX) == 1) // read key
break;
- if (UnArchiveValue(TABLESINDEX) == 2)
+ if (UnArchiveValue(TABLESINDEX) == 2) // read value
n++;
- lua_rawset(gL, -3);
+ if (lua_isnil(gL, -2)) // if key is nil (if a function etc was accidentally saved)
+ {
+ CONS_Alert(CONS_ERROR, "A nil key in table %d was found! (Invalid key type or corrupted save?)\n", i);
+ lua_pop(gL, 2); // pop key and value instead of setting them in the table, to prevent Lua panic errors
+ }
+ else
+ lua_rawset(gL, -3);
}
lua_pop(gL, 1);
}
diff --git a/src/m_argv.c b/src/m_argv.c
index 5c5dc37cf..e8bfdd3db 100644
--- a/src/m_argv.c
+++ b/src/m_argv.c
@@ -214,4 +214,4 @@ void M_FindResponseFile(void)
break;
}
-}
\ No newline at end of file
+}
diff --git a/src/m_menu.c b/src/m_menu.c
index 2ac2ba7fb..ea49c0199 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -57,6 +57,8 @@
#include "st_stuff.h"
#include "i_sound.h"
+#include "i_joy.h" // for joystick menu controls
+
// Condition Sets
#include "m_cond.h"
@@ -278,7 +280,7 @@ static void M_SetupMultiPlayer2(INT32 choice);
// Split into multiple parts due to size
// Controls
menu_t OP_ControlsDef, OP_ControlListDef, OP_MoveControlsDef;
-menu_t OP_MPControlsDef, OP_CameraControlsDef, OP_MiscControlsDef;
+menu_t OP_MPControlsDef, OP_MiscControlsDef;
menu_t OP_P1ControlsDef, OP_P2ControlsDef, OP_MouseOptionsDef;
menu_t OP_Mouse2OptionsDef, OP_Joystick1Def, OP_Joystick2Def;
static void M_VideoModeMenu(INT32 choice);
@@ -1047,20 +1049,30 @@ static menuitem_t OP_ControlListMenu[] =
{
{IT_SUBMENU | IT_STRING, NULL, "Movement Controls...", &OP_MoveControlsDef, 10},
{IT_SUBMENU | IT_STRING, NULL, "Multiplayer Controls...", &OP_MPControlsDef, 20},
- {IT_SUBMENU | IT_STRING, NULL, "Camera Controls...", &OP_CameraControlsDef, 30},
- {IT_SUBMENU | IT_STRING, NULL, "Miscellaneous Controls...", &OP_MiscControlsDef, 40},
+ {IT_SUBMENU | IT_STRING, NULL, "Miscellaneous Controls...", &OP_MiscControlsDef, 30},
};
static menuitem_t OP_MoveControlsMenu[] =
{
- {IT_CALL | IT_STRING2, NULL, "Forward", M_ChangeControl, gc_forward },
- {IT_CALL | IT_STRING2, NULL, "Reverse", M_ChangeControl, gc_backward },
- {IT_CALL | IT_STRING2, NULL, "Turn Left", M_ChangeControl, gc_turnleft },
- {IT_CALL | IT_STRING2, NULL, "Turn Right", M_ChangeControl, gc_turnright },
- {IT_CALL | IT_STRING2, NULL, "Jump", M_ChangeControl, gc_jump },
- {IT_CALL | IT_STRING2, NULL, "Spin", M_ChangeControl, gc_use },
- {IT_CALL | IT_STRING2, NULL, "Strafe Left", M_ChangeControl, gc_strafeleft },
- {IT_CALL | IT_STRING2, NULL, "Strafe Right", M_ChangeControl, gc_straferight},
+ {IT_HEADER, NULL, " Movement", NULL, 0},
+ {IT_CALL | IT_STRING2, NULL, "Move Forward", M_ChangeControl, gc_forward },
+ {IT_CALL | IT_STRING2, NULL, "Move Backward", M_ChangeControl, gc_backward },
+ {IT_CALL | IT_STRING2, NULL, "Move Left", M_ChangeControl, gc_strafeleft },
+ {IT_CALL | IT_STRING2, NULL, "Move Right", M_ChangeControl, gc_straferight },
+ {IT_CALL | IT_STRING2, NULL, "Jump", M_ChangeControl, gc_jump },
+ {IT_CALL | IT_STRING2, NULL, "Spin", M_ChangeControl, gc_use },
+ {IT_HEADER, NULL, " Camera", NULL, 0},
+ {IT_CALL | IT_STRING2, NULL, "Look Up", M_ChangeControl, gc_lookup },
+ {IT_CALL | IT_STRING2, NULL, "Look Down", M_ChangeControl, gc_lookdown },
+ {IT_CALL | IT_STRING2, NULL, "Turn Left", M_ChangeControl, gc_turnleft },
+ {IT_CALL | IT_STRING2, NULL, "Turn Right", M_ChangeControl, gc_turnright },
+ {IT_CALL | IT_STRING2, NULL, "Center View", M_ChangeControl, gc_centerview },
+ {IT_CALL | IT_STRING2, NULL, "Toggle Mouselook", M_ChangeControl, gc_mouseaiming },
+ {IT_CALL | IT_STRING2, NULL, "Toggle Third-Person", M_ChangeControl, gc_camtoggle},
+ {IT_CALL | IT_STRING2, NULL, "Reset Camera", M_ChangeControl, gc_camreset },
+ {IT_HEADER, NULL, " Advanced", NULL, 0},
+ {IT_CALL | IT_STRING2, NULL, "Rotate Camera L", M_ChangeControl, gc_camleft },
+ {IT_CALL | IT_STRING2, NULL, "Rotate Camera R", M_ChangeControl, gc_camright },
};
static menuitem_t OP_MPControlsMenu[] =
@@ -1082,18 +1094,6 @@ static menuitem_t OP_MPControlsMenu[] =
{IT_CALL | IT_STRING2, NULL, "Ring Toss Normal", M_ChangeControl, gc_firenormal },
};
-static menuitem_t OP_CameraControlsMenu[] =
-{
- {IT_CALL | IT_STRING2, NULL, "Look Up", M_ChangeControl, gc_lookup },
- {IT_CALL | IT_STRING2, NULL, "Look Down", M_ChangeControl, gc_lookdown },
- {IT_CALL | IT_STRING2, NULL, "Rotate Camera L", M_ChangeControl, gc_camleft },
- {IT_CALL | IT_STRING2, NULL, "Rotate Camera R", M_ChangeControl, gc_camright },
- {IT_CALL | IT_STRING2, NULL, "Center View", M_ChangeControl, gc_centerview },
- {IT_CALL | IT_STRING2, NULL, "Mouselook", M_ChangeControl, gc_mouseaiming },
- {IT_CALL | IT_STRING2, NULL, "Reset Camera", M_ChangeControl, gc_camreset },
- {IT_CALL | IT_STRING2, NULL, "Toggle Chasecam", M_ChangeControl, gc_camtoggle },
-};
-
static menuitem_t OP_MiscControlsMenu[] =
{
{IT_CALL | IT_STRING2, NULL, "Custom Action 1", M_ChangeControl, gc_custom1 },
@@ -1101,6 +1101,10 @@ static menuitem_t OP_MiscControlsMenu[] =
{IT_CALL | IT_STRING2, NULL, "Custom Action 3", M_ChangeControl, gc_custom3 },
{IT_CALL | IT_STRING2, NULL, "Pause", M_ChangeControl, gc_pause },
+ {IT_CALL | IT_STRING2, NULL, "Screenshot", M_ChangeControl, gc_screenshot },
+ {IT_CALL | IT_STRING2, NULL, "Toggle GIF Recording", M_ChangeControl, gc_recordgif },
+ {IT_CALL | IT_STRING2, NULL, "Open/Close Menu (ESC)", M_ChangeControl, gc_systemmenu },
+ {IT_CALL | IT_STRING2, NULL, "Change Viewpoint", M_ChangeControl, gc_viewpoint },
{IT_CALL | IT_STRING2, NULL, "Console", M_ChangeControl, gc_console },
};
@@ -1113,6 +1117,9 @@ static menuitem_t OP_Joystick1Menu[] =
{IT_STRING | IT_CVAR, NULL, "Axis For Looking" , &cv_lookaxis , 60},
{IT_STRING | IT_CVAR, NULL, "Axis For Firing" , &cv_fireaxis , 70},
{IT_STRING | IT_CVAR, NULL, "Axis For NFiring" , &cv_firenaxis , 80},
+
+ {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook, 100},
+ {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook, 110},
};
static menuitem_t OP_Joystick2Menu[] =
@@ -1124,6 +1131,9 @@ static menuitem_t OP_Joystick2Menu[] =
{IT_STRING | IT_CVAR, NULL, "Axis For Looking" , &cv_lookaxis2 , 60},
{IT_STRING | IT_CVAR, NULL, "Axis For Firing" , &cv_fireaxis2 , 70},
{IT_STRING | IT_CVAR, NULL, "Axis For NFiring" , &cv_firenaxis2 , 80},
+
+ {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook2, 100},
+ {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook2, 110},
};
static menuitem_t OP_JoystickSetMenu[] =
@@ -1142,13 +1152,14 @@ static menuitem_t OP_MouseOptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Use Mouse", &cv_usemouse, 10},
- {IT_STRING | IT_CVAR, NULL, "Always MouseLook", &cv_alwaysfreelook, 30},
- {IT_STRING | IT_CVAR, NULL, "Mouse Move", &cv_mousemove, 40},
- {IT_STRING | IT_CVAR, NULL, "Invert Mouse", &cv_invertmouse, 50},
+ {IT_STRING | IT_CVAR, NULL, "First-Person MouseLook", &cv_alwaysfreelook, 30},
+ {IT_STRING | IT_CVAR, NULL, "Third-Person MouseLook", &cv_chasefreelook, 40},
+ {IT_STRING | IT_CVAR, NULL, "Mouse Move", &cv_mousemove, 50},
+ {IT_STRING | IT_CVAR, NULL, "Invert Mouse", &cv_invertmouse, 60},
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
- NULL, "Mouse X Speed", &cv_mousesens, 60},
+ NULL, "Mouse X Speed", &cv_mousesens, 70},
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
- NULL, "Mouse Y Speed", &cv_mouseysens, 70},
+ NULL, "Mouse Y Speed", &cv_mouseysens, 80},
};
static menuitem_t OP_Mouse2OptionsMenu[] =
@@ -1156,13 +1167,14 @@ static menuitem_t OP_Mouse2OptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Use Mouse 2", &cv_usemouse2, 10},
{IT_STRING | IT_CVAR, NULL, "Second Mouse Serial Port",
&cv_mouse2port, 20},
- {IT_STRING | IT_CVAR, NULL, "Always MouseLook", &cv_alwaysfreelook2, 30},
- {IT_STRING | IT_CVAR, NULL, "Mouse Move", &cv_mousemove2, 40},
- {IT_STRING | IT_CVAR, NULL, "Invert Mouse", &cv_invertmouse2, 50},
+ {IT_STRING | IT_CVAR, NULL, "First-Person MouseLook", &cv_alwaysfreelook2, 30},
+ {IT_STRING | IT_CVAR, NULL, "Third-Person MouseLook", &cv_chasefreelook2, 40},
+ {IT_STRING | IT_CVAR, NULL, "Mouse Move", &cv_mousemove2, 50},
+ {IT_STRING | IT_CVAR, NULL, "Invert Mouse", &cv_invertmouse2, 60},
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
- NULL, "Mouse X Speed", &cv_mousesens2, 60},
+ NULL, "Mouse X Speed", &cv_mousesens2, 70},
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
- NULL, "Mouse Y Speed", &cv_mouseysens2, 70},
+ NULL, "Mouse Y Speed", &cv_mouseysens2, 80},
};
static menuitem_t OP_VideoOptionsMenu[] =
@@ -1711,7 +1723,6 @@ menu_t OP_ControlsDef = DEFAULTMENUSTYLE("M_CONTRO", OP_ControlsMenu, &OP_MainDe
menu_t OP_ControlListDef = DEFAULTMENUSTYLE("M_CONTRO", OP_ControlListMenu, &OP_ControlsDef, 60, 30);
menu_t OP_MoveControlsDef = CONTROLMENUSTYLE(OP_MoveControlsMenu, &OP_ControlListDef);
menu_t OP_MPControlsDef = CONTROLMENUSTYLE(OP_MPControlsMenu, &OP_ControlListDef);
-menu_t OP_CameraControlsDef = CONTROLMENUSTYLE(OP_CameraControlsMenu, &OP_ControlListDef);
menu_t OP_MiscControlsDef = CONTROLMENUSTYLE(OP_MiscControlsMenu, &OP_ControlListDef);
menu_t OP_P1ControlsDef = DEFAULTMENUSTYLE("M_CONTRO", OP_P1ControlsMenu, &OP_ControlsDef, 60, 30);
menu_t OP_P2ControlsDef = DEFAULTMENUSTYLE("M_CONTRO", OP_P2ControlsMenu, &OP_ControlsDef, 60, 30);
@@ -2127,6 +2138,7 @@ boolean M_Responder(event_t *ev)
INT32 ch = -1;
// INT32 i;
static tic_t joywait = 0, mousewait = 0;
+ static INT32 pjoyx = 0, pjoyy = 0;
static INT32 pmousex = 0, pmousey = 0;
static INT32 lastx = 0, lasty = 0;
void (*routine)(INT32 choice); // for some casting problem
@@ -2142,63 +2154,84 @@ boolean M_Responder(event_t *ev)
// (but still allow shift keyup so caps doesn't get stuck)
return false;
}
- else if (ev->type == ev_keydown)
- {
- ch = ev->data1;
-
- // added 5-2-98 remap virtual keys (mouse & joystick buttons)
- switch (ch)
- {
- case KEY_MOUSE1:
- case KEY_JOY1:
- case KEY_JOY1 + 2:
- ch = KEY_ENTER;
- break;
- case KEY_JOY1 + 3:
- ch = 'n';
- break;
- case KEY_MOUSE1 + 1:
- case KEY_JOY1 + 1:
- ch = KEY_BACKSPACE;
- break;
- case KEY_HAT1:
- ch = KEY_UPARROW;
- break;
- case KEY_HAT1 + 1:
- ch = KEY_DOWNARROW;
- break;
- case KEY_HAT1 + 2:
- ch = KEY_LEFTARROW;
- break;
- case KEY_HAT1 + 3:
- ch = KEY_RIGHTARROW;
- break;
- }
- }
else if (menuactive)
{
- if (ev->type == ev_joystick && ev->data1 == 0 && joywait < I_GetTime())
+ if (ev->type == ev_keydown)
{
- if (ev->data3 == -1)
+ ch = ev->data1;
+
+ // added 5-2-98 remap virtual keys (mouse & joystick buttons)
+ switch (ch)
{
- ch = KEY_UPARROW;
- joywait = I_GetTime() + NEWTICRATE/7;
+ case KEY_MOUSE1:
+ case KEY_JOY1:
+ ch = KEY_ENTER;
+ break;
+ case KEY_JOY1 + 3:
+ ch = 'n';
+ break;
+ case KEY_MOUSE1 + 1:
+ case KEY_JOY1 + 1:
+ ch = KEY_ESCAPE;
+ break;
+ case KEY_JOY1 + 2:
+ ch = KEY_BACKSPACE;
+ break;
+ case KEY_HAT1:
+ ch = KEY_UPARROW;
+ break;
+ case KEY_HAT1 + 1:
+ ch = KEY_DOWNARROW;
+ break;
+ case KEY_HAT1 + 2:
+ ch = KEY_LEFTARROW;
+ break;
+ case KEY_HAT1 + 3:
+ ch = KEY_RIGHTARROW;
+ break;
}
- else if (ev->data3 == 1)
+ }
+ else if (ev->type == ev_joystick && ev->data1 == 0 && joywait < I_GetTime())
+ {
+ const INT32 jdeadzone = JOYAXISRANGE/4;
+ if (ev->data3 != INT32_MAX)
{
- ch = KEY_DOWNARROW;
- joywait = I_GetTime() + NEWTICRATE/7;
+ if (Joystick.bGamepadStyle || abs(ev->data3) > jdeadzone)
+ {
+ if (ev->data3 < 0 && pjoyy >= 0)
+ {
+ ch = KEY_UPARROW;
+ joywait = I_GetTime() + NEWTICRATE/7;
+ }
+ else if (ev->data3 > 0 && pjoyy <= 0)
+ {
+ ch = KEY_DOWNARROW;
+ joywait = I_GetTime() + NEWTICRATE/7;
+ }
+ pjoyy = ev->data3;
+ }
+ else
+ pjoyy = 0;
}
- if (ev->data2 == -1)
+ if (ev->data2 != INT32_MAX)
{
- ch = KEY_LEFTARROW;
- joywait = I_GetTime() + NEWTICRATE/17;
- }
- else if (ev->data2 == 1)
- {
- ch = KEY_RIGHTARROW;
- joywait = I_GetTime() + NEWTICRATE/17;
+ if (Joystick.bGamepadStyle || abs(ev->data2) > jdeadzone)
+ {
+ if (ev->data2 < 0 && pjoyx >= 0)
+ {
+ ch = KEY_LEFTARROW;
+ joywait = I_GetTime() + NEWTICRATE/17;
+ }
+ else if (ev->data2 > 0 && pjoyx <= 0)
+ {
+ ch = KEY_RIGHTARROW;
+ joywait = I_GetTime() + NEWTICRATE/17;
+ }
+ pjoyx = ev->data2;
+ }
+ else
+ pjoyx = 0;
}
}
else if (ev->type == ev_mouse && mousewait < I_GetTime())
@@ -2232,9 +2265,13 @@ boolean M_Responder(event_t *ev)
}
}
}
+ else if (ev->type == ev_keydown) // Preserve event for other responders
+ ch = ev->data1;
if (ch == -1)
return false;
+ else if (ch == gamecontrol[gc_systemmenu][0] || ch == gamecontrol[gc_systemmenu][1]) // allow remappable ESC key
+ ch = KEY_ESCAPE;
// F-Keys
if (!menuactive)
@@ -2636,7 +2673,7 @@ void M_StartControlPanel(void)
MPauseMenu[mpause_switchteam].status = IT_DISABLED;
MPauseMenu[mpause_psetup].status = IT_DISABLED;
- if ((server || adminplayer == consoleplayer))
+ if ((server || IsPlayerAdmin(consoleplayer)))
{
MPauseMenu[mpause_switchmap].status = IT_STRING | IT_CALL;
MPauseMenu[mpause_addons].status = IT_STRING | IT_CALL;
@@ -4519,7 +4556,7 @@ static void M_Options(INT32 choice)
(void)choice;
// if the player is not admin or server, disable server options
- OP_MainMenu[5].status = (Playing() && !(server || adminplayer == consoleplayer)) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
+ OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
// if the player is playing _at all_, disable the erase data options
OP_DataOptionsMenu[1].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
@@ -7458,6 +7495,7 @@ static void M_DrawControl(void)
}
static INT32 controltochange;
+static char controltochangetext[55];
static void M_ChangecontrolResponse(event_t *ev)
{
@@ -7465,8 +7503,8 @@ static void M_ChangecontrolResponse(event_t *ev)
INT32 found;
INT32 ch = ev->data1;
- // ESCAPE cancels
- if (ch != KEY_ESCAPE)
+ // ESCAPE cancels; dummy out PAUSE
+ if (ch != KEY_ESCAPE && ch != KEY_PAUSE)
{
switch (ev->type)
@@ -7525,8 +7563,28 @@ static void M_ChangecontrolResponse(event_t *ev)
G_CheckDoubleUsage(ch);
setupcontrols[control][found] = ch;
}
-
+ S_StartSound(NULL, sfx_strpst);
}
+ else if (ch == KEY_PAUSE)
+ {
+ static char tmp[155];
+ menu_t *prev = currentMenu->prevMenu;
+
+ if (controltochange == gc_pause)
+ sprintf(tmp, M_GetText("The \x82Pause Key \x80is enabled, but \nyou may select another key. \n\nHit another key for\n%s\nESC for Cancel"),
+ controltochangetext);
+ else
+ sprintf(tmp, M_GetText("The \x82Pause Key \x80is enabled, but \nit is not configurable. \n\nHit another key for\n%s\nESC for Cancel"),
+ controltochangetext);
+
+ M_StartMessage(tmp, M_ChangecontrolResponse, MM_EVENTHANDLER);
+ currentMenu->prevMenu = prev;
+
+ S_StartSound(NULL, sfx_s3k42);
+ return;
+ }
+ else
+ S_StartSound(NULL, sfx_skid);
M_StopMessage(0);
}
@@ -7538,6 +7596,7 @@ static void M_ChangeControl(INT32 choice)
controltochange = currentMenu->menuitems[choice].alphaKey;
sprintf(tmp, M_GetText("Hit the new key for\n%s\nESC for Cancel"),
currentMenu->menuitems[choice].text);
+ strncpy(controltochangetext, currentMenu->menuitems[choice].text, 55);
M_StartMessage(tmp, M_ChangecontrolResponse, MM_EVENTHANDLER);
}
diff --git a/src/m_misc.c b/src/m_misc.c
index cd87c9849..a4f53c711 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -37,6 +37,7 @@
#include "d_main.h"
#include "m_argv.h"
#include "i_system.h"
+#include "command.h" // cv_execversion
#include "m_anigif.h"
@@ -440,7 +441,18 @@ void Command_LoadConfig_f(void)
strcpy(configfile, COM_Argv(1));
FIL_ForceExtension(configfile, ".cfg");
+
+ // temporarily reset execversion to default
+ cv_execversion.flags &= ~CV_HIDEN;
+ COM_BufInsertText(va("%s \"%s\"\n", cv_execversion.name, cv_execversion.defaultvalue));
+ CV_InitFilterVar();
+
+ // exec the config
COM_BufInsertText(va("exec \"%s\"\n", configfile));
+
+ // don't filter anymore vars and don't let this convsvar be changed
+ COM_BufInsertText(va("%s \"%d\"\n", cv_execversion.name, MODVERSION));
+ cv_execversion.flags |= CV_HIDEN;
}
/** Saves the current configuration and loads another.
@@ -477,10 +489,23 @@ void M_FirstLoadConfig(void)
// load default control
G_Controldefault();
+ // register execversion here before we load any configs
+ CV_RegisterVar(&cv_execversion);
+
+ // temporarily reset execversion to default
+ // we shouldn't need to do this, but JUST in case...
+ cv_execversion.flags &= ~CV_HIDEN;
+ COM_BufInsertText(va("%s \"%s\"\n", cv_execversion.name, cv_execversion.defaultvalue));
+ CV_InitFilterVar();
+
// load config, make sure those commands doesnt require the screen...
COM_BufInsertText(va("exec \"%s\"\n", configfile));
// no COM_BufExecute() needed; that does it right away
+ // don't filter anymore vars and don't let this convsvar be changed
+ COM_BufInsertText(va("%s \"%d\"\n", cv_execversion.name, MODVERSION));
+ cv_execversion.flags |= CV_HIDEN;
+
// make sure I_Quit() will write back the correct config
// (do not write back the config if it crash before)
gameconfig_loaded = true;
@@ -493,6 +518,7 @@ void M_FirstLoadConfig(void)
void M_SaveConfig(const char *filename)
{
FILE *f;
+ char *filepath;
// make sure not to write back the config until it's been correctly loaded
if (!gameconfig_loaded)
@@ -507,13 +533,20 @@ void M_SaveConfig(const char *filename)
return;
}
- f = fopen(filename, "w");
+ // append srb2home to beginning of filename
+ // but check if srb2home isn't already there, first
+ if (!strstr(filename, srb2home))
+ filepath = va(pandf,srb2home, filename);
+ else
+ filepath = Z_StrDup(filename);
+
+ f = fopen(filepath, "w");
// change it only if valid
if (f)
- strcpy(configfile, filename);
+ strcpy(configfile, filepath);
else
{
- CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), filename);
+ CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), filepath);
return;
}
}
@@ -536,6 +569,10 @@ void M_SaveConfig(const char *filename)
// header message
fprintf(f, "// SRB2 configuration file.\n");
+ // print execversion FIRST, because subsequent consvars need to be filtered
+ // always print current MODVERSION
+ fprintf(f, "%s \"%d\"\n", cv_execversion.name, MODVERSION);
+
// FIXME: save key aliases if ever implemented..
CV_SaveVariables(f);
@@ -1494,9 +1531,9 @@ boolean M_ScreenshotResponder(event_t *ev)
return false;
ch = ev->data1;
- if (ch == KEY_F8)
+ if (ch == KEY_F8 || ch == gamecontrol[gc_screenshot][0] || ch == gamecontrol[gc_screenshot][1]) // remappable F8
M_ScreenShot();
- else if (ch == KEY_F9)
+ else if (ch == KEY_F9 || ch == gamecontrol[gc_recordgif][0] || ch == gamecontrol[gc_recordgif][1]) // remappable F9
((moviemode) ? M_StopMovie : M_StartMovie)();
else
return false;
diff --git a/src/p_floor.c b/src/p_floor.c
index aca92e091..6db4310ac 100644
--- a/src/p_floor.c
+++ b/src/p_floor.c
@@ -811,6 +811,8 @@ void T_BounceCheese(levelspecthink_t *bouncer)
{
bouncer->sector->ceilingheight = actionsector->ceilingheight;
bouncer->sector->floorheight = bouncer->sector->ceilingheight - (halfheight*2);
+ T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, 0, 1, -1); // update things on ceiling
+ T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, 0, 0, -1); // update things on floor
P_RecalcPrecipInSector(actionsector);
bouncer->sector->ceilingdata = NULL;
bouncer->sector->floordata = NULL;
@@ -825,6 +827,8 @@ void T_BounceCheese(levelspecthink_t *bouncer)
{
bouncer->sector->ceilingheight = floorheight + (halfheight << 1);
bouncer->sector->floorheight = floorheight;
+ T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, 0, 1, -1); // update things on ceiling
+ T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, 0, 0, -1); // update things on floor
P_RecalcPrecipInSector(actionsector);
bouncer->sector->ceilingdata = NULL;
bouncer->sector->floordata = NULL;
@@ -841,9 +845,9 @@ void T_BounceCheese(levelspecthink_t *bouncer)
}
T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->ceilingheight -
- 70*FRACUNIT, 0, 1, -1); // move floor
+ 70*FRACUNIT, 0, 1, -1); // move ceiling
T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->floorheight - 70*FRACUNIT,
- 0, 0, -1); // move ceiling
+ 0, 0, -1); // move floor
bouncer->sector->floorspeed = -bouncer->speed/2;
bouncer->sector->ceilspeed = 42;
@@ -893,6 +897,8 @@ void T_BounceCheese(levelspecthink_t *bouncer)
{
bouncer->sector->floorheight = bouncer->floorwasheight;
bouncer->sector->ceilingheight = bouncer->ceilingwasheight;
+ T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, 0, 1, -1); // update things on ceiling
+ T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, 0, 0, -1); // update things on floor
bouncer->sector->ceilingdata = NULL;
bouncer->sector->floordata = NULL;
bouncer->sector->floorspeed = 0;
@@ -1818,6 +1824,7 @@ void T_ThwompSector(levelspecthink_t *thwomp)
#define ceilingwasheight vars[5]
fixed_t thwompx, thwompy;
sector_t *actionsector;
+ ffloor_t *rover = NULL;
INT32 secnum;
// If you just crashed down, wait a second before coming back up.
@@ -1832,7 +1839,16 @@ void T_ThwompSector(levelspecthink_t *thwomp)
secnum = P_FindSectorFromTag((INT16)thwomp->vars[0], -1);
if (secnum > 0)
+ {
actionsector = §ors[secnum];
+
+ // Look for thwomp FFloor
+ for (rover = actionsector->ffloors; rover; rover = rover->next)
+ {
+ if (rover->master == thwomp->sourceline)
+ break;
+ }
+ }
else
return; // Bad bad bad!
@@ -1921,10 +1937,13 @@ void T_ThwompSector(levelspecthink_t *thwomp)
{
mobj_t *mp = (void *)&actionsector->soundorg;
- if (thwomp->sourceline->flags & ML_EFFECT4)
- S_StartSound(mp, sides[thwomp->sourceline->sidenum[0]].textureoffset>>FRACBITS);
- else
- S_StartSound(mp, sfx_thwomp);
+ if (!rover || (rover->flags & FF_EXISTS))
+ {
+ if (thwomp->sourceline->flags & ML_EFFECT4)
+ S_StartSound(mp, sides[thwomp->sourceline->sidenum[0]].textureoffset>>FRACBITS);
+ else
+ S_StartSound(mp, sfx_thwomp);
+ }
thwomp->direction = 1; // start heading back up
thwomp->distance = TICRATE; // but only after a small delay
@@ -1938,18 +1957,22 @@ void T_ThwompSector(levelspecthink_t *thwomp)
thinker_t *th;
mobj_t *mo;
- // scan the thinkers to find players!
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ if (!rover || (rover->flags & FF_EXISTS))
{
- if (th->function.acp1 != (actionf_p1)P_MobjThinker)
- continue;
-
- mo = (mobj_t *)th;
- if (mo->type == MT_PLAYER && mo->health && mo->z <= thwomp->sector->ceilingheight
- && P_AproxDistance(thwompx - mo->x, thwompy - mo->y) <= 96*FRACUNIT)
+ // scan the thinkers to find players!
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
- thwomp->direction = -1;
- break;
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo = (mobj_t *)th;
+ if (mo->type == MT_PLAYER && mo->health && mo->player && !mo->player->spectator
+ && mo->z <= thwomp->sector->ceilingheight
+ && P_AproxDistance(thwompx - mo->x, thwompy - mo->y) <= 96*FRACUNIT)
+ {
+ thwomp->direction = -1;
+ break;
+ }
}
}
@@ -2103,6 +2126,7 @@ void T_EachTimeThinker(levelspecthink_t *eachtime)
boolean floortouch = false;
fixed_t bottomheight, topheight;
msecnode_t *node;
+ ffloor_t *rover;
for (i = 0; i < MAXPLAYERS; i++)
{
@@ -2150,6 +2174,19 @@ void T_EachTimeThinker(levelspecthink_t *eachtime)
{
targetsec = §ors[targetsecnum];
+ // Find the FOF corresponding to the control linedef
+ for (rover = targetsec->ffloors; rover; rover = rover->next)
+ {
+ if (rover->master == sec->lines[i])
+ break;
+ }
+
+ if (!rover) // This should be impossible, but don't complain if it is the case somehow
+ continue;
+
+ if (!(rover->flags & FF_EXISTS)) // If the FOF does not "exist", we pretend that nobody's there
+ continue;
+
for (j = 0; j < MAXPLAYERS; j++)
{
if (!playeringame[j])
diff --git a/src/p_map.c b/src/p_map.c
index 865877c90..95ad02588 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -777,14 +777,14 @@ static boolean PIT_CheckThing(mobj_t *thing)
// not (your direction) xor (stored direction)
// In other words, you can't u-turn and respawn rings near the drone.
if (pl->bonustime && (pl->pflags & PF_NIGHTSMODE) && (INT32)leveltime > droneobj->extravalue2 && (
- !(pl->anotherflyangle >= 90 && pl->anotherflyangle <= 270)
- ^ (droneobj->extravalue1 >= 90 && droneobj->extravalue1 <= 270)
+ !(pl->flyangle > 90 && pl->flyangle < 270)
+ ^ (droneobj->extravalue1 > 90 && droneobj->extravalue1 < 270)
))
{
// Reload all the fancy ring stuff!
P_ReloadRings();
}
- droneobj->extravalue1 = pl->anotherflyangle;
+ droneobj->extravalue1 = pl->flyangle;
droneobj->extravalue2 = (INT32)leveltime + TICRATE;
}
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 7fa781153..4fea15802 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -3506,7 +3506,7 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
if (player->pflags & PF_FLIPCAM && !(player->pflags & PF_NIGHTSMODE) && player->mo->eflags & MFE_VERTICALFLIP)
postimg = postimg_flip;
- else if (player->awayviewtics && player->awayviewmobj != NULL) // Camera must obviously exist
+ else if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj)) // Camera must obviously exist
{
camera_t dummycam;
dummycam.subsector = player->awayviewmobj->subsector;
@@ -9198,9 +9198,6 @@ ML_NOCLIMB : Direction not controllable
// the bumper in 30 degree increments.
mobj->threshold = (mthing->options & 15) % 12; // It loops over, etc
P_SetMobjState(mobj, mobj->info->spawnstate+mobj->threshold);
-
- // you can shut up now, OBJECTFLIP. And all of the other options, for that matter.
- mthing->options &= ~0xF;
break;
case MT_EGGCAPSULE:
if (mthing->angle <= 0)
@@ -9388,6 +9385,14 @@ ML_NOCLIMB : Direction not controllable
}
}
+ // ignore MTF_ flags and return early
+ if (i == MT_NIGHTSBUMPER)
+ {
+ mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
+ mthing->mobj = mobj;
+ return;
+ }
+
mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
if ((mthing->options & MTF_AMBUSH)
diff --git a/src/p_saveg.c b/src/p_saveg.c
index c8c38d3cb..12ee1345b 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -489,16 +489,34 @@ static void P_NetArchiveWorld(void)
UINT8 *put;
// reload the map just to see difference
- const mapsector_t *ms;
- const mapsidedef_t *msd;
- const maplinedef_t *mld;
+ mapsector_t *ms;
+ mapsidedef_t *msd;
+ maplinedef_t *mld;
const sector_t *ss = sectors;
UINT8 diff, diff2;
WRITEUINT32(save_p, ARCHIVEBLOCK_WORLD);
put = save_p;
- ms = W_CacheLumpNum(lastloadedmaplumpnum+ML_SECTORS, PU_CACHE);
+ if (W_IsLumpWad(lastloadedmaplumpnum)) // welp it's a map wad in a pk3
+ { // HACK: Open wad file rather quickly so we can get the data from the relevant lumps
+ UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
+ filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
+#define retrieve_mapdata(d, f)\
+ d = Z_Malloc((f)->size, PU_CACHE, NULL); \
+ M_Memcpy(d, wadData + (f)->filepos, (f)->size)
+ retrieve_mapdata(ms, fileinfo + ML_SECTORS);
+ retrieve_mapdata(mld, fileinfo + ML_LINEDEFS);
+ retrieve_mapdata(msd, fileinfo + ML_SIDEDEFS);
+#undef retrieve_mapdata
+ Z_Free(wadData); // we're done with this now
+ }
+ else // phew it's just a WAD
+ {
+ ms = W_CacheLumpNum(lastloadedmaplumpnum+ML_SECTORS, PU_CACHE);
+ mld = W_CacheLumpNum(lastloadedmaplumpnum+ML_LINEDEFS, PU_CACHE);
+ msd = W_CacheLumpNum(lastloadedmaplumpnum+ML_SIDEDEFS, PU_CACHE);
+ }
for (i = 0; i < numsectors; i++, ss++, ms++)
{
@@ -950,6 +968,7 @@ typedef enum
tc_bouncecheese,
tc_startcrumble,
tc_marioblock,
+ tc_marioblockchecker,
tc_spikesector,
tc_floatsector,
tc_bridgethinker,
@@ -1259,7 +1278,10 @@ static void SaveSpecialLevelThinker(const thinker_t *th, const UINT8 type)
size_t i;
WRITEUINT8(save_p, type);
for (i = 0; i < 16; i++)
+ {
WRITEFIXED(save_p, ht->vars[i]); //var[16]
+ WRITEFIXED(save_p, ht->var2s[i]); //var[16]
+ }
WRITEUINT32(save_p, SaveLine(ht->sourceline));
WRITEUINT32(save_p, SaveSector(ht->sector));
}
@@ -1772,6 +1794,11 @@ static void P_NetArchiveThinkers(void)
SaveSpecialLevelThinker(th, tc_marioblock);
continue;
}
+ else if (th->function.acp1 == (actionf_p1)T_MarioBlockChecker)
+ {
+ SaveSpecialLevelThinker(th, tc_marioblockchecker);
+ continue;
+ }
else if (th->function.acp1 == (actionf_p1)T_SpikeSector)
{
SaveSpecialLevelThinker(th, tc_spikesector);
@@ -2155,7 +2182,10 @@ static void LoadSpecialLevelThinker(actionf_p1 thinker, UINT8 floorOrCeiling)
size_t i;
ht->thinker.function.acp1 = thinker;
for (i = 0; i < 16; i++)
+ {
ht->vars[i] = READFIXED(save_p); //var[16]
+ ht->var2s[i] = READFIXED(save_p); //var[16]
+ }
ht->sourceline = LoadLine(READUINT32(save_p));
ht->sector = LoadSector(READUINT32(save_p));
@@ -2728,6 +2758,10 @@ static void P_NetUnArchiveThinkers(void)
LoadSpecialLevelThinker((actionf_p1)T_MarioBlock, 3);
break;
+ case tc_marioblockchecker:
+ LoadSpecialLevelThinker((actionf_p1)T_MarioBlockChecker, 0);
+ break;
+
case tc_spikesector:
LoadSpecialLevelThinker((actionf_p1)T_SpikeSector, 0);
break;
diff --git a/src/p_setup.c b/src/p_setup.c
index ca8f066e9..8cf0365a6 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -350,16 +350,13 @@ UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade)
* \param lump VERTEXES lump number.
* \sa ML_VERTEXES
*/
-static inline void P_LoadVertexes(lumpnum_t lumpnum)
+
+static inline void P_LoadRawVertexes(UINT8 *data, size_t i)
{
- UINT8 *data;
- size_t i;
mapvertex_t *ml;
vertex_t *li;
- // Determine number of lumps:
- // total lump length / vertex record length.
- numvertexes = W_LumpLength(lumpnum) / sizeof (mapvertex_t);
+ numvertexes = i / sizeof (mapvertex_t);
if (numvertexes <= 0)
I_Error("Level has no vertices"); // instead of crashing
@@ -367,9 +364,6 @@ static inline void P_LoadVertexes(lumpnum_t lumpnum)
// Allocate zone memory for buffer.
vertexes = Z_Calloc(numvertexes * sizeof (*vertexes), PU_LEVEL, NULL);
- // Load data into cache.
- data = W_CacheLumpNum(lumpnum, PU_STATIC);
-
ml = (mapvertex_t *)data;
li = vertexes;
@@ -379,11 +373,16 @@ static inline void P_LoadVertexes(lumpnum_t lumpnum)
li->x = SHORT(ml->x)<y = SHORT(ml->y)<numlights = 0;
li->rlights = NULL;
}
+}
+static void P_LoadSegs(lumpnum_t lumpnum)
+{
+ UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+ P_LoadRawSegs(data, W_LumpLength(lumpnum));
Z_Free(data);
}
+
/** Loads the SSECTORS resource from a level.
*
* \param lump Lump number of the SSECTORS resource.
* \sa ::ML_SSECTORS
*/
-static inline void P_LoadSubsectors(lumpnum_t lumpnum)
+static inline void P_LoadRawSubsectors(void *data, size_t i)
{
- void *data;
- size_t i;
mapsubsector_t *ms;
subsector_t *ss;
- numsubsectors = W_LumpLength(lumpnum) / sizeof (mapsubsector_t);
+ numsubsectors = i / sizeof (mapsubsector_t);
if (numsubsectors <= 0)
I_Error("Level has no subsectors (did you forget to run it through a nodesbuilder?)");
ss = subsectors = Z_Calloc(numsubsectors * sizeof (*subsectors), PU_LEVEL, NULL);
- data = W_CacheLumpNum(lumpnum,PU_STATIC);
ms = (mapsubsector_t *)data;
@@ -506,7 +505,12 @@ static inline void P_LoadSubsectors(lumpnum_t lumpnum)
#endif
ss->validcount = 0;
}
+}
+static void P_LoadSubsectors(lumpnum_t lumpnum)
+{
+ UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+ P_LoadRawSubsectors(data, W_LumpLength(lumpnum));
Z_Free(data);
}
@@ -640,29 +644,31 @@ INT32 P_CheckLevelFlat(const char *flatname)
return (INT32)i;
}
-static void P_LoadSectors(lumpnum_t lumpnum)
+// Sets up the ingame sectors structures.
+// Lumpnum is the lumpnum of a SECTORS lump.
+static void P_LoadRawSectors(UINT8 *data, size_t i)
{
- UINT8 *data;
- size_t i;
mapsector_t *ms;
sector_t *ss;
levelflat_t *foundflats;
- numsectors = W_LumpLength(lumpnum) / sizeof (mapsector_t);
+ // We count how many sectors we got.
+ numsectors = i / sizeof (mapsector_t);
if (numsectors <= 0)
I_Error("Level has no sectors");
+
+ // Allocate as much memory as we need into the global sectors table.
sectors = Z_Calloc(numsectors*sizeof (*sectors), PU_LEVEL, NULL);
- data = W_CacheLumpNum(lumpnum,PU_STATIC);
-
- //Fab : FIXME: allocate for whatever number of flats
- // 512 different flats per level should be plenty
+ // Allocate a big chunk of memory as big as our MAXLEVELFLATS limit.
+ //Fab : FIXME: allocate for whatever number of flats - 512 different flats per level should be plenty
foundflats = calloc(MAXLEVELFLATS, sizeof (*foundflats));
if (foundflats == NULL)
I_Error("Ran out of memory while loading sectors\n");
numlevelflats = 0;
+ // For each counted sector, copy the sector raw data from our cache pointer ms, to the global table pointer ss.
ms = (mapsector_t *)data;
ss = sectors;
for (i = 0; i < numsectors; i++, ss++, ms++)
@@ -670,9 +676,6 @@ static void P_LoadSectors(lumpnum_t lumpnum)
ss->floorheight = SHORT(ms->floorheight)<ceilingheight = SHORT(ms->ceilingheight)<floorpic = P_AddLevelFlat(ms->floorpic, foundflats);
ss->ceilingpic = P_AddLevelFlat(ms->ceilingpic, foundflats);
@@ -737,8 +740,6 @@ static void P_LoadSectors(lumpnum_t lumpnum)
#endif // ----- end special tricks -----
}
- Z_Free(data);
-
// set the sky flat num
skyflatnum = P_AddLevelFlat(SKYFLATNAME, foundflats);
@@ -750,22 +751,26 @@ static void P_LoadSectors(lumpnum_t lumpnum)
P_SetupLevelFlatAnims();
}
+static void P_LoadSectors(lumpnum_t lumpnum)
+{
+ UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+ P_LoadRawSectors(data, W_LumpLength(lumpnum));
+ Z_Free(data);
+}
+
//
// P_LoadNodes
//
-static void P_LoadNodes(lumpnum_t lumpnum)
+static void P_LoadRawNodes(UINT8 *data, size_t i)
{
- UINT8 *data;
- size_t i;
UINT8 j, k;
mapnode_t *mn;
node_t *no;
- numnodes = W_LumpLength(lumpnum) / sizeof (mapnode_t);
+ numnodes = i / sizeof (mapnode_t);
if (numnodes <= 0)
I_Error("Level has no nodes");
nodes = Z_Calloc(numnodes * sizeof (*nodes), PU_LEVEL, NULL);
- data = W_CacheLumpNum(lumpnum, PU_STATIC);
mn = (mapnode_t *)data;
no = nodes;
@@ -783,7 +788,12 @@ static void P_LoadNodes(lumpnum_t lumpnum)
no->bbox[j][k] = SHORT(mn->bbox[j][k])<slopetype = ST_VERTICAL;
else if (!ld->dy)
ld->slopetype = ST_HORIZONTAL;
- else if (FixedDiv(ld->dy, ld->dx) > 0)
+ else if ((ld->dy > 0) == (ld->dx > 0))
ld->slopetype = ST_POSITIVE;
else
ld->slopetype = ST_NEGATIVE;
@@ -1219,7 +1226,7 @@ static void P_LoadLineDefs(lumpnum_t lumpnum)
if (ld->sidenum[j] != 0xffff && ld->sidenum[j] >= (UINT16)numsides)
{
ld->sidenum[j] = 0xffff;
- CONS_Debug(DBG_SETUP, "P_LoadLineDefs: linedef %s has out-of-range sidedef number\n", sizeu1(numlines-i-1));
+ CONS_Debug(DBG_SETUP, "P_LoadRawLineDefs: linedef %s has out-of-range sidedef number\n", sizeu1(numlines-i-1));
}
}
}
@@ -1234,14 +1241,14 @@ static void P_LoadLineDefs(lumpnum_t lumpnum)
{
ld->sidenum[0] = 0; // Substitute dummy sidedef for missing right side
// cph - print a warning about the bug
- CONS_Debug(DBG_SETUP, "P_LoadLineDefs: linedef %s missing first sidedef\n", sizeu1(numlines-i-1));
+ CONS_Debug(DBG_SETUP, "P_LoadRawLineDefs: linedef %s missing first sidedef\n", sizeu1(numlines-i-1));
}
if ((ld->sidenum[1] == 0xffff) && (ld->flags & ML_TWOSIDED))
{
ld->flags &= ~ML_TWOSIDED; // Clear 2s flag for missing left side
// cph - print a warning about the bug
- CONS_Debug(DBG_SETUP, "P_LoadLineDefs: linedef %s has two-sided flag set, but no second sidedef\n", sizeu1(numlines-i-1));
+ CONS_Debug(DBG_SETUP, "P_LoadRawLineDefs: linedef %s has two-sided flag set, but no second sidedef\n", sizeu1(numlines-i-1));
}
if (ld->sidenum[0] != 0xffff && ld->special)
@@ -1253,7 +1260,12 @@ static void P_LoadLineDefs(lumpnum_t lumpnum)
ld->polyobj = NULL;
#endif
}
+}
+static void P_LoadLineDefs(lumpnum_t lumpnum)
+{
+ UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+ P_LoadRawLineDefs(data, W_LumpLength(lumpnum));
Z_Free(data);
}
@@ -1355,22 +1367,24 @@ static void P_LoadLineDefs2(void)
}
}
-//
-// P_LoadSideDefs
-//
-static inline void P_LoadSideDefs(lumpnum_t lumpnum)
+
+
+static inline void P_LoadRawSideDefs(size_t i)
{
- numsides = W_LumpLength(lumpnum) / sizeof (mapsidedef_t);
+ numsides = i / sizeof (mapsidedef_t);
if (numsides <= 0)
I_Error("Level has no sidedefs");
sides = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
}
-// Delay loading texture names until after loaded linedefs.
-
-static void P_LoadSideDefs2(lumpnum_t lumpnum)
+static inline void P_LoadSideDefs(lumpnum_t lumpnum)
+{
+ P_LoadRawSideDefs(W_LumpLength(lumpnum));
+}
+
+
+static void P_LoadRawSideDefs2(void *data)
{
- UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
UINT16 i;
INT32 num;
@@ -1388,7 +1402,7 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
if (sector_num >= numsectors)
{
- CONS_Debug(DBG_SETUP, "P_LoadSideDefs2: sidedef %u has out-of-range sector num %u\n", i, sector_num);
+ CONS_Debug(DBG_SETUP, "P_LoadRawSideDefs2: sidedef %u has out-of-range sector num %u\n", i, sector_num);
sector_num = 0;
}
sd->sector = sec = §ors[sector_num];
@@ -1530,6 +1544,8 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
sd->text[6] = 0;
break;
}
+
+ case 4: // Speed pad parameters
case 414: // Play SFX
{
sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
@@ -1543,6 +1559,9 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
break;
}
+ case 9: // Mace parameters
+ case 14: // Bustable block parameters
+ case 15: // Fan particle spawner parameters
case 425: // Calls P_SetMobjState on calling mobj
case 434: // Custom Power
case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors
@@ -1597,11 +1616,18 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
break;
}
}
-
- Z_Free(data);
R_ClearTextureNumCache(true);
}
+// Delay loading texture names until after loaded linedefs.
+static void P_LoadSideDefs2(lumpnum_t lumpnum)
+{
+ UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
+ P_LoadRawSideDefs2(data);
+ Z_Free(data);
+}
+
+
static boolean LineInBlock(fixed_t cx1, fixed_t cy1, fixed_t cx2, fixed_t cy2, fixed_t bx1, fixed_t by1)
{
fixed_t bbox[4];
@@ -1857,6 +1883,30 @@ static void P_CreateBlockMap(void)
}
}
+// Split from P_LoadBlockMap for convenience
+// -- Monster Iestyn 08/01/18
+static void P_ReadBlockMapLump(INT16 *wadblockmaplump, size_t count)
+{
+ size_t i;
+ blockmaplump = Z_Calloc(sizeof (*blockmaplump) * count, PU_LEVEL, NULL);
+
+ // killough 3/1/98: Expand wad blockmap into larger internal one,
+ // by treating all offsets except -1 as unsigned and zero-extending
+ // them. This potentially doubles the size of blockmaps allowed,
+ // because Doom originally considered the offsets as always signed.
+
+ blockmaplump[0] = SHORT(wadblockmaplump[0]);
+ blockmaplump[1] = SHORT(wadblockmaplump[1]);
+ blockmaplump[2] = (INT32)(SHORT(wadblockmaplump[2])) & 0xffff;
+ blockmaplump[3] = (INT32)(SHORT(wadblockmaplump[3])) & 0xffff;
+
+ for (i = 4; i < count; i++)
+ {
+ INT16 t = SHORT(wadblockmaplump[i]); // killough 3/1/98
+ blockmaplump[i] = t == -1 ? (INT32)-1 : (INT32) t & 0xffff;
+ }
+}
+
//
// P_LoadBlockMap
//
@@ -1883,38 +1933,20 @@ static boolean P_LoadBlockMap(lumpnum_t lumpnum)
return false;
{
- size_t i;
INT16 *wadblockmaplump = malloc(count); //INT16 *wadblockmaplump = W_CacheLumpNum (lump, PU_LEVEL);
-
- if (wadblockmaplump) W_ReadLump(lumpnum, wadblockmaplump);
- else return false;
+ if (!wadblockmaplump)
+ return false;
+ W_ReadLump(lumpnum, wadblockmaplump);
count /= 2;
- blockmaplump = Z_Calloc(sizeof (*blockmaplump) * count, PU_LEVEL, 0);
-
- // killough 3/1/98: Expand wad blockmap into larger internal one,
- // by treating all offsets except -1 as unsigned and zero-extending
- // them. This potentially doubles the size of blockmaps allowed,
- // because Doom originally considered the offsets as always signed.
-
- blockmaplump[0] = SHORT(wadblockmaplump[0]);
- blockmaplump[1] = SHORT(wadblockmaplump[1]);
- blockmaplump[2] = (INT32)(SHORT(wadblockmaplump[2])) & 0xffff;
- blockmaplump[3] = (INT32)(SHORT(wadblockmaplump[3])) & 0xffff;
-
- for (i = 4; i < count; i++)
- {
- INT16 t = SHORT(wadblockmaplump[i]); // killough 3/1/98
- blockmaplump[i] = t == -1 ? (INT32)-1 : (INT32) t & 0xffff;
- }
-
+ P_ReadBlockMapLump(wadblockmaplump, count);
free(wadblockmaplump);
-
- bmaporgx = blockmaplump[0]<= 0x20000)
+ return false;
+
+ CONS_Printf("Reading blockmap lump for pk3...\n");
+
+ // no need to malloc anything, assume the data is uncompressed for now
+ count /= 2;
+ P_ReadBlockMapLump((INT16 *)data, count);
+
+ bmaporgx = blockmaplump[0]<infotableofs);
+ fileinfo += ML_THINGS; // we only need the THINGS lump
+ P_PrepareRawThings(wadData + fileinfo->filepos, fileinfo->size);
+ Z_Free(wadData); // we're done with this now
+ }
+ else // phew it's just a WAD
+ P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
P_LoadThings();
P_SpawnSecretItems(true);
@@ -2690,7 +2802,12 @@ boolean P_SetupLevel(boolean skipprecip)
}
// internal game map
- lastloadedmaplumpnum = W_GetNumForName(maplumpname = G_BuildMapName(gamemap));
+ maplumpname = G_BuildMapName(gamemap);
+ //lastloadedmaplumpnum = LUMPERROR;
+ lastloadedmaplumpnum = W_CheckNumForName(maplumpname);
+
+ if (lastloadedmaplumpnum == INT16_MAX)
+ I_Error("Map %s not found.\n", maplumpname);
R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);
CON_SetupBackColormap();
@@ -2700,38 +2817,93 @@ boolean P_SetupLevel(boolean skipprecip)
P_MakeMapMD5(lastloadedmaplumpnum, &mapmd5);
- // note: most of this ordering is important
- loadedbm = P_LoadBlockMap(lastloadedmaplumpnum + ML_BLOCKMAP);
+ // HACK ALERT: Cache the WAD, get the map data into the tables, free memory.
+ // As it is implemented right now, we're assuming an uncompressed WAD.
+ // (As in, a normal PWAD, not ZWAD or anything. The lump itself can be compressed.)
+ // We're not accounting for extra lumps and scrambled lump positions. Any additional data will cause an error.
+ if (W_IsLumpWad(lastloadedmaplumpnum))
+ {
+ // Remember that we're assuming that the WAD will have a specific set of lumps in a specific order.
+ UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
+ //filelump_t *fileinfo = wadData + ((wadinfo_t *)wadData)->infotableofs;
+ filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
+ UINT32 numlumps = ((wadinfo_t *)wadData)->numlumps;
- P_LoadVertexes(lastloadedmaplumpnum + ML_VERTEXES);
- P_LoadSectors(lastloadedmaplumpnum + ML_SECTORS);
+ if (numlumps < ML_REJECT) // at least 9 lumps should be in the wad for a map to be loaded
+ {
+ I_Error("Bad WAD file for map %s!\n", maplumpname);
+ }
- P_LoadSideDefs(lastloadedmaplumpnum + ML_SIDEDEFS);
+ if (numlumps > ML_BLOCKMAP) // enough room for a BLOCKMAP lump at least
+ {
+ loadedbm = P_LoadRawBlockMap(
+ wadData + (fileinfo + ML_BLOCKMAP)->filepos,
+ (fileinfo + ML_BLOCKMAP)->size,
+ (fileinfo + ML_BLOCKMAP)->name);
+ }
+ P_LoadRawVertexes(wadData + (fileinfo + ML_VERTEXES)->filepos, (fileinfo + ML_VERTEXES)->size);
+ P_LoadRawSectors(wadData + (fileinfo + ML_SECTORS)->filepos, (fileinfo + ML_SECTORS)->size);
+ P_LoadRawSideDefs((fileinfo + ML_SIDEDEFS)->size);
+ P_LoadRawLineDefs(wadData + (fileinfo + ML_LINEDEFS)->filepos, (fileinfo + ML_LINEDEFS)->size);
+ P_LoadRawSideDefs2(wadData + (fileinfo + ML_SIDEDEFS)->filepos);
+ P_LoadRawSubsectors(wadData + (fileinfo + ML_SSECTORS)->filepos, (fileinfo + ML_SSECTORS)->size);
+ P_LoadRawNodes(wadData + (fileinfo + ML_NODES)->filepos, (fileinfo + ML_NODES)->size);
+ P_LoadRawSegs(wadData + (fileinfo + ML_SEGS)->filepos, (fileinfo + ML_SEGS)->size);
+ if (numlumps > ML_REJECT) // enough room for a REJECT lump at least
+ {
+ P_LoadRawReject(
+ wadData + (fileinfo + ML_REJECT)->filepos,
+ (fileinfo + ML_REJECT)->size,
+ (fileinfo + ML_REJECT)->name);
+ }
- P_LoadLineDefs(lastloadedmaplumpnum + ML_LINEDEFS);
- if (!loadedbm)
- P_CreateBlockMap(); // Graue 02-29-2004
- P_LoadSideDefs2(lastloadedmaplumpnum + ML_SIDEDEFS);
+ // Important: take care of the ordering of the next functions.
+ if (!loadedbm)
+ P_CreateBlockMap(); // Graue 02-29-2004
+ P_LoadLineDefs2();
+ P_GroupLines();
+ numdmstarts = numredctfstarts = numbluectfstarts = 0;
- P_LoadLineDefs2();
- P_LoadSubsectors(lastloadedmaplumpnum + ML_SSECTORS);
- P_LoadNodes(lastloadedmaplumpnum + ML_NODES);
- P_LoadSegs(lastloadedmaplumpnum + ML_SEGS);
- P_LoadReject(lastloadedmaplumpnum + ML_REJECT);
- P_GroupLines();
+ // reset the player starts
+ for (i = 0; i < MAXPLAYERS; i++)
+ playerstarts[i] = NULL;
+ for (i = 0; i < 2; i++)
+ skyboxmo[i] = NULL;
+ P_MapStart();
- numdmstarts = numredctfstarts = numbluectfstarts = 0;
+ P_PrepareRawThings(wadData + (fileinfo + ML_THINGS)->filepos, (fileinfo + ML_THINGS)->size);
+ Z_Free(wadData);
+ }
+ else
+ {
+ // Important: take care of the ordering of the next functions.
+ loadedbm = P_LoadBlockMap(lastloadedmaplumpnum + ML_BLOCKMAP);
+ P_LoadVertexes(lastloadedmaplumpnum + ML_VERTEXES);
+ P_LoadSectors(lastloadedmaplumpnum + ML_SECTORS);
+ P_LoadSideDefs(lastloadedmaplumpnum + ML_SIDEDEFS);
+ P_LoadLineDefs(lastloadedmaplumpnum + ML_LINEDEFS);
+ P_LoadSideDefs2(lastloadedmaplumpnum + ML_SIDEDEFS);
+ P_LoadSubsectors(lastloadedmaplumpnum + ML_SSECTORS);
+ P_LoadNodes(lastloadedmaplumpnum + ML_NODES);
+ P_LoadSegs(lastloadedmaplumpnum + ML_SEGS);
+ P_LoadReject(lastloadedmaplumpnum + ML_REJECT);
- // reset the player starts
- for (i = 0; i < MAXPLAYERS; i++)
- playerstarts[i] = NULL;
+ // Important: take care of the ordering of the next functions.
+ if (!loadedbm)
+ P_CreateBlockMap(); // Graue 02-29-2004
- for (i = 0; i < 2; i++)
- skyboxmo[i] = NULL;
+ P_LoadLineDefs2();
+ P_GroupLines();
+ numdmstarts = numredctfstarts = numbluectfstarts = 0;
- P_MapStart();
-
- P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
+ // reset the player starts
+ for (i = 0; i < MAXPLAYERS; i++)
+ playerstarts[i] = NULL;
+ for (i = 0; i < 2; i++)
+ skyboxmo[i] = NULL;
+ P_MapStart();
+ P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
+ }
#ifdef ESLOPE
P_ResetDynamicSlopes();
@@ -2979,7 +3151,7 @@ boolean P_RunSOC(const char *socfilename)
lumpnum_t lump;
if (strstr(socfilename, ".soc") != NULL)
- return P_AddWadFile(socfilename, NULL);
+ return P_AddWadFile(socfilename);
lump = W_CheckNumForName(socfilename);
if (lump == LUMPERROR)
@@ -2995,17 +3167,17 @@ boolean P_RunSOC(const char *socfilename)
// Add a wadfile to the active wad files,
// replace sounds, musics, patches, textures, sprites and maps
//
-boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
+boolean P_AddWadFile(const char *wadfilename)
{
size_t i, j, sreplaces = 0, mreplaces = 0, digmreplaces = 0;
UINT16 numlumps, wadnum;
- INT16 firstmapreplaced = 0, num;
char *name;
lumpinfo_t *lumpinfo;
boolean texturechange = false;
+ boolean mapsadded = false;
boolean replacedcurrentmap = false;
- if ((numlumps = W_LoadWadFile(wadfilename)) == INT16_MAX)
+ if ((numlumps = W_InitFile(wadfilename)) == INT16_MAX)
{
refreshdirmenu |= REFRESHDIR_NOTLOADED;
CONS_Printf(M_GetText("Errors occurred while loading %s; not added.\n"), wadfilename);
@@ -3062,6 +3234,7 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
if (!devparm && digmreplaces)
CONS_Printf(M_GetText("%s digital musics replaced\n"), sizeu1(digmreplaces));
+
//
// search for sprite replacements
//
@@ -3096,10 +3269,10 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
for (i = 0; i < numlumps; i++, lumpinfo++)
{
name = lumpinfo->name;
- num = firstmapreplaced;
if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers
{
+ INT16 num;
if (name[5]!='\0')
continue;
num = (INT16)M_MapNumber(name[3], name[4]);
@@ -3109,16 +3282,10 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
replacedcurrentmap = true;
CONS_Printf("%s\n", name);
- }
-
- if (num && (num < firstmapreplaced || !firstmapreplaced))
- {
- firstmapreplaced = num;
- if (firstmapname)
- *firstmapname = name;
+ mapsadded = true;
}
}
- if (!firstmapreplaced)
+ if (!mapsadded)
CONS_Printf(M_GetText("No maps added\n"));
// reload status bar (warning should have valid player!)
diff --git a/src/p_setup.h b/src/p_setup.h
index add786f49..41c2bf133 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -59,7 +59,7 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum);
#endif
void P_LoadThingsOnly(void);
boolean P_SetupLevel(boolean skipprecip);
-boolean P_AddWadFile(const char *wadfilename, char **firstmapname);
+boolean P_AddWadFile(const char *wadfilename);
#ifdef DELFILE
boolean P_DelWadFile(void);
#endif
diff --git a/src/p_slopes.c b/src/p_slopes.c
index 2d7f13319..68767b50c 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -29,7 +29,7 @@ static pslope_t *slopelist = NULL;
static UINT16 slopecount = 0;
// Calculate line normal
-static void P_CalculateSlopeNormal(pslope_t *slope) {
+void P_CalculateSlopeNormal(pslope_t *slope) {
slope->normal.z = FINECOSINE(slope->zangle>>ANGLETOFINESHIFT);
slope->normal.x = -FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.x);
slope->normal.y = -FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.y);
diff --git a/src/p_slopes.h b/src/p_slopes.h
index f083024dd..708a9107d 100644
--- a/src/p_slopes.h
+++ b/src/p_slopes.h
@@ -14,6 +14,7 @@
#define P_SLOPES_H__
#ifdef ESLOPE
+void P_CalculateSlopeNormal(pslope_t *slope);
void P_ResetDynamicSlopes(void);
void P_RunDynamicSlopes(void);
// P_SpawnSlope_Line
diff --git a/src/p_spec.c b/src/p_spec.c
index 198d87e56..50b8aec9d 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -279,10 +279,13 @@ void P_InitPicAnims(void)
Z_Free(animatedLump);
}
- // Now find ANIMDEFS
+ // Find ANIMDEFS lump in the WAD
animdefsLumpNum = W_CheckNumForNamePwad("ANIMDEFS", w, 0);
- if (animdefsLumpNum != INT16_MAX)
+ while (animdefsLumpNum != INT16_MAX)
+ {
P_ParseANIMDEFSLump(w, animdefsLumpNum);
+ animdefsLumpNum = W_CheckNumForNamePwad("ANIMDEFS", (UINT16)w, animdefsLumpNum + 1);
+ }
}
// Define the last one
animdefs[maxanims].istexture = -1;
@@ -6243,9 +6246,21 @@ void P_SpawnSpecials(INT32 fromnetsave)
case 259: // Make-Your-Own FOF!
if (lines[i].sidenum[1] != 0xffff)
{
- UINT8 *data = W_CacheLumpNum(lastloadedmaplumpnum + ML_SIDEDEFS,PU_STATIC);
+ UINT8 *data;
UINT16 b;
+ if (W_IsLumpWad(lastloadedmaplumpnum)) // welp it's a map wad in a pk3
+ { // HACK: Open wad file rather quickly so we can get the data from the sidedefs lump
+ UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
+ filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
+ fileinfo += ML_SIDEDEFS; // we only need the SIDEDEFS lump
+ data = Z_Malloc(fileinfo->size, PU_STATIC, NULL);
+ M_Memcpy(data, wadData + fileinfo->filepos, fileinfo->size); // copy data
+ Z_Free(wadData); // we're done with this now
+ }
+ else // phew it's just a WAD
+ data = W_CacheLumpNum(lastloadedmaplumpnum + ML_SIDEDEFS,PU_STATIC);
+
for (b = 0; b < (INT16)numsides; b++)
{
register mapsidedef_t *msd = (mapsidedef_t *)data + b;
@@ -6614,6 +6629,7 @@ void T_Scroll(scroll_t *s)
line_t *line;
size_t i;
INT32 sect;
+ ffloor_t *rover;
case sc_side: // scroll wall texture
side = sides + s->affectee;
@@ -6655,6 +6671,19 @@ void T_Scroll(scroll_t *s)
sector_t *psec;
psec = sectors + sect;
+ // Find the FOF corresponding to the control linedef
+ for (rover = psec->ffloors; rover; rover = rover->next)
+ {
+ if (rover->master == sec->lines[i])
+ break;
+ }
+
+ if (!rover) // This should be impossible, but don't complain if it is the case somehow
+ continue;
+
+ if (!(rover->flags & FF_EXISTS)) // If the FOF does not "exist", we pretend that nobody's there
+ continue;
+
for (node = psec->touching_thinglist; node; node = node->m_thinglist_next)
{
thing = node->m_thing;
@@ -6718,6 +6747,19 @@ void T_Scroll(scroll_t *s)
sector_t *psec;
psec = sectors + sect;
+ // Find the FOF corresponding to the control linedef
+ for (rover = psec->ffloors; rover; rover = rover->next)
+ {
+ if (rover->master == sec->lines[i])
+ break;
+ }
+
+ if (!rover) // This should be impossible, but don't complain if it is the case somehow
+ continue;
+
+ if (!(rover->flags & FF_EXISTS)) // If the FOF does not "exist", we pretend that nobody's there
+ continue;
+
for (node = psec->touching_thinglist; node; node = node->m_thinglist_next)
{
thing = node->m_thing;
diff --git a/src/p_tick.c b/src/p_tick.c
index 89cd218e3..75844b55e 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -607,7 +607,8 @@ void P_Ticker(boolean run)
}
// Keep track of how long they've been playing!
- totalplaytime++;
+ if (!demoplayback) // Don't increment if a demo is playing.
+ totalplaytime++;
if (!useNightsSS && G_IsSpecialStage(gamemap))
P_DoSpecialStageStuff();
diff --git a/src/p_user.c b/src/p_user.c
index 1ac2cbd2a..906167548 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -1621,6 +1621,9 @@ boolean P_InSpaceSector(mobj_t *mo) // Returns true if you are in space
for (rover = sector->ffloors; rover; rover = rover->next)
{
+ if (!(rover->flags & FF_EXISTS))
+ continue;
+
if (GETSECSPECIAL(rover->master->frontsector->special, 1) != SPACESPECIAL)
continue;
#ifdef ESLOPE
@@ -1835,6 +1838,12 @@ static void P_CheckBouncySectors(player_t *player)
for (rover = node->m_sector->ffloors; rover; rover = rover->next)
{
+ if (!(rover->flags & FF_EXISTS))
+ continue; // FOFs should not be bouncy if they don't even "exist"
+
+ if (GETSECSPECIAL(rover->master->frontsector->special, 1) != 15)
+ continue; // this sector type is required for FOFs to be bouncy
+
topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
@@ -1848,7 +1857,6 @@ static void P_CheckBouncySectors(player_t *player)
&& oldz + player->mo->height > P_GetFOFBottomZ(player->mo, node->m_sector, rover, oldx, oldy, NULL))
top = false;
- if (GETSECSPECIAL(rover->master->frontsector->special, 1) == 15)
{
fixed_t linedist;
@@ -7845,7 +7853,13 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
subsector_t *newsubsec;
fixed_t f1, f2;
- cameranoclip = (player->pflags & (PF_NOCLIP|PF_NIGHTSMODE)) || (player->mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!!
+ // We probably shouldn't move the camera if there is no player or player mobj somehow
+ if (!player || !player->mo)
+ return true;
+
+ mo = player->mo;
+
+ cameranoclip = (player->pflags & (PF_NOCLIP|PF_NIGHTSMODE)) || (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!!
if (!(player->climbing || (player->pflags & PF_NIGHTSMODE) || player->playerstate == PST_DEAD))
{
@@ -7866,7 +7880,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
else if (player == &players[secondarydisplayplayer])
focusangle = localangle2;
else
- focusangle = player->mo->angle;
+ focusangle = mo->angle;
if (thiscam == &camera)
camrotate = cv_cam_rotate.value;
else if (thiscam == &camera2)
@@ -7878,17 +7892,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
return true;
}
- if (!player || !player->mo)
- return true;
-
- mo = player->mo;
-
thiscam->radius = FixedMul(20*FRACUNIT, mo->scale);
thiscam->height = FixedMul(16*FRACUNIT, mo->scale);
- if (!mo)
- return true;
-
// Don't run while respawning from a starpost
// Inu 4/8/13 Why not?!
// if (leveltime > 0 && timeinmap <= 0)
@@ -7896,7 +7902,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
if (player->pflags & PF_NIGHTSMODE)
{
- focusangle = player->mo->angle;
+ focusangle = mo->angle;
focusaiming = 0;
}
else if (player == &players[consoleplayer])
@@ -7911,7 +7917,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
else
{
- focusangle = player->mo->angle;
+ focusangle = mo->angle;
focusaiming = player->aiming;
}
@@ -7958,12 +7964,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
angle = R_PointToAngle2(player->axis1->x, player->axis1->y, player->axis2->x, player->axis2->y);
angle += ANGLE_90;
}
- else if (player->mo->target)
+ else if (mo->target)
{
- if (player->mo->target->flags & MF_AMBUSH)
- angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
+ if (mo->target->flags & MF_AMBUSH)
+ angle = R_PointToAngle2(mo->target->x, mo->target->y, mo->x, mo->y);
else
- angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->target->x, player->mo->target->y);
+ angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y);
}
}
else if (P_AnalogMove(player)) // Analog
@@ -8058,7 +8064,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
if (twodlevel || (mo->flags2 & MF2_TWOD))
{
// Camera doesn't ALWAYS need to move, only when running...
- if (abs(player->mo->momx) > 10)
+ if (abs(mo->momx) > 10)
{
// Move the camera all smooth-like, not jerk it around...
if (mo->momx > 0)
@@ -8366,19 +8372,19 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
if (!(multiplayer || netgame) && !splitscreen)
{
fixed_t vx = thiscam->x, vy = thiscam->y;
- if (player->awayviewtics && player->awayviewmobj != NULL) // Camera must obviously exist
+ if (player->awayviewtics && player->awayviewmobj != NULL && !P_MobjWasRemoved(player->awayviewmobj)) // Camera must obviously exist
{
vx = player->awayviewmobj->x;
vy = player->awayviewmobj->y;
}
- if (P_AproxDistance(vx - player->mo->x, vy - player->mo->y) < FixedMul(48*FRACUNIT, mo->scale))
- player->mo->flags2 |= MF2_SHADOW;
+ if (P_AproxDistance(vx - mo->x, vy - mo->y) < FixedMul(48*FRACUNIT, mo->scale))
+ mo->flags2 |= MF2_SHADOW;
else
- player->mo->flags2 &= ~MF2_SHADOW;
+ mo->flags2 &= ~MF2_SHADOW;
}
else
- player->mo->flags2 &= ~MF2_SHADOW;
+ mo->flags2 &= ~MF2_SHADOW;
/* if (!resetcalled && (player->pflags & PF_NIGHTSMODE && player->exiting))
{
@@ -8528,7 +8534,7 @@ static void P_CalcPostImg(player_t *player)
else
pviewheight = player->mo->z + player->viewheight;
- if (player->awayviewtics)
+ if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj))
{
sector = player->awayviewmobj->subsector->sector;
pviewheight = player->awayviewmobj->z + 20*FRACUNIT;
@@ -8695,6 +8701,13 @@ void P_PlayerThink(player_t *player)
}
}
#endif
+
+ if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj))
+ {
+ P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid
+ player->awayviewtics = 0; // reset to zero
+ }
+
if (player->pflags & PF_GLIDING)
{
if (player->panim != PA_ABILITY)
diff --git a/src/r_data.c b/src/r_data.c
index 96c89915a..453ef69ec 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -365,8 +365,8 @@ void R_FlushTextureCache(void)
}
// Need these prototypes for later; defining them here instead of r_data.h so they're "private"
-int R_CountTexturesInTEXTURESLump(UINT16 wadNum);
-void R_ParseTEXTURESLump(UINT16 wadNum, INT32 *index);
+int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum);
+void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index);
//
// R_LoadTextures
@@ -404,13 +404,22 @@ void R_LoadTextures(void)
// but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures.
for (w = 0, numtextures = 0; w < numwadfiles; w++)
{
- texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1;
- texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
- texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
-
- if (texturesLumpPos != INT16_MAX)
+ if (wadfiles[w]->type == RET_PK3)
{
- numtextures += R_CountTexturesInTEXTURESLump((UINT16)w);
+ texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0);
+ texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart);
+ }
+ else
+ {
+ texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1;
+ texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
+ }
+
+ texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
+ while (texturesLumpPos != INT16_MAX)
+ {
+ numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos);
+ texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1);
}
// Add all the textures between TX_START and TX_END
@@ -447,12 +456,25 @@ void R_LoadTextures(void)
for (i = 0, w = 0; w < numwadfiles; w++)
{
// Get the lump numbers for the markers in the WAD, if they exist.
- texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1;
- texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
- texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
-
- if (texturesLumpPos != INT16_MAX)
- R_ParseTEXTURESLump(w,&i);
+ if (wadfiles[w]->type == RET_PK3)
+ {
+ texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0);
+ texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart);
+ texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
+ while (texturesLumpPos != INT16_MAX)
+ {
+ R_ParseTEXTURESLump(w, texturesLumpPos, &i);
+ texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1);
+ }
+ }
+ else
+ {
+ texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0) + 1;
+ texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
+ texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
+ if (texturesLumpPos != INT16_MAX)
+ R_ParseTEXTURESLump(w, texturesLumpPos, &i);
+ }
if (texstart == INT16_MAX || texend == INT16_MAX)
continue;
@@ -472,40 +494,22 @@ void R_LoadTextures(void)
}
else
{
- UINT16 patchcount = 1;
//CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height);
- if (SHORT(patchlump->width) == 64
- && SHORT(patchlump->height) == 64)
- { // 64x64 patch
- const column_t *column;
- for (k = 0; k < SHORT(patchlump->width); k++)
- { // Find use of transparency.
- column = (const column_t *)((const UINT8 *)patchlump + LONG(patchlump->columnofs[k]));
- if (column->length != SHORT(patchlump->height))
- break;
- }
- if (k == SHORT(patchlump->width))
- patchcount = 2; // No transparency? 64x128 texture.
- }
- texture = textures[i] = Z_Calloc(sizeof(texture_t) + (sizeof(texpatch_t) * patchcount), PU_STATIC, NULL);
+ texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
// Set texture properties.
M_Memcpy(texture->name, W_CheckNameForNumPwad((UINT16)w, texstart + j), sizeof(texture->name));
texture->width = SHORT(patchlump->width);
- texture->height = SHORT(patchlump->height)*patchcount;
- texture->patchcount = patchcount;
+ texture->height = SHORT(patchlump->height);
+ texture->patchcount = 1;
texture->holes = false;
// Allocate information for the texture's patches.
- for (k = 0; k < patchcount; k++)
- {
- patch = &texture->patches[k];
+ patch = &texture->patches[0];
- patch->originx = 0;
- patch->originy = (INT16)(k*patchlump->height);
- patch->wad = (UINT16)w;
- patch->lump = texstart + j;
- }
+ patch->originx = patch->originy = 0;
+ patch->wad = (UINT16)w;
+ patch->lump = texstart + j;
Z_Unlock(patchlump);
@@ -817,7 +821,7 @@ static texture_t *R_ParseTexture(boolean actuallyLoadTexture)
}
// Parses the TEXTURES lump... but just to count the number of textures.
-int R_CountTexturesInTEXTURESLump(UINT16 wadNum)
+int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum)
{
char *texturesLump;
size_t texturesLumpLength;
@@ -828,11 +832,11 @@ int R_CountTexturesInTEXTURESLump(UINT16 wadNum)
// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
// need to make a space of memory where I can ensure that it will terminate
// correctly. Start by loading the relevant data from the WAD.
- texturesLump = (char *)W_CacheLumpNumPwad(wadNum,W_CheckNumForNamePwad("TEXTURES", wadNum, 0),PU_STATIC);
+ texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC);
// If that didn't exist, we have nothing to do here.
if (texturesLump == NULL) return 0;
// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
- texturesLumpLength = W_LumpLengthPwad(wadNum,W_CheckNumForNamePwad("TEXTURES",wadNum,0));
+ texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum);
texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL);
// Now move the contents of the lump into this new location.
memmove(texturesText,texturesLump,texturesLumpLength);
@@ -864,7 +868,7 @@ int R_CountTexturesInTEXTURESLump(UINT16 wadNum)
}
// Parses the TEXTURES lump... for real, this time.
-void R_ParseTEXTURESLump(UINT16 wadNum, INT32 *texindex)
+void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex)
{
char *texturesLump;
size_t texturesLumpLength;
@@ -877,11 +881,11 @@ void R_ParseTEXTURESLump(UINT16 wadNum, INT32 *texindex)
// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
// need to make a space of memory where I can ensure that it will terminate
// correctly. Start by loading the relevant data from the WAD.
- texturesLump = (char *)W_CacheLumpNumPwad(wadNum,W_CheckNumForNamePwad("TEXTURES", wadNum, 0),PU_STATIC);
+ texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC);
// If that didn't exist, we have nothing to do here.
if (texturesLump == NULL) return;
// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
- texturesLumpLength = W_LumpLengthPwad(wadNum,W_CheckNumForNamePwad("TEXTURES",wadNum,0));
+ texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum);
texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL);
// Now move the contents of the lump into this new location.
memmove(texturesText,texturesLump,texturesLumpLength);
@@ -968,12 +972,51 @@ static void R_InitExtraColormaps(void)
CONS_Printf(M_GetText("Number of Extra Colormaps: %s\n"), sizeu1(numcolormaplumps));
}
-// 12/14/14 -- only take flats in F_START/F_END
+// Search for flat name.
lumpnum_t R_GetFlatNumForName(const char *name)
{
- lumpnum_t lump = W_CheckNumForNameInBlock(name, "F_START", "F_END");
- if (lump == LUMPERROR)
- lump = W_CheckNumForNameInBlock(name, "FF_START", "FF_END"); // deutex, some other old things
+ INT32 i;
+ lumpnum_t lump;
+ lumpnum_t start;
+ lumpnum_t end;
+
+ // Scan wad files backwards so patched flats take preference.
+ for (i = numwadfiles - 1; i >= 0; i--)
+ {
+ switch (wadfiles[i]->type)
+ {
+ case RET_WAD:
+ if ((start = W_CheckNumForNamePwad("F_START", (UINT16)i, 0)) == INT16_MAX)
+ {
+ if ((start = W_CheckNumForNamePwad("FF_START", (UINT16)i, 0)) == INT16_MAX)
+ continue;
+ else if ((end = W_CheckNumForNamePwad("FF_END", (UINT16)i, start)) == INT16_MAX)
+ continue;
+ }
+ else
+ if ((end = W_CheckNumForNamePwad("F_END", (UINT16)i, start)) == INT16_MAX)
+ continue;
+ break;
+ case RET_PK3:
+ if ((start = W_CheckNumForFolderStartPK3("Flats/", i, 0)) == INT16_MAX)
+ continue;
+ if ((end = W_CheckNumForFolderEndPK3("Flats/", i, start)) == INT16_MAX)
+ continue;
+ break;
+ default:
+ continue;
+ }
+
+ // Now find lump with specified name in that range.
+ lump = W_CheckNumForNamePwad(name, (UINT16)i, start);
+ if (lump < end)
+ {
+ lump += (i<<16); // found it, in our constraints
+ break;
+ }
+ lump = LUMPERROR;
+ }
+
if (lump == LUMPERROR)
{
if (strcmp(name, SKYFLATNAME))
diff --git a/src/r_things.c b/src/r_things.c
index 734179f66..b7c925e39 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -327,21 +327,28 @@ void R_AddSpriteDefs(UINT16 wadnum)
UINT16 start, end;
char wadname[MAX_WADPATH];
- // find the sprites section in this pwad
- // we need at least the S_END
- // (not really, but for speedup)
+ switch (wadfiles[wadnum]->type)
+ {
+ case RET_WAD:
+ start = W_CheckNumForNamePwad("S_START", wadnum, 0);
+ if (start == INT16_MAX)
+ start = W_CheckNumForNamePwad("SS_START", wadnum, 0); //deutex compatib.
+ if (start == INT16_MAX)
+ start = 0; //let say S_START is lump 0
+ else
+ start++; // just after S_START
+ end = W_CheckNumForNamePwad("S_END",wadnum,start);
+ if (end == INT16_MAX)
+ end = W_CheckNumForNamePwad("SS_END",wadnum,start); //deutex compatib.
+ break;
+ case RET_PK3:
+ start = W_CheckNumForFolderStartPK3("Sprites/", wadnum, 0);
+ end = W_CheckNumForFolderEndPK3("Sprites/", wadnum, start);
+ break;
+ default:
+ return;
+ }
- start = W_CheckNumForNamePwad("S_START", wadnum, 0);
- if (start == INT16_MAX)
- start = W_CheckNumForNamePwad("SS_START", wadnum, 0); //deutex compatib.
- if (start == INT16_MAX)
- start = 0; //let say S_START is lump 0
- else
- start++; // just after S_START
-
- end = W_CheckNumForNamePwad("S_END",wadnum,start);
- if (end == INT16_MAX)
- end = W_CheckNumForNamePwad("SS_END",wadnum,start); //deutex compatib.
if (end == INT16_MAX)
{
CONS_Debug(DBG_SETUP, "no sprites in pwad %d\n", wadnum);
@@ -2441,7 +2448,7 @@ void SetPlayerSkin(INT32 playernum, const char *skinname)
if (P_IsLocalPlayer(player))
CONS_Alert(CONS_WARNING, M_GetText("Skin '%s' not found.\n"), skinname);
- else if(server || adminplayer == consoleplayer)
+ else if(server || IsPlayerAdmin(consoleplayer))
CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) skin '%s' not found\n"), playernum, player_names[playernum], skinname);
SetPlayerSkinByNum(playernum, 0);
@@ -2499,7 +2506,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
if (P_IsLocalPlayer(player))
CONS_Alert(CONS_WARNING, M_GetText("Skin %d not found\n"), skinnum);
- else if(server || adminplayer == consoleplayer)
+ else if(server || IsPlayerAdmin(consoleplayer))
CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum);
SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin
}
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 467d2829b..3e4a9ebb1 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -297,6 +297,7 @@
+
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index 364deb497..ac0b03177 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -639,6 +639,9 @@
LUA
+
+ LUA
+
LUA
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 68ebc5e94..50c3018aa 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -894,8 +894,8 @@ void I_GetJoystickEvents(void)
UINT64 joyhats = 0;
#if 0
UINT64 joybuttons = 0;
-#endif
Sint16 axisx, axisy;
+#endif
if (!joystick_started) return;
@@ -963,6 +963,7 @@ void I_GetJoystickEvents(void)
}
}
+#if 0
// send joystick axis positions
event.type = ev_joystick;
@@ -1013,6 +1014,7 @@ void I_GetJoystickEvents(void)
}
D_PostEvent(&event);
}
+#endif
}
/** \brief Open joystick handle
@@ -1176,8 +1178,8 @@ void I_GetJoystick2Events(void)
UINT64 joyhats = 0;
#if 0
INT64 joybuttons = 0;
-#endif
INT32 axisx, axisy;
+#endif
if (!joystick2_started)
return;
@@ -1247,6 +1249,7 @@ void I_GetJoystick2Events(void)
}
}
+#if 0
// send joystick axis positions
event.type = ev_joystick2;
@@ -1297,7 +1300,7 @@ void I_GetJoystick2Events(void)
}
D_PostEvent(&event);
}
-
+#endif
}
/** \brief Open joystick handle
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 3cc29dbb4..2c199c2d0 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -767,6 +767,33 @@ static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt)
D_PostEvent(&event);
}
+#if 0
+static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt)
+{
+ event_t event;
+ SDL_JoystickID joyid[2];
+
+ // Determine the Joystick IDs for each current open joystick
+ joyid[0] = SDL_JoystickInstanceID(JoyInfo.dev);
+ joyid[1] = SDL_JoystickInstanceID(JoyInfo2.dev);
+
+ if (evt.hat >= JOYHATS)
+ return; // ignore hats with too high an index
+
+ if (evt.which == joyid[0])
+ {
+ event.data1 = KEY_HAT1 + (evt.hat*4);
+ }
+ else if (evt.which == joyid[1])
+ {
+ event.data1 = KEY_2HAT1 + (evt.hat*4);
+ }
+ else return;
+
+ // NOTE: UNFINISHED
+}
+#endif
+
static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type)
{
event_t event;
@@ -804,6 +831,8 @@ static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type)
if (event.type != ev_console) D_PostEvent(&event);
}
+
+
void I_GetEvent(void)
{
SDL_Event evt;
@@ -844,6 +873,11 @@ void I_GetEvent(void)
case SDL_JOYAXISMOTION:
Impl_HandleJoystickAxisEvent(evt.jaxis);
break;
+#if 0
+ case SDL_JOYHATMOTION:
+ Impl_HandleJoystickHatEvent(evt.jhat)
+ break;
+#endif
case SDL_JOYBUTTONUP:
case SDL_JOYBUTTONDOWN:
Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type);
diff --git a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
index eaac87deb..6eabee56b 100644
--- a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
+++ b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
@@ -1214,7 +1214,7 @@
C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CURRENT_PROJECT_VERSION = 2.1.20;
+ CURRENT_PROJECT_VERSION = 2.1.21;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
NORMALSRB2,
@@ -1226,7 +1226,7 @@
C01FCF4C08A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CURRENT_PROJECT_VERSION = 2.1.20;
+ CURRENT_PROJECT_VERSION = 2.1.21;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
diff --git a/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj
index 574161c68..93f63309c 100644
--- a/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj
+++ b/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj
@@ -1214,7 +1214,7 @@
C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CURRENT_PROJECT_VERSION = 2.1.20;
+ CURRENT_PROJECT_VERSION = 2.1.21;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
NORMALSRB2,
@@ -1226,7 +1226,7 @@
C01FCF4C08A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CURRENT_PROJECT_VERSION = 2.1.20;
+ CURRENT_PROJECT_VERSION = 2.1.21;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
diff --git a/src/st_stuff.c b/src/st_stuff.c
index f2e62feda..1f8dbbf61 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -1871,12 +1871,12 @@ static void ST_overlayDrawer(void)
if (!hu_showscores && !splitscreen && netgame && displayplayer == consoleplayer)
{
if (G_GametypeUsesLives() && stplyr->lives <= 0 && countdown != 1)
- V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(132), 0, M_GetText("Press F12 to watch another player."));
+ V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(132), 0, M_GetText("Press Viewpoint Key to watch a player."));
else if (gametype == GT_HIDEANDSEEK &&
(!stplyr->spectator && !(stplyr->pflags & PF_TAGIT)) && (leveltime > hidetime * TICRATE))
{
V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(116), 0, M_GetText("You cannot move while hiding."));
- V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(132), 0, M_GetText("Press F12 to watch another player."));
+ V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(132), 0, M_GetText("Press Viewpoint Key to watch a player."));
}
else if (!G_PlatformGametype() && stplyr->playerstate == PST_DEAD && stplyr->lives) //Death overrides spectator text.
{
@@ -1899,7 +1899,7 @@ static void ST_overlayDrawer(void)
V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(132), V_HUDTRANSHALF, M_GetText("You cannot join the game until the stage has ended."));
else
V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(132), V_HUDTRANSHALF, M_GetText("Press Fire to enter the game."));
- V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(148), V_HUDTRANSHALF, M_GetText("Press F12 to watch another player."));
+ V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(148), V_HUDTRANSHALF, M_GetText("Press Viewpoint Key to watch a player."));
V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(164), V_HUDTRANSHALF, M_GetText("Press Jump to float and Spin to sink."));
}
}
diff --git a/src/w_wad.c b/src/w_wad.c
index 5eded4265..dee55c6c8 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -65,22 +65,25 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#define O_BINARY 0
#endif
-#if defined(_MSC_VER)
-#pragma pack(1)
+#ifdef HAVE_ZLIB
+#ifndef _MSC_VER
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
#endif
-// a raw entry of the wad directory
-typedef struct
-{
- UINT32 filepos; // file offset of the resource
- UINT32 size; // size of the resource
- char name[8]; // name of the resource
-} ATTRPACK filelump_t;
-
-#if defined(_MSC_VER)
-#pragma pack()
+#ifndef _LFS64_LARGEFILE
+#define _LFS64_LARGEFILE
#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 0
+#endif
+
+#include "zlib.h"
+#endif
+
+
typedef struct
{
const char *name;
@@ -115,8 +118,10 @@ void W_Shutdown(void)
while (numwadfiles--)
{
fclose(wadfiles[numwadfiles]->handle);
- Z_Free(wadfiles[numwadfiles]->lumpinfo);
Z_Free(wadfiles[numwadfiles]->filename);
+ while (wadfiles[numwadfiles]->numlumps--)
+ Z_Free(wadfiles[numwadfiles]->lumpinfo[wadfiles[numwadfiles]->numlumps].name2);
+ Z_Free(wadfiles[numwadfiles]->lumpinfo);
Z_Free(wadfiles[numwadfiles]);
}
}
@@ -176,6 +181,38 @@ FILE *W_OpenWadFile(const char **filename, boolean useerrors)
return handle;
}
+// Look for all DEHACKED and Lua scripts inside a PK3 archive.
+static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum)
+{
+ UINT16 posStart, posEnd;
+ posStart = W_CheckNumForFolderStartPK3("Lua/", wadnum, 0);
+ if (posStart != INT16_MAX)
+ {
+ posEnd = W_CheckNumForFolderEndPK3("Lua/", wadnum, posStart);
+ posStart++;
+ for (; posStart < posEnd; posStart++)
+ LUA_LoadLump(wadnum, posStart);
+ }
+ posStart = W_CheckNumForFolderStartPK3("SOC/", wadnum, 0);
+ if (posStart != INT16_MAX)
+ {
+ posEnd = W_CheckNumForFolderEndPK3("SOC/", wadnum, posStart);
+ posStart++;
+ for(; posStart < posEnd; posStart++)
+ {
+ lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[posStart];
+ size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
+ char *name = malloc(length + 1);
+ sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->name2);
+ name[length] = '\0';
+ CONS_Printf(M_GetText("Loading SOC from %s\n"), name);
+ DEH_LoadDehackedLumpPwad(wadnum, posStart);
+ free(name);
+ }
+
+ }
+}
+
// search for all DEHACKED lump in all wads and load it
static inline void W_LoadDehackedLumps(UINT16 wadnum)
{
@@ -196,20 +233,13 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum)
for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++)
if (memcmp(lump_p->name,"SOC_",4)==0) // Check for generic SOC lump
{ // shameless copy+paste of code from LUA_LoadLump
- size_t len = strlen(wadfiles[wadnum]->filename);
- char *name = malloc(len+10);
-
- strcpy(name, wadfiles[wadnum]->filename);
- if (!fasticmp(&name[len - 4], ".soc")) {
- // If it's not a .soc file, copy the lump name in too.
- name[len] = '|';
- M_Memcpy(name+len+1, lump_p->name, 8);
- name[len+9] = '\0';
- }
+ size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
+ char *name = malloc(length + 1);
+ sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->name2);
+ name[length] = '\0';
CONS_Printf(M_GetText("Loading SOC from %s\n"), name);
DEH_LoadDehackedLumpPwad(wadnum, lump);
-
free(name);
}
else if (memcmp(lump_p->name,"MAINCFG",8)==0) // Check for MAINCFG
@@ -282,6 +312,324 @@ static void W_InvalidateLumpnumCache(void)
memset(lumpnumcache, 0, sizeof (lumpnumcache));
}
+/** Detect a file type.
+ * \todo Actually detect the wad/pkzip headers and whatnot, instead of just checking the extensions.
+ */
+static restype_t ResourceFileDetect (const char* filename)
+{
+ if (!stricmp(&filename[strlen(filename) - 4], ".pk3"))
+ return RET_PK3;
+ if (!stricmp(&filename[strlen(filename) - 4], ".soc"))
+ return RET_SOC;
+ if (!stricmp(&filename[strlen(filename) - 4], ".lua"))
+ return RET_LUA;
+
+ return RET_WAD;
+}
+
+/** Create a 1-lump lumpinfo_t for standalone files.
+ */
+static lumpinfo_t* ResGetLumpsStandalone (FILE* handle, UINT16* numlumps, const char* lumpname)
+{
+ lumpinfo_t* lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL);
+ lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL);
+ lumpinfo->position = 0;
+ fseek(handle, 0, SEEK_END);
+ lumpinfo->size = ftell(handle);
+ fseek(handle, 0, SEEK_SET);
+ strcpy(lumpinfo->name, lumpname);
+ // Allocate the lump's full name.
+ lumpinfo->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+ strcpy(lumpinfo->name2, lumpname);
+ lumpinfo->name2[8] = '\0';
+ *numlumps = 1;
+ return lumpinfo;
+}
+
+/** Create a lumpinfo_t array for a WAD file.
+ */
+static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filename)
+{
+ UINT16 numlumps = *nlmp;
+ lumpinfo_t* lumpinfo;
+ size_t i;
+ INT32 compressed = 0;
+
+ wadinfo_t header;
+ lumpinfo_t *lump_p;
+ filelump_t *fileinfo;
+ void *fileinfov;
+
+ // read the header
+ if (fread(&header, 1, sizeof header, handle) < sizeof header)
+ {
+ CONS_Alert(CONS_ERROR, M_GetText("Can't read wad header because %s\n"), strerror(ferror(handle)));
+ return NULL;
+ }
+
+ if (memcmp(header.identification, "ZWAD", 4) == 0)
+ compressed = 1;
+ else if (memcmp(header.identification, "IWAD", 4) != 0
+ && memcmp(header.identification, "PWAD", 4) != 0
+ && memcmp(header.identification, "SDLL", 4) != 0)
+ {
+ CONS_Alert(CONS_ERROR, M_GetText("Invalid WAD header\n"));
+ return NULL;
+ }
+
+ header.numlumps = LONG(header.numlumps);
+ header.infotableofs = LONG(header.infotableofs);
+
+ // read wad file directory
+ i = header.numlumps * sizeof (*fileinfo);
+ fileinfov = fileinfo = malloc(i);
+ if (fseek(handle, header.infotableofs, SEEK_SET) == -1
+ || fread(fileinfo, 1, i, handle) < i)
+ {
+ CONS_Alert(CONS_ERROR, M_GetText("Corrupt wadfile directory (%s)\n"), strerror(ferror(handle)));
+ free(fileinfov);
+ return NULL;
+ }
+
+ numlumps = header.numlumps;
+
+ // fill in lumpinfo for this wad
+ lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL);
+ for (i = 0; i < numlumps; i++, lump_p++, fileinfo++)
+ {
+ lump_p->position = LONG(fileinfo->filepos);
+ lump_p->size = lump_p->disksize = LONG(fileinfo->size);
+ if (compressed) // wad is compressed, lump might be
+ {
+ UINT32 realsize = 0;
+ if (fseek(handle, lump_p->position, SEEK_SET)
+ == -1 || fread(&realsize, 1, sizeof realsize,
+ handle) < sizeof realsize)
+ {
+ I_Error("corrupt compressed file: %s; maybe %s", /// \todo Avoid the bailout?
+ filename, strerror(ferror(handle)));
+ }
+ realsize = LONG(realsize);
+ if (realsize != 0)
+ {
+ lump_p->size = realsize;
+ lump_p->compression = CM_LZF;
+ }
+ else
+ {
+ lump_p->size -= 4;
+ lump_p->compression = CM_NOCOMPRESSION;
+ }
+
+ lump_p->position += 4;
+ lump_p->disksize -= 4;
+ }
+ else
+ lump_p->compression = CM_NOCOMPRESSION;
+ memset(lump_p->name, 0x00, 9);
+ strncpy(lump_p->name, fileinfo->name, 8);
+ // Allocate the lump's full name.
+ lump_p->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+ strncpy(lump_p->name2, fileinfo->name, 8);
+ lump_p->name2[8] = '\0';
+ }
+ free(fileinfov);
+ *nlmp = numlumps;
+ return lumpinfo;
+}
+
+/** Optimized pattern search in a file.
+ */
+static boolean ResFindSignature (FILE* handle, char endPat[], UINT32 startpos)
+{
+ char *s;
+ int c;
+
+ fseek(handle, startpos, SEEK_SET);
+ s = endPat;
+ while((c = fgetc(handle)) != EOF)
+ {
+ if (*s != c && s > endPat) // No match?
+ s = endPat; // We "reset" the counter by sending the s pointer back to the start of the array.
+ if (*s == c)
+ {
+ s++;
+ if (*s == 0x00) // The array pointer has reached the key char which marks the end. It means we have matched the signature.
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+#if defined(_MSC_VER)
+#pragma pack(1)
+#endif
+typedef struct zend_s
+{
+ char signature[4];
+ UINT16 diskpos;
+ UINT16 cdirdisk;
+ UINT16 diskentries;
+ UINT16 entries;
+ UINT32 cdirsize;
+ UINT32 cdiroffset;
+ UINT16 commentlen;
+} ATTRPACK zend_t;
+
+typedef struct zentry_s
+{
+ char signature[4];
+ UINT16 version;
+ UINT16 versionneeded;
+ UINT16 flags;
+ UINT16 compression;
+ UINT16 modtime;
+ UINT16 moddate;
+ UINT32 CRC32;
+ UINT32 compsize;
+ UINT32 size;
+ UINT16 namelen;
+ UINT16 xtralen;
+ UINT16 commlen;
+ UINT16 diskstart;
+ UINT16 attrint;
+ UINT32 attrext;
+ UINT32 offset;
+} ATTRPACK zentry_t;
+
+typedef struct zlentry_s
+{
+ char signature[4];
+ UINT16 versionneeded;
+ UINT16 flags;
+ UINT16 compression;
+ UINT16 modtime;
+ UINT16 moddate;
+ UINT32 CRC32;
+ UINT32 compsize;
+ UINT32 size;
+ UINT16 namelen;
+ UINT16 xtralen;
+} ATTRPACK zlentry_t;
+#if defined(_MSC_VER)
+#pragma pack()
+#endif
+
+/** Create a lumpinfo_t array for a PKZip file.
+ */
+static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp)
+{
+ zend_t zend;
+ zentry_t* zentries;
+ zentry_t* zentry;
+
+ UINT16 numlumps = *nlmp;
+ lumpinfo_t* lumpinfo;
+ lumpinfo_t *lump_p;
+ size_t i;
+
+ char pat_central[] = {0x50, 0x4b, 0x01, 0x02, 0x00};
+ char pat_end[] = {0x50, 0x4b, 0x05, 0x06, 0x00};
+
+ // Look for central directory end signature near end of file.
+ // Contains entry number (number of lumps), and central directory start offset.
+ fseek(handle, 0, SEEK_END);
+ if (!ResFindSignature(handle, pat_end, max(0, ftell(handle) - (22 + 65536))))
+ {
+ CONS_Alert(CONS_ERROR, "Missing central directory\n");
+ return NULL;
+ }
+
+ fseek(handle, -4, SEEK_CUR);
+ if (fread(&zend, 1, sizeof zend, handle) < sizeof zend)
+ {
+ CONS_Alert(CONS_ERROR, "Corrupt central directory (%s)\n", strerror(ferror(handle)));
+ return NULL;
+ }
+ numlumps = zend.entries;
+
+ lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL);
+ zentry = zentries = malloc(numlumps * sizeof (*zentries));
+
+ fseek(handle, zend.cdiroffset, SEEK_SET);
+ for (i = 0; i < numlumps; i++, zentry++, lump_p++)
+ {
+ char* fullname;
+ char* trimname;
+ char* dotpos;
+
+ if (fread(zentry, 1, sizeof(zentry_t), handle) < sizeof(zentry_t))
+ {
+ CONS_Alert(CONS_ERROR, "Failed to read central directory (%s)\n", strerror(ferror(handle)));
+ Z_Free(lumpinfo);
+ free(zentry);
+ return NULL;
+ }
+ if (memcmp(zentry->signature, pat_central, 4))
+ {
+ CONS_Alert(CONS_ERROR, "Central directory is corrupt\n");
+ Z_Free(lumpinfo);
+ free(zentry);
+ return NULL;
+ }
+
+ lump_p->position = zentry->offset + zentry->namelen + zentry->xtralen + sizeof(zlentry_t);
+ lump_p->disksize = zentry->compsize;
+ lump_p->size = zentry->size;
+
+ fullname = malloc(zentry->namelen + 1);
+ if (fgets(fullname, zentry->namelen + 1, handle) != fullname)
+ {
+ CONS_Alert(CONS_ERROR, "Unable to read lumpname (%s)\n", strerror(ferror(handle)));
+ Z_Free(lumpinfo);
+ free(zentry);
+ free(fullname);
+ return NULL;
+ }
+
+ // Strip away file address and extension for the 8char name.
+ if ((trimname = strrchr(fullname, '/')) != 0)
+ trimname++;
+ else
+ trimname = fullname; // Care taken for root files.
+
+ if ((dotpos = strrchr(trimname, '.')) == 0)
+ dotpos = fullname + strlen(fullname); // Watch for files without extension.
+
+ memset(lump_p->name, '\0', 9); // Making sure they're initialized to 0. Is it necessary?
+ strncpy(lump_p->name, trimname, min(8, dotpos - trimname));
+
+ lump_p->name2 = Z_Calloc(zentry->namelen + 1, PU_STATIC, NULL);
+ strncpy(lump_p->name2, fullname, zentry->namelen);
+
+ free(fullname);
+
+ switch(zentry->compression)
+ {
+ case 0:
+ lump_p->compression = CM_NOCOMPRESSION;
+ break;
+#ifdef HAVE_ZLIB
+ case 8:
+ lump_p->compression = CM_DEFLATE;
+ break;
+#endif
+ case 14:
+ lump_p->compression = CM_LZF;
+ break;
+ default:
+ CONS_Alert(CONS_WARNING, "%s: Unsupported compression method\n", fullname);
+ lump_p->compression = CM_UNSUPPORTED;
+ break;
+ }
+ }
+
+ *nlmp = numlumps;
+ return lumpinfo;
+}
+
// Allocate a wadfile, setup the lumpinfo (directory) and
// lumpcache, add the wadfile to the current active wadfiles
//
@@ -293,12 +641,13 @@ static void W_InvalidateLumpnumCache(void)
//
// Can now load dehacked files (.soc)
//
-UINT16 W_LoadWadFile(const char *filename)
+UINT16 W_InitFile(const char *filename)
{
FILE *handle;
- lumpinfo_t *lumpinfo;
+ lumpinfo_t *lumpinfo = NULL;
wadfile_t *wadfile;
- UINT32 numlumps;
+ restype_t type;
+ UINT16 numlumps = 0;
size_t i;
INT32 compressed = 0;
size_t packetsize;
@@ -352,122 +701,6 @@ UINT16 W_LoadWadFile(const char *filename)
packetsizetally = packetsize;
}
- // detect dehacked file with the "soc" extension
- if (!stricmp(&filename[strlen(filename) - 4], ".soc"))
- {
- // This code emulates a wadfile with one lump name "OBJCTCFG"
- // at position 0 and size of the whole file.
- // This allows soc files to be like all wads, copied by network and loaded at the console.
- numlumps = 1;
- lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL);
- lumpinfo->position = 0;
- fseek(handle, 0, SEEK_END);
- lumpinfo->size = ftell(handle);
- fseek(handle, 0, SEEK_SET);
- strcpy(lumpinfo->name, "OBJCTCFG");
- }
-#ifdef HAVE_BLUA
- // detect lua script with the "lua" extension
- else if (!stricmp(&filename[strlen(filename) - 4], ".lua"))
- {
- // This code emulates a wadfile with one lump name "LUA_INIT"
- // at position 0 and size of the whole file.
- // This allows soc files to be like all wads, copied by network and loaded at the console.
- numlumps = 1;
- lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL);
- lumpinfo->position = 0;
- fseek(handle, 0, SEEK_END);
- lumpinfo->size = ftell(handle);
- fseek(handle, 0, SEEK_SET);
- strcpy(lumpinfo->name, "LUA_INIT");
- }
-#endif
- else
- {
- // assume wad file
- wadinfo_t header;
- lumpinfo_t *lump_p;
- filelump_t *fileinfo;
- void *fileinfov;
-
- // read the header
- if (fread(&header, 1, sizeof header, handle) < sizeof header)
- {
- CONS_Alert(CONS_ERROR, M_GetText("Can't read wad header from %s because %s\n"), filename, strerror(ferror(handle)));
- if (handle)
- fclose(handle);
- return INT16_MAX;
- }
-
- if (memcmp(header.identification, "ZWAD", 4) == 0)
- compressed = 1;
- else if (memcmp(header.identification, "IWAD", 4) != 0
- && memcmp(header.identification, "PWAD", 4) != 0
- && memcmp(header.identification, "SDLL", 4) != 0)
- {
- CONS_Alert(CONS_ERROR, M_GetText("%s does not have a valid WAD header\n"), filename);
- if (handle)
- fclose(handle);
- return INT16_MAX;
- }
-
- header.numlumps = LONG(header.numlumps);
- header.infotableofs = LONG(header.infotableofs);
-
- // read wad file directory
- i = header.numlumps * sizeof (*fileinfo);
- fileinfov = fileinfo = malloc(i);
- if (fseek(handle, header.infotableofs, SEEK_SET) == -1
- || fread(fileinfo, 1, i, handle) < i)
- {
- CONS_Alert(CONS_ERROR, M_GetText("Wadfile directory in %s is corrupted (%s)\n"), filename, strerror(ferror(handle)));
- free(fileinfov);
- if (handle)
- fclose(handle);
- return INT16_MAX;
- }
-
- numlumps = header.numlumps;
-
- // fill in lumpinfo for this wad
- lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL);
- for (i = 0; i < numlumps; i++, lump_p++, fileinfo++)
- {
- lump_p->position = LONG(fileinfo->filepos);
- lump_p->size = lump_p->disksize = LONG(fileinfo->size);
- if (compressed) // wad is compressed, lump might be
- {
- UINT32 realsize = 0;
-
- if (fseek(handle, lump_p->position, SEEK_SET)
- == -1 || fread(&realsize, 1, sizeof realsize,
- handle) < sizeof realsize)
- {
- I_Error("corrupt compressed file: %s; maybe %s",
- filename, strerror(ferror(handle)));
- }
- realsize = LONG(realsize);
- if (realsize != 0)
- {
- lump_p->size = realsize;
- lump_p->compressed = 1;
- }
- else
- {
- lump_p->size -= 4;
- lump_p->compressed = 0;
- }
-
- lump_p->position += 4;
- lump_p->disksize -= 4;
- }
- else lump_p->compressed = 0;
- memset(lump_p->name, 0x00, 9);
- strncpy(lump_p->name, fileinfo->name, 8);
- }
- free(fileinfov);
- }
-
#ifndef NOMD5
//
// w-waiiiit!
@@ -488,6 +721,32 @@ UINT16 W_LoadWadFile(const char *filename)
}
#endif
+ switch(type = ResourceFileDetect(filename))
+ {
+ case RET_SOC:
+ lumpinfo = ResGetLumpsStandalone(handle, &numlumps, "OBJCTCFG");
+ break;
+#ifdef HAVE_BLUA
+ case RET_LUA:
+ lumpinfo = ResGetLumpsStandalone(handle, &numlumps, "LUA_INIT");
+ break;
+#endif
+ case RET_PK3:
+ lumpinfo = ResGetLumpsZip(handle, &numlumps);
+ break;
+ case RET_WAD:
+ lumpinfo = ResGetLumpsWad(handle, &numlumps, filename);
+ break;
+ default:
+ CONS_Alert(CONS_ERROR, "Unsupported file format\n");
+ }
+
+ if (lumpinfo == NULL)
+ {
+ fclose(handle);
+ return INT16_MAX;
+ }
+
//
// link wad file to search files
//
@@ -499,6 +758,7 @@ UINT16 W_LoadWadFile(const char *filename)
wadfile->important = important;
fseek(handle, 0, SEEK_END);
wadfile->filesize = (unsigned)ftell(handle);
+ wadfile->type = type;
// already generated, just copy it over
M_Memcpy(&wadfile->md5sum, &md5sum, 16);
@@ -519,10 +779,28 @@ UINT16 W_LoadWadFile(const char *filename)
CONS_Printf(M_GetText("Added file %s (%u lumps)\n"), filename, numlumps);
wadfiles[numwadfiles] = wadfile;
numwadfiles++; // must come BEFORE W_LoadDehackedLumps, so any addfile called by COM_BufInsertText called by Lua doesn't overwrite what we just loaded
- W_LoadDehackedLumps(numwadfiles-1);
+
+ // TODO: HACK ALERT - Load Lua & SOC stuff right here. I feel like this should be out of this place, but... Let's stick with this for now.
+ switch (wadfile->type)
+ {
+ case RET_WAD:
+ W_LoadDehackedLumps(numwadfiles - 1);
+ break;
+ case RET_PK3:
+ W_LoadDehackedLumpsPK3(numwadfiles - 1);
+ break;
+ case RET_SOC:
+ CONS_Printf(M_GetText("Loading SOC from %s\n"), wadfile->filename);
+ DEH_LoadDehackedLumpPwad(numwadfiles - 1, 0);
+ break;
+ case RET_LUA:
+ LUA_LoadLump(numwadfiles - 1, 0);
+ break;
+ default:
+ break;
+ }
W_InvalidateLumpnumCache();
-
return wadfile->numlumps;
}
@@ -580,7 +858,7 @@ INT32 W_InitMultipleFiles(char **filenames)
for (; *filenames; filenames++)
{
//CONS_Debug(DBG_SETUP, "Loading %s\n", *filenames);
- rc &= (W_LoadWadFile(*filenames) != INT16_MAX) ? 1 : 0;
+ rc &= (W_InitFile(*filenames) != INT16_MAX) ? 1 : 0;
}
if (!numwadfiles)
@@ -658,6 +936,51 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump)
return INT16_MAX;
}
+// Look for the first lump from a folder.
+UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump)
+{
+ INT32 i;
+ lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
+ for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
+ {
+ if (strnicmp(name, lump_p->name2, strlen(name)) == 0)
+ break;
+ }
+ return i;
+}
+
+// In a PK3 type of resource file, it looks for the next lumpinfo entry that doesn't share the specified pathfile.
+// Useful for finding folder ends.
+// Returns the position of the lumpinfo entry.
+UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump)
+{
+ INT32 i;
+ lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
+ for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
+ {
+ if (strnicmp(name, lump_p->name2, strlen(name)))
+ break;
+ }
+ return i;
+}
+
+// In a PK3 type of resource file, it looks for an entry with the specified full name.
+// Returns lump position in PK3's lumpinfo, or INT16_MAX if not found.
+UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump)
+{
+ INT32 i;
+ lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
+ for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
+ {
+ if (!strnicmp(name, lump_p->name2, strlen(name)))
+ {
+ return i;
+ }
+ }
+ // Not found at all?
+ return INT16_MAX;
+}
+
//
// W_CheckNumForName
// Returns LUMPERROR if name not found.
@@ -698,6 +1021,37 @@ lumpnum_t W_CheckNumForName(const char *name)
}
}
+// Look for valid map data through all added files in descendant order.
+// Get a map marker for WADs, and a standalone WAD file lump inside PK3s.
+// TODO: Make it search through cache first, maybe...?
+lumpnum_t W_CheckNumForMap(const char *name)
+{
+ UINT16 lumpNum, end;
+ UINT32 i;
+ for (i = numwadfiles - 1; i < numwadfiles; i--)
+ {
+ if (wadfiles[i]->type == RET_WAD)
+ {
+ for (lumpNum = 0; lumpNum < wadfiles[i]->numlumps; lumpNum++)
+ if (!strncmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8))
+ return (i<<16) + lumpNum;
+ }
+ else if (wadfiles[i]->type == RET_PK3)
+ {
+ lumpNum = W_CheckNumForFolderStartPK3("maps/", i, 0);
+ if (lumpNum != INT16_MAX)
+ end = W_CheckNumForFolderEndPK3("maps/", i, lumpNum);
+ else
+ continue;
+ // Now look for the specified map.
+ for (++lumpNum; lumpNum < end; lumpNum++)
+ if (!strnicmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8))
+ return (i<<16) + lumpNum;
+ }
+ }
+ return LUMPERROR;
+}
+
//
// W_GetNumForName
//
@@ -728,15 +1082,19 @@ lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, con
// scan wad files backwards so patch lump files take precedence
for (i = numwadfiles - 1; i >= 0; i--)
{
- bsid = W_CheckNumForNamePwad(blockstart,(UINT16)i,0);
- if (bsid == INT16_MAX)
- continue; // block doesn't exist, keep going
- beid = W_CheckNumForNamePwad(blockend,(UINT16)i,0);
- // if block end doesn't exist, just search through everything
+ if (wadfiles[i]->type == RET_WAD)
+ {
+ bsid = W_CheckNumForNamePwad(blockstart, (UINT16)i, 0);
+ if (bsid == INT16_MAX)
+ continue; // Start block doesn't exist?
+ beid = W_CheckNumForNamePwad(blockend, (UINT16)i, 0);
+ if (beid == INT16_MAX)
+ continue; // End block doesn't exist?
- check = W_CheckNumForNamePwad(name,(UINT16)i,bsid);
- if (check < beid)
- return (i<<16)+check; // found it, in our constraints
+ check = W_CheckNumForNamePwad(name, (UINT16)i, bsid);
+ if (check < beid)
+ return (i<<16)+check; // found it, in our constraints
+ }
}
return LUMPERROR;
}
@@ -773,80 +1131,55 @@ size_t W_LumpLength(lumpnum_t lumpnum)
return W_LumpLengthPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum));
}
-/** Reads bytes from the head of a lump, without doing decompression.
- *
- * \param wad Wad number to read from.
- * \param lump Lump number to read from, within wad.
- * \param dest Buffer in memory to serve as destination.
- * \param size Number of bytes to read.
- * \param offest Number of bytes to offset.
- * \return Number of bytes read (should equal size).
- * \sa W_ReadLumpHeader
- */
-static size_t W_RawReadLumpHeader(UINT16 wad, UINT16 lump, void *dest, size_t size, size_t offset)
+//
+// W_IsLumpWad
+// Is the lump a WAD? (presumably in a PK3)
+//
+boolean W_IsLumpWad(lumpnum_t lumpnum)
{
- size_t bytesread;
- lumpinfo_t *l;
- FILE *handle;
+ if (wadfiles[WADFILENUM(lumpnum)]->type == RET_PK3)
+ {
+ const char *lumpfullName = (wadfiles[WADFILENUM(lumpnum)]->lumpinfo + LUMPNUM(lumpnum))->name2;
- l = wadfiles[wad]->lumpinfo + lump;
+ if (strlen(lumpfullName) < 4)
+ return false; // can't possibly be a WAD can it?
+ return !strnicmp(lumpfullName + strlen(lumpfullName) - 4, ".wad", 4);
+ }
- handle = wadfiles[wad]->handle;
-
- fseek(handle, (long)(l->position + offset), SEEK_SET);
- bytesread = fread(dest, 1, size, handle);
-
- return bytesread;
+ return false; // WADs should never be inside non-PK3s as far as SRB2 is concerned
}
-// Read a compressed lump; return it in newly Z_Malloc'd memory.
-// wad is number of wad file, lump is number of lump in wad.
-static void *W_ReadCompressedLump(UINT16 wad, UINT16 lump)
+#ifdef HAVE_ZLIB
+/* report a zlib or i/o error */
+void zerr(int ret)
{
-#ifdef ZWAD
- char *compressed, *data;
- const lumpinfo_t *l = &wadfiles[wad]->lumpinfo[lump];
- size_t retval;
-
- compressed = Z_Malloc(l->disksize, PU_STATIC, NULL);
- data = Z_Malloc(l->size, PU_STATIC, NULL);
- if (W_RawReadLumpHeader(wad, lump, compressed, l->disksize, 0)
- < l->disksize)
- {
- I_Error("wad %d, lump %d: cannot read compressed data",
- wad, lump);
- }
-
- retval = lzf_decompress(compressed, l->disksize, data, l->size);
-#ifndef AVOID_ERRNO
- if (retval == 0 && errno == E2BIG)
- {
- I_Error("wad %d, lump %d: compressed data too big "
- "(bigger than %s)", wad, lump, sizeu1(l->size));
- }
- else if (retval == 0 && errno == EINVAL)
- I_Error("wad %d, lump %d: invalid compressed data", wad, lump);
- else
-#endif
- if (retval != l->size)
- {
- I_Error("wad %d, lump %d: decompressed to wrong number of "
- "bytes (expected %s, got %s)", wad, lump,
- sizeu1(l->size), sizeu2(retval));
- }
- Z_Free(compressed);
- return data;
-#else
- (void)wad;
- (void)lump;
- //I_Error("ZWAD files not supported on this platform.");
- return NULL;
-#endif
+ CONS_Printf("zpipe: ");
+ switch (ret) {
+ case Z_ERRNO:
+ if (ferror(stdin))
+ CONS_Printf("error reading stdin\n");
+ if (ferror(stdout))
+ CONS_Printf("error writing stdout\n");
+ break;
+ case Z_STREAM_ERROR:
+ CONS_Printf("invalid compression level\n");
+ break;
+ case Z_DATA_ERROR:
+ CONS_Printf("invalid or incomplete deflate data\n");
+ break;
+ case Z_MEM_ERROR:
+ CONS_Printf("out of memory\n");
+ break;
+ case Z_VERSION_ERROR:
+ CONS_Printf("zlib version mismatch!\n");
+ }
}
+#endif
/** Reads bytes from the head of a lump.
* Note: If the lump is compressed, the whole thing has to be read anyway.
*
+ * \param wad Wad number to read from.
* \param lump Lump number to read from.
* \param dest Buffer in memory to serve as destination.
* \param size Number of bytes to read.
@@ -857,6 +1190,8 @@ static void *W_ReadCompressedLump(UINT16 wad, UINT16 lump)
size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, size_t offset)
{
size_t lumpsize;
+ lumpinfo_t *l;
+ FILE *handle;
if (!TestValidLump(wad,lump))
return 0;
@@ -870,17 +1205,116 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
if (!size || size+offset > lumpsize)
size = lumpsize - offset;
- if (wadfiles[wad]->lumpinfo[lump].compressed)
+ // Let's get the raw lump data.
+ // We setup the desired file handle to read the lump data.
+ l = wadfiles[wad]->lumpinfo + lump;
+ handle = wadfiles[wad]->handle;
+ fseek(handle, (long)(l->position + offset), SEEK_SET);
+
+ // But let's not copy it yet. We support different compression formats on lumps, so we need to take that into account.
+ switch(wadfiles[wad]->lumpinfo[lump].compression)
{
- UINT8 *data;
- data = W_ReadCompressedLump(wad, lump);
- if (!data) return 0;
- M_Memcpy(dest, data+offset, size);
- Z_Free(data);
- return size;
+ case CM_NOCOMPRESSION: // If it's uncompressed, we directly write the data into our destination, and return the bytes read.
+ return fread(dest, 1, size, handle);
+ case CM_LZF: // Is it LZF compressed? Used by ZWADs.
+ {
+#ifdef ZWAD
+ char *rawData; // The lump's raw data.
+ char *decData; // Lump's decompressed real data.
+ size_t retval; // Helper var, lzf_decompress returns 0 when an error occurs.
+
+ rawData = Z_Malloc(l->disksize, PU_STATIC, NULL);
+ decData = Z_Malloc(l->size, PU_STATIC, NULL);
+
+ if (fread(rawData, 1, l->disksize, handle) < l->disksize)
+ I_Error("wad %d, lump %d: cannot read compressed data", wad, lump);
+ retval = lzf_decompress(rawData, l->disksize, decData, l->size);
+#ifndef AVOID_ERRNO
+ if (retval == 0) // If this was returned, check if errno was set
+ {
+ // errno is a global var set by the lzf functions when something goes wrong.
+ if (errno == E2BIG)
+ I_Error("wad %d, lump %d: compressed data too big (bigger than %s)", wad, lump, sizeu1(l->size));
+ else if (errno == EINVAL)
+ I_Error("wad %d, lump %d: invalid compressed data", wad, lump);
+ }
+ // Otherwise, fall back on below error (if zero was actually the correct size then ???)
+#endif
+ if (retval != l->size)
+ {
+ I_Error("wad %d, lump %d: decompressed to wrong number of bytes (expected %s, got %s)", wad, lump, sizeu1(l->size), sizeu2(retval));
+ }
+
+ if (!decData) // Did we get no data at all?
+ return 0;
+ M_Memcpy(dest, decData + offset, size);
+ Z_Free(rawData);
+ Z_Free(decData);
+ return size;
+#else
+ //I_Error("ZWAD files not supported on this platform.");
+ return 0;
+#endif
+ }
+#ifdef HAVE_ZLIB
+ case CM_DEFLATE: // Is it compressed via DEFLATE? Very common in ZIPs/PK3s, also what most doom-related editors support.
+ {
+ UINT8 *rawData; // The lump's raw data.
+ UINT8 *decData; // Lump's decompressed real data.
+
+ int zErr; // Helper var.
+ z_stream strm;
+ unsigned long rawSize = l->disksize;
+ unsigned long decSize = l->size;
+
+ rawData = Z_Malloc(rawSize, PU_STATIC, NULL);
+ decData = Z_Malloc(decSize, PU_STATIC, NULL);
+
+ if (fread(rawData, 1, rawSize, handle) < rawSize)
+ I_Error("wad %d, lump %d: cannot read compressed data", wad, lump);
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+
+ strm.total_in = strm.avail_in = rawSize;
+ strm.total_out = strm.avail_out = decSize;
+
+ strm.next_in = rawData;
+ strm.next_out = decData;
+
+ zErr = inflateInit2(&strm, -15);
+ if (zErr == Z_OK)
+ {
+ zErr = inflate(&strm, Z_FINISH);
+ if (zErr == Z_STREAM_END)
+ {
+ M_Memcpy(dest, decData, size);
+ }
+ else
+ {
+ size = 0;
+ zerr(zErr);
+ (void)inflateEnd(&strm);
+ }
+ }
+ else
+ {
+ CONS_Printf("whopet\n");
+ size = 0;
+ zerr(zErr);
+ }
+
+ Z_Free(rawData);
+ Z_Free(decData);
+
+ return size;
+ }
+#endif
+ default:
+ I_Error("wad %d, lump %d: unsupported compression type!", wad, lump);
}
- else
- return W_RawReadLumpHeader(wad, lump, dest, size, offset);
+ return -1;
}
size_t W_ReadLumpHeader(lumpnum_t lumpnum, void *dest, size_t size, size_t offset)
@@ -1164,12 +1598,12 @@ static int W_VerifyFile(const char *filename, lumpchecklist_t *checklist,
if ((handle = W_OpenWadFile(&filename, false)) == NULL)
return -1;
- // detect dehacked file with the "soc" extension
- if (stricmp(&filename[strlen(filename) - 4], ".soc") != 0
+ // detect wad file by the absence of the other supported extensions
+ if (stricmp(&filename[strlen(filename) - 4], ".soc")
#ifdef HAVE_BLUA
- && stricmp(&filename[strlen(filename) - 4], ".lua") != 0
+ && stricmp(&filename[strlen(filename) - 4], ".lua")
#endif
- )
+ && stricmp(&filename[strlen(filename) - 4], ".pk3"))
{
// assume wad file
wadinfo_t header;
diff --git a/src/w_wad.h b/src/w_wad.h
index 62e418767..e2e17740f 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -22,6 +22,22 @@
#pragma interface
#endif
+// a raw entry of the wad directory
+// NOTE: This sits here and not in w_wad.c because p_setup.c makes use of it to load map WADs inside PK3s.
+#if defined(_MSC_VER)
+#pragma pack(1)
+#endif
+typedef struct
+{
+ UINT32 filepos; // file offset of the resource
+ UINT32 size; // size of the resource
+ char name[8]; // name of the resource
+} ATTRPACK filelump_t;
+#if defined(_MSC_VER)
+#pragma pack()
+#endif
+
+
// ==============================================================
// WAD FILE STRUCTURE DEFINITIONS
// ==============================================================
@@ -34,14 +50,26 @@ typedef struct
UINT32 infotableofs; // the 'directory' of resources
} wadinfo_t;
+// Available compression methods for lumps.
+typedef enum
+{
+ CM_NOCOMPRESSION,
+#ifdef HAVE_ZLIB
+ CM_DEFLATE,
+#endif
+ CM_LZF,
+ CM_UNSUPPORTED
+} compmethod;
+
// a memory entry of the wad directory
typedef struct
{
unsigned long position; // filelump_t filepos
unsigned long disksize; // filelump_t size
char name[9]; // filelump_t name[]
+ char *name2; // Used by PK3s. Dynamically allocated name.
size_t size; // real (uncompressed) size
- INT32 compressed; // i
+ compmethod compression; // lump compression method
} lumpinfo_t;
// =========================================================================
@@ -58,9 +86,21 @@ typedef struct
#include "m_aatree.h"
#endif
+// Resource type of the WAD. Yeah, I know this sounds dumb, but I'll leave it like this until I clean up the code further.
+typedef enum restype
+{
+ RET_WAD,
+ RET_SOC,
+ RET_LUA,
+ RET_PK3,
+ RET_UNKNOWN,
+} restype_t;
+
+
typedef struct wadfile_s
{
char *filename;
+ restype_t type;
lumpinfo_t *lumpinfo;
lumpcache_t *lumpcache;
#ifdef HWRENDER
@@ -86,7 +126,7 @@ void W_Shutdown(void);
// Opens a WAD file. Returns the FILE * handle for the file, or NULL if not found or could not be opened
FILE *W_OpenWadFile(const char **filename, boolean useerrors);
// Load and add a wadfile to the active wad files, returns numbers of lumps, INT16_MAX on error
-UINT16 W_LoadWadFile(const char *filename);
+UINT16 W_InitFile(const char *filename);
#ifdef DELFILE
void W_UnloadWadFile(UINT16 num);
#endif
@@ -99,6 +139,12 @@ const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump);
const char *W_CheckNameForNum(lumpnum_t lumpnum);
UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump); // checks only in one pwad
+
+UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump);
+UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump);
+UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump);
+
+lumpnum_t W_CheckNumForMap(const char *name);
lumpnum_t W_CheckNumForName(const char *name);
lumpnum_t W_GetNumForName(const char *name); // like W_CheckNumForName but I_Error on LUMPERROR
lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, const char *blockend);
@@ -107,6 +153,12 @@ UINT8 W_LumpExists(const char *name); // Lua uses this.
size_t W_LumpLengthPwad(UINT16 wad, UINT16 lump);
size_t W_LumpLength(lumpnum_t lumpnum);
+boolean W_IsLumpWad(lumpnum_t lumpnum); // for loading maps from WADs in PK3s
+
+#ifdef HAVE_ZLIB
+void zerr(int ret); // zlib error checking
+#endif
+
size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, size_t offset);
size_t W_ReadLumpHeader(lumpnum_t lump, void *dest, size_t size, size_t offest); // read all or a part of a lump
void W_ReadLumpPwad(UINT16 wad, UINT16 lump, void *dest);
diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj
index 0722c0b68..5bfa29b8c 100644
--- a/src/win32/Srb2win-vc10.vcxproj
+++ b/src/win32/Srb2win-vc10.vcxproj
@@ -132,6 +132,7 @@
+
diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters
index 95e79cab1..d20dd672b 100644
--- a/src/win32/Srb2win-vc10.vcxproj.filters
+++ b/src/win32/Srb2win-vc10.vcxproj.filters
@@ -228,6 +228,9 @@
LUA
+
+ LUA
+
LUA
diff --git a/src/y_inter.c b/src/y_inter.c
index ea3cfc33b..4b340cabd 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -1796,37 +1796,6 @@ void Y_EndIntermission(void)
usebuffer = false;
}
-//
-// Y_EndGame
-//
-// Why end the game?
-// Because Y_FollowIntermission and F_EndCutscene would
-// both do this exact same thing *in different ways* otherwise,
-// which made it so that you could only unlock Ultimate mode
-// if you had a cutscene after the final level and crap like that.
-// This function simplifies it so only one place has to be updated
-// when something new is added.
-void Y_EndGame(void)
-{
- // Only do evaluation and credits in coop games.
- if (gametype == GT_COOP)
- {
- if (nextmap == 1102-1) // end game with credits
- {
- F_StartCredits();
- return;
- }
- if (nextmap == 1101-1) // end game with evaluation
- {
- F_StartGameEvaluation();
- return;
- }
- }
-
- // 1100 or competitive multiplayer, so go back to title screen.
- D_StartTitle();
-}
-
//
// Y_FollowIntermission
//
@@ -1838,21 +1807,10 @@ static void Y_FollowIntermission(void)
return;
}
- if (nextmap < 1100-1)
- {
- // normal level
- G_AfterIntermission();
- return;
- }
-
- // Start a custom cutscene if there is one.
- if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking)
- {
- F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
- return;
- }
-
- Y_EndGame();
+ // This handles whether to play a post-level cutscene, end the game,
+ // or simply go to the next level.
+ // No need to duplicate the code here!
+ G_AfterIntermission();
}
#define UNLOAD(x) Z_ChangeTag(x, PU_CACHE); x = NULL
diff --git a/src/y_inter.h b/src/y_inter.h
index d7d3c95d7..4c6ad2bdf 100644
--- a/src/y_inter.h
+++ b/src/y_inter.h
@@ -15,7 +15,6 @@ void Y_IntermissionDrawer(void);
void Y_Ticker(void);
void Y_StartIntermission(void);
void Y_EndIntermission(void);
-void Y_EndGame(void);
typedef enum
{
diff --git a/windows-installer/! MAKE SURE FILES ARE IN new-install ARCHIVE FOLDER !.txt b/windows-installer/! MAKE SURE FILES ARE IN new-install ARCHIVE FOLDER !.txt
deleted file mode 100644
index e69de29bb..000000000
diff --git a/windows-installer/.gitignore b/windows-installer/.gitignore
deleted file mode 100644
index 40ca37130..000000000
--- a/windows-installer/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-*
-*.*
-!staging
-!sfx
-!uninstaller
-!! MAKE SURE FILES ARE IN new-install ARCHIVE FOLDER !.txt
-!BuildInstaller.bat
-!README.txt
-!VersionFileName.txt
-!.gitignore
diff --git a/windows-installer/BuildInstaller.bat b/windows-installer/BuildInstaller.bat
deleted file mode 100644
index cc6ff74b4..000000000
--- a/windows-installer/BuildInstaller.bat
+++ /dev/null
@@ -1,76 +0,0 @@
-@echo off
-
-set "SCRIPTDIR=%~dp0"
-set "SCRIPTDIR=%SCRIPTDIR:~0,-1%"
-
-IF [%SRB2VERSIONNAME%] == [] set /p SRB2VERSIONNAME=<"%SCRIPTDIR%\VersionFileName.txt"
-
-:: Find 7z
-
-set SVZIP=
-
-if NOT [%1] == [] (
- echo.%~1 | findstr /C:"7z" 1>nul
- if NOT errorlevel 1 (
- if exist "%~1" set "SVZIP=%~1"
- )
-)
-
-if ["%SVZIP%"] == [""] (
- if exist "%ProgramFiles(x86)%\7-Zip\7z.exe" set "SVZIP=%ProgramFiles(x86)%\7-Zip\7z.exe"
- if exist "%ProgramFiles%\7-Zip\7z.exe" set "SVZIP=%ProgramFiles%\7-Zip\7z.exe"
- if exist "%ProgramW6432%\7-Zip\7z.exe" set "SVZIP=%ProgramW6432%\7-Zip\7z.exe"
-)
-
-:: Is it in PATH?
-
-if ["%SVZIP%"] == [""] (
- "7z.exe" --help > NUL 2> NUL
- if NOT errorlevel 1 (
- set "SVZIP=7z.exe"
- )
-)
-
-:: Create the uninstaller
-
-if NOT ["%SVZIP%"] == [""] (
- del /f /q "%SCRIPTDIR%\Uninstaller.7z" 2> NUL
- "%SVZIP%" a -t7z "%SCRIPTDIR%\Uninstaller.7z" "%SCRIPTDIR%\uninstaller\*" -m5=LZMA2
- copy /y /b "%SCRIPTDIR%\sfx\7zsd_LZMA2.sfx" + "%SCRIPTDIR%\sfx\config-uninstaller.txt" + "%SCRIPTDIR%\Uninstaller.7z" "%SCRIPTDIR%\staging\new-install\uninstall.exe"
- del /f /q "%SCRIPTDIR%\uninstaller.7z"
-)
-
-:: Operate on install archives
-
-type NUL > "%SCRIPTDIR%\staging\new-install\staging.txt"
-
-if exist "%SCRIPTDIR%\Installer.7z" (
- if NOT ["%SVZIP%"] == [""] (
- "%SVZIP%" a "%SCRIPTDIR%\Installer.7z" "%SCRIPTDIR%\staging\*"
- )
- copy /y /b "%SCRIPTDIR%\sfx\7zsd_LZMA2.sfx" + "%SCRIPTDIR%\sfx\config-installer.txt" + "%SCRIPTDIR%\Installer.7z" "%SCRIPTDIR%\SRB2-%SRB2VERSIONNAME%-Installer.exe"
-)
-
-if exist "%SCRIPTDIR%\Patch.7z" (
- if NOT ["%SVZIP%"] == [""] (
- "%SVZIP%" a "%SCRIPTDIR%\Patch.7z" "%SCRIPTDIR%\staging\*"
- )
- copy /y /b "%SCRIPTDIR%\sfx\7zsd_LZMA2.sfx" + "%SCRIPTDIR%\sfx\config-patch.txt" + "%SCRIPTDIR%\Patch.7z" "%SCRIPTDIR%\SRB2-%SRB2VERSIONNAME%-Patch.exe"
-)
-
-if exist "%SCRIPTDIR%\Installer_x64.7z" (
- if NOT ["%SVZIP%"] == [""] (
- "%SVZIP%" a "%SCRIPTDIR%\Installer_x64.7z" "%SCRIPTDIR%\staging\*"
- )
- copy /y /b "%SCRIPTDIR%\sfx\7zsd_LZMA2_x64.sfx" + "%SCRIPTDIR%\sfx\config-installer.txt" + "%SCRIPTDIR%\Installer_x64.7z" "%SCRIPTDIR%\SRB2-%SRB2VERSIONNAME%-x64-Installer.exe"
-)
-
-if exist "%SCRIPTDIR%\Patch_x64.7z" (
- if NOT ["%SVZIP%"] == [""] (
- "%SVZIP%" a "%SCRIPTDIR%\Patch_x64.7z" "%SCRIPTDIR%\staging\*"
- )
- copy /y /b "%SCRIPTDIR%\sfx\7zsd_LZMA2_x64.sfx" + "%SCRIPTDIR%\sfx\config-patch.txt" + "%SCRIPTDIR%\Patch_x64.7z" "%SCRIPTDIR%\SRB2-%SRB2VERSIONNAME%-x64-Patch.exe"
-)
-
-del /f /q "%SCRIPTDIR%\staging\new-install\staging.txt"
-del /f /q "%SCRIPTDIR%\staging\new-install\uninstall.exe"
diff --git a/windows-installer/README.txt b/windows-installer/README.txt
deleted file mode 100644
index 67995f336..000000000
--- a/windows-installer/README.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-Windows Install Builder
-for SRB2
-
-This installer is much like the 7-Zip self-extracting archive, except
-this allows for scripting the post-install step.
-
-This also allows for some light customization, including dialog messages
-and program shortcuts.
-
-The included install scripts manage the game data location depending on the
-install location -- if installed in Program Files or AppData\Local, the
-game data location is set to %UserProfile%\SRB2.
-
-Program shortcuts are also added, as well as an uninstaller that
-will remove the icons and also selectively uninstall the core game files.
-The uninstaller gives you the option to preserve your game data and mods.
-
-How to Use
-----------
-
-1. Zip up the install contents in 7z format.
- * ALL FILES MUST BE IN THE `new-install/` ARCHIVE SUBFOLDER, OR THE
- POST-INSTALL SCRIPT WILL NOT WORK!
- * Make sure you are using the LZMA2 algorithm, which is 7-Zip's default.
-
-2. Copy the 7z archive to this folder using the following names:
- * Installer.7z - 32-bit full installer
- * Patch.7z - 32-bit patch
- * Installer_x64.7z - 64-bit full installer
- * Patch_x64.7z - 64-bit patch
-
-3. Set the text in VersionFilename.txt to the version identifier for the
- installer's filename.
- * e.g., v2121 for v2.1.21, from "SRB2-v2121-Installer.exe"
- * Also look through sfx/config-installer.txt and sfx/config-patch.txt
- and update the version strings. Templating is TODO.
-
-4. Run BuildInstaller.bat [7z.exe install path]
- * First argument is the path to 7z.exe. If this is not specified, the
- script will try to look for it in C:\Program Files [(x86)]
- * This script will automatically add the install scripts to each
- installer.
-
-Credit
-------
-
-OlegScherbakov/7zSFX
-https://github.com/OlegScherbakov/7zSFX
diff --git a/windows-installer/VersionFileName.txt b/windows-installer/VersionFileName.txt
deleted file mode 100644
index 559abffe4..000000000
--- a/windows-installer/VersionFileName.txt
+++ /dev/null
@@ -1 +0,0 @@
-v2121
\ No newline at end of file
diff --git a/windows-installer/sfx/7zsd_LZMA2.sfx b/windows-installer/sfx/7zsd_LZMA2.sfx
deleted file mode 100644
index c4ba9c584..000000000
Binary files a/windows-installer/sfx/7zsd_LZMA2.sfx and /dev/null differ
diff --git a/windows-installer/sfx/7zsd_LZMA2_x64.sfx b/windows-installer/sfx/7zsd_LZMA2_x64.sfx
deleted file mode 100644
index 758e4c2d0..000000000
Binary files a/windows-installer/sfx/7zsd_LZMA2_x64.sfx and /dev/null differ
diff --git a/windows-installer/sfx/config-installer.txt b/windows-installer/sfx/config-installer.txt
deleted file mode 100644
index bdbddea55..000000000
--- a/windows-installer/sfx/config-installer.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-;!@Install@!UTF-8!
-
-GUIFlags="8+32+64+4096"
-GUIMode="1"
-
-Title="Sonic Robo Blast 2 v2.1.21"
-BeginPrompt="Sonic Robo Blast 2 v2.1.21\nFull Installer\n\nSelect a folder to install SRB2 in.\n\nIf you install in \"AppData\\Local\" or \"Program Files\", your game data will be saved to:\n%UserProfile%\\SRB2\n\nOtherwise, your game data will be in the installation folder.\n\nShortcuts will be created in your Start Menu."
-
-ExtractPathText="Installation folder: (no exclamation points, please!)"
-InstallPath="%LocalAppData%\\SRB2"
-ExtractTitle="Installing..."
-ExtractDialogText="Installing SRB2 v2.1.21...\n\nCheck out our modding community!\nWe make levels, characters, and much more!\n\nVisit http://www.srb2.org/mods"
-
-Shortcut="Pu,{%%T\\srb2win.exe},{},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2},{%%T\\},{%%T\\srb2win.exe},{0}"
-Shortcut="Pu,{%%T\\srb2win.exe},{-win},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2 (Windowed)},{%%T\\},{%%T\\srb2win.exe},{0}"
-Shortcut="Pu,{%%T\\srb2win.exe},{-opengl},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2 (OpenGL)},{%%T\\},{%%T\\srb2win.exe},{0}"
-Shortcut="Pu,{%%T\\srb2win.exe},{-opengl -win},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2 (OpenGL, Windowed)},{%%T\\},{%%T\\srb2win.exe},{0}"
-Shortcut="Pu,{%%T\\srb2dd.exe},{},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2 (DirectDraw)},{%%T\\},{%%T\\srb2dd.exe},{0}"
-Shortcut="Pu,{%%T\\srb2dd.exe},{-win},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2 (DirectDraw, Windowed)},{%%T\\},{%%T\\srb2dd.exe},{0}"
-Shortcut="Pu,{%%T\\uninstall.exe},{},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{Uninstall SRB2},{%%T\\},{shell32.dll},{31}"
-
-RunProgram="nowait:\"%%T\\new-install\\staging.bat\""
-
-;!@InstallEnd@!
diff --git a/windows-installer/sfx/config-patch.txt b/windows-installer/sfx/config-patch.txt
deleted file mode 100644
index bde6135dc..000000000
--- a/windows-installer/sfx/config-patch.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-;!@Install@!UTF-8!
-
-GUIFlags="8+32+64+4096"
-GUIMode="1"
-
-Title="Sonic Robo Blast 2 v2.1.21"
-BeginPrompt="Sonic Robo Blast 2 v2.1.21\nPatch Installer\n\nYou must have at least v2.1.15 to use this patch.\n\nSelect your current SRB2 folder.\n\nShortcuts will be created in your Start Menu."
-
-ExtractPathText="Current SRB2 folder: (no exclamation points, please!)"
-InstallPath="%LocalAppData%\\SRB2"
-ExtractTitle="Installing..."
-ExtractDialogText="Installing SRB2 v2.1.21...\n\nCheck out our modding community!\nWe make levels, characters, and much more!\n\nVisit http://www.srb2.org/mods"
-
-Shortcut="Pu,{%%T\\srb2win.exe},{},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2},{%%T\\},{%%T\\srb2win.exe},{0}"
-Shortcut="Pu,{%%T\\srb2win.exe},{-win},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2 (Windowed)},{%%T\\},{%%T\\srb2win.exe},{0}"
-Shortcut="Pu,{%%T\\srb2win.exe},{-opengl},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2 (OpenGL)},{%%T\\},{%%T\\srb2win.exe},{0}"
-Shortcut="Pu,{%%T\\srb2win.exe},{-opengl -win},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2 (OpenGL, Windowed)},{%%T\\},{%%T\\srb2win.exe},{0}"
-Shortcut="Pu,{%%T\\srb2dd.exe},{},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2 (DirectDraw)},{%%T\\},{%%T\\srb2dd.exe},{0}"
-Shortcut="Pu,{%%T\\srb2dd.exe},{-win},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{SRB2 (DirectDraw, Windowed)},{%%T\\},{%%T\\srb2dd.exe},{0}"
-Shortcut="Pu,{%%T\\uninstall.exe},{},{Sonic Robo Blast 2},{Sonic Robo Blast 2 (SRB2), a 3D Sonic the Hedgehog fangame.},{Uninstall SRB2},{%%T\\},{shell32.dll},{31}"
-
-RunProgram="nowait:\"%%T\\new-install\\staging.bat\""
-
-;!@InstallEnd@!
diff --git a/windows-installer/sfx/config-uninstaller.txt b/windows-installer/sfx/config-uninstaller.txt
deleted file mode 100644
index f3de9e269..000000000
--- a/windows-installer/sfx/config-uninstaller.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-;!@Install@!UTF-8!
-
-GUIFlags="1+2+8"
-GUIMode="2"
-
-Title="Uninstall SRB2"
-BeginPrompt="Are you sure you want to uninstall Sonic Robo Blast 2?\n\nYour game data and mods will be preserved, as well as\nany extra files in your install folder."
-
-InstallPath="%%S"
-
-RunProgram="nowait:\"%%S\\uninstall.bat\" /y"
-
-;!@InstallEnd@!
diff --git a/windows-installer/staging/! SRB2 INSTALL INSTRUCTIONS !.txt b/windows-installer/staging/! SRB2 INSTALL INSTRUCTIONS !.txt
deleted file mode 100644
index f367296f5..000000000
--- a/windows-installer/staging/! SRB2 INSTALL INSTRUCTIONS !.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-SRB2 Install Instructions
-
-1. Move every file from the "new-install" folder to this main install folder.
-
-2. DELETE "staging.bat" and "staging.txt"! These can mess up your installation if run by accident!
-
-3. Optionally, create a folder in your user profile named "SRB2". This is where your game data and addons may live. For example,
-
- C:\Users\[User]\SRB2
-
-4. Run the game! Double-click srb2win.exe -- or see if you have Start Menu icons under "Sonic Robo Blast 2".
\ No newline at end of file
diff --git a/windows-installer/staging/new-install/old-install-list.txt b/windows-installer/staging/new-install/old-install-list.txt
deleted file mode 100644
index 6f33660ac..000000000
--- a/windows-installer/staging/new-install/old-install-list.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-exchndl.dll
-fmodex.dll
-libFLAC-8.dll
-libgme.dll
-libintl-8.dll
-libmikmod-2.dll
-libogg-0.dll
-libvorbis-0.dll
-libvorbisfile-3.dll
-r_opengl.dll
-SDL2.dll
-SDL2_mixer.dll
-smpeg2.dll
-srb2dd.exe
-srb2win.exe
\ No newline at end of file
diff --git a/windows-installer/staging/new-install/staging.bat b/windows-installer/staging/new-install/staging.bat
deleted file mode 100644
index 2c3c94915..000000000
--- a/windows-installer/staging/new-install/staging.bat
+++ /dev/null
@@ -1,363 +0,0 @@
-@echo off
-
-setlocal enabledelayedexpansion
-
-cls
-
-:: SRB2 Install Staging
-::
-:: This accomplishes the following tasks:
-::
-:: 1. Creates a user profile folder if SRB2 is installed in AppData or Program Files, and config.cfg is not already in the install folder
-::
-:: 2. Moves old installation files into old-install
-::
-:: 3. Moves new installaton files into install folder
-::
-
-:: Get Parent folder (the SRB2 install folder)
-::
-:: https://wiert.me/2011/08/30/batch-file-to-get-parent-directory-not-the-directory-of-the-batch-file-but-the-parent-of-that-directory/
-
-set "STAGINGDIR=%~dp0"
-:: strip trailing backslash
-set "STAGINGDIR=!STAGINGDIR:~0,-1!"
-:: ~dp only works for batch file parameters and loop indexes
-for %%d in ("!STAGINGDIR!") do set "INSTALLDIR=%%~dpd"
-set "INSTALLDIR=!INSTALLDIR:~0,-1!"
-
-:: Find 7z
-
-set SVZIP=
-if ["%SVZIP%"] == [""] (
- if exist "!ProgramFiles(x86)!\7-Zip\7z.exe" set "SVZIP=!ProgramFiles(x86)!\7-Zip\7z.exe"
- if exist "!ProgramFiles!\7-Zip\7z.exe" set "SVZIP=!ProgramFiles!\7-Zip\7z.exe"
- if exist "!ProgramW6432!\7-Zip\7z.exe" set "SVZIP=!ProgramW6432!\7-Zip\7z.exe"
-)
-
-:: Is it in PATH?
-
-if ["%SVZIP%"] == [""] (
- "7z.exe" --help > NUL 2> NUL
- if NOT errorlevel 1 (
- set "SVZIP=7z.exe"
- )
-)
-
-:: FAILSAFE: Check if staging.txt exists in the directory
-:: If not, exit, so we don't mess up anything by accident.
-
-if NOT exist "!STAGINGDIR!\staging.txt" (
- exit
-)
-
-: CheckPermissionsInstall
-
-:: Write a dummy file and check for an error. If error, we need administrator rights
-
-:: NOTE: We should never have to deal with this because the main installer should
-:: already have the rights.
-
-mkdir "!INSTALLDIR!\install-dummy"
-
-:: TODO elevate automatically
-if errorlevel 1 (
- echo Finish installing SRB2 with these steps:
- echo.
- echo 1. Go to your SRB2 install folder
- echo.
- echo !INSTALLDIR!
- echo.
- echo 2. Copy all files from the "new-install" subfolder into the main folder
- echo and DELETE staging.bat and staging.txt!!!
- echo.
- echo 3. Optionally, create a folder in your user profile named "SRB2".
- echo This is where your game data and addons may live.
- echo To create the folder, go here:
- echo.
- echo !USERPROFILE!
- echo.
- echo If anything fails, you may delete the files and try to run the installer
- echo again with Administrator Rights: Right-click on the icon and click
- echo "Run as administrator."
- echo.
- "!SystemRoot!\explorer.exe" "!INSTALLDIR!"
- set /p ADMINFINAL="Press Enter key to exit. "
-
- exit
-) else (
- rmdir /s /q "!INSTALLDIR!\install-dummy"
- goto CheckUserDir
-)
-
-: CheckUserDir
-
-:: Check if we need to create !userprofile!\SRB2
-
-set "USERDIR=!INSTALLDIR!"
-
-:: Is config.cfg in our install dir?
-if exist "!INSTALLDIR!\config.cfg" goto MoveOldInstall
-
-:: Are we in AppData?
-echo.!STAGINGDIR! | findstr /C:"!LocalAppData!" 1>nul
-if errorlevel 1 (
- echo.
-) else (
- goto SetUserDir
-)
-
-: Are we in Program Files?
-echo.!STAGINGDIR! | findstr /C:"!ProgramFiles!" 1>nul
-if NOT errorlevel 1 (
- goto SetUserDir
-)
-
-:: Are we in Program Files (x86)?
-echo.!STAGINGDIR! | findstr /C:"!ProgramFiles(X86)!" 1>nul
-if NOT errorlevel 1 (
- goto SetUserDir
-)
-
-:: Are we 32-bit and actually in Program Files?
-echo.!STAGINGDIR! | findstr /C:"!ProgramW6432!" 1>nul
-if NOT errorlevel 1 (
- goto SetUserDir
-)
-
-goto MoveOldInstall
-
-: SetUserDir
-: CheckPermissionsUserDir
-
-set "USERDIR=!UserProfile!\SRB2"
-
-:: Check for permissions and create the folder
-if exist "!USERDIR!\*" (
- mkdir "!USERDIR!\install-dummy"
-
- if errorlevel 1 (
- echo User profile folder exists, but we won't operate on it.
- echo.
- goto MoveOldInstall
- ) else (
- rmdir /s /q "!USERDIR!\install-dummy"
- )
-) else (
- mkdir "!USERDIR!"
-
- if errorlevel 1 (
- echo Could not create user profile folder
- echo Defaulting to install dir: "!INSTALLDIR!"
- echo.
- set "USERDIR=!INSTALLDIR!"
- goto MoveOldInstall
- )
-)
-
-:: Now copy READMEs
-:: echo f answers xcopy's prompt as to whether the destination is a file or a folder
-echo f | xcopy /y "!STAGINGDIR!\README.txt" "!USERDIR!\README.txt"
-echo f | xcopy /y "!STAGINGDIR!\LICENSE.txt" "!USERDIR!\LICENSE.txt"
-echo f | xcopy /y "!STAGINGDIR!\LICENSE-3RD-PARTY.txt" "!USERDIR!\LICENSE-3RD-PARTY.txt"
-echo Your game data and mods folder is: > "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-echo. >> "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-echo !USERDIR! >> "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-echo. >> "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-echo Your install folder is: >> "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-echo. >> "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-echo !INSTALLDIR! >> "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-echo. >> "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-echo To run SRB2, go to: >> "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-echo. >> "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-echo Start Menu ^> Programs ^> Sonic Robo Blast 2 >> "!USERDIR!\^! Data and Mods Go Here ^!.txt"
-
-:: Copy path to install folder
-
-set "SCRIPT=!TEMP!\!RANDOM!-!RANDOM!-!RANDOM!-!RANDOM!.vbs"
-echo Set oWS = WScript.CreateObject("WScript.Shell") >> "!SCRIPT!"
-echo sLinkFile = "!USERDIR!\^! SRB2 Install Folder ^!.lnk" >> "!SCRIPT!"
-echo Set oLink = oWS.CreateShortcut(sLinkFile) >> "!SCRIPT!"
-echo oLink.TargetPath = "!INSTALLDIR!" >> "!SCRIPT!"
-echo oLink.WorkingDirectory = "!INSTALLDIR!" >> "!SCRIPT!"
-echo oLink.Arguments = "" >> "!SCRIPT!"
-echo oLink.IconLocation = "!INSTALLDIR!\srb2win.exe,0" >> "!SCRIPT!"
-echo oLink.Save >> "!SCRIPT!"
-cscript /nologo "!SCRIPT!"
-del "!SCRIPT!"
-
-:: Also do it the other way around
-
-set "SCRIPT=!TEMP!\!RANDOM!-!RANDOM!-!RANDOM!-!RANDOM!.vbs"
-echo Set oWS = WScript.CreateObject("WScript.Shell") >> "!SCRIPT!"
-echo sLinkFile = "!INSTALLDIR!\^! SRB2 Data Folder ^!.lnk" >> "!SCRIPT!"
-echo Set oLink = oWS.CreateShortcut(sLinkFile) >> "!SCRIPT!"
-echo oLink.TargetPath = "!USERDIR!" >> "!SCRIPT!"
-echo oLink.WorkingDirectory = "!USERDIR!" >> "!SCRIPT!"
-echo oLink.Arguments = "" >> "!SCRIPT!"
-echo oLink.IconLocation = "!INSTALLDIR!\srb2win.exe,0" >> "!SCRIPT!"
-echo oLink.Save >> "!SCRIPT!"
-cscript /nologo "!SCRIPT!"
-del "!SCRIPT!"
-
-: MoveOldInstall
-
-if exist "!INSTALLDIR!\old-install\*" (
- set "OLDINSTALLDIR=!INSTALLDIR!\old-install-!RANDOM!"
-) else (
- set "OLDINSTALLDIR=!INSTALLDIR!\old-install"
-)
-
-mkdir "!OLDINSTALLDIR!"
-
-::
-:: Move all old install files
-:: We support a list of explicit files to copy to old-install
-:: And later, we also loop through our staging files, look for the pre-existing copy in
-:: install root, then copy that also to old-install
-::
-
-:: Extract the uninstall-list.txt and uninstall-userdir.txt files from uninstaller.exe
-:: if it exists
-
-if exist "!INSTALLDIR!\uninstall.exe" (
- if NOT ["!SVZIP!"] == [""] (
- "!SVZIP!" x "!INSTALLDIR!\uninstall.exe" "uninstall-list.txt" -o"!INSTALLDIR!"
- "!SVZIP!" x "!INSTALLDIR!\uninstall.exe" "uninstall-userdir.txt" -o"!INSTALLDIR!"
- )
-)
-
-set OLDINSTALLCHANGED=
-
-if exist "!STAGINGDIR!\old-install-list.txt" (
- goto MoveOldInstallOldFiles
-) else (
- goto MoveOldInstallNewFiles
-)
-
-: MoveOldInstallOldFiles
-
-set "TESTFILE=!TEMP!\!RANDOM!.txt"
-
-:: Do our failsafes before copying the file in the list
-:: See uninstall.bat for details
-for /F "usebackq tokens=*" %%A in ("!STAGINGDIR!\old-install-list.txt") do (
- if exist "!INSTALLDIR!\%%A" (
- if NOT ["%%A"] == [""] (
- if NOT ["%%A"] == ["%~nx0"] (
- echo %%A> "!TESTFILE!"
- findstr /r ".*[<>:\"\"/\\|?*%%].*" "!TESTFILE!" >nul
- if !errorlevel! equ 0 (
- echo %%A has invalid characters, skipping...
- ) else (
- if exist "!INSTALLDIR!\%%A\*" (
- echo %%A is a folder, skipping...
- ) else (
- echo Moving !INSTALLDIR!\%%A to "old-install" folder
- echo f | xcopy /y /v "!INSTALLDIR!\%%A" "!OLDINSTALLDIR!\%%A"
- if errorlevel 0 del /f /q "!INSTALLDIR!\%%A"
- )
- )
- )
- )
- )
-)
-
-del /q /f "!STAGINGDIR!\old-install-list.txt"
-
-for %%F in ("!OLDINSTALLDIR!\*") DO (
- set OLDINSTALLCHANGED=1
- goto MoveOldInstallNewFiles
-)
-
-: MoveOldInstallNewFiles
-
-:: Save a list of standard files
-:: So the uninstall script will know what to remove
-:: Append to any existing file, in case we are a patch
-
-dir /b /a-d "!STAGINGDIR!" >> "!INSTALLDIR!\uninstall-list.txt"
-
-:: Overwrite the last known gamedata folder
-
-echo !USERDIR! > "!INSTALLDIR!\uninstall-userdir.txt"
-
-:: Add the install-generated to the uninstall list
-:: NO FOLLOWING SPACES AFTER THE FILENAME!!!
-
-echo uninstall.bat>> "!INSTALLDIR!\uninstall-list.txt"
-echo uninstall-list.txt>> "!INSTALLDIR!\uninstall-list.txt"
-echo uninstall-userdir.txt>> "!INSTALLDIR!\uninstall-list.txt"
-:: *ahem* Prints as ^! SRB2 Data Folder ^!.lnk
-:: We need to escape the exclamations (^^!) and the carets themselves (^^^^)
-echo ^^^^^^! SRB2 Data Folder ^^^^^^!.lnk>> "!INSTALLDIR!\uninstall-list.txt"
-
-:: Add the uninstall list files to the uninstall EXE
-
-if NOT ["!SVZIP!"] == [""] (
- if exist "!INSTALLDIR!\new-install\uninstall.exe" (
- "!SVZIP!" a "!INSTALLDIR!\new-install\uninstall.exe" "!INSTALLDIR!\uninstall-list.txt" -sdel
- "!SVZIP!" a "!INSTALLDIR!\new-install\uninstall.exe" "!INSTALLDIR!\uninstall-userdir.txt" -sdel
- )
-)
-
-:: Start moving files
-
-for %%F in ("!STAGINGDIR!\*") DO (
- if exist "!INSTALLDIR!\%%~nxF" (
- set OLDINSTALLCHANGED=1
- move "!INSTALLDIR!\%%~nxF" "!OLDINSTALLDIR!\%%~nxF"
- )
- if NOT ["%%~nxF"] == ["staging.bat"] (
- if NOT ["%%~nxF"] == ["staging.txt"] (
- move "!STAGINGDIR!\%%~nxF" "!INSTALLDIR!\%%~nxF"
- )
- )
-)
-
-: Finished
-
-del /q /f "!INSTALLDIR!\^! SRB2 INSTALL INSTRUCTIONS ^!.txt"
-
-set MSGEXE=
-if exist "!SystemRoot!\System32\msg.exe" (
- set MSGEXE=!SystemRoot!\System32\msg.exe
-) else (
- if exist "!SystemRoot!\Sysnative\msg.exe" (
- set MSGEXE=!SystemRoot!\Sysnative\msg.exe
- )
-)
-
-if ["!OLDINSTALLCHANGED!"] == ["1"] (
- "!systemroot!\explorer.exe" /select, "!OLDINSTALLDIR!"
- echo Finished^^! Some of your old installation files were moved to the "old-install" folder. > !TEMP!\srb2msgprompt.txt
- echo. >> !TEMP!\srb2msgprompt.txt
- echo If you no longer need these files, you may delete the folder safely. >> !TEMP!\srb2msgprompt.txt
- echo. >> !TEMP!\srb2msgprompt.txt
- echo To run SRB2, go to: Start Menu ^> Programs ^> Sonic Robo Blast 2. >> !TEMP!\srb2msgprompt.txt
- !MSGEXE! "!username!" < !TEMP!\srb2msgprompt.txt
- del !TEMP!\srb2msgprompt.txt
-) else (
- if /I ["!USERDIR!"] == ["!INSTALLDIR!"] (
- "!systemroot!\explorer.exe" "!INSTALLDIR!"
- echo Finished^^! > !TEMP!\srb2msgprompt.txt
- echo. >> !TEMP!\srb2msgprompt.txt
- echo To run SRB2, go to: Start Menu ^> Programs ^> Sonic Robo Blast 2. >> !TEMP!\srb2msgprompt.txt
- !MSGEXE! "!username!" < !TEMP!\srb2msgprompt.txt
- del !TEMP!\srb2msgprompt.txt
- ) else (
- "!systemroot!\explorer.exe" "!USERDIR!"
- echo Finished^^! You may find your game data in this folder: > !TEMP!\srb2msgprompt.txt
- echo. >> !TEMP!\srb2msgprompt.txt
- echo !USERDIR! >> !TEMP!\srb2msgprompt.txt
- echo. >> !TEMP!\srb2msgprompt.txt
- echo To run SRB2, go to: Start Menu ^> Programs ^> Sonic Robo Blast 2. >> !TEMP!\srb2msgprompt.txt
- !MSGEXE! "!username!" < !TEMP!\srb2msgprompt.txt
- del !TEMP!\srb2msgprompt.txt
- )
-)
-
-: Attempt to remove OLDINSTALLDIR, in case it's empty
-rmdir /q "!OLDINSTALLDIR!"
-cd \
-start "" /b "cmd" /s /c " del /f /q "%STAGINGDIR%\*"&rmdir /s /q "%STAGINGDIR%"&exit /b "
diff --git a/windows-installer/uninstaller/uninstall.bat b/windows-installer/uninstaller/uninstall.bat
deleted file mode 100644
index ed7c33391..000000000
--- a/windows-installer/uninstaller/uninstall.bat
+++ /dev/null
@@ -1,183 +0,0 @@
-@echo off
-
-setlocal enabledelayedexpansion
-
-cls
-
-set "INSTALLDIR=%~dp0"
-set "INSTALLDIR=!INSTALLDIR:~0,-1!"
-set /p USERDIR=<"!INSTALLDIR!\uninstall-userdir.txt"
-set "USERDIR=!USERDIR:~0,-1!"
-
-: ProceedPrompt
-
-if ["%1"] == ["/y"] (
- set "PROCEED=1"
-) else (
- set PROCEED=
- set /p PROCEED="Are you sure you want to uninstall SRB2? [yes/no] "
-
- if /I ["!PROCEED:~0,1!"] == ["n"] exit
- if /I ["!PROCEED!"] == ["y"] (
- echo Type Yes or No
- echo.
- goto ProceedPrompt
- ) else (
- if /I ["!PROCEED!"] == ["yes"] (
- set PROCEED=1
- ) else (
- echo.
- goto ProceedPrompt
- )
- )
-)
-
-:: Failsafe, in case we Ctrl+C and decline "Terminate batch file?"
-
-if NOT ["!PROCEED!"] == ["1"] (
- exit
-)
-
-: CheckPermissions
-
-:: Write a dummy file and check for an error. If error, we need administrator rights
-
-mkdir "!INSTALLDIR!\uninstall-dummy"
-
-:: TODO elevate automatically
-if errorlevel 1 (
- echo We need Administrator Rights to uninstall SRB2.
- echo.
- echo Try running this uninstaller by right-clicking on the icon
- echo and click "Run as administrator"
- echo.
- set /p ADMINFINAL="Press Enter key to exit. "
- exit
-) else (
- rmdir /s /q "!INSTALLDIR!\uninstall-dummy"
- goto DeleteFiles
-)
-
-: DeleteFiles
-
-:: Our deletion list is a list of filenames, no paths, in the current folder
-::
-:: We apply the following failsafes:
-:: 1. Is filename the script itself?
-:: 2. Does filename have illegal characters? https://stackoverflow.com/a/33625339/241046
-:: 3. Is filename a directory?
-::
-:: TODO hack this to support .\file.txt relative paths
-:: Can %%A be substring'd to get only the filename and extension?
-:: If so, print that to the temp file instead of the whole line
-:: And possibly do the folder check before the invalid char check.
-:: ALSO: Don't honor upward relative paths! (..\)
-::
-set "TESTFILE=!TEMP!\!RANDOM!.txt"
-
-for /F "usebackq tokens=*" %%A in ("!INSTALLDIR!\uninstall-list.txt") do (
- if exist "!INSTALLDIR!\%%A" (
- if NOT ["%%A"] == [""] (
- if NOT ["%%A"] == ["%~nx0"] (
- echo %%A> "!TESTFILE!"
- findstr /r ".*[<>:\"\"/\\|?*%%].*" "!TESTFILE!" >nul
- if !errorlevel! equ 0 (
- echo %%A has invalid characters, skipping...
- ) else (
- if exist "!INSTALLDIR!\%%A\*" (
- echo %%A is a folder, skipping...
- ) else (
- echo Deleting !INSTALLDIR!\%%A
- del /q /f "!INSTALLDIR!\%%A"
- )
- )
- )
- )
- )
-)
-
-del /q /f "!TESTFILE!"
-
-: AllDone
-
-:: Delete the program icons
-echo Deleting your program icons...
-echo.
-
-cd \
-rmdir /s /q "!AppData!\Microsoft\Windows\Start Menu\Programs\Sonic Robo Blast 2"
-
-:: Check if our install folder is non-empty
-
-set USERDIRFILLED=
-set INSTALLDIRFILLED=
-for /F %%i in ('dir /b /a "!USERDIR!\*"') do (
- if NOT ["%%i"] == ["%~nx0"] (
- set USERDIRFILLED=1
- goto InstallFilledCheck
- )
-)
-
-: InstallFilledCheck
-
-if /I NOT ["!USERDIR!"] == ["!INSTALLDIR!"] (
- for /F %%i in ('dir /b /a "!INSTALLDIR!\*"') do (
- if ["%%i"] == ["%~nx0"] (
- echo.
- ) else (
- set INSTALLDIRFILLED=1
- goto Final
- )
- )
-)
-
-: Final
-
-echo All done^^! Visit http://www.srb2.org if you want to play SRB2 again^^!
-echo.
-
-set "FINALPROMPT=Press Enter key to exit."
-if ["!USERDIRFILLED!"] == ["1"] (
- echo We left your game data and mods alone, so you may delete those manually.
- echo.
- echo !USERDIR!
- echo.
- set "FINALPROMPT=Do you want to view your data? [yes/no]"
-)
-
-if ["!INSTALLDIRFILLED!"] == ["1"] (
- echo We left some extra files alone in your install folder.
- echo.
- echo !INSTALLDIR!
- echo.
- set "FINALPROMPT=Do you want to view your data? [yes/no]"
-)
-
-set FINALRESPONSE=
-set /p FINALRESPONSE="!FINALPROMPT! "
-
-if NOT ["!FINALPROMPT!"] == ["Press Enter key to exit."] (
- if /I ["!FINALRESPONSE:~0,1!"] == ["y"] (
- if ["!USERDIRFILLED!"] == ["1"] (
- "!SystemRoot!\explorer.exe" "!USERDIR!"
- )
- if ["!INSTALLDIRFILLED!"] == ["1"] (
- "!SystemRoot!\explorer.exe" "!INSTALLDIR!"
- )
- ) else (
- if ["!FINALRESPONSE!"] == [""] (
- if ["!USERDIRFILLED!"] == ["1"] (
- "!SystemRoot!\explorer.exe" "!USERDIR!"
- )
- if ["!INSTALLDIRFILLED!"] == ["1"] (
- "!SystemRoot!\explorer.exe" "!INSTALLDIR!"
- )
- )
- )
-)
-
-: DeferredDelete
-
-:: Now let's delete our installation folder!
-cd \
-start "" /b "cmd" /s /c " del /q /f "%INSTALLDIR%\uninstall.bat"&timeout /t 2 > NUL&rmdir "%INSTALLDIR%"&exit /b "