Merge branch 'master' into next

This commit is contained in:
SteelT 2022-11-04 14:53:51 -04:00
commit 32f88155f3
34 changed files with 1104 additions and 291 deletions

View file

@ -42,12 +42,12 @@ jobs:
paths: paths:
- /var/cache/apt/archives - /var/cache/apt/archives
- checkout - checkout
- run: #- run:
name: Compile without network support and BLUA # name: Compile without network support and BLUA
command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1 # command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1
- run: #- run:
name: wipe build # name: wipe build
command: make -C src LINUX=1 cleandep # command: make -C src LINUX=1 cleandep
- run: - run:
name: rebuild depend name: rebuild depend
command: make -C src LINUX=1 clean command: make -C src LINUX=1 clean

View file

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0)
# DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string. # DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string.
# Version change is fine. # Version change is fine.
project(SRB2 project(SRB2
VERSION 1.4 VERSION 1.6
LANGUAGES C) LANGUAGES C)
if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR}) if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})

View file

@ -353,6 +353,40 @@ size_t COM_CheckParm(const char *check)
return 0; return 0;
} }
/** \brief COM_CheckParm, but checks only the start of each argument.
* E.g. checking for "-no" would match "-noerror" too.
*/
size_t COM_CheckPartialParm(const char *check)
{
int len;
size_t i;
len = strlen(check);
for (i = 1; i < com_argc; i++)
{
if (strncasecmp(check, com_argv[i], len) == 0)
return i;
}
return 0;
}
/** Find the first argument that starts with a hyphen (-).
* \return The index of the argument, or 0
* if there are no such arguments.
*/
size_t COM_FirstOption(void)
{
size_t i;
for (i = 1; i < com_argc; i++)
{
if (com_argv[i][0] == '-')/* options start with a hyphen */
return i;
}
return 0;
}
/** Parses a string into command-line tokens. /** Parses a string into command-line tokens.
* *
* \param ptext A null-terminated string. Does not need to be * \param ptext A null-terminated string. Does not need to be

View file

@ -37,6 +37,8 @@ size_t COM_Argc(void);
const char *COM_Argv(size_t arg); // if argv > argc, returns empty string const char *COM_Argv(size_t arg); // if argv > argc, returns empty string
char *COM_Args(void); char *COM_Args(void);
size_t COM_CheckParm(const char *check); // like M_CheckParm :) size_t COM_CheckParm(const char *check); // like M_CheckParm :)
size_t COM_CheckPartialParm(const char *check);
size_t COM_FirstOption(void);
// match existing command or NULL // match existing command or NULL
const char *COM_CompleteCommand(const char *partial, INT32 skips); const char *COM_CompleteCommand(const char *partial, INT32 skips);

View file

@ -40,6 +40,7 @@
* Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart * Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart
* Last updated 2022 / 08 / 16 - Kart v1.4 - Main assets * Last updated 2022 / 08 / 16 - Kart v1.4 - Main assets
* Last updated 2022 / 08 / 19 - Kart v1.5 - gfx.kart * Last updated 2022 / 08 / 19 - Kart v1.5 - gfx.kart
* Last updated 2022 / 11 / 01 - Kart v1.6 - gfx.kart, maps.kart
*/ */
// Base SRB2 hashes // Base SRB2 hashes
@ -49,10 +50,10 @@
#endif #endif
// SRB2Kart-specific hashes // SRB2Kart-specific hashes
#define ASSET_HASH_GFX_KART "30b2d9fb5009f1b3a3d7216a0fe28e51" #define ASSET_HASH_GFX_KART "06f86ee16136eb8a7043b15001797034"
#define ASSET_HASH_TEXTURES_KART "abb53d56aba47c3a8cb0f764da1c8b80" #define ASSET_HASH_TEXTURES_KART "abb53d56aba47c3a8cb0f764da1c8b80"
#define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964" #define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964"
#define ASSET_HASH_MAPS_KART "13e273292576b71af0cdb3a98ca066eb" #define ASSET_HASH_MAPS_KART "d051e55141ba736582228c456953cd98"
#ifdef USE_PATCH_KART #ifdef USE_PATCH_KART
#define ASSET_HASH_PATCH_KART "00000000000000000000000000000000" #define ASSET_HASH_PATCH_KART "00000000000000000000000000000000"
#endif #endif

View file

