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.cpp
actorid
Marc Zinnschlag 11 years ago
commit 1b5301eec0

@ -236,7 +236,9 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
// Loop through all the references
ESM::CellRef ref;
if(!quiet) std::cout << " References:\n";
while(cell.getNextRef(esm, ref))
bool deleted = false;
while(cell.getNextRef(esm, ref, deleted))
{
if (save) {
info.data.mCellRefs[&cell].push_back(ref);
@ -244,13 +246,14 @@ 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";
std::cout << " Uses/health: '" << ref.mCharge << "'\n";
std::cout << " Gold value: '" << ref.mGoldValue << "'\n";
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
std::cout << " Deleted: " << deleted << std::endl;
}
}

@ -133,16 +133,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector<std::
state==CSMWorld::RecordBase::State_ModifiedOnly ||
infoModified)
{
// always write the topic record
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&topic.mModified.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().startRecord (topic.mModified.sRecordId);
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
topic.mModified.save (mState.getWriter());
mState.getWriter().endRecord (type);
mState.getWriter().endRecord (topic.mModified.sRecordId);
// write modified selected info records
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second;
@ -178,15 +172,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector<std::
next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1);
}
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&info.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().startRecord (info.sRecordId);
mState.getWriter().writeHNCString ("INAM", info.mId);
info.save (mState.getWriter());
mState.getWriter().endRecord (type);
mState.getWriter().endRecord (info.sRecordId);
}
}
}

@ -104,10 +104,10 @@ namespace CSMDoc
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&mCollection.getRecord (stage).mModified.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().startRecord (mCollection.getRecord (stage).mModified.sRecordId);
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
mCollection.getRecord (stage).mModified.save (mState.getWriter());
mState.getWriter().endRecord (type);
mState.getWriter().endRecord (mCollection.getRecord (stage).mModified.sRecordId);
}
else if (state==CSMWorld::RecordBase::State_Deleted)
{

@ -8,6 +8,5 @@ void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string
mId = id;
mCell = cell.mId;
if (!mDeleted)
cell.addRef (mId);
cell.addRef (mId);
}

@ -16,7 +16,8 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
CellRef ref;
while (cell2.getNextRef (reader, ref))
bool deleted = false;
while (cell2.getNextRef (reader, ref, deleted))
{
/// \todo handle deleted and moved references
ref.load (reader, cell2, getNewId());

@ -147,15 +147,10 @@ namespace CSMWorld
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
{
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&mContainer.at (index).mModified.sRecordId)[i];
writer.startRecord (type);
writer.startRecord (mContainer.at (index).mModified.sRecordId);
writer.writeHNCString ("NAME", getId (index));
mContainer.at (index).mModified.save (writer);
writer.endRecord (type);
writer.endRecord (mContainer.at (index).mModified.sRecordId);
}
else if (state==CSMWorld::RecordBase::State_Deleted)
{

@ -77,9 +77,13 @@ add_openmw_dir (mwmechanics
disease pickpocket levelledlist combat steering
)
add_openmw_dir (mwstate
statemanagerimp charactermanager character
)
add_openmw_dir (mwbase
environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager
inputmanager windowmanager
inputmanager windowmanager statemanager
)
# Main executable

@ -7,6 +7,8 @@
#include <MyGUI_WidgetManager.h>
#include <SDL.h>
#include <components/compiler/extensions0.hpp>
#include <components/bsa/bsa_archive.hpp>
@ -41,8 +43,7 @@
#include "mwmechanics/mechanicsmanagerimp.hpp"
#include <SDL.h>
#include "mwstate/statemanagerimp.hpp"
void OMW::Engine::executeLocalScripts()
{
@ -88,31 +89,47 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
if (mUseSound)
MWBase::Environment::get().getSoundManager()->update(frametime);
// global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running)
{
// global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
bool changed = MWBase::Environment::get().getWorld()->hasCellChanged();
bool changed = MWBase::Environment::get().getWorld()->hasCellChanged();
// local scripts
executeLocalScripts(); // This does not handle the case where a global script causes a
// cell change, followed by a cell change in a local script during
// the same frame.
// local scripts
executeLocalScripts(); // This does not handle the case where a global script causes a cell
// change, followed by a cell change in a local script during the same
// frame.
if (changed) // keep change flag for another frame, if cell changed happened in local script
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
// passing of time
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
if (!paused)
MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
// update game state
MWBase::Environment::get().getStateManager()->update (frametime);
}
if (changed) // keep change flag for another frame, if cell changed happend in local script
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
// update actors
MWBase::Environment::get().getMechanicsManager()->update(frametime,
MWBase::Environment::get().getWindowManager()->isGuiMode());
paused);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running)
{
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
if(!paused && player.getClass().getCreatureStats(player).isDead())
MWBase::Environment::get().getStateManager()->endGame();
}
// update world
MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode());
MWBase::Environment::get().getWorld()->update(frametime, paused);
// update GUI
Ogre::RenderWindow* window = mOgre->getWindow();
@ -135,7 +152,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mOgre (0)
, mFpsLevel(0)
, mVerboseScripts (false)
, mNewGame (false)
, mSkipMenu (false)
, mUseSound (true)
, mCompileAll (false)
, mScriptContext (0)
@ -265,12 +282,7 @@ void OMW::Engine::setCell (const std::string& cellName)
void OMW::Engine::addContentFile(const std::string& file)
{
if (file.find_last_of(".") == std::string::npos)
{
throw std::runtime_error("Missing extension in content file!");
}
mContentFiles.push_back(file);
mContentFiles.push_back(file);
}
void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
@ -278,9 +290,9 @@ void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
mVerboseScripts = scriptsVerbosity;
}
void OMW::Engine::setNewGame(bool newGame)
void OMW::Engine::setSkipMenu (bool skipMenu)
{
mNewGame = newGame;
mSkipMenu = skipMenu;
}
std::string OMW::Engine::loadSettings (Settings::Manager & settings)
@ -326,6 +338,9 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
void OMW::Engine::prepareEngine (Settings::Manager & settings)
{
mEnvironment.setStateManager (
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0)));
Nif::NIFFile::CacheLock cachelock;
std::string renderSystem = settings.getString("render system", "Video");
@ -392,10 +407,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
window->initUI();
if (mNewGame)
// still redundant work here: recreate CharacterCreation(),
// double update visibility etc.
window->setNewGame(true);
window->renderWorldMap();
//Load translation data
@ -403,7 +414,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
for (size_t i = 0; i < mContentFiles.size(); i++)
mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]);
Compiler::registerExtensions (mExtensions);
Compiler::registerExtensions (mExtensions);
// Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound));
@ -427,12 +438,12 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mechanics->buildPlayer();
window->updatePlayer();
if (!mNewGame)
{
// load cell
ESM::Position pos;
MWBase::World *world = MWBase::Environment::get().getWorld();
// load cell
ESM::Position pos;
MWBase::World *world = MWBase::Environment::get().getWorld();
if (!mCellName.empty())
{
if (world->findExteriorPosition(mCellName, pos)) {
world->changeToExteriorCell (pos);
}
@ -442,7 +453,11 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
}
}
else
mEnvironment.getWorld()->startNewGame();
{
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.pos[2] = 0;
world->changeToExteriorCell (pos);
}
Ogre::FrameEvent event;
event.timeSinceLastEvent = 0;
@ -468,7 +483,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
void OMW::Engine::go()
{
assert (!mCellName.empty());
assert (!mContentFiles.empty());
assert (!mOgre);
@ -489,8 +503,14 @@ void OMW::Engine::go()
if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
// start in main menu
if (!mSkipMenu)
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
else
MWBase::Environment::get().getStateManager()->newGame (true);
// Start the main rendering loop
while (!mEnvironment.getRequestExit())
while (!mEnvironment.get().getStateManager()->hasQuitRequest())
Ogre::Root::getSingleton().renderOneFrame();
// Save user settings

@ -71,7 +71,7 @@ namespace OMW
std::vector<std::string> mContentFiles;
int mFpsLevel;
bool mVerboseScripts;
bool mNewGame;
bool mSkipMenu;
bool mUseSound;
bool mCompileAll;
std::string mFocusName;
@ -151,8 +151,7 @@ namespace OMW
/// Disable or enable all sounds
void setSoundUsage(bool soundUsage);
/// Start as a new game.
void setNewGame(bool newGame);
void setSkipMenu (bool skipMenu);
void setGrabMouse(bool grab) { mGrab = grab; }

@ -116,7 +116,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("resources", bpo::value<std::string>()->default_value("resources"),
"set resources directory")
("start", bpo::value<std::string>()->default_value("Beshara"),
("start", bpo::value<std::string>()->default_value(""),
"set initial cell")
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
@ -137,8 +137,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("script-run", bpo::value<std::string>()->default_value(""),
"select a file containing a list of console commands that is executed on startup")
("new-game", bpo::value<bool>()->implicit_value(true)
->default_value(false), "activate char gen/new game mechanics")
("skip-menu", bpo::value<bool>()->implicit_value(true)
->default_value(false), "skip main menu on game startup")
("fs-strict", bpo::value<bool>()->implicit_value(true)
->default_value(false), "strict file system handling (no case folding)")
@ -232,7 +232,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
// startup-settings
engine.setCell(variables["start"].as<std::string>());
engine.setNewGame(variables["new-game"].as<bool>());
engine.setSkipMenu (variables["skip-menu"].as<bool>());
// other settings
engine.setSoundUsage(!variables["no-sound"].as<bool>());

@ -11,13 +11,14 @@
#include "mechanicsmanager.hpp"
#include "inputmanager.hpp"
#include "windowmanager.hpp"
#include "statemanager.hpp"
MWBase::Environment *MWBase::Environment::sThis = 0;
bool MWBase::Environment::sExit = false;
MWBase::Environment::Environment()
: mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0),
mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mFrameDuration (0)
mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mFrameDuration (0),
mStateManager (0)
{
assert (!sThis);
sThis = this;
@ -69,6 +70,11 @@ void MWBase::Environment::setInputManager (InputManager *inputManager)
mInputManager = inputManager;
}
void MWBase::Environment::setStateManager (StateManager *stateManager)
{
mStateManager = stateManager;
}
void MWBase::Environment::setFrameDuration (float duration)
{
mFrameDuration = duration;
@ -122,6 +128,12 @@ MWBase::InputManager *MWBase::Environment::getInputManager() const
return mInputManager;
}
MWBase::StateManager *MWBase::Environment::getStateManager() const
{
assert (mStateManager);
return mStateManager;
}
float MWBase::Environment::getFrameDuration() const
{
return mFrameDuration;
@ -152,6 +164,9 @@ void MWBase::Environment::cleanup()
delete mInputManager;
mInputManager = 0;
delete mStateManager;
mStateManager = 0;
}
const MWBase::Environment& MWBase::Environment::get()

@ -11,6 +11,7 @@ namespace MWBase
class MechanicsManager;
class InputManager;
class WindowManager;
class StateManager;
/// \brief Central hub for mw-subsystems
///
@ -30,10 +31,9 @@ namespace MWBase
DialogueManager *mDialogueManager;
Journal *mJournal;
InputManager *mInputManager;
StateManager *mStateManager;
float mFrameDuration;
static bool sExit;
Environment (const Environment&);
///< not implemented
@ -46,9 +46,6 @@ namespace MWBase
~Environment();
static void setRequestExit () { sExit = true; }
static bool getRequestExit () { return sExit; }
void setWorld (World *world);
void setSoundManager (SoundManager *soundManager);
@ -65,6 +62,8 @@ namespace MWBase
void setInputManager (InputManager *inputManager);
void setStateManager (StateManager *stateManager);
void setFrameDuration (float duration);
///< Set length of current frame in seconds.
@ -84,6 +83,8 @@ namespace MWBase
InputManager *getInputManager() const;
StateManager *getStateManager() const;
float getFrameDuration() const;
void cleanup();

@ -5,10 +5,18 @@
#include <deque>
#include <map>
#include <libs/platform/stdint.h>
#include "../mwdialogue/journalentry.hpp"
#include "../mwdialogue/topic.hpp"
#include "../mwdialogue/quest.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace MWBase
{
/// \brief Interface for the player's journal (implemented in MWDialogue)
@ -69,6 +77,12 @@ namespace MWBase
virtual TTopicIter topicEnd() const = 0;
///< Iterator pointing past the last topic.
virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
};
}

@ -136,33 +136,35 @@ namespace MWBase
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
///< Perform a persuasion action on NPC
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
///< Forces an object to refresh its animation state.
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
///< Forces an object to refresh its animation state.
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the scene should be ignored.
///
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
/// \param count How many times the animation should be run
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the scene should be ignored.
///
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
/// \param count How many times the animation should be run
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the scene should be ignored.
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the scene should be ignored.
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
/// paused we may want to do it manually (after equipping permanent enchantment)
virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0;
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
/// paused we may want to do it manually (after equipping permanent enchantment)
virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0;
virtual void toggleAI() = 0;
virtual bool isAIActive() = 0;
virtual void toggleAI() = 0;
virtual bool isAIActive() = 0;
virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects) = 0;
///return the list of actors which are following the given actor (ie AiFollow is active and the target is the actor)
virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) = 0;
virtual void playerLoaded() = 0;
};
}

@ -35,8 +35,6 @@ namespace MWBase
virtual ~ScriptManager() {}
virtual void resetGlobalScripts() = 0;
virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
///< Run the script with the given name (compile first, if not compiled yet)

@ -149,6 +149,8 @@ namespace MWBase
virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0;
virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated) = 0;
virtual void clear() = 0;
};
}

@ -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

@ -33,6 +33,8 @@ namespace OEngine
namespace ESM
{
struct Class;
class ESMReader;
class ESMWriter;
}
namespace MWWorld
@ -285,6 +287,12 @@ namespace MWBase
/// Should the cursor be visible?
virtual bool getCursorVisible() = 0;
/// Clear all savegame-specific data
virtual void clear() = 0;
virtual void write (ESM::ESMWriter& writer) = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
};
}

@ -2,6 +2,7 @@
#define GAME_MWBASE_WORLD_H
#include <vector>
#include <map>
#include <components/settings/settings.hpp>
@ -30,12 +31,14 @@ namespace OEngine
namespace ESM
{
class ESMReader;
class ESMWriter;
struct Position;
struct Cell;
struct Class;
struct Potion;
struct Spell;
struct NPC;
struct CellId;
}
namespace MWRender
@ -94,13 +97,26 @@ namespace MWBase
virtual void startNewGame() = 0;
virtual void clear() = 0;
virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type,
const std::map<int, int>& contentFileMap) = 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;
virtual void toggleWater() = 0;
@ -139,16 +155,26 @@ namespace MWBase
virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior) = 0;
///< see MWRender::LocalMap::isPositionExplored
virtual MWWorld::Globals::Data& getGlobalVariable (const std::string& name) = 0;
virtual void setGlobalInt (const std::string& name, int value) = 0;
///< Set value independently from real type.
virtual MWWorld::Globals::Data getGlobalVariable (const std::string& name) const = 0;
virtual void setGlobalFloat (const std::string& name, float value) = 0;
///< Set value independently from real type.
virtual int getGlobalInt (const std::string& name) const = 0;
///< Get value independently from real type.
virtual float getGlobalFloat (const std::string& name) const = 0;
///< Get value independently from real type.
virtual char getGlobalVariableType (const std::string& name) const = 0;
///< Return ' ', if there is no global variable with this name.
virtual std::vector<std::string> getGlobals () const = 0;
virtual std::string getCurrentCellName() const = 0;
virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const = 0;
///< Return name of the cell.
///
/// \note If cell==0, the cell the player is currently in will be used instead to
/// generate a name.
virtual void removeRefScript (MWWorld::RefData *ref) = 0;
//< Remove the script attached to ref from mLocalScripts
@ -185,8 +211,12 @@ namespace MWBase
virtual void setDay (int day) = 0;
///< Set in-game time day.
virtual int getDay() = 0;
virtual int getMonth() = 0;
virtual int getDay() const = 0;
virtual int getMonth() const = 0;
virtual int getYear() const = 0;
virtual std::string getMonthName (int month = -1) const = 0;
///< Return name of month (-1: current month)
virtual MWWorld::TimeStamp getTimeStamp() const = 0;
///< Return current in-game time stamp.
@ -215,6 +245,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.
@ -336,7 +368,7 @@ namespace MWBase
virtual bool isSwimming(const MWWorld::Ptr &object) const = 0;
///Is the head of the creature underwater?
virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0;
virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0;
virtual bool isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const = 0;
virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;
virtual void togglePOV() = 0;
@ -384,6 +416,7 @@ namespace MWBase
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
virtual void stopVideo() = 0;
virtual void frameStarted (float dt, bool paused) = 0;
virtual void screenshot (Ogre::Image& image, int w, int h) = 0;
/// Find default position inside exterior cell specified by name
/// \return false if exterior with given name not exists, true otherwise
@ -428,6 +461,8 @@ namespace MWBase
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
virtual const std::vector<std::string>& getContentFiles() const = 0;
virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0;
// Are we in an exterior or pseudo-exterior cell and it's night?

