forked from mirror/openmw-tes3mp
Merge branch 'savedgame'
Conflicts: apps/openmw/mwbase/mechanicsmanager.hpp apps/openmw/mwbase/soundmanager.hpp apps/openmw/mwgui/mapwindow.hpp apps/openmw/mwmechanics/actors.cpp apps/openmw/mwmechanics/mechanicsmanagerimp.hpp apps/openmw/mwsound/soundmanagerimp.hpp components/esm/loadcell.cppactorid
commit
1b5301eec0
@ -0,0 +1,81 @@
|
||||
#ifndef GAME_MWSTATE_STATEMANAGER_H
|
||||
#define GAME_MWSTATE_STATEMANAGER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace MWState
|
||||
{
|
||||
struct Slot;
|
||||
class Character;
|
||||
}
|
||||
|
||||
namespace MWBase
|
||||
{
|
||||
/// \brief Interface for game state manager (implemented in MWState)
|
||||
class StateManager
|
||||
{
|
||||
public:
|
||||
|
||||
enum State
|
||||
{
|
||||
State_NoGame,
|
||||
State_Ended,
|
||||
State_Running
|
||||
};
|
||||
|
||||
typedef std::vector<MWState::Character>::const_iterator CharacterIterator;
|
||||
|
||||
private:
|
||||
|
||||
StateManager (const StateManager&);
|
||||
///< not implemented
|
||||
|
||||
StateManager& operator= (const StateManager&);
|
||||
///< not implemented
|
||||
|
||||
public:
|
||||
|
||||
StateManager() {}
|
||||
|
||||
virtual ~StateManager() {}
|
||||
|
||||
virtual void requestQuit() = 0;
|
||||
|
||||
virtual bool hasQuitRequest() const = 0;
|
||||
|
||||
virtual void askLoadRecent() = 0;
|
||||
|
||||
virtual State getState() const = 0;
|
||||
|
||||
virtual void newGame (bool bypass = false) = 0;
|
||||
///< Start a new game.
|
||||
///
|
||||
/// \param bypass Skip new game mechanics.
|
||||
|
||||
virtual void endGame() = 0;
|
||||
|
||||
virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0;
|
||||
///< Write a saved game to \a slot or create a new slot if \a slot == 0.
|
||||
///
|
||||
/// \note Slot must belong to the current character.
|
||||
|
||||
virtual void loadGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
|
||||
///< Load a saved game file from \a slot.
|
||||
///
|
||||
/// \note \a slot must belong to \a character.
|
||||
|
||||
virtual MWState::Character *getCurrentCharacter (bool create = true) = 0;
|
||||
///< \param create Create a new character, if there is no current character.
|
||||
|
||||
virtual CharacterIterator characterBegin() = 0;
|
||||
///< Any call to SaveGame and getCurrentCharacter can invalidate the returned
|
||||
/// iterator.
|
||||
|
||||
virtual CharacterIterator characterEnd() = 0;
|
||||
|
||||
virtual void update (float duration) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,153 @@
|
||||
|
||||
#include "character.hpp"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/defs.hpp>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
bool MWState::operator< (const Slot& left, const Slot& right)
|
||||
{
|
||||
return left.mTimeStamp<right.mTimeStamp;
|
||||
}
|
||||
|
||||
|
||||
void MWState::Character::addSlot (const boost::filesystem::path& path, const std::string& game)
|
||||
{
|
||||
Slot slot;
|
||||
slot.mPath = path;
|
||||
slot.mTimeStamp = boost::filesystem::last_write_time (path);
|
||||
|
||||
ESM::ESMReader reader;
|
||||
reader.open (slot.mPath.string());
|
||||
|
||||
if (reader.getFormat()>ESM::Header::CurrentFormat)
|
||||
return; // format is too new -> ignore
|
||||
|
||||
if (reader.getRecName()!=ESM::REC_SAVE)
|
||||
return; // invalid save file -> ignore
|
||||
|
||||
reader.getRecHeader();
|
||||
|
||||
slot.mProfile.load (reader);
|
||||
|
||||
if (Misc::StringUtils::lowerCase (slot.mProfile.mContentFiles.at (0))!=
|
||||
Misc::StringUtils::lowerCase (game))
|
||||
return; // this file is for a different game -> ignore
|
||||
|
||||
mSlots.push_back (slot);
|
||||
}
|
||||
|
||||
void MWState::Character::addSlot (const ESM::SavedGame& profile)
|
||||
{
|
||||
Slot slot;
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << mNext++;
|
||||
|
||||
slot.mPath = mPath / stream.str();
|
||||
slot.mProfile = profile;
|
||||
slot.mTimeStamp = std::time (0);
|
||||
|
||||
mSlots.push_back (slot);
|
||||
}
|
||||
|
||||
MWState::Character::Character (const boost::filesystem::path& saves, const std::string& game)
|
||||
: mPath (saves), mNext (0)
|
||||
{
|
||||
if (!boost::filesystem::is_directory (mPath))
|
||||
{
|
||||
boost::filesystem::create_directories (mPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (boost::filesystem::directory_iterator iter (mPath);
|
||||
iter!=boost::filesystem::directory_iterator(); ++iter)
|
||||
{
|
||||
boost::filesystem::path slotPath = *iter;
|
||||
|
||||
try
|
||||
{
|
||||
addSlot (slotPath, game);
|
||||
}
|
||||
catch (...) {} // ignoring bad saved game files for now
|
||||
|
||||
std::istringstream stream (slotPath.filename().string());
|
||||
|
||||
int index = 0;
|
||||
|
||||
if ((stream >> index) && index>=mNext)
|
||||
mNext = index+1;
|
||||
}
|
||||
|
||||
std::sort (mSlots.begin(), mSlots.end());
|
||||
}
|
||||
}
|
||||
|
||||
const MWState::Slot *MWState::Character::createSlot (const ESM::SavedGame& profile)
|
||||
{
|
||||
addSlot (profile);
|
||||
|
||||
return &mSlots.back();
|
||||
}
|
||||
|
||||
const MWState::Slot *MWState::Character::updateSlot (const Slot *slot, const ESM::SavedGame& profile)
|
||||
{
|
||||
int index = slot - &mSlots[0];
|
||||
|
||||
if (index<0 || index>=static_cast<int> (mSlots.size()))
|
||||
{
|
||||
// sanity check; not entirely reliable
|
||||
throw std::logic_error ("slot not found");
|
||||
}
|
||||
|
||||
Slot newSlot = *slot;
|
||||
newSlot.mProfile = profile;
|
||||
newSlot.mTimeStamp = std::time (0);
|
||||
|
||||
mSlots.erase (mSlots.begin()+index);
|
||||
|
||||
mSlots.push_back (newSlot);
|
||||
|
||||
return &mSlots.back();
|
||||
}
|
||||
|
||||
MWState::Character::SlotIterator MWState::Character::begin() const
|
||||
{
|
||||
return mSlots.rbegin();
|
||||
}
|
||||
|
||||
MWState::Character::SlotIterator MWState::Character::end() const
|
||||
{
|
||||
return mSlots.rend();
|
||||
}
|
||||
|
||||
ESM::SavedGame MWState::Character::getSignature() const
|
||||
{
|
||||
if (mSlots.empty())
|
||||
throw std::logic_error ("character signature not available");
|
||||
|
||||
std::vector<Slot>::const_iterator iter (mSlots.begin());
|
||||
|
||||
Slot slot = *iter;
|
||||
|
||||
for (++iter; iter!=mSlots.end(); ++iter)
|
||||
if (iter->mProfile.mPlayerLevel>slot.mProfile.mPlayerLevel)
|
||||
slot = *iter;
|
||||
else if (iter->mProfile.mPlayerLevel==slot.mProfile.mPlayerLevel &&
|
||||
iter->mTimeStamp>slot.mTimeStamp)
|
||||
slot = *iter;
|
||||
|
||||
return slot.mProfile;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
#ifndef GAME_STATE_CHARACTER_H
|
||||
#define GAME_STATE_CHARACTER_H
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <components/esm/savedgame.hpp>
|
||||
|
||||
namespace MWState
|
||||
{
|
||||
struct Slot
|
||||
{
|
||||
boost::filesystem::path mPath;
|
||||
ESM::SavedGame mProfile;
|
||||
std::time_t mTimeStamp;
|
||||
};
|
||||
|
||||
bool operator< (const Slot& left, const Slot& right);
|
||||
|
||||
class Character
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector<Slot>::const_reverse_iterator SlotIterator;
|
||||
|
||||
private:
|
||||
|
||||
boost::filesystem::path mPath;
|
||||
std::vector<Slot> mSlots;
|
||||
int mNext;
|
||||
|
||||
void addSlot (const boost::filesystem::path& path, const std::string& game);
|
||||
|
||||
void addSlot (const ESM::SavedGame& profile);
|
||||
|
||||
public:
|
||||
|
||||
Character (const boost::filesystem::path& saves, const std::string& game);
|
||||
|
||||
const Slot *createSlot (const ESM::SavedGame& profile);
|
||||
///< Create new slot.
|
||||
///
|
||||
/// \attention The ownership of the slot is not transferred.
|
||||
|
||||
const Slot *updateSlot (const Slot *slot, const ESM::SavedGame& profile);
|
||||
/// \note Slot must belong to this character.
|
||||
///
|
||||
/// \attention The \a slot pointer will be invalidated by this call.
|
||||
|
||||
SlotIterator begin() const;
|
||||
///< First slot is the most recent. Other slots follow in descending order of save date.
|
||||
///
|
||||
/// Any call to createSlot and updateSlot can invalidate the returned iterator.
|
||||
|
||||
SlotIterator end() const;
|
||||
|
||||
ESM::SavedGame getSignature() const;
|
||||
///< Return signature information for this character.
|
||||
///
|
||||
/// \attention This function must not be called if there are no slots.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,85 @@
|
||||
|
||||
#include "charactermanager.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves,
|
||||
const std::string& game)
|
||||
: mPath (saves), mNext (0), mCurrent (0), mGame (game)
|
||||
{
|
||||
if (!boost::filesystem::is_directory (mPath))
|
||||
{
|
||||
boost::filesystem::create_directories (mPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (boost::filesystem::directory_iterator iter (mPath);
|
||||
iter!=boost::filesystem::directory_iterator(); ++iter)
|
||||
{
|
||||
boost::filesystem::path characterDir = *iter;
|
||||
|
||||
if (boost::filesystem::is_directory (characterDir))
|
||||
{
|
||||
Character character (characterDir, mGame);
|
||||
|
||||
if (character.begin()!=character.end())
|
||||
mCharacters.push_back (character);
|
||||
}
|
||||
|
||||
std::istringstream stream (characterDir.filename().string());
|
||||
|
||||
int index = 0;
|
||||
|
||||
if ((stream >> index) && index>=mNext)
|
||||
mNext = index+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MWState::Character *MWState::CharacterManager::getCurrentCharacter (bool create)
|
||||
{
|
||||
if (!mCurrent && create)
|
||||
createCharacter();
|
||||
|
||||
return mCurrent;
|
||||
}
|
||||
|
||||
void MWState::CharacterManager::createCharacter()
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << mNext++;
|
||||
|
||||
boost::filesystem::path path = mPath / stream.str();
|
||||
|
||||
mCharacters.push_back (Character (path, mGame));
|
||||
|
||||
mCurrent = &mCharacters.back();
|
||||
}
|
||||
|
||||
void MWState::CharacterManager::setCurrentCharacter (const Character *character)
|
||||
{
|
||||
int index = character - &mCharacters[0];
|
||||
|
||||
if (index<0 || index>=static_cast<int> (mCharacters.size()))
|
||||
throw std::logic_error ("invalid character");
|
||||
|
||||
mCurrent = &mCharacters[index];
|
||||
}
|
||||
|
||||
void MWState::CharacterManager::clearCurrentCharacter()
|
||||
{
|
||||
mCurrent = 0;
|
||||
}
|
||||
|
||||
std::vector<MWState::Character>::const_iterator MWState::CharacterManager::begin() const
|
||||
{
|
||||
return mCharacters.begin();
|
||||
}
|
||||
|
||||
std::vector<MWState::Character>::const_iterator MWState::CharacterManager::end() const
|
||||
{
|
||||
return mCharacters.end();
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
#ifndef GAME_STATE_CHARACTERMANAGER_H
|
||||
#define GAME_STATE_CHARACTERMANAGER_H
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include "character.hpp"
|
||||
|
||||
namespace MWState
|
||||
{
|
||||
class CharacterManager
|
||||
{
|
||||
boost::filesystem::path mPath;
|
||||
int mNext;
|
||||
std::vector<Character> mCharacters;
|
||||
Character *mCurrent;
|
||||
std::string mGame;
|
||||
|
||||
private:
|
||||
|
||||
CharacterManager (const CharacterManager&);
|
||||
///< Not implemented
|
||||
|
||||
CharacterManager& operator= (const CharacterManager&);
|
||||
///< Not implemented
|
||||
|
||||
public:
|
||||
|
||||
CharacterManager (const boost::filesystem::path& saves, const std::string& game);
|
||||
|
||||
Character *getCurrentCharacter (bool create = true);
|
||||
///< \param create Create a new character, if there is no current character.
|
||||
|
||||
void createCharacter();
|
||||
///< Create new character within saved game management
|
||||
|
||||
void setCurrentCharacter (const Character *character);
|
||||
|
||||
void clearCurrentCharacter();
|
||||
|
||||
std::vector<Character>::const_iterator begin() const;
|
||||
|
||||
std::vector<Character>::const_iterator end() const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,343 @@
|
||||
|
||||
#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/misc/stringops.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <OgreImage.h>
|
||||
|
||||
#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 "../mwworld/player.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
|
||||
#include "../mwscript/globalscripts.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();
|
||||
|
||||
mState = State_NoGame;
|
||||
mCharacterManager.clearCurrentCharacter();
|
||||
mTimePlayed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWState::Slot lastSave = *getCurrentCharacter()->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()->messageBox(message, buttons);
|
||||
mAskLoadRecent = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton();
|
||||
if(iButton==0)
|
||||
{
|
||||
mAskLoadRecent = false;
|
||||
//Load last saved game for current character
|
||||
MWState::Character *curCharacter = getCurrentCharacter();
|
||||
MWState::Slot lastSave = *curCharacter->begin();
|
||||
loadGame(curCharacter, &lastSave);
|
||||
}
|
||||
else if(iButton==1)
|
||||
{
|
||||
mAskLoadRecent = false;
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MWState::StateManager::State MWState::StateManager::getState() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
void MWState::StateManager::newGame (bool bypass)
|
||||
{
|
||||
cleanup();
|
||||
|
||||
if (!bypass)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->startNewGame();
|
||||
MWBase::Environment::get().getWindowManager()->setNewGame (true);
|
||||
}
|
||||
else
|
||||
MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1);
|
||||
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
|
||||
|
||||
mState = State_Running;
|
||||
}
|
||||
|
||||
void MWState::StateManager::endGame()
|
||||
{
|
||||
mState = State_Ended;
|
||||
MWBase::Environment::get().getWorld()->useDeathCamera();
|
||||
}
|
||||
|
||||
void MWState::StateManager::saveGame (const std::string& description, const Slot *slot)
|
||||
{
|
||||
ESM::SavedGame profile;
|
||||
|
||||
MWBase::World& world = *MWBase::Environment::get().getWorld();
|
||||
|
||||
MWWorld::Ptr player = world.getPlayer().getPlayer();
|
||||
|
||||
profile.mContentFiles = world.getContentFiles();
|
||||
|
||||
profile.mPlayerName = player.getClass().getName (player);
|
||||
profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel();
|
||||
profile.mPlayerClass = player.get<ESM::NPC>()->mBase->mClass;
|
||||
|
||||
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;
|
||||
|
||||
int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing
|
||||
Ogre::Image screenshot;
|
||||
world.screenshot(screenshot, screenshotW, screenshotH);
|
||||
Ogre::DataStreamPtr encoded = screenshot.encode("jpg");
|
||||
profile.mScreenshot.resize(encoded->size());
|
||||
encoded->read(&profile.mScreenshot[0], encoded->size());
|
||||
|
||||
if (!slot)
|
||||
slot = mCharacterManager.getCurrentCharacter()->createSlot (profile);
|
||||
else
|
||||
slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile);
|
||||
|
||||
std::ofstream stream (slot->mPath.string().c_str());
|
||||
|
||||
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::Header::CurrentFormat);
|
||||
writer.setRecordCount (
|
||||
1 // saved game header
|
||||
+MWBase::Environment::get().getJournal()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getWorld()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
|
||||
+ 1 // global map
|
||||
);
|
||||
|
||||
writer.save (stream);
|
||||
|
||||
writer.startRecord (ESM::REC_SAVE);
|
||||
slot->mProfile.save (writer);
|
||||
writer.endRecord (ESM::REC_SAVE);
|
||||
|
||||
MWBase::Environment::get().getJournal()->write (writer);
|
||||
MWBase::Environment::get().getWorld()->write (writer);
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer);
|
||||
MWBase::Environment::get().getWindowManager()->write(writer);
|
||||
|
||||
writer.close();
|
||||
|
||||
Settings::Manager::setString ("character", "Saves",
|
||||
slot->mPath.parent_path().filename().string());
|
||||
}
|
||||
|
||||
void MWState::StateManager::loadGame (const Character *character, const Slot *slot)
|
||||
{
|
||||
try
|
||||
{
|
||||
cleanup();
|
||||
|
||||
mTimePlayed = slot->mProfile.mTimePlayed;
|
||||
|
||||
ESM::ESMReader reader;
|
||||
reader.open (slot->mPath.string());
|
||||
|
||||
std::map<int, int> contentFileMap = buildContentFileIndexMap (reader);
|
||||
|
||||
while (reader.hasMoreRecs())
|
||||
{
|
||||
ESM::NAME n = reader.getRecName();
|
||||
reader.getRecHeader();
|
||||
|
||||
switch (n.val)
|
||||
{
|
||||
case ESM::REC_SAVE:
|
||||
|
||||
// don't need to read that here
|
||||
reader.skipRecord();
|
||||
break;
|
||||
|
||||
case ESM::REC_JOUR:
|
||||
case ESM::REC_QUES:
|
||||
|
||||
MWBase::Environment::get().getJournal()->readRecord (reader, n.val);
|
||||
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:
|
||||
|
||||
MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap);
|
||||
break;
|
||||
|
||||
case ESM::REC_GSCR:
|
||||
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val);
|
||||
break;
|
||||
|
||||
case ESM::REC_GMAP:
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
// ignore invalid records
|
||||
/// \todo log error
|
||||
reader.skipRecord();
|
||||
}
|
||||
}
|
||||
|
||||
mCharacterManager.setCurrentCharacter(character);
|
||||
|
||||
mState = State_Running;
|
||||
|
||||
Settings::Manager::setString ("character", "Saves",
|
||||
slot->mPath.parent_path().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();
|
||||
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
|
||||
ESM::CellId cellId = ptr.getCell()->mCell->getCellId();
|
||||
|
||||
MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "failed to load saved game: " << e.what() << std::endl;
|
||||
cleanup (true);
|
||||
}
|
||||
}
|
||||
|
||||
MWState::Character *MWState::StateManager::getCurrentCharacter (bool create)
|
||||
{
|
||||
return mCharacterManager.getCurrentCharacter (create);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
#ifndef GAME_STATE_STATEMANAGER_H
|
||||
#define GAME_STATE_STATEMANAGER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include "charactermanager.hpp"
|
||||
|
||||
namespace MWState
|
||||
{
|
||||
class StateManager : public MWBase::StateManager
|
||||
{
|
||||
bool mQuitRequest;
|
||||
bool mAskLoadRecent;
|
||||
State mState;
|
||||
CharacterManager mCharacterManager;
|
||||
double mTimePlayed;
|
||||
|
||||
private:
|
||||
|
||||
void cleanup (bool force = false);
|
||||
|
||||
std::map<int, int> buildContentFileIndexMap (const ESM::ESMReader& reader) const;
|
||||
|
||||
public:
|
||||
|
||||
StateManager (const boost::filesystem::path& saves, const std::string& game);
|
||||
|
||||
virtual void requestQuit();
|
||||
|
||||
virtual bool hasQuitRequest() const;
|
||||
|
||||
virtual void askLoadRecent();
|
||||
|
||||
virtual State getState() const;
|
||||
|
||||
virtual void newGame (bool bypass = false);
|
||||
///< Start a new game.
|
||||
///
|
||||
/// \param bypass Skip new game mechanics.
|
||||
|
||||
virtual void endGame();
|
||||
|
||||
virtual void saveGame (const std::string& description, const Slot *slot = 0);
|
||||
///< Write a saved game to \a slot or create a new slot if \a slot == 0.
|
||||
///
|
||||
/// \note Slot must belong to the current character.
|
||||
|
||||
virtual void loadGame (const Character *character, const Slot *slot);
|
||||
///< Load a saved game file from \a slot.
|
||||
///
|
||||
/// \note \a slot must belong to \a character.
|
||||
|
||||
virtual Character *getCurrentCharacter (bool create = true);
|
||||
///< \param create Create a new character, if there is no current character.
|
||||
|
||||
virtual CharacterIterator characterBegin();
|
||||
///< Any call to SaveGame and getCurrentCharacter can invalidate the returned
|
||||
/// iterator.
|
||||
|
||||
virtual CharacterIterator characterEnd();
|
||||
|
||||
virtual void update (float duration);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,29 @@
|
||||
|
||||
#include "livecellref.hpp"
|
||||
|
||||
#include <components/esm/objectstate.hpp>
|
||||
|
||||
#include "ptr.hpp"
|
||||
#include "class.hpp"
|
||||
|
||||
void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state)
|
||||
{
|
||||
mRef = state.mRef;
|
||||
mData = RefData (state);
|
||||
Ptr ptr (this);
|
||||
mClass->readAdditionalState (ptr, state);
|
||||
}
|
||||
|
||||
void MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const
|
||||
{
|
||||
state.mRef = mRef;
|
||||
mData.write (state);
|
||||
/// \todo get rid of this cast once const-correct Ptr are available
|
||||
Ptr ptr (const_cast<LiveCellRefBase *> (this));
|
||||
mClass->writeAdditionalState (ptr, state);
|
||||
}
|
||||
|
||||
bool MWWorld::LiveCellRefBase::checkStateImp (const ESM::ObjectState& state)
|
||||
{
|
||||
return true;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue