Gamedata-related changes + Crash tracking

- Combine multiple adjacent saves
    - Generally could happen during game ticking, combined via gamedata->deferredstate
    - DEFINITELY happened in splitscreen PWR handling, adjust those loops directly
- Write "dirty" state via gamedata->crashflags on everything except safe, intentful unloads
    - Add UC_CRASH, which unlocks dependent on the above "dirty" state being present at gamedata load
    - We can use this for something more useful and less funny later.
- Play "O_LOSERC" on the menu, starting with the Challenges screen, if a UC_CRASH condition has been met.
This commit is contained in:
toaster 2023-03-03 13:38:31 +00:00
parent b4217e1afd
commit 53ce2e4287
17 changed files with 117 additions and 40 deletions

View file

@ -977,6 +977,9 @@ void D_ClearState(void)
cursongcredit.def = NULL;
if (gamedata->deferredsave)
G_SaveGameData(true);
G_SetGamestate(GS_NULL);
wipegamestate = GS_NULL;
}

View file

@ -2482,6 +2482,11 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
return;
}
}
else if (fastcmp(params[0], "CRASH"))
{
//PARAMCHECK(1);
ty = UC_CRASH;
}
else if (fastcmp(params[0], "AND"))
{
//PARAMCHECK(1);
@ -2689,7 +2694,7 @@ void readmaincfg(MYFILE *f, boolean mainfile)
if (!GoodDataFileName(word2))
I_Error("Maincfg: bad data file name '%s'\n", word2);
G_SaveGameData();
G_SaveGameData(false); // undirty your old gamedata
strlcpy(gamedatafilename, word2, sizeof (gamedatafilename));
strlwr(gamedatafilename);
savemoddata = true;

View file

@ -1081,7 +1081,7 @@ void F_GameEvaluationTicker(void)
++gamedata->timesBeaten;
M_UpdateUnlockablesAndExtraEmblems(true);
G_SaveGameData();
G_SaveGameData(true);
}
else
{

View file

@ -649,7 +649,7 @@ void G_UpdateRecords(void)
}
M_UpdateUnlockablesAndExtraEmblems(true);
G_SaveGameData();
gamedata->deferredsave = true;
}
//
@ -2282,19 +2282,6 @@ static inline void G_PlayerFinishLevel(INT32 player)
p->starpostnum = 0;
memset(&p->respawn, 0, sizeof (p->respawn));
// SRB2kart: Increment the "matches played" counter.
if (player == consoleplayer)
{
if (legitimateexit && !demo.playback && !mapreset) // (yes you're allowed to unlock stuff this way when the game is modified)
{
gamedata->matchesplayed++;
M_UpdateUnlockablesAndExtraEmblems(true);
G_SaveGameData();
}
legitimateexit = false;
}
}
//
@ -3697,7 +3684,7 @@ static void G_UpdateVisited(void)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
M_UpdateUnlockablesAndExtraEmblems(true);
G_SaveGameData();
G_SaveGameData(true);
}
static boolean CanSaveLevel(INT32 mapnum)
@ -4005,6 +3992,19 @@ static void G_DoCompleted(void)
if (modeattacking && pausedelay)
pausedelay = 0;
if (legitimateexit && !demo.playback && !mapreset) // (yes you're allowed to unlock stuff this way when the game is modified)
{
// Done before forced addition of PF_NOCONTEST to make UCRP_NOCONTEST harder to achieve
gamedata->matchesplayed++;
M_UpdateUnlockablesAndExtraEmblems(true);
gamedata->deferredsave = true;
}
if (gamedata->deferredsave)
G_SaveGameData(true);
legitimateexit = false;
gameaction = ga_nothing;
if (metalplayback)
@ -4317,7 +4317,7 @@ void G_LoadGameSettings(void)
}
#define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual
#define GD_VERSIONMINOR 1 // Change every format update
#define GD_VERSIONMINOR 2 // Change every format update
static const char *G_GameDataFolder(void)
{
@ -4351,6 +4351,7 @@ void G_LoadGameData(void)
gamedata->totalplaytime = 0; // total play time (separate from all)
gamedata->matchesplayed = 0; // SRB2Kart: matches played & finished
gamedata->crashflags = 0;
if (M_CheckParm("-nodata"))
{
@ -4398,6 +4399,13 @@ void G_LoadGameData(void)
gamedata->totalplaytime = READUINT32(save.p);
gamedata->matchesplayed = READUINT32(save.p);
if (versionMinor > 1)
{
gamedata->crashflags = READUINT8(save.p);
if (gamedata->crashflags & GDCRASH_LAST)
gamedata->crashflags |= GDCRASH_ANY;
}
{
// Quick & dirty hash for what mod this save file is for.
UINT32 modID = READUINT32(save.p);
@ -4543,13 +4551,15 @@ void G_LoadGameData(void)
// G_SaveGameData
// Saves the main data file, which stores information such as emblems found, etc.
void G_SaveGameData(void)
void G_SaveGameData(boolean dirty)
{
size_t length;
INT32 i, j;
UINT8 btemp;
savebuffer_t save = {0};
gamedata->deferredsave = false;
if (!gamedata->loaded)
return; // If never loaded (-nodata), don't save
@ -4561,7 +4571,7 @@ void G_SaveGameData(void)
return;
}
length = (4+1+4+4+1+(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+4+4+2);
length = (4+1+4+4+1+4+(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+4+4+2);
if (gamedata->challengegrid)
{
length += gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT;
@ -4580,6 +4590,14 @@ void G_SaveGameData(void)
WRITEUINT8(save.p, GD_VERSIONMINOR); // 1
WRITEUINT32(save.p, gamedata->totalplaytime); // 4
WRITEUINT32(save.p, gamedata->matchesplayed); // 4
{
UINT8 crashflags = (gamedata->crashflags & GDCRASH_ANY);
if (dirty)
crashflags |= GDCRASH_LAST;
WRITEUINT8(save.p, crashflags); // 1
}
WRITEUINT32(save.p, quickncasehash(timeattackfolder, 64));
// To save space, use one bit per collected/achieved/unlocked flag

View file

@ -173,7 +173,7 @@ 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_SaveGameData(void);
void G_SaveGameData(boolean dirty);
void G_SaveGame(UINT32 slot, INT16 mapnum);

View file

@ -15,6 +15,7 @@
#include "v_video.h"
#include "f_finale.h"
#include "m_misc.h"
#include "m_cond.h"
#ifdef PC_DOS
#include <stdio.h> // for snprintf
@ -338,9 +339,17 @@ boolean M_Responder(event_t *ev)
void M_PlayMenuJam(void)
{
menu_t *refMenu = (menuactive ? currentMenu : restoreMenu);
static boolean loserclubpermitted = false;
boolean loserclub = (loserclubpermitted && (gamedata->crashflags & GDCRASH_LOSERCLUB));
if (challengesmenu.pending)
{
S_StopMusic();
cursongcredit.def = NULL;
loserclubpermitted = true;
return;
}
if (Playing())
return;
@ -351,15 +360,27 @@ void M_PlayMenuJam(void)
{
S_StopMusic();
cursongcredit.def = NULL;
return;
}
else
else if (!loserclub)
{
if (NotCurrentlyPlaying(refMenu->music))
{
S_ChangeMusicInternal(refMenu->music, true);
S_ShowMusicCredit();
}
return;
}
}
if (loserclub)
{
if (refMenu != NULL && NotCurrentlyPlaying("LOSERC"))
{
S_ChangeMusicInternal("LOSERC", true);
S_ShowMusicCredit();
}
return;
}

View file

@ -397,6 +397,7 @@ void K_CashInPowerLevels(void)
{
SINT8 powerType = K_UsingPowerLevels();
UINT8 i;
boolean gamedataupdate;
//CONS_Printf("\n========\n");
//CONS_Printf("Cashing in power level changes...\n");
@ -417,14 +418,19 @@ void K_CashInPowerLevels(void)
{
pr->powerlevels[powerType] = clientpowerlevels[i][powerType];
M_UpdateUnlockablesAndExtraEmblems(true);
G_SaveGameData();
gamedataupdate = true;
}
}
clientPowerAdd[i] = 0;
}
if (gamedataupdate)
{
M_UpdateUnlockablesAndExtraEmblems(true);
G_SaveGameData(true);
}
//CONS_Printf("========\n");
}
@ -638,6 +644,6 @@ void K_PlayerForfeit(UINT8 playerNum, boolean pointLoss)
pr->powerlevels[powerType] = yourPower + inc;
M_UpdateUnlockablesAndExtraEmblems(true);
G_SaveGameData();
G_SaveGameData(true);
}
}

View file

@ -486,6 +486,7 @@ void M_ClearSecrets(void)
gamedata->challengegridwidth = 0;
gamedata->timesBeaten = 0;
gamedata->crashflags = 0;
// Re-unlock any always unlocked things
M_UpdateUnlockablesAndExtraEmblems(false);
@ -592,6 +593,13 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
return gamedata->unlocked[cn->requirement-1];
case UC_CONDITIONSET: // requires condition set x to already be achieved
return M_Achieved(cn->requirement-1);
case UC_CRASH:
if (gamedata->crashflags & (GDCRASH_LAST|GDCRASH_ANY))
{
gamedata->crashflags |= GDCRASH_LOSERCLUB;
return true;
}
return false;
case UC_AND: // Just for string building
return true;
@ -859,6 +867,10 @@ static const char *M_GetConditionString(condition_t *cn)
gamedata->unlocked[cn->requirement-1]
? unlockables[cn->requirement-1].name
: "???");
case UC_CRASH:
if (gamedata->crashflags & (GDCRASH_LAST|GDCRASH_ANY))
return "Relaunch \"Dr. Robotnik's Ring Racers\" after a crash";
return NULL;
case UC_AND:
return "&";

View file

@ -43,6 +43,8 @@ typedef enum
UC_UNLOCKABLE, // UNLOCKABLE [unlockable number]
UC_CONDITIONSET, // CONDITIONSET [condition set number]
UC_CRASH, // Hee ho !
UC_AND, // Just for string building
UCRP_REQUIRESPLAYING, // All conditions below this can only be checked if (Playing() && gamestate == GS_LEVEL).
@ -177,12 +179,17 @@ typedef enum
#endif
#define challengegridloops (gamedata->challengegridwidth >= CHALLENGEGRIDLOOPWIDTH)
#define GDCRASH_LAST 0x01
#define GDCRASH_ANY 0x02
#define GDCRASH_LOSERCLUB 0x04
// GAMEDATA STRUCTURE
// Everything that would get saved in gamedata.dat
struct gamedata_t
{
// WHENEVER OR NOT WE'RE READY TO SAVE
boolean loaded;
boolean deferredsave;
// CONDITION SETS ACHIEVED
boolean achieved[MAXCONDITIONSETS];
@ -204,6 +211,9 @@ struct gamedata_t
// PLAY TIME
UINT32 totalplaytime;
UINT32 matchesplayed;
// Funny
UINT8 crashflags;
};
extern gamedata_t *gamedata;

View file

@ -324,7 +324,7 @@ void M_ChallengesTick(void)
{
// All done! Let's save the unlocks we've busted open.
challengesmenu.pending = false;
G_SaveGameData();
G_SaveGameData(true);
}
}
else if (challengesmenu.fade < 5)

View file

@ -554,7 +554,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
gamedata->collected[special->health-1] = gotcollected = true;
if (!M_UpdateUnlockablesAndExtraEmblems(true))
S_StartSound(NULL, sfx_ncitem);
G_SaveGameData();
gamedata->deferredsave = true;
}
if (netgame)

View file

@ -7444,6 +7444,7 @@ static void P_InitGametype(void)
// Started a game? Move on to the next jam when you go back to the title screen
CV_SetValue(&cv_menujam_update, 1);
gamedata->crashflags &= ~GDCRASH_LOSERCLUB;
}
struct minimapinfo minimapinfo;
@ -7936,7 +7937,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
mapheaderinfo[gamemap-1]->mapvisited |= MV_VISITED;
M_UpdateUnlockablesAndExtraEmblems(true);
G_SaveGameData();
G_SaveGameData(true);
}
G_AddMapToBuffer(gamemap-1);

View file

@ -3310,7 +3310,7 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
// Unlocked something?
if (M_UpdateUnlockablesAndExtraEmblems(true))
{
G_SaveGameData(); // only save if unlocked something
gamedata->deferredsave = true; // only save if unlocked something
}
}
}

View file

@ -632,8 +632,9 @@ void P_Ticker(boolean run)
ps_playerthink_time = I_GetPreciseTime() - ps_playerthink_time;
// TODO would this be laggy with more conditions in play...
if (M_UpdateUnlockablesAndExtraEmblems(true))
G_SaveGameData();
if ((!demo.playback && M_UpdateUnlockablesAndExtraEmblems(true))
|| gamedata->deferredsave)
G_SaveGameData(true);
}
// Keep track of how long they've been playing!

