From eddd6bf48dca2986c592342dfefa8b7627d0a998 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 15 Nov 2013 10:29:53 +0100 Subject: [PATCH 001/120] enabled load and save items in main menu --- apps/openmw/mwgui/mainmenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index fa7ed2aceb..95753f540e 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -35,8 +35,8 @@ namespace MWGui std::vector buttons; buttons.push_back("return"); buttons.push_back("newgame"); - //buttons.push_back("loadgame"); - //buttons.push_back("savegame"); + buttons.push_back("loadgame"); + buttons.push_back("savegame"); buttons.push_back("options"); //buttons.push_back("credits"); buttons.push_back("exitgame"); From 79b7fa258bfdb8b3a955fd9052e645d6fb49d819 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 10:31:46 +0100 Subject: [PATCH 002/120] added new mwstate subsystem --- apps/openmw/CMakeLists.txt | 4 ++++ apps/openmw/engine.cpp | 9 ++++++--- apps/openmw/mwbase/environment.cpp | 18 +++++++++++++++++- apps/openmw/mwbase/environment.hpp | 6 ++++++ apps/openmw/mwbase/statemanager.hpp | 25 +++++++++++++++++++++++++ apps/openmw/mwstate/statemanagerimp.cpp | 7 +++++++ apps/openmw/mwstate/statemanagerimp.hpp | 17 +++++++++++++++++ 7 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 apps/openmw/mwbase/statemanager.hpp create mode 100644 apps/openmw/mwstate/statemanagerimp.cpp create mode 100644 apps/openmw/mwstate/statemanagerimp.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 04bd89f959..0358b96d13 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -72,6 +72,10 @@ add_openmw_dir (mwmechanics aiescort aiactivate repair enchanting pathfinding security spellsuccess ) +add_openmw_dir (mwstate + statemanagerimp + ) + add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager inputmanager windowmanager diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4a3c418f6c..f900e6cb48 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -7,6 +7,8 @@ #include +#include + #include #include @@ -39,8 +41,7 @@ #include "mwmechanics/mechanicsmanagerimp.hpp" - -#include +#include "mwstate/statemanagerimp.hpp" void OMW::Engine::executeLocalScripts() { @@ -320,6 +321,8 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings) { + mEnvironment.setStateManager (new MWState::StateManager); + Nif::NIFFile::CacheLock cachelock; std::string renderSystem = settings.getString("render system", "Video"); @@ -397,7 +400,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)); diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 6b309025cc..4a629743f9 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -11,13 +11,15 @@ #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 +71,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 +129,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 +165,9 @@ void MWBase::Environment::cleanup() delete mInputManager; mInputManager = 0; + + delete mStateManager; + mStateManager = 0; } const MWBase::Environment& MWBase::Environment::get() diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 4663029077..d7c63601f6 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -11,6 +11,7 @@ namespace MWBase class MechanicsManager; class InputManager; class WindowManager; + class StateManager; /// \brief Central hub for mw-subsystems /// @@ -30,6 +31,7 @@ namespace MWBase DialogueManager *mDialogueManager; Journal *mJournal; InputManager *mInputManager; + StateManager *mStateManager; float mFrameDuration; static bool sExit; @@ -65,6 +67,8 @@ namespace MWBase void setInputManager (InputManager *inputManager); + void setStateManager (StateManager *stateManager); + void setFrameDuration (float duration); ///< Set length of current frame in seconds. @@ -84,6 +88,8 @@ namespace MWBase InputManager *getInputManager() const; + StateManager *getStateManager() const; + float getFrameDuration() const; void cleanup(); diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp new file mode 100644 index 0000000000..42b1dcc81b --- /dev/null +++ b/apps/openmw/mwbase/statemanager.hpp @@ -0,0 +1,25 @@ +#ifndef GAME_MWSTATE_STATEMANAGER_H +#define GAME_MWSTATE_STATEMANAGER_H + +namespace MWBase +{ + /// \brief Interface for game state manager (implemented in MWState) + class StateManager + { + private: + + StateManager (const StateManager&); + ///< not implemented + + StateManager& operator= (const StateManager&); + ///< not implemented + + public: + + StateManager() {} + + virtual ~StateManager() {} + }; +} + +#endif diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp new file mode 100644 index 0000000000..fa82894199 --- /dev/null +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -0,0 +1,7 @@ + +#include "statemanagerimp.hpp" + +MWState::StateManager::StateManager() +{ + +} \ No newline at end of file diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp new file mode 100644 index 0000000000..73cb0a86fe --- /dev/null +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -0,0 +1,17 @@ +#ifndef GAME_STATE_STATEMANAGER_H +#define GAME_STATE_STATEMANAGER_H + +#include "../mwbase/statemanager.hpp" + +namespace MWState +{ + class StateManager : public MWBase::StateManager + { + public: + + StateManager(); + + }; +} + +#endif From f19973450f4ed6fddece051a11522d8ac1f7bad4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 11:07:23 +0100 Subject: [PATCH 003/120] moved exit game flag from Environment to StateManager --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 1 - apps/openmw/mwbase/environment.hpp | 5 ----- apps/openmw/mwbase/statemanager.hpp | 4 ++++ apps/openmw/mwgui/mainmenu.cpp | 5 ++--- apps/openmw/mwinput/inputmanagerimp.cpp | 3 ++- apps/openmw/mwstate/statemanagerimp.cpp | 11 +++++++++++ apps/openmw/mwstate/statemanagerimp.hpp | 5 +++++ 8 files changed, 25 insertions(+), 11 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f900e6cb48..d11e72f757 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -487,7 +487,7 @@ void OMW::Engine::go() MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); // Start the main rendering loop - while (!mEnvironment.getRequestExit()) + while (!mEnvironment.get().getStateManager()->hasQuitRequest()) Ogre::Root::getSingleton().renderOneFrame(); // Save user settings diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 4a629743f9..052bba9abf 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -14,7 +14,6 @@ #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), diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index d7c63601f6..eb636ea2fb 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -34,8 +34,6 @@ namespace MWBase StateManager *mStateManager; float mFrameDuration; - static bool sExit; - Environment (const Environment&); ///< not implemented @@ -48,9 +46,6 @@ namespace MWBase ~Environment(); - static void setRequestExit () { sExit = true; } - static bool getRequestExit () { return sExit; } - void setWorld (World *world); void setSoundManager (SoundManager *soundManager); diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 42b1dcc81b..e906872933 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -19,6 +19,10 @@ namespace MWBase StateManager() {} virtual ~StateManager() {} + + virtual void requestQuit() = 0; + + virtual bool hasQuitRequest() const = 0; }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 95753f540e..a58a3d0ebc 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -1,13 +1,12 @@ #include "mainmenu.hpp" -#include - #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 "savegamedialog.hpp" @@ -79,7 +78,7 @@ namespace MWGui else if (sender == mButtons["options"]) MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); else if (sender == mButtons["exitgame"]) - MWBase::Environment::get().setRequestExit(); + MWBase::Environment::get().getStateManager()->requestQuit(); else if (sender == mButtons["newgame"]) { MWBase::Environment::get().getWorld()->startNewGame(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 35487e3391..c25e9ce526 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -19,6 +19,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwgui/bookwindow.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -644,7 +645,7 @@ namespace MWInput void InputManager::windowClosed() { - MWBase::Environment::setRequestExit(); + MWBase::Environment::get().getStateManager()->requestQuit(); } void InputManager::toggleMainMenu() diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index fa82894199..f5e71d0ce6 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -2,6 +2,17 @@ #include "statemanagerimp.hpp" MWState::StateManager::StateManager() +: mQuitRequest (false) { +} + +void MWState::StateManager::requestQuit() +{ + mQuitRequest = true; +} + +bool MWState::StateManager::hasQuitRequest() const +{ + return mQuitRequest; } \ No newline at end of file diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 73cb0a86fe..271403ce5c 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -7,10 +7,15 @@ namespace MWState { class StateManager : public MWBase::StateManager { + bool mQuitRequest; + public: StateManager(); + virtual void requestQuit(); + + virtual bool hasQuitRequest() const; }; } From 7a4b6043763598b8dc25d7b3e33555f792c6dc9e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 11:33:20 +0100 Subject: [PATCH 004/120] added --skip-menu switch --- apps/openmw/engine.cpp | 10 ++++++++++ apps/openmw/engine.hpp | 3 +++ apps/openmw/main.cpp | 4 ++++ 3 files changed, 17 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d11e72f757..47cd01a800 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -139,6 +139,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mFpsLevel(0) , mVerboseScripts (false) , mNewGame (false) + , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) , mScriptContext (0) @@ -282,6 +283,11 @@ void OMW::Engine::setNewGame(bool newGame) mNewGame = newGame; } +void OMW::Engine::setSkipMenu (bool skipMenu) +{ + mSkipMenu = skipMenu; +} + std::string OMW::Engine::loadSettings (Settings::Manager & settings) { // Create the settings manager and load default settings file @@ -486,6 +492,10 @@ 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); + // Start the main rendering loop while (!mEnvironment.get().getStateManager()->hasQuitRequest()) Ogre::Root::getSingleton().renderOneFrame(); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 553d290687..72d2041b8f 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -72,6 +72,7 @@ namespace OMW int mFpsLevel; bool mVerboseScripts; bool mNewGame; + bool mSkipMenu; bool mUseSound; bool mCompileAll; std::string mFocusName; @@ -152,6 +153,8 @@ namespace OMW /// Start as a new game. void setNewGame(bool newGame); + void setSkipMenu (bool skipMenu); + /// Initialise and enter main loop. void go(); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 33f740b311..8c6bdf8a66 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -134,6 +134,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("new-game", bpo::value()->implicit_value(true) ->default_value(false), "activate char gen/new game mechanics") + ("skip-menu", bpo::value()->implicit_value(true) + ->default_value(false), "skip main menu on game startup") + ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") @@ -223,6 +226,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // startup-settings engine.setCell(variables["start"].as()); engine.setNewGame(variables["new-game"].as()); + engine.setSkipMenu (variables["skip-menu"].as()); // other settings engine.setSoundUsage(!variables["nosound"].as()); From b3a7c8c0980d23a3614f1b557d8a81241b1eff4a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 11:36:32 +0100 Subject: [PATCH 005/120] removed --new-game switch --- apps/openmw/engine.cpp | 33 +++++++++------------------------ apps/openmw/engine.hpp | 4 ---- apps/openmw/main.cpp | 4 ---- 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 47cd01a800..8a02c855a2 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -138,7 +138,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) : mOgre (0) , mFpsLevel(0) , mVerboseScripts (false) - , mNewGame (false) , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) @@ -278,11 +277,6 @@ void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) mVerboseScripts = scriptsVerbosity; } -void OMW::Engine::setNewGame(bool newGame) -{ - mNewGame = newGame; -} - void OMW::Engine::setSkipMenu (bool skipMenu) { mSkipMenu = skipMenu; @@ -395,10 +389,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 @@ -430,22 +420,17 @@ 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 (world->findExteriorPosition(mCellName, pos)) { - world->changeToExteriorCell (pos); - } - else { - world->findInteriorPosition(mCellName, pos); - world->changeToInteriorCell (mCellName, pos); - } + if (world->findExteriorPosition(mCellName, pos)) { + world->changeToExteriorCell (pos); + } + else { + world->findInteriorPosition(mCellName, pos); + world->changeToInteriorCell (mCellName, pos); } - else - mEnvironment.getWorld()->startNewGame(); Ogre::FrameEvent event; event.timeSinceLastEvent = 0; diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 72d2041b8f..02fb737052 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -71,7 +71,6 @@ namespace OMW std::vector mContentFiles; int mFpsLevel; bool mVerboseScripts; - bool mNewGame; bool mSkipMenu; bool mUseSound; bool mCompileAll; @@ -150,9 +149,6 @@ namespace OMW /// Disable or enable all sounds void setSoundUsage(bool soundUsage); - /// Start as a new game. - void setNewGame(bool newGame); - void setSkipMenu (bool skipMenu); /// Initialise and enter main loop. diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 8c6bdf8a66..2bdfb91c35 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -131,9 +131,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") - ("new-game", bpo::value()->implicit_value(true) - ->default_value(false), "activate char gen/new game mechanics") - ("skip-menu", bpo::value()->implicit_value(true) ->default_value(false), "skip main menu on game startup") @@ -225,7 +222,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // startup-settings engine.setCell(variables["start"].as()); - engine.setNewGame(variables["new-game"].as()); engine.setSkipMenu (variables["skip-menu"].as()); // other settings From 31ec973c9c07ac54e5f57962d6597ddcee050e3f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 12:08:00 +0100 Subject: [PATCH 006/120] removed default value for --start --- apps/openmw/engine.cpp | 20 ++++++++++++++------ apps/openmw/main.cpp | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 8a02c855a2..cfb5522f15 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -424,12 +424,21 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); - if (world->findExteriorPosition(mCellName, pos)) { - world->changeToExteriorCell (pos); + if (!mCellName.empty()) + { + if (world->findExteriorPosition(mCellName, pos)) { + world->changeToExteriorCell (pos); + } + else { + world->findInteriorPosition(mCellName, pos); + world->changeToInteriorCell (mCellName, pos); + } } - else { - world->findInteriorPosition(mCellName, pos); - world->changeToInteriorCell (mCellName, pos); + else + { + 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; @@ -456,7 +465,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) void OMW::Engine::go() { - assert (!mCellName.empty()); assert (!mContentFiles.empty()); assert (!mOgre); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 2bdfb91c35..89613fda47 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -107,7 +107,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("resources", bpo::value()->default_value("resources"), "set resources directory") - ("start", bpo::value()->default_value("Beshara"), + ("start", bpo::value()->default_value(""), "set initial cell") ("content", bpo::value()->default_value(StringsVector(), "") From ec5b2e9a7e90f3e9c3eb8ebb59f3d50995845dad Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 12:22:28 +0100 Subject: [PATCH 007/120] added running flag; moved new game code to MWState --- apps/openmw/engine.cpp | 2 ++ apps/openmw/mwbase/statemanager.hpp | 7 +++++ apps/openmw/mwgui/mainmenu.cpp | 5 +--- apps/openmw/mwstate/statemanagerimp.cpp | 34 +++++++++++++++++++++++-- apps/openmw/mwstate/statemanagerimp.hpp | 8 ++++++ 5 files changed, 50 insertions(+), 6 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index cfb5522f15..c013cdaaec 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -488,6 +488,8 @@ void OMW::Engine::go() // 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.get().getStateManager()->hasQuitRequest()) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index e906872933..076e66b8c4 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -23,6 +23,13 @@ namespace MWBase virtual void requestQuit() = 0; virtual bool hasQuitRequest() const = 0; + + virtual bool isGameRunning() const = 0; + + virtual void newGame (bool bypass = false) = 0; + ///< Start a new game. + /// + /// \param bypass Skip new game mechanics. }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index a58a3d0ebc..cb34367150 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -81,10 +81,7 @@ namespace MWGui MWBase::Environment::get().getStateManager()->requestQuit(); else if (sender == mButtons["newgame"]) { - MWBase::Environment::get().getWorld()->startNewGame(); - MWBase::Environment::get().getWindowManager()->setNewGame(true); - MWBase::Environment::get().getDialogueManager()->clear(); - MWBase::Environment::get().getJournal()->clear(); + MWBase::Environment::get().getStateManager()->newGame(); } else if (sender == mButtons["loadgame"]) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f5e71d0ce6..a226b166d9 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -1,8 +1,14 @@ #include "statemanagerimp.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/windowmanager.hpp" + MWState::StateManager::StateManager() -: mQuitRequest (false) +: mQuitRequest (false), mRunning (false) { } @@ -15,4 +21,28 @@ void MWState::StateManager::requestQuit() bool MWState::StateManager::hasQuitRequest() const { return mQuitRequest; -} \ No newline at end of file +} + +bool MWState::StateManager::isGameRunning() const +{ + return mRunning; +} + +void MWState::StateManager::newGame (bool bypass) +{ + if (mRunning) + { + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + mRunning = false; + } + + if (!bypass) + { + /// \todo extract cleanup code + MWBase::Environment::get().getWorld()->startNewGame(); + MWBase::Environment::get().getWindowManager()->setNewGame (true); + } + + mRunning = true; +} diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 271403ce5c..9f8096a4a9 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -8,6 +8,7 @@ namespace MWState class StateManager : public MWBase::StateManager { bool mQuitRequest; + bool mRunning; public: @@ -16,6 +17,13 @@ namespace MWState virtual void requestQuit(); virtual bool hasQuitRequest() const; + + virtual bool isGameRunning() const; + + virtual void newGame (bool bypass = false); + ///< Start a new game. + /// + /// \param bypass Skip new game mechanics. }; } From c5f81e3508e37ac7055728cb2cb197a84ba1dd0c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 17:46:48 +0100 Subject: [PATCH 008/120] don't run udpates if no game is running --- apps/openmw/engine.cpp | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index c013cdaaec..741f7564f5 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -92,30 +92,31 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) MWBase::Environment::get().getSoundManager()->update(frametime); // global scripts - MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); + if (MWBase::Environment::get().getStateManager()->isGameRunning()) + { + 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. - // passing of time - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->advanceTime( - frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); + if (changed) // keep change flag for another frame, if cell changed happened in local script + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWorld()->advanceTime( + frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); - 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()); - // update actors - MWBase::Environment::get().getMechanicsManager()->update(frametime, - MWBase::Environment::get().getWindowManager()->isGuiMode()); - - // update world - MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); + // update world + MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); + } // update GUI Ogre::RenderWindow* window = mOgre->getWindow(); From 82c84953383aad3dbf2f8466f93a594b87318495 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 18 Nov 2013 15:15:47 +0100 Subject: [PATCH 009/120] removed boolean running flag with state enum --- apps/openmw/engine.cpp | 3 ++- apps/openmw/mwbase/statemanager.hpp | 11 ++++++++++- apps/openmw/mwstate/statemanagerimp.cpp | 12 ++++++------ apps/openmw/mwstate/statemanagerimp.hpp | 4 ++-- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 741f7564f5..4de198b643 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -92,7 +92,8 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) MWBase::Environment::get().getSoundManager()->update(frametime); // global scripts - if (MWBase::Environment::get().getStateManager()->isGameRunning()) + if (MWBase::Environment::get().getStateManager()->getState()== + MWBase::StateManager::State_Running) { MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 076e66b8c4..4fd1a297d0 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -6,6 +6,15 @@ namespace MWBase /// \brief Interface for game state manager (implemented in MWState) class StateManager { + public: + + enum State + { + State_NoGame, + State_Ended, + State_Running + }; + private: StateManager (const StateManager&); @@ -24,7 +33,7 @@ namespace MWBase virtual bool hasQuitRequest() const = 0; - virtual bool isGameRunning() const = 0; + virtual State getState() const = 0; virtual void newGame (bool bypass = false) = 0; ///< Start a new game. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a226b166d9..c4cd45c8eb 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -8,7 +8,7 @@ #include "../mwbase/windowmanager.hpp" MWState::StateManager::StateManager() -: mQuitRequest (false), mRunning (false) +: mQuitRequest (false), mState (State_NoGame) { } @@ -23,18 +23,18 @@ bool MWState::StateManager::hasQuitRequest() const return mQuitRequest; } -bool MWState::StateManager::isGameRunning() const +MWState::StateManager::State MWState::StateManager::getState() const { - return mRunning; + return mState; } void MWState::StateManager::newGame (bool bypass) { - if (mRunning) + if (mState!=State_NoGame) { MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); - mRunning = false; + mState = State_NoGame; } if (!bypass) @@ -44,5 +44,5 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getWindowManager()->setNewGame (true); } - mRunning = true; + mState = State_Running; } diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 9f8096a4a9..078a899b23 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -8,7 +8,7 @@ namespace MWState class StateManager : public MWBase::StateManager { bool mQuitRequest; - bool mRunning; + State mState; public: @@ -18,7 +18,7 @@ namespace MWState virtual bool hasQuitRequest() const; - virtual bool isGameRunning() const; + virtual State getState() const; virtual void newGame (bool bypass = false); ///< Start a new game. From f45cff8aff4d8bf29fd0a3200f78bb565cda0cb5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 18 Nov 2013 15:38:08 +0100 Subject: [PATCH 010/120] flag game as ended when player dies --- apps/openmw/mwbase/statemanager.hpp | 2 ++ apps/openmw/mwmechanics/actors.cpp | 25 +++++++++++++++---------- apps/openmw/mwstate/statemanagerimp.cpp | 5 +++++ apps/openmw/mwstate/statemanagerimp.hpp | 2 ++ 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 4fd1a297d0..74fcc3f7ad 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -39,6 +39,8 @@ namespace MWBase ///< Start a new game. /// /// \param bypass Skip new game mechanics. + + virtual void endGame() = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 3d52ce8e69..cc431a7b04 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -17,6 +17,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" + #include "../mwbase/statemanager.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" @@ -344,20 +345,24 @@ namespace MWMechanics continue; } - // If it's the player and God Mode is turned on, keep it alive - if(iter->first.getRefData().getHandle()=="player" && - MWBase::Environment::get().getWorld()->getGodModeState()) + if (iter->first.getRefData().getHandle()=="player") { - MWMechanics::DynamicStat stat(stats.getHealth()); - - if(stat.getModified()<1) + // If it's the player and God Mode is turned on, keep it alive + if (MWBase::Environment::get().getWorld()->getGodModeState()) { - stat.setModified(1, 0); - stats.setHealth(stat); + MWMechanics::DynamicStat stat (stats.getHealth()); + + if (stat.getModified()<1) + { + stat.setModified(1, 0); + stats.setHealth(stat); + } + + stats.resurrect(); + continue; } - stats.resurrect(); - continue; + MWBase::Environment::get().getStateManager()->endGame(); } if(iter->second->isDead()) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index c4cd45c8eb..66a44872c4 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -46,3 +46,8 @@ void MWState::StateManager::newGame (bool bypass) mState = State_Running; } + +void MWState::StateManager::endGame() +{ + mState = State_Ended; +} diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 078a899b23..007a9b1369 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -24,6 +24,8 @@ namespace MWState ///< Start a new game. /// /// \param bypass Skip new game mechanics. + + virtual void endGame(); }; } From 1c7a4d4b3a2556c3064f622355e0200c91759a11 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 18 Nov 2013 15:52:25 +0100 Subject: [PATCH 011/120] adjust availability of main menu items based on game state --- apps/openmw/mwgui/mainmenu.cpp | 110 ++++++++++++++++++++------------- apps/openmw/mwgui/mainmenu.hpp | 23 ++++--- 2 files changed, 84 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index cb34367150..d4a4e74ba7 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -15,56 +15,25 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") - , mButtonBox(0) + , mButtonBox(0), mWidth (w), mHeight (h) { - onResChange(w,h); + updateMenu(); } void MainMenu::onResChange(int w, int h) { - setCoord(0,0,w,h); + mWidth = w; + mHeight = h; + updateMenu(); + } - if (mButtonBox) - MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); + void MainMenu::setVisible (bool visible) + { + if (visible) + updateMenu(); - mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); - int curH = 0; - - std::vector buttons; - buttons.push_back("return"); - buttons.push_back("newgame"); - buttons.push_back("loadgame"); - buttons.push_back("savegame"); - buttons.push_back("options"); - //buttons.push_back("credits"); - buttons.push_back("exitgame"); - - int maxwidth = 0; - - mButtons.clear(); - for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) - { - MWGui::ImageButton* button = mButtonBox->createWidget - ("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; - } - for (std::map::iterator it = mButtons.begin(); it != mButtons.end(); ++it) - { - MyGUI::IntSize requested = it->second->getRequestedSize(); - it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height); - } - - mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH); + OEngine::GUI::Layout::setVisible (visible); } void MainMenu::onButtonClicked(MyGUI::Widget *sender) @@ -98,4 +67,61 @@ namespace MWGui } } + void MainMenu::updateMenu() + { + setCoord(0,0, mWidth, mHeight); + + + if (mButtonBox) + MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); + + mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); + int curH = 0; + + MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState(); + + std::vector buttons; + + if (state==MWBase::StateManager::State_Running) + buttons.push_back("return"); + + buttons.push_back("newgame"); + + /// \todo hide, if no saved game is available + buttons.push_back("loadgame"); + + if (state==MWBase::StateManager::State_Running) + buttons.push_back("savegame"); + + buttons.push_back("options"); + //buttons.push_back("credits"); + buttons.push_back("exitgame"); + + int maxwidth = 0; + + mButtons.clear(); + for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) + { + MWGui::ImageButton* button = mButtonBox->createWidget + ("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; + } + for (std::map::iterator it = mButtons.begin(); it != mButtons.end(); ++it) + { + MyGUI::IntSize requested = it->second->getRequestedSize(); + it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height); + } + + mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH); + + } } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 4e76a64df7..511f726729 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -7,17 +7,26 @@ namespace MWGui class MainMenu : public OEngine::GUI::Layout { - public: - MainMenu(int w, int h); + int mWidth; + int mHeight; - void onResChange(int w, int h); + public: - private: - MyGUI::Widget* mButtonBox; + MainMenu(int w, int h); - std::map mButtons; + void onResChange(int w, int h); - void onButtonClicked (MyGUI::Widget* sender); + virtual void setVisible (bool visible); + + private: + + MyGUI::Widget* mButtonBox; + + std::map mButtons; + + void onButtonClicked (MyGUI::Widget* sender); + + void updateMenu(); }; } From dc75627d53b3404458276cb84db822db4c23f8bd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 10:51:30 +0100 Subject: [PATCH 012/120] added secondary saved game header record --- components/CMakeLists.txt | 1 + components/esm/defs.hpp | 3 +++ components/esm/savedgame.cpp | 36 +++++++++++++++++++++++++++++++ components/esm/savedgame.hpp | 41 ++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 components/esm/savedgame.cpp create mode 100644 components/esm/savedgame.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 59fb084a8e..ce5965be12 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,6 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter + savedgame ) add_component_dir (misc diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index dd7ebfe932..5a5ef9f1cf 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -83,6 +83,9 @@ enum RecNameInts REC_STAT = 0x54415453, REC_WEAP = 0x50414557, + // format 0 - saved games + REC_SAVE = 0x45564153, + // format 1 REC_FILT = 0x544C4946 }; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp new file mode 100644 index 0000000000..a717c6469f --- /dev/null +++ b/components/esm/savedgame.cpp @@ -0,0 +1,36 @@ + +#include "savedgame.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" +#include "defs.hpp" + +unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; + +void ESM::SavedGame::load (ESMReader &esm) +{ + mPlayerName = esm.getHNString("PNAM"); + esm.getHNOT (mPlayerLevel, "PLEV"); + mPlayerClass = esm.getHNString("PCLA"); + mPlayerCell = esm.getHNString("PCEL"); + esm.getHNT (mInGameTime, "TSTM", 16); + esm.getHNT (mTimePlayed, "TIME"); + + while (esm.isNextSub ("DEPE")) + mContentFiles.push_back (esm.getHString()); +} + +void ESM::SavedGame::save (ESMWriter &esm) const +{ + esm.writeHNCString (mPlayerName, "PNAM"); + esm.writeHNT ("PLEV", mPlayerLevel); + esm.writeHNCString (mPlayerClass, "PCLA"); + esm.writeHNCString (mPlayerCell, "PCEL"); + esm.writeHNT ("TSTM", mInGameTime, 16); + esm.writeHNT ("TIME", mTimePlayed); + + for (std::vector::const_iterator iter (mContentFiles.begin()); + iter!=mContentFiles.end(); ++iter) + esm.writeHNCString (*iter, "DEPE"); + +} diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp new file mode 100644 index 0000000000..ae8f79263d --- /dev/null +++ b/components/esm/savedgame.hpp @@ -0,0 +1,41 @@ +#ifndef OPENMW_ESM_SAVEDGAME_H +#define OPENMW_ESM_SAVEDGAME_H + +#include +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct SavedGame + { + static unsigned int sRecordId; + + struct TimeStamp + { + float mGameHour; + int mDay; + int mMonth; + int mYear; + }; + + std::vector mContentFiles; + std::string mPlayerName; + int mPlayerLevel; + std::string mPlayerClass; + std::string mPlayerCell; + TimeStamp mInGameTime; + float mTimePlayed; + + /// \todo add field for screenshot + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif From 903e867c24559740c9e16fc193e9e09d8cca6ea4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 13:44:18 +0100 Subject: [PATCH 013/120] change to TES3 record (moved format field to the top) --- components/esm/loadtes3.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index 87a8d1d57e..e5d6ec8370 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -19,8 +19,6 @@ void ESM::Header::blank() void ESM::Header::load (ESMReader &esm) { - esm.getHNT (mData, "HEDR", 300); - if (esm.isNextSub ("FORM")) { esm.getHT (mFormat); @@ -30,6 +28,8 @@ void ESM::Header::load (ESMReader &esm) else mFormat = 0; + esm.getHNT (mData, "HEDR", 300); + while (esm.isNextSub ("MAST")) { MasterData m; @@ -41,11 +41,11 @@ void ESM::Header::load (ESMReader &esm) void ESM::Header::save (ESMWriter &esm) { - esm.writeHNT ("HEDR", mData, 300); - if (mFormat>0) esm.writeHNT ("FORM", mFormat); + esm.writeHNT ("HEDR", mData, 300); + for (std::vector::iterator iter = mMaster.begin(); iter != mMaster.end(); ++iter) { From 35bf98a940aeb95fee28529869388a409c5a8611 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 13:46:24 +0100 Subject: [PATCH 014/120] modified esm reader/writer to use UTF8 when no encoder is given --- components/esm/esmreader.cpp | 5 ++++- components/esm/esmwriter.cpp | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 51d86a2eeb..f02ed2d6e1 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -303,7 +303,10 @@ std::string ESMReader::getString(int size) getExact(ptr, size); // Convert to UTF8 and return - return mEncoder->getUtf8(ptr, size); + if (mEncoder) + return mEncoder->getUtf8(ptr, size); + + return std::string (ptr, size); } void ESMReader::fail(const std::string &msg) diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index f39aa2b898..c9ef61b635 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -6,7 +6,7 @@ namespace ESM { - ESMWriter::ESMWriter() : mRecordCount (0), mCounting (true) {} + ESMWriter::ESMWriter() : mEncoder (0), mRecordCount (0), mCounting (true) {} unsigned int ESMWriter::getVersion() const { @@ -152,9 +152,9 @@ namespace ESM else { // Convert to UTF8 and return - std::string ascii = mEncoder->getLegacyEnc(data); + std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; - write(ascii.c_str(), ascii.size()); + write(string.c_str(), string.size()); } } From 4c61deca8df2ea4167091a4598a525f61e5d49b6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 15:31:39 +0100 Subject: [PATCH 015/120] fixed save code for SavedGame record --- components/esm/savedgame.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index a717c6469f..5a5fc9fa83 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -22,10 +22,10 @@ void ESM::SavedGame::load (ESMReader &esm) void ESM::SavedGame::save (ESMWriter &esm) const { - esm.writeHNCString (mPlayerName, "PNAM"); + esm.writeHNCString ("PNAM", mPlayerName); esm.writeHNT ("PLEV", mPlayerLevel); - esm.writeHNCString (mPlayerClass, "PCLA"); - esm.writeHNCString (mPlayerCell, "PCEL"); + esm.writeHNCString ("PCLA", mPlayerClass); + esm.writeHNCString ("PCEL", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); From 5e64888227f3f8e6a7796cf74ce70ac268bed5ae Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 15:38:26 +0100 Subject: [PATCH 016/120] added basic save slot management and connected main menu save to save function (bypassing the save GUI for now) --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/statemanager.hpp | 14 ++++ apps/openmw/mwgui/mainmenu.cpp | 7 +- apps/openmw/mwstate/character.cpp | 101 +++++++++++++++++++++++ apps/openmw/mwstate/character.hpp | 45 ++++++++++ apps/openmw/mwstate/charactermanager.cpp | 57 +++++++++++++ apps/openmw/mwstate/charactermanager.hpp | 37 +++++++++ apps/openmw/mwstate/statemanagerimp.cpp | 31 ++++++- apps/openmw/mwstate/statemanagerimp.hpp | 15 +++- 10 files changed, 304 insertions(+), 9 deletions(-) create mode 100644 apps/openmw/mwstate/character.cpp create mode 100644 apps/openmw/mwstate/character.hpp create mode 100644 apps/openmw/mwstate/charactermanager.cpp create mode 100644 apps/openmw/mwstate/charactermanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 0358b96d13..2b078a7ff0 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -73,12 +73,12 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwstate - statemanagerimp + statemanagerimp charactermanager character ) add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager - inputmanager windowmanager + inputmanager windowmanager statemanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4de198b643..cda0683470 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -323,7 +323,7 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings) { - mEnvironment.setStateManager (new MWState::StateManager); + mEnvironment.setStateManager (new MWState::StateManager (mCfgMgr.getUserPath() / "saves")); Nif::NIFFile::CacheLock cachelock; diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 74fcc3f7ad..b341fbb034 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -1,6 +1,12 @@ #ifndef GAME_MWSTATE_STATEMANAGER_H #define GAME_MWSTATE_STATEMANAGER_H +namespace MWState +{ + struct Slot; + class Character; +} + namespace MWBase { /// \brief Interface for game state manager (implemented in MWState) @@ -41,6 +47,14 @@ namespace MWBase /// \param bypass Skip new game mechanics. virtual void endGame() = 0; + + virtual void saveGame (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 MWState::Character *getCurrentCharacter() = 0; + ///< Must not be called, if there is no current character. }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index d4a4e74ba7..f25b72d377 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -61,9 +61,10 @@ namespace MWGui } else if (sender == mButtons["savegame"]) { - MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); - dialog->setLoadOrSave(false); - dialog->setVisible(true); + MWBase::Environment::get().getStateManager()->saveGame (0); +// MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); +// dialog->setLoadOrSave(false); +// dialog->setVisible(true); } } diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp new file mode 100644 index 0000000000..dc16085d83 --- /dev/null +++ b/apps/openmw/mwstate/character.cpp @@ -0,0 +1,101 @@ + +#include "character.hpp" + +#include + +#include +#include +#include + +#include + +bool MWState::operator< (const Slot& left, const Slot& right) +{ + return left.mTimeStamp> 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 (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(); +} \ No newline at end of file diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp new file mode 100644 index 0000000000..30182e4047 --- /dev/null +++ b/apps/openmw/mwstate/character.hpp @@ -0,0 +1,45 @@ +#ifndef GAME_STATE_CHARACTER_H +#define GAME_STATE_CHARACTER_H + +#include + +#include + +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 + { + boost::filesystem::path mPath; + std::vector mSlots; + int mNext; + + void addSlot (const boost::filesystem::path& path); + + void addSlot (const ESM::SavedGame& profile); + + public: + + Character (const boost::filesystem::path& saves); + + 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 \æ slot pointer will be invalidated by this call. + }; +} + +#endif diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp new file mode 100644 index 0000000000..c73adb5bb9 --- /dev/null +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -0,0 +1,57 @@ + +#include "charactermanager.hpp" + +#include +#include + +#include + +MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves) +: mPath (saves), mNext (0), mCurrent (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 characterDir = *iter; + + if (boost::filesystem::is_directory (characterDir)) + { + Character character (characterDir); + 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() +{ + if (!mCurrent) + throw std::logic_error ("no character selected"); + + return mCurrent; +} + +void MWState::CharacterManager::createCharacter() +{ + std::ostringstream stream; + stream << mNext++; + + boost::filesystem::path path = mPath / stream.str(); + + mCharacters.push_back (Character (path)); + + mCurrent = &mCharacters.back(); +} \ No newline at end of file diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp new file mode 100644 index 0000000000..a3fe171314 --- /dev/null +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -0,0 +1,37 @@ +#ifndef GAME_STATE_CHARACTERMANAGER_H +#define GAME_STATE_CHARACTERMANAGER_H + +#include + +#include "character.hpp" + +namespace MWState +{ + class CharacterManager + { + boost::filesystem::path mPath; + int mNext; + std::vector mCharacters; + Character *mCurrent; + + private: + + CharacterManager (const CharacterManager&); + ///< Not implemented + + CharacterManager& operator= (const CharacterManager&); + ///< Not implemented + + public: + + CharacterManager (const boost::filesystem::path& saves); + + Character *getCurrentCharacter(); + ///< Must not be called, if there is no current character. + + void createCharacter(); + ///< Create new character within saved game management + }; +} + +#endif diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 66a44872c4..f068093a00 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -1,14 +1,16 @@ #include "statemanagerimp.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/windowmanager.hpp" -MWState::StateManager::StateManager() -: mQuitRequest (false), mState (State_NoGame) +MWState::StateManager::StateManager (const boost::filesystem::path& saves) +: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves) { } @@ -44,6 +46,8 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getWindowManager()->setNewGame (true); } + mCharacterManager.createCharacter(); + mState = State_Running; } @@ -51,3 +55,26 @@ void MWState::StateManager::endGame() { mState = State_Ended; } + +void MWState::StateManager::saveGame (const Slot *slot) +{ + ESM::SavedGame profile; + + /// \todo configure profile + + if (!slot) + slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); + else + slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); + + ESM::ESMWriter writer; +// writer.setFormat (); + writer.save (slot->mPath.string()); + slot->mProfile.save (writer); + writer.close(); +} + +MWState::Character *MWState::StateManager::getCurrentCharacter() +{ + return mCharacterManager.getCurrentCharacter(); +} \ No newline at end of file diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 007a9b1369..9dfcc4d8fd 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -3,16 +3,21 @@ #include "../mwbase/statemanager.hpp" +#include + +#include "charactermanager.hpp" + namespace MWState { class StateManager : public MWBase::StateManager { bool mQuitRequest; State mState; + CharacterManager mCharacterManager; public: - StateManager(); + StateManager (const boost::filesystem::path& saves); virtual void requestQuit(); @@ -26,6 +31,14 @@ namespace MWState /// \param bypass Skip new game mechanics. virtual void endGame(); + + virtual void saveGame (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 Character *getCurrentCharacter(); + ///< Must not be called, if there is no current character. }; } From 9487bd33c3057f5d3f55e2b23ee108fa62778995 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 16:07:36 +0100 Subject: [PATCH 017/120] removed broken save function from ESMWriter --- apps/openmw/mwstate/statemanagerimp.cpp | 5 ++++- components/esm/esmwriter.cpp | 6 ------ components/esm/esmwriter.hpp | 3 --- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f068093a00..4b1d9e0f6e 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -67,10 +67,13 @@ void MWState::StateManager::saveGame (const Slot *slot) else slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); + std::ofstream stream (slot->mPath.string().c_str()); ESM::ESMWriter writer; // writer.setFormat (); - writer.save (slot->mPath.string()); + writer.save (stream); + writer.startRecord ("SAVE"); slot->mProfile.save (writer); + writer.endRecord ("SAVE"); writer.close(); } diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index c9ef61b635..069d75c7bb 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -51,12 +51,6 @@ namespace ESM mHeader.mMaster.push_back(d); } - void ESMWriter::save(const std::string& file) - { - std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc); - save(fs); - } - void ESMWriter::save(std::ostream& file) { mRecordCount = 0; diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 104f97f909..d6646471b8 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -36,9 +36,6 @@ class ESMWriter void addMaster(const std::string& name, uint64_t size); - void save(const std::string& file); - ///< Start saving a file by writing the TES3 header. - void save(std::ostream& file); ///< Start saving a file by writing the TES3 header. From d6e2701dd605fb0c8a4972e92ba35fd391101be1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 10:10:41 +0100 Subject: [PATCH 018/120] changed played time data type from float to double --- components/esm/savedgame.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index ae8f79263d..c0e6f1aeb3 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -29,7 +29,7 @@ namespace ESM std::string mPlayerClass; std::string mPlayerCell; TimeStamp mInGameTime; - float mTimePlayed; + double mTimePlayed; /// \todo add field for screenshot From e938c5a0eec1c10929c177415af086d8e4906e93 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 10:20:50 +0100 Subject: [PATCH 019/120] changed character creation logic (create on save instead of on new game) --- apps/openmw/mwbase/statemanager.hpp | 1 - apps/openmw/mwstate/charactermanager.cpp | 7 ++++++- apps/openmw/mwstate/charactermanager.hpp | 4 +++- apps/openmw/mwstate/statemanagerimp.cpp | 3 +-- apps/openmw/mwstate/statemanagerimp.hpp | 1 - 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index b341fbb034..3aa464ebeb 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -54,7 +54,6 @@ namespace MWBase /// \note Slot must belong to the current character. virtual MWState::Character *getCurrentCharacter() = 0; - ///< Must not be called, if there is no current character. }; } diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index c73adb5bb9..6eccb63dca 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -39,7 +39,7 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save MWState::Character *MWState::CharacterManager::getCurrentCharacter() { if (!mCurrent) - throw std::logic_error ("no character selected"); + createCharacter(); return mCurrent; } @@ -54,4 +54,9 @@ void MWState::CharacterManager::createCharacter() mCharacters.push_back (Character (path)); mCurrent = &mCharacters.back(); +} + +void MWState::CharacterManager::clearCurrentCharacter() +{ + mCurrent = 0; } \ No newline at end of file diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index a3fe171314..543e0c94af 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -27,10 +27,12 @@ namespace MWState CharacterManager (const boost::filesystem::path& saves); Character *getCurrentCharacter(); - ///< Must not be called, if there is no current character. + ///< A character is implicitly created, if there is none. void createCharacter(); ///< Create new character within saved game management + + void clearCurrentCharacter(); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 4b1d9e0f6e..8f22408fe5 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -37,6 +37,7 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); mState = State_NoGame; + mCharacterManager.clearCurrentCharacter(); } if (!bypass) @@ -46,8 +47,6 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getWindowManager()->setNewGame (true); } - mCharacterManager.createCharacter(); - mState = State_Running; } diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 9dfcc4d8fd..8d1ff0641c 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -38,7 +38,6 @@ namespace MWState /// \note Slot must belong to the current character. virtual Character *getCurrentCharacter(); - ///< Must not be called, if there is no current character. }; } From fc1501a5109a93316674afe2e29b5ae90f0b849a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 10:53:42 +0100 Subject: [PATCH 020/120] store character profile information in saved game file --- apps/openmw/mwstate/statemanagerimp.cpp | 20 +++++++++++++++++++- components/esm/savedgame.hpp | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 8f22408fe5..0f4e1e020d 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -9,6 +9,11 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" + MWState::StateManager::StateManager (const boost::filesystem::path& saves) : mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves) { @@ -59,7 +64,20 @@ void MWState::StateManager::saveGame (const Slot *slot) { ESM::SavedGame profile; - /// \todo configure profile + MWBase::World& world = *MWBase::Environment::get().getWorld(); + + MWWorld::Ptr player = world.getPlayer().getPlayer(); + + /// \todo store content file list + profile.mPlayerName = player.getClass().getName (player); + profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); + profile.mPlayerClass = player.get()->mBase->mId; + /// \todo player cell + /// \todo gamehour + profile.mInGameTime.mDay = world.getDay(); + profile.mInGameTime.mMonth = world.getMonth(); + /// \todo year + /// \todo time played if (!slot) slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index c0e6f1aeb3..e712e8f1f8 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -26,7 +26,7 @@ namespace ESM std::vector mContentFiles; std::string mPlayerName; int mPlayerLevel; - std::string mPlayerClass; + std::string mPlayerClass; // this is the ID and not the name of the class std::string mPlayerCell; TimeStamp mInGameTime; double mTimePlayed; From cbbdf390ad9902fc4d55e74e43f829e34c8ae45b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 11:10:18 +0100 Subject: [PATCH 021/120] added function for inspection of saved characters and character slots --- apps/openmw/mwbase/statemanager.hpp | 8 ++++++++ apps/openmw/mwstate/character.cpp | 10 ++++++++++ apps/openmw/mwstate/character.hpp | 11 +++++++++++ apps/openmw/mwstate/charactermanager.cpp | 12 +++++++++++- apps/openmw/mwstate/charactermanager.hpp | 4 ++++ apps/openmw/mwstate/statemanagerimp.cpp | 12 +++++++++++- apps/openmw/mwstate/statemanagerimp.hpp | 4 ++++ 7 files changed, 59 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 3aa464ebeb..ce8094632e 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWSTATE_STATEMANAGER_H #define GAME_MWSTATE_STATEMANAGER_H +#include + namespace MWState { struct Slot; @@ -21,6 +23,8 @@ namespace MWBase State_Running }; + typedef std::vector::const_iterator CharacterIterator; + private: StateManager (const StateManager&); @@ -54,6 +58,10 @@ namespace MWBase /// \note Slot must belong to the current character. virtual MWState::Character *getCurrentCharacter() = 0; + + virtual CharacterIterator characterBegin() = 0; + + virtual CharacterIterator characterEnd() = 0; }; } diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index dc16085d83..660af7d891 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -98,4 +98,14 @@ const MWState::Slot *MWState::Character::updateSlot (const Slot *slot, const ESM 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(); } \ No newline at end of file diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp index 30182e4047..a6cb6fa6d1 100644 --- a/apps/openmw/mwstate/character.hpp +++ b/apps/openmw/mwstate/character.hpp @@ -18,6 +18,12 @@ namespace MWState class Character { + public: + + typedef std::vector::const_reverse_iterator SlotIterator; + + private: + boost::filesystem::path mPath; std::vector mSlots; int mNext; @@ -39,6 +45,11 @@ namespace MWState /// \note Slot must belong to this character. /// /// \attention The \æ 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. + + SlotIterator end() const; }; } diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index 6eccb63dca..6b67d0d04d 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -59,4 +59,14 @@ void MWState::CharacterManager::createCharacter() void MWState::CharacterManager::clearCurrentCharacter() { mCurrent = 0; -} \ No newline at end of file +} + +std::vector::const_iterator MWState::CharacterManager::begin() const +{ + return mCharacters.begin(); +} + +std::vector::const_iterator MWState::CharacterManager::end() const +{ + return mCharacters.end(); +} diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index 543e0c94af..ce82ccc46a 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -33,6 +33,10 @@ namespace MWState ///< Create new character within saved game management void clearCurrentCharacter(); + + std::vector::const_iterator begin() const; + + std::vector::const_iterator end() const; }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 0f4e1e020d..9d8fcafb02 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -97,4 +97,14 @@ void MWState::StateManager::saveGame (const Slot *slot) MWState::Character *MWState::StateManager::getCurrentCharacter() { return mCharacterManager.getCurrentCharacter(); -} \ No newline at end of file +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() +{ + return mCharacterManager.begin(); +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() +{ + return mCharacterManager.end(); +} diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 8d1ff0641c..764a09a7e1 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -38,6 +38,10 @@ namespace MWState /// \note Slot must belong to the current character. virtual Character *getCurrentCharacter(); + + virtual CharacterIterator characterBegin(); + + virtual CharacterIterator characterEnd(); }; } From 5ba56a5ea5f50c2469b780ebe4d8a1abf10311e9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 11:18:56 +0100 Subject: [PATCH 022/120] character signatures --- apps/openmw/mwstate/character.cpp | 19 +++++++++++++++++++ apps/openmw/mwstate/character.hpp | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 660af7d891..54d2b9dd81 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -108,4 +108,23 @@ MWState::Character::SlotIterator MWState::Character::begin() const 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::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; } \ No newline at end of file diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp index a6cb6fa6d1..676c04680f 100644 --- a/apps/openmw/mwstate/character.hpp +++ b/apps/openmw/mwstate/character.hpp @@ -50,6 +50,11 @@ namespace MWState ///< First slot is the most recent. Other slots follow in descending order of save date. SlotIterator end() const; + + ESM::SavedGame getSignature() const; + ///< Return signature information for this character. + /// + /// \todo attention This function must not be called if there are no slots. }; } From c165894869e227cfe8e3f541e3abdd2166ac60d0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 12:24:24 +0100 Subject: [PATCH 023/120] load saved game record --- apps/openmw/mwbase/statemanager.hpp | 5 +++++ apps/openmw/mwgui/mainmenu.cpp | 22 +++++++++++++++++----- apps/openmw/mwstate/character.cpp | 13 ++++++++++++- apps/openmw/mwstate/charactermanager.cpp | 10 ++++++++++ apps/openmw/mwstate/charactermanager.hpp | 2 ++ apps/openmw/mwstate/statemanagerimp.cpp | 22 ++++++++++++++++++++++ apps/openmw/mwstate/statemanagerimp.hpp | 5 +++++ 7 files changed, 73 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index ce8094632e..3369fd3bc9 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -57,6 +57,11 @@ namespace MWBase /// /// \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() = 0; virtual CharacterIterator characterBegin() = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index f25b72d377..ac272d1c41 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -8,6 +8,8 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/statemanager.hpp" +#include "../mwstate/character.hpp" + #include "savegamedialog.hpp" namespace MWGui @@ -55,13 +57,22 @@ namespace MWGui else if (sender == mButtons["loadgame"]) { - MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); - dialog->setLoadOrSave(true); - dialog->setVisible(true); + // for testing purpose, pick the first slot of the first character: + const MWState::Character& character = + *MWBase::Environment::get().getStateManager()->characterBegin(); + const MWState::Slot& slot = *character.begin(); + + MWBase::Environment::get().getStateManager()->loadGame (&character, &slot); + +// MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); +// dialog->setLoadOrSave(true); +// dialog->setVisible(true); } else if (sender == mButtons["savegame"]) { + // for testing purpose, save into a new slot: MWBase::Environment::get().getStateManager()->saveGame (0); + // MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); // dialog->setLoadOrSave(false); // dialog->setVisible(true); @@ -88,8 +99,9 @@ namespace MWGui buttons.push_back("newgame"); - /// \todo hide, if no saved game is available - buttons.push_back("loadgame"); + if (MWBase::Environment::get().getStateManager()->characterBegin()!= + MWBase::Environment::get().getStateManager()->characterEnd()) + buttons.push_back("loadgame"); if (state==MWBase::StateManager::State_Running) buttons.push_back("savegame"); diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 54d2b9dd81..7185ce89d7 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -9,6 +9,9 @@ #include +#include +#include + bool MWState::operator< (const Slot& left, const Slot& right) { return left.mTimeStamp ignore + + reader.getRecHeader(); + + slot.mProfile.load (reader); mSlots.push_back (slot); } diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index 6b67d0d04d..c632f61e4a 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -56,6 +56,16 @@ void MWState::CharacterManager::createCharacter() mCurrent = &mCharacters.back(); } +void MWState::CharacterManager::setCurrentCharacter (const Character *character) +{ + int index = character - &mCharacters[0]; + + if (index<0 || index>=static_cast (mCharacters.size())) + throw std::logic_error ("invalid character"); + + mCurrent = &mCharacters[index]; +} + void MWState::CharacterManager::clearCurrentCharacter() { mCurrent = 0; diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index ce82ccc46a..9995393aa0 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -32,6 +32,8 @@ namespace MWState void createCharacter(); ///< Create new character within saved game management + void setCurrentCharacter (const Character *character); + void clearCurrentCharacter(); std::vector::const_iterator begin() const; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9d8fcafb02..12bd295961 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -2,6 +2,7 @@ #include "statemanagerimp.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -94,6 +95,27 @@ void MWState::StateManager::saveGame (const Slot *slot) writer.close(); } +void MWState::StateManager::loadGame (const Character *character, const Slot *slot) +{ + if (mState!=State_NoGame) + { + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + mState = State_NoGame; + mCharacterManager.clearCurrentCharacter(); + } + + ESM::ESMReader reader; + reader.open (slot->mPath.string()); + + reader.getRecName(); // don't need to read that here + reader.getRecHeader(); + + /// \todo read saved game data + + mState = State_Running; +} + MWState::Character *MWState::StateManager::getCurrentCharacter() { return mCharacterManager.getCurrentCharacter(); diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 764a09a7e1..d387404cbf 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -37,6 +37,11 @@ namespace MWState /// /// \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(); virtual CharacterIterator characterBegin(); From b5f99522c72759149ddb1888815abc3739db84e6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 12:29:24 +0100 Subject: [PATCH 024/120] added a few comments --- apps/openmw/mwbase/statemanager.hpp | 4 ++++ apps/openmw/mwstate/statemanagerimp.hpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 3369fd3bc9..3bd99c315b 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -63,8 +63,12 @@ namespace MWBase /// \note \a slot must belong to \a character. virtual MWState::Character *getCurrentCharacter() = 0; + ///< \attention Do not call this function to check if there is a current character. Use + /// characterBegin()!=characterEnd() instead. virtual CharacterIterator characterBegin() = 0; + ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned + /// iterator. virtual CharacterIterator characterEnd() = 0; }; diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index d387404cbf..08b0776c19 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -43,8 +43,12 @@ namespace MWState /// \note \a slot must belong to \a character. virtual Character *getCurrentCharacter(); + ///< \attention Do not call this function to check if there is a current character. Use + /// characterBegin()!=characterEnd() instead. virtual CharacterIterator characterBegin(); + ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned + /// iterator. virtual CharacterIterator characterEnd(); }; From 2702d10911a3a2a82da21b9bae95d8a3215521e9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 12:31:04 +0100 Subject: [PATCH 025/120] more comments --- apps/openmw/mwstate/character.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp index 676c04680f..d678c165b6 100644 --- a/apps/openmw/mwstate/character.hpp +++ b/apps/openmw/mwstate/character.hpp @@ -44,17 +44,19 @@ namespace MWState const Slot *updateSlot (const Slot *slot, const ESM::SavedGame& profile); /// \note Slot must belong to this character. /// - /// \attention The \æ slot pointer will be invalidated by this call. + /// \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. /// - /// \todo attention This function must not be called if there are no slots. + /// \attention This function must not be called if there are no slots. }; } From cf79a83d4fcc06a4893176fb9f1ba51bf6269b6e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 21 Nov 2013 19:07:54 +0100 Subject: [PATCH 026/120] Avoid recreating widgets in MainMenu::updateMenu. Fix crash when pressing new game due to the button being destroyed from within it's own delegate. --- apps/openmw/mwgui/mainmenu.cpp | 61 +++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index ac272d1c41..962b5dd311 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -40,22 +40,23 @@ namespace MWGui void MainMenu::onButtonClicked(MyGUI::Widget *sender) { + std::string name = *sender->getUserData(); MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); - if (sender == mButtons["return"]) + if (name == "return") { MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx); MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); } - else if (sender == mButtons["options"]) + else if (name == "options") MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); - else if (sender == mButtons["exitgame"]) + else if (name == "exitgame") MWBase::Environment::get().getStateManager()->requestQuit(); - else if (sender == mButtons["newgame"]) + else if (name == "newgame") { MWBase::Environment::get().getStateManager()->newGame(); } - else if (sender == mButtons["loadgame"]) + else if (name == "loadgame") { // for testing purpose, pick the first slot of the first character: const MWState::Character& character = @@ -68,7 +69,7 @@ namespace MWGui // dialog->setLoadOrSave(true); // dialog->setVisible(true); } - else if (sender == mButtons["savegame"]) + else if (name == "savegame") { // for testing purpose, save into a new slot: MWBase::Environment::get().getStateManager()->saveGame (0); @@ -84,10 +85,9 @@ namespace MWGui setCoord(0,0, mWidth, mHeight); - if (mButtonBox) - MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); + if (!mButtonBox) + mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); - mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); int curH = 0; MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState(); @@ -110,28 +110,41 @@ namespace MWGui //buttons.push_back("credits"); buttons.push_back("exitgame"); - int maxwidth = 0; - - mButtons.clear(); + // Create new buttons if needed for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) { - MWGui::ImageButton* button = mButtonBox->createWidget - ("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 (mButtons.find(*it) == mButtons.end()) + { + MWGui::ImageButton* button = mButtonBox->createWidget + ("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::iterator it = mButtons.begin(); it != mButtons.end(); ++it) + { + it->second->setVisible(false); + MyGUI::IntSize requested = it->second->getRequestedSize(); if (requested.width > maxwidth) maxwidth = requested.width; } - for (std::map::iterator it = mButtons.begin(); it != mButtons.end(); ++it) + + // Now show and position the ones we want + for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) { - MyGUI::IntSize requested = it->second->getRequestedSize(); - it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height); + 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; } mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH); From 2e87cbc2313c568510de3b70cca4cd52c5f9016a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 21 Nov 2013 20:24:58 +0100 Subject: [PATCH 027/120] Add basic functionality to SaveGameDialog --- apps/openmw/mwbase/statemanager.hpp | 4 +- apps/openmw/mwgui/mainmenu.cpp | 34 ++-- apps/openmw/mwgui/mainmenu.hpp | 5 + apps/openmw/mwgui/savegamedialog.cpp | 179 ++++++++++++++++++++++ apps/openmw/mwgui/savegamedialog.hpp | 12 ++ apps/openmw/mwstate/statemanagerimp.cpp | 27 +++- files/mygui/openmw_savegame_dialog.layout | 9 -- 7 files changed, 236 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 3bd99c315b..e9d10a796e 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -63,8 +63,8 @@ namespace MWBase /// \note \a slot must belong to \a character. virtual MWState::Character *getCurrentCharacter() = 0; - ///< \attention Do not call this function to check if there is a current character. Use - /// characterBegin()!=characterEnd() instead. + ///< \attention Do not call this function to check if there is a current character. + /// Instead, assume there is a character if getState() == Running. virtual CharacterIterator characterBegin() = 0; ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 962b5dd311..ff8ab8c937 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -18,10 +18,16 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0), mWidth (w), mHeight (h) + , mSaveGameDialog(NULL) { updateMenu(); } + MainMenu::~MainMenu() + { + delete mSaveGameDialog; + } + void MainMenu::onResChange(int w, int h) { mWidth = w; @@ -56,27 +62,15 @@ namespace MWGui MWBase::Environment::get().getStateManager()->newGame(); } - else if (name == "loadgame") + else { - // for testing purpose, pick the first slot of the first character: - const MWState::Character& character = - *MWBase::Environment::get().getStateManager()->characterBegin(); - const MWState::Slot& slot = *character.begin(); - - MWBase::Environment::get().getStateManager()->loadGame (&character, &slot); - -// MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); -// dialog->setLoadOrSave(true); -// dialog->setVisible(true); - } - else if (name == "savegame") - { - // for testing purpose, save into a new slot: - MWBase::Environment::get().getStateManager()->saveGame (0); - -// MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); -// dialog->setLoadOrSave(false); -// dialog->setVisible(true); + if (!mSaveGameDialog) + mSaveGameDialog = new SaveGameDialog(); + if (name == "loadgame") + mSaveGameDialog->setLoadOrSave(true); + else if (name == "savegame") + mSaveGameDialog->setLoadOrSave(false); + mSaveGameDialog->setVisible(true); } } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 511f726729..6d52f26d5d 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -5,6 +5,8 @@ namespace MWGui { + class SaveGameDialog; + class MainMenu : public OEngine::GUI::Layout { int mWidth; @@ -13,6 +15,7 @@ namespace MWGui public: MainMenu(int w, int h); + ~MainMenu(); void onResChange(int w, int h); @@ -27,6 +30,8 @@ namespace MWGui void onButtonClicked (MyGUI::Widget* sender); void updateMenu(); + + SaveGameDialog* mSaveGameDialog; }; } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index a1acd35884..648dd46838 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -1,12 +1,68 @@ #include "savegamedialog.hpp" #include "widgets.hpp" +#include "../mwbase/statemanager.hpp" +#include "../mwbase/environment.hpp" + + +#include "../mwstate/character.hpp" + +namespace +{ +std::string getMonth(int m) +{ + std::string month; + 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; + } + return month; +} +} namespace MWGui { SaveGameDialog::SaveGameDialog() : WindowModal("openmw_savegame_dialog.layout") + , mSaving(true) + , mCurrentCharacter(NULL) { getWidget(mScreenshot, "Screenshot"); getWidget(mCharacterSelection, "SelectCharacter"); @@ -18,21 +74,57 @@ 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(); + center(); + + MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); + if (mgr->characterBegin() == mgr->characterEnd()) + return; + + // If we are running, there must be a current character + if (mgr->getState() == MWBase::StateManager::State_Running) + { + mCurrentCharacter = mgr->getCurrentCharacter(); + } + + mCharacterSelection->removeAllItems(); + for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it) + { + std::stringstream title; + title << it->getSignature().mPlayerName; + title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClass << ")"; + + mCharacterSelection->addItem (title.str()); + + if (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(); + } + center(); } @@ -43,7 +135,94 @@ namespace MWGui void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) { + // Get the selected slot, if any + unsigned int i=0; + const MWState::Slot* slot = NULL; + 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 (slot); + } + else + { + MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); + } + setVisible(false); } + 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->mPath.string()); + } + onSlotSelected(mSaveList, MyGUI::ITEM_NONE); + } + + void SaveGameDialog::onSlotSelected(MyGUI::ListBox *sender, size_t pos) + { + if (pos == MyGUI::ITEM_NONE) + { + mInfoText->setCaption(""); + 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 << " " + << getMonth(slot->mProfile.mInGameTime.mMonth) + << " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); + + mInfoText->setCaptionWithReplacing(text.str()); + + } } diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 1a3178ef30..2a188061c6 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -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; + }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 12bd295961..6aba93dc83 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -72,9 +72,28 @@ void MWState::StateManager::saveGame (const Slot *slot) /// \todo store content file list profile.mPlayerName = player.getClass().getName (player); profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); - profile.mPlayerClass = player.get()->mBase->mId; - /// \todo player cell - /// \todo gamehour + profile.mPlayerClass = player.get()->mBase->mClass; + + std::string cellName; + if (player.getCell()->mCell->isExterior()) + { + if (player.getCell()->mCell->mName != "") + cellName = player.getCell()->mCell->mName; + else + { + const ESM::Region* region = + MWBase::Environment::get().getWorld()->getStore().get().search(player.getCell()->mCell->mRegion); + if (region) + cellName = region->mName; + else + cellName = MWBase::Environment::get().getWindowManager()->getGameSettingString("sDefaultCellname", "Wilderness"); + } + } + else + cellName = player.getCell()->mCell->mName; + profile.mPlayerCell = cellName; + + profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); profile.mInGameTime.mDay = world.getDay(); profile.mInGameTime.mMonth = world.getMonth(); /// \todo year @@ -113,6 +132,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl /// \todo read saved game data + mCharacterManager.setCurrentCharacter(character); + mState = State_Running; } diff --git a/files/mygui/openmw_savegame_dialog.layout b/files/mygui/openmw_savegame_dialog.layout index 18de6a2399..ceb1a84288 100644 --- a/files/mygui/openmw_savegame_dialog.layout +++ b/files/mygui/openmw_savegame_dialog.layout @@ -17,8 +17,6 @@ - - @@ -26,11 +24,6 @@ - - - - - @@ -51,8 +44,6 @@ - - From 18a3b38fb49e42d410fe2729fd26d9db99ee7abb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 24 Nov 2013 14:42:58 +0100 Subject: [PATCH 028/120] when scanning saved game directory, reject characters without a valid saved game --- apps/openmw/mwstate/charactermanager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index c632f61e4a..2b9c49fcc2 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -23,7 +23,9 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save if (boost::filesystem::is_directory (characterDir)) { Character character (characterDir); - mCharacters.push_back (character); + + if (character.begin()!=character.end()) + mCharacters.push_back (character); } std::istringstream stream (characterDir.filename().string()); From 7efac4c9a52127c806979182d40d36501dc7174f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 24 Nov 2013 15:05:00 +0100 Subject: [PATCH 029/120] fixed handling of characters without a valid slot --- apps/openmw/mwbase/statemanager.hpp | 2 -- apps/openmw/mwgui/savegamedialog.cpp | 21 ++++++++++----------- apps/openmw/mwstate/statemanagerimp.hpp | 2 -- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index e9d10a796e..7101be25d8 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -63,8 +63,6 @@ namespace MWBase /// \note \a slot must belong to \a character. virtual MWState::Character *getCurrentCharacter() = 0; - ///< \attention Do not call this function to check if there is a current character. - /// Instead, assume there is a character if getState() == Running. virtual CharacterIterator characterBegin() = 0; ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 648dd46838..2ea83cfa36 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -89,23 +89,22 @@ namespace MWGui if (mgr->characterBegin() == mgr->characterEnd()) return; - // If we are running, there must be a current character - if (mgr->getState() == MWBase::StateManager::State_Running) - { - mCurrentCharacter = mgr->getCurrentCharacter(); - } + mCurrentCharacter = mgr->getCurrentCharacter(); mCharacterSelection->removeAllItems(); for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it) { - std::stringstream title; - title << it->getSignature().mPlayerName; - title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClass << ")"; + if (it->begin()!=it->end()) + { + std::stringstream title; + title << it->getSignature().mPlayerName; + title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClass << ")"; - mCharacterSelection->addItem (title.str()); + mCharacterSelection->addItem (title.str()); - if (mCurrentCharacter == &*it) - mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); + if (mgr->getCurrentCharacter() == &*it) + mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); + } } fillSaveList(); diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 08b0776c19..a380c9fee6 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -43,8 +43,6 @@ namespace MWState /// \note \a slot must belong to \a character. virtual Character *getCurrentCharacter(); - ///< \attention Do not call this function to check if there is a current character. Use - /// characterBegin()!=characterEnd() instead. virtual CharacterIterator characterBegin(); ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned From 67cd0887e64afa88ec5139fcb4b8a60e74bd8a3f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 24 Nov 2013 15:19:56 +0100 Subject: [PATCH 030/120] added description field to saved game record; make use of description in GUI --- apps/openmw/mwbase/statemanager.hpp | 3 ++- apps/openmw/mwgui/savegamedialog.cpp | 6 ++++-- apps/openmw/mwstate/statemanagerimp.cpp | 3 ++- apps/openmw/mwstate/statemanagerimp.hpp | 2 +- components/esm/savedgame.cpp | 2 ++ components/esm/savedgame.hpp | 1 + 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 7101be25d8..f1f1308241 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -2,6 +2,7 @@ #define GAME_MWSTATE_STATEMANAGER_H #include +#include namespace MWState { @@ -52,7 +53,7 @@ namespace MWBase virtual void endGame() = 0; - virtual void saveGame (const MWState::Slot *slot = 0) = 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. diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 2ea83cfa36..38574e2fdf 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -83,6 +83,8 @@ namespace MWGui { WindowModal::open(); + mSaveNameEdit->setCaption (""); + center(); MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); @@ -145,7 +147,7 @@ namespace MWGui if (mSaving) { - MWBase::Environment::get().getStateManager()->saveGame (slot); + MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), slot); } else { @@ -179,7 +181,7 @@ namespace MWGui return; for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) { - mSaveList->addItem(it->mPath.string()); + mSaveList->addItem(it->mProfile.mDescription); } onSlotSelected(mSaveList, MyGUI::ITEM_NONE); } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 6aba93dc83..9d8cf2a813 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -61,7 +61,7 @@ void MWState::StateManager::endGame() mState = State_Ended; } -void MWState::StateManager::saveGame (const Slot *slot) +void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) { ESM::SavedGame profile; @@ -98,6 +98,7 @@ void MWState::StateManager::saveGame (const Slot *slot) profile.mInGameTime.mMonth = world.getMonth(); /// \todo year /// \todo time played + profile.mDescription = description; if (!slot) slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index a380c9fee6..abf7e123bb 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -32,7 +32,7 @@ namespace MWState virtual void endGame(); - virtual void saveGame (const Slot *slot = 0); + 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. diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 5a5fc9fa83..8169b01a19 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -15,6 +15,7 @@ void ESM::SavedGame::load (ESMReader &esm) mPlayerCell = esm.getHNString("PCEL"); esm.getHNT (mInGameTime, "TSTM", 16); esm.getHNT (mTimePlayed, "TIME"); + mDescription = esm.getHNString ("DESC"); while (esm.isNextSub ("DEPE")) mContentFiles.push_back (esm.getHString()); @@ -28,6 +29,7 @@ void ESM::SavedGame::save (ESMWriter &esm) const esm.writeHNCString ("PCEL", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); + esm.writeHNCString ("DESC", mDescription); for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index e712e8f1f8..6c11d318f6 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -30,6 +30,7 @@ namespace ESM std::string mPlayerCell; TimeStamp mInGameTime; double mTimePlayed; + std::string mDescription; /// \todo add field for screenshot From e3670cff8a14bc6d028e0116d5bf500064660358 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 24 Nov 2013 16:58:41 +0100 Subject: [PATCH 031/120] improved character selection logic --- apps/openmw/mwbase/statemanager.hpp | 3 ++- apps/openmw/mwgui/savegamedialog.cpp | 20 +++++++++++++------- apps/openmw/mwstate/charactermanager.cpp | 4 ++-- apps/openmw/mwstate/charactermanager.hpp | 4 ++-- apps/openmw/mwstate/statemanagerimp.cpp | 4 ++-- apps/openmw/mwstate/statemanagerimp.hpp | 3 ++- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index f1f1308241..f376d72c19 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -63,7 +63,8 @@ namespace MWBase /// /// \note \a slot must belong to \a character. - virtual MWState::Character *getCurrentCharacter() = 0; + 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 diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 38574e2fdf..2ce0f0fbf7 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -91,9 +91,10 @@ namespace MWGui if (mgr->characterBegin() == mgr->characterEnd()) return; - mCurrentCharacter = mgr->getCurrentCharacter(); + mCurrentCharacter = mgr->getCurrentCharacter (false); mCharacterSelection->removeAllItems(); + for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it) { if (it->begin()!=it->end()) @@ -104,7 +105,7 @@ namespace MWGui mCharacterSelection->addItem (title.str()); - if (mgr->getCurrentCharacter() == &*it) + if (mCurrentCharacter == &*it) mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); } } @@ -123,7 +124,7 @@ namespace MWGui if (!load) { - mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(); + mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter (false); } center(); @@ -139,10 +140,14 @@ namespace MWGui // Get the selected slot, if any unsigned int i=0; const MWState::Slot* slot = NULL; - for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it,++i) + + if (mCurrentCharacter) { - if (i == mSaveList->getIndexSelected()) - slot = &*it; + for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it,++i) + { + if (i == mSaveList->getIndexSelected()) + slot = &*it; + } } if (mSaving) @@ -151,7 +156,8 @@ namespace MWGui } else { - MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); + if (mCurrentCharacter && slot) + MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); } setVisible(false); diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index 2b9c49fcc2..cb64da2c2d 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -38,9 +38,9 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save } } -MWState::Character *MWState::CharacterManager::getCurrentCharacter() +MWState::Character *MWState::CharacterManager::getCurrentCharacter (bool create) { - if (!mCurrent) + if (!mCurrent && create) createCharacter(); return mCurrent; diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index 9995393aa0..a02927d583 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -26,8 +26,8 @@ namespace MWState CharacterManager (const boost::filesystem::path& saves); - Character *getCurrentCharacter(); - ///< A character is implicitly created, if there is none. + 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 diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9d8cf2a813..9a7e3d158b 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -138,9 +138,9 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl mState = State_Running; } -MWState::Character *MWState::StateManager::getCurrentCharacter() +MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) { - return mCharacterManager.getCurrentCharacter(); + return mCharacterManager.getCurrentCharacter (create); } MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index abf7e123bb..8bf1fab201 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -42,7 +42,8 @@ namespace MWState /// /// \note \a slot must belong to \a character. - virtual Character *getCurrentCharacter(); + 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 From 55544e931cee0b55ad8550958f64f493ba9fa8ec Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 25 Nov 2013 10:21:49 +0100 Subject: [PATCH 032/120] reject newer formats when scanning saved games --- apps/openmw/mwstate/character.cpp | 3 +++ apps/openmw/mwstate/statemanagerimp.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 7185ce89d7..2bd2af1395 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -27,6 +27,9 @@ void MWState::Character::addSlot (const boost::filesystem::path& 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 diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9a7e3d158b..46239f5a21 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -107,7 +107,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot std::ofstream stream (slot->mPath.string().c_str()); ESM::ESMWriter writer; -// writer.setFormat (); + writer.setFormat (ESM::Header::CurrentFormat); writer.save (stream); writer.startRecord ("SAVE"); slot->mProfile.save (writer); From 1ecadccb280545474cadeb1389e3d1ece4f48cc0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 25 Nov 2013 12:59:40 +0100 Subject: [PATCH 033/120] fixed save function of SavedGame record --- components/esm/savedgame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 8169b01a19..a37043da6a 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -33,6 +33,6 @@ void ESM::SavedGame::save (ESMWriter &esm) const for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) - esm.writeHNCString (*iter, "DEPE"); + esm.writeHNCString ("DEPE", *iter); } From 616e3aa32ffcfa36ed594a8b049e439d2c9288f6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 25 Nov 2013 13:00:05 +0100 Subject: [PATCH 034/120] store content file list in saved games and reject saved games not matching the current game --- apps/openmw/engine.cpp | 3 ++- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwstate/character.cpp | 14 +++++++++++--- apps/openmw/mwstate/character.hpp | 4 ++-- apps/openmw/mwstate/charactermanager.cpp | 9 +++++---- apps/openmw/mwstate/charactermanager.hpp | 3 ++- apps/openmw/mwstate/statemanagerimp.cpp | 7 ++++--- apps/openmw/mwstate/statemanagerimp.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 7 ++++++- apps/openmw/mwworld/worldimp.hpp | 3 +++ 10 files changed, 38 insertions(+), 16 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index cda0683470..bee7fa8fdc 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -323,7 +323,8 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings) { - mEnvironment.setStateManager (new MWState::StateManager (mCfgMgr.getUserPath() / "saves")); + mEnvironment.setStateManager ( + new MWState::StateManager (mCfgMgr.getUserPath() / "saves", mContentFiles.at (0))); Nif::NIFFile::CacheLock cachelock; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index e1adfbec46..fe7d8f7ac8 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -416,6 +416,8 @@ namespace MWBase virtual void launchProjectile (const std::string& id, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName) = 0; + + virtual const std::vector& getContentFiles() const = 0; }; } diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 2bd2af1395..0766753ca3 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -12,13 +12,18 @@ #include #include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + bool MWState::operator< (const Slot& left, const Slot& right) { return left.mTimeStamp ignore + mSlots.push_back (slot); } @@ -54,7 +62,7 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile) mSlots.push_back (slot); } -MWState::Character::Character (const boost::filesystem::path& saves) +MWState::Character::Character (const boost::filesystem::path& saves, const std::string& game) : mPath (saves), mNext (0) { if (!boost::filesystem::is_directory (mPath)) @@ -70,7 +78,7 @@ MWState::Character::Character (const boost::filesystem::path& saves) try { - addSlot (slotPath); + addSlot (slotPath, game); } catch (...) {} // ignoring bad saved game files for now diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp index d678c165b6..61e4e5b25f 100644 --- a/apps/openmw/mwstate/character.hpp +++ b/apps/openmw/mwstate/character.hpp @@ -28,13 +28,13 @@ namespace MWState std::vector mSlots; int mNext; - void addSlot (const boost::filesystem::path& path); + void addSlot (const boost::filesystem::path& path, const std::string& game); void addSlot (const ESM::SavedGame& profile); public: - Character (const boost::filesystem::path& saves); + Character (const boost::filesystem::path& saves, const std::string& game); const Slot *createSlot (const ESM::SavedGame& profile); ///< Create new slot. diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index cb64da2c2d..2a40fb1cc5 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -6,8 +6,9 @@ #include -MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves) -: mPath (saves), mNext (0), mCurrent (0) +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)) { @@ -22,7 +23,7 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save if (boost::filesystem::is_directory (characterDir)) { - Character character (characterDir); + Character character (characterDir, mGame); if (character.begin()!=character.end()) mCharacters.push_back (character); @@ -53,7 +54,7 @@ void MWState::CharacterManager::createCharacter() boost::filesystem::path path = mPath / stream.str(); - mCharacters.push_back (Character (path)); + mCharacters.push_back (Character (path, mGame)); mCurrent = &mCharacters.back(); } diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index a02927d583..bc2e23f89a 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -13,6 +13,7 @@ namespace MWState int mNext; std::vector mCharacters; Character *mCurrent; + std::string mGame; private: @@ -24,7 +25,7 @@ namespace MWState public: - CharacterManager (const boost::filesystem::path& saves); + 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. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 46239f5a21..4cb97ed508 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -15,8 +15,8 @@ #include "../mwmechanics/npcstats.hpp" -MWState::StateManager::StateManager (const boost::filesystem::path& saves) -: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves) +MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) +: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves, game) { } @@ -69,7 +69,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot MWWorld::Ptr player = world.getPlayer().getPlayer(); - /// \todo store content file list + profile.mContentFiles = world.getContentFiles(); + profile.mPlayerName = player.getClass().getName (player); profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); profile.mPlayerClass = player.get()->mBase->mClass; diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 8bf1fab201..3965632cc3 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -17,7 +17,7 @@ namespace MWState public: - StateManager (const boost::filesystem::path& saves); + StateManager (const boost::filesystem::path& saves, const std::string& game); virtual void requestQuit(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index eca2ebb79f..5a4192529b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -215,7 +215,7 @@ namespace MWWorld mSky (true), mCells (mStore, mEsm), mActivationDistanceOverride (mActivationDistanceOverride), mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), - mFacedDistance(FLT_MAX), mGodMode(false) + mFacedDistance(FLT_MAX), mGodMode(false), mContentFiles (contentFiles) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -2246,4 +2246,9 @@ namespace MWWorld ++it; } } + + const std::vector& World::getContentFiles() const + { + return mContentFiles; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 19890b8310..32ad848657 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -73,6 +73,7 @@ namespace MWWorld OEngine::Physic::PhysicEngine* mPhysEngine; bool mGodMode; + std::vector mContentFiles; // not implemented World (const World&); @@ -492,6 +493,8 @@ namespace MWWorld virtual void launchProjectile (const std::string& id, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName); + + virtual const std::vector& getContentFiles() const; }; } From ad143e0524278e6af70c37417a5545a47ed9449c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 25 Nov 2013 14:56:05 +0100 Subject: [PATCH 035/120] case fix (content file names) --- apps/openmw/mwstate/character.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 0766753ca3..304eaddd35 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -42,7 +42,8 @@ void MWState::Character::addSlot (const boost::filesystem::path& path, const std slot.mProfile.load (reader); - if (Misc::StringUtils::lowerCase (slot.mProfile.mContentFiles.at (0))!=game) + 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); From b40c0f2a07eb592c695ef3c2a188374a7112969f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 26 Nov 2013 09:56:08 +0100 Subject: [PATCH 036/120] one more fix to SavedGame record saving --- components/esm/savedgame.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index a37043da6a..e98608fe6d 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -23,13 +23,13 @@ void ESM::SavedGame::load (ESMReader &esm) void ESM::SavedGame::save (ESMWriter &esm) const { - esm.writeHNCString ("PNAM", mPlayerName); + esm.writeHNString ("PNAM", mPlayerName); esm.writeHNT ("PLEV", mPlayerLevel); - esm.writeHNCString ("PCLA", mPlayerClass); - esm.writeHNCString ("PCEL", mPlayerCell); + esm.writeHNString ("PCLA", mPlayerClass); + esm.writeHNString ("PCEL", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); - esm.writeHNCString ("DESC", mDescription); + esm.writeHNString ("DESC", mDescription); for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) From eea433f14192e2d36a84494a85762b06787dfcf5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 26 Nov 2013 10:37:58 +0100 Subject: [PATCH 037/120] restore last played character selection across sessions --- apps/openmw/mwgui/savegamedialog.cpp | 12 ++++++++++-- apps/openmw/mwstate/statemanagerimp.cpp | 8 ++++++++ files/settings-default.cfg | 7 +++++-- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 2ce0f0fbf7..8ec6dbfcf3 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -1,10 +1,13 @@ #include "savegamedialog.hpp" #include "widgets.hpp" +#include + +#include + #include "../mwbase/statemanager.hpp" #include "../mwbase/environment.hpp" - #include "../mwstate/character.hpp" namespace @@ -93,6 +96,9 @@ namespace MWGui 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) @@ -105,7 +111,9 @@ namespace MWGui mCharacterSelection->addItem (title.str()); - if (mCurrentCharacter == &*it) + if (mCurrentCharacter == &*it || + (!mCurrentCharacter && directory==Misc::StringUtils::lowerCase ( + it->begin()->mPath.parent_path().filename().string()))) mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); } } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 4cb97ed508..97f8b8e55b 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" @@ -114,6 +116,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot slot->mProfile.save (writer); writer.endRecord ("SAVE"); writer.close(); + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); } void MWState::StateManager::loadGame (const Character *character, const Slot *slot) @@ -137,6 +142,9 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl mCharacterManager.setCurrentCharacter(character); mState = State_Running; + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index f191430df1..b3b1861420 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -1,4 +1,4 @@ -# WARNING: Editing this file might have no effect, as these +# WARNING: Editing this file might have no effect, as these # settings are overwritten by your user settings file. [Video] @@ -13,7 +13,7 @@ screen = 0 # Valid values: # OpenGL Rendering Subsystem # Direct3D9 Rendering Subsystem -render system = +render system = # Valid values: # none @@ -169,3 +169,6 @@ ui y multiplier = 1.0 [Game] # Always use the most powerful attack when striking with a weapon (chop, slash or thrust) best attack = false + +[Saves] +character = \ No newline at end of file From bc6fe682c91c81ab0080ca85041f34c90e4c7eb4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 26 Nov 2013 11:39:58 +0100 Subject: [PATCH 038/120] replaced getCurrentCellName function with a more general getCellName function --- apps/openmw/mwbase/world.hpp | 6 +++- apps/openmw/mwscript/interpretercontext.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 38 +++++---------------- apps/openmw/mwworld/worldimp.hpp | 6 +++- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index fe7d8f7ac8..37d3c10dab 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -149,7 +149,11 @@ namespace MWBase virtual std::vector 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 diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index b8fc9ed477..5639ea208a 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -322,8 +322,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 diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5a4192529b..7d2da389c8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -442,40 +442,20 @@ namespace MWWorld return mGlobalVariables->getGlobals(); } - std::string World::getCurrentCellName () const + std::string World::getCellName (const MWWorld::CellStore *cell) const { - std::string name; + if (!cell) + cell = mWorldScene->getCurrentCell(); - Ptr::CellStore *cell = mWorldScene->getCurrentCell(); - if (cell->mCell->isExterior()) - { - if (cell->mCell->mName != "") - { - name = cell->mCell->mName; - } - else - { - const ESM::Region* region = - MWBase::Environment::get().getWorld()->getStore().get().search(cell->mCell->mRegion); - if (region) - name = region->mName; - else - { - const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().search("sDefaultCellname"); + if (!cell->mCell->isExterior() || !cell->mCell->mName.empty()) + return cell->mCell->mName; - if (setting && setting->mValue.getType()==ESM::VT_String) - name = setting->mValue.getString(); - } + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - } - } - else - { - name = cell->mCell->mName; - } + if (const ESM::Region* region = store.get().search (cell->mCell->mRegion)) + return region->mName; - return name; + return store.get().find ("sDefaultCellname")->mValue.getString(); } void World::removeRefScript (MWWorld::RefData *ref) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 32ad848657..33f6f1c2f8 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -213,7 +213,11 @@ namespace MWWorld virtual std::vector getGlobals () const; - virtual std::string getCurrentCellName () const; + virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const; + ///< 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); //< Remove the script attached to ref from mLocalScripts From e6dc927f1163d3933533afaf7797a864830153ee Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 26 Nov 2013 11:49:07 +0100 Subject: [PATCH 039/120] removed duplicates of the cell name function --- apps/openmw/mwgui/windowmanagerimp.cpp | 26 ++++++------------------- apps/openmw/mwstate/statemanagerimp.cpp | 19 +----------------- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 83325de23a..3524c6d70d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -752,29 +752,18 @@ namespace MWGui void WindowManager::changeCell(MWWorld::Ptr::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().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() ); @@ -782,12 +771,9 @@ namespace MWGui } else { - mMap->setCellName( cell->mCell->mName ); - mHud->setCellName( cell->mCell->mName ); mMap->setCellPrefix( cell->mCell->mName ); mHud->setCellPrefix( cell->mCell->mName ); } - } void WindowManager::setInteriorMapTexture(const int x, const int y) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 97f8b8e55b..8faab16090 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -77,24 +77,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); profile.mPlayerClass = player.get()->mBase->mClass; - std::string cellName; - if (player.getCell()->mCell->isExterior()) - { - if (player.getCell()->mCell->mName != "") - cellName = player.getCell()->mCell->mName; - else - { - const ESM::Region* region = - MWBase::Environment::get().getWorld()->getStore().get().search(player.getCell()->mCell->mRegion); - if (region) - cellName = region->mName; - else - cellName = MWBase::Environment::get().getWindowManager()->getGameSettingString("sDefaultCellname", "Wilderness"); - } - } - else - cellName = player.getCell()->mCell->mName; - profile.mPlayerCell = cellName; + profile.mPlayerCell = world.getCellName(); profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); profile.mInGameTime.mDay = world.getDay(); From 99ea63dc4ab648063e332cb08cb1383c4d043c67 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 26 Nov 2013 12:47:30 +0100 Subject: [PATCH 040/120] factored out code for generating month names --- apps/openmw/mwbase/world.hpp | 7 ++- apps/openmw/mwgui/journalviewmodel.cpp | 46 +++---------------- apps/openmw/mwgui/savegamedialog.cpp | 62 +++----------------------- apps/openmw/mwgui/waitdialog.cpp | 44 +----------------- apps/openmw/mwworld/worldimp.cpp | 30 ++++++++++--- apps/openmw/mwworld/worldimp.hpp | 7 ++- 6 files changed, 47 insertions(+), 149 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 37d3c10dab..dd9e20de1b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -186,8 +186,11 @@ 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 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. diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 79a77070ae..89885d303d 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -20,8 +20,6 @@ namespace MWGui { struct JournalViewModelImpl; -static void injectMonthName (std::ostream & os, int month); - struct JournalViewModelImpl : JournalViewModel { typedef KeywordSearch KeywordSearchT; @@ -242,14 +240,14 @@ struct JournalViewModelImpl : JournalViewModel { if (timestamp_buffer.empty ()) { + std::string dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}"); + std::ostringstream os; - os << itr->mDayOfMonth << ' '; - - injectMonthName (os, itr->mMonth); - - 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 (); } @@ -349,38 +347,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 (); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 8ec6dbfcf3..5f9b6a3c15 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -7,61 +7,12 @@ #include "../mwbase/statemanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwstate/character.hpp" -namespace -{ -std::string getMonth(int m) -{ - std::string month; - 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; - } - return month; -} -} - namespace MWGui { - SaveGameDialog::SaveGameDialog() : WindowModal("openmw_savegame_dialog.layout") , mSaving(true) @@ -225,19 +176,18 @@ namespace MWGui text << asctime(timeinfo) << "\n"; text << "Level " << slot->mProfile.mPlayerLevel << "\n"; text << slot->mProfile.mPlayerCell << "\n"; - //text << "Time played: " << slot->mProfile.mTimePlayed << "\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 << " " - << getMonth(slot->mProfile.mInGameTime.mMonth) - << " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); + text + << slot->mProfile.mInGameTime.mDay << " " + << MWBase::Environment::get().getWorld()->getMonthName(slot->mProfile.mInGameTime.mMonth) + << " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); mInfoText->setCaptionWithReplacing(text.str()); - } } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 2467f6c40d..071bb88049 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -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; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7d2da389c8..3d64585f92 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -450,12 +450,10 @@ namespace MWWorld if (!cell->mCell->isExterior() || !cell->mCell->mName.empty()) return cell->mCell->mName; - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - - if (const ESM::Region* region = store.get().search (cell->mCell->mRegion)) + if (const ESM::Region* region = getStore().get().search (cell->mCell->mRegion)) return region->mName; - return store.get().find ("sDefaultCellname")->mValue.getString(); + return getStore().get().find ("sDefaultCellname")->mValue.getString(); } void World::removeRefScript (MWWorld::RefData *ref) @@ -673,16 +671,36 @@ namespace MWWorld mRendering->skySetDate (mGlobalVariables->getInt ("day"), month); } - int World::getDay() + int World::getDay() const { return mGlobalVariables->getInt("day"); } - int World::getMonth() + int World::getMonth() const { return mGlobalVariables->getInt("month"); } + std::string World::getMonthName (int month) const + { + if (month==-1) + month = getMonth(); + + const int months = 12; + + if (month<0 || month>=months) + return ""; + + static const char *monthNames[months] = + { + "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", + "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", + "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" + }; + + return getStore().get().find (monthNames[month])->mValue.getString(); + } + TimeStamp World::getTimeStamp() const { return TimeStamp (mGlobalVariables->getFloat ("gamehour"), diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 33f6f1c2f8..08a3182e9c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -251,8 +251,11 @@ namespace MWWorld virtual void setDay (int day); ///< Set in-game time day. - virtual int getDay(); - virtual int getMonth(); + virtual int getDay() const; + virtual int getMonth() const; + + virtual std::string getMonthName (int month = -1) const; + ///< Return name of month (-1: current month) virtual TimeStamp getTimeStamp() const; ///< Return current in-game time stamp. From 71436b11609525d9e9dd6a0ddf7a8db91f60f5ad Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 09:10:38 +0100 Subject: [PATCH 041/120] changed interface for global variable access --- apps/openmw/mwbase/world.hpp | 12 +++++-- apps/openmw/mwdialogue/filter.cpp | 2 +- apps/openmw/mwdialogue/journalentry.cpp | 6 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwscript/interpretercontext.cpp | 12 +++---- apps/openmw/mwscript/miscextensions.cpp | 35 ++++++++++++++++----- apps/openmw/mwworld/worldimp.cpp | 18 ++++++++--- apps/openmw/mwworld/worldimp.hpp | 12 +++++-- 8 files changed, 72 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index dd9e20de1b..41cacd3655 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -140,9 +140,17 @@ 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. diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 11dccde42d..c6089c9e49 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -170,7 +170,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: { diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 5ffde54991..dd1ad3f661 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -60,9 +60,9 @@ namespace MWDialogue 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); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 3524c6d70d..2be88b7f0a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1238,7 +1238,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; } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 5639ea208a..bbfa77e189 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -127,18 +127,18 @@ 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) @@ -150,7 +150,7 @@ namespace MWScript 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) @@ -162,7 +162,7 @@ namespace MWScript 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) @@ -174,7 +174,7 @@ namespace MWScript 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 InterpreterContext::getGlobals () const diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 35f7a40447..c9d27b09ae 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -657,6 +657,8 @@ namespace MWScript void printGlobalVars(Interpreter::Runtime &runtime) { + Interpreter::Context& context = runtime.getContext(); + std::stringstream str; str<< "Global variables:"; @@ -664,16 +666,33 @@ namespace MWScript std::vector names = world->getGlobals(); for(size_t i = 0;i < names.size();++i) { - char type = world->getGlobalVariableType(names[i]); - if(type == 's') - str<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 << ""; + } } - runtime.getContext().report(str.str()); + context.report (str.str()); } public: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3d64585f92..afa434cd24 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -422,14 +422,24 @@ namespace MWWorld return mWorldScene->hasCellChanged(); } - Globals::Data& World::getGlobalVariable (const std::string& name) + void World::setGlobalInt (const std::string& name, int value) { - return (*mGlobalVariables)[name]; + mGlobalVariables->setInt (name, value); } - Globals::Data World::getGlobalVariable (const std::string& name) const + void World::setGlobalFloat (const std::string& name, float value) { - return (*mGlobalVariables)[name]; + mGlobalVariables->setFloat (name, value); + } + + int World::getGlobalInt (const std::string& name) const + { + return mGlobalVariables->getInt (name); + } + + float World::getGlobalFloat (const std::string& name) const + { + return mGlobalVariables->getFloat (name); } char World::getGlobalVariableType (const std::string& name) const diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 08a3182e9c..eda79b4339 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -204,9 +204,17 @@ namespace MWWorld virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior); ///< see MWRender::LocalMap::isPositionExplored - virtual Globals::Data& getGlobalVariable (const std::string& name); + virtual void setGlobalInt (const std::string& name, int value); + ///< Set value independently from real type. - virtual Globals::Data getGlobalVariable (const std::string& name) const; + virtual void setGlobalFloat (const std::string& name, float value); + ///< Set value independently from real type. + + virtual int getGlobalInt (const std::string& name) const; + ///< Get value independently from real type. + + virtual float getGlobalFloat (const std::string& name) const; + ///< Get value independently from real type. virtual char getGlobalVariableType (const std::string& name) const; ///< Return ' ', if there is no global variable with this name. From b0eb5938bfdd7ff3a24f219e6722bb2b5b790042 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 09:13:54 +0100 Subject: [PATCH 042/120] removed some redundant code --- apps/openmw/mwscript/interpretercontext.cpp | 28 +++------------------ apps/openmw/mwworld/worldimp.cpp | 18 +++++++++++-- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index bbfa77e189..a977d34402 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -143,45 +143,23 @@ namespace MWScript 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()->setGlobalInt (name, 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()->setGlobalInt (name, 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()->setGlobalFloat (name, value); + MWBase::Environment::get().getWorld()->setGlobalFloat (name, value); } std::vector InterpreterContext::getGlobals () const { MWBase::World *world = MWBase::Environment::get().getWorld(); return world->getGlobals(); - } char InterpreterContext::getGlobalType (const std::string& name) const diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index afa434cd24..0767718b8c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -424,12 +424,26 @@ namespace MWWorld void World::setGlobalInt (const std::string& name, int value) { - mGlobalVariables->setInt (name, value); + if (name=="gamehour") + setHour (value); + else if (name=="day") + setDay (value); + else if (name=="month") + setMonth (value); + else + mGlobalVariables->setInt (name, value); } void World::setGlobalFloat (const std::string& name, float value) { - mGlobalVariables->setFloat (name, value); + if (name=="gamehour") + setHour (value); + else if (name=="day") + setDay (value); + else if (name=="month") + setMonth (value); + else + mGlobalVariables->setFloat (name, value); } int World::getGlobalInt (const std::string& name) const From 7e2819c62e8721020ba5459d8a8177e44a36a560 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 09:27:10 +0100 Subject: [PATCH 043/120] store year in saved game profile --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwstate/statemanagerimp.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 41cacd3655..87a9b5bbc3 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -196,6 +196,7 @@ namespace MWBase 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) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 8faab16090..60b19fd46e 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -82,7 +82,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); profile.mInGameTime.mDay = world.getDay(); profile.mInGameTime.mMonth = world.getMonth(); - /// \todo year + profile.mInGameTime.mYear = world.getYear(); /// \todo time played profile.mDescription = description; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0767718b8c..1e7b8122fb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -705,6 +705,11 @@ namespace MWWorld return mGlobalVariables->getInt("month"); } + int World::getYear() const + { + return mGlobalVariables->getInt("year"); + } + std::string World::getMonthName (int month) const { if (month==-1) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index eda79b4339..2b7d157ff4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -261,6 +261,7 @@ namespace MWWorld virtual int getDay() const; virtual int getMonth() const; + virtual int getYear() const; virtual std::string getMonthName (int month = -1) const; ///< Return name of month (-1: current month) From 35e8e2303745dc38ff4180ca5bfdcbfb62ce7456 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 09:33:50 +0100 Subject: [PATCH 044/120] keep track of total play time per character --- apps/openmw/engine.cpp | 3 +++ apps/openmw/mwbase/statemanager.hpp | 2 ++ apps/openmw/mwstate/statemanagerimp.cpp | 12 ++++++++++-- apps/openmw/mwstate/statemanagerimp.hpp | 3 +++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index bee7fa8fdc..7dd3214ebf 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -117,6 +117,9 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // update world MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); + + // update game state + MWBase::Environment::get().getStateManager()->update (frametime); } // update GUI diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index f376d72c19..8548a74f7b 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -71,6 +71,8 @@ namespace MWBase /// iterator. virtual CharacterIterator characterEnd() = 0; + + virtual void update (float duration) = 0; }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 60b19fd46e..a273aced7f 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -18,7 +18,7 @@ #include "../mwmechanics/npcstats.hpp" MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) -: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves, game) +: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) { } @@ -46,6 +46,7 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getJournal()->clear(); mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); + mTimePlayed = 0; } if (!bypass) @@ -83,7 +84,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mInGameTime.mDay = world.getDay(); profile.mInGameTime.mMonth = world.getMonth(); profile.mInGameTime.mYear = world.getYear(); - /// \todo time played + profile.mTimePlayed = mTimePlayed; profile.mDescription = description; if (!slot) @@ -114,6 +115,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl mCharacterManager.clearCurrentCharacter(); } + mTimePlayed = slot->mProfile.mTimePlayed; + ESM::ESMReader reader; reader.open (slot->mPath.string()); @@ -144,3 +147,8 @@ MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() { return mCharacterManager.end(); } + +void MWState::StateManager::update (float duration) +{ + mTimePlayed += duration; +} diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 3965632cc3..720b1b6b0e 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -14,6 +14,7 @@ namespace MWState bool mQuitRequest; State mState; CharacterManager mCharacterManager; + double mTimePlayed; public: @@ -50,6 +51,8 @@ namespace MWState /// iterator. virtual CharacterIterator characterEnd(); + + virtual void update (float duration); }; } From 5aea6ef80f6bce0718b8987afc9c414e9fea59b6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 11:22:34 +0100 Subject: [PATCH 045/120] some clean up for the cleanup code --- apps/openmw/mwstate/statemanagerimp.cpp | 33 +++++++++++++------------ apps/openmw/mwstate/statemanagerimp.hpp | 4 +++ apps/openmw/mwworld/worldimp.cpp | 3 ++- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a273aced7f..b750f56fed 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -17,6 +17,18 @@ #include "../mwmechanics/npcstats.hpp" +void MWState::StateManager::cleanup() +{ + if (mState!=State_NoGame) + { + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + mState = State_NoGame; + mCharacterManager.clearCurrentCharacter(); + mTimePlayed = 0; + } +} + MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) : mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) { @@ -40,18 +52,10 @@ MWState::StateManager::State MWState::StateManager::getState() const void MWState::StateManager::newGame (bool bypass) { - if (mState!=State_NoGame) - { - MWBase::Environment::get().getDialogueManager()->clear(); - MWBase::Environment::get().getJournal()->clear(); - mState = State_NoGame; - mCharacterManager.clearCurrentCharacter(); - mTimePlayed = 0; - } + cleanup(); if (!bypass) { - /// \todo extract cleanup code MWBase::Environment::get().getWorld()->startNewGame(); MWBase::Environment::get().getWindowManager()->setNewGame (true); } @@ -99,6 +103,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.startRecord ("SAVE"); slot->mProfile.save (writer); writer.endRecord ("SAVE"); + + /// \todo write saved game data + writer.close(); Settings::Manager::setString ("character", "Saves", @@ -107,13 +114,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot void MWState::StateManager::loadGame (const Character *character, const Slot *slot) { - if (mState!=State_NoGame) - { - MWBase::Environment::get().getDialogueManager()->clear(); - MWBase::Environment::get().getJournal()->clear(); - mState = State_NoGame; - mCharacterManager.clearCurrentCharacter(); - } + cleanup(); mTimePlayed = slot->mProfile.mTimePlayed; diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 720b1b6b0e..78b5787660 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -16,6 +16,10 @@ namespace MWState CharacterManager mCharacterManager; double mTimePlayed; + private: + + void cleanup(); + public: StateManager (const boost::filesystem::path& saves, const std::string& game); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1e7b8122fb..d7fdc3bc1d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -290,7 +290,6 @@ namespace MWWorld pos.rot[2] = 0; mWorldScene->changeToExteriorCell(pos); - // enable collision if(!mPhysics->toggleCollisionMode()) mPhysics->toggleCollisionMode(); @@ -300,6 +299,7 @@ namespace MWWorld // global variables delete mGlobalVariables; + mGlobalVariables = 0; mGlobalVariables = new Globals (mStore); // set new game mark @@ -308,6 +308,7 @@ namespace MWWorld // we don't want old weather to persist on a new game delete mWeatherManager; + mWeatherManager = 0; mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); From e432ab5e8ace6e4a7e7f03e9635c244e6d66bffc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 11:51:21 +0100 Subject: [PATCH 046/120] fixed static problem in Land recrod save function --- components/esm/loadland.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index ede200d79d..bc16c65d3b 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -14,7 +14,7 @@ void Land::LandData::save(ESMWriter &esm) esm.writeHNT("VNML", mNormals, sizeof(VNML)); } if (mDataTypes & Land::DATA_VHGT) { - static VHGT offsets; + VHGT offsets; offsets.mHeightOffset = mHeights[0] / HEIGHT_SCALE; offsets.mUnk1 = mUnk1; offsets.mUnk2 = mUnk2; From 750133c0dd18c39c795402ff7148740bb09a7b5e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 11:05:07 +0100 Subject: [PATCH 047/120] one more fix to SavedGame record saving --- components/esm/savedgame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index e98608fe6d..7c76f40007 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -33,6 +33,6 @@ void ESM::SavedGame::save (ESMWriter &esm) const for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) - esm.writeHNCString ("DEPE", *iter); + esm.writeHNString ("DEPE", *iter); } From aebc2791a50f09d518a0cc40f35286f90d813c38 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 11:08:41 +0100 Subject: [PATCH 048/120] fixed selecting current character based on value stored in settings --- apps/openmw/mwgui/savegamedialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 5f9b6a3c15..d1e047c099 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -65,7 +65,10 @@ namespace MWGui if (mCurrentCharacter == &*it || (!mCurrentCharacter && directory==Misc::StringUtils::lowerCase ( it->begin()->mPath.parent_path().filename().string()))) + { + mCurrentCharacter = &*it; mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); + } } } From 9d64c92d33efb3aac740e3cbe5f8afa8607434f9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 11:57:33 +0100 Subject: [PATCH 049/120] store text in journal entries --- apps/openmw/mwdialogue/journalentry.cpp | 16 +++++++++++----- apps/openmw/mwdialogue/journalentry.hpp | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index dd1ad3f661..20963eb79c 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -14,21 +14,27 @@ namespace MWDialogue JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId) : mTopic (topic), mInfoId (infoId) - {} - - std::string JournalEntry::getText (const MWWorld::ESMStore& store) const { const ESM::Dialogue *dialogue = - store.get().find (mTopic); + MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); for (std::vector::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 " + mTopic); } + std::string JournalEntry::getText (const MWWorld::ESMStore& store) const + { + return mText; + } + JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) { return JournalEntry (topic, idFromIndex (topic, index)); diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 9d009b48b2..ab4adece99 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -15,6 +15,7 @@ namespace MWDialogue { std::string mTopic; std::string mInfoId; + std::string mText; JournalEntry(); From eed46960fe7a769b979f91a1fafa3ba7e0af13d7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 12:02:56 +0100 Subject: [PATCH 050/120] some spelling fixes --- apps/openmw/mwdialogue/quest.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/quest.hpp b/apps/openmw/mwdialogue/quest.hpp index 3afa81fac0..566eef6bfa 100644 --- a/apps/openmw/mwdialogue/quest.hpp +++ b/apps/openmw/mwdialogue/quest.hpp @@ -5,7 +5,7 @@ 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; @@ -23,7 +23,7 @@ namespace MWDialogue 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; From 177aab536dff53175b01241984296fca386e669b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 12:41:18 +0100 Subject: [PATCH 051/120] storing topic name in MWDialogue::Topic (avoids a lookup in the GUI and also serves as preparation for better localisation support in OpenMW 1.1) --- apps/openmw/mwdialogue/quest.cpp | 2 +- apps/openmw/mwdialogue/quest.hpp | 2 +- apps/openmw/mwdialogue/topic.cpp | 11 ++++++++++- apps/openmw/mwdialogue/topic.hpp | 3 ++- apps/openmw/mwgui/journalviewmodel.cpp | 9 ++------- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 5e2739be16..75dcaa028b 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -16,7 +16,7 @@ namespace MWDialogue : Topic (topic), mIndex (0), mFinished (false) {} - const std::string Quest::getName() const + std::string Quest::getName() const { const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); diff --git a/apps/openmw/mwdialogue/quest.hpp b/apps/openmw/mwdialogue/quest.hpp index 566eef6bfa..c6f0d0becb 100644 --- a/apps/openmw/mwdialogue/quest.hpp +++ b/apps/openmw/mwdialogue/quest.hpp @@ -17,7 +17,7 @@ namespace MWDialogue Quest (const std::string& topic); - const std::string getName() const; + virtual std::string getName() const; ///< May be an empty string int getIndex() const; diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index 3253b20d66..fc8545b5e2 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -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().find (topic)->mId) {} Topic::~Topic() @@ -27,6 +31,11 @@ namespace MWDialogue mEntries.push_back (entry.mInfoId); } + std::string Topic::getName() const + { + return mName; + } + Topic::TEntryIter Topic::begin() const { return mEntries.begin(); diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index c3f0baabc2..17601977a9 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -19,6 +19,7 @@ namespace MWDialogue protected: std::string mTopic; + std::string mName; TEntryContainer mEntries; // info-IDs public: @@ -34,7 +35,7 @@ namespace MWDialogue /// /// \note Redundant entries are ignored. - std::string const & getName () const { return mTopic; } + virtual std::string getName () const; TEntryIter begin() const; ///< Iterator pointing to the begin of the journal for this topic. diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 89885d303d..e35d350135 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -288,9 +288,7 @@ struct JournalViewModelImpl : JournalViewModel void visitTopicName (TopicId topicId, boost::function visitor) const { MWDialogue::Topic const & topic = * reinterpret_cast (topicId); - // This is to get the correct case for the topic - const std::string& name = MWBase::Environment::get().getWorld()->getStore().get().find(topic.getName())->mId; - visitor (toUtf8Span (name)); + visitor (toUtf8Span (topic.getName())); } void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const @@ -302,10 +300,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().find(i->first)->mId; - - visitor (TopicId (&i->second), toUtf8Span (name)); + visitor (TopicId (&i->second), toUtf8Span (i->second.getName())); } } From 43f5f16731cb195497f4832426222930213b7185 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 12:54:44 +0100 Subject: [PATCH 052/120] removed a todo comment --- apps/openmw/mwgui/journalviewmodel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index e35d350135..049515ac01 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -317,7 +317,6 @@ 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()); } From eec9821cd8345235e95479a46819e9b0a06ea2f9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 14:41:12 +0100 Subject: [PATCH 053/120] added records for storing journals in saved game files --- components/CMakeLists.txt | 2 +- components/esm/defs.hpp | 2 ++ components/esm/journalentry.cpp | 35 +++++++++++++++++++++++++++++++++ components/esm/journalentry.hpp | 35 +++++++++++++++++++++++++++++++++ components/esm/queststate.cpp | 19 ++++++++++++++++++ components/esm/queststate.hpp | 24 ++++++++++++++++++++++ 6 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 components/esm/journalentry.cpp create mode 100644 components/esm/journalentry.hpp create mode 100644 components/esm/queststate.cpp create mode 100644 components/esm/queststate.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index ce5965be12..3223ab1a72 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame + savedgame journalentry queststate ) add_component_dir (misc diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 5a5ef9f1cf..03091d9d8c 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -85,6 +85,8 @@ enum RecNameInts // format 0 - saved games REC_SAVE = 0x45564153, + REC_JOUR = 0x524f55a4, + REC_QUES = 0x53455551, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/journalentry.cpp b/components/esm/journalentry.cpp new file mode 100644 index 0000000000..514bf35972 --- /dev/null +++ b/components/esm/journalentry.cpp @@ -0,0 +1,35 @@ + +#include "journalentry.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::JournalEntry::load (ESMReader &esm) +{ + esm.getHNOT (mType, "JETY"); + mTopic = esm.getHNString ("YETO"); + mInfo = esm.getHNString ("YEIN"); + mText = esm.getHNString ("TEXT"); + + if (mType==Type_Journal) + { + esm.getHNT (mDay, "JEDA"); + esm.getHNT (mMonth, "JEMO"); + esm.getHNT (mDayOfMonth, "JEDM"); + } +} + +void ESM::JournalEntry::save (ESMWriter &esm) const +{ + esm.writeHNT ("JETY", mType); + esm.writeHNString ("YETO", mTopic); + esm.writeHNString ("YEIN", mInfo); + esm.writeHNString ("TEXT", mText); + + if (mType==Type_Journal) + { + esm.writeHNT ("JEDA", mDay); + esm.writeHNT ("JEMO", mMonth); + esm.writeHNT ("JEDM", mDayOfMonth); + } +} \ No newline at end of file diff --git a/components/esm/journalentry.hpp b/components/esm/journalentry.hpp new file mode 100644 index 0000000000..94808dde6b --- /dev/null +++ b/components/esm/journalentry.hpp @@ -0,0 +1,35 @@ +#ifndef OPENMW_ESM_JOURNALENTRY_H +#define OPENMW_ESM_JOURNALENTRY_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct JournalEntry + { + enum Type + { + Type_Journal = 0, + Type_Topic = 1, + Type_Quest = 2 + }; + + int mType; + std::string mTopic; + std::string mInfo; + std::string mText; + int mDay; // time stamp + int mMonth; + int mDayOfMonth; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif diff --git a/components/esm/queststate.cpp b/components/esm/queststate.cpp new file mode 100644 index 0000000000..5931e8b909 --- /dev/null +++ b/components/esm/queststate.cpp @@ -0,0 +1,19 @@ + +#include "queststate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::QuestState::load (ESMReader &esm) +{ + mTopic = esm.getHNString ("YETO"); + esm.getHNOT (mState, "QSTAT"); + esm.getHNOT (mFinished, "QFIN"); +} + +void ESM::QuestState::save (ESMWriter &esm) const +{ + esm.writeHNString ("YETO", mTopic); + esm.writeHNT ("QSTAT", mState); + esm.writeHNT ("QFIN", mFinished); +} \ No newline at end of file diff --git a/components/esm/queststate.hpp b/components/esm/queststate.hpp new file mode 100644 index 0000000000..1769336f2f --- /dev/null +++ b/components/esm/queststate.hpp @@ -0,0 +1,24 @@ +#ifndef OPENMW_ESM_QUESTSTATE_H +#define OPENMW_ESM_QUESTSTATE_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct QuestState + { + std::string mTopic; + int mState; + unsigned char mFinished; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file From 16e2d67b1f6fb47fe0336b23e0b064a940949b23 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 1 Dec 2013 13:32:11 +0100 Subject: [PATCH 054/120] added overloaded start/endRecord functions to ESMWriter --- apps/opencs/model/doc/savingstages.cpp | 19 ++++--------------- apps/opencs/model/doc/savingstages.hpp | 4 ++-- apps/opencs/model/world/refiddata.hpp | 9 ++------- components/esm/esmwriter.cpp | 20 ++++++++++++++++++++ components/esm/esmwriter.hpp | 2 ++ 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 8e9bcfc0de..d7df2117d0 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -133,16 +133,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector (&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::vectormModified.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 (&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); } } } diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index ca5586511d..b8eb0a3b30 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -104,10 +104,10 @@ namespace CSMDoc /// \todo make endianess agnostic (change ESMWriter interface?) type += reinterpret_cast (&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) { diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 9595ab23b5..761c7feaac 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -136,15 +136,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 (&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) { diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 069d75c7bb..f38591b7bf 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -88,6 +88,16 @@ namespace ESM assert(mRecords.back().size == 0); } + void ESMWriter::startRecord (uint32_t name, uint32_t flags) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + startRecord (type, flags); + } + void ESMWriter::startSubRecord(const std::string& name) { writeName(name); @@ -117,6 +127,16 @@ namespace ESM } + void ESMWriter::endRecord (uint32_t name) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + endRecord (type); + } + void ESMWriter::writeHNString(const std::string& name, const std::string& data) { startSubRecord(name); diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index d6646471b8..94f0a10048 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -90,8 +90,10 @@ class ESMWriter } void startRecord(const std::string& name, uint32_t flags = 0); + void startRecord(uint32_t name, uint32_t flags = 0); void startSubRecord(const std::string& name); void endRecord(const std::string& name); + void endRecord(uint32_t name); void writeHString(const std::string& data); void writeHCString(const std::string& data); void writeName(const std::string& data); From b273f9e3874e5f52cc8025e76f47559bdb91e93a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 1 Dec 2013 14:44:27 +0100 Subject: [PATCH 055/120] splitting JournalEntry into Entry and JournalEntry --- apps/openmw/mwdialogue/journalentry.cpp | 20 +++++++++++++------ apps/openmw/mwdialogue/journalentry.hpp | 26 +++++++++++++++---------- apps/openmw/mwgui/journalviewmodel.cpp | 5 ++--- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 20963eb79c..b83d19f8c4 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -10,13 +10,13 @@ namespace MWDialogue { - JournalEntry::JournalEntry() {} + Entry::Entry() {} - JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId) - : mTopic (topic), mInfoId (infoId) + Entry::Entry (const std::string& topic, const std::string& infoId) + : mInfoId (infoId) { const ESM::Dialogue *dialogue = - MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); + MWBase::Environment::get().getWorld()->getStore().get().find (topic); for (std::vector::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) @@ -27,14 +27,21 @@ namespace MWDialogue return; } - throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic); + throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + topic); } - std::string JournalEntry::getText (const MWWorld::ESMStore& store) const + std::string Entry::getText() const { return mText; } + + JournalEntry::JournalEntry() {} + + JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId) + : Entry (topic, infoId), mTopic (topic) + {} + JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) { return JournalEntry (topic, idFromIndex (topic, index)); @@ -55,6 +62,7 @@ namespace MWDialogue throw std::runtime_error ("unknown journal index for topic " + topic); } + StampedJournalEntry::StampedJournalEntry() : mDay (0), mMonth (0), mDayOfMonth (0) {} diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index ab4adece99..b51c2d2d49 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -3,26 +3,32 @@ #include -namespace MWWorld -{ - struct ESMStore; -} - 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); + + std::string getText() 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; - static JournalEntry makeFromQuest (const std::string& topic, int index); static std::string idFromIndex (const std::string& topic, int index); diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 049515ac01..7ee24a288d 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -233,7 +233,7 @@ struct JournalViewModelImpl : JournalViewModel std::string getText () const { - return itr->getText(MWBase::Environment::get().getWorld()->getStore()); + return itr->getText(); } Utf8Span timestamp () const @@ -317,8 +317,7 @@ struct JournalViewModelImpl : JournalViewModel std::string getText () const { - return mTopic.getEntry (*itr).getText(MWBase::Environment::get().getWorld()->getStore()); - + return mTopic.getEntry (*itr).getText(); } Utf8Span source () const From 0f971163f709e31f98810b8ecf205faea19f96e2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 1 Dec 2013 14:50:25 +0100 Subject: [PATCH 056/120] use Entry instead of plain string for topic and quest entries --- apps/openmw/mwdialogue/quest.cpp | 4 ++-- apps/openmw/mwdialogue/topic.cpp | 6 +----- apps/openmw/mwdialogue/topic.hpp | 4 ++-- apps/openmw/mwgui/journalviewmodel.cpp | 4 ++-- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 75dcaa028b..12763effd3 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -82,9 +82,9 @@ 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 } } diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index fc8545b5e2..f40e585dc0 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -24,11 +24,7 @@ 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.mInfoId); + mEntries.push_back (entry); // we want slicing here } std::string Topic::getName() const diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index 17601977a9..ffc5e94708 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -13,14 +13,14 @@ namespace MWDialogue { public: - typedef std::vector TEntryContainer; + typedef std::vector TEntryContainer; typedef TEntryContainer::const_iterator TEntryIter; protected: std::string mTopic; std::string mName; - TEntryContainer mEntries; // info-IDs + TEntryContainer mEntries; public: diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 7ee24a288d..ad0af3f053 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -268,7 +268,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 (this, i)); } } @@ -317,7 +317,7 @@ struct JournalViewModelImpl : JournalViewModel std::string getText () const { - return mTopic.getEntry (*itr).getText(); + return itr->getText(); } Utf8Span source () const From 7d8e3ac6518e4bf3449a58dce83c6ee1abcf0f00 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Dec 2013 13:51:44 +0100 Subject: [PATCH 057/120] fixed QuestState::load/save --- components/esm/queststate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esm/queststate.cpp b/components/esm/queststate.cpp index 5931e8b909..e938267255 100644 --- a/components/esm/queststate.cpp +++ b/components/esm/queststate.cpp @@ -7,13 +7,13 @@ void ESM::QuestState::load (ESMReader &esm) { mTopic = esm.getHNString ("YETO"); - esm.getHNOT (mState, "QSTAT"); + esm.getHNOT (mState, "QSTA"); esm.getHNOT (mFinished, "QFIN"); } void ESM::QuestState::save (ESMWriter &esm) const { esm.writeHNString ("YETO", mTopic); - esm.writeHNT ("QSTAT", mState); + esm.writeHNT ("QSTA", mState); esm.writeHNT ("QFIN", mFinished); } \ No newline at end of file From 2293b92efe5534b97c021683d444a18584b6212c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Dec 2013 14:28:46 +0100 Subject: [PATCH 058/120] storing and loading the journal --- apps/openmw/mwbase/journal.hpp | 14 +++ apps/openmw/mwdialogue/journalentry.cpp | 33 ++++++ apps/openmw/mwdialogue/journalentry.hpp | 17 +++ apps/openmw/mwdialogue/journalimp.cpp | 135 ++++++++++++++++++++++-- apps/openmw/mwdialogue/journalimp.hpp | 10 ++ apps/openmw/mwdialogue/quest.cpp | 13 +++ apps/openmw/mwdialogue/quest.hpp | 9 ++ apps/openmw/mwdialogue/topic.cpp | 10 ++ apps/openmw/mwdialogue/topic.hpp | 13 ++- apps/openmw/mwstate/statemanagerimp.cpp | 42 ++++++-- 10 files changed, 278 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index 51e51edda9..81b4ba0b4b 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -5,10 +5,18 @@ #include #include +#include + #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; }; } diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index b83d19f8c4..7828d18ad7 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -3,6 +3,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -30,11 +32,19 @@ namespace MWDialogue 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() {} @@ -42,6 +52,16 @@ namespace MWDialogue : Entry (topic, infoId), mTopic (topic) {} + JournalEntry::JournalEntry (const ESM::JournalEntry& record) + : Entry (record), mTopic (record.mTopic) + {} + + void JournalEntry::write (ESM::JournalEntry& entry) const + { + Entry::write (entry); + entry.mTopic = mTopic; + } + JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) { return JournalEntry (topic, idFromIndex (topic, index)); @@ -72,6 +92,19 @@ 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()->getGlobalInt ("dayspassed"); diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index b51c2d2d49..18d022aab9 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -3,6 +3,11 @@ #include +namespace ESM +{ + struct JournalEntry; +} + namespace MWDialogue { /// \brief Basic quest/dialogue/topic entry @@ -15,7 +20,11 @@ namespace MWDialogue 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 @@ -29,6 +38,10 @@ namespace MWDialogue JournalEntry (const std::string& topic, const std::string& infoId); + JournalEntry (const ESM::JournalEntry& record); + + void write (ESM::JournalEntry& entry) const; + static JournalEntry makeFromQuest (const std::string& topic, int index); static std::string idFromIndex (const std::string& topic, int index); @@ -46,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); }; } diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 23cfb5fdd1..b9359aae60 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -1,6 +1,13 @@ #include "journalimp.hpp" +#include + +#include +#include +#include +#include + #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" @@ -26,6 +33,22 @@ namespace MWDialogue return iter->second; } + Topic& Journal::getTopic (const std::string& id) + { + TTopicContainer::iterator iter = mTopics.find (id); + + if (iter==mTopics.end()) + { + std::pair result + = mTopics.insert (std::make_pair (id, Topic (id))); + + iter = result.first; + } + + return iter->second; + } + + Journal::Journal() {} @@ -66,17 +89,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 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 +133,104 @@ namespace MWDialogue { return mTopics.end(); } + + int Journal::countSavedGameRecords() const + { + int count = static_cast (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); + + 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); + + mQuests.insert (std::make_pair (record.mTopic, record)); + } + } } diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index f4f8eb1c29..54c49df018 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -15,8 +15,12 @@ namespace MWDialogue TQuestContainer mQuests; TTopicContainer mTopics; + private: + Quest& getQuest (const std::string& id); + Topic& getTopic (const std::string& id); + public: Journal(); @@ -55,6 +59,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); }; } diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 12763effd3..14c5c1da94 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -1,6 +1,8 @@ #include "quest.hpp" +#include + #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" @@ -16,6 +18,10 @@ namespace MWDialogue : Topic (topic), mIndex (0), mFinished (false) {} + 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 = @@ -87,4 +93,11 @@ namespace MWDialogue mEntries.push_back (entry); // we want slicing here } + + void Quest::write (ESM::QuestState& state) const + { + state.mTopic = getTopic(); + state.mState = mIndex; + state.mFinished = mFinished; + } } diff --git a/apps/openmw/mwdialogue/quest.hpp b/apps/openmw/mwdialogue/quest.hpp index c6f0d0becb..40824f398e 100644 --- a/apps/openmw/mwdialogue/quest.hpp +++ b/apps/openmw/mwdialogue/quest.hpp @@ -3,6 +3,11 @@ #include "topic.hpp" +namespace ESM +{ + struct QuestState; +} + namespace MWDialogue { /// \brief A quest in progress or a completed quest @@ -17,6 +22,8 @@ namespace MWDialogue Quest (const std::string& topic); + Quest (const ESM::QuestState& state); + virtual std::string getName() const; ///< May be an empty string @@ -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; }; } diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index f40e585dc0..0e546f43bc 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -27,6 +27,16 @@ namespace MWDialogue mEntries.push_back (entry); // we want slicing here } + void Topic::insertEntry (const ESM::JournalEntry& entry) + { + mEntries.push_back (entry); + } + + std::string Topic::getTopic() const + { + return mTopic; + } + std::string Topic::getName() const { return mName; diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index ffc5e94708..02fa6d5246 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -6,6 +6,11 @@ #include "journalentry.hpp" +namespace ESM +{ + struct JournalEntry; +} + namespace MWDialogue { /// \brief Collection of seen responses for a topic @@ -35,7 +40,13 @@ namespace MWDialogue /// /// \note Redundant entries are ignored. - virtual std::string getName () const; + 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. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index b750f56fed..5bc313f37d 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -99,11 +99,17 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot std::ofstream stream (slot->mPath.string().c_str()); ESM::ESMWriter writer; writer.setFormat (ESM::Header::CurrentFormat); - writer.save (stream); - writer.startRecord ("SAVE"); - slot->mProfile.save (writer); - writer.endRecord ("SAVE"); + writer.setRecordCount ( + 1+ // saved game header + MWBase::Environment::get().getJournal()->countSavedGameRecords()); + writer.save (stream); + + writer.startRecord (ESM::REC_SAVE); + slot->mProfile.save (writer); + writer.endRecord (ESM::REC_SAVE); + + MWBase::Environment::get().getJournal()->write (writer); /// \todo write saved game data writer.close(); @@ -121,10 +127,32 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl ESM::ESMReader reader; reader.open (slot->mPath.string()); - reader.getRecName(); // don't need to read that here - reader.getRecHeader(); + while (reader.hasMoreRecs()) + { + ESM::NAME n = reader.getRecName(); + reader.getRecHeader(); - /// \todo read saved game data + 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; + + default: + + // ignore invalid records + /// \todo log error + reader.skipRecord(); + } + } mCharacterManager.setCurrentCharacter(character); From e269c9e6898314f7c6201b30f3f439f7b7321d64 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Dec 2013 14:30:18 +0100 Subject: [PATCH 059/120] changed a few sub record names to make them more unique --- components/esm/savedgame.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 7c76f40007..55b17289c3 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -9,10 +9,10 @@ unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; void ESM::SavedGame::load (ESMReader &esm) { - mPlayerName = esm.getHNString("PNAM"); - esm.getHNOT (mPlayerLevel, "PLEV"); - mPlayerClass = esm.getHNString("PCLA"); - mPlayerCell = esm.getHNString("PCEL"); + mPlayerName = esm.getHNString("PLNA"); + esm.getHNOT (mPlayerLevel, "PLLE"); + mPlayerClass = esm.getHNString("PLCL"); + mPlayerCell = esm.getHNString("PLCE"); esm.getHNT (mInGameTime, "TSTM", 16); esm.getHNT (mTimePlayed, "TIME"); mDescription = esm.getHNString ("DESC"); @@ -23,10 +23,10 @@ void ESM::SavedGame::load (ESMReader &esm) void ESM::SavedGame::save (ESMWriter &esm) const { - esm.writeHNString ("PNAM", mPlayerName); - esm.writeHNT ("PLEV", mPlayerLevel); - esm.writeHNString ("PCLA", mPlayerClass); - esm.writeHNString ("PCEL", mPlayerCell); + esm.writeHNString ("PLNA", mPlayerName); + esm.writeHNT ("PLLE", mPlayerLevel); + esm.writeHNString ("PLCL", mPlayerClass); + esm.writeHNString ("PLCE", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); esm.writeHNString ("DESC", mDescription); From 34cdd2bb1f63a38dbe4741c817259f6cd986eea2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Dec 2013 14:39:54 +0100 Subject: [PATCH 060/120] deal with dialogue/info records that don't exist anymore --- apps/openmw/mwdialogue/journalimp.cpp | 44 +++++++++++++++++++-------- apps/openmw/mwdialogue/journalimp.hpp | 2 ++ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index b9359aae60..f24a93356d 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -48,6 +48,22 @@ namespace MWDialogue 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().search (topicId)) + { + if (infoId.empty()) + return true; + + for (std::vector::const_iterator iter (dialogue->mInfo.begin()); + iter!=dialogue->mInfo.end(); ++iter) + if (iter->mId == infoId) + return true; + } + + return false; + } Journal::Journal() {} @@ -207,30 +223,32 @@ namespace MWDialogue ESM::JournalEntry record; record.load (reader); - switch (record.mType) - { - case ESM::JournalEntry::Type_Quest: + if (isThere (record.mTopic, record.mInfo)) + switch (record.mType) + { + case ESM::JournalEntry::Type_Quest: - getQuest (record.mTopic).insertEntry (record); - break; + getQuest (record.mTopic).insertEntry (record); + break; - case ESM::JournalEntry::Type_Journal: + case ESM::JournalEntry::Type_Journal: - mJournal.push_back (record); - break; + mJournal.push_back (record); + break; - case ESM::JournalEntry::Type_Topic: + case ESM::JournalEntry::Type_Topic: - getTopic (record.mTopic).insertEntry (record); - break; - } + getTopic (record.mTopic).insertEntry (record); + break; + } } else if (type==ESM::REC_QUES) { ESM::QuestState record; record.load (reader); - mQuests.insert (std::make_pair (record.mTopic, record)); + if (isThere (record.mTopic)) + mQuests.insert (std::make_pair (record.mTopic, record)); } } } diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index 54c49df018..86091a12d2 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -21,6 +21,8 @@ namespace MWDialogue Topic& getTopic (const std::string& id); + bool isThere (const std::string& topicId, const std::string& infoId = "") const; + public: Journal(); From 63721682f6724c84de992f15d301ff4e47a66a80 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Dec 2013 15:19:13 +0100 Subject: [PATCH 061/120] GUI fix: previous character was selected when saving new character --- apps/openmw/mwgui/savegamedialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index d1e047c099..04461fef99 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -63,7 +63,7 @@ namespace MWGui mCharacterSelection->addItem (title.str()); if (mCurrentCharacter == &*it || - (!mCurrentCharacter && directory==Misc::StringUtils::lowerCase ( + (!mCurrentCharacter && !mSaving && directory==Misc::StringUtils::lowerCase ( it->begin()->mPath.parent_path().filename().string()))) { mCurrentCharacter = &*it; From 537b2efe8ebb3d4b3ee29aee5cee69cbdcd007a7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 12:49:25 +0100 Subject: [PATCH 062/120] first round of cleaning up world cleanup --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/refdata.cpp | 15 +++++++++-- apps/openmw/mwworld/refdata.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 36 ++++++++++++------------- apps/openmw/mwworld/worldimp.hpp | 2 ++ 6 files changed, 38 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 87a9b5bbc3..837b4c23ec 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -96,6 +96,8 @@ namespace MWBase virtual void startNewGame() = 0; + virtual void clear() = 0; + virtual OEngine::Render::Fader* getFader() = 0; ///< \ŧodo remove this function. Rendering details should not be exposed. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 5bc313f37d..09e574e84d 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -23,6 +23,7 @@ void MWState::StateManager::cleanup() { MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); + MWBase::Environment::get().getWorld()->clear(); mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); mTimePlayed = 0; diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index c1a3ae7859..87d0efe190 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -32,6 +32,17 @@ namespace MWWorld mCustomData = 0; } + RefData::RefData() + : mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mCustomData (0) + { + for (int i=0; i<3; ++i) + { + mLocalRotation.rot[i] = 0; + mPosition.pos[i] = 0; + mPosition.rot[i] = 0; + } + } + RefData::RefData (const ESM::CellRef& cellRef) : mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mPosition (cellRef.mPos), mCustomData (0) @@ -88,7 +99,7 @@ namespace MWWorld static const std::string empty; return empty; } - + return mBaseNode->getName(); } @@ -120,7 +131,7 @@ namespace MWWorld { if(count == 0) MWBase::Environment::get().getWorld()->removeRefScript(this); - + mCount = count; } diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index d5701efc51..07841e4704 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -48,6 +48,8 @@ namespace MWWorld public: + RefData(); + /// @param cellRef Used to copy constant data such as position into this class where it can /// be altered without effecting the original data. This makes it possible /// to reset the position as the orignal data is still held in the CellRef diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d7fdc3bc1d..509aee983f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -258,26 +258,12 @@ namespace MWWorld void World::startNewGame() { - mWorldScene->changeToVoid(); - - mStore.clearDynamic(); - mStore.setUp(); - - mCells.clear(); - // Rebuild player setupPlayer(); - MWWorld::Ptr player = mPlayer->getPlayer(); - - // removes NpcStats, ContainerStore etc - player.getRefData().setCustomData(NULL); renderPlayer(); mRendering->resetCamera(); - // make sure to do this so that local scripts from items that were in the players inventory are removed - mLocalScripts.clear(); - MWBase::Environment::get().getWindowManager()->updatePlayer(); ESM::Position pos; @@ -290,10 +276,6 @@ namespace MWWorld pos.rot[2] = 0; mWorldScene->changeToExteriorCell(pos); - // enable collision - if(!mPhysics->toggleCollisionMode()) - mPhysics->toggleCollisionMode(); - // FIXME: should be set to 1, but the sound manager won't pause newly started sounds mPlayIntro = 2; @@ -314,6 +296,24 @@ namespace MWWorld MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); } + void World::clear() + { + mLocalScripts.clear(); + + // enable collision + if (!mPhysics->toggleCollisionMode()) + mPhysics->toggleCollisionMode(); + + mWorldScene->changeToVoid(); + + if (mPlayer) + mPlayer->getPlayer().getRefData() = RefData(); + + mStore.clearDynamic(); + mStore.setUp(); + + mCells.clear(); + } void World::ensureNeededRecords() { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2b7d157ff4..1cee7d50f4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -160,6 +160,8 @@ namespace MWWorld virtual void startNewGame(); + virtual void clear(); + virtual OEngine::Render::Fader* getFader(); ///< \ŧodo remove this function. Rendering details should not be exposed. From e818d43bc3d541b1d253e3db9800850c1dba6f98 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 13:21:26 +0100 Subject: [PATCH 063/120] removed an outdated typedef and some dead code --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwgui/referenceinterface.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/objects.cpp | 2 +- apps/openmw/mwrender/actors.cpp | 2 +- apps/openmw/mwrender/debugging.cpp | 8 ++-- apps/openmw/mwrender/localmap.cpp | 6 +-- apps/openmw/mwrender/objects.cpp | 6 +-- apps/openmw/mwrender/renderingmanager.cpp | 14 +++---- apps/openmw/mwscript/cellextensions.cpp | 6 +-- apps/openmw/mwsound/soundmanagerimp.cpp | 4 +- apps/openmw/mwworld/cells.cpp | 46 ++++++++++----------- apps/openmw/mwworld/localscripts.cpp | 14 +++---- apps/openmw/mwworld/ptr.hpp | 5 +-- apps/openmw/mwworld/scene.cpp | 10 ++--- apps/openmw/mwworld/scene.hpp | 4 +- apps/openmw/mwworld/worldimp.cpp | 50 ++++++----------------- apps/openmw/mwworld/worldimp.hpp | 6 +-- 19 files changed, 82 insertions(+), 109 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 837b4c23ec..36c9708398 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -351,7 +351,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; diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index 86a85be18e..9ba7154c26 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -18,7 +18,7 @@ namespace MWGui void ReferenceInterface::checkReferenceAvailable() { - MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); // check if player has changed cell, or count of the reference has become 0 if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2be88b7f0a..887bf2c68d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -750,7 +750,7 @@ 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); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index cc431a7b04..66d8de6f85 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -302,7 +302,7 @@ namespace MWMechanics } } - void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore) + void Actors::dropActors (const MWWorld::CellStore *cellStore) { PtrControllerMap::iterator iter = mActors.begin(); while(iter != mActors.end()) diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 694987855e..8a1e6ee6bf 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -45,7 +45,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()) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 1bdec6e193..5eecfaf35a 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -126,7 +126,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();) { diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index b318c2d569..2b61e109b6 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -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().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()) { diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 5f41289788..f70533d22d 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -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 diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index fd81baf6ed..852c25044d 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -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]; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 57e00d76c7..cd309df47c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -221,7 +221,7 @@ OEngine::Render::Fader* RenderingManager::getFader() return mRendering.getFader(); } -void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store) +void RenderingManager::removeCell (MWWorld::CellStore *store) { mObjects.removeCell(store); mActors.removeCell(store); @@ -238,7 +238,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(); @@ -410,7 +410,7 @@ void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt) mOcclusionQuery->setActive(false); } -void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store) +void RenderingManager::waterAdded (MWWorld::CellStore *store) { const MWWorld::Store &lands = MWBase::Environment::get().getWorld()->getStore().get(); @@ -501,7 +501,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); @@ -554,7 +554,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); @@ -651,7 +651,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()) { @@ -670,7 +670,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); } diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 316f912dad..ce39cfa653 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -125,7 +125,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); runtime.push (cell->mWaterLevel); } }; @@ -138,7 +138,7 @@ namespace MWScript { Interpreter::Type_Float level = runtime[0].mFloat; - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); if (cell->mCell->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); @@ -156,7 +156,7 @@ namespace MWScript { Interpreter::Type_Float level = runtime[0].mFloat; - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); if (cell->mCell->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 372be83938..c1c891ab47 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -399,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()) @@ -595,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; diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 37c4b6a3f4..2cb22cf59c 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -7,28 +7,28 @@ #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::map::iterator result = mInteriors.find (Misc::StringUtils::lowerCase(cell->mName)); + std::map::iterator result = mInteriors.find (Misc::StringUtils::lowerCase(cell->mName)); if (result==mInteriors.end()) { - result = mInteriors.insert (std::make_pair (Misc::StringUtils::lowerCase(cell->mName), Ptr::CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (Misc::StringUtils::lowerCase(cell->mName), CellStore (cell))).first; } return &result->second; } else { - std::map, Ptr::CellStore>::iterator result = + std::map, 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; } @@ -40,11 +40,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); @@ -61,13 +61,13 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector& reader) : mStore (store), mReader (reader), - mIdCache (40, std::pair ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable + mIdCache (40, std::pair ("", (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, Ptr::CellStore>::iterator result = + std::map, CellStore>::iterator result = mExteriors.find (std::make_pair (x, y)); if (result==mExteriors.end()) @@ -92,7 +92,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); @@ -101,19 +101,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::iterator result = mInteriors.find (lowerName); + std::map::iterator result = mInteriors.find (lowerName); if (result==mInteriors.end()) { const ESM::Cell *cell = mStore.get().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); } @@ -121,13 +121,13 @@ 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::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) { std::string lowerCase = Misc::StringUtils::lowerCase(name); @@ -208,7 +208,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 >::iterator iter (mIdCache.begin()); + for (std::vector >::iterator iter (mIdCache.begin()); iter!=mIdCache.end(); ++iter) if (iter->first==name && iter->second) { @@ -218,7 +218,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) } // Then check cells that are already listed - for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); + for (std::map, CellStore>::iterator iter = mExteriors.begin(); iter!=mExteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); @@ -226,7 +226,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) return ptr; } - for (std::map::iterator iter = mInteriors.begin(); + for (std::map::iterator iter = mInteriors.begin(); iter!=mInteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); @@ -240,7 +240,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); @@ -250,7 +250,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); diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 5ec5ca9b56..997e9e32c1 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -11,7 +11,7 @@ namespace { template void listCellScripts (MWWorld::LocalScripts& localScripts, - MWWorld::CellRefList& cellRefList, MWWorld::Ptr::CellStore *cell) + MWWorld::CellRefList& cellRefList, MWWorld::CellStore *cell) { for (typename MWWorld::CellRefList::List::iterator iter ( cellRefList.mList.begin()); @@ -27,15 +27,15 @@ namespace // Adds scripts for items in containers (containers/npcs/creatures) template void listCellScriptsCont (MWWorld::LocalScripts& localScripts, - MWWorld::CellRefList& cellRefList, MWWorld::Ptr::CellStore *cell) + MWWorld::CellRefList& cellRefList, MWWorld::CellStore *cell) { for (typename MWWorld::CellRefList::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 >::iterator iter = mScripts.begin(); diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index e5352da280..8b70382d0a 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -14,9 +14,6 @@ namespace MWWorld { public: - typedef MWWorld::CellStore CellStore; - ///< \deprecated - MWWorld::LiveCellRefBase *mRef; CellStore *mCell; ContainerStore *mContainerStore; @@ -59,7 +56,7 @@ namespace MWWorld RefData& getRefData() const; - Ptr::CellStore *getCell() const + CellStore *getCell() const { assert(mCell); return mCell; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 254ad98cfb..348f01dc55 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -116,7 +116,7 @@ namespace MWWorld mActiveCells.erase(*iter); } - void Scene::loadCell (Ptr::CellStore *cell, Loading::Listener* loadingListener) + void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener) { std::pair result = mActiveCells.insert(cell); @@ -161,7 +161,7 @@ namespace MWWorld MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell); } - void Scene::playerCellChange(MWWorld::CellStore *cell, const ESM::Position& pos, bool adjustPlayerPos) + void Scene::playerCellChange(CellStore *cell, const ESM::Position& pos, bool adjustPlayerPos) { MWBase::World *world = MWBase::Environment::get().getWorld(); world->getPlayer().setCell(cell); @@ -441,7 +441,7 @@ namespace MWWorld changeCell (x, y, position, true); } - Ptr::CellStore* Scene::getCurrentCell () + CellStore* Scene::getCurrentCell () { return mCurrentCell; } @@ -451,7 +451,7 @@ namespace MWWorld mCellChanged = false; } - int Scene::countRefs (const Ptr::CellStore& cell) + int Scene::countRefs (const CellStore& cell) { return cell.mActivators.mList.size() + cell.mPotions.mList.size() @@ -475,7 +475,7 @@ namespace MWWorld + cell.mNpcs.mList.size(); } - void Scene::insertCell (Ptr::CellStore &cell, bool rescale, Loading::Listener* loadingListener) + void Scene::insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener) { // Loop through all references in the cell insertCellRefList(mRendering, cell.mActivators, cell, *mPhysics, rescale, loadingListener); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index e3edad352a..b7bb944edb 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -56,9 +56,9 @@ namespace MWWorld void playerCellChange (CellStore *cell, const ESM::Position& position, bool adjustPlayerPos = true); - void insertCell (Ptr::CellStore &cell, bool rescale, Loading::Listener* loadingListener); + void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); - int countRefs (const Ptr::CellStore& cell); + int countRefs (const CellStore& cell); public: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 509aee983f..b2d18a27fb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -48,30 +48,6 @@ using namespace Ogre; namespace { -/* // NOTE this code is never instantiated (proper copy in localscripts.cpp), - // so this commented out to not produce syntactic errors - - template - void listCellScripts (const MWWorld::ESMStore& store, - MWWorld::CellRefList& cellRefList, MWWorld::LocalScripts& localScripts, - MWWorld::Ptr::CellStore *cell) - { - for (typename MWWorld::CellRefList::List::iterator iter ( - cellRefList.mList.begin()); - iter!=cellRefList.mList.end(); ++iter) - { - if (!iter->mBase->mScript.empty() && iter->mData.getCount()) - { - if (const ESM::Script *script = store.get().find (iter->mBase->mScript)) - { - iter->mData.setLocals (*script); - - localScripts.add (iter->mBase->mScript, MWWorld::Ptr (&*iter, cell)); - } - } - } - } -*/ template MWWorld::LiveCellRef *searchViaHandle (const std::string& handle, MWWorld::CellRefList& refList) @@ -125,7 +101,7 @@ namespace MWWorld LoadersContainer mLoaders; }; - Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell) + Ptr World::getPtrViaHandle (const std::string& handle, CellStore& cell) { if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mActivators)) @@ -388,12 +364,12 @@ namespace MWWorld return &mFallback; } - Ptr::CellStore *World::getExterior (int x, int y) + CellStore *World::getExterior (int x, int y) { return mCells.getExterior (x, y); } - Ptr::CellStore *World::getInterior (const std::string& name) + CellStore *World::getInterior (const std::string& name) { return mCells.getInterior (name); } @@ -504,7 +480,7 @@ namespace MWWorld for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { - Ptr::CellStore* cellstore = *iter; + CellStore* cellstore = *iter; Ptr ptr = mCells.getPtr (name, *cellstore, true); if (!ptr.isEmpty()) @@ -537,7 +513,7 @@ namespace MWWorld for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { - Ptr::CellStore* cellstore = *iter; + CellStore* cellstore = *iter; Ptr ptr = getPtrViaHandle (handle, *cellstore); if (!ptr.isEmpty()) @@ -547,7 +523,7 @@ namespace MWWorld return MWWorld::Ptr(); } - void World::addContainerScripts(const Ptr& reference, Ptr::CellStore * cell) + void World::addContainerScripts(const Ptr& reference, CellStore * cell) { if( reference.getTypeName()==typeid (ESM::Container).name() || reference.getTypeName()==typeid (ESM::NPC).name() || @@ -1400,7 +1376,7 @@ namespace MWWorld bool World::isCellExterior() const { - Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); + CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { return currentCell->mCell->isExterior(); @@ -1410,7 +1386,7 @@ namespace MWWorld bool World::isCellQuasiExterior() const { - Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); + CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { if (!(currentCell->mCell->mData.mFlags & ESM::Cell::QuasiEx)) @@ -1587,7 +1563,7 @@ namespace MWWorld void World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount) { - MWWorld::Ptr::CellStore* cell = actor.getCell(); + MWWorld::CellStore* cell = actor.getCell(); ESM::Position pos = actor.getRefData().getPosition(); @@ -1684,7 +1660,7 @@ namespace MWWorld } bool - World::isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const + World::isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const { if (!(cell->mCell->mData.mFlags & ESM::Cell::HasWater)) { return false; @@ -1704,9 +1680,9 @@ namespace MWWorld return mRendering->vanityRotateCamera(rot); } - void World::setCameraDistance(float dist, bool adjust, bool override) + void World::setCameraDistance(float dist, bool adjust, bool override_) { - return mRendering->setCameraDistance(dist, adjust, override);; + return mRendering->setCameraDistance(dist, adjust, override_); } void World::setupPlayer() @@ -1734,7 +1710,7 @@ namespace MWWorld int World::canRest () { - Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); + CellStore *currentCell = mWorldScene->getCurrentCell(); Ptr player = mPlayer->getPlayer(); RefData &refdata = player.getRefData(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1cee7d50f4..b8a61c471f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -79,7 +79,7 @@ namespace MWWorld World (const World&); World& operator= (const World&); - Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore); + Ptr getPtrViaHandle (const std::string& handle, CellStore& cellStore); int mActivationDistanceOverride; std::string mFacedHandle; @@ -121,7 +121,7 @@ namespace MWWorld float getObjectActivationDistance (); void removeContainerScripts(const Ptr& reference); - void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell); + void addContainerScripts(const Ptr& reference, CellStore* cell); void PCDropped (const Ptr& item); void processDoors(float duration); @@ -413,7 +413,7 @@ namespace MWWorld ///Is the head of the creature underwater? virtual bool isSubmerged(const MWWorld::Ptr &object) const; virtual bool isSwimming(const MWWorld::Ptr &object) const; - virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const; + virtual bool isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const; virtual bool isOnGround(const MWWorld::Ptr &ptr) const; virtual void togglePOV() { From ce624e024b0225afe3815ef65a4f9ce3b506bd55 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 14:03:25 +0100 Subject: [PATCH 064/120] make sure player record stays in place across cleanups --- apps/openmw/mwworld/store.hpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index c25197319e..233f2f702e 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -292,6 +292,20 @@ namespace MWWorld } }; + template <> + inline void Store::clearDynamic() + { + std::map::iterator iter = mDynamic.begin(); + + while (iter!=mDynamic.end()) + if (iter->first=="player") + ++iter; + else + mDynamic.erase (iter++); + + mShared.clear(); + } + template <> inline void Store::load(ESM::ESMReader &esm, const std::string &id) { std::string idLower = Misc::StringUtils::lowerCase(id); From a7b42b867bb559511886973d3b8ecfaa986588d3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 14:18:43 +0100 Subject: [PATCH 065/120] more cleanup --- apps/openmw/mwworld/worldimp.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b2d18a27fb..539c959b36 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -238,7 +238,6 @@ namespace MWWorld setupPlayer(); renderPlayer(); - mRendering->resetCamera(); MWBase::Environment::get().getWindowManager()->updatePlayer(); @@ -289,6 +288,15 @@ namespace MWWorld mStore.setUp(); mCells.clear(); + + mProjectiles.clear(); + mDoorStates.clear(); + + mGodMode = false; + mSky = true; + mTeleportEnabled = true; + mPlayIntro = 0; + mFacedDistance = FLT_MAX; } void World::ensureNeededRecords() @@ -1701,6 +1709,7 @@ namespace MWWorld { mRendering->renderPlayer(mPlayer->getPlayer()); mPhysics->addActor(mPlayer->getPlayer()); + mRendering->resetCamera(); } void World::setupExternalRendering (MWRender::ExternalRendering& rendering) From 8b7889f8e54d1e834ee983a6ac8cb18dfced7c2c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 14:22:08 +0100 Subject: [PATCH 066/120] setup player after loading a saved game --- apps/openmw/mwstate/statemanagerimp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 09e574e84d..9df1e2dc0a 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -161,6 +161,10 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl Settings::Manager::setString ("character", "Saves", slot->mPath.parent_path().filename().string()); + + MWBase::Environment::get().getWorld()->setupPlayer(); + MWBase::Environment::get().getWorld()->renderPlayer(); + MWBase::Environment::get().getWindowManager()->updatePlayer(); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) From ee6ddc34045cdd8207c14971ad0b310856ea5f34 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 14:56:30 +0100 Subject: [PATCH 067/120] block saving while chargen is in progress --- apps/openmw/mwgui/mainmenu.cpp | 3 ++- apps/openmw/mwstate/statemanagerimp.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index ff8ab8c937..da1992474b 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -97,7 +97,8 @@ namespace MWGui MWBase::Environment::get().getStateManager()->characterEnd()) buttons.push_back("loadgame"); - if (state==MWBase::StateManager::State_Running) + if (state==MWBase::StateManager::State_Running && + MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) buttons.push_back("savegame"); buttons.push_back("options"); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9df1e2dc0a..293a9e232e 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -60,6 +60,8 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getWorld()->startNewGame(); MWBase::Environment::get().getWindowManager()->setNewGame (true); } + else + MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); mState = State_Running; } From 14eff87339a27c5a30c4e3f47791eed8678d52b5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Dec 2013 11:17:14 +0100 Subject: [PATCH 068/120] removed some junk from ESM store --- apps/openmw/mwworld/esmstore.hpp | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index ebb086cee5..d5da5a866d 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -24,10 +24,8 @@ namespace MWWorld Store mBirthSigns; Store mClasses; Store mClothes; - Store mContChange; Store mContainers; Store mCreatures; - Store mCreaChange; Store mDialogs; Store mDoors; Store mEnchants; @@ -40,7 +38,6 @@ namespace MWWorld Store mLockpicks; Store mMiscItems; Store mNpcs; - Store mNpcChange; Store mProbes; Store mRaces; Store 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; @@ -287,11 +281,6 @@ namespace MWWorld return mClothes; } - template <> - inline const Store &ESMStore::get() const { - return mContChange; - } - template <> inline const Store &ESMStore::get() const { return mContainers; @@ -302,11 +291,6 @@ namespace MWWorld return mCreatures; } - template <> - inline const Store &ESMStore::get() const { - return mCreaChange; - } - template <> inline const Store &ESMStore::get() const { return mDialogs; @@ -367,11 +351,6 @@ namespace MWWorld return mNpcs; } - template <> - inline const Store &ESMStore::get() const { - return mNpcChange; - } - template <> inline const Store &ESMStore::get() const { return mProbes; From 674931a8512240e6cd080a17b1a039937370d4ad Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Dec 2013 14:24:14 +0100 Subject: [PATCH 069/120] remove terminating 0 from strings read from ESM records --- components/esm/esmreader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index f02ed2d6e1..4e1860bab7 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -302,6 +302,9 @@ std::string ESMReader::getString(int size) char *ptr = &mBuffer[0]; getExact(ptr, size); + if (size>0 && ptr[size-1]==0) + --size; + // Convert to UTF8 and return if (mEncoder) return mEncoder->getUtf8(ptr, size); From 1c13a9037adc13a50e40c5f66650c5fe509b7a04 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 7 Dec 2013 13:17:28 +0100 Subject: [PATCH 070/120] save and load dynamic records --- apps/openmw/mwbase/mechanicsmanager.hpp | 36 ++++++------- apps/openmw/mwbase/world.hpp | 7 +++ .../mwmechanics/mechanicsmanagerimp.cpp | 8 +++ .../mwmechanics/mechanicsmanagerimp.hpp | 20 ++++---- apps/openmw/mwstate/statemanagerimp.cpp | 29 +++++++++-- apps/openmw/mwworld/esmstore.cpp | 50 +++++++++++++++++++ apps/openmw/mwworld/esmstore.hpp | 7 +++ apps/openmw/mwworld/store.hpp | 33 ++++++++++++ apps/openmw/mwworld/worldimp.cpp | 22 ++++++++ apps/openmw/mwworld/worldimp.hpp | 6 +++ 10 files changed, 189 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 3ab234de1c..eeeab77e82 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -101,28 +101,30 @@ 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 playerLoaded() = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 7d1678a116..717012f72d 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -30,6 +30,7 @@ namespace OEngine namespace ESM { class ESMReader; + class ESMWriter; struct Position; struct Cell; struct Class; @@ -98,6 +99,12 @@ namespace MWBase 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) = 0; + virtual OEngine::Render::Fader* getFader() = 0; ///< \ŧodo remove this function. Rendering details should not be exposed. diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 1316baaeb4..b6a7a7b755 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -701,4 +701,12 @@ namespace MWMechanics { return mAI; } + + void MechanicsManager::playerLoaded() + { + mUpdatePlayer = true; + mClassSelected = true; + mRaceSelected = true; + mAI = true; + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index ec03b457b2..ebc879d261 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -98,18 +98,20 @@ namespace MWMechanics void toLower(std::string npcFaction); ///< Perform a persuasion action on NPC - 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); + /// 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 toggleAI(); + virtual bool isAIActive(); + + virtual void playerLoaded(); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 293a9e232e..94219b8fca 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -11,6 +11,7 @@ #include "../mwbase/journal.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -103,8 +104,10 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot ESM::ESMWriter writer; writer.setFormat (ESM::Header::CurrentFormat); writer.setRecordCount ( - 1+ // saved game header - MWBase::Environment::get().getJournal()->countSavedGameRecords()); + 1 // saved game header + +MWBase::Environment::get().getJournal()->countSavedGameRecords() + +MWBase::Environment::get().getWorld()->countSavedGameRecords() + ); writer.save (stream); @@ -113,7 +116,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.endRecord (ESM::REC_SAVE); MWBase::Environment::get().getJournal()->write (writer); - /// \todo write saved game data + MWBase::Environment::get().getWorld()->write (writer); writer.close(); @@ -149,6 +152,19 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl 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: + + MWBase::Environment::get().getWorld()->readRecord (reader, n.val); + break; + default: // ignore invalid records @@ -167,6 +183,13 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getWorld()->setupPlayer(); MWBase::Environment::get().getWorld()->renderPlayer(); MWBase::Environment::get().getWindowManager()->updatePlayer(); + MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + + // for testing purpose only + ESM::Position pos; + pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; + MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index f1bff11a28..cb4e441fc9 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -139,4 +139,54 @@ 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); + mNpcs.write (writer); + mSpells.write (writer); + mWeapons.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_NPC_: + case ESM::REC_SPEL: + case ESM::REC_WEAP: + + mStores[type]->read (reader); + return true; + + default: + + return false; + } + } + } // end namespace diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index d5da5a866d..e6730c3074 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -209,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 <> diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 233f2f702e..df957408d5 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -6,6 +6,8 @@ #include #include +#include + #include "recordcmp.hpp" namespace MWWorld @@ -18,10 +20,16 @@ namespace MWWorld virtual void listIdentifier(std::vector &list) const {} virtual size_t getSize() const = 0; + virtual int getDynamicSize() const { return 0; } virtual void load(ESM::ESMReader &esm, const std::string &id) = 0; virtual bool eraseStatic(const std::string &id) {return false;} virtual void clearDynamic() {} + + virtual void write (ESM::ESMWriter& writer) const {} + + virtual void read (ESM::ESMReader& reader) {} + ///< Read into dynamic storage }; template @@ -212,6 +220,11 @@ namespace MWWorld return mShared.size(); } + int getDynamicSize() const + { + return mDynamic.size(); + } + void listIdentifier(std::vector &list) const { list.reserve(list.size() + getSize()); typename std::vector::const_iterator it = mShared.begin(); @@ -290,6 +303,26 @@ namespace MWWorld bool erase(const T &item) { return erase(item.mId); } + + void write (ESM::ESMWriter& writer) const + { + for (typename Dynamic::const_iterator iter (mDynamic.begin()); iter!=mDynamic.end(); + ++iter) + { + writer.startRecord (T::sRecordId); + writer.writeHNString ("NAME", iter->second.mId); + iter->second.save (writer); + writer.endRecord (T::sRecordId); + } + } + + void read (ESM::ESMReader& reader) + { + T record; + record.mId = reader.getHNString ("NAME"); + record.load (reader); + insert (record); + } }; template <> diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 82b2301db6..bfcd0ae1c0 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -282,7 +282,10 @@ namespace MWWorld mWorldScene->changeToVoid(); if (mPlayer) + { + mPlayer->setCell (0); mPlayer->getPlayer().getRefData() = RefData(); + } mStore.clearDynamic(); mStore.setUp(); @@ -299,6 +302,25 @@ namespace MWWorld mFacedDistance = FLT_MAX; } + int World::countSavedGameRecords() const + { + return mStore.countSavedGameRecords(); + } + + void World::write (ESM::ESMWriter& writer) const + { + mStore.write (writer); + } + + void World::readRecord (ESM::ESMReader& reader, int32_t type) + { + if (!mStore.readRecord (reader, type)) + { + /// \todo handle other world state records + + } + } + void World::ensureNeededRecords() { if (!mStore.get().search("sCompanionShare")) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index b09b42e944..40245b78d8 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -171,6 +171,12 @@ namespace MWWorld virtual void clear(); + virtual int countSavedGameRecords() const; + + virtual void write (ESM::ESMWriter& writer) const; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type); + virtual OEngine::Render::Fader* getFader(); ///< \ŧodo remove this function. Rendering details should not be exposed. From 1fdd43bbb75f516b990ae995df9ef0dedaa901b1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 10 Dec 2013 12:31:18 +0100 Subject: [PATCH 071/120] removed a redundant new --- apps/openmw/mwworld/worldimp.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index bfcd0ae1c0..c4615c099b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -255,9 +255,7 @@ namespace MWWorld mPlayIntro = 2; // global variables - delete mGlobalVariables; - mGlobalVariables = 0; - mGlobalVariables = new Globals (mStore); + *mGlobalVariables = Globals (mStore); // set new game mark mGlobalVariables->setInt ("chargenstate", 1); From 51bfa5cde3be379ceb1ebc541bbb830dc05c8f68 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 10 Dec 2013 15:09:58 +0100 Subject: [PATCH 072/120] rewrote global variable storage (using ESM variant type now) --- apps/openmw/mwworld/globals.cpp | 132 ++++++------------------------- apps/openmw/mwworld/globals.hpp | 49 ++++-------- apps/openmw/mwworld/worldimp.cpp | 75 +++++++++--------- apps/openmw/mwworld/worldimp.hpp | 3 +- 4 files changed, 80 insertions(+), 179 deletions(-) diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index a905f8aaed..19bfa15299 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -7,15 +7,14 @@ namespace MWWorld { - std::vector Globals::getGlobals () const + std::vector Globals::getGlobals() const { - std::vector retval; - Collection::const_iterator it; - for(it = mVariables.begin(); it != mVariables.end(); ++it){ - retval.push_back(it->first); - } + std::vector ids; - return retval; + for (Collection::const_iterator iter = mVariables.begin(); iter!=mVariables.end(); ++iter) + ids.push_back (iter->first); + + return ids; } Globals::Collection::const_iterator Globals::find (const std::string& name) const @@ -38,112 +37,27 @@ namespace MWWorld return iter; } - Globals::Globals (const MWWorld::ESMStore& store) + void Globals::fill (const MWWorld::ESMStore& store) { - const MWWorld::Store &globals = store.get(); - MWWorld::Store::iterator iter = globals.begin(); - for (; iter != globals.end(); ++iter) + mVariables.clear(); + + const MWWorld::Store& globals = store.get(); + + for (MWWorld::Store::iterator iter = globals.begin(); iter!=globals.end(); + ++iter) { - char type = ' '; - Data value; - - switch (iter->mValue.getType()) - { - case ESM::VT_Short: - - type = 's'; - value.mShort = iter->mValue.getInteger(); - break; - - case ESM::VT_Long: - - 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))); + 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; - } - - void Globals::setInt (const std::string& name, int value) - { - 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"); - } - } - - void Globals::setFloat (const std::string& name, float value) - { - 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"); - } - } - - int Globals::getInt (const std::string& name) const - { - Collection::const_iterator iter = find (name); - - switch (iter->second.first) - { - 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"); - } - } - - float Globals::getFloat (const std::string& name) const - { - Collection::const_iterator iter = find (name); - - switch (iter->second.first) - { - 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"); - } + return find (name)->second; } char Globals::getType (const std::string& name) const @@ -153,7 +67,13 @@ namespace MWWorld if (iter==mVariables.end()) return ' '; - return iter->second.first; + switch (iter->second.getType()) + { + case ESM::VT_Short: return 's'; + case ESM::VT_Long: return 'l'; + case ESM::VT_Float: return 'f'; + + default: return ' '; + } } } - diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index 681bd560e5..587dd2092e 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace MWWorld { @@ -13,49 +14,29 @@ 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 > Collection; - private: - + + typedef std::map 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. - + public: + + 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 getGlobals () const; + std::vector getGlobals() const; + + void fill (const MWWorld::ESMStore& store); + ///< Replace variables with variables from \a store with default values. }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c4615c099b..7e96471677 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -172,9 +172,9 @@ namespace MWWorld { if (mSky && (isCellExterior() || isCellQuasiExterior())) { - mRendering->skySetHour (mGlobalVariables->getFloat ("gamehour")); - mRendering->skySetDate (mGlobalVariables->getInt ("day"), - mGlobalVariables->getInt ("month")); + mRendering->skySetHour (mGlobalVariables["gamehour"].getFloat()); + mRendering->skySetDate (mGlobalVariables["day"].getInteger(), + mGlobalVariables["month"].getInteger()); mRendering->skyEnable(); } @@ -187,7 +187,7 @@ namespace MWWorld const std::vector& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride) - : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), + : mPlayer (0), mLocalScripts (mStore), mSky (true), mCells (mStore, mEsm), mActivationDistanceOverride (mActivationDistanceOverride), mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), mLevitationEnabled(false), @@ -227,7 +227,7 @@ namespace MWWorld mStore.setUp(); mStore.movePlayerRecord(); - mGlobalVariables = new Globals (mStore); + mGlobalVariables.fill (mStore); mWorldScene = new Scene(*mRendering, mPhysics); } @@ -254,12 +254,9 @@ namespace MWWorld // FIXME: should be set to 1, but the sound manager won't pause newly started sounds mPlayIntro = 2; - // global variables - *mGlobalVariables = Globals (mStore); - // set new game mark - mGlobalVariables->setInt ("chargenstate", 1); - mGlobalVariables->setInt ("pcrace", 3); + mGlobalVariables["chargenstate"].setInteger (1); + mGlobalVariables["pcrace"].setInteger (3); // we don't want old weather to persist on a new game delete mWeatherManager; @@ -298,6 +295,8 @@ namespace MWWorld mTeleportEnabled = true; mPlayIntro = 0; mFacedDistance = FLT_MAX; + + mGlobalVariables.fill (mStore); } int World::countSavedGameRecords() const @@ -358,7 +357,6 @@ namespace MWWorld { delete mWeatherManager; delete mWorldScene; - delete mGlobalVariables; delete mRendering; delete mPhysics; @@ -436,7 +434,7 @@ namespace MWWorld else if (name=="month") setMonth (value); else - mGlobalVariables->setInt (name, value); + mGlobalVariables[name].setInteger (value); } void World::setGlobalFloat (const std::string& name, float value) @@ -448,27 +446,27 @@ namespace MWWorld else if (name=="month") setMonth (value); else - mGlobalVariables->setFloat (name, value); + mGlobalVariables[name].setFloat (value); } int World::getGlobalInt (const std::string& name) const { - return mGlobalVariables->getInt (name); + return mGlobalVariables[name].getInteger(); } float World::getGlobalFloat (const std::string& name) const { - return mGlobalVariables->getFloat (name); + return mGlobalVariables[name].getFloat(); } char World::getGlobalVariableType (const std::string& name) const { - return mGlobalVariables->getType (name); + return mGlobalVariables.getType (name); } - std::vector World::getGlobals () const + std::vector World::getGlobals() const { - return mGlobalVariables->getGlobals(); + return mGlobalVariables.getGlobals(); } std::string World::getCellName (const MWWorld::CellStore *cell) const @@ -618,14 +616,15 @@ namespace MWWorld mWeatherManager->advanceTime (hours); - hours += mGlobalVariables->getFloat ("gamehour"); + hours += mGlobalVariables["gamehour"].getFloat(); setHour (hours); int days = hours / 24; if (days>0) - mGlobalVariables->setInt ("dayspassed", days + mGlobalVariables->getInt ("dayspassed")); + mGlobalVariables["dayspassed"].setInteger ( + days + mGlobalVariables["dayspassed"].getInteger()); } void World::setHour (double hour) @@ -637,14 +636,14 @@ namespace MWWorld hour = std::fmod (hour, 24); - mGlobalVariables->setFloat ("gamehour", hour); + mGlobalVariables["gamehour"].setFloat (hour); mRendering->skySetHour (hour); mWeatherManager->setHour (hour); if (days>0) - setDay (days + mGlobalVariables->getInt ("day")); + setDay (days + mGlobalVariables["day"].getInteger()); } void World::setDay (int day) @@ -652,7 +651,7 @@ namespace MWWorld if (day<1) day = 1; - int month = mGlobalVariables->getInt ("month"); + int month = mGlobalVariables["month"].getInteger(); while (true) { @@ -667,14 +666,14 @@ namespace MWWorld else { month = 0; - mGlobalVariables->setInt ("year", mGlobalVariables->getInt ("year")+1); + mGlobalVariables["year"].setInteger (mGlobalVariables["year"].getInteger()+1); } day -= days; } - mGlobalVariables->setInt ("day", day); - mGlobalVariables->setInt ("month", month); + mGlobalVariables["day"].setInteger (day); + mGlobalVariables["month"].setInteger (month); mRendering->skySetDate (day, month); @@ -691,30 +690,30 @@ namespace MWWorld int days = getDaysPerMonth (month); - if (mGlobalVariables->getInt ("day")>days) - mGlobalVariables->setInt ("day", days); + if (mGlobalVariables["day"].getInteger()>days) + mGlobalVariables["day"].setInteger (days); - mGlobalVariables->setInt ("month", month); + mGlobalVariables["month"].setInteger (month); if (years>0) - mGlobalVariables->setInt ("year", years+mGlobalVariables->getInt ("year")); + mGlobalVariables["year"].setInteger (years+mGlobalVariables["year"].getInteger()); - mRendering->skySetDate (mGlobalVariables->getInt ("day"), month); + mRendering->skySetDate (mGlobalVariables["day"].getInteger(), month); } int World::getDay() const { - return mGlobalVariables->getInt("day"); + return mGlobalVariables["day"].getInteger(); } int World::getMonth() const { - return mGlobalVariables->getInt("month"); + return mGlobalVariables["month"].getInteger(); } int World::getYear() const { - return mGlobalVariables->getInt("year"); + return mGlobalVariables["year"].getInteger(); } std::string World::getMonthName (int month) const @@ -739,8 +738,8 @@ namespace MWWorld TimeStamp World::getTimeStamp() const { - return TimeStamp (mGlobalVariables->getFloat ("gamehour"), - mGlobalVariables->getInt ("dayspassed")); + return TimeStamp (mGlobalVariables["gamehour"].getFloat(), + mGlobalVariables["dayspassed"].getInteger()); } bool World::toggleSky() @@ -776,7 +775,7 @@ namespace MWWorld float World::getTimeScaleFactor() const { - return mGlobalVariables->getFloat ("timescale"); + return mGlobalVariables["timescale"].getFloat(); } void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) @@ -1267,7 +1266,7 @@ namespace MWWorld if (Misc::StringUtils::ciEqual (ids[i], record.mRace)) break; - mGlobalVariables->setInt ("pcrace", (i == ids.size()) ? 0 : i+1); + mGlobalVariables["pcrace"].setInteger (i == ids.size() ? 0 : i+1); const ESM::NPC *player = mPlayer->getPlayer().get()->mBase; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 40245b78d8..92c99733ef 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -11,6 +11,7 @@ #include "localscripts.hpp" #include "timestamp.hpp" #include "fallback.hpp" +#include "globals.hpp" #include "../mwbase/world.hpp" @@ -64,7 +65,7 @@ namespace MWWorld std::vector mEsm; MWWorld::ESMStore mStore; LocalScripts mLocalScripts; - MWWorld::Globals *mGlobalVariables; + MWWorld::Globals mGlobalVariables; MWWorld::PhysicsSystem *mPhysics; bool mSky; From b38bfe1f214aa39cf1e7f183b3b77ff2f39c9f8c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 10 Dec 2013 15:22:38 +0100 Subject: [PATCH 073/120] removed a redundant function for listing global variables --- apps/openmw/mwbase/world.hpp | 2 -- apps/openmw/mwscript/interpretercontext.cpp | 16 +++++++++++++--- apps/openmw/mwscript/miscextensions.cpp | 5 +++-- apps/openmw/mwworld/globals.cpp | 10 ---------- apps/openmw/mwworld/globals.hpp | 2 -- apps/openmw/mwworld/worldimp.cpp | 5 ----- apps/openmw/mwworld/worldimp.hpp | 2 -- 7 files changed, 16 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 717012f72d..740114aff0 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -164,8 +164,6 @@ namespace MWBase virtual char getGlobalVariableType (const std::string& name) const = 0; ///< Return ' ', if there is no global variable with this name. - virtual std::vector getGlobals () const = 0; - virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const = 0; ///< Return name of the cell. /// diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index a977d34402..aedaec208e 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -156,10 +156,20 @@ namespace MWScript MWBase::Environment::get().getWorld()->setGlobalFloat (name, value); } - std::vector InterpreterContext::getGlobals () const + std::vector InterpreterContext::getGlobals() const { - MWBase::World *world = MWBase::Environment::get().getWorld(); - return world->getGlobals(); + std::vector ids; + + const MWWorld::Store& globals = + MWBase::Environment::get().getWorld()->getStore().get(); + + for (MWWorld::Store::iterator iter = globals.begin(); iter!=globals.end(); + ++iter) + { + ids.push_back (iter->mId); + } + + return ids; } char InterpreterContext::getGlobalType (const std::string& name) const diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index d7b1479704..8ca97d2881 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -652,13 +652,14 @@ namespace MWScript void printGlobalVars(Interpreter::Runtime &runtime) { - Interpreter::Context& context = runtime.getContext(); + InterpreterContext& context = + static_cast (runtime.getContext()); std::stringstream str; str<< "Global variables:"; MWBase::World *world = MWBase::Environment::get().getWorld(); - std::vector names = world->getGlobals(); + std::vector names = context.getGlobals(); for(size_t i = 0;i < names.size();++i) { char type = world->getGlobalVariableType (names[i]); diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 19bfa15299..d8e96ddc36 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -7,16 +7,6 @@ namespace MWWorld { - std::vector Globals::getGlobals() const - { - std::vector ids; - - for (Collection::const_iterator iter = mVariables.begin(); iter!=mVariables.end(); ++iter) - ids.push_back (iter->first); - - return ids; - } - Globals::Collection::const_iterator Globals::find (const std::string& name) const { Collection::const_iterator iter = mVariables.find (name); diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index 587dd2092e..ad140b0c1d 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -33,8 +33,6 @@ namespace MWWorld char getType (const std::string& name) const; ///< If there is no global variable with this name, ' ' is returned. - std::vector getGlobals() const; - void fill (const MWWorld::ESMStore& store); ///< Replace variables with variables from \a store with default values. }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7e96471677..c4e63fad0c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -464,11 +464,6 @@ namespace MWWorld return mGlobalVariables.getType (name); } - std::vector World::getGlobals() const - { - return mGlobalVariables.getGlobals(); - } - std::string World::getCellName (const MWWorld::CellStore *cell) const { if (!cell) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 92c99733ef..8456684494 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -237,8 +237,6 @@ namespace MWWorld virtual char getGlobalVariableType (const std::string& name) const; ///< Return ' ', if there is no global variable with this name. - virtual std::vector getGlobals () const; - virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const; ///< Return name of the cell. /// From fc37c77a9101972374cddbbe82d6079aa3468c2d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 12 Dec 2013 12:19:25 +0100 Subject: [PATCH 074/120] store global variables in saved game files --- apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/globals.cpp | 40 +++++++++++++++++++++++++ apps/openmw/mwworld/globals.hpp | 18 +++++++++++ apps/openmw/mwworld/worldimp.cpp | 11 ++++--- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 94219b8fca..a8f5631a02 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -161,6 +161,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_NPC_: case ESM::REC_SPEL: case ESM::REC_WEAP: + case ESM::REC_GLOB: MWBase::Environment::get().getWorld()->readRecord (reader, n.val); break; diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index d8e96ddc36..879ffa8e34 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -3,6 +3,11 @@ #include +#include + +#include +#include + #include "esmstore.hpp" namespace MWWorld @@ -66,4 +71,39 @@ namespace MWWorld default: return ' '; } } + + int Globals::countSavedGameRecords() const + { + return mVariables.size(); + } + + void Globals::write (ESM::ESMWriter& writer) const + { + for (Collection::const_iterator iter (mVariables.begin()); iter!=mVariables.end(); ++iter) + { + writer.startRecord (ESM::REC_GLOB); + writer.writeHNString ("NAME", iter->first); + iter->second.write (writer, ESM::Variant::Format_Global); + writer.endRecord (ESM::REC_GLOB); + } + } + + bool Globals::readRecord (ESM::ESMReader& reader, int32_t type) + { + if (type==ESM::REC_GLOB) + { + std::string id = reader.getHNString ("NAME"); + + Collection::iterator iter = mVariables.find (Misc::StringUtils::lowerCase (id)); + + if (iter!=mVariables.end()) + iter->second.read (reader, ESM::Variant::Format_Global); + else + reader.skipHRecord(); + + return true; + } + + return false; + } } diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index ad140b0c1d..8f521c8a68 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -5,9 +5,17 @@ #include #include +#include + #include #include +namespace ESM +{ + class ESMWriter; + class ESMReader; +} + namespace MWWorld { class ESMStore; @@ -35,6 +43,16 @@ namespace MWWorld 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? + }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c4e63fad0c..92091097c4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -301,20 +301,23 @@ namespace MWWorld int World::countSavedGameRecords() const { - return mStore.countSavedGameRecords(); + return + mStore.countSavedGameRecords() + +mGlobalVariables.countSavedGameRecords(); } void World::write (ESM::ESMWriter& writer) const { mStore.write (writer); + mGlobalVariables.write (writer); } void World::readRecord (ESM::ESMReader& reader, int32_t type) { - if (!mStore.readRecord (reader, type)) + if (!mStore.readRecord (reader, type) && + !mGlobalVariables.readRecord (reader, type)) { - /// \todo handle other world state records - + throw std::runtime_error ("unknown record in saved game"); } } From 74793c1c2f5d34bf6f5954d9bca8eafbf0eb5ea9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 12 Dec 2013 13:15:38 +0100 Subject: [PATCH 075/120] globals script cleanup; fixed potential case folding bug --- apps/openmw/mwbase/scriptmanager.hpp | 2 -- apps/openmw/mwscript/globalscripts.cpp | 41 +++++++++++++---------- apps/openmw/mwscript/globalscripts.hpp | 9 +++-- apps/openmw/mwscript/scriptmanagerimp.cpp | 5 --- apps/openmw/mwscript/scriptmanagerimp.hpp | 2 -- apps/openmw/mwstate/statemanagerimp.cpp | 7 ++++ apps/openmw/mwworld/worldimp.cpp | 3 -- 7 files changed, 37 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp index 32df2bfa3b..ae146e0646 100644 --- a/apps/openmw/mwbase/scriptmanager.hpp +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -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) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 608725ae6f..d55ad0063e 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -3,6 +3,8 @@ #include +#include + #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" @@ -15,25 +17,12 @@ namespace MWScript GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store) : mStore (store) { - reset(); - } - - void GlobalScripts::reset() - { - mScripts.clear(); - addScript ("Main"); - - MWWorld::Store::iterator iter = - mStore.get().begin(); - - for (; iter != mStore.get().end(); ++iter) { - addScript (iter->mScript); - } + addStartup(); } void GlobalScripts::addScript (const std::string& name) { - if (mScripts.find (name)==mScripts.end()) + if (mScripts.find (Misc::StringUtils::lowerCase (name))==mScripts.end()) if (const ESM::Script *script = mStore.get().find (name)) { Locals locals; @@ -46,7 +35,8 @@ namespace MWScript void GlobalScripts::removeScript (const std::string& name) { - std::map >::iterator iter = mScripts.find (name); + std::map >::iterator iter = + mScripts.find (Misc::StringUtils::lowerCase (name)); if (iter!=mScripts.end()) iter->second.first = false; @@ -55,7 +45,7 @@ namespace MWScript bool GlobalScripts::isRunning (const std::string& name) const { std::map >::const_iterator iter = - mScripts.find (name); + mScripts.find (Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) return false; @@ -76,4 +66,21 @@ namespace MWScript } } } + + void GlobalScripts::clear() + { + mScripts.clear(); + } + + void GlobalScripts::addStartup() + { + addScript ("main"); + + for (MWWorld::Store::iterator iter = + mStore.get().begin(); + iter != mStore.get().end(); ++iter) + { + addScript (iter->mScript); + } + } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 628919d1d2..67b619d1a0 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -6,7 +6,7 @@ #include "locals.hpp" -namespace MWWorld +namespace MWWorld { struct ESMStore; } @@ -22,8 +22,6 @@ namespace MWScript GlobalScripts (const MWWorld::ESMStore& store); - void reset(); - void addScript (const std::string& name); void removeScript (const std::string& name); @@ -32,6 +30,11 @@ namespace MWScript void run(); ///< run all active global scripts + + void clear(); + + void addStartup(); + ///< Add startup script }; } diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 14fe5b7fd6..be9082eb63 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -222,9 +222,4 @@ namespace MWScript throw std::runtime_error ("unable to access local variable " + variable + " of " + scriptId); } - - void ScriptManager::resetGlobalScripts() - { - mGlobalScripts.reset(); - } } diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index 7bb98ffbd3..1a856e0c58 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -61,8 +61,6 @@ namespace MWScript ///< Compile script with the given namen /// \return Success? - virtual void resetGlobalScripts(); - virtual std::pair compileAll(); ///< Compile all scripts /// \return count, success diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a8f5631a02..e1086f121b 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -12,19 +12,24 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwscript/globalscripts.hpp" + void MWState::StateManager::cleanup() { if (mState!=State_NoGame) { MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); MWBase::Environment::get().getWorld()->clear(); + mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); mTimePlayed = 0; @@ -64,6 +69,8 @@ void MWState::StateManager::newGame (bool bypass) else MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); + mState = State_Running; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 92091097c4..c10556fa90 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -21,7 +21,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/scriptmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/movement.hpp" @@ -262,8 +261,6 @@ namespace MWWorld delete mWeatherManager; mWeatherManager = 0; mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); - - MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); } void World::clear() From 2a35c7d33a825f293b07769f87ccf7949f2a06da Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 12 Dec 2013 13:16:32 +0100 Subject: [PATCH 076/120] fixed running global scripts a second time after they have been stopped --- apps/openmw/mwscript/globalscripts.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index d55ad0063e..1400d6675a 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -22,7 +22,11 @@ namespace MWScript void GlobalScripts::addScript (const std::string& name) { - if (mScripts.find (Misc::StringUtils::lowerCase (name))==mScripts.end()) + std::map >::iterator iter = + mScripts.find (Misc::StringUtils::lowerCase (name)); + + if (iter==mScripts.end()) + { if (const ESM::Script *script = mStore.get().find (name)) { Locals locals; @@ -31,6 +35,9 @@ 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) From 3590fa40bd13c3e0b95ee8783b685266e2a261c1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Dec 2013 16:16:50 +0100 Subject: [PATCH 077/120] store global script state in saved game files --- apps/openmw/mwscript/globalscripts.cpp | 58 ++++++++++++++++++++++ apps/openmw/mwscript/globalscripts.hpp | 17 +++++++ apps/openmw/mwscript/locals.cpp | 66 +++++++++++++++++++++++++ apps/openmw/mwscript/locals.hpp | 4 ++ apps/openmw/mwstate/statemanagerimp.cpp | 7 +++ components/CMakeLists.txt | 2 +- components/compiler/locals.cpp | 42 ++++++++-------- components/compiler/locals.hpp | 20 ++++---- components/esm/defs.hpp | 1 + components/esm/globalscript.cpp | 25 ++++++++++ components/esm/globalscript.hpp | 24 +++++++++ components/esm/locals.cpp | 28 +++++++++++ components/esm/locals.hpp | 27 ++++++++++ components/esm/variant.hpp | 2 +- 14 files changed, 290 insertions(+), 33 deletions(-) create mode 100644 components/esm/globalscript.cpp create mode 100644 components/esm/globalscript.hpp create mode 100644 components/esm/locals.cpp create mode 100644 components/esm/locals.hpp diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 1400d6675a..8f269a0153 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "../mwworld/esmstore.hpp" @@ -90,4 +91,61 @@ namespace MWScript addScript (iter->mScript); } } + + int GlobalScripts::countSavedGameRecords() const + { + return mScripts.size(); + } + + void GlobalScripts::write (ESM::ESMWriter& writer) const + { + for (std::map >::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 >::iterator iter = + mScripts.find (script.mId); + + if (iter==mScripts.end()) + { + if (const ESM::Script *scriptRecord = mStore.get().search (script.mId)) + { + std::pair 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; + } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 67b619d1a0..cf716c8e42 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -4,8 +4,16 @@ #include #include +#include + #include "locals.hpp" +namespace ESM +{ + class ESMWriter; + class ESMReader; +} + namespace MWWorld { struct ESMStore; @@ -35,6 +43,15 @@ namespace MWScript 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? }; } diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index 180a2791bc..094fe085a2 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -1,6 +1,8 @@ #include "locals.hpp" #include +#include +#include #include @@ -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& names = declarations.get (type); + + for (int i2=0; i2 (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 >::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 + } + } + } } diff --git a/apps/openmw/mwscript/locals.hpp b/apps/openmw/mwscript/locals.hpp index deae0d44ea..d17d1be2dc 100644 --- a/apps/openmw/mwscript/locals.hpp +++ b/apps/openmw/mwscript/locals.hpp @@ -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); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index e1086f121b..93650fc44a 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -114,6 +114,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot 1 // saved game header +MWBase::Environment::get().getJournal()->countSavedGameRecords() +MWBase::Environment::get().getWorld()->countSavedGameRecords() + +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() ); writer.save (stream); @@ -124,6 +125,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot MWBase::Environment::get().getJournal()->write (writer); MWBase::Environment::get().getWorld()->write (writer); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); writer.close(); @@ -173,6 +175,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getWorld()->readRecord (reader, n.val); break; + case ESM::REC_GSCR: + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); + break; + default: // ignore invalid records diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3223ab1a72..6f01216f1a 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate + savedgame journalentry queststate locals globalscript ) add_component_dir (misc diff --git a/components/compiler/locals.cpp b/components/compiler/locals.cpp index d93e738494..e2b1c5c966 100644 --- a/components/compiler/locals.cpp +++ b/components/compiler/locals.cpp @@ -15,27 +15,27 @@ namespace Compiler { case 's': return mShorts; case 'l': return mLongs; - case 'f': return mFloats; + case 'f': return mFloats; } - + throw std::logic_error ("unknown variable type"); } - + int Locals::searchIndex (char type, const std::string& name) const { const std::vector& collection = get (type); - + std::vector::const_iterator iter = std::find (collection.begin(), collection.end(), name); - + if (iter==collection.end()) return -1; - + return iter-collection.begin(); } - + bool Locals::search (char type, const std::string& name) const - { + { return searchIndex (type, name)!=-1; } @@ -45,10 +45,10 @@ namespace Compiler { case 's': return mShorts; case 'l': return mLongs; - case 'f': return mFloats; + case 'f': return mFloats; } - - throw std::logic_error ("unknown variable type"); + + throw std::logic_error ("unknown variable type"); } char Locals::getType (const std::string& name) const @@ -58,35 +58,35 @@ namespace Compiler if (search ('l', name)) return 'l'; - + if (search ('f', name)) return 'f'; - + return ' '; } - + int Locals::getIndex (const std::string& name) const { int index = searchIndex ('s', name); - + if (index!=-1) return index; - + index = searchIndex ('l', name); if (index!=-1) return index; - return searchIndex ('f', name); + return searchIndex ('f', name); } - + void Locals::write (std::ostream& localFile) const { localFile << get ('s').size() << ' ' << get ('l').size() << ' ' << get ('f').size() << std::endl; - + std::copy (get ('s').begin(), get ('s').end(), std::ostream_iterator (localFile, " ")); std::copy (get ('l').begin(), get ('l').end(), @@ -94,12 +94,12 @@ namespace Compiler std::copy (get ('f').begin(), get ('f').end(), std::ostream_iterator (localFile, " ")); } - + void Locals::declare (char type, const std::string& name) { get (type).push_back (name); } - + void Locals::clear() { get ('s').clear(); diff --git a/components/compiler/locals.hpp b/components/compiler/locals.hpp index e54b7798c5..d5bf052538 100644 --- a/components/compiler/locals.hpp +++ b/components/compiler/locals.hpp @@ -8,35 +8,35 @@ namespace Compiler { /// \brief Local variable declarations - + class Locals { std::vector mShorts; std::vector mLongs; std::vector mFloats; - + int searchIndex (char type, const std::string& name) const; bool search (char type, const std::string& name) const; - - std::vector& get (char type); - + + std::vector& get (char type); + public: - + char getType (const std::string& name) const; ///< 's': short, 'l': long, 'f': float, ' ': does not exist. - + int getIndex (const std::string& name) const; ///< return index for local variable \a name (-1: does not exist). - + const std::vector& get (char type) const; void write (std::ostream& localFile) const; ///< write declarations to file. - + void declare (char type, const std::string& name); ///< declares a variable. - + void clear(); ///< remove all declarations. }; diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 03091d9d8c..4cf0b1dacf 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -87,6 +87,7 @@ enum RecNameInts REC_SAVE = 0x45564153, REC_JOUR = 0x524f55a4, REC_QUES = 0x53455551, + REC_GSCR = 0x52435347, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/globalscript.cpp b/components/esm/globalscript.cpp new file mode 100644 index 0000000000..dcbd91140a --- /dev/null +++ b/components/esm/globalscript.cpp @@ -0,0 +1,25 @@ + +#include "globalscript.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::GlobalScript::load (ESMReader &esm) +{ + mId = esm.getHNString ("NAME"); + + mLocals.load (esm); + + mRunning = 0; + esm.getHNOT (mRunning, "RUN_"); +} + +void ESM::GlobalScript::save (ESMWriter &esm) const +{ + esm.writeHNString ("NAME", mId); + + mLocals.save (esm); + + if (mRunning) + esm.writeHNT ("RUN_", mRunning); +} \ No newline at end of file diff --git a/components/esm/globalscript.hpp b/components/esm/globalscript.hpp new file mode 100644 index 0000000000..4fb8b7c480 --- /dev/null +++ b/components/esm/globalscript.hpp @@ -0,0 +1,24 @@ +#ifndef OPENMW_ESM_GLOBALSCRIPT_H +#define OPENMW_ESM_GLOBALSCRIPT_H + +#include "locals.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + /// \brief Storage structure for global script state (only used in saved games) + + struct GlobalScript + { + std::string mId; + Locals mLocals; + int mRunning; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif diff --git a/components/esm/locals.cpp b/components/esm/locals.cpp new file mode 100644 index 0000000000..9c470a0253 --- /dev/null +++ b/components/esm/locals.cpp @@ -0,0 +1,28 @@ + +#include "locals.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::Locals::load (ESMReader &esm) +{ + while (esm.isNextSub ("LOCA")) + { + std::string id = esm.getHString(); + + Variant value; + value.read (esm, Variant::Format_Info); + + mVariables.push_back (std::make_pair (id, value)); + } +} + +void ESM::Locals::save (ESMWriter &esm) const +{ + for (std::vector >::const_iterator iter (mVariables.begin()); + iter!=mVariables.end(); ++iter) + { + esm.writeHNString ("LOCA", iter->first); + iter->second.write (esm, Variant::Format_Info); + } +} \ No newline at end of file diff --git a/components/esm/locals.hpp b/components/esm/locals.hpp new file mode 100644 index 0000000000..af5afb23bd --- /dev/null +++ b/components/esm/locals.hpp @@ -0,0 +1,27 @@ +#ifndef OPENMW_ESM_LOCALS_H +#define OPENMW_ESM_LOCALS_H + +#include +#include + +#include "variant.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + /// \brief Storage structure for local variables (only used in saved games) + /// + /// \note This is not a top-level record. + + struct Locals + { + std::vector > mVariables; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif diff --git a/components/esm/variant.hpp b/components/esm/variant.hpp index 2bba60a152..8ba9bb34f1 100644 --- a/components/esm/variant.hpp +++ b/components/esm/variant.hpp @@ -33,7 +33,7 @@ namespace ESM { Format_Global, Format_Gmst, - Format_Info + Format_Info // also used for local variables in saved game files }; Variant(); From bf4ffe94dc4a6cb27f71c16911037094fe446bfa Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Dec 2013 16:19:45 +0100 Subject: [PATCH 078/120] fixed a memory leak in the script record --- components/esm/loadscpt.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 30460c17a6..de679e8152 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -26,24 +26,24 @@ void Script::load(ESMReader &esm) if (esm.isNextSub("SCVR")) { int s = mData.mStringTableSize; - char* tmp = new char[s]; - esm.getHExact(tmp, s); + + std::vector tmp (s); + esm.getHExact (&tmp[0], s); // Set up the list of variable names mVarNames.resize(mData.mNumShorts + mData.mNumLongs + mData.mNumFloats); // The tmp buffer is a null-byte separated string list, we // just have to pick out one string at a time. - char* str = tmp; + char* str = &tmp[0]; for (size_t i = 0; i < mVarNames.size(); i++) { mVarNames[i] = std::string(str); str += mVarNames[i].size() + 1; - if (str - tmp > s) + if (str - &tmp[0] > s) esm.fail("String table overflow"); } - delete[] tmp; } // Script mData From fd9f8c34f6065d2645db9bd1443df00939fa020a Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 15 Dec 2013 18:50:25 +0200 Subject: [PATCH 079/120] bug fix http://bugs.openmw.org/issues/428 --- apps/openmw/mwbase/world.hpp | 3 +++ apps/openmw/mwinput/inputmanagerimp.cpp | 7 +++-- apps/openmw/mwmechanics/actors.cpp | 16 +++++------- apps/openmw/mwmechanics/character.cpp | 32 ++++++++++++++++++++--- apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 5 ++++ apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 5 ++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 9 files changed, 58 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 740114aff0..ea8619b3a3 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -43,6 +43,7 @@ namespace MWRender { class ExternalRendering; class Animation; + class Camera; } namespace MWMechanics @@ -112,6 +113,8 @@ namespace MWBase virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; + virtual MWRender::Camera* getCamera() const = 0; + virtual void setWaterHeight(const float height) = 0; virtual void toggleWater() = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 850a62becf..47fb979d79 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -181,7 +181,8 @@ namespace MWInput switch (action) { case A_GameMenu: - toggleMainMenu (); + if(MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running) + toggleMainMenu (); break; case A_Screenshot: screenshot(); @@ -301,7 +302,9 @@ namespace MWInput return; // Disable movement in Gui mode - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + if (MWBase::Environment::get().getWindowManager()->isGuiMode() + || MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() ) + return; // Configure player movement according to keyboard input. Actual movement will diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 19c2dac0c5..c7ab0322d0 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -571,19 +571,15 @@ namespace MWMechanics stats.resurrect(); continue; } - - MWBase::Environment::get().getStateManager()->endGame(); } - if(iter->second->isDead()) - continue; + if (iter->second->kill()) + { + ++mDeathCount[cls.getId(iter->first)]; - iter->second->kill(); - - ++mDeathCount[cls.getId(iter->first)]; - - if(cls.isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + if(cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + } } } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 68f87ef4c7..7c7f40e971 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -26,12 +26,14 @@ #include "creaturestats.hpp" #include "security.hpp" +#include "../mwrender/camera.hpp" #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -1052,10 +1054,20 @@ void CharacterController::forceStateUpdate() } } -void CharacterController::kill() +bool CharacterController::kill() { - if(mDeathState != CharState_None) - return; + if( isDead() ) + { + //state=end game only when player's death animation is over + if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) + && MWBase::Environment::get().getWindowManager()->getMode () != MWGui::GM_MainMenu ) + { + MWBase::Environment::get().getStateManager()->endGame(); + MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + return false; + } if(mPtr.getTypeName() == typeid(ESM::NPC).name()) { @@ -1093,6 +1105,18 @@ void CharacterController::kill() if(mAnimation) { + //switch to 3rd person before player's death animation + if (mPtr.getRefData().getHandle()=="player") + { + if(MWBase::Environment::get().getWorld()->getCamera()->isVanityOrPreviewModeEnabled() ) + { + MWBase::Environment::get().getWorld()->getCamera()->togglePreviewMode(false); + MWBase::Environment::get().getWorld()->getCamera()->toggleVanityMode(false); + } + if(MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson()) + MWBase::Environment::get().getWorld()->togglePOV(); + } + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); @@ -1100,6 +1124,8 @@ void CharacterController::kill() mIdleState = CharState_None; mCurrentIdle.clear(); + + return true; } void CharacterController::resurrect() diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 0b55534a60..5e7a7c6d41 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -188,7 +188,7 @@ public: void skipAnim(); bool isAnimPlaying(const std::string &groupName); - void kill(); + bool kill(); void resurrect(); bool isDead() const { return mDeathState != CharState_None; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2eb2b1523d..8aca3b4891 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -221,6 +221,11 @@ OEngine::Render::Fader* RenderingManager::getFader() return mRendering.getFader(); } + MWRender::Camera* RenderingManager::getCamera() const +{ + return mCamera; +} + void RenderingManager::removeCell (MWWorld::CellStore *store) { mObjects.removeCell(store); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 2d08139128..5bbc3055d7 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -98,6 +98,8 @@ public: SkyManager* getSkyManager(); Compositors* getCompositors(); + MWRender::Camera* getCamera() const; + void toggleLight(); bool toggleRenderMode(int mode); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c4e63fad0c..2676be4b75 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -400,6 +400,11 @@ namespace MWWorld return mCells.getInterior (name); } + MWRender::Camera* World::getCamera() const + { + return mRendering->getCamera(); + } + MWWorld::Player& World::getPlayer() { return *mPlayer; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8456684494..7c24adfa83 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -185,6 +185,8 @@ namespace MWWorld virtual CellStore *getInterior (const std::string& name); + virtual MWRender::Camera* getCamera() const; + virtual void setWaterHeight(const float height); virtual void toggleWater(); From 1cf1d49bc43d717db5b3d2f4ca6334cb721a423f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 16 Dec 2013 11:39:24 +0100 Subject: [PATCH 080/120] fix to content file reading error reporting in case of missing dependency --- apps/openmw/mwworld/esmstore.cpp | 2 +- components/esm/esmreader.cpp | 5 +++++ components/esm/esmreader.hpp | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index cb4e441fc9..62b91c2e44 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -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); } diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 4e1860bab7..ebdb1e41f7 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -8,6 +8,11 @@ namespace ESM using namespace Misc; + std::string ESMReader::getName() const + { + return mCtx.filename; + } + ESM_Context ESMReader::getContext() { // Update the file position before returning diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 3bf194c4e3..897c8fe731 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -38,6 +38,7 @@ public: int getFormat() const; const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } + std::string getName() const; /************************************************************************* * From f50ff0b1c4df394bc451e1fbd137e846cf81e39c Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 16 Dec 2013 15:40:47 +0200 Subject: [PATCH 081/120] reworked http://bugs.openmw.org/issues/428 --- apps/openmw/engine.cpp | 54 +++++++++++++------------ apps/openmw/mwbase/world.hpp | 3 +- apps/openmw/mwinput/inputmanagerimp.cpp | 9 +++-- apps/openmw/mwmechanics/actors.cpp | 1 - apps/openmw/mwmechanics/character.cpp | 19 +-------- apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/worldimp.cpp | 10 ++++- apps/openmw/mwworld/worldimp.hpp | 4 +- 8 files changed, 49 insertions(+), 52 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 8fc36a5d54..97c2844ebb 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -94,35 +94,39 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) MWBase::Environment::get().getSoundManager()->update(frametime); // global scripts + MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); + + 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. + + if (changed) // keep change flag for another frame, if cell changed happened in local script + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWorld()->advanceTime( + frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); + + // update actors + MWBase::Environment::get().getMechanicsManager()->update(frametime, + MWBase::Environment::get().getWindowManager()->isGuiMode()); + if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) { - MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); - - 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. - - if (changed) // keep change flag for another frame, if cell changed happened in local script - MWBase::Environment::get().getWorld()->markCellAsUnchanged(); - - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->advanceTime( - frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); - - // update actors - MWBase::Environment::get().getMechanicsManager()->update(frametime, - MWBase::Environment::get().getWindowManager()->isGuiMode()); - - // update world - MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); - - // update game state - MWBase::Environment::get().getStateManager()->update (frametime); + MWWorld::Ptr player = mEnvironment.getWorld()->getPlayer().getPlayer(); + if(MWWorld::Class::get(player).getCreatureStats(player).isDead()) + MWBase::Environment::get().getStateManager()->endGame(); } + + // update world + MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); + + // update game state + MWBase::Environment::get().getStateManager()->update (frametime); // update GUI Ogre::RenderWindow* window = mOgre->getWindow(); diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index ea8619b3a3..f7041a47ba 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -43,7 +43,6 @@ namespace MWRender { class ExternalRendering; class Animation; - class Camera; } namespace MWMechanics @@ -113,7 +112,7 @@ namespace MWBase virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; - virtual MWRender::Camera* getCamera() const = 0; + virtual void useDeathCamera() = 0; virtual void setWaterHeight(const float height) = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 47fb979d79..e6e349c4dd 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -181,8 +181,9 @@ namespace MWInput switch (action) { case A_GameMenu: - if(MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running) - 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(); @@ -303,8 +304,8 @@ namespace MWInput // Disable movement in Gui mode if (MWBase::Environment::get().getWindowManager()->isGuiMode() - || MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() ) - return; + || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) + return; // Configure player movement according to keyboard input. Actual movement will diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c7ab0322d0..7180eb8833 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -19,7 +19,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" - #include "../mwbase/statemanager.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 7c7f40e971..9a19ac7fa3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -26,14 +26,12 @@ #include "creaturestats.hpp" #include "security.hpp" -#include "../mwrender/camera.hpp" #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/statemanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -1058,11 +1056,10 @@ bool CharacterController::kill() { if( isDead() ) { - //state=end game only when player's death animation is over + //player's death animation is over if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) - && MWBase::Environment::get().getWindowManager()->getMode () != MWGui::GM_MainMenu ) + && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu ) { - MWBase::Environment::get().getStateManager()->endGame(); MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } @@ -1105,18 +1102,6 @@ bool CharacterController::kill() if(mAnimation) { - //switch to 3rd person before player's death animation - if (mPtr.getRefData().getHandle()=="player") - { - if(MWBase::Environment::get().getWorld()->getCamera()->isVanityOrPreviewModeEnabled() ) - { - MWBase::Environment::get().getWorld()->getCamera()->togglePreviewMode(false); - MWBase::Environment::get().getWorld()->getCamera()->toggleVanityMode(false); - } - if(MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson()) - MWBase::Environment::get().getWorld()->togglePOV(); - } - mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 94219b8fca..bad4de03d5 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -70,6 +70,7 @@ void MWState::StateManager::newGame (bool bypass) void MWState::StateManager::endGame() { mState = State_Ended; + MWBase::Environment::get().getWorld()->useDeathCamera(); } void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2676be4b75..357101e450 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -400,9 +400,15 @@ namespace MWWorld return mCells.getInterior (name); } - MWRender::Camera* World::getCamera() const + void World::useDeathCamera() { - return mRendering->getCamera(); + if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() ) + { + mRendering->getCamera()->togglePreviewMode(false); + mRendering->getCamera()->toggleVanityMode(false); + } + if(mRendering->getCamera()->isFirstPerson()) + togglePOV(); } MWWorld::Player& World::getPlayer() diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7c24adfa83..fb9c4cb685 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -42,6 +42,7 @@ namespace MWRender class SkyManager; class CellRender; class Animation; + class Camera; } struct ContentLoader; @@ -185,7 +186,8 @@ namespace MWWorld virtual CellStore *getInterior (const std::string& name); - virtual MWRender::Camera* getCamera() const; + //switch to POV before showing player's death animation + virtual void useDeathCamera(); virtual void setWaterHeight(const float height); From a854eb73db8e0629cf5c707b69ccbb7b2a1d4943 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 16 Dec 2013 21:02:21 +0200 Subject: [PATCH 082/120] StateRunning check returns --- apps/openmw/engine.cpp | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 97c2844ebb..5b8872b0f0 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -93,22 +93,30 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (mUseSound) MWBase::Environment::get().getSoundManager()->update(frametime); - // global scripts - MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); + 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(); + if (changed) // keep change flag for another frame, if cell changed happened in local script + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWorld()->advanceTime( + frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); + + // update game state + MWBase::Environment::get().getStateManager()->update (frametime); + } - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->advanceTime( - frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); // update actors MWBase::Environment::get().getMechanicsManager()->update(frametime, @@ -125,9 +133,6 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // update world MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); - // update game state - MWBase::Environment::get().getStateManager()->update (frametime); - // update GUI Ogre::RenderWindow* window = mOgre->getWindow(); unsigned int tri, batch; From c22e38f825827d408833de11b19bcf5a8210206f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 17 Dec 2013 21:19:05 +0100 Subject: [PATCH 083/120] removing 255 content file limitation --- apps/esmtool/esmtool.cpp | 2 +- apps/openmw/mwworld/cellstore.cpp | 5 +- apps/openmw/mwworld/livecellref.hpp | 7 ++- apps/openmw/mwworld/manualref.hpp | 5 +- apps/openmw/mwworld/store.cpp | 8 +-- components/esm/cellref.cpp | 11 +++- components/esm/cellref.hpp | 10 +++- components/esm/loadcell.cpp | 86 ++++++++++++----------------- components/esm/loadcell.hpp | 8 +-- 9 files changed, 74 insertions(+), 68 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 27980096e6..3bbdaef35c 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -244,7 +244,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) if(quiet) continue; - std::cout << " Refnum: " << ref.mRefnum << std::endl; + std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl; std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n"; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 0c145ab600..06ae083ce4 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -38,7 +38,7 @@ namespace MWWorld void CellRefList::load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore) { // Get existing reference, in case we need to overwrite it. - typename std::list::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefnum); + typename std::list::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefNum); // Skip this when reference was deleted. // TODO: Support respawning references, in this case, we need to track it somehow. @@ -148,13 +148,14 @@ 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)) { // 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; } diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index 415351e783..558639a3b3 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -31,6 +31,11 @@ namespace MWWorld virtual ~LiveCellRefBase() { } }; + 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 @@ -51,8 +56,6 @@ namespace MWWorld // The object that this instance is based on. const X* mBase; }; - -// template bool operator==(const LiveCellRef& ref, int pRefnum); } #endif diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 1cdcd8484e..f138e3732b 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -25,6 +25,8 @@ namespace MWWorld { LiveCellRef ref; ref.mBase = instance; + ref.mRef.mRefNum.mIndex = 0; + ref.mRef.mRefNum.mContentFile = -1; mRef = ref; mPtr = Ptr (&boost::any_cast&> (mRef), 0); @@ -65,7 +67,8 @@ namespace MWWorld // initialise ESM::CellRef& cellRef = mPtr.getCellRef(); cellRef.mRefID = name; - cellRef.mRefnum = -1; + cellRef.mRefNum.mIndex = 0; + cellRef.mRefNum.mContentFile = -1; cellRef.mScale = 1; cellRef.mFactIndex = 0; cellRef.mCharge = -1; diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 512883f1a3..9ba2d81334 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -10,7 +10,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // and we merge all this data into one Cell object. However, we can't simply search for the cell id, // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they // are not available until both cells have been loaded! So first, proceed as usual. - + // All cells have a name record, even nameless exterior cells. std::string idLower = Misc::StringUtils::lowerCase(id); ESM::Cell *cell = new ESM::Cell; @@ -40,7 +40,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // We should not need to test for duplicates, as this part of the code is pre-cell merge. cell->mMovedRefs.push_back(cMRef); // But there may be duplicates here! - ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum); + ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefNum); if (iter == cellAlt->mLeasedRefs.end()) cellAlt->mLeasedRefs.push_back(ref); else @@ -76,11 +76,11 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // merge lists of leased references, use newer data in case of conflict for (ESM::MovedCellRefTracker::const_iterator it = cell->mMovedRefs.begin(); it != cell->mMovedRefs.end(); ++it) { // remove reference from current leased ref tracker and add it to new cell - ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefnum); + ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum); if (itold != oldcell->mMovedRefs.end()) { ESM::MovedCellRef target0 = *itold; ESM::Cell *wipecell = const_cast(search(target0.mTarget[0], target0.mTarget[1])); - ESM::CellRefTracker::iterator it_lease = std::find(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), it->mRefnum); + ESM::CellRefTracker::iterator it_lease = std::find(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), it->mRefNum); wipecell->mLeasedRefs.erase(it_lease); *itold = *it; } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index e91059b26f..23a95a4ab1 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -5,7 +5,8 @@ void ESM::CellRef::save(ESMWriter &esm) const { - esm.writeHNT("FRMR", mRefnum); + esm.writeHNT("FRMR", mRefNum.mIndex); + /// \todo read content file index (if present) esm.writeHNCString("NAME", mRefID); if (mScale != 1.0) { @@ -58,7 +59,8 @@ void ESM::CellRef::save(ESMWriter &esm) const void ESM::CellRef::blank() { - mRefnum = 0; + mRefNum.mIndex = 0; + mRefNum.mContentFile = -1; mRefID.clear(); mScale = 1; mOwner.clear(); @@ -84,4 +86,9 @@ void ESM::CellRef::blank() mPos.pos[i] = 0; mPos.rot[i] = 0; } +} + +bool ESM::operator== (const CellRef::RefNum& left, const CellRef::RefNum& right) +{ + return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile; } \ No newline at end of file diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 47cb0b99ed..ef2523869d 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -19,7 +19,13 @@ namespace ESM { public: - int mRefnum; // Reference number + struct RefNum + { + int mIndex; + int mContentFile; // -1 no content file + }; + + RefNum mRefNum; // Reference number std::string mRefID; // ID of object being referenced float mScale; // Scale applied to mesh @@ -87,6 +93,8 @@ namespace ESM void blank(); }; + + bool operator== (const CellRef::RefNum& left, const CellRef::RefNum& right); } #endif \ No newline at end of file diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index c22c1b22b6..ba41953700 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -9,20 +9,42 @@ #include "esmwriter.hpp" #include "defs.hpp" +namespace +{ + ///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum + void adjustRefNum (ESM::CellRef::RefNum& refNum, ESM::ESMReader& reader) + { + int local = (refNum.mIndex & 0xff000000) >> 24; + + if (local) + { + // If the most significant 8 bits are used, then this reference already exists. + // In this case, do not spawn a new reference, but overwrite the old one. + refNum.mIndex &= 0x00ffffff; // delete old plugin ID + refNum.mContentFile = reader.getGameFiles()[local-1].index; + } + else + { + // This is an addition by the present plugin. Set the corresponding plugin index. + refNum.mContentFile = reader.getIndex(); + } + } +} + namespace ESM { unsigned int Cell::sRecordId = REC_CELL; -/// Some overloaded compare operators. -bool operator==(const MovedCellRef& ref, int pRefnum) -{ - return (ref.mRefnum == pRefnum); -} + // Some overloaded compare operators. + bool operator== (const MovedCellRef& ref, const CellRef::RefNum& refNum) + { + return ref.mRefNum == refNum; + } -bool operator==(const CellRef& ref, int pRefnum) -{ - return (ref.mRefnum == pRefnum); -} + bool operator== (const CellRef& ref, const CellRef::RefNum& refNum) + { + return ref.mRefNum == refNum; + } void Cell::load(ESMReader &esm, bool saveContext) @@ -163,49 +185,17 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) //esm.getHNOT(NAM0, "NAM0"); } - esm.getHNT(ref.mRefnum, "FRMR"); + esm.getHNT (ref.mRefNum.mIndex, "FRMR"); ref.mRefID = esm.getHNString("NAME"); // Identify references belonging to a parent file and adapt the ID accordingly. - int local = (ref.mRefnum & 0xff000000) >> 24; - size_t global = esm.getIndex() + 1; - if (local) - { - // If the most significant 8 bits are used, then this reference already exists. - // In this case, do not spawn a new reference, but overwrite the old one. - ref.mRefnum &= 0x00ffffff; // delete old plugin ID - const std::vector &masters = esm.getGameFiles(); - global = masters[local-1].index + 1; - ref.mRefnum |= global << 24; // insert global plugin ID - } - else - { - // This is an addition by the present plugin. Set the corresponding plugin index. - ref.mRefnum |= global << 24; // insert global plugin ID - } + adjustRefNum (ref.mRefNum, esm); // getHNOT will not change the existing value if the subrecord is // missing ref.mScale = 1.0; esm.getHNOT(ref.mScale, "XSCL"); - // TODO: support loading references from saves, there are tons of keys not recognized yet. - // The following is just an incomplete list. - if (esm.isNextSub("ACTN")) - esm.skipHSub(); - if (esm.isNextSub("STPR")) - esm.skipHSub(); - if (esm.isNextSub("ACDT")) - esm.skipHSub(); - if (esm.isNextSub("ACSC")) - esm.skipHSub(); - if (esm.isNextSub("ACSL")) - esm.skipHSub(); - if (esm.isNextSub("CHRD")) - esm.skipHSub(); - else if (esm.isNextSub("CRED")) // ??? - esm.skipHSub(); - ref.mOwner = esm.getHNOString("ANAM"); ref.mGlob = esm.getHNOString("BNAM"); ref.mSoul = esm.getHNOString("XSOL"); @@ -271,16 +261,10 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) { - esm.getHT(mref.mRefnum); + esm.getHT(mref.mRefNum.mIndex); esm.getHNOT(mref.mTarget, "CNDT"); - // Identify references belonging to a parent file and adapt the ID accordingly. - int local = (mref.mRefnum & 0xff000000) >> 24; - size_t global = esm.getIndex() + 1; - mref.mRefnum &= 0x00ffffff; // delete old plugin ID - const std::vector &masters = esm.getGameFiles(); - global = masters[local-1].index + 1; - mref.mRefnum |= global << 24; // insert global plugin ID + adjustRefNum (mref.mRefNum, esm); return true; } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 61d586b9d8..b0340e945e 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -27,7 +27,7 @@ class ESMWriter; class MovedCellRef { public: - int mRefnum; + CellRef::RefNum mRefNum; // Target cell (if exterior) int mTarget[2]; @@ -37,9 +37,9 @@ public: // introduces a henchman (which no one uses), so we may need this as well. }; -/// Overloaded copare operator used to search inside a list of cell refs. -bool operator==(const MovedCellRef& ref, int pRefnum); -bool operator==(const CellRef& ref, int pRefnum); +/// Overloaded compare operator used to search inside a list of cell refs. +bool operator==(const MovedCellRef& ref, const CellRef::RefNum& refNum); +bool operator==(const CellRef& ref, const CellRef::RefNum& refNum); typedef std::list MovedCellRefTracker; typedef std::list CellRefTracker; From 3816a09c6fd98d4d0b4ede58b8891c9ee845df26 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 19 Dec 2013 22:08:34 +0200 Subject: [PATCH 084/120] bug 428 ask to load recent saved game --- apps/openmw/mwbase/statemanager.hpp | 2 ++ apps/openmw/mwmechanics/character.cpp | 6 ++-- apps/openmw/mwstate/statemanagerimp.cpp | 44 ++++++++++++++++++++++++- apps/openmw/mwstate/statemanagerimp.hpp | 3 ++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 8548a74f7b..cd907408a5 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -44,6 +44,8 @@ namespace MWBase virtual bool hasQuitRequest() const = 0; + virtual void askLoadRecent() = 0; + virtual State getState() const = 0; virtual void newGame (bool bypass = false) = 0; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9a19ac7fa3..614d697ec5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -32,6 +32,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -1057,11 +1058,10 @@ bool CharacterController::kill() if( isDead() ) { //player's death animation is over - if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) - && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu ) + if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) ) { MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + MWBase::Environment::get().getStateManager()->askLoadRecent(); } return false; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index bad4de03d5..9e6395812a 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -32,7 +32,7 @@ void MWState::StateManager::cleanup() } MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) -: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) +: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) { } @@ -47,6 +47,48 @@ bool MWState::StateManager::hasQuitRequest() const return mQuitRequest; } +void MWState::StateManager::askLoadRecent() +{ + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) + return; + + if( !mAskLoadRecent ) + { + if(MWBase::Environment::get().getStateManager()->getCurrentCharacter()->begin() + == MWBase::Environment::get().getStateManager()->getCurrentCharacter()->end() )//no saves + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + else + { + MWState::Slot lastSave = *getCurrentCharacter()->begin(); + std::vector buttons; + buttons.push_back("Yes"); + buttons.push_back("No"); + std::string message = "The most recent Save Game is '" + lastSave.mProfile.mDescription + "'.\n Would you like to load it?"; + 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; diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 78b5787660..a2abcfd1bd 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -12,6 +12,7 @@ namespace MWState class StateManager : public MWBase::StateManager { bool mQuitRequest; + bool mAskLoadRecent; State mState; CharacterManager mCharacterManager; double mTimePlayed; @@ -28,6 +29,8 @@ namespace MWState virtual bool hasQuitRequest() const; + virtual void askLoadRecent(); + virtual State getState() const; virtual void newGame (bool bypass = false); From 8eb2696f6ccf690a23f927dbe031e2c1e3f2d9b0 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 20 Dec 2013 14:04:59 +0200 Subject: [PATCH 085/120] using gmst string --- apps/openmw/mwstate/statemanagerimp.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9e6395812a..a408a9fb76 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -54,8 +54,7 @@ void MWState::StateManager::askLoadRecent() if( !mAskLoadRecent ) { - if(MWBase::Environment::get().getStateManager()->getCurrentCharacter()->begin() - == MWBase::Environment::get().getStateManager()->getCurrentCharacter()->end() )//no saves + if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves { MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } @@ -65,7 +64,10 @@ void MWState::StateManager::askLoadRecent() std::vector buttons; buttons.push_back("Yes"); buttons.push_back("No"); - std::string message = "The most recent Save Game is '" + lastSave.mProfile.mDescription + "'.\n Would you like to load it?"; + 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; } From 43dd3b8ef22f691a7e55b176aafaba9714660ccb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 6 Jan 2014 13:53:20 +0100 Subject: [PATCH 086/120] removed redundant deletion flag from CellRef --- apps/esmtool/esmtool.cpp | 5 +- apps/opencs/model/world/ref.cpp | 3 +- apps/opencs/model/world/refcollection.cpp | 3 +- apps/openmw/mwworld/cellstore.cpp | 177 ++++++++-------------- apps/openmw/mwworld/cellstore.hpp | 7 +- apps/openmw/mwworld/esmstore.cpp | 2 +- apps/openmw/mwworld/store.cpp | 9 +- components/esm/cellref.hpp | 3 - components/esm/loadcell.cpp | 13 +- components/esm/loadcell.hpp | 2 +- 10 files changed, 91 insertions(+), 133 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 27980096e6..a0593e60d4 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -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); @@ -251,6 +253,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Uses/health: '" << ref.mCharge << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; + std::cout << " Deleted: " << deleted << std::endl; } } diff --git a/apps/opencs/model/world/ref.cpp b/apps/opencs/model/world/ref.cpp index 74f60419b9..cf9e496ee6 100644 --- a/apps/opencs/model/world/ref.cpp +++ b/apps/opencs/model/world/ref.cpp @@ -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); } \ No newline at end of file diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 696aeefaa2..7c95c2d309 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -14,7 +14,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()); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 0c145ab600..88f422a1d2 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -35,32 +35,30 @@ namespace MWWorld { template - void CellRefList::load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore) + void CellRefList::load(ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore) { - // Get existing reference, in case we need to overwrite it. - typename std::list::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefnum); - - // 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; - } - - // for throwing exception on unhandled record type const MWWorld::Store &store = esmStore.get(); - const X *ptr = store.search(ref.mRefID); - /// \note no longer redundant - changed to Store::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)); + if (const X *ptr = store.search (ref.mRefID)) + { + typename std::list::iterator iter = + std::find(mList.begin(), mList.end(), ref.mRefnum); + + LiveRef liveCellRef (ref, ptr); + + if (deleted) + liveCellRef.mData.setCount (0); + + if (iter != mList.end()) + *iter = liveCellRef; + else + mList.push_back (liveCellRef); + } + else + { + std::cerr + << "Error: could not resolve cell reference " << ref.mRefID + << " (dropping reference)" << std::endl; } } @@ -117,16 +115,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 +130,7 @@ namespace MWWorld void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector &esm) { - assert (mCell); + assert (mCell); if (mCell->mContextList.empty()) return; // this is a dynamically generated cell -> skipping. @@ -150,102 +145,25 @@ 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)) { // 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); 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(*it); - //ESM::CellRef &ref = const_cast(it->second); - - std::string lowerCase; - - std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - 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, false, store); } } @@ -274,4 +192,39 @@ 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"; + } + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 8a01caf183..b109557f9f 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -25,7 +25,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) { @@ -154,6 +154,11 @@ namespace MWWorld void loadRefs(const MWWorld::ESMStore &store, std::vector &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. + }; } diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 62b91c2e44..cab10ee517 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -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); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 512883f1a3..44c814e81e 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -10,7 +10,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // and we merge all this data into one Cell object. However, we can't simply search for the cell id, // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they // are not available until both cells have been loaded! So first, proceed as usual. - + // All cells have a name record, even nameless exterior cells. std::string idLower = Misc::StringUtils::lowerCase(id); ESM::Cell *cell = new ESM::Cell; @@ -30,11 +30,10 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following // implementation when the oher implementation works as well. - cell->getNextRef(esm, ref); - std::string lowerCase; + bool deleted = false; + cell->getNextRef(esm, ref, deleted); - std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); + Misc::StringUtils::toLower (ref.mRefID); // Add data required to make reference appear in the correct cell. // We should not need to test for duplicates, as this part of the code is pre-cell merge. diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 47cb0b99ed..04451535be 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -71,9 +71,6 @@ namespace ESM // -1 is not blocked, otherwise it is blocked. signed char mReferenceBlocked; - // Track deleted references. 0 - not deleted, 1 - deleted, but respawns, 2 - deleted and does not respawn. - int mDeleted; - // Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza // Brindisi Dorom", where it has the value 100. Also only for // activators. diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index c22c1b22b6..0d69b02639 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -141,7 +141,7 @@ std::string Cell::getDescription() const } } -bool Cell::getNextRef(ESMReader &esm, CellRef &ref) +bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted) { // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) @@ -259,12 +259,13 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) //esm.getHNOT(NAM0, "NAM0"); } - if (esm.isNextSub("DELE")) { + if (esm.isNextSub("DELE")) + { esm.skipHSub(); - ref.mDeleted = 2; // Deleted, will not respawn. - // TODO: find out when references do respawn. - } else - ref.mDeleted = 0; + deleted = true; + } + else + deleted = false; return true; } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 61d586b9d8..38aaa0494e 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -141,7 +141,7 @@ struct Cell All fields of the CellRef struct are overwritten. You can safely reuse one memory location without blanking it between calls. */ - static bool getNextRef(ESMReader &esm, CellRef &ref); + static bool getNextRef(ESMReader &esm, CellRef &ref, bool& deleted); /* This fetches an MVRF record, which is used to track moved references. * Since they are comparably rare, we use a separate method for this. From aba72ffefe9aec66b86ccf537054ce8366e8c0b9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 02:12:41 +0100 Subject: [PATCH 087/120] Fix up the merge a bit --- apps/openmw/mwmechanics/actors.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c488d4d2b8..53adc694c7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -797,22 +797,22 @@ namespace MWMechanics } } - // 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); - 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); + } + + // 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); + if(cls.isEssential(iter->first)) MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); } From 5ea25dc26958267e39d04461ce571284566a404c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Jan 2014 15:34:32 +0100 Subject: [PATCH 088/120] player state cleanup --- apps/openmw/mwworld/player.cpp | 10 ++++++++++ apps/openmw/mwworld/player.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 1 + 3 files changed, 13 insertions(+) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index c594454028..a2777d4896 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -171,4 +171,14 @@ namespace MWWorld if (mMarkedCell) markedPosition = mMarkedPosition; } + + void Player::clear() + { + mCellStore = 0; + mSign.clear(); + mMarkedCell = 0; + mAutoMove = false; + mForwardBackward = 0; + mTeleported = false; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 1df848111b..fef577cec5 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -86,6 +86,8 @@ namespace MWWorld bool wasTeleported() const; void setTeleported(bool teleported); + + void clear(); }; } #endif diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 83885e5d51..5224ffdceb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -267,6 +267,7 @@ namespace MWWorld void World::clear() { mLocalScripts.clear(); + mPlayer->clear(); // enable collision if (!mPhysics->toggleCollisionMode()) From e453468eff18e769b0478fdf9f1fc87bd924d98a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Jan 2014 19:23:08 +0100 Subject: [PATCH 089/120] moved CellRef loading code to the CellRef class --- components/esm/cellref.cpp | 70 ++++++++++++++++++++++++++++++++++++- components/esm/cellref.hpp | 3 ++ components/esm/loadcell.cpp | 69 +----------------------------------- 3 files changed, 73 insertions(+), 69 deletions(-) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 23a95a4ab1..bdb0e23de4 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -1,12 +1,80 @@ #include "cellref.hpp" +#include "esmreader.hpp" #include "esmwriter.hpp" +void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) +{ + // NAM0 sometimes appears here, sometimes further on + mNam0 = 0; + if (esm.isNextSub ("NAM0")) + esm.getHT (mNam0); + + if (wideRefNum) + esm.getHNT (mRefNum, "FRMR", 8); + else + esm.getHNT (mRefNum.mIndex, "FRMR"); + + mRefID = esm.getHNString ("NAME"); + + mScale = 1.0; + esm.getHNOT (mScale, "XSCL"); + + mOwner = esm.getHNOString ("ANAM"); + mGlob = esm.getHNOString ("BNAM"); + mSoul = esm.getHNOString ("XSOL"); + + mFaction = esm.getHNOString ("CNAM"); + mFactIndex = -2; + esm.getHNOT (mFactIndex, "INDX"); + + mGoldValue = 1; + mCharge = -1; + mEnchantmentCharge = -1; + + esm.getHNOT (mEnchantmentCharge, "XCHG"); + + esm.getHNOT (mCharge, "INTV"); + + esm.getHNOT (mGoldValue, "NAM9"); + + // Present for doors that teleport you to another cell. + if (esm.isNextSub ("DODT")) + { + mTeleport = true; + esm.getHT (mDoorDest); + mDestCell = esm.getHNOString ("DNAM"); + } + else + mTeleport = false; + + mLockLevel = -1; + esm.getHNOT (mLockLevel, "FLTV"); + mKey = esm.getHNOString ("KNAM"); + mTrap = esm.getHNOString ("TNAM"); + + mReferenceBlocked = -1; + mFltv = 0; + esm.getHNOT (mReferenceBlocked, "UNAM"); + esm.getHNOT (mFltv, "FLTV"); + + esm.getHNOT(mPos, "DATA", 24); + + // Number of references in the cell? Maximum once in each cell, + // but not always at the beginning, and not always right. In other + // words, completely useless. + // Update: Well, maybe not completely useless. This might actually be + // number_of_references + number_of_references_moved_here_Across_boundaries, + // and could be helpful for collecting these weird moved references. + if (esm.isNextSub ("NAM0")) + esm.getHT (mNam0); +} + void ESM::CellRef::save(ESMWriter &esm) const { esm.writeHNT("FRMR", mRefNum.mIndex); - /// \todo read content file index (if present) + esm.writeHNCString("NAME", mRefID); if (mScale != 1.0) { diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 60c2bc625a..3d80a51bd4 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -8,6 +8,7 @@ namespace ESM { class ESMWriter; + class ESMReader; /* Cell reference. This represents ONE object (of many) inside the cell. The cell references are not loaded as part of the normal @@ -86,6 +87,8 @@ namespace ESM // Position and rotation of this object within the cell Position mPos; + void load (ESMReader& esm, bool wideRefNum = false); + void save(ESMWriter &esm) const; void blank(); diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 1fe92ffb1e..efd6979b48 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -177,78 +177,11 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted) // That should be it, I haven't seen any other fields yet. } - // NAM0 sometimes appears here, sometimes further on - ref.mNam0 = 0; - if (esm.isNextSub("NAM0")) - { - esm.getHT(ref.mNam0); - //esm.getHNOT(NAM0, "NAM0"); - } - - esm.getHNT (ref.mRefNum.mIndex, "FRMR"); - ref.mRefID = esm.getHNString("NAME"); + ref.load (esm); // Identify references belonging to a parent file and adapt the ID accordingly. adjustRefNum (ref.mRefNum, esm); - // getHNOT will not change the existing value if the subrecord is - // missing - ref.mScale = 1.0; - esm.getHNOT(ref.mScale, "XSCL"); - - ref.mOwner = esm.getHNOString("ANAM"); - ref.mGlob = esm.getHNOString("BNAM"); - ref.mSoul = esm.getHNOString("XSOL"); - - ref.mFaction = esm.getHNOString("CNAM"); - ref.mFactIndex = -2; - esm.getHNOT(ref.mFactIndex, "INDX"); - - ref.mGoldValue = 1; - ref.mCharge = -1; - ref.mEnchantmentCharge = -1; - - esm.getHNOT(ref.mEnchantmentCharge, "XCHG"); - - esm.getHNOT(ref.mCharge, "INTV"); - - esm.getHNOT(ref.mGoldValue, "NAM9"); - - // Present for doors that teleport you to another cell. - if (esm.isNextSub("DODT")) - { - ref.mTeleport = true; - esm.getHT(ref.mDoorDest); - ref.mDestCell = esm.getHNOString("DNAM"); - } else { - ref.mTeleport = false; - } - - // Integer, despite the name suggesting otherwise - ref.mLockLevel = -1; - esm.getHNOT(ref.mLockLevel, "FLTV"); - ref.mKey = esm.getHNOString("KNAM"); - ref.mTrap = esm.getHNOString("TNAM"); - - ref.mReferenceBlocked = -1; - ref.mFltv = 0; - esm.getHNOT(ref.mReferenceBlocked, "UNAM"); - esm.getHNOT(ref.mFltv, "FLTV"); - - esm.getHNOT(ref.mPos, "DATA", 24); - - // Number of references in the cell? Maximum once in each cell, - // but not always at the beginning, and not always right. In other - // words, completely useless. - // Update: Well, maybe not completely useless. This might actually be - // number_of_references + number_of_references_moved_here_Across_boundaries, - // and could be helpful for collecting these weird moved references. - if (esm.isNextSub("NAM0")) - { - esm.getHT(ref.mNam0); - //esm.getHNOT(NAM0, "NAM0"); - } - if (esm.isNextSub("DELE")) { esm.skipHSub(); From 8c5f3135462e0b6e44de4733d917d66c2dfdaed1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 14 Jan 2014 12:25:35 +0100 Subject: [PATCH 090/120] added savedgame-specifc record structs for objects state --- components/CMakeLists.txt | 2 +- components/esm/cellid.cpp | 26 +++++++++++++++++++ components/esm/cellid.hpp | 28 ++++++++++++++++++++ components/esm/defs.hpp | 1 + components/esm/loadcell.cpp | 24 +++++++++++++++++ components/esm/loadcell.hpp | 3 +++ components/esm/objectstate.cpp | 47 ++++++++++++++++++++++++++++++++++ components/esm/objectstate.hpp | 36 ++++++++++++++++++++++++++ components/esm/player.cpp | 44 +++++++++++++++++++++++++++++++ components/esm/player.hpp | 32 +++++++++++++++++++++++ 10 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 components/esm/cellid.cpp create mode 100644 components/esm/cellid.hpp create mode 100644 components/esm/objectstate.cpp create mode 100644 components/esm/objectstate.hpp create mode 100644 components/esm/player.cpp create mode 100644 components/esm/player.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index dbf8c1132a..4c0bff59de 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript + savedgame journalentry queststate locals globalscript player objectstate cellid ) add_component_dir (misc diff --git a/components/esm/cellid.cpp b/components/esm/cellid.cpp new file mode 100644 index 0000000000..5bc8b7aef3 --- /dev/null +++ b/components/esm/cellid.cpp @@ -0,0 +1,26 @@ + +#include "cellid.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::CellId::load (ESMReader &esm) +{ + mWorldspace = esm.getHNString ("SPAC"); + + if (esm.isNextSub ("CIDX")) + { + esm.getHT (mIndex, 8); + mPaged = true; + } + else + mPaged = false; +} + +void ESM::CellId::save (ESMWriter &esm) const +{ + esm.writeHNString ("SPAC", mWorldspace); + + if (mPaged) + esm.writeHNT ("CIDX", mIndex, 8); +} \ No newline at end of file diff --git a/components/esm/cellid.hpp b/components/esm/cellid.hpp new file mode 100644 index 0000000000..54dbdae780 --- /dev/null +++ b/components/esm/cellid.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_ESM_CELLID_H +#define OPENMW_ESM_CELLID_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + struct CellId + { + struct CellIndex + { + int mX; + int mY; + }; + + std::string mWorldspace; + CellIndex mIndex; + bool mPaged; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 4cf0b1dacf..2b956d2165 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -88,6 +88,7 @@ enum RecNameInts REC_JOUR = 0x524f55a4, REC_QUES = 0x53455551, REC_GSCR = 0x52435347, + REC_PLAY = 0x504c4159, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index efd6979b48..649e3175df 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -3,11 +3,15 @@ #include #include #include + #include +#include + #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" +#include "cellid.hpp" namespace { @@ -221,4 +225,24 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) mAmbi.mFog = 0; mAmbi.mFogDensity = 0; } + + CellId Cell::getCellId() const + { + CellId id; + + id.mPaged = (mData.mFlags & Interior); + + if (id.mPaged) + { + id.mWorldspace = "default"; + id.mIndex.mX = mData.mX; + id.mIndex.mY = mData.mY; + } + else + { + id.mWorldspace = Misc::StringUtils::lowerCase (mName); + } + + return id; + } } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 85b3d89546..643119e671 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -18,6 +18,7 @@ namespace ESM { class ESMReader; class ESMWriter; + class CellId; /* Moved cell reference tracking object. This mainly stores the target cell of the reference, so we can easily know where it has been moved when another @@ -150,6 +151,8 @@ struct Cell void blank(); ///< Set record to default state (does not touch the ID/index). + + CellId getCellId() const; }; } #endif diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp new file mode 100644 index 0000000000..b13b6c226f --- /dev/null +++ b/components/esm/objectstate.cpp @@ -0,0 +1,47 @@ + +#include "objectstate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::ObjectState::load (ESMReader &esm) +{ + mRef.load (esm, true); + + mHasLocals = 0; + esm.getHNOT (mHasLocals, "HLOC"); + + if (mHasLocals) + mLocals.load (esm); + + mEnabled = 1; + esm.getHNOT (mEnabled, "ENAB"); + + mCount = 1; + esm.getHNOT (mCount, "COUN"); + + esm.getHNT (mPosition, "POS_", 24); + + esm.getHNT (mLocalRotation, "LROT", 12); +} + +void ESM::ObjectState::save (ESMWriter &esm) const +{ + mRef.save (esm); + + if (mHasLocals) + { + esm.writeHNT ("HLOC", mHasLocals); + mLocals.save (esm); + } + + if (!mEnabled) + esm.writeHNT ("ENAB", mEnabled); + + if (mCount!=1) + esm.writeHNT ("COUN", mCount); + + esm.writeHNT ("POS_", mPosition, 24); + + esm.writeHNT ("LROT", mLocalRotation, 12); +} \ No newline at end of file diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp new file mode 100644 index 0000000000..bbbc4798f4 --- /dev/null +++ b/components/esm/objectstate.hpp @@ -0,0 +1,36 @@ +#ifndef OPENMW_ESM_OBJECTSTATE_H +#define OPENMW_ESM_OBJECTSTATE_H + +#include +#include + +#include "cellref.hpp" +#include "locals.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + ///< \brief Save state for objects, that do not use custom data + struct ObjectState + { + std::string mId; + + CellRef mRef; + + unsigned char mHasLocals; + Locals mLocals; + unsigned char mEnabled; + int mCount; + ESM::Position mPosition; + float mLocalRotation[3]; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/player.cpp b/components/esm/player.cpp new file mode 100644 index 0000000000..13602fb67d --- /dev/null +++ b/components/esm/player.cpp @@ -0,0 +1,44 @@ + +#include "player.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::Player::load (ESMReader &esm) +{ + mObject.load (esm); + + mCellId.load (esm); + + esm.getHNT (mLastKnownExteriorPosition, "LKEP", 12); + + if (esm.isNextSub ("MARK")) + { + mHasMark = true; + esm.getHT (mMarkedPosition, 24); + mMarkedCell.load (esm); + } + else + mHasMark = false; + + mAutoMove = 0; + esm.getHNOT (mAutoMove, "AMOV"); +} + +void ESM::Player::save (ESMWriter &esm) const +{ + mObject.save (esm); + + mCellId.save (esm); + + esm.writeHNT ("LKEP", mLastKnownExteriorPosition, 12); + + if (mHasMark) + { + esm.writeHNT ("MARK", mMarkedPosition, 24); + mMarkedCell.save (esm); + } + + if (mAutoMove) + esm.writeHNT ("AMOV", mAutoMove); +} \ No newline at end of file diff --git a/components/esm/player.hpp b/components/esm/player.hpp new file mode 100644 index 0000000000..3f7db17f7f --- /dev/null +++ b/components/esm/player.hpp @@ -0,0 +1,32 @@ +#ifndef OPENMW_ESM_PLAYER_H +#define OPENMW_ESM_PLAYER_H + +#include + +#include "objectstate.hpp" +#include "cellid.hpp" +#include "defs.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct Player + { + ObjectState mObject; + CellId mCellId; + float mLastKnownExteriorPosition[3]; + unsigned char mHasMark; + ESM::Position mMarkedPosition; + CellId mMarkedCell; + unsigned char mAutoMove; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file From d8d4f1a15e0bd49155031dd7fffdd3012e99af31 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Jan 2014 12:02:45 +0100 Subject: [PATCH 091/120] some fixes to record structs --- components/esm/cellref.cpp | 7 +++++-- components/esm/cellref.hpp | 2 +- components/esm/loadcell.cpp | 2 +- components/esm/objectstate.cpp | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index bdb0e23de4..b9f6302909 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -71,9 +71,12 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) esm.getHT (mNam0); } -void ESM::CellRef::save(ESMWriter &esm) const +void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum) const { - esm.writeHNT("FRMR", mRefNum.mIndex); + if (wideRefNum) + esm.writeHNT ("FRMR", mRefNum, 8); + else + esm.writeHNT ("FRMR", mRefNum.mIndex, 4); esm.writeHNCString("NAME", mRefID); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 3d80a51bd4..01b546d5a5 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -89,7 +89,7 @@ namespace ESM void load (ESMReader& esm, bool wideRefNum = false); - void save(ESMWriter &esm) const; + void save(ESMWriter &esm, bool wideRefNum = false) const; void blank(); }; diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 649e3175df..cfd73554a7 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -230,7 +230,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) { CellId id; - id.mPaged = (mData.mFlags & Interior); + id.mPaged = !(mData.mFlags & Interior); if (id.mPaged) { diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index b13b6c226f..56289acae3 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -27,7 +27,7 @@ void ESM::ObjectState::load (ESMReader &esm) void ESM::ObjectState::save (ESMWriter &esm) const { - mRef.save (esm); + mRef.save (esm, true); if (mHasLocals) { From c300cd93752ad23dd3db600433ba2e7c9e447720 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Jan 2014 12:03:23 +0100 Subject: [PATCH 092/120] loading/saving of some player state (cell/coordinates and some other bits) --- apps/openmw/mwbase/world.hpp | 7 ++- apps/openmw/mwstate/statemanagerimp.cpp | 13 +++--- apps/openmw/mwworld/livecellref.cpp | 21 +++++++++ apps/openmw/mwworld/livecellref.hpp | 57 +++++++++++++++++++++++ apps/openmw/mwworld/player.cpp | 60 +++++++++++++++++++++++-- apps/openmw/mwworld/player.hpp | 6 +++ apps/openmw/mwworld/refdata.cpp | 21 +++++++++ apps/openmw/mwworld/refdata.hpp | 9 ++++ apps/openmw/mwworld/worldimp.cpp | 21 ++++++++- apps/openmw/mwworld/worldimp.hpp | 6 ++- 10 files changed, 210 insertions(+), 11 deletions(-) create mode 100644 apps/openmw/mwworld/livecellref.cpp diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 06f6d6fac6..eaf411d205 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -37,6 +37,7 @@ namespace ESM struct Potion; struct Spell; struct NPC; + struct CellId; } namespace MWRender @@ -105,12 +106,14 @@ namespace MWBase virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; virtual OEngine::Render::Fader* getFader() = 0; - ///< \ŧodo remove this function. Rendering details should not be exposed. + ///< \todo remove this function. Rendering details should not be exposed. virtual MWWorld::CellStore *getExterior (int x, int y) = 0; virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; + virtual MWWorld::CellStore *getCell (const ESM::CellId& id) = 0; + virtual void useDeathCamera() = 0; virtual void setWaterHeight(const float height) = 0; @@ -236,6 +239,8 @@ namespace MWBase virtual void changeToExteriorCell (const ESM::Position& position) = 0; ///< Move to exterior cell. + virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position) = 0; + virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 11b2d546f0..2eb54a1251 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include @@ -216,6 +218,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_SPEL: case ESM::REC_WEAP: case ESM::REC_GLOB: + case ESM::REC_PLAY: MWBase::Environment::get().getWorld()->readRecord (reader, n.val); break; @@ -245,11 +248,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getWindowManager()->updatePlayer(); MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - // for testing purpose only - ESM::Position pos; - pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; - pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); + 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()); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp new file mode 100644 index 0000000000..a12d20e6ab --- /dev/null +++ b/apps/openmw/mwworld/livecellref.cpp @@ -0,0 +1,21 @@ + +#include "livecellref.hpp" + +#include + +void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) +{ + mRef = state.mRef; + mData = RefData (state); +} + +void MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const +{ + state.mRef = mRef; + mData.write (state); +} + +bool MWWorld::LiveCellRefBase::checkStateImp (const ESM::ObjectState& state) +{ + return true; +} \ No newline at end of file diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index 558639a3b3..46f49df78c 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -7,6 +7,11 @@ #include "refdata.hpp" +namespace ESM +{ + class ObjectState; +} + namespace MWWorld { class Ptr; @@ -29,6 +34,24 @@ namespace MWWorld LiveCellRefBase(std::string type, const ESM::CellRef &cref=ESM::CellRef()); /* Need this for the class to be recognized as polymorphic */ virtual ~LiveCellRefBase() { } + + protected: + + void loadImp (const ESM::ObjectState& state); + ///< Load state into a LiveCellRef, that has already been initialised with base and + /// class. + /// + /// \attention Must not be called with an invalid \a state. + + void saveImp (ESM::ObjectState& state) const; + ///< Save LiveCellRef state into \a state. + + static bool checkStateImp (const ESM::ObjectState& state); + ///< Check if state is valid and report errors. + /// + /// \return Valid? + /// + /// \note Does not check if the RefId exists. }; inline bool operator== (const LiveCellRefBase& cellRef, const ESM::CellRef::RefNum refNum) @@ -55,7 +78,41 @@ namespace MWWorld // The object that this instance is based on. const X* mBase; + + void load (const ESM::ObjectState& state); + ///< Load state into a LiveCellRef, that has already been initialised with base and class. + /// + /// \attention Must not be called with an invalid \a state. + + void save (ESM::ObjectState& state) const; + ///< Save LiveCellRef state into \a state. + + static bool checkState (const ESM::ObjectState& state); + ///< Check if state is valid and report errors. + /// + /// \return Valid? + /// + /// \note Does not check if the RefId exists. }; + + template + void LiveCellRef::load (const ESM::ObjectState& state) + { + loadImp (state); + } + + template + void LiveCellRef::save (ESM::ObjectState& state) const + { + saveImp (state); + } + + template + bool LiveCellRef::checkState (const ESM::ObjectState& state) + { + return checkStateImp (state); + } + } #endif diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index a2777d4896..14e3104320 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -1,6 +1,13 @@ #include "player.hpp" +#include + +#include +#include +#include +#include + #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) @@ -181,4 +185,54 @@ namespace MWWorld mForwardBackward = 0; mTeleported = false; } + + void Player::write (ESM::ESMWriter& writer) const + { + ESM::Player player; + + mPlayer.save (player.mObject); + player.mCellId = mCellStore->mCell->getCellId(); + + /// \todo sign + /// \todo last know exterior position + /// \todo mark + + 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"); + } + + mPlayer.load (player.mObject); + + mCellStore = MWBase::Environment::get().getWorld()->getCell (player.mCellId); + + /// \todo sign + /// \todo last know exterior position + /// \todo mark + + mAutoMove = player.mAutoMove!=0; + + mForwardBackward = 0; + mTeleported = false; + + return true; + } + + return false; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index fef577cec5..7eb023a2b1 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -11,6 +11,8 @@ namespace ESM { struct NPC; + class ESMWriter; + class ESMReader; } namespace MWBase @@ -88,6 +90,10 @@ namespace MWWorld void setTeleported(bool teleported); void clear(); + + void write (ESM::ESMWriter& writer) const; + + bool readRecord (ESM::ESMReader& reader, int32_t type); }; } #endif diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 87d0efe190..8d48078b1d 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -3,6 +3,8 @@ #include +#include + #include "customdata.hpp" #include "cellstore.hpp" @@ -52,6 +54,14 @@ namespace MWWorld mLocalRotation.rot[2]=0; } + RefData::RefData (const ESM::ObjectState& objectState) + : mBaseNode (0), mHasLocals (false), mEnabled (objectState.mEnabled), + mCount (objectState.mCount), mPosition (objectState.mPosition), mCustomData (0) + { + for (int i=0; i<3; ++i) + mLocalRotation.rot[i] = objectState.mLocalRotation[i]; + } + RefData::RefData (const RefData& refData) : mBaseNode(0), mCustomData (0) { @@ -66,6 +76,17 @@ namespace MWWorld } } + void RefData::write (ESM::ObjectState& objectState) const + { + objectState.mHasLocals = false; + objectState.mEnabled = mEnabled; + objectState.mCount = mCount; + objectState.mPosition = mPosition; + + for (int i=0; i<3; ++i) + objectState.mLocalRotation[i] = mLocalRotation.rot[i]; + } + RefData& RefData::operator= (const RefData& refData) { try diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 07841e4704..d9f5697bd1 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -14,6 +14,7 @@ namespace ESM { class Script; class CellRef; + class ObjectState; } namespace MWWorld @@ -55,10 +56,18 @@ namespace MWWorld /// to reset the position as the orignal data is still held in the CellRef RefData (const ESM::CellRef& cellRef); + RefData (const ESM::ObjectState& objectState); + ///< Ignores local variables and custom data (not enough context available here to + /// perform these operations). + RefData (const RefData& refData); ~RefData(); + void write (ESM::ObjectState& objectState) const; + ///< Ignores local variables and custom data (not enough context available here to + /// perform these operations). + RefData& operator= (const RefData& refData); /// Return OGRE handle (may be empty). diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5224ffdceb..8edba0892a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -309,12 +310,14 @@ namespace MWWorld { mStore.write (writer); mGlobalVariables.write (writer); + mPlayer->write (writer); } void World::readRecord (ESM::ESMReader& reader, int32_t type) { if (!mStore.readRecord (reader, type) && - !mGlobalVariables.readRecord (reader, type)) + !mGlobalVariables.readRecord (reader, type) && + !mPlayer->readRecord (reader, type)) { throw std::runtime_error ("unknown record in saved game"); } @@ -402,6 +405,14 @@ namespace MWWorld return mCells.getInterior (name); } + CellStore *World::getCell (const ESM::CellId& id) + { + if (id.mPaged) + return getExterior (id.mIndex.mX, id.mIndex.mY); + else + return getInterior (id.mWorldspace); + } + void World::useDeathCamera() { if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() ) @@ -802,6 +813,14 @@ namespace MWWorld addContainerScripts(getPlayer().getPlayer(), getPlayer().getPlayer().getCell()); } + void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position) + { + if (cellId.mPaged) + changeToExteriorCell (position); + else + changeToInteriorCell (cellId.mWorldspace, position); + } + void World::markCellAsUnchanged() { return mWorldScene->markCellAsUnchanged(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d7befcc6e5..58a6111c59 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -181,12 +181,14 @@ namespace MWWorld virtual void readRecord (ESM::ESMReader& reader, int32_t type); virtual OEngine::Render::Fader* getFader(); - ///< \ŧodo remove this function. Rendering details should not be exposed. + ///< \todo remove this function. Rendering details should not be exposed. virtual CellStore *getExterior (int x, int y); virtual CellStore *getInterior (const std::string& name); + virtual CellStore *getCell (const ESM::CellId& id); + //switch to POV before showing player's death animation virtual void useDeathCamera(); @@ -314,6 +316,8 @@ namespace MWWorld virtual void changeToExteriorCell (const ESM::Position& position); ///< Move to exterior cell. + virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position); + virtual const ESM::Cell *getExterior (const std::string& cellName) const; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. From ce00639d31eea587cc8e921c94914479ccd711bf Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Jan 2014 10:51:52 +0100 Subject: [PATCH 093/120] added missing birthsign field to player state record --- components/esm/player.cpp | 4 ++++ components/esm/player.hpp | 1 + 2 files changed, 5 insertions(+) diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 13602fb67d..d5ddc74d0a 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -23,6 +23,8 @@ void ESM::Player::load (ESMReader &esm) mAutoMove = 0; esm.getHNOT (mAutoMove, "AMOV"); + + mBirthsign = esm.getHNString ("SIGN"); } void ESM::Player::save (ESMWriter &esm) const @@ -41,4 +43,6 @@ void ESM::Player::save (ESMWriter &esm) const if (mAutoMove) esm.writeHNT ("AMOV", mAutoMove); + + esm.writeHNString ("SIGN", mBirthsign); } \ No newline at end of file diff --git a/components/esm/player.hpp b/components/esm/player.hpp index 3f7db17f7f..bd618457e1 100644 --- a/components/esm/player.hpp +++ b/components/esm/player.hpp @@ -23,6 +23,7 @@ namespace ESM ESM::Position mMarkedPosition; CellId mMarkedCell; unsigned char mAutoMove; + std::string mBirthsign; void load (ESMReader &esm); void save (ESMWriter &esm) const; From 1b7697a4b21dd6b1af49eedcbfdf8598a9b4c732 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Jan 2014 13:07:57 +0100 Subject: [PATCH 094/120] handle missing player specific state during load/save --- apps/openmw/mwworld/player.cpp | 54 +++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 14e3104320..f03abe5bcf 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -193,9 +193,20 @@ namespace MWWorld mPlayer.save (player.mObject); player.mCellId = mCellStore->mCell->getCellId(); - /// \todo sign - /// \todo last know exterior position - /// \todo mark + 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; @@ -214,16 +225,43 @@ namespace MWWorld if (!mPlayer.checkState (player.mObject)) { // this is the one object we can not silently drop. - throw std::runtime_error ("invalid player state record"); + throw std::runtime_error ("invalid player state record (object state)"); } mPlayer.load (player.mObject); - mCellStore = MWBase::Environment::get().getWorld()->getCell (player.mCellId); + MWBase::World& world = *MWBase::Environment::get().getWorld(); - /// \todo sign - /// \todo last know exterior position - /// \todo mark + mCellStore = world.getCell (player.mCellId); + + if (!player.mBirthsign.empty() && + !world.getStore().get().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().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; From 6584a01d01190ed9d2bccc6ebdc4ab6dd87f07fa Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 18 Jan 2014 14:53:25 +0100 Subject: [PATCH 095/120] reset reference to player NPC record during cleanup --- apps/openmw/mwworld/worldimp.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8edba0892a..9704239d74 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -276,15 +276,16 @@ namespace MWWorld mWorldScene->changeToVoid(); + mStore.clearDynamic(); + mStore.setUp(); + if (mPlayer) { mPlayer->setCell (0); mPlayer->getPlayer().getRefData() = RefData(); + mPlayer->set (mStore.get().find ("player")); } - mStore.clearDynamic(); - mStore.setUp(); - mCells.clear(); mProjectiles.clear(); From 14e64c180f0b685327c51a22ba78f81a296e0a28 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 18 Jan 2014 15:06:58 +0100 Subject: [PATCH 096/120] on load check player record for dangling ID references --- apps/openmw/mwworld/esmstore.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index cab10ee517..c5c826d471 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -161,9 +161,9 @@ void ESMStore::setUp() mClasses.write (writer); mClothes.write (writer); mEnchants.write (writer); - mNpcs.write (writer); mSpells.write (writer); mWeapons.write (writer); + mNpcs.write (writer); } bool ESMStore::readRecord (ESM::ESMReader& reader, int32_t type) @@ -176,11 +176,25 @@ void ESMStore::setUp() case ESM::REC_CLAS: case ESM::REC_CLOT: case ESM::REC_ENCH: - case ESM::REC_NPC_: case ESM::REC_SPEL: case ESM::REC_WEAP: + case ESM::REC_NPC_: mStores[type]->read (reader); + + if (type==ESM::REC_NPC_) + { + // NPC record will always be last and we know that there can be only one + // dynamic NPC record (player) -> We are done here with dynamic record laoding + setUp(); + + const ESM::NPC *player = mNpcs.find ("player"); + + if (!mRaces.find (player->mRace) || + !mClasses.find (player->mClass)) + throw std::runtime_error ("Invalid player record (race or class unavilable"); + } + return true; default: From 0f6089851759129daca3ce9995df6a170f7cf077 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 21 Jan 2014 14:13:13 +0100 Subject: [PATCH 097/120] adding missing cleanup for SoundManager --- apps/openmw/mwbase/soundmanager.hpp | 2 ++ apps/openmw/mwsound/soundmanagerimp.cpp | 9 +++++++++ apps/openmw/mwsound/soundmanagerimp.hpp | 2 ++ apps/openmw/mwstate/statemanagerimp.cpp | 2 ++ 4 files changed, 15 insertions(+) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 4d764597cf..1b3719e604 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -147,6 +147,8 @@ namespace MWBase virtual void update(float duration) = 0; virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0; + + virtual void clear() = 0; }; } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 304c871916..95a4cd1dc8 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -705,4 +705,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(); + } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index f62e62d503..bc8ef1db73 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -148,6 +148,8 @@ namespace MWSound virtual void update(float duration); virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up); + + virtual void clear(); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 2eb54a1251..8571e93ff6 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -15,6 +15,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -27,6 +28,7 @@ void MWState::StateManager::cleanup() { if (mState!=State_NoGame) { + MWBase::Environment::get().getSoundManager()->clear(); MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); From 9ebe66e693bcd38ddc6f2175e1a1e01f07095826 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 21 Jan 2014 14:50:58 +0100 Subject: [PATCH 098/120] improved cleanup; failed loads will now drop back into the main menu instead of crashing --- apps/openmw/mwgui/savegamedialog.cpp | 7 +++++++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwrender/renderingmanager.cpp | 9 +++++++-- apps/openmw/mwsound/soundmanagerimp.cpp | 10 ++++++++-- apps/openmw/mwworld/worldimp.cpp | 2 +- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 04461fef99..552489bc4b 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -8,6 +8,7 @@ #include "../mwbase/statemanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwstate/character.hpp" @@ -123,6 +124,12 @@ namespace MWGui } setVisible(false); + + if (MWBase::Environment::get().getStateManager()->getState()== + MWBase::StateManager::State_NoGame) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } } void SaveGameDialog::onCharacterSelected(MyGUI::ComboBox *sender, size_t pos) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 9e57f50419..27d37eacf7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -14,6 +14,7 @@ #include #include "../mwbase/inputmanager.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -709,6 +710,10 @@ namespace MWGui mToolTips->onFrame(frameDuration); + if (MWBase::Environment::get().getStateManager()->getState()== + MWBase::StateManager::State_NoGame) + return; + if (mDragAndDrop->mIsOnDragAndDrop) { assert(mDragAndDrop->mDraggedWidget); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f97d7bebf5..55ead476b9 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -30,6 +30,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" // FIXME #include "../mwbase/windowmanager.hpp" // FIXME +#include "../mwbase/statemanager.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -329,6 +330,12 @@ void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) void RenderingManager::update (float duration, bool paused) { + mVideoPlayer->update (); + + if (MWBase::Environment::get().getStateManager()->getState()== + MWBase::StateManager::State_NoGame) + return; + MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayer().getPlayer(); @@ -365,8 +372,6 @@ void RenderingManager::update (float duration, bool paused) mOcclusionQuery->update(duration); - mVideoPlayer->update (); - mRendering.update(duration); Ogre::ControllerManager::getSingleton().setTimeFactor(paused ? 0.f : 1.f); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 95a4cd1dc8..827ecd6434 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" @@ -607,8 +608,13 @@ namespace MWSound { if(!mOutput->isInitialized()) return; - updateSounds(duration); - updateRegionSound(duration); + + if (MWBase::Environment::get().getStateManager()->getState()!= + MWBase::StateManager::State_NoGame) + { + updateSounds(duration); + updateRegionSound(duration); + } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9704239d74..706701fa8b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1335,7 +1335,7 @@ namespace MWWorld updateWindowManager (); - if (mPlayer->getPlayer().getCell()->isExterior()) + if (!paused && mPlayer->getPlayer().getCell()->isExterior()) { ESM::Position pos = mPlayer->getPlayer().getRefData().getPosition(); mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos)); From 22cb4784b5079bb8d3d453bdb076dfc4b438d1d4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 23 Jan 2014 11:29:40 +0100 Subject: [PATCH 099/120] store cell state in saved game files (no references yet) --- apps/openmw/mwstate/statemanagerimp.cpp | 127 +++++++++++++----------- apps/openmw/mwstate/statemanagerimp.hpp | 2 +- apps/openmw/mwworld/cells.cpp | 96 ++++++++++++++++++ apps/openmw/mwworld/cells.hpp | 17 +++- apps/openmw/mwworld/cellstore.cpp | 21 ++++ apps/openmw/mwworld/cellstore.hpp | 10 +- apps/openmw/mwworld/worldimp.cpp | 8 +- components/CMakeLists.txt | 2 +- components/esm/cellstate.cpp | 17 ++++ components/esm/cellstate.hpp | 25 +++++ components/esm/defs.hpp | 3 +- 11 files changed, 261 insertions(+), 67 deletions(-) create mode 100644 components/esm/cellstate.cpp create mode 100644 components/esm/cellstate.hpp diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 8571e93ff6..7020678d0a 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -24,9 +24,9 @@ #include "../mwscript/globalscripts.hpp" -void MWState::StateManager::cleanup() +void MWState::StateManager::cleanup (bool force) { - if (mState!=State_NoGame) + if (mState!=State_NoGame || force) { MWBase::Environment::get().getSoundManager()->clear(); MWBase::Environment::get().getDialogueManager()->clear(); @@ -184,77 +184,86 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot void MWState::StateManager::loadGame (const Character *character, const Slot *slot) { - cleanup(); - - mTimePlayed = slot->mProfile.mTimePlayed; - - ESM::ESMReader reader; - reader.open (slot->mPath.string()); - - while (reader.hasMoreRecs()) + try { - ESM::NAME n = reader.getRecName(); - reader.getRecHeader(); + cleanup(); - switch (n.val) + mTimePlayed = slot->mProfile.mTimePlayed; + + ESM::ESMReader reader; + reader.open (slot->mPath.string()); + + while (reader.hasMoreRecs()) { - case ESM::REC_SAVE: + ESM::NAME n = reader.getRecName(); + reader.getRecHeader(); - // don't need to read that here - reader.skipRecord(); - break; + switch (n.val) + { + case ESM::REC_SAVE: - case ESM::REC_JOUR: - case ESM::REC_QUES: + // don't need to read that here + reader.skipRecord(); + break; - MWBase::Environment::get().getJournal()->readRecord (reader, n.val); - break; + case ESM::REC_JOUR: + case ESM::REC_QUES: - 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: + MWBase::Environment::get().getJournal()->readRecord (reader, n.val); + break; - MWBase::Environment::get().getWorld()->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: - case ESM::REC_GSCR: + MWBase::Environment::get().getWorld()->readRecord (reader, n.val); + break; - MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); - break; + case ESM::REC_GSCR: - default: + MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); + break; - // ignore invalid records - /// \todo log error - reader.skipRecord(); + 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().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); } - - mCharacterManager.setCurrentCharacter(character); - - mState = State_Running; - - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); - - MWBase::Environment::get().getWorld()->setupPlayer(); - MWBase::Environment::get().getWorld()->renderPlayer(); - MWBase::Environment::get().getWindowManager()->updatePlayer(); - MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - - 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()); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index a2abcfd1bd..d6bb7575d7 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -19,7 +19,7 @@ namespace MWState private: - void cleanup(); + void cleanup (bool force = false); public: diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index c844b689e3..ead12567fa 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -1,5 +1,10 @@ #include "cells.hpp" +#include +#include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -59,6 +64,30 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, CellStore& return ptr; } +void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, const CellStore& cell) const +{ + ESM::CellState cellState; + + cell.saveState (cellState); + + writer.startRecord (ESM::REC_CSTA); + cellState.mId.save (writer); + cellState.save (writer); + /// \todo write references + writer.endRecord (ESM::REC_CSTA); +} + +bool MWWorld::Cells::hasState (const CellStore& cellStore) const +{ + if (cellStore.mState==CellStore::State_Loaded) + return true; + + if (cellStore.mCell->mData.mFlags & ESM::Cell::Interior) + return cellStore.mCell->mData.mFlags & ESM::Cell::HasWater; + else + return false; +} + MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector& reader) : mStore (store), mReader (reader), mIdCache (40, std::pair ("", (CellStore*)0)), /// \todo make cache size configurable @@ -121,6 +150,14 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name) return &result->second; } +MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id) +{ + if (id.mPaged) + return getExterior (id.mIndex.mX, id.mIndex.mY); + + return getInterior (id.mWorldspace); +} + MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell, bool searchInContainers) { @@ -271,3 +308,62 @@ void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector::const_iterator iter (mInteriors.begin()); + iter!=mInteriors.end(); ++iter) + if (hasState (iter->second)) + ++count; + + for (std::map, 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, CellStore>::const_iterator iter (mExteriors.begin()); + iter!=mExteriors.end(); ++iter) + if (hasState (iter->second)) + writeCell (writer, iter->second); + + for (std::map::const_iterator iter (mInteriors.begin()); + iter!=mInteriors.end(); ++iter) + if (hasState (iter->second)) + writeCell (writer, iter->second); +} + +bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type) +{ + if (type==ESM::REC_CSTA) + { + ESM::CellState state; + state.mId.load (reader); + + CellStore *cellStore = 0; + + try + { + cellStore = getCell (state.mId); + } + catch (...) + { + // silently drop cells that don't exist anymore + /// \todo log + } + + state.load (reader); + cellStore->loadState (state); + reader.skipRecord(); + + return true; + } + + return false; +} \ No newline at end of file diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 31de2f60e8..27e1076461 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -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& reader); - ///< \todo pass the dynamic part of the ESMStore isntead (once it is written) of the whole - /// world CellStore *getExterior (int x, int y); CellStore *getInterior (const std::string& name); + CellStore *getCell (const ESM::CellId& id); + Ptr getPtr (const std::string& name, CellStore& cellStore, bool searchInContainers = false); ///< \param searchInContainers Only affect loaded cells. /// @note name must be lower case @@ -56,6 +63,12 @@ namespace MWWorld /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. /// @note name must be lower case void getExteriorPtrs (const std::string& name, std::vector& out); + + int countSavedGameRecords() const; + + void write (ESM::ESMWriter& writer) const; + + bool readRecord (ESM::ESMReader& reader, int32_t type); }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index cffa0537a5..3cbf85e8e7 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -2,6 +2,9 @@ #include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -230,4 +233,22 @@ namespace MWWorld << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; } } + + void CellStore::loadState (const ESM::CellState& state) + { + if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) + mWaterLevel = state.mWaterLevel; + + mWaterLevel = state.mWaterLevel; + } + + void CellStore::saveState (ESM::CellState& state) const + { + state.mId = mCell->getCellId(); + + if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) + state.mWaterLevel = mWaterLevel; + + state.mWaterLevel = mWaterLevel; + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 1da219a9ec..64843e7a40 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -7,6 +7,11 @@ #include "livecellref.hpp" #include "esmstore.hpp" +namespace ESM +{ + struct CellState; +} + namespace MWWorld { @@ -133,6 +138,10 @@ namespace MWWorld Ptr searchInContainer (const std::string& id); + void loadState (const ESM::CellState& state); + + void saveState (ESM::CellState& state) const; + private: template @@ -158,7 +167,6 @@ namespace MWWorld ///< Make case-adjustments to \a ref and insert it into the respective container. /// /// Invalid \a ref objects are silently dropped. - }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 706701fa8b..f8321d74e6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -304,13 +304,16 @@ namespace MWWorld { return mStore.countSavedGameRecords() - +mGlobalVariables.countSavedGameRecords(); + +mGlobalVariables.countSavedGameRecords() + +1 // player record + +mCells.countSavedGameRecords(); } void World::write (ESM::ESMWriter& writer) const { mStore.write (writer); mGlobalVariables.write (writer); + mCells.write (writer); mPlayer->write (writer); } @@ -318,7 +321,8 @@ namespace MWWorld { if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && - !mPlayer->readRecord (reader, type)) + !mPlayer->readRecord (reader, type) && + !mCells.readRecord (reader, type)) { throw std::runtime_error ("unknown record in saved game"); } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 4c0bff59de..d9ab8129dd 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate ) add_component_dir (misc diff --git a/components/esm/cellstate.cpp b/components/esm/cellstate.cpp new file mode 100644 index 0000000000..1f7e8197ec --- /dev/null +++ b/components/esm/cellstate.cpp @@ -0,0 +1,17 @@ + +#include "cellstate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::CellState::load (ESMReader &esm) +{ + mWaterLevel = 0; + esm.getHNOT (mWaterLevel, "WLVL"); +} + +void ESM::CellState::save (ESMWriter &esm) const +{ + if (!mId.mPaged) + esm.writeHNT ("WLVL", mWaterLevel); +} \ No newline at end of file diff --git a/components/esm/cellstate.hpp b/components/esm/cellstate.hpp new file mode 100644 index 0000000000..cd0db30675 --- /dev/null +++ b/components/esm/cellstate.hpp @@ -0,0 +1,25 @@ +#ifndef OPENMW_ESM_CELLSTATE_H +#define OPENMW_ESM_CELLSTATE_H + +#include "cellid.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + /// \note Does not include references + struct CellState + { + CellId mId; + + float mWaterLevel; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 2b956d2165..1ca6e88fc8 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -88,7 +88,8 @@ enum RecNameInts REC_JOUR = 0x524f55a4, REC_QUES = 0x53455551, REC_GSCR = 0x52435347, - REC_PLAY = 0x504c4159, + REC_PLAY = 0x59414c50, + REC_CSTA = 0x41545343, // format 1 REC_FILT = 0x544C4946 From dd7d80ffbc6e5b7eca0cef25d18c1d71fcc83742 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 23 Jan 2014 12:51:25 +0100 Subject: [PATCH 100/120] removed a redundant field from object state --- components/esm/objectstate.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index bbbc4798f4..c599bb9734 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -17,8 +17,6 @@ namespace ESM ///< \brief Save state for objects, that do not use custom data struct ObjectState { - std::string mId; - CellRef mRef; unsigned char mHasLocals; From 419e3a7d30f223088d16dfdc1a838b59b5cf6121 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 23 Jan 2014 12:51:42 +0100 Subject: [PATCH 101/120] write references in cells to saved game file --- apps/openmw/mwworld/cells.cpp | 2 +- apps/openmw/mwworld/cellstore.cpp | 50 +++++++++++++++++++++++++++++++ apps/openmw/mwworld/cellstore.hpp | 2 ++ components/esm/defs.hpp | 1 + 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index ead12567fa..77ea3856cc 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -73,7 +73,7 @@ void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, const CellStore& cell) c writer.startRecord (ESM::REC_CSTA); cellState.mId.save (writer); cellState.save (writer); - /// \todo write references + cell.writeReferences (writer); writer.endRecord (ESM::REC_CSTA); } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 3cbf85e8e7..b8cd15f53a 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -32,6 +34,30 @@ namespace return MWWorld::Ptr(); } + + template + void writeReferenceCollection (ESM::ESMWriter& writer, + const MWWorld::CellRefList& collection) + { + if (!collection.mList.empty()) + { + // section header + writer.writeHNT ("CSEC", collection.mList.front().mBase->sRecordId); + + // references + for (typename MWWorld::CellRefList::List::const_iterator + iter (collection.mList.begin()); + iter!=collection.mList.end(); ++iter) + { + RecordType state; + iter->save (state); + + writer.startRecord (ESM::REC_OBJE); + state.save (writer); + writer.endRecord (ESM::REC_OBJE); + } + } + } } namespace MWWorld @@ -251,4 +277,28 @@ namespace MWWorld state.mWaterLevel = mWaterLevel; } + + void CellStore::writeReferences (ESM::ESMWriter& writer) const + { + writeReferenceCollection (writer, mActivators); + writeReferenceCollection (writer, mPotions); + writeReferenceCollection (writer, mAppas); + writeReferenceCollection (writer, mArmors); + writeReferenceCollection (writer, mBooks); + writeReferenceCollection (writer, mClothes); + writeReferenceCollection (writer, mContainers); + writeReferenceCollection (writer, mCreatures); + writeReferenceCollection (writer, mDoors); + writeReferenceCollection (writer, mIngreds); + writeReferenceCollection (writer, mCreatureLists); + writeReferenceCollection (writer, mItemLists); + writeReferenceCollection (writer, mLights); + writeReferenceCollection (writer, mLockpicks); + writeReferenceCollection (writer, mMiscItems); + writeReferenceCollection (writer, mNpcs); + writeReferenceCollection (writer, mProbes); + writeReferenceCollection (writer, mRepairs); + writeReferenceCollection (writer, mStatics); + writeReferenceCollection (writer, mWeapons); + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 64843e7a40..feebfe90a3 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -142,6 +142,8 @@ namespace MWWorld void saveState (ESM::CellState& state) const; + void writeReferences (ESM::ESMWriter& writer) const; + private: template diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 1ca6e88fc8..74d987df85 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -90,6 +90,7 @@ enum RecNameInts REC_GSCR = 0x52435347, REC_PLAY = 0x59414c50, REC_CSTA = 0x41545343, + REC_OBJE = 0x454a424f, // format 1 REC_FILT = 0x544C4946 From 460089c0aa1079764de73ff27f160ce0c337c845 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 23 Jan 2014 12:53:55 +0100 Subject: [PATCH 102/120] ignore deleted references that did not came from a content file --- apps/openmw/mwworld/cellstore.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index b8cd15f53a..aea4e5b6a2 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -49,6 +49,9 @@ namespace 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); From 786ed6ca5b462f3e28f6db2a647f6ca2fba361d3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 20:56:24 +0100 Subject: [PATCH 103/120] Include some required Ogre headers explicitely --- apps/opencs/view/render/scenewidget.cpp | 1 + apps/openmw/mwrender/characterpreview.cpp | 1 + apps/openmw/mwrender/occlusionquery.cpp | 1 + apps/openmw/mwrender/shadows.cpp | 1 + apps/openmw/mwrender/water.cpp | 1 + components/nifogre/ogrenifloader.cpp | 4 +++- libs/openengine/ogre/renderer.cpp | 1 + 7 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index c8b37e9bb0..620586bd2d 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace CSVRender { diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 643225515b..b1ec2d5ff7 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index a69511acd7..2461034717 100644 --- a/apps/openmw/mwrender/occlusionquery.cpp +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "renderconst.hpp" diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 21bbe51b63..9ebb0ab087 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 0a4db30e9c..9e3105168b 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "sky.hpp" #include "renderingmanager.hpp" diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 63e9057664..b97d1dbe98 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -36,6 +35,9 @@ #include #include #include +#include +#include +#include #include diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 9e5ec5414d..c86697497f 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include From 295aed3533592f3bc09d688c923a38a634668817 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 24 Jan 2014 17:49:16 +0100 Subject: [PATCH 104/120] Implement savegame screenshots --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwgui/savegamedialog.cpp | 28 ++++++++++++++++++ apps/openmw/mwrender/renderingmanager.cpp | 35 +++++++++++++++++++++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwstate/statemanagerimp.cpp | 9 ++++++ apps/openmw/mwworld/worldimp.cpp | 5 ++++ apps/openmw/mwworld/worldimp.hpp | 1 + components/esm/savedgame.cpp | 8 ++++++ components/esm/savedgame.hpp | 3 +- 9 files changed, 89 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index eaf411d205..99346bc6fd 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -412,6 +412,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 diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 552489bc4b..91993b0be8 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -1,6 +1,9 @@ #include "savegamedialog.hpp" #include "widgets.hpp" +#include +#include + #include #include @@ -166,6 +169,7 @@ namespace MWGui if (pos == MyGUI::ITEM_NONE) { mInfoText->setCaption(""); + mScreenshot->setImageTexture(""); return; } @@ -199,5 +203,29 @@ namespace MWGui << " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); mInfoText->setCaptionWithReplacing(text.str()); + + // Decode screenshot + std::vector 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); } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 55ead476b9..84cc0ac237 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -289,6 +289,9 @@ void RenderingManager::rotateObject(const MWWorld::Ptr &ptr) void RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { + Ogre::Image im; + im.encode(".jpg"); + Ogre::SceneNode *child = mRendering.getScene()->getSceneNode(old.getRefData().getHandle()); @@ -966,6 +969,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(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 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) { diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 5631c94701..f62ca8b3c2 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -213,6 +213,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); protected: virtual void windowResized(int x, int y); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 7020678d0a..6251c49c4e 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -8,6 +8,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" @@ -151,6 +153,13 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot 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 diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f8321d74e6..46ed312175 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1810,6 +1810,11 @@ namespace MWWorld mRendering->frameStarted(dt, paused); } + void World::screenshot(Ogre::Image &image, int w, int h) + { + mRendering->screenshot(image, w, h); + } + void World::activateDoor(const MWWorld::Ptr& door) { if (mDoorStates.find(door) != mDoorStates.end()) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 58a6111c59..0e0bb28148 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -504,6 +504,7 @@ namespace MWWorld virtual void playVideo(const std::string& name, bool allowSkipping); virtual void stopVideo(); virtual void frameStarted (float dt, bool paused); + virtual void screenshot (Ogre::Image& image, int w, int h); /// Find center of exterior cell above land surface /// \return false if exterior with given name not exists, true otherwise diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 55b17289c3..d6887f1704 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -19,6 +19,11 @@ void ESM::SavedGame::load (ESMReader &esm) while (esm.isNextSub ("DEPE")) mContentFiles.push_back (esm.getHString()); + + esm.getSubNameIs("SCRN"); + esm.getSubHeader(); + mScreenshot.resize(esm.getSubSize()); + esm.getExact(&mScreenshot[0], mScreenshot.size()); } void ESM::SavedGame::save (ESMWriter &esm) const @@ -35,4 +40,7 @@ void ESM::SavedGame::save (ESMWriter &esm) const iter!=mContentFiles.end(); ++iter) esm.writeHNString ("DEPE", *iter); + esm.startSubRecord("SCRN"); + esm.write(&mScreenshot[0], mScreenshot.size()); + esm.endRecord("SCRN"); } diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index 6c11d318f6..9c7bf551d7 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -31,8 +31,7 @@ namespace ESM TimeStamp mInGameTime; double mTimePlayed; std::string mDescription; - - /// \todo add field for screenshot + std::vector mScreenshot; // raw jpg-encoded data void load (ESMReader &esm); void save (ESMWriter &esm) const; From f09328ca845306f2a033157ee924ba7d91b5ef48 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 13:34:56 +0100 Subject: [PATCH 105/120] Clear global map overlay when starting/loading a game --- apps/openmw/mwbase/windowmanager.hpp | 3 +++ apps/openmw/mwgui/mapwindow.cpp | 19 +++++++++++++---- apps/openmw/mwgui/mapwindow.hpp | 3 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwgui/windowmanagerimp.hpp | 3 +++ apps/openmw/mwrender/globalmap.cpp | 25 +++++++++++------------ apps/openmw/mwrender/globalmap.hpp | 3 +++ apps/openmw/mwrender/renderingmanager.cpp | 3 --- apps/openmw/mwstate/statemanagerimp.cpp | 6 ++++-- apps/openmw/mwworld/worldimp.cpp | 1 + 10 files changed, 49 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c39de44006..4d47e7eb7a 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -285,6 +285,9 @@ namespace MWBase /// Should the cursor be visible? virtual bool getCursorVisible() = 0; + + /// Clear all savegame-specific data + virtual void clear() = 0; }; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index ba6114262a..c09b4fea09 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -434,7 +434,7 @@ namespace MWGui static int _counter=0; - MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", + MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget("ButtonImage", widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast(_counter)); markerWidget->setImageResource("DoorMarker"); markerWidget->setUserString("ToolTipType", "Layout"); @@ -499,10 +499,11 @@ namespace MWGui mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - for (unsigned int i=0; igetChildCount (); ++i) + // force markers to foreground + for (unsigned int i=0; igetChildCount (); ++i) { - if (mGlobalMapImage->getChildAt (i)->getName().substr(0,4) == "Door") - mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); + if (mGlobalMapOverlay->getChildAt (i)->getName().substr(0,4) == "Door") + mGlobalMapOverlay->getChildAt (i)->castType()->setImageResource("DoorMarker"); } globalMapUpdatePlayer(); @@ -573,4 +574,14 @@ 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)); + } + } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 7df2105dcf..dec27199af 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -92,6 +92,9 @@ namespace MWGui virtual void open(); + /// Clear all savegame-specific data + void clear(); + 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); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e11d1afcc5..8e49d66142 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1381,4 +1381,9 @@ namespace MWGui Settings::Manager::setFloat(setting + " h", "Windows", h); } + void WindowManager::clear() + { + mMap->clear(); + } + } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 9838a667f9..68dc947af6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -280,6 +280,9 @@ namespace MWGui virtual bool getCursorVisible(); + /// Clear all savegame-specific data + virtual void clear(); + private: bool mConsoleOnlyScripts; diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 120a83fae7..522bbb321b 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -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 buffer; - buffer.resize(mWidth * mHeight); - - // initialize to (0, 0, 0, 0) - for (int p=0; pgetBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mWidth*mHeight*4); - mOverlayTexture->getBuffer()->unlock(); + clear(); loadingListener->loadingOff(); } @@ -227,9 +216,19 @@ 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 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); + } } diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index aad9adcc42..20f40de99f 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -31,6 +31,9 @@ namespace MWRender void exploreCell (int cellX, int cellY); + /// Clears the overlay + void clear(); + private: std::string mCacheDir; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 56096b2f56..f9dd4e5665 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -290,9 +290,6 @@ void RenderingManager::rotateObject(const MWWorld::Ptr &ptr) void RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { - Ogre::Image im; - im.encode(".jpg"); - Ogre::SceneNode *child = mRendering.getScene()->getSceneNode(old.getRefData().getHandle()); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 6251c49c4e..e94f790c72 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -35,6 +35,7 @@ void MWState::StateManager::cleanup (bool force) 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(); @@ -73,8 +74,8 @@ void MWState::StateManager::askLoadRecent() { MWState::Slot lastSave = *getCurrentCharacter()->begin(); std::vector buttons; - buttons.push_back("Yes"); - buttons.push_back("No"); + 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); @@ -257,6 +258,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl 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(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9d07d0119e..e5138eaea3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -248,6 +248,7 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->updatePlayer(); + // FIXME: this will add cell 0,0 as visible on the global map ESM::Position pos; const int cellSize = 8192; pos.pos[0] = cellSize/2; From 9c0ed6955041285dfc9df39be6e06634a70c3239 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 15:10:25 +0100 Subject: [PATCH 106/120] Fix CellRef loading issue causing a startup script failure when TR_Mainland.esm is loaded. --- components/esm/cellref.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index b9f6302909..19427af0c4 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -18,6 +18,11 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mRefID = esm.getHNString ("NAME"); + // Again, UNAM sometimes appears after NAME and sometimes later. + // Or perhaps this UNAM means something different? + mReferenceBlocked = -1; + esm.getHNOT (mReferenceBlocked, "UNAM"); + mScale = 1.0; esm.getHNOT (mScale, "XSCL"); @@ -54,7 +59,6 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mKey = esm.getHNOString ("KNAM"); mTrap = esm.getHNOString ("TNAM"); - mReferenceBlocked = -1; mFltv = 0; esm.getHNOT (mReferenceBlocked, "UNAM"); esm.getHNOT (mFltv, "FLTV"); @@ -162,4 +166,4 @@ void ESM::CellRef::blank() bool ESM::operator== (const CellRef::RefNum& left, const CellRef::RefNum& right) { return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile; -} \ No newline at end of file +} From 28185e20174b994b28cbc52cb636219781eff654 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 15:54:24 +0100 Subject: [PATCH 107/120] Death/godmode fixes: Revive player *after* character update, since there might be fall damage. --- apps/openmw/engine.cpp | 12 ++++---- apps/openmw/mwmechanics/actors.cpp | 40 ++++++++++++++------------- apps/openmw/mwmechanics/character.cpp | 1 - 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b2c0c1968d..66dea4f1d0 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -89,6 +89,8 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (mUseSound) MWBase::Environment::get().getSoundManager()->update(frametime); + bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); + if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) { @@ -105,7 +107,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (changed) // keep change flag for another frame, if cell changed happened in local script MWBase::Environment::get().getWorld()->markCellAsUnchanged(); - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + if (!paused) MWBase::Environment::get().getWorld()->advanceTime( frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); @@ -116,18 +118,18 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // 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()->getPlayer().getPlayer(); - if(MWWorld::Class::get(player).getCreatureStats(player).isDead()) + 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(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 51e91a9546..34cfe16fa2 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -771,6 +771,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++) @@ -804,7 +822,6 @@ namespace MWMechanics stat.setModified(1, 0); stats.setHealth(stat); } - stats.resurrect(); continue; } @@ -818,6 +835,9 @@ namespace MWMechanics spells.purge(iter->first.getRefData().getHandle()); } + // FIXME: see http://bugs.openmw.org/issues/869 + MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); + if (iter->second->kill()) { ++mDeathCount[cls.getId(iter->first)]; @@ -839,24 +859,6 @@ namespace MWMechanics } } } - - 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); - } - } } void Actors::restoreDynamicStats(bool sleep) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ee0d077317..40e6cde69b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1153,7 +1153,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)); } From bdb03926c29d837bb23ce436a998e10b277a9426 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 16:13:45 +0100 Subject: [PATCH 108/120] Fix crash when loading another game after dying --- apps/openmw/mwworld/worldimp.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e5138eaea3..076fcfdb66 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1783,6 +1783,12 @@ namespace MWWorld void World::renderPlayer() { mRendering->renderPlayer(mPlayer->getPlayer()); + + // At this point the Animation object is live, and the CharacterController associated with it must be created. + // It has to be done at this point: resetCamera below does animation->setViewMode -> CharacterController::forceStateUpdate + // so we should make sure not to use a "stale" controller for that. + MWBase::Environment::get().getMechanicsManager()->add(mPlayer->getPlayer()); + mPhysics->addActor(mPlayer->getPlayer()); mRendering->resetCamera(); } From 79a9c4e048a839156ff084251bac3b8bdcca2dbe Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 16:33:39 +0100 Subject: [PATCH 109/120] Clear mShared before populating it. Fixes an issue with duplicate records (e.g. dialogue keywords) after loading a savegame. --- apps/openmw/mwworld/store.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index df957408d5..7bd00d6bfd 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -201,6 +201,7 @@ namespace MWWorld void setUp() { //std::sort(mStatic.begin(), mStatic.end(), RecordCmp()); + mShared.clear(); mShared.reserve(mStatic.size()); typename std::map::iterator it = mStatic.begin(); for (; it != mStatic.end(); ++it) { From e62bf8fca9900a7c747d03ffa2afd6a3f99e8504 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 16:41:26 +0100 Subject: [PATCH 110/120] Remove some garbage --- apps/openmw/mwgui/journalbooks.cpp | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index dbea10e775..8caea770ee 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -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 (); } From e0de76a6f714100df72135476cc9738bc6babf87 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 18:20:17 +0100 Subject: [PATCH 111/120] Save/load global map --- apps/openmw/mwbase/windowmanager.hpp | 5 ++ apps/openmw/mwgui/mapwindow.cpp | 17 +++++ apps/openmw/mwgui/mapwindow.hpp | 9 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 10 +++ apps/openmw/mwgui/windowmanagerimp.hpp | 3 + apps/openmw/mwrender/globalmap.cpp | 99 ++++++++++++++++++++++++- apps/openmw/mwrender/globalmap.hpp | 10 ++- apps/openmw/mwstate/statemanagerimp.cpp | 7 ++ components/CMakeLists.txt | 2 +- components/esm/defs.hpp | 1 + components/esm/globalmap.cpp | 26 +++++++ components/esm/globalmap.hpp | 34 +++++++++ 12 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 components/esm/globalmap.cpp create mode 100644 components/esm/globalmap.hpp diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 4d47e7eb7a..fa0fe888ba 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -33,6 +33,8 @@ namespace OEngine namespace ESM { struct Class; + class ESMReader; + class ESMWriter; } namespace MWWorld @@ -288,6 +290,9 @@ namespace MWBase /// 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; }; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index c09b4fea09..9ed3bf80ff 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -584,4 +584,21 @@ namespace MWGui 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 > exploredCells; + mGlobalMapRender->readRecord(reader, type, exploredCells); + + for (std::vector >::iterator it = exploredCells.begin(); it != exploredCells.end(); ++it) + { + const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first, it->second); + if (cell && !cell->mName.empty()) + addVisitedLocation(cell->mName, it->first, it->second); + } + } } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index dec27199af..6ace7dc0fd 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -8,6 +8,12 @@ namespace MWRender class GlobalMap; } +namespace ESM +{ + class ESMReader; + class ESMWriter; +} + namespace Loading { class Listener; @@ -95,6 +101,9 @@ namespace MWGui /// 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); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8e49d66142..66bd805af7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1386,4 +1386,14 @@ namespace MWGui mMap->clear(); } + void WindowManager::write(ESM::ESMWriter &writer) + { + mMap->write(writer); + } + + void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type) + { + mMap->readRecord(reader, type); + } + } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 68dc947af6..bc440d8182 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -283,6 +283,9 @@ namespace MWGui /// 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; diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 522bbb321b..6fbcfdc6b5 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -12,6 +12,8 @@ #include +#include + #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) { @@ -231,4 +231,99 @@ namespace MWRender 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 >& 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"); + } + } } diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index 20f40de99f..5fe878cd45 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -10,6 +10,12 @@ namespace Loading class Listener; } +namespace ESM +{ + class ESMWriter; + class ESMReader; +} + namespace MWRender { @@ -34,13 +40,15 @@ namespace MWRender /// Clears the overlay void clear(); + void write (ESM::ESMWriter& writer); + void readRecord (ESM::ESMReader& reader, int32_t type, std::vector >& exploredCells); + private: std::string mCacheDir; std::vector< std::pair > mExploredCells; Ogre::TexturePtr mOverlayTexture; - std::vector mExploredBuffer; int mWidth; int mHeight; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index e94f790c72..a396d78c5b 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -174,6 +174,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot +MWBase::Environment::get().getJournal()->countSavedGameRecords() +MWBase::Environment::get().getWorld()->countSavedGameRecords() +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() + + 1 // global map ); writer.save (stream); @@ -185,6 +186,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot 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(); @@ -243,6 +245,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl 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 diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index d9ab8129dd..d73bcaf74f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap ) add_component_dir (misc diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 74d987df85..40ef7ecb65 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -91,6 +91,7 @@ enum RecNameInts REC_PLAY = 0x59414c50, REC_CSTA = 0x41545343, REC_OBJE = 0x454a424f, + REC_GMAP = 0x50414d47, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/globalmap.cpp b/components/esm/globalmap.cpp new file mode 100644 index 0000000000..1fa5f907e0 --- /dev/null +++ b/components/esm/globalmap.cpp @@ -0,0 +1,26 @@ +#include "globalmap.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" +#include "defs.hpp" + +unsigned int ESM::GlobalMap::sRecordId = ESM::REC_GMAP; + +void ESM::GlobalMap::load (ESMReader &esm) +{ + esm.getHNT(mBounds, "BNDS"); + + esm.getSubNameIs("DATA"); + esm.getSubHeader(); + mImageData.resize(esm.getSubSize()); + esm.getExact(&mImageData[0], mImageData.size()); +} + +void ESM::GlobalMap::save (ESMWriter &esm) const +{ + esm.writeHNT("BNDS", mBounds); + + esm.startSubRecord("DATA"); + esm.write(&mImageData[0], mImageData.size()); + esm.endRecord("DATA"); +} diff --git a/components/esm/globalmap.hpp b/components/esm/globalmap.hpp new file mode 100644 index 0000000000..5d036c736f --- /dev/null +++ b/components/esm/globalmap.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_COMPONENTS_ESM_GLOBALMAP_H +#define OPENMW_COMPONENTS_ESM_GLOBALMAP_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + ///< \brief An image containing the explored areas on the global map. + struct GlobalMap + { + static unsigned int sRecordId; + + // The minimum and maximum cell coordinates + struct Bounds + { + int mMinX, mMaxX, mMinY, mMaxY; + }; + + Bounds mBounds; + + std::vector mImageData; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + +} + +#endif From 29c3a288e3525ca29cef275e66536dded60a3683 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 27 Jan 2014 13:27:42 +0100 Subject: [PATCH 112/120] load and save of reference in cells (without CustomData state) --- apps/openmw/engine.cpp | 9 +- apps/openmw/mwbase/world.hpp | 4 +- apps/openmw/mwstate/statemanagerimp.cpp | 40 +++++- apps/openmw/mwstate/statemanagerimp.hpp | 4 + apps/openmw/mwworld/cells.cpp | 9 +- apps/openmw/mwworld/cells.hpp | 3 +- apps/openmw/mwworld/cellstore.cpp | 163 +++++++++++++++++++++++- apps/openmw/mwworld/cellstore.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 5 +- apps/openmw/mwworld/worldimp.hpp | 3 +- components/esm/defs.hpp | 1 - 11 files changed, 222 insertions(+), 21 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 66dea4f1d0..08f9cdb5cc 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -127,7 +127,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if(!paused && player.getClass().getCreatureStats(player).isDead()) MWBase::Environment::get().getStateManager()->endGame(); } - + // update world MWBase::Environment::get().getWorld()->update(frametime, paused); @@ -282,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) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 64104f2ba0..cbf363581e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -2,6 +2,7 @@ #define GAME_MWBASE_WORLD_H #include +#include #include @@ -102,7 +103,8 @@ namespace MWBase virtual void write (ESM::ESMWriter& writer) const = 0; - virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; + virtual void readRecord (ESM::ESMReader& reader, int32_t type, + const std::map& contentFileMap) = 0; virtual OEngine::Render::Fader* getFader() = 0; ///< \todo remove this function. Rendering details should not be exposed. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a396d78c5b..fb2d10bbfd 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include @@ -43,6 +45,31 @@ void MWState::StateManager::cleanup (bool force) } } +std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) + const +{ + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + const std::vector& prev = reader.getGameFiles(); + + std::map map; + + for (int iPrev = 0; iPrev (prev.size()); ++iPrev) + { + std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); + + for (int iCurrent = 0; iCurrent (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) { @@ -167,7 +194,16 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); std::ofstream stream (slot->mPath.string().c_str()); + ESM::ESMWriter writer; + + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + for (std::vector::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 @@ -205,6 +241,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl ESM::ESMReader reader; reader.open (slot->mPath.string()); + std::map contentFileMap = buildContentFileIndexMap (reader); + while (reader.hasMoreRecs()) { ESM::NAME n = reader.getRecName(); @@ -237,7 +275,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_PLAY: case ESM::REC_CSTA: - MWBase::Environment::get().getWorld()->readRecord (reader, n.val); + MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); break; case ESM::REC_GSCR: diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index d6bb7575d7..46ade236b6 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -1,6 +1,8 @@ #ifndef GAME_STATE_STATEMANAGER_H #define GAME_STATE_STATEMANAGER_H +#include + #include "../mwbase/statemanager.hpp" #include @@ -21,6 +23,8 @@ namespace MWState void cleanup (bool force = false); + std::map buildContentFileIndexMap (const ESM::ESMReader& reader) const; + public: StateManager (const boost::filesystem::path& saves, const std::string& game); diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 26ec14a6dc..965c9fc5de 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -352,7 +352,8 @@ void MWWorld::Cells::write (ESM::ESMWriter& writer) const writeCell (writer, iter->second); } -bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type) +bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type, + const std::map& contentFileMap) { if (type==ESM::REC_CSTA) { @@ -373,7 +374,11 @@ bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type) state.load (reader); cellStore->loadState (state); - reader.skipRecord(); + + if (cellStore->mState!=CellStore::State_Loaded) + cellStore->load (mStore, mReader); + + cellStore->readReferences (reader, contentFileMap); return true; } diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 1b0fe6724b..7ee8a3f6c2 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -73,7 +73,8 @@ namespace MWWorld void write (ESM::ESMWriter& writer) const; - bool readRecord (ESM::ESMReader& reader, int32_t type); + bool readRecord (ESM::ESMReader& reader, int32_t type, + const std::map& contentFileMap); }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index aea4e5b6a2..88c241e1ab 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -41,9 +41,6 @@ namespace { if (!collection.mList.empty()) { - // section header - writer.writeHNT ("CSEC", collection.mList.front().mBase->sRecordId); - // references for (typename MWWorld::CellRefList::List::const_iterator iter (collection.mList.begin()); @@ -55,12 +52,51 @@ namespace RecordType state; iter->save (state); - writer.startRecord (ESM::REC_OBJE); + writer.writeHNT ("OBJE", collection.mList.front().mBase->sRecordId); state.save (writer); - writer.endRecord (ESM::REC_OBJE); } } } + + template + void readReferenceCollection (ESM::ESMReader& reader, + MWWorld::CellRefList& collection, const std::map& contentFileMap) + { + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); + + RecordType state; + state.load (reader); + + std::map::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::checkState (state)) + return; // not valid anymore with current content files -> skip + + const T *record = esmStore.get().search (state.mRef.mRefID); + + if (!record) + return; + + for (typename MWWorld::CellRefList::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 ref (record); + ref.load (state); + collection.mList.push_back (ref); + } } namespace MWWorld @@ -304,4 +340,121 @@ namespace MWWorld writeReferenceCollection (writer, mStatics); writeReferenceCollection (writer, mWeapons); } + + void CellStore::readReferences (ESM::ESMReader& reader, + const std::map& contentFileMap) + { + while (reader.isNextSub ("OBJE")) + { + unsigned int id = 0; + reader.getHT (id); + + switch (id) + { + case ESM::REC_ACTI: + + readReferenceCollection (reader, mActivators, contentFileMap); + break; + + case ESM::REC_ALCH: + + readReferenceCollection (reader, mPotions, contentFileMap); + break; + + case ESM::REC_APPA: + + readReferenceCollection (reader, mAppas, contentFileMap); + break; + + case ESM::REC_ARMO: + + readReferenceCollection (reader, mArmors, contentFileMap); + break; + + case ESM::REC_BOOK: + + readReferenceCollection (reader, mBooks, contentFileMap); + break; + + case ESM::REC_CLOT: + + readReferenceCollection (reader, mClothes, contentFileMap); + break; + + case ESM::REC_CONT: + + readReferenceCollection (reader, mContainers, contentFileMap); + break; + + case ESM::REC_CREA: + + readReferenceCollection (reader, mCreatures, contentFileMap); + break; + + case ESM::REC_DOOR: + + readReferenceCollection (reader, mDoors, contentFileMap); + break; + + case ESM::REC_INGR: + + readReferenceCollection (reader, mIngreds, contentFileMap); + break; + + case ESM::REC_LEVC: + + readReferenceCollection (reader, mCreatureLists, contentFileMap); + break; + + case ESM::REC_LEVI: + + readReferenceCollection (reader, mItemLists, contentFileMap); + break; + + case ESM::REC_LIGH: + + readReferenceCollection (reader, mLights, contentFileMap); + break; + + case ESM::REC_LOCK: + + readReferenceCollection (reader, mLockpicks, contentFileMap); + break; + + case ESM::REC_MISC: + + readReferenceCollection (reader, mMiscItems, contentFileMap); + break; + + case ESM::REC_NPC_: + + readReferenceCollection (reader, mNpcs, contentFileMap); + break; + + case ESM::REC_PROB: + + readReferenceCollection (reader, mProbes, contentFileMap); + break; + + case ESM::REC_REPA: + + readReferenceCollection (reader, mRepairs, contentFileMap); + break; + + case ESM::REC_STAT: + + readReferenceCollection (reader, mStatics, contentFileMap); + break; + + case ESM::REC_WEAP: + + readReferenceCollection (reader, mWeapons, contentFileMap); + break; + + default: + + throw std::runtime_error ("unknown type in cell reference section"); + } + } + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index feebfe90a3..a4f2190135 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -144,6 +144,8 @@ namespace MWWorld void writeReferences (ESM::ESMWriter& writer) const; + void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap); + private: template diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 076fcfdb66..26cb349770 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -324,12 +324,13 @@ namespace MWWorld mPlayer->write (writer); } - void World::readRecord (ESM::ESMReader& reader, int32_t type) + void World::readRecord (ESM::ESMReader& reader, int32_t type, + const std::map& contentFileMap) { if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && !mPlayer->readRecord (reader, type) && - !mCells.readRecord (reader, type)) + !mCells.readRecord (reader, type, contentFileMap)) { throw std::runtime_error ("unknown record in saved game"); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 0fb4ed1eab..b995093223 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -179,7 +179,8 @@ namespace MWWorld virtual void write (ESM::ESMWriter& writer) const; - virtual void readRecord (ESM::ESMReader& reader, int32_t type); + virtual void readRecord (ESM::ESMReader& reader, int32_t type, + const std::map& contentFileMap); virtual OEngine::Render::Fader* getFader(); ///< \todo remove this function. Rendering details should not be exposed. diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 40ef7ecb65..1b0125e78b 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -90,7 +90,6 @@ enum RecNameInts REC_GSCR = 0x52435347, REC_PLAY = 0x59414c50, REC_CSTA = 0x41545343, - REC_OBJE = 0x454a424f, REC_GMAP = 0x50414d47, // format 1 From a623f038504232f152b04a79d7bd790f0565f1db Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Jan 2014 13:49:59 +0100 Subject: [PATCH 113/120] reduced object state for objects in containers --- components/esm/cellref.cpp | 31 +++++++++++++++++-------------- components/esm/cellref.hpp | 2 +- components/esm/objectstate.cpp | 14 ++++++++------ components/esm/objectstate.hpp | 2 +- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 19427af0c4..00b15f4a30 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -75,7 +75,7 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) esm.getHT (mNam0); } -void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum) const +void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) const { if (wideRefNum) esm.writeHNT ("FRMR", mRefNum, 8); @@ -107,29 +107,32 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum) const esm.writeHNT("NAM9", mGoldValue); } - if (mTeleport) + if (mTeleport && !inInventory) { esm.writeHNT("DODT", mDoorDest); esm.writeHNOCString("DNAM", mDestCell); } - if (mLockLevel != -1) { + if (mLockLevel != -1 && !inInventory) esm.writeHNT("FLTV", mLockLevel); - } - esm.writeHNOCString("KNAM", mKey); - esm.writeHNOCString("TNAM", mTrap); - if (mReferenceBlocked != -1) { + if (!inInventory) + esm.writeHNOCString ("KNAM", mKey); + + if (!inInventory) + esm.writeHNOCString ("TNAM", mTrap); + + if (mReferenceBlocked != -1) esm.writeHNT("UNAM", mReferenceBlocked); - } - if (mFltv != 0) { - esm.writeHNT("FLTV", mFltv); - } - esm.writeHNT("DATA", mPos, 24); - if (mNam0 != 0) { + if (mFltv != 0 && !inInventory) + esm.writeHNT("FLTV", mFltv); + + if (!inInventory) + esm.writeHNT("DATA", mPos, 24); + + if (mNam0 != 0 && !inInventory) esm.writeHNT("NAM0", mNam0); - } } void ESM::CellRef::blank() diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 0cd6a36734..16f6603a28 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -89,7 +89,7 @@ namespace ESM void load (ESMReader& esm, bool wideRefNum = false); - void save(ESMWriter &esm, bool wideRefNum = false) const; + void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false) const; void blank(); }; diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 56289acae3..21585e09de 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -25,9 +25,9 @@ void ESM::ObjectState::load (ESMReader &esm) esm.getHNT (mLocalRotation, "LROT", 12); } -void ESM::ObjectState::save (ESMWriter &esm) const +void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const { - mRef.save (esm, true); + mRef.save (esm, true, inInventory); if (mHasLocals) { @@ -35,13 +35,15 @@ void ESM::ObjectState::save (ESMWriter &esm) const mLocals.save (esm); } - if (!mEnabled) + if (!mEnabled && !inInventory) esm.writeHNT ("ENAB", mEnabled); if (mCount!=1) esm.writeHNT ("COUN", mCount); - esm.writeHNT ("POS_", mPosition, 24); - - esm.writeHNT ("LROT", mLocalRotation, 12); + if (!inInventory) + { + esm.writeHNT ("POS_", mPosition, 24); + esm.writeHNT ("LROT", mLocalRotation, 12); + } } \ No newline at end of file diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index c599bb9734..34226ea91a 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -27,7 +27,7 @@ namespace ESM float mLocalRotation[3]; void load (ESMReader &esm); - void save (ESMWriter &esm) const; + void save (ESMWriter &esm, bool inInventory = false) const; }; } From e0537a3253022246d473bc179a3d9a6629562b14 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Jan 2014 13:53:24 +0100 Subject: [PATCH 114/120] made object state polymorphic --- components/esm/objectstate.cpp | 4 +++- components/esm/objectstate.hpp | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 21585e09de..6aa8205991 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -46,4 +46,6 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const esm.writeHNT ("POS_", mPosition, 24); esm.writeHNT ("LROT", mLocalRotation, 12); } -} \ No newline at end of file +} + +ESM::ObjectState::~ObjectState() {} \ No newline at end of file diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 34226ea91a..9c9ca5f2e8 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -26,8 +26,10 @@ namespace ESM ESM::Position mPosition; float mLocalRotation[3]; - void load (ESMReader &esm); - void save (ESMWriter &esm, bool inInventory = false) const; + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + + virtual ~ObjectState(); }; } From ec7cb90ca466f11bcb67ee34271af5f46f896e3c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 30 Jan 2014 11:50:13 +0100 Subject: [PATCH 115/120] added support for serialisation of CustomData state --- apps/openmw/mwworld/class.cpp | 4 ++++ apps/openmw/mwworld/class.hpp | 13 +++++++++++++ apps/openmw/mwworld/livecellref.cpp | 8 ++++++++ 3 files changed, 25 insertions(+) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 6c00b949cf..6e43b8dc21 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -367,4 +367,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 {} } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index ec22d03066..6e3a56907b 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -9,6 +9,11 @@ #include "ptr.hpp" +namespace ESM +{ + struct ObjectState; +} + namespace Ogre { class Vector3; @@ -299,6 +304,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. diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp index a12d20e6ab..d71704fd70 100644 --- a/apps/openmw/mwworld/livecellref.cpp +++ b/apps/openmw/mwworld/livecellref.cpp @@ -3,16 +3,24 @@ #include +#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 (this)); + mClass->writeAdditionalState (ptr, state); } bool MWWorld::LiveCellRefBase::checkStateImp (const ESM::ObjectState& state) From 900532a6ca2e5bb8394c1a96fb6f6d36792ef27b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 30 Jan 2014 12:37:33 +0100 Subject: [PATCH 116/120] store additional state of lights in saved game files --- apps/openmw/mwclass/light.cpp | 21 +++++++++++++++++++++ apps/openmw/mwclass/light.hpp | 8 ++++++++ apps/openmw/mwworld/cellstore.cpp | 5 +++-- components/CMakeLists.txt | 2 +- components/esm/lightstate.cpp | 21 +++++++++++++++++++++ components/esm/lightstate.hpp | 19 +++++++++++++++++++ 6 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 components/esm/lightstate.cpp create mode 100644 components/esm/lightstate.hpp diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index cc56ec4c8f..ddb2c16d66 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -2,6 +2,7 @@ #include "light.hpp" #include +#include #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 (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state2.mTime; + } + + void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const + { + ESM::LightState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + state2.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index c15228a6a0..5568e1727e 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -71,6 +71,14 @@ namespace MWClass virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; std::pair 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. }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 88c241e1ab..77fdc971d8 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -331,7 +332,7 @@ namespace MWWorld writeReferenceCollection (writer, mIngreds); writeReferenceCollection (writer, mCreatureLists); writeReferenceCollection (writer, mItemLists); - writeReferenceCollection (writer, mLights); + writeReferenceCollection (writer, mLights); writeReferenceCollection (writer, mLockpicks); writeReferenceCollection (writer, mMiscItems); writeReferenceCollection (writer, mNpcs); @@ -413,7 +414,7 @@ namespace MWWorld case ESM::REC_LIGH: - readReferenceCollection (reader, mLights, contentFileMap); + readReferenceCollection (reader, mLights, contentFileMap); break; case ESM::REC_LOCK: diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index d73bcaf74f..f37a537c57 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate ) add_component_dir (misc diff --git a/components/esm/lightstate.cpp b/components/esm/lightstate.cpp new file mode 100644 index 0000000000..1ef0408237 --- /dev/null +++ b/components/esm/lightstate.cpp @@ -0,0 +1,21 @@ + +#include "lightstate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::LightState::load (ESMReader &esm) +{ + ObjectState::load (esm); + + mTime = 0; + esm.getHNOT (mTime, "LTIM"); +} + +void ESM::LightState::save (ESMWriter &esm, bool inInventory) const +{ + ObjectState::save (esm, inInventory); + + if (mTime) + esm.writeHNT ("LTIM", mTime); +} \ No newline at end of file diff --git a/components/esm/lightstate.hpp b/components/esm/lightstate.hpp new file mode 100644 index 0000000000..a22735e079 --- /dev/null +++ b/components/esm/lightstate.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_ESM_LIGHTSTATE_H +#define OPENMW_ESM_LIGHTSTATE_H + +#include "objectstate.hpp" + +namespace ESM +{ + // format 0, saved games only + + struct LightState : public ObjectState + { + float mTime; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + }; +} + +#endif From dd674566a2aa85b4ed62dc4438777a97868e7ec8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 31 Jan 2014 13:25:32 +0100 Subject: [PATCH 117/120] store content of containers in saved game files --- apps/openmw/mwclass/container.cpp | 23 ++++++ apps/openmw/mwclass/container.hpp | 8 ++ apps/openmw/mwworld/cellstore.cpp | 5 +- apps/openmw/mwworld/containerstore.cpp | 100 +++++++++++++++++++++++++ apps/openmw/mwworld/containerstore.hpp | 9 ++- apps/openmw/mwworld/livecellref.hpp | 12 ++- apps/openmw/mwworld/ptr.cpp | 8 ++ apps/openmw/mwworld/ptr.hpp | 2 + components/CMakeLists.txt | 2 +- components/esm/containerstate.cpp | 16 ++++ components/esm/containerstate.hpp | 20 +++++ components/esm/inventorystate.cpp | 60 +++++++++++++++ components/esm/inventorystate.hpp | 28 +++++++ components/esm/objectstate.cpp | 4 +- 14 files changed, 288 insertions(+), 9 deletions(-) create mode 100644 components/esm/containerstate.cpp create mode 100644 components/esm/containerstate.hpp create mode 100644 components/esm/inventorystate.cpp create mode 100644 components/esm/inventorystate.hpp diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index f89a6bce0f..546d6538c9 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -2,6 +2,7 @@ #include "container.hpp" #include +#include #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 (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + readState (state2.mInventory); + } + + void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const + { + ESM::ContainerState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + writeState (state2.mInventory); + } } diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 006e4bd223..c97867d35a 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -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; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 77fdc971d8..154a2d1e7e 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -326,7 +327,7 @@ namespace MWWorld writeReferenceCollection (writer, mArmors); writeReferenceCollection (writer, mBooks); writeReferenceCollection (writer, mClothes); - writeReferenceCollection (writer, mContainers); + writeReferenceCollection (writer, mContainers); writeReferenceCollection (writer, mCreatures); writeReferenceCollection (writer, mDoors); writeReferenceCollection (writer, mIngreds); @@ -384,7 +385,7 @@ namespace MWWorld case ESM::REC_CONT: - readReferenceCollection (reader, mContainers, contentFileMap); + readReferenceCollection (reader, mContainers, contentFileMap); break; case ESM::REC_CREA: diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 0c4226f9bf..2d5a9bbd34 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -55,6 +57,43 @@ namespace return MWWorld::Ptr(); } + + template + void getState (MWWorld::CellRefList& collection, const ESM::ObjectState& state) + { + if (!MWWorld::LiveCellRef::checkState (state)) + return; // not valid anymore with current content files -> skip + + const T *record = MWBase::Environment::get().getWorld()->getStore(). + get().search (state.mRef.mRefID); + + if (!record) + return; + + MWWorld::LiveCellRef ref (record); + ref.load (state); + ref.mRef.mRefNum.mContentFile = -1; + collection.mList.push_back (ref); + } + + template + void storeState (const MWWorld::LiveCellRef& ref, ESM::ObjectState& state) + { + ref.save (state); + } + + template + void storeStates (const MWWorld::CellRefList& collection, + std::vector > >& states) + { + for (typename MWWorld::CellRefList::List::const_iterator iter (collection.mList.begin()); + iter!=collection.mList.end(); ++iter) + { + ESM::ObjectState state; + storeState (*iter, state); + states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, -1))); + } + } } const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; @@ -495,6 +534,67 @@ 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); + storeStates (books, state.mItems); + storeStates (clothes, state.mItems); + storeStates (ingreds, state.mItems); + storeStates (lockpicks, state.mItems); + storeStates (miscItems, state.mItems); + storeStates (probes, state.mItems); + storeStates (repairs, state.mItems); + storeStates (weapons, state.mItems); + + state.mLights.clear(); + + for (MWWorld::CellRefList::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, -1)); + } +} + +void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) +{ + clear(); + + for (std::vector > >::const_iterator + iter (state.mItems.begin()); iter!=state.mItems.end(); ++iter) + { + 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: getState (armors, iter->first); break; + case ESM::REC_BOOK: getState (books, iter->first); break; + case ESM::REC_CLOT: getState (clothes, iter->first); break; + case ESM::REC_INGR: getState (ingreds, iter->first); break; + case ESM::REC_LOCK: getState (lockpicks, iter->first); break; + case ESM::REC_MISC: getState (miscItems, iter->first); break; + case ESM::REC_PROB: getState (probes, iter->first); break; + case ESM::REC_REPA: getState (repairs, iter->first); break; + case ESM::REC_WEAP: getState (weapons, iter->first); break; + + default: + + std::cerr << "invalid item type in inventory state" << std::endl; + } + } + + for (std::vector >::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) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 0a17287408..3bdefb1ec2 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -8,6 +8,7 @@ namespace ESM { struct InventoryList; + struct InventoryState; } namespace MWWorld @@ -123,6 +124,10 @@ namespace MWWorld Ptr search (const std::string& id); + void writeState (ESM::InventoryState& state) const; + + void readState (const ESM::InventoryState& state); + friend class ContainerStoreIterator; }; @@ -172,7 +177,7 @@ namespace MWWorld ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - void copy (const ContainerStoreIterator& src); + void copy (const ContainerStoreIterator& src); void incType(); @@ -200,7 +205,7 @@ namespace MWWorld ContainerStoreIterator operator++ (int); - ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); + ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); bool isEqual (const ContainerStoreIterator& iter) const; diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index 46f49df78c..b2089fa7ae 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -35,6 +35,14 @@ namespace MWWorld /* 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); @@ -79,12 +87,12 @@ namespace MWWorld // The object that this instance is based on. const X* mBase; - void load (const ESM::ObjectState& state); + 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. - void save (ESM::ObjectState& state) const; + virtual void save (ESM::ObjectState& state) const; ///< Save LiveCellRef state into \a state. static bool checkState (const ESM::ObjectState& state); diff --git a/apps/openmw/mwworld/ptr.cpp b/apps/openmw/mwworld/ptr.cpp index 384bd71b11..67bfe49007 100644 --- a/apps/openmw/mwworld/ptr.cpp +++ b/apps/openmw/mwworld/ptr.cpp @@ -21,6 +21,14 @@ const std::string& MWWorld::Ptr::getTypeName() const throw std::runtime_error("Can't get type name from an empty object."); } +MWWorld::LiveCellRefBase *MWWorld::Ptr::getBase() const +{ + if (!mRef) + throw std::runtime_error ("Can't access cell ref pointed to by null Ptr"); + + return mRef; +} + ESM::CellRef& MWWorld::Ptr::getCellRef() const { assert(mRef); diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 8b70382d0a..1212619d03 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -52,6 +52,8 @@ namespace MWWorld throw std::runtime_error(str.str()); } + MWWorld::LiveCellRefBase *getBase() const; + ESM::CellRef& getCellRef() const; RefData& getRefData() const; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f37a537c57..854d1f1aea 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate ) add_component_dir (misc diff --git a/components/esm/containerstate.cpp b/components/esm/containerstate.cpp new file mode 100644 index 0000000000..5dcf17733e --- /dev/null +++ b/components/esm/containerstate.cpp @@ -0,0 +1,16 @@ + +#include "containerstate.hpp" + +void ESM::ContainerState::load (ESMReader &esm) +{ + ObjectState::load (esm); + + mInventory.load (esm); +} + +void ESM::ContainerState::save (ESMWriter &esm, bool inInventory) const +{ + ObjectState::save (esm, inInventory); + + mInventory.save (esm); +} \ No newline at end of file diff --git a/components/esm/containerstate.hpp b/components/esm/containerstate.hpp new file mode 100644 index 0000000000..1ecf2b46ea --- /dev/null +++ b/components/esm/containerstate.hpp @@ -0,0 +1,20 @@ +#ifndef OPENMW_ESM_CONTAINERSTATE_H +#define OPENMW_ESM_CONTAINERSTATE_H + +#include "objectstate.hpp" +#include "inventorystate.hpp" + +namespace ESM +{ + // format 0, saved games only + + struct ContainerState : public ObjectState + { + InventoryState mInventory; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + }; +} + +#endif diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp new file mode 100644 index 0000000000..4d8cbc6228 --- /dev/null +++ b/components/esm/inventorystate.cpp @@ -0,0 +1,60 @@ + +#include "inventorystate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace +{ + void read (ESM::ESMReader &esm, ESM::ObjectState& state, int& slot) + { + slot = -1; + esm.getHNOT (slot, "SLOT"); + + state.load (esm); + } + + void write (ESM::ESMWriter &esm, const ESM::ObjectState& state, unsigned int type, int slot) + { + esm.writeHNT ("IOBJ", type); + + if (slot!=-1) + esm.writeHNT ("SLOT", slot); + + state.save (esm, true); + } +} + +void ESM::InventoryState::load (ESMReader &esm) +{ + while (esm.isNextSub ("IOBJ")) + { + unsigned int id = 0; + esm.getHT (id); + + if (id==ESM::REC_LIGH) + { + LightState state; + int slot; + read (esm, state, slot); + mLights.push_back (std::make_pair (state, slot)); + } + else + { + ObjectState state; + int slot; + read (esm, state, slot); + mItems.push_back (std::make_pair (state, std::make_pair (id, slot))); + } + } +} + +void ESM::InventoryState::save (ESMWriter &esm) const +{ + for (std::vector > >::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter) + write (esm, iter->first, iter->second.first, iter->second.second); + + for (std::vector >::const_iterator iter (mLights.begin()); + iter!=mLights.end(); ++iter) + write (esm, iter->first, ESM::REC_LIGH, iter->second); +} \ No newline at end of file diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp new file mode 100644 index 0000000000..3cfffbccc9 --- /dev/null +++ b/components/esm/inventorystate.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_ESM_INVENTORYSTATE_H +#define OPENMW_ESM_INVENTORYSTATE_H + +#include "objectstate.hpp" +#include "lightstate.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + /// \brief State for inventories and containers + struct InventoryState + { + // anything but lights (type, slot) + std::vector > > mItems; + + // lights (slot) + std::vector > mLights; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm) const; + }; +} + +#endif diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 6aa8205991..be00f3ef6e 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -20,9 +20,9 @@ void ESM::ObjectState::load (ESMReader &esm) mCount = 1; esm.getHNOT (mCount, "COUN"); - esm.getHNT (mPosition, "POS_", 24); + esm.getHNOT (mPosition, "POS_", 24); - esm.getHNT (mLocalRotation, "LROT", 12); + esm.getHNOT (mLocalRotation, "LROT", 12); } void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const From bcc5894e2d820b7497f2381ddef8af7da61cf836 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 1 Feb 2014 15:24:01 +0100 Subject: [PATCH 118/120] changed implementation functions for container serialisation from free functions to member functions (will need some polymorphism later) --- apps/openmw/mwworld/containerstore.cpp | 69 +++++++++++++------------- apps/openmw/mwworld/containerstore.hpp | 11 ++++ 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 2d5a9bbd34..7d12c7b6e6 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -57,42 +57,43 @@ namespace return MWWorld::Ptr(); } +} - template - void getState (MWWorld::CellRefList& collection, const ESM::ObjectState& state) +template +void MWWorld::ContainerStore::getState (CellRefList& collection, const ESM::ObjectState& state) +{ + if (!LiveCellRef::checkState (state)) + return; // not valid anymore with current content files -> skip + + const T *record = MWBase::Environment::get().getWorld()->getStore(). + get().search (state.mRef.mRefID); + + if (!record) + return; + + LiveCellRef ref (record); + ref.load (state); + ref.mRef.mRefNum.mContentFile = -1; + collection.mList.push_back (ref); +} + +template +void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::ObjectState& state) + const +{ + ref.save (state); +} + +template +void MWWorld::ContainerStore::storeStates (const CellRefList& collection, + std::vector > >& states) const +{ + for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); + iter!=collection.mList.end(); ++iter) { - if (!MWWorld::LiveCellRef::checkState (state)) - return; // not valid anymore with current content files -> skip - - const T *record = MWBase::Environment::get().getWorld()->getStore(). - get().search (state.mRef.mRefID); - - if (!record) - return; - - MWWorld::LiveCellRef ref (record); - ref.load (state); - ref.mRef.mRefNum.mContentFile = -1; - collection.mList.push_back (ref); - } - - template - void storeState (const MWWorld::LiveCellRef& ref, ESM::ObjectState& state) - { - ref.save (state); - } - - template - void storeStates (const MWWorld::CellRefList& collection, - std::vector > >& states) - { - for (typename MWWorld::CellRefList::List::const_iterator iter (collection.mList.begin()); - iter!=collection.mList.end(); ++iter) - { - ESM::ObjectState state; - storeState (*iter, state); - states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, -1))); - } + ESM::ObjectState state; + storeState (*iter, state); + states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, -1))); } } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 3bdefb1ec2..5e305d4085 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -57,6 +57,17 @@ 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 + void getState (CellRefList& collection, const ESM::ObjectState& state); + + template + void storeState (const LiveCellRef& ref, ESM::ObjectState& state) const; + + template + void storeStates (const CellRefList& collection, + std::vector > >& states) + const; + public: ContainerStore(); From d2ec3ffdc89f55eb496eaf4623ad04a879637412 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 1 Feb 2014 17:07:08 +0100 Subject: [PATCH 119/120] handle equipped items when serialising inventory state --- apps/openmw/mwworld/containerstore.cpp | 48 ++++++++++++++++---------- apps/openmw/mwworld/containerstore.hpp | 13 +++++-- apps/openmw/mwworld/inventorystore.cpp | 22 ++++++++++++ apps/openmw/mwworld/inventorystore.hpp | 9 +++++ 4 files changed, 71 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 7d12c7b6e6..86cf3bbdeb 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -60,43 +60,53 @@ namespace } template -void MWWorld::ContainerStore::getState (CellRefList& collection, const ESM::ObjectState& state) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState (CellRefList& collection, + const ESM::ObjectState& state) { if (!LiveCellRef::checkState (state)) - return; // not valid anymore with current content files -> skip + return ContainerStoreIterator (this); // not valid anymore with current content files -> skip const T *record = MWBase::Environment::get().getWorld()->getStore(). get().search (state.mRef.mRefID); if (!record) - return; + return ContainerStoreIterator (this); LiveCellRef ref (record); ref.load (state); ref.mRef.mRefNum.mContentFile = -1; collection.mList.push_back (ref); + + return ContainerStoreIterator (this, --collection.mList.end()); } template -void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::ObjectState& state) - const +void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::ObjectState& state) const { ref.save (state); } template void MWWorld::ContainerStore::storeStates (const CellRefList& collection, - std::vector > >& states) const + std::vector > >& states, bool equipable) const { for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { ESM::ObjectState state; storeState (*iter, state); - states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, -1))); + 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) {} @@ -541,15 +551,15 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const storeStates (potions, state.mItems); storeStates (appas, state.mItems); - storeStates (armors, state.mItems); + storeStates (armors, state.mItems, true); storeStates (books, state.mItems); - storeStates (clothes, state.mItems); + storeStates (clothes, state.mItems, true); storeStates (ingreds, state.mItems); - storeStates (lockpicks, state.mItems); + storeStates (lockpicks, state.mItems, true); storeStates (miscItems, state.mItems); - storeStates (probes, state.mItems); + storeStates (probes, state.mItems, true); storeStates (repairs, state.mItems); - storeStates (weapons, state.mItems); + storeStates (weapons, state.mItems, true); state.mLights.clear(); @@ -558,7 +568,7 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const { ESM::LightState objectState; storeState (*iter, objectState); - state.mLights.push_back (std::make_pair (objectState, -1)); + state.mLights.push_back (std::make_pair (objectState, getSlot (*iter))); } } @@ -569,19 +579,21 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) for (std::vector > >::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: getState (armors, 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: getState (clothes, 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: getState (lockpicks, 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: getState (probes, 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: getState (weapons, iter->first); break; + case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break; default: diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 5e305d4085..2e01eb8565 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -58,15 +58,22 @@ namespace MWWorld void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, bool topLevel=true); template - void getState (CellRefList& collection, const ESM::ObjectState& state); + ContainerStoreIterator getState (CellRefList& collection, + const ESM::ObjectState& state); template void storeState (const LiveCellRef& ref, ESM::ObjectState& state) const; template void storeStates (const CellRefList& collection, - std::vector > >& states) - const; + std::vector > >& 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: diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 82b827e755..93573b401a 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -42,6 +42,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 (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 Date: Sat, 1 Feb 2014 17:36:23 +0100 Subject: [PATCH 120/120] added creature/NPC state to saved games (only container/inventory for now) --- apps/openmw/mwclass/creature.cpp | 23 ++++++++++++++++++++++ apps/openmw/mwclass/creature.hpp | 10 +++++++++- apps/openmw/mwclass/npc.cpp | 27 ++++++++++++++++++++++++-- apps/openmw/mwclass/npc.hpp | 10 +++++++++- apps/openmw/mwworld/cellstore.cpp | 10 ++++++---- apps/openmw/mwworld/containerstore.hpp | 2 +- components/CMakeLists.txt | 2 +- components/esm/creaturestate.cpp | 16 +++++++++++++++ components/esm/creaturestate.hpp | 20 +++++++++++++++++++ components/esm/npcstate.cpp | 16 +++++++++++++++ components/esm/npcstate.hpp | 20 +++++++++++++++++++ components/esm/player.hpp | 4 ++-- 12 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 components/esm/creaturestate.cpp create mode 100644 components/esm/creaturestate.hpp create mode 100644 components/esm/npcstate.cpp create mode 100644 components/esm/npcstate.hpp diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a972683181..1174f1bd2a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -2,6 +2,7 @@ #include "creature.hpp" #include +#include #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/magiceffects.hpp" @@ -613,6 +614,28 @@ namespace MWClass return 0; } + void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const + { + const ESM::CreatureState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + readState (state2.mInventory); + } + + void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const + { + ESM::CreatureState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + writeState (state2.mInventory); + } + const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting *Creature::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index d518d0056b..484afdaf53 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -84,7 +84,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; @@ -118,6 +118,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. }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f93a3e342d..7c0f0b6ead 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -514,7 +515,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); @@ -964,7 +965,7 @@ namespace MWClass return ref->mBase->mFlags & ESM::NPC::Essential; } - + void Npc::registerSelf() { boost::shared_ptr instance (new Npc); @@ -1233,6 +1234,28 @@ namespace MWClass return 0; } + void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const + { + const ESM::NpcState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore. + readState (state2.mInventory); + } + + void Npc::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const + { + ESM::NpcState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore. + writeState (state2.mInventory); + } + const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 497d0ced85..237746de8e 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -131,7 +131,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; @@ -152,6 +152,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. }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 154a2d1e7e..42c954afb3 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -328,7 +330,7 @@ namespace MWWorld writeReferenceCollection (writer, mBooks); writeReferenceCollection (writer, mClothes); writeReferenceCollection (writer, mContainers); - writeReferenceCollection (writer, mCreatures); + writeReferenceCollection (writer, mCreatures); writeReferenceCollection (writer, mDoors); writeReferenceCollection (writer, mIngreds); writeReferenceCollection (writer, mCreatureLists); @@ -336,7 +338,7 @@ namespace MWWorld writeReferenceCollection (writer, mLights); writeReferenceCollection (writer, mLockpicks); writeReferenceCollection (writer, mMiscItems); - writeReferenceCollection (writer, mNpcs); + writeReferenceCollection (writer, mNpcs); writeReferenceCollection (writer, mProbes); writeReferenceCollection (writer, mRepairs); writeReferenceCollection (writer, mStatics); @@ -390,7 +392,7 @@ namespace MWWorld case ESM::REC_CREA: - readReferenceCollection (reader, mCreatures, contentFileMap); + readReferenceCollection (reader, mCreatures, contentFileMap); break; case ESM::REC_DOOR: @@ -430,7 +432,7 @@ namespace MWWorld case ESM::REC_NPC_: - readReferenceCollection (reader, mNpcs, contentFileMap); + readReferenceCollection (reader, mNpcs, contentFileMap); break; case ESM::REC_PROB: diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 2e01eb8565..68bad4b9bb 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -130,7 +130,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; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 854d1f1aea..f2b16d4d53 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate ) add_component_dir (misc diff --git a/components/esm/creaturestate.cpp b/components/esm/creaturestate.cpp new file mode 100644 index 0000000000..43cde30251 --- /dev/null +++ b/components/esm/creaturestate.cpp @@ -0,0 +1,16 @@ + +#include "creaturestate.hpp" + +void ESM::CreatureState::load (ESMReader &esm) +{ + ObjectState::load (esm); + + mInventory.load (esm); +} + +void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const +{ + ObjectState::save (esm, inInventory); + + mInventory.save (esm); +} \ No newline at end of file diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp new file mode 100644 index 0000000000..f7f9b80380 --- /dev/null +++ b/components/esm/creaturestate.hpp @@ -0,0 +1,20 @@ +#ifndef OPENMW_ESM_CREATURESTATE_H +#define OPENMW_ESM_CREATURESTATE_H + +#include "objectstate.hpp" +#include "inventorystate.hpp" + +namespace ESM +{ + // format 0, saved games only + + struct CreatureState : public ObjectState + { + InventoryState mInventory; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + }; +} + +#endif diff --git a/components/esm/npcstate.cpp b/components/esm/npcstate.cpp new file mode 100644 index 0000000000..c452611a0a --- /dev/null +++ b/components/esm/npcstate.cpp @@ -0,0 +1,16 @@ + +#include "npcstate.hpp" + +void ESM::NpcState::load (ESMReader &esm) +{ + ObjectState::load (esm); + + mInventory.load (esm); +} + +void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const +{ + ObjectState::save (esm, inInventory); + + mInventory.save (esm); +} \ No newline at end of file diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp new file mode 100644 index 0000000000..ceb18b88b6 --- /dev/null +++ b/components/esm/npcstate.hpp @@ -0,0 +1,20 @@ +#ifndef OPENMW_ESM_NPCSTATE_H +#define OPENMW_ESM_NPCSTATE_H + +#include "objectstate.hpp" +#include "inventorystate.hpp" + +namespace ESM +{ + // format 0, saved games only + + struct NpcState : public ObjectState + { + InventoryState mInventory; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + }; +} + +#endif diff --git a/components/esm/player.hpp b/components/esm/player.hpp index bd618457e1..0d70ee0904 100644 --- a/components/esm/player.hpp +++ b/components/esm/player.hpp @@ -3,7 +3,7 @@ #include -#include "objectstate.hpp" +#include "npcstate.hpp" #include "cellid.hpp" #include "defs.hpp" @@ -16,7 +16,7 @@ namespace ESM struct Player { - ObjectState mObject; + NpcState mObject; CellId mCellId; float mLastKnownExteriorPosition[3]; unsigned char mHasMark;