store cell state in saved game files (no references yet)

pull/22/head
Marc Zinnschlag 11 years ago
parent 9ebe66e693
commit 22cb4784b5

@ -24,9 +24,9 @@
#include "../mwscript/globalscripts.hpp"
void MWState::StateManager::cleanup()
void MWState::StateManager::cleanup (bool force)
{
if (mState!=State_NoGame)
if (mState!=State_NoGame || force)
{
MWBase::Environment::get().getSoundManager()->clear();
MWBase::Environment::get().getDialogueManager()->clear();
@ -184,77 +184,86 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
void MWState::StateManager::loadGame (const Character *character, const Slot *slot)
{
cleanup();
mTimePlayed = slot->mProfile.mTimePlayed;
ESM::ESMReader reader;
reader.open (slot->mPath.string());
while (reader.hasMoreRecs())
try
{
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:
cleanup();
MWBase::Environment::get().getWorld()->readRecord (reader, n.val);
break;
mTimePlayed = slot->mProfile.mTimePlayed;
case ESM::REC_GSCR:
ESM::ESMReader reader;
reader.open (slot->mPath.string());
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val);
break;
default:
// ignore invalid records
/// \todo log error
reader.skipRecord();
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);
break;
case ESM::REC_GSCR:
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val);
break;
default:
// ignore invalid records
/// \todo log error
reader.skipRecord();
}
}
}
mCharacterManager.setCurrentCharacter(character);
mCharacterManager.setCurrentCharacter(character);
mState = State_Running;
mState = State_Running;
Settings::Manager::setString ("character", "Saves",
slot->mPath.parent_path().filename().string());
Settings::Manager::setString ("character", "Saves",
slot->mPath.parent_path().filename().string());
MWBase::Environment::get().getWorld()->setupPlayer();
MWBase::Environment::get().getWorld()->renderPlayer();
MWBase::Environment::get().getWindowManager()->updatePlayer();
MWBase::Environment::get().getMechanicsManager()->playerLoaded();
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();
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
ESM::CellId cellId = ptr.getCell()->mCell->getCellId();
ESM::CellId cellId = ptr.getCell()->mCell->getCellId();
MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition());
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)

@ -19,7 +19,7 @@ namespace MWState
private:
void cleanup();
void cleanup (bool force = false);
public:

@ -1,5 +1,10 @@
#include "cells.hpp"
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/defs.hpp>
#include <components/esm/cellstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -59,6 +64,30 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, CellStore&
return ptr;
}
void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, const CellStore& cell) const
{
ESM::CellState cellState;
cell.saveState (cellState);
writer.startRecord (ESM::REC_CSTA);
cellState.mId.save (writer);
cellState.save (writer);
/// \todo write references
writer.endRecord (ESM::REC_CSTA);
}
bool MWWorld::Cells::hasState (const CellStore& cellStore) const
{
if (cellStore.mState==CellStore::State_Loaded)
return true;
if (cellStore.mCell->mData.mFlags & ESM::Cell::Interior)
return cellStore.mCell->mData.mFlags & ESM::Cell::HasWater;
else
return false;
}
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader)
: mStore (store), mReader (reader),
mIdCache (40, std::pair<std::string, CellStore *> ("", (CellStore*)0)), /// \todo make cache size configurable
@ -121,6 +150,14 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name)
return &result->second;
}
MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id)
{
if (id.mPaged)
return getExterior (id.mIndex.mX, id.mIndex.mY);
return getInterior (id.mWorldspace);
}
MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell,
bool searchInContainers)
{
@ -271,3 +308,62 @@ void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector<MWWorl
}
}
int MWWorld::Cells::countSavedGameRecords() const
{
int count = 0;
for (std::map<std::string, CellStore>::const_iterator iter (mInteriors.begin());
iter!=mInteriors.end(); ++iter)
if (hasState (iter->second))
++count;
for (std::map<std::pair<int, int>, CellStore>::const_iterator iter (mExteriors.begin());
iter!=mExteriors.end(); ++iter)
if (hasState (iter->second))
++count;
return count;
}
void MWWorld::Cells::write (ESM::ESMWriter& writer) const
{
for (std::map<std::pair<int, int>, CellStore>::const_iterator iter (mExteriors.begin());
iter!=mExteriors.end(); ++iter)
if (hasState (iter->second))
writeCell (writer, iter->second);
for (std::map<std::string, CellStore>::const_iterator iter (mInteriors.begin());
iter!=mInteriors.end(); ++iter)
if (hasState (iter->second))
writeCell (writer, iter->second);
}
bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type)
{
if (type==ESM::REC_CSTA)
{
ESM::CellState state;
state.mId.load (reader);
CellStore *cellStore = 0;
try
{
cellStore = getCell (state.mId);
}
catch (...)
{
// silently drop cells that don't exist anymore
/// \todo log
}
state.load (reader);
cellStore->loadState (state);
reader.skipRecord();
return true;
}
return false;
}

@ -10,6 +10,8 @@
namespace ESM
{
class ESMReader;
class ESMWriter;
struct CellId;
}
namespace MWWorld
@ -33,18 +35,23 @@ namespace MWWorld
Ptr getPtrAndCache (const std::string& name, CellStore& cellStore);
void writeCell (ESM::ESMWriter& writer, const CellStore& cell) const;
bool hasState (const CellStore& cellStore) const;
///< Check if cell has state that needs to be included in a saved game file.
public:
void clear();
Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader);
///< \todo pass the dynamic part of the ESMStore isntead (once it is written) of the whole
/// world
CellStore *getExterior (int x, int y);
CellStore *getInterior (const std::string& name);
CellStore *getCell (const ESM::CellId& id);
Ptr getPtr (const std::string& name, CellStore& cellStore, bool searchInContainers = false);
///< \param searchInContainers Only affect loaded cells.
/// @note name must be lower case
@ -56,6 +63,12 @@ namespace MWWorld
/// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
/// @note name must be lower case
void getExteriorPtrs (const std::string& name, std::vector<MWWorld::Ptr>& out);
int countSavedGameRecords() const;
void write (ESM::ESMWriter& writer) const;
bool readRecord (ESM::ESMReader& reader, int32_t type);
};
}

@ -2,6 +2,9 @@
#include <iostream>
#include <components/esm/cellstate.hpp>
#include <components/esm/cellid.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -230,4 +233,22 @@ namespace MWWorld
<< "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n";
}
}
void CellStore::loadState (const ESM::CellState& state)
{
if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater)
mWaterLevel = state.mWaterLevel;
mWaterLevel = state.mWaterLevel;
}
void CellStore::saveState (ESM::CellState& state) const
{
state.mId = mCell->getCellId();
if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater)
state.mWaterLevel = mWaterLevel;
state.mWaterLevel = mWaterLevel;
}
}

@ -7,6 +7,11 @@
#include "livecellref.hpp"
#include "esmstore.hpp"
namespace ESM
{
struct CellState;
}
namespace MWWorld
{
@ -133,6 +138,10 @@ namespace MWWorld
Ptr searchInContainer (const std::string& id);
void loadState (const ESM::CellState& state);
void saveState (ESM::CellState& state) const;
private:
template<class Functor, class List>
@ -158,7 +167,6 @@ namespace MWWorld
///< Make case-adjustments to \a ref and insert it into the respective container.
///
/// Invalid \a ref objects are silently dropped.
};
}

@ -304,13 +304,16 @@ namespace MWWorld
{
return
mStore.countSavedGameRecords()
+mGlobalVariables.countSavedGameRecords();
+mGlobalVariables.countSavedGameRecords()
+1 // player record
+mCells.countSavedGameRecords();
}
void World::write (ESM::ESMWriter& writer) const
{
mStore.write (writer);
mGlobalVariables.write (writer);
mCells.write (writer);
mPlayer->write (writer);
}
@ -318,7 +321,8 @@ namespace MWWorld
{
if (!mStore.readRecord (reader, type) &&
!mGlobalVariables.readRecord (reader, type) &&
!mPlayer->readRecord (reader, type))
!mPlayer->readRecord (reader, type) &&
!mCells.readRecord (reader, type))
{
throw std::runtime_error ("unknown record in saved game");
}

@ -40,7 +40,7 @@ add_component_dir (esm
loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate
)
add_component_dir (misc

@ -0,0 +1,17 @@
#include "cellstate.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
void ESM::CellState::load (ESMReader &esm)
{
mWaterLevel = 0;
esm.getHNOT (mWaterLevel, "WLVL");
}
void ESM::CellState::save (ESMWriter &esm) const
{
if (!mId.mPaged)
esm.writeHNT ("WLVL", mWaterLevel);
}

@ -0,0 +1,25 @@
#ifndef OPENMW_ESM_CELLSTATE_H
#define OPENMW_ESM_CELLSTATE_H
#include "cellid.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
/// \note Does not include references
struct CellState
{
CellId mId;
float mWaterLevel;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif

@ -88,7 +88,8 @@ enum RecNameInts
REC_JOUR = 0x524f55a4,
REC_QUES = 0x53455551,
REC_GSCR = 0x52435347,
REC_PLAY = 0x504c4159,
REC_PLAY = 0x59414c50,
REC_CSTA = 0x41545343,
// format 1
REC_FILT = 0x544C4946

Loading…
Cancel
Save