Merge branch 'master' of https://git.do.srb2.org/KartKrew/Kart into separate-spb-attack-records

# Conflicts:
#	src/g_game.c
This commit is contained in:
toaster 2024-02-23 00:45:46 +00:00
commit 7e217b74f5
52 changed files with 2400 additions and 2113 deletions

View file

@ -19,6 +19,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
g_build_ticcmd.cpp
g_demo.c
g_game.c
g_gamedata.cpp
g_input.c
g_party.cpp
am_map.c

View file

@ -261,7 +261,7 @@ static int io_openlocal (lua_State *L) {
}
void Got_LuaFile(UINT8 **cp, INT32 playernum)
void Got_LuaFile(const UINT8 **cp, INT32 playernum)
{
FILE **pf = NULL;
UINT8 success = READUINT8(*cp); // The first (and only) byte indicates whether the file could be opened

View file

@ -52,25 +52,25 @@ extern "C" {
// what is this?
#if defined (__GNUC__) && defined (DEALIGNED)
#define READUINT8(p) ({ UINT8 *p_tmp = (UINT8 *)p; UINT8 b; memcpy(&b, p, sizeof( UINT8)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; b; })
#define READSINT8(p) ({ SINT8 *p_tmp = (SINT8 *)p; SINT8 b; memcpy(&b, p, sizeof( SINT8)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; b; })
#define READINT16(p) ({ INT16 *p_tmp = (INT16 *)p; INT16 b; memcpy(&b, p, sizeof( INT16)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; b; })
#define READUINT16(p) ({ UINT16 *p_tmp = (UINT16 *)p; UINT16 b; memcpy(&b, p, sizeof( UINT16)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; b; })
#define READINT32(p) ({ INT32 *p_tmp = (INT32 *)p; INT32 b; memcpy(&b, p, sizeof( INT32)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; b; })
#define READUINT32(p) ({ UINT32 *p_tmp = (UINT32 *)p; UINT32 b; memcpy(&b, p, sizeof( UINT32)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; b; })
#define READCHAR(p) ({ char *p_tmp = (char *)p; char b; memcpy(&b, p, sizeof( char)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; b; })
#define READFIXED(p) ({ fixed_t *p_tmp = (fixed_t *)p; fixed_t b; memcpy(&b, p, sizeof(fixed_t)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; b; })
#define READANGLE(p) ({ angle_t *p_tmp = (angle_t *)p; angle_t b; memcpy(&b, p, sizeof(angle_t)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; b; })
#define READUINT8(p) ({ const UINT8 *p_tmp = (const UINT8 *)p; UINT8 b; memcpy(&b, p, sizeof( UINT8)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; })
#define READSINT8(p) ({ const SINT8 *p_tmp = (const SINT8 *)p; SINT8 b; memcpy(&b, p, sizeof( SINT8)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; })
#define READINT16(p) ({ const INT16 *p_tmp = (const INT16 *)p; INT16 b; memcpy(&b, p, sizeof( INT16)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; })
#define READUINT16(p) ({ const UINT16 *p_tmp = (const UINT16 *)p; UINT16 b; memcpy(&b, p, sizeof( UINT16)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; })
#define READINT32(p) ({ const INT32 *p_tmp = (const INT32 *)p; INT32 b; memcpy(&b, p, sizeof( INT32)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; })
#define READUINT32(p) ({ const UINT32 *p_tmp = (const UINT32 *)p; UINT32 b; memcpy(&b, p, sizeof( UINT32)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; })
#define READCHAR(p) ({ const char *p_tmp = (const char *)p; char b; memcpy(&b, p, sizeof( char)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; })
#define READFIXED(p) ({ const fixed_t *p_tmp = (const fixed_t *)p; fixed_t b; memcpy(&b, p, sizeof(fixed_t)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; })
#define READANGLE(p) ({ const angle_t *p_tmp = (const angle_t *)p; angle_t b; memcpy(&b, p, sizeof(angle_t)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; })
#else
#define READUINT8(p) ((UINT8*)(*(void**)(&(p)) = (void*)&((UINT8*)(p))[1]))[-1]
#define READSINT8(p) ((SINT8*)(*(void**)(&(p)) = (void*)&((SINT8*)(p))[1]))[-1]
#define READINT16(p) ((INT16*)(*(void**)(&(p)) = (void*)&((INT16*)(p))[1]))[-1]
#define READUINT16(p) ((UINT16*)(*(void**)(&(p)) = (void*)&((UINT16*)(p))[1]))[-1]
#define READINT32(p) ((INT32*)(*(void**)(&(p)) = (void*)&((INT32*)(p))[1]))[-1]
#define READUINT32(p) ((UINT32*)(*(void**)(&(p)) = (void*)&((UINT32*)(p))[1]))[-1]
#define READCHAR(p) ((char*)(*(void**)(&(p)) = (void*)&((char*)(p))[1]))[-1]
#define READFIXED(p) ((fixed_t*)(*(void**)(&(p)) = (void*)&((fixed_t*)(p))[1]))[-1]
#define READANGLE(p) ((angle_t*)(*(void**)(&(p)) = (void*)&((angle_t*)(p))[1]))[-1]
#define READUINT8(p) ((const UINT8*)(*(const void**)(&(p)) = (const void*)&((const UINT8*)(p))[1]))[-1]
#define READSINT8(p) ((const SINT8*)(*(const void**)(&(p)) = (const void*)&((const SINT8*)(p))[1]))[-1]
#define READINT16(p) ((const INT16*)(*(const void**)(&(p)) = (const void*)&((const INT16*)(p))[1]))[-1]
#define READUINT16(p) ((const UINT16*)(*(const void**)(&(p)) = (const void*)&((const UINT16*)(p))[1]))[-1]
#define READINT32(p) ((const INT32*)(*(const void**)(&(p)) = (const void*)&((const INT32*)(p))[1]))[-1]
#define READUINT32(p) ((const UINT32*)(*(const void**)(&(p)) = (const void*)&((const UINT32*)(p))[1]))[-1]
#define READCHAR(p) ((const char*)(*(const void**)(&(p)) = (const void*)&((const char*)(p))[1]))[-1]
#define READFIXED(p) ((const fixed_t*)(*(const void**)(&(p)) = (const void*)&((const fixed_t*)(p))[1]))[-1]
#define READANGLE(p) ((const angle_t*)(*(const void**)(&(p)) = (const void*)&((const angle_t*)(p))[1]))[-1]
#endif
#else //SRB2_BIG_ENDIAN
@ -133,15 +133,15 @@ FUNCINLINE static ATTRINLINE UINT32 readulong(void *ptr)
return (ucp[3] << 24) | (ucp[2] << 16) | (ucp[1] << 8) | ucp[0];
}
#define READUINT8(p) ((UINT8*)(p = (void*)&((UINT8*)p)[1]))[-1]
#define READSINT8(p) ((SINT8*)(p = (void*)&((SINT8*)p)[1]))[-1]
#define READINT16(p) readshort(&((INT16*)(p = (void*)&((INT16*)p)[1]))[-1])
#define READUINT16(p) readushort(&((UINT16*)(p = (void*)&((UINT16*)p)[1]))[-1])
#define READINT32(p) readlong(&((INT32*)(p = (void*)&((INT32*)p)[1]))[-1])
#define READUINT32(p) readulong(&((UINT32*)(p = (void*)&((UINT32*)p)[1]))
#define READCHAR(p) ((char*)(p = (void*)&((char*)p)[1]))[-1]
#define READFIXED(p) readlong(&((fixed_t*)(p = (void*)&((fixed_t*)p)[1]))[-1])
#define READANGLE(p) readulong(&((angle_t*)(p = (void*)&((angle_t*)p)[1]))[-1])
#define READUINT8(p) ((const UINT8*)(p = (const void*)&((const UINT8*)p)[1]))[-1]
#define READSINT8(p) ((const SINT8*)(p = (const void*)&((const SINT8*)p)[1]))[-1]
#define READINT16(p) readshort(&((const INT16*)(p = (const void*)&((const INT16*)p)[1]))[-1])
#define READUINT16(p) readushort(&((const UINT16*)(p = (const void*)&((const UINT16*)p)[1]))[-1])
#define READINT32(p) readlong(&((const INT32*)(p = (const void*)&((const INT32*)p)[1]))[-1])
#define READUINT32(p) readulong(&((const UINT32*)(p = (const void*)&((const UINT32*)p)[1]))
#define READCHAR(p) ((const char*)(p = (const void*)&((const char*)p)[1]))[-1]
#define READFIXED(p) readlong(&((const fixed_t*)(p = (const void*)&((const fixed_t*)p)[1]))[-1])
#define READANGLE(p) readulong(&((const angle_t*)(p = (const void*)&((const angle_t*)p)[1]))[-1])
#endif //SRB2_BIG_ENDIAN
#undef DEALIGNED

View file

@ -341,7 +341,7 @@ static const char *com_null_string = "";
static char *com_args = NULL; // current command args or NULL
static int com_flags;
static void Got_NetVar(UINT8 **p, INT32 playernum);
static void Got_NetVar(const UINT8 **p, INT32 playernum);
/** Initializes command buffer and adds basic commands.
*/
@ -1749,16 +1749,16 @@ badinput:
static boolean serverloading = false;
static consvar_t *
ReadNetVar (UINT8 **p, char **return_value, boolean *return_stealth)
ReadNetVar (const UINT8 **p, const char **return_value, boolean *return_stealth)
{
UINT16 netid;
char *val;
const char *val;
boolean stealth;
consvar_t *cvar;
netid = READUINT16 (*p);
val = (char *)*p;
val = (const char *)*p;
SKIPSTRING (*p);
stealth = READUINT8 (*p);
@ -1778,17 +1778,17 @@ ReadNetVar (UINT8 **p, char **return_value, boolean *return_stealth)
}
static consvar_t *
ReadDemoVar (UINT8 **p, char **return_value, boolean *return_stealth)
ReadDemoVar (const UINT8 **p, const char **return_value, boolean *return_stealth)
{
char *name;
char *val;
const char *name;
const char *val;
boolean stealth;
consvar_t *cvar;
name = (char *)*p;
name = (const char *)*p;
SKIPSTRING (*p);
val = (char *)*p;
val = (const char *)*p;
SKIPSTRING (*p);
stealth = READUINT8 (*p);
@ -1805,10 +1805,10 @@ ReadDemoVar (UINT8 **p, char **return_value, boolean *return_stealth)
return cvar;
}
static void Got_NetVar(UINT8 **p, INT32 playernum)
static void Got_NetVar(const UINT8 **p, INT32 playernum)
{
consvar_t *cvar;
char *svalue;
const char *svalue;
boolean stealth;
if (playernum != serverplayer && !IsPlayerAdmin(playernum) && !serverloading)
@ -1858,15 +1858,16 @@ void CV_SaveVars(UINT8 **p, boolean in_demo)
WRITEUINT16(count_p, count);
}
static void CV_LoadVars(UINT8 **p,
consvar_t *(*got)(UINT8 **p, char **ret_value, boolean *ret_stealth))
static size_t CV_LoadVars(const UINT8 *bufstart,
consvar_t *(*got)(const UINT8 **p, const char **ret_value, boolean *ret_stealth))
{
const UINT8 *p = bufstart;
const boolean store = (client || demo.playback);
consvar_t *cvar;
UINT16 count;
char *val;
const char *val;
boolean stealth;
// prevent "invalid command received"
@ -1887,16 +1888,18 @@ static void CV_LoadVars(UINT8 **p,
}
}
count = READUINT16(*p);
count = READUINT16(p);
while (count--)
{
cvar = (*got)(p, &val, &stealth);
cvar = (*got)(&p, &val, &stealth);
if (cvar)
Setvalue(cvar, val, stealth);
}
serverloading = false;
return p - bufstart;
}
void CV_RevertNetVars(void)
@ -1928,14 +1931,14 @@ void CV_RevertNetVars(void)
}
}
void CV_LoadNetVars(UINT8 **p)
size_t CV_LoadNetVars(const UINT8 *p)
{
CV_LoadVars(p, ReadNetVar);
return CV_LoadVars(p, ReadNetVar);
}
void CV_LoadDemoVars(UINT8 **p)
size_t CV_LoadDemoVars(const UINT8 *p)
{
CV_LoadVars(p, ReadDemoVar);
return CV_LoadVars(p, ReadDemoVar);
}
static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth);

View file

@ -281,13 +281,13 @@ void CV_SaveVariables(FILE *f);
void CV_SaveVars(UINT8 **p, boolean in_demo);
#define CV_SaveNetVars(p) CV_SaveVars(p, false)
void CV_LoadNetVars(UINT8 **p);
size_t CV_LoadNetVars(const UINT8 *p);
// then revert after leaving a netgame
void CV_RevertNetVars(void);
#define CV_SaveDemoVars(p) CV_SaveVars(p, true)
void CV_LoadDemoVars(UINT8 **p);
size_t CV_LoadDemoVars(const UINT8 *p);
// reset cheat netvars after cheats is deactivated
void CV_CheatsChanged(void);

View file

@ -320,9 +320,9 @@ tic_t ExpandTics(INT32 low, tic_t basetic)
// Some extra data function for handle textcmd buffer
// -----------------------------------------------------------------
static void (*listnetxcmd[MAXNETXCMD])(UINT8 **p, INT32 playernum);
static void (*listnetxcmd[MAXNETXCMD])(const UINT8 **p, INT32 playernum);
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum))
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(const UINT8 **p, INT32 playernum))
{
#ifdef PARANOIA
if (id >= MAXNETXCMD)
@ -463,12 +463,12 @@ static boolean ExtraDataTicker(void)
{
if (playeringame[i] || i == 0)
{
UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i);
const UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i);
if (bufferstart)
{
UINT8 *curpos = bufferstart;
UINT8 *bufferend = &curpos[((UINT16*)curpos)[0]+2];
const UINT8 *curpos = bufferstart;
const UINT8 *bufferend = &curpos[((const UINT16*)curpos)[0]+2];
curpos += 2;
while (curpos < bufferend)
@ -2825,7 +2825,7 @@ static void Command_Kick(void)
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
}
static void Got_KickCmd(UINT8 **p, INT32 playernum)
static void Got_KickCmd(const UINT8 **p, INT32 playernum)
{
INT32 pnum, msg;
char buf[3 + MAX_REASONLENGTH];
@ -3195,9 +3195,9 @@ static void Command_ResendGamestate(void)
}
}
static void Got_AddPlayer(UINT8 **p, INT32 playernum);
static void Got_RemovePlayer(UINT8 **p, INT32 playernum);
static void Got_AddBot(UINT8 **p, INT32 playernum);
static void Got_AddPlayer(const UINT8 **p, INT32 playernum);
static void Got_RemovePlayer(const UINT8 **p, INT32 playernum);
static void Got_AddBot(const UINT8 **p, INT32 playernum);
void Joinable_OnChange(void);
void Joinable_OnChange(void)
@ -3457,7 +3457,7 @@ static inline void SV_AddNode(INT32 node)
}
// Xcmd XD_ADDPLAYER
static void Got_AddPlayer(UINT8 **p, INT32 playernum)
static void Got_AddPlayer(const UINT8 **p, INT32 playernum)
{
INT16 node, newplayernum;
UINT8 console;
@ -3566,7 +3566,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
}
// Xcmd XD_REMOVEPLAYER
static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
static void Got_RemovePlayer(const UINT8 **p, INT32 playernum)
{
SINT8 pnum, reason;
@ -3593,7 +3593,7 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
// Xcmd XD_ADDBOT
// Compacted version of XD_ADDPLAYER for simplicity
static void Got_AddBot(UINT8 **p, INT32 playernum)
static void Got_AddBot(const UINT8 **p, INT32 playernum)
{
INT16 newplayernum;
UINT8 skinnum = 0;

View file

@ -582,7 +582,7 @@ void GenerateChallenge(uint8_t *buf);
shouldsign_t ShouldSignChallenge(uint8_t *message);
// Initialise the other field
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum));
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(const UINT8 **p, INT32 playernum));
void SendNetXCmdForPlayer(UINT8 playerid, netxcmd_t id, const void *param, size_t nparam);
#define SendNetXCmd(id, param, nparam) SendNetXCmdForPlayer(0, id, param, nparam) // Shortcut for P1
void SendKick(UINT8 playernum, UINT8 msg);

View file

@ -1093,7 +1093,6 @@ void D_ClearState(void)
memset(displayplayers, 0, sizeof(displayplayers));
memset(g_localplayers, 0, sizeof g_localplayers);
consoleplayer = 0;
demo.attract = DEMO_ATTRACT_OFF;
G_SetGametype(GT_RACE); // SRB2kart
paused = false;

View file

@ -84,31 +84,31 @@
// protos
// ------
static void Got_NameAndColor(UINT8 **cp, INT32 playernum);
static void Got_WeaponPref(UINT8 **cp, INT32 playernum);
static void Got_PartyInvite(UINT8 **cp, INT32 playernum);
static void Got_AcceptPartyInvite(UINT8 **cp, INT32 playernum);
static void Got_CancelPartyInvite(UINT8 **cp, INT32 playernum);
static void Got_LeaveParty(UINT8 **cp, INT32 playernum);
static void Got_Mapcmd(UINT8 **cp, INT32 playernum);
static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum);
static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum);
static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum);
static void Got_PickVotecmd(UINT8 **cp, INT32 playernum);
static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum);
static void Got_Addfilecmd(UINT8 **cp, INT32 playernum);
static void Got_Pause(UINT8 **cp, INT32 playernum);
static void Got_RandomSeed(UINT8 **cp, INT32 playernum);
static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum);
static void Got_Teamchange(UINT8 **cp, INT32 playernum);
static void Got_Clearscores(UINT8 **cp, INT32 playernum);
static void Got_DiscordInfo(UINT8 **cp, INT32 playernum);
static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum);
static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum);
static void Got_Automatecmd(UINT8 **cp, INT32 playernum);
static void Got_RequestMapQueuecmd(UINT8 **cp, INT32 playernum);
static void Got_MapQueuecmd(UINT8 **cp, INT32 playernum);
static void Got_Cheat(UINT8 **cp, INT32 playernum);
static void Got_NameAndColor(const UINT8 **cp, INT32 playernum);
static void Got_WeaponPref(const UINT8 **cp, INT32 playernum);
static void Got_PartyInvite(const UINT8 **cp, INT32 playernum);
static void Got_AcceptPartyInvite(const UINT8 **cp, INT32 playernum);
static void Got_CancelPartyInvite(const UINT8 **cp, INT32 playernum);
static void Got_LeaveParty(const UINT8 **cp, INT32 playernum);
static void Got_Mapcmd(const UINT8 **cp, INT32 playernum);
static void Got_ExitLevelcmd(const UINT8 **cp, INT32 playernum);
static void Got_SetupVotecmd(const UINT8 **cp, INT32 playernum);
static void Got_ModifyVotecmd(const UINT8 **cp, INT32 playernum);
static void Got_PickVotecmd(const UINT8 **cp, INT32 playernum);
static void Got_RequestAddfilecmd(const UINT8 **cp, INT32 playernum);
static void Got_Addfilecmd(const UINT8 **cp, INT32 playernum);
static void Got_Pause(const UINT8 **cp, INT32 playernum);
static void Got_RandomSeed(const UINT8 **cp, INT32 playernum);
static void Got_RunSOCcmd(const UINT8 **cp, INT32 playernum);
static void Got_Teamchange(const UINT8 **cp, INT32 playernum);
static void Got_Clearscores(const UINT8 **cp, INT32 playernum);
static void Got_DiscordInfo(const UINT8 **cp, INT32 playernum);
static void Got_ScheduleTaskcmd(const UINT8 **cp, INT32 playernum);
static void Got_ScheduleClearcmd(const UINT8 **cp, INT32 playernum);
static void Got_Automatecmd(const UINT8 **cp, INT32 playernum);
static void Got_RequestMapQueuecmd(const UINT8 **cp, INT32 playernum);
static void Got_MapQueuecmd(const UINT8 **cp, INT32 playernum);
static void Got_Cheat(const UINT8 **cp, INT32 playernum);
static void Command_Playdemo_f(void);
static void Command_Timedemo_f(void);
@ -165,12 +165,12 @@ static void Command_Clearscores_f(void);
// Remote Administration
static void Command_Changepassword_f(void);
static void Command_Login_f(void);
static void Got_Verification(UINT8 **cp, INT32 playernum);
static void Got_Removal(UINT8 **cp, INT32 playernum);
static void Got_Verification(const UINT8 **cp, INT32 playernum);
static void Got_Removal(const UINT8 **cp, INT32 playernum);
static void Command_Verify_f(void);
static void Command_RemoveAdmin_f(void);
static void Command_MotD_f(void);
static void Got_MotD_f(UINT8 **cp, INT32 playernum);
static void Got_MotD_f(const UINT8 **cp, INT32 playernum);
static void Command_ShowScores_f(void);
static void Command_ShowTime_f(void);
@ -1094,7 +1094,7 @@ static void FinalisePlaystateChange(INT32 playernum)
P_CheckRacers(); // also SRB2Kart
}
static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
static void Got_NameAndColor(const UINT8 **cp, INT32 playernum)
{
player_t *p = &players[playernum];
char name[MAXPLAYERNAME+1];
@ -1278,11 +1278,12 @@ void WeaponPref_Save(UINT8 **cp, INT32 playernum)
WRITEUINT8(*cp, prefs);
}
void WeaponPref_Parse(UINT8 **cp, INT32 playernum)
size_t WeaponPref_Parse(const UINT8 *bufstart, INT32 playernum)
{
const UINT8 *p = bufstart;
player_t *player = &players[playernum];
UINT8 prefs = READUINT8(*cp);
UINT8 prefs = READUINT8(p);
player->pflags &= ~(PF_KICKSTARTACCEL|PF_SHRINKME|PF_AUTOROULETTE);
@ -1302,17 +1303,19 @@ void WeaponPref_Parse(UINT8 **cp, INT32 playernum)
// so this will have to do.
K_UpdateShrinkCheat(player);
}
return p - bufstart;
}
static void Got_WeaponPref(UINT8 **cp,INT32 playernum)
static void Got_WeaponPref(const UINT8 **cp,INT32 playernum)
{
WeaponPref_Parse(cp, playernum);
*cp += WeaponPref_Parse(*cp, playernum);
// SEE ALSO g_demo.c
demo_extradata[playernum] |= DXD_WEAPONPREF;
}
static void Got_PartyInvite(UINT8 **cp,INT32 playernum)
static void Got_PartyInvite(const UINT8 **cp,INT32 playernum)
{
UINT8 invitee;
@ -1356,7 +1359,7 @@ static void Got_PartyInvite(UINT8 **cp,INT32 playernum)
}
}
static void Got_AcceptPartyInvite(UINT8 **cp,INT32 playernum)
static void Got_AcceptPartyInvite(const UINT8 **cp,INT32 playernum)
{
int invitation;
@ -1397,7 +1400,7 @@ static void Got_AcceptPartyInvite(UINT8 **cp,INT32 playernum)
}
}
static void Got_CancelPartyInvite(UINT8 **cp,INT32 playernum)
static void Got_CancelPartyInvite(const UINT8 **cp,INT32 playernum)
{
UINT8 invitee;
@ -1427,7 +1430,7 @@ static void Got_CancelPartyInvite(UINT8 **cp,INT32 playernum)
}
}
static void Got_LeaveParty(UINT8 **cp,INT32 playernum)
static void Got_LeaveParty(const UINT8 **cp,INT32 playernum)
{
(void)cp;
@ -2700,7 +2703,7 @@ static void Command_Map_f(void)
* ::serverplayer or ::adminplayer.
* \sa D_MapChange
*/
static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
static void Got_Mapcmd(const UINT8 **cp, INT32 playernum)
{
UINT8 flags;
INT32 presetplayer = 1;
@ -3113,7 +3116,7 @@ static void Command_QueueMap_f(void)
Z_Free(mapname);
}
static void Got_RequestMapQueuecmd(UINT8 **cp, INT32 playernum)
static void Got_RequestMapQueuecmd(const UINT8 **cp, INT32 playernum)
{
UINT8 flags;
boolean setencore;
@ -3158,7 +3161,7 @@ static void Got_RequestMapQueuecmd(UINT8 **cp, INT32 playernum)
Handle_MapQueueSend(mapnumber, setgametype, setencore);
}
static void Got_MapQueuecmd(UINT8 **cp, INT32 playernum)
static void Got_MapQueuecmd(const UINT8 **cp, INT32 playernum)
{
UINT8 flags, queueposition, i;
boolean setencore;
@ -3260,7 +3263,7 @@ static void Command_Pause(void)
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
}
static void Got_Pause(UINT8 **cp, INT32 playernum)
static void Got_Pause(const UINT8 **cp, INT32 playernum)
{
UINT8 dedicatedpause = false;
const char *playername;
@ -3321,7 +3324,7 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
* \param playernum Player responsible for the message. Must be ::serverplayer.
* \author Graue <graue@oceanbase.org>
*/
static void Got_RandomSeed(UINT8 **cp, INT32 playernum)
static void Got_RandomSeed(const UINT8 **cp, INT32 playernum)
{
UINT32 seed;
@ -3358,7 +3361,7 @@ static void Command_Clearscores_f(void)
* \sa XD_CLEARSCORES, Command_Clearscores_f
* \author SSNTails <http://www.ssntails.org>
*/
static void Got_Clearscores(UINT8 **cp, INT32 playernum)
static void Got_Clearscores(const UINT8 **cp, INT32 playernum)
{
INT32 i;
@ -3608,7 +3611,7 @@ void P_SetPlayerSpectator(INT32 playernum)
}
//todo: This and the other teamchange functions are getting too long and messy. Needs cleaning.
static void Got_Teamchange(UINT8 **cp, INT32 playernum)
static void Got_Teamchange(const UINT8 **cp, INT32 playernum)
{
changeteam_union NetPacket;
boolean error = false, wasspectator = false;
@ -3861,10 +3864,6 @@ void RemoveAdminPlayer(INT32 playernum)
static void Command_Verify_f(void)
{
char buf[8]; // Should be plenty
char *temp;
INT32 playernum;
if (client)
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
@ -3883,19 +3882,16 @@ static void Command_Verify_f(void)
return;
}
strlcpy(buf, COM_Argv(1), sizeof (buf));
INT32 playernum = atoi(COM_Argv(1));
playernum = atoi(buf);
temp = buf;
WRITEUINT8(temp, playernum);
if (playeringame[playernum])
if (playernum >= 0 && playernum < MAXPLAYERS && playeringame[playernum])
{
UINT8 buf[1] = {playernum};
SendNetXCmd(XD_VERIFIED, buf, 1);
}
}
static void Got_Verification(UINT8 **cp, INT32 playernum)
static void Got_Verification(const UINT8 **cp, INT32 playernum)
{
INT16 num = READUINT8(*cp);
@ -3917,10 +3913,6 @@ static void Got_Verification(UINT8 **cp, INT32 playernum)
static void Command_RemoveAdmin_f(void)
{
char buf[8]; // Should be plenty
char *temp;
INT32 playernum;
if (client)
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
@ -3933,19 +3925,16 @@ static void Command_RemoveAdmin_f(void)
return;
}
strlcpy(buf, COM_Argv(1), sizeof(buf));
INT32 playernum = atoi(COM_Argv(1));
playernum = atoi(buf);
temp = buf;
WRITEUINT8(temp, playernum);
if (playeringame[playernum])
if (playernum >= 0 && playernum < MAXPLAYERS && playeringame[playernum])
{
UINT8 buf[1] = {playernum};
SendNetXCmd(XD_DEMOTED, buf, 1);
}
}
static void Got_Removal(UINT8 **cp, INT32 playernum)
static void Got_Removal(const UINT8 **cp, INT32 playernum)
{
UINT8 num = READUINT8(*cp);
@ -4224,7 +4213,7 @@ static void Command_MotD_f(void)
Z_Free(mymotd);
}
static void Got_MotD_f(UINT8 **cp, INT32 playernum)
static void Got_MotD_f(const UINT8 **cp, INT32 playernum)
{
char *mymotd = Z_Malloc(sizeof(motd), PU_STATIC, NULL);
INT32 i;
@ -4288,7 +4277,7 @@ static void Command_RunSOC(void)
SendNetXCmd(XD_RUNSOC, buf, length);
}
static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum)
static void Got_RunSOCcmd(const UINT8 **cp, INT32 playernum)
{
char filename[256];
filestatus_t ncs = FS_NOTCHECKED;
@ -4480,7 +4469,7 @@ static void Command_Addfile(void)
#endif/*TESTERS*/
}
static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
static void Got_RequestAddfilecmd(const UINT8 **cp, INT32 playernum)
{
char filename[241];
filestatus_t ncs = FS_NOTCHECKED;
@ -4539,7 +4528,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
COM_BufAddText(va("addfile %s\n", filename));
}
static void Got_Addfilecmd(UINT8 **cp, INT32 playernum)
static void Got_Addfilecmd(const UINT8 **cp, INT32 playernum)
{
char filename[241];
filestatus_t ncs = FS_NOTCHECKED;
@ -5635,7 +5624,7 @@ static void Command_ExitLevel_f(void)
}
}
static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
static void Got_ExitLevelcmd(const UINT8 **cp, INT32 playernum)
{
(void)cp;
@ -5657,7 +5646,7 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
G_FinishExitLevel();
}
static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)
static void Got_SetupVotecmd(const UINT8 **cp, INT32 playernum)
{
INT16 newGametype = 0;
boolean baseEncore = false;
@ -5732,7 +5721,7 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)
Y_StartVote();
}
static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum)
static void Got_ModifyVotecmd(const UINT8 **cp, INT32 playernum)
{
UINT8 targetID = READUINT8(*cp);
SINT8 vote = READSINT8(*cp);
@ -5778,7 +5767,7 @@ fail:
}
}
static void Got_PickVotecmd(UINT8 **cp, INT32 playernum)
static void Got_PickVotecmd(const UINT8 **cp, INT32 playernum)
{
SINT8 pick = READSINT8(*cp);
SINT8 level = READSINT8(*cp);
@ -5794,7 +5783,7 @@ static void Got_PickVotecmd(UINT8 **cp, INT32 playernum)
Y_SetupVoteFinish(pick, level);
}
static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum)
static void Got_ScheduleTaskcmd(const UINT8 **cp, INT32 playernum)
{
char command[MAXTEXTCMD];
INT16 seconds;
@ -5824,7 +5813,7 @@ static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum)
}
}
static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum)
static void Got_ScheduleClearcmd(const UINT8 **cp, INT32 playernum)
{
(void)cp;
@ -5846,7 +5835,7 @@ static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum)
}
}
static void Got_Automatecmd(UINT8 **cp, INT32 playernum)
static void Got_Automatecmd(const UINT8 **cp, INT32 playernum)
{
UINT8 eventID;
char command[MAXTEXTCMD];
@ -5889,7 +5878,7 @@ static void Got_Automatecmd(UINT8 **cp, INT32 playernum)
}
}
static void Got_Cheat(UINT8 **cp, INT32 playernum)
static void Got_Cheat(const UINT8 **cp, INT32 playernum)
{
UINT8 targetPlayer = READUINT8(*cp);
cheat_t cheat = READUINT8(*cp);
@ -7125,7 +7114,7 @@ void LiveStudioAudience_OnChange(void)
livestudioaudience_timer = 90;
}
void Got_DiscordInfo(UINT8 **p, INT32 playernum)
void Got_DiscordInfo(const UINT8 **p, INT32 playernum)
{
if (playernum != serverplayer /*&& !IsPlayerAdmin(playernum)*/)
{

View file

@ -236,7 +236,7 @@ void CleanupPlayerName(INT32 playernum, const char *newname);
boolean EnsurePlayerNameIsGood(char *name, INT32 playernum);
void WeaponPref_Send(UINT8 ssplayer);
void WeaponPref_Save(UINT8 **cp, INT32 playernum);
void WeaponPref_Parse(UINT8 **cp, INT32 playernum);
size_t WeaponPref_Parse(const UINT8 *p, INT32 playernum);
void D_SendPlayerConfig(UINT8 n);
void Command_ExitGame_f(void);
void Command_Retry_f(void);

View file

@ -142,7 +142,7 @@ void RemoveLuaFileTransfer(void);
void RemoveAllLuaFileTransfers(void);
void SV_AbortLuaFileTransfer(INT32 node);
void CL_PrepareDownloadLuaFile(void);
void Got_LuaFile(UINT8 **cp, INT32 playernum);
void Got_LuaFile(const UINT8 **cp, INT32 playernum);
void StoreLuaFileCallback(INT32 id);
void RemoveLuaFileCallback(INT32 id);
void MakePathDirs(char *path);

View file

@ -121,9 +121,6 @@ extern preciptype_t curWeather;
struct skinrecord_t
{
UINT32 wins;
// Purely assistive in gamedata save processes
UINT32 _saveid;
};
struct unloaded_skin_t
@ -561,7 +558,6 @@ struct mapheader_t
mobjtype_t destroyforchallenge[MAXDESTRUCTIBLES]; ///< Assistive for UCRP_MAPDESTROYOBJECTS
UINT8 destroyforchallenge_size; ///< Number for above
UINT32 _saveid; ///< Purely assistive in gamedata save processes
UINT16 cache_spraycan; ///< Cached Spraycan ID
UINT16 cache_maplock; ///< Cached Unlockable ID

View file

@ -2082,7 +2082,7 @@ void F_EndTextPrompt(boolean forceexec, boolean noexec)
// \todo net safety, maybe loop all player thinkers?
if ((promptwasactive || forceexec) && !noexec && promptpostexectag)
{
if (tm.thing) // edge case where starting an invalid prompt immediately on level load will make P_MapStart fail
if (g_tm.thing) // edge case where starting an invalid prompt immediately on level load will make P_MapStart fail
P_LinedefExecute(promptpostexectag, promptmo, NULL);
else
{

View file

@ -334,7 +334,7 @@ void G_ReadDemoExtraData(void)
}
if (extradata & DXD_WEAPONPREF)
{
WeaponPref_Parse(&demobuf.p, p);
demobuf.p += WeaponPref_Parse(demobuf.p, p);
//CONS_Printf("weaponpref is %d for player %d\n", i, p);
}
@ -3060,7 +3060,7 @@ void G_DoPlayDemo(const char *defdemoname)
}
// net var data
CV_LoadDemoVars(&demobuf.p);
demobuf.p += CV_LoadDemoVars(demobuf.p);
memset(&grandprixinfo, 0, sizeof grandprixinfo);
if ((demoflags & DF_GRANDPRIX))
@ -3836,6 +3836,8 @@ boolean G_CheckDemoStatus(void)
D_StartTitle();
}
demo.attract = DEMO_ATTRACT_OFF;
return true;
}

File diff suppressed because it is too large Load diff

730
src/g_gamedata.cpp Normal file
View file

@ -0,0 +1,730 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2024 by Ronald "Eidolon" Kinard
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
#include "g_gamedata.h"
#include <algorithm>
#include <cstdint>
#include <cstddef>
#include <filesystem>
#include <fmt/format.h>
#include "io/streams.hpp"
#include "d_main.h"
#include "m_argv.h"
#include "m_cond.h"
#include "g_game.h"
#include "r_skins.h"
#include "z_zone.h"
namespace fs = std::filesystem;
using json = nlohmann::json;
void srb2::save_ng_gamedata()
{
if (gamedata == NULL || !gamedata->loaded)
return; // If never loaded (-nodata), don't save
gamedata->deferredsave = false;
if (usedCheats)
{
#ifdef DEVELOP
CONS_Alert(CONS_WARNING, M_GetText("Cheats used - Gamedata will not be saved.\n"));
#endif
return;
}
GamedataJson ng {};
ng.playtime.total = gamedata->totalplaytime;
ng.rings.total = gamedata->totalrings;
ng.playtime.tumble = gamedata->totaltumbletime;
ng.rounds.race = gamedata->roundsplayed[GDGT_RACE];
ng.rounds.battle = gamedata->roundsplayed[GDGT_BATTLE];
ng.rounds.prisons = gamedata->roundsplayed[GDGT_PRISONS];
ng.rounds.special = gamedata->roundsplayed[GDGT_SPECIAL];
ng.rounds.custom = gamedata->roundsplayed[GDGT_CUSTOM];
ng.challengekeys.pendingkeyrounds = gamedata->pendingkeyrounds;
ng.challengekeys.pendingkeyroundoffset = gamedata->pendingkeyroundoffset;
ng.challengekeys.keyspending = gamedata->keyspending;
ng.challengekeys.chaokeys = gamedata->chaokeys;
ng.milestones.everloadedaddon = gamedata->everloadedaddon;
ng.milestones.everfinishcredits = gamedata->everfinishedcredits;
ng.milestones.eversavedreplay = gamedata->eversavedreplay;
ng.milestones.everseenspecial = gamedata->everseenspecial;
ng.milestones.chaokeytutorial = gamedata->chaokeytutorial;
ng.milestones.majorkeyskipattempted = gamedata->majorkeyskipattempted;
ng.milestones.finishedtutorialchallenge = gamedata->finishedtutorialchallenge;
ng.milestones.enteredtutorialchallenge = gamedata->enteredtutorialchallenge;
ng.milestones.gonerlevel = gamedata->gonerlevel;
ng.prisons.thisprisoneggpickup = gamedata->thisprisoneggpickup;
ng.prisons.prisoneggstothispickup = gamedata->prisoneggstothispickup;
ng.tafolderhash = quickncasehash(timeattackfolder, 64);
ng.emblems.resize(MAXEMBLEMS, false);
for (int i = 0; i < MAXEMBLEMS; i++)
{
ng.emblems[i] = gamedata->collected[i];
}
ng.unlockables.resize(MAXUNLOCKABLES, false);
for (int i = 0; i < MAXUNLOCKABLES; i++)
{
ng.unlockables[i] = gamedata->unlocked[i];
}
ng.unlockpending.resize(MAXUNLOCKABLES, false);
for (int i = 0; i < MAXUNLOCKABLES; i++)
{
ng.unlockpending[i] = gamedata->unlockpending[i];
}
ng.conditionsets.resize(MAXCONDITIONSETS, false);
for (int i = 0; i < MAXCONDITIONSETS; i++)
{
ng.conditionsets[i] = gamedata->achieved[i];
}
if (gamedata->challengegrid)
{
ng.challengegrid.width = gamedata->challengegridwidth;
ng.challengegrid.grid.resize(gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT, 0);
for (int i = 0; i < gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT; i++)
{
ng.challengegrid.grid[i] = gamedata->challengegrid[i];
}
}
ng.timesBeaten = gamedata->timesBeaten;
for (int i = 0; i < numskins; i++)
{
srb2::GamedataSkinJson skin {};
std::string name = std::string(skins[i].name);
skin.records.wins = skins[i].records.wins;
ng.skins[name] = std::move(skin);
}
for (auto unloadedskin = unloadedskins; unloadedskin; unloadedskin = unloadedskin->next)
{
srb2::GamedataSkinJson skin {};
std::string name = std::string(unloadedskin->name);
skin.records.wins = unloadedskin->records.wins;
ng.skins[name] = std::move(skin);
}
for (int i = 0; i < nummapheaders; i++)
{
srb2::GamedataMapJson map {};
std::string lumpname = std::string(mapheaderinfo[i]->lumpname);
map.visited.visited = mapheaderinfo[i]->records.mapvisited & MV_VISITED;
map.visited.beaten = mapheaderinfo[i]->records.mapvisited & MV_BEATEN;
map.visited.encore = mapheaderinfo[i]->records.mapvisited & MV_ENCORE;
map.visited.spbattack = mapheaderinfo[i]->records.mapvisited & MV_SPBATTACK;
map.visited.mysticmelody = mapheaderinfo[i]->records.mapvisited & MV_MYSTICMELODY;
map.stats.timeattack.besttime = mapheaderinfo[i]->records.timeattack.time;
map.stats.timeattack.bestlap = mapheaderinfo[i]->records.timeattack.lap;
map.stats.spbattack.besttime = mapheaderinfo[i]->records.spbattack.time;
map.stats.spbattack.bestlap = mapheaderinfo[i]->records.spbattack.lap;
ng.maps[lumpname] = std::move(map);
}
for (auto unloadedmap = unloadedmapheaders; unloadedmap; unloadedmap = unloadedmap->next)
{
srb2::GamedataMapJson map {};
std::string lumpname = std::string(unloadedmap->lumpname);
map.visited.visited = unloadedmap->records.mapvisited & MV_VISITED;
map.visited.beaten = unloadedmap->records.mapvisited & MV_BEATEN;
map.visited.encore = unloadedmap->records.mapvisited & MV_ENCORE;
map.visited.spbattack = unloadedmap->records.mapvisited & MV_SPBATTACK;
map.visited.mysticmelody = unloadedmap->records.mapvisited & MV_MYSTICMELODY;
map.stats.timeattack.besttime = unloadedmap->records.timeattack.time;
map.stats.timeattack.bestlap = unloadedmap->records.timeattack.lap;
map.stats.spbattack.besttime = unloadedmap->records.spbattack.time;
map.stats.spbattack.bestlap = unloadedmap->records.spbattack.lap;
ng.maps[lumpname] = std::move(map);
}
for (int i = 0; i < gamedata->numspraycans; i++)
{
srb2::GamedataSprayCanJson spraycan {};
candata_t* can = &gamedata->spraycans[i];
if (can->col >= numskincolors)
{
continue;
}
spraycan.color = std::string(skincolors[can->col].name);
if (can->map == NEXTMAP_INVALID)
{
spraycan.map = "";
ng.spraycans.emplace_back(std::move(spraycan));
continue;
}
if (can->map >= nummapheaders)
{
continue;
}
mapheader_t* mapheader = mapheaderinfo[can->map];
if (!mapheader)
{
continue;
}
spraycan.map = std::string(mapheader->lumpname);
ng.spraycans.emplace_back(std::move(spraycan));
}
for (auto cup = kartcupheaders; cup; cup = cup->next)
{
if (cup->windata[0].best_placement == 0)
{
continue;
}
srb2::GamedataCupJson cupdata {};
cupdata.name = std::string(cup->name);
for (size_t i = 0; i < std::min<size_t>(KARTGP_MAX, cupdata.records.size()); i++)
{
cupdata.records[i].bestgrade = cup->windata[i].best_grade;
cupdata.records[i].bestplacement = cup->windata[i].best_placement;
cupdata.records[i].bestskin = std::string(skins[cup->windata[i].best_skin.id].name);
cupdata.records[i].gotemerald = cup->windata[i].got_emerald;
}
ng.cups[cupdata.name] = std::move(cupdata);
}
for (auto unloadedcup = unloadedcupheaders; unloadedcup; unloadedcup = unloadedcup->next)
{
if (unloadedcup->windata[0].best_placement == 0)
{
continue;
}
srb2::GamedataCupJson cupdata {};
cupdata.name = std::string(unloadedcup->name);
for (int i = 0; i < 4; i++)
{
cupdata.records[i].bestgrade = unloadedcup->windata[i].best_grade;
cupdata.records[i].bestplacement = unloadedcup->windata[i].best_placement;
cupdata.records[i].bestskin = std::string(skins[unloadedcup->windata[i].best_skin.id].name);
cupdata.records[i].gotemerald = unloadedcup->windata[i].got_emerald;
}
ng.cups[cupdata.name] = std::move(cupdata);
}
std::string gamedataname_s {gamedatafilename};
fs::path savepath {fmt::format("{}/{}", srb2home, gamedataname_s)};
fs::path tmpsavepath {fmt::format("{}/{}.tmp", srb2home, gamedataname_s)};
json ngdata_json = ng;
try
{
std::string tmpsavepathstring = tmpsavepath.string();
srb2::io::FileStream file {tmpsavepathstring, srb2::io::FileStreamMode::kWrite};
srb2::io::BufferedOutputStream<srb2::io::FileStream> bos {std::move(file)};
// The header is necessary to validate during loading.
srb2::io::write(static_cast<uint32_t>(0xBA5ED321), bos); // major
srb2::io::write(static_cast<uint8_t>(0), bos); // minor/flags
srb2::io::write(static_cast<uint8_t>(gamedata->evercrashed), bos); // dirty (crash recovery)
std::vector<uint8_t> ubjson = json::to_ubjson(ng);
srb2::io::write_exact(bos, tcb::as_bytes(tcb::make_span(ubjson)));
bos.flush();
file = bos.stream();
file.close();
}
catch (const srb2::io::FileStreamException& ex)
{
CONS_Alert(CONS_ERROR, "NG Gamedata save failed: %s\n", ex.what());
}
catch (...)
{
CONS_Alert(CONS_ERROR, "NG Gamedata save failed\n");
}
try
{
// Now that the save is written successfully, move it over the old save
fs::rename(tmpsavepath, savepath);
}
catch (const fs::filesystem_error& ex)
{
CONS_Alert(CONS_ERROR, "NG Gamedata save succeeded but did not replace old save successfully: %s\n", ex.what());
}
}
// G_SaveGameData
// Saves the main data file, which stores information such as emblems found, etc.
void G_SaveGameData(void)
{
try
{
srb2::save_ng_gamedata();
}
catch (...)
{
CONS_Alert(CONS_ERROR, "Gamedata save failed\n");
return;
}
// Also save profiles here.
PR_SaveProfiles();
#ifdef DEVELOP
CONS_Alert(CONS_NOTICE, M_GetText("Gamedata saved.\n"));
#endif
}
static const char *G_GameDataFolder(void)
{
if (strcmp(srb2home,"."))
return srb2home;
else
return "the Ring Racers folder";
}
void srb2::load_ng_gamedata()
{
// Stop saving, until we successfully load it again.
gamedata->loaded = false;
// Clear things so previously read gamedata doesn't transfer
// to new gamedata
// see also M_EraseDataResponse
G_ClearRecords(); // records
M_ClearStats(); // statistics
M_ClearSecrets(); // emblems, unlocks, maps visited, etc
if (M_CheckParm("-nodata"))
{
// Don't load at all.
// The following used to be in M_ClearSecrets, but that was silly.
M_UpdateUnlockablesAndExtraEmblems(false, true);
M_FinaliseGameData();
gamedata->loaded = true;
return;
}
if (M_CheckParm("-resetdata"))
{
// Don't load, but do save. (essentially, reset)
M_FinaliseGameData();
gamedata->loaded = true;
return;
}
std::string datapath {fmt::format("{}/{}", srb2home, gamedatafilename)};
srb2::io::BufferedInputStream<srb2::io::FileStream> bis;
try
{
srb2::io::FileStream file {datapath, srb2::io::FileStreamMode::kRead };
bis = srb2::io::BufferedInputStream(std::move(file));
}
catch (const srb2::io::FileStreamException& ex)
{
M_FinaliseGameData();
gamedata->loaded = true;
return;
}
uint32_t majorversion;
uint8_t minorversion;
uint8_t dirty;
try
{
majorversion = srb2::io::read_uint32(bis);
minorversion = srb2::io::read_uint8(bis);
dirty = srb2::io::read_uint8(bis);
}
catch (...)
{
CONS_Alert(CONS_ERROR, "Failed to read ng gamedata header\n");
return;
}
if (majorversion != 0xBA5ED321)
{
const char* gdfolder = G_GameDataFolder();
I_Error("Game data is not for Ring Racers v2.0.\nDelete %s (maybe in %s) and try again.", gamedatafilename, gdfolder);
return;
}
std::vector<std::byte> remainder = srb2::io::read_to_vec(bis);
GamedataJson js;
try
{
// safety: std::byte repr is always uint8_t 1-byte aligned
tcb::span<uint8_t> remainder_as_u8 = tcb::span((uint8_t*)remainder.data(), remainder.size());
json parsed = json::from_ubjson(remainder_as_u8);
js = parsed.template get<GamedataJson>();
}
catch (...)
{
const char* gdfolder = G_GameDataFolder();
I_Error("Game data is corrupt.\nDelete %s (maybe in %s) and try again.", gamedatafilename, gdfolder);
return;
}
// Quick & dirty hash for what mod this save file is for.
if (js.tafolderhash != quickncasehash(timeattackfolder, 64))
{
const char* gdfolder = G_GameDataFolder();
I_Error("Game data is corrupt.\nDelete %s (maybe in %s) and try again.", gamedatafilename, gdfolder);
return;
}
// Now we extract the json struct's data and put it into the C-side gamedata.
gamedata->evercrashed = dirty;
gamedata->totalplaytime = js.playtime.total;
gamedata->totalrings = js.rings.total;
gamedata->totaltumbletime = js.playtime.tumble;
gamedata->roundsplayed[GDGT_RACE] = js.rounds.race;
gamedata->roundsplayed[GDGT_BATTLE] = js.rounds.battle;
gamedata->roundsplayed[GDGT_PRISONS] = js.rounds.prisons;
gamedata->roundsplayed[GDGT_SPECIAL] = js.rounds.special;
gamedata->roundsplayed[GDGT_CUSTOM] = js.rounds.custom;
gamedata->pendingkeyrounds = js.challengekeys.pendingkeyrounds;
gamedata->pendingkeyroundoffset = js.challengekeys.pendingkeyroundoffset;
gamedata->keyspending = js.challengekeys.keyspending;
gamedata->chaokeys = js.challengekeys.chaokeys;
gamedata->everloadedaddon = js.milestones.everloadedaddon;
gamedata->everfinishedcredits = js.milestones.everfinishcredits;
gamedata->eversavedreplay = js.milestones.eversavedreplay;
gamedata->everseenspecial = js.milestones.everseenspecial;
gamedata->chaokeytutorial = js.milestones.chaokeytutorial;
gamedata->majorkeyskipattempted = js.milestones.majorkeyskipattempted;
gamedata->finishedtutorialchallenge = js.milestones.finishedtutorialchallenge;
gamedata->enteredtutorialchallenge = js.milestones.enteredtutorialchallenge;
gamedata->gonerlevel = js.milestones.gonerlevel;
gamedata->thisprisoneggpickup = js.prisons.thisprisoneggpickup;
gamedata->prisoneggstothispickup = js.prisons.prisoneggstothispickup;
size_t emblems_size = js.emblems.size();
for (size_t i = 0; i < std::min((size_t)MAXEMBLEMS, emblems_size); i++)
{
gamedata->collected[i] = js.emblems[i];
}
size_t unlocks_size = js.unlockables.size();
for (size_t i = 0; i < std::min((size_t)MAXUNLOCKABLES, unlocks_size); i++)
{
gamedata->unlocked[i] = js.unlockables[i];
}
size_t pending_unlocks_size = js.unlockpending.size();
for (size_t i = 0; i < std::min((size_t)MAXUNLOCKABLES, pending_unlocks_size); i++)
{
gamedata->unlockpending[i] = js.unlockpending[i];
}
size_t conditions_size = js.conditionsets.size();
for (size_t i = 0; i < std::min((size_t)MAXCONDITIONSETS, conditions_size); i++)
{
gamedata->achieved[i] = js.conditionsets[i];
}
if (M_CheckParm("-resetchallengegrid"))
{
gamedata->challengegridwidth = 0;
if (gamedata->challengegrid)
{
Z_Free(gamedata->challengegrid);
gamedata->challengegrid = nullptr;
}
}
else
{
gamedata->challengegridwidth = std::min(js.challengegrid.width, (uint32_t)0);
if (gamedata->challengegrid)
{
Z_Free(gamedata->challengegrid);
gamedata->challengegrid = nullptr;
}
if (gamedata->challengegridwidth)
{
gamedata->challengegrid = static_cast<uint16_t*>(Z_Malloc(
(gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT16)),
PU_STATIC, NULL));
for (size_t i = 0; i < std::min((size_t)(gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT), js.challengegrid.grid.size()); i++)
{
int16_t gridvalue = js.challengegrid.grid[i];
if (gridvalue < 0)
{
gamedata->challengegrid[i] = MAXUNLOCKABLES;
}
else
{
gamedata->challengegrid[i] = static_cast<uint8_t>(gridvalue);
}
}
M_SanitiseChallengeGrid();
}
else
{
gamedata->challengegrid = nullptr;
}
}
gamedata->timesBeaten = js.timesBeaten;
// Main records
for (auto& skinpair : js.skins)
{
INT32 skin = R_SkinAvailable(skinpair.first.c_str());
skinrecord_t dummyrecord {};
dummyrecord.wins = skinpair.second.records.wins;
if (skin != -1)
{
skins[skin].records = dummyrecord;
}
else if (dummyrecord.wins)
{
// Invalid, but we don't want to lose all the juicy statistics.
// Instead, update a FILO linked list of "unloaded skins".
unloaded_skin_t *unloadedskin =
static_cast<unloaded_skin_t*>(Z_Malloc(
sizeof(unloaded_skin_t),
PU_STATIC, NULL
));
// Establish properties, for later retrieval on file add.
strlcpy(unloadedskin->name, skinpair.first.c_str(), sizeof(unloadedskin->name));
unloadedskin->namehash = quickncasehash(unloadedskin->name, SKINNAMESIZE);
// Insert at the head, just because it's convenient.
unloadedskin->next = unloadedskins;
unloadedskins = unloadedskin;
// Finally, copy into.
unloadedskin->records = dummyrecord;
}
}
for (auto& mappair : js.maps)
{
UINT16 mapnum = G_MapNumber(mappair.first.c_str());
recorddata_t dummyrecord {};
dummyrecord.mapvisited |= mappair.second.visited.visited ? MV_VISITED : 0;
dummyrecord.mapvisited |= mappair.second.visited.beaten ? MV_BEATEN : 0;
dummyrecord.mapvisited |= mappair.second.visited.encore ? MV_ENCORE : 0;
dummyrecord.mapvisited |= mappair.second.visited.spbattack ? MV_SPBATTACK : 0;
dummyrecord.mapvisited |= mappair.second.visited.mysticmelody ? MV_SPBATTACK : 0;
dummyrecord.timeattack.time = mappair.second.stats.timeattack.besttime;
dummyrecord.timeattack.lap = mappair.second.stats.timeattack.bestlap;
dummyrecord.spbattack.time = mappair.second.stats.spbattack.besttime;
dummyrecord.spbattack.lap = mappair.second.stats.spbattack.bestlap;
if (mapnum < nummapheaders && mapheaderinfo[mapnum])
{
// Valid mapheader, time to populate with record data.
mapheaderinfo[mapnum]->records = dummyrecord;
}
else if (dummyrecord.mapvisited & MV_BEATEN
|| dummyrecord.timeattack.time != 0 || dummyrecord.timeattack.lap != 0
|| dummyrecord.spbattack.time != 0 || dummyrecord.spbattack.lap != 0)
{
// Invalid, but we don't want to lose all the juicy statistics.
// Instead, update a FILO linked list of "unloaded mapheaders".
unloaded_mapheader_t *unloadedmap =
static_cast<unloaded_mapheader_t*>(Z_Malloc(
sizeof(unloaded_mapheader_t),
PU_STATIC, NULL
));
// Establish properties, for later retrieval on file add.
unloadedmap->lumpname = Z_StrDup(mappair.first.c_str());
unloadedmap->lumpnamehash = quickncasehash(unloadedmap->lumpname, MAXMAPLUMPNAME);
// Insert at the head, just because it's convenient.
unloadedmap->next = unloadedmapheaders;
unloadedmapheaders = unloadedmap;
// Finally, copy into.
unloadedmap->records = dummyrecord;
}
}
gamedata->gotspraycans = 0;
gamedata->numspraycans = js.spraycans.size();
if (gamedata->spraycans)
{
Z_Free(gamedata->spraycans);
}
if (gamedata->numspraycans)
{
gamedata->spraycans = static_cast<candata_t*>(Z_Malloc(
(gamedata->numspraycans * sizeof(candata_t)),
PU_STATIC, NULL));
for (size_t i = 0; i < gamedata->numspraycans; i++)
{
auto& can = js.spraycans[i];
// Find the skin color index for the name
bool foundcolor = false;
for (size_t j = 0; j < numskincolors; j++)
{
if (can.color == skincolors[j].name)
{
gamedata->spraycans[i].col = j;
foundcolor = true;
break;
}
}
if (!foundcolor)
{
// Invalid color name? Ignore the spraycan
gamedata->numspraycans -= 1;
i -= 1;
continue;
}
gamedata->spraycans[i].map = NEXTMAP_INVALID;
UINT16 mapnum = NEXTMAP_INVALID;
if (!can.map.empty())
{
mapnum = G_MapNumber(can.map.c_str());
}
gamedata->spraycans[i].map = mapnum;
if (mapnum >= nummapheaders)
{
// Can has not been grabbed on any map, this is intentional.
continue;
}
if (gamedata->gotspraycans != i)
{
//CONS_Printf("LOAD - Swapping gotten can %u, color %s with prior ungotten can %u\n", i, skincolors[col].name, gamedata->gotspraycans);
// All grabbed cans should be at the head of the list.
// Let's swap with the can the disjoint occoured at.
// This will prevent a gap from occouring on reload.
candata_t copycan = gamedata->spraycans[gamedata->gotspraycans];
gamedata->spraycans[gamedata->gotspraycans] = gamedata->spraycans[i];
gamedata->spraycans[i] = copycan;
mapheaderinfo[copycan.map]->cache_spraycan = i;
}
mapheaderinfo[mapnum]->cache_spraycan = gamedata->gotspraycans;
gamedata->gotspraycans++;
}
}
else
{
gamedata->spraycans = nullptr;
}
for (auto& cuppair : js.cups)
{
std::array<cupwindata_t, KARTGP_MAX> dummywindata {{}};
cupheader_t* cup = nullptr;
// Find the loaded cup
for (cup = kartcupheaders; cup; cup = cup->next)
{
std::string cupname = std::string(cup->name);
if (cupname == cuppair.first)
{
break;
}
}
// Digest its data...
for (size_t j = 0; j < std::min<size_t>(KARTGP_MAX, cuppair.second.records.size()); j++)
{
dummywindata[j].best_placement = cuppair.second.records[j].bestplacement;
dummywindata[j].best_grade = static_cast<gp_rank_e>(cuppair.second.records[j].bestgrade);
dummywindata[j].got_emerald = cuppair.second.records[j].gotemerald;
dummywindata[j].best_skin.id = MAXSKINS;
dummywindata[j].best_skin.unloaded = nullptr;
bool skinfound = false;
for (int skin = 0; skin < numskins; skin++)
{
std::string skinname = std::string(skins[skin].name);
if (skinname == cuppair.second.records[j].bestskin)
{
skinreference_t ref {};
ref.id = skin;
ref.unloaded = nullptr;
dummywindata[j].best_skin = ref;
skinfound = true;
break;
}
}
if (skinfound)
{
continue;
}
for (auto unloadedskin = unloadedskins; unloadedskin; unloadedskin = unloadedskin->next)
{
std::string skinname = std::string(unloadedskin->name);
if (skinname == cuppair.second.records[j].bestskin)
{
skinreference_t ref {};
ref.id = MAXSKINS;
ref.unloaded = unloadedskin;
dummywindata[j].best_skin = ref;
skinfound = true;
break;
}
}
}
if (cup)
{
// We found a cup, so assign the windata.
memcpy(cup->windata, dummywindata.data(), sizeof(cup->windata));
}
else if (dummywindata[0].best_placement != 0)
{
// Invalid, but we don't want to lose all the juicy statistics.
// Instead, update a FILO linked list of "unloaded cupheaders".
unloaded_cupheader_t *unloadedcup =
static_cast<unloaded_cupheader_t*>(Z_Malloc(
sizeof(unloaded_cupheader_t),
PU_STATIC, NULL
));
// Establish properties, for later retrieval on file add.
strlcpy(unloadedcup->name, cuppair.first.c_str(), sizeof(unloadedcup->name));
unloadedcup->namehash = quickncasehash(unloadedcup->name, MAXCUPNAME);
// Insert at the head, just because it's convenient.
unloadedcup->next = unloadedcupheaders;
unloadedcupheaders = unloadedcup;
// Finally, copy into.
memcpy(unloadedcup->windata, dummywindata.data(), sizeof(cup->windata));
}
}
M_FinaliseGameData();
}
// G_LoadGameData
// Loads the main data file, which stores information such as emblems found, etc.
void G_LoadGameData(void)
{
try
{
srb2::load_ng_gamedata();
}
catch (...)
{
CONS_Alert(CONS_ERROR, "NG Gamedata loading failed\n");
}
}