@ -1130,6 +1130,7 @@ typedef enum
CL_PREPAREHTTPFILES, CL_PREPAREHTTPFILES,
CL_DOWNLOADHTTPFILES, CL_DOWNLOADHTTPFILES,
#endif #endif
CL_LEGACYREQUESTFAILED,
} cl_mode_t; } cl_mode_t;
static void GetPackets(void); static void GetPackets(void);
@ -1227,6 +1228,7 @@ static inline void CL_DrawConnectionStatus(void)
#endif #endif
case CL_ASKFULLFILELIST: case CL_ASKFULLFILELIST:
case CL_CONFIRMCONNECT: case CL_CONFIRMCONNECT:
case CL_LEGACYREQUESTFAILED:
cltext = ""; cltext = "";
break; break;
case CL_SETUPFILES: case CL_SETUPFILES:
@ -1333,8 +1335,10 @@ static inline void CL_DrawConnectionStatus(void)
strncpy(tempname, filename, sizeof(tempname)-1); strncpy(tempname, filename, sizeof(tempname)-1);
} }
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-30, 0,
va(M_GetText("%s downloading"), ((cl_mode == CL_DOWNLOADHTTPFILES) ? "\x82""HTTP" : "\x85""Direct")));
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-22, V_YELLOWMAP, V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-22, V_YELLOWMAP,
va(M_GetText("Downloading \"%s\""), tempname)); va(M_GetText("\"%s\""), tempname));
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE, V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10)); va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10));
V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE, V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
@ -2124,6 +2128,10 @@ static void M_ConfirmConnect(event_t *ev)
{ {
cl_mode = CL_DOWNLOADFILES; cl_mode = CL_DOWNLOADFILES;
} }
else
{
cl_mode = CL_LEGACYREQUESTFAILED;
}
} }
#ifdef HAVE_CURL #ifdef HAVE_CURL
else else
@ -2279,6 +2287,10 @@ static boolean CL_FinishedFileList(void)
{ {
cl_mode = CL_DOWNLOADFILES; cl_mode = CL_DOWNLOADFILES;
} }
else
{
cl_mode = CL_LEGACYREQUESTFAILED;
}
} }
#endif #endif
} }
@ -2465,6 +2477,21 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
cl_mode = CL_LOADFILES; cl_mode = CL_LOADFILES;
break; break;
case CL_LEGACYREQUESTFAILED:
{
CONS_Printf(M_GetText("Legacy downloader request packet failed.\n"));
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(M_GetText(
"The direct download encountered an error.\n"
"See the logfile for more info.\n"
"\n"
"Press ESC\n"
), NULL, MM_NOTHING);
return false;
}
case CL_LOADFILES: case CL_LOADFILES:
if (CL_LoadServerFiles()) if (CL_LoadServerFiles())
cl_mode = CL_SETUPFILES; cl_mode = CL_SETUPFILES;
@ -5945,23 +5972,20 @@ boolean TryRunTics(tic_t realtics)
if (ticking) if (ticking)
{ {
if (advancedemo) // run the count * tics
D_StartTitle(); while (neededtic > gametic)
else {
// run the count * tics DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic));
while (neededtic > gametic)
{
DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic));
G_Ticker((gametic % NEWTICRATERATIO) == 0); G_Ticker((gametic % NEWTICRATERATIO) == 0);
ExtraDataTicker(); ExtraDataTicker();
gametic++; gametic++;
consistancy[gametic%TICQUEUE] = Consistancy(); consistancy[gametic%TICQUEUE] = Consistancy();
// Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame.
if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
break; break;
} }
} }
else else
{ {
@ -5983,53 +6007,77 @@ boolean TryRunTics(tic_t realtics)
static INT32 pingtimeout[MAXPLAYERS]; static INT32 pingtimeout[MAXPLAYERS];
#define PINGKICK_TICQUEUE 2
#define PINGKICK_LIMIT 1
static inline void PingUpdate(void) static inline void PingUpdate(void)
{ {
INT32 i; INT32 i;
boolean laggers[MAXPLAYERS]; UINT8 pingkick[MAXPLAYERS];
UINT8 numlaggers = 0; UINT8 nonlaggers = 0;
memset(laggers, 0, sizeof(boolean) * MAXPLAYERS); memset(pingkick, 0, sizeof(pingkick));
netbuffer->packettype = PT_PING; netbuffer->packettype = PT_PING;
//check for ping limit breakage. //check for ping limit breakage.
if (cv_maxping.value) //if (cv_maxping.value) -- always check for TICQUEUE overrun
{ {
for (i = 1; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) if (!playeringame[i] || P_IsLocalPlayer(&players[i])) // should be P_IsMachineLocalPlayer for DRRR
{ {
if (players[i].jointime > 30 * TICRATE) pingtimeout[i] = 0;
laggers[i] = true; continue;
numlaggers++; }
if ((maketic + 5) >= nettics[playernode[i]] + (TICQUEUE-(2*TICRATE)))
{
// Anyone who's gobbled most of the TICQUEUE and is likely to halt the server the next few times this runs has to die *right now*. (See also NetUpdate)
pingkick[i] = PINGKICK_TICQUEUE;
}
else if ((cv_maxping.value)
&& (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value))
{
if (players[i].jointime > 10 * TICRATE)
{
pingkick[i] = PINGKICK_LIMIT;
}
} }
else else
pingtimeout[i] = 0; {
nonlaggers++;
// you aren't lagging, but you aren't free yet. In case you'll keep spiking, we just make the timer go back down. (Very unstable net must still get kicked).
if (pingtimeout[i] > 0)
pingtimeout[i]--;
}
} }
//kick lagging players... unless everyone but the server's ping sucks. //kick lagging players... unless everyone but the server's ping sucks.
//in that case, it is probably the server's fault. //in that case, it is probably the server's fault.
if (numlaggers < D_NumPlayers() - 1) // Always kick TICQUEUE-overrunners, too.
{ {
for (i = 1; i < MAXPLAYERS; i++) UINT8 minimumkicklevel = (nonlaggers > 0) ? PINGKICK_LIMIT : PINGKICK_TICQUEUE;
for (i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && laggers[i]) XBOXSTATIC char buf[2];
if (!playeringame[i] || pingkick[i] < minimumkicklevel)
continue;
if (pingkick[i] == PINGKICK_LIMIT)
{ {
pingtimeout[i]++; // Don't kick on ping alone if we haven't reached our threshold yet.
if (pingtimeout[i] > cv_pingtimeout.value) // ok your net has been bad for too long, you deserve to die. if (++pingtimeout[i] < cv_pingtimeout.value)
{ continue;
XBOXSTATIC char buf[2];
pingtimeout[i] = 0;
buf[0] = (char)i;
buf[1] = KICK_MSG_PING_HIGH;
SendNetXCmd(XD_KICK, &buf, 2);
}
} }
else // you aren't lagging, but you aren't free yet. In case you'll keep spiking, we just make the timer go back down. (Very unstable net must still get kicked).
pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1); pingtimeout[i] = 0;
buf[0] = (char)i;
buf[1] = KICK_MSG_PING_HIGH;
SendNetXCmd(XD_KICK, &buf, 2);
} }
} }
} }
@ -6054,9 +6102,12 @@ static inline void PingUpdate(void)
if (nodeingame[i]) if (nodeingame[i])
HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
pingmeasurecount = 1; //Reset count pingmeasurecount = 0; //Reset count
} }
#undef PINGKICK_DANGER
#undef PINGKICK_LIMIT
static tic_t gametime = 0; static tic_t gametime = 0;
static void UpdatePingTable(void) static void UpdatePingTable(void)
@ -6147,6 +6198,9 @@ FILESTAMP
SV_FileSendTicker(); SV_FileSendTicker();
} }
// If a tree falls in the forest but nobody is around to hear it, does it make a tic?
#define DEDICATEDIDLETIME (10*TICRATE)
void NetUpdate(void) void NetUpdate(void)
{ {
static tic_t resptime = 0; static tic_t resptime = 0;
@ -6159,6 +6213,55 @@ void NetUpdate(void)
if (realtics <= 0) // nothing new to update if (realtics <= 0) // nothing new to update
return; return;
#ifdef DEDICATEDIDLETIME
if (server && dedicated && gamestate == GS_LEVEL)
{
static tic_t dedicatedidle = 0;
for (i = 1; i < MAXNETNODES; ++i)
if (nodeingame[i])
{
if (dedicatedidle == DEDICATEDIDLETIME)
{
CONS_Printf("DEDICATED: Awakening from idle (Node %d detected...)\n", i);
dedicatedidle = 0;
}
break;
}
if (i == MAXNETNODES)
{
if (leveltime == 2)
{
// On next tick...
dedicatedidle = DEDICATEDIDLETIME-1;
}
else if (dedicatedidle == DEDICATEDIDLETIME)
{
if (D_GetExistingTextcmd(gametic, 0) || D_GetExistingTextcmd(gametic+1, 0))
{
CONS_Printf("DEDICATED: Awakening from idle (Netxcmd detected...)\n");
dedicatedidle = 0;
}
else
{
realtics = 0;
}
}
else if (++dedicatedidle == DEDICATEDIDLETIME)
{
const char *idlereason = "at round start";
if (leveltime > 3)
idlereason = va("for %d seconds", dedicatedidle/TICRATE);
CONS_Printf("DEDICATED: No nodes %s, idling...\n", idlereason);
realtics = 0;
}
}
}
#endif
if (realtics > 5) if (realtics > 5)
{ {
if (server) if (server)
@ -6201,7 +6304,7 @@ FILESTAMP
} }
else else
{ {
if (!demo.playback) if (!demo.playback && realtics > 0)
{ {
INT32 counts; INT32 counts;
@ -6225,6 +6328,7 @@ FILESTAMP
// Do not make tics while resynching // Do not make tics while resynching
if (counts != -666) if (counts != -666)
{ {
// See also PingUpdate
if (maketic + counts >= firstticstosend + TICQUEUE) if (maketic + counts >= firstticstosend + TICQUEUE)
counts = firstticstosend+TICQUEUE-maketic-1; counts = firstticstosend+TICQUEUE-maketic-1;

View file

@ -144,7 +144,6 @@ boolean sound_disabled = false;
boolean digital_disabled = false; boolean digital_disabled = false;
#endif #endif
boolean advancedemo;
#ifdef DEBUGFILE #ifdef DEBUGFILE
INT32 debugload = 0; INT32 debugload = 0;
#endif #endif
@ -815,15 +814,6 @@ void D_SRB2Loop(void)
} }
} }
//
// D_AdvanceDemo
// Called after each demo or intro demosequence finishes
//
void D_AdvanceDemo(void)
{
advancedemo = true;
}
// ========================================================================= // =========================================================================
// D_SRB2Main // D_SRB2Main
// ========================================================================= // =========================================================================
@ -883,7 +873,6 @@ void D_StartTitle(void)
//demosequence = -1; //demosequence = -1;
gametype = GT_RACE; // SRB2kart gametype = GT_RACE; // SRB2kart
paused = false; paused = false;
advancedemo = false;
F_StartTitleScreen(); F_StartTitleScreen();
// Reset the palette -- SRB2Kart: actually never mind let's do this in the middle of every fade // Reset the palette -- SRB2Kart: actually never mind let's do this in the middle of every fade
@ -1154,6 +1143,8 @@ void D_SRB2Main(void)
{ {
const char *userhome = D_Home(); //Alam: path to home const char *userhome = D_Home(); //Alam: path to home
FILE *tmpfile;
char testfile[MAX_WADPATH];
if (!userhome) if (!userhome)
{ {
@ -1203,9 +1194,6 @@ void D_SRB2Main(void)
// If config isn't writable, tons of behavior will be broken. // If config isn't writable, tons of behavior will be broken.
// Fail loudly before things get confusing! // Fail loudly before things get confusing!
FILE *tmpfile;
char testfile[MAX_WADPATH];
snprintf(testfile, sizeof testfile, "%s" PATHSEP "file.tmp", srb2home); snprintf(testfile, sizeof testfile, "%s" PATHSEP "file.tmp", srb2home);
testfile[sizeof testfile - 1] = '\0'; testfile[sizeof testfile - 1] = '\0';
@ -1258,26 +1246,6 @@ void D_SRB2Main(void)
if (M_CheckParm("-server") || dedicated) if (M_CheckParm("-server") || dedicated)
netgame = server = true; netgame = server = true;
if (M_CheckParm("-warp") && M_IsNextParm())
{
const char *word = M_GetNextParm();
char ch; // use this with sscanf to catch non-digits with
if (fastncmp(word, "MAP", 3)) // MAPxx name
pstartmap = M_MapNumber(word[3], word[4]);
else if (sscanf(word, "%d%c", &pstartmap, &ch) != 1) // a plain number
I_Error("Cannot warp to map %s (invalid map name)\n", word);
// Don't check if lump exists just yet because the wads haven't been loaded!
// Just do a basic range check here.
if (pstartmap < 1 || pstartmap > NUMMAPS)
I_Error("Cannot warp to map %d (out of range)\n", pstartmap);
else
{
if (!M_CheckParm("-server"))
G_SetGameModified(true, true);
autostart = true;
}
}
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n"); CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
Z_Init(); Z_Init();
@ -1457,6 +1425,23 @@ void D_SRB2Main(void)
//------------------------------------------------ COMMAND LINE PARAMS //------------------------------------------------ COMMAND LINE PARAMS
// this must be done after loading gamedata,
// to avoid setting off the corrupted gamedata code in G_LoadGameData if a SOC with custom gamedata is added
// -- Monster Iestyn 20/02/20
if (M_CheckParm("-warp") && M_IsNextParm())
{
const char *word = M_GetNextParm();
pstartmap = G_FindMapByNameOrCode(word, 0);
if (! pstartmap)
I_Error("Cannot find a map remotely named '%s'\n", word);
else
{
if (!M_CheckParm("-server"))
G_SetGameModified(true, true);
autostart = true;
}
}
// Initialize CD-Audio // Initialize CD-Audio
if (M_CheckParm("-usecd") && !dedicated) if (M_CheckParm("-usecd") && !dedicated)
I_InitCD(); I_InitCD();

View file

@ -17,8 +17,6 @@
#include "d_event.h" #include "d_event.h"
#include "w_wad.h" // for MAX_WADFILES #include "w_wad.h" // for MAX_WADFILES
extern boolean advancedemo;
// make sure not to write back the config until it's been correctly loaded // make sure not to write back the config until it's been correctly loaded
extern tic_t rendergametic; extern tic_t rendergametic;
@ -34,7 +32,6 @@ void D_SRB2Loop(void) FUNCNORETURN;
// D_SRB2Main() // D_SRB2Main()
// Not a globally visible function, just included for source reference, // Not a globally visible function, just included for source reference,
// calls all startup code, parses command line options. // calls all startup code, parses command line options.
// If not overrided by user input, calls D_AdvanceDemo.
// //
void D_SRB2Main(void); void D_SRB2Main(void);
@ -51,7 +48,6 @@ const char *D_Home(void);
// //
// BASE LEVEL // BASE LEVEL
// //
void D_AdvanceDemo(void);
void D_StartTitle(void); void D_StartTitle(void);
#endif //__D_MAIN__ #endif //__D_MAIN__

View file

@ -2519,27 +2519,67 @@ void D_PickVote(void)
SendNetXCmd(XD_PICKVOTE, &buf, 2); SendNetXCmd(XD_PICKVOTE, &buf, 2);
} }
static char *
ConcatCommandArgv (int start, int end)
{
char *final;
size_t size;
int i;
char *p;
size = 0;
for (i = start; i < end; ++i)
{
/*
one space after each argument, but terminating
character on final argument
*/
size += strlen(COM_Argv(i)) + 1;
}
final = ZZ_Alloc(size);
p = final;
--end;/* handle the final argument separately */
for (i = start; i < end; ++i)
{
p += sprintf(p, "%s ", COM_Argv(i));
}
/* at this point "end" is actually the last argument's position */
strcpy(p, COM_Argv(end));
return final;
}
//
// Warp to map code. // Warp to map code.
// Called either from map <mapname> console command, or idclev cheat. // Called either from map <mapname> console command, or idclev cheat.
// //
// Largely rewritten by James.
//
static void Command_Map_f(void) static void Command_Map_f(void)
{ {
const char *mapname; size_t first_option;
size_t i; size_t option_force;
INT32 newmapnum; size_t option_gametype;
boolean newresetplayers, newencoremode; size_t option_encore;
INT32 newgametype = gametype; const char *gametypename;
boolean newresetplayers;
// max length of command: map map03 -gametype race -noresetplayers -force -encore boolean mustmodifygame;
// 1 2 3 4 5 6 7
// = 8 arg max INT32 newmapnum;
// i don't know whether this is intrinsic to the system or just someone being weird but
// "noresetplayers" is pretty useless for kart if it turns out this is too close to the limit char * mapname;
if (COM_Argc() < 2 || COM_Argc() > 8) char *realmapname = NULL;
{
CONS_Printf(M_GetText("map <mapname> [-gametype <type> [-force]: warp to map\n")); INT32 newgametype = gametype;
return; boolean newencoremode = cv_kartencore.value;
}
INT32 d;
if (client && !IsPlayerAdmin(consoleplayer)) if (client && !IsPlayerAdmin(consoleplayer))
{ {
@ -2547,99 +2587,129 @@ static void Command_Map_f(void)
return; return;
} }
// internal wad lump always: map command doesn't support external files as in doom legacy option_force = COM_CheckPartialParm("-f");
if (W_CheckNumForName(COM_Argv(1)) == LUMPERROR) option_gametype = COM_CheckPartialParm("-g");
option_encore = COM_CheckPartialParm("-e");
newresetplayers = ! COM_CheckParm("-noresetplayers");
mustmodifygame = !( netgame || multiplayer || majormods );
if (mustmodifygame && !option_force)
{ {
CONS_Alert(CONS_ERROR, M_GetText("Internal game level '%s' not found\n"), COM_Argv(1)); /* May want to be more descriptive? */
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
return; return;
} }
if (!(netgame || multiplayer) && !majormods)
{
if (COM_CheckParm("-force"))
{
G_SetGameModified(false, true);
}
else
{
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
return;
}
}
newresetplayers = !COM_CheckParm("-noresetplayers");
if (!newresetplayers && !cv_debug) if (!newresetplayers && !cv_debug)
{ {
CONS_Printf(M_GetText("DEVMODE must be enabled.\n")); CONS_Printf(M_GetText("DEVMODE must be enabled.\n"));
return; return;
} }
mapname = COM_Argv(1); if (option_gametype)
if (strlen(mapname) != 5
|| (newmapnum = M_MapNumber(mapname[3], mapname[4])) == 0)
{ {
CONS_Alert(CONS_ERROR, M_GetText("Invalid level name %s\n"), mapname); if (!multiplayer)
{
CONS_Printf(M_GetText(
"You can't switch gametypes in single player!\n"));
return;
}
else if (COM_Argc() < option_gametype + 2)/* no argument after? */
{
CONS_Alert(CONS_ERROR,
"No gametype name follows parameter '%s'.\n",
COM_Argv(option_gametype));
return;
}
}
if (!( first_option = COM_FirstOption() ))
first_option = COM_Argc();
if (first_option < 2)
{
/* I'm going over the fucking lines and I DON'T CAREEEEE */
CONS_Printf("map <name / [MAP]code / number> [-gametype <type>] [-encore] [-force]:\n");
CONS_Printf(M_GetText(
"Warp to a map, by its name, two character code, with optional \"MAP\" prefix, or by its number (though why would you).\n"
"All parameters are case-insensitive and may be abbreviated.\n"));
return; return;
} }
mapname = ConcatCommandArgv(1, first_option);
newmapnum = G_FindMapByNameOrCode(mapname, &realmapname);
if (newmapnum == 0)
{
CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname);
Z_Free(mapname);
return;
}
if (mustmodifygame && option_force)
{
G_SetGameModified(false, true);
}
// new gametype value // new gametype value
// use current one by default // use current one by default
i = COM_CheckParm("-gametype"); if (option_gametype)
if (i)
{ {
if (!multiplayer) gametypename = COM_Argv(option_gametype + 1);
{
CONS_Printf(M_GetText("You can't switch gametypes in single player!\n")); newgametype = G_GetGametypeByName(gametypename);
return;
}
newgametype = G_GetGametypeByName(COM_Argv(i+1));
if (newgametype == -1) // reached end of the list with no match if (newgametype == -1) // reached end of the list with no match
{ {
INT32 j = atoi(COM_Argv(i+1)); // assume they gave us a gametype number, which is okay too /* Did they give us a gametype number? That's okay too! */
if (j >= 0 && j < NUMGAMETYPES) if (isdigit(gametypename[0]))
newgametype = (INT16)j; {
d = atoi(gametypename);
if (d >= 0 && d < NUMGAMETYPES)
newgametype = d;
else
{
CONS_Alert(CONS_ERROR,
"Gametype number %d is out of range. Use a number between"
" 0 and %d inclusive. ...Or just use the name. :v\n",
d,
NUMGAMETYPES-1);
Z_Free(realmapname);
Z_Free(mapname);
return;
}
}
else
{
CONS_Alert(CONS_ERROR,
"'%s' is not a gametype.\n",
gametypename);
Z_Free(realmapname);
Z_Free(mapname);
return;
}
} }
} }
// new encoremode value if (!option_force && newgametype == gametype) // SRB2Kart
// use cvar by default
newencoremode = (boolean)cv_kartencore.value;
if (COM_CheckParm("-encore"))
{
if (!M_SecretUnlocked(SECRET_ENCORE) && !newencoremode)
{
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
return;
}
newencoremode = !newencoremode;
}
if (!(i = COM_CheckParm("-force")) && newgametype == gametype) // SRB2Kart
newresetplayers = false; // if not forcing and gametypes is the same newresetplayers = false; // if not forcing and gametypes is the same
// don't use a gametype the map doesn't support // don't use a gametype the map doesn't support
if (cv_debug || i || cv_skipmapcheck.value) if (cv_debug || option_force || cv_skipmapcheck.value)
; // The player wants us to trek on anyway. Do so. fromlevelselect = false; // The player wants us to trek on anyway. Do so.
// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer // G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
// Alternatively, bail if the map header is completely missing anyway. else
else if (!mapheaderinfo[newmapnum-1]
|| !(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
{ {
char gametypestring[32] = "Single Player"; if (!(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
if (multiplayer)
{ {
if (newgametype >= 0 && newgametype < NUMGAMETYPES CONS_Alert(CONS_WARNING, M_GetText("Course %s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum),
&& Gametype_Names[newgametype]) (multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player"));
strcpy(gametypestring, Gametype_Names[newgametype]); Z_Free(realmapname);
Z_Free(mapname);
return;
} }
CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring);
return;
} }
// Prevent warping to locked levels // Prevent warping to locked levels
@ -2649,11 +2719,25 @@ static void Command_Map_f(void)
if (!dedicated && M_MapLocked(newmapnum)) if (!dedicated && M_MapLocked(newmapnum))
{ {
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n")); CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
Z_Free(realmapname);
Z_Free(mapname);
return; return;
} }
if (option_encore)
{
newencoremode = ! newencoremode;
if (! M_SecretUnlocked(SECRET_ENCORE) && newencoremode)
{
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
return;
}
}
fromlevelselect = false; fromlevelselect = false;
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false); D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
Z_Free(realmapname);
} }
/** Receives a map command and changes the map. /** Receives a map command and changes the map.
@ -2702,7 +2786,9 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
lastgametype = gametype; lastgametype = gametype;
gametype = READUINT8(*cp); gametype = READUINT8(*cp);
if (gametype != lastgametype) if (gametype < 0 || gametype >= NUMGAMETYPES)
gametype = lastgametype;
else if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
if (!G_RaceGametype()) if (!G_RaceGametype())

View file

@ -299,6 +299,9 @@ boolean CL_CheckDownloadable(void)
return false; return false;
} }
// The following was written and, against all odds, works.
#define MORELEGACYDOWNLOADER
/** Sends requests for files in the ::fileneeded table with a status of /** Sends requests for files in the ::fileneeded table with a status of
* ::FS_NOTFOUND. * ::FS_NOTFOUND.
* *
@ -311,42 +314,132 @@ boolean CL_SendRequestFile(void)
char *p; char *p;
INT32 i; INT32 i;
INT64 totalfreespaceneeded = 0, availablefreespace; INT64 totalfreespaceneeded = 0, availablefreespace;
INT32 skippedafile = -1;
#ifdef MORELEGACYDOWNLOADER
boolean firstloop = true;
#endif
#ifdef PARANOIA #ifdef PARANOIA
if (M_CheckParm("-nodownload")) if (M_CheckParm("-nodownload"))
I_Error("Attempted to download files in -nodownload mode"); {
CONS_Printf("Direct download - Attempted to download files in -nodownload mode");
return false;
}
#endif
for (i = 0; i < fileneedednum; i++) for (i = 0; i < fileneedednum; i++)
{
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN
&& (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2)) && (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2))
{ {
I_Error("Attempted to download files that were not sendable"); CONS_Printf("Direct download - attempted to download files that were not sendable\n");
return false;
} }
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK))
{
// Error check for the first time around.
totalfreespaceneeded += fileneeded[i].totalsize;
}
}
I_GetDiskFreeSpace(&availablefreespace);
if (totalfreespaceneeded > availablefreespace)
{
CONS_Printf("Direct download -\n"
" To play on this server you must download %s KB,\n"
" but you have only %s KB free space on this drive\n",
sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10)));
return false;
}
#ifdef MORELEGACYDOWNLOADER
tryagain:
skippedafile = -1;
#endif
#ifdef VERBOSEREQUESTFILE
CONS_Printf("Preparing packet\n");
#endif #endif
netbuffer->packettype = PT_REQUESTFILE; netbuffer->packettype = PT_REQUESTFILE;
p = (char *)netbuffer->u.textcmd; p = (char *)netbuffer->u.textcmd;
for (i = 0; i < fileneedednum; i++) for (i = 0; i < fileneedednum; i++)
{
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK)) if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK))
{ {
totalfreespaceneeded += fileneeded[i].totalsize; // Pre-prepare.
size_t checklen;
nameonly(fileneeded[i].filename); nameonly(fileneeded[i].filename);
// Figure out if we'd overrun our buffer.
checklen = strlen(fileneeded[i].filename)+2; // plus the fileid (and terminator, in case this is last)
if ((UINT8 *)(p + checklen) >= netbuffer->u.textcmd + MAXTEXTCMD)
{
skippedafile = i;
// we might have a shorter file that can fit in the remaining space, and file ID permits out-of-order data
continue;
}
// Now write.
WRITEUINT8(p, i); // fileid WRITEUINT8(p, i); // fileid
WRITESTRINGN(p, fileneeded[i].filename, MAX_WADPATH); WRITESTRINGN(p, fileneeded[i].filename, MAX_WADPATH);
#ifdef VERBOSEREQUESTFILE
CONS_Printf(" file \"%s\" (id %d)\n", i, fileneeded[i].filename);
#endif
// put it in download dir // put it in download dir
strcatbf(fileneeded[i].filename, downloaddir, "/"); strcatbf(fileneeded[i].filename, downloaddir, "/");
fileneeded[i].status = FS_REQUESTED; fileneeded[i].status = FS_REQUESTED;
} }
WRITEUINT8(p, 0xFF); }
I_GetDiskFreeSpace(&availablefreespace);
if (totalfreespaceneeded > availablefreespace)
I_Error("To play on this server you must download %s KB,\n"
"but you have only %s KB free space on this drive\n",
sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10)));
// prepare to download #ifdef MORELEGACYDOWNLOADER
I_mkdir(downloaddir, 0755); if (firstloop)
return HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd); #else
// If we're not trying extralong legacy download requests, gotta bail.
if (skippedafile != -1)
{
CONS_Printf("Direct download - missing files are as follows:\n");
for (i = 0; i < fileneedednum; i++)
{
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK || fileneeded[i].status == FS_REQUESTED)) // FS_REQUESTED added
CONS_Printf(" %s\n", fileneeded[i].filename);
}
return false;
}
#endif
I_mkdir(downloaddir, 0755);
// Couldn't fit a single one in?
if (p == (char *)netbuffer->u.textcmd)
{
CONS_Printf("Direct download - fileneeded name for %s (fileneeded[%d]) too long??\n", (skippedafile != -1 ? fileneeded[skippedafile].filename : NULL), skippedafile);
return false;
}
WRITEUINT8(p, 0xFF); // terminator
if (!HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd))
{
CONS_Printf("Direct download - unable to send packet.\n");
return false;
}
#ifdef MORELEGACYDOWNLOADER
if (skippedafile != -1)
{
firstloop = false;
goto tryagain;
}
#endif
#ifdef VERBOSEREQUESTFILE
CONS_Printf("Returning true\n");
#endif
return true;
} }
// get request filepak and put it on the send queue // get request filepak and put it on the send queue
@ -356,16 +449,18 @@ boolean Got_RequestFilePak(INT32 node)
char wad[MAX_WADPATH+1]; char wad[MAX_WADPATH+1];
UINT8 *p = netbuffer->u.textcmd; UINT8 *p = netbuffer->u.textcmd;
UINT8 id; UINT8 id;
while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow while (p < netbuffer->u.textcmd + MAXTEXTCMD) // Don't allow hacked client to overflow
{ {
id = READUINT8(p); id = READUINT8(p);
if (id == 0xFF) if (id == 0xFF)
break; break;
READSTRINGN(p, wad, MAX_WADPATH); READSTRINGN(p, wad, MAX_WADPATH);
if (!SV_SendFile(node, wad, id)) if (p >= netbuffer->u.textcmd + MAXTEXTCMD || !SV_SendFile(node, wad, id))
{ {
if (cv_noticedownload.value)
CONS_Printf("Bad PT_REQUESTFILE from node %d!\n", node);
SV_AbortSendFiles(node); SV_AbortSendFiles(node);
return false; // don't read the rest of the files return false; // don't read any more
} }
} }
return true; // no problems with any files return true; // no problems with any files
@ -538,7 +633,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
char wadfilename[MAX_WADPATH]; char wadfilename[MAX_WADPATH];
if (cv_noticedownload.value) if (cv_noticedownload.value)
CONS_Printf("Sending file \"%s\" to node %d (%s)\n", filename, node, I_GetNodeAddress(node)); CONS_Printf("Sending file \"%s\" (id %d) to node %d (%s)\n", filename, fileid, node, I_GetNodeAddress(node));
// Find the last file in the list and set a pointer to its "next" field // Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist; q = &transfer[node].txlist;
@ -664,7 +759,7 @@ static void SV_EndFileSend(INT32 node)
{ {
case SF_FILE: // It's a file, close it and free its filename case SF_FILE: // It's a file, close it and free its filename
if (cv_noticedownload.value) if (cv_noticedownload.value)
CONS_Printf("Ending file transfer for node %d\n", node); CONS_Printf("Ending file transfer (id %d) for node %d\n", p->fileid, node);
if (transfer[node].currentfile) if (transfer[node].currentfile)
fclose(transfer[node].currentfile); fclose(transfer[node].currentfile);
free(p->id.filename); free(p->id.filename);
@ -1156,6 +1251,7 @@ void CURLGetFile(void)
int msgs_left; /* how many messages are left */ int msgs_left; /* how many messages are left */
const char *easy_handle_error; const char *easy_handle_error;
long response_code = 0; long response_code = 0;
static char *filename;
if (curl_runninghandles) if (curl_runninghandles)
{ {
@ -1180,6 +1276,8 @@ void CURLGetFile(void)
{ {
e = m->easy_handle; e = m->easy_handle;
easyres = m->data.result; easyres = m->data.result;
filename = Z_StrDup(curl_realname);
nameonly(filename);
if (easyres != CURLE_OK) if (easyres != CURLE_OK)
{ {
if (easyres == CURLE_HTTP_RETURNED_ERROR) if (easyres == CURLE_HTTP_RETURNED_ERROR)
@ -1192,21 +1290,30 @@ void CURLGetFile(void)
curl_failedwebdownload = true; curl_failedwebdownload = true;
fclose(curl_curfile->file); fclose(curl_curfile->file);
remove(curl_curfile->filename); remove(curl_curfile->filename);
curl_curfile->file = NULL; CONS_Printf(M_GetText("Failed to download %s (%s)\n"), filename, easy_handle_error);
//nameonly(curl_curfile->filename);
nameonly(curl_realname);
CONS_Printf(M_GetText("Failed to download %s (%s)\n"), curl_realname, easy_handle_error);
} }
else else
{ {
nameonly(curl_realname);
CONS_Printf(M_GetText("Finished downloading %s\n"), curl_realname);
downloadcompletednum++;
downloadcompletedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
fclose(curl_curfile->file); fclose(curl_curfile->file);
if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD)
{
CONS_Alert(CONS_ERROR, M_GetText("HTTP Download of %s finished but is corrupt or has been modified\n"), filename);
curl_curfile->status = FS_FALLBACK;
curl_failedwebdownload = true;
}
else
{
CONS_Printf(M_GetText("Finished HTTP download of %s\n"), filename);
downloadcompletednum++;
downloadcompletedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
}
} }
Z_Free(filename);
curl_curfile->file = NULL;
curl_running = false; curl_running = false;
curl_transfers--; curl_transfers--;
curl_multi_remove_handle(multi_handle, e); curl_multi_remove_handle(multi_handle, e);

View file

@ -148,9 +148,9 @@ extern char logfilename[1024];
// we use comprevision and compbranch instead. // we use comprevision and compbranch instead.
#else #else
#define VERSION 1 // Game version #define VERSION 1 // Game version
#define SUBVERSION 4 // more precise version number #define SUBVERSION 6 // more precise version number
#define VERSIONSTRING "v1.5" #define VERSIONSTRING "v1.6"
#define VERSIONSTRINGW L"v1.5" #define VERSIONSTRINGW L"v1.6"
// Hey! If you change this, add 1 to the MODVERSION below! Otherwise we can't force updates! // Hey! If you change this, add 1 to the MODVERSION below! Otherwise we can't force updates!
// And change CMakeLists.txt (not src/, but in root), for CMake users! // And change CMakeLists.txt (not src/, but in root), for CMake users!
// AND appveyor.yml, for the build bots! // AND appveyor.yml, for the build bots!
@ -532,6 +532,7 @@ extern boolean capslock;
// if we ever make our alloc stuff... // if we ever make our alloc stuff...
#define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL) #define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL)
#define ZZ_Calloc(x) Z_Calloc(x, PU_STATIC, NULL)
// i_system.c, replace getchar() once the keyboard has been appropriated // i_system.c, replace getchar() once the keyboard has been appropriated
INT32 I_GetKey(void); INT32 I_GetKey(void);

View file

@ -139,6 +139,9 @@ typedef long ssize_t;
#define strlwr _strlwr #define strlwr _strlwr
#endif #endif
char *strcasestr(const char *in, const char *what);
#define stristr strcasestr
#if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap #if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap
#define true 1 #define true 1
#define false 0 #define false 0

View file

@ -431,10 +431,12 @@ static const char *credits[] = {
"\"JugadorXEI\"", "\"JugadorXEI\"",
"\"Kimberly\"", "\"Kimberly\"",
"\"Lighto97\"", "\"Lighto97\"",
"\"Lonsfor\"",
"\"mazmazz\"", "\"mazmazz\"",
"\"minenice\"", "\"minenice\"",
"\"Shuffle\"", "\"Shuffle\"",
"\"Snu\"", "\"Snu\"",
"\"X.organic\"",
"", "",
"\1Lead Artists", "\1Lead Artists",
"Desmond \"Blade\" DesJardins", "Desmond \"Blade\" DesJardins",
@ -512,6 +514,7 @@ static const char *credits[] = {
"", "",
"\1Testing", "\1Testing",
"RKH License holders", "RKH License holders",
"The KCS",
"\"CyberIF\"", "\"CyberIF\"",
"\"Dani\"", "\"Dani\"",
"Karol \"Fooruman\" D""\x1E""browski", // Dąbrowski, <Sryder> accents in srb2 :ytho: "Karol \"Fooruman\" D""\x1E""browski", // Dąbrowski, <Sryder> accents in srb2 :ytho:
@ -559,7 +562,7 @@ static struct {
// This Tyler52 gag is troublesome // This Tyler52 gag is troublesome
// Alignment should be ((spaces+1 * 100) + (headers+1 * 38) + (lines * 15)) // Alignment should be ((spaces+1 * 100) + (headers+1 * 38) + (lines * 15))
// Current max image spacing: (216*17) // Current max image spacing: (216*17)
{112, (16*100)+(19*38)+(100*15), "TYLER52", SKINCOLOR_NONE}, {112, (16*100)+(19*38)+(103*15), "TYLER52", SKINCOLOR_NONE},
{0, 0, NULL, SKINCOLOR_NONE} {0, 0, NULL, SKINCOLOR_NONE}
}; };

View file

@ -3339,7 +3339,9 @@ void G_DoReborn(INT32 playernum)
// respawn at the start // respawn at the start
mobj_t *oldmo = NULL; mobj_t *oldmo = NULL;
if (player->starpostnum || ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) && player->laps)) // SRB2kart if (player->spectator)
;
else if (player->starpostnum || ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) && player->laps)) // SRB2kart
starpost = true; starpost = true;
// first dissasociate the corpse // first dissasociate the corpse
@ -4864,6 +4866,242 @@ char *G_BuildMapTitle(INT32 mapnum)
return title; return title;
} }
static void measurekeywords(mapsearchfreq_t *fr,
struct searchdim **dimp, UINT8 *cuntp,
const char *s, const char *q, boolean wanttable)
{
char *qp;
char *sp;
if (wanttable)
(*dimp) = Z_Realloc((*dimp), 255 * sizeof (struct searchdim),
PU_STATIC, NULL);
for (qp = strtok(va("%s", q), " ");
qp && fr->total < 255;
qp = strtok(0, " "))
{
if (( sp = strcasestr(s, qp) ))
{
if (wanttable)
{
(*dimp)[(*cuntp)].pos = sp - s;
(*dimp)[(*cuntp)].siz = strlen(qp);
}
(*cuntp)++;
fr->total++;
}
}
if (wanttable)
(*dimp) = Z_Realloc((*dimp), (*cuntp) * sizeof (struct searchdim),
PU_STATIC, NULL);
}
static void writesimplefreq(mapsearchfreq_t *fr, INT32 *frc,
INT32 mapnum, UINT8 pos, UINT8 siz)
{
fr[(*frc)].mapnum = mapnum;
fr[(*frc)].matchd = ZZ_Alloc(sizeof (struct searchdim));
fr[(*frc)].matchd[0].pos = pos;
fr[(*frc)].matchd[0].siz = siz;
fr[(*frc)].matchc = 1;
fr[(*frc)].total = 1;
(*frc)++;
}
INT32 G_FindMap(const char *mapname, char **foundmapnamep,
mapsearchfreq_t **freqp, INT32 *freqcp)
{
INT32 newmapnum = 0;
INT32 mapnum;
INT32 apromapnum = 0;
size_t mapnamelen;
char *realmapname = NULL;
char *newmapname = NULL;
char *apromapname = NULL;
char *aprop = NULL;
mapsearchfreq_t *freq;
boolean wanttable;
INT32 freqc;
UINT8 frequ;
INT32 i;
mapnamelen = strlen(mapname);
/* Count available maps; how ugly. */
for (i = 0, freqc = 0; i < NUMMAPS; ++i)
{
if (mapheaderinfo[i])
freqc++;
}
freq = ZZ_Calloc(freqc * sizeof (mapsearchfreq_t));
wanttable = !!( freqp );
freqc = 0;
for (i = 0, mapnum = 1; i < NUMMAPS; ++i, ++mapnum)
if (mapheaderinfo[i])
{
if (!( realmapname = G_BuildMapTitle(mapnum) ))
continue;
aprop = realmapname;
/* Now that we found a perfect match no need to fucking guess. */
if (strnicmp(realmapname, mapname, mapnamelen) == 0)
{
if (wanttable)
{
writesimplefreq(freq, &freqc, mapnum, 0, mapnamelen);
}
if (newmapnum == 0)
{
newmapnum = mapnum;
newmapname = realmapname;
realmapname = 0;
Z_Free(apromapname);
if (!wanttable)
break;
}
}
else
if (apromapnum == 0 || wanttable)
{
/* LEVEL 1--match keywords verbatim */
if (( aprop = strcasestr(realmapname, mapname) ))
{
if (wanttable)
{
writesimplefreq(freq, &freqc,
mapnum, aprop - realmapname, mapnamelen);
}
if (apromapnum == 0)
{
apromapnum = mapnum;
apromapname = realmapname;
realmapname = 0;
}
}
else/* ...match individual keywords */
{
freq[freqc].mapnum = mapnum;
measurekeywords(&freq[freqc],
&freq[freqc].matchd, &freq[freqc].matchc,
realmapname, mapname, wanttable);
if (freq[freqc].total)
freqc++;
}
}
Z_Free(realmapname);/* leftover old name */
}
if (newmapnum == 0)/* no perfect match--try a substring */
{
newmapnum = apromapnum;
newmapname = apromapname;
}
if (newmapnum == 0)/* calculate most queries met! */
{
frequ = 0;
for (i = 0; i < freqc; ++i)
{
if (freq[i].total > frequ)
{
frequ = freq[i].total;
newmapnum = freq[i].mapnum;
}
}
if (newmapnum)
{
newmapname = G_BuildMapTitle(newmapnum);
}
}
if (freqp)
(*freqp) = freq;
else
Z_Free(freq);
if (freqcp)
(*freqcp) = freqc;
if (foundmapnamep)
(*foundmapnamep) = newmapname;
else
Z_Free(newmapname);
return newmapnum;
}
void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc)
{
INT32 i;
for (i = 0; i < freqc; ++i)
{
Z_Free(freq[i].matchd);
}
Z_Free(freq);
}
INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
{
boolean usemapcode = false;
INT32 newmapnum;
size_t mapnamelen;
char *p;
mapnamelen = strlen(mapname);
if (mapnamelen == 2)/* maybe two digit code */
{
if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) ))
usemapcode = true;
}
else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0)
{
if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ))
usemapcode = true;
}
if (!usemapcode)
{
/* Now detect map number in base 10, which no one asked for. */
newmapnum = strtol(mapname, &p, 10);
if (*p == '\0')/* we got it */
{
if (newmapnum < 1 || newmapnum > NUMMAPS)
{
CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum);
return 0;
}
usemapcode = true;
}
else
{
newmapnum = G_FindMap(mapname, realmapnamep, NULL, NULL);
}
}
if (usemapcode)
{
/* we can't check mapheaderinfo for this hahahaha */
if (W_CheckNumForName(G_BuildMapName(newmapnum)) == LUMPERROR)
return 0;
if (realmapnamep)
(*realmapnamep) = G_BuildMapTitle(newmapnum);
}
return newmapnum;
}
// //
// DEMO RECORDING // DEMO RECORDING
// //
@ -6496,7 +6734,7 @@ void G_BeginRecording(void)
demoflags |= DF_ENCORE; demoflags |= DF_ENCORE;
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (!modeattacking) // Ghosts don't read luavars, and you shouldn't ever need to save Lua in replays, you doof! if (!modeattacking && gL) // Ghosts don't read luavars, and you shouldn't ever need to save Lua in replays, you doof!
// SERIOUSLY THOUGH WHY WOULD YOU LOAD HOSTMOD AND RECORD A GHOST WITH IT !???? // SERIOUSLY THOUGH WHY WOULD YOU LOAD HOSTMOD AND RECORD A GHOST WITH IT !????
demoflags |= DF_LUAVARS; demoflags |= DF_LUAVARS;
#endif #endif
@ -6531,7 +6769,7 @@ void G_BeginRecording(void)
if (wadfiles[i]->important) if (wadfiles[i]->important)
{ {
nameonly(( filename = va("%s", wadfiles[i]->filename) )); nameonly(( filename = va("%s", wadfiles[i]->filename) ));
WRITESTRINGN(demo_p, filename, 64); WRITESTRINGL(demo_p, filename, MAX_WADPATH);
WRITEMEM(demo_p, wadfiles[i]->md5sum, 16); WRITEMEM(demo_p, wadfiles[i]->md5sum, 16);
totalfiles++; totalfiles++;
@ -6605,7 +6843,7 @@ void G_BeginRecording(void)
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
// player lua vars, always saved even if empty... Unless it's record attack. // player lua vars, always saved even if empty... Unless it's record attack.
if (!modeattacking) if (demoflags & DF_LUAVARS)
LUA_ArchiveDemo(); LUA_ArchiveDemo();
#endif #endif
@ -6776,10 +7014,13 @@ static void G_LoadDemoExtraFiles(UINT8 **pp)
} }
else else
{ {
P_AddWadFile(filename); P_PartialAddWadFile(filename);
} }
} }
} }
if (P_PartialAddGetStage() >= 0)
P_MultiSetupWadFiles(true); // in case any partial adds were done
} }
static void G_SkipDemoExtraFiles(UINT8 **pp) static void G_SkipDemoExtraFiles(UINT8 **pp)
@ -8358,7 +8599,7 @@ boolean G_CheckDemoStatus(void)
CONS_Printf(M_GetText("timed %u gametics in %d realtics\n%f seconds, %f avg fps\n"), leveltime,demotime,f1/TICRATE,f2/f1); CONS_Printf(M_GetText("timed %u gametics in %d realtics\n%f seconds, %f avg fps\n"), leveltime,demotime,f1/TICRATE,f2/f1);
if (restorecv_vidwait != cv_vidwait.value) if (restorecv_vidwait != cv_vidwait.value)
CV_SetValue(&cv_vidwait, restorecv_vidwait); CV_SetValue(&cv_vidwait, restorecv_vidwait);
D_AdvanceDemo(); D_StartTitle();
return true; return true;
} }
@ -8376,7 +8617,7 @@ boolean G_CheckDemoStatus(void)
if (modeattacking) if (modeattacking)
M_EndModeAttackRun(); M_EndModeAttackRun();
else else
D_AdvanceDemo(); D_StartTitle();
} }
return true; return true;

