Merge branch 'savedgame' of https://github.com/zinnschlag/openmw into savedgame

actorid
scrawl 11 years ago
commit ac1859fb77

@ -246,7 +246,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
if(quiet) continue;
std::cout << " Refnum: " << ref.mRefnum << std::endl;
std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl;
std::cout << " ID: '" << ref.mRefID << "'\n";
std::cout << " Owner: '" << ref.mOwner << "'\n";
std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n";

@ -147,6 +147,8 @@ namespace MWBase
virtual void update(float duration) = 0;
virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0;
virtual void clear() = 0;
};
}

@ -37,6 +37,7 @@ namespace ESM
struct Potion;
struct Spell;
struct NPC;
struct CellId;
}
namespace MWRender
@ -105,12 +106,14 @@ namespace MWBase
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
virtual OEngine::Render::Fader* getFader() = 0;
///< \ŧodo remove this function. Rendering details should not be exposed.
///< \todo remove this function. Rendering details should not be exposed.
virtual MWWorld::CellStore *getExterior (int x, int y) = 0;
virtual MWWorld::CellStore *getInterior (const std::string& name) = 0;
virtual MWWorld::CellStore *getCell (const ESM::CellId& id) = 0;
virtual void useDeathCamera() = 0;
virtual void setWaterHeight(const float height) = 0;
@ -236,6 +239,8 @@ namespace MWBase
virtual void changeToExteriorCell (const ESM::Position& position) = 0;
///< Move to exterior cell.
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position) = 0;
virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0;
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.

@ -8,6 +8,7 @@
#include "../mwbase/statemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwstate/character.hpp"
@ -123,6 +124,12 @@ namespace MWGui
}
setVisible(false);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_NoGame)
{
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
}
}
void SaveGameDialog::onCharacterSelected(MyGUI::ComboBox *sender, size_t pos)

@ -14,6 +14,7 @@
#include <extern/sdl4ogre/sdlcursormanager.hpp>
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp"
@ -709,6 +710,10 @@ namespace MWGui
mToolTips->onFrame(frameDuration);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_NoGame)
return;
if (mDragAndDrop->mIsOnDragAndDrop)
{
assert(mDragAndDrop->mDraggedWidget);

@ -30,6 +30,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp" // FIXME
#include "../mwbase/windowmanager.hpp" // FIXME
#include "../mwbase/statemanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
@ -329,6 +330,12 @@ void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr)
void RenderingManager::update (float duration, bool paused)
{
mVideoPlayer->update ();
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_NoGame)
return;
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayer().getPlayer();
@ -365,8 +372,6 @@ void RenderingManager::update (float duration, bool paused)
mOcclusionQuery->update(duration);
mVideoPlayer->update ();
mRendering.update(duration);
Ogre::ControllerManager::getSingleton().setTimeFactor(paused ? 0.f : 1.f);

@ -6,6 +6,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/player.hpp"
@ -607,8 +608,13 @@ namespace MWSound
{
if(!mOutput->isInitialized())
return;
updateSounds(duration);
updateRegionSound(duration);
if (MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
updateSounds(duration);
updateRegionSound(duration);
}
}
@ -705,4 +711,13 @@ namespace MWSound
{
return bytes / framesToBytes(1, config, type);
}
void SoundManager::clear()
{
for (SoundMap::iterator iter (mActiveSounds.begin()); iter!=mActiveSounds.end(); ++iter)
iter->first->stop();
mActiveSounds.clear();
stopMusic();
}
}

@ -148,6 +148,8 @@ namespace MWSound
virtual void update(float duration);
virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up);
virtual void clear();
};
}

@ -3,6 +3,8 @@
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/cellid.hpp>
#include <components/esm/loadcell.hpp>
#include <components/settings/settings.hpp>
@ -13,6 +15,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
@ -21,10 +24,11 @@
#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();
MWBase::Environment::get().getJournal()->clear();
MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear();
@ -180,76 +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();
cleanup();
switch (n.val)
{
case ESM::REC_SAVE:
mTimePlayed = slot->mProfile.mTimePlayed;
// don't need to read that here
reader.skipRecord();
break;
ESM::ESMReader reader;
reader.open (slot->mPath.string());
case ESM::REC_JOUR:
case ESM::REC_QUES:
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();
}
}
MWBase::Environment::get().getJournal()->readRecord (reader, n.val);
break;
mCharacterManager.setCurrentCharacter(character);
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:
mState = State_Running;
MWBase::Environment::get().getWorld()->readRecord (reader, n.val);
break;
Settings::Manager::setString ("character", "Saves",
slot->mPath.parent_path().filename().string());
case ESM::REC_GSCR:
MWBase::Environment::get().getWorld()->setupPlayer();
MWBase::Environment::get().getWorld()->renderPlayer();
MWBase::Environment::get().getWindowManager()->updatePlayer();
MWBase::Environment::get().getMechanicsManager()->playerLoaded();
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val);
break;
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
default:
ESM::CellId cellId = ptr.getCell()->mCell->getCellId();
// ignore invalid records
/// \todo log error
reader.skipRecord();
}
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);
}
mCharacterManager.setCurrentCharacter(character);
mState = State_Running;
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();
// for testing purpose only
ESM::Position pos;
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
MWBase::Environment::get().getWorld()->changeToExteriorCell (pos);
}
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);
cell.writeReferences (writer);
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,11 @@
#include <iostream>
#include <components/esm/cellstate.hpp>
#include <components/esm/cellid.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/objectstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -29,6 +34,33 @@ namespace
return MWWorld::Ptr();
}
template<typename RecordType, typename T>
void writeReferenceCollection (ESM::ESMWriter& writer,
const MWWorld::CellRefList<T>& collection)
{
if (!collection.mList.empty())
{
// section header
writer.writeHNT ("CSEC", collection.mList.front().mBase->sRecordId);
// references
for (typename MWWorld::CellRefList<T>::List::const_iterator
iter (collection.mList.begin());
iter!=collection.mList.end(); ++iter)
{
if (iter->mData.getCount()==0 && iter->mRef.mRefNum.mContentFile==-1)
continue; // deleted file that did not came from a content file -> ignore
RecordType state;
iter->save (state);
writer.startRecord (ESM::REC_OBJE);
state.save (writer);
writer.endRecord (ESM::REC_OBJE);
}
}
}
}
namespace MWWorld
@ -42,7 +74,7 @@ namespace MWWorld
if (const X *ptr = store.search (ref.mRefID))
{
typename std::list<LiveRef>::iterator iter =
std::find(mList.begin(), mList.end(), ref.mRefnum);
std::find(mList.begin(), mList.end(), ref.mRefNum);
LiveRef liveCellRef (ref, ptr);
@ -143,13 +175,16 @@ namespace MWWorld
mCell->restore (esm[index], i);
ESM::CellRef ref;
ref.mRefNum.mContentFile = -1;
// Get each reference in turn
bool deleted = false;
while(mCell->getNextRef(esm[index], ref, deleted))
{
// Don't load reference if it was moved to a different cell.
ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefnum);
std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID);
ESM::MovedCellRefTracker::const_iterator iter =
std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum);
if (iter != mCell->mMovedRefs.end()) {
continue;
}
@ -227,4 +262,46 @@ 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;
}
void CellStore::writeReferences (ESM::ESMWriter& writer) const
{
writeReferenceCollection<ESM::ObjectState> (writer, mActivators);
writeReferenceCollection<ESM::ObjectState> (writer, mPotions);
writeReferenceCollection<ESM::ObjectState> (writer, mAppas);
writeReferenceCollection<ESM::ObjectState> (writer, mArmors);
writeReferenceCollection<ESM::ObjectState> (writer, mBooks);
writeReferenceCollection<ESM::ObjectState> (writer, mClothes);
writeReferenceCollection<ESM::ObjectState> (writer, mContainers);
writeReferenceCollection<ESM::ObjectState> (writer, mCreatures);
writeReferenceCollection<ESM::ObjectState> (writer, mDoors);
writeReferenceCollection<ESM::ObjectState> (writer, mIngreds);
writeReferenceCollection<ESM::ObjectState> (writer, mCreatureLists);
writeReferenceCollection<ESM::ObjectState> (writer, mItemLists);
writeReferenceCollection<ESM::ObjectState> (writer, mLights);
writeReferenceCollection<ESM::ObjectState> (writer, mLockpicks);
writeReferenceCollection<ESM::ObjectState> (writer, mMiscItems);
writeReferenceCollection<ESM::ObjectState> (writer, mNpcs);
writeReferenceCollection<ESM::ObjectState> (writer, mProbes);
writeReferenceCollection<ESM::ObjectState> (writer, mRepairs);
writeReferenceCollection<ESM::ObjectState> (writer, mStatics);
writeReferenceCollection<ESM::ObjectState> (writer, mWeapons);
}
}

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

@ -161,9 +161,9 @@ void ESMStore::setUp()
mClasses.write (writer);
mClothes.write (writer);
mEnchants.write (writer);
mNpcs.write (writer);
mSpells.write (writer);
mWeapons.write (writer);
mNpcs.write (writer);
}
bool ESMStore::readRecord (ESM::ESMReader& reader, int32_t type)
@ -176,11 +176,25 @@ void ESMStore::setUp()
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_NPC_:
mStores[type]->read (reader);
if (type==ESM::REC_NPC_)
{
// NPC record will always be last and we know that there can be only one
// dynamic NPC record (player) -> We are done here with dynamic record laoding
setUp();
const ESM::NPC *player = mNpcs.find ("player");
if (!mRaces.find (player->mRace) ||
!mClasses.find (player->mClass))
throw std::runtime_error ("Invalid player record (race or class unavilable");
}
return true;
default:

@ -0,0 +1,21 @@
#include "livecellref.hpp"
#include <components/esm/objectstate.hpp>
void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state)
{
mRef = state.mRef;
mData = RefData (state);
}
void MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const
{
state.mRef = mRef;
mData.write (state);
}
bool MWWorld::LiveCellRefBase::checkStateImp (const ESM::ObjectState& state)
{
return true;
}

@ -7,6 +7,11 @@
#include "refdata.hpp"
namespace ESM
{
class ObjectState;
}
namespace MWWorld
{
class Ptr;
@ -29,8 +34,31 @@ namespace MWWorld
LiveCellRefBase(std::string type, const ESM::CellRef &cref=ESM::CellRef());
/* Need this for the class to be recognized as polymorphic */
virtual ~LiveCellRefBase() { }
protected:
void loadImp (const ESM::ObjectState& state);
///< Load state into a LiveCellRef, that has already been initialised with base and
/// class.
///
/// \attention Must not be called with an invalid \a state.
void saveImp (ESM::ObjectState& state) const;
///< Save LiveCellRef state into \a state.
static bool checkStateImp (const ESM::ObjectState& state);
///< Check if state is valid and report errors.
///
/// \return Valid?
///
/// \note Does not check if the RefId exists.
};
inline bool operator== (const LiveCellRefBase& cellRef, const ESM::CellRef::RefNum refNum)
{
return cellRef.mRef.mRefNum==refNum;
}
/// A reference to one object (of any type) in a cell.
///
/// Constructing this with a CellRef instance in the constructor means that
@ -50,9 +78,41 @@ namespace MWWorld
// The object that this instance is based on.
const X* mBase;
void load (const ESM::ObjectState& state);
///< Load state into a LiveCellRef, that has already been initialised with base and class.
///
/// \attention Must not be called with an invalid \a state.
void save (ESM::ObjectState& state) const;
///< Save LiveCellRef state into \a state.
static bool checkState (const ESM::ObjectState& state);
///< Check if state is valid and report errors.
///
/// \return Valid?
///
/// \note Does not check if the RefId exists.
};
// template<typename X> bool operator==(const LiveCellRef<X>& ref, int pRefnum);
template <typename X>
void LiveCellRef<X>::load (const ESM::ObjectState& state)
{
loadImp (state);
}
template <typename X>
void LiveCellRef<X>::save (ESM::ObjectState& state) const
{
saveImp (state);
}
template <typename X>
bool LiveCellRef<X>::checkState (const ESM::ObjectState& state)
{
return checkStateImp (state);
}
}
#endif

@ -25,6 +25,8 @@ namespace MWWorld
{
LiveCellRef<T> ref;
ref.mBase = instance;
ref.mRef.mRefNum.mIndex = 0;
ref.mRef.mRefNum.mContentFile = -1;
mRef = ref;
mPtr = Ptr (&boost::any_cast<LiveCellRef<T>&> (mRef), 0);
@ -64,8 +66,9 @@ namespace MWWorld
// initialise
ESM::CellRef& cellRef = mPtr.getCellRef();
cellRef.mRefID = Misc::StringUtils::lowerCase(name);
cellRef.mRefnum = -1;
cellRef.mRefID = Misc::StringUtils::lowerCase (name);
cellRef.mRefNum.mIndex = 0;
cellRef.mRefNum.mContentFile = -1;
cellRef.mScale = 1;
cellRef.mFactIndex = 0;
cellRef.mCharge = -1;

@ -1,6 +1,13 @@
#include "player.hpp"
#include <stdexcept>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/player.hpp>
#include <components/esm/defs.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
@ -34,9 +41,6 @@ namespace MWWorld
void Player::set(const ESM::NPC *player)
{
mPlayer.mBase = player;
float* playerPos = mPlayer.mData.getPosition().pos;
playerPos[0] = playerPos[1] = playerPos[2] = 0;
}
void Player::setCell (MWWorld::CellStore *cellStore)
@ -171,4 +175,102 @@ namespace MWWorld
if (mMarkedCell)
markedPosition = mMarkedPosition;
}
void Player::clear()
{
mCellStore = 0;
mSign.clear();
mMarkedCell = 0;
mAutoMove = false;
mForwardBackward = 0;
mTeleported = false;
}
void Player::write (ESM::ESMWriter& writer) const
{
ESM::Player player;
mPlayer.save (player.mObject);
player.mCellId = mCellStore->mCell->getCellId();
player.mBirthsign = mSign;
player.mLastKnownExteriorPosition[0] = mLastKnownExteriorPosition.x;
player.mLastKnownExteriorPosition[1] = mLastKnownExteriorPosition.y;
player.mLastKnownExteriorPosition[2] = mLastKnownExteriorPosition.z;
if (mMarkedCell)
{
player.mHasMark = true;
player.mMarkedPosition = mMarkedPosition;
player.mMarkedCell = mMarkedCell->mCell->getCellId();
}
else
player.mHasMark = false;
player.mAutoMove = mAutoMove ? 1 : 0;
writer.startRecord (ESM::REC_PLAY);
player.save (writer);
writer.endRecord (ESM::REC_PLAY);
}
bool Player::readRecord (ESM::ESMReader& reader, int32_t type)
{
if (type==ESM::REC_PLAY)
{
ESM::Player player;
player.load (reader);
if (!mPlayer.checkState (player.mObject))
{
// this is the one object we can not silently drop.
throw std::runtime_error ("invalid player state record (object state)");
}
mPlayer.load (player.mObject);
MWBase::World& world = *MWBase::Environment::get().getWorld();
mCellStore = world.getCell (player.mCellId);
if (!player.mBirthsign.empty() &&
!world.getStore().get<ESM::BirthSign>().search (player.mBirthsign))
throw std::runtime_error ("invalid player state record (birthsign)");
mSign = player.mBirthsign;
mLastKnownExteriorPosition.x = player.mLastKnownExteriorPosition[0];
mLastKnownExteriorPosition.y = player.mLastKnownExteriorPosition[1];
mLastKnownExteriorPosition.z = player.mLastKnownExteriorPosition[2];
if (player.mHasMark && !player.mMarkedCell.mPaged)
{
// interior cell -> need to check if it exists (exterior cell will be
// generated on the fly)
if (!world.getStore().get<ESM::Cell>().search (player.mMarkedCell.mWorldspace))
player.mHasMark = false; // drop mark silently
}
if (player.mHasMark)
{
mMarkedPosition = player.mMarkedPosition;
mMarkedCell = world.getCell (player.mMarkedCell);
}
else
{
mMarkedCell = 0;
}
mAutoMove = player.mAutoMove!=0;
mForwardBackward = 0;
mTeleported = false;
return true;
}
return false;
}
}

