Implemented autosave (#292)

dev-mp
krzys-h 2014-11-11 14:50:44 +01:00
parent b8103963c3
commit a1fe9c8d7f
9 changed files with 366 additions and 39 deletions

View File

@ -241,6 +241,9 @@ enum EventType
EVENT_INTERFACE_EDITVALUE= 477,
EVENT_INTERFACE_SOLUCE4 = 478,
EVENT_INTERFACE_BLOOD = 479,
EVENT_INTERFACE_AUTOSAVE_ENABLE = 780,
EVENT_INTERFACE_AUTOSAVE_INTERVAL = 781,
EVENT_INTERFACE_AUTOSAVE_SLOTS = 782,
EVENT_INTERFACE_KINFO1 = 500,
EVENT_INTERFACE_KINFO2 = 501,

View File

@ -154,47 +154,66 @@ CSNDFile* CResourceManager::GetSNDFileHandler(const std::string &filename)
bool CResourceManager::Exists(const std::string &filename)
{
return PHYSFS_exists(CleanPath(filename).c_str());
if(PHYSFS_isInit())
{
return PHYSFS_exists(CleanPath(filename).c_str());
}
return false;
}
bool CResourceManager::DirectoryExists(const std::string& directory)
{
return PHYSFS_exists(CleanPath(directory).c_str()) && PHYSFS_isDirectory(CleanPath(directory).c_str());
if(PHYSFS_isInit())
{
return PHYSFS_exists(CleanPath(directory).c_str()) && PHYSFS_isDirectory(CleanPath(directory).c_str());
}
return false;
}
bool CResourceManager::CreateDirectory(const std::string& directory)
{
return PHYSFS_mkdir(CleanPath(directory).c_str());
if(PHYSFS_isInit())
{
return PHYSFS_mkdir(CleanPath(directory).c_str());
}
return false;
}
//TODO: Don't use boost::filesystem here
bool CResourceManager::RemoveDirectory(const std::string& directory)
{
bool success = true;
std::string writeDir = PHYSFS_getWriteDir();
try
if(PHYSFS_isInit())
{
fs::remove_all(writeDir + "/" + CleanPath(directory));
bool success = true;
std::string writeDir = PHYSFS_getWriteDir();
try
{
fs::remove_all(writeDir + "/" + CleanPath(directory));
}
catch (std::exception & e)
{
success = false;
}
return success;
}
catch (std::exception & e)
{
success = false;
}
return success;
return false;
}
std::vector<std::string> CResourceManager::ListFiles(const std::string &directory)
{
std::vector<std::string> result;
char **files = PHYSFS_enumerateFiles(CleanPath(directory).c_str());
for (char **i = files; *i != nullptr; i++)
if(PHYSFS_isInit())
{
result.push_back(*i);
}
char **files = PHYSFS_enumerateFiles(CleanPath(directory).c_str());
PHYSFS_freeList(files);
for (char **i = files; *i != nullptr; i++)
{
result.push_back(*i);
}
PHYSFS_freeList(files);
}
return result;
}
@ -203,18 +222,21 @@ std::vector<std::string> CResourceManager::ListDirectories(const std::string &di
{
std::vector<std::string> result;
char **files = PHYSFS_enumerateFiles(CleanPath(directory).c_str());
for (char **i = files; *i != nullptr; i++)
if(PHYSFS_isInit())
{
std::string path = CleanPath(directory) + "/" + (*i);
if (PHYSFS_isDirectory(path.c_str()))
{
result.push_back(*i);
}
}
char **files = PHYSFS_enumerateFiles(CleanPath(directory).c_str());
PHYSFS_freeList(files);
for (char **i = files; *i != nullptr; i++)
{
std::string path = CleanPath(directory) + "/" + (*i);
if (PHYSFS_isDirectory(path.c_str()))
{
result.push_back(*i);
}
}
PHYSFS_freeList(files);
}
return result;
}
@ -241,6 +263,45 @@ long long CResourceManager::GetLastModificationTime(const std::string& filename)
return -1;
}
//TODO: Don't use boost::filesystem. Why doesn't PHYSFS have this?
bool CResourceManager::Move(const std::string& from, const std::string& to)
{
if(PHYSFS_isInit())
{
bool success = true;
std::string writeDir = PHYSFS_getWriteDir();
try
{
fs::rename(writeDir + "/" + CleanPath(from), writeDir + "/" + CleanPath(to));
}
catch (std::exception & e)
{
success = false;
}
return success;
}
return false;
}
//TODO: Don't use boost::filesystem. Why doesn't PHYSFS have this?
bool CResourceManager::Copy(const std::string& from, const std::string& to)
{
if(PHYSFS_isInit())
{
bool success = true;
std::string writeDir = PHYSFS_getWriteDir();
try
{
fs::copy(writeDir + "/" + CleanPath(from), writeDir + "/" + CleanPath(to));
}
catch (std::exception & e)
{
success = false;
}
return success;
}
return false;
}
int CResourceManager::SDLClose(SDL_RWops *context)
{

View File

@ -62,6 +62,11 @@ public:
static long long GetFileSize(const std::string &filename);
//! Returns last modification date as timestamp
static long long GetLastModificationTime(const std::string &filename);
//! Move file/directory
static bool Move(const std::string &from, const std::string &to);
//! Copy file/directory
static bool Copy(const std::string &from, const std::string &to);
private:
static int SDLSeek(SDL_RWops *context, int offset, int whence);

View File

@ -206,6 +206,9 @@ void InitializeRestext()
stringsEvent[EVENT_INTERFACE_EDITVALUE] = TR("Big indent\\Indent 2 or 4 spaces per level defined by braces");
stringsEvent[EVENT_INTERFACE_SOLUCE4] = TR("Access to solutions\\Show program \"4: Solution\" in the exercises");
stringsEvent[EVENT_INTERFACE_BLOOD] = TR("Blood\\Display blood when the astronaut or the alien queen is hit");
stringsEvent[EVENT_INTERFACE_AUTOSAVE_ENABLE] = TR("Autosave\\Enables autosave");
stringsEvent[EVENT_INTERFACE_AUTOSAVE_INTERVAL] = TR("Autosave interval\\How often your game will autosave");
stringsEvent[EVENT_INTERFACE_AUTOSAVE_SLOTS] = TR("Autosave slots\\How many autosave slots you'll have");
stringsEvent[EVENT_INTERFACE_KDEF] = TR("Standard controls\\Standard key functions");
stringsEvent[EVENT_INTERFACE_KLEFT] = TR("Turn left\\turns the bot to the left");

View File

@ -708,6 +708,11 @@ CRobotMain::CRobotMain(CApplication* app, bool loadProfile)
m_winTerminate = false;
m_exitAfterMission = false;
m_autosave = true;
m_autosaveInterval = 15;
m_autosaveSlots = 3;
m_autosaveLast = 0.0f;
m_joystickDeadzone = 0.2f;
SetDefaultInputBindings();
@ -3393,7 +3398,7 @@ void CRobotMain::InitEye()
bool CRobotMain::EventFrame(const Event &event)
{
m_time += event.rTime;
if (!m_movieLock) m_gameTime += event.rTime;
if (!m_movieLock && m_pause->GetPause() == PAUSE_NONE) m_gameTime += event.rTime;
if (!m_immediatSatCom && !m_beginSatCom &&
m_gameTime > 0.1f && m_phase == PHASE_SIMUL)
@ -3404,6 +3409,12 @@ bool CRobotMain::EventFrame(const Event &event)
if(!m_movieLock && m_pause->GetPause() == PAUSE_NONE && m_missionTimerStarted)
m_missionTimer += event.rTime;
if(m_pause->GetPause() == PAUSE_NONE && m_autosave && m_gameTime >= m_autosaveLast+(m_autosaveInterval*60) && m_phase == PHASE_SIMUL) {
m_autosaveLast = m_gameTime;
Autosave();
}
//CLogger::GetInstancePointer()->Debug("%f %f %d\n", m_gameTime, m_autosaveLast, m_autosaveInterval);
m_water->EventProcess(event);
m_cloud->EventProcess(event);
@ -4742,6 +4753,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
m_app->ResetKeyStates();
m_time = 0.0f;
m_gameTime = 0.0f;
m_autosaveLast = 0.0f;
m_infoUsed = 0;
m_selectObject = sel;
@ -6930,3 +6942,121 @@ void CRobotMain::StartMissionTimer()
m_missionTimerStarted = true;
}
}
void CRobotMain::SetAutosave(bool enable)
{
m_autosave = enable;
m_autosaveLast = m_gameTime;
AutosaveRotate(false);
}
bool CRobotMain::GetAutosave()
{
return m_autosave;
}
void CRobotMain::SetAutosaveInterval(int interval)
{
m_autosaveInterval = interval;
m_autosaveLast = m_gameTime;
}
int CRobotMain::GetAutosaveInterval()
{
return m_autosaveInterval;
}
void CRobotMain::SetAutosaveSlots(int slots)
{
m_autosaveSlots = slots;
AutosaveRotate(false);
}
int CRobotMain::GetAutosaveSlots()
{
return m_autosaveSlots;
}
int CRobotMain::AutosaveRotate(bool freeOne)
{
CLogger::GetInstancePointer()->Debug("Rotate autosaves...\n");
// Find autosave dirs
auto saveDirs = CResourceManager::ListDirectories(std::string(GetSavegameDir()) + "/" + GetGamerName());
std::map<int, std::string> autosaveDirs;
for(auto& dir : saveDirs)
{
try
{
const std::string autosavePrefix = "autosave";
if(dir.substr(0, autosavePrefix.length()) == "autosave")
{
int id = boost::lexical_cast<int>(dir.substr(autosavePrefix.length()));
autosaveDirs[id] = std::string(GetSavegameDir()) + "/" + GetGamerName() + "/" + dir;
}
}
catch(...)
{
CLogger::GetInstancePointer()->Debug("bad?\n");
// skip
}
}
if(autosaveDirs.size() == 0) return 1;
// Remove all but last m_autosaveSlots
std::map<int, std::string> autosavesToKeep;
int last_id = autosaveDirs.rbegin()->first;
int count = 0;
int to_keep = m_autosaveSlots-(freeOne ? 1 : 0);
int new_last_id = Math::Min(autosaveDirs.size(), to_keep);
bool rotate = false;
for(int i = last_id; i > 0; i--)
{
if(autosaveDirs.count(i) > 0)
{
count++;
if(count > m_autosaveSlots-(freeOne ? 1 : 0) || !m_autosave)
{
CLogger::GetInstancePointer()->Trace("Remove %s\n", autosaveDirs[i].c_str());
CResourceManager::RemoveDirectory(autosaveDirs[i]);
rotate = true;
}
else
{
CLogger::GetInstancePointer()->Trace("Keep %s\n", autosaveDirs[i].c_str());
autosavesToKeep[new_last_id-count+1] = autosaveDirs[i];
}
}
}
// Rename autosaves that we kept
if(rotate) {
for(auto& save : autosavesToKeep) {
std::string newDir = std::string(GetSavegameDir()) + "/" + GetGamerName() + "/autosave" + boost::lexical_cast<std::string>(save.first);
CLogger::GetInstancePointer()->Trace("Rename %s -> %s\n", save.second.c_str(), newDir.c_str());
CResourceManager::Move(save.second, newDir);
}
}
return rotate ? count : count+1;
}
void CRobotMain::Autosave()
{
int id = AutosaveRotate(true);
CLogger::GetInstancePointer()->Info("Autosave!\n");
std::string dir = std::string(GetSavegameDir()) + "/" + GetGamerName() + "/autosave" + boost::lexical_cast<std::string>(id);
if (!CResourceManager::DirectoryExists(dir))
{
CResourceManager::CreateDirectory(dir);
}
std::string savegameFileName = dir + "/data.sav";
std::string fileCBot = CResourceManager::GetSaveLocation() + "/" + dir + "/cbot.run";
char timestr[100];
TimeToAscii(time(NULL), timestr);
IOWriteScene(savegameFileName.c_str(), fileCBot.c_str(), const_cast<char*>((std::string("[AUTOSAVE] ")+timestr).c_str()));
m_dialog->MakeSaveScreenshot(dir + "/screen.png");
}

View File

@ -396,6 +396,13 @@ public:
std::string& GetUserLevelName(int id);
void StartMissionTimer();
void SetAutosave(bool enable);
bool GetAutosave();
void SetAutosaveInterval(int interval);
int GetAutosaveInterval();
void SetAutosaveSlots(int slots);
int GetAutosaveSlots();
protected:
bool EventFrame(const Event &event);
@ -429,6 +436,9 @@ protected:
void ExecuteCmd(char *cmd);
bool TestGadgetQuantity(int rank);
void UpdateSpeedLabel();
int AutosaveRotate(bool freeOne);
void Autosave();
protected:
@ -589,5 +599,10 @@ protected:
bool m_missionTimerEnabled;
bool m_missionTimerStarted;
float m_missionTimer;
bool m_autosave;
int m_autosaveInterval;
int m_autosaveSlots;
float m_autosaveLast;
};

View File

@ -167,6 +167,7 @@ CMainDialog::CMainDialog()
m_bCameraInvertY = false;
m_bEffect = true;
m_bBlood = true;
m_bAutosave = true;
m_shotDelay = 0;
m_glintMouse = Math::Point(0.0f, 0.0f);
@ -1239,6 +1240,34 @@ void CMainDialog::ChangePhase(Phase phase)
pos.y -= 0.048f;
pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_BLOOD);
pc->SetState(STATE_SHADOW);
pos.y -= 0.048f;
pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_AUTOSAVE_ENABLE);
pc->SetState(STATE_SHADOW);
pos.y -= 0.048f;
pos.y -= ddim.y;
ddim.x = dim.x*2.5f;
psl = pw->CreateSlider(pos, ddim, -1, EVENT_INTERFACE_AUTOSAVE_INTERVAL);
psl->SetState(STATE_SHADOW);
psl->SetLimit(1.0f, 30.0f);
psl->SetArrowStep(1.0f);
pos.y += ddim.y/2;
GetResource(RES_EVENT, EVENT_INTERFACE_AUTOSAVE_INTERVAL, name);
pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, name);
pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
pos.y -= ddim.y/2;
pos.x = ox+sx*3+dim.x*3.5f;
psl = pw->CreateSlider(pos, ddim, -1, EVENT_INTERFACE_AUTOSAVE_SLOTS);
psl->SetState(STATE_SHADOW);
psl->SetLimit(1.0f, 10.0f);
psl->SetArrowStep(1.0f);
pos.y += ddim.y/2;
GetResource(RES_EVENT, EVENT_INTERFACE_AUTOSAVE_SLOTS, name);
pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, name);
pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
pos.y -= ddim.y/2;
//? pos.y -= 0.048f;
//? pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_NICERST);
//? pc->SetState(STATE_SHADOW);
@ -2570,6 +2599,23 @@ bool CMainDialog::EventProcess(const Event &event)
ChangeSetupButtons();
UpdateSetupButtons();
break;
case EVENT_INTERFACE_AUTOSAVE_ENABLE:
m_bAutosave = !m_bAutosave;
m_main->SetAutosave(m_bAutosave);
ChangeSetupButtons();
UpdateSetupButtons();
break;
case EVENT_INTERFACE_AUTOSAVE_INTERVAL:
ChangeSetupButtons();
UpdateSetupButtons();
break;
case EVENT_INTERFACE_AUTOSAVE_SLOTS:
ChangeSetupButtons();
UpdateSetupButtons();
break;
default:
break;
@ -4175,12 +4221,17 @@ bool CMainDialog::IOWriteScene()
std::string fileCBot = CResourceManager::GetSaveLocation() + "/" + dir + "/cbot.run";
m_main->IOWriteScene(savegameFileName.c_str(), fileCBot.c_str(), info);
m_shotDelay = 3;
m_shotName = CResourceManager::GetSaveLocation() + "/" + dir + "/screen.png"; //TODO: Use PHYSFS?
MakeSaveScreenshot(dir + "/screen.png");
return true;
}
void CMainDialog::MakeSaveScreenshot(const std::string& name)
{
m_shotDelay = 3;
m_shotName = CResourceManager::GetSaveLocation() + "/" + name; //TODO: Use PHYSFS?
}
// Reads the scene.
bool CMainDialog::IOReadScene()
@ -4726,6 +4777,27 @@ void CMainDialog::UpdateSetupButtons()
{
pc->SetState(STATE_CHECK, m_bBlood);
}
pc = static_cast<CCheck*>(pw->SearchControl(EVENT_INTERFACE_AUTOSAVE_ENABLE));
if ( pc != 0 )
{
pc->SetState(STATE_CHECK, m_bAutosave);
}
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_AUTOSAVE_INTERVAL));
if ( ps != 0 )
{
ps->SetState(STATE_ENABLE, m_bAutosave);
ps->SetVisibleValue(m_main->GetAutosaveInterval());
}
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_AUTOSAVE_SLOTS));
if ( ps != 0 )
{
ps->SetState(STATE_ENABLE, m_bAutosave);
ps->SetVisibleValue(m_main->GetAutosaveSlots());
}
pc = static_cast<CCheck*>(pw->SearchControl(EVENT_INTERFACE_SHADOW));
if ( pc != 0 )
@ -4891,6 +4963,20 @@ void CMainDialog::ChangeSetupButtons()
value = ps->GetVisibleValue();
m_sound->SetMusicVolume(static_cast<int>(value));
}
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_AUTOSAVE_INTERVAL));
if ( ps != 0 )
{
value = ps->GetVisibleValue();
m_main->SetAutosaveInterval(static_cast<int>(value));
}
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_AUTOSAVE_SLOTS));
if ( ps != 0 )
{
value = ps->GetVisibleValue();
m_main->SetAutosaveSlots(static_cast<int>(value));
}
}
@ -4913,6 +4999,9 @@ void CMainDialog::SetupMemorize()
GetProfile().SetIntProperty("Setup", "CameraInvertY", m_bCameraInvertY);
GetProfile().SetIntProperty("Setup", "InterfaceEffect", m_bEffect);
GetProfile().SetIntProperty("Setup", "Blood", m_bBlood);
GetProfile().SetIntProperty("Setup", "Autosave", m_bAutosave);
GetProfile().SetIntProperty("Setup", "AutosaveInterval", m_main->GetAutosaveInterval());
GetProfile().SetIntProperty("Setup", "AutosaveSlots", m_main->GetAutosaveSlots());
GetProfile().SetIntProperty("Setup", "GroundShadow", m_engine->GetShadow());
GetProfile().SetIntProperty("Setup", "GroundSpot", m_engine->GetGroundSpot());
GetProfile().SetIntProperty("Setup", "ObjectDirty", m_engine->GetDirty());
@ -5066,6 +5155,21 @@ void CMainDialog::SetupRecall()
m_bBlood = iValue;
}
if ( GetProfile().GetIntProperty("Setup", "Autosave", iValue) )
{
m_bAutosave = iValue;
}
if ( GetProfile().GetIntProperty("Setup", "AutosaveInterval", iValue) )
{
m_main->SetAutosaveInterval(iValue);
}
if ( GetProfile().GetIntProperty("Setup", "AutosaveSlots", iValue) )
{
m_main->SetAutosaveSlots(iValue);
}
if ( GetProfile().GetIntProperty("Setup", "GroundShadow", iValue) )
{
m_engine->SetShadow(iValue);

View File

@ -139,6 +139,8 @@ public:
void ShowSoluceUpdate();
std::string& GetUserLevelName(int id);
void MakeSaveScreenshot(const std::string& name);
protected:
void GlintMove();
@ -240,6 +242,7 @@ protected:
bool m_bCameraInvertY; // for CCamera
bool m_bEffect; // for CCamera
bool m_bBlood; // for CCamera
bool m_bAutosave;
Math::Point m_glintMouse;
float m_glintTime;

View File

@ -469,15 +469,18 @@ void CSlider::Draw()
if ( m_bHoriz )
{
sprintf(text, "%d", static_cast<int>(m_min+m_visibleValue*(m_max-m_min)));
h = m_engine->GetText()->GetHeight(m_fontType, m_fontSize);
pos.x = m_pos.x+m_dim.x+(10.0f/640.0f);
pos.y = m_pos.y+(m_dim.y-h)/2.0f;
m_engine->GetText()->DrawText(text, m_fontType, m_fontSize, pos, m_dim.x, Gfx::TEXT_ALIGN_LEFT, 0);
if ( m_state & STATE_ENABLE )
{
sprintf(text, "%d", static_cast<int>(m_min+m_visibleValue*(m_max-m_min)));
h = m_engine->GetText()->GetHeight(m_fontType, m_fontSize);
pos.x = m_pos.x+m_dim.x+(10.0f/640.0f);
pos.y = m_pos.y+(m_dim.y-h)/2.0f;
m_engine->GetText()->DrawText(text, m_fontType, m_fontSize, pos, m_dim.x, Gfx::TEXT_ALIGN_LEFT, 0);
}
}
else
{
if ( m_state & STATE_VALUE )
if ( m_state & STATE_VALUE && m_state & STATE_ENABLE )
{
pos.x = m_pos.x+m_dim.x+4.0f/640.0f;
h = m_dim.y-m_marginButton*2.0f;