@ -2,6 +2,7 @@
#include "container.hpp"
#include <components/esm/loadcont.hpp>
#include <components/esm/containerstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -258,4 +259,26 @@ namespace MWClass
return MWWorld::Ptr(&cell.mContainers.insert(*ref), &cell);
}
void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
readState (state2.mInventory);
}
void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::ContainerState& state2 = dynamic_cast<ESM::ContainerState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
writeState (state2.mInventory);
}
}

@ -54,6 +54,14 @@ namespace MWClass
virtual void unlock (const MWWorld::Ptr& ptr) const;
///< Unlock object
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const;

@ -2,6 +2,7 @@
#include "creature.hpp"
#include <components/esm/loadcrea.hpp>
#include <components/esm/creaturestate.hpp>
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magiceffects.hpp"
@ -758,6 +759,28 @@ namespace MWClass
return 0;
}
void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore->
readState (state2.mInventory);
}
void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::CreatureState& state2 = dynamic_cast<ESM::CreatureState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore->
writeState (state2.mInventory);
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect;

@ -91,7 +91,7 @@ namespace MWClass
virtual bool isEssential (const MWWorld::Ptr& ptr) const;
///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable)
virtual int getServices (const MWWorld::Ptr& actor) const;
virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
@ -125,6 +125,14 @@ namespace MWClass
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
};
}

@ -2,6 +2,7 @@
#include "light.hpp"
#include <components/esm/loadligh.hpp>
#include <components/esm/lightstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -269,4 +270,24 @@ namespace MWClass
}
return std::make_pair(1,"");
}
void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::LightState& state2 = dynamic_cast<const ESM::LightState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime = state2.mTime;
}
void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::LightState& state2 = dynamic_cast<ESM::LightState&> (state);
ensureCustomData (ptr);
state2.mTime = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime;
}
}

@ -71,6 +71,14 @@ namespace MWClass
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
std::pair<int, std::string> canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
};
}

@ -7,6 +7,7 @@
#include <components/esm/loadmgef.hpp>
#include <components/esm/loadnpc.hpp>
#include <components/esm/npcstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -536,7 +537,7 @@ namespace MWClass
weapon.getCellRef().mCharge = weapmaxhealth;
damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth;
}
if (!MWBase::Environment::get().getWorld()->getGodModeState())
weapon.getCellRef().mCharge -= std::min(std::max(1,
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge);
@ -1001,7 +1002,7 @@ namespace MWClass
return ref->mBase->mFlags & ESM::NPC::Essential;
}
void Npc::registerSelf()
{
boost::shared_ptr<Class> instance (new Npc);
@ -1270,6 +1271,28 @@ namespace MWClass
return 0;
}
void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore.
readState (state2.mInventory);
}
void Npc::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::NpcState& state2 = dynamic_cast<ESM::NpcState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore.
writeState (state2.mInventory);
}
const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect;

@ -137,7 +137,7 @@ namespace MWClass
///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable)
virtual int getServices (const MWWorld::Ptr& actor) const;
virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const;
@ -158,6 +158,14 @@ namespace MWClass
virtual bool isNpc() const {
return true;
}
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
};
}

@ -171,7 +171,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
// internally all globals are float :(
return select.selectCompare (
MWBase::Environment::get().getWorld()->getGlobalVariable (select.getName()).mFloat);
MWBase::Environment::get().getWorld()->getGlobalFloat (select.getName()));
case SelectWrapper::Function_Local:
{

@ -3,6 +3,8 @@
#include <stdexcept>
#include <components/esm/journalentry.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -10,23 +12,54 @@
namespace MWDialogue
{
JournalEntry::JournalEntry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId)
: mTopic (topic), mInfoId (infoId)
{}
Entry::Entry() {}
std::string JournalEntry::getText (const MWWorld::ESMStore& store) const
Entry::Entry (const std::string& topic, const std::string& infoId)
: mInfoId (infoId)
{
const ESM::Dialogue *dialogue =
store.get<ESM::Dialogue>().find (mTopic);
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic);
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == mInfoId)
return iter->mResponse;
{
/// \todo text replacement
mText = iter->mResponse;
return;
}
throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + topic);
}
Entry::Entry (const ESM::JournalEntry& record) : mInfoId (record.mInfo), mText (record.mText) {}
std::string Entry::getText() const
{
return mText;
}
void Entry::write (ESM::JournalEntry& entry) const
{
entry.mInfo = mInfoId;
entry.mText = mText;
}
JournalEntry::JournalEntry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId)
: Entry (topic, infoId), mTopic (topic)
{}
JournalEntry::JournalEntry (const ESM::JournalEntry& record)
: Entry (record), mTopic (record.mTopic)
{}
throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic);
void JournalEntry::write (ESM::JournalEntry& entry) const
{
Entry::write (entry);
entry.mTopic = mTopic;
}
JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index)
@ -49,6 +82,7 @@ namespace MWDialogue
throw std::runtime_error ("unknown journal index for topic " + topic);
}
StampedJournalEntry::StampedJournalEntry()
: mDay (0), mMonth (0), mDayOfMonth (0)
{}
@ -58,11 +92,24 @@ namespace MWDialogue
: JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth)
{}
StampedJournalEntry::StampedJournalEntry (const ESM::JournalEntry& record)
: JournalEntry (record), mDay (record.mDay), mMonth (record.mMonth),
mDayOfMonth (record.mDayOfMonth)
{}
void StampedJournalEntry::write (ESM::JournalEntry& entry) const
{
JournalEntry::write (entry);
entry.mDay = mDay;
entry.mMonth = mMonth;
entry.mDayOfMonth = mDayOfMonth;
}
StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index)
{
int day = MWBase::Environment::get().getWorld()->getGlobalVariable ("dayspassed").mLong;
int month = MWBase::Environment::get().getWorld()->getGlobalVariable ("month").mLong;
int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalVariable ("day").mLong;
int day = MWBase::Environment::get().getWorld()->getGlobalInt ("dayspassed");
int month = MWBase::Environment::get().getWorld()->getGlobalInt ("month");
int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalInt ("day");
return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth);
}

@ -3,24 +3,44 @@
#include <string>
namespace MWWorld
namespace ESM
{
struct ESMStore;
struct JournalEntry;
}
namespace MWDialogue
{
/// \brief A quest or dialogue entry
struct JournalEntry
/// \brief Basic quest/dialogue/topic entry
struct Entry
{
std::string mTopic;
std::string mInfoId;
std::string mText;
Entry();
Entry (const std::string& topic, const std::string& infoId);
Entry (const ESM::JournalEntry& record);
std::string getText() const;
void write (ESM::JournalEntry& entry) const;
};
/// \brief A dialogue entry
///
/// Same as entry, but store TopicID
struct JournalEntry : public Entry
{
std::string mTopic;
JournalEntry();
JournalEntry (const std::string& topic, const std::string& infoId);
std::string getText (const MWWorld::ESMStore& store) const;
JournalEntry (const ESM::JournalEntry& record);
void write (ESM::JournalEntry& entry) const;
static JournalEntry makeFromQuest (const std::string& topic, int index);
@ -39,6 +59,10 @@ namespace MWDialogue
StampedJournalEntry (const std::string& topic, const std::string& infoId,
int day, int month, int dayOfMonth);
StampedJournalEntry (const ESM::JournalEntry& record);
void write (ESM::JournalEntry& entry) const;
static StampedJournalEntry makeFromQuest (const std::string& topic, int index);
};
}

@ -1,6 +1,13 @@
#include "journalimp.hpp"
#include <iterator>
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/queststate.hpp>
#include <components/esm/journalentry.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
@ -26,6 +33,38 @@ namespace MWDialogue
return iter->second;
}
Topic& Journal::getTopic (const std::string& id)
{
TTopicContainer::iterator iter = mTopics.find (id);
if (iter==mTopics.end())
{
std::pair<TTopicContainer::iterator, bool> result
= mTopics.insert (std::make_pair (id, Topic (id)));
iter = result.first;
}
return iter->second;
}
bool Journal::isThere (const std::string& topicId, const std::string& infoId) const
{
if (const ESM::Dialogue *dialogue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().search (topicId))
{
if (infoId.empty())
return true;
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == infoId)
return true;
}
return false;
}
Journal::Journal()
{}
@ -66,17 +105,9 @@ namespace MWDialogue
void Journal::addTopic (const std::string& topicId, const std::string& infoId)
{
TTopicContainer::iterator iter = mTopics.find (topicId);
Topic& topic = getTopic (topicId);
if (iter==mTopics.end())
{
std::pair<TTopicContainer::iterator, bool> result
= mTopics.insert (std::make_pair (topicId, Topic (topicId)));
iter = result.first;
}
iter->second.addEntry (JournalEntry (topicId, infoId));
topic.addEntry (JournalEntry (topicId, infoId));
}
int Journal::getJournalIndex (const std::string& id) const
@ -118,4 +149,106 @@ namespace MWDialogue
{
return mTopics.end();
}
int Journal::countSavedGameRecords() const
{
int count = static_cast<int> (mQuests.size());
for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)
count += std::distance (iter->second.begin(), iter->second.end());
count += std::distance (mJournal.begin(), mJournal.end());
for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)
count += std::distance (iter->second.begin(), iter->second.end());
return count;
}
void Journal::write (ESM::ESMWriter& writer) const
{
for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)
{
const Quest& quest = iter->second;
ESM::QuestState state;
quest.write (state);
writer.startRecord (ESM::REC_QUES);
state.save (writer);
writer.endRecord (ESM::REC_QUES);
for (Topic::TEntryIter iter (quest.begin()); iter!=quest.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Quest;
entry.mTopic = quest.getTopic();
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
}
for (TEntryIter iter (mJournal.begin()); iter!=mJournal.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Journal;
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)
{
const Topic& topic = iter->second;
for (Topic::TEntryIter iter (topic.begin()); iter!=topic.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Topic;
entry.mTopic = topic.getTopic();
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
}
}
void Journal::readRecord (ESM::ESMReader& reader, int32_t type)
{
if (type==ESM::REC_JOUR)
{
ESM::JournalEntry record;
record.load (reader);
if (isThere (record.mTopic, record.mInfo))
switch (record.mType)
{
case ESM::JournalEntry::Type_Quest:
getQuest (record.mTopic).insertEntry (record);
break;
case ESM::JournalEntry::Type_Journal:
mJournal.push_back (record);
break;
case ESM::JournalEntry::Type_Topic:
getTopic (record.mTopic).insertEntry (record);
break;
}
}
else if (type==ESM::REC_QUES)
{
ESM::QuestState record;
record.load (reader);
if (isThere (record.mTopic))
mQuests.insert (std::make_pair (record.mTopic, record));
}
}
}

@ -15,8 +15,14 @@ namespace MWDialogue
TQuestContainer mQuests;
TTopicContainer mTopics;
private:
Quest& getQuest (const std::string& id);
Topic& getTopic (const std::string& id);
bool isThere (const std::string& topicId, const std::string& infoId = "") const;
public:
Journal();
@ -55,6 +61,12 @@ namespace MWDialogue
virtual TTopicIter topicEnd() const;
///< Iterator pointing past the last topic.
virtual int countSavedGameRecords() const;
virtual void write (ESM::ESMWriter& writer) const;
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
};
}

@ -1,6 +1,8 @@
#include "quest.hpp"
#include <components/esm/queststate.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
@ -16,7 +18,11 @@ namespace MWDialogue
: Topic (topic), mIndex (0), mFinished (false)
{}
const std::string Quest::getName() const
Quest::Quest (const ESM::QuestState& state)
: Topic (state.mTopic), mIndex (state.mState), mFinished (state.mFinished!=0)
{}
std::string Quest::getName() const
{
const ESM::Dialogue *dialogue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (mTopic);
@ -86,9 +92,16 @@ namespace MWDialogue
setIndex (index);
for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter)
if (*iter==entry.mInfoId)
if (iter->mInfoId==entry.mInfoId)
return;
mEntries.push_back (entry.mInfoId);
mEntries.push_back (entry); // we want slicing here
}
void Quest::write (ESM::QuestState& state) const
{
state.mTopic = getTopic();
state.mState = mIndex;
state.mFinished = mFinished;
}
}

@ -3,9 +3,14 @@
#include "topic.hpp"
namespace ESM
{
struct QuestState;
}
namespace MWDialogue
{
/// \brief A quest in progress or a compelted quest
/// \brief A quest in progress or a completed quest
class Quest : public Topic
{
int mIndex;
@ -17,13 +22,15 @@ namespace MWDialogue
Quest (const std::string& topic);
const std::string getName() const;
Quest (const ESM::QuestState& state);
virtual std::string getName() const;
///< May be an empty string
int getIndex() const;
void setIndex (int index);
///< Calling this function with a non-existant index while throw an exception.
///< Calling this function with a non-existent index will throw an exception.
bool isFinished() const;
@ -31,6 +38,8 @@ namespace MWDialogue
///< Add entry and adjust index accordingly.
///
/// \note Redundant entries are ignored, but the index is still adjusted.
void write (ESM::QuestState& state) const;
};
}

@ -1,6 +1,9 @@
#include "topic.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp"
namespace MWDialogue
@ -9,7 +12,8 @@ namespace MWDialogue
{}
Topic::Topic (const std::string& topic)
: mTopic (topic)
: mTopic (topic), mName (
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic)->mId)
{}
Topic::~Topic()
@ -20,11 +24,22 @@ namespace MWDialogue
if (entry.mTopic!=mTopic)
throw std::runtime_error ("topic does not match: " + mTopic);
for (TEntryIter iter = begin(); iter!=end(); ++iter)
if (*iter==entry.mInfoId)
return;
mEntries.push_back (entry); // we want slicing here
}
void Topic::insertEntry (const ESM::JournalEntry& entry)
{
mEntries.push_back (entry);
}
mEntries.push_back (entry.mInfoId);
std::string Topic::getTopic() const
{
return mTopic;
}
std::string Topic::getName() const
{
return mName;
}
Topic::TEntryIter Topic::begin() const

@ -6,6 +6,11 @@
#include "journalentry.hpp"
namespace ESM
{
struct JournalEntry;
}
namespace MWDialogue
{
/// \brief Collection of seen responses for a topic
@ -13,13 +18,14 @@ namespace MWDialogue
{
public:
typedef std::vector<std::string> TEntryContainer;
typedef std::vector<Entry> TEntryContainer;
typedef TEntryContainer::const_iterator TEntryIter;
protected:
std::string mTopic;
TEntryContainer mEntries; // info-IDs
std::string mName;
TEntryContainer mEntries;
public:
@ -34,7 +40,13 @@ namespace MWDialogue
///
/// \note Redundant entries are ignored.
std::string const & getName () const { return mTopic; }
void insertEntry (const ESM::JournalEntry& entry);
///< Add entry without checking for redundant entries or modifying the state of the
/// topic otherwise
std::string getTopic() const;
virtual std::string getName() const;
TEntryIter begin() const;
///< Iterator pointing to the begin of the journal for this topic.

@ -196,34 +196,6 @@ book JournalBooks::createEmptyJournalBook ()
typesetter->lineBreak ();
typesetter->write (body, to_utf8_span ("You should have gone though the starting quest and got an initial quest."));
BookTypesetter::Style* big = typesetter->createStyle ("", MyGUI::Colour::Black);
BookTypesetter::Style* test = typesetter->createStyle ("MonoFont", MyGUI::Colour::Blue);
typesetter->sectionBreak (20);
typesetter->write (body, to_utf8_span (
"The layout engine doesn't currently support aligning fonts to "
"their baseline within a single line so the following text looks "
"funny. In order to properly implement it, a stupidly simple "
"change is needed in MyGUI to report the where the baseline is for "
"a particular font"
));
typesetter->sectionBreak (20);
typesetter->write (big, to_utf8_span ("big text g"));
typesetter->write (body, to_utf8_span (" проверяем только в дебаге"));
typesetter->write (body, to_utf8_span (" normal g"));
typesetter->write (big, to_utf8_span (" done g"));
typesetter->sectionBreak (20);
typesetter->write (test, to_utf8_span (
"int main (int argc,\n"
" char ** argv)\n"
"{\n"
" print (\"hello world!\\n\");\n"
" return 0;\n"
"}\n"
));
return typesetter->complete ();
}

@ -20,8 +20,6 @@ namespace MWGui {
struct JournalViewModelImpl;
static void injectMonthName (std::ostream & os, int month);
struct JournalViewModelImpl : JournalViewModel
{
typedef KeywordSearch <std::string, intptr_t> KeywordSearchT;
@ -237,21 +235,21 @@ struct JournalViewModelImpl : JournalViewModel
std::string getText () const
{
return itr->getText(MWBase::Environment::get().getWorld()->getStore());
return itr->getText();
}
Utf8Span timestamp () const
{
if (timestamp_buffer.empty ())
{
std::ostringstream os;
os << itr->mDayOfMonth << ' ';
std::string dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}");
injectMonthName (os, itr->mMonth);
std::ostringstream os;
const std::string& dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}");
os << " (" << dayStr << " " << (itr->mDay + 1) << ')';
os
<< itr->mDayOfMonth << ' '
<< MWBase::Environment::get().getWorld()->getMonthName (itr->mMonth)
<< " (" << dayStr << " " << (itr->mDay + 1) << ')';
timestamp_buffer = os.str ();
}
@ -272,7 +270,7 @@ struct JournalViewModelImpl : JournalViewModel
{
for (MWDialogue::Topic::TEntryIter j = quest->begin (); j != quest->end (); ++j)
{
if (i->mInfoId == *j)
if (i->mInfoId == j->mInfoId)
visitor (JournalEntryImpl <MWBase::Journal::TEntryIter> (this, i));
}
}
@ -292,9 +290,7 @@ struct JournalViewModelImpl : JournalViewModel
void visitTopicName (TopicId topicId, boost::function <void (Utf8Span)> visitor) const
{
MWDialogue::Topic const & topic = * reinterpret_cast <MWDialogue::Topic const *> (topicId);
// This is to get the correct case for the topic
const std::string& name = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find(topic.getName())->mId;
visitor (toUtf8Span (name));
visitor (toUtf8Span (topic.getName()));
}
void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const
@ -306,10 +302,7 @@ struct JournalViewModelImpl : JournalViewModel
if (i->first [0] != std::tolower (character, mLocale))
continue;
// This is to get the correct case for the topic
const std::string& name = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find(i->first)->mId;
visitor (TopicId (&i->second), toUtf8Span (name));
visitor (TopicId (&i->second), toUtf8Span (i->second.getName()));
}
}
@ -326,9 +319,7 @@ struct JournalViewModelImpl : JournalViewModel
std::string getText () const
{
/// \todo defines are not replaced (%PCName etc). should probably be done elsewhere though since we need the actor
return mTopic.getEntry (*itr).getText(MWBase::Environment::get().getWorld()->getStore());
return itr->getText();
}
Utf8Span source () const
@ -351,38 +342,6 @@ struct JournalViewModelImpl : JournalViewModel
}
};
static void injectMonthName (std::ostream & os, int month)
{
MyGUI::LanguageManager& lm = MyGUI::LanguageManager::getInstance();
if (month == 0)
os << lm.replaceTags ("#{sMonthMorningstar}");
else if (month == 1)
os << lm.replaceTags ("#{sMonthSunsdawn}");
else if (month == 2)
os << lm.replaceTags ("#{sMonthFirstseed}");
else if (month == 3)
os << lm.replaceTags ("#{sMonthRainshand}");
else if (month == 4)
os << lm.replaceTags ("#{sMonthSecondseed}");
else if (month == 5)
os << lm.replaceTags ("#{sMonthMidyear}");
else if (month == 6)
os << lm.replaceTags ("#{sMonthSunsheight}");
else if (month == 7)
os << lm.replaceTags ("#{sMonthLastseed}");
else if (month == 8)
os << lm.replaceTags ("#{sMonthHeartfire}");
else if (month == 9)
os << lm.replaceTags ("#{sMonthFrostfall}");
else if (month == 10)
os << lm.replaceTags ("#{sMonthSunsdusk}");
else if (month == 11)
os << lm.replaceTags ("#{sMonthEveningstar}");
else
os << month;
}
JournalViewModel::Ptr JournalViewModel::create ()
{
return boost::make_shared <JournalViewModelImpl> ();

@ -1,13 +1,14 @@
#include "mainmenu.hpp"
#include <OgreRoot.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwstate/character.hpp"
#include "savegamedialog.hpp"
@ -16,90 +17,132 @@ namespace MWGui
MainMenu::MainMenu(int w, int h)
: OEngine::GUI::Layout("openmw_mainmenu.layout")
, mButtonBox(0)
, mButtonBox(0), mWidth (w), mHeight (h)
, mSaveGameDialog(NULL)
{
updateMenu();
}
MainMenu::~MainMenu()
{
onResChange(w,h);
delete mSaveGameDialog;
}
void MainMenu::onResChange(int w, int h)
{
setCoord(0,0,w,h);
mWidth = w;
mHeight = h;
updateMenu();
}
void MainMenu::setVisible (bool visible)
{
if (visible)
updateMenu();
OEngine::GUI::Layout::setVisible (visible);
}
void MainMenu::onButtonClicked(MyGUI::Widget *sender)
{
std::string name = *sender->getUserData<std::string>();
MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
if (name == "return")
{
MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx);
MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu);
}
else if (name == "options")
MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings);
else if (name == "exitgame")
MWBase::Environment::get().getStateManager()->requestQuit();
else if (name == "newgame")
{
MWBase::Environment::get().getStateManager()->newGame();
}
else
{
if (!mSaveGameDialog)
mSaveGameDialog = new SaveGameDialog();
if (name == "loadgame")
mSaveGameDialog->setLoadOrSave(true);
else if (name == "savegame")
mSaveGameDialog->setLoadOrSave(false);
mSaveGameDialog->setVisible(true);
}
}
void MainMenu::updateMenu()
{
setCoord(0,0, mWidth, mHeight);
if (mButtonBox)
MyGUI::Gui::getInstance ().destroyWidget(mButtonBox);
if (!mButtonBox)
mButtonBox = mMainWidget->createWidget<MyGUI::Widget>("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default);
mButtonBox = mMainWidget->createWidget<MyGUI::Widget>("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default);
int curH = 0;
MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState();
std::vector<std::string> buttons;
buttons.push_back("return");
if (state==MWBase::StateManager::State_Running)
buttons.push_back("return");
buttons.push_back("newgame");
//buttons.push_back("loadgame");
//buttons.push_back("savegame");
if (MWBase::Environment::get().getStateManager()->characterBegin()!=
MWBase::Environment::get().getStateManager()->characterEnd())
buttons.push_back("loadgame");
if (state==MWBase::StateManager::State_Running &&
MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1)
buttons.push_back("savegame");
buttons.push_back("options");
//buttons.push_back("credits");
buttons.push_back("exitgame");
int maxwidth = 0;
mButtons.clear();
// Create new buttons if needed
for (std::vector<std::string>::iterator it = buttons.begin(); it != buttons.end(); ++it)
{
MWGui::ImageButton* button = mButtonBox->createWidget<MWGui::ImageButton>
("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default);
button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds");
button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds");
button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds");
MyGUI::IntSize requested = button->getRequestedSize();
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked);
mButtons[*it] = button;
curH += requested.height;
if (requested.width > maxwidth)
maxwidth = requested.width;
if (mButtons.find(*it) == mButtons.end())
{
MWGui::ImageButton* button = mButtonBox->createWidget<MWGui::ImageButton>
("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default);
button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds");
button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds");
button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds");
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked);
button->setUserData(std::string(*it));
mButtons[*it] = button;
}
}
// Start by hiding all buttons
int maxwidth = 0;
for (std::map<std::string, MWGui::ImageButton*>::iterator it = mButtons.begin(); it != mButtons.end(); ++it)
{
it->second->setVisible(false);
MyGUI::IntSize requested = it->second->getRequestedSize();
it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height);
if (requested.width > maxwidth)
maxwidth = requested.width;
}
mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH);
}
void MainMenu::onButtonClicked(MyGUI::Widget *sender)
{
MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
if (sender == mButtons["return"])
{
MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx);
MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu);
}
else if (sender == mButtons["options"])
MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings);
else if (sender == mButtons["exitgame"])
MWBase::Environment::get().setRequestExit();
else if (sender == mButtons["newgame"])
// Now show and position the ones we want
for (std::vector<std::string>::iterator it = buttons.begin(); it != buttons.end(); ++it)
{
MWBase::Environment::get().getWorld()->startNewGame();
MWBase::Environment::get().getWindowManager()->setNewGame(true);
MWBase::Environment::get().getDialogueManager()->clear();
MWBase::Environment::get().getJournal()->clear();
assert(mButtons.find(*it) != mButtons.end());
MWGui::ImageButton* button = mButtons[*it];
button->setVisible(true);
MyGUI::IntSize requested = button->getRequestedSize();
button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, requested.height);
curH += requested.height;
}
else if (sender == mButtons["loadgame"])
{
MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog();
dialog->setLoadOrSave(true);
dialog->setVisible(true);
}
else if (sender == mButtons["savegame"])
{
MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog();
dialog->setLoadOrSave(false);
dialog->setVisible(true);
}
}
mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH);
}
}

@ -5,19 +5,33 @@
namespace MWGui
{
class SaveGameDialog;
class MainMenu : public OEngine::GUI::Layout
{
public:
MainMenu(int w, int h);
int mWidth;
int mHeight;
public:
MainMenu(int w, int h);
~MainMenu();
void onResChange(int w, int h);
virtual void setVisible (bool visible);
private:
MyGUI::Widget* mButtonBox;
void onResChange(int w, int h);
std::map<std::string, MWGui::ImageButton*> mButtons;
private:
MyGUI::Widget* mButtonBox;
void onButtonClicked (MyGUI::Widget* sender);
std::map<std::string, MWGui::ImageButton*> mButtons;
void updateMenu();
void onButtonClicked (MyGUI::Widget* sender);
SaveGameDialog* mSaveGameDialog;
};
}

@ -435,7 +435,7 @@ namespace MWGui
static int _counter=0;
MyGUI::Button* markerWidget = mGlobalMapImage->createWidget<MyGUI::Button>("ButtonImage",
MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget<MyGUI::Button>("ButtonImage",
widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast<std::string>(_counter));
markerWidget->setImageResource("DoorMarker");
markerWidget->setUserString("ToolTipType", "Layout");
@ -500,10 +500,11 @@ namespace MWGui
mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
for (unsigned int i=0; i<mGlobalMapImage->getChildCount (); ++i)
// force markers to foreground
for (unsigned int i=0; i<mGlobalMapOverlay->getChildCount (); ++i)
{
if (mGlobalMapImage->getChildAt (i)->getName().substr(0,4) == "Door")
mGlobalMapImage->getChildAt (i)->castType<MyGUI::Button>()->setImageResource("DoorMarker");
if (mGlobalMapOverlay->getChildAt (i)->getName().substr(0,4) == "Door")
mGlobalMapOverlay->getChildAt (i)->castType<MyGUI::Button>()->setImageResource("DoorMarker");
}
globalMapUpdatePlayer();
@ -574,4 +575,31 @@ namespace MWGui
mGlobalMap->setViewOffset(viewoffs);
}
void MapWindow::clear()
{
mGlobalMapRender->clear();
while (mEventBoxGlobal->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mEventBoxGlobal->getChildAt(0));
while (mGlobalMapOverlay->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mGlobalMapOverlay->getChildAt(0));
}
void MapWindow::write(ESM::ESMWriter &writer)
{
mGlobalMapRender->write(writer);
}
void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type)
{
std::vector<std::pair<int, int> > exploredCells;
mGlobalMapRender->readRecord(reader, type, exploredCells);
for (std::vector<std::pair<int, int> >::iterator it = exploredCells.begin(); it != exploredCells.end(); ++it)
{
const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().search(it->first, it->second);
if (cell && !cell->mName.empty())
addVisitedLocation(cell->mName, it->first, it->second);
}
}
}

@ -8,6 +8,12 @@ namespace MWRender
class GlobalMap;
}
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace Loading
{
class Listener;
@ -94,6 +100,12 @@ namespace MWGui
void onFrame(float dt) { NoDrop::onFrame(dt); }
/// Clear all savegame-specific data
void clear();
void write (ESM::ESMWriter& writer);
void readRecord (ESM::ESMReader& reader, int32_t type);
private:
void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);

@ -16,7 +16,7 @@ namespace MWGui
void ReferenceInterface::checkReferenceAvailable()
{
MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
MWWorld::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
// check if player has changed cell, or count of the reference has become 0
if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL)

@ -1,12 +1,26 @@
#include "savegamedialog.hpp"
#include "widgets.hpp"
#include <OgreImage.h>
#include <OgreTextureManager.h>
#include <components/misc/stringops.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/statemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwstate/character.hpp"
namespace MWGui
{
SaveGameDialog::SaveGameDialog()
: WindowModal("openmw_savegame_dialog.layout")
, mSaving(true)
, mCurrentCharacter(NULL)
{
getWidget(mScreenshot, "Screenshot");
getWidget(mCharacterSelection, "SelectCharacter");
@ -18,21 +32,67 @@ namespace MWGui
getWidget(mSpacer, "Spacer");
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);
mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);
}
void SaveGameDialog::open()
{
WindowModal::open();
mSaveNameEdit->setCaption ("");
center();
MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();
if (mgr->characterBegin() == mgr->characterEnd())
return;
mCurrentCharacter = mgr->getCurrentCharacter (false);
std::string directory =
Misc::StringUtils::lowerCase (Settings::Manager::getString ("character", "Saves"));
mCharacterSelection->removeAllItems();
for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it)
{
if (it->begin()!=it->end())
{
std::stringstream title;
title << it->getSignature().mPlayerName;
title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClass << ")";
mCharacterSelection->addItem (title.str());
if (mCurrentCharacter == &*it ||
(!mCurrentCharacter && !mSaving && directory==Misc::StringUtils::lowerCase (
it->begin()->mPath.parent_path().filename().string())))
{
mCurrentCharacter = &*it;
mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1);
}
}
}
fillSaveList();
}
void SaveGameDialog::setLoadOrSave(bool load)
{
mSaving = !load;
mSaveNameEdit->setVisible(!load);
mCharacterSelection->setUserString("Hidden", load ? "false" : "true");
mCharacterSelection->setVisible(load);
mSpacer->setUserString("Hidden", load ? "false" : "true");
if (!load)
{
mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter (false);
}
center();
}
@ -43,7 +103,129 @@ namespace MWGui
void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender)
{
// Get the selected slot, if any
unsigned int i=0;
const MWState::Slot* slot = NULL;
if (mCurrentCharacter)
{
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it,++i)
{
if (i == mSaveList->getIndexSelected())
slot = &*it;
}
}
if (mSaving)
{
MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), slot);
}
else
{
if (mCurrentCharacter && slot)
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot);
}
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)
{
MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();
unsigned int i=0;
const MWState::Character* character = NULL;
for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it, ++i)
{
if (i == pos)
character = &*it;
}
assert(character && "Can't find selected character");
mCurrentCharacter = character;
fillSaveList();
}
void SaveGameDialog::fillSaveList()
{
mSaveList->removeAllItems();
if (!mCurrentCharacter)
return;
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it)
{
mSaveList->addItem(it->mProfile.mDescription);
}
onSlotSelected(mSaveList, MyGUI::ITEM_NONE);
}
void SaveGameDialog::onSlotSelected(MyGUI::ListBox *sender, size_t pos)
{
if (pos == MyGUI::ITEM_NONE)
{
mInfoText->setCaption("");
mScreenshot->setImageTexture("");
return;
}
const MWState::Slot* slot = NULL;
unsigned int i=0;
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i)
{
if (i == pos)
slot = &*it;
}
assert(slot && "Can't find selected slot");
std::stringstream text;
time_t time = slot->mTimeStamp;
struct tm* timeinfo;
timeinfo = localtime(&time);
text << asctime(timeinfo) << "\n";
text << "Level " << slot->mProfile.mPlayerLevel << "\n";
text << slot->mProfile.mPlayerCell << "\n";
// text << "Time played: " << slot->mProfile.mTimePlayed << "\n";
int hour = int(slot->mProfile.mInGameTime.mGameHour);
bool pm = hour >= 12;
if (hour >= 13) hour -= 12;
if (hour == 0) hour = 12;
text
<< slot->mProfile.mInGameTime.mDay << " "
<< MWBase::Environment::get().getWorld()->getMonthName(slot->mProfile.mInGameTime.mMonth)
<< " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}");
mInfoText->setCaptionWithReplacing(text.str());
// Decode screenshot
std::vector<char> data = slot->mProfile.mScreenshot; // MemoryDataStream doesn't work with const data :(
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
Ogre::Image image;
image.load(stream, "jpg");
const std::string textureName = "@savegame_screenshot";
Ogre::TexturePtr texture;
texture = Ogre::TextureManager::getSingleton().getByName(textureName);
mScreenshot->setImageTexture("");
if (texture.isNull())
{
texture = Ogre::TextureManager::getSingleton().createManual(textureName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
image.getWidth(), image.getHeight(), 0, Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY);
}
texture->unload();
texture->setWidth(image.getWidth());
texture->setHeight(image.getHeight());
texture->loadImage(image);
mScreenshot->setImageTexture(textureName);
}
}

@ -3,6 +3,11 @@
#include "windowbase.hpp"
namespace MWState
{
class Character;
}
namespace MWGui
{
@ -17,10 +22,15 @@ namespace MWGui
void onCancelButtonClicked (MyGUI::Widget* sender);
void onOkButtonClicked (MyGUI::Widget* sender);
void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos);
void onSlotSelected (MyGUI::ListBox* sender, size_t pos);
void fillSaveList();
private:
MyGUI::ImageBox* mScreenshot;
bool mSaving;
MyGUI::ComboBox* mCharacterSelection;
MyGUI::EditBox* mInfoText;
@ -30,6 +40,8 @@ namespace MWGui
MyGUI::EditBox* mSaveNameEdit;
MyGUI::Widget* mSpacer;
const MWState::Character* mCurrentCharacter;
};
}

@ -87,49 +87,7 @@ namespace MWGui
onHourSliderChangedPosition(mHourSlider, 0);
mHourSlider->setScrollPosition (0);
// http://www.uesp.net/wiki/Lore:Calendar
std::string month;
int m = MWBase::Environment::get().getWorld ()->getMonth ();
switch (m) {
case 0:
month = "#{sMonthMorningstar}";
break;
case 1:
month = "#{sMonthSunsdawn}";
break;
case 2:
month = "#{sMonthFirstseed}";
break;
case 3:
month = "#{sMonthRainshand}";
break;
case 4:
month = "#{sMonthSecondseed}";
break;
case 5:
month = "#{sMonthMidyear}";
break;
case 6:
month = "#{sMonthSunsheight}";
break;
case 7:
month = "#{sMonthLastseed}";
break;
case 8:
month = "#{sMonthHeartfire}";
break;
case 9:
month = "#{sMonthFrostfall}";
break;
case 10:
month = "#{sMonthSunsdusk}";
break;
case 11:
month = "#{sMonthEveningstar}";
break;
default:
break;
}
std::string month = MWBase::Environment::get().getWorld ()->getMonthName();
int hour = MWBase::Environment::get().getWorld ()->getTimeStamp ().getHour ();
bool pm = hour >= 12;
if (hour >= 13) hour -= 12;

@ -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"
@ -699,6 +700,10 @@ namespace MWGui
mToolTips->onFrame(frameDuration);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_NoGame)
return;
if (mDragAndDrop->mIsOnDragAndDrop)
{
assert(mDragAndDrop->mDraggedWidget);
@ -732,31 +737,20 @@ namespace MWGui
mCompanionWindow->onFrame();
}
void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell)
void WindowManager::changeCell(MWWorld::CellStore* cell)
{
std::string name = MWBase::Environment::get().getWorld()->getCellName (cell);
mMap->setCellName( name );
mHud->setCellName( name );
if (cell->mCell->isExterior())
{
std::string name;
if (cell->mCell->mName != "")
{
name = cell->mCell->mName;
if (!cell->mCell->mName.empty())
mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ());
}
else
{
const ESM::Region* region =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Region>().search(cell->mCell->mRegion);
if (region)
name = region->mName;
else
name = getGameSettingString("sDefaultCellname", "Wilderness");
}
mMap->cellExplored(cell->mCell->getGridX(), cell->mCell->getGridY());
mMap->setCellName( name );
mHud->setCellName( name );
mMap->setCellPrefix("Cell");
mHud->setCellPrefix("Cell");
mMap->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() );
@ -764,8 +758,6 @@ namespace MWGui
}
else
{
mMap->setCellName( cell->mCell->mName );
mHud->setCellName( cell->mCell->mName );
mMap->setCellPrefix( cell->mCell->mName );
mHud->setCellPrefix( cell->mCell->mName );
@ -776,7 +768,6 @@ namespace MWGui
MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos);
mMap->setGlobalMapPlayerPosition(worldPos.x, worldPos.y);
}
}
void WindowManager::setInteriorMapTexture(const int x, const int y)
@ -1239,7 +1230,7 @@ namespace MWGui
bool WindowManager::getRestEnabled()
{
//Enable rest dialogue if character creation finished
if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalVariable ("chargenstate").mFloat==-1)
if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1)
mRestAllowed=true;
return mRestAllowed;
}
@ -1392,4 +1383,19 @@ namespace MWGui
Settings::Manager::setFloat(setting + " h", "Windows", h);
}
void WindowManager::clear()
{
mMap->clear();
}
void WindowManager::write(ESM::ESMWriter &writer)
{
mMap->write(writer);
}
void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type)
{
mMap->readRecord(reader, type);
}
}

@ -280,6 +280,12 @@ namespace MWGui
virtual bool getCursorVisible();
/// Clear all savegame-specific data
virtual void clear();
virtual void write (ESM::ESMWriter& writer);
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
private:
bool mConsoleOnlyScripts;

@ -20,6 +20,7 @@
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
using namespace ICS;
@ -167,7 +168,9 @@ namespace MWInput
switch (action)
{
case A_GameMenu:
toggleMainMenu ();
if(!(MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running
&& MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu))
toggleMainMenu ();
break;
case A_Screenshot:
screenshot();
@ -280,7 +283,9 @@ namespace MWInput
return;
// Disable movement in Gui mode
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return;
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|| MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
return;
// Configure player movement according to keyboard input. Actual movement will
@ -613,7 +618,7 @@ namespace MWInput
void InputManager::windowClosed()
{
MWBase::Environment::setRequestExit();
MWBase::Environment::get().getStateManager()->requestQuit();
}
void InputManager::toggleMainMenu()

@ -757,7 +757,7 @@ namespace MWMechanics
}
}
void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore, const MWWorld::Ptr& ignore)
void Actors::dropActors (const MWWorld::CellStore *cellStore, const MWWorld::Ptr& ignore)
{
PtrControllerMap::iterator iter = mActors.begin();
while(iter != mActors.end())
@ -774,6 +774,24 @@ namespace MWMechanics
void Actors::update (float duration, bool paused)
{
if(!paused)
{
// Note: we need to do this before any of the animations are updated.
// Reaching the text keys may trigger Hit / Spellcast (and as such, particles),
// so updating VFX immediately after that would just remove the particle effects instantly.
// There needs to be a magic effect update in between.
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
iter->second->updateContinuousVfx();
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{
if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get(
ESM::MagicEffect::Paralyze).mMagnitude > 0)
iter->second->skipAnim();
iter->second->update(duration);
}
}
if (!paused)
{
for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
@ -806,65 +824,47 @@ namespace MWMechanics
if(iter->first.getRefData().getHandle()=="player" &&
MWBase::Environment::get().getWorld()->getGodModeState())
{
MWMechanics::DynamicStat<float> stat(stats.getHealth());
MWMechanics::DynamicStat<float> stat (stats.getHealth());
if(stat.getModified()<1)
if (stat.getModified()<1)
{
stat.setModified(1, 0);
stats.setHealth(stat);
}
stats.resurrect();
continue;
}
if(iter->second->isDead())
continue;
iter->second->kill();
// Apply soultrap
if (iter->first.getTypeName() == typeid(ESM::Creature).name())
{
SoulTrap soulTrap (iter->first);
stats.getActiveSpells().visitEffectSources(soulTrap);
}
// Reset magic effects and recalculate derived effects
// One case where we need this is to make sure bound items are removed upon death
stats.setMagicEffects(MWMechanics::MagicEffects());
calculateCreatureStatModifiers(iter->first, 0);
// Make sure spell effects with CasterLinked flag are removed
// TODO: would be nice not to do this all the time...
for(PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
{
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
spells.purge(iter->first.getRefData().getHandle());
}
++mDeathCount[cls.getId(iter->first)];
// FIXME: see http://bugs.openmw.org/issues/869
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
if(cls.isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
if (iter->second->kill())
{
++mDeathCount[cls.getId(iter->first)];
}
}
// Apply soultrap
if (iter->first.getTypeName() == typeid(ESM::Creature).name())
{
SoulTrap soulTrap (iter->first);
stats.getActiveSpells().visitEffectSources(soulTrap);
}
if(!paused)
{
// Note: we need to do this before any of the animations are updated.
// Reaching the text keys may trigger Hit / Spellcast (and as such, particles),
// so updating VFX immediately after that would just remove the particle effects instantly.
// There needs to be a magic effect update in between.
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
iter->second->updateContinuousVfx();
// Reset magic effects and recalculate derived effects
// One case where we need this is to make sure bound items are removed upon death
stats.setMagicEffects(MWMechanics::MagicEffects());
calculateCreatureStatModifiers(iter->first, 0);
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{
if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get(
ESM::MagicEffect::Paralyze).mMagnitude > 0)
iter->second->skipAnim();
iter->second->update(duration);
if(cls.isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
}
}
}
}

@ -32,6 +32,7 @@
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
@ -1135,7 +1136,6 @@ void CharacterController::update(float duration)
}
else if(cls.getCreatureStats(mPtr).isDead())
{
MWBase::Environment::get().getWorld()->enableActorCollision(mPtr, false);
world->queueMovement(mPtr, Ogre::Vector3(0.0f));
}
@ -1237,10 +1237,18 @@ void CharacterController::forceStateUpdate()
}
}
void CharacterController::kill()
bool CharacterController::kill()
{
if(mDeathState != CharState_None)
return;
if( isDead() )
{
//player's death animation is over
if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) )
{
MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0);
MWBase::Environment::get().getStateManager()->askLoadRecent();
}
return false;
}
playRandomDeath();
@ -1251,6 +1259,8 @@ void CharacterController::kill()
mIdleState = CharState_None;
mCurrentIdle.clear();
return true;
}
void CharacterController::resurrect()

@ -198,7 +198,7 @@ public:
void skipAnim();
bool isAnimPlaying(const std::string &groupName);
void kill();
bool kill();
void resurrect();
bool isDead() const
{ return mDeathState != CharState_None; }

@ -760,6 +760,14 @@ namespace MWMechanics
return mAI;
}
void MechanicsManager::playerLoaded()
{
mUpdatePlayer = true;
mClassSelected = true;
mRaceSelected = true;
mAI = true;
}
bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed)
{
MWWorld::Ptr victim;

@ -123,22 +123,24 @@ namespace MWMechanics
/// @return was it illegal, and someone saw you doing it?
virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed);
virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
virtual void skipAnimation(const MWWorld::Ptr& ptr);
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName);
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
virtual void skipAnimation(const MWWorld::Ptr& ptr);
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName);
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
/// paused we may want to do it manually (after equipping permanent enchantment)
virtual void updateMagicEffects (const MWWorld::Ptr& ptr);
virtual void toggleAI();
virtual bool isAIActive();
virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects);
virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects);
virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor);
virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor);
virtual void toggleAI();
virtual bool isAIActive();
virtual void playerLoaded();
};
}

@ -55,7 +55,7 @@ void Objects::updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
}
}
void Objects::dropObjects (const MWWorld::Ptr::CellStore *cellStore)
void Objects::dropObjects (const MWWorld::CellStore *cellStore)
{
PtrControllerMap::iterator iter = mObjects.begin();
while(iter != mObjects.end())

@ -127,7 +127,7 @@ bool Actors::deleteObject (const MWWorld::Ptr& ptr)
return true;
}
void Actors::removeCell(MWWorld::Ptr::CellStore* store)
void Actors::removeCell(MWWorld::CellStore* store)
{
for(PtrAnimationMap::iterator iter = mAllActors.begin();iter != mAllActors.end();)
{

@ -185,14 +185,14 @@ bool Debugging::toggleRenderMode (int mode){
return false;
}
void Debugging::cellAdded(MWWorld::Ptr::CellStore *store)
void Debugging::cellAdded(MWWorld::CellStore *store)
{
mActiveCells.push_back(store);
if (mPathgridEnabled)
enableCellPathgrid(store);
}
void Debugging::cellRemoved(MWWorld::Ptr::CellStore *store)
void Debugging::cellRemoved(MWWorld::CellStore *store)
{
mActiveCells.erase(std::remove(mActiveCells.begin(), mActiveCells.end(), store), mActiveCells.end());
if (mPathgridEnabled)
@ -227,7 +227,7 @@ void Debugging::togglePathgrid()
}
}
void Debugging::enableCellPathgrid(MWWorld::Ptr::CellStore *store)
void Debugging::enableCellPathgrid(MWWorld::CellStore *store)
{
const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*store->mCell);
@ -254,7 +254,7 @@ void Debugging::enableCellPathgrid(MWWorld::Ptr::CellStore *store)
}
}
void Debugging::disableCellPathgrid(MWWorld::Ptr::CellStore *store)
void Debugging::disableCellPathgrid(MWWorld::CellStore *store)
{
if (store->mCell->isExterior())
{

@ -12,6 +12,8 @@
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/esm/globalmap.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -60,8 +62,6 @@ namespace MWRender
loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1));
loadingListener->setProgress(0);
mExploredBuffer.resize((mMaxX-mMinX+1) * (mMaxY-mMinY+1) * 4);
//if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png"))
if (1)
{
@ -170,21 +170,10 @@ namespace MWRender
tex->load();
mOverlayTexture = Ogre::TextureManager::getSingleton().createManual("GlobalMapOverlay", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_A8B8G8R8, Ogre::TU_DYNAMIC_WRITE_ONLY);
std::vector<Ogre::uint32> buffer;
buffer.resize(mWidth * mHeight);
// initialize to (0, 0, 0, 0)
for (int p=0; p<mWidth * mHeight; ++p)
buffer[p] = 0;
memcpy(mOverlayTexture->getBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mWidth*mHeight*4);
mOverlayTexture->getBuffer()->unlock();
clear();
loadingListener->loadingOff();
}
@ -227,9 +216,114 @@ namespace MWRender
if (!localMapTexture.isNull())
{
mOverlayTexture->getBuffer()->blit(localMapTexture->getBuffer(), Ogre::Image::Box(0,0,512,512),
Ogre::Image::Box(originX,originY,originX+24,originY+24));
}
}
void GlobalMap::clear()
{
std::vector<Ogre::uint32> buffer;
// initialize to (0,0,0,0)
buffer.resize(mWidth * mHeight, 0);
Ogre::PixelBox pb(mWidth, mHeight, 1, Ogre::PF_A8B8G8R8, &buffer[0]);
mOverlayTexture->getBuffer()->blitFromMemory(pb);
}
void GlobalMap::write(ESM::ESMWriter &writer)
{
ESM::GlobalMap map;
map.mBounds.mMinX = mMinX;
map.mBounds.mMaxX = mMaxX;
map.mBounds.mMinY = mMinY;
map.mBounds.mMaxY = mMaxY;
Ogre::Image image;
mOverlayTexture->convertToImage(image);
Ogre::DataStreamPtr encoded = image.encode("png");
map.mImageData.resize(encoded->size());
encoded->read(&map.mImageData[0], encoded->size());
writer.startRecord(ESM::REC_GMAP);
map.save(writer);
writer.endRecord(ESM::REC_GMAP);
}
void GlobalMap::readRecord(ESM::ESMReader &reader, int32_t type, std::vector<std::pair<int, int> >& exploredCells)
{
if (type == ESM::REC_GMAP)
{
ESM::GlobalMap map;
map.load(reader);
const ESM::GlobalMap::Bounds& bounds = map.mBounds;
if (bounds.mMaxX-bounds.mMinX <= 0)
return;
if (bounds.mMaxY-bounds.mMinY <= 0)
return;
Ogre::Image image;
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&map.mImageData[0], map.mImageData.size()));
image.load(stream, "png");
int xLength = (bounds.mMaxX-bounds.mMinX+1);
int yLength = (bounds.mMaxY-bounds.mMinY+1);
// Size of one cell in image space
int cellImageSizeSrc = image.getWidth() / xLength;
if (int(image.getHeight() / yLength) != cellImageSizeSrc)
throw std::runtime_error("cell size must be quadratic");
// Determine which cells were explored by reading the image data
for (int x=0; x < xLength; ++x)
{
for (int y=0; y < yLength; ++y)
{
unsigned int imageX = (x) * cellImageSizeSrc;
// NB y + 1, because we want the top left corner, not bottom left where the origin of the cell is
unsigned int imageY = (yLength - (y + 1)) * cellImageSizeSrc;
assert(imageX < image.getWidth());
assert(imageY < image.getWidth());
if (image.getColourAt(imageX, imageY, 0).a > 0)
exploredCells.push_back(std::make_pair(x+bounds.mMinX,y+bounds.mMinY));
}
}
// If cell bounds of the currently loaded content and the loaded savegame do not match,
// we need to resize source/dest boxes to accommodate
// This means nonexisting cells will be dropped silently
int cellImageSizeDst = 24;
int leftDiff = (mMinX - bounds.mMinX);
int topDiff = (bounds.mMaxY - mMaxY);
int rightDiff = (bounds.mMaxX - mMaxX);
int bottomDiff = (mMinY - bounds.mMinY);
Ogre::Image::Box srcBox ( std::max(0, leftDiff * cellImageSizeSrc),
std::max(0, topDiff * cellImageSizeSrc),
std::min(image.getWidth(), image.getWidth() - rightDiff * cellImageSizeSrc),
std::min(image.getHeight(), image.getHeight() - bottomDiff * cellImageSizeSrc));
Ogre::Image::Box destBox ( std::max(0, -leftDiff * cellImageSizeDst),
std::max(0, -topDiff * cellImageSizeDst),
std::min(mOverlayTexture->getWidth(), mOverlayTexture->getWidth() + rightDiff * cellImageSizeDst),
std::min(mOverlayTexture->getHeight(), mOverlayTexture->getHeight() + bottomDiff * cellImageSizeDst));
// Looks like there is no interface for blitting from memory with src/dst boxes.
// So we create a temporary texture for blitting.
Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().createManual("@temp",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, image.getWidth(),
image.getHeight(), 0, Ogre::PF_A8B8G8R8);
tex->loadImage(image);
mOverlayTexture->getBuffer()->blit(tex->getBuffer(), srcBox, destBox);
Ogre::TextureManager::getSingleton().remove("@temp");
}
}
}

@ -10,6 +10,12 @@ namespace Loading
class Listener;
}
namespace ESM
{
class ESMWriter;
class ESMReader;
}
namespace MWRender
{
@ -31,13 +37,18 @@ namespace MWRender
void exploreCell (int cellX, int cellY);
/// Clears the overlay
void clear();
void write (ESM::ESMWriter& writer);
void readRecord (ESM::ESMReader& reader, int32_t type, std::vector<std::pair<int, int> >& exploredCells);
private:
std::string mCacheDir;
std::vector< std::pair<int,int> > mExploredCells;
Ogre::TexturePtr mOverlayTexture;
std::vector<Ogre::uchar> mExploredBuffer;
int mWidth;
int mHeight;

@ -79,7 +79,7 @@ std::string LocalMap::coordStr(const int x, const int y)
return StringConverter::toString(x) + "_" + StringConverter::toString(y);
}
void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell)
void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
{
if (!mInterior)
{
@ -108,7 +108,7 @@ void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell)
}
}
void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, float zMin, float zMax)
void LocalMap::requestMap(MWWorld::CellStore* cell, float zMin, float zMax)
{
mInterior = false;
@ -125,7 +125,7 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, float zMin, float zMax)
render((x+0.5)*sSize, (y+0.5)*sSize, zMin, zMax, sSize, sSize, name);
}
void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell,
void LocalMap::requestMap(MWWorld::CellStore* cell,
AxisAlignedBox bounds)
{
// if we're in an empty cell, don't bother rendering anything

@ -172,7 +172,7 @@ bool Objects::deleteObject (const MWWorld::Ptr& ptr)
}
void Objects::removeCell(MWWorld::Ptr::CellStore* store)
void Objects::removeCell(MWWorld::CellStore* store)
{
for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();)
{
@ -212,7 +212,7 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store)
}
}
void Objects::buildStaticGeometry(MWWorld::Ptr::CellStore& cell)
void Objects::buildStaticGeometry(MWWorld::CellStore& cell)
{
if(mStaticGeometry.find(&cell) != mStaticGeometry.end())
{
@ -226,7 +226,7 @@ void Objects::buildStaticGeometry(MWWorld::Ptr::CellStore& cell)
}
}
Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)
Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::CellStore* cell)
{
return mBounds[cell];
}

@ -29,6 +29,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp" // FIXME
#include "../mwbase/windowmanager.hpp" // FIXME
#include "../mwbase/statemanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
@ -217,7 +218,12 @@ OEngine::Render::Fader* RenderingManager::getFader()
return mRendering.getFader();
}
void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store)
MWRender::Camera* RenderingManager::getCamera() const
{
return mCamera;
}
void RenderingManager::removeCell (MWWorld::CellStore *store)
{
mObjects->removeCell(store);
mActors->removeCell(store);
@ -234,7 +240,7 @@ void RenderingManager::toggleWater()
mWater->toggle();
}
void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store)
void RenderingManager::cellAdded (MWWorld::CellStore *store)
{
mObjects->buildStaticGeometry (*store);
sh::Factory::getInstance().unloadUnreferencedMaterials();
@ -325,6 +331,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->getPlayerPtr();
@ -363,8 +375,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);
@ -410,7 +420,7 @@ void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt)
mOcclusionQuery->setActive(false);
}
void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store)
void RenderingManager::waterAdded (MWWorld::CellStore *store)
{
if(store->mCell->mData.mFlags & ESM::Cell::HasWater)
{
@ -488,7 +498,7 @@ bool RenderingManager::toggleRenderMode(int mode)
}
}
void RenderingManager::configureFog(MWWorld::Ptr::CellStore &mCell)
void RenderingManager::configureFog(MWWorld::CellStore &mCell)
{
Ogre::ColourValue color;
color.setAsABGR (mCell.mCell->mAmbi.mFog);
@ -541,7 +551,7 @@ void RenderingManager::setAmbientMode()
}
}
void RenderingManager::configureAmbient(MWWorld::Ptr::CellStore &mCell)
void RenderingManager::configureAmbient(MWWorld::CellStore &mCell)
{
if (mCell.mCell->mData.mFlags & ESM::Cell::Interior)
mAmbientColor.setAsABGR (mCell.mCell->mAmbi.mAmbient);
@ -638,7 +648,7 @@ void RenderingManager::setGlare(bool glare)
mSkyManager->setGlare(glare);
}
void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell)
void RenderingManager::requestMap(MWWorld::CellStore* cell)
{
if (cell->mCell->isExterior())
{
@ -657,7 +667,7 @@ void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell)
mLocalMap->requestMap(cell, mObjects->getDimensions(cell));
}
void RenderingManager::preCellChange(MWWorld::Ptr::CellStore* cell)
void RenderingManager::preCellChange(MWWorld::CellStore* cell)
{
mLocalMap->saveFogOfWar(cell);
}
@ -949,6 +959,38 @@ Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
return anim;
}
void RenderingManager::screenshot(Image &image, int w, int h)
{
// Create a temporary render target. We do not use the RenderWindow since we want a specific size.
// Also, the GUI should not be visible (and it is only rendered on the RenderWindow's primary viewport)
const std::string tempName = "@temp";
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual(tempName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, w, h, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
float oldAspect = mRendering.getCamera()->getAspectRatio();
mRendering.getCamera()->setAspectRatio(w / static_cast<float>(h));
Ogre::RenderTarget* rt = texture->getBuffer()->getRenderTarget();
Ogre::Viewport* vp = rt->addViewport(mRendering.getCamera());
vp->setBackgroundColour(mRendering.getViewport()->getBackgroundColour());
vp->setOverlaysEnabled(false);
vp->setVisibilityMask(mRendering.getViewport()->getVisibilityMask());
rt->update();
Ogre::PixelFormat pf = rt->suggestPixelFormat();
std::vector<Ogre::uchar> data;
data.resize(w * h * Ogre::PixelUtil::getNumElemBytes(pf));
Ogre::PixelBox pb(w, h, 1, pf, &data[0]);
rt->copyContentsToMemory(pb);
image.loadDynamicImage(&data[0], w, h, pf);
Ogre::TextureManager::getSingleton().remove(tempName);
mRendering.getCamera()->setAspectRatio(oldAspect);
}
void RenderingManager::playVideo(const std::string& name, bool allowSkipping)
{

@ -94,6 +94,8 @@ public:
SkyManager* getSkyManager();
MWRender::Camera* getCamera() const;
void toggleLight();
bool toggleRenderMode(int mode);
@ -206,6 +208,7 @@ public:
void playVideo(const std::string& name, bool allowSkipping);
void stopVideo();
void frameStarted(float dt, bool paused);
void screenshot(Ogre::Image& image, int w, int h);
void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition, float scale=1.f);

@ -130,7 +130,7 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
if (cell->mCell->hasWater())
runtime.push (cell->mWaterLevel);
else
@ -146,7 +146,7 @@ namespace MWScript
{
Interpreter::Type_Float level = runtime[0].mFloat;
MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
if (cell->mCell->isExterior())
throw std::runtime_error("Can't set water level in exterior cell");
@ -164,7 +164,7 @@ namespace MWScript
{
Interpreter::Type_Float level = runtime[0].mFloat;
MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
if (cell->mCell->isExterior())
throw std::runtime_error("Can't set water level in exterior cell");

@ -3,6 +3,9 @@
#include <cassert>
#include <components/misc/stringops.hpp>
#include <components/esm/globalscript.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
@ -15,25 +18,16 @@ namespace MWScript
GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store)
: mStore (store)
{
reset();
}
void GlobalScripts::reset()
{
mScripts.clear();
addScript ("Main");
MWWorld::Store<ESM::StartScript>::iterator iter =
mStore.get<ESM::StartScript>().begin();
for (; iter != mStore.get<ESM::StartScript>().end(); ++iter) {
addScript (iter->mScript);
}
addStartup();
}
void GlobalScripts::addScript (const std::string& name)
{
if (mScripts.find (name)==mScripts.end())
std::map<std::string, std::pair<bool, Locals> >::iterator iter =
mScripts.find (Misc::StringUtils::lowerCase (name));
if (iter==mScripts.end())
{
if (const ESM::Script *script = mStore.get<ESM::Script>().find (name))
{
Locals locals;
@ -42,11 +36,15 @@ namespace MWScript
mScripts.insert (std::make_pair (name, std::make_pair (true, locals)));
}
}
else
iter->second.first = true;
}
void GlobalScripts::removeScript (const std::string& name)
{
std::map<std::string, std::pair<bool, Locals> >::iterator iter = mScripts.find (name);
std::map<std::string, std::pair<bool, Locals> >::iterator iter =
mScripts.find (Misc::StringUtils::lowerCase (name));
if (iter!=mScripts.end())
iter->second.first = false;
@ -55,7 +53,7 @@ namespace MWScript
bool GlobalScripts::isRunning (const std::string& name) const
{
std::map<std::string, std::pair<bool, Locals> >::const_iterator iter =
mScripts.find (name);
mScripts.find (Misc::StringUtils::lowerCase (name));
if (iter==mScripts.end())
return false;
@ -76,4 +74,78 @@ namespace MWScript
}
}
}
void GlobalScripts::clear()
{
mScripts.clear();
}
void GlobalScripts::addStartup()
{
addScript ("main");
for (MWWorld::Store<ESM::StartScript>::iterator iter =
mStore.get<ESM::StartScript>().begin();
iter != mStore.get<ESM::StartScript>().end(); ++iter)
{
addScript (iter->mScript);
}
}
int GlobalScripts::countSavedGameRecords() const
{
return mScripts.size();
}
void GlobalScripts::write (ESM::ESMWriter& writer) const
{
for (std::map<std::string, std::pair<bool, Locals> >::const_iterator iter (mScripts.begin());
iter!=mScripts.end(); ++iter)
{
ESM::GlobalScript script;
script.mId = iter->first;
iter->second.second.write (script.mLocals, iter->first);
script.mRunning = iter->second.first ? 1 : 0;
writer.startRecord (ESM::REC_GSCR);
script.save (writer);
writer.endRecord (ESM::REC_GSCR);
}
}
bool GlobalScripts::readRecord (ESM::ESMReader& reader, int32_t type)
{
if (type==ESM::REC_GSCR)
{
ESM::GlobalScript script;
script.load (reader);
std::map<std::string, std::pair<bool, Locals> >::iterator iter =
mScripts.find (script.mId);
if (iter==mScripts.end())
{
if (const ESM::Script *scriptRecord = mStore.get<ESM::Script>().search (script.mId))
{
std::pair<bool, Locals> data (false, Locals());
data.second.configure (*scriptRecord);
iter = mScripts.insert (std::make_pair (script.mId, data)).first;
}
else // script does not exist anymore
return true;
}
iter->second.first = script.mRunning!=0;
iter->second.second.read (script.mLocals, script.mId);
return true;
}
return false;
}
}

@ -4,9 +4,17 @@
#include <string>
#include <map>
#include <libs/platform/stdint.h>
#include "locals.hpp"
namespace MWWorld
namespace ESM
{
class ESMWriter;
class ESMReader;
}
namespace MWWorld
{
struct ESMStore;
}
@ -22,8 +30,6 @@ namespace MWScript
GlobalScripts (const MWWorld::ESMStore& store);
void reset();
void addScript (const std::string& name);
void removeScript (const std::string& name);
@ -32,6 +38,20 @@ namespace MWScript
void run();
///< run all active global scripts
void clear();
void addStartup();
///< Add startup script
int countSavedGameRecords() const;
void write (ESM::ESMWriter& writer) const;
bool readRecord (ESM::ESMReader& reader, int32_t type);
///< Records for variables that do not exist are dropped silently.
///
/// \return Known type?
};
}

@ -126,61 +126,49 @@ namespace MWScript
int InterpreterContext::getGlobalShort (const std::string& name) const
{
return MWBase::Environment::get().getWorld()->getGlobalVariable (name).mShort;
return MWBase::Environment::get().getWorld()->getGlobalInt (name);
}
int InterpreterContext::getGlobalLong (const std::string& name) const
{
// a global long is internally a float.
return MWBase::Environment::get().getWorld()->getGlobalVariable (name).mLong;
return MWBase::Environment::get().getWorld()->getGlobalInt (name);
}
float InterpreterContext::getGlobalFloat (const std::string& name) const
{
return MWBase::Environment::get().getWorld()->getGlobalVariable (name).mFloat;
return MWBase::Environment::get().getWorld()->getGlobalFloat (name);
}
void InterpreterContext::setGlobalShort (const std::string& name, int value)
{
if (name=="gamehour")
MWBase::Environment::get().getWorld()->setHour (value);
else if (name=="day")
MWBase::Environment::get().getWorld()->setDay (value);
else if (name=="month")
MWBase::Environment::get().getWorld()->setMonth (value);
else
MWBase::Environment::get().getWorld()->getGlobalVariable (name).mShort = value;
MWBase::Environment::get().getWorld()->setGlobalInt (name, value);
}
void InterpreterContext::setGlobalLong (const std::string& name, int value)
{
if (name=="gamehour")
MWBase::Environment::get().getWorld()->setHour (value);
else if (name=="day")
MWBase::Environment::get().getWorld()->setDay (value);
else if (name=="month")
MWBase::Environment::get().getWorld()->setMonth (value);
else
MWBase::Environment::get().getWorld()->getGlobalVariable (name).mLong = value;
MWBase::Environment::get().getWorld()->setGlobalInt (name, value);
}
void InterpreterContext::setGlobalFloat (const std::string& name, float value)
{
if (name=="gamehour")
MWBase::Environment::get().getWorld()->setHour (value);
else if (name=="day")
MWBase::Environment::get().getWorld()->setDay (value);
else if (name=="month")
MWBase::Environment::get().getWorld()->setMonth (value);
else
MWBase::Environment::get().getWorld()->getGlobalVariable (name).mFloat = value;
MWBase::Environment::get().getWorld()->setGlobalFloat (name, value);
}
std::vector<std::string> InterpreterContext::getGlobals () const
std::vector<std::string> InterpreterContext::getGlobals() const
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->getGlobals();
std::vector<std::string> ids;
const MWWorld::Store<ESM::Global>& globals =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Global>();
for (MWWorld::Store<ESM::Global>::iterator iter = globals.begin(); iter!=globals.end();
++iter)
{
ids.push_back (iter->mId);
}
return ids;
}
char InterpreterContext::getGlobalType (const std::string& name) const
@ -321,8 +309,7 @@ namespace MWScript
std::string InterpreterContext::getCurrentCellName() const
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->getCurrentCellName();
return MWBase::Environment::get().getWorld()->getCellName();
}
bool InterpreterContext::isScriptRunning (const std::string& name) const

@ -1,6 +1,8 @@
#include "locals.hpp"
#include <components/esm/loadscpt.hpp>
#include <components/esm/variant.hpp>
#include <components/esm/locals.hpp>
#include <components/compiler/locals.hpp>
@ -65,4 +67,68 @@ namespace MWScript
}
return false;
}
void Locals::write (ESM::Locals& locals, const std::string& script) const
{
const Compiler::Locals& declarations =
MWBase::Environment::get().getScriptManager()->getLocals(script);
for (int i=0; i<3; ++i)
{
char type = 0;
switch (i)
{
case 0: type = 's'; break;
case 1: type = 'l'; break;
case 2: type = 'f'; break;
}
const std::vector<std::string>& names = declarations.get (type);
for (int i2=0; i2<static_cast<int> (names.size()); ++i2)
{
ESM::Variant value;
switch (i)
{
case 0: value.setType (ESM::VT_Int); value.setInteger (mShorts.at (i2)); break;
case 1: value.setType (ESM::VT_Int); value.setInteger (mLongs.at (i2)); break;
case 2: value.setType (ESM::VT_Float); value.setFloat (mFloats.at (i2)); break;
}
locals.mVariables.push_back (std::make_pair (names[i2], value));
}
}
}
void Locals::read (const ESM::Locals& locals, const std::string& script)
{
const Compiler::Locals& declarations =
MWBase::Environment::get().getScriptManager()->getLocals(script);
for (std::vector<std::pair<std::string, ESM::Variant> >::const_iterator iter
= locals.mVariables.begin(); iter!=locals.mVariables.end(); ++iter)
{
char type = declarations.getType (iter->first);
char index = declarations.getIndex (iter->first);
try
{
switch (type)
{
case 's': mShorts.at (index) = iter->second.getInteger(); break;
case 'l': mLongs.at (index) = iter->second.getInteger(); break;
case 'f': mFloats.at (index) = iter->second.getFloat(); break;
// silently ignore locals that don't exist anymore
}
}
catch (...)
{
// ignore type changes
/// \todo write to log
}
}
}
}

@ -8,6 +8,7 @@
namespace ESM
{
struct Script;
struct Locals;
}
namespace MWScript
@ -23,6 +24,9 @@ namespace MWScript
bool setVarByInt(const std::string& script, const std::string& var, int val);
int getIntVar (const std::string& script, const std::string& var); ///< if var does not exist, returns 0
void write (ESM::Locals& locals, const std::string& script) const;
void read (const ESM::Locals& locals, const std::string& script);
};
}

@ -682,23 +682,43 @@ namespace MWScript
void printGlobalVars(Interpreter::Runtime &runtime)
{
InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext());
std::stringstream str;
str<< "Global variables:";
MWBase::World *world = MWBase::Environment::get().getWorld();
std::vector<std::string> names = world->getGlobals();
std::vector<std::string> names = context.getGlobals();
for(size_t i = 0;i < names.size();++i)
{
char type = world->getGlobalVariableType(names[i]);
if(type == 's')
str<<std::endl<< " "<<names[i]<<" = "<<world->getGlobalVariable(names[i]).mShort<<" (short)";
else if(type == 'l')
str<<std::endl<< " "<<names[i]<<" = "<<world->getGlobalVariable(names[i]).mLong<<" (long)";
else if(type == 'f')
str<<std::endl<< " "<<names[i]<<" = "<<world->getGlobalVariable(names[i]).mFloat<<" (float)";
char type = world->getGlobalVariableType (names[i]);
str << std::endl << " " << names[i] << " = ";
switch (type)
{
case 's':
str << context.getGlobalShort (names[i]) << " (short)";
break;
case 'l':
str << context.getGlobalLong (names[i]) << " (long)";
break;
case 'f':
str << context.getGlobalFloat (names[i]) << " (float)";
break;
default:
str << "<unknown type>";
}
}
runtime.getContext().report(str.str());
context.report (str.str());
}
public:

@ -220,9 +220,4 @@ namespace MWScript
throw std::runtime_error ("unable to access local variable " + variable + " of " + scriptId);
}
void ScriptManager::resetGlobalScripts()
{
mGlobalScripts.reset();
}
}

@ -61,8 +61,6 @@ namespace MWScript
///< Compile script with the given namen
/// \return Success?
virtual void resetGlobalScripts();
virtual std::pair<int, int> compileAll();
///< Compile all scripts
/// \return count, success

@ -6,6 +6,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwworld/esmstore.hpp"
@ -398,7 +399,7 @@ namespace MWSound
}
}
void SoundManager::stopSound(const MWWorld::Ptr::CellStore *cell)
void SoundManager::stopSound(const MWWorld::CellStore *cell)
{
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
@ -594,7 +595,7 @@ namespace MWSound
soundDuration=snditer->first->mFadeOutTime;
snditer->first->setVolume(snditer->first->mVolume
- soundDuration / snditer->first->mFadeOutTime * snditer->first->mVolume);
snditer->first->mFadeOutTime -= soundDuration;
snditer->first->mFadeOutTime -= soundDuration;
}
snditer->first->update();
++snditer;
@ -606,8 +607,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);
}
}
@ -713,4 +719,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();
}
}

@ -150,6 +150,8 @@ namespace MWSound
virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up);
virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
virtual void clear();
};
}

@ -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

@ -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"
@ -7,29 +12,29 @@
#include "esmstore.hpp"
#include "containerstore.hpp"
MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell)
MWWorld::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell)
{
if (cell->mData.mFlags & ESM::Cell::Interior)
{
std::string lowerName(Misc::StringUtils::lowerCase(cell->mName));
std::map<std::string, Ptr::CellStore>::iterator result = mInteriors.find (lowerName);
std::map<std::string, CellStore>::iterator result = mInteriors.find (lowerName);
if (result==mInteriors.end())
{
result = mInteriors.insert (std::make_pair (lowerName, Ptr::CellStore (cell))).first;
result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell))).first;
}
return &result->second;
}
else
{
std::map<std::pair<int, int>, Ptr::CellStore>::iterator result =
std::map<std::pair<int, int>, CellStore>::iterator result =
mExteriors.find (std::make_pair (cell->getGridX(), cell->getGridY()));
if (result==mExteriors.end())
{
result = mExteriors.insert (std::make_pair (
std::make_pair (cell->getGridX(), cell->getGridY()), Ptr::CellStore (cell))).first;
std::make_pair (cell->getGridX(), cell->getGridY()), CellStore (cell))).first;
}
@ -41,11 +46,11 @@ void MWWorld::Cells::clear()
{
mInteriors.clear();
mExteriors.clear();
std::fill(mIdCache.begin(), mIdCache.end(), std::make_pair("", (MWWorld::Ptr::CellStore*)0));
std::fill(mIdCache.begin(), mIdCache.end(), std::make_pair("", (MWWorld::CellStore*)0));
mIdCacheIndex = 0;
}
MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellStore& cellStore)
MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, CellStore& cellStore)
{
Ptr ptr = getPtr (name, cellStore);
@ -60,15 +65,39 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS
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, Ptr::CellStore *> ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable
mIdCache (40, std::pair<std::string, CellStore *> ("", (CellStore*)0)), /// \todo make cache size configurable
mIdCacheIndex (0)
{}
MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y)
MWWorld::CellStore *MWWorld::Cells::getExterior (int x, int y)
{
std::map<std::pair<int, int>, Ptr::CellStore>::iterator result =
std::map<std::pair<int, int>, CellStore>::iterator result =
mExteriors.find (std::make_pair (x, y));
if (result==mExteriors.end())
@ -93,7 +122,7 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y)
std::make_pair (x, y), CellStore (cell))).first;
}
if (result->second.mState!=Ptr::CellStore::State_Loaded)
if (result->second.mState!=CellStore::State_Loaded)
{
// Multiple plugin support for landscape data is much easier than for references. The last plugin wins.
result->second.load (mStore, mReader);
@ -102,19 +131,19 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y)
return &result->second;
}
MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name)
MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name)
{
std::string lowerName = Misc::StringUtils::lowerCase(name);
std::map<std::string, Ptr::CellStore>::iterator result = mInteriors.find (lowerName);
std::map<std::string, CellStore>::iterator result = mInteriors.find (lowerName);
if (result==mInteriors.end())
{
const ESM::Cell *cell = mStore.get<ESM::Cell>().find(lowerName);
result = mInteriors.insert (std::make_pair (lowerName, Ptr::CellStore (cell))).first;
result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell))).first;
}
if (result->second.mState!=Ptr::CellStore::State_Loaded)
if (result->second.mState!=CellStore::State_Loaded)
{
result->second.load (mStore, mReader);
}
@ -122,13 +151,21 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name)
return &result->second;
}
MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& cell,
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)
{
if (cell.mState==Ptr::CellStore::State_Unloaded)
if (cell.mState==CellStore::State_Unloaded)
cell.preload (mStore, mReader);
if (cell.mState==Ptr::CellStore::State_Preloaded)
if (cell.mState==CellStore::State_Preloaded)
{
if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), name))
{
@ -207,7 +244,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce
MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)
{
// First check the cache
for (std::vector<std::pair<std::string, Ptr::CellStore *> >::iterator iter (mIdCache.begin());
for (std::vector<std::pair<std::string, CellStore *> >::iterator iter (mIdCache.begin());
iter!=mIdCache.end(); ++iter)
if (iter->first==name && iter->second)
{
@ -217,7 +254,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)
}
// Then check cells that are already listed
for (std::map<std::pair<int, int>, Ptr::CellStore>::iterator iter = mExteriors.begin();
for (std::map<std::pair<int, int>, CellStore>::iterator iter = mExteriors.begin();
iter!=mExteriors.end(); ++iter)
{
Ptr ptr = getPtrAndCache (name, iter->second);
@ -225,7 +262,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)
return ptr;
}
for (std::map<std::string, Ptr::CellStore>::iterator iter = mInteriors.begin();
for (std::map<std::string, CellStore>::iterator iter = mInteriors.begin();
iter!=mInteriors.end(); ++iter)
{
Ptr ptr = getPtrAndCache (name, iter->second);
@ -239,7 +276,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)
for (iter = cells.extBegin(); iter != cells.extEnd(); ++iter)
{
Ptr::CellStore *cellStore = getCellStore (&(*iter));
CellStore *cellStore = getCellStore (&(*iter));
Ptr ptr = getPtrAndCache (name, *cellStore);
@ -249,7 +286,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)
for (iter = cells.intBegin(); iter != cells.intEnd(); ++iter)
{
Ptr::CellStore *cellStore = getCellStore (&(*iter));
CellStore *cellStore = getCellStore (&(*iter));
Ptr ptr = getPtrAndCache (name, *cellStore);
@ -263,7 +300,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)
void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector<MWWorld::Ptr> &out)
{
for (std::map<std::pair<int, int>, Ptr::CellStore>::iterator iter = mExteriors.begin();
for (std::map<std::pair<int, int>, CellStore>::iterator iter = mExteriors.begin();
iter!=mExteriors.end(); ++iter)
{
Ptr ptr = getPtrAndCache (name, iter->second);
@ -275,7 +312,7 @@ void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector<MWWorl
void MWWorld::Cells::getInteriorPtrs(const std::string &name, std::vector<MWWorld::Ptr> &out)
{
for (std::map<std::string, Ptr::CellStore>::iterator iter = mInteriors.begin();
for (std::map<std::string, CellStore>::iterator iter = mInteriors.begin();
iter!=mInteriors.end(); ++iter)
{
Ptr ptr = getPtrAndCache (name, iter->second);
@ -284,3 +321,67 @@ void MWWorld::Cells::getInteriorPtrs(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,
const std::map<int, int>& contentFileMap)
{
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);
if (cellStore->mState!=CellStore::State_Loaded)
cellStore->load (mStore, mReader);
cellStore->readReferences (reader, contentFileMap);
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
@ -62,6 +69,12 @@ namespace MWWorld
/// @note name must be lower case
void getInteriorPtrs (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,
const std::map<int, int>& contentFileMap);
};
}

@ -2,6 +2,15 @@
#include <iostream>
#include <components/esm/cellstate.hpp>
#include <components/esm/cellid.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/objectstate.hpp>
#include <components/esm/lightstate.hpp>
#include <components/esm/containerstate.hpp>
#include <components/esm/npcstate.hpp>
#include <components/esm/creaturestate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -29,38 +38,99 @@ namespace
return MWWorld::Ptr();
}
template<typename RecordType, typename T>
void writeReferenceCollection (ESM::ESMWriter& writer,
const MWWorld::CellRefList<T>& collection)
{
if (!collection.mList.empty())
{
// 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.writeHNT ("OBJE", collection.mList.front().mBase->sRecordId);
state.save (writer);
}
}
}
template<typename RecordType, typename T>
void readReferenceCollection (ESM::ESMReader& reader,
MWWorld::CellRefList<T>& collection, const std::map<int, int>& contentFileMap)
{
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
RecordType state;
state.load (reader);
std::map<int, int>::const_iterator iter =
contentFileMap.find (state.mRef.mRefNum.mContentFile);
if (iter==contentFileMap.end())
return; // content file has been removed -> skip
state.mRef.mRefNum.mContentFile = iter->second;
if (!MWWorld::LiveCellRef<T>::checkState (state))
return; // not valid anymore with current content files -> skip
const T *record = esmStore.get<T>().search (state.mRef.mRefID);
if (!record)
return;
for (typename MWWorld::CellRefList<T>::List::iterator iter (collection.mList.begin());
iter!=collection.mList.end(); ++iter)
if (iter->mRef.mRefNum==state.mRef.mRefNum)
{
// overwrite existing reference
iter->load (state);
return;
}
// new reference
MWWorld::LiveCellRef<T> ref (record);
ref.load (state);
collection.mList.push_back (ref);
}
}
namespace MWWorld
{
template <typename X>
void CellRefList<X>::load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore)
void CellRefList<X>::load(ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore)
{
// Get existing reference, in case we need to overwrite it.
typename std::list<LiveRef>::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefnum);
const MWWorld::Store<X> &store = esmStore.get<X>();
if (const X *ptr = store.search (ref.mRefID))
{
typename std::list<LiveRef>::iterator iter =
std::find(mList.begin(), mList.end(), ref.mRefNum);
LiveRef liveCellRef (ref, ptr);
if (deleted)
liveCellRef.mData.setCount (0);
// Skip this when reference was deleted.
// TODO: Support respawning references, in this case, we need to track it somehow.
if (ref.mDeleted) {
if (iter != mList.end())
mList.erase(iter);
return;
*iter = liveCellRef;
else
mList.push_back (liveCellRef);
}
// for throwing exception on unhandled record type
const MWWorld::Store<X> &store = esmStore.get<X>();
const X *ptr = store.search(ref.mRefID);
/// \note no longer redundant - changed to Store<X>::search(), don't throw
/// an exception on miss, try to continue (that's how MW does it, anyway)
if (ptr == NULL) {
std::cout << "Warning: could not resolve cell reference " << ref.mRefID << ", trying to continue anyway" << std::endl;
} else {
if (iter != mList.end())
*iter = LiveRef(ref, ptr);
else
mList.push_back(LiveRef(ref, ptr));
else
{
std::cerr
<< "Error: could not resolve cell reference " << ref.mRefID
<< " (dropping reference)" << std::endl;
}
}
@ -117,16 +187,13 @@ namespace MWWorld
ESM::CellRef ref;
// Get each reference in turn
while (mCell->getNextRef (esm[index], ref))
bool deleted = false;
while (mCell->getNextRef (esm[index], ref, deleted))
{
std::string lowerCase = Misc::StringUtils::lowerCase (ref.mRefID);
if (ref.mDeleted) {
// Right now, don't do anything. Where is "listRefs" actually used, anyway?
// Skipping for now...
if (deleted)
continue;
}
mIds.push_back (lowerCase);
mIds.push_back (Misc::StringUtils::lowerCase (ref.mRefID));
}
}
@ -135,7 +202,7 @@ namespace MWWorld
void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
{
assert (mCell);
assert (mCell);
if (mCell->mContextList.empty())
return; // this is a dynamically generated cell -> skipping.
@ -148,98 +215,30 @@ namespace MWWorld
mCell->restore (esm[index], i);
ESM::CellRef ref;
ref.mRefNum.mContentFile = -1;
// Get each reference in turn
while(mCell->getNextRef(esm[index], ref))
bool deleted = false;
while(mCell->getNextRef(esm[index], ref, deleted))
{
// Don't load reference if it was moved to a different cell.
std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID);
ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefnum);
ESM::MovedCellRefTracker::const_iterator iter =
std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum);
if (iter != mCell->mMovedRefs.end()) {
continue;
}
int rec = store.find(ref.mRefID);
ref.mRefID = lowerCase;
/* We can optimize this further by storing the pointer to the
record itself in store.all, so that we don't need to look it
up again here. However, never optimize. There are infinite
opportunities to do that later.
*/
switch(rec)
{
case ESM::REC_ACTI: mActivators.load(ref, store); break;
case ESM::REC_ALCH: mPotions.load(ref, store); break;
case ESM::REC_APPA: mAppas.load(ref, store); break;
case ESM::REC_ARMO: mArmors.load(ref, store); break;
case ESM::REC_BOOK: mBooks.load(ref, store); break;
case ESM::REC_CLOT: mClothes.load(ref, store); break;
case ESM::REC_CONT: mContainers.load(ref, store); break;
case ESM::REC_CREA: mCreatures.load(ref, store); break;
case ESM::REC_DOOR: mDoors.load(ref, store); break;
case ESM::REC_INGR: mIngreds.load(ref, store); break;
case ESM::REC_LEVC: mCreatureLists.load(ref, store); break;
case ESM::REC_LEVI: mItemLists.load(ref, store); break;
case ESM::REC_LIGH: mLights.load(ref, store); break;
case ESM::REC_LOCK: mLockpicks.load(ref, store); break;
case ESM::REC_MISC: mMiscItems.load(ref, store); break;
case ESM::REC_NPC_: mNpcs.load(ref, store); break;
case ESM::REC_PROB: mProbes.load(ref, store); break;
case ESM::REC_REPA: mRepairs.load(ref, store); break;
case ESM::REC_STAT: mStatics.load(ref, store); break;
case ESM::REC_WEAP: mWeapons.load(ref, store); break;
case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break;
default:
std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n";
}
loadRef (ref, deleted, store);
}
}
// Load moved references, from separately tracked list.
for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it)
{
// Doesn't seem to work in one line... huh? Too sleepy to check...
ESM::CellRef &ref = const_cast<ESM::CellRef&>(*it);
//ESM::CellRef &ref = const_cast<ESM::CellRef&>(it->second);
int rec = store.find(ref.mRefID);
Misc::StringUtils::toLower(ref.mRefID);
/* We can optimize this further by storing the pointer to the
record itself in store.all, so that we don't need to look it
up again here. However, never optimize. There are infinite
opportunities to do that later.
*/
switch(rec)
{
case ESM::REC_ACTI: mActivators.load(ref, store); break;
case ESM::REC_ALCH: mPotions.load(ref, store); break;
case ESM::REC_APPA: mAppas.load(ref, store); break;
case ESM::REC_ARMO: mArmors.load(ref, store); break;
case ESM::REC_BOOK: mBooks.load(ref, store); break;
case ESM::REC_CLOT: mClothes.load(ref, store); break;
case ESM::REC_CONT: mContainers.load(ref, store); break;
case ESM::REC_CREA: mCreatures.load(ref, store); break;
case ESM::REC_DOOR: mDoors.load(ref, store); break;
case ESM::REC_INGR: mIngreds.load(ref, store); break;
case ESM::REC_LEVC: mCreatureLists.load(ref, store); break;
case ESM::REC_LEVI: mItemLists.load(ref, store); break;
case ESM::REC_LIGH: mLights.load(ref, store); break;
case ESM::REC_LOCK: mLockpicks.load(ref, store); break;
case ESM::REC_MISC: mMiscItems.load(ref, store); break;
case ESM::REC_NPC_: mNpcs.load(ref, store); break;
case ESM::REC_PROB: mProbes.load(ref, store); break;
case ESM::REC_REPA: mRepairs.load(ref, store); break;
case ESM::REC_STAT: mStatics.load(ref, store); break;
case ESM::REC_WEAP: mWeapons.load(ref, store); break;
case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break;
default:
std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n";
}
loadRef (ref, false, store);
}
}
@ -268,4 +267,198 @@ namespace MWWorld
return Ptr();
}
void CellStore::loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store)
{
Misc::StringUtils::toLower (ref.mRefID);
switch (store.find (ref.mRefID))
{
case ESM::REC_ACTI: mActivators.load(ref, deleted, store); break;
case ESM::REC_ALCH: mPotions.load(ref, deleted, store); break;
case ESM::REC_APPA: mAppas.load(ref, deleted, store); break;
case ESM::REC_ARMO: mArmors.load(ref, deleted, store); break;
case ESM::REC_BOOK: mBooks.load(ref, deleted, store); break;
case ESM::REC_CLOT: mClothes.load(ref, deleted, store); break;
case ESM::REC_CONT: mContainers.load(ref, deleted, store); break;
case ESM::REC_CREA: mCreatures.load(ref, deleted, store); break;
case ESM::REC_DOOR: mDoors.load(ref, deleted, store); break;
case ESM::REC_INGR: mIngreds.load(ref, deleted, store); break;
case ESM::REC_LEVC: mCreatureLists.load(ref, deleted, store); break;
case ESM::REC_LEVI: mItemLists.load(ref, deleted, store); break;
case ESM::REC_LIGH: mLights.load(ref, deleted, store); break;
case ESM::REC_LOCK: mLockpicks.load(ref, deleted, store); break;
case ESM::REC_MISC: mMiscItems.load(ref, deleted, store); break;
case ESM::REC_NPC_: mNpcs.load(ref, deleted, store); break;
case ESM::REC_PROB: mProbes.load(ref, deleted, store); break;
case ESM::REC_REPA: mRepairs.load(ref, deleted, store); break;
case ESM::REC_STAT: mStatics.load(ref, deleted, store); break;
case ESM::REC_WEAP: mWeapons.load(ref, deleted, store); break;
case 0: std::cerr << "Cell reference " + ref.mRefID + " not found!\n"; break;
default:
std::cerr
<< "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::ContainerState> (writer, mContainers);
writeReferenceCollection<ESM::CreatureState> (writer, mCreatures);
writeReferenceCollection<ESM::ObjectState> (writer, mDoors);
writeReferenceCollection<ESM::ObjectState> (writer, mIngreds);
writeReferenceCollection<ESM::ObjectState> (writer, mCreatureLists);
writeReferenceCollection<ESM::ObjectState> (writer, mItemLists);
writeReferenceCollection<ESM::LightState> (writer, mLights);
writeReferenceCollection<ESM::ObjectState> (writer, mLockpicks);
writeReferenceCollection<ESM::ObjectState> (writer, mMiscItems);
writeReferenceCollection<ESM::NpcState> (writer, mNpcs);
writeReferenceCollection<ESM::ObjectState> (writer, mProbes);
writeReferenceCollection<ESM::ObjectState> (writer, mRepairs);
writeReferenceCollection<ESM::ObjectState> (writer, mStatics);
writeReferenceCollection<ESM::ObjectState> (writer, mWeapons);
}
void CellStore::readReferences (ESM::ESMReader& reader,
const std::map<int, int>& contentFileMap)
{
while (reader.isNextSub ("OBJE"))
{
unsigned int id = 0;
reader.getHT (id);
switch (id)
{
case ESM::REC_ACTI:
readReferenceCollection<ESM::ObjectState> (reader, mActivators, contentFileMap);
break;
case ESM::REC_ALCH:
readReferenceCollection<ESM::ObjectState> (reader, mPotions, contentFileMap);
break;
case ESM::REC_APPA:
readReferenceCollection<ESM::ObjectState> (reader, mAppas, contentFileMap);
break;
case ESM::REC_ARMO:
readReferenceCollection<ESM::ObjectState> (reader, mArmors, contentFileMap);
break;
case ESM::REC_BOOK:
readReferenceCollection<ESM::ObjectState> (reader, mBooks, contentFileMap);
break;
case ESM::REC_CLOT:
readReferenceCollection<ESM::ObjectState> (reader, mClothes, contentFileMap);
break;
case ESM::REC_CONT:
readReferenceCollection<ESM::ContainerState> (reader, mContainers, contentFileMap);
break;
case ESM::REC_CREA:
readReferenceCollection<ESM::CreatureState> (reader, mCreatures, contentFileMap);
break;
case ESM::REC_DOOR:
readReferenceCollection<ESM::ObjectState> (reader, mDoors, contentFileMap);
break;
case ESM::REC_INGR:
readReferenceCollection<ESM::ObjectState> (reader, mIngreds, contentFileMap);
break;
case ESM::REC_LEVC:
readReferenceCollection<ESM::ObjectState> (reader, mCreatureLists, contentFileMap);
break;
case ESM::REC_LEVI:
readReferenceCollection<ESM::ObjectState> (reader, mItemLists, contentFileMap);
break;
case ESM::REC_LIGH:
readReferenceCollection<ESM::LightState> (reader, mLights, contentFileMap);
break;
case ESM::REC_LOCK:
readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, contentFileMap);
break;
case ESM::REC_MISC:
readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, contentFileMap);
break;
case ESM::REC_NPC_:
readReferenceCollection<ESM::NpcState> (reader, mNpcs, contentFileMap);
break;
case ESM::REC_PROB:
readReferenceCollection<ESM::ObjectState> (reader, mProbes, contentFileMap);
break;
case ESM::REC_REPA:
readReferenceCollection<ESM::ObjectState> (reader, mRepairs, contentFileMap);
break;
case ESM::REC_STAT:
readReferenceCollection<ESM::ObjectState> (reader, mStatics, contentFileMap);
break;
case ESM::REC_WEAP:
readReferenceCollection<ESM::ObjectState> (reader, mWeapons, contentFileMap);
break;
default:
throw std::runtime_error ("unknown type in cell reference section");
}
}
}
}

@ -7,6 +7,11 @@
#include "livecellref.hpp"
#include "esmstore.hpp"
namespace ESM
{
struct CellState;
}
namespace MWWorld
{
@ -25,7 +30,7 @@ namespace MWWorld
// and the build will fail with an ugly three-way cyclic header dependence
// so we need to pass the instantiation of the method to the lnker, when
// all methods are known.
void load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore);
void load(ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore);
LiveRef *find (const std::string& name)
{
@ -133,6 +138,14 @@ 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;
void readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap);
private:
template<class Functor, class List>
@ -154,6 +167,10 @@ namespace MWWorld
void loadRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
void loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store);
///< Make case-adjustments to \a ref and insert it into the respective container.
///
/// Invalid \a ref objects are silently dropped.
};
}

@ -377,4 +377,8 @@ namespace MWWorld
{
throw std::runtime_error("class does not support gore");
}
void Class::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const {}
void Class::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const {}
}

@ -9,6 +9,11 @@
#include "ptr.hpp"
namespace ESM
{
struct ObjectState;
}
namespace Ogre
{
class Vector3;
@ -306,6 +311,14 @@ namespace MWWorld
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
static const Class& get (const std::string& key);
///< If there is no class for this \a key, an exception is thrown.

@ -5,6 +5,8 @@
#include <typeinfo>
#include <stdexcept>
#include <components/esm/inventorystate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -57,6 +59,54 @@ namespace
}
}
template<typename T>
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState (CellRefList<T>& collection,
const ESM::ObjectState& state)
{
if (!LiveCellRef<T>::checkState (state))
return ContainerStoreIterator (this); // not valid anymore with current content files -> skip
const T *record = MWBase::Environment::get().getWorld()->getStore().
get<T>().search (state.mRef.mRefID);
if (!record)
return ContainerStoreIterator (this);
LiveCellRef<T> ref (record);
ref.load (state);
ref.mRef.mRefNum.mContentFile = -1;
collection.mList.push_back (ref);
return ContainerStoreIterator (this, --collection.mList.end());
}
template<typename T>
void MWWorld::ContainerStore::storeState (const LiveCellRef<T>& ref, ESM::ObjectState& state) const
{
ref.save (state);
}
template<typename T>
void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,
std::vector<std::pair<ESM::ObjectState, std::pair<unsigned int, int> > >& states, bool equipable) const
{
for (typename CellRefList<T>::List::const_iterator iter (collection.mList.begin());
iter!=collection.mList.end(); ++iter)
{
ESM::ObjectState state;
storeState (*iter, state);
int slot = equipable ? getSlot (*iter) : -1;
states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, slot)));
}
}
int MWWorld::ContainerStore::getSlot (const MWWorld::LiveCellRefBase& ref) const
{
return -1;
}
void MWWorld::ContainerStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {}
const std::string MWWorld::ContainerStore::sGoldId = "gold_001";
MWWorld::ContainerStore::ContainerStore() : mCachedWeight (0), mWeightUpToDate (false) {}
@ -493,6 +543,69 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)
return Ptr();
}
void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const
{
state.mItems.clear();
storeStates (potions, state.mItems);
storeStates (appas, state.mItems);
storeStates (armors, state.mItems, true);
storeStates (books, state.mItems);
storeStates (clothes, state.mItems, true);
storeStates (ingreds, state.mItems);
storeStates (lockpicks, state.mItems, true);
storeStates (miscItems, state.mItems);
storeStates (probes, state.mItems, true);
storeStates (repairs, state.mItems);
storeStates (weapons, state.mItems, true);
state.mLights.clear();
for (MWWorld::CellRefList<ESM::Light>::List::const_iterator iter (lights.mList.begin());
iter!=lights.mList.end(); ++iter)
{
ESM::LightState objectState;
storeState (*iter, objectState);
state.mLights.push_back (std::make_pair (objectState, getSlot (*iter)));
}
}
void MWWorld::ContainerStore::readState (const ESM::InventoryState& state)
{
clear();
for (std::vector<std::pair<ESM::ObjectState, std::pair<unsigned int, int> > >::const_iterator
iter (state.mItems.begin()); iter!=state.mItems.end(); ++iter)
{
int slot = iter->second.second;
switch (iter->second.first)
{
case ESM::REC_ALCH: getState (potions, iter->first); break;
case ESM::REC_APPA: getState (appas, iter->first); break;
case ESM::REC_ARMO: setSlot (getState (armors, iter->first), slot); break;
case ESM::REC_BOOK: getState (books, iter->first); break;
case ESM::REC_CLOT: setSlot (getState (clothes, iter->first), slot); break;
case ESM::REC_INGR: getState (ingreds, iter->first); break;
case ESM::REC_LOCK: setSlot (getState (lockpicks, iter->first), slot); break;
case ESM::REC_MISC: getState (miscItems, iter->first); break;
case ESM::REC_PROB: setSlot (getState (probes, iter->first), slot); break;
case ESM::REC_REPA: getState (repairs, iter->first); break;
case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break;
default:
std::cerr << "invalid item type in inventory state" << std::endl;
}
}
for (std::vector<std::pair<ESM::LightState, int> >::const_iterator iter (state.mLights.begin());
iter!=state.mLights.end(); ++iter)
{
getState (lights, iter->first);
}
}
MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container)
: mType (-1), mMask (0), mContainer (container)

@ -8,6 +8,7 @@
namespace ESM
{
struct InventoryList;
struct InventoryState;
}
namespace MWWorld
@ -56,6 +57,24 @@ namespace MWWorld
ContainerStoreIterator addImp (const Ptr& ptr, int count);
void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, bool topLevel=true);
template<typename T>
ContainerStoreIterator getState (CellRefList<T>& collection,
const ESM::ObjectState& state);
template<typename T>
void storeState (const LiveCellRef<T>& ref, ESM::ObjectState& state) const;
template<typename T>
void storeStates (const CellRefList<T>& collection,
std::vector<std::pair<ESM::ObjectState, std::pair<unsigned int, int> > >& states,
bool equipable = false) const;
virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const;
///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot).
virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot);
///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1.
public:
ContainerStore();
@ -113,7 +132,7 @@ namespace MWWorld
void fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, const MWWorld::ESMStore& store);
///< Insert items into *this.
void clear();
virtual void clear();
///< Empty container.
float getWeight() const;
@ -125,6 +144,10 @@ namespace MWWorld
Ptr search (const std::string& id);
void writeState (ESM::InventoryState& state) const;
void readState (const ESM::InventoryState& state);
friend class ContainerStoreIterator;
};
@ -174,7 +197,7 @@ namespace MWWorld
ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Repair>::List::iterator);
ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList<ESM::Weapon>::List::iterator);
void copy (const ContainerStoreIterator& src);
void copy (const ContainerStoreIterator& src);
void incType();
@ -202,7 +225,7 @@ namespace MWWorld
ContainerStoreIterator operator++ (int);
ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs);
ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs);
bool isEqual (const ContainerStoreIterator& iter) const;

@ -52,7 +52,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
if (index == (int)~0) {
// Tried to load a parent file that has not been loaded yet. This is bad,
// the launcher should have taken care of this.
std::string fstring = "File " + fname + " asks for parent file " + masters[j].name
std::string fstring = "File " + esm.getName() + " asks for parent file " + masters[j].name
+ ", but it has not been loaded yet. Please check your load order.";
esm.fail(fstring);
}
@ -108,7 +108,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
}
// Insert the reference into the global lookup
if (!id.empty() && isCacheableRecord(n.val)) {
mIds[id] = n.val;
mIds[Misc::StringUtils::lowerCase (id)] = n.val;
}
}
listener->setProgress(esm.getFileOffset() / (float)esm.getFileSize() * 1000);
@ -139,4 +139,68 @@ void ESMStore::setUp()
mAttributes.setUp();
}
int ESMStore::countSavedGameRecords() const
{
return
mPotions.getDynamicSize()
+mArmors.getDynamicSize()
+mBooks.getDynamicSize()
+mClasses.getDynamicSize()
+mClothes.getDynamicSize()
+mEnchants.getDynamicSize()
+mNpcs.getDynamicSize()
+mSpells.getDynamicSize()
+mWeapons.getDynamicSize();
}
void ESMStore::write (ESM::ESMWriter& writer) const
{
mPotions.write (writer);
mArmors.write (writer);
mBooks.write (writer);
mClasses.write (writer);
mClothes.write (writer);
mEnchants.write (writer);
mSpells.write (writer);
mWeapons.write (writer);
mNpcs.write (writer);
}
bool ESMStore::readRecord (ESM::ESMReader& reader, int32_t type)
{
switch (type)
{
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_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:
return false;
}
}
} // end namespace

@ -24,10 +24,8 @@ namespace MWWorld
Store<ESM::BirthSign> mBirthSigns;
Store<ESM::Class> mClasses;
Store<ESM::Clothing> mClothes;
Store<ESM::LoadCNTC> mContChange;
Store<ESM::Container> mContainers;
Store<ESM::Creature> mCreatures;
Store<ESM::LoadCREC> mCreaChange;
Store<ESM::Dialogue> mDialogs;
Store<ESM::Door> mDoors;
Store<ESM::Enchantment> mEnchants;
@ -40,7 +38,6 @@ namespace MWWorld
Store<ESM::Lockpick> mLockpicks;
Store<ESM::Miscellaneous> mMiscItems;
Store<ESM::NPC> mNpcs;
Store<ESM::LoadNPCC> mNpcChange;
Store<ESM::Probe> mProbes;
Store<ESM::Race> mRaces;
Store<ESM::Region> mRegions;
@ -103,7 +100,7 @@ namespace MWWorld
{
// Cell store needs access to this for tracking moved references
mCells.mEsmStore = this;
mStores[ESM::REC_ACTI] = &mActivators;
mStores[ESM::REC_ALCH] = &mPotions;
mStores[ESM::REC_APPA] = &mAppas;
@ -114,10 +111,8 @@ namespace MWWorld
mStores[ESM::REC_CELL] = &mCells;
mStores[ESM::REC_CLAS] = &mClasses;
mStores[ESM::REC_CLOT] = &mClothes;
mStores[ESM::REC_CNTC] = &mContChange;
mStores[ESM::REC_CONT] = &mContainers;
mStores[ESM::REC_CREA] = &mCreatures;
mStores[ESM::REC_CREC] = &mCreaChange;
mStores[ESM::REC_DIAL] = &mDialogs;
mStores[ESM::REC_DOOR] = &mDoors;
mStores[ESM::REC_ENCH] = &mEnchants;
@ -133,7 +128,6 @@ namespace MWWorld
mStores[ESM::REC_LTEX] = &mLandTextures;
mStores[ESM::REC_MISC] = &mMiscItems;
mStores[ESM::REC_NPC_] = &mNpcs;
mStores[ESM::REC_NPCC] = &mNpcChange;
mStores[ESM::REC_PGRD] = &mPathgrids;
mStores[ESM::REC_PROB] = &mProbes;
mStores[ESM::REC_RACE] = &mRaces;
@ -215,6 +209,13 @@ namespace MWWorld
// This method must be called once, after loading all master/plugin files. This can only be done
// from the outside, so it must be public.
void setUp();
int countSavedGameRecords() const;
void write (ESM::ESMWriter& writer) const;
bool readRecord (ESM::ESMReader& reader, int32_t type);
///< \return Known type?
};
template <>
@ -287,11 +288,6 @@ namespace MWWorld
return mClothes;
}
template <>
inline const Store<ESM::LoadCNTC> &ESMStore::get<ESM::LoadCNTC>() const {
return mContChange;
}
template <>
inline const Store<ESM::Container> &ESMStore::get<ESM::Container>() const {
return mContainers;
@ -302,11 +298,6 @@ namespace MWWorld
return mCreatures;
}
template <>
inline const Store<ESM::LoadCREC> &ESMStore::get<ESM::LoadCREC>() const {
return mCreaChange;
}
template <>
inline const Store<ESM::Dialogue> &ESMStore::get<ESM::Dialogue>() const {
return mDialogs;
@ -367,11 +358,6 @@ namespace MWWorld
return mNpcs;
}
template <>
inline const Store<ESM::LoadNPCC> &ESMStore::get<ESM::LoadNPCC>() const {
return mNpcChange;
}
template <>
inline const Store<ESM::Probe> &ESMStore::get<ESM::Probe>() const {
return mProbes;

@ -3,21 +3,15 @@
#include <stdexcept>
#include <components/misc/stringops.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
#include "esmstore.hpp"
namespace MWWorld
{
std::vector<std::string> Globals::getGlobals () const
{
std::vector<std::string> retval;
Collection::const_iterator it;
for(it = mVariables.begin(); it != mVariables.end(); ++it){
retval.push_back(it->first);
}
return retval;
}
Globals::Collection::const_iterator Globals::find (const std::string& name) const
{
Collection::const_iterator iter = mVariables.find (name);
@ -38,122 +32,78 @@ namespace MWWorld
return iter;
}
Globals::Globals (const MWWorld::ESMStore& store)
void Globals::fill (const MWWorld::ESMStore& store)
{
const MWWorld::Store<ESM::Global> &globals = store.get<ESM::Global>();
MWWorld::Store<ESM::Global>::iterator iter = globals.begin();
for (; iter != globals.end(); ++iter)
{
char type = ' ';
Data value;
switch (iter->mValue.getType())
{
case ESM::VT_Short:
type = 's';
value.mShort = iter->mValue.getInteger();
break;
mVariables.clear();
case ESM::VT_Long:
const MWWorld::Store<ESM::Global>& globals = store.get<ESM::Global>();
type = 'l';
value.mLong = iter->mValue.getInteger();
break;
case ESM::VT_Float:
type = 'f';
value.mFloat = iter->mValue.getFloat();
break;
default:
throw std::runtime_error ("unsupported global variable type");
}
mVariables.insert (std::make_pair (iter->mId, std::make_pair (type, value)));
for (MWWorld::Store<ESM::Global>::iterator iter = globals.begin(); iter!=globals.end();
++iter)
{
mVariables.insert (std::make_pair (iter->mId, iter->mValue));
}
}
const Globals::Data& Globals::operator[] (const std::string& name) const
const ESM::Variant& Globals::operator[] (const std::string& name) const
{
Collection::const_iterator iter = find (name);
return iter->second.second;
return find (name)->second;
}
Globals::Data& Globals::operator[] (const std::string& name)
ESM::Variant& Globals::operator[] (const std::string& name)
{
Collection::iterator iter = find (name);
return iter->second.second;
return find (name)->second;
}
void Globals::setInt (const std::string& name, int value)
char Globals::getType (const std::string& name) const
{
Collection::iterator iter = find (name);
Collection::const_iterator iter = mVariables.find (name);
switch (iter->second.first)
if (iter==mVariables.end())
return ' ';
switch (iter->second.getType())
{
case 's': iter->second.second.mShort = value; break;
case 'l': iter->second.second.mLong = value; break;
case 'f': iter->second.second.mFloat = value; break;
case ESM::VT_Short: return 's';
case ESM::VT_Long: return 'l';
case ESM::VT_Float: return 'f';
default: throw std::runtime_error ("unsupported global variable type");
default: return ' ';
}
}
void Globals::setFloat (const std::string& name, float value)
int Globals::countSavedGameRecords() const
{
Collection::iterator iter = find (name);
switch (iter->second.first)
{
case 's': iter->second.second.mShort = value; break;
case 'l': iter->second.second.mLong = value; break;
case 'f': iter->second.second.mFloat = value; break;
default: throw std::runtime_error ("unsupported global variable type");
}
return mVariables.size();
}
int Globals::getInt (const std::string& name) const
void Globals::write (ESM::ESMWriter& writer) const
{
Collection::const_iterator iter = find (name);
switch (iter->second.first)
for (Collection::const_iterator iter (mVariables.begin()); iter!=mVariables.end(); ++iter)
{
case 's': return iter->second.second.mShort;
case 'l': return iter->second.second.mLong;
case 'f': return iter->second.second.mFloat;
default: throw std::runtime_error ("unsupported global variable type");
writer.startRecord (ESM::REC_GLOB);
writer.writeHNString ("NAME", iter->first);
iter->second.write (writer, ESM::Variant::Format_Global);
writer.endRecord (ESM::REC_GLOB);
}
}
float Globals::getFloat (const std::string& name) const
bool Globals::readRecord (ESM::ESMReader& reader, int32_t type)
{
Collection::const_iterator iter = find (name);
switch (iter->second.first)
if (type==ESM::REC_GLOB)
{
case 's': return iter->second.second.mShort;
case 'l': return iter->second.second.mLong;
case 'f': return iter->second.second.mFloat;
std::string id = reader.getHNString ("NAME");
default: throw std::runtime_error ("unsupported global variable type");
}
}
Collection::iterator iter = mVariables.find (Misc::StringUtils::lowerCase (id));
char Globals::getType (const std::string& name) const
{
Collection::const_iterator iter = mVariables.find (name);
if (iter!=mVariables.end())
iter->second.read (reader, ESM::Variant::Format_Global);
else
reader.skipHRecord();
if (iter==mVariables.end())
return ' ';
return true;
}
return iter->second.first;
return false;
}
}

@ -5,7 +5,16 @@
#include <string>
#include <map>
#include <libs/platform/stdint.h>
#include <components/interpreter/types.hpp>
#include <components/esm/variant.hpp>
namespace ESM
{
class ESMWriter;
class ESMReader;
}
namespace MWWorld
{
@ -13,49 +22,37 @@ namespace MWWorld
class Globals
{
public:
union Data
{
Interpreter::Type_Float mFloat;
Interpreter::Type_Float mLong; // Why Morrowind, why? :(
Interpreter::Type_Float mShort;
};
typedef std::map<std::string, std::pair<char, Data> > Collection;
private:
typedef std::map<std::string, ESM::Variant> Collection;
Collection mVariables; // type, value
Collection::const_iterator find (const std::string& name) const;
Collection::iterator find (const std::string& name);
public:
Globals (const MWWorld::ESMStore& store);
const Data& operator[] (const std::string& name) const;
Data& operator[] (const std::string& name);
void setInt (const std::string& name, int value);
///< Set value independently from real type.
void setFloat (const std::string& name, float value);
///< Set value independently from real type.
int getInt (const std::string& name) const;
///< Get value independently from real type.
float getFloat (const std::string& name) const;
///< Get value independently from real type.
const ESM::Variant& operator[] (const std::string& name) const;
ESM::Variant& operator[] (const std::string& name);
char getType (const std::string& name) const;
///< If there is no global variable with this name, ' ' is returned.
std::vector<std::string> getGlobals () const;
void fill (const MWWorld::ESMStore& store);
///< Replace variables with variables from \a store with default values.
int countSavedGameRecords() const;
void write (ESM::ESMWriter& writer) const;
bool readRecord (ESM::ESMReader& reader, int32_t type);
///< Records for variables that do not exist are dropped silently.
///
/// \return Known type?
};
}

@ -49,6 +49,21 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots_)
slots_.push_back (end());
}
int MWWorld::InventoryStore::getSlot (const MWWorld::LiveCellRefBase& ref) const
{
for (int i = 0; i<static_cast<int> (mSlots.size()); ++i)
if (mSlots[i].getType()!=-1 && mSlots[i]->getBase()==&ref)
return i;
return -1;
}
void MWWorld::InventoryStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot)
{
if (iter!=end() && slot>=0 && slot<Slots)
mSlots[slot] = iter;
}
MWWorld::InventoryStore::InventoryStore()
: mSelectedEnchantItem(end())
, mUpdatesEnabled (true)
@ -637,3 +652,10 @@ void MWWorld::InventoryStore::purgeEffect(short effectId)
{
mMagicEffects.add(MWMechanics::EffectKey(effectId), -mMagicEffects.get(MWMechanics::EffectKey(effectId)).mMagnitude);
}
void MWWorld::InventoryStore::clear()
{
mSlots.clear();
initSlots (mSlots);
ContainerStore::clear();
}

@ -105,6 +105,12 @@ namespace MWWorld
void fireEquipmentChangedEvent();
virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const;
///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot).
virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot);
///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1.
public:
InventoryStore();
@ -187,6 +193,9 @@ namespace MWWorld
void purgeEffect (short effectId);
///< Remove a magic effect
virtual void clear();
///< Empty container.
};
}

@ -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;
}

@ -7,6 +7,11 @@
#include "refdata.hpp"
namespace ESM
{
class ObjectState;
}
namespace MWWorld
{
class Ptr;
@ -29,8 +34,39 @@ namespace MWWorld
LiveCellRefBase(std::string type, const ESM::CellRef &cref=ESM::CellRef());
/* Need this for the class to be recognized as polymorphic */
virtual ~LiveCellRefBase() { }
virtual void load (const ESM::ObjectState& state) = 0;
///< Load state into a LiveCellRef, that has already been initialised with base and class.
///
/// \attention Must not be called with an invalid \a state.
virtual void save (ESM::ObjectState& state) const = 0;
///< Save LiveCellRef state into \a state.
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 +86,41 @@ namespace MWWorld
// The object that this instance is based on.
const X* mBase;
virtual 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.
virtual 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

@ -11,7 +11,7 @@ namespace
{
template<typename T>
void listCellScripts (MWWorld::LocalScripts& localScripts,
MWWorld::CellRefList<T>& cellRefList, MWWorld::Ptr::CellStore *cell)
MWWorld::CellRefList<T>& cellRefList, MWWorld::CellStore *cell)
{
for (typename MWWorld::CellRefList<T>::List::iterator iter (
cellRefList.mList.begin());
@ -27,15 +27,15 @@ namespace
// Adds scripts for items in containers (containers/npcs/creatures)
template<typename T>
void listCellScriptsCont (MWWorld::LocalScripts& localScripts,
MWWorld::CellRefList<T>& cellRefList, MWWorld::Ptr::CellStore *cell)
MWWorld::CellRefList<T>& cellRefList, MWWorld::CellStore *cell)
{
for (typename MWWorld::CellRefList<T>::List::iterator iter (
cellRefList.mList.begin());
iter!=cellRefList.mList.end(); ++iter)
{
MWWorld::Ptr containerPtr (&*iter, cell);
MWWorld::Ptr containerPtr (&*iter, cell);
MWWorld::ContainerStore& container = MWWorld::Class::get(containerPtr).getContainerStore(containerPtr);
for(MWWorld::ContainerStoreIterator it3 = container.begin(); it3 != container.end(); ++it3)
{
@ -99,7 +99,7 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr)
}
}
void MWWorld::LocalScripts::addCell (Ptr::CellStore *cell)
void MWWorld::LocalScripts::addCell (CellStore *cell)
{
listCellScripts (*this, cell->mActivators, cell);
listCellScripts (*this, cell->mPotions, cell);
@ -128,7 +128,7 @@ void MWWorld::LocalScripts::clear()
mScripts.clear();
}
void MWWorld::LocalScripts::clearCell (Ptr::CellStore *cell)
void MWWorld::LocalScripts::clearCell (CellStore *cell)
{
std::list<std::pair<std::string, Ptr> >::iterator iter = mScripts.begin();

@ -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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save