mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-02-14 09:36:20 +00:00
Live Event Backup
Associated menu stuff is rough and unpolished, but it's 1am for the author of this commit.
- Activated for this session by either of the following Passwords.
- `savetheframes`
- `savetheanimals`
- The idea is that if Ring Racers were ever ran at a GDQ, runners would swear their alliegance to a particular Metroid routing at the startup stage, because I think it's funny and cute.
- A live event backup is created/overwritten on non-cheated grandprix level change.
- It's wiped on reaching the podium.
- When hitting PLAY on the main menu, if a live event backup exists, make a Menu Message.
- If you hit A, turn live event backups on for this session, and load the backup.
- If you hit B/X, delete the backup and proceed to character select.
- Done this way to avoid cheating a different character to the end of GP.
- Unlike the (maybe a little over-)engineering of ringdata.dat, liveringbak.bkp is streamlined as all hells and has very little recovery for differing file lists.
This commit is contained in:
parent
3cbbcfd253
commit
e372d348cd
13 changed files with 441 additions and 300 deletions
|
|
@ -982,7 +982,7 @@ void D_ClearState(void)
|
|||
cht_debug = 0;
|
||||
emeralds = 0;
|
||||
memset(&luabanks, 0, sizeof(luabanks));
|
||||
lastmaploaded = 0;
|
||||
lastqueuesaved = 0;
|
||||
|
||||
// In case someone exits out at the same time they start a time attack run,
|
||||
// reset modeattacking
|
||||
|
|
|
|||
|
|
@ -517,7 +517,7 @@ void CONS_Debug(UINT32 debugflags, const char *fmt, ...) FUNCDEBUG;
|
|||
#include "m_swap.h"
|
||||
|
||||
// Things that used to be in dstrings.h
|
||||
#define SAVEGAMENAME "srb2sav"
|
||||
#define SAVEGAMENAME "ringsav"
|
||||
extern char savegamename[256];
|
||||
extern char liveeventbackup[256];
|
||||
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ extern UINT8 mapmusrng;
|
|||
extern UINT32 maptol;
|
||||
|
||||
extern INT32 cursaveslot;
|
||||
//extern INT16 lastmapsaved;
|
||||
extern INT16 lastmaploaded;
|
||||
extern UINT8 lastqueuesaved;
|
||||
extern boolean makelivebackup;
|
||||
extern UINT8 gamecomplete;
|
||||
|
||||
// Extra abilities/settings for skins (combinable stuff)
|
||||
|
|
|
|||
235
src/g_game.c
235
src/g_game.c
|
|
@ -123,8 +123,8 @@ precipprops_t precipprops[MAXPRECIP] =
|
|||
preciptype_t precip_freeslot = PRECIP_FIRSTFREESLOT;
|
||||
|
||||
INT32 cursaveslot = 0; // Auto-save 1p savegame slot
|
||||
//INT16 lastmapsaved = 0; // Last map we auto-saved at
|
||||
INT16 lastmaploaded = 0; // Last map the game loaded
|
||||
UINT8 lastqueuesaved = 0;
|
||||
boolean makelivebackup = false;
|
||||
UINT8 gamecomplete = 0;
|
||||
|
||||
marathonmode_t marathonmode = 0;
|
||||
|
|
@ -4018,39 +4018,26 @@ void G_UpdateVisited(void)
|
|||
G_SaveGameData();
|
||||
}
|
||||
|
||||
static boolean CanSaveLevel(INT32 mapnum)
|
||||
void G_HandleSaveLevel(void)
|
||||
{
|
||||
// SRB2Kart: No save files yet
|
||||
(void)mapnum;
|
||||
return false;
|
||||
}
|
||||
if (!grandprixinfo.gp || !grandprixinfo.cup
|
||||
|| splitscreen || netgame
|
||||
|| !(makelivebackup || cursaveslot > 0))
|
||||
return;
|
||||
|
||||
static void G_HandleSaveLevel(void)
|
||||
{
|
||||
// do this before running the intermission or custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c
|
||||
if (nextmap >= NEXTMAP_SPECIAL)
|
||||
if (gamestate == GS_CEREMONY && makelivebackup)
|
||||
{
|
||||
if (!gamecomplete)
|
||||
gamecomplete = 2; // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission
|
||||
if (cursaveslot > 0)
|
||||
{
|
||||
if (marathonmode)
|
||||
{
|
||||
// don't keep a backup around when the run is done!
|
||||
if (FIL_FileExists(liveeventbackup))
|
||||
remove(liveeventbackup);
|
||||
cursaveslot = 0;
|
||||
}
|
||||
else if (!usedCheats && !(netgame || multiplayer || ultimatemode || demo.recording || metalrecording || modeattacking))
|
||||
G_SaveGame((UINT32)cursaveslot, 0); // TODO when we readd a campaign one day
|
||||
}
|
||||
}
|
||||
// and doing THIS here means you don't lose your progress if you close the game mid-intermission
|
||||
else if (!(ultimatemode || demo.playback || demo.recording || metalrecording || modeattacking)
|
||||
&& cursaveslot > 0 && CanSaveLevel(lastmap+1))
|
||||
{
|
||||
G_SaveGame((UINT32)cursaveslot, lastmap+1); // not nextmap+1 to route around special stages
|
||||
if (FIL_FileExists(liveeventbackup))
|
||||
remove(liveeventbackup);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gamestate != GS_LEVEL
|
||||
|| roundqueue.size == 0
|
||||
|| lastqueuesaved == roundqueue.position)
|
||||
return;
|
||||
|
||||
G_SaveGame();
|
||||
}
|
||||
|
||||
// Next map apparatus
|
||||
|
|
@ -4645,9 +4632,6 @@ static void G_DoContinued(void)
|
|||
// Reset score
|
||||
pl->score = 0;
|
||||
|
||||
if (!(netgame || multiplayer || demo.playback || demo.recording || metalrecording || modeattacking) && !usedCheats && cursaveslot > 0)
|
||||
G_SaveGameOver((UINT32)cursaveslot, true);
|
||||
|
||||
// Reset # of lives
|
||||
pl->lives = 3;
|
||||
|
||||
|
|
@ -5673,7 +5657,7 @@ void G_SaveGameData(void)
|
|||
// G_InitFromSavegame
|
||||
// Can be called by the startup code or the menu task.
|
||||
//
|
||||
void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
||||
void G_LoadGame(void)
|
||||
{
|
||||
char vcheck[VERSIONSIZE];
|
||||
char savename[255];
|
||||
|
|
@ -5682,15 +5666,10 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
// memset savedata to all 0, fixes calling perfectly valid saves corrupt because of bots
|
||||
memset(&savedata, 0, sizeof(savedata));
|
||||
|
||||
#ifdef SAVEGAME_OTHERVERSIONS
|
||||
//Oh christ. The force load response needs access to mapoverride too...
|
||||
startonmapnum = mapoverride;
|
||||
#endif
|
||||
|
||||
if (marathonmode)
|
||||
if (makelivebackup)
|
||||
strcpy(savename, liveeventbackup);
|
||||
else
|
||||
sprintf(savename, savegamename, slot);
|
||||
sprintf(savename, savegamename, cursaveslot);
|
||||
|
||||
if (P_SaveBufferFromFile(&save, savename) == false)
|
||||
{
|
||||
|
|
@ -5699,22 +5678,11 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
}
|
||||
|
||||
memset(vcheck, 0, sizeof (vcheck));
|
||||
sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
sprintf(vcheck, (makelivebackup ? "back-up %d" : "version %d"), VERSION);
|
||||
if (strcmp((const char *)save.p, (const char *)vcheck))
|
||||
{
|
||||
#ifdef SAVEGAME_OTHERVERSIONS
|
||||
M_StartMessage("Savegame Load", M_GetText("Save game from different version.\nYou can load this savegame, but\nsaving afterwards will be disabled.\n\nDo you want to continue anyway?\n"),
|
||||
M_ForceLoadGameResponse, MM_YESNO, NULL, NULL);
|
||||
//Freeing done by the callback function of the above message
|
||||
#else
|
||||
M_ClearMenus(true); // so ESC backs out to title
|
||||
M_StartMessage("Savegame Load", M_GetText("Save game from different version\n\n"), NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
Command_ExitGame_f();
|
||||
M_StartMessage("Savegame Load", va(M_GetText("Save game %s from different version"), savename), NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
P_SaveBufferFree(&save);
|
||||
|
||||
// no cheating!
|
||||
memset(&savedata, 0, sizeof(savedata));
|
||||
#endif
|
||||
return; // bad version
|
||||
}
|
||||
save.p += VERSIONSIZE;
|
||||
|
|
@ -5726,39 +5694,18 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
// automapactive = false;
|
||||
|
||||
// dearchive all the modifications
|
||||
if (!P_LoadGame(&save, mapoverride))
|
||||
if (!P_LoadGame(&save))
|
||||
{
|
||||
M_ClearMenus(true); // so ESC backs out to title
|
||||
M_StartMessage("Savegame Load", M_GetText("Savegame file corrupted\n"), NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
Command_ExitGame_f();
|
||||
M_StartMessage("Savegame Load", va(M_GetText("Savegame %s corrupted\n"), savename), NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
Z_Free(save.buffer);
|
||||
save.p = save.buffer = NULL;
|
||||
|
||||
// no cheating!
|
||||
memset(&savedata, 0, sizeof(savedata));
|
||||
return;
|
||||
}
|
||||
if (marathonmode)
|
||||
{
|
||||
marathontime = READUINT32(save.p);
|
||||
marathonmode |= READUINT8(save.p);
|
||||
}
|
||||
|
||||
// done
|
||||
P_SaveBufferFree(&save);
|
||||
|
||||
// gameaction = ga_nothing;
|
||||
// G_SetGamestate(GS_LEVEL);
|
||||
displayplayers[0] = consoleplayer;
|
||||
multiplayer = false;
|
||||
splitscreen = 0;
|
||||
SplitScreen_OnChange(); // not needed?
|
||||
|
||||
// G_DeferedInitNew(sk_medium, G_BuildMapName(1), 0, 0, 1);
|
||||
if (setsizeneeded)
|
||||
R_ExecuteSetViewSize();
|
||||
|
||||
M_ClearMenus(true);
|
||||
CON_ToggleOff();
|
||||
}
|
||||
|
||||
|
|
@ -5766,18 +5713,16 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
// G_SaveGame
|
||||
// Saves your game.
|
||||
//
|
||||
void G_SaveGame(UINT32 slot, INT16 mapnum)
|
||||
void G_SaveGame(void)
|
||||
{
|
||||
boolean saved;
|
||||
char savename[256] = "";
|
||||
const char *backup;
|
||||
savebuffer_t save = {0};
|
||||
|
||||
if (marathonmode)
|
||||
if (makelivebackup)
|
||||
strcpy(savename, liveeventbackup);
|
||||
else
|
||||
sprintf(savename, savegamename, slot);
|
||||
backup = va("%s",savename);
|
||||
sprintf(savename, savegamename, cursaveslot);
|
||||
|
||||
gameaction = ga_nothing;
|
||||
{
|
||||
|
|
@ -5791,138 +5736,24 @@ void G_SaveGame(UINT32 slot, INT16 mapnum)
|
|||
}
|
||||
|
||||
memset(name, 0, sizeof (name));
|
||||
sprintf(name, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
sprintf(name, (makelivebackup ? "back-up %d" : "version %d"), VERSION);
|
||||
WRITEMEM(save.p, name, VERSIONSIZE);
|
||||
|
||||
P_SaveGame(&save, mapnum);
|
||||
if (marathonmode)
|
||||
{
|
||||
UINT32 writetime = marathontime;
|
||||
if (!(marathonmode & MA_INGAME))
|
||||
writetime += TICRATE*5; // live event backup penalty because we don't know how long it takes to get to the next map
|
||||
WRITEUINT32(save.p, writetime);
|
||||
WRITEUINT8(save.p, (marathonmode & ~MA_INIT));
|
||||
}
|
||||
P_SaveGame(&save);
|
||||
|
||||
length = save.p - save.buffer;
|
||||
saved = FIL_WriteFile(backup, save.buffer, length);
|
||||
saved = FIL_WriteFile(savename, save.buffer, length);
|
||||
P_SaveBufferFree(&save);
|
||||
}
|
||||
|
||||
gameaction = ga_nothing;
|
||||
|
||||
if (cht_debug && saved)
|
||||
CONS_Printf(M_GetText("Game saved.\n"));
|
||||
CONS_Printf(M_GetText("%s saved.\n"), savename);
|
||||
else if (!saved)
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s\n"), savename);
|
||||
}
|
||||
|
||||
#define BADSAVE goto cleanup;
|
||||
#define CHECKPOS if (save.p >= save.end) BADSAVE
|
||||
void G_SaveGameOver(UINT32 slot, boolean modifylives)
|
||||
{
|
||||
boolean saved = false;
|
||||
size_t length;
|
||||
char vcheck[VERSIONSIZE];
|
||||
char savename[255];
|
||||
const char *backup;
|
||||
savebuffer_t save = {0};
|
||||
|
||||
if (marathonmode)
|
||||
strcpy(savename, liveeventbackup);
|
||||
else
|
||||
sprintf(savename, savegamename, slot);
|
||||
backup = va("%s",savename);
|
||||
|
||||
if (P_SaveBufferFromFile(&save, savename) == false)
|
||||
{
|
||||
CONS_Printf(M_GetText("Couldn't read file %s\n"), savename);
|
||||
return;
|
||||
}
|
||||
|
||||
length = save.size;
|
||||
|
||||
{
|
||||
char temp[sizeof(timeattackfolder)];
|
||||
UINT8 *lives_p;
|
||||
SINT8 pllives;
|
||||
|
||||
// Version check
|
||||
memset(vcheck, 0, sizeof (vcheck));
|
||||
sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
if (strcmp((const char *)save.p, (const char *)vcheck)) BADSAVE
|
||||
save.p += VERSIONSIZE;
|
||||
|
||||
// P_UnArchiveMisc()
|
||||
(void)READINT16(save.p);
|
||||
CHECKPOS
|
||||
(void)READUINT16(save.p); // emeralds
|
||||
CHECKPOS
|
||||
READSTRINGN(save.p, temp, sizeof(temp)); // mod it belongs to
|
||||
if (strcmp(temp, timeattackfolder)) BADSAVE
|
||||
|
||||
// P_UnArchivePlayer()
|
||||
CHECKPOS
|
||||
(void)READUINT16(save.p);
|
||||
CHECKPOS
|
||||
|
||||
WRITEUINT8(save.p, numgameovers);
|
||||
CHECKPOS
|
||||
|
||||
lives_p = save.p;
|
||||
pllives = READSINT8(save.p); // lives
|
||||
CHECKPOS
|
||||
if (modifylives && pllives < startinglivesbalance[numgameovers])
|
||||
{
|
||||
pllives = startinglivesbalance[numgameovers];
|
||||
WRITESINT8(lives_p, pllives);
|
||||
}
|
||||
|
||||
(void)READINT32(save.p); // Score
|
||||
CHECKPOS
|
||||
(void)READINT32(save.p); // continues
|
||||
|
||||
// File end marker check
|
||||
CHECKPOS
|
||||
switch (READUINT8(save.p))
|
||||
{
|
||||
case 0xb7:
|
||||
{
|
||||
UINT8 i, banksinuse;
|
||||
CHECKPOS
|
||||
banksinuse = READUINT8(save.p);
|
||||
CHECKPOS
|
||||
if (banksinuse > NUM_LUABANKS)
|
||||
BADSAVE
|
||||
for (i = 0; i < banksinuse; i++)
|
||||
{
|
||||
(void)READINT32(save.p);
|
||||
CHECKPOS
|
||||
}
|
||||
if (READUINT8(save.p) != 0x1d)
|
||||
BADSAVE
|
||||
}
|
||||
case 0x1d:
|
||||
break;
|
||||
default:
|
||||
BADSAVE
|
||||
}
|
||||
|
||||
// done
|
||||
saved = FIL_WriteFile(backup, save.buffer, length);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (cht_debug && saved)
|
||||
CONS_Printf(M_GetText("Game saved.\n"));
|
||||
else if (!saved)
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
|
||||
|
||||
P_SaveBufferFree(&save);
|
||||
}
|
||||
#undef CHECKPOS
|
||||
#undef BADSAVE
|
||||
|
||||
//
|
||||
// G_DeferedInitNew
|
||||
// Can be called by the startup code or the menu task,
|
||||
|
|
|
|||
|
|
@ -187,16 +187,13 @@ void G_StartTitleCard(void);
|
|||
void G_PreLevelTitleCard(void);
|
||||
boolean G_IsTitleCardAvailable(void);
|
||||
|
||||
// Can be called by the startup code or M_Responder, calls P_SetupLevel.
|
||||
void G_LoadGame(UINT32 slot, INT16 mapoverride);
|
||||
void G_HandleSaveLevel(void);
|
||||
void G_SaveGame(void);
|
||||
void G_LoadGame(void);
|
||||
|
||||
void G_SaveGameData(void);
|
||||
void G_DirtyGameData(void);
|
||||
|
||||
void G_SaveGame(UINT32 slot, INT16 mapnum);
|
||||
|
||||
void G_SaveGameOver(UINT32 slot, boolean modifylives);
|
||||
|
||||
void G_SetGametype(INT16 gametype);
|
||||
char *G_PrepareGametypeConstant(const char *newgtconst);
|
||||
void G_AddTOL(UINT32 newtol, const char *tolname);
|
||||
|
|
|
|||
|
|
@ -274,6 +274,44 @@ void K_InitGrandPrixBots(void)
|
|||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_LoadGrandPrixSaveGame(void)
|
||||
|
||||
See header file for description.
|
||||
---------------------------------------------------*/
|
||||
|
||||
void K_LoadGrandPrixSaveGame(void)
|
||||
{
|
||||
if (splitscreen)
|
||||
{
|
||||
// You're not doing splitscreen runs at GDQ.
|
||||
// We are literally 14 days from code freeze
|
||||
// and I am not accomodating weird setup this
|
||||
// second in my last minute QoL feature.
|
||||
// I will *actually* fight you
|
||||
return;
|
||||
}
|
||||
|
||||
players[consoleplayer].lives = savedata.lives;
|
||||
players[consoleplayer].score = savedata.score;
|
||||
players[consoleplayer].totalring = savedata.totalring;
|
||||
|
||||
UINT8 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (savedata.bots[i].valid == false)
|
||||
continue;
|
||||
|
||||
K_SetBot(i, savedata.bots[i].skin, savedata.bots[i].difficulty, BOT_STYLE_NORMAL);
|
||||
|
||||
players[i].botvars.rival = savedata.bots[i].rival;
|
||||
players[i].score = savedata.bots[i].score;
|
||||
|
||||
players[i].spectator = !(gametyperules & GTR_BOTS) || (grandprixinfo.eventmode != GPEVENT_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static INT16 K_RivalScore(player_t *bot)
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ typedef enum
|
|||
GPEVENT_SPECIAL,
|
||||
} gpEvent_e;
|
||||
|
||||
// Please also see P_ArchiveMisc
|
||||
extern struct grandprixinfo
|
||||
{
|
||||
boolean gp; ///< If true, then we are in a Grand Prix.
|
||||
|
|
@ -100,6 +101,15 @@ UINT8 K_GetGPPlayerCount(UINT8 humans);
|
|||
void K_InitGrandPrixBots(void);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_LoadGrandPrixSaveGame(void)
|
||||
|
||||
Handles loading savedata_t info for Grand Prix context.
|
||||
---------------------------------------------------*/
|
||||
|
||||
void K_LoadGrandPrixSaveGame(void);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_UpdateGrandPrixBots(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Please also see P_ArchiveMisc
|
||||
struct gpRank_t
|
||||
{
|
||||
UINT8 players;
|
||||
|
|
|
|||
|
|
@ -116,6 +116,26 @@ static UINT8 cheatf_wrongwarp(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static UINT8 cheatf_backup(void)
|
||||
{
|
||||
/*if (modifiedgame)
|
||||
return 0;*/
|
||||
|
||||
makelivebackup = true;
|
||||
|
||||
M_ClearMenus(true);
|
||||
S_StartSound(0, sfx_kc42);
|
||||
|
||||
M_StartMessage("Live Event Mode",
|
||||
M_GetText(
|
||||
"Your progression in GP cups will be.\n"
|
||||
"backed up whenever you complete a\n"
|
||||
"round, in case of game crashes.\n"
|
||||
), NULL, MM_NOTHING, NULL, NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef DEVELOP
|
||||
static UINT8 cheatf_devmode(void)
|
||||
{
|
||||
|
|
@ -152,6 +172,16 @@ static cheatseq_t cheat_wrongwarp = {
|
|||
(UINT8[]){ SCRAMBLE('b'), SCRAMBLE('a'), SCRAMBLE('n'), SCRAMBLE('a'), SCRAMBLE('n'), SCRAMBLE('a'), 0xff }
|
||||
};
|
||||
|
||||
static cheatseq_t cheat_backup1 = {
|
||||
NULL, cheatf_backup,
|
||||
(UINT8[]){ SCRAMBLE('s'), SCRAMBLE('a'), SCRAMBLE('v'), SCRAMBLE('e'), SCRAMBLE('t'), SCRAMBLE('h'), SCRAMBLE('e'), SCRAMBLE('f'), SCRAMBLE('r'), SCRAMBLE('a'), SCRAMBLE('m'), SCRAMBLE('e'), SCRAMBLE('s'), 0xff }
|
||||
};
|
||||
|
||||
static cheatseq_t cheat_backup2 = {
|
||||
NULL, cheatf_backup,
|
||||
(UINT8[]){ SCRAMBLE('s'), SCRAMBLE('a'), SCRAMBLE('v'), SCRAMBLE('e'), SCRAMBLE('t'), SCRAMBLE('h'), SCRAMBLE('e'), SCRAMBLE('a'), SCRAMBLE('n'), SCRAMBLE('i'), SCRAMBLE('m'), SCRAMBLE('a'), SCRAMBLE('l'), SCRAMBLE('s'), 0xff }
|
||||
};
|
||||
|
||||
#ifdef DEVELOP
|
||||
static cheatseq_t cheat_devmode = {
|
||||
NULL, cheatf_devmode,
|
||||
|
|
@ -163,6 +193,8 @@ cheatseq_t *cheatseqlist[] =
|
|||
{
|
||||
&cheat_warp,
|
||||
&cheat_wrongwarp,
|
||||
&cheat_backup1,
|
||||
&cheat_backup2,
|
||||
#ifdef DEVELOP
|
||||
&cheat_devmode,
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#include "../s_sound.h"
|
||||
#include "../k_grandprix.h" // K_CanChangeRules
|
||||
#include "../m_cond.h" // Condition Sets
|
||||
#include "../r_local.h" // SplitScreen_OnChange
|
||||
#include "../m_misc.h" // FIL_FileExists
|
||||
|
||||
//#define CHARSELECT_DEVICEDEBUG
|
||||
|
||||
|
|
@ -484,9 +486,96 @@ void M_CharacterSelectInit(void)
|
|||
setup_page = 0;
|
||||
}
|
||||
|
||||
|
||||
static void M_MarathonLiveEventBackup(INT32 choice)
|
||||
{
|
||||
if (choice == MA_YES)
|
||||
{
|
||||
makelivebackup = true;
|
||||
G_LoadGame();
|
||||
|
||||
if (savedata.lives != 0)
|
||||
{
|
||||
// Only do this after confirming savegame is ok
|
||||
const UINT8 ssplayers = 0;
|
||||
|
||||
{
|
||||
CV_StealthSetValue(&cv_playercolor[0], savedata.skincolor);
|
||||
|
||||
// follower
|
||||
if (savedata.followerskin < 0 || savedata.followerskin > numfollowers)
|
||||
CV_StealthSet(&cv_follower[0], "None");
|
||||
else
|
||||
CV_StealthSet(&cv_follower[0], followers[savedata.followerskin].name);
|
||||
|
||||
// finally, call the skin[x] console command.
|
||||
// This will call SendNameAndColor which will synch everything we sent here and apply the changes!
|
||||
|
||||
CV_StealthSet(&cv_skin[0], skins[savedata.skin].name);
|
||||
|
||||
// ...actually, let's do this last - Skin_OnChange has some return-early occasions
|
||||
// follower color
|
||||
CV_SetValue(&cv_followercolor[0], savedata.followercolor);
|
||||
}
|
||||
|
||||
paused = false;
|
||||
|
||||
S_StopMusicCredit();
|
||||
|
||||
if (cv_maxconnections.value < ssplayers+1)
|
||||
CV_SetValue(&cv_maxconnections, ssplayers+1);
|
||||
|
||||
if (splitscreen != ssplayers)
|
||||
{
|
||||
splitscreen = ssplayers;
|
||||
SplitScreen_OnChange();
|
||||
}
|
||||
|
||||
const UINT8 entry = lastqueuesaved-1;
|
||||
|
||||
SV_StartSinglePlayerServer(roundqueue.entries[entry].gametype, false);
|
||||
|
||||
D_MapChange(
|
||||
roundqueue.entries[entry].mapnum + 1,
|
||||
roundqueue.entries[entry].gametype,
|
||||
roundqueue.entries[entry].encore,
|
||||
true,
|
||||
1,
|
||||
false,
|
||||
roundqueue.entries[lastqueuesaved-1].rankrestricted
|
||||
);
|
||||
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
}
|
||||
else if (choice == MA_NO)
|
||||
{
|
||||
if (FIL_FileExists(liveeventbackup)) // just in case someone deleted it while we weren't looking.
|
||||
remove(liveeventbackup);
|
||||
|
||||
M_CharacterSelect(0);
|
||||
}
|
||||
}
|
||||
|
||||
void M_CharacterSelect(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
|
||||
if (currentMenu == &MainDef
|
||||
&& FIL_FileExists(liveeventbackup))
|
||||
{
|
||||
M_StartMessage(
|
||||
"Live Event Backup",
|
||||
"A Live Event Backup was found.\n"
|
||||
"Do you want to resurrect the last run?\n"
|
||||
"(Fs in chat if we crashed on stream.)\n",
|
||||
M_MarathonLiveEventBackup,
|
||||
MM_YESNO,
|
||||
"Resume the last run",
|
||||
"Delete, play another way");
|
||||
return;
|
||||
}
|
||||
|
||||
PLAY_CharSelectDef.music = currentMenu->music;
|
||||
PLAY_CharSelectDef.prevMenu = currentMenu;
|
||||
M_SetupNextMenu(&PLAY_CharSelectDef, false);
|
||||
|
|
|
|||
259
src/p_saveg.c
259
src/p_saveg.c
|
|
@ -38,6 +38,7 @@
|
|||
#include "m_cond.h" // netUnlocked
|
||||
|
||||
// SRB2Kart
|
||||
#include "k_grandprix.h"
|
||||
#include "k_battle.h"
|
||||
#include "k_pwrlv.h"
|
||||
#include "k_terrain.h"
|
||||
|
|
@ -82,25 +83,66 @@ typedef enum
|
|||
static inline void P_ArchivePlayer(savebuffer_t *save)
|
||||
{
|
||||
const player_t *player = &players[consoleplayer];
|
||||
INT16 skininfo = player->skin;
|
||||
SINT8 pllives = player->lives;
|
||||
if (pllives < startinglivesbalance[numgameovers]) // Bump up to 3 lives if the player
|
||||
pllives = startinglivesbalance[numgameovers]; // has less than that.
|
||||
|
||||
WRITEUINT16(save->p, skininfo);
|
||||
WRITEUINT8(save->p, numgameovers);
|
||||
WRITESINT8(save->p, pllives);
|
||||
WRITESINT8(save->p, player->lives);
|
||||
WRITEUINT32(save->p, player->score);
|
||||
WRITEUINT16(save->p, player->totalring);
|
||||
|
||||
WRITEUINT8(save->p, player->skin);
|
||||
WRITEUINT16(save->p, player->skincolor);
|
||||
WRITEINT32(save->p, player->followerskin);
|
||||
WRITEUINT16(save->p, player->followercolor);
|
||||
|
||||
UINT8 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] == false)
|
||||
continue;
|
||||
if (players[i].bot == false)
|
||||
continue;
|
||||
|
||||
WRITEUINT8(save->p, i);
|
||||
|
||||
WRITEUINT8(save->p, players[i].skin);
|
||||
|
||||
WRITEUINT8(save->p, players[i].botvars.difficulty);
|
||||
WRITEUINT8(save->p, (UINT8)players[i].botvars.rival);
|
||||
|
||||
WRITEUINT32(save->p, players[i].score);
|
||||
}
|
||||
|
||||
WRITEUINT8(save->p, 0xFE);
|
||||
}
|
||||
|
||||
static inline void P_UnArchivePlayer(savebuffer_t *save)
|
||||
static boolean P_UnArchivePlayer(savebuffer_t *save)
|
||||
{
|
||||
INT16 skininfo = READUINT16(save->p);
|
||||
savedata.skin = skininfo;
|
||||
|
||||
savedata.numgameovers = READUINT8(save->p);
|
||||
savedata.lives = READSINT8(save->p);
|
||||
savedata.score = READUINT32(save->p);
|
||||
savedata.totalring = READUINT16(save->p);
|
||||
|
||||
savedata.skin = READUINT8(save->p);
|
||||
savedata.skincolor = READUINT16(save->p);
|
||||
savedata.followerskin = READINT32(save->p);
|
||||
savedata.followercolor = READUINT16(save->p);
|
||||
|
||||
if (savedata.skin >= numskins)
|
||||
return false;
|
||||
|
||||
memset(&savedata.bots, 0, sizeof(savedata.bots));
|
||||
|
||||
UINT8 pid;
|
||||
|
||||
while ((pid = READUINT8(save->p)) < MAXPLAYERS)
|
||||
{
|
||||
savedata.bots[pid].valid = true;
|
||||
savedata.bots[pid].skin = READUINT8(save->p);
|
||||
savedata.bots[pid].difficulty = READUINT8(save->p);
|
||||
savedata.bots[pid].rival = (boolean)READUINT8(save->p);
|
||||
savedata.bots[pid].score = READUINT32(save->p);
|
||||
}
|
||||
|
||||
return (pid == 0xFE);
|
||||
}
|
||||
|
||||
static void P_NetArchivePlayers(savebuffer_t *save)
|
||||
|
|
@ -1039,6 +1081,9 @@ static void P_NetUnArchiveRoundQueue(savebuffer_t *save)
|
|||
|
||||
roundqueue.position = READUINT8(save->p);
|
||||
roundqueue.size = READUINT8(save->p);
|
||||
if (roundqueue.size > ROUNDQUEUE_MAX)
|
||||
I_Error("Bad $$$.sav at illegitimate roundqueue size");
|
||||
|
||||
roundqueue.roundnum = READUINT8(save->p);
|
||||
|
||||
for (i = 0; i < roundqueue.size; i++)
|
||||
|
|
@ -5257,55 +5302,148 @@ static void P_NetUnArchiveSpecials(savebuffer_t *save)
|
|||
// =======================================================================
|
||||
// Misc
|
||||
// =======================================================================
|
||||
static inline void P_ArchiveMisc(savebuffer_t *save, INT16 mapnum)
|
||||
static inline void P_ArchiveMisc(savebuffer_t *save)
|
||||
{
|
||||
//lastmapsaved = mapnum;
|
||||
lastmaploaded = mapnum;
|
||||
UINT8 i;
|
||||
|
||||
if (gamecomplete)
|
||||
mapnum |= 8192;
|
||||
|
||||
WRITEINT16(save->p, mapnum);
|
||||
WRITEUINT16(save->p, emeralds+357);
|
||||
WRITESTRINGN(save->p, timeattackfolder, sizeof(timeattackfolder));
|
||||
|
||||
WRITEUINT8(save->p, grandprixinfo.gamespeed);
|
||||
WRITEUINT8(save->p, (UINT8)grandprixinfo.encore);
|
||||
WRITEUINT8(save->p, (UINT8)grandprixinfo.masterbots);
|
||||
|
||||
WRITEUINT16(save->p, grandprixinfo.cup->id);
|
||||
|
||||
{
|
||||
WRITEUINT8(save->p, grandprixinfo.rank.players);
|
||||
WRITEUINT8(save->p, grandprixinfo.rank.totalPlayers);
|
||||
|
||||
WRITEUINT8(save->p, grandprixinfo.rank.position);
|
||||
WRITEUINT8(save->p, grandprixinfo.rank.skin);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.winPoints);
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.totalPoints);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.laps);
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.totalLaps);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.continuesUsed);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.prisons);
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.totalPrisons);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.rings);
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.totalRings);
|
||||
|
||||
WRITEUINT8(save->p, (UINT8)grandprixinfo.rank.specialWon);
|
||||
}
|
||||
|
||||
WRITEUINT8(save->p, roundqueue.position);
|
||||
WRITEUINT8(save->p, roundqueue.size);
|
||||
WRITEUINT8(save->p, roundqueue.roundnum);
|
||||
|
||||
for (i = 0; i < roundqueue.size; i++)
|
||||
{
|
||||
WRITEUINT16(save->p, roundqueue.entries[i].mapnum);
|
||||
WRITEUINT8(save->p, roundqueue.entries[i].gametype);
|
||||
WRITEUINT8(save->p, (UINT8)roundqueue.entries[i].encore);
|
||||
WRITEUINT8(save->p, (UINT8)roundqueue.entries[i].rankrestricted);
|
||||
}
|
||||
|
||||
WRITEUINT8(save->p, (marathonmode & ~MA_INIT));
|
||||
|
||||
UINT32 writetime = marathontime;
|
||||
if (!(marathonmode & MA_INGAME))
|
||||
writetime += TICRATE*5; // live event backup penalty because we don't know how long it takes to get to the next map
|
||||
WRITEUINT32(save->p, writetime);
|
||||
}
|
||||
|
||||
static inline void P_UnArchiveSPGame(savebuffer_t *save, INT16 mapoverride)
|
||||
static boolean P_UnArchiveSPGame(savebuffer_t *save)
|
||||
{
|
||||
UINT8 i;
|
||||
char testname[sizeof(timeattackfolder)];
|
||||
|
||||
gamemap = READINT16(save->p);
|
||||
|
||||
if (mapoverride != 0)
|
||||
{
|
||||
gamemap = mapoverride;
|
||||
gamecomplete = 1;
|
||||
}
|
||||
else
|
||||
gamecomplete = 0;
|
||||
|
||||
// gamemap changed; we assume that its map header is always valid,
|
||||
// so make it so
|
||||
if (!gamemap || gamemap > nummapheaders || !mapheaderinfo[gamemap-1])
|
||||
I_Error("P_UnArchiveSPGame: Internal map ID %d not found (nummapheaders = %d)", gamemap-1, nummapheaders);
|
||||
|
||||
//lastmapsaved = gamemap;
|
||||
lastmaploaded = gamemap;
|
||||
|
||||
savedata.emeralds = READUINT16(save->p)-357;
|
||||
|
||||
READSTRINGN(save->p, testname, sizeof(testname));
|
||||
|
||||
if (strcmp(testname, timeattackfolder))
|
||||
{
|
||||
if (modifiedgame)
|
||||
I_Error("Save game not for this modification.");
|
||||
else
|
||||
I_Error("This save file is for a particular mod, it cannot be used with the regular game.");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(playeringame, 0, sizeof(*playeringame));
|
||||
playeringame[consoleplayer] = true;
|
||||
// TODO do not work off grandprixinfo/roundqueue directly
|
||||
// This is only strictly necessary if we ever re-add a save
|
||||
// select screen or something, for live event backup only
|
||||
// it's *fine* and, more importantly, shippable
|
||||
|
||||
memset(&grandprixinfo, 0, sizeof(grandprixinfo));
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
|
||||
grandprixinfo.gamespeed = READUINT8(save->p);
|
||||
grandprixinfo.encore = (boolean)READUINT8(save->p);
|
||||
grandprixinfo.masterbots = (boolean)READUINT8(save->p);
|
||||
|
||||
UINT16 cupid = READUINT16(save->p);
|
||||
grandprixinfo.cup = kartcupheaders;
|
||||
while (grandprixinfo.cup)
|
||||
{
|
||||
if (grandprixinfo.cup->id == cupid)
|
||||
break;
|
||||
grandprixinfo.cup = grandprixinfo.cup->next;
|
||||
}
|
||||
|
||||
if (!grandprixinfo.cup)
|
||||
return false;
|
||||
|
||||
{
|
||||
grandprixinfo.rank.players = READUINT8(save->p);
|
||||
grandprixinfo.rank.totalPlayers = READUINT8(save->p);
|
||||
|
||||
grandprixinfo.rank.position = READUINT8(save->p);
|
||||
grandprixinfo.rank.skin = READUINT8(save->p);
|
||||
|
||||
grandprixinfo.rank.winPoints = READUINT32(save->p);
|
||||
grandprixinfo.rank.totalPoints = READUINT32(save->p);
|
||||
|
||||
grandprixinfo.rank.laps = READUINT32(save->p);
|
||||
grandprixinfo.rank.totalLaps = READUINT32(save->p);
|
||||
|
||||
grandprixinfo.rank.continuesUsed = READUINT32(save->p);
|
||||
|
||||
grandprixinfo.rank.prisons = READUINT32(save->p);
|
||||
grandprixinfo.rank.totalPrisons = READUINT32(save->p);
|
||||
|
||||
grandprixinfo.rank.rings = READUINT32(save->p);
|
||||
grandprixinfo.rank.totalRings = READUINT32(save->p);
|
||||
|
||||
grandprixinfo.rank.specialWon = (boolean)READUINT8(save->p);
|
||||
}
|
||||
|
||||
memset(&roundqueue, 0, sizeof(roundqueue));
|
||||
|
||||
roundqueue.position = READUINT8(save->p);
|
||||
roundqueue.size = READUINT8(save->p);
|
||||
if (roundqueue.size > ROUNDQUEUE_MAX
|
||||
|| roundqueue.position > roundqueue.size)
|
||||
return false;
|
||||
|
||||
roundqueue.roundnum = READUINT8(save->p);
|
||||
|
||||
for (i = 0; i < roundqueue.size; i++)
|
||||
{
|
||||
roundqueue.entries[i].mapnum = READUINT16(save->p);
|
||||
if (roundqueue.entries[i].mapnum >= nummapheaders)
|
||||
return false;
|
||||
|
||||
roundqueue.entries[i].gametype = READUINT8(save->p);
|
||||
roundqueue.entries[i].encore = (boolean)READUINT8(save->p);
|
||||
roundqueue.entries[i].rankrestricted = (boolean)READUINT8(save->p);
|
||||
}
|
||||
|
||||
marathonmode = READUINT8(save->p);
|
||||
marathontime = READUINT32(save->p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
|
||||
|
|
@ -5718,9 +5856,9 @@ static inline void P_NetUnArchiveRNG(savebuffer_t *save)
|
|||
}
|
||||
}
|
||||
|
||||
void P_SaveGame(savebuffer_t *save, INT16 mapnum)
|
||||
void P_SaveGame(savebuffer_t *save)
|
||||
{
|
||||
P_ArchiveMisc(save, mapnum);
|
||||
P_ArchiveMisc(save);
|
||||
P_ArchivePlayer(save);
|
||||
P_ArchiveLuabanksAndConsistency(save);
|
||||
}
|
||||
|
|
@ -5775,7 +5913,7 @@ void P_SaveNetGame(savebuffer_t *save, boolean resending)
|
|||
P_ArchiveLuabanksAndConsistency(save);
|
||||
}
|
||||
|
||||
boolean P_LoadGame(savebuffer_t *save, INT16 mapoverride)
|
||||
boolean P_LoadGame(savebuffer_t *save)
|
||||
{
|
||||
if (gamestate == GS_INTERMISSION)
|
||||
Y_EndIntermission();
|
||||
|
|
@ -5783,17 +5921,26 @@ boolean P_LoadGame(savebuffer_t *save, INT16 mapoverride)
|
|||
Y_EndVote();
|
||||
G_SetGamestate(GS_NULL); // should be changed in P_UnArchiveMisc
|
||||
|
||||
P_UnArchiveSPGame(save, mapoverride);
|
||||
P_UnArchivePlayer(save);
|
||||
if (!P_UnArchiveSPGame(save))
|
||||
goto badloadgame;
|
||||
if (!P_UnArchivePlayer(save))
|
||||
goto badloadgame;
|
||||
|
||||
if (!P_UnArchiveLuabanksAndConsistency(save))
|
||||
return false;
|
||||
goto badloadgame;
|
||||
|
||||
// Only do this after confirming savegame is ok
|
||||
G_DeferedInitNew(false, gamemap, savedata.skin, 0, true);
|
||||
COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this
|
||||
lastqueuesaved = roundqueue.position;
|
||||
|
||||
return true;
|
||||
|
||||
badloadgame:
|
||||
// these are the side effects of P_UnarchiveSPGame
|
||||
savedata.lives = 0;
|
||||
roundqueue.size = 0;
|
||||
grandprixinfo.gp = false;
|
||||
marathonmode = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean P_LoadNetGame(savebuffer_t *save, boolean reloading)
|
||||
|
|
|
|||
|
|
@ -31,20 +31,34 @@ extern "C" {
|
|||
// Persistent storage/archiving.
|
||||
// These are the load / save game routines.
|
||||
|
||||
void P_SaveGame(savebuffer_t *save, INT16 mapnum);
|
||||
void P_SaveGame(savebuffer_t *save);
|
||||
void P_SaveNetGame(savebuffer_t *save, boolean resending);
|
||||
boolean P_LoadGame(savebuffer_t *save, INT16 mapoverride);
|
||||
boolean P_LoadGame(savebuffer_t *save);
|
||||
boolean P_LoadNetGame(savebuffer_t *save, boolean reloading);
|
||||
|
||||
mobj_t *P_FindNewPosition(UINT32 oldposition);
|
||||
|
||||
struct savedata_bot_s
|
||||
{
|
||||
boolean valid;
|
||||
UINT8 skin;
|
||||
UINT8 difficulty;
|
||||
boolean rival;
|
||||
UINT32 score;
|
||||
};
|
||||
|
||||
struct savedata_t
|
||||
{
|
||||
UINT32 score;
|
||||
SINT8 lives;
|
||||
UINT16 totalring;
|
||||
|
||||
UINT8 skin;
|
||||
INT32 score;
|
||||
INT32 lives;
|
||||
UINT16 emeralds;
|
||||
UINT8 numgameovers;
|
||||
UINT16 skincolor;
|
||||
INT32 followerskin;
|
||||
UINT16 followercolor;
|
||||
|
||||
struct savedata_bot_s bots[MAXPLAYERS];
|
||||
};
|
||||
|
||||
extern savedata_t savedata;
|
||||
|
|
|
|||
|
|
@ -7734,7 +7734,12 @@ static void P_InitGametype(void)
|
|||
|
||||
if (grandprixinfo.gp == true)
|
||||
{
|
||||
if (grandprixinfo.initalize == true)
|
||||
if (savedata.lives > 0)
|
||||
{
|
||||
K_LoadGrandPrixSaveGame();
|
||||
savedata.lives = 0;
|
||||
}
|
||||
else if (grandprixinfo.initalize == true)
|
||||
{
|
||||
K_InitGrandPrixRank(&grandprixinfo.rank);
|
||||
K_InitGrandPrixBots();
|
||||
|
|
@ -8152,15 +8157,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
R_InitMobjInterpolators();
|
||||
P_InitCachedActions();
|
||||
|
||||
if (!fromnetsave && savedata.lives > 0)
|
||||
{
|
||||
numgameovers = savedata.numgameovers;
|
||||
players[consoleplayer].lives = savedata.lives;
|
||||
players[consoleplayer].score = savedata.score;
|
||||
emeralds = savedata.emeralds;
|
||||
savedata.lives = 0;
|
||||
}
|
||||
|
||||
// internal game map
|
||||
maplumpname = mapheaderinfo[gamemap-1]->lumpname;
|
||||
lastloadedmaplumpnum = mapheaderinfo[gamemap-1]->lumpnum;
|
||||
|
|
@ -8311,22 +8307,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
|
||||
P_MapEnd(); // tm.thing is no longer needed from this point onwards
|
||||
|
||||
// Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap...
|
||||
if (gamestate == GS_LEVEL)
|
||||
{
|
||||
if (!lastmaploaded) // Start a new game?
|
||||
{
|
||||
// I'd love to do this in the menu code instead of here, but everything's a mess and I can't guarantee saving proper player struct info before the first act's started. You could probably refactor it, but it'd be a lot of effort. Easier to just work off known good code. ~toast 22/06/2020
|
||||
if (!(ultimatemode || netgame || multiplayer || demo.playback || demo.recording || metalrecording || modeattacking || marathonmode)
|
||||
&& !usedCheats && cursaveslot > 0)
|
||||
{
|
||||
G_SaveGame((UINT32)cursaveslot, gamemap);
|
||||
}
|
||||
// If you're looking for saving sp file progression (distinct from G_SaveGameOver), check G_DoCompleted.
|
||||
}
|
||||
lastmaploaded = gamemap; // HAS to be set after saving!!
|
||||
}
|
||||
|
||||
if (!fromnetsave)
|
||||
{
|
||||
INT32 buf = gametic % BACKUPTICS;
|
||||
|
|
@ -8382,6 +8362,8 @@ void P_PostLoadLevel(void)
|
|||
|
||||
P_RunCachedActions();
|
||||
|
||||
G_HandleSaveLevel();
|
||||
|
||||
if (marathonmode & MA_INGAME)
|
||||
{
|
||||
marathonmode &= ~MA_INIT;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue