mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-15 22:19:54 +00:00
3bdd989a50
When multiple quicksaves occurred in quick succession, the numeric order of the saves could not be guaranteed. To prevent players from getting confused as to why their saves appear out of order, don't number them.
666 lines
24 KiB
C++
666 lines
24 KiB
C++
#include "statemanagerimp.hpp"
|
|
|
|
#include <components/esm/esmwriter.hpp>
|
|
#include <components/esm/esmreader.hpp>
|
|
#include <components/esm/cellid.hpp>
|
|
#include <components/esm/loadcell.hpp>
|
|
|
|
#include <components/loadinglistener/loadinglistener.hpp>
|
|
|
|
#include <components/settings/settings.hpp>
|
|
|
|
#include <osg/Image>
|
|
|
|
#include <osgDB/Registry>
|
|
|
|
#include <boost/filesystem/fstream.hpp>
|
|
#include <boost/filesystem/operations.hpp>
|
|
|
|
#include "../mwbase/environment.hpp"
|
|
#include "../mwbase/world.hpp"
|
|
#include "../mwbase/journal.hpp"
|
|
#include "../mwbase/dialoguemanager.hpp"
|
|
#include "../mwbase/windowmanager.hpp"
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
|
#include "../mwbase/scriptmanager.hpp"
|
|
#include "../mwbase/soundmanager.hpp"
|
|
#include "../mwbase/inputmanager.hpp"
|
|
|
|
#include "../mwworld/player.hpp"
|
|
#include "../mwworld/class.hpp"
|
|
#include "../mwworld/cellstore.hpp"
|
|
#include "../mwworld/esmstore.hpp"
|
|
#include "../mwworld/inventorystore.hpp"
|
|
|
|
#include "../mwmechanics/npcstats.hpp"
|
|
#include "../mwmechanics/actorutil.hpp"
|
|
|
|
#include "../mwscript/globalscripts.hpp"
|
|
|
|
#include "quicksavemanager.hpp"
|
|
|
|
void MWState::StateManager::cleanup (bool force)
|
|
{
|
|
if (mState!=State_NoGame || force)
|
|
{
|
|
MWBase::Environment::get().getSoundManager()->clear();
|
|
MWBase::Environment::get().getDialogueManager()->clear();
|
|
MWBase::Environment::get().getJournal()->clear();
|
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear();
|
|
MWBase::Environment::get().getWorld()->clear();
|
|
MWBase::Environment::get().getWindowManager()->clear();
|
|
MWBase::Environment::get().getInputManager()->clear();
|
|
MWBase::Environment::get().getMechanicsManager()->clear();
|
|
|
|
mState = State_NoGame;
|
|
mCharacterManager.setCurrentCharacter(NULL);
|
|
mTimePlayed = 0;
|
|
|
|
MWMechanics::CreatureStats::cleanup();
|
|
}
|
|
}
|
|
|
|
std::map<int, int> MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader)
|
|
const
|
|
{
|
|
const std::vector<std::string>& current =
|
|
MWBase::Environment::get().getWorld()->getContentFiles();
|
|
|
|
const std::vector<ESM::Header::MasterData>& prev = reader.getGameFiles();
|
|
|
|
std::map<int, int> map;
|
|
|
|
for (int iPrev = 0; iPrev<static_cast<int> (prev.size()); ++iPrev)
|
|
{
|
|
std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name);
|
|
|
|
for (int iCurrent = 0; iCurrent<static_cast<int> (current.size()); ++iCurrent)
|
|
if (id==Misc::StringUtils::lowerCase (current[iCurrent]))
|
|
{
|
|
map.insert (std::make_pair (iPrev, iCurrent));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return map;
|
|
}
|
|
|
|
MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game)
|
|
: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0)
|
|
{
|
|
|
|
}
|
|
|
|
void MWState::StateManager::requestQuit()
|
|
{
|
|
mQuitRequest = true;
|
|
}
|
|
|
|
bool MWState::StateManager::hasQuitRequest() const
|
|
{
|
|
return mQuitRequest;
|
|
}
|
|
|
|
void MWState::StateManager::askLoadRecent()
|
|
{
|
|
if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu)
|
|
return;
|
|
|
|
if( !mAskLoadRecent )
|
|
{
|
|
const MWState::Character* character = getCurrentCharacter();
|
|
if(!character || character->begin() == character->end())//no saves
|
|
{
|
|
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
|
}
|
|
else
|
|
{
|
|
MWState::Slot lastSave = *character->begin();
|
|
std::vector<std::string> buttons;
|
|
buttons.push_back("#{sYes}");
|
|
buttons.push_back("#{sNo}");
|
|
std::string tag("%s");
|
|
std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag);
|
|
size_t pos = message.find(tag);
|
|
message.replace(pos, tag.length(), lastSave.mProfile.mDescription);
|
|
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons);
|
|
mAskLoadRecent = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
MWState::StateManager::State MWState::StateManager::getState() const
|
|
{
|
|
return mState;
|
|
}
|
|
|
|
void MWState::StateManager::newGame (bool bypass)
|
|
{
|
|
cleanup();
|
|
|
|
if (!bypass)
|
|
MWBase::Environment::get().getWindowManager()->setNewGame (true);
|
|
|
|
try
|
|
{
|
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
|
|
|
|
MWBase::Environment::get().getWorld()->startNewGame (bypass);
|
|
|
|
mState = State_Running;
|
|
|
|
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0);
|
|
MWBase::Environment::get().getWindowManager()->fadeScreenIn(1);
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
std::stringstream error;
|
|
error << "Failed to start new game: " << e.what();
|
|
|
|
std::cerr << error.str() << std::endl;
|
|
cleanup (true);
|
|
|
|
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
|
|
|
std::vector<std::string> buttons;
|
|
buttons.push_back("#{sOk}");
|
|
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons);
|
|
}
|
|
}
|
|
|
|
void MWState::StateManager::endGame()
|
|
{
|
|
mState = State_Ended;
|
|
}
|
|
|
|
void MWState::StateManager::saveGame (const std::string& description, const Slot *slot)
|
|
{
|
|
MWState::Character* character = getCurrentCharacter();
|
|
|
|
try
|
|
{
|
|
if (!character)
|
|
{
|
|
MWWorld::ConstPtr player = MWMechanics::getPlayer();
|
|
std::string name = player.get<ESM::NPC>()->mBase->mName;
|
|
|
|
character = mCharacterManager.createCharacter(name);
|
|
mCharacterManager.setCurrentCharacter(character);
|
|
}
|
|
|
|
ESM::SavedGame profile;
|
|
|
|
MWBase::World& world = *MWBase::Environment::get().getWorld();
|
|
|
|
MWWorld::Ptr player = world.getPlayerPtr();
|
|
|
|
profile.mContentFiles = world.getContentFiles();
|
|
|
|
profile.mPlayerName = player.get<ESM::NPC>()->mBase->mName;
|
|
profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel();
|
|
|
|
std::string classId = player.get<ESM::NPC>()->mBase->mClass;
|
|
if (world.getStore().get<ESM::Class>().isDynamic(classId))
|
|
profile.mPlayerClassName = world.getStore().get<ESM::Class>().find(classId)->mName;
|
|
else
|
|
profile.mPlayerClassId = classId;
|
|
|
|
profile.mPlayerCell = world.getCellName();
|
|
|
|
profile.mInGameTime.mGameHour = world.getTimeStamp().getHour();
|
|
profile.mInGameTime.mDay = world.getDay();
|
|
profile.mInGameTime.mMonth = world.getMonth();
|
|
profile.mInGameTime.mYear = world.getYear();
|
|
profile.mTimePlayed = mTimePlayed;
|
|
profile.mDescription = description;
|
|
|
|
writeScreenshot(profile.mScreenshot);
|
|
|
|
if (!slot)
|
|
slot = character->createSlot (profile);
|
|
else
|
|
slot = character->updateSlot (slot, profile);
|
|
|
|
// Make sure the animation state held by references is up to date before saving the game.
|
|
MWBase::Environment::get().getMechanicsManager()->persistAnimationStates();
|
|
|
|
// Write to a memory stream first. If there is an exception during the save process, we don't want to trash the
|
|
// existing save file we are overwriting.
|
|
std::stringstream stream;
|
|
|
|
ESM::ESMWriter writer;
|
|
|
|
const std::vector<std::string>& current =
|
|
MWBase::Environment::get().getWorld()->getContentFiles();
|
|
|
|
for (std::vector<std::string>::const_iterator iter (current.begin()); iter!=current.end();
|
|
++iter)
|
|
writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0
|
|
|
|
writer.setFormat (ESM::SavedGame::sCurrentFormat);
|
|
|
|
// all unused
|
|
writer.setVersion(0);
|
|
writer.setType(0);
|
|
writer.setAuthor("");
|
|
writer.setDescription("");
|
|
|
|
int recordCount = 1 // saved game header
|
|
+MWBase::Environment::get().getJournal()->countSavedGameRecords()
|
|
+MWBase::Environment::get().getWorld()->countSavedGameRecords()
|
|
+MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
|
|
+MWBase::Environment::get().getDialogueManager()->countSavedGameRecords()
|
|
+MWBase::Environment::get().getWindowManager()->countSavedGameRecords()
|
|
+MWBase::Environment::get().getMechanicsManager()->countSavedGameRecords()
|
|
+MWBase::Environment::get().getInputManager()->countSavedGameRecords();
|
|
writer.setRecordCount (recordCount);
|
|
|
|
writer.save (stream);
|
|
|
|
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
|
// Using only Cells for progress information, since they typically have the largest records by far
|
|
listener.setProgressRange(MWBase::Environment::get().getWorld()->countSavedGameCells());
|
|
listener.setLabel("#{sNotifyMessage4}", true);
|
|
|
|
Loading::ScopedLoad load(&listener);
|
|
|
|
writer.startRecord (ESM::REC_SAVE);
|
|
slot->mProfile.save (writer);
|
|
writer.endRecord (ESM::REC_SAVE);
|
|
|
|
MWBase::Environment::get().getJournal()->write (writer, listener);
|
|
MWBase::Environment::get().getDialogueManager()->write (writer, listener);
|
|
MWBase::Environment::get().getWorld()->write (writer, listener);
|
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer, listener);
|
|
MWBase::Environment::get().getWindowManager()->write(writer, listener);
|
|
MWBase::Environment::get().getMechanicsManager()->write(writer, listener);
|
|
MWBase::Environment::get().getInputManager()->write(writer, listener);
|
|
|
|
// Ensure we have written the number of records that was estimated
|
|
if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record
|
|
std::cerr << "Warning: number of written savegame records does not match. Estimated: " << recordCount+1 << ", written: " << writer.getRecordCount() << std::endl;
|
|
|
|
writer.close();
|
|
|
|
if (stream.fail())
|
|
throw std::runtime_error("Write operation failed (memory stream)");
|
|
|
|
// All good, write to file
|
|
boost::filesystem::ofstream filestream (slot->mPath, std::ios::binary);
|
|
filestream << stream.rdbuf();
|
|
|
|
if (filestream.fail())
|
|
throw std::runtime_error("Write operation failed (file stream)");
|
|
|
|
Settings::Manager::setString ("character", "Saves",
|
|
slot->mPath.parent_path().filename().string());
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
std::stringstream error;
|
|
error << "Failed to save game: " << e.what();
|
|
|
|
std::cerr << error.str() << std::endl;
|
|
|
|
std::vector<std::string> buttons;
|
|
buttons.push_back("#{sOk}");
|
|
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons);
|
|
|
|
// If no file was written, clean up the slot
|
|
if (character && slot && !boost::filesystem::exists(slot->mPath))
|
|
{
|
|
character->deleteSlot(slot);
|
|
character->cleanup();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MWState::StateManager::quickSave (std::string name)
|
|
{
|
|
if (!(mState==State_Running &&
|
|
MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1 // char gen
|
|
&& MWBase::Environment::get().getWindowManager()->isSavingAllowed()))
|
|
{
|
|
//You can not save your game right now
|
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sSaveGameDenied}");
|
|
return;
|
|
}
|
|
|
|
int maxSaves = Settings::Manager::getInt("max quicksaves", "Saves");
|
|
if(maxSaves < 1)
|
|
maxSaves = 1;
|
|
|
|
Character* currentCharacter = getCurrentCharacter(); //Get current character
|
|
QuickSaveManager saveFinder = QuickSaveManager(name, maxSaves);
|
|
|
|
if (currentCharacter)
|
|
{
|
|
for (Character::SlotIterator it = currentCharacter->begin(); it != currentCharacter->end(); ++it)
|
|
{
|
|
//Visiting slots allows the quicksave finder to find the oldest quicksave
|
|
saveFinder.visitSave(&*it);
|
|
}
|
|
}
|
|
|
|
//Once all the saves have been visited, the save finder can tell us which
|
|
//one to replace (or create)
|
|
saveGame(name, saveFinder.getNextQuickSaveSlot());
|
|
}
|
|
|
|
void MWState::StateManager::loadGame(const std::string& filepath)
|
|
{
|
|
for (CharacterIterator it = mCharacterManager.begin(); it != mCharacterManager.end(); ++it)
|
|
{
|
|
const MWState::Character& character = *it;
|
|
for (MWState::Character::SlotIterator slotIt = character.begin(); slotIt != character.end(); ++slotIt)
|
|
{
|
|
const MWState::Slot& slot = *slotIt;
|
|
if (slot.mPath == boost::filesystem::path(filepath))
|
|
{
|
|
loadGame(&character, slot.mPath.string());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
MWState::Character* character = getCurrentCharacter();
|
|
loadGame(character, filepath);
|
|
}
|
|
|
|
void MWState::StateManager::loadGame (const Character *character, const std::string& filepath)
|
|
{
|
|
try
|
|
{
|
|
cleanup();
|
|
|
|
ESM::ESMReader reader;
|
|
reader.open (filepath);
|
|
|
|
if (reader.getFormat() > ESM::SavedGame::sCurrentFormat)
|
|
throw std::runtime_error("This save file was created using a newer version of OpenMW and is thus not supported. Please upgrade to the newest OpenMW version to load this file.");
|
|
|
|
std::map<int, int> contentFileMap = buildContentFileIndexMap (reader);
|
|
|
|
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
|
|
|
listener.setProgressRange(100);
|
|
listener.setLabel("#{sLoadingMessage14}");
|
|
|
|
Loading::ScopedLoad load(&listener);
|
|
|
|
bool firstPersonCam = false;
|
|
|
|
size_t total = reader.getFileSize();
|
|
int currentPercent = 0;
|
|
while (reader.hasMoreRecs())
|
|
{
|
|
ESM::NAME n = reader.getRecName();
|
|
reader.getRecHeader();
|
|
|
|
switch (n.intval)
|
|
{
|
|
case ESM::REC_SAVE:
|
|
{
|
|
ESM::SavedGame profile;
|
|
profile.load(reader);
|
|
if (!verifyProfile(profile))
|
|
{
|
|
cleanup (true);
|
|
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
|
return;
|
|
}
|
|
mTimePlayed = profile.mTimePlayed;
|
|
}
|
|
break;
|
|
|
|
case ESM::REC_JOUR:
|
|
case ESM::REC_JOUR_LEGACY:
|
|
case ESM::REC_QUES:
|
|
|
|
MWBase::Environment::get().getJournal()->readRecord (reader, n.intval);
|
|
break;
|
|
|
|
case ESM::REC_DIAS:
|
|
|
|
MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.intval);
|
|
break;
|
|
|
|
case ESM::REC_ALCH:
|
|
case ESM::REC_ARMO:
|
|
case ESM::REC_BOOK:
|
|
case ESM::REC_CLAS:
|
|
case ESM::REC_CLOT:
|
|
case ESM::REC_ENCH:
|
|
case ESM::REC_NPC_:
|
|
case ESM::REC_SPEL:
|
|
case ESM::REC_WEAP:
|
|
case ESM::REC_GLOB:
|
|
case ESM::REC_PLAY:
|
|
case ESM::REC_CSTA:
|
|
case ESM::REC_WTHR:
|
|
case ESM::REC_DYNA:
|
|
case ESM::REC_ACTC:
|
|
case ESM::REC_PROJ:
|
|
case ESM::REC_MPRJ:
|
|
case ESM::REC_ENAB:
|
|
case ESM::REC_LEVC:
|
|
case ESM::REC_LEVI:
|
|
MWBase::Environment::get().getWorld()->readRecord(reader, n.intval, contentFileMap);
|
|
break;
|
|
|
|
case ESM::REC_CAM_:
|
|
reader.getHNT(firstPersonCam, "FIRS");
|
|
break;
|
|
|
|
case ESM::REC_GSCR:
|
|
|
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.intval);
|
|
break;
|
|
|
|
case ESM::REC_GMAP:
|
|
case ESM::REC_KEYS:
|
|
case ESM::REC_ASPL:
|
|
case ESM::REC_MARK:
|
|
|
|
MWBase::Environment::get().getWindowManager()->readRecord(reader, n.intval);
|
|
break;
|
|
|
|
case ESM::REC_DCOU:
|
|
case ESM::REC_STLN:
|
|
|
|
MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.intval);
|
|
break;
|
|
|
|
case ESM::REC_INPU:
|
|
MWBase::Environment::get().getInputManager()->readRecord(reader, n.intval);
|
|
break;
|
|
|
|
default:
|
|
|
|
// ignore invalid records
|
|
std::cerr << "Warning: Ignoring unknown record: " << n.toString() << std::endl;
|
|
reader.skipRecord();
|
|
}
|
|
int progressPercent = static_cast<int>(float(reader.getFileOffset())/total*100);
|
|
if (progressPercent > currentPercent)
|
|
{
|
|
listener.increaseProgress(progressPercent-currentPercent);
|
|
currentPercent = progressPercent;
|
|
}
|
|
}
|
|
|
|
mCharacterManager.setCurrentCharacter(character);
|
|
|
|
mState = State_Running;
|
|
|
|
if (character)
|
|
Settings::Manager::setString ("character", "Saves",
|
|
character->getPath().filename().string());
|
|
|
|
MWBase::Environment::get().getWindowManager()->setNewGame(false);
|
|
MWBase::Environment::get().getWorld()->setupPlayer();
|
|
MWBase::Environment::get().getWorld()->renderPlayer();
|
|
MWBase::Environment::get().getWindowManager()->updatePlayer();
|
|
MWBase::Environment::get().getMechanicsManager()->playerLoaded();
|
|
|
|
if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson())
|
|
MWBase::Environment::get().getWorld()->togglePOV();
|
|
|
|
MWWorld::ConstPtr ptr = MWMechanics::getPlayer();
|
|
|
|
if (ptr.isInCell())
|
|
{
|
|
const ESM::CellId& cellId = ptr.getCell()->getCell()->getCellId();
|
|
|
|
// Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again
|
|
MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false, false);
|
|
}
|
|
else
|
|
{
|
|
// Cell no longer exists (i.e. changed game files), choose a default cell
|
|
MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(0,0);
|
|
float x,y;
|
|
MWBase::Environment::get().getWorld()->indexToPosition(0,0,x,y,false);
|
|
ESM::Position pos;
|
|
pos.pos[0] = x;
|
|
pos.pos[1] = y;
|
|
pos.pos[2] = 0; // should be adjusted automatically (adjustPlayerPos=true)
|
|
pos.rot[0] = 0;
|
|
pos.rot[1] = 0;
|
|
pos.rot[2] = 0;
|
|
MWBase::Environment::get().getWorld()->changeToCell(cell->getCell()->getCellId(), pos, true, false);
|
|
}
|
|
|
|
// Vanilla MW will restart startup scripts when a save game is loaded. This is unintuitive,
|
|
// but some mods may be using it as a reload detector.
|
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
|
|
|
|
// Since we passed "changeEvent=false" to changeCell, we shouldn't have triggered the cell change flag.
|
|
// But make sure the flag is cleared anyway in case it was set from an earlier game.
|
|
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
std::stringstream error;
|
|
error << "Failed to load saved game: " << e.what();
|
|
|
|
std::cerr << error.str() << std::endl;
|
|
cleanup (true);
|
|
|
|
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
|
|
|
std::vector<std::string> buttons;
|
|
buttons.push_back("#{sOk}");
|
|
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons);
|
|
}
|
|
}
|
|
|
|
void MWState::StateManager::quickLoad()
|
|
{
|
|
if (Character* currentCharacter = getCurrentCharacter ())
|
|
{
|
|
if (currentCharacter->begin() == currentCharacter->end())
|
|
return;
|
|
loadGame (currentCharacter, currentCharacter->begin()->mPath.string()); //Get newest save
|
|
}
|
|
}
|
|
|
|
void MWState::StateManager::deleteGame(const MWState::Character *character, const MWState::Slot *slot)
|
|
{
|
|
mCharacterManager.deleteSlot(character, slot);
|
|
}
|
|
|
|
MWState::Character *MWState::StateManager::getCurrentCharacter ()
|
|
{
|
|
return mCharacterManager.getCurrentCharacter();
|
|
}
|
|
|
|
MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin()
|
|
{
|
|
return mCharacterManager.begin();
|
|
}
|
|
|
|
MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd()
|
|
{
|
|
return mCharacterManager.end();
|
|
}
|
|
|
|
void MWState::StateManager::update (float duration)
|
|
{
|
|
mTimePlayed += duration;
|
|
|
|
// Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update.
|
|
if (mAskLoadRecent)
|
|
{
|
|
int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton();
|
|
MWState::Character *curCharacter = getCurrentCharacter();
|
|
if(iButton==0 && curCharacter)
|
|
{
|
|
mAskLoadRecent = false;
|
|
//Load last saved game for current character
|
|
|
|
MWState::Slot lastSave = *curCharacter->begin();
|
|
loadGame(curCharacter, lastSave.mPath.string());
|
|
}
|
|
else if(iButton==1)
|
|
{
|
|
mAskLoadRecent = false;
|
|
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const
|
|
{
|
|
const std::vector<std::string>& selectedContentFiles = MWBase::Environment::get().getWorld()->getContentFiles();
|
|
bool notFound = false;
|
|
for (std::vector<std::string>::const_iterator it = profile.mContentFiles.begin();
|
|
it != profile.mContentFiles.end(); ++it)
|
|
{
|
|
if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), *it)
|
|
== selectedContentFiles.end())
|
|
{
|
|
std::cerr << "Warning: Savegame dependency " << *it << " is missing." << std::endl;
|
|
notFound = true;
|
|
}
|
|
}
|
|
if (notFound)
|
|
{
|
|
std::vector<std::string> buttons;
|
|
buttons.push_back("#{sYes}");
|
|
buttons.push_back("#{sNo}");
|
|
MWBase::Environment::get().getWindowManager()->interactiveMessageBox("#{sMissingMastersMsg}", buttons, true);
|
|
int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton();
|
|
if (selectedButton == 1 || selectedButton == -1)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MWState::StateManager::writeScreenshot(std::vector<char> &imageData) const
|
|
{
|
|
int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing
|
|
|
|
osg::ref_ptr<osg::Image> screenshot (new osg::Image);
|
|
|
|
MWBase::Environment::get().getWorld()->screenshot(screenshot.get(), screenshotW, screenshotH);
|
|
|
|
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg");
|
|
if (!readerwriter)
|
|
{
|
|
std::cerr << "Error: Unable to write screenshot, can't find a jpg ReaderWriter" << std::endl;
|
|
return;
|
|
}
|
|
|
|
std::ostringstream ostream;
|
|
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*screenshot, ostream);
|
|
if (!result.success())
|
|
{
|
|
std::cerr << "Error: Unable to write screenshot: " << result.message() << " code " << result.status() << std::endl;
|
|
return;
|
|
}
|
|
|
|
std::string data = ostream.str();
|
|
imageData = std::vector<char>(data.begin(), data.end());
|
|
|
|
}
|