View file

@ -1737,7 +1737,7 @@ void I_Quit(void)
if (Playing())
K_PlayerForfeit(consoleplayer, true);
G_SaveGameData(); // Tails 12-08-2002
G_SaveGameData(false); // Tails 12-08-2002 -- undirty your save
//added:16-02-98: when recording a demo, should exit using 'q' key,
// but sometimes we forget and use 'F10'.. so save here too.
@ -1821,7 +1821,7 @@ void I_Error(const char *error, ...)
if (errorcount == 8)
{
M_SaveConfig(NULL);
G_SaveGameData();
G_SaveGameData(true);
}
if (errorcount > 20)
{
@ -1852,7 +1852,7 @@ void I_Error(const char *error, ...)
M_SaveConfig(NULL); // save game config, cvars..
D_SaveBan(); // save the ban list
G_SaveGameData(); // Tails 12-08-2002
G_SaveGameData(true); // Tails 12-08-2002
// Shutdown. Here might be other errors.

View file

@ -2983,7 +2983,7 @@ void I_Quit(void)
if (Playing())
K_PlayerForfeit(consoleplayer, true);
G_SaveGameData(); // Tails 12-08-2002
G_SaveGameData(false); // Tails 12-08-2002 -- undirty your save
//added:16-02-98: when recording a demo, should exit using 'q' key,
// but sometimes we forget and use 'F10'.. so save here too.
@ -3078,7 +3078,7 @@ void I_Error(const char *error, ...)
if (errorcount == 9)
{
M_SaveConfig(NULL);
G_SaveGameData();
G_SaveGameData(true);
}
if (errorcount > 20)
{
@ -3142,7 +3142,7 @@ void I_Error(const char *error, ...)
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
G_SaveGameData(); // Tails 12-08-2002
G_SaveGameData(true); // Tails 12-08-2002
// Shutdown. Here might be other errors.
if (demorecording)

View file

@ -607,7 +607,7 @@ void I_Error(const char *error, ...)
if (errorcount == 7)
{
M_SaveConfig(NULL);
G_SaveGameData();
G_SaveGameData(true);
}
if (errorcount > 20)
{
@ -636,7 +636,7 @@ void I_Error(const char *error, ...)
if (!errorcount)
{
M_SaveConfig(NULL); // save game config, cvars..
G_SaveGameData();
G_SaveGameData(true);
}
// save demo, could be useful for debug
@ -726,7 +726,7 @@ void I_Quit(void)
G_CheckDemoStatus();
M_SaveConfig(NULL); // save game config, cvars..
G_SaveGameData();
G_SaveGameData(false); // undirty your save
// maybe it needs that the ticcount continues,
// or something else that will be finished by I_ShutdownSystem(),