1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-29 18:32:36 +00:00
OpenMW/apps/openmw/mwstate/statemanagerimp.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

720 lines
26 KiB
C++
Raw Normal View History

2013-11-16 10:31:46 +01:00
#include "statemanagerimp.hpp"
2022-05-25 21:16:26 +02:00
#include <filesystem>
2018-08-14 23:05:43 +04:00
#include <components/debug/debuglog.hpp>
#include <components/esm3/esmreader.hpp>
#include <components/esm3/esmwriter.hpp>
#include <components/esm3/loadcell.hpp>
#include <components/esm3/loadclas.hpp>
#include <components/l10n/manager.hpp>
2015-07-09 19:22:04 +02:00
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/files/conversion.hpp>
#include <components/settings/settings.hpp>
2015-06-03 16:40:16 +02:00
#include <osg/Image>
#include <osgDB/Registry>
2014-01-24 17:49:16 +01:00
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/journal.hpp"
#include "../mwbase/luamanager.hpp"
2013-12-07 13:17:28 +01:00
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
2014-02-23 20:11:05 +01:00
#include "../mwworld/cellstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/globals.hpp"
#include "../mwworld/scene.hpp"
2022-12-06 00:11:19 +01:00
#include "../mwworld/worldmodel.hpp"
2015-08-21 21:12:39 +12:00
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwscript/globalscripts.hpp"
#include "quicksavemanager.hpp"
void MWState::StateManager::cleanup(bool force)
2013-11-28 11:22:34 +01:00
{
if (mState != State_NoGame || force)
2013-11-28 11:22:34 +01:00
{
MWBase::Environment::get().getSoundManager()->clear();
2013-11-28 11:22:34 +01:00
MWBase::Environment::get().getDialogueManager()->clear();
MWBase::Environment::get().getJournal()->clear();
MWBase::Environment::get().getScriptManager()->clear();
MWBase::Environment::get().getWindowManager()->clear();
2020-11-18 17:28:09 +01:00
MWBase::Environment::get().getWorld()->clear();
MWBase::Environment::get().getInputManager()->clear();
MWBase::Environment::get().getMechanicsManager()->clear();
2013-11-28 11:22:34 +01:00
mState = State_NoGame;
2018-10-09 10:21:12 +04:00
mCharacterManager.setCurrentCharacter(nullptr);
2013-11-28 11:22:34 +01:00
mTimePlayed = 0;
2014-04-29 19:56:33 +02:00
MWMechanics::CreatureStats::cleanup();
2013-11-28 11:22:34 +01:00
}
MWBase::Environment::get().getLuaManager()->clear();
2013-11-28 11:22:34 +01:00
}
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 std::filesystem::path& saves, const std::vector<std::string>& contentFiles)
: mQuitRequest(false)
, mAskLoadRecent(false)
, mState(State_NoGame)
, mCharacterManager(saves, contentFiles)
, mTimePlayed(0)
2013-11-16 10:31:46 +01:00
{
}
void MWState::StateManager::requestQuit()
{
mQuitRequest = true;
}
bool MWState::StateManager::hasQuitRequest() const
{
return mQuitRequest;
}
2013-12-19 22:08:34 +02:00
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
2013-12-19 22:08:34 +02:00
{
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu);
}
else
{
MWState::Slot lastSave = *character->begin();
2013-12-19 22:08:34 +02:00
std::vector<std::string> buttons;
buttons.emplace_back("#{Interface:Yes}");
buttons.emplace_back("#{Interface:No}");
std::string message
= MWBase::Environment::get().getL10nManager()->getMessage("OMWEngine", "AskLoadLastSave");
std::string_view tag = "%s";
2013-12-20 14:04:59 +02:00
size_t pos = message.find(tag);
message.replace(pos, tag.length(), lastSave.mProfile.mDescription);
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons);
2013-12-19 22:08:34 +02:00
mAskLoadRecent = true;
}
}
}
MWState::StateManager::State MWState::StateManager::getState() const
{
return mState;
}
void MWState::StateManager::newGame(bool bypass)
{
2013-11-28 11:22:34 +01:00
cleanup();
if (!bypass)
MWBase::Environment::get().getWindowManager()->setNewGame(true);
try
{
2020-05-09 20:17:49 +03:00
Log(Debug::Info) << "Starting a new game";
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
MWBase::Environment::get().getLuaManager()->newGameStarted();
MWBase::Environment::get().getWorld()->startNewGame(bypass);
mState = State_Running;
2017-07-23 11:04:58 +04:00
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();
2018-08-14 23:05:43 +04:00
Log(Debug::Error) << error.str();
cleanup(true);
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu);
std::vector<std::string> buttons;
buttons.emplace_back("#{Interface:OK}");
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons);
}
}
2013-11-18 15:38:08 +01:00
void MWState::StateManager::endGame()
{
mState = State_Ended;
}
2018-07-26 19:54:08 +03:00
void MWState::StateManager::resumeGame()
{
mState = State_Running;
}
void MWState::StateManager::saveGame(const std::string& description, const Slot* slot)
{
MWState::Character* character = getCurrentCharacter();
try
{
2022-02-13 20:13:14 +01:00
const auto start = std::chrono::steady_clock::now();
MWBase::Environment::get().getWindowManager()->asyncPrepareSaveMap();
if (!character)
{
MWWorld::ConstPtr player = MWMechanics::getPlayer();
const 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();
2014-09-13 02:58:01 +02:00
profile.mPlayerName = player.get<ESM::NPC>()->mBase->mName;
profile.mPlayerLevel = player.getClass().getNpcStats(player).getLevel();
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID Slowly going through all the changes to make, still hundreds of errors a lot of functions/structures use std::string or stringview to designate an ID. So it takes time Continues slowly replacing ids. There are technically more and more compilation errors I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type Continue moving forward, changes to the stores slowly moving along Starting to see the fruit of those changes. still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type. More replacements. Things are starting to get easier I can see more and more often the issue is that the function is awaiting a RefId, but is given a string there is less need to go down functions and to fix a long list of them. Still moving forward, and for the first time error count is going down! Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably Cells are back to using string for the name, haven't fixed everything yet. Many other changes Under the bar of 400 compilation errors. more good progress <100 compile errors! More progress Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string some more progress on other fronts Mostly game settings clean one error opened a lot of other errors. Down to 18, but more will prbably appear only link errors left?? Fixed link errors OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
const ESM::RefId& 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.mPlayerCellName = world.getCellName();
profile.mInGameTime = world.getEpochTimeStamp();
profile.mTimePlayed = mTimePlayed;
profile.mDescription = description;
2021-07-05 13:11:54 +02:00
Log(Debug::Info) << "Making a screenshot for saved game '" << description << "'";
2015-06-03 16:40:16 +02:00
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();
Log(Debug::Info) << "Writing saved game '" << description << "' for character '" << profile.mPlayerName << "'";
2020-05-09 20:17:49 +03:00
// 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;
for (const std::string& contentFile : MWBase::Environment::get().getWorld()->getContentFiles())
writer.addMaster(contentFile, 0); // not using the size information anyway -> use value of 0
writer.setFormatVersion(ESM::CurrentSaveGameFormatVersion);
// 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().getLuaManager()->countSavedGameRecords()
+ MWBase::Environment::get().getWorld()->countSavedGameRecords()
+ MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
+ MWBase::Environment::get().getDialogueManager()->countSavedGameRecords()
2016-10-20 02:12:01 +02:00
+ MWBase::Environment::get().getMechanicsManager()->countSavedGameRecords()
+ MWBase::Environment::get().getInputManager()->countSavedGameRecords()
+ MWBase::Environment::get().getWindowManager()->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("#{OMWEngine:SavingInProgress}", true);
2013-12-03 14:28:46 +01:00
Loading::ScopedLoad load(&listener);
2013-12-03 14:28:46 +01:00
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);
// LuaManager::write should be called before World::write because world also saves
// local scripts that depend on LuaManager.
MWBase::Environment::get().getLuaManager()->write(writer, listener);
MWBase::Environment::get().getWorld()->write(writer, listener);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write(writer, listener);
MWBase::Environment::get().getMechanicsManager()->write(writer, listener);
2016-10-20 02:12:01 +02:00
MWBase::Environment::get().getInputManager()->write(writer, listener);
MWBase::Environment::get().getWindowManager()->write(writer, listener);
// Ensure we have written the number of records that was estimated
if (writer.getRecordCount() != recordCount + 1) // 1 extra for TES3 record
2018-08-14 23:05:43 +04:00
Log(Debug::Warning) << "Warning: number of written savegame records does not match. Estimated: "
<< recordCount + 1 << ", written: " << writer.getRecordCount();
writer.close();
2013-11-28 11:22:34 +01:00
if (stream.fail())
throw std::runtime_error("Write operation failed (memory stream)");
// All good, write to file
std::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", Files::pathToUnicodeString(slot->mPath.parent_path().filename()));
2022-02-13 20:13:14 +01:00
const auto finish = std::chrono::steady_clock::now();
Log(Debug::Info) << '\'' << description << "' is saved in "
<< std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(finish - start).count()
<< "ms";
}
catch (const std::exception& e)
{
std::stringstream error;
error << "Failed to save game: " << e.what();
2013-11-28 11:22:34 +01:00
2018-08-14 23:05:43 +04:00
Log(Debug::Error) << error.str();
std::vector<std::string> buttons;
buttons.emplace_back("#{Interface:OK}");
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons);
// If no file was written, clean up the slot
if (character && slot && !std::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(MWWorld::Globals::sCharGenState) == -1 // char gen
&& MWBase::Environment::get().getWindowManager()->isSavingAllowed()))
{
// You can not save your game right now
MWBase::Environment::get().getWindowManager()->messageBox("#{OMWEngine:SaveGameDenied}");
return;
}
2014-04-24 09:54:47 +02:00
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 (auto& save : *currentCharacter)
{
// Visiting slots allows the quicksave finder to find the oldest quicksave
saveFinder.visitSave(&save);
}
}
// 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::filesystem::path& filepath)
{
for (const auto& character : mCharacterManager)
{
for (const auto& slot : character)
{
if (slot.mPath == filepath)
{
loadGame(&character, slot.mPath);
return;
}
}
}
MWState::Character* character = getCurrentCharacter();
loadGame(character, filepath);
}
struct VersionMismatchError : public std::runtime_error
{
using std::runtime_error::runtime_error;
};
void MWState::StateManager::loadGame(const Character* character, const std::filesystem::path& filepath)
2013-11-21 12:24:24 +01:00
{
try
2013-12-03 14:28:46 +01:00
{
cleanup();
2013-12-07 13:17:28 +01:00
Log(Debug::Info) << "Reading save file " << filepath.filename();
2020-05-09 20:17:49 +03:00
ESM::ESMReader reader;
reader.open(filepath);
if (reader.getFormatVersion() > ESM::CurrentSaveGameFormatVersion)
throw VersionMismatchError(
"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);
MWBase::Environment::get().getLuaManager()->setContentFileMapping(contentFileMap);
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener.setProgressRange(100);
listener.setLabel("#{OMWEngine:LoadingInProgress}");
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.toInt())
{
case ESM::REC_SAVE:
2022-09-22 21:26:05 +03:00
{
ESM::SavedGame profile;
profile.load(reader);
if (!verifyProfile(profile))
{
cleanup(true);
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu);
return;
}
mTimePlayed = profile.mTimePlayed;
Log(Debug::Info) << "Loading saved game '" << profile.mDescription << "' for character '"
<< profile.mPlayerName << "'";
2022-09-22 21:26:05 +03:00
}
break;
case ESM::REC_JOUR:
case ESM::REC_JOUR_LEGACY:
case ESM::REC_QUES:
MWBase::Environment::get().getJournal()->readRecord(reader, n.toInt());
break;
case ESM::REC_DIAS:
MWBase::Environment::get().getDialogueManager()->readRecord(reader, n.toInt());
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:
2014-05-14 09:47:49 +02:00
case ESM::REC_ACTC:
2014-05-17 05:21:17 +02:00
case ESM::REC_PROJ:
case ESM::REC_MPRJ:
case ESM::REC_ENAB:
case ESM::REC_LEVC:
case ESM::REC_LEVI:
case ESM::REC_CREA:
case ESM::REC_CONT:
case ESM::REC_RAND:
MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt(), contentFileMap);
break;
case ESM::REC_CAM_:
reader.getHNT(firstPersonCam, "FIRS");
break;
case ESM::REC_GSCR:
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord(
reader, n.toInt(), contentFileMap);
break;
2014-01-25 18:20:17 +01:00
case ESM::REC_GMAP:
case ESM::REC_KEYS:
2014-05-12 21:04:02 +02:00
case ESM::REC_ASPL:
case ESM::REC_MARK:
2014-05-12 21:04:02 +02:00
MWBase::Environment::get().getWindowManager()->readRecord(reader, n.toInt());
2014-01-25 18:20:17 +01:00
break;
case ESM::REC_DCOU:
case ESM::REC_STLN:
MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.toInt());
break;
2016-10-20 02:12:01 +02:00
case ESM::REC_INPU:
MWBase::Environment::get().getInputManager()->readRecord(reader, n.toInt());
2016-10-20 02:12:01 +02:00
break;
case ESM::REC_LUAM:
MWBase::Environment::get().getLuaManager()->readRecord(reader, n.toInt());
break;
default:
// ignore invalid records
Log(Debug::Warning) << "Warning: Ignoring unknown record: " << n.toStringView();
reader.skipRecord();
}
int progressPercent = static_cast<int>(float(reader.getFileOffset()) / total * 100);
if (progressPercent > currentPercent)
{
listener.increaseProgress(progressPercent - currentPercent);
currentPercent = progressPercent;
}
2013-12-03 14:28:46 +01:00
}
2013-11-21 12:24:24 +01:00
mCharacterManager.setCurrentCharacter(character);
mState = State_Running;
if (character)
Settings::Manager::setString(
"character", "Saves", Files::pathToUnicodeString(character->getPath().filename()));
MWBase::Environment::get().getWindowManager()->setNewGame(false);
MWBase::Environment::get().getWorld()->saveLoaded();
MWBase::Environment::get().getWorld()->setupPlayer();
MWBase::Environment::get().getWorld()->renderPlayer();
MWBase::Environment::get().getWindowManager()->updatePlayer();
MWBase::Environment::get().getMechanicsManager()->playerLoaded();
2013-12-07 13:17:28 +01:00
if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson())
MWBase::Environment::get().getWorld()->togglePOV();
MWWorld::ConstPtr ptr = MWMechanics::getPlayer();
if (ptr.isInCell())
{
const ESM::RefId cellId = ptr.getCell()->getCell()->getId();
// 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
2020-05-09 20:17:49 +03:00
Log(Debug::Warning) << "Warning: Player character's cell no longer exists, changing to the default cell";
MWWorld::CellStore* cell = MWBase::Environment::get().getWorldModel()->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()->getId(), pos, true, false);
}
2014-05-17 05:21:17 +02:00
MWBase::Environment::get().getWorld()->updateProjectilesCasters();
// 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().getWorldScene()->markCellAsUnchanged();
MWBase::Environment::get().getLuaManager()->gameLoaded();
}
catch (const std::exception& e)
{
Log(Debug::Error) << "Failed to load saved game: " << e.what();
cleanup(true);
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu);
std::vector<std::string> buttons;
buttons.emplace_back("#{Interface:OK}");
std::string error;
if (typeid(e) == typeid(VersionMismatchError))
error = "#{OMWEngine:LoadingFailed}: #{OMWEngine:LoadingRequiresNewVersionError}";
else
error = "#{OMWEngine:LoadingFailed}: " + std::string(e.what());
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error, buttons);
}
2013-11-21 12:24:24 +01:00
}
void MWState::StateManager::quickLoad()
{
if (Character* currentCharacter = getCurrentCharacter())
{
if (currentCharacter->begin() == currentCharacter->end())
return;
loadGame(currentCharacter, currentCharacter->begin()->mPath); // 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);
}
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 (const std::string& contentFile : profile.mContentFiles)
{
if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), contentFile)
== selectedContentFiles.end())
{
Log(Debug::Warning) << "Warning: Saved game dependency " << contentFile << " is missing.";
notFound = true;
}
}
if (notFound)
{
std::vector<std::string> buttons;
buttons.emplace_back("#{Interface:Yes}");
buttons.emplace_back("#{Interface:No}");
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(
"#{OMWEngine:MissingContentFilesConfirmation}", buttons, true);
int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton();
if (selectedButton == 1 || selectedButton == -1)
return false;
}
return true;
}
2015-06-03 16:40:16 +02:00
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)
{
2018-08-14 23:05:43 +04:00
Log(Debug::Error) << "Error: Unable to write screenshot, can't find a jpg ReaderWriter";
2015-06-03 16:40:16 +02:00
return;
}
std::ostringstream ostream;
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*screenshot, ostream);
if (!result.success())
{
2018-08-14 23:05:43 +04:00
Log(Debug::Error) << "Error: Unable to write screenshot: " << result.message() << " code " << result.status();
2015-06-03 16:40:16 +02:00
return;
}
std::string data = ostream.str();
imageData = std::vector<char>(data.begin(), data.end());
}