@ -11,6 +11,8 @@
namespace ESM
{
struct NPC;
class ESMWriter;
class ESMReader;
}
namespace MWBase
@ -86,6 +88,12 @@ namespace MWWorld
bool wasTeleported() const;
void setTeleported(bool teleported);
void clear();
void write (ESM::ESMWriter& writer) const;
bool readRecord (ESM::ESMReader& reader, int32_t type);
};
}
#endif

@ -3,6 +3,8 @@
#include <OgreSceneNode.h>
#include <components/esm/objectstate.hpp>
#include "customdata.hpp"
#include "cellstore.hpp"
@ -52,6 +54,14 @@ namespace MWWorld
mLocalRotation.rot[2]=0;
}
RefData::RefData (const ESM::ObjectState& objectState)
: mBaseNode (0), mHasLocals (false), mEnabled (objectState.mEnabled),
mCount (objectState.mCount), mPosition (objectState.mPosition), mCustomData (0)
{
for (int i=0; i<3; ++i)
mLocalRotation.rot[i] = objectState.mLocalRotation[i];
}
RefData::RefData (const RefData& refData)
: mBaseNode(0), mCustomData (0)
{
@ -66,6 +76,17 @@ namespace MWWorld
}
}
void RefData::write (ESM::ObjectState& objectState) const
{
objectState.mHasLocals = false;
objectState.mEnabled = mEnabled;
objectState.mCount = mCount;
objectState.mPosition = mPosition;
for (int i=0; i<3; ++i)
objectState.mLocalRotation[i] = mLocalRotation.rot[i];
}
RefData& RefData::operator= (const RefData& refData)
{
try

@ -14,6 +14,7 @@ namespace ESM
{
class Script;
class CellRef;
class ObjectState;
}
namespace MWWorld
@ -55,10 +56,18 @@ namespace MWWorld
/// to reset the position as the orignal data is still held in the CellRef
RefData (const ESM::CellRef& cellRef);
RefData (const ESM::ObjectState& objectState);
///< Ignores local variables and custom data (not enough context available here to
/// perform these operations).
RefData (const RefData& refData);
~RefData();
void write (ESM::ObjectState& objectState) const;
///< Ignores local variables and custom data (not enough context available here to
/// perform these operations).
RefData& operator= (const RefData& refData);
/// Return OGRE handle (may be empty).

@ -39,7 +39,7 @@ void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
// We should not need to test for duplicates, as this part of the code is pre-cell merge.
cell->mMovedRefs.push_back(cMRef);
// But there may be duplicates here!
ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum);
ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefNum);
if (iter == cellAlt->mLeasedRefs.end())
cellAlt->mLeasedRefs.push_back(ref);
else
@ -75,11 +75,11 @@ void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
// merge lists of leased references, use newer data in case of conflict
for (ESM::MovedCellRefTracker::const_iterator it = cell->mMovedRefs.begin(); it != cell->mMovedRefs.end(); ++it) {
// remove reference from current leased ref tracker and add it to new cell
ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefnum);
ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum);
if (itold != oldcell->mMovedRefs.end()) {
ESM::MovedCellRef target0 = *itold;
ESM::Cell *wipecell = const_cast<ESM::Cell*>(search(target0.mTarget[0], target0.mTarget[1]));
ESM::CellRefTracker::iterator it_lease = std::find(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), it->mRefnum);
ESM::CellRefTracker::iterator it_lease = std::find(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), it->mRefNum);
wipecell->mLeasedRefs.erase(it_lease);
*itold = *it;
}

@ -14,6 +14,7 @@
#include <components/bsa/bsa_archive.hpp>
#include <components/files/collections.hpp>
#include <components/compiler/locals.hpp>
#include <components/esm/cellid.hpp>
#include <boost/math/special_functions/sign.hpp>
@ -267,6 +268,7 @@ namespace MWWorld
void World::clear()
{
mLocalScripts.clear();
mPlayer->clear();
// enable collision
if (!mPhysics->toggleCollisionMode())
@ -274,15 +276,16 @@ namespace MWWorld
mWorldScene->changeToVoid();
mStore.clearDynamic();
mStore.setUp();
if (mPlayer)
{
mPlayer->setCell (0);
mPlayer->getPlayer().getRefData() = RefData();
mPlayer->set (mStore.get<ESM::NPC>().find ("player"));
}
mStore.clearDynamic();
mStore.setUp();
mCells.clear();
mProjectiles.clear();
@ -301,19 +304,25 @@ 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);
}
void World::readRecord (ESM::ESMReader& reader, int32_t type)
{
if (!mStore.readRecord (reader, type) &&
!mGlobalVariables.readRecord (reader, type))
!mGlobalVariables.readRecord (reader, type) &&
!mPlayer->readRecord (reader, type) &&
!mCells.readRecord (reader, type))
{
throw std::runtime_error ("unknown record in saved game");
}
@ -401,6 +410,14 @@ namespace MWWorld
return mCells.getInterior (name);
}
CellStore *World::getCell (const ESM::CellId& id)
{
if (id.mPaged)
return getExterior (id.mIndex.mX, id.mIndex.mY);
else
return getInterior (id.mWorldspace);
}
void World::useDeathCamera()
{
if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() )
@ -801,6 +818,14 @@ namespace MWWorld
addContainerScripts(getPlayer().getPlayer(), getPlayer().getPlayer().getCell());
}
void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position)
{
if (cellId.mPaged)
changeToExteriorCell (position);
else
changeToInteriorCell (cellId.mWorldspace, position);
}
void World::markCellAsUnchanged()
{
return mWorldScene->markCellAsUnchanged();
@ -1314,7 +1339,7 @@ namespace MWWorld
updateWindowManager ();
if (mPlayer->getPlayer().getCell()->isExterior())
if (!paused && mPlayer->getPlayer().getCell()->isExterior())
{
ESM::Position pos = mPlayer->getPlayer().getRefData().getPosition();
mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos));

@ -181,12 +181,14 @@ namespace MWWorld
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
virtual OEngine::Render::Fader* getFader();
///< \ŧodo remove this function. Rendering details should not be exposed.
///< \todo remove this function. Rendering details should not be exposed.
virtual CellStore *getExterior (int x, int y);
virtual CellStore *getInterior (const std::string& name);
virtual CellStore *getCell (const ESM::CellId& id);
//switch to POV before showing player's death animation
virtual void useDeathCamera();
@ -314,6 +316,8 @@ namespace MWWorld
virtual void changeToExteriorCell (const ESM::Position& position);
///< Move to exterior cell.
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position);
virtual const ESM::Cell *getExterior (const std::string& cellName) const;
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.

@ -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
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate
)
add_component_dir (misc

@ -0,0 +1,26 @@
#include "cellid.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
void ESM::CellId::load (ESMReader &esm)
{
mWorldspace = esm.getHNString ("SPAC");
if (esm.isNextSub ("CIDX"))
{
esm.getHT (mIndex, 8);
mPaged = true;
}
else
mPaged = false;
}
void ESM::CellId::save (ESMWriter &esm) const
{
esm.writeHNString ("SPAC", mWorldspace);
if (mPaged)
esm.writeHNT ("CIDX", mIndex, 8);
}

@ -0,0 +1,28 @@
#ifndef OPENMW_ESM_CELLID_H
#define OPENMW_ESM_CELLID_H
#include <string>
namespace ESM
{
class ESMReader;
class ESMWriter;
struct CellId
{
struct CellIndex
{
int mX;
int mY;
};
std::string mWorldspace;
CellIndex mIndex;
bool mPaged;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif

@ -1,11 +1,83 @@
#include "cellref.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
void ESM::CellRef::save(ESMWriter &esm) const
void ESM::CellRef::load (ESMReader& esm, bool wideRefNum)
{
esm.writeHNT("FRMR", mRefnum);
// NAM0 sometimes appears here, sometimes further on
mNam0 = 0;
if (esm.isNextSub ("NAM0"))
esm.getHT (mNam0);
if (wideRefNum)
esm.getHNT (mRefNum, "FRMR", 8);
else
esm.getHNT (mRefNum.mIndex, "FRMR");
mRefID = esm.getHNString ("NAME");
mScale = 1.0;
esm.getHNOT (mScale, "XSCL");
mOwner = esm.getHNOString ("ANAM");
mGlob = esm.getHNOString ("BNAM");
mSoul = esm.getHNOString ("XSOL");
mFaction = esm.getHNOString ("CNAM");
mFactIndex = -2;
esm.getHNOT (mFactIndex, "INDX");
mGoldValue = 1;
mCharge = -1;
mEnchantmentCharge = -1;
esm.getHNOT (mEnchantmentCharge, "XCHG");
esm.getHNOT (mCharge, "INTV");
esm.getHNOT (mGoldValue, "NAM9");
// Present for doors that teleport you to another cell.
if (esm.isNextSub ("DODT"))
{
mTeleport = true;
esm.getHT (mDoorDest);
mDestCell = esm.getHNOString ("DNAM");
}
else
mTeleport = false;
mLockLevel = -1;
esm.getHNOT (mLockLevel, "FLTV");
mKey = esm.getHNOString ("KNAM");
mTrap = esm.getHNOString ("TNAM");
mReferenceBlocked = -1;
mFltv = 0;
esm.getHNOT (mReferenceBlocked, "UNAM");
esm.getHNOT (mFltv, "FLTV");
esm.getHNOT(mPos, "DATA", 24);
// Number of references in the cell? Maximum once in each cell,
// but not always at the beginning, and not always right. In other
// words, completely useless.
// Update: Well, maybe not completely useless. This might actually be
// number_of_references + number_of_references_moved_here_Across_boundaries,
// and could be helpful for collecting these weird moved references.
if (esm.isNextSub ("NAM0"))
esm.getHT (mNam0);
}
void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum) const
{
if (wideRefNum)
esm.writeHNT ("FRMR", mRefNum, 8);
else
esm.writeHNT ("FRMR", mRefNum.mIndex, 4);
esm.writeHNCString("NAME", mRefID);
if (mScale != 1.0) {
@ -58,7 +130,8 @@ void ESM::CellRef::save(ESMWriter &esm) const
void ESM::CellRef::blank()
{
mRefnum = 0;
mRefNum.mIndex = 0;
mRefNum.mContentFile = -1;
mRefID.clear();
mScale = 1;
mOwner.clear();
@ -84,4 +157,9 @@ void ESM::CellRef::blank()
mPos.pos[i] = 0;
mPos.rot[i] = 0;
}
}
bool ESM::operator== (const CellRef::RefNum& left, const CellRef::RefNum& right)
{
return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile;
}

@ -8,6 +8,7 @@
namespace ESM
{
class ESMWriter;
class ESMReader;
/* Cell reference. This represents ONE object (of many) inside the
cell. The cell references are not loaded as part of the normal
@ -19,8 +20,14 @@ namespace ESM
{
public:
int mRefnum; // Reference number
std::string mRefID; // ID of object being referenced (must be lowercase)
struct RefNum
{
int mIndex;
int mContentFile; // -1 no content file
};
RefNum mRefNum; // Reference number
std::string mRefID; // ID of object being referenced
float mScale; // Scale applied to mesh
@ -80,10 +87,14 @@ namespace ESM
// Position and rotation of this object within the cell
Position mPos;
void save(ESMWriter &esm) const;
void load (ESMReader& esm, bool wideRefNum = false);
void save(ESMWriter &esm, bool wideRefNum = false) const;
void blank();
};
bool operator== (const CellRef::RefNum& left, const CellRef::RefNum& right);
}
#endif

@ -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,6 +88,9 @@ enum RecNameInts
REC_JOUR = 0x524f55a4,
REC_QUES = 0x53455551,
REC_GSCR = 0x52435347,
REC_PLAY = 0x59414c50,
REC_CSTA = 0x41545343,
REC_OBJE = 0x454a424f,
// format 1
REC_FILT = 0x544C4946

@ -3,26 +3,52 @@
#include <string>
#include <sstream>
#include <list>
#include <boost/concept_check.hpp>
#include <components/misc/stringops.hpp>
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
#include "cellid.hpp"
namespace ESM
namespace
{
unsigned int Cell::sRecordId = REC_CELL;
///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum
void adjustRefNum (ESM::CellRef::RefNum& refNum, ESM::ESMReader& reader)
{
int local = (refNum.mIndex & 0xff000000) >> 24;
/// Some overloaded compare operators.
bool operator==(const MovedCellRef& ref, int pRefnum)
{
return (ref.mRefnum == pRefnum);
if (local)
{
// If the most significant 8 bits are used, then this reference already exists.
// In this case, do not spawn a new reference, but overwrite the old one.
refNum.mIndex &= 0x00ffffff; // delete old plugin ID
refNum.mContentFile = reader.getGameFiles()[local-1].index;
}
else
{
// This is an addition by the present plugin. Set the corresponding plugin index.
refNum.mContentFile = reader.getIndex();
}
}
}
bool operator==(const CellRef& ref, int pRefnum)
namespace ESM
{
return (ref.mRefnum == pRefnum);
}
unsigned int Cell::sRecordId = REC_CELL;
// Some overloaded compare operators.
bool operator== (const MovedCellRef& ref, const CellRef::RefNum& refNum)
{
return ref.mRefNum == refNum;
}
bool operator== (const CellRef& ref, const CellRef::RefNum& refNum)
{
return ref.mRefNum == refNum;
}
void Cell::load(ESMReader &esm, bool saveContext)
@ -155,109 +181,10 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted)
// That should be it, I haven't seen any other fields yet.
}
// NAM0 sometimes appears here, sometimes further on
ref.mNam0 = 0;
if (esm.isNextSub("NAM0"))
{
esm.getHT(ref.mNam0);
//esm.getHNOT(NAM0, "NAM0");
}
esm.getHNT(ref.mRefnum, "FRMR");
ref.mRefID = esm.getHNString("NAME");
ref.load (esm);
// Identify references belonging to a parent file and adapt the ID accordingly.
int local = (ref.mRefnum & 0xff000000) >> 24;
size_t global = esm.getIndex() + 1;
if (local)
{
// If the most significant 8 bits are used, then this reference already exists.
// In this case, do not spawn a new reference, but overwrite the old one.
ref.mRefnum &= 0x00ffffff; // delete old plugin ID
const std::vector<Header::MasterData> &masters = esm.getGameFiles();
global = masters[local-1].index + 1;
ref.mRefnum |= global << 24; // insert global plugin ID
}
else
{
// This is an addition by the present plugin. Set the corresponding plugin index.
ref.mRefnum |= global << 24; // insert global plugin ID
}
// getHNOT will not change the existing value if the subrecord is
// missing
ref.mScale = 1.0;
esm.getHNOT(ref.mScale, "XSCL");
// TODO: support loading references from saves, there are tons of keys not recognized yet.
// The following is just an incomplete list.
if (esm.isNextSub("ACTN"))
esm.skipHSub();
if (esm.isNextSub("STPR"))
esm.skipHSub();
if (esm.isNextSub("ACDT"))
esm.skipHSub();
if (esm.isNextSub("ACSC"))
esm.skipHSub();
if (esm.isNextSub("ACSL"))
esm.skipHSub();
if (esm.isNextSub("CHRD"))
esm.skipHSub();
else if (esm.isNextSub("CRED")) // ???
esm.skipHSub();
ref.mOwner = esm.getHNOString("ANAM");
ref.mGlob = esm.getHNOString("BNAM");
ref.mSoul = esm.getHNOString("XSOL");
ref.mFaction = esm.getHNOString("CNAM");
ref.mFactIndex = -2;
esm.getHNOT(ref.mFactIndex, "INDX");
ref.mGoldValue = 1;
ref.mCharge = -1;
ref.mEnchantmentCharge = -1;
esm.getHNOT(ref.mEnchantmentCharge, "XCHG");
esm.getHNOT(ref.mCharge, "INTV");
esm.getHNOT(ref.mGoldValue, "NAM9");
// Present for doors that teleport you to another cell.
if (esm.isNextSub("DODT"))
{
ref.mTeleport = true;
esm.getHT(ref.mDoorDest);
ref.mDestCell = esm.getHNOString("DNAM");
} else {
ref.mTeleport = false;
}
// Integer, despite the name suggesting otherwise
ref.mLockLevel = -1;
esm.getHNOT(ref.mLockLevel, "FLTV");
ref.mKey = esm.getHNOString("KNAM");
ref.mTrap = esm.getHNOString("TNAM");
ref.mReferenceBlocked = -1;
ref.mFltv = 0;
esm.getHNOT(ref.mReferenceBlocked, "UNAM");
esm.getHNOT(ref.mFltv, "FLTV");
esm.getHNOT(ref.mPos, "DATA", 24);
// Number of references in the cell? Maximum once in each cell,
// but not always at the beginning, and not always right. In other
// words, completely useless.
// Update: Well, maybe not completely useless. This might actually be
// number_of_references + number_of_references_moved_here_Across_boundaries,
// and could be helpful for collecting these weird moved references.
if (esm.isNextSub("NAM0"))
{
esm.getHT(ref.mNam0);
//esm.getHNOT(NAM0, "NAM0");
}
adjustRefNum (ref.mRefNum, esm);
if (esm.isNextSub("DELE"))
{
@ -272,16 +199,10 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted)
bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)
{
esm.getHT(mref.mRefnum);
esm.getHT(mref.mRefNum.mIndex);
esm.getHNOT(mref.mTarget, "CNDT");
// Identify references belonging to a parent file and adapt the ID accordingly.
int local = (mref.mRefnum & 0xff000000) >> 24;
size_t global = esm.getIndex() + 1;
mref.mRefnum &= 0x00ffffff; // delete old plugin ID
const std::vector<Header::MasterData> &masters = esm.getGameFiles();
global = masters[local-1].index + 1;
mref.mRefnum |= global << 24; // insert global plugin ID
adjustRefNum (mref.mRefNum, esm);
return true;
}
@ -304,4 +225,24 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)
mAmbi.mFog = 0;
mAmbi.mFogDensity = 0;
}
CellId Cell::getCellId() const
{
CellId id;
id.mPaged = !(mData.mFlags & Interior);
if (id.mPaged)
{
id.mWorldspace = "default";
id.mIndex.mX = mData.mX;
id.mIndex.mY = mData.mY;
}
else
{
id.mWorldspace = Misc::StringUtils::lowerCase (mName);
}
return id;
}
}

@ -18,6 +18,7 @@ namespace ESM
{
class ESMReader;
class ESMWriter;
class CellId;
/* Moved cell reference tracking object. This mainly stores the target cell
of the reference, so we can easily know where it has been moved when another
@ -27,7 +28,7 @@ class ESMWriter;
class MovedCellRef
{
public:
int mRefnum;
CellRef::RefNum mRefNum;
// Target cell (if exterior)
int mTarget[2];
@ -37,9 +38,9 @@ public:
// introduces a henchman (which no one uses), so we may need this as well.
};
/// Overloaded copare operator used to search inside a list of cell refs.
bool operator==(const MovedCellRef& ref, int pRefnum);
bool operator==(const CellRef& ref, int pRefnum);
/// Overloaded compare operator used to search inside a list of cell refs.
bool operator==(const MovedCellRef& ref, const CellRef::RefNum& refNum);
bool operator==(const CellRef& ref, const CellRef::RefNum& refNum);
typedef std::list<MovedCellRef> MovedCellRefTracker;
typedef std::list<CellRef> CellRefTracker;
@ -150,6 +151,8 @@ struct Cell
void blank();
///< Set record to default state (does not touch the ID/index).
CellId getCellId() const;
};
}
#endif

@ -0,0 +1,47 @@
#include "objectstate.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
void ESM::ObjectState::load (ESMReader &esm)
{
mRef.load (esm, true);
mHasLocals = 0;
esm.getHNOT (mHasLocals, "HLOC");
if (mHasLocals)
mLocals.load (esm);
mEnabled = 1;
esm.getHNOT (mEnabled, "ENAB");
mCount = 1;
esm.getHNOT (mCount, "COUN");
esm.getHNT (mPosition, "POS_", 24);
esm.getHNT (mLocalRotation, "LROT", 12);
}
void ESM::ObjectState::save (ESMWriter &esm) const
{
mRef.save (esm, true);
if (mHasLocals)
{
esm.writeHNT ("HLOC", mHasLocals);
mLocals.save (esm);
}
if (!mEnabled)
esm.writeHNT ("ENAB", mEnabled);
if (mCount!=1)
esm.writeHNT ("COUN", mCount);
esm.writeHNT ("POS_", mPosition, 24);
esm.writeHNT ("LROT", mLocalRotation, 12);
}

@ -0,0 +1,34 @@
#ifndef OPENMW_ESM_OBJECTSTATE_H
#define OPENMW_ESM_OBJECTSTATE_H
#include <string>
#include <vector>
#include "cellref.hpp"
#include "locals.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
///< \brief Save state for objects, that do not use custom data
struct ObjectState
{
CellRef mRef;
unsigned char mHasLocals;
Locals mLocals;
unsigned char mEnabled;
int mCount;
ESM::Position mPosition;
float mLocalRotation[3];
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif

@ -0,0 +1,48 @@
#include "player.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
void ESM::Player::load (ESMReader &esm)
{
mObject.load (esm);
mCellId.load (esm);
esm.getHNT (mLastKnownExteriorPosition, "LKEP", 12);
if (esm.isNextSub ("MARK"))
{
mHasMark = true;
esm.getHT (mMarkedPosition, 24);
mMarkedCell.load (esm);
}
else
mHasMark = false;
mAutoMove = 0;
esm.getHNOT (mAutoMove, "AMOV");
mBirthsign = esm.getHNString ("SIGN");
}
void ESM::Player::save (ESMWriter &esm) const
{
mObject.save (esm);
mCellId.save (esm);
esm.writeHNT ("LKEP", mLastKnownExteriorPosition, 12);
if (mHasMark)
{
esm.writeHNT ("MARK", mMarkedPosition, 24);
mMarkedCell.save (esm);
}
if (mAutoMove)
esm.writeHNT ("AMOV", mAutoMove);
esm.writeHNString ("SIGN", mBirthsign);
}

@ -0,0 +1,33 @@
#ifndef OPENMW_ESM_PLAYER_H
#define OPENMW_ESM_PLAYER_H
#include <string>
#include "objectstate.hpp"
#include "cellid.hpp"
#include "defs.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
struct Player
{
ObjectState mObject;
CellId mCellId;
float mLastKnownExteriorPosition[3];
unsigned char mHasMark;
ESM::Position mMarkedPosition;
CellId mMarkedCell;
unsigned char mAutoMove;
std::string mBirthsign;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif
Loading…
Cancel
Save