Rework how GP Backups are accessed

- Now actually from the relevant GP difficulty's Cupgrid, instead of the top-level Play choice
    - Permits a much cleaner M_StartCup, combining two of the previously four copypasted, slightly modified level startup regions (which could be further combined for sanity's sake, but would take a LITTLE more work right now than I have in me)
- Shows a funny exclamation mark from Sonic Rush on the relevant cup on the grid
- Selected by default when loading the menu, if appropriate
This commit is contained in:
toaster 2023-06-25 23:20:33 +01:00
parent e9df563826
commit a016a54e52
10 changed files with 296 additions and 182 deletions

View file

@ -5719,6 +5719,57 @@ void G_LoadGame(void)
CON_ToggleOff();
}
void G_GetBackupCupData(boolean actuallygetdata)
{
if (actuallygetdata == false)
{
cupsavedata.cup = NULL;
return;
}
char vcheck[VERSIONSIZE+1];
char savename[255];
UINT8 versionMinor;
savebuffer_t save = {0};
//if (makelivebackup)
strcpy(savename, gpbackup);
//else
//sprintf(savename, savegamename, cursaveslot);
if (P_SaveBufferFromFile(&save, savename) == false)
{
cupsavedata.cup = NULL;
return;
}
versionMinor = READUINT8(save.p);
memset(vcheck, 0, sizeof (vcheck));
sprintf(vcheck, "version %d", VERSION);
if (versionMinor != SAV_VERSIONMINOR
|| memcmp(save.p, vcheck, VERSIONSIZE))
{
cupsavedata.cup = NULL;
P_SaveBufferFree(&save);
return; // bad version
}
save.p += VERSIONSIZE;
P_GetBackupCupData(&save);
if (cv_dummygpdifficulty.value != cupsavedata.difficulty
|| !!cv_dummygpencore.value != cupsavedata.encore)
{
// Still not compatible.
cupsavedata.cup = NULL;
}
// done
P_SaveBufferFree(&save);
}
//
// G_SaveGame
// Saves your game.

View file

@ -190,6 +190,7 @@ boolean G_IsTitleCardAvailable(void);
void G_HandleSaveLevel(boolean removecondition);
void G_SaveGame(void);
void G_LoadGame(void);
void G_GetBackupCupData(boolean actuallygetdata);
void G_SaveGameData(void);
void G_DirtyGameData(void);

View file

@ -19,6 +19,7 @@
#include "command.h"
#include "doomstat.h" // MAXSPLITSCREENPLAYERS
#include "g_demo.h" //menudemo_t
#include "p_saveg.h" // savedata_cup_t
#include "k_profiles.h" // profile data & functions
#include "g_input.h" // gc_
#include "i_threads.h"

View file

@ -2368,6 +2368,7 @@ static void M_DrawCupTitle(INT16 y, levelsearch_t *levelsearch)
void M_DrawCupSelect(void)
{
UINT8 i, j, temp = 0;
INT16 x, y;
UINT8 *colormap = NULL;
cupwindata_t *windata = NULL;
levelsearch_t templevelsearch = levellist.levelsearch; // full copy
@ -2378,7 +2379,6 @@ void M_DrawCupSelect(void)
{
size_t id = (i + (j * CUPMENU_COLUMNS)) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS));
patch_t *patch = NULL;
INT16 x, y;
INT16 icony = 7;
char status = 'A';
char monitor;
@ -2463,6 +2463,13 @@ void M_DrawCupSelect(void)
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(templevelsearch.cup->icon, PU_CACHE));
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE));
if (cupgrid.grandprix == true
&& templevelsearch.cup == cupsavedata.cup
&& id != CUPMENU_CURSORID)
{
V_DrawScaledPatch(x + 32, y + 32, 0, W_CachePatchName("CUPBKUP1", PU_CACHE));
}
if (!windata)
;
else if (windata->best_placement != 0)
@ -2562,13 +2569,20 @@ void M_DrawCupSelect(void)
}
}
V_DrawScaledPatch(14 + (cupgrid.x*42) - 4,
20 + (cupgrid.y*44) - 1 - (24*menutransition.tics),
0, W_CachePatchName("CUPCURS", PU_CACHE)
);
x = 14 + (cupgrid.x*42);
y = 20 + (cupgrid.y*44) - (30*menutransition.tics);
V_DrawScaledPatch(x - 4, y - 1, 0, W_CachePatchName("CUPCURS", PU_CACHE));
templevelsearch.cup = cupgrid.builtgrid[CUPMENU_CURSORID];
if (cupgrid.grandprix == true
&& templevelsearch.cup != NULL
&& templevelsearch.cup == cupsavedata.cup)
{
V_DrawScaledPatch(x + 32, y + 32, 0, W_CachePatchName("CUPBKUP2", PU_CACHE));
}
V_DrawFill(0, 146 + (24*menutransition.tics), BASEVIDWIDTH, 54, 31);
M_DrawCupPreview(146 + (24*menutransition.tics), &templevelsearch);

View file

@ -5,10 +5,7 @@
#include "../r_skins.h"
#include "../s_sound.h"
#include "../k_grandprix.h" // K_CanChangeRules
#include "../k_podium.h" // K_StartCeremony
#include "../m_cond.h" // Condition Sets
#include "../r_local.h" // SplitScreen_OnChange
#include "../m_misc.h" // FIL_FileExists
//#define CHARSELECT_DEVICEDEBUG
@ -488,123 +485,9 @@ void M_CharacterSelectInit(void)
}
static void M_GPBackup(INT32 choice)
{
if (choice == MA_YES)
{
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();
}
UINT8 entry = roundqueue.position-1;
SV_StartSinglePlayerServer(roundqueue.entries[entry].gametype, false);
// Skip Bonus rounds.
if (roundqueue.entries[entry].gametype != roundqueue.entries[0].gametype
&& roundqueue.entries[entry].rankrestricted == false)
{
G_GetNextMap(); // updates position in the roundqueue
entry = roundqueue.position-1;
}
if (entry < roundqueue.size)
{
D_MapChange(
roundqueue.entries[entry].mapnum + 1,
roundqueue.entries[entry].gametype,
roundqueue.entries[entry].encore,
true,
1,
false,
roundqueue.entries[entry].rankrestricted
);
}
else
{
if (K_StartCeremony() == false)
{
// Accomodate our buffoonery with the artificial fade.
wipegamestate = -1;
M_StartMessage(
"Grand Prix Backup",
"The session is concluded!\n"
"You exited a final Bonus Round,\n"
"and the Podium failed to load.\n",
NULL, MM_NOTHING, NULL, NULL);
G_HandleSaveLevel(true);
return;
}
}
M_ClearMenus(true);
// We can't put it deeper in the menuflow due to lack of guaranteed setup
restoreMenu = &MainDef;
}
return;
}
M_CharacterSelect(-1);
}
void M_CharacterSelect(INT32 choice)
{
if (currentMenu == &MainDef
&& choice != -1
&& FIL_FileExists(gpbackup))
{
M_StartMessage(
"Grand Prix Backup",
"A progress backup was found.\n"
"Do you want to resurrect your\n"
"last Grand Prix session?\n",
M_GPBackup,
MM_YESNO,
"Yes, let's try again",
"No, play another way");
return;
}
(void)choice;
PLAY_CharSelectDef.music = currentMenu->music;
PLAY_CharSelectDef.prevMenu = currentMenu;
M_SetupNextMenu(&PLAY_CharSelectDef, false);

View file

@ -7,6 +7,9 @@
#include "../../v_video.h"
#include "../../k_grandprix.h"
#include "../../r_local.h" // SplitScreen_OnChange
#include "../../k_podium.h" // K_StartCeremony
#include "../../m_misc.h" // FIL_FileExists
#include "../../d_main.h" // D_ClearState
menuitem_t PLAY_CupSelect[] =
{
@ -32,6 +35,150 @@ menu_t PLAY_CupSelectDef = {
struct cupgrid_s cupgrid;
static void M_StartCup(UINT8 entry)
{
UINT8 ssplayers = cv_splitplayers.value-1;
if (ssplayers > 0)
{
// Splitscreen is not accomodated with this recovery feature.
entry = 0;
}
S_StartSound(NULL, sfx_s3k63);
paused = false;
S_StopMusicCredit();
// Early fadeout to let the sound finish playing
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
if (cv_maxconnections.value < ssplayers+1)
CV_SetValue(&cv_maxconnections, ssplayers+1);
if (splitscreen != ssplayers)
{
splitscreen = ssplayers;
SplitScreen_OnChange();
}
if (entry == 0)
{
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
// read our dummy cvars
grandprixinfo.gamespeed = min(KARTSPEED_HARD, cv_dummygpdifficulty.value);
grandprixinfo.masterbots = (cv_dummygpdifficulty.value == 3);
grandprixinfo.gp = true;
grandprixinfo.initalize = true;
grandprixinfo.cup = levellist.levelsearch.cup;
// Populate the roundqueue
memset(&roundqueue, 0, sizeof(struct roundqueue));
G_GPCupIntoRoundQueue(levellist.levelsearch.cup, levellist.newgametype, (boolean)cv_dummygpencore.value);
roundqueue.position = roundqueue.roundnum = 1;
roundqueue.netcommunicate = true; // relevant for future Online GP
}
else
{
// Silently change player setup
{
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);
}
// Skip Bonus rounds.
if (roundqueue.entries[entry].gametype != roundqueue.entries[0].gametype
&& roundqueue.entries[entry].rankrestricted == false)
{
G_GetNextMap(); // updates position in the roundqueue
entry = roundqueue.position-1;
}
}
paused = false;
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
M_ClearMenus(true);
restoreMenu = &PLAY_CupSelectDef;
if (entry < roundqueue.size)
{
D_MapChange(
roundqueue.entries[entry].mapnum + 1,
roundqueue.entries[entry].gametype,
roundqueue.entries[entry].encore,
true,
1,
false,
roundqueue.entries[entry].rankrestricted
);
}
else if (entry == 0)
{
I_Error("M_StartCup: roundqueue is empty on startup!!");
}
else
{
if (K_StartCeremony() == false)
{
// Accomodate our buffoonery
D_ClearState();
M_StartControlPanel();
M_StartMessage(
"Grand Prix Backup",
"The session is concluded!\n"
"You exited a final Bonus Round,\n"
"and the Podium failed to load.\n",
NULL, MM_NOTHING, NULL, NULL
);
G_HandleSaveLevel(true);
return;
}
}
}
static void M_GPBackup(INT32 choice)
{
if (choice == MA_YES)
{
G_LoadGame();
if (savedata.lives != 0)
{
M_StartCup(roundqueue.position-1);
}
return;
}
M_StartCup(0);
}
void M_CupSelectHandler(INT32 choice)
{
const UINT8 pid = 0;
@ -104,67 +251,24 @@ void M_CupSelectHandler(INT32 choice)
if (cupgrid.grandprix == true)
{
UINT8 ssplayers = cv_splitplayers.value-1;
S_StartSound(NULL, sfx_s3k63);
paused = false;
S_StopMusicCredit();
// Early fadeout to let the sound finish playing
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
if (cv_maxconnections.value < ssplayers+1)
CV_SetValue(&cv_maxconnections, ssplayers+1);
if (splitscreen != ssplayers)
if (newcup == cupsavedata.cup
&& FIL_FileExists(gpbackup))
{
splitscreen = ssplayers;
SplitScreen_OnChange();
M_StartMessage(
"Grand Prix Backup",
"A progress backup was found.\n"
"Do you want to resurrect your\n"
"last Grand Prix session?\n",
M_GPBackup,
MM_YESNO,
"Yes, let's try again",
"No, start from Round 1"
);
return;
}
// read our dummy cvars
grandprixinfo.gamespeed = min(KARTSPEED_HARD, cv_dummygpdifficulty.value);
grandprixinfo.masterbots = (cv_dummygpdifficulty.value == 3);
grandprixinfo.gp = true;
grandprixinfo.initalize = true;
grandprixinfo.cup = newcup;
// Populate the roundqueue
memset(&roundqueue, 0, sizeof(struct roundqueue));
G_GPCupIntoRoundQueue(newcup, levellist.newgametype, (boolean)cv_dummygpencore.value);
roundqueue.position = roundqueue.roundnum = 1;
roundqueue.netcommunicate = true; // relevant for future Online GP
paused = false;
// Don't restart the server if we're already in a game lol
if (gamestate == GS_MENU)
{
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
}
D_MapChange(
roundqueue.entries[0].mapnum + 1,
roundqueue.entries[0].gametype,
roundqueue.entries[0].encore,
true,
1,
false,
roundqueue.entries[0].rankrestricted
);
M_ClearMenus(true);
restoreMenu = &PLAY_CupSelectDef;
M_StartCup(0);
}
else if (count == 1 && levellist.levelsearch.timeattack == true)
{

View file

@ -8,6 +8,8 @@
#include "../../r_local.h" // SplitScreen_OnChange
#include "../../f_finale.h" // F_WipeStartScreen
#include "../../v_video.h"
#include "../../g_game.h" // G_GetBackupCupData
#include "../../p_saveg.h" // cupsavedata
cupheader_t dummy_lostandfound;
@ -262,6 +264,11 @@ boolean M_LevelListFromGametype(INT16 gt)
const size_t pagelen = sizeof(cupheader_t*) * (CUPMENU_COLUMNS * CUPMENU_ROWS);
boolean foundany = false, currentvalid = false;
G_GetBackupCupData(
cupgrid.grandprix == true
|| cv_splitplayers.value <= 1
);
templevelsearch.cup = kartcupheaders;
#if 0
@ -331,7 +338,8 @@ boolean M_LevelListFromGametype(INT16 gt)
if (Playing()
? (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == templevelsearch.cup)
: (gt == -1 && levellist.levelsearch.cup == templevelsearch.cup))
: (cupsavedata.cup == templevelsearch.cup
|| (gt == -1 && levellist.levelsearch.cup == templevelsearch.cup)))
{
GRID_FOCUSCUP;
}

View file

@ -48,6 +48,7 @@
#include "k_zvote.h"
savedata_t savedata;
savedata_cup_t cupsavedata;
// Block UINT32s to attempt to ensure that the correct data is
// being sent and received
@ -5407,6 +5408,46 @@ static inline void P_ArchiveMisc(savebuffer_t *save)
WRITEUINT32(save->p, writetime);
}
void P_GetBackupCupData(savebuffer_t *save)
{
char testname[sizeof(timeattackfolder)];
READSTRINGN(save->p, testname, sizeof(testname));
if (strcmp(testname, timeattackfolder))
{
cupsavedata.cup = NULL;
return;
}
// Grand Prix information
cupsavedata.difficulty = READUINT8(save->p);
cupsavedata.encore = (boolean)READUINT8(save->p);
boolean masterbots = (boolean)READUINT8(save->p);
if (masterbots == true)
cupsavedata.difficulty = KARTGP_MASTER;
// Find the relevant cup.
char cupname[MAXCUPNAME];
READSTRINGL(save->p, cupname, sizeof(cupname));
UINT32 hash = quickncasehash(cupname, MAXCUPNAME);
for (cupsavedata.cup = kartcupheaders; cupsavedata.cup; cupsavedata.cup = cupsavedata.cup->next)
{
if (cupsavedata.cup->namehash != hash)
continue;
if (strcmp(cupsavedata.cup->name, cupname))
continue;
break;
}
// Okay, no further! We've got everything we need.
}
static boolean P_UnArchiveSPGame(savebuffer_t *save)
{
char testname[sizeof(timeattackfolder)];

View file

@ -34,6 +34,7 @@ extern "C" {
// Local Play
void P_SaveGame(savebuffer_t *save);
boolean P_LoadGame(savebuffer_t *save);
void P_GetBackupCupData(savebuffer_t *save);
// Online
void P_SaveNetGame(savebuffer_t *save, boolean resending);
@ -66,6 +67,15 @@ struct savedata_t
extern savedata_t savedata;
struct savedata_cup_t
{
cupheader_t *cup;
UINT8 difficulty;
boolean encore;
};
extern savedata_cup_t cupsavedata;
struct savebuffer_t
{
UINT8 *buffer;

View file

@ -297,6 +297,7 @@ TYPEDEF (polyfadedata_t);
// p_saveg.h
TYPEDEF (savedata_t);
TYPEDEF (savedata_cup_t);
TYPEDEF (savebuffer_t);
// p_setup.h