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 "