From 53ce2e4287b3ff0368991297df5b6a56e5ba7a50 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 3 Mar 2023 13:38:31 +0000 Subject: [PATCH] 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. --- src/d_main.c | 3 ++ src/deh_soc.c | 7 ++++- src/f_finale.c | 2 +- src/g_game.c | 54 +++++++++++++++++++++++------------ src/g_game.h | 2 +- src/k_menufunc.c | 23 ++++++++++++++- src/k_pwrlv.c | 12 ++++++-- src/m_cond.c | 12 ++++++++ src/m_cond.h | 10 +++++++ src/menus/extras-challenges.c | 2 +- src/p_inter.c | 2 +- src/p_setup.c | 3 +- src/p_spec.c | 2 +- src/p_tick.c | 5 ++-- src/sdl/i_system.c | 6 ++-- src/sdl12/i_system.c | 6 ++-- src/win32ce/win_sys.c | 6 ++-- 17 files changed, 117 insertions(+), 40 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index f41d70a31..65d199912 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -977,6 +977,9 @@ void D_ClearState(void) cursongcredit.def = NULL; + if (gamedata->deferredsave) + G_SaveGameData(true); + G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; } diff --git a/src/deh_soc.c b/src/deh_soc.c index 076713297..d904bef02 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -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; diff --git a/src/f_finale.c b/src/f_finale.c index acd923765..80e8e7d4e 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1081,7 +1081,7 @@ void F_GameEvaluationTicker(void) ++gamedata->timesBeaten; M_UpdateUnlockablesAndExtraEmblems(true); - G_SaveGameData(); + G_SaveGameData(true); } else { diff --git a/src/g_game.c b/src/g_game.c index c858bc85e..42d2ddd92 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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 diff --git a/src/g_game.h b/src/g_game.h index fac245c9a..788e2ed9b 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -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); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4300ef933..16c173d00 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -15,6 +15,7 @@ #include "v_video.h" #include "f_finale.h" #include "m_misc.h" +#include "m_cond.h" #ifdef PC_DOS #include // 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; } diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index db46a9266..3bc2466d6 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -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); } } diff --git a/src/m_cond.c b/src/m_cond.c index b840ffd47..73d2310ca 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -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 "&"; diff --git a/src/m_cond.h b/src/m_cond.h index 6a0949f58..7a4931c94 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -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; diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index 17ced0502..9caf144bd 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -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) diff --git a/src/p_inter.c b/src/p_inter.c index 3d69eec25..78ccc8143 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -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) diff --git a/src/p_setup.c b/src/p_setup.c index d97696309..a9c4894ad 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -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); diff --git a/src/p_spec.c b/src/p_spec.c index 0317599e4..cc6e3cf0e 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -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 } } } diff --git a/src/p_tick.c b/src/p_tick.c index 9a1324f2f..8433b4ac3 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -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! diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 9362a540b..cce361bd6 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -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. diff --git a/src/sdl12/i_system.c b/src/sdl12/i_system.c index b388a9bf5..032c535d6 100644 --- a/src/sdl12/i_system.c +++ b/src/sdl12/i_system.c @@ -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) diff --git a/src/win32ce/win_sys.c b/src/win32ce/win_sys.c index 091171b52..c4f4f4859 100644 --- a/src/win32ce/win_sys.c +++ b/src/win32ce/win_sys.c @@ -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(),