diff --git a/src/d_event.h b/src/d_event.h index cfc7390a..eac541da 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -78,6 +78,8 @@ typedef enum ga_worlddone, ga_screenshot, ga_reloadlevel, + ga_loadautosave, + ga_saveautosave, } gameaction_t; diff --git a/src/g_game.c b/src/g_game.c index f85049d5..39ef7d9b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #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) diff --git a/src/g_game.h b/src/g_game.h index 635e6746..af24ce67 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -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 diff --git a/src/mn_menu.c b/src/mn_menu.c index a43184cd..f1bc905d 100644 --- a/src/mn_menu.c +++ b/src/mn_menu.c @@ -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 = ¤tMenu->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 = ¤tMenu->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 = ¤tMenu->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; diff --git a/src/mn_menu.h b/src/mn_menu.h index b425088e..7fefaf60 100644 --- a/src/mn_menu.h +++ b/src/mn_menu.h @@ -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); diff --git a/src/mn_setup.c b/src/mn_setup.c index 7f3f9474..5886b56f 100644 --- a/src/mn_setup.c +++ b/src/mn_setup.c @@ -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"}}, diff --git a/src/p_user.c b/src/p_user.c index 91a64be8..c20f7b0d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -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; } }