mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2025-01-18 15:32:33 +00:00
Merge branch 'special_save' into 'next'
Special saves! (Resolves #162) Closes #162 See merge request STJr/SRB2!1016
This commit is contained in:
commit
696f9707fc
5 changed files with 110 additions and 96 deletions
152
src/g_game.c
152
src/g_game.c
|
@ -3711,6 +3711,24 @@ static void G_UpdateVisited(void)
|
|||
}
|
||||
}
|
||||
|
||||
static boolean CanSaveLevel(INT32 mapnum)
|
||||
{
|
||||
// You can never save in a special stage.
|
||||
if (G_IsSpecialStage(mapnum))
|
||||
return false;
|
||||
|
||||
// If the game is complete for this save slot, then any level can save!
|
||||
if (gamecomplete)
|
||||
return true;
|
||||
|
||||
// Be kind with Marathon Mode live event backups.
|
||||
if (marathonmode)
|
||||
return true;
|
||||
|
||||
// Any levels that have the savegame flag can save normally.
|
||||
return (mapheaderinfo[mapnum-1] && (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME));
|
||||
}
|
||||
|
||||
//
|
||||
// G_DoCompleted
|
||||
//
|
||||
|
@ -3755,65 +3773,65 @@ static void G_DoCompleted(void)
|
|||
nextmap = 1100-1; // No infinite loop for you
|
||||
}
|
||||
|
||||
// Remember last map for when you come out of the special stage.
|
||||
if (!spec)
|
||||
lastmap = nextmap;
|
||||
|
||||
// If nextmap is actually going to get used, make sure it points to
|
||||
// a map of the proper gametype -- skip levels that don't support
|
||||
// the current gametype. (Helps avoid playing boss levels in Race,
|
||||
// for instance).
|
||||
if (!token && !spec
|
||||
&& (nextmap >= 0 && nextmap < NUMMAPS))
|
||||
if (!spec)
|
||||
{
|
||||
register INT16 cm = nextmap;
|
||||
UINT32 tolflag = G_TOLFlag(gametype);
|
||||
UINT8 visitedmap[(NUMMAPS+7)/8];
|
||||
|
||||
memset(visitedmap, 0, sizeof (visitedmap));
|
||||
|
||||
while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag))
|
||||
if (nextmap >= 0 && nextmap < NUMMAPS)
|
||||
{
|
||||
visitedmap[cm/8] |= (1<<(cm&7));
|
||||
if (!mapheaderinfo[cm])
|
||||
cm = -1; // guarantee error execution
|
||||
else if (marathonmode && mapheaderinfo[cm]->marathonnext)
|
||||
cm = (INT16)(mapheaderinfo[cm]->marathonnext-1);
|
||||
else
|
||||
cm = (INT16)(mapheaderinfo[cm]->nextlevel-1);
|
||||
register INT16 cm = nextmap;
|
||||
UINT32 tolflag = G_TOLFlag(gametype);
|
||||
UINT8 visitedmap[(NUMMAPS+7)/8];
|
||||
|
||||
if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error)
|
||||
memset(visitedmap, 0, sizeof (visitedmap));
|
||||
|
||||
while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag))
|
||||
{
|
||||
cm = nextmap; //Start the loop again so that the error checking below is executed.
|
||||
visitedmap[cm/8] |= (1<<(cm&7));
|
||||
if (!mapheaderinfo[cm])
|
||||
cm = -1; // guarantee error execution
|
||||
else if (marathonmode && mapheaderinfo[cm]->marathonnext)
|
||||
cm = (INT16)(mapheaderinfo[cm]->marathonnext-1);
|
||||
else
|
||||
cm = (INT16)(mapheaderinfo[cm]->nextlevel-1);
|
||||
|
||||
//Make sure the map actually exists before you try to go to it!
|
||||
if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR))
|
||||
if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1);
|
||||
cm = 0;
|
||||
cm = nextmap; //Start the loop again so that the error checking below is executed.
|
||||
|
||||
//Make sure the map actually exists before you try to go to it!
|
||||
if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR))
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1);
|
||||
cm = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar
|
||||
{
|
||||
// We got stuck in a loop, came back to the map we started on
|
||||
// without finding one supporting the current gametype.
|
||||
// Thus, print a warning, and just use this map anyways.
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar
|
||||
{
|
||||
// We got stuck in a loop, came back to the map we started on
|
||||
// without finding one supporting the current gametype.
|
||||
// Thus, print a warning, and just use this map anyways.
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1);
|
||||
break;
|
||||
}
|
||||
nextmap = cm;
|
||||
}
|
||||
nextmap = cm;
|
||||
|
||||
// wrap around in race
|
||||
if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
|
||||
nextmap = (INT16)(spstage_start-1);
|
||||
|
||||
if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1)
|
||||
I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1);
|
||||
|
||||
lastmap = nextmap; // Remember last map for when you come out of the special stage.
|
||||
}
|
||||
|
||||
if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1)
|
||||
I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1);
|
||||
|
||||
// wrap around in race
|
||||
if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
|
||||
nextmap = (INT16)(spstage_start-1);
|
||||
|
||||
if ((gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token)))
|
||||
{
|
||||
token--;
|
||||
|
@ -3851,25 +3869,6 @@ static void G_DoCompleted(void)
|
|||
if (nextmap < NUMMAPS && !mapheaderinfo[nextmap])
|
||||
P_AllocMapHeader(nextmap);
|
||||
|
||||
// do this before going to the intermission or starting a custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c
|
||||
if (nextmap >= 1100-1)
|
||||
{
|
||||
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 ((!modifiedgame || savemoddata) && !(netgame || multiplayer))
|
||||
G_SaveGame((UINT32)cursaveslot);
|
||||
}
|
||||
}
|
||||
|
||||
// If the current gametype has no intermission screen set, then don't start it.
|
||||
Y_DetermineIntermissionType();
|
||||
|
||||
|
@ -3884,6 +3883,29 @@ static void G_DoCompleted(void)
|
|||
Y_StartIntermission();
|
||||
G_UpdateVisited();
|
||||
}
|
||||
|
||||
// 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 >= 1100-1)
|
||||
{
|
||||
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 ((!modifiedgame || savemoddata) && !(netgame || multiplayer || ultimatemode || demorecording || metalrecording || modeattacking))
|
||||
G_SaveGame((UINT32)cursaveslot, spstage_start);
|
||||
}
|
||||
}
|
||||
// and doing THIS here means you don't lose your progress if you close the game mid-intermission
|
||||
else if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking)
|
||||
&& (!modifiedgame || savemoddata) && cursaveslot > 0 && CanSaveLevel(lastmap+1))
|
||||
G_SaveGame((UINT32)cursaveslot, lastmap+1); // not nextmap+1 to route around special stages
|
||||
}
|
||||
|
||||
// See also F_EndCutscene, the only other place which handles intra-map/ending transitions
|
||||
|
@ -3902,7 +3924,7 @@ void G_AfterIntermission(void)
|
|||
|
||||
HU_ClearCEcho();
|
||||
|
||||
if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1 && !(marathonmode & MA_NOCUTSCENES)) // Start a custom cutscene.
|
||||
if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene.
|
||||
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
|
||||
else
|
||||
{
|
||||
|
@ -4445,7 +4467,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
// G_SaveGame
|
||||
// Saves your game.
|
||||
//
|
||||
void G_SaveGame(UINT32 slot)
|
||||
void G_SaveGame(UINT32 slot, INT16 mapnum)
|
||||
{
|
||||
boolean saved;
|
||||
char savename[256] = "";
|
||||
|
@ -4473,7 +4495,7 @@ void G_SaveGame(UINT32 slot)
|
|||
sprintf(name, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
WRITEMEM(save_p, name, VERSIONSIZE);
|
||||
|
||||
P_SaveGame();
|
||||
P_SaveGame(mapnum);
|
||||
if (marathonmode)
|
||||
{
|
||||
WRITEUINT32(save_p, marathontime);
|
||||
|
|
|
@ -166,7 +166,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride);
|
|||
|
||||
void G_SaveGameData(void);
|
||||
|
||||
void G_SaveGame(UINT32 slot);
|
||||
void G_SaveGame(UINT32 slot, INT16 mapnum);
|
||||
|
||||
void G_SaveGameOver(UINT32 slot, boolean modifylives);
|
||||
|
||||
|
|
|
@ -3785,16 +3785,15 @@ static void P_NetUnArchiveSpecials(void)
|
|||
// =======================================================================
|
||||
// Misc
|
||||
// =======================================================================
|
||||
static inline void P_ArchiveMisc(void)
|
||||
static inline void P_ArchiveMisc(INT16 mapnum)
|
||||
{
|
||||
//lastmapsaved = mapnum;
|
||||
lastmaploaded = mapnum;
|
||||
|
||||
if (gamecomplete)
|
||||
WRITEINT16(save_p, gamemap | 8192);
|
||||
else
|
||||
WRITEINT16(save_p, gamemap);
|
||||
|
||||
//lastmapsaved = gamemap;
|
||||
lastmaploaded = gamemap;
|
||||
mapnum |= 8192;
|
||||
|
||||
WRITEINT16(save_p, mapnum);
|
||||
WRITEUINT16(save_p, emeralds+357);
|
||||
WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder));
|
||||
}
|
||||
|
@ -4035,9 +4034,9 @@ static inline boolean P_UnArchiveLuabanksAndConsistency(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
void P_SaveGame(void)
|
||||
void P_SaveGame(INT16 mapnum)
|
||||
{
|
||||
P_ArchiveMisc();
|
||||
P_ArchiveMisc(mapnum);
|
||||
P_ArchivePlayer();
|
||||
P_ArchiveLuabanksAndConsistency();
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
// Persistent storage/archiving.
|
||||
// These are the load / save game routines.
|
||||
|
||||
void P_SaveGame(void);
|
||||
void P_SaveGame(INT16 mapnum);
|
||||
void P_SaveNetGame(void);
|
||||
boolean P_LoadGame(INT16 mapoverride);
|
||||
boolean P_LoadNetGame(void);
|
||||
|
|
|
@ -3297,21 +3297,6 @@ static void P_InitCamera(void)
|
|||
}
|
||||
}
|
||||
|
||||
static boolean CanSaveLevel(INT32 mapnum)
|
||||
{
|
||||
if (ultimatemode) // never save in ultimate (probably redundant with cursaveslot also being checked)
|
||||
return false;
|
||||
|
||||
if (G_IsSpecialStage(mapnum) // don't save in special stages
|
||||
|| mapnum == lastmaploaded) // don't save if the last map loaded was this one
|
||||
return false;
|
||||
|
||||
// Any levels that have the savegame flag can save normally.
|
||||
// If the game is complete for this save slot, then any level can save!
|
||||
// On the other side of the spectrum, if lastmaploaded is 0, then the save file has only just been created and needs to save ASAP!
|
||||
return (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME || (gamecomplete != 0) || marathonmode || !lastmaploaded);
|
||||
}
|
||||
|
||||
static void P_RunSpecialStageWipe(void)
|
||||
{
|
||||
tic_t starttime = I_GetTime();
|
||||
|
@ -3748,11 +3733,19 @@ boolean P_LoadLevel(boolean fromnetsave)
|
|||
|
||||
P_RunCachedActions();
|
||||
|
||||
if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || players[consoleplayer].lives <= 0)
|
||||
&& (!modifiedgame || savemoddata) && cursaveslot > 0 && CanSaveLevel(gamemap))
|
||||
G_SaveGame((UINT32)cursaveslot);
|
||||
|
||||
lastmaploaded = gamemap; // HAS to be set after saving!!
|
||||
// Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap...
|
||||
if (!titlemapinaction)
|
||||
{
|
||||
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 || demoplayback || demorecording || metalrecording || modeattacking)
|
||||
&& (!modifiedgame || savemoddata) && 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) // uglier hack
|
||||
{ // to make a newly loaded level start on the second frame.
|
||||
|
|
Loading…
Reference in a new issue