245
src/g_gamedata.h Normal file
View file

@ -0,0 +1,245 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2024 by Ronald "Eidolon" Kinard
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
#ifndef SRB2_G_GAMEDATA_H
#define SRB2_G_GAMEDATA_H
#ifdef __cplusplus
#include <array>
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
#include <nlohmann/json.hpp>
namespace srb2
{
struct GamedataPlaytimeJson final
{
uint32_t total;
uint32_t tumble;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataPlaytimeJson, total, tumble)
};
struct GamedataRingsJson final
{
uint32_t total;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataRingsJson, total)
};
struct GamedataRoundsJson final
{
uint32_t race;
uint32_t battle;
uint32_t prisons;
uint32_t special;
uint32_t custom;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataRoundsJson, race, battle, prisons, special, custom)
};
struct GamedataChallengeKeysJson final
{
uint32_t pendingkeyrounds;
uint8_t pendingkeyroundoffset;
uint16_t keyspending;
uint16_t chaokeys;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataChallengeKeysJson, pendingkeyrounds, pendingkeyroundoffset, keyspending, chaokeys)
};
struct GamedataMilestonesJson final
{
uint32_t gonerlevel;
bool everloadedaddon;
bool everfinishcredits;
bool eversavedreplay;
bool everseenspecial;
bool chaokeytutorial;
bool majorkeyskipattempted;
bool finishedtutorialchallenge;
bool enteredtutorialchallenge;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
GamedataMilestonesJson,
gonerlevel,
everloadedaddon,
everfinishcredits,
eversavedreplay,
everseenspecial,
chaokeytutorial,
majorkeyskipattempted,
finishedtutorialchallenge,
enteredtutorialchallenge
)
};
struct GamedataPrisonEggPickupsJson final
{
uint16_t thisprisoneggpickup;
uint16_t prisoneggstothispickup;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataPrisonEggPickupsJson, thisprisoneggpickup, prisoneggstothispickup)
};
struct GamedataChallengeGridJson final
{
uint32_t width;
std::vector<int16_t> grid;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataChallengeGridJson, width, grid)
};
struct GamedataSkinRecordsJson final
{
uint32_t wins;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSkinRecordsJson, wins)
};
struct GamedataSkinJson final
{
GamedataSkinRecordsJson records;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSkinJson, records)
};
struct GamedataMapVisitedJson final
{
bool visited;
bool beaten;
bool encore;
bool spbattack;
bool mysticmelody;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapVisitedJson, visited, beaten, encore, spbattack, mysticmelody)
};
struct GamedataMapStatsTimeAttackJson final
{
uint32_t besttime;
uint32_t bestlap;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapStatsTimeAttackJson, besttime, bestlap)
};
struct GamedataMapStatsSpbAttackJson final
{
uint32_t besttime;
uint32_t bestlap;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapStatsSpbAttackJson, besttime, bestlap)
};
struct GamedataMapStatsJson final
{
GamedataMapStatsTimeAttackJson timeattack;
GamedataMapStatsSpbAttackJson spbattack;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapStatsJson, timeattack, spbattack)
};
struct GamedataMapJson final
{
GamedataMapVisitedJson visited;
GamedataMapStatsJson stats;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapJson, visited, stats)
};
struct GamedataSprayCanJson final
{
std::string map;
std::string color;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSprayCanJson, map, color)
};
struct GamedataCupRecordsJson final
{
uint8_t bestplacement;
uint8_t bestgrade;
bool gotemerald;
std::string bestskin;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataCupRecordsJson, bestplacement, bestgrade, gotemerald, bestskin)
};
struct GamedataCupJson final
{
std::string name;
std::vector<GamedataCupRecordsJson> records;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataCupJson, name, records)
};
struct GamedataJson final
{
GamedataPlaytimeJson playtime;
GamedataRingsJson rings;
GamedataRoundsJson rounds;
GamedataChallengeKeysJson challengekeys;
GamedataMilestonesJson milestones;
GamedataPrisonEggPickupsJson prisons;
uint32_t tafolderhash;
std::vector<bool> emblems;
std::vector<bool> unlockables;
std::vector<bool> unlockpending;
std::vector<bool> conditionsets;
GamedataChallengeGridJson challengegrid;
uint32_t timesBeaten;
std::unordered_map<std::string, GamedataSkinJson> skins;
std::unordered_map<std::string, GamedataMapJson> maps;
std::vector<GamedataSprayCanJson> spraycans;
std::unordered_map<std::string, GamedataCupJson> cups;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
GamedataJson,
playtime,
rings,
rounds,
challengekeys,
milestones,
prisons,
tafolderhash,
emblems,
unlockables,
unlockpending,
conditionsets,
challengegrid,
timesBeaten,
skins,
maps,
spraycans,
cups
)
};
void save_ng_gamedata(void);
void load_ng_gamedata(void);
}
extern "C"
{
#endif // __cplusplus
void G_SaveGameData(void);
void G_LoadGameData(void);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // SRB2_G_GAMEDATA_H

View file

@ -143,6 +143,7 @@ enum
SHADER_FLOOR,
SHADER_WALL,
SHADER_SPRITE,
SHADER_SPRITECLIPHACK,
SHADER_MODEL, SHADER_MODEL_LIGHTING,
SHADER_WATER,
SHADER_FOG,

View file

@ -3423,7 +3423,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
if (HWR_UseShader())
{
shader = SHADER_SPRITE;
shader = (R_ThingIsPaperSprite(spr->mobj) || R_ThingIsFloorSprite(spr->mobj)) ? SHADER_SPRITE : SHADER_SPRITECLIPHACK;
blend |= PF_ColorMapped;
}
@ -3916,7 +3916,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
if (HWR_UseShader())
{
shader = SHADER_SPRITE;
shader = (R_ThingIsPaperSprite(spr->mobj) || R_ThingIsFloorSprite(spr->mobj)) ? SHADER_SPRITE : SHADER_SPRITECLIPHACK;;
blend |= PF_ColorMapped;
}

View file

@ -922,6 +922,9 @@ static struct {
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
// Sprite shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
// Sprite clipping hack shader (for pulling things out of the floor)
{GLSL_SPRITECLIP_HACK_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
// Model shader

View file

@ -183,7 +183,7 @@ static void Command_Sayto_f(void);
static void Command_Sayteam_f(void);
static void Command_CSay_f(void);
static void Command_Shout(void);
static void Got_Saycmd(UINT8 **p, INT32 playernum);
static void Got_Saycmd(const UINT8 **p, INT32 playernum);
void HU_LoadGraphics(void)
{
@ -660,11 +660,12 @@ static void Command_Shout(void)
* \sa DoSayPacket
* \author Graue <graue@oceanbase.org>
*/
static void Got_Saycmd(UINT8 **p, INT32 playernum)
static void Got_Saycmd(const UINT8 **p, INT32 playernum)
{
SINT8 target;
UINT8 flags;
const char *dispname;
char buf[HU_MAXMSGLEN + 1];
char *msg;
boolean action = false;
char *ptr;
@ -678,8 +679,8 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
target = READSINT8(*p);
flags = READUINT8(*p);
playernum = READUINT8(*p);
msg = (char *)*p;
SKIPSTRINGL(*p, HU_MAXMSGLEN + 1);
msg = buf;
READSTRINGL(*p, msg, HU_MAXMSGLEN + 1);
//check for invalid characters (0x80 or above)
{

View file

@ -20954,7 +20954,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_RUNSPAWNFUNC|MF_NOGRAVITY, // flags
MF_RUNSPAWNFUNC|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
{ // MT_SSCANDLE_SIDE
@ -20980,7 +20980,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_NOGRAVITY|MF_NOTHINK, // flags
MF_SCENERY|MF_NOGRAVITY|MF_NOTHINK|MF_DRAWFROMFARAWAY, // flags
S_NULL // raisestate
},
{ // MT_SSCANDLE_FLAME
@ -21006,7 +21006,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_NOGRAVITY, // flags
MF_SCENERY|MF_NOGRAVITY|MF_DRAWFROMFARAWAY, // flags
S_NULL // raisestate
},
{ // MT_SS_HOLOGRAM

View file

@ -9,5 +9,160 @@
#include "streams.hpp"
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <stdexcept>
template class srb2::io::ZlibInputStream<srb2::io::SpanStream>;
template class srb2::io::ZlibInputStream<srb2::io::VecStream>;
template class srb2::io::BufferedOutputStream<srb2::io::FileStream>;
template class srb2::io::BufferedInputStream<srb2::io::FileStream>;
using namespace srb2::io;
static FileStreamException make_exception_from_errno(int err)
{
char* errnostr = strerror(err);
return FileStreamException(std::string(errnostr));
}
FileStreamException::FileStreamException(const char* msg) : msg_(msg) {}
FileStreamException::FileStreamException(const std::string& msg) : msg_(msg) {}
FileStreamException::FileStreamException(const FileStreamException&) = default;
FileStreamException::FileStreamException(FileStreamException&& r) noexcept = default;
FileStreamException::~FileStreamException() = default;
FileStreamException& FileStreamException::operator=(const FileStreamException&) = default;
FileStreamException& FileStreamException::operator=(FileStreamException&&) noexcept = default;
const char* FileStreamException::what() const noexcept
{
return msg_.c_str();
}
FileStream::FileStream() noexcept = default;
FileStream::FileStream(FileStream&& r) noexcept
{
*this = std::move(r);
};
FileStream::~FileStream()
{
if (file_)
{
// We don't care about the result. Exceptions can't be thrown in destructors.
std::fclose((std::FILE*)(this->file_));
}
file_ = nullptr;
}
FileStream::FileStream(const std::string& path, FileStreamMode mode) : file_(nullptr), mode_(mode)
{
const char* fopenmode = "r";
switch (mode_)
{
case FileStreamMode::kRead:
fopenmode = "rb";
break;
case FileStreamMode::kWrite:
fopenmode = "wb";
break;
case FileStreamMode::kAppend:
fopenmode = "ab";
break;
default:
throw std::invalid_argument("file stream mode unsupported");
}
std::FILE* file = std::fopen(path.c_str(), fopenmode);
if (file == nullptr)
{
int err = errno;
throw make_exception_from_errno(err);
}
// We want raw, unbuffered IO for the stream. Buffering can be layered on top.
if (std::setvbuf(file, nullptr, _IONBF, 0) != 0)
{
int err = errno;
throw make_exception_from_errno(err);
}
this->file_ = (void*) file;
}
FileStream& FileStream::operator=(FileStream&& r) noexcept
{
file_ = r.file_;
r.file_ = nullptr;
mode_ = r.mode_;
return *this;
};
StreamSize FileStream::read(tcb::span<std::byte> buffer)
{
if (this->file_ == nullptr)
{
throw std::domain_error("FileStream is empty");
}
if (this->mode_ != FileStreamMode::kRead)
{
throw std::domain_error("FileStream is not in read mode");
}
void* cbuf = (void*)(buffer.data());
std::size_t cbufsize = buffer.size_bytes();
std::size_t bytesread = fread(cbuf, 1, cbufsize, (std::FILE*)(this->file_));
if (std::ferror((std::FILE*)(this->file_)) != 0)
{
int err = errno;
throw make_exception_from_errno(err);
}
return bytesread;
}
StreamSize FileStream::write(tcb::span<const std::byte> buffer)
{
if (this->file_ == nullptr)
{
throw std::domain_error("FileStream is empty");
}
if (this->mode_ == FileStreamMode::kRead)
{
throw std::domain_error("FileStream is not in writable mode");
}
void* cbuf = (void*)(buffer.data());
std::size_t cbufsize = buffer.size_bytes();
std::size_t byteswritten = std::fwrite(cbuf, 1, cbufsize, (std::FILE*)(this->file_));
if (std::ferror((std::FILE*)(this->file_)) != 0)
{
int err = errno;
throw make_exception_from_errno(err);
}
return byteswritten;
}
void FileStream::close()
{
if (!file_)
{
return;
}
if (std::fclose((std::FILE*)(this->file_)) != 0)
{
// The FILE is now invalid even though fclose failed.
// There is nothing we can do but abandon the pointer.
file_ = nullptr;
int err = errno;
throw make_exception_from_errno(err);
}
file_ = nullptr;
}

View file

@ -47,6 +47,9 @@ struct IsSeekableStream
: public std::is_same<decltype(std::declval<T&>().seek(std::declval<SeekFrom>(), std::declval<StreamOffset>())),
StreamSize> {};
template <typename T>
struct IsFlushableStream : public std::is_same<decltype(std::declval<T&>().flush()), void> {};
template <typename T>
struct IsStream : public std::disjunction<IsInputStream<T>, IsOutputStream<T>> {};
@ -60,6 +63,8 @@ inline constexpr const bool IsOutputStreamV = IsOutputStream<T>::value;
template <typename T>
inline constexpr const bool IsSeekableStreamV = IsSeekableStream<T>::value;
template <typename T>
inline constexpr const bool IsFlushableStreamV = IsFlushableStream<T>::value;
template <typename T>
inline constexpr const bool IsStreamV = IsStream<T>::value;
template <typename T>
inline constexpr const bool IsInputOutputStreamV = IsInputOutputStream<T>::value;
@ -570,6 +575,54 @@ inline void read_exact(VecStream& stream, tcb::span<std::byte> buffer)
std::copy(copy_begin, copy_end, buffer.begin());
}
enum class FileStreamMode
{
kRead,
kWrite,
kAppend,
};
class FileStreamException final : public std::exception
{
std::string msg_;
public:
explicit FileStreamException(const char* msg);
explicit FileStreamException(const std::string& msg);
FileStreamException(const FileStreamException&);
FileStreamException(FileStreamException&&) noexcept;
~FileStreamException();
FileStreamException& operator=(const FileStreamException&);
FileStreamException& operator=(FileStreamException&&) noexcept;
virtual const char* what() const noexcept override;
};
class FileStream final
{
void* file_ = nullptr; // Type is omitted to avoid include cstdio
FileStreamMode mode_;
public:
FileStream() noexcept;
FileStream(const FileStream&) = delete;
FileStream(FileStream&&) noexcept;
FileStream(const std::string& path, FileStreamMode mode = FileStreamMode::kRead);
~FileStream();
FileStream& operator=(const FileStream&) = delete;
FileStream& operator=(FileStream&&) noexcept;
StreamSize read(tcb::span<std::byte> buffer);
StreamSize write(tcb::span<const std::byte> buffer);
// not bothering with seeking for now -- apparently 64-bit file positions is not available in ansi c
// StreamSize seek(SeekFrom seek_from, StreamOffset offset);
void close();
};
class ZlibException : public std::exception {
int err_ {0};
std::string msg_;
@ -759,6 +812,124 @@ private:
}
};
extern template class ZlibInputStream<SpanStream>;
extern template class ZlibInputStream<VecStream>;
template <typename O,
typename std::enable_if_t<IsOutputStreamV<O> && std::is_move_constructible_v<O> &&
std::is_move_assignable_v<O>>* = nullptr>
class BufferedOutputStream final
{
O inner_;
std::vector<std::byte> buf_;
tcb::span<const std::byte>::size_type cap_;
public:
explicit BufferedOutputStream(O&& o) : inner_(std::forward<O>(o)), buf_(), cap_(8192) {}
BufferedOutputStream(O&& o, tcb::span<const std::byte>::size_type capacity) : inner_(std::forward<O>(o)), buf_(), cap_(capacity) {}
BufferedOutputStream(const BufferedOutputStream&) = delete;
BufferedOutputStream(BufferedOutputStream&&) = default;
~BufferedOutputStream() = default;
BufferedOutputStream& operator=(const BufferedOutputStream&) = delete;
BufferedOutputStream& operator=(BufferedOutputStream&&) = default;
StreamSize write(tcb::span<const std::byte> buffer)
{
StreamSize totalwritten = 0;
while (buffer.size() > 0)
{
std::size_t tocopy = std::min(std::min(cap_, cap_ - buf_.size()), buffer.size());
tcb::span<const std::byte> copy_slice = buffer.subspan(0, tocopy);
buf_.reserve(cap_);
buf_.insert(buf_.end(), copy_slice.begin(), copy_slice.end());
flush();
totalwritten += copy_slice.size();
buffer = buffer.subspan(tocopy);
}
return totalwritten;
}
void flush()
{
tcb::span<const std::byte> writebuf = tcb::make_span(buf_);
write_exact(inner_, writebuf);
buf_.resize(0);
}
O&& stream() noexcept
{
return std::move(inner_);
}
};
extern template class BufferedOutputStream<FileStream>;
template <typename I,
typename std::enable_if_t<IsInputStreamV<I> && std::is_move_constructible_v<I> &&
std::is_move_assignable_v<I>>* = nullptr>
class BufferedInputStream final
{
I inner_;
std::vector<std::byte> buf_;
tcb::span<std::byte>::size_type cap_;
public:
template <typename std::enable_if_t<std::is_default_constructible_v<I>>* = nullptr>
BufferedInputStream() : inner_(), buf_(), cap_(8192) {}
explicit BufferedInputStream(I&& i) : inner_(std::forward<I>(i)), buf_(), cap_(8192) {}
BufferedInputStream(I&& i, tcb::span<std::byte>::size_type capacity) : inner_(std::forward<I>(i)), buf_(), cap_(capacity) {}
BufferedInputStream(const BufferedInputStream&) = delete;
BufferedInputStream(BufferedInputStream&&) = default;
~BufferedInputStream() = default;
BufferedInputStream& operator=(const BufferedInputStream&) = delete;
BufferedInputStream& operator=(BufferedInputStream&&) = default;
StreamSize read(tcb::span<std::byte> buffer)
{
StreamSize totalread = 0;
buf_.reserve(cap_);
while (buffer.size() > 0)
{
std::size_t toread = cap_ - buf_.size();
std::size_t prereadsize = buf_.size();
buf_.resize(prereadsize + toread);
tcb::span<std::byte> readspan{buf_.data() + prereadsize, buf_.data() + prereadsize + toread};
StreamSize bytesread = inner_.read(readspan);
buf_.resize(prereadsize + bytesread);
StreamSize tocopyfrombuf = std::min(buffer.size(), buf_.size());
std::copy(buf_.begin(), std::next(buf_.begin(), tocopyfrombuf), buffer.begin());
buffer = buffer.subspan(tocopyfrombuf);
totalread += tocopyfrombuf;
// Move the remaining buffer backwards
std::size_t bufremaining = buf_.size() - tocopyfrombuf;
std::move(std::next(buf_.begin(), tocopyfrombuf), buf_.end(), buf_.begin());
buf_.resize(bufremaining);
// If we read 0 bytes from the stream, assume the inner stream won't return more for a while.
// The caller can read in a loop if it must (i.e. read_exact)
if (bytesread == 0)
{
break;
}
}
return totalread;
}
I&& stream() noexcept
{
return std::move(inner_);
}
};
extern template class BufferedInputStream<FileStream>;
// Utility functions
template <typename I, typename O>

View file

@ -793,12 +793,12 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2)
{
// Counter desyncs
/*mobj_t *oldthing = thing;
mobj_t *oldtm.thing = tm.thing;
mobj_t *oldg_tm.thing = g_tm.thing;
P_Thrust(tm.thing, R_PointToAngle2(thing->x, thing->y, tm.thing->x, tm.thing->y), 4*thing->scale);
P_Thrust(g_tm.thing, R_PointToAngle2(thing->x, thing->y, g_tm.thing->x, g_tm.thing->y), 4*thing->scale);
thing = oldthing;
P_SetTarget(&tm.thing, oldtm.thing);*/
P_SetTarget(&g_tm.thing, oldg_tm.thing);*/
if (K_KartBouncing(t2, t1->target) == true)
{

View file

@ -5874,6 +5874,7 @@ static void K_DrawMessageFeed(void)
Draw::TextElement text(submsg);
text.font(Draw::Font::kMenu);
submsg = text.parse(submsg).string();
UINT8 x = 160;
UINT8 y = 10;
@ -5899,7 +5900,7 @@ static void K_DrawMessageFeed(void)
if (i >= 1)
y += BASEVIDHEIGHT / 2;
}
UINT8 sw = text.width();
UINT16 sw = text.width();
K_DrawSticker(x - sw/2, y, sw, 0, true);
Draw(x, y+shift).align(Draw::Align::kCenter).text(text);

View file

@ -233,7 +233,7 @@ void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable)
}
/*--------------------------------------------------
static void Got_CallZVote(UINT8 **cp, INT32 playernum)
static void Got_CallZVote(const UINT8 **cp, INT32 playernum)
Callback function for XD_CALLZVOTE NetXCmd.
Attempts to start a new vote using K_InitNewMidVote.
@ -245,7 +245,7 @@ void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable)
Return:-
N/A
--------------------------------------------------*/
static void Got_CallZVote(UINT8 **cp, INT32 playernum)
static void Got_CallZVote(const UINT8 **cp, INT32 playernum)
{
midVoteType_e type = MVT__MAX;
INT32 variable = 0;
@ -288,7 +288,7 @@ static void K_PlayerSendMidVote(const UINT8 id)
}
/*--------------------------------------------------
static void Got_SetZVote(UINT8 **cp, INT32 playernum)
static void Got_SetZVote(const UINT8 **cp, INT32 playernum)
Callback function for XD_SETZVOTE NetXCmd.
Updates the vote table.
@ -300,7 +300,7 @@ static void K_PlayerSendMidVote(const UINT8 id)
Return:-
N/A
--------------------------------------------------*/
static void Got_SetZVote(UINT8 **cp, INT32 playernum)
static void Got_SetZVote(const UINT8 **cp, INT32 playernum)
{
(void)cp;

View file

@ -986,7 +986,7 @@ static int lib_pRemoveFloorSpriteSlope(lua_State *L)
static int lib_pRailThinker(lua_State *L)
{
mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
tm_t ptm = tm;
tm_t ptm = g_tm;
NOHUD
INLEVEL
if (!mobj)
@ -999,7 +999,7 @@ static int lib_pRailThinker(lua_State *L)
static int lib_pXYMovement(lua_State *L)
{
mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
tm_t ptm = tm;
tm_t ptm = g_tm;
NOHUD
INLEVEL
if (!actor)
@ -1012,7 +1012,7 @@ static int lib_pXYMovement(lua_State *L)
static int lib_pRingXYMovement(lua_State *L)
{
mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
tm_t ptm = tm;
tm_t ptm = g_tm;
NOHUD
INLEVEL
if (!actor)
@ -1025,7 +1025,7 @@ static int lib_pRingXYMovement(lua_State *L)
static int lib_pSceneryXYMovement(lua_State *L)
{
mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
tm_t ptm = tm;
tm_t ptm = g_tm;
NOHUD
INLEVEL
if (!actor)
@ -1038,7 +1038,7 @@ static int lib_pSceneryXYMovement(lua_State *L)
static int lib_pZMovement(lua_State *L)
{
mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
tm_t ptm = tm;
tm_t ptm = g_tm;
NOHUD
INLEVEL
if (!actor)
@ -1052,7 +1052,7 @@ static int lib_pZMovement(lua_State *L)
static int lib_pRingZMovement(lua_State *L)
{
mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
tm_t ptm = tm;
tm_t ptm = g_tm;
NOHUD
INLEVEL
if (!actor)
@ -1066,7 +1066,7 @@ static int lib_pRingZMovement(lua_State *L)
static int lib_pSceneryZMovement(lua_State *L)
{
mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
tm_t ptm = tm;
tm_t ptm = g_tm;
NOHUD
INLEVEL
if (!actor)
@ -1080,7 +1080,7 @@ static int lib_pSceneryZMovement(lua_State *L)
static int lib_pPlayerZMovement(lua_State *L)
{
mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
tm_t ptm = tm;
tm_t ptm = g_tm;
NOHUD
INLEVEL
if (!actor)
@ -1235,7 +1235,7 @@ static int lib_pGivePlayerLives(lua_State *L)
static int lib_pMovePlayer(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
tm_t ptm = tm;
tm_t ptm = g_tm;
NOHUD
INLEVEL
if (!player)
@ -1314,7 +1314,7 @@ static int lib_pNukeEnemies(lua_State *L)
static int lib_pCheckPosition(lua_State *L)
{
tm_t ptm = tm;
tm_t ptm = g_tm;
mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
fixed_t x = luaL_checkfixed(L, 2);
fixed_t y = luaL_checkfixed(L, 3);
@ -1323,14 +1323,14 @@ static int lib_pCheckPosition(lua_State *L)
if (!thing)
return LUA_ErrInvalid(L, "mobj_t");
lua_pushboolean(L, P_CheckPosition(thing, x, y, NULL));
LUA_PushUserdata(L, tm.thing, META_MOBJ);
LUA_PushUserdata(L, g_tm.thing, META_MOBJ);
P_RestoreTMStruct(ptm);
return 2;
}
static int lib_pTryMove(lua_State *L)
{
tm_t ptm = tm;
tm_t ptm = g_tm;
mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
fixed_t x = luaL_checkfixed(L, 2);
fixed_t y = luaL_checkfixed(L, 3);
@ -1340,14 +1340,14 @@ static int lib_pTryMove(lua_State *L)
if (!thing)
return LUA_ErrInvalid(L, "mobj_t");
lua_pushboolean(L, P_TryMove(thing, x, y, allowdropoff, NULL));
LUA_PushUserdata(L, tm.thing, META_MOBJ);
LUA_PushUserdata(L, g_tm.thing, META_MOBJ);
P_RestoreTMStruct(ptm);
return 2;
}
static int lib_pMove(lua_State *L)
{
tm_t ptm = tm;
tm_t ptm = g_tm;
mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
fixed_t speed = luaL_checkfixed(L, 2);
NOHUD
@ -1355,14 +1355,14 @@ static int lib_pMove(lua_State *L)
if (!actor)
return LUA_ErrInvalid(L, "mobj_t");
lua_pushboolean(L, P_Move(actor, speed));
LUA_PushUserdata(L, tm.thing, META_MOBJ);
LUA_PushUserdata(L, g_tm.thing, META_MOBJ);
P_RestoreTMStruct(ptm);
return 2;
}
static int lib_pTeleportMove(lua_State *L)
{
tm_t ptm = tm;
tm_t ptm = g_tm;
mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
fixed_t x = luaL_checkfixed(L, 2);
fixed_t y = luaL_checkfixed(L, 3);
@ -1373,14 +1373,14 @@ static int lib_pTeleportMove(lua_State *L)
return LUA_ErrInvalid(L, "mobj_t");
LUA_Deprecated(L, "P_TeleportMove", "P_SetOrigin\" or \"P_MoveOrigin");
lua_pushboolean(L, P_MoveOrigin(thing, x, y, z));
LUA_PushUserdata(L, tm.thing, META_MOBJ);
LUA_PushUserdata(L, g_tm.thing, META_MOBJ);
P_RestoreTMStruct(ptm);
return 2;
}
static int lib_pSetOrigin(lua_State *L)
{
tm_t ptm = tm;
tm_t ptm = g_tm;
mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
fixed_t x = luaL_checkfixed(L, 2);
fixed_t y = luaL_checkfixed(L, 3);
@ -1390,14 +1390,14 @@ static int lib_pSetOrigin(lua_State *L)
if (!thing)
return LUA_ErrInvalid(L, "mobj_t");
lua_pushboolean(L, P_SetOrigin(thing, x, y, z));
LUA_PushUserdata(L, tm.thing, META_MOBJ);
LUA_PushUserdata(L, g_tm.thing, META_MOBJ);
P_RestoreTMStruct(ptm);
return 2;
}
static int lib_pMoveOrigin(lua_State *L)
{
tm_t ptm = tm;
tm_t ptm = g_tm;
mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
fixed_t x = luaL_checkfixed(L, 2);
fixed_t y = luaL_checkfixed(L, 3);
@ -1407,7 +1407,7 @@ static int lib_pMoveOrigin(lua_State *L)
if (!thing)
return LUA_ErrInvalid(L, "mobj_t");
lua_pushboolean(L, P_MoveOrigin(thing, x, y, z));
LUA_PushUserdata(L, tm.thing, META_MOBJ);
LUA_PushUserdata(L, g_tm.thing, META_MOBJ);
P_RestoreTMStruct(ptm);
return 2;
}
@ -3850,10 +3850,10 @@ static luaL_Reg lib[] = {
{"VS_GetArena", lib_vsGetArena},
{"VS_PredictAroundArena", lib_vsPredictAroundArena},
{"VS_RandomPointOnArena", lib_vsRandomPointOnArena},
// hu_stuff technically?
{"HU_DoTitlecardCEcho", lib_startTitlecardCecho},
{NULL, NULL}
};

View file

@ -30,11 +30,27 @@ return luaL_error(L, "HUD rendering code should not call this function!");
static consvar_t *this_cvar;
void Got_Luacmd(UINT8 **cp, INT32 playernum)
static void clear_lua_stack(void)
{
if (gL) // check if Lua is actually turned on first, you dummmy -- Monster Iestyn 04/07/18
lua_settop(gL, 0); // clear stack
}
void Got_Luacmd(const UINT8 **cp, INT32 playernum)
{
UINT8 i, argc, flags;
const char *argv[256];
char buf[256];
argc = READUINT8(*cp);
argv[0] = (const char*)*cp;
SKIPSTRINGN(*cp, 255);
for (i = 1; i < argc; i++)
{
argv[i] = (const char*)*cp;
SKIPSTRINGN(*cp, 255);
}
// don't use I_Assert here, goto the deny code below
// to clean up and kick people who try nefarious exploits
// like sending random junk lua commands to crash the server
@ -47,8 +63,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum)
lua_getfield(gL, LUA_REGISTRYINDEX, "COM_Command"); // push COM_Command
if (!lua_istable(gL, -1)) goto deny;
argc = READUINT8(*cp);
READSTRINGN(*cp, buf, 255);
strlcpy(buf, argv[0], 255);
strlwr(buf); // must lowercase buffer
lua_getfield(gL, -1, buf); // push command info table
if (!lua_istable(gL, -1)) goto deny;
@ -74,10 +89,17 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum)
lua_remove(gL, -2); // pop command info table
if (!lua_checkstack(gL, argc)) // player + command arguments
{
clear_lua_stack();
CONS_Alert(CONS_WARNING, "lua command stack overflow from %s (%d, need %d more)\n", player_names[playernum], lua_gettop(gL), argc);
return;
}
LUA_PushUserdata(gL, &players[playernum], META_PLAYER);
for (i = 1; i < argc; i++)
{
READSTRINGN(*cp, buf, 255);
strlcpy(buf, argv[i], 255);
lua_pushstring(gL, buf);
}
LUA_Call(gL, (int)argc, 0, 1); // argc is 1-based, so this will cover the player we passed too.
@ -85,8 +107,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum)
deny:
//must be hacked/buggy client
if (gL) // check if Lua is actually turned on first, you dummmy -- Monster Iestyn 04/07/18
lua_settop(gL, 0); // clear stack
clear_lua_stack();
CONS_Alert(CONS_WARNING, M_GetText("Illegal lua command received from %s\n"), player_names[playernum]);
if (server)
@ -175,6 +196,11 @@ void COM_Lua_f(void)
I_Assert(lua_isfunction(gL, -1));
lua_remove(gL, -2); // pop command info table
if (!lua_checkstack(gL, COM_Argc() + 1))
{
CONS_Alert(CONS_WARNING, "lua command stack overflow (%d, need %s more)\n", lua_gettop(gL), sizeu1(COM_Argc() + 1));
return;
}
LUA_PushUserdata(gL, &players[playernum], META_PLAYER);
for (i = 1; i < COM_Argc(); i++)
lua_pushstring(gL, COM_Argv(i));

View file

@ -796,7 +796,7 @@ static int sector_set(lua_State *L)
return luaL_error(L, "sector_t field " LUA_QS " cannot be set.", sector_opt[field]);
case sector_floorheight: { // floorheight
boolean flag;
tm_t ptm = tm;
tm_t ptm = g_tm;
fixed_t lastpos = sector->floorheight;
sector->floorheight = luaL_checkfixed(L, 3);
flag = P_CheckSector(sector, true);
@ -810,7 +810,7 @@ static int sector_set(lua_State *L)
}
case sector_ceilingheight: { // ceilingheight
boolean flag;
tm_t ptm = tm;
tm_t ptm = g_tm;
fixed_t lastpos = sector->ceilingheight;
sector->ceilingheight = luaL_checkfixed(L, 3);
flag = P_CheckSector(sector, true);
@ -2203,7 +2203,7 @@ static int ffloor_set(lua_State *L)
case ffloor_topheight: { // topheight
boolean flag;
fixed_t lastpos = *ffloor->topheight;
tm_t ptm = tm;
tm_t ptm = g_tm;
sector_t *sector = &sectors[ffloor->secnum];
sector->ceilingheight = luaL_checkfixed(L, 3);
flag = P_CheckSector(sector, true);
@ -2224,7 +2224,7 @@ static int ffloor_set(lua_State *L)
case ffloor_bottomheight: { // bottomheight
boolean flag;
fixed_t lastpos = *ffloor->bottomheight;
tm_t ptm = tm;
tm_t ptm = g_tm;
sector_t *sector = &sectors[ffloor->secnum];
sector->floorheight = luaL_checkfixed(L, 3);
flag = P_CheckSector(sector, true);

View file

@ -565,13 +565,13 @@ static int mobj_set(lua_State *L)
case mobj_z:
{
// z doesn't cross sector bounds so it's okay.
tm_t ptm = tm;
tm_t ptm = g_tm;
mo->z = luaL_checkfixed(L, 3);
P_CheckPosition(mo, mo->x, mo->y, NULL);
mo->floorz = tm.floorz;
mo->ceilingz = tm.ceilingz;
mo->floorrover = tm.floorrover;
mo->ceilingrover = tm.ceilingrover;
mo->floorz = g_tm.floorz;
mo->ceilingz = g_tm.ceilingz;
mo->floorrover = g_tm.floorrover;
mo->ceilingrover = g_tm.ceilingrover;
P_RestoreTMStruct(ptm);
break;
}
@ -636,29 +636,29 @@ static int mobj_set(lua_State *L)
return NOSET;
case mobj_radius:
{
tm_t ptm = tm;
tm_t ptm = g_tm;
mo->radius = luaL_checkfixed(L, 3);
if (mo->radius < 0)
mo->radius = 0;
P_CheckPosition(mo, mo->x, mo->y, NULL);
mo->floorz = tm.floorz;
mo->ceilingz = tm.ceilingz;
mo->floorrover = tm.floorrover;
mo->ceilingrover = tm.ceilingrover;
mo->floorz = g_tm.floorz;
mo->ceilingz = g_tm.ceilingz;
mo->floorrover = g_tm.floorrover;
mo->ceilingrover = g_tm.ceilingrover;
P_RestoreTMStruct(ptm);
break;
}
case mobj_height:
{
tm_t ptm = tm;
tm_t ptm = g_tm;
mo->height = luaL_checkfixed(L, 3);
if (mo->height < 0)
mo->height = 0;
P_CheckPosition(mo, mo->x, mo->y, NULL);
mo->floorz = tm.floorz;
mo->ceilingz = tm.ceilingz;
mo->floorrover = tm.floorrover;
mo->ceilingrover = tm.ceilingrover;
mo->floorz = g_tm.floorz;
mo->ceilingz = g_tm.ceilingz;
mo->floorrover = g_tm.floorrover;
mo->ceilingrover = g_tm.ceilingrover;
P_RestoreTMStruct(ptm);
break;
}

View file

@ -63,7 +63,7 @@ void LUA_UnArchive(savebuffer_t *save, boolean network);
int LUA_PushGlobals(lua_State *L, const char *word);
int LUA_WriteGlobals(lua_State *L, const char *word);
void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c
void Got_Luacmd(const UINT8 **cp, INT32 playernum); // lua_consolelib.c
void LUA_CVarChanged(void *cvar); // lua_consolelib.c
int Lua_optoption(lua_State *L, int narg,
const char *def, const char *const lst[]);

View file

@ -1628,7 +1628,7 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
)
return false;
if (cn->extrainfo2 != 0)
return (K_PodiumGrade() >= cn->requirement);
return (K_PodiumGrade() >= cn->extrainfo1);
if (cn->extrainfo1 != 0)
return (player->position != 0
&& player->position <= cn->extrainfo1);

View file

@ -20,6 +20,8 @@
#pragma GCC diagnostic ignored "-Wclobbered"
#endif
#include <filesystem>
#include <unistd.h>
#endif
@ -673,7 +675,7 @@ void M_FirstLoadConfig(void)
void M_SaveConfig(const char *filename)
{
FILE *f;
char *filepath;
char tmppath[2048];
// make sure not to write back the config until it's been correctly loaded
if (!gameconfig_loaded)
@ -691,17 +693,23 @@ void M_SaveConfig(const char *filename)
// append srb2home to beginning of filename
// but check if srb2home isn't already there, first
if (!strstr(filename, srb2home))
filepath = va(pandf,srb2home, filename);
else
filepath = Z_StrDup(filename);
f = fopen(filepath, "w");
// change it only if valid
if (f)
strcpy(configfile, filepath);
{
sprintf(tmppath, "%s" PATHSEP "%s.tmp", srb2home, filename);
}
else
{
CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), filepath);
sprintf(tmppath, "%s", filename);
}
f = fopen(tmppath, "w");
// change it only if valid
if (f)
{
strcpy(configfile, tmppath);
}
else
{
CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), tmppath);
return;
}
}
@ -713,7 +721,9 @@ void M_SaveConfig(const char *filename)
return;
}
f = fopen(configfile, "w");
sprintf(tmppath, "%s.tmp", configfile);
f = fopen(tmppath, "w");
if (!f)
{
CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), configfile);
@ -738,6 +748,24 @@ void M_SaveConfig(const char *filename)
}
fclose(f);
{
// Atomically replace the old config once the new one has been written.
namespace fs = std::filesystem;
fs::path tmp{tmppath};
fs::path real{configfile};
try
{
fs::rename(tmp, real);
}
catch (const fs::filesystem_error& ex)
{
CONS_Alert(CONS_ERROR, M_GetText("Failed to move temp config file to real destination\n"));
}
}
}
// ==========================================================================

View file

@ -47,7 +47,7 @@ menu_t MISC_StatisticsDef = {
280, 185,
0, 0,
0,
"EXTRAS",
"EXSTAT",
98, 0,
M_DrawStatistics,
M_DrawExtrasBack,
@ -526,7 +526,7 @@ void M_ChallengesTick(void)
challengesmenu.chaokeyhold++;
UINT32 chaohold_duration =
CHAOHOLD_PADDING
CHAOHOLD_PADDING
+ ((unlockables[challengesmenu.currentunlock].majorunlock == true)
? CHAOHOLD_MAJOR
: CHAOHOLD_STANDARD
@ -792,6 +792,11 @@ boolean M_ChallengesInputs(INT32 ch)
if (cv_debugchallenges.value && challengesmenu.currentunlock < MAXUNLOCKABLES && challengesmenu.unlockanim >= UNLOCKTIME && gamedata->unlocked[challengesmenu.currentunlock] == true)
{
gamedata->unlocked[challengesmenu.currentunlock] = gamedata->unlockpending[challengesmenu.currentunlock] = false;
UINT16 set = unlockables[challengesmenu.currentunlock].conditionset;
if (set > 0 && set <= MAXCONDITIONSETS)
{
gamedata->achieved[set - 1] = false;
}
M_UpdateChallengeGridVisuals();
}

View file

@ -216,6 +216,20 @@ void Music_Play(const char* id)
}
}
void Music_PlayIntermission(void)
{
// why aren't the ATTACK_ enums declared alongside modeattacking?
if (modeattacking != 0)
{
Music_Remap("intermission", "timent");
}
else
{
Music_Remap("intermission", "racent");
}
Music_Play("intermission");
}
void Music_DelayEnd(const char* id, tic_t duration)
{
Tune* tune = g_tunes.find(id);

View file

@ -59,6 +59,10 @@ const char *Music_CurrentId(void);
// back to the start.)
void Music_Play(const char *id);
// Same as Music_Play, but a convenience for remapping intermission
// based on game state.
void Music_PlayIntermission(void);
// Postpone the end of this tune until N tics from now. The
// tune should already be playing before calling this.
void Music_DelayEnd(const char *id, tic_t duration);

View file

@ -123,6 +123,8 @@ public:
ufo->sprzoff(ofs * spawner->scale());
ufo->spawner(spawner);
ufo->extravalue1 = 0; // Lifetime
}
};
@ -134,6 +136,8 @@ void Obj_BattleUFOThink(mobj_t *mobj)
{
UFO* ufo = static_cast<UFO*>(mobj);
ufo->extravalue1++;
ufo->bob();
if ((leveltime/2) & 1)
@ -141,7 +145,12 @@ void Obj_BattleUFOThink(mobj_t *mobj)
ufo->spawn_beam();
}
if (!battleovertime.enabled)
if ((ufo->extravalue1 % (TICRATE*2)) == 0)
{
S_StartSound(ufo, sfx_s3ka5);
}
if (!battleovertime.enabled && ufo->extravalue1 <= 5*TICRATE)
{
Obj_PointPlayersToXY(mobj->x, mobj->y);
}

View file

@ -49,13 +49,13 @@ void Obj_SneakerPanelSetup(mobj_t *mobj, mapthing_t *mthing)
{
if (P_IsObjectFlipped(mobj))
{
if (tm.ceilingz <= mobj->z + mobj->height)
mobj->standingslope = tm.ceilingslope;
if (g_tm.ceilingz <= mobj->z + mobj->height)
mobj->standingslope = g_tm.ceilingslope;
}
else
{
if (mobj->z <= tm.floorz)
mobj->standingslope = tm.floorslope;
if (mobj->z <= g_tm.floorz)
mobj->standingslope = g_tm.floorslope;
}
}

View file

@ -495,10 +495,10 @@ boolean P_Move(mobj_t *actor, fixed_t speed)
if (!P_TryMove(actor, tryx, tryy, false, NULL))
{
if (actor->flags & MF_FLOAT && tm.floatok)
if (actor->flags & MF_FLOAT && g_tm.floatok)
{
// must adjust height
if (actor->z < tm.floorz)
if (actor->z < g_tm.floorz)
actor->z += FixedMul(FLOATSPEED, actor->scale);
else
actor->z -= FixedMul(FLOATSPEED, actor->scale);
@ -9940,13 +9940,13 @@ void A_FlickyCenter(mobj_t *actor)
{
actor->extravalue2 = 1;
P_SetOrigin(actor, actor->target->x, actor->target->y, actor->target->z);
P_SetTarget(&tm.thing, NULL);
P_SetTarget(&g_tm.thing, NULL);
}
else if(actor->extravalue2)
{
actor->extravalue2 = 0;
P_SetOrigin(actor, originx, originy, originz);
P_SetTarget(&tm.thing, NULL);
P_SetTarget(&g_tm.thing, NULL);
}
}
}
@ -12139,11 +12139,11 @@ void A_SSMineExplode(mobj_t *actor)
return;
delay = K_MineExplodeAttack(actor, (3*actor->info->painchance)>>1, (boolean)locvar1);
skincolornum_t color = SKINCOLOR_KETCHUP;
if (!P_MobjWasRemoved(actor->target) && actor->target->player)
color = actor->target->player->skincolor;
K_SpawnMineExplosion(actor, color, delay);
}
@ -12612,7 +12612,7 @@ void A_MakeSSCandle(mobj_t* actor)
P_SetTarget(&actor->tracer, fire);
// Sides
for (i = 0; i < 5; i++)
for (i = 0; i < 6; i++)
{
fixed_t a = FixedAngle(60 * FRACUNIT) * i;
fixed_t offsetx = actor->x + FixedMul(dist, FCOS(a));

View file

@ -1505,7 +1505,7 @@ boolean P_CheckRacers(void)
}
else
{
if (griefed == true)
if (griefed == true && numHumans > 0)
{
// Don't do this if someone spectated
eliminateLast = false;
@ -2503,7 +2503,7 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
if (gametyperules & (GTR_BUMPERS|GTR_CHECKPOINTS))
{
if ((player->pitblame > -1) && (player->pitblame < MAXPLAYERS)
&& (playeringame[player->pitblame]) && (!players[player->pitblame].spectator)
&& (playeringame[player->pitblame]) && (!players[player->pitblame].spectator)
&& (players[player->pitblame].mo) && (!P_MobjWasRemoved(players[player->pitblame].mo)))
{
P_DamageMobj(player->mo, players[player->pitblame].mo, players[player->pitblame].mo, 1, DMG_KARMA);
@ -3163,6 +3163,10 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
Obj_EndBungee(player);
K_BumperInflate(target->player);
// Explosions are explicit combo setups.
if (damagetype & DMG_EXPLODE)
player->bumperinflate = 0;
if (player->spectator == false && !(player->charflags & SF_IRONMAN))
{
UINT32 skinflags = (demo.playback)

View file

@ -398,7 +398,7 @@ struct tm_t
fixed_t maxstep;
};
extern tm_t tm;
extern tm_t g_tm;
void P_RestoreTMStruct(tm_t tmrestore);

File diff suppressed because it is too large Load diff

View file

@ -375,8 +375,8 @@ void P_CameraLineOpening(line_t *linedef, opening_t *open)
}
else
{
frontfloor = P_CameraGetFloorZ (mapcampointer, front, tm.x, tm.y, linedef);
frontceiling = P_CameraGetCeilingZ(mapcampointer, front, tm.x, tm.y, linedef);
frontfloor = P_CameraGetFloorZ (mapcampointer, front, g_tm.x, g_tm.y, linedef);
frontceiling = P_CameraGetCeilingZ(mapcampointer, front, g_tm.x, g_tm.y, linedef);
}
if (back->camsec >= 0)
@ -393,8 +393,8 @@ void P_CameraLineOpening(line_t *linedef, opening_t *open)
}
else
{
backfloor = P_CameraGetFloorZ (mapcampointer, back, tm.x, tm.y, linedef);
backceiling = P_CameraGetCeilingZ(mapcampointer, back, tm.x, tm.y, linedef);
backfloor = P_CameraGetFloorZ (mapcampointer, back, g_tm.x, g_tm.y, linedef);
backceiling = P_CameraGetCeilingZ(mapcampointer, back, g_tm.x, g_tm.y, linedef);
}
thingtop = mapcampointer->z + mapcampointer->height;
@ -435,8 +435,8 @@ void P_CameraLineOpening(line_t *linedef, opening_t *open)
if (!(rover->fofflags & FOF_BLOCKOTHERS) || !(rover->fofflags & FOF_RENDERALL) || !(rover->fofflags & FOF_EXISTS) || (rover->master->frontsector->flags & MSF_NOCLIPCAMERA))
continue;
topheight = P_CameraGetFOFTopZ(mapcampointer, front, rover, tm.x, tm.y, linedef);
bottomheight = P_CameraGetFOFBottomZ(mapcampointer, front, rover, tm.x, tm.y, linedef);
topheight = P_CameraGetFOFTopZ(mapcampointer, front, rover, g_tm.x, g_tm.y, linedef);
bottomheight = P_CameraGetFOFBottomZ(mapcampointer, front, rover, g_tm.x, g_tm.y, linedef);
delta1 = abs(mapcampointer->z - (bottomheight + ((topheight - bottomheight)/2)));
delta2 = abs(thingtop - (bottomheight + ((topheight - bottomheight)/2)));
@ -459,8 +459,8 @@ void P_CameraLineOpening(line_t *linedef, opening_t *open)
if (!(rover->fofflags & FOF_BLOCKOTHERS) || !(rover->fofflags & FOF_RENDERALL) || !(rover->fofflags & FOF_EXISTS) || (rover->master->frontsector->flags & MSF_NOCLIPCAMERA))
continue;
topheight = P_CameraGetFOFTopZ(mapcampointer, back, rover, tm.x, tm.y, linedef);
bottomheight = P_CameraGetFOFBottomZ(mapcampointer, back, rover, tm.x, tm.y, linedef);
topheight = P_CameraGetFOFTopZ(mapcampointer, back, rover, g_tm.x, g_tm.y, linedef);
bottomheight = P_CameraGetFOFBottomZ(mapcampointer, back, rover, g_tm.x, g_tm.y, linedef);
delta1 = abs(mapcampointer->z - (bottomheight + ((topheight - bottomheight)/2)));
delta2 = abs(thingtop - (bottomheight + ((topheight - bottomheight)/2)));
@ -617,7 +617,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj, opening_t *open)
return;
}
P_ClosestPointOnLine(tm.x, tm.y, linedef, &cross);
P_ClosestPointOnLine(g_tm.x, g_tm.y, linedef, &cross);
// Treat polyobjects kind of like 3D Floors
if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT))
@ -645,8 +645,8 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj, opening_t *open)
fixed_t height[2];
const sector_t * sector[2] = { front, back };
height[FRONT] = P_GetCeilingZ(mobj, front, tm.x, tm.y, linedef);
height[BACK] = P_GetCeilingZ(mobj, back, tm.x, tm.y, linedef);
height[FRONT] = P_GetCeilingZ(mobj, front, g_tm.x, g_tm.y, linedef);
height[BACK] = P_GetCeilingZ(mobj, back, g_tm.x, g_tm.y, linedef);
hi = ( height[0] < height[1] );
lo = ! hi;
@ -665,8 +665,8 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj, opening_t *open)
open->ceilingdrop = ( topedge[hi] - topedge[lo] );
}
height[FRONT] = P_GetFloorZ(mobj, front, tm.x, tm.y, linedef);
height[BACK] = P_GetFloorZ(mobj, back, tm.x, tm.y, linedef);
height[FRONT] = P_GetFloorZ(mobj, front, g_tm.x, g_tm.y, linedef);
height[BACK] = P_GetFloorZ(mobj, back, g_tm.x, g_tm.y, linedef);
hi = ( height[0] < height[1] );
lo = ! hi;
@ -855,8 +855,8 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj, opening_t *open)
}
else
{
topheight = P_GetFOFTopZ(mobj, front, rover, tm.x, tm.y, linedef);
bottomheight = P_GetFOFBottomZ(mobj, front, rover, tm.x, tm.y, linedef);
topheight = P_GetFOFTopZ(mobj, front, rover, g_tm.x, g_tm.y, linedef);
bottomheight = P_GetFOFBottomZ(mobj, front, rover, g_tm.x, g_tm.y, linedef);
}
switch (open->fofType)
@ -947,8 +947,8 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj, opening_t *open)
}
else
{
topheight = P_GetFOFTopZ(mobj, back, rover, tm.x, tm.y, linedef);
bottomheight = P_GetFOFBottomZ(mobj, back, rover, tm.x, tm.y, linedef);
topheight = P_GetFOFTopZ(mobj, back, rover, g_tm.x, g_tm.y, linedef);
bottomheight = P_GetFOFBottomZ(mobj, back, rover, g_tm.x, g_tm.y, linedef);
}
switch (open->fofType)
@ -1754,16 +1754,16 @@ boolean P_RadiusLinesCheck(fixed_t radius, fixed_t x, fixed_t y,
INT32 xl, xh, yl, yh;
INT32 bx, by;
tm.bbox[BOXTOP] = y + radius;
tm.bbox[BOXBOTTOM] = y - radius;
tm.bbox[BOXRIGHT] = x + radius;
tm.bbox[BOXLEFT] = x - radius;
g_tm.bbox[BOXTOP] = y + radius;
g_tm.bbox[BOXBOTTOM] = y - radius;
g_tm.bbox[BOXRIGHT] = x + radius;
g_tm.bbox[BOXLEFT] = x - radius;
// check lines
xl = (unsigned)(tm.bbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(tm.bbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(tm.bbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(tm.bbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
xl = (unsigned)(g_tm.bbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(g_tm.bbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(g_tm.bbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(g_tm.bbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
for (bx = xl; bx <= xh; bx++)
for (by = yl; by <= yh; by++)

View file

@ -1558,12 +1558,12 @@ bustupdone:
//
static boolean P_CheckSkyHit(mobj_t *mo)
{
if (tm.ceilingline && tm.ceilingline->backsector
&& tm.ceilingline->backsector->ceilingpic == skyflatnum
&& tm.ceilingline->frontsector
&& tm.ceilingline->frontsector->ceilingpic == skyflatnum
&& (mo->z >= tm.ceilingline->frontsector->ceilingheight
|| mo->z >= tm.ceilingline->backsector->ceilingheight))
if (g_tm.ceilingline && g_tm.ceilingline->backsector
&& g_tm.ceilingline->backsector->ceilingpic == skyflatnum
&& g_tm.ceilingline->frontsector
&& g_tm.ceilingline->frontsector->ceilingpic == skyflatnum
&& (mo->z >= g_tm.ceilingline->frontsector->ceilingheight
|| mo->z >= g_tm.ceilingline->backsector->ceilingheight))
return true;
return false;
}
@ -1668,7 +1668,7 @@ void P_XYMovement(mobj_t *mo)
// blocked move
moved = false;
if (LUA_HookMobjMoveBlocked(mo, tm.hitthing, result.line))
if (LUA_HookMobjMoveBlocked(mo, g_tm.hitthing, result.line))
{
if (P_MobjWasRemoved(mo))
return;
@ -1695,7 +1695,7 @@ void P_XYMovement(mobj_t *mo)
// draw damage on wall
//SPLAT TEST ----------------------------------------------------------
#ifdef WALLSPLATS
if (tm.blockingline && mo->type != MT_REDRING && mo->type != MT_FIREBALL
if (g_tm.blockingline && mo->type != MT_REDRING && mo->type != MT_FIREBALL
&& !(mo->flags2 & (MF2_AUTOMATIC|MF2_RAILRING|MF2_BOUNCERING|MF2_EXPLOSION|MF2_SCATTER)))
// set by last P_TryMove() that failed
{
@ -1703,13 +1703,13 @@ void P_XYMovement(mobj_t *mo)
divline_t misl;
fixed_t frac;
P_MakeDivline(tm.blockingline, &divl);
P_MakeDivline(g_tm.blockingline, &divl);
misl.x = mo->x;
misl.y = mo->y;
misl.dx = mo->momx;
misl.dy = mo->momy;
frac = P_InterceptVector(&divl, &misl);
R_AddWallSplat(tm.blockingline, P_PointOnLineSide(mo->x,mo->y,tm.blockingline),
R_AddWallSplat(g_tm.blockingline, P_PointOnLineSide(mo->x,mo->y,g_tm.blockingline),
"A_DMG3", mo->z, frac, SPLATDRAWMODE_SHADE);
}
#endif
@ -2367,11 +2367,11 @@ boolean P_ZMovement(mobj_t *mo)
mom.y = mo->momy;
mom.z = mo->momz;
K_UpdateMobjTerrain(mo, ((mo->eflags & MFE_VERTICALFLIP) ? tm.ceilingpic : tm.floorpic));
K_UpdateMobjTerrain(mo, ((mo->eflags & MFE_VERTICALFLIP) ? g_tm.ceilingpic : g_tm.floorpic));
if (((mo->eflags & MFE_VERTICALFLIP) ? tm.ceilingslope : tm.floorslope) && (mo->type != MT_STEAM))
if (((mo->eflags & MFE_VERTICALFLIP) ? g_tm.ceilingslope : g_tm.floorslope) && (mo->type != MT_STEAM))
{
mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tm.ceilingslope : tm.floorslope;
mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? g_tm.ceilingslope : g_tm.floorslope;
P_SetPitchRollFromSlope(mo, mo->standingslope);
P_ReverseQuantizeMomentumToSlope(&mom, mo->standingslope);
}
@ -2598,11 +2598,11 @@ boolean P_ZMovement(mobj_t *mo)
}
}
else
mom.z = (tm.floorthing ? tm.floorthing->momz : 0);
mom.z = (g_tm.floorthing ? g_tm.floorthing->momz : 0);
}
else if (tm.floorthing)
mom.z = tm.floorthing->momz;
else if (g_tm.floorthing)
mom.z = g_tm.floorthing->momz;
if (mo->standingslope) { // MT_STEAM will never have a standingslope, see above.
P_QuantizeMomentumToSlope(&mom, mo->standingslope);
@ -2843,7 +2843,7 @@ void P_PlayerZMovement(mobj_t *mo)
mo->z = mo->floorz;
}
K_UpdateMobjTerrain(mo, (mo->eflags & MFE_VERTICALFLIP ? tm.ceilingpic : tm.floorpic));
K_UpdateMobjTerrain(mo, (mo->eflags & MFE_VERTICALFLIP ? g_tm.ceilingpic : g_tm.floorpic));
// Get up if you fell.
if (mo->player->panim == PA_HURT && mo->player->spinouttimer == 0 && mo->player->tumbleBounces == 0)
@ -2851,10 +2851,10 @@ void P_PlayerZMovement(mobj_t *mo)
P_SetPlayerMobjState(mo, S_KART_STILL);
}
if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? tm.ceilingslope : tm.floorslope))
if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? g_tm.ceilingslope : g_tm.floorslope))
{
// Handle landing on slope during Z movement
P_HandleSlopeLanding(mo, (mo->eflags & MFE_VERTICALFLIP ? tm.ceilingslope : tm.floorslope));
P_HandleSlopeLanding(mo, (mo->eflags & MFE_VERTICALFLIP ? g_tm.ceilingslope : g_tm.floorslope));
}
if (P_MobjFlip(mo) * mo->momz < 0) // falling
@ -2869,12 +2869,12 @@ void P_PlayerZMovement(mobj_t *mo)
if (clipmomz)
{
mo->momz = (tm.floorthing ? tm.floorthing->momz : 0);
mo->momz = (g_tm.floorthing ? g_tm.floorthing->momz : 0);
}
}
else if (tm.floorthing)
else if (g_tm.floorthing)
{
mo->momz = tm.floorthing->momz;
mo->momz = g_tm.floorthing->momz;
}
}
else
@ -3124,9 +3124,9 @@ boolean P_SceneryZMovement(mobj_t *mo)
{
mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack
if (tm.floorthing)
mo->momz = tm.floorthing->momz;
else if (!tm.floorthing)
if (g_tm.floorthing)
mo->momz = g_tm.floorthing->momz;
else if (!g_tm.floorthing)
mo->momz = 0;
}
}
@ -3868,8 +3868,8 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
}
thiscam->subsector = R_PointInSubsector(thiscam->x, thiscam->y);
thiscam->floorz = tm.floorz;
thiscam->ceilingz = tm.ceilingz;
thiscam->floorz = g_tm.floorz;
thiscam->ceilingz = g_tm.ceilingz;
if (thiscam->momz || thiscam->pmomz)
{
@ -4029,8 +4029,8 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
mobj->z += mobj->momz;
P_SetThingPosition(mobj);
P_CheckPosition(mobj, mobj->x, mobj->y, NULL);
mobj->floorz = tm.floorz;
mobj->ceilingz = tm.ceilingz;
mobj->floorz = g_tm.floorz;
mobj->ceilingz = g_tm.ceilingz;
mobj->terrain = NULL;
goto animonly;
}
@ -7159,7 +7159,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
// the backdrop can also go transparent. EUUUAUUAUUAAAUUUUGGGHGHHHHHHHGSSS
if (mobj->extravalue1 > 0)
mobj->extravalue1--;
break;
}
case MT_ITEMCAPSULE:
@ -10166,8 +10166,8 @@ void P_MobjThinker(mobj_t *mobj)
mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG|MFE_JUSTBOUNCEDWALL|MFE_DAMAGEHITLAG|MFE_SLOPELAUNCHED);
// sal: what the hell? is there any reason this isn't done, like, literally ANYWHERE else?
P_SetTarget(&tm.floorthing, NULL);
P_SetTarget(&tm.hitthing, NULL);
P_SetTarget(&g_tm.floorthing, NULL);
P_SetTarget(&g_tm.hitthing, NULL);
if (udmf)
{
@ -10520,10 +10520,10 @@ void P_SceneryThinker(mobj_t *mobj)
P_CheckPosition(mobj, mobj->x, mobj->y, NULL); // Need this to pick up objects!
if (P_MobjWasRemoved(mobj))
return;
mobj->floorz = tm.floorz;
mobj->ceilingz = tm.ceilingz;
mobj->floorrover = tm.floorrover;
mobj->ceilingrover = tm.ceilingrover;
mobj->floorz = g_tm.floorz;
mobj->ceilingz = g_tm.ceilingz;
mobj->floorrover = g_tm.floorrover;
mobj->ceilingrover = g_tm.ceilingrover;
}
else
{

View file

@ -763,7 +763,7 @@ static void Polyobj_removeFromBlockmap(polyobj_t *po)
// Movement functions
// A version of Lee's routine from p_maputl.c that accepts an mobj pointer
// argument instead of using tm.thing. Returns true if the line isn't contacted
// argument instead of using g_tm.thing. Returns true if the line isn't contacted
// and false otherwise.
static inline boolean Polyobj_untouched(line_t *ld, mobj_t *mo)
{
@ -807,10 +807,10 @@ static void Polyobj_pushThing(polyobj_t *po, line_t *line, mobj_t *mo)
if (po->damage && (mo->flags & MF_SHOOTABLE))
{
P_CheckPosition(mo, mo->x + momx, mo->y + momy, NULL);
mo->floorz = tm.floorz;
mo->ceilingz = tm.ceilingz;
mo->floorrover = tm.floorrover;
mo->ceilingrover = tm.ceilingrover;
mo->floorz = g_tm.floorz;
mo->ceilingz = g_tm.ceilingz;
mo->floorrover = g_tm.floorrover;
mo->ceilingrover = g_tm.ceilingrover;
}
}
@ -2199,7 +2199,7 @@ boolean EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
R_CreateInterpolator_Polyobj(&th->thinker, po);
// T_PolyObjWaypoint is the only polyobject movement
// that can adjust z, so we add these ones too.
R_CreateInterpolator_SectorPlane(&th->thinker, po->lines[0]->backsector, false);
R_CreateInterpolator_SectorPlane(&th->thinker, po->lines[0]->backsector, false);
R_CreateInterpolator_SectorPlane(&th->thinker, po->lines[0]->backsector, true);
// Most other polyobject functions handle children by recursively

View file

@ -6937,7 +6937,7 @@ boolean P_LoadNetGame(savebuffer_t *save, boolean reloading)
current_savebuffer = save;
CV_LoadNetVars(&save->p);
save->p += CV_LoadNetVars(save->p);
if (!P_NetUnArchiveMisc(save, reloading))
return false;

View file

@ -380,13 +380,13 @@ static boolean P_CanBotTraverse(seg_t *seg, divline_t *divl, register los_t *los
frac = P_InterceptVector2(&los->strace, divl);
// calculate position at intercept
tm.x = los->strace.x + FixedMul(los->strace.dx, frac);
tm.y = los->strace.y + FixedMul(los->strace.dy, frac);
g_tm.x = los->strace.x + FixedMul(los->strace.dx, frac);
g_tm.y = los->strace.y + FixedMul(los->strace.dy, frac);
// set openrange, opentop, openbottom
open.fofType = (flip ? LO_FOF_CEILINGS : LO_FOF_FLOORS);
P_LineOpening(line, los->t1, &open);
maxstep = P_GetThingStepUp(los->t1, tm.x, tm.y);
maxstep = P_GetThingStepUp(los->t1, g_tm.x, g_tm.y);
if (open.range < los->t1->height)
{
@ -408,7 +408,7 @@ static boolean P_CanBotTraverse(seg_t *seg, divline_t *divl, register los_t *los
UINT8 side = P_DivlineSide(los->t2x, los->t2y, divl) & 1;
sector_t *sector = (side == 1) ? seg->backsector : seg->frontsector;
if (K_BotHatesThisSector(los->t1->player, sector, tm.x, tm.y))
if (K_BotHatesThisSector(los->t1->player, sector, g_tm.x, g_tm.y))
{
// This line does not block us, but we don't want to cross it regardless.
return false;
@ -448,13 +448,13 @@ static boolean P_CanWaypointTraverse(seg_t *seg, divline_t *divl, register los_t
frac = P_InterceptVector2(&los->strace, divl);
// calculate position at intercept
tm.x = los->strace.x + FixedMul(los->strace.dx, frac);
tm.y = los->strace.y + FixedMul(los->strace.dy, frac);
g_tm.x = los->strace.x + FixedMul(los->strace.dx, frac);
g_tm.y = los->strace.y + FixedMul(los->strace.dy, frac);
// set openrange, opentop, openbottom
open.fofType = (flip ? LO_FOF_CEILINGS : LO_FOF_FLOORS);
P_LineOpening(line, los->t1, &open);
maxstep = P_GetThingStepUp(los->t1, tm.x, tm.y);
maxstep = P_GetThingStepUp(los->t1, g_tm.x, g_tm.y);
#if 0
if (los->t2->type == MT_WAYPOINT)

View file

@ -1206,14 +1206,26 @@ void P_DoPlayerExit(player_t *player, pflags_t flags)
player->exiting = 1;
if (!player->spectator && (gametyperules & GTR_CIRCUIT)) // Special Race-like handling
{
K_UpdateAllPlayerPositions();
}
const boolean losing = K_IsPlayerLosing(player); // HEY!!!! Set it AFTER K_UpdateAllPlayerPositions!!!!
const boolean specialout = (specialstageinfo.valid == true && losing == true);
if (G_GametypeUsesLives() && losing)
{
// Remove a life from the losing player
K_PlayerLoseLife(player);
}
if (!player->spectator)
{
ClearFakePlayerSkin(player);
if ((gametyperules & GTR_CIRCUIT)) // Special Race-like handling
{
K_UpdateAllPlayerPositions();
if (P_CheckRacers() && !exitcountdown)
{
G_BeginLevelExit();
@ -1225,17 +1237,8 @@ void P_DoPlayerExit(player_t *player, pflags_t flags)
}
}
const boolean losing = K_IsPlayerLosing(player); // HEY!!!! Set it AFTER K_UpdateAllPlayerPositions!!!!
const boolean specialout = (specialstageinfo.valid == true && losing == true);
K_UpdatePowerLevelsFinalize(player, false);
if (G_GametypeUsesLives() && losing)
{
// Remove a life from the losing player
K_PlayerLoseLife(player);
}
if (P_IsLocalPlayer(player) && !specialout && musiccountdown == 0)
{
Music_Play("finish_silence");

View file

@ -45,7 +45,7 @@ static boolean S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source,
static void Command_Tunes_f(void);
static void Command_RestartAudio_f(void);
static void Command_PlaySound(void);
static void Got_PlaySound(UINT8 **p, INT32 playernum);
static void Got_PlaySound(const UINT8 **p, INT32 playernum);
static void Command_MusicDef_f(void);
void Captioning_OnChange(void);
@ -1217,7 +1217,7 @@ void S_AttemptToRestoreMusic(void)
}
// FALLTHRU
case GS_INTERMISSION:
Music_Play("intermission");
Music_PlayIntermission();
break;
case GS_CEREMONY:
Music_Play("level");
@ -2414,7 +2414,7 @@ static void Command_PlaySound(void)
SendNetXCmd(XD_PLAYSOUND, buf, buf_p - buf);
}
static void Got_PlaySound(UINT8 **cp, INT32 playernum)
static void Got_PlaySound(const UINT8 **cp, INT32 playernum)
{
INT32 sound_id = READINT32(*cp);

View file

@ -566,7 +566,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"s3ka2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Launch"},
{"s3ka3", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Rising charge"},
{"s3ka4", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Powering up"},
{"s3ka5", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s3ka5", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // bufo x8away
{"s3ka6", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Attraction fizzle"},
{"s3ka7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Countdown beep"},
{"s3ka8", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Energy"},

View file

@ -2132,7 +2132,9 @@ void Y_StartIntermission(void)
G_SetGamestate(GS_INTERMISSION);
if (musiccountdown == 0)
Music_Play("intermission");
{
Music_PlayIntermission();
}
S_ShowMusicCredit(); // Always call