raze/source/common/menu/savegamemanager.cpp

1101 lines
24 KiB
C++
Raw Normal View History

/*
** loadsavemenu.cpp
** The load game and save game menus
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "menu/menu.h"
#include "version.h"
#include "m_png.h"
#include "filesystem.h"
#include "v_text.h"
#include "d_event.h"
#include "gstrings.h"
#include "d_gui.h"
#include "v_draw.h"
#include "files.h"
#include "resourcefile.h"
#include "sjson.h"
#include "cmdlib.h"
#include "files.h"
#include "savegamehelp.h"
#include "i_specialpaths.h"
2019-11-30 18:23:54 +00:00
#include "c_dispatch.h"
#include "../../platform/win32/i_findfile.h" // This is a temporary direct path. Needs to be fixed when stuff gets cleaned up.
FSavegameManager savegameManager;
2019-11-30 21:46:00 +00:00
void FSavegameManager::LoadGame(FSaveGameNode* node)
2019-11-30 18:23:54 +00:00
{
if (gi->LoadGame(node))
2019-11-30 21:46:00 +00:00
{
}
}
void FSavegameManager::SaveGame(FSaveGameNode* node, bool ok4q, bool forceq)
{
if (gi->SaveGame(node))
2019-11-30 18:23:54 +00:00
{
FString fn = node->Filename;
FString desc = node->SaveTitle;
NotifyNewSave(fn, desc, ok4q, forceq);
}
}
//=============================================================================
//
// Save data maintenance
//
//=============================================================================
void FSavegameManager::ClearSaveGames()
{
for (unsigned i = 0; i<SaveGames.Size(); i++)
{
if (!SaveGames[i]->bNoDelete)
delete SaveGames[i];
}
SaveGames.Clear();
}
FSavegameManager::~FSavegameManager()
{
ClearSaveGames();
}
//=============================================================================
//
// Save data maintenance
//
//=============================================================================
int FSavegameManager::RemoveSaveSlot(int index)
{
int listindex = SaveGames[0]->bNoDelete ? index - 1 : index;
if (listindex < 0) return index;
remove(SaveGames[index]->Filename.GetChars());
UnloadSaveData();
FSaveGameNode *file = SaveGames[index];
if (quickSaveSlot == SaveGames[index])
{
quickSaveSlot = nullptr;
}
if (!file->bNoDelete) delete file;
if (LastSaved == listindex) LastSaved = -1;
else if (LastSaved > listindex) LastSaved--;
if (LastAccessed == listindex) LastAccessed = -1;
else if (LastAccessed > listindex) LastAccessed--;
SaveGames.Delete(index);
if ((unsigned)index >= SaveGames.Size()) index--;
ExtractSaveData(index);
return index;
}
//=============================================================================
//
//
//
//=============================================================================
int FSavegameManager::InsertSaveNode(FSaveGameNode *node)
{
if (SaveGames.Size() == 0)
{
return SaveGames.Push(node);
}
if (node->bOldVersion)
{ // Add node at bottom of list
return SaveGames.Push(node);
}
else
{ // Add node at top of list
unsigned int i;
for (i = 0; i < SaveGames.Size(); i++)
{
if (SaveGames[i]->bOldVersion || node->SaveTitle.CompareNoCase(SaveGames[i]->SaveTitle) <= 0)
{
break;
}
}
SaveGames.Insert(i, node);
return i;
}
}
//=============================================================================
//
// M_ReadSaveStrings
//
// Find savegames and read their titles
//
//=============================================================================
void FSavegameManager::ReadSaveStrings()
{
if (SaveGames.Size() == 0)
{
void *filefirst;
findstate_t c_file;
FString filter;
LastSaved = LastAccessed = -1;
quickSaveSlot = nullptr;
filter = G_BuildSaveName("*");
filefirst = I_FindFirst(filter.GetChars(), &c_file);
if (filefirst != ((void *)(-1)))
{
do
{
// I_FindName only returns the file's name and not its full path
FString filepath = G_BuildSaveName(I_FindName(&c_file));
FResourceFile *savegame = FResourceFile::OpenResourceFile(filepath, true, true);
if (savegame != nullptr)
{
FResourceLump *info = savegame->FindLump("info.json");
if (info == nullptr)
{
// savegame info not found. This is not a savegame so leave it alone.
delete savegame;
continue;
}
auto fr = info->NewReader();
FString title;
int check = G_ValidateSavegame(fr, &title);
2019-11-30 21:46:00 +00:00
fr.Close();
delete savegame;
if (check != 0)
{
FSaveGameNode *node = new FSaveGameNode;
node->Filename = filepath;
node->bOldVersion = true;
node->bMissingWads = false;
node->SaveTitle = title;
InsertSaveNode(node);
}
}
} while (I_FindNext (filefirst, &c_file) == 0);
I_FindClose (filefirst);
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave)
{
FSaveGameNode *node;
if (file.IsEmpty())
return;
ReadSaveStrings();
// See if the file is already in our list
for (unsigned i = 0; i<SaveGames.Size(); i++)
{
FSaveGameNode *node = SaveGames[i];
#ifdef __unix__
if (node->Filename.Compare(file) == 0)
#else
if (node->Filename.CompareNoCase(file) == 0)
#endif
{
node->SaveTitle = title;
node->bOldVersion = false;
node->bMissingWads = false;
if (okForQuicksave)
{
if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node;
LastAccessed = LastSaved = i;
}
return;
}
}
node = new FSaveGameNode;
node->SaveTitle = title;
node->Filename = file;
node->bOldVersion = false;
node->bMissingWads = false;
int index = InsertSaveNode(node);
if (okForQuicksave)
{
if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node;
LastAccessed = LastSaved = index;
}
else
{
LastAccessed = ++LastSaved;
}
}
//=============================================================================
//
// Loads the savegame
//
//=============================================================================
void FSavegameManager::LoadSavegame(int Selected)
{
2019-11-30 18:23:54 +00:00
savegameManager.LoadGame(SaveGames[Selected]);
if (quickSaveSlot == (FSaveGameNode*)1)
{
quickSaveSlot = SaveGames[Selected];
}
M_ClearMenus();
LastAccessed = Selected;
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::DoSave(int Selected, const char *savegamestring)
{
if (Selected != 0)
{
2019-11-30 18:23:54 +00:00
auto node = *SaveGames[Selected];
node.SaveTitle = savegamestring;
2019-11-30 21:46:00 +00:00
savegameManager.SaveGame(&node, true, false);
}
else
{
// Find an unused filename and save as that
FString filename;
int i;
for (i = 0;; ++i)
{
filename = G_BuildSaveName(FStringf("save%04d", i));
if (!FileExists(filename))
{
break;
}
}
2019-11-30 18:23:54 +00:00
FSaveGameNode sg{ savegamestring, filename };
2019-11-30 21:46:00 +00:00
savegameManager.SaveGame(&sg, true, false);
}
M_ClearMenus();
}
//=============================================================================
//
//
//
//=============================================================================
unsigned FSavegameManager::ExtractSaveData(int index)
{
FResourceFile *resf;
FSaveGameNode *node;
if (index == -1)
{
if (SaveGames.Size() > 0 && SaveGames[0]->bNoDelete)
{
index = LastSaved + 1;
}
else
{
index = LastAccessed < 0? 0 : LastAccessed;
}
}
UnloadSaveData();
if ((unsigned)index < SaveGames.Size() &&
(node = SaveGames[index]) &&
!node->Filename.IsEmpty() &&
!node->bOldVersion &&
(resf = FResourceFile::OpenResourceFile(node->Filename.GetChars(), true)) != nullptr)
{
FResourceLump *info = resf->FindLump("info.json");
if (info == nullptr)
{
// this should not happen because the file has already been verified.
return index;
}
auto fr = info->NewReader();
auto data = fr.ReadPadded(1);
sjson_context* ctx = sjson_create_context(0, 0, NULL);
if (ctx)
{
sjson_node* root = sjson_decode(ctx, (const char*)data.Data());
FString comment = sjson_get_string(root, "Creation Time", "");
FString pcomment = sjson_get_string(root, "Comment", "");
if (comment.Len() > 0) comment += "\n";
comment += pcomment;
SaveCommentString = comment;
// Extract pic (todo: let the renderer write a proper PNG file instead of a raw canvas dunp of the software renderer - and make it work for all games.)
FResourceLump *pic = resf->FindLump("savepic.png");
if (pic != nullptr)
{
FileReader picreader;
picreader.OpenMemoryArray([=](TArray<uint8_t> &array)
{
auto cache = pic->Lock();
array.Resize(pic->LumpSize);
memcpy(&array[0], cache, pic->LumpSize);
pic->Unlock();
return true;
});
PNGHandle *png = M_VerifyPNG(picreader);
if (png != nullptr)
{
SavePic = nullptr; // not yet implemented: PNGTexture_CreateFromFile(png, node->Filename);
delete png;
if (SavePic && SavePic->GetWidth() == 1 && SavePic->GetHeight() == 1)
{
delete SavePic;
SavePic = nullptr;
SavePicData.Clear();
}
}
}
}
delete resf;
}
return index;
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::UnloadSaveData()
{
if (SavePic != nullptr)
{
delete SavePic;
}
SaveCommentString = "";
SavePic = nullptr;
SavePicData.Clear();
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::ClearSaveStuff()
{
UnloadSaveData();
if (quickSaveSlot == (FSaveGameNode*)1)
{
quickSaveSlot = nullptr;
}
}
//=============================================================================
//
//
//
//=============================================================================
bool FSavegameManager::DrawSavePic(int x, int y, int w, int h)
{
if (SavePic == nullptr) return false;
DrawTexture(&twod, SavePic, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_Masked, false, TAG_DONE);
return true;
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::SetFileInfo(int Selected)
{
if (!SaveGames[Selected]->Filename.IsEmpty())
{
SaveCommentString.Format("File on disk:\n%s", SaveGames[Selected]->Filename.GetChars());
}
}
//=============================================================================
//
//
//
//=============================================================================
unsigned FSavegameManager::SavegameCount()
{
return SaveGames.Size();
}
//=============================================================================
//
//
//
//=============================================================================
FSaveGameNode *FSavegameManager::GetSavegame(int i)
{
return SaveGames[i];
}
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::InsertNewSaveNode()
{
2019-11-30 21:46:00 +00:00
NewSaveNode.SaveTitle = GStrings("NEWSAVE");
NewSaveNode.bNoDelete = true;
SaveGames.Insert(0, &NewSaveNode);
}
//=============================================================================
//
//
//
//=============================================================================
bool FSavegameManager::RemoveNewSaveNode()
{
if (SaveGames[0] == &NewSaveNode)
{
SaveGames.Delete(0);
return true;
}
return false;
}
2019-11-30 18:23:54 +00:00
//=============================================================================
//
//
//
//=============================================================================
CVAR(Bool, saveloadconfirmation, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Int, autosavenum, 0, CVAR_NOSET | CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
static int nextautosave = -1;
CVAR(Int, disableautosave, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CUSTOM_CVAR(Int, autosavecount, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 1)
self = 1;
}
CVAR(Int, quicksavenum, 0, CVAR_NOSET | CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
static int nextquicksave = -1;
CUSTOM_CVAR(Int, quicksavecount, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 1)
self = 1;
}
void M_Autosave()
{
if (!gi->CanSave()) return;
FString description;
FString file;
// Keep a rotating sets of autosaves
UCVarValue num;
const char* readableTime;
int count = autosavecount != 0 ? autosavecount : 1;
if (nextautosave == -1)
{
nextautosave = (autosavenum + 1) % count;
}
num.Int = nextautosave;
autosavenum.ForceSet(num, CVAR_Int);
FSaveGameNode sg;
sg.Filename = G_BuildSaveName(FStringf("auto%04d", nextautosave));
readableTime = myasctime();
sg.SaveTitle.Format("Autosave %s", readableTime);
nextautosave = (nextautosave + 1) % count;
2019-11-30 21:46:00 +00:00
savegameManager.SaveGame(&sg, false, false);
2019-11-30 18:23:54 +00:00
}
CCMD(autosave)
{
if (disableautosave) return;
M_Autosave();
}
CCMD(rotatingquicksave)
{
if (!gi->CanSave()) return;
FString description;
FString file;
// Keep a rotating sets of quicksaves
UCVarValue num;
const char* readableTime;
int count = quicksavecount != 0 ? quicksavecount : 1;
if (nextquicksave == -1)
{
nextquicksave = (quicksavenum + 1) % count;
}
num.Int = nextquicksave;
quicksavenum.ForceSet(num, CVAR_Int);
FSaveGameNode sg;
sg.Filename = G_BuildSaveName(FStringf("quick%04d", nextquicksave));
readableTime = myasctime();
sg.SaveTitle.Format("Quicksave %s", readableTime);
nextquicksave = (nextquicksave + 1) % count;
2019-11-30 21:46:00 +00:00
savegameManager.SaveGame(&sg, false, false);
2019-11-30 18:23:54 +00:00
}
//=============================================================================
//
//
//
//=============================================================================
CCMD(quicksave)
{ // F6
if (!gi->CanSave()) return;
if (savegameManager.quickSaveSlot == NULL || savegameManager.quickSaveSlot == (FSaveGameNode*)1)
{
M_StartControlPanel(true);
M_SetMenu(NAME_SaveGameMenu);
return;
}
auto slot = savegameManager.quickSaveSlot;
// [mxd]. Just save the game, no questions asked.
if (!saveloadconfirmation)
{
2019-11-30 21:46:00 +00:00
savegameManager.SaveGame(savegameManager.quickSaveSlot, true, true);
2019-11-30 18:23:54 +00:00
return;
}
gi->MenuSound(ActivateSound);
FString tempstring = GStrings("QSPROMPT");
tempstring.Substitute("%s", slot->SaveTitle.GetChars());
DMenu* newmenu = CreateMessageBoxMenu(DMenu::CurrentMenu, tempstring, 0, INT_MAX, false, NAME_None, [](bool res)
{
if (res)
{
2019-11-30 21:46:00 +00:00
savegameManager.SaveGame(savegameManager.quickSaveSlot, true, true);
2019-11-30 18:23:54 +00:00
}
});
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
CCMD(quickload)
{ // F9
#if 0
if (netgame)
{
M_StartControlPanel(true);
M_StartMessage(GStrings("QLOADNET"), 1);
return;
}
#endif
if (savegameManager.quickSaveSlot == nullptr || savegameManager.quickSaveSlot == (FSaveGameNode*)1)
{
M_StartControlPanel(true);
// signal that whatever gets loaded should be the new quicksave
savegameManager.quickSaveSlot = (FSaveGameNode*)1;
M_SetMenu(NAME_LoadGameMenu);
return;
}
// [mxd]. Just load the game, no questions asked.
if (!saveloadconfirmation)
{
savegameManager.LoadGame(savegameManager.quickSaveSlot);
return;
}
FString tempstring = GStrings("QLPROMPT");
tempstring.Substitute("%s", savegameManager.quickSaveSlot->SaveTitle.GetChars());
M_StartControlPanel(true);
DMenu* newmenu = CreateMessageBoxMenu(DMenu::CurrentMenu, tempstring, 0, INT_MAX, false, NAME_None, [](bool res)
{
if (res)
{
savegameManager.LoadGame(savegameManager.quickSaveSlot);
}
});
M_ActivateMenu(newmenu);
}
#if 0
// Duke
if ((buttonMap.ButtonDown(gamefunc_Quick_Load) || g_doQuickSave == 2) && (myplayer.gm & MODE_GAME))
{
buttonMap.ClearButton(gamefunc_Quick_Load);
g_doQuickSave = 0;
if (g_quickload == nullptr || !g_quickload->isValid())
{
C_DoCommand("openloadmenu");
}
else if (g_quickload->isValid())
{
inputState.keyFlushChars();
inputState.ClearKeysDown();
if (G_LoadPlayerMaybeMulti(*g_quickload) != 0)
g_quickload->reset();
}
}
if ((buttonMap.ButtonDown(gamefunc_Quick_Save) || g_doQuickSave == 1) && (myplayer.gm & MODE_GAME))
{
buttonMap.ClearButton(gamefunc_Quick_Save);
g_doQuickSave = 0;
if (!g_lastusersave.isValid())
{
C_DoCommand("opensavemenu");
return;
}
inputState.keyFlushChars();
if (sprite[myplayer.i].extra <= 0)
{
P_DoQuote(QUOTE_SAVE_DEAD, &myplayer);
return;
}
g_screenCapture = 1;
G_DrawRooms(myconnectindex, 65536);
g_screenCapture = 0;
if (g_lastusersave.isValid())
{
savebrief_t& sv = g_lastusersave;
// dirty hack... char 127 in last position indicates an auto-filled name
if (sv.name[MAXSAVEGAMENAME] == 127)
{
strncpy(sv.name, g_mapInfo[ud.volume_number * MAXLEVELS + ud.level_number].name, MAXSAVEGAMENAME);
sv.name[MAXSAVEGAMENAME] = 127;
}
g_quickload = &sv;
G_SavePlayerMaybeMulti(sv);
}
}
// handle CON_SAVE and CON_SAVENN
if (g_saveRequested)
{
inputState.keyFlushChars();
videoNextPage();
g_screenCapture = 1;
G_DrawRooms(myconnectindex, 65536);
g_screenCapture = 0;
G_SavePlayerMaybeMulti(g_lastautosave, true);
g_quickload = &g_lastautosave;
OSD_Printf("Saved: %s\n", g_lastautosave.path);
g_saveRequested = false;
}
// RR
if ((buttonMap.ButtonDown(gamefunc_Quick_Load) || g_doQuickSave == 2) && (!RRRA || ud.player_skill != 4) && (!RR || RRRA || ud.player_skill != 5) && (g_player[myconnectindex].ps->gm & MODE_GAME))
{
buttonMap.ClearButton(gamefunc_Quick_Load);
g_doQuickSave = 0;
if (g_quickload == nullptr || !g_quickload->isValid())
{
C_DoCommand("openloadmenu");
}
else if (g_quickload->isValid())
{
inputState.keyFlushChars();
inputState.ClearKeysDown();
S_PauseSounds(true);
if (G_LoadPlayerMaybeMulti(*g_quickload) != 0)
g_quickload->reset();
}
}
if ((buttonMap.ButtonDown(gamefunc_Quick_Save) || g_doQuickSave == 1) && (!RRRA || ud.player_skill != 4) && (!RR || RRRA || ud.player_skill != 5) && (g_player[myconnectindex].ps->gm & MODE_GAME))
{
buttonMap.ClearButton(gamefunc_Quick_Save);
g_doQuickSave = 0;
if (!g_lastusersave.isValid())
{
C_DoCommand("opensavemenu");
return;
}
inputState.keyFlushChars();
if (sprite[g_player[myconnectindex].ps->i].extra <= 0)
{
P_DoQuote(QUOTE_SAVE_DEAD, g_player[myconnectindex].ps);
return;
}
g_screenCapture = 1;
G_DrawRooms(myconnectindex, 65536);
g_screenCapture = 0;
if (g_lastusersave.isValid())
{
savebrief_t& sv = g_lastusersave;
// dirty hack... char 127 in last position indicates an auto-filled name
if (sv.name[MAXSAVEGAMENAME] == 127)
{
strncpy(sv.name, g_mapInfo[ud.volume_number * MAXLEVELS + ud.level_number].name, MAXSAVEGAMENAME);
sv.name[MAXSAVEGAMENAME] = 127;
}
g_quickload = &sv;
G_SavePlayerMaybeMulti(sv);
}
}
// sw
SWBOOL DoQuickSave(short save_num)
{
PauseAction();
if (SaveGame(save_num) != -1)
{
QuickLoadNum = save_num;
LastSaveNum = -1;
return FALSE;
}
return TRUE;
}
SWBOOL DoQuickLoad()
{
inputState.ClearKeysDown();
PauseAction();
ReloadPrompt = FALSE;
if (LoadGame(QuickLoadNum) == -1)
{
return FALSE;
}
ready2send = 1;
LastSaveNum = -1;
return TRUE;
}
// F6 quick save
if (inputState.GetKeyStatus(KEYSC_F6))
{
inputState.ClearKeyStatus(KEYSC_F6);
if (!TEST(pp->Flags, PF_DEAD))
{
if (QuickLoadNum < 0)
{
inputState.SetKeyStatus(sc_Escape);
ControlPanelType = ct_savemenu;
}
else
{
inputState.ClearAllInput();
DoQuickSave(QuickLoadNum);
ResumeAction();
}
}
}
// F9 quick load
if (inputState.GetKeyStatus(KEYSC_F9))
{
inputState.ClearKeyStatus(KEYSC_F9);
if (!TEST(pp->Flags, PF_DEAD))
{
if (QuickLoadNum < 0)
{
inputState.SetKeyStatus(sc_Escape);
ControlPanelType = ct_loadmenu;
}
else
{
DoQuickLoad();
ResumeAction();
}
}
}
////////////////////////////////////////////////
// Load Game menu
// This function gets called whenever you
// press enter on one of the load game
// spots.
// I'm figuring it need to do the following:
// . Load the game if there is one by calling: MNU_LoadGameCustom.
////////////////////////////////////////////////
SWBOOL MNU_GetLoadCustom(void)
{
short load_num;
load_num = currentmenu->cursor;
// no saved game exists - don't do anything
if (SaveGameDescr[load_num][0] == '\0')
return FALSE;
if (InMenuLevel || DemoMode || DemoPlaying)
{
LoadSaveMsg("Loading...");
if (LoadGame(load_num) == -1)
return FALSE;
QuickLoadNum = load_num;
// the (Quick)Save menu should default to the last loaded game
SaveGameGroup.cursor = load_num;
ExitMenus();
ExitLevel = TRUE;
LoadGameOutsideMoveLoop = TRUE;
if (DemoMode || DemoPlaying)
LoadGameFromDemo = TRUE;
return TRUE;
}
LoadSaveMsg("Loading...");
PauseAction();
if (LoadGame(load_num) == -1)
{
ResumeAction();
return FALSE;
}
QuickLoadNum = load_num;
// the (Quick)Save menu should default to the last loaded game
SaveGameGroup.cursor = load_num;
ready2send = 1;
LastSaveNum = -1;
ExitMenus();
if (DemoMode)
{
ExitLevel = TRUE;
DemoPlaying = FALSE;
}
return TRUE;
}
////////////////////////////////////////////////
// Save Game menu
// This function gets called whenever you
// press enter on one of the save game
// spots.
// I'm figuring it need to do the following:
// . Call MNU_GetInput to allow string input of description.
// . Save the game if there is one by calling: MNU_SaveGameCustom.
////////////////////////////////////////////////
SWBOOL MNU_GetSaveCustom(void)
{
short save_num;
extern SWBOOL InMenuLevel, LoadGameOutsideMoveLoop;
save_num = currentmenu->cursor;
if (InMenuLevel)
return FALSE;
if (MenuInputMode)
{
LoadSaveMsg("Saving...");
if (DoQuickSave(save_num) == FALSE)
{
LoadGameGroup.cursor = save_num;
}
ResumeAction();
ExitMenus();
// toggle edit mode
MenuInputMode = FALSE;
}
else
{
strcpy(BackupSaveGameDescr, SaveGameDescr[save_num]);
// clear keyboard buffer
while (inputState.keyBufferWaiting())
{
if (inputState.keyGetChar() == 0)
inputState.keyGetChar();
}
// toggle edit mode
MenuInputMode = TRUE;
}
return TRUE;
}
// Blood
static void DoQuickLoad(void)
{
if (!gGameMenuMgr.m_bActive)
{
if (gQuickLoadSlot != -1)
{
QuickLoadGame();
return;
}
if (gQuickLoadSlot == -1 && gQuickSaveSlot != -1)
{
gQuickLoadSlot = gQuickSaveSlot;
QuickLoadGame();
return;
}
gGameMenuMgr.Push(&menuLoadGame, -1);
}
}
static void DoQuickSave(void)
{
if (gGameStarted && !gGameMenuMgr.m_bActive && gPlayer[myconnectindex].pXSprite->health != 0)
{
if (gQuickSaveSlot != -1)
{
QuickSaveGame();
return;
}
gGameMenuMgr.Push(&menuSaveGame, -1);
}
}
if (gDoQuickSave)
{
inputState.keyFlushScans();
switch (gDoQuickSave)
{
case 1:
DoQuickSave();
break;
case 2:
DoQuickLoad();
break;
}
gDoQuickSave = 0;
return;
}
case sc_F6:
inputState.keyFlushScans();
DoQuickSave();
break;
case sc_F9:
inputState.keyFlushScans();
DoQuickLoad();
break;
#endif