View file

@ -173,6 +173,30 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer,
boolean skipprecutscene); boolean skipprecutscene);
char *G_BuildMapTitle(INT32 mapnum); char *G_BuildMapTitle(INT32 mapnum);
struct searchdim
{
UINT8 pos;
UINT8 siz;
};
typedef struct
{
INT16 mapnum;
UINT8 matchc;
struct searchdim *matchd;/* offset that a pattern was matched */
UINT8 keywhc;
struct searchdim *keywhd;/* ...in KEYWORD */
UINT8 total;/* total hits */
}
mapsearchfreq_t;
INT32 G_FindMap(const char *query, char **foundmapnamep,
mapsearchfreq_t **freqp, INT32 *freqc);
void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc);
/* Match map name by search + 2 digit map code or map number. */
INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep);
// XMOD spawning // XMOD spawning
mapthing_t *G_FindCTFStart(INT32 playernum); mapthing_t *G_FindCTFStart(INT32 playernum);
mapthing_t *G_FindMatchStart(INT32 playernum); mapthing_t *G_FindMatchStart(INT32 playernum);

View file

@ -3019,12 +3019,44 @@ static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts
{ {
if (cv_grspritebillboarding.value && spr && spr->mobj && !(spr->mobj->frame & FF_PAPERSPRITE) && wallVerts) if (cv_grspritebillboarding.value && spr && spr->mobj && !(spr->mobj->frame & FF_PAPERSPRITE) && wallVerts)
{ {
float basey = FIXED_TO_FLOAT(spr->mobj->z); // uncapped/interpolation
float lowy = wallVerts[0].y; interpmobjstate_t interp = {0};
float basey, lowy;
// do interpolation
if (R_UsingFrameInterpolation() && !paused)
{
if (spr->precip)
{
R_InterpolatePrecipMobjState((precipmobj_t *)spr->mobj, rendertimefrac, &interp);
}
else
{
R_InterpolateMobjState(spr->mobj, rendertimefrac, &interp);
}
}
else
{
if (spr->precip)
{
R_InterpolatePrecipMobjState((precipmobj_t *)spr->mobj, FRACUNIT, &interp);
}
else
{
R_InterpolateMobjState(spr->mobj, FRACUNIT, &interp);
}
}
if (P_MobjFlip(spr->mobj) == -1) if (P_MobjFlip(spr->mobj) == -1)
{ {
basey = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height); basey = FIXED_TO_FLOAT(interp.z + spr->mobj->height);
} }
else
{
basey = FIXED_TO_FLOAT(interp.z);
}
lowy = wallVerts[0].y;
// Rotate sprites to fully billboard with the camera // Rotate sprites to fully billboard with the camera
// X, Y, AND Z need to be manipulated for the polys to rotate around the // X, Y, AND Z need to be manipulated for the polys to rotate around the
// origin, because of how the origin setting works I believe that should // origin, because of how the origin setting works I believe that should

View file

@ -2166,7 +2166,7 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto
player->kartstuff[k_spinouttype] = type; player->kartstuff[k_spinouttype] = type;
if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout, type 2 is spb
{ {
// At spinout, player speed is increased to 1/4 their regular speed, moving them forward // At spinout, player speed is increased to 1/4 their regular speed, moving them forward
if (player->speed < K_GetKartSpeed(player, true)/4) if (player->speed < K_GetKartSpeed(player, true)/4)
@ -2332,6 +2332,7 @@ void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor)
void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A bit of a hack, we just throw the player up higher here and extend their spinout timer void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A bit of a hack, we just throw the player up higher here and extend their spinout timer
{ {
fixed_t upgoer;
UINT8 scoremultiply = 1; UINT8 scoremultiply = 1;
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
boolean force = false; // Used to check if Lua ShouldExplode should get us damaged reguardless of flashtics or heck knows what. boolean force = false; // Used to check if Lua ShouldExplode should get us damaged reguardless of flashtics or heck knows what.
@ -2378,9 +2379,6 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b
if (source && source != player->mo && source->player) if (source && source != player->mo && source->player)
K_PlayHitEmSound(source); K_PlayHitEmSound(source);
player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!!
player->mo->momx = player->mo->momy = 0;
player->kartstuff[k_sneakertimer] = 0; player->kartstuff[k_sneakertimer] = 0;
player->kartstuff[k_driftboost] = 0; player->kartstuff[k_driftboost] = 0;
@ -2428,19 +2426,34 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b
K_CheckBumpers(); K_CheckBumpers();
} }
player->kartstuff[k_spinouttype] = 1;
player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2;
player->powers[pw_flashing] = K_GetKartFlashing(player); player->powers[pw_flashing] = K_GetKartFlashing(player);
upgoer = (18*mapobjectscale*P_MobjFlip(player->mo));
if (player->mo->eflags & MFE_UNDERWATER)
upgoer = (117 * upgoer) / 200;
#define EXPLODESPINTIME ((3*TICRATE/2)+2)
if (inflictor && inflictor->type == MT_SPBEXPLOSION && inflictor->extravalue1) if (inflictor && inflictor->type == MT_SPBEXPLOSION && inflictor->extravalue1)
{ {
player->kartstuff[k_spinouttimer] = ((5*player->kartstuff[k_spinouttimer])/2)+1; player->kartstuff[k_spinouttype] = 2;
player->mo->momz *= 2; player->kartstuff[k_spinouttimer] = (5*EXPLODESPINTIME/2)+1;
player->mo->momz = upgoer*2;
} }
else
{
if (player->kartstuff[k_spinouttype] == 2)
{
// We're on to your tricks. But let's not STOP the tech - let's make you have to work extra hard for it to pay off.
indirectitemcooldown = 0;
}
player->kartstuff[k_spinouttype] = 1;
player->kartstuff[k_spinouttimer] = EXPLODESPINTIME;
player->mo->momz = upgoer;
}
player->mo->momx = player->mo->momy = 0;
if (player->mo->eflags & MFE_UNDERWATER) #undef SPINTIME
player->mo->momz = (117 * player->mo->momz) / 200;
if (player->mo->state != &states[S_KART_SPIN]) if (player->mo->state != &states[S_KART_SPIN])
P_SetPlayerMobjState(player->mo, S_KART_SPIN); P_SetPlayerMobjState(player->mo, S_KART_SPIN);
@ -4748,7 +4761,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->kartstuff[k_spinouttimer]) if (player->kartstuff[k_spinouttimer])
{ {
if ((P_IsObjectOnGround(player->mo) || player->kartstuff[k_spinouttype] == 1) if ((P_IsObjectOnGround(player->mo) || ((player->kartstuff[k_spinouttype]+1)/2 == 1)) // spinouttype 1 and 2 - explosion and spb
&& (player->kartstuff[k_sneakertimer] == 0)) && (player->kartstuff[k_sneakertimer] == 0))
{ {
player->kartstuff[k_spinouttimer]--; player->kartstuff[k_spinouttimer]--;

View file

@ -5603,6 +5603,18 @@ static void DrawReplayHutReplayInfo(void)
if (demolist[dir_on[menudepthleft]].gametype == GT_RACE) if (demolist[dir_on[menudepthleft]].gametype == GT_RACE)
{ {
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "TIME"); V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "TIME");
}
else
{
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE");
}
if (demolist[dir_on[menudepthleft]].standings[0].timeorscore == (UINT32_MAX-1))
{
V_DrawThinString(x+32, y+40-1, V_SNAPTOTOP, "NO CONTEST");
}
else if (demolist[dir_on[menudepthleft]].gametype == GT_RACE)
{
V_DrawRightAlignedString(x+84, y+40, V_SNAPTOTOP, va("%d'%02d\"%02d", V_DrawRightAlignedString(x+84, y+40, V_SNAPTOTOP, va("%d'%02d\"%02d",
G_TicsToMinutes(demolist[dir_on[menudepthleft]].standings[0].timeorscore, true), G_TicsToMinutes(demolist[dir_on[menudepthleft]].standings[0].timeorscore, true),
G_TicsToSeconds(demolist[dir_on[menudepthleft]].standings[0].timeorscore), G_TicsToSeconds(demolist[dir_on[menudepthleft]].standings[0].timeorscore),
@ -5611,12 +5623,11 @@ static void DrawReplayHutReplayInfo(void)
} }
else else
{ {
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE");
V_DrawString(x+32, y+40, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[0].timeorscore)); V_DrawString(x+32, y+40, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[0].timeorscore));
} }
// Character face! // Character face!
if (W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[0].skin].facewant) != LUMPERROR) if (demolist[dir_on[menudepthleft]].standings[0].skin < numskins && W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[0].skin].facewant) != LUMPERROR)
{ {
patch = facewantprefix[demolist[dir_on[menudepthleft]].standings[0].skin]; patch = facewantprefix[demolist[dir_on[menudepthleft]].standings[0].skin];
colormap = R_GetTranslationColormap( colormap = R_GetTranslationColormap(
@ -5816,7 +5827,7 @@ static void M_DrawReplayStartMenu(void)
V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[i].timeorscore)); V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[i].timeorscore));
// Character face! // Character face!
if (W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR) if (demolist[dir_on[menudepthleft]].standings[i].skin < numskins && W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR)
{ {
patch = facerankprefix[demolist[dir_on[menudepthleft]].standings[i].skin]; patch = facerankprefix[demolist[dir_on[menudepthleft]].standings[i].skin];
colormap = R_GetTranslationColormap( colormap = R_GetTranslationColormap(

View file

@ -1034,7 +1034,10 @@ boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *))
{ {
P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed! P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
if (!func(mobj)) if (!func(mobj))
{
P_SetTarget(&bnext, NULL);
return false; return false;
}
if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue. if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue.
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue. || (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
{ {
@ -1042,6 +1045,7 @@ boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *))
return true; return true;
} }
} }
P_SetTarget(&bnext, NULL);
return true; return true;
} }

View file

@ -9893,6 +9893,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
break; break;
} }
if (!(mobj->flags & MF_NOTHINK))
P_AddThinker(&mobj->thinker); // Needs to come before the shadow spawn, or else the shadow's reference gets forgotten
switch (mobj->type) switch (mobj->type)
{ {
case MT_PLAYER: case MT_PLAYER:
@ -9916,9 +9919,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
break; break;
} }
if (!(mobj->flags & MF_NOTHINK))
P_AddThinker(&mobj->thinker);
// Call action functions when the state is set // Call action functions when the state is set
if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC)) if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC))
{ {
@ -10298,21 +10298,41 @@ void P_RemovePrecipMobj(precipmobj_t *mobj)
void P_RemoveSavegameMobj(mobj_t *mobj) void P_RemoveSavegameMobj(mobj_t *mobj)
{ {
// unlink from sector and block lists // unlink from sector and block lists
P_UnsetThingPosition(mobj); if (((thinker_t *)mobj)->function.acp1 == (actionf_p1)P_NullPrecipThinker)
// Remove touching_sectorlist from mobj.
if (sector_list)
{ {
P_DelSeclist(sector_list); P_UnsetPrecipThingPosition((precipmobj_t *)mobj);
sector_list = NULL;
if (precipsector_list)
{
P_DelPrecipSeclist(precipsector_list);
precipsector_list = NULL;
}
}
else
{
// unlink from sector and block lists
P_UnsetThingPosition(mobj);
// Remove touching_sectorlist from mobj.
if (sector_list)
{
P_DelSeclist(sector_list);
sector_list = NULL;
}
} }
// stop any playing sound // stop any playing sound
S_StopSound(mobj); S_StopSound(mobj);
R_RemoveMobjInterpolator(mobj);
// free block // free block
P_RemoveThinker((thinker_t *)mobj); // Here we use the same code as R_RemoveThinkerDelayed, but without reference counting (we're removing everything so it shouldn't matter) and without touching currentthinker since we aren't in P_RunThinkers
R_RemoveMobjInterpolator(mobj); {
thinker_t *thinker = (thinker_t *)mobj;
thinker_t *next = thinker->next;
(next->prev = thinker->prev)->next = next;
Z_Free(thinker);
}
} }
static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}}; static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}};

