mirror of
https://git.do.srb2.org/KartKrew/Kart-Public.git
synced 2025-01-13 13:21:31 +00:00
Merge branch 'master' into next
This commit is contained in:
commit
32f88155f3
34 changed files with 1104 additions and 291 deletions
|
@ -42,12 +42,12 @@ jobs:
|
|||
paths:
|
||||
- /var/cache/apt/archives
|
||||
- checkout
|
||||
- run:
|
||||
name: Compile without network support and BLUA
|
||||
command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1
|
||||
- run:
|
||||
name: wipe build
|
||||
command: make -C src LINUX=1 cleandep
|
||||
#- run:
|
||||
# name: Compile without network support and BLUA
|
||||
# command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1
|
||||
#- run:
|
||||
# name: wipe build
|
||||
# command: make -C src LINUX=1 cleandep
|
||||
- run:
|
||||
name: rebuild depend
|
||||
command: make -C src LINUX=1 clean
|
||||
|
|
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0)
|
|||
# DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string.
|
||||
# Version change is fine.
|
||||
project(SRB2
|
||||
VERSION 1.4
|
||||
VERSION 1.6
|
||||
LANGUAGES C)
|
||||
|
||||
if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})
|
||||
|
|
|
@ -353,6 +353,40 @@ size_t COM_CheckParm(const char *check)
|
|||
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.
|
||||
*
|
||||
* \param ptext A null-terminated string. Does not need to be
|
||||
|
|
|
@ -37,6 +37,8 @@ size_t COM_Argc(void);
|
|||
const char *COM_Argv(size_t arg); // if argv > argc, returns empty string
|
||||
char *COM_Args(void);
|
||||
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
|
||||
const char *COM_CompleteCommand(const char *partial, INT32 skips);
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
* Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart
|
||||
* Last updated 2022 / 08 / 16 - Kart v1.4 - Main assets
|
||||
* 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
|
||||
|
@ -49,10 +50,10 @@
|
|||
#endif
|
||||
|
||||
// SRB2Kart-specific hashes
|
||||
#define ASSET_HASH_GFX_KART "30b2d9fb5009f1b3a3d7216a0fe28e51"
|
||||
#define ASSET_HASH_GFX_KART "06f86ee16136eb8a7043b15001797034"
|
||||
#define ASSET_HASH_TEXTURES_KART "abb53d56aba47c3a8cb0f764da1c8b80"
|
||||
#define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964"
|
||||
#define ASSET_HASH_MAPS_KART "13e273292576b71af0cdb3a98ca066eb"
|
||||
#define ASSET_HASH_MAPS_KART "d051e55141ba736582228c456953cd98"
|
||||
#ifdef USE_PATCH_KART
|
||||
#define ASSET_HASH_PATCH_KART "00000000000000000000000000000000"
|
||||
#endif
|
||||
|
|
158
src/d_clisrv.c
158
src/d_clisrv.c
|
@ -1130,6 +1130,7 @@ typedef enum
|
|||
CL_PREPAREHTTPFILES,
|
||||
CL_DOWNLOADHTTPFILES,
|
||||
#endif
|
||||
CL_LEGACYREQUESTFAILED,
|
||||
} cl_mode_t;
|
||||
|
||||
static void GetPackets(void);
|
||||
|
@ -1227,6 +1228,7 @@ static inline void CL_DrawConnectionStatus(void)
|
|||
#endif
|
||||
case CL_ASKFULLFILELIST:
|
||||
case CL_CONFIRMCONNECT:
|
||||
case CL_LEGACYREQUESTFAILED:
|
||||
cltext = "";
|
||||
break;
|
||||
case CL_SETUPFILES:
|
||||
|
@ -1333,8 +1335,10 @@ static inline void CL_DrawConnectionStatus(void)
|
|||
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,
|
||||
va(M_GetText("Downloading \"%s\""), tempname));
|
||||
va(M_GetText("\"%s\""), tempname));
|
||||
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
|
||||
va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10));
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
cl_mode = CL_LEGACYREQUESTFAILED;
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_CURL
|
||||
else
|
||||
|
@ -2279,6 +2287,10 @@ static boolean CL_FinishedFileList(void)
|
|||
{
|
||||
cl_mode = CL_DOWNLOADFILES;
|
||||
}
|
||||
else
|
||||
{
|
||||
cl_mode = CL_LEGACYREQUESTFAILED;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -2465,6 +2477,21 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
|
|||
|
||||
cl_mode = CL_LOADFILES;
|
||||
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:
|
||||
if (CL_LoadServerFiles())
|
||||
cl_mode = CL_SETUPFILES;
|
||||
|
@ -5945,9 +5972,6 @@ boolean TryRunTics(tic_t realtics)
|
|||
|
||||
if (ticking)
|
||||
{
|
||||
if (advancedemo)
|
||||
D_StartTitle();
|
||||
else
|
||||
// run the count * tics
|
||||
while (neededtic > gametic)
|
||||
{
|
||||
|
@ -5983,44 +6007,72 @@ boolean TryRunTics(tic_t realtics)
|
|||
|
||||
static INT32 pingtimeout[MAXPLAYERS];
|
||||
|
||||
#define PINGKICK_TICQUEUE 2
|
||||
#define PINGKICK_LIMIT 1
|
||||
|
||||
static inline void PingUpdate(void)
|
||||
{
|
||||
INT32 i;
|
||||
boolean laggers[MAXPLAYERS];
|
||||
UINT8 numlaggers = 0;
|
||||
memset(laggers, 0, sizeof(boolean) * MAXPLAYERS);
|
||||
UINT8 pingkick[MAXPLAYERS];
|
||||
UINT8 nonlaggers = 0;
|
||||
memset(pingkick, 0, sizeof(pingkick));
|
||||
|
||||
netbuffer->packettype = PT_PING;
|
||||
|
||||
//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)
|
||||
laggers[i] = true;
|
||||
numlaggers++;
|
||||
pingtimeout[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
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.
|
||||
//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++)
|
||||
{
|
||||
if (playeringame[i] && laggers[i])
|
||||
{
|
||||
pingtimeout[i]++;
|
||||
if (pingtimeout[i] > cv_pingtimeout.value) // ok your net has been bad for too long, you deserve to die.
|
||||
UINT8 minimumkicklevel = (nonlaggers > 0) ? PINGKICK_LIMIT : PINGKICK_TICQUEUE;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
XBOXSTATIC char buf[2];
|
||||
|
||||
if (!playeringame[i] || pingkick[i] < minimumkicklevel)
|
||||
continue;
|
||||
|
||||
if (pingkick[i] == PINGKICK_LIMIT)
|
||||
{
|
||||
// Don't kick on ping alone if we haven't reached our threshold yet.
|
||||
if (++pingtimeout[i] < cv_pingtimeout.value)
|
||||
continue;
|
||||
}
|
||||
|
||||
pingtimeout[i] = 0;
|
||||
|
||||
buf[0] = (char)i;
|
||||
|
@ -6028,10 +6080,6 @@ static inline void PingUpdate(void)
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//make the ping packet and clear server data for next one
|
||||
|
@ -6054,9 +6102,12 @@ static inline void PingUpdate(void)
|
|||
if (nodeingame[i])
|
||||
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 void UpdatePingTable(void)
|
||||
|
@ -6147,6 +6198,9 @@ FILESTAMP
|
|||
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)
|
||||
{
|
||||
static tic_t resptime = 0;
|
||||
|
@ -6159,6 +6213,55 @@ void NetUpdate(void)
|
|||
|
||||
if (realtics <= 0) // nothing new to update
|
||||
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 (server)
|
||||
|
@ -6201,7 +6304,7 @@ FILESTAMP
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!demo.playback)
|
||||
if (!demo.playback && realtics > 0)
|
||||
{
|
||||
INT32 counts;
|
||||
|
||||
|
@ -6225,6 +6328,7 @@ FILESTAMP
|
|||
// Do not make tics while resynching
|
||||
if (counts != -666)
|
||||
{
|
||||
// See also PingUpdate
|
||||
if (maketic + counts >= firstticstosend + TICQUEUE)
|
||||
counts = firstticstosend+TICQUEUE-maketic-1;
|
||||
|
||||
|
|
53
src/d_main.c
53
src/d_main.c
|
@ -144,7 +144,6 @@ boolean sound_disabled = false;
|
|||
boolean digital_disabled = false;
|
||||
#endif
|
||||
|
||||
boolean advancedemo;
|
||||
#ifdef DEBUGFILE
|
||||
INT32 debugload = 0;
|
||||
#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
|
||||
// =========================================================================
|
||||
|
@ -883,7 +873,6 @@ void D_StartTitle(void)
|
|||
//demosequence = -1;
|
||||
gametype = GT_RACE; // SRB2kart
|
||||
paused = false;
|
||||
advancedemo = false;
|
||||
F_StartTitleScreen();
|
||||
|
||||
// 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
|
||||
FILE *tmpfile;
|
||||
char testfile[MAX_WADPATH];
|
||||
|
||||
if (!userhome)
|
||||
{
|
||||
|
@ -1203,9 +1194,6 @@ void D_SRB2Main(void)
|
|||
|
||||
// If config isn't writable, tons of behavior will be broken.
|
||||
// Fail loudly before things get confusing!
|
||||
FILE *tmpfile;
|
||||
char testfile[MAX_WADPATH];
|
||||
|
||||
snprintf(testfile, sizeof testfile, "%s" PATHSEP "file.tmp", srb2home);
|
||||
testfile[sizeof testfile - 1] = '\0';
|
||||
|
||||
|
@ -1258,26 +1246,6 @@ void D_SRB2Main(void)
|
|||
if (M_CheckParm("-server") || dedicated)
|
||||
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");
|
||||
Z_Init();
|
||||
|
||||
|
@ -1457,6 +1425,23 @@ void D_SRB2Main(void)
|
|||
|
||||
//------------------------------------------------ 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
|
||||
if (M_CheckParm("-usecd") && !dedicated)
|
||||
I_InitCD();
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
#include "d_event.h"
|
||||
#include "w_wad.h" // for MAX_WADFILES
|
||||
|
||||
extern boolean advancedemo;
|
||||
|
||||
// make sure not to write back the config until it's been correctly loaded
|
||||
extern tic_t rendergametic;
|
||||
|
||||
|
@ -34,7 +32,6 @@ void D_SRB2Loop(void) FUNCNORETURN;
|
|||
// D_SRB2Main()
|
||||
// Not a globally visible function, just included for source reference,
|
||||
// calls all startup code, parses command line options.
|
||||
// If not overrided by user input, calls D_AdvanceDemo.
|
||||
//
|
||||
void D_SRB2Main(void);
|
||||
|
||||
|
@ -51,7 +48,6 @@ const char *D_Home(void);
|
|||
//
|
||||
// BASE LEVEL
|
||||
//
|
||||
void D_AdvanceDemo(void);
|
||||
void D_StartTitle(void);
|
||||
|
||||
#endif //__D_MAIN__
|
||||
|
|
234
src/d_netcmd.c
234
src/d_netcmd.c
|
@ -2519,27 +2519,67 @@ void D_PickVote(void)
|
|||
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.
|
||||
// Called either from map <mapname> console command, or idclev cheat.
|
||||
//
|
||||
// Largely rewritten by James.
|
||||
//
|
||||
static void Command_Map_f(void)
|
||||
{
|
||||
const char *mapname;
|
||||
size_t i;
|
||||
INT32 newmapnum;
|
||||
boolean newresetplayers, newencoremode;
|
||||
INT32 newgametype = gametype;
|
||||
size_t first_option;
|
||||
size_t option_force;
|
||||
size_t option_gametype;
|
||||
size_t option_encore;
|
||||
const char *gametypename;
|
||||
boolean newresetplayers;
|
||||
|
||||
// max length of command: map map03 -gametype race -noresetplayers -force -encore
|
||||
// 1 2 3 4 5 6 7
|
||||
// = 8 arg max
|
||||
// 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
|
||||
if (COM_Argc() < 2 || COM_Argc() > 8)
|
||||
{
|
||||
CONS_Printf(M_GetText("map <mapname> [-gametype <type> [-force]: warp to map\n"));
|
||||
return;
|
||||
}
|
||||
boolean mustmodifygame;
|
||||
|
||||
INT32 newmapnum;
|
||||
|
||||
char * mapname;
|
||||
char *realmapname = NULL;
|
||||
|
||||
INT32 newgametype = gametype;
|
||||
boolean newencoremode = cv_kartencore.value;
|
||||
|
||||
INT32 d;
|
||||
|
||||
if (client && !IsPlayerAdmin(consoleplayer))
|
||||
{
|
||||
|
@ -2547,27 +2587,19 @@ static void Command_Map_f(void)
|
|||
return;
|
||||
}
|
||||
|
||||
// internal wad lump always: map command doesn't support external files as in doom legacy
|
||||
if (W_CheckNumForName(COM_Argv(1)) == LUMPERROR)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Internal game level '%s' not found\n"), COM_Argv(1));
|
||||
return;
|
||||
}
|
||||
option_force = COM_CheckPartialParm("-f");
|
||||
option_gametype = COM_CheckPartialParm("-g");
|
||||
option_encore = COM_CheckPartialParm("-e");
|
||||
newresetplayers = ! COM_CheckParm("-noresetplayers");
|
||||
|
||||
if (!(netgame || multiplayer) && !majormods)
|
||||
{
|
||||
if (COM_CheckParm("-force"))
|
||||
{
|
||||
G_SetGameModified(false, true);
|
||||
}
|
||||
else
|
||||
mustmodifygame = !( netgame || multiplayer || majormods );
|
||||
|
||||
if (mustmodifygame && !option_force)
|
||||
{
|
||||
/* May want to be more descriptive? */
|
||||
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
newresetplayers = !COM_CheckParm("-noresetplayers");
|
||||
|
||||
if (!newresetplayers && !cv_debug)
|
||||
{
|
||||
|
@ -2575,72 +2607,110 @@ static void Command_Map_f(void)
|
|||
return;
|
||||
}
|
||||
|
||||
mapname = COM_Argv(1);
|
||||
if (strlen(mapname) != 5
|
||||
|| (newmapnum = M_MapNumber(mapname[3], mapname[4])) == 0)
|
||||
if (option_gametype)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
// use current one by default
|
||||
i = COM_CheckParm("-gametype");
|
||||
if (i)
|
||||
if (option_gametype)
|
||||
{
|
||||
if (!multiplayer)
|
||||
{
|
||||
CONS_Printf(M_GetText("You can't switch gametypes in single player!\n"));
|
||||
return;
|
||||
}
|
||||
gametypename = COM_Argv(option_gametype + 1);
|
||||
|
||||
newgametype = G_GetGametypeByName(gametypename);
|
||||
|
||||
newgametype = G_GetGametypeByName(COM_Argv(i+1));
|
||||
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
|
||||
if (j >= 0 && j < NUMGAMETYPES)
|
||||
newgametype = (INT16)j;
|
||||
}
|
||||
}
|
||||
|
||||
// new encoremode value
|
||||
// use cvar by default
|
||||
|
||||
newencoremode = (boolean)cv_kartencore.value;
|
||||
|
||||
if (COM_CheckParm("-encore"))
|
||||
/* Did they give us a gametype number? That's okay too! */
|
||||
if (isdigit(gametypename[0]))
|
||||
{
|
||||
if (!M_SecretUnlocked(SECRET_ENCORE) && !newencoremode)
|
||||
d = atoi(gametypename);
|
||||
if (d >= 0 && d < NUMGAMETYPES)
|
||||
newgametype = d;
|
||||
else
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
|
||||
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;
|
||||
}
|
||||
newencoremode = !newencoremode;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Alert(CONS_ERROR,
|
||||
"'%s' is not a gametype.\n",
|
||||
gametypename);
|
||||
Z_Free(realmapname);
|
||||
Z_Free(mapname);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(i = COM_CheckParm("-force")) && newgametype == gametype) // SRB2Kart
|
||||
if (!option_force && newgametype == gametype) // SRB2Kart
|
||||
newresetplayers = false; // if not forcing and gametypes is the same
|
||||
|
||||
// don't use a gametype the map doesn't support
|
||||
if (cv_debug || i || cv_skipmapcheck.value)
|
||||
; // The player wants us to trek on anyway. Do so.
|
||||
if (cv_debug || option_force || cv_skipmapcheck.value)
|
||||
fromlevelselect = false; // The player wants us to trek on anyway. Do so.
|
||||
// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
|
||||
// Alternatively, bail if the map header is completely missing anyway.
|
||||
else if (!mapheaderinfo[newmapnum-1]
|
||||
|| !(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
|
||||
else
|
||||
{
|
||||
char gametypestring[32] = "Single Player";
|
||||
|
||||
if (multiplayer)
|
||||
if (!(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
|
||||
{
|
||||
if (newgametype >= 0 && newgametype < NUMGAMETYPES
|
||||
&& Gametype_Names[newgametype])
|
||||
strcpy(gametypestring, Gametype_Names[newgametype]);
|
||||
}
|
||||
|
||||
CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring);
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Course %s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum),
|
||||
(multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player"));
|
||||
Z_Free(realmapname);
|
||||
Z_Free(mapname);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent warping to locked levels
|
||||
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
|
||||
|
@ -2649,11 +2719,25 @@ static void Command_Map_f(void)
|
|||
if (!dedicated && M_MapLocked(newmapnum))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
|
||||
|
||||
Z_Free(realmapname);
|
||||
}
|
||||
|
||||
/** Receives a map command and changes the map.
|
||||
|
@ -2702,7 +2786,9 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
|
|||
lastgametype = gametype;
|
||||
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
|
||||
|
||||
if (!G_RaceGametype())
|
||||
|
|
153
src/d_netfil.c
153
src/d_netfil.c
|
@ -299,6 +299,9 @@ boolean CL_CheckDownloadable(void)
|
|||
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
|
||||
* ::FS_NOTFOUND.
|
||||
*
|
||||
|
@ -311,42 +314,132 @@ boolean CL_SendRequestFile(void)
|
|||
char *p;
|
||||
INT32 i;
|
||||
INT64 totalfreespaceneeded = 0, availablefreespace;
|
||||
INT32 skippedafile = -1;
|
||||
#ifdef MORELEGACYDOWNLOADER
|
||||
boolean firstloop = true;
|
||||
#endif
|
||||
|
||||
#ifdef PARANOIA
|
||||
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++)
|
||||
{
|
||||
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN
|
||||
&& (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
|
||||
|
||||
netbuffer->packettype = PT_REQUESTFILE;
|
||||
p = (char *)netbuffer->u.textcmd;
|
||||
|
||||
for (i = 0; i < fileneedednum; i++)
|
||||
{
|
||||
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);
|
||||
|
||||
// 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
|
||||
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
|
||||
strcatbf(fileneeded[i].filename, downloaddir, "/");
|
||||
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
|
||||
if (firstloop)
|
||||
#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);
|
||||
return HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd);
|
||||
|
||||
// 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
|
||||
|
@ -356,16 +449,18 @@ boolean Got_RequestFilePak(INT32 node)
|
|||
char wad[MAX_WADPATH+1];
|
||||
UINT8 *p = netbuffer->u.textcmd;
|
||||
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);
|
||||
if (id == 0xFF)
|
||||
break;
|
||||
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);
|
||||
return false; // don't read the rest of the files
|
||||
return false; // don't read any more
|
||||
}
|
||||
}
|
||||
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];
|
||||
|
||||
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
|
||||
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
|
||||
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)
|
||||
fclose(transfer[node].currentfile);
|
||||
free(p->id.filename);
|
||||
|
@ -1156,6 +1251,7 @@ void CURLGetFile(void)
|
|||
int msgs_left; /* how many messages are left */
|
||||
const char *easy_handle_error;
|
||||
long response_code = 0;
|
||||
static char *filename;
|
||||
|
||||
if (curl_runninghandles)
|
||||
{
|
||||
|
@ -1180,6 +1276,8 @@ void CURLGetFile(void)
|
|||
{
|
||||
e = m->easy_handle;
|
||||
easyres = m->data.result;
|
||||
filename = Z_StrDup(curl_realname);
|
||||
nameonly(filename);
|
||||
if (easyres != CURLE_OK)
|
||||
{
|
||||
if (easyres == CURLE_HTTP_RETURNED_ERROR)
|
||||
|
@ -1192,21 +1290,30 @@ void CURLGetFile(void)
|
|||
curl_failedwebdownload = true;
|
||||
fclose(curl_curfile->file);
|
||||
remove(curl_curfile->filename);
|
||||
curl_curfile->file = NULL;
|
||||
//nameonly(curl_curfile->filename);
|
||||
nameonly(curl_realname);
|
||||
CONS_Printf(M_GetText("Failed to download %s (%s)\n"), curl_realname, easy_handle_error);
|
||||
CONS_Printf(M_GetText("Failed to download %s (%s)\n"), filename, easy_handle_error);
|
||||
}
|
||||
else
|
||||
{
|
||||
nameonly(curl_realname);
|
||||
CONS_Printf(M_GetText("Finished downloading %s\n"), curl_realname);
|
||||
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;
|
||||
fclose(curl_curfile->file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Z_Free(filename);
|
||||
curl_curfile->file = NULL;
|
||||
curl_running = false;
|
||||
curl_transfers--;
|
||||
curl_multi_remove_handle(multi_handle, e);
|
||||
|
|
|
@ -148,9 +148,9 @@ extern char logfilename[1024];
|
|||
// we use comprevision and compbranch instead.
|
||||
#else
|
||||
#define VERSION 1 // Game version
|
||||
#define SUBVERSION 4 // more precise version number
|
||||
#define VERSIONSTRING "v1.5"
|
||||
#define VERSIONSTRINGW L"v1.5"
|
||||
#define SUBVERSION 6 // more precise version number
|
||||
#define VERSIONSTRING "v1.6"
|
||||
#define VERSIONSTRINGW L"v1.6"
|
||||
// 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 appveyor.yml, for the build bots!
|
||||
|
@ -532,6 +532,7 @@ extern boolean capslock;
|
|||
|
||||
// if we ever make our alloc stuff...
|
||||
#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
|
||||
INT32 I_GetKey(void);
|
||||
|
|
|
@ -139,6 +139,9 @@ typedef long ssize_t;
|
|||
#define strlwr _strlwr
|
||||
#endif
|
||||
|
||||
char *strcasestr(const char *in, const char *what);
|
||||
#define stristr strcasestr
|
||||
|
||||
#if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
|
|
@ -431,10 +431,12 @@ static const char *credits[] = {
|
|||
"\"JugadorXEI\"",
|
||||
"\"Kimberly\"",
|
||||
"\"Lighto97\"",
|
||||
"\"Lonsfor\"",
|
||||
"\"mazmazz\"",
|
||||
"\"minenice\"",
|
||||
"\"Shuffle\"",
|
||||
"\"Snu\"",
|
||||
"\"X.organic\"",
|
||||
"",
|
||||
"\1Lead Artists",
|
||||
"Desmond \"Blade\" DesJardins",
|
||||
|
@ -512,6 +514,7 @@ static const char *credits[] = {
|
|||
"",
|
||||
"\1Testing",
|
||||
"RKH License holders",
|
||||
"The KCS",
|
||||
"\"CyberIF\"",
|
||||
"\"Dani\"",
|
||||
"Karol \"Fooruman\" D""\x1E""browski", // Dąbrowski, <Sryder> accents in srb2 :ytho:
|
||||
|
@ -559,7 +562,7 @@ static struct {
|
|||
// This Tyler52 gag is troublesome
|
||||
// Alignment should be ((spaces+1 * 100) + (headers+1 * 38) + (lines * 15))
|
||||
// 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}
|
||||
};
|
||||
|
||||
|
|
255
src/g_game.c
255
src/g_game.c
|
@ -3339,7 +3339,9 @@ void G_DoReborn(INT32 playernum)
|
|||
// respawn at the start
|
||||
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;
|
||||
|
||||
// first dissasociate the corpse
|
||||
|
@ -4864,6 +4866,242 @@ char *G_BuildMapTitle(INT32 mapnum)
|
|||
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
|
||||
//
|
||||
|
@ -6496,7 +6734,7 @@ void G_BeginRecording(void)
|
|||
demoflags |= DF_ENCORE;
|
||||
|
||||
#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 !????
|
||||
demoflags |= DF_LUAVARS;
|
||||
#endif
|
||||
|
@ -6531,7 +6769,7 @@ void G_BeginRecording(void)
|
|||
if (wadfiles[i]->important)
|
||||
{
|
||||
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);
|
||||
|
||||
totalfiles++;
|
||||
|
@ -6605,7 +6843,7 @@ void G_BeginRecording(void)
|
|||
|
||||
#ifdef HAVE_BLUA
|
||||
// player lua vars, always saved even if empty... Unless it's record attack.
|
||||
if (!modeattacking)
|
||||
if (demoflags & DF_LUAVARS)
|
||||
LUA_ArchiveDemo();
|
||||
#endif
|
||||
|
||||
|
@ -6776,10 +7014,13 @@ static void G_LoadDemoExtraFiles(UINT8 **pp)
|
|||
}
|
||||
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)
|
||||
|
@ -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);
|
||||
if (restorecv_vidwait != cv_vidwait.value)
|
||||
CV_SetValue(&cv_vidwait, restorecv_vidwait);
|
||||
D_AdvanceDemo();
|
||||
D_StartTitle();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -8376,7 +8617,7 @@ boolean G_CheckDemoStatus(void)
|
|||
if (modeattacking)
|
||||
M_EndModeAttackRun();
|
||||
else
|
||||
D_AdvanceDemo();
|
||||
D_StartTitle();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
24
src/g_game.h
24
src/g_game.h
|
@ -173,6 +173,30 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer,
|
|||
boolean skipprecutscene);
|
||||
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
|
||||
mapthing_t *G_FindCTFStart(INT32 playernum);
|
||||
mapthing_t *G_FindMatchStart(INT32 playernum);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
float basey = FIXED_TO_FLOAT(spr->mobj->z);
|
||||
float lowy = wallVerts[0].y;
|
||||
// uncapped/interpolation
|
||||
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)
|
||||
{
|
||||
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
|
||||
// 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
|
||||
|
|
37
src/k_kart.c
37
src/k_kart.c
|
@ -2166,7 +2166,7 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto
|
|||
|
||||
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
|
||||
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
|
||||
{
|
||||
fixed_t upgoer;
|
||||
UINT8 scoremultiply = 1;
|
||||
#ifdef HAVE_BLUA
|
||||
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)
|
||||
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_driftboost] = 0;
|
||||
|
||||
|
@ -2428,19 +2426,34 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b
|
|||
K_CheckBumpers();
|
||||
}
|
||||
|
||||
player->kartstuff[k_spinouttype] = 1;
|
||||
player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2;
|
||||
|
||||
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)
|
||||
{
|
||||
player->kartstuff[k_spinouttimer] = ((5*player->kartstuff[k_spinouttimer])/2)+1;
|
||||
player->mo->momz *= 2;
|
||||
player->kartstuff[k_spinouttype] = 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)
|
||||
player->mo->momz = (117 * player->mo->momz) / 200;
|
||||
#undef SPINTIME
|
||||
|
||||
if (player->mo->state != &states[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 ((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_spinouttimer]--;
|
||||
|
|
17
src/m_menu.c
17
src/m_menu.c
|
@ -5603,6 +5603,18 @@ static void DrawReplayHutReplayInfo(void)
|
|||
if (demolist[dir_on[menudepthleft]].gametype == GT_RACE)
|
||||
{
|
||||
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",
|
||||
G_TicsToMinutes(demolist[dir_on[menudepthleft]].standings[0].timeorscore, true),
|
||||
G_TicsToSeconds(demolist[dir_on[menudepthleft]].standings[0].timeorscore),
|
||||
|
@ -5611,12 +5623,11 @@ static void DrawReplayHutReplayInfo(void)
|
|||
}
|
||||
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));
|
||||
}
|
||||
|
||||
// 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];
|
||||
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));
|
||||
|
||||
// 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];
|
||||
colormap = R_GetTranslationColormap(
|
||||
|
|
|
@ -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!
|
||||
if (!func(mobj))
|
||||
{
|
||||
P_SetTarget(&bnext, NULL);
|
||||
return false;
|
||||
}
|
||||
if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, 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;
|
||||
}
|
||||
}
|
||||
P_SetTarget(&bnext, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
30
src/p_mobj.c
30
src/p_mobj.c
|
@ -9893,6 +9893,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
|
|||
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)
|
||||
{
|
||||
case MT_PLAYER:
|
||||
|
@ -9916,9 +9919,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
|
|||
break;
|
||||
}
|
||||
|
||||
if (!(mobj->flags & MF_NOTHINK))
|
||||
P_AddThinker(&mobj->thinker);
|
||||
|
||||
// Call action functions when the state is set
|
||||
if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC))
|
||||
{
|
||||
|
@ -10296,6 +10296,19 @@ void P_RemovePrecipMobj(precipmobj_t *mobj)
|
|||
|
||||
// Clearing out stuff for savegames
|
||||
void P_RemoveSavegameMobj(mobj_t *mobj)
|
||||
{
|
||||
// unlink from sector and block lists
|
||||
if (((thinker_t *)mobj)->function.acp1 == (actionf_p1)P_NullPrecipThinker)
|
||||
{
|
||||
P_UnsetPrecipThingPosition((precipmobj_t *)mobj);
|
||||
|
||||
if (precipsector_list)
|
||||
{
|
||||
P_DelPrecipSeclist(precipsector_list);
|
||||
precipsector_list = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// unlink from sector and block lists
|
||||
P_UnsetThingPosition(mobj);
|
||||
|
@ -10306,13 +10319,20 @@ void P_RemoveSavegameMobj(mobj_t *mobj)
|
|||
P_DelSeclist(sector_list);
|
||||
sector_list = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// stop any playing sound
|
||||
S_StopSound(mobj);
|
||||
R_RemoveMobjInterpolator(mobj);
|
||||
|
||||
// free block
|
||||
P_RemoveThinker((thinker_t *)mobj);
|
||||
R_RemoveMobjInterpolator(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
|
||||
{
|
||||
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}};
|
||||
|
|
|
@ -1564,6 +1564,7 @@ static inline void SavePolyrotatetThinker(const thinker_t *th, const UINT8 type)
|
|||
WRITEINT32(save_p, ht->polyObjNum);
|
||||
WRITEINT32(save_p, ht->speed);
|
||||
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;
|
||||
}
|
||||
|
||||
if (mobj->type == MT_SKYBOX)
|
||||
{
|
||||
if (mobj->spawnpoint->options & MTF_OBJECTSPECIAL)
|
||||
skyboxmo[1] = mobj;
|
||||
else
|
||||
skyboxmo[0] = mobj;
|
||||
}
|
||||
|
||||
P_AddThinker(&mobj->thinker);
|
||||
|
||||
if (diff2 & MD2_WAYPOINTCAP)
|
||||
|
@ -2525,6 +2534,7 @@ static inline void LoadPolyrotatetThinker(actionf_p1 thinker)
|
|||
ht->polyObjNum = READINT32(save_p);
|
||||
ht->speed = READINT32(save_p);
|
||||
ht->distance = READINT32(save_p);
|
||||
ht->turnobjs = READUINT8(save_p);
|
||||
P_AddThinker(&ht->thinker);
|
||||
}
|
||||
|
||||
|
@ -2666,11 +2676,15 @@ static void P_NetUnArchiveThinkers(void)
|
|||
{
|
||||
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
|
||||
else
|
||||
{
|
||||
(next->prev = currentthinker->prev)->next = next;
|
||||
R_DestroyLevelInterpolators(currentthinker);
|
||||
Z_Free(currentthinker);
|
||||
}
|
||||
}
|
||||
|
||||
// we don't want the removed mobjs to come back
|
||||
iquetail = iquehead = 0;
|
||||
|
|
|
@ -3071,6 +3071,10 @@ boolean P_SetupLevel(boolean skipprecip)
|
|||
(fileinfo + ML_REJECT)->size,
|
||||
(fileinfo + ML_REJECT)->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
rejectmatrix = NULL;
|
||||
}
|
||||
|
||||
// Important: take care of the ordering of the next functions.
|
||||
if (!loadedbm)
|
||||
|
@ -3130,14 +3134,14 @@ boolean P_SetupLevel(boolean skipprecip)
|
|||
if (!playerstarts[numcoopstarts])
|
||||
break;
|
||||
|
||||
globalweather = mapheaderinfo[gamemap-1]->weather;
|
||||
|
||||
// set up world state
|
||||
P_SpawnSpecials(fromnetsave);
|
||||
|
||||
if (loadprecip) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
|
||||
P_SpawnPrecipitation();
|
||||
|
||||
globalweather = mapheaderinfo[gamemap-1]->weather;
|
||||
|
||||
#ifdef HWRENDER // not win32 only 19990829 by Kin
|
||||
if (rendermode != render_soft && rendermode != render_none)
|
||||
{
|
||||
|
|
|
@ -2395,6 +2395,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
camera[i].y += y;
|
||||
camera[i].z += z;
|
||||
camera[i].subsector = R_PointInSubsector(camera[i].x, camera[i].y);
|
||||
R_RelativeTeleportViewInterpolation(i, x, y, z, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "r_state.h"
|
||||
#include "s_sound.h"
|
||||
#include "r_main.h"
|
||||
#include "r_fps.h"
|
||||
|
||||
/** \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
|
||||
|
||||
// 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
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
if (thing->player == &players[displayplayers[i]] && camera[i].chase)
|
||||
if (thing->player == &players[displayplayers[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
|
||||
|
|
10
src/p_tick.c
10
src/p_tick.c
|
@ -25,6 +25,7 @@
|
|||
#include "k_kart.h"
|
||||
#include "r_main.h"
|
||||
#include "r_fps.h"
|
||||
#include "i_video.h" // rendermode
|
||||
|
||||
// Object place
|
||||
#include "m_cheat.h"
|
||||
|
@ -818,17 +819,22 @@ void P_Ticker(boolean run)
|
|||
// Hack: ensure newview is assigned every tic.
|
||||
// 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
|
||||
if (rendermode != render_none)
|
||||
{
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
player_t *player = &players[displayplayers[i]];
|
||||
boolean skyVisible = skyVisiblePerPlayer[i];
|
||||
if (skyVisible && skyboxmo[0] && cv_skybox.value)
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
P_MapEnd();
|
||||
|
||||
|
|
23
src/p_user.c
23
src/p_user.c
|
@ -8518,16 +8518,21 @@ void P_PlayerThink(player_t *player)
|
|||
|
||||
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 UINT8 n = player - players;
|
||||
|
||||
if (n != serverplayer
|
||||
#ifndef DEVELOP
|
||||
&& !IsPlayerAdmin(n)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (player->grieftime > griefval)
|
||||
{
|
||||
|
@ -8536,7 +8541,11 @@ void P_PlayerThink(player_t *player)
|
|||
|
||||
if (server)
|
||||
{
|
||||
if (player->griefstrikes > 2)
|
||||
if ((player->griefstrikes > 2)
|
||||
#ifndef DEVELOP
|
||||
&& !IsPlayerAdmin(n)
|
||||
#endif
|
||||
&& !P_IsLocalPlayer(player)) // P_IsMachineLocalPlayer for DRRR
|
||||
{
|
||||
// Send kick
|
||||
XBOXSTATIC UINT8 buf[2];
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
UINT8 i = 0;
|
||||
|
|
|
@ -115,6 +115,8 @@ void R_InterpolateView(fixed_t frac);
|
|||
void R_UpdateViewInterpolation(void);
|
||||
// Reset the view states (e.g. after level load) so R_InterpolateView doesn't interpolate invalid data
|
||||
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)
|
||||
void R_SetViewContext(enum viewcontext_e _viewcontext);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
// Heights!
|
||||
newview->z += dest->frontsector->floorheight - start->frontsector->floorheight;
|
||||
viewz += dest->frontsector->floorheight - start->frontsector->floorheight;
|
||||
|
||||
// calculate the difference in position and rotation!
|
||||
#ifdef ANGLED_PORTALS
|
||||
|
|
|
@ -567,14 +567,12 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co)
|
|||
case VK_TAB:
|
||||
event.data1 = KEY_NULL;
|
||||
break;
|
||||
case VK_SHIFT:
|
||||
event.data1 = KEY_LSHIFT;
|
||||
break;
|
||||
case VK_RETURN:
|
||||
entering_con_command = false;
|
||||
// Fall through.
|
||||
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))
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -101,8 +101,10 @@ rendermode_t rendermode = render_none;
|
|||
|
||||
boolean highcolor = false;
|
||||
|
||||
static void Impl_SetVsync(void);
|
||||
|
||||
// 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};
|
||||
|
||||
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
|
||||
// to not write RPT files. Every other driver
|
||||
// seems fine.
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "direct3d11");
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
||||
|
||||
renderer = SDL_CreateRenderer(window, -1, flags);
|
||||
if (renderer == NULL)
|
||||
|
@ -2131,4 +2133,11 @@ UINT32 I_GetRefreshRate(void)
|
|||
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
|
||||
|
|
|
@ -818,10 +818,10 @@ sfxinfo_t S_sfx[NUMSFX] =
|
|||
{"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?
|
||||
{"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
|
||||
{"leave", false, 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
|
||||
{"syfail", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Funny sync failure
|
||||
{"join", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player joined server
|
||||
{"leave", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player left server
|
||||
{"requst", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Got a Discord join request
|
||||
{"syfail", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Funny sync failure
|
||||
{"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree:
|
||||
{"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Debug notification
|
||||
|
||||
|
|
111
src/strcasestr.c
Normal file
111
src/strcasestr.c
Normal 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;
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2006 by Graue.
|
||||
// Copyright (C) 2006-2018 by Sonic Team Junior.
|
||||
// Copyright (C) 2019 by James R.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// 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
|
||||
|
||||
#include "strcasestr.c"
|
||||
|
|
Loading…
Reference in a new issue