Add auto save feature (#1939)

* Add auto save menu item

* Allow appending to default save name

* Add saving auto save

* Add loading auto save

* Add auto save slot to load game menu

* Fix rect x-offset

* Enable by default
This commit is contained in:
ceski 2024-10-03 18:07:22 -07:00 committed by GitHub
parent ed349ec34e
commit 9a43e5494b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 570 additions and 152 deletions

View File

@ -78,6 +78,8 @@ typedef enum
ga_worlddone,
ga_screenshot,
ga_reloadlevel,
ga_loadautosave,
ga_saveautosave,
} gameaction_t;

View File

@ -23,6 +23,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include "am_map.h"
@ -212,6 +213,9 @@ boolean joybuttons[NUM_GAMEPAD_BUTTONS];
int savegameslot = -1;
char savedescription[32];
static boolean save_autosave;
static boolean autosave;
//jff 3/24/98 declare startskill external, define defaultskill here
int default_skill; //note 1-based
@ -1961,6 +1965,11 @@ static void G_DoWorldDone(void)
gameaction = ga_nothing;
viewactive = true;
AM_clearMarks(); //jff 4/12/98 clear any marks on the automap
if (autosave && !demorecording && !demoplayback && !netgame)
{
M_SaveAutoSave();
}
}
// killough 2/28/98: A ridiculously large number
@ -2268,6 +2277,12 @@ static char *savename = NULL;
static boolean forced_loadgame = false;
static boolean command_loadgame = false;
void G_ForcedLoadAutoSave(void)
{
gameaction = ga_loadautosave;
forced_loadgame = true;
}
void G_ForcedLoadGame(void)
{
gameaction = ga_loadgame;
@ -2277,6 +2292,15 @@ void G_ForcedLoadGame(void)
// killough 3/16/98: add slot info
// killough 5/15/98: add command-line
void G_LoadAutoSave(char *name)
{
free(savename);
savename = M_StringDuplicate(name);
gameaction = ga_loadautosave;
forced_loadgame = false;
command_loadgame = false;
}
void G_LoadGame(char *name, int slot, boolean command)
{
if (savename) free(savename);
@ -2290,6 +2314,12 @@ void G_LoadGame(char *name, int slot, boolean command)
// killough 5/15/98:
// Consistency Error when attempting to load savegame.
static void G_LoadAutoSaveErr(const char *msg)
{
Z_Free(savebuffer);
MN_ForcedLoadAutoSave(msg);
}
static void G_LoadGameErr(const char *msg)
{
Z_Free(savebuffer); // Free the savegame buffer
@ -2308,6 +2338,12 @@ static void G_LoadGameErr(const char *msg)
// Description is a 24 byte text string
//
void G_SaveAutoSave(char *description)
{
strcpy(savedescription, description);
save_autosave = true;
}
void G_SaveGame(int slot, char *description)
{
savegameslot = slot;
@ -2330,13 +2366,8 @@ void CheckSaveGame(size_t size)
// [FG] support up to 8 pages of savegames
char* G_SaveGameName(int slot)
static char *SaveGameName(const char *buf)
{
// Ty 05/04/98 - use savegamename variable (see d_deh.c)
// killough 12/98: add .7 to truncate savegamename
char buf[16] = {0};
sprintf(buf, "%.7s%d.dsg", savegamename, 10*savepage+slot);
char *filepath = M_StringJoin(basesavegame, DIR_SEPARATOR_S, buf);
char *existing = M_FileCaseExists(filepath);
@ -2353,6 +2384,20 @@ char* G_SaveGameName(int slot)
}
}
char *G_AutoSaveName(void)
{
return SaveGameName("autosave.dsg");
}
char *G_SaveGameName(int slot)
{
// Ty 05/04/98 - use savegamename variable (see d_deh.c)
// killough 12/98: add .7 to truncate savegamename
char buf[16] = {0};
sprintf(buf, "%.7s%d.dsg", savegamename, 10 * savepage + slot);
return SaveGameName(buf);
}
char* G_MBFSaveGameName(int slot)
{
char buf[16] = {0};
@ -2397,15 +2442,12 @@ static uint64_t G_Signature(int sig_epi, int sig_map)
return s;
}
static void G_DoSaveGame(void)
static void DoSaveGame(char *name)
{
char *name = NULL;
char name2[VERSIONSIZE];
char *description;
int length, i;
name = G_SaveGameName(savegameslot);
description = savedescription;
save_p = savebuffer = Z_Malloc(savegamesize, PU_STATIC, 0);
@ -2511,11 +2553,22 @@ static void G_DoSaveGame(void)
if (name) free(name);
MN_SetQuickSaveSlot(savegameslot);
drs_skip_frame = true;
}
static void G_DoSaveGame(void)
{
char *name = G_SaveGameName(savegameslot);
DoSaveGame(name);
MN_SetQuickSaveSlot(savegameslot);
}
static void G_DoSaveAutoSave(void)
{
char *name = G_AutoSaveName();
DoSaveGame(name);
}
static void CheckSaveVersion(const char *str, saveg_compat_t ver)
{
if (strncmp((char *) save_p, str, strlen(str)) == 0)
@ -2524,7 +2577,7 @@ static void CheckSaveVersion(const char *str, saveg_compat_t ver)
}
}
static void G_DoLoadGame(void)
static boolean DoLoadGame(boolean do_load_autosave)
{
int length, i;
char vcheck[VERSIONSIZE];
@ -2562,8 +2615,12 @@ static void G_DoLoadGame(void)
// killough 2/22/98: Friendly savegame version difference message
if (!forced_loadgame && saveg_compat != saveg_mbf && saveg_compat < saveg_woof600)
{
G_LoadGameErr("Different Savegame Version!!!\n\nAre you sure?");
return;
const char *msg = "Different Savegame Version!!!\n\nAre you sure?";
if (do_load_autosave)
G_LoadAutoSaveErr(msg);
else
G_LoadGameErr(msg);
return false;
}
save_p += VERSIONSIZE;
@ -2595,9 +2652,12 @@ static void G_DoLoadGame(void)
if (save_p[sizeof checksum])
strcat(strcat(msg,"Wads expected:\n\n"), (char *) save_p);
strcat(msg, "\nAre you sure?");
G_LoadGameErr(msg);
if (do_load_autosave)
G_LoadAutoSaveErr(msg);
else
G_LoadGameErr(msg);
free(msg);
return;
return false;
}
}
@ -2723,15 +2783,86 @@ static void G_DoLoadGame(void)
if (demorecording) // So this can only possibly be a -recordfrom command.
G_BeginRecording();// Start the -recordfrom, since the game was loaded.
I_Printf(VB_INFO, "G_DoLoadGame: Slot %02d, Time ", 10 * savepage + savegameslot);
return true;
}
static void PrintLevelTimes(void)
{
if (totalleveltimes)
I_Printf(VB_INFO, "(%d:%02d) ", ((totalleveltimes + leveltime) / TICRATE) / 60,
((totalleveltimes + leveltime) / TICRATE) % 60);
I_Printf(VB_INFO, "%d:%05.2f", leveltime / TICRATE / 60,
(float)(leveltime % (60 * TICRATE)) / TICRATE);
{
I_Printf(VB_INFO, "(%d:%02d) ",
((totalleveltimes + leveltime) / TICRATE) / 60,
((totalleveltimes + leveltime) / TICRATE) % 60);
}
MN_SetQuickSaveSlot(savegameslot);
I_Printf(VB_INFO, "%d:%05.2f", leveltime / TICRATE / 60,
(float)(leveltime % (60 * TICRATE)) / TICRATE);
}
static void G_DoLoadGame(void)
{
if (DoLoadGame(false))
{
const int slot_num = 10 * savepage + savegameslot;
I_Printf(VB_INFO, "G_DoLoadGame: Slot %02d, Time ", slot_num);
PrintLevelTimes();
MN_SetQuickSaveSlot(savegameslot);
}
}
static void G_DoLoadAutoSave(void)
{
if (DoLoadGame(true))
{
I_Printf(VB_INFO, "G_DoLoadGame: Auto Save, Time ");
PrintLevelTimes();
}
}
boolean G_AutoSaveEnabled(void)
{
return autosave;
}
//
// G_LoadAutoSaveDeathUse
// Loads the auto save if it's more recent than the current save slot.
// Returns true if the auto save is loaded.
//
boolean G_LoadAutoSaveDeathUse(void)
{
struct stat st;
char *auto_path = G_AutoSaveName();
time_t auto_time = (M_stat(auto_path, &st) != -1 ? st.st_mtime : 0);
boolean result = (auto_time > 0);
if (result)
{
if (savegameslot >= 0)
{
char *save_path = G_SaveGameName(savegameslot);
time_t save_time = (M_stat(save_path, &st) != -1 ? st.st_mtime : 0);
free(save_path);
result = (auto_time > save_time);
}
if (result)
{
G_LoadAutoSave(auto_path);
}
}
free(auto_path);
return result;
}
static void CheckSaveAutoSave(void)
{
if (save_autosave)
{
save_autosave = false;
gameaction = ga_saveautosave;
}
}
boolean clean_screenshot;
@ -2812,11 +2943,19 @@ void G_Ticker(void)
case ga_reloadlevel:
G_ReloadLevel();
break;
case ga_loadautosave:
G_DoLoadAutoSave();
break;
case ga_saveautosave:
G_DoSaveAutoSave();
break;
default: // killough 9/29/98
gameaction = ga_nothing;
break;
}
CheckSaveAutoSave();
// killough 10/6/98: allow games to be saved during demo
// playback, by the playback user (not by demo itself)
@ -4674,6 +4813,8 @@ void G_BindGameVariables(void)
"Maximum number of player corpses (< 0 = No limit)");
BIND_NUM_GENERAL(death_use_action, 0, 0, 2,
"Use-button action upon death (0 = Default; 1 = Load save; 2 = Nothing)");
BIND_BOOL_GENERAL(autosave, true,
"Auto save at the beginning of a map, after completing the previous one.");
}
void G_BindEnemVariables(void)

View File

@ -46,9 +46,14 @@ void G_DeathMatchSpawnPlayer(int playernum);
void G_InitNew(skill_t skill, int episode, int map);
void G_DeferedInitNew(skill_t skill, int episode, int map);
void G_DeferedPlayDemo(char *demo);
void G_LoadAutoSave(char *name);
void G_LoadGame(char *name, int slot, boolean is_command); // killough 5/15/98
void G_ForcedLoadAutoSave(void);
void G_ForcedLoadGame(void); // killough 5/15/98: forced loadgames
void G_SaveAutoSave(char *description);
void G_SaveGame(int slot, char *description); // Called by M_Responder.
boolean G_AutoSaveEnabled(void);
boolean G_LoadAutoSaveDeathUse(void);
void G_RecordDemo(char *name); // Only called by startup code.
void G_BeginRecording(void);
void G_PlayDemo(char *name);
@ -58,6 +63,7 @@ void G_WorldDone(void);
void G_Ticker(void);
void G_ScreenShot(void);
void G_ReloadDefaults(boolean keep_demover); // killough 3/1/98: loads game defaults
char *G_AutoSaveName(void);
char *G_SaveGameName(int); // killough 3/22/98: sets savegame filename
char *G_MBFSaveGameName(int); // MBF savegame filename
void G_SetFastParms(int); // killough 4/10/98: sets -fast parameters

View File

@ -117,8 +117,13 @@ backdrop_t menu_backdrop;
#define M_X_LOADSAVE 80
#define M_Y_LOADSAVE 34
#define M_Y_AUTOSAVE 26
#define M_LOADSAVE_WIDTH (24 * 8 + 8) // [FG] c.f. M_DrawSaveLoadBorder()
#define LOADGRAPHIC_Y 8
#define AUTOGRAPHIC_Y 2
static int loadsave_title_y = LOADGRAPHIC_Y;
static char savegamestrings[10][SAVESTRINGSIZE];
// [FG] support up to 8 pages of savegames
@ -181,7 +186,7 @@ short whichSkull; // which skull to draw (he blinks)
char skullName[2][/*8*/ 9] = {"M_SKULL1", "M_SKULL2"};
static menu_t SaveDef, LoadDef;
static menu_t SaveDef, LoadDef, LoadAutoSaveDef;
static menu_t *currentMenu; // current menudef
// end of externs added for setup menus
@ -206,6 +211,7 @@ static void M_MusicVol(int choice);
static void M_FinishReadThis(int choice);
static void M_FinishHelp(int choice); // killough 10/98
static void M_LoadAutoSaveSelect(int choice);
static void M_LoadSelect(int choice);
static void M_SaveSelect(int choice);
static void M_ReadSaveStrings(void);
@ -732,8 +738,15 @@ enum
load_end
} load_e;
#define SAVE_LOAD_RECT(n) \
{M_X_LOADSAVE, M_Y_LOADSAVE + (n) * LINEHEIGHT - 7, M_LOADSAVE_WIDTH, LINEHEIGHT}
#define SAVE_LOAD_RECT_Y(m_y, n) ((m_y) + (n) * LINEHEIGHT - 7)
#define SAVE_LOAD_RECT(n) \
{M_X_LOADSAVE, SAVE_LOAD_RECT_Y(M_Y_LOADSAVE, n), M_LOADSAVE_WIDTH, \
LINEHEIGHT}
#define AUTO_SAVE_RECT(n) \
{M_X_LOADSAVE, SAVE_LOAD_RECT_Y(M_Y_AUTOSAVE, n), M_LOADSAVE_WIDTH, \
LINEHEIGHT}
// The definitions of the Load Game screen
@ -761,39 +774,76 @@ static menu_t LoadDef =
0
};
#define LOADGRAPHIC_Y 8
enum
{
autosave_page = load_end,
autosave_end
} autosave_e;
static menuitem_t LoadAutoSaveMenu[] = {
// Auto save slot.
{1, "", M_LoadAutoSaveSelect, 'a', NULL, AUTO_SAVE_RECT(0)},
// Regular save slots.
{1, "", M_LoadSelect, '1', NULL, AUTO_SAVE_RECT(1)},
{1, "", M_LoadSelect, '2', NULL, AUTO_SAVE_RECT(2)},
{1, "", M_LoadSelect, '3', NULL, AUTO_SAVE_RECT(3)},
{1, "", M_LoadSelect, '4', NULL, AUTO_SAVE_RECT(4)},
{1, "", M_LoadSelect, '5', NULL, AUTO_SAVE_RECT(5)},
{1, "", M_LoadSelect, '6', NULL, AUTO_SAVE_RECT(6)},
{1, "", M_LoadSelect, '7', NULL, AUTO_SAVE_RECT(7)},
{1, "", M_LoadSelect, '8', NULL, AUTO_SAVE_RECT(8)},
// Save page navigation.
{-1, "", NULL, 0, NULL, AUTO_SAVE_RECT(9), MF_PAGE}
};
static menu_t LoadAutoSaveDef =
{
autosave_end,
&MainDef,
LoadAutoSaveMenu,
M_DrawLoad,
M_X_LOADSAVE,
M_Y_AUTOSAVE,
};
// [FG] draw a snapshot of the n'th savegame into a separate window,
// or fill window with solid color and "n/a" if snapshot not available
static int snapshot_width, snapshot_height;
static void M_DrawBorderedSnapshot(int n)
static void M_DrawBorderedSnapshot(int slot)
{
const char *txt = "n/a";
const int snapshot_x =
MAX(video.deltaw + SaveDef.x + SKULLXOFF - snapshot_width - 8, 8);
const int snapshot_y =
LoadDef.y
+ MAX((load_end * LINEHEIGHT - snapshot_height) * n / load_end, 0);
// [FG] a snapshot window smaller than 80*48 px is considered too small
if (snapshot_width < SCREENWIDTH / 4)
{
return;
}
if (!MN_DrawSnapshot(n, snapshot_x, snapshot_y, snapshot_width,
snapshot_height))
const int x =
video.deltaw + currentMenu->x + SKULLXOFF - snapshot_width - 8;
const int y = (currentMenu->numitems * LINEHEIGHT - snapshot_height) * slot
/ currentMenu->numitems;
const int snapshot_x = MAX(8, x);
const int snapshot_y = currentMenu->y + MAX(0, y);
const boolean draw_shot = MN_DrawSnapshot(slot, snapshot_x, snapshot_y,
snapshot_width, snapshot_height);
const boolean is_autosave = (currentMenu == &LoadAutoSaveDef && slot == 0);
const char *txt;
if (!draw_shot || is_autosave)
{
txt = (is_autosave ? "Auto Save" : "N/A");
WriteText(snapshot_x + (snapshot_width - MN_StringWidth(txt)) / 2
- video.deltaw,
snapshot_y + snapshot_height / 2 - MN_StringHeight(txt) / 2,
txt);
}
txt = MN_GetSavegameTime(n);
txt = MN_GetSavegameTime(slot);
MN_DrawString(snapshot_x + snapshot_width / 2 - MN_GetPixelWidth(txt) / 2
- video.deltaw,
snapshot_y + snapshot_height + MN_StringHeight(txt), CR_GOLD,
@ -807,26 +857,49 @@ static void M_DrawBorderedSnapshot(int n)
static boolean delete_verify = false;
static void M_DeleteGame(int i)
static void DeleteAutoSave(void)
{
char *name = G_SaveGameName(i);
char *name = G_AutoSaveName();
M_remove(name);
free(name);
}
if (i == quickSaveSlot)
static void DeleteSaveGame(int slot)
{
char *name = G_SaveGameName(slot);
M_remove(name);
free(name);
if (slot == quickSaveSlot)
{
quickSaveSlot = -1;
}
if (i == savegameslot)
if (slot == savegameslot)
{
savegameslot = -1;
}
}
M_ReadSaveStrings();
static void M_DeleteGame(int choice)
{
int slot = choice;
if (name)
if (currentMenu == &LoadAutoSaveDef)
{
free(name);
if (slot == 0)
{
DeleteAutoSave();
}
else
{
slot--;
DeleteSaveGame(slot);
}
}
else
{
DeleteSaveGame(slot);
}
}
@ -834,7 +907,8 @@ static void M_DeleteGame(int i)
static void M_DrawSaveLoadBottomLine(void)
{
char pagestr[16];
const int y = LoadDef.y + LINEHEIGHT * load_page;
const int x = currentMenu->x;
const int y = currentMenu->y + LINEHEIGHT * (currentMenu->numitems - 1);
// [crispy] force status bar refresh
inhelpscreens = true;
@ -844,21 +918,38 @@ static void M_DrawSaveLoadBottomLine(void)
int flags = currentMenu->menuitems[index].flags;
byte *cr = (flags & MF_PAGE) ? cr_bright : NULL;
M_DrawSaveLoadBorder(LoadDef.x, y, cr);
M_DrawSaveLoadBorder(x, y, cr);
if (savepage > 0)
{
MN_DrawString(LoadDef.x, y, CR_GOLD, "<-");
MN_DrawString(x, y, CR_GOLD, "<-");
}
if (savepage < savepage_max)
{
MN_DrawString(LoadDef.x + (SAVESTRINGSIZE - 2) * 8, y, CR_GOLD, "->");
MN_DrawString(x + (SAVESTRINGSIZE - 2) * 8, y, CR_GOLD, "->");
}
M_snprintf(pagestr, sizeof(pagestr), "page %d/%d", savepage + 1,
savepage_max + 1);
MN_DrawString(LoadDef.x + M_LOADSAVE_WIDTH / 2 - MN_StringWidth(pagestr) / 2,
y, CR_GOLD, pagestr);
MN_DrawString(x + M_LOADSAVE_WIDTH / 2 - MN_StringWidth(pagestr) / 2, y,
CR_GOLD, pagestr);
}
static void M_DrawSaveLoadBorders(void)
{
const int num_slots = currentMenu->numitems - 1;
const int x = currentMenu->x;
for (int i = 0; i < num_slots; i++)
{
const int y = currentMenu->y + LINEHEIGHT * i;
const menuitem_t *item = &currentMenu->menuitems[i];
byte *cr = (item->flags & MF_HILITE) ? cr_bright : NULL;
M_DrawSaveLoadBorder(x, y, cr);
WriteText(x, y, savegamestrings[i]);
}
}
//
@ -867,22 +958,13 @@ static void M_DrawSaveLoadBottomLine(void)
static void M_DrawLoad(void)
{
int i;
// jff 3/15/98 use symbolic load position
MN_DrawTitle(72, LOADGRAPHIC_Y, "M_LOADG", "Load Game");
for (i = 0; i < load_page; i++)
{
menuitem_t *item = &currentMenu->menuitems[i];
byte *cr = (item->flags & MF_HILITE) ? cr_bright : NULL;
M_DrawSaveLoadBorder(LoadDef.x, LoadDef.y + LINEHEIGHT * i, cr);
WriteText(LoadDef.x, LoadDef.y + LINEHEIGHT * i, savegamestrings[i]);
}
MN_DrawTitle(M_X_CENTER, loadsave_title_y, "M_LOADG", "Load Game");
M_DrawSaveLoadBorders();
int index = (menu_input == mouse_mode ? highlight_item : itemOn);
if (index < load_page)
if (index < currentMenu->numitems - 1)
{
M_DrawBorderedSnapshot(index);
}
@ -913,12 +995,28 @@ static void M_DrawSaveLoadBorder(int x, int y, byte *cr)
// User wants to load this game
//
static void M_LoadAutoSaveSelect(int choice)
{
saveg_compat = saveg_woof510;
char *name = G_AutoSaveName();
G_LoadAutoSave(name);
free(name);
MN_ClearMenus();
// Auto save slot doesn't exist for save menu, so don't change lastOn.
//SaveDef.lastOn = choice;
}
static void M_LoadSelect(int choice)
{
char *name = NULL; // killough 3/22/98
int slot = choice;
name = G_SaveGameName(choice);
if (currentMenu == &LoadAutoSaveDef)
{
slot--;
}
name = G_SaveGameName(slot);
saveg_compat = saveg_woof510;
if (M_access(name, F_OK) != 0)
@ -927,11 +1025,11 @@ static void M_LoadSelect(int choice)
{
free(name);
}
name = G_MBFSaveGameName(choice);
name = G_MBFSaveGameName(slot);
saveg_compat = saveg_mbf;
}
G_LoadGame(name, choice, false); // killough 3/16/98, 5/15/98: add slot, cmd
G_LoadGame(name, slot, false); // killough 3/16/98, 5/15/98: add slot, cmd
MN_ClearMenus();
if (name)
@ -940,13 +1038,23 @@ static void M_LoadSelect(int choice)
}
// [crispy] save the last game you loaded
SaveDef.lastOn = choice;
SaveDef.lastOn = slot;
}
//
// killough 5/15/98: add forced loadgames
//
static void M_VerifyForcedLoadAutoSave(int ch)
{
if (ch == 'y')
{
G_ForcedLoadAutoSave();
}
free(messageString);
MN_ClearMenus();
}
static void M_VerifyForcedLoadGame(int ch)
{
if (ch == 'y')
@ -957,6 +1065,11 @@ static void M_VerifyForcedLoadGame(int ch)
MN_ClearMenus();
}
void MN_ForcedLoadAutoSave(const char *msg)
{
M_StartMessage(strdup(msg), M_VerifyForcedLoadAutoSave, true);
}
void MN_ForcedLoadGame(const char *msg)
{
M_StartMessage(strdup(msg), M_VerifyForcedLoadGame, true); // free()'d above
@ -984,7 +1097,15 @@ static void M_LoadGame(int choice)
return;
}
SetNextMenu(&LoadDef);
if (G_AutoSaveEnabled() && savepage == 0)
{
SetNextMenu(&LoadAutoSaveDef);
}
else
{
SetNextMenu(&LoadDef);
}
M_ReadSaveStrings();
}
@ -1019,18 +1140,102 @@ static menu_t SaveDef =
0
};
static void SetLoadSlotStatus(int slot, int status)
{
if (currentMenu == &LoadAutoSaveDef)
{
if (slot > 0)
{
LoadDef.menuitems[slot - 1].status = status;
}
LoadAutoSaveDef.menuitems[slot].status = status;
}
else
{
LoadDef.menuitems[slot].status = status;
LoadAutoSaveDef.menuitems[slot + 1].status = status;
}
}
static void EmptySaveString(char *name, boolean is_autosave)
{
if (is_autosave)
{
M_snprintf(name, SAVESTRINGSIZE, "%s (Auto)", s_EMPTYSTRING);
}
else
{
M_snprintf(name, SAVESTRINGSIZE, "%s", s_EMPTYSTRING);
}
}
static void M_ReadSaveString(char *name, int menu_slot, int save_slot,
boolean is_autosave)
{
FILE *fp = M_fopen(name, "rb");
MN_ReadSavegameTime(menu_slot, name);
free(name);
MN_ResetSnapshot(menu_slot);
if (!fp)
{
if (!is_autosave)
{
// Ty 03/27/98 - externalized:
name = G_MBFSaveGameName(save_slot);
fp = M_fopen(name, "rb");
free(name);
}
if (!fp)
{
EmptySaveString(savegamestrings[menu_slot], is_autosave);
SetLoadSlotStatus(menu_slot, 0);
return;
}
}
// [FG] check return value
if (!fread(&savegamestrings[menu_slot], SAVESTRINGSIZE, 1, fp))
{
fclose(fp);
EmptySaveString(savegamestrings[menu_slot], is_autosave);
SetLoadSlotStatus(menu_slot, 0);
return;
}
if (!MN_ReadSnapshot(menu_slot, fp))
{
MN_ResetSnapshot(menu_slot);
}
fclose(fp);
SetLoadSlotStatus(menu_slot, 1);
}
static void UpdateRectX(menu_t *menu, int x)
{
for (int i = 0; i < menu->numitems; i++)
{
menu->menuitems[i].rect.x = x;
}
}
//
// M_ReadSaveStrings
// read the strings from the savegame files
//
static void M_ReadSaveStrings(void)
{
int i;
// [FG] shift savegame descriptions a bit to the right
// to make room for the snapshots on the left
SaveDef.x = LoadDef.x =
M_X_LOADSAVE + MIN(M_LOADSAVE_WIDTH / 2, video.deltaw);
const int x = M_X_LOADSAVE + MIN(M_LOADSAVE_WIDTH / 2, video.deltaw);
SaveDef.x = LoadDef.x = LoadAutoSaveDef.x = x;
UpdateRectX(&SaveDef, x);
UpdateRectX(&LoadDef, x);
UpdateRectX(&LoadAutoSaveDef, x);
// [FG] fit the snapshots into the resulting space
snapshot_width = MIN((video.deltaw + SaveDef.x + 2 * SKULLXOFF) & ~7,
@ -1038,51 +1243,22 @@ static void M_ReadSaveStrings(void)
snapshot_height = MIN((snapshot_width * SCREENHEIGHT / SCREENWIDTH) & ~7,
SCREENHEIGHT / 2);
for (i = 0; i < load_page; i++)
int start_slot = 0;
if (currentMenu == &LoadAutoSaveDef)
{
FILE *fp; // killough 11/98: change to use stdio
char *name = G_AutoSaveName();
M_ReadSaveString(name, 0, 0, true);
start_slot = 1;
}
char *name = G_SaveGameName(i); // killough 3/22/98
fp = M_fopen(name, "rb");
MN_ReadSavegameTime(i, name);
if (name)
{
free(name);
}
const int num_slots = currentMenu->numitems - 1;
MN_ResetSnapshot(i);
if (!fp)
{ // Ty 03/27/98 - externalized:
name = G_MBFSaveGameName(i);
fp = M_fopen(name, "rb");
if (name)
{
free(name);
}
if (!fp)
{
strcpy(&savegamestrings[i][0], s_EMPTYSTRING);
LoadMenu[i].status = 0;
continue;
}
}
// [FG] check return value
if (!fread(&savegamestrings[i], SAVESTRINGSIZE, 1, fp))
{
strcpy(&savegamestrings[i][0], s_EMPTYSTRING);
LoadMenu[i].status = 0;
fclose(fp);
continue;
}
if (!MN_ReadSnapshot(i, fp))
{
MN_ResetSnapshot(i);
}
fclose(fp);
LoadMenu[i].status = 1;
for (int menu_slot = start_slot; menu_slot < num_slots; menu_slot++)
{
const int save_slot = menu_slot - start_slot;
char *name = G_SaveGameName(save_slot);
M_ReadSaveString(name, menu_slot, save_slot, false);
}
}
@ -1094,25 +1270,19 @@ static void M_DrawSave(void)
int i;
// jff 3/15/98 use symbolic load position
MN_DrawTitle(72, LOADGRAPHIC_Y, "M_SAVEG", "Save Game");
for (i = 0; i < load_page; i++)
{
menuitem_t *item = &currentMenu->menuitems[i];
byte *cr = (item->flags & MF_HILITE) ? cr_bright : NULL;
M_DrawSaveLoadBorder(LoadDef.x, LoadDef.y + LINEHEIGHT * i, cr);
WriteText(LoadDef.x, LoadDef.y + LINEHEIGHT * i, savegamestrings[i]);
}
MN_DrawTitle(M_X_CENTER, loadsave_title_y, "M_SAVEG", "Save Game");
M_DrawSaveLoadBorders();
if (saveStringEnter)
{
i = MN_StringWidth(savegamestrings[saveSlot]);
WriteText(LoadDef.x + i, LoadDef.y + LINEHEIGHT * saveSlot, "_");
WriteText(currentMenu->x + i, currentMenu->y + LINEHEIGHT * saveSlot,
"_");
}
int index = (menu_input == mouse_mode ? highlight_item : itemOn);
if (index < load_page)
if (index < currentMenu->numitems - 1)
{
M_DrawBorderedSnapshot(index);
}
@ -1138,7 +1308,7 @@ void MN_SetQuickSaveSlot(int slot)
}
// [FG] generate a default save slot name when the user saves to an empty slot
static void SetDefaultSaveName(int slot)
static void SetDefaultSaveName(char *name, const char *append)
{
char *maplump = MAPNAME(gameepisode, gamemap);
int maplumpnum = W_CheckNumForName(maplump);
@ -1158,16 +1328,31 @@ static void SetDefaultSaveName(int slot)
*ext = '\0';
}
M_snprintf(savegamestrings[slot], SAVESTRINGSIZE, "%s (%s)", maplump,
wadname);
if (append)
{
M_snprintf(name, SAVESTRINGSIZE, "%s (%s) (%s)", maplump, wadname,
append);
}
else
{
M_snprintf(name, SAVESTRINGSIZE, "%s (%s)", maplump, wadname);
}
free(wadname);
}
else
{
M_snprintf(savegamestrings[slot], SAVESTRINGSIZE, "%s", maplump);
if (append)
{
M_snprintf(name, SAVESTRINGSIZE, "%s (%s)", maplump, append);
}
else
{
M_snprintf(name, SAVESTRINGSIZE, "%s", maplump);
}
}
M_StringToUpper(savegamestrings[slot]);
M_StringToUpper(name);
}
// [FG] override savegame name if it already starts with a map identifier
@ -1188,16 +1373,22 @@ boolean MN_StartsWithMapIdentifier(char *str)
return false;
}
void M_SaveAutoSave(void)
{
char autosave_string[SAVESTRINGSIZE];
SetDefaultSaveName(autosave_string, "Auto");
G_SaveAutoSave(autosave_string);
}
static boolean GamepadSave(int choice)
{
if (menu_input == pad_mode)
{
// Immediately save game using a default name.
saveSlot = choice;
savegamestrings[choice][0] = 0;
SetDefaultSaveName(choice);
SetDefaultSaveName(savegamestrings[choice], NULL);
M_DoSave(choice);
LoadDef.lastOn = choice;
LoadAutoSaveDef.lastOn = choice + 1;
return true;
}
@ -1224,12 +1415,13 @@ static void M_SaveSelect(int choice)
|| MN_StartsWithMapIdentifier(savegamestrings[choice]))
{
savegamestrings[choice][0] = 0;
SetDefaultSaveName(choice);
SetDefaultSaveName(savegamestrings[choice], NULL);
}
saveCharIndex = strlen(savegamestrings[choice]);
// [crispy] load the last game you saved
LoadDef.lastOn = choice;
LoadAutoSaveDef.lastOn = choice + 1;
}
//
@ -1449,7 +1641,7 @@ static void M_QuickSaveResponse(int ch)
{
if (MN_StartsWithMapIdentifier(savegamestrings[quickSaveSlot]))
{
SetDefaultSaveName(quickSaveSlot);
SetDefaultSaveName(savegamestrings[quickSaveSlot], NULL);
}
M_DoSave(quickSaveSlot);
M_StartSound(sfx_swtchx);
@ -1472,8 +1664,8 @@ static void M_QuickSave(void)
if (quickSaveSlot < 0)
{
MN_StartControlPanel();
M_ReadSaveStrings();
SetNextMenu(&SaveDef);
M_ReadSaveStrings();
quickSaveSlot = -2; // means to pick a slot now
return;
}
@ -1514,8 +1706,8 @@ static void M_QuickLoad(void)
{
// [crispy] allow quickload before quicksave
MN_StartControlPanel();
M_ReadSaveStrings();
SetNextMenu(&LoadDef);
M_ReadSaveStrings();
quickSaveSlot = -2; // means to pick a slot now
return;
}
@ -2010,6 +2202,36 @@ void MN_BackSecondary(void)
}
}
static void UpdateRectY(menu_t *menu, int m_y)
{
for (int i = 0; i < menu->numitems; i++)
{
menu->menuitems[i].rect.y = SAVE_LOAD_RECT_Y(m_y, i);
}
}
void M_ResetAutoSave(void)
{
int m_y;
if (G_AutoSaveEnabled())
{
loadsave_title_y = AUTOGRAPHIC_Y;
m_y = M_Y_AUTOSAVE;
}
else
{
loadsave_title_y = LOADGRAPHIC_Y;
m_y = M_Y_LOADSAVE;
}
LoadDef.y = m_y;
UpdateRectY(&LoadDef, m_y);
SaveDef.y = m_y;
UpdateRectY(&SaveDef, m_y);
}
//
// M_Init
//
@ -2029,6 +2251,7 @@ void M_Init(void)
messageString = NULL;
messageLastMenuActive = menuactive;
quickSaveSlot = -1;
M_ResetAutoSave();
int lumpnum = W_CheckNumForName("DBIGFONT");
if (lumpnum > 0)
@ -2467,9 +2690,31 @@ static void ClearHighlightedItems(void)
}
}
static void M_UpdateLoadMenu(void)
{
if (G_AutoSaveEnabled()
&& (currentMenu == &LoadDef || currentMenu == &LoadAutoSaveDef))
{
if (savepage == 0 && currentMenu == &LoadDef)
{
SetNextMenu(&LoadAutoSaveDef);
}
else if (savepage != 0 && currentMenu == &LoadAutoSaveDef)
{
SetNextMenu(&LoadDef);
}
}
}
static boolean AnyLoadSaveMenu(void)
{
return (currentMenu == &LoadDef || currentMenu == &SaveDef
|| currentMenu == &LoadAutoSaveDef);
}
static boolean SaveLoadResponder(menu_action_t action, int ch)
{
if (currentMenu != &LoadDef && currentMenu != &SaveDef)
if (!AnyLoadSaveMenu())
{
return false;
}
@ -2481,6 +2726,7 @@ static boolean SaveLoadResponder(menu_action_t action, int ch)
if (M_ToUpper(ch) == 'Y' || action == MENU_ENTER)
{
M_DeleteGame(old_menu_input == mouse_mode ? highlight_item : itemOn);
M_ReadSaveStrings();
M_StartSound(sfx_itemup);
delete_verify = false;
}
@ -2501,6 +2747,7 @@ static boolean SaveLoadResponder(menu_action_t action, int ch)
{
savepage--;
quickSaveSlot = -1;
M_UpdateLoadMenu();
M_ReadSaveStrings();
M_StartSound(sfx_pstop);
}
@ -2512,6 +2759,7 @@ static boolean SaveLoadResponder(menu_action_t action, int ch)
{
savepage++;
quickSaveSlot = -1;
M_UpdateLoadMenu();
M_ReadSaveStrings();
M_StartSound(sfx_pstop);
}
@ -2627,6 +2875,14 @@ static boolean MouseResponder(void)
return false;
}
static boolean AllowDeleteSaveGame(void)
{
return (((currentMenu == &LoadDef || currentMenu == &SaveDef)
&& LoadDef.menuitems[itemOn].status)
|| (currentMenu == &LoadAutoSaveDef
&& LoadAutoSaveDef.menuitems[itemOn].status));
}
boolean M_Responder(event_t *ev)
{
int ch;
@ -3036,9 +3292,9 @@ boolean M_Responder(event_t *ev)
if (action == MENU_CLEAR)
{
if (currentMenu == &LoadDef || currentMenu == &SaveDef)
if (AnyLoadSaveMenu())
{
if (LoadMenu[itemOn].status)
if (AllowDeleteSaveGame())
{
M_StartSound(sfx_itemup);
currentMenu->lastOn = itemOn;

View File

@ -57,6 +57,7 @@ void M_Init(void);
void MN_StartControlPanel(void);
void MN_ForcedLoadAutoSave(const char *msg);
void MN_ForcedLoadGame(const char *msg); // killough 5/15/98: forced loadgames
void MN_Trans(void); // killough 11/98: reset translucency
void MN_SetupResetMenu(void);
@ -87,8 +88,12 @@ extern int savepage;
extern const char *default_skill_strings[];
void M_ResetAutoSave(void);
void MN_SetQuickSaveSlot(int slot);
void M_SaveAutoSave(void);
void MN_InitMenuStrings(void);
boolean MN_StartsWithMapIdentifier(char *str);

View File

@ -3289,17 +3289,20 @@ static setup_menu_t gen_settings6[] = {
{"Screen wipe effect", S_CHOICE | S_STRICT, OFF_CNTR_X, M_SPC,
{"screen_melt"}, .strings_id = str_screen_melt},
{"On death action", S_CHOICE, OFF_CNTR_X, M_SPC, {"death_use_action"},
.strings_id = str_death_use_action},
{"Demo progress bar", S_ONOFF, OFF_CNTR_X, M_SPC, {"demobar"}},
{"Screen flashes", S_ONOFF | S_STRICT, OFF_CNTR_X, M_SPC,
{"palette_changes"}},
{"Invulnerability effect", S_CHOICE | S_STRICT, OFF_CNTR_X, M_SPC,
{"invul_mode"}, .strings_id = str_invul_mode, .action = R_InvulMode},
{"Demo progress bar", S_ONOFF, OFF_CNTR_X, M_SPC, {"demobar"}},
{"On death action", S_CHOICE, OFF_CNTR_X, M_SPC, {"death_use_action"},
.strings_id = str_death_use_action},
{"Auto save", S_ONOFF, OFF_CNTR_X, M_SPC, {"autosave"},
.action = M_ResetAutoSave},
{"Organize save files", S_ONOFF | S_PRGWARN, OFF_CNTR_X, M_SPC,
{"organize_savefiles"}},

View File

@ -322,14 +322,19 @@ void P_DeathThink (player_t* player)
{
activate_death_use_reload = 1;
if (savegameslot >= 0)
if (!G_AutoSaveEnabled() || !G_LoadAutoSaveDeathUse())
{
char *file = G_SaveGameName(savegameslot);
G_LoadGame(file, savegameslot, false);
free(file);
if (savegameslot >= 0)
{
char *file = G_SaveGameName(savegameslot);
G_LoadGame(file, savegameslot, false);
free(file);
}
else
{
player->playerstate = PST_REBORN;
}
}
else
player->playerstate = PST_REBORN;
}
}