View file

@ -1564,6 +1564,7 @@ static inline void SavePolyrotatetThinker(const thinker_t *th, const UINT8 type)
WRITEINT32(save_p, ht->polyObjNum); WRITEINT32(save_p, ht->polyObjNum);
WRITEINT32(save_p, ht->speed); WRITEINT32(save_p, ht->speed);
WRITEINT32(save_p, ht->distance); WRITEINT32(save_p, ht->distance);
WRITEUINT8(save_p, ht->turnobjs);
} }
// //
@ -2166,6 +2167,14 @@ static void LoadMobjThinker(actionf_p1 thinker)
mobj->player->viewz = mobj->player->mo->z + mobj->player->viewheight; mobj->player->viewz = mobj->player->mo->z + mobj->player->viewheight;
} }
if (mobj->type == MT_SKYBOX)
{
if (mobj->spawnpoint->options & MTF_OBJECTSPECIAL)
skyboxmo[1] = mobj;
else
skyboxmo[0] = mobj;
}
P_AddThinker(&mobj->thinker); P_AddThinker(&mobj->thinker);
if (diff2 & MD2_WAYPOINTCAP) if (diff2 & MD2_WAYPOINTCAP)
@ -2525,6 +2534,7 @@ static inline void LoadPolyrotatetThinker(actionf_p1 thinker)
ht->polyObjNum = READINT32(save_p); ht->polyObjNum = READINT32(save_p);
ht->speed = READINT32(save_p); ht->speed = READINT32(save_p);
ht->distance = READINT32(save_p); ht->distance = READINT32(save_p);
ht->turnobjs = READUINT8(save_p);
P_AddThinker(&ht->thinker); P_AddThinker(&ht->thinker);
} }
@ -2666,10 +2676,14 @@ static void P_NetUnArchiveThinkers(void)
{ {
next = currentthinker->next; next = currentthinker->next;
if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker || currentthinker->function.acp1 == (actionf_p1)P_NullPrecipThinker)
P_RemoveSavegameMobj((mobj_t *)currentthinker); // item isn't saved, don't remove it P_RemoveSavegameMobj((mobj_t *)currentthinker); // item isn't saved, don't remove it
else else
{
(next->prev = currentthinker->prev)->next = next;
R_DestroyLevelInterpolators(currentthinker);
Z_Free(currentthinker); Z_Free(currentthinker);
}
} }
// we don't want the removed mobjs to come back // we don't want the removed mobjs to come back

View file

@ -3071,6 +3071,10 @@ boolean P_SetupLevel(boolean skipprecip)
(fileinfo + ML_REJECT)->size, (fileinfo + ML_REJECT)->size,
(fileinfo + ML_REJECT)->name); (fileinfo + ML_REJECT)->name);
} }
else
{
rejectmatrix = NULL;
}
// Important: take care of the ordering of the next functions. // Important: take care of the ordering of the next functions.
if (!loadedbm) if (!loadedbm)
@ -3130,14 +3134,14 @@ boolean P_SetupLevel(boolean skipprecip)
if (!playerstarts[numcoopstarts]) if (!playerstarts[numcoopstarts])
break; break;
globalweather = mapheaderinfo[gamemap-1]->weather;
// set up world state // set up world state
P_SpawnSpecials(fromnetsave); P_SpawnSpecials(fromnetsave);
if (loadprecip) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame) if (loadprecip) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
P_SpawnPrecipitation(); P_SpawnPrecipitation();
globalweather = mapheaderinfo[gamemap-1]->weather;
#ifdef HWRENDER // not win32 only 19990829 by Kin #ifdef HWRENDER // not win32 only 19990829 by Kin
if (rendermode != render_soft && rendermode != render_none) if (rendermode != render_soft && rendermode != render_none)
{ {

View file

@ -2395,6 +2395,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
camera[i].y += y; camera[i].y += y;
camera[i].z += z; camera[i].z += z;
camera[i].subsector = R_PointInSubsector(camera[i].x, camera[i].y); camera[i].subsector = R_PointInSubsector(camera[i].x, camera[i].y);
R_RelativeTeleportViewInterpolation(i, x, y, z, 0);
break; break;
} }
} }

View file

@ -17,6 +17,7 @@
#include "r_state.h" #include "r_state.h"
#include "s_sound.h" #include "s_sound.h"
#include "r_main.h" #include "r_main.h"
#include "r_fps.h"
/** \brief The P_MixUp function /** \brief The P_MixUp function
@ -149,25 +150,17 @@ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle
thing->reactiontime = TICRATE/2; // don't move for about half a second thing->reactiontime = TICRATE/2; // don't move for about half a second
// absolute angle position // absolute angle position
if (thing == players[consoleplayer].mo)
localangle[0] = angle;
else if (splitscreen)
{
for (i = 1; i <= splitscreen; i++)
{
if (thing == players[displayplayers[i]].mo)
{
localangle[i] = angle;
break;
}
}
}
// move chasecam at new player location // move chasecam at new player location
for (i = 0; i <= splitscreen; i++) for (i = 0; i <= splitscreen; i++)
{ {
if (thing->player == &players[displayplayers[i]] && camera[i].chase) if (thing->player == &players[displayplayers[i]])
P_ResetCamera(thing->player, &camera[i]); {
localangle[i] = angle;
if (camera[i].chase)
P_ResetCamera(thing->player, &camera[i]);
R_ResetViewInterpolation(i + 1);
break;
}
} }
// don't run in place after a teleport // don't run in place after a teleport

View file

@ -25,6 +25,7 @@
#include "k_kart.h" #include "k_kart.h"
#include "r_main.h" #include "r_main.h"
#include "r_fps.h" #include "r_fps.h"
#include "i_video.h" // rendermode
// Object place // Object place
#include "m_cheat.h" #include "m_cheat.h"
@ -818,15 +819,20 @@ void P_Ticker(boolean run)
// Hack: ensure newview is assigned every tic. // Hack: ensure newview is assigned every tic.
// Ensures view interpolation is T-1 to T in poor network conditions // Ensures view interpolation is T-1 to T in poor network conditions
// We need a better way to assign view state decoupled from game logic // We need a better way to assign view state decoupled from game logic
for (i = 0; i <= splitscreen; i++) if (rendermode != render_none)
{ {
player_t *player = &players[displayplayers[i]]; for (i = 0; i <= splitscreen; i++)
boolean skyVisible = skyVisiblePerPlayer[i];
if (skyVisible && skyboxmo[0] && cv_skybox.value)
{ {
R_SkyboxFrame(player); player_t *player = &players[displayplayers[i]];
boolean isSkyVisibleForPlayer = skyVisiblePerPlayer[i];
if (!player->mo)
continue;
if (isSkyVisibleForPlayer && skyboxmo[0] && cv_skybox.value)
{
R_SkyboxFrame(player);
}
R_SetupFrame(player, (skyboxmo[0] && cv_skybox.value));
} }
R_SetupFrame(player, (skyboxmo[0] && cv_skybox.value));
} }
} }

View file

@ -8518,16 +8518,21 @@ void P_PlayerThink(player_t *player)
if (netgame && cv_antigrief.value != 0 && G_RaceGametype()) if (netgame && cv_antigrief.value != 0 && G_RaceGametype())
{ {
if (!player->spectator && !player->exiting && !(player->pflags & PF_TIMEOVER)) INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
if (&players[i] == player)
continue;
break;
}
if (i < MAXPLAYERS && !player->spectator && !player->exiting && !(player->pflags & PF_TIMEOVER))
{ {
const tic_t griefval = cv_antigrief.value * TICRATE; const tic_t griefval = cv_antigrief.value * TICRATE;
const UINT8 n = player - players; const UINT8 n = player - players;
if (n != serverplayer
#ifndef DEVELOP
&& !IsPlayerAdmin(n)
#endif
)
{ {
if (player->grieftime > griefval) if (player->grieftime > griefval)
{ {
@ -8536,7 +8541,11 @@ void P_PlayerThink(player_t *player)
if (server) if (server)
{ {
if (player->griefstrikes > 2) if ((player->griefstrikes > 2)
#ifndef DEVELOP
&& !IsPlayerAdmin(n)
#endif
&& !P_IsLocalPlayer(player)) // P_IsMachineLocalPlayer for DRRR
{ {
// Send kick // Send kick
XBOXSTATIC UINT8 buf[2]; XBOXSTATIC UINT8 buf[2];

View file

@ -210,6 +210,14 @@ void R_ResetViewInterpolation(UINT8 p)
} }
} }
void R_RelativeTeleportViewInterpolation(UINT8 p, fixed_t xdiff, fixed_t ydiff, fixed_t zdiff, angle_t angdiff)
{
pview_old[p].x += xdiff;
pview_old[p].y += ydiff;
pview_old[p].z += zdiff;
pview_old[p].angle += angdiff;
}
void R_SetViewContext(enum viewcontext_e _viewcontext) void R_SetViewContext(enum viewcontext_e _viewcontext)
{ {
UINT8 i = 0; UINT8 i = 0;

View file

@ -115,6 +115,8 @@ void R_InterpolateView(fixed_t frac);
void R_UpdateViewInterpolation(void); void R_UpdateViewInterpolation(void);
// Reset the view states (e.g. after level load) so R_InterpolateView doesn't interpolate invalid data // Reset the view states (e.g. after level load) so R_InterpolateView doesn't interpolate invalid data
void R_ResetViewInterpolation(UINT8 p); void R_ResetViewInterpolation(UINT8 p);
// Update old view for seamless relative teleport
void R_RelativeTeleportViewInterpolation(UINT8 p, fixed_t xdiff, fixed_t ydiff, fixed_t zdiff, angle_t angdiff);
// Set the current view context (the viewvars pointed to by newview) // Set the current view context (the viewvars pointed to by newview)
void R_SetViewContext(enum viewcontext_e _viewcontext); void R_SetViewContext(enum viewcontext_e _viewcontext);

View file

@ -1220,7 +1220,7 @@ static void R_PortalFrame(line_t *start, line_t *dest, portal_pair *portal)
dest_c.y = (dest->v1->y + dest->v2->y) / 2; dest_c.y = (dest->v1->y + dest->v2->y) / 2;
// Heights! // Heights!
newview->z += dest->frontsector->floorheight - start->frontsector->floorheight; viewz += dest->frontsector->floorheight - start->frontsector->floorheight;
// calculate the difference in position and rotation! // calculate the difference in position and rotation!
#ifdef ANGLED_PORTALS #ifdef ANGLED_PORTALS

View file

@ -567,14 +567,12 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co)
case VK_TAB: case VK_TAB:
event.data1 = KEY_NULL; event.data1 = KEY_NULL;
break; break;
case VK_SHIFT:
event.data1 = KEY_LSHIFT;
break;
case VK_RETURN: case VK_RETURN:
entering_con_command = false; entering_con_command = false;
// Fall through. // Fall through.
default: default:
event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char //event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char
event.data1 = evt.uChar.AsciiChar;
} }
if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t)) if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t))
{ {
@ -593,18 +591,6 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co)
} }
} }
} }
else
{
event.type = ev_keyup;
switch (evt.wVirtualKeyCode)
{
case VK_SHIFT:
event.data1 = KEY_LSHIFT;
break;
default:
break;
}
}
if (event.data1) D_PostEvent(&event); if (event.data1) D_PostEvent(&event);
} }

View file

@ -101,8 +101,10 @@ rendermode_t rendermode = render_none;
boolean highcolor = false; boolean highcolor = false;
static void Impl_SetVsync(void);
// synchronize page flipping with screen refresh // synchronize page flipping with screen refresh
consvar_t cv_vidwait = {"vid_wait", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_vidwait = {"vid_wait", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, Impl_SetVsync, 0, NULL, NULL, 0, 0, NULL};
static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
UINT8 graphics_started = 0; // Is used in console.c and screen.c UINT8 graphics_started = 0; // Is used in console.c and screen.c
@ -1782,7 +1784,7 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
// "direct3d" driver (D3D9) causes Drmingw exchndl // "direct3d" driver (D3D9) causes Drmingw exchndl
// to not write RPT files. Every other driver // to not write RPT files. Every other driver
// seems fine. // seems fine.
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "direct3d11"); SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
renderer = SDL_CreateRenderer(window, -1, flags); renderer = SDL_CreateRenderer(window, -1, flags);
if (renderer == NULL) if (renderer == NULL)
@ -2131,4 +2133,11 @@ UINT32 I_GetRefreshRate(void)
return refresh_rate; return refresh_rate;
} }
static void Impl_SetVsync(void)
{
#if SDL_VERSION_ATLEAST(2,0,18)
if (renderer)
SDL_RenderSetVSync(renderer, cv_vidwait.value);
#endif
}
#endif #endif

View file

@ -818,10 +818,10 @@ sfxinfo_t S_sfx[NUMSFX] =
{"gemhit", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Opulence gem/coin tumbling {"gemhit", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Opulence gem/coin tumbling
{"wrink", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Some sort of ghoulie? {"wrink", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Some sort of ghoulie?
{"bsnipe", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Banana sniping {"bsnipe", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Banana sniping
{"join", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player joined server {"join", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player joined server
{"leave", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player left server {"leave", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player left server
{"requst", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Got a Discord join request {"requst", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Got a Discord join request
{"syfail", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Funny sync failure {"syfail", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Funny sync failure
{"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree: {"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree:
{"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Debug notification {"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Debug notification

111
src/strcasestr.c Normal file
View file

@ -0,0 +1,111 @@
/*
strcasestr -- case insensitive substring searching function.
*/
/*
Copyright 2019 James R.
All rights reserved.
Redistribution and use in source forms, with or without modification, is
permitted provided that the following condition is met:
1. Redistributions of source code must retain the above copyright notice, this
condition and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
static inline int
trycmp (char **pp, char *cp,
const char *q, size_t qn)
{
char *p;
p = (*pp);
if (strncasecmp(p, q, qn) == 0)
return 0;
(*pp) = strchr(&p[1], (*cp));
return 1;
}
static inline void
swapp (char ***ppap, char ***ppbp, char **cpap, char **cpbp)
{
char **pp;
char *p;
pp = *ppap;
*ppap = *ppbp;
*ppbp = pp;
p = *cpap;
*cpap = *cpbp;
*cpbp = p;
}
char *
strcasestr (const char *s, const char *q)
{
size_t qn;
char uc;
char lc;
char *up;
char *lp;
char **ppa;
char **ppb;
char *cpa;
char *cpb;
uc = toupper(*q);
lc = tolower(*q);
up = strchr(s, uc);
lp = strchr(s, lc);
if (!( (intptr_t)up|(intptr_t)lp ))
return 0;
if (!lp || ( up && up < lp ))
{
ppa = &up;
ppb = &lp;
cpa = &uc;
cpb = &lc;
}
else
{
ppa = &lp;
ppb = &up;
cpa = &lc;
cpb = &uc;
}
qn = strlen(q);
for (;;)
{
if (trycmp(ppa, cpa, q, qn) == 0)
return (*ppa);
if (!( (intptr_t)up|(intptr_t)lp ))
break;
if (!(*ppa) || ( (*ppb) && (*ppb) < (*ppa) ))
swapp(&ppa, &ppb, &cpa, &cpb);
}
return 0;
}

View file

@ -2,6 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 2006 by Graue. // Copyright (C) 2006 by Graue.
// Copyright (C) 2006-2018 by Sonic Team Junior. // Copyright (C) 2006-2018 by Sonic Team Junior.
// Copyright (C) 2019 by James R.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
@ -50,3 +51,5 @@ size_t strlcpy(char *dst, const char *src, size_t siz)
} }
#endif #endif
#include "strcasestr.c"