From eddd6bf48dca2986c592342dfefa8b7627d0a998 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 15 Nov 2013 10:29:53 +0100 Subject: [PATCH 001/301] 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 fa7ed2ace..95753f540 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/301] 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 04bd89f95..0358b96d1 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 4a3c418f6..f900e6cb4 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 6b309025c..4a629743f 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 466302907..d7c63601f 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 000000000..42b1dcc81 --- /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 000000000..fa8289419 --- /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 000000000..73cb0a86f --- /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/301] 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 f900e6cb4..d11e72f75 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 4a629743f..052bba9ab 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 d7c63601f..eb636ea2f 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 42b1dcc81..e90687293 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 95753f540..a58a3d0eb 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 35487e339..c25e9ce52 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 fa8289419..f5e71d0ce 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 73cb0a86f..271403ce5 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/301] 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 d11e72f75..47cd01a80 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 553d29068..72d2041b8 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 33f740b31..8c6bdf8a6 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/301] 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 47cd01a80..8a02c855a 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 72d2041b8..02fb73705 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 8c6bdf8a6..2bdfb91c3 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/301] 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 8a02c855a..cfb5522f1 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 2bdfb91c3..89613fda4 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/301] 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 cfb5522f1..c013cdaae 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 e90687293..076e66b8c 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 a58a3d0eb..cb3436715 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 f5e71d0ce..a226b166d 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 271403ce5..9f8096a4a 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/301] 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 c013cdaae..741f7564f 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(); - - bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); + if (MWBase::Environment::get().getStateManager()->isGameRunning()) + { + MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); - // 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. + bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); - // passing of time - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->advanceTime( - frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); + // 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 happend 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 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/301] 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 741f7564f..4de198b64 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 076e66b8c..4fd1a297d 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 a226b166d..c4cd45c8e 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 9f8096a4a..078a899b2 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/301] 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 4fd1a297d..74fcc3f7a 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 3d52ce8e6..cc431a7b0 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 c4cd45c8e..66a44872c 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 078a899b2..007a9b136 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/301] adjust availability of main menu items based on game state --- apps/openmw/mwgui/mainmenu.cpp | 100 +++++++++++++++++++++------------ apps/openmw/mwgui/mainmenu.hpp | 23 +++++--- 2 files changed, 79 insertions(+), 44 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index cb3436715..d4a4e74ba 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -15,14 +15,61 @@ 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(); + } + + void MainMenu::setVisible (bool visible) + { + if (visible) + updateMenu(); + + OEngine::GUI::Layout::setVisible (visible); + } + + void MainMenu::onButtonClicked(MyGUI::Widget *sender) + { + MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); + if (sender == mButtons["return"]) + { + MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx); + MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); + } + else if (sender == mButtons["options"]) + MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); + else if (sender == mButtons["exitgame"]) + MWBase::Environment::get().getStateManager()->requestQuit(); + else if (sender == mButtons["newgame"]) + { + MWBase::Environment::get().getStateManager()->newGame(); + } + + else if (sender == mButtons["loadgame"]) + { + MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); + dialog->setLoadOrSave(true); + dialog->setVisible(true); + } + else if (sender == mButtons["savegame"]) + { + MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); + dialog->setLoadOrSave(false); + dialog->setVisible(true); + } + } + + void MainMenu::updateMenu() + { + setCoord(0,0, mWidth, mHeight); if (mButtonBox) @@ -31,11 +78,21 @@ namespace MWGui 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; - buttons.push_back("return"); + + 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"); - buttons.push_back("savegame"); + + if (state==MWBase::StateManager::State_Running) + buttons.push_back("savegame"); + buttons.push_back("options"); //buttons.push_back("credits"); buttons.push_back("exitgame"); @@ -64,38 +121,7 @@ namespace MWGui 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); - } - - void MainMenu::onButtonClicked(MyGUI::Widget *sender) - { - MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); - if (sender == mButtons["return"]) - { - MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx); - MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); - } - else if (sender == mButtons["options"]) - MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); - else if (sender == mButtons["exitgame"]) - MWBase::Environment::get().getStateManager()->requestQuit(); - else if (sender == mButtons["newgame"]) - { - MWBase::Environment::get().getStateManager()->newGame(); - } + mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH); - else if (sender == mButtons["loadgame"]) - { - MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); - dialog->setLoadOrSave(true); - dialog->setVisible(true); - } - else if (sender == mButtons["savegame"]) - { - MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); - dialog->setLoadOrSave(false); - dialog->setVisible(true); - } } - } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 4e76a64df..511f72672 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/301] 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 59fb084a8..ce5965be1 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 dd7ebfe93..5a5ef9f1c 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 000000000..a717c6469 --- /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 000000000..ae8f79263 --- /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/301] 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 87a8d1d57..e5d6ec837 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/301] 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 51d86a2ee..f02ed2d6e 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 f39aa2b89..c9ef61b63 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/301] 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 a717c6469..5a5fc9fa8 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/301] 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 0358b96d1..2b078a7ff 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 4de198b64..cda068347 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 74fcc3f7a..b341fbb03 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 d4a4e74ba..f25b72d37 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 000000000..dc16085d8 --- /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 000000000..30182e404 --- /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 000000000..c73adb5bb --- /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 000000000..a3fe17131 --- /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 66a44872c..f068093a0 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 007a9b136..9dfcc4d8f 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/301] 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 f068093a0..4b1d9e0f6 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 c9ef61b63..069d75c7b 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 104f97f90..d6646471b 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/301] 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 ae8f79263..c0e6f1aeb 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/301] 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 b341fbb03..3aa464ebe 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 c73adb5bb..6eccb63dc 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 a3fe17131..543e0c94a 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 4b1d9e0f6..8f22408fe 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 9dfcc4d8f..8d1ff0641 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/301] 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 8f22408fe..0f4e1e020 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 c0e6f1aeb..e712e8f1f 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/301] 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 3aa464ebe..ce8094632 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 dc16085d8..660af7d89 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 30182e404..a6cb6fa6d 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 6eccb63dc..6b67d0d04 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 543e0c94a..ce82ccc46 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 0f4e1e020..9d8fcafb0 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 8d1ff0641..764a09a7e 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/301] 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 660af7d89..54d2b9dd8 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 a6cb6fa6d..676c04680 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/301] 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 ce8094632..3369fd3bc 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 f25b72d37..ac272d1c4 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 54d2b9dd8..7185ce89d 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 6b67d0d04..c632f61e4 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 ce82ccc46..9995393aa 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 9d8fcafb0..12bd29596 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 764a09a7e..d387404cb 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/301] 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 3369fd3bc..3bd99c315 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 d387404cb..08b0776c1 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/301] 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 676c04680..d678c165b 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/301] 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 ac272d1c4..962b5dd31 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/301] 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 3bd99c315..e9d10a796 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 962b5dd31..ff8ab8c93 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") - { - // 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") + else { - // 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 511f72672..6d52f26d5 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 a1acd3588..648dd4683 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 1a3178ef3..2a188061c 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 12bd29596..6aba93dc8 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 18de6a239..ceb1a8428 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/301] 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 c632f61e4..2b9c49fcc 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/301] 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 e9d10a796..7101be25d 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 648dd4683..2ea83cfa3 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 08b0776c1..a380c9fee 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/301] 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 7101be25d..f1f130824 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 2ea83cfa3..38574e2fd 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 6aba93dc8..9d8cf2a81 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 a380c9fee..abf7e123b 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 5a5fc9fa8..8169b01a1 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 e712e8f1f..6c11d318f 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/301] 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 f1f130824..f376d72c1 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 38574e2fd..2ce0f0fbf 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 2b9c49fcc..cb64da2c2 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 9995393aa..a02927d58 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 9d8cf2a81..9a7e3d158 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 abf7e123b..8bf1fab20 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/301] 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 7185ce89d..2bd2af139 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 9a7e3d158..46239f5a2 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/301] 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 8169b01a1..a37043da6 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/301] 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 cda068347..bee7fa8fd 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 e1adfbec4..fe7d8f7ac 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 2bd2af139..0766753ca 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 d678c165b..61e4e5b25 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 cb64da2c2..2a40fb1cc 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 a02927d58..bc2e23f89 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 46239f5a2..4cb97ed50 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 8bf1fab20..3965632cc 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 eca2ebb79..5a4192529 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 19890b831..32ad84865 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/301] 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 0766753ca..304eaddd3 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/301] 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 a37043da6..e98608fe6 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/301] 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 2ce0f0fbf..8ec6dbfcf 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 4cb97ed50..97f8b8e55 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 f191430df..b3b186142 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/301] 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 fe7d8f7ac..37d3c10da 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 b8fc9ed47..5639ea208 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 5a4192529..7d2da389c 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 32ad84865..33f6f1c2f 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/301] 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 83325de23..3524c6d70 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 97f8b8e55..8faab1609 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/301] 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 37d3c10da..dd9e20de1 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 79a77070a..89885d303 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::ostringstream os; + std::string dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}"); - os << itr->mDayOfMonth << ' '; - - injectMonthName (os, itr->mMonth); + std::ostringstream os; - const std::string& dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}"); - os << " (" << dayStr << " " << (itr->mDay + 1) << ')'; + os + << itr->mDayOfMonth << ' ' + << MWBase::Environment::get().getWorld()->getMonthName (itr->mMonth) + << " (" << dayStr << " " << (itr->mDay + 1) << ')'; timestamp_buffer = os.str (); } @@ -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 8ec6dbfcf..5f9b6a3c1 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 2467f6c40..071bb8804 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 7d2da389c..3d64585f9 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 33f6f1c2f..08a3182e9 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/301] 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 dd9e20de1..41cacd365 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 11dccde42..c6089c9e4 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 5ffde5499..dd1ad3f66 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 3524c6d70..2be88b7f0 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 5639ea208..bbfa77e18 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 35f7a4044..c9d27b09a 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 3d64585f9..afa434cd2 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 08a3182e9..eda79b433 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/301] 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 bbfa77e18..a977d3440 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 afa434cd2..0767718b8 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/301] 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 41cacd365..87a9b5bbc 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 8faab1609..60b19fd46 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 0767718b8..1e7b8122f 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 eda79b433..2b7d157ff 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/301] 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 bee7fa8fd..7dd3214eb 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 f376d72c1..8548a74f7 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 60b19fd46..a273aced7 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 3965632cc..720b1b6b0 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/301] 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 a273aced7..b750f56fe 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 720b1b6b0..78b578766 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 1e7b8122f..d7fdc3bc1 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/301] 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 ede200d79..bc16c65d3 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/301] 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 e98608fe6..7c76f4000 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/301] 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 5f9b6a3c1..d1e047c09 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/301] 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 dd1ad3f66..20963eb79 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 9d009b48b..ab4adece9 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/301] 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 3afa81fac..566eef6bf 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/301] 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 5e2739be1..75dcaa028 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 566eef6bf..c6f0d0bec 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 3253b20d6..fc8545b5e 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 c3f0baabc..17601977a 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 89885d303..e35d35013 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/301] 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 e35d35013..049515ac0 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/301] 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 ce5965be1..3223ab1a7 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 5a5ef9f1c..03091d9d8 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 000000000..514bf3597 --- /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 000000000..94808dde6 --- /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 000000000..5931e8b90 --- /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 000000000..1769336f2 --- /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/301] 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 8e9bcfc0d..d7df2117d 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 ca5586511..b8eb0a3b3 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 9595ab23b..761c7feaa 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 069d75c7b..f38591b7b 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 d6646471b..94f0a1004 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/301] 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 20963eb79..b83d19f8c 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 ab4adece9..b51c2d2d4 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 049515ac0..7ee24a288 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/301] 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 75dcaa028..12763effd 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 fc8545b5e..f40e585dc 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 17601977a..ffc5e9470 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 7ee24a288..ad0af3f05 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/301] 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 5931e8b90..e93826725 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/301] 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 | 38 ++++++- 10 files changed, 276 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index 51e51edda..81b4ba0b4 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 b83d19f8c..7828d18ad 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 b51c2d2d4..18d022aab 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 23cfb5fdd..b9359aae6 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 f4f8eb1c2..54c49df01 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 12763effd..14c5c1da9 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 c6f0d0bec..40824f398 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 f40e585dc..0e546f43b 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 ffc5e9470..02fa6d524 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 b750f56fe..5bc313f37 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.setRecordCount ( + 1+ // saved game header + MWBase::Environment::get().getJournal()->countSavedGameRecords()); + writer.save (stream); - writer.startRecord ("SAVE"); + + writer.startRecord (ESM::REC_SAVE); slot->mProfile.save (writer); - writer.endRecord ("SAVE"); + 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(); + + switch (n.val) + { + case ESM::REC_SAVE: + + // don't need to read that here + reader.skipRecord(); + break; - /// \todo read saved game data + 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/301] 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 7c76f4000..55b17289c 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/301] 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 b9359aae6..f24a93356 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 54c49df01..86091a12d 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/301] 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 d1e047c09..04461fef9 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/301] 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 87a9b5bbc..837b4c23e 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 5bc313f37..09e574e84 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 c1a3ae785..87d0efe19 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 d5701efc5..07841e470 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 d7fdc3bc1..509aee983 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 2b7d157ff..1cee7d50f 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/301] 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 837b4c23e..36c970839 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 86a85be18..9ba7154c2 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 2be88b7f0..887bf2c68 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 cc431a7b0..66d8de6f8 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 694987855..8a1e6ee6b 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 1bdec6e19..5eecfaf35 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 b318c2d56..2b61e109b 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 5f4128978..f70533d22 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 fd81baf6e..852c25044 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 57e00d76c..cd309df47 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 316f912da..ce39cfa65 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 372be8393..c1c891ab4 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 37c4b6a3f..2cb22cf59 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 5ec5ca9b5..997e9e32c 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 e5352da28..8b70382d0 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 254ad98cf..348f01dc5 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 e3edad352..b7bb944ed 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 509aee983..b2d18a27f 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 1cee7d50f..b8a61c471 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/301] 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 c25197319..233f2f702 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/301] 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 b2d18a27f..539c959b3 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/301] 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 09e574e84..9df1e2dc0 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/301] 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 ff8ab8c93..da1992474 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 9df1e2dc0..293a9e232 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/301] 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 ebb086cee..d5da5a866 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/301] 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 f02ed2d6e..4e1860bab 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/301] 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 3ab234de1..eeeab77e8 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 7d1678a11..717012f72 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 1316baaeb..b6a7a7b75 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 ec03b457b..ebc879d26 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 293a9e232..94219b8fc 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 f1bff11a2..cb4e441fc 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 d5da5a866..e6730c307 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 233f2f702..df957408d 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 82b2301db..bfcd0ae1c 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 b09b42e94..40245b78d 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/301] 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 bfcd0ae1c..c4615c099 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/301] rewrote global variable storage (using ESM variant type now) --- apps/openmw/mwworld/globals.cpp | 128 ++++++------------------------- apps/openmw/mwworld/globals.hpp | 49 ++++-------- apps/openmw/mwworld/worldimp.cpp | 75 +++++++++--------- apps/openmw/mwworld/worldimp.hpp | 3 +- 4 files changed, 78 insertions(+), 177 deletions(-) diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index a905f8aae..19bfa1529 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; + + for (Collection::const_iterator iter = mVariables.begin(); iter!=mVariables.end(); ++iter) + ids.push_back (iter->first); - return retval; + 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) - { - const MWWorld::Store &globals = store.get(); - MWWorld::Store::iterator iter = globals.begin(); - for (; iter != globals.end(); ++iter) - { - char type = ' '; - Data value; - - switch (iter->mValue.getType()) - { - case ESM::VT_Short: - - type = 's'; - value.mShort = iter->mValue.getInteger(); - break; - - 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))); - } - } - - const Globals::Data& Globals::operator[] (const std::string& name) const + void Globals::fill (const MWWorld::ESMStore& store) { - Collection::const_iterator iter = find (name); + mVariables.clear(); - return iter->second.second; - } + const MWWorld::Store& globals = store.get(); - Globals::Data& 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) + for (MWWorld::Store::iterator iter = globals.begin(); iter!=globals.end(); + ++iter) { - 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"); + mVariables.insert (std::make_pair (iter->mId, iter->mValue)); } } - void Globals::setFloat (const std::string& name, float value) + const ESM::Variant& Globals::operator[] (const std::string& name) const { - Collection::iterator iter = find (name); - - switch (iter->second.first) - { - case 's': iter->second.second.mShort = value; break; - case 'l': iter->second.second.mLong = value; break; - case 'f': iter->second.second.mFloat = value; break; - - default: throw std::runtime_error ("unsupported global variable type"); - } + return find (name)->second; } - int Globals::getInt (const std::string& name) const + ESM::Variant& Globals::operator[] (const std::string& name) { - 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 681bd560e..587dd2092 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. - + + 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 c4615c099..7e9647167 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 40245b78d..92c99733e 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/301] 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 717012f72..740114aff 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 a977d3440..aedaec208 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 d7b147970..8ca97d288 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 19bfa1529..d8e96ddc3 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 587dd2092..ad140b0c1 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 7e9647167..c4e63fad0 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 92c99733e..845668449 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/301] 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 94219b8fc..a8f5631a0 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 d8e96ddc3..879ffa8e3 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 ad140b0c1..8f521c8a6 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 c4e63fad0..92091097c 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/301] 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 32df2bfa3..ae146e064 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 608725ae6..d55ad0063 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 628919d1d..67b619d1a 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 14fe5b7fd..be9082eb6 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 7bb98ffbd..1a856e0c5 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 a8f5631a0..e1086f121 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 92091097c..c10556fa9 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/301] 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 d55ad0063..1400d6675 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/301] 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 1400d6675..8f269a015 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 67b619d1a..cf716c8e4 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 180a2791b..094fe085a 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 deae0d44e..d17d1be2d 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 e1086f121..93650fc44 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 3223ab1a7..6f01216f1 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 d93e73849..e2b1c5c96 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 e54b7798c..d5bf05253 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 03091d9d8..4cf0b1dac 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 000000000..dcbd91140 --- /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 000000000..4fb8b7c48 --- /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 000000000..9c470a025 --- /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 000000000..af5afb23b --- /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 2bba60a15..8ba9bb34f 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/301] 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 30460c17a..de679e815 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/301] 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 740114aff..ea8619b3a 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 850a62bec..47fb979d7 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 19c2dac0c..c7ab0322d 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; - - iter->second->kill(); - - ++mDeathCount[cls.getId(iter->first)]; + if (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 68f87ef4c..7c7f40e97 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 0b55534a6..5e7a7c6d4 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 2eb2b1523..8aca3b489 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 2d0813912..5bbc3055d 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 c4e63fad0..2676be4b7 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 845668449..7c24adfa8 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/301] 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 cb4e441fc..62b91c2e4 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 4e1860bab..ebdb1e41f 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 3bf194c4e..897c8fe73 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/301] reworked http://bugs.openmw.org/issues/428 --- apps/openmw/engine.cpp | 48 +++++++++++++------------ 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, 46 insertions(+), 49 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 8fc36a5d5..97c2844eb 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 - if (MWBase::Environment::get().getStateManager()->getState()== - MWBase::StateManager::State_Running) - { - MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); - - bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); - // 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. + bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); - if (changed) // keep change flag for another frame, if cell changed happened in local script - MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + // 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 (!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(); - // update actors - MWBase::Environment::get().getMechanicsManager()->update(frametime, - MWBase::Environment::get().getWindowManager()->isGuiMode()); + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWorld()->advanceTime( + frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); - // update world - MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); + // update actors + MWBase::Environment::get().getMechanicsManager()->update(frametime, + MWBase::Environment::get().getWindowManager()->isGuiMode()); - // update game state - MWBase::Environment::get().getStateManager()->update (frametime); + 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()) + 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 ea8619b3a..f7041a47b 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 47fb979d7..e6e349c4d 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 c7ab0322d..7180eb883 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 7c7f40e97..9a19ac7fa 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 94219b8fc..bad4de03d 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 2676be4b7..357101e45 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 7c24adfa8..fb9c4cb68 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/301] 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 97c2844eb..5b8872b0f 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/301] 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 27980096e..3bbdaef35 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 0c145ab60..06ae083ce 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 415351e78..558639a3b 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 1cdcd8484..f138e3732 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 512883f1a..9ba2d8133 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 e91059b26..23a95a4ab 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 47cb0b99e..ef2523869 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 c22c1b22b..ba4195370 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -9,20 +9,42 @@ #include "esmwriter.hpp" #include "defs.hpp" -namespace ESM +namespace { - unsigned int Cell::sRecordId = REC_CELL; + ///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum + void adjustRefNum (ESM::CellRef::RefNum& refNum, ESM::ESMReader& reader) + { + int local = (refNum.mIndex & 0xff000000) >> 24; -/// Some overloaded compare operators. -bool operator==(const MovedCellRef& ref, int pRefnum) -{ - return (ref.mRefnum == pRefnum); + if (local) + { + // If the most significant 8 bits are used, then this reference already exists. + // In this case, do not spawn a new reference, but overwrite the old one. + refNum.mIndex &= 0x00ffffff; // delete old plugin ID + refNum.mContentFile = reader.getGameFiles()[local-1].index; + } + else + { + // This is an addition by the present plugin. Set the corresponding plugin index. + refNum.mContentFile = reader.getIndex(); + } + } } -bool operator==(const CellRef& ref, int pRefnum) +namespace ESM { - return (ref.mRefnum == pRefnum); -} + unsigned int Cell::sRecordId = REC_CELL; + + // Some overloaded compare operators. + bool operator== (const MovedCellRef& ref, const CellRef::RefNum& refNum) + { + return ref.mRefNum == refNum; + } + + bool operator== (const CellRef& ref, const CellRef::RefNum& refNum) + { + return ref.mRefNum == refNum; + } void Cell::load(ESMReader &esm, bool saveContext) @@ -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 61d586b9d..b0340e945 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/301] 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 8548a74f7..cd907408a 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 9a19ac7fa..614d697ec 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 bad4de03d..9e6395812 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 78b578766..a2abcfd1b 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/301] 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 9e6395812..a408a9fb7 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/301] 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 27980096e..a0593e60d 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 74f60419b..cf9e496ee 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 696aeefaa..7c95c2d30 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 0c145ab60..88f422a1d 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); + const MWWorld::Store &store = esmStore.get(); + + 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); - // Skip this when reference was deleted. - // TODO: Support respawning references, in this case, we need to track it somehow. - if (ref.mDeleted) { if (iter != mList.end()) - mList.erase(iter); - return; + *iter = liveCellRef; + else + mList.push_back (liveCellRef); } - - // for throwing exception on unhandled record type - const MWWorld::Store &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)); + 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 8a01caf18..b109557f9 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 62b91c2e4..cab10ee51 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 512883f1a..44c814e81 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 47cb0b99e..04451535b 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 c22c1b22b..0d69b0263 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 61d586b9d..38aaa0494 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/301] 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 c488d4d2b..53adc694c 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/301] 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 c59445402..a2777d489 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 1df848111..fef577cec 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 83885e5d5..5224ffdce 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/301] 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 23a95a4ab..bdb0e23de 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 60c2bc625..3d80a51bd 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 1fe92ffb1..efd6979b4 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 6397d9d40e4716f1e93886c6ad9e55fea92191c2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 14 Jan 2014 13:12:15 +0100 Subject: [PATCH 090/301] Added mCloneAction member --- apps/opencs/view/world/table.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 71bdb9000..9bc5ef038 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -199,6 +199,9 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q mCreateAction = new QAction (tr ("Add Record"), this); connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); addAction (mCreateAction); + + mCloneAction = new QAction(tr("Clone Record"), this); + addAction(mCloneAction); } mRevertAction = new QAction (tr ("Revert Record"), this); From 8c5f3135462e0b6e44de4733d917d66c2dfdaed1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 14 Jan 2014 12:25:35 +0100 Subject: [PATCH 091/301] 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 dbf8c1132..4c0bff59d 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 000000000..5bc8b7aef --- /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 000000000..54dbdae78 --- /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 4cf0b1dac..2b956d216 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 efd6979b4..649e3175d 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 85b3d8954..643119e67 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 000000000..b13b6c226 --- /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 000000000..bbbc4798f --- /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 000000000..13602fb67 --- /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 000000000..3f7db17f7 --- /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 344cae8f993d0ae4c52552fafd832f26090cd6bc Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 14 Jan 2014 15:44:04 +0100 Subject: [PATCH 092/301] added new entry to the context menu --- apps/opencs/view/world/table.cpp | 20 ++++++++++++++++++-- apps/opencs/view/world/table.hpp | 4 ++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 9bc5ef038..31a7d8b58 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -28,7 +28,11 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (!mEditLock) { if (selectedRows.size()==1) + { menu.addAction (mEditAction); + if (mCreateAction) + menu.addAction(mCloneAction); + } if (mCreateAction) menu.addAction (mCreateAction); @@ -155,7 +159,7 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, bool sorting) - : mUndoStack (undoStack), mCreateAction (0), mEditLock (false), mRecordStatusDisplay (0) + : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0) { mModel = &dynamic_cast (*data.getTableModel (id)); @@ -200,7 +204,8 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); addAction (mCreateAction); - mCloneAction = new QAction(tr("Clone Record"), this); + mCloneAction = new QAction (tr ("Clone Record"), this); + connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); addAction(mCloneAction); } @@ -298,6 +303,17 @@ void CSVWorld::Table::editRecord() } } +void CSVWorld::Table::cloneRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + emit cloneRequest (selectedRows.begin()->row()); + } +} + void CSVWorld::Table::moveUpRecord() { QModelIndexList selectedRows = selectionModel()->selectedRows(); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 889e2847a..f4a5d3c9e 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -32,6 +32,7 @@ namespace CSVWorld QUndoStack& mUndoStack; QAction *mEditAction; QAction *mCreateAction; + QAction *mCloneAction; QAction *mRevertAction; QAction *mDeleteAction; QAction *mMoveUpAction; @@ -73,6 +74,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); + void cloneRequest(int row); private slots: @@ -82,6 +84,8 @@ namespace CSVWorld void editRecord(); + void cloneRecord(); + void moveUpRecord(); void moveDownRecord(); From d5f794d4fbd16ccf3d82e877e98f1d731ce84061 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 15 Jan 2014 22:56:55 +0200 Subject: [PATCH 093/301] update to combat ai behaviour --- apps/openmw/mwmechanics/actors.cpp | 3 +- apps/openmw/mwmechanics/aicombat.cpp | 366 +++++++++++++++++++++----- apps/openmw/mwmechanics/aicombat.hpp | 28 +- apps/openmw/mwmechanics/character.cpp | 5 +- apps/openmw/mwmechanics/character.hpp | 10 +- 5 files changed, 331 insertions(+), 81 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0a4adb6e2..8ce03f2b9 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -13,6 +13,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/actionequip.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -190,7 +191,7 @@ namespace MWMechanics && LOS ) { - creatureStats.getAiSequence().stack(AiCombat("player")); + creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayer().getPlayer())); creatureStats.setHostile(true); } } diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 32b0063b6..5588e8482 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -4,14 +4,19 @@ #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" -#include "../mwbase/world.hpp" + #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" + +#include "character.hpp" +#include "../mwworld/inventorystore.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" #include +#include namespace { @@ -25,111 +30,309 @@ namespace namespace MWMechanics { - - AiCombat::AiCombat(const std::string &targetId) - :mTargetId(targetId),mTimer(0),mTimer2(0) + AiCombat::AiCombat(const MWWorld::Ptr& actor) : + mTarget(actor), + mTimerAttack(0), + mTimerReact(0), + mTimerCombatMove(0), + mCloseUp(false), + mReadyToAttack(false), + mStrike(false), + mCombatMove(false), + mMovement() { } bool AiCombat::execute (const MWWorld::Ptr& actor,float duration) { - if(!MWWorld::Class::get(actor).getCreatureStats(actor).isHostile()) return true; + //General description + if(!actor.getClass().getCreatureStats(actor).isHostile()) + return true; + if(actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0) + return true; - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mTargetId, false); + //update every frame + determineAttackType(actor, mMovement); - if(MWWorld::Class::get(actor).getCreatureStats(actor).getHealth().getCurrent() <= 0) return true; + if(mCombatMove) + { + mTimerCombatMove -= duration; + if( mTimerCombatMove <= 0) + { + mTimerCombatMove = 0; + mMovement.mPosition[1] = mMovement.mPosition[0] = 0; + mCombatMove = false; + } + } + actor.getClass().getMovementSettings(actor) = mMovement; + + + //actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mReadyToAttack); + mTimerAttack -= duration; + actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); + float tReaction = 0.25f; + if(mTimerReact < tReaction) + { + mTimerReact += duration; + return false; + } + else + { + mTimerReact = 0; + } + + //actual attacking logic + //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f + float attackPeriod = 1.0f; + if(mReadyToAttack) + { + if(mTimerAttack <= -attackPeriod) + { + //TODO: should depend on time between 'start' to 'min attack' + //for better controlling of NPCs' attack strength. + //Also it seems that this time is different for slash/thrust/chop + mTimerAttack = 0.35f * static_cast(rand())/RAND_MAX; + mStrike = true; + + //say a provoking combat phrase + if (actor.getClass().isNpc()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + int chance = store.get().find("iVoiceAttackOdds")->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < chance) + { + MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); + } + } + } + else if (mTimerAttack <= 0) + mStrike = false; + } + else + { + mTimerAttack = -attackPeriod; + mStrike = false; + } + + const MWWorld::Class &cls = actor.getClass(); + const ESM::Weapon *weapon = NULL; + MWMechanics::WeaponType weaptype; + float weapRange, weapSpeed = 1.0f; if(actor.getTypeName() == typeid(ESM::NPC).name()) { - MWWorld::Class::get(actor). - MWWorld::Class::get(actor).setStance(actor, MWWorld::Class::Run,true); - MWMechanics::DrawState_ state = MWWorld::Class::get(actor).getNpcStats(actor).getDrawState(); + actor.getClass().setStance(actor, MWWorld::Class::Run,true); + MWMechanics::DrawState_ state = actor.getClass().getNpcStats(actor).getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - MWWorld::Class::get(actor).getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); + actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + + //Get weapon speed and range + MWWorld::ContainerStoreIterator weaponSlot = + MWMechanics::getActiveWeapon(cls.getNpcStats(actor), cls.getInventoryStore(actor), &weaptype); + if (weaptype == WeapType_HandToHand) + { + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + weapRange = gmst.find("fHandToHandReach")->getFloat(); + } + else + { + weapon = weaponSlot->get()->mBase; + weapRange = weapon->mData.mReach; + weapSpeed = weapon->mData.mSpeed; + } + weapRange *= 100.0f; } - ESM::Position pos = actor.getRefData().getPosition(); - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); - float xCell = 0; - float yCell = 0; + //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - if (actor.getCell()->mCell->isExterior()) + ESM::Position pos = actor.getRefData().getPosition(); + + float zAngle; + + float rangeMelee; + float rangeCloseUp; + bool distantCombat = false; + if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) // || WeapType_Spell_OnTarget + { + rangeMelee = 1000; // TODO: should depend on archer skill + rangeCloseUp = 0; //doesn't needed when attacking from distance + distantCombat = true; + } + else { - xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; - yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + rangeMelee = weapRange; + rangeCloseUp = 300; } - ESM::Pathgrid::Point dest; - dest.mX = target.getRefData().getPosition().pos[0]; - dest.mY = target.getRefData().getPosition().pos[1]; - dest.mZ = target.getRefData().getPosition().pos[2]; + Ogre::Vector3 vStart(pos.pos[0], pos.pos[1], pos.pos[2]); + ESM::Position targetPos = mTarget.getRefData().getPosition(); + Ogre::Vector3 vDest(targetPos.pos[0], targetPos.pos[1], targetPos.pos[2]); + Ogre::Vector3 vDir = vDest - vStart; + float distBetween = vDir.length(); + + if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mCloseUp) ) + { + //Melee and Close-up combat + vDir.z = 0; + float dirLen = vDir.length(); + zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); + + // TODO: use movement settings instead of rotating directly + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + + if(mPathFinder.isPathConstructed()) + mPathFinder.clearPath(); + + //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - ESM::Pathgrid::Point start; - start.mX = pos.pos[0]; - start.mY = pos.pos[1]; - start.mZ = pos.pos[2]; + //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); + if (mCloseUp && distBetween > rangeMelee) + { + //Close-up combat: just run up on target + mMovement.mPosition[1] = 1; + } + else + { + //Melee: stop running and attack + mMovement.mPosition[1] = 0; + chooseBestAttack(weapon, mMovement); + + if(mMovement.mPosition[0] != 0 || mMovement.mPosition[1]) + { + mTimerCombatMove = 0.1f + 0.1f * static_cast(rand())/RAND_MAX; + mCombatMove = true; + } + else if(!distantCombat || (distantCombat && rangeMelee/5)) + { + //apply sideway movement (kind of dodging) with some probability + if(static_cast(rand())/RAND_MAX < 0.25) + { + mMovement.mPosition[0] = static_cast(rand())/RAND_MAX < 0.5? 1: -1; + mTimerCombatMove = 0.05f + 0.15f * static_cast(rand())/RAND_MAX; + mCombatMove = true; + } + } - mTimer2 = mTimer2 + duration; + if(distantCombat && distBetween < rangeMelee/4) + { + mMovement.mPosition[1] = -1; + } - if(!mPathFinder.isPathConstructed()) - mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); + mReadyToAttack = true; + //only once got in melee combat, actor is allowed to use close-up shortcutting + mCloseUp = true; + } + } else { - mPathFinder2.buildPath(start, dest, pathgrid, xCell, yCell, true); - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - if((mTimer2 > 0.25)&&(mPathFinder2.getPathSize() < mPathFinder.getPathSize() || - (dest.mX - lastPt.mX)*(dest.mX - lastPt.mX)+(dest.mY - lastPt.mY)*(dest.mY - lastPt.mY)+(dest.mZ - lastPt.mZ)*(dest.mZ - lastPt.mZ) > 200*200)) + //target is at far distance: build & follow the path + mCloseUp = false; + + buildNewPath(actor); + + //delete visited path node + mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); + + zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + + // TODO: use movement settings instead of rotating directly + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + mMovement.mPosition[1] = 1; + mReadyToAttack = false; + } + + if(distBetween > rangeMelee) + { + //special run attack; it shouldn't affect melee combat tactics + if(actor.getClass().getMovementSettings(actor).mPosition[1] == 1) { - mTimer2 = 0; - mPathFinder = mPathFinder2; + //check if actor can overcome the distance = distToTarget - attackerWeapRange + //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) + //then start attacking + float speed1 = cls.getSpeed(actor); + float speed2 = cls.getSpeed(mTarget); + if(actor.getClass().getMovementSettings(mTarget).mPosition[0] == 0 + && actor.getClass().getMovementSettings(mTarget).mPosition[1] == 0) + speed2 = 0; + + float s1 = distBetween - weapRange; + float t = s1/speed1; + float s2 = speed2 * t; + float t_swing = 0.17f/weapSpeed;//0.17 should be the time of playing weapon anim from 'start' to 'hit' tags + if (t + s2/speed1 <= t_swing) + { + mReadyToAttack = true; + if(mTimerAttack <= -attackPeriod) + { + mTimerAttack = 0.45f*static_cast(rand())/RAND_MAX; + mStrike = true; + } + } } } - mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); + return false; + } - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + void AiCombat::buildNewPath(const MWWorld::Ptr& actor) + { + //Construct path to target + ESM::Pathgrid::Point dest; + dest.mX = mTarget.getRefData().getPosition().pos[0]; + dest.mY = mTarget.getRefData().getPosition().pos[1]; + dest.mZ = mTarget.getRefData().getPosition().pos[2]; + Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); + float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + float targetPosThreshold; + bool isOutside = actor.getCell()->mCell->isExterior(); + if (isOutside) + targetPosThreshold = 300; + else + targetPosThreshold = 100; - float range = 100; - MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - if((dest.mX - start.mX)*(dest.mX - start.mX)+(dest.mY - start.mY)*(dest.mY - start.mY)+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) - < range*range) + if(dist > targetPosThreshold) { - float directionX = dest.mX - start.mX; - float directionY = dest.mY - start.mY; - float directionResult = sqrt(directionX * directionX + directionY * directionY); + //construct new path only if target has moved away more than on + ESM::Position pos = actor.getRefData().getPosition(); - zAngle = Ogre::Radian( Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult)) ).valueDegrees(); - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; - mPathFinder.clearPath(); + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + float xCell = 0; + float yCell = 0; - if(mTimer == 0) - { - MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - //mTimer = mTimer + duration; - } - if( mTimer > 1) + if (actor.getCell()->mCell->isExterior()) { - MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); - mTimer = 0; + xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; } + + if(!mPathFinder.isPathConstructed()) + mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside); else { - mTimer = mTimer + duration; - } + PathFinder newPathFinder; + newPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(!MWWorld::Class::get(actor).getCreatureStats(actor).getAttackingOrSpell()); + //TO EXPLORE: + //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, + //not the actual path length. Here we should know if the new path is actually more effective. + //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) + mPathFinder = newPathFinder; + } } - return false; } int AiCombat::getTypeId() const @@ -146,5 +349,36 @@ namespace MWMechanics { return new AiCombat(*this); } + + static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) + { + if (movement.mPosition[0] && !movement.mPosition[1]) //sideway + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); + else if (movement.mPosition[1]) //forward + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); + else + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); + } + + static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) + { + if (weapon == NULL) + return; + + int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; + int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; + int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; + + float total = slash + chop + thrust; + + if(static_cast(rand())/RAND_MAX <= static_cast(slash)/total) + { + movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; + movement.mPosition[1] = 0; + } + if (static_cast(rand())/RAND_MAX <= static_cast(thrust)/total) + movement.mPosition[1] = 1; + //else chop + } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index fa71e261f..f6a0c85c3 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -7,12 +7,14 @@ #include "movement.hpp" +#include "../mwbase/world.hpp" + namespace MWMechanics { class AiCombat : public AiPackage { public: - AiCombat(const std::string &targetId); + AiCombat(const MWWorld::Ptr& actor); virtual AiCombat *clone() const; @@ -24,13 +26,27 @@ namespace MWMechanics virtual unsigned int getPriority() const; private: - std::string mTargetId; - PathFinder mPathFinder; - PathFinder mPathFinder2; - float mTimer; - float mTimer2; + //controls duration of the actual strike + float mTimerAttack; + float mTimerReact; + //controls duration of the sideway & forward moves + //when mCombatMove is true + float mTimerCombatMove; + + bool mReadyToAttack, mStrike; + bool mCloseUp; + bool mCombatMove; + + MWMechanics::Movement mMovement; + MWWorld::Ptr mTarget; + + void buildNewPath(const MWWorld::Ptr& actor); }; + + static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); + //chooses an attack depending on probability to avoid uniformity + static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } #endif \ No newline at end of file diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2a077abc7..03d131c74 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -315,7 +315,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat } -void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group) +void getWeaponGroup(WeaponType weaptype, std::string &group) { const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); if(info != sWeaponTypeListEnd) @@ -323,7 +323,7 @@ void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group } -MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) +MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) { if(stats.getDrawState() == DrawState_Spell) { @@ -441,6 +441,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim { getWeaponGroup(mWeaponType, mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; + mAnimation->showWeapons(true); } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 438f542f0..d1930b77a 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -168,12 +168,6 @@ class CharacterController void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); - static void getWeaponGroup(WeaponType weaptype, std::string &group); - - static MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, - MWWorld::InventoryStore &inv, - WeaponType *weaptype); - void clearAnimQueue(); bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); @@ -205,6 +199,10 @@ public: void forceStateUpdate(); }; + void getWeaponGroup(WeaponType weaptype, std::string &group); + MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, + MWWorld::InventoryStore &inv, + WeaponType *weaptype); } #endif /* GAME_MWMECHANICS_CHARACTER_HPP */ From d8d4f1a15e0bd49155031dd7fffdd3012e99af31 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Jan 2014 12:02:45 +0100 Subject: [PATCH 094/301] 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 bdb0e23de..b9f630290 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 3d80a51bd..01b546d5a 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 649e3175d..cfd73554a 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 b13b6c226..56289acae 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 095/301] 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 06f6d6fac..eaf411d20 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 11b2d546f..2eb54a125 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 000000000..a12d20e6a --- /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 558639a3b..46f49df78 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 a2777d489..14e310432 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 fef577cec..7eb023a2b 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 87d0efe19..8d48078b1 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 07841e470..d9f5697bd 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 5224ffdce..8edba0892 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 d7befcc6e..58a6111c5 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 096/301] 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 13602fb67..d5ddc74d0 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 3f7db17f7..bd618457e 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 097/301] 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 14e310432..f03abe5bc 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(); + + 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]; - /// \todo sign - /// \todo last know exterior position - /// \todo mark + 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 5357f569e629e5b108ae21016f63ac2e87956780 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 19:36:22 +0200 Subject: [PATCH 098/301] fix for scrawl --- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index e3cce0f95..4ab358a52 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -358,7 +358,7 @@ namespace MWMechanics return new AiCombat(*this); } - static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) + void MWMechanics::determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) { if (movement.mPosition[0] && !movement.mPosition[1]) //sideway actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); @@ -368,7 +368,7 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); } - static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) + void MWMechanics::chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) { //the more damage attackType deals the more probability it has From 3dcb292bc13d170e65e72b52825c4df8a03e04e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 18:43:45 +0100 Subject: [PATCH 099/301] Do not soul trap creatures without a soul (which apparently exist) --- apps/openmw/mwmechanics/actors.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 99381a204..1b65b1472 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -131,6 +131,8 @@ namespace MWMechanics static const float fSoulgemMult = world->getStore().get().find("fSoulgemMult")->getFloat(); float creatureSoulValue = mCreature.get()->mBase->mData.mSoul; + if (creatureSoulValue == 0) + return; // Use the smallest soulgem that is large enough to hold the soul MWWorld::ContainerStore& container = caster.getClass().getContainerStore(caster); From 7df8273d717d71187ecb2774d3e9b0863fa8166b Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 20:01:15 +0200 Subject: [PATCH 100/301] fix for everybody --- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4ab358a52..0f3536f0a 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -358,7 +358,7 @@ namespace MWMechanics return new AiCombat(*this); } - void MWMechanics::determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) + void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) { if (movement.mPosition[0] && !movement.mPosition[1]) //sideway actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); @@ -368,7 +368,7 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); } - void MWMechanics::chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) + void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) { //the more damage attackType deals the more probability it has From 46a4790cb1213a626de2a11ff4a5d7410c0a96af Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 20:33:49 +0200 Subject: [PATCH 101/301] final warnings removal --- apps/openmw/mwmechanics/aicombat.cpp | 81 +++++++++++++++------------- apps/openmw/mwmechanics/aicombat.hpp | 4 -- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 0f3536f0a..487a4773e 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -26,6 +26,10 @@ namespace return 1.0; return -1.0; } + + void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); + //chooses an attack depending on probability to avoid uniformity + void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } namespace MWMechanics @@ -256,7 +260,7 @@ namespace MWMechanics //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) //then start attacking float speed1 = cls.getSpeed(actor); - float speed2 = cls.getSpeed(mTarget); + float speed2 = mTarget.getClass().getSpeed(mTarget); if(actor.getClass().getMovementSettings(mTarget).mPosition[0] == 0 && actor.getClass().getMovementSettings(mTarget).mPosition[1] == 0) speed2 = 0; @@ -357,51 +361,54 @@ namespace MWMechanics { return new AiCombat(*this); } +} - void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) - { - if (movement.mPosition[0] && !movement.mPosition[1]) //sideway - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); - else if (movement.mPosition[1]) //forward - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - else - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); - } - - void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) - { - //the more damage attackType deals the more probability it has - - if (weapon == NULL) - { - //hand-to-hand deals equal damage - float roll = static_cast(rand())/RAND_MAX; - if(roll <= 0.333f) //side punch - { - movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; - movement.mPosition[1] = 0; - } - else if(roll <= 0.666f) //forward punch - movement.mPosition[1] = 1; - - return; - } +namespace +{ +void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) +{ + if (movement.mPosition[0] && !movement.mPosition[1]) //sideway + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); + else if (movement.mPosition[1]) //forward + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); + else + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); +} - int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; - int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; - int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; +void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) +{ + //the more damage attackType deals the more probability it has - float total = slash + chop + thrust; - + if (weapon == NULL) + { + //hand-to-hand deals equal damage float roll = static_cast(rand())/RAND_MAX; - if(roll <= static_cast(slash)/total) + if(roll <= 0.333f) //side punch { movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; movement.mPosition[1] = 0; } - else if(roll <= (static_cast(slash) + static_cast(thrust))/total) + else if(roll <= 0.666f) //forward punch movement.mPosition[1] = 1; - //else chop + + return; + } + + int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; + int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; + int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; + + float total = slash + chop + thrust; + + float roll = static_cast(rand())/RAND_MAX; + if(roll <= static_cast(slash)/total) + { + movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; + movement.mPosition[1] = 0; } + else if(roll <= (static_cast(slash) + static_cast(thrust))/total) + movement.mPosition[1] = 1; + //else chop } +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 8c494648b..cb9eee973 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -45,10 +45,6 @@ namespace MWMechanics void buildNewPath(const MWWorld::Ptr& actor); }; - - static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); - //chooses an attack depending on probability to avoid uniformity - static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } #endif From 435bbdd53070f17c48fd1d8e393ae381f355e917 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 20:55:21 +0200 Subject: [PATCH 102/301] one more mismatch fixed --- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 487a4773e..9a78e44ff 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -261,8 +261,8 @@ namespace MWMechanics //then start attacking float speed1 = cls.getSpeed(actor); float speed2 = mTarget.getClass().getSpeed(mTarget); - if(actor.getClass().getMovementSettings(mTarget).mPosition[0] == 0 - && actor.getClass().getMovementSettings(mTarget).mPosition[1] == 0) + if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0 + && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0) speed2 = 0; float s1 = distBetween - weapRange; From 5049f3b1346ee80eb9f0bede5b24eb5329d7e976 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 20:18:35 +0100 Subject: [PATCH 103/301] Fix the group for creature attack animations --- apps/openmw/mwmechanics/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 013af5b3a..6ff947306 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -504,7 +504,7 @@ bool CharacterController::updateCreatureState() } mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, + MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); mUpperBodyState = UpperCharState_StartToMinAttack; From df4df5b0946708a5e8d98de8bac7c998a964e1fd Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 23:30:28 +0200 Subject: [PATCH 104/301] fixed weapRange for creatures/startcombat script(?) --- apps/openmw/mwmechanics/aicombat.cpp | 6 ++++++ apps/openmw/mwscript/aiextensions.cpp | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 9a78e44ff..6bf97f6ac 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -151,6 +151,11 @@ namespace MWMechanics } weapRange *= 100.0f; } + else //is creature + { + weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon + weapRange = 100; //TODO: use true attack range (the same problem in Creature::hit) + } //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); @@ -381,6 +386,7 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement if (weapon == NULL) { + //hand-to-hand and creatures' attacks handled here //hand-to-hand deals equal damage float roll = static_cast(rand())/RAND_MAX; if(roll <= 0.333f) //side punch diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index d09a72acc..53b757d76 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -435,10 +435,14 @@ namespace MWScript std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); - creatureStats.getAiSequence().stack(MWMechanics::AiCombat(actor)); + MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); + if (actorID == "player") + { creatureStats.setHostile(true); + creatureStats.getAiSequence().stack( + MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(actorID,true))); + } } }; From 9653355cf1f5e78ea131c5e4187d8052c8e86776 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Jan 2014 07:26:27 +0100 Subject: [PATCH 105/301] Feature #701: Spawn levelled creatures in cells --- apps/openmw/mwclass/creaturelevlist.cpp | 47 ++++++++++++++++++++++++ apps/openmw/mwclass/creaturelevlist.hpp | 5 +++ apps/openmw/mwmechanics/levelledlist.hpp | 1 + apps/openmw/mwworld/scene.cpp | 3 +- 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index 53dd34bb4..ea586e5b6 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -3,6 +3,24 @@ #include +#include "../mwmechanics/levelledlist.hpp" + +#include "../mwworld/customdata.hpp" + +namespace +{ + struct CustomData : public MWWorld::CustomData + { + // TODO: save the creature we spawned here + virtual MWWorld::CustomData *clone() const; + }; + + MWWorld::CustomData *CustomData::clone() const + { + return new CustomData (*this); + } +} + namespace MWClass { std::string CreatureLevList::getName (const MWWorld::Ptr& ptr) const @@ -16,4 +34,33 @@ namespace MWClass registerClass (typeid (ESM::CreatureLevList).name(), instance); } + + void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, MWRender::RenderingInterface &renderingInterface) const + { + ensureCustomData(ptr); + } + + void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const + { + if (!ptr.getRefData().getCustomData()) + { + std::auto_ptr data (new CustomData); + + MWWorld::LiveCellRef *ref = + ptr.get(); + + std::string id = MWMechanics::getLevelledItem(ref->mBase, true); + + if (!id.empty()) + { + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + MWWorld::ManualRef ref(store, id); + ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos; + // TODO: hold on to this for respawn purposes later + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), *ptr.getCell() , ptr.getCellRef().mPos); + } + + ptr.getRefData().setCustomData(data.release()); + } + } } diff --git a/apps/openmw/mwclass/creaturelevlist.hpp b/apps/openmw/mwclass/creaturelevlist.hpp index 81965efd5..d2c02043e 100644 --- a/apps/openmw/mwclass/creaturelevlist.hpp +++ b/apps/openmw/mwclass/creaturelevlist.hpp @@ -7,6 +7,8 @@ namespace MWClass { class CreatureLevList : public MWWorld::Class { + void ensureCustomData (const MWWorld::Ptr& ptr) const; + public: virtual std::string getName (const MWWorld::Ptr& ptr) const; @@ -14,6 +16,9 @@ namespace MWClass /// can return an empty string. static void registerSelf(); + + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + ///< Add reference into a cell for rendering }; } diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp index d65503011..120616f9f 100644 --- a/apps/openmw/mwmechanics/levelledlist.hpp +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -6,6 +6,7 @@ #include "../mwworld/class.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwmechanics/creaturestats.hpp" namespace MWMechanics { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 3607b8276..0d5fb0560 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -487,7 +487,6 @@ namespace MWWorld insertCellRefList(mRendering, cell.mContainers, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mDoors, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mIngreds, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mCreatureLists, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mItemLists, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mLights, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mLockpicks, cell, *mPhysics, rescale, loadingListener); @@ -499,6 +498,8 @@ namespace MWWorld // Load NPCs and creatures _after_ everything else (important for adjustPosition to work correctly) insertCellRefList(mRendering, cell.mCreatures, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mNpcs, cell, *mPhysics, rescale, loadingListener); + // Since this adds additional creatures, load afterwards, or they would be loaded twice + insertCellRefList(mRendering, cell.mCreatureLists, cell, *mPhysics, rescale, loadingListener); } void Scene::addObjectToScene (const Ptr& ptr) From c04a8afc8bb1757f64886627877a1be6d7658ee3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Jan 2014 10:51:52 +0100 Subject: [PATCH 106/301] Make sure onPcEquip is also set for Equip script instruction --- apps/openmw/mwscript/containerextensions.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 7bac7cdbe..6674481f2 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -171,6 +171,9 @@ namespace MWScript MWWorld::ActionEquip action (*it); action.execute(ptr); + + if (ptr.getRefData().getHandle() == "player" && !ptr.getClass().getScript(ptr).empty()) + ptr.getRefData().getLocals().setVarByInt(ptr.getClass().getScript(ptr), "onpcequip", 1); } }; From 525ce2f04273a76d59045a2d669fa6a58dc2f7da Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Jan 2014 10:52:16 +0100 Subject: [PATCH 107/301] Some cleanup - move definitions to implementation file --- apps/openmw/mwgui/quickkeysmenu.cpp | 18 ++- apps/openmw/mwgui/spellcreationdialog.cpp | 6 +- apps/openmw/mwgui/spellwindow.cpp | 17 +-- apps/openmw/mwmechanics/spellcasting.cpp | 164 ++++++++++++++++++++- apps/openmw/mwmechanics/spellcasting.hpp | 171 ++-------------------- apps/openmw/mwmechanics/spells.hpp | 2 +- 6 files changed, 197 insertions(+), 181 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index e1d430307..ff13ae1af 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -3,8 +3,15 @@ #include #include "../mwworld/inventorystore.hpp" -#include "../mwworld/actionequip.hpp" +#include "../mwworld/class.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "../mwgui/inventorywindow.hpp" #include "../mwgui/bookwindow.hpp" #include "../mwgui/scrollwindow.hpp" @@ -17,8 +24,8 @@ namespace { bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right) { - int cmp = MWWorld::Class::get(left).getName(left).compare( - MWWorld::Class::get(right).getName(right)); + int cmp = left.getClass().getName(left).compare( + right.getClass().getName(right)); return cmp < 0; } @@ -334,10 +341,7 @@ namespace MWGui // equip, if it can be equipped if (!MWWorld::Class::get(item).getEquipmentSlots(item).first.empty()) { - // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping - - MWWorld::ActionEquip action(item); - action.execute (MWBase::Environment::get().getWorld ()->getPlayerPtr()); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item); } store.setSelectedEnchantItem(it); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 857dd76d5..469d7188e 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -3,13 +3,17 @@ #include #include "../mwbase/windowmanager.hpp" - #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" #include "tooltips.hpp" #include "class.hpp" diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 21257ef9c..07076159c 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -4,11 +4,15 @@ #include #include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/actionequip.hpp" +#include "../mwworld/class.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" #include "spellicons.hpp" #include "inventorywindow.hpp" @@ -231,8 +235,7 @@ namespace MWGui MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); float enchantCost = enchant->mData.mCost; - MWMechanics::NpcStats &stats = player.getClass().getNpcStats(player); - int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); + int eSkill = player.getClass().getSkill(player, ESM::Skill::Enchant); int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); std::string cost = boost::lexical_cast(castCost); @@ -316,13 +319,7 @@ namespace MWGui if (_sender->getUserString("Equipped") == "false" && !MWWorld::Class::get(item).getEquipmentSlots(item).first.empty()) { - // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping - - MWWorld::ActionEquip action(item); - action.execute (MWBase::Environment::get().getWorld ()->getPlayerPtr()); - - // since we changed equipping status, update the inventory window - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item); } store.setSelectedEnchantItem(it); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index a0e91791b..f70050628 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -1,20 +1,182 @@ #include "spellcasting.hpp" +#include + #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/actionteleport.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" #include "../mwrender/animation.hpp" +#include "magiceffects.hpp" +#include "npcstats.hpp" + namespace MWMechanics { + ESM::Skill::SkillEnum spellSchoolToSkill(int school) + { + std::map schoolSkillMap; // maps spell school to skill id + schoolSkillMap[0] = ESM::Skill::Alteration; + schoolSkillMap[1] = ESM::Skill::Conjuration; + schoolSkillMap[3] = ESM::Skill::Illusion; + schoolSkillMap[2] = ESM::Skill::Destruction; + schoolSkillMap[4] = ESM::Skill::Mysticism; + schoolSkillMap[5] = ESM::Skill::Restoration; + assert(schoolSkillMap.find(school) != schoolSkillMap.end()); + return schoolSkillMap[school]; + } + + float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool) + { + CreatureStats& stats = actor.getClass().getCreatureStats(actor); + + if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).mMagnitude) + return 0; + + float y = FLT_MAX; + float lowestSkill = 0; + + for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it) + { + float x = it->mDuration; + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find( + it->mEffectID); + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) + x = std::max(1.f, x); + x *= 0.1 * magicEffect->mData.mBaseCost; + x *= 0.5 * (it->mMagnMin + it->mMagnMax); + x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost; + if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) + x *= 1.5; + static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( + "fEffectCostMult")->getFloat(); + x *= fEffectCostMult; + + float s = 2 * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); + if (s - x < y) + { + y = s - x; + if (effectiveSchool) + *effectiveSchool = magicEffect->mData.mSchool; + lowestSkill = s; + } + } + + if (spell->mData.mType != ESM::Spell::ST_Spell) + return 100; + + if (spell->mData.mFlags & ESM::Spell::F_Always) + return 100; + + int castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).mMagnitude; + + int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + + float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2 * actorWillpower + 0.1 * actorLuck) * stats.getFatigueTerm(); + if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player") + castChance = 100; + + return std::max(0.f, std::min(100.f, castChance)); + } + + float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool) + { + const ESM::Spell* spell = + MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + return getSpellSuccessChance(spell, actor, effectiveSchool); + } + + + int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor) + { + int school = 0; + getSpellSuccessChance(spellId, actor, &school); + return school; + } + + int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor) + { + int school = 0; + getSpellSuccessChance(spell, actor, &school); + return school; + } + + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell) + { + const ESM::MagicEffect *magicEffect = + MWBase::Environment::get().getWorld()->getStore().get().find ( + effectId); + + const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + + float resisted = 0; + if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) + { + + short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); + short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); + + float resistance = 0; + if (resistanceEffect != -1) + resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude; + if (weaknessEffect != -1) + resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude; + + + float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float x = (willpower + 0.1 * luck) * stats.getFatigueTerm(); + + // This makes spells that are easy to cast harder to resist and vice versa + if (spell != NULL && caster.getClass().isActor()) + { + float castChance = getSpellSuccessChance(spell, caster); + if (castChance > 0) + x *= 50 / castChance; + } + + float roll = static_cast(std::rand()) / RAND_MAX * 100; + if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) + roll -= resistance; + + if (x <= roll) + x = 0; + else + { + if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) + x = 100; + else + x = roll / std::min(x, 100.f); + } + + x = std::min(x + resistance, 100.f); + + resisted = x; + } + + return resisted; + } + + float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell) + { + float resistance = getEffectResistance(effectId, actor, caster, spell); + if (resistance >= 0) + return 1 - resistance / 100.f; + else + return -(resistance-100) / 100.f; + } + CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target) : mCaster(caster) , mTarget(target) @@ -230,7 +392,7 @@ namespace MWMechanics MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault); } - void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, MWMechanics::EffectKey effect, float magnitude) + void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude) { short effectId = effect.mId; if (!target.getClass().isActor()) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 52af26ad1..b58e7bb09 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -1,33 +1,13 @@ #ifndef MWMECHANICS_SPELLSUCCESS_H #define MWMECHANICS_SPELLSUCCESS_H -#include - -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" - #include "../mwworld/ptr.hpp" -#include "../mwworld/class.hpp" -#include "../mwmechanics/creaturestats.hpp" - -#include "../mwworld/esmstore.hpp" - -#include "npcstats.hpp" namespace MWMechanics { - inline ESM::Skill::SkillEnum spellSchoolToSkill(int school) - { - std::map schoolSkillMap; // maps spell school to skill id - schoolSkillMap[0] = ESM::Skill::Alteration; - schoolSkillMap[1] = ESM::Skill::Conjuration; - schoolSkillMap[3] = ESM::Skill::Illusion; - schoolSkillMap[2] = ESM::Skill::Destruction; - schoolSkillMap[4] = ESM::Skill::Mysticism; - schoolSkillMap[5] = ESM::Skill::Restoration; - assert(schoolSkillMap.find(school) != schoolSkillMap.end()); - return schoolSkillMap[school]; - } + class EffectKey; + + ESM::Skill::SkillEnum spellSchoolToSkill(int school); /** * @param spell spell to cast @@ -36,147 +16,16 @@ namespace MWMechanics * @attention actor has to be an NPC and not a creature! * @return success chance from 0 to 100 (in percent) */ - inline float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL) - { - CreatureStats& stats = actor.getClass().getCreatureStats(actor); - - if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).mMagnitude) - return 0; - - float y = FLT_MAX; - float lowestSkill = 0; - - for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it) - { - float x = it->mDuration; - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find( - it->mEffectID); - if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) - x = std::max(1.f, x); - x *= 0.1 * magicEffect->mData.mBaseCost; - x *= 0.5 * (it->mMagnMin + it->mMagnMax); - x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost; - if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) - x *= 1.5; - static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fEffectCostMult")->getFloat(); - x *= fEffectCostMult; + float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL); + float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL); - float s = 2 * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); - if (s - x < y) - { - y = s - x; - if (effectiveSchool) - *effectiveSchool = magicEffect->mData.mSchool; - lowestSkill = s; - } - } - - if (spell->mData.mType != ESM::Spell::ST_Spell) - return 100; - - if (spell->mData.mFlags & ESM::Spell::F_Always) - return 100; - - int castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).mMagnitude; - - int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - - float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2 * actorWillpower + 0.1 * actorLuck) * stats.getFatigueTerm(); - if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player") - castChance = 100; - - return std::max(0.f, std::min(100.f, castChance)); - } - - inline float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL) - { - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - return getSpellSuccessChance(spell, actor, effectiveSchool); - } - - inline int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor) - { - int school = 0; - getSpellSuccessChance(spellId, actor, &school); - return school; - } - - inline int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor) - { - int school = 0; - getSpellSuccessChance(spell, actor, &school); - return school; - } + int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor); + int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); /// @return >=100 for fully resisted. can also return negative value for damage amplification. - inline float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL) - { - const ESM::MagicEffect *magicEffect = - MWBase::Environment::get().getWorld()->getStore().get().find ( - effectId); - - const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - - float resisted = 0; - if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) - { - - short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); - short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); - - float resistance = 0; - if (resistanceEffect != -1) - resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude; - if (weaknessEffect != -1) - resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude; - - - float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - float x = (willpower + 0.1 * luck) * stats.getFatigueTerm(); - - // This makes spells that are easy to cast harder to resist and vice versa - if (spell != NULL && caster.getClass().isActor()) - { - float castChance = getSpellSuccessChance(spell, caster); - if (castChance > 0) - x *= 50 / castChance; - } - - float roll = static_cast(std::rand()) / RAND_MAX * 100; - if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) - roll -= resistance; - - if (x <= roll) - x = 0; - else - { - if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) - x = 100; - else - x = roll / std::min(x, 100.f); - } - - x = std::min(x + resistance, 100.f); - - resisted = x; - } - - return resisted; - } - - inline float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL) - { - float resistance = getEffectResistance(effectId, actor, caster, spell); - if (resistance >= 0) - return 1 - resistance / 100.f; - else - return -(resistance-100) / 100.f; - } + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL); + float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL); class CastSpell { @@ -202,7 +51,7 @@ namespace MWMechanics void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false); - void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, MWMechanics::EffectKey effect, float magnitude); + void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); }; } diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index facf02da8..cc239a650 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -20,7 +20,7 @@ namespace MWMechanics /// \brief Spell list /// /// This class manages known spells as well as abilities, powers and permanent negative effects like - /// diseaes. + /// diseases. class Spells { public: From 969340d61ba3e2a0fbc8fdf9fa3ab937b4a271c1 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sat, 18 Jan 2014 12:55:17 +0200 Subject: [PATCH 108/301] fixed StartCombat script --- apps/openmw/mwscript/aiextensions.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 53b757d76..759d0ba94 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -432,17 +432,14 @@ namespace MWScript virtual void execute (Interpreter::Runtime &runtime) { MWWorld::Ptr actor = R()(runtime); - std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); + std::string targetID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - if (actorID == "player") - { - creatureStats.setHostile(true); - creatureStats.getAiSequence().stack( - MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(actorID,true))); - } + creatureStats.setHostile(true); + creatureStats.getAiSequence().stack( + MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(targetID, true) )); } }; From 6584a01d01190ed9d2bccc6ebdc4ab6dd87f07fa Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 18 Jan 2014 14:53:25 +0100 Subject: [PATCH 109/301] 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 8edba0892..9704239d7 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 110/301] 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 cab10ee51..c5c826d47 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 4c0045b41875d7952ccd47ef1f0fc82701e18e63 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Jan 2014 21:11:12 +0100 Subject: [PATCH 111/301] Bug #1109: Do not reset water level when loading a plugin with no water level record (for real this time) --- apps/openmw/mwworld/store.cpp | 4 ++-- components/esm/loadcell.cpp | 19 ++++++++++++++++++- components/esm/loadcell.hpp | 6 +++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index ca37cc591..8e4c5ecef 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -56,7 +56,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // copy list into new cell cell->mContextList = oldcell->mContextList; // have new cell replace old cell - *oldcell = *cell; + ESM::Cell::merge(oldcell, cell); } else mInt[idLower] = *cell; } @@ -83,7 +83,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) } cell->mMovedRefs = oldcell->mMovedRefs; // have new cell replace old cell - *oldcell = *cell; + ESM::Cell::merge(oldcell, cell); } else mExt[std::make_pair(cell->mData.mX, cell->mData.mY)] = *cell; } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index f6bc29ae1..f4bba7f19 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -47,9 +47,13 @@ void Cell::load(ESMReader &esm, bool saveContext) esm.getHT(waterl); mWater = (float) waterl; mWaterInt = true; + mHasWaterLevelRecord = true; } else if (esm.isNextSub("WHGT")) + { esm.getHT(mWater); + mHasWaterLevelRecord = true; + } // Quasi-exterior cells have a region (which determines the // weather), pure interior cells have ambient lighting @@ -94,7 +98,7 @@ void Cell::save(ESMWriter &esm) const esm.writeHNT("DATA", mData, 12); if (mData.mFlags & Interior) { - if (mWater != -1) { + if (mHasWaterLevelRecord) { if (mWaterInt) { int water = (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5); @@ -301,4 +305,17 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) mAmbi.mFog = 0; mAmbi.mFogDensity = 0; } + + void Cell::merge(Cell *original, Cell *modified) + { + float waterLevel = original->mWater; + if (modified->mHasWaterLevelRecord) + { + waterLevel = modified->mWater; + } + // else: keep original water level, instead of resetting to 0 + + *original = *modified; + original->mWater = waterLevel; + } } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index b066f497e..71478946f 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -77,7 +77,10 @@ struct Cell float mFogDensity; }; - Cell() : mWater(-1) {} + Cell() : mWater(0), mHasWaterLevelRecord(false) {} + + /// Merge \a modified into \a original + static void merge (Cell* original, Cell* modified); // Interior cells are indexed by this (it's the 'id'), for exterior // cells it is optional. @@ -90,6 +93,7 @@ struct Cell DATAstruct mData; AMBIstruct mAmbi; float mWater; // Water level + bool mHasWaterLevelRecord; bool mWaterInt; int mMapColor; int mNAM0; From 947b6c964541932acf7e28e75709d1f597c17281 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Jan 2014 22:16:31 +0100 Subject: [PATCH 112/301] kOgrePi unused --- apps/openmw/mwclass/npc.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f93a3e342..7f75d910b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -36,9 +36,6 @@ namespace { - const Ogre::Radian kOgrePi (Ogre::Math::PI); - const Ogre::Radian kOgrePiOverTwo (Ogre::Math::PI / Ogre::Real(2.0)); - struct CustomData : public MWWorld::CustomData { MWMechanics::NpcStats mNpcStats; From 69ca03c308d84abc7ffbccb0a880d121cad4428c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 09:43:41 +0100 Subject: [PATCH 113/301] Issue #777: Move DrawState to CreatureStats. All creatures can cast spells, and some creatures have weapons. --- apps/openmw/mwmechanics/aiwander.cpp | 6 ++---- apps/openmw/mwmechanics/creaturestats.cpp | 12 +++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 5 +++++ apps/openmw/mwmechanics/npcstats.cpp | 13 +------------ apps/openmw/mwmechanics/npcstats.hpp | 5 ----- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 853d0ff7b..9a78f1acd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -7,8 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "../mwmechanics/npcstats.hpp" - +#include "creaturestats.hpp" #include namespace @@ -66,8 +65,7 @@ namespace MWMechanics bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { - if (actor.getClass().isNpc()) - actor.getClass().getNpcStats(actor).setDrawState(DrawState_Nothing); + actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); MWBase::World *world = MWBase::Environment::get().getWorld(); if(mDuration) { diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 8d37e34c8..70e4bd989 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -16,7 +16,7 @@ namespace MWMechanics mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), - mMovementFlags(0) + mMovementFlags(0), mDrawState (DrawState_Nothing) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -452,4 +452,14 @@ namespace MWMechanics return false; // shut up, compiler } + DrawState_ CreatureStats::getDrawState() const + { + return mDrawState; + } + + void CreatureStats::setDrawState(DrawState_ state) + { + mDrawState = state; + } + } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 308883fc5..0981522a5 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -10,6 +10,7 @@ #include "spells.hpp" #include "activespells.hpp" #include "aisequence.hpp" +#include "drawstate.hpp" namespace MWMechanics { @@ -18,6 +19,7 @@ namespace MWMechanics /// class CreatureStats { + DrawState_ mDrawState; AttributeValue mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue int mLevel; @@ -57,6 +59,9 @@ namespace MWMechanics public: CreatureStats(); + DrawState_ getDrawState() const; + void setDrawState(DrawState_ state); + bool needToRecalcDynamicStats(); void addToFallHeight(float height); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 293b078da..d18519168 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -22,8 +22,7 @@ #include "../mwbase/soundmanager.hpp" MWMechanics::NpcStats::NpcStats() -: mDrawState (DrawState_Nothing) -, mBounty (0) + : mBounty (0) , mLevelProgress(0) , mDisposition(0) , mReputation(0) @@ -36,16 +35,6 @@ MWMechanics::NpcStats::NpcStats() mSkillIncreases.resize (ESM::Attribute::Length, 0); } -MWMechanics::DrawState_ MWMechanics::NpcStats::getDrawState() const -{ - return mDrawState; -} - -void MWMechanics::NpcStats::setDrawState (DrawState_ state) -{ - mDrawState = state; -} - float MWMechanics::NpcStats::getAttackStrength() const { return mAttackStrength; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index b89a2b4b3..bf76b80d6 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -7,7 +7,6 @@ #include #include "stat.hpp" -#include "drawstate.hpp" #include "creaturestats.hpp" @@ -30,7 +29,6 @@ namespace MWMechanics /// \note the faction key must be in lowercase std::map mFactionRank; - DrawState_ mDrawState; int mDisposition; SkillValue mSkill[27]; SkillValue mWerewolfSkill[27]; @@ -61,9 +59,6 @@ namespace MWMechanics int getProfit() const; void modifyProfit(int diff); - DrawState_ getDrawState() const; - void setDrawState (DrawState_ state); - /// When attacking, stores how strong the attack should be (0 = weakest, 1 = strongest) float getAttackStrength() const; void setAttackStrength(float value); From 589fbbd8718f27926cf67365be6e85f9616e0e55 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 11:42:58 +0100 Subject: [PATCH 114/301] Issue #777: Create InventoryStore for creatures with weapons/shields --- apps/openmw/mwclass/armor.cpp | 27 +++++----- apps/openmw/mwclass/clothing.cpp | 25 +++++---- apps/openmw/mwclass/creature.cpp | 52 +++++++++++++++---- apps/openmw/mwclass/creature.hpp | 5 ++ apps/openmw/mwclass/npc.hpp | 2 + apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 2 +- apps/openmw/mwgui/companionitemmodel.cpp | 4 +- apps/openmw/mwgui/inventoryitemmodel.cpp | 4 +- apps/openmw/mwgui/tradeitemmodel.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 5 +- apps/openmw/mwmechanics/actors.cpp | 3 +- .../mwmechanics/mechanicsmanagerimp.cpp | 4 +- apps/openmw/mwrender/actors.cpp | 3 -- apps/openmw/mwrender/characterpreview.cpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 2 - apps/openmw/mwscript/containerextensions.cpp | 2 +- apps/openmw/mwworld/actionequip.cpp | 4 +- apps/openmw/mwworld/class.cpp | 5 ++ apps/openmw/mwworld/class.hpp | 3 ++ apps/openmw/mwworld/containerstore.hpp | 2 + apps/openmw/mwworld/inventorystore.cpp | 4 +- apps/openmw/mwworld/inventorystore.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 9 ++-- 23 files changed, 111 insertions(+), 62 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 5e37426c9..83bda25d1 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -289,7 +289,7 @@ namespace MWClass std::pair Armor::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const { - MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc); if (ptr.getCellRef().mCharge == 0) return std::make_pair(0, "#{sInventoryMessage1}"); @@ -300,20 +300,23 @@ namespace MWClass if (slots_.first.empty()) return std::make_pair(0, ""); - std::string npcRace = npc.get()->mBase->mRace; - - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); - if(race->mData.mFlags & ESM::Race::Beast) + if (npc.getClass().isNpc()) { - std::vector parts = ptr.get()->mBase->mParts.mParts; + std::string npcRace = npc.get()->mBase->mRace; - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) { - if((*itr).mPart == ESM::PRT_Head) - return std::make_pair(0, "#{sNotifyMessage13}"); - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - return std::make_pair(0, "#{sNotifyMessage14}"); + std::vector parts = ptr.get()->mBase->mParts.mParts; + + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PRT_Head) + return std::make_pair(0, "#{sNotifyMessage13}"); + if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) + return std::make_pair(0, "#{sNotifyMessage14}"); + } } } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 62fc26a71..a135585eb 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -238,20 +238,23 @@ namespace MWClass if (slots_.first.empty()) return std::make_pair(0, ""); - std::string npcRace = npc.get()->mBase->mRace; - - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); - if(race->mData.mFlags & ESM::Race::Beast) + if (npc.getClass().isNpc()) { - std::vector parts = ptr.get()->mBase->mParts.mParts; + std::string npcRace = npc.get()->mBase->mRace; - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) { - if((*itr).mPart == ESM::PRT_Head) - return std::make_pair(0, "#{sNotifyMessage13}"); - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - return std::make_pair(0, "#{sNotifyMessage15}"); + std::vector parts = ptr.get()->mBase->mParts.mParts; + + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PRT_Head) + return std::make_pair(0, "#{sNotifyMessage13}"); + if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) + return std::make_pair(0, "#{sNotifyMessage15}"); + } } } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a97268318..1a8f7b5c7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -26,6 +26,8 @@ #include "../mwgui/tooltips.hpp" +#include "../mwworld/inventorystore.hpp" + #include "../mwmechanics/npcstats.hpp" namespace @@ -33,15 +35,20 @@ namespace struct CustomData : public MWWorld::CustomData { MWMechanics::CreatureStats mCreatureStats; - MWWorld::ContainerStore mContainerStore; + MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures MWMechanics::Movement mMovement; virtual MWWorld::CustomData *clone() const; + + CustomData() : mContainerStore(0) {} + virtual ~CustomData() { delete mContainerStore; } }; MWWorld::CustomData *CustomData::clone() const { - return new CustomData (*this); + CustomData* cloned = new CustomData (*this); + cloned->mContainerStore = mContainerStore->clone(); + return cloned; } } @@ -106,15 +113,23 @@ namespace MWClass data->mCreatureStats.getSpells().add (*iter); // inventory - data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr), "", + if (ref->mBase->mFlags & ESM::Creature::Weapon) + data->mContainerStore = new MWWorld::InventoryStore(); + else + data->mContainerStore = new MWWorld::ContainerStore(); + + // store + ptr.getRefData().setCustomData (data.release()); + + getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", MWBase::Environment::get().getWorld()->getStore()); // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. // (except for gold you gave him) - data->mContainerStore.add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); + getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); - // store - ptr.getRefData().setCustomData (data.release()); + if (ref->mBase->mFlags & ESM::Creature::Weapon) + getInventoryStore(ptr).autoEquip(ptr); } } @@ -325,18 +340,33 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); } - MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) - const + MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; + return *dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; + } + + MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + + if (ref->mBase->mFlags & ESM::Creature::Weapon) + return dynamic_cast(getContainerStore(ptr)); + else + throw std::runtime_error("this creature has no inventory store"); + } + + bool Creature::hasInventoryStore(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + + return (ref->mBase->mFlags & ESM::Creature::Weapon); } std::string Creature::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index d518d0056..cfa06ed62 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -68,6 +68,11 @@ namespace MWClass const MWWorld::Ptr& ptr) const; ///< Return container store + virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; + ///< Return inventory store + + virtual bool hasInventoryStore (const MWWorld::Ptr &ptr) const; + virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 497d0ced8..e658dde5f 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -71,6 +71,8 @@ namespace MWClass virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; ///< Return inventory store + virtual bool hasInventoryStore(const MWWorld::Ptr &ptr) const { return true; } + virtual void hit(const MWWorld::Ptr& ptr, int type) const; virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index b2107c329..263c101e5 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -420,7 +420,7 @@ namespace MWDialogue MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); // Apply disposition change to NPC's base disposition - if (mActor.getTypeName() == typeid(ESM::NPC).name()) + if (mActor.getClass().isNpc()) { MWMechanics::NpcStats& npcStats = MWWorld::Class::get(mActor).getNpcStats(mActor); npcStats.setBaseDisposition(npcStats.getBaseDisposition() + mPermanentDispositionChange); diff --git a/apps/openmw/mwgui/companionitemmodel.cpp b/apps/openmw/mwgui/companionitemmodel.cpp index 3212ed701..bb6cf2800 100644 --- a/apps/openmw/mwgui/companionitemmodel.cpp +++ b/apps/openmw/mwgui/companionitemmodel.cpp @@ -12,7 +12,7 @@ namespace MWGui void CompanionItemModel::copyItem (const ItemStack& item, size_t count) { - if (mActor.getTypeName() == typeid(ESM::NPC).name()) + if (mActor.getClass().isNpc()) { MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); @@ -23,7 +23,7 @@ namespace MWGui void CompanionItemModel::removeItem (const ItemStack& item, size_t count) { - if (mActor.getTypeName() == typeid(ESM::NPC).name()) + if (mActor.getClass().isNpc()) { MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index d26feba88..97e1e9a2b 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -74,9 +74,9 @@ void InventoryItemModel::update() ItemStack newItem (item, this, item.getRefData().getCount()); - if (mActor.getTypeName() == typeid(ESM::NPC).name()) + if (mActor.getClass().hasInventoryStore(mActor)) { - MWWorld::InventoryStore& store = MWWorld::Class::get(mActor).getInventoryStore(mActor); + MWWorld::InventoryStore& store = mActor.getClass().getInventoryStore(mActor); for (int slot=0; slot(0,std::min(int(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange()),100)); - const MWMechanics::NpcStats &sellerStats = mPtr.getClass().getNpcStats(mPtr); - const MWMechanics::NpcStats &playerStats = player.getClass().getNpcStats(player); + const MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); + const MWMechanics::CreatureStats &playerStats = player.getClass().getCreatureStats(player); float a1 = std::min(player.getClass().getSkill(player, ESM::Skill::Mercantile), 100); float b1 = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1b65b1472..3daecbe1c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -47,8 +47,7 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) { - // TODO: remove this check once creatures support inventory store - if (ptr.getTypeName() == typeid(ESM::NPC).name()) + if (ptr.getClass().hasInventoryStore(ptr)) { MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); MWWorld::ContainerStoreIterator item = diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 0dee4706a..fff3db8a9 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -196,8 +196,8 @@ namespace MWMechanics creatureStats.setDynamic (i, stat); } - // auto-equip again. we need this for when the race is changed to a beast race - MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); + // auto-equip again. we need this for when the race is changed to a beast race and shoes are no longer equippable + MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); for (int i=0; iaddWaterRippleEmitter (ptr); - - // Create CustomData, will do autoEquip and trigger animation parts update - ptr.getClass().getInventoryStore(ptr); } void Actors::insertCreature (const MWWorld::Ptr& ptr) { diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 2dbc72e26..32145928e 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -142,7 +142,7 @@ namespace MWRender { mAnimation->updateParts(); - MWWorld::InventoryStore &inv = MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter); + MWWorld::InventoryStore &inv = mCharacter.getClass().getInventoryStore(mCharacter); MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); std::string groupname; if(iter == inv.end()) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8a2ab1c7a..572d2abdb 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -892,8 +892,6 @@ void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) mPlayerAnimation->~NpcAnimation(); new(mPlayerAnimation) NpcAnimation(ptr, ptr.getRefData().getBaseNode(), RV_Actors); } - // Ensure CustomData -> autoEquip -> animation update - ptr.getClass().getInventoryStore(ptr); mCamera->setAnimation(mPlayerAnimation); mWater->removeEmitter(ptr); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 6674481f2..e06505e86 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -159,7 +159,7 @@ namespace MWScript std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); + MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index d34773bd5..2a1b7a3aa 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -19,9 +19,9 @@ namespace MWWorld void ActionEquip::executeImp (const Ptr& actor) { MWWorld::Ptr object = getTarget(); - MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); + MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor); - std::pair result = MWWorld::Class::get (object).canBeEquipped (object, actor); + std::pair result = object.getClass().canBeEquipped (object, actor); // display error message if the player tried to equip something if (!result.second.empty() && actor == MWBase::Environment::get().getWorld()->getPlayerPtr()) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 6c00b949c..c7b0d2e1f 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -122,6 +122,11 @@ namespace MWWorld throw std::runtime_error ("class does not have an inventory store"); } + bool Class::hasInventoryStore(const Ptr &ptr) const + { + return false; + } + void Class::lock (const Ptr& ptr, int lockLevel) const { throw std::runtime_error ("class does not support locking"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index ec22d0306..823538cf8 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -150,6 +150,9 @@ namespace MWWorld ///< Return inventory store or throw an exception, if class does not have a /// inventory store (default implementation: throw an exceoption) + virtual bool hasInventoryStore (const Ptr& ptr) const; + ///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false) + virtual void lock (const Ptr& ptr, int lockLevel) const; ///< Lock object (default implementation: throw an exception) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 0a1728740..913ec544a 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -62,6 +62,8 @@ namespace MWWorld virtual ~ContainerStore(); + virtual ContainerStore* clone() { return new ContainerStore(*this); } + ContainerStoreIterator begin (int mask = Type_All); ContainerStoreIterator end(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 82b827e75..ea43314e6 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -81,7 +81,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, // Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves if ((actorPtr.getRefData().getHandle() != "player") - && !(MWWorld::Class::get(actorPtr).getNpcStats(actorPtr).isWerewolf()) + && !(actorPtr.getClass().isNpc() && actorPtr.getClass().getNpcStats(actorPtr).isWerewolf()) && !actorPtr.getClass().getCreatureStats(actorPtr).isDead()) { std::string type = itemPtr.getTypeName(); @@ -457,7 +457,7 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor // If an armor/clothing item is removed, try to find a replacement, // but not for the player nor werewolves. if ((actor.getRefData().getHandle() != "player") - && !(MWWorld::Class::get(actor).getNpcStats(actor).isWerewolf())) + && !(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())) { std::string type = item.getTypeName(); if (((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name())) diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 067c8261e..d97bdf365 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -113,6 +113,8 @@ namespace MWWorld InventoryStore& operator= (const InventoryStore& store); + virtual InventoryStore* clone() { return new InventoryStore(*this); } + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// Auto-equip items if specific conditions are fulfilled (see the implementation). diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0d7802081..e6569a178 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2097,7 +2097,6 @@ namespace MWWorld void World::castSpell(const Ptr &actor) { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - InventoryStore& inv = actor.getClass().getInventoryStore(actor); MWWorld::Ptr target = getFacedObject(); @@ -2111,9 +2110,11 @@ namespace MWWorld cast.cast(spell); } - else if (inv.getSelectedEnchantItem() != inv.end()) + else if (actor.getClass().hasInventoryStore(actor)) { - cast.cast(*inv.getSelectedEnchantItem()); + MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); + if (inv.getSelectedEnchantItem() != inv.end()) + cast.cast(*inv.getSelectedEnchantItem()); } } @@ -2282,7 +2283,7 @@ namespace MWWorld void World::breakInvisibility(const Ptr &actor) { actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); - if (actor.getClass().isNpc()) + if (actor.getClass().hasInventoryStore(actor)) actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); } From b3e45c55bcfc45d4fe4ee4f9b10d989c32b15a72 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 19 Jan 2014 11:44:47 +0100 Subject: [PATCH 115/301] progressing with the cloning --- apps/opencs/view/world/creator.hpp | 3 +++ apps/opencs/view/world/genericcreator.cpp | 32 +++++++++++++++++------ apps/opencs/view/world/genericcreator.hpp | 6 +++++ apps/opencs/view/world/tablebottombox.cpp | 11 +++++++- apps/opencs/view/world/tablebottombox.hpp | 2 ++ apps/opencs/view/world/tablesubview.cpp | 15 +++++++++-- apps/opencs/view/world/tablesubview.hpp | 4 +++ 7 files changed, 62 insertions(+), 11 deletions(-) diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index df9b116ee..58a5e21ad 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -2,6 +2,7 @@ #define CSV_WORLD_CREATOR_H #include +#include "../../model/world/universalid.hpp" class QUndoStack; @@ -23,6 +24,8 @@ namespace CSVWorld virtual ~Creator(); virtual void reset() = 0; + + virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type) = 0; virtual void setEditLock (bool locked) = 0; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index ba2b3665a..b89c2dccf 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -2,6 +2,7 @@ #include "genericcreator.hpp" #include +#include #include #include @@ -58,7 +59,7 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, bool relaxedIdRules) -: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false) +: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mCloneMode(false), mClonedType(CSMWorld::UniversalId::Type_None) { mLayout = new QHBoxLayout; mLayout->setContentsMargins (0, 0, 0, 0); @@ -89,6 +90,7 @@ void CSVWorld::GenericCreator::setEditLock (bool locked) void CSVWorld::GenericCreator::reset() { + mCloneMode = false; mId->setText (""); update(); } @@ -120,16 +122,30 @@ void CSVWorld::GenericCreator::create() { if (!mLocked) { - std::string id = getId(); + if (mCloneMode) + { + std::string id = getId(); + } else { + std::string id = getId(); - std::auto_ptr command (new CSMWorld::CreateCommand ( + std::auto_ptr command (new CSMWorld::CreateCommand ( dynamic_cast (*mData.getTableModel (mListId)), id)); - configureCreateCommand (*command); + configureCreateCommand (*command); - mUndoStack.push (command.release()); + mUndoStack.push (command.release()); - emit done(); - emit requestFocus (id); + emit done(); + emit requestFocus (id); + } } -} \ No newline at end of file +} + +void CSVWorld::GenericCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type) +{ + mCloneMode = true; + mClonedId = originid; + mClonedType = type; + + mId->setText(QString::fromStdString(mClonedId)); +} diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 8dd2ca911..a1acb2e79 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -1,6 +1,7 @@ #ifndef CSV_WORLD_GENERICCREATOR_H #define CSV_WORLD_GENERICCREATOR_H +class QString; class QPushButton; class QLineEdit; class QHBoxLayout; @@ -28,6 +29,9 @@ namespace CSVWorld std::string mErrors; QHBoxLayout *mLayout; bool mLocked; + bool mCloneMode; + std::string mClonedId; + CSMWorld::UniversalId::Type mClonedType; protected: @@ -57,6 +61,8 @@ namespace CSVWorld virtual void reset(); + virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type); + virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index 3edf9af31..b89f5180b 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -156,4 +156,13 @@ void CSVWorld::TableBottomBox::createRequest() mLayout->setCurrentWidget (mCreator); setVisible (true); mCreating = true; -} \ No newline at end of file +} + +void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type) +{ + mCreator->reset(); + mCreator->cloneMode(id, type); + mLayout->setCurrentWidget(mCreator); + setVisible (true); + mCreating = true; +} diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index a5ae5e0bd..58a6a5a2f 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -2,6 +2,7 @@ #define CSV_WORLD_BOTTOMBOX_H #include +#include class QLabel; class QStackedLayout; @@ -76,6 +77,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); + void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); }; } diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 55ded09de..36200dbbc 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -46,8 +46,13 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D mTable->selectionSizeUpdate(); if (mBottom->canCreateAndDelete()) + { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - + + connect (mTable, SIGNAL (cloneRequest(int)), this, SLOT(cloneRequest(int))); + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), + mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); + } connect (mBottom, SIGNAL (requestFocus (const std::string&)), mTable, SLOT (requestFocus (const std::string&))); @@ -75,4 +80,10 @@ void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, con void CSVWorld::TableSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); -} \ No newline at end of file +} + +void CSVWorld::TableSubView::cloneRequest(int row) +{ + const CSMWorld::UniversalId& toClone(mTable->getUniversalId(row)); + emit cloneRequest(toClone.getId(), toClone.getType()); +} diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 56a441a4d..5b19110b6 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -33,10 +33,14 @@ namespace CSVWorld virtual void updateEditorSetting (const QString& key, const QString& value); virtual void setStatusBar (bool show); + + signals: + void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); private slots: void editRequest (int row); + void cloneRequest (int row); }; } From 198bb0de60017bd9663a2eb5046296892be6c577 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 13:05:26 +0100 Subject: [PATCH 116/301] Issue #777: Add CreatureAnimation variant for creatures with weapons/shields --- apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwrender/actors.cpp | 8 +- apps/openmw/mwrender/actors.hpp | 2 +- apps/openmw/mwrender/creatureanimation.cpp | 124 ++++++++++++++++++++- apps/openmw/mwrender/creatureanimation.hpp | 28 ++++- 5 files changed, 155 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 1a8f7b5c7..c272b2825 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -148,8 +148,10 @@ namespace MWClass void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { + MWWorld::LiveCellRef *ref = ptr.get(); + MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertCreature(ptr); + actors.insertCreature(ptr, ref->mBase->mFlags & ESM::Creature::Weapon); } void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index be123d39c..4955dd6cb 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -76,10 +76,14 @@ void Actors::insertNPC(const MWWorld::Ptr& ptr) mAllActors[ptr] = anim; mRendering->addWaterRippleEmitter (ptr); } -void Actors::insertCreature (const MWWorld::Ptr& ptr) +void Actors::insertCreature (const MWWorld::Ptr& ptr, bool weaponsShields) { insertBegin(ptr); - CreatureAnimation* anim = new CreatureAnimation(ptr); + Animation* anim = NULL; + if (weaponsShields) + anim = new CreatureWeaponAnimation(ptr); + else + anim = new CreatureAnimation(ptr); delete mAllActors[ptr]; mAllActors[ptr] = anim; mRendering->addWaterRippleEmitter (ptr); diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index af71525fa..d5d6c52bb 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -40,7 +40,7 @@ namespace MWRender void setRootNode(Ogre::SceneNode* root); void insertNPC(const MWWorld::Ptr& ptr); - void insertCreature (const MWWorld::Ptr& ptr); + void insertCreature (const MWWorld::Ptr& ptr, bool weaponsShields); void insertActivator (const MWWorld::Ptr& ptr); bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 20e5ff8ef..2cffd7f0d 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,32 +1,144 @@ #include "creatureanimation.hpp" +#include +#include +#include + #include "renderconst.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/class.hpp" + namespace MWRender { -CreatureAnimation::~CreatureAnimation() -{ -} CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) : Animation(ptr, ptr.getRefData().getBaseNode()) { MWWorld::LiveCellRef *ref = mPtr.get(); - assert (ref->mBase != NULL); - if(!ref->mBase->mModel.empty()) + std::string model = ptr.getClass().getModel(ptr); + if(!model.empty()) { - std::string model = "meshes\\"+ref->mBase->mModel; + setObjectRoot(model, false); + setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); + + if((ref->mBase->mFlags&ESM::Creature::Bipedal)) + addAnimSource("meshes\\base_anim.nif"); + addAnimSource(model); + } +} + +CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr) + : Animation(ptr, ptr.getRefData().getBaseNode()) + , mShowWeapons(true) // TODO: change to false, once charactercontroller handles creature weapons + , mShowCarriedLeft(true) // TODO: change to false, once charactercontroller handles creature weapons +{ + MWWorld::LiveCellRef *ref = mPtr.get(); + + std::string model = ptr.getClass().getModel(ptr); + if(!model.empty()) + { setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\base_anim.nif"); addAnimSource(model); + + mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr); + + updateParts(); + } +} + +void CreatureWeaponAnimation::showWeapons(bool showWeapon) +{ + if (showWeapon != mShowWeapons) + { + mShowWeapons = showWeapon; + updateParts(); + } +} + +void CreatureWeaponAnimation::showCarriedLeft(bool show) +{ + if (show != mShowCarriedLeft) + { + mShowCarriedLeft = show; + updateParts(); + } +} + +void CreatureWeaponAnimation::updateParts() +{ + mWeapon.setNull(); + mShield.setNull(); + + if (mShowWeapons) + updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight); + if (mShowCarriedLeft) + updatePart(mShield, MWWorld::InventoryStore::Slot_CarriedLeft); +} + +void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slot) +{ + MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator it = inv.getSlot(slot); + + if (it == inv.end()) + { + scene.setNull(); + return; + } + MWWorld::Ptr item = *it; + + std::string bonename; + if (slot == MWWorld::InventoryStore::Slot_CarriedRight) + bonename = "Weapon Bone"; + else + bonename = "Shield Bone"; + + scene = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, item.getClass().getModel(item)); + Ogre::Vector3 glowColor = getEnchantmentColor(item); + + setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0, + !item.getClass().getEnchantment(item).empty(), &glowColor); + + if(scene->mSkelBase) + { + Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton(); + if(scene->mSkelBase->isParentTagPoint()) + { + Ogre::Node *root = scene->mSkelBase->getParentNode(); + if(skel->hasBone("BoneOffset")) + { + Ogre::Bone *offset = skel->getBone("BoneOffset"); + + root->translate(offset->getPosition()); + + // It appears that the BoneOffset rotation is completely bogus, at least for light models. + //root->rotate(offset->getOrientation()); + root->pitch(Ogre::Degree(-90.0f)); + + root->scale(offset->getScale()); + root->setInitialState(); + } + } + updateSkeletonInstance(mSkelBase->getSkeleton(), skel); + } + + // TODO: + // type == ESM::PRT_Weapon should get an animation source based on the current offset + // of the weapon attack animation (from its beginning, or start marker?) + std::vector >::iterator ctrl(scene->mControllers.begin()); + for(;ctrl != scene->mControllers.end();ctrl++) + { + if(ctrl->getSource().isNull()) + ctrl->setSource(Ogre::SharedPtr(new NullAnimationTime())); } } diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index a902df5d8..37826673d 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -2,6 +2,7 @@ #define GAME_RENDER_CREATUREANIMATION_H #include "animation.hpp" +#include "../mwworld/inventorystore.hpp" namespace MWWorld { @@ -14,7 +15,32 @@ namespace MWRender { public: CreatureAnimation(const MWWorld::Ptr& ptr); - virtual ~CreatureAnimation(); + virtual ~CreatureAnimation() {} + }; + + // For creatures with weapons and shields + // Animation is already virtual anyway, so might as well make a separate class. + // Most creatures don't need weapons/shields, so this will save some memory. + class CreatureWeaponAnimation : public Animation, public MWWorld::InventoryStoreListener + { + public: + CreatureWeaponAnimation(const MWWorld::Ptr& ptr); + virtual ~CreatureWeaponAnimation() {} + + virtual void equipmentChanged() { updateParts(); } + + virtual void showWeapons(bool showWeapon); + virtual void showCarriedLeft(bool show); + + void updateParts(); + + void updatePart(NifOgre::ObjectScenePtr& scene, int slot); + + private: + NifOgre::ObjectScenePtr mWeapon; + NifOgre::ObjectScenePtr mShield; + bool mShowWeapons; + bool mShowCarriedLeft; }; } From 13646a651be8f27b7c3180d56087cd8f87d06556 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 13:31:17 +0100 Subject: [PATCH 117/301] Issue #777: Handle creatures with weapons in CharacterController. Move attack strength to CreatureStats. --- apps/openmw/mwmechanics/aicombat.cpp | 7 ++++--- apps/openmw/mwmechanics/character.cpp | 19 ++++++++++--------- apps/openmw/mwmechanics/character.hpp | 6 +++--- apps/openmw/mwmechanics/creaturestats.cpp | 12 +++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 5 +++++ apps/openmw/mwmechanics/npcstats.cpp | 11 ----------- apps/openmw/mwmechanics/npcstats.hpp | 4 ---- apps/openmw/mwrender/creatureanimation.cpp | 4 ++-- 8 files changed, 35 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 42b618143..a0d57e89d 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -42,13 +42,14 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); - if(actor.getTypeName() == typeid(ESM::NPC).name()) + if (actor.getClass().hasInventoryStore(actor)) { - MWMechanics::DrawState_ state = actor.getClass().getNpcStats(actor).getDrawState(); + MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon); //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); } + ESM::Position pos = actor.getRefData().getPosition(); const ESM::Pathgrid *pathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6ff947306..6183b65d3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -323,7 +323,7 @@ void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group } -MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) +MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) { if(stats.getDrawState() == DrawState_Spell) { @@ -434,15 +434,16 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim * handle knockout and death which moves the character down. */ mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); - if(mPtr.getTypeName() == typeid(ESM::NPC).name()) + if (cls.hasInventoryStore(mPtr)) { - getActiveWeapon(cls.getNpcStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); + getActiveWeapon(cls.getCreatureStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); if(mWeaponType != WeapType_None) { getWeaponGroup(mWeaponType, mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; mAnimation->showWeapons(true); } + mAnimation->showCarriedLeft(mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand); } if(!cls.getCreatureStats(mPtr).isDead()) @@ -517,14 +518,14 @@ bool CharacterController::updateCreatureState() return false; } -bool CharacterController::updateNpcState(bool inwater, bool isrunning) +bool CharacterController::updateWeaponState(bool inwater, bool isrunning) { const MWWorld::Class &cls = MWWorld::Class::get(mPtr); - NpcStats &stats = cls.getNpcStats(mPtr); + CreatureStats &stats = cls.getCreatureStats(mPtr); WeaponType weaptype = WeapType_None; MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype); - const bool isWerewolf = stats.isWerewolf(); + const bool isWerewolf = cls.isNpc() && cls.getNpcStats(mPtr).isWerewolf(); bool forcestateupdate = false; if(weaptype != mWeaponType && mHitState != CharState_KnockDown) @@ -613,7 +614,7 @@ bool CharacterController::updateNpcState(bool inwater, bool isrunning) { // Unset casting flag, otherwise pressing the mouse button down would // continue casting every frame if there is no animation - mPtr.getClass().getCreatureStats(mPtr).setAttackingOrSpell(false); + stats.setAttackingOrSpell(false); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -1128,8 +1129,8 @@ void CharacterController::update(float duration) } } - if(cls.isNpc()) - forcestateupdate = updateNpcState(inwater, isrunning) || forcestateupdate; + if(cls.hasInventoryStore(mPtr)) + forcestateupdate = updateWeaponState(inwater, isrunning) || forcestateupdate; else forcestateupdate = updateCreatureState() || forcestateupdate; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index d3d4b4435..33e6cae52 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -22,7 +22,7 @@ namespace MWMechanics { class Movement; -class NpcStats; +class CreatureStats; enum Priority { Priority_Default, @@ -170,13 +170,13 @@ class CharacterController static void getWeaponGroup(WeaponType weaptype, std::string &group); - static MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, + static MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype); void clearAnimQueue(); - bool updateNpcState(bool inwater, bool isrunning); + bool updateWeaponState(bool inwater, bool isrunning); bool updateCreatureState(); void updateVisibility(); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 70e4bd989..62bae8ca8 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -16,7 +16,7 @@ namespace MWMechanics mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), - mMovementFlags(0), mDrawState (DrawState_Nothing) + mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -462,4 +462,14 @@ namespace MWMechanics mDrawState = state; } + float CreatureStats::getAttackStrength() const + { + return mAttackStrength; + } + + void CreatureStats::setAttackStrength(float value) + { + mAttackStrength = value; + } + } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 0981522a5..6893f385e 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -40,6 +40,7 @@ namespace MWMechanics bool mKnockdown; bool mHitRecovery; unsigned int mMovementFlags; + float mAttackStrength; // Note only some creatures attack with weapons float mFallHeight; @@ -62,6 +63,10 @@ namespace MWMechanics DrawState_ getDrawState() const; void setDrawState(DrawState_ state); + /// When attacking, stores how strong the attack should be (0 = weakest, 1 = strongest) + float getAttackStrength() const; + void setAttackStrength(float value); + bool needToRecalcDynamicStats(); void addToFallHeight(float height); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index d18519168..4fff6eac8 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -28,23 +28,12 @@ MWMechanics::NpcStats::NpcStats() , mReputation(0) , mWerewolfKills (0) , mProfit(0) -, mAttackStrength(0.0f) , mTimeToStartDrowning(20.0) , mLastDrowningHit(0) { mSkillIncreases.resize (ESM::Attribute::Length, 0); } -float MWMechanics::NpcStats::getAttackStrength() const -{ - return mAttackStrength; -} - -void MWMechanics::NpcStats::setAttackStrength(float value) -{ - mAttackStrength = value; -} - int MWMechanics::NpcStats::getBaseDisposition() const { return mDisposition; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index bf76b80d6..8cdeeea5d 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -59,10 +59,6 @@ namespace MWMechanics int getProfit() const; void modifyProfit(int diff); - /// When attacking, stores how strong the attack should be (0 = weakest, 1 = strongest) - float getAttackStrength() const; - void setAttackStrength(float value); - int getBaseDisposition() const; void setBaseDisposition(int disposition); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 2cffd7f0d..e2aa9a2b8 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -34,8 +34,8 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr) : Animation(ptr, ptr.getRefData().getBaseNode()) - , mShowWeapons(true) // TODO: change to false, once charactercontroller handles creature weapons - , mShowCarriedLeft(true) // TODO: change to false, once charactercontroller handles creature weapons + , mShowWeapons(false) + , mShowCarriedLeft(false) { MWWorld::LiveCellRef *ref = mPtr.get(); From 047bbe43b2ca08a63c8e18d2b4af52e0dbefb37b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 13:46:40 +0100 Subject: [PATCH 118/301] Don't complain about missing jump animations --- apps/openmw/mwmechanics/character.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6183b65d3..7c2f7472d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -250,14 +250,16 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mAnimation->disable(mCurrentJump); mCurrentJump = jump; - mAnimation->play(mCurrentJump, Priority_Jump, jumpgroup, false, + if (mAnimation->hasAnimation("jump")) + mAnimation->play(mCurrentJump, Priority_Jump, jumpgroup, false, 1.0f, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); } else { mAnimation->disable(mCurrentJump); mCurrentJump.clear(); - mAnimation->play(jump, Priority_Jump, jumpgroup, true, + if (mAnimation->hasAnimation("jump")) + mAnimation->play(jump, Priority_Jump, jumpgroup, true, 1.0f, "loop stop", "stop", 0.0f, 0); } } From 1a00f26390e28fb15153f7910b41e51a614a8a6b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 14:02:39 +0100 Subject: [PATCH 119/301] Bug #1126: Golden saints and many other creatures only have "Bip01 Head" --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3ad716b72..3ce98c0bf 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -225,7 +225,7 @@ namespace MWBase /// Returns a pointer to the object the provided object would hit (if within the /// specified distance), and the point where the hit occurs. This will attempt to - /// use the "Head" node as a basis. + /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis. virtual std::pair getHitContact(const MWWorld::Ptr &ptr, float distance) = 0; virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e6569a178..46a1e9fda 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -821,6 +821,8 @@ namespace MWWorld if(anim != NULL) { Ogre::Node *node = anim->getNode("Head"); + if (node == NULL) + node = anim->getNode("Bip01 Head"); if(node != NULL) pos += node->_getDerivedPosition(); } From 9723263730b3c101c2bf38d5009ca06c637dfa91 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 14:47:58 +0100 Subject: [PATCH 120/301] Bug #1126: Tweak creature attack distance a bit (still no idea where this should come from) --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwmechanics/aicombat.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index c272b2825..7fcc7066b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -197,7 +197,7 @@ namespace MWClass ptr.get(); // TODO: where is the distance defined? - std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 100); + std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 200); if (result.first.isEmpty()) return; // Didn't hit anything diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index c36c10665..f38b7ef75 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -152,7 +152,7 @@ namespace MWMechanics else //is creature { weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon - weapRange = 100; //TODO: use true attack range (the same problem in Creature::hit) + weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) } //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); From 0adc0f56ed844c017a574ad2e19095ffa43e9984 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 16:13:16 +0100 Subject: [PATCH 121/301] Issue #777: Consider weapons in Creature::hit --- apps/openmw/mwclass/creature.cpp | 96 +++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 7fcc7066b..4a244f6a9 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -6,6 +6,7 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/magiceffects.hpp" #include "../mwmechanics/movement.hpp" +#include "../mwmechanics/spellcasting.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -195,9 +196,38 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + MWMechanics::CreatureStats &stats = getCreatureStats(ptr); + + // Get the weapon used (if hand-to-hand, weapon = inv.end()) + MWWorld::Ptr weapon; + if (ptr.getClass().hasInventoryStore(ptr)) + { + MWWorld::InventoryStore &inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weaponslot != inv.end() && weaponslot->getTypeName() == typeid(ESM::Weapon).name()) + weapon = *weaponslot; + } + + // Reduce fatigue + // somewhat of a guess, but using the weapon weight makes sense + const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat(); + const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); + const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); + MWMechanics::DynamicStat fatigue = stats.getFatigue(); + const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); + float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; + if (!weapon.isEmpty()) + fatigueLoss += weapon.getClass().getWeight(weapon) * stats.getAttackStrength() * fWeaponFatigueMult; + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); + stats.setFatigue(fatigue); // TODO: where is the distance defined? - std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 200); + float dist = 200.f; + if (!weapon.isEmpty()) + dist = 100.f * weapon.get()->mBase->mData.mReach; + + std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist); if (result.first.isEmpty()) return; // Didn't hit anything @@ -208,7 +238,6 @@ namespace MWClass Ogre::Vector3 hitPosition = result.second; - MWMechanics::CreatureStats &stats = getCreatureStats(ptr); MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); float hitchance = ref->mBase->mData.mCombat + @@ -243,8 +272,71 @@ namespace MWClass break; } + // I think this should be random, since attack1-3 animations don't have an attack strength like NPCs do float damage = min + (max - min) * ::rand()/(RAND_MAX+1.0); + if (!weapon.isEmpty()) + { + const bool weaphashealth = get(weapon).hasItemHealth(weapon); + const unsigned char *attack = NULL; + if(type == MWMechanics::CreatureStats::AT_Chop) + attack = weapon.get()->mBase->mData.mChop; + else if(type == MWMechanics::CreatureStats::AT_Slash) + attack = weapon.get()->mBase->mData.mSlash; + else if(type == MWMechanics::CreatureStats::AT_Thrust) + attack = weapon.get()->mBase->mData.mThrust; + if(attack) + { + float weaponDamage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); + weaponDamage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); + if(weaphashealth) + { + int weapmaxhealth = weapon.get()->mBase->mData.mHealth; + if(weapon.getCellRef().mCharge == -1) + weapon.getCellRef().mCharge = weapmaxhealth; + weaponDamage *= 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); + + // Weapon broken? unequip it + if (weapon.getCellRef().mCharge == 0) + weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr); + + damage += weaponDamage; + } + + // Apply "On hit" enchanted weapons + std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : ""; + if (!enchantmentName.empty()) + { + const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( + enchantmentName); + if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) + { + // Check if we have enough charges + const float enchantCost = enchantment->mData.mCost; + int eSkill = getSkill(ptr, ESM::Skill::Enchant); + const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); + + if (weapon.getCellRef().mEnchantmentCharge == -1) + weapon.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; + if (weapon.getCellRef().mEnchantmentCharge < castCost) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); + } + else + { + weapon.getCellRef().mEnchantmentCharge -= castCost; + MWMechanics::CastSpell cast(ptr, victim); + cast.cast(weapon); + } + } + } + } + // TODO: do not do this if the attack is blocked MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); From 0ea2bb7c4cc835f969fdfe92bba153fd9f3bebdc Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 19 Jan 2014 16:49:39 +0100 Subject: [PATCH 122/301] Working on commands --- apps/opencs/model/world/commands.cpp | 21 +++++++++++++++++++++ apps/opencs/model/world/commands.hpp | 20 ++++++++++++++++++++ apps/opencs/model/world/idtable.cpp | 8 ++++++++ apps/opencs/model/world/idtable.hpp | 2 ++ apps/opencs/view/world/genericcreator.cpp | 2 ++ 5 files changed, 53 insertions(+) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 21feb14be..281afb15f 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -25,6 +25,27 @@ void CSMWorld::ModifyCommand::undo() mModel.setData (mIndex, mOld); } +CSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model, + const std::string& idOrigin, + const std::string& IdDestination, + CSMWorld::UniversalId::Type type, + QUndoCommand* parent) : + QUndoCommand(parent), + mModel(model), + mIdOrigin(idOrigin), + mIdDestination(IdDestination), + mType(type) +{ + setText (("Clone record " + idOrigin + " to the " + IdDestination).c_str()); +} + + +void CSMWorld::CloneCommand::redo() +{ + mModel.cloneRecord(mIdOrigin, mIdDestination, mType); +} + + CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent) : QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) { diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index f0480a369..1af4bd919 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -39,6 +39,26 @@ namespace CSMWorld virtual void undo(); }; + class CloneCommand : public QUndoCommand + { + IdTable& mModel; + std::string mIdOrigin; + std::string mIdDestination; + UniversalId::Type mType; + std::map mValues; + + public: + + CloneCommand (IdTable& model, const std::string& idOrigin, + const std::string& IdDestination, + UniversalId::Type type, + QUndoCommand* parent = 0); + + virtual void redo(); + +// virtual void undo(); + }; + class CreateCommand : public QUndoCommand { IdTable& mModel; diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 809d64339..b90bf1a9b 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -124,6 +124,14 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type endInsertRows(); } +void CSMWorld::IdTable::cloneRecord(const std::string& origin, + const std::string& destination, + CSMWorld::UniversalId::Type type) +{ + +} + + QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const { return index (mIdCollection->getIndex (id), column); diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index e4ae58fd0..dfbc04134 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -63,6 +63,8 @@ namespace CSMWorld void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types + void cloneRecord(const std::string& origin, const std::string& destination, UniversalId::Type type = UniversalId::Type_None); + QModelIndex getModelIndex (const std::string& id, int column) const; void setRecord (const std::string& id, const RecordBase& record); diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index b89c2dccf..f21a62d97 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -125,6 +125,8 @@ void CSVWorld::GenericCreator::create() if (mCloneMode) { std::string id = getId(); + std::auto_ptr command (new CSMWorld::CloneCommand ( + dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType)); } else { std::string id = getId(); From bea161331c2f1e4d173c8ad70d1125ce3bbd4970 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 17:03:25 +0100 Subject: [PATCH 123/301] Play 'hello' voiced dialogue entries in AIWander --- apps/openmw/mwmechanics/aiwander.cpp | 34 ++++++++++++++++++++++++++++ apps/openmw/mwmechanics/aiwander.hpp | 2 ++ 2 files changed, 36 insertions(+) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 9a78f1acd..185805153 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -7,6 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" + #include "creaturestats.hpp" #include @@ -31,6 +32,7 @@ namespace MWMechanics , mX(0) , mY(0) , mZ(0) + , mSaidGreeting(false) { for(unsigned short counter = 0; counter < mIdle.size(); counter++) { @@ -197,6 +199,38 @@ namespace MWMechanics if(mIdleNow) { + // Play a random voice greeting if the player gets too close + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + + float hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); + float helloDistance = hello; + int iGreetDistanceMultiplier = store.get().find("iGreetDistanceMultiplier")->getInt(); + helloDistance *= iGreetDistanceMultiplier; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + float playerDist = Ogre::Vector3(player.getRefData().getPosition().pos).distance( + Ogre::Vector3(actor.getRefData().getPosition().pos)); + + if (!mSaidGreeting) + { + // TODO: check if actor is aware / has line of sight + if (playerDist <= helloDistance + // Only play a greeting if the player is not moving + && Ogre::Vector3(player.getClass().getMovementSettings(player).mPosition).squaredLength() == 0) + { + mSaidGreeting = true; + MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); + // TODO: turn to face player and interrupt the idle animation? + } + } + else + { + float fGreetDistanceReset = store.get().find("fGreetDistanceReset")->getFloat(); + if (playerDist >= fGreetDistanceReset * iGreetDistanceMultiplier) + mSaidGreeting = false; + } + + // Check if idle animation finished if(!checkIdle(actor, mPlayedIdle)) { mPlayedIdle = 0; diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 48bc62c62..9a44aa065 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -32,6 +32,8 @@ namespace MWMechanics std::vector mIdle; bool mRepeat; + bool mSaidGreeting; + float mX; float mY; float mZ; From 05e75e121432923ab94ca8fab39a8c266aa674a6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 20:27:33 +0100 Subject: [PATCH 124/301] Closes #1127: Fix for subtitles for idle dialogue appearing outside of hearing range --- apps/openmw/mwmechanics/aiwander.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 185805153..77b52f5be 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -191,8 +191,11 @@ namespace MWMechanics const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); float chance = store.get().find("fVoiceIdleOdds")->getFloat(); int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - // TODO: do not show subtitle messagebox if player is too far away? or do not say at all? - if (roll < chance) + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + // Don't bother if the player is out of hearing range + if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(actor.getRefData().getPosition().pos)) < 1500) MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } } From 339399f8b1dada292ccb038e4b7f039aecfc122d Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 19 Jan 2014 22:09:51 +0200 Subject: [PATCH 125/301] bug fix/logic fix/future suggestion --- apps/openmw/mwmechanics/aicombat.cpp | 27 +++++++++++++++---------- apps/openmw/mwmechanics/aicombat.hpp | 2 +- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 17 ++++++++++++++++ apps/openmw/mwmechanics/pathfinding.hpp | 5 +++++ 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6bf97f6ac..f12a10f9b 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -1,4 +1,5 @@ #include "aicombat.hpp" +#include "aifollow.hpp" #include "movement.hpp" @@ -39,7 +40,7 @@ namespace MWMechanics mTimerAttack(0), mTimerReact(0), mTimerCombatMove(0), - mCloseUp(false), + mFollowTarget(false), mReadyToAttack(false), mStrike(false), mCombatMove(false), @@ -184,7 +185,7 @@ namespace MWMechanics Ogre::Vector3 vDir = vDest - vStart; float distBetween = vDir.length(); - if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mCloseUp) ) + if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mFollowTarget) ) { //Melee and Close-up combat vDir.z = 0; @@ -194,13 +195,10 @@ namespace MWMechanics // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - if(mPathFinder.isPathConstructed()) - mPathFinder.clearPath(); - //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); - if (mCloseUp && distBetween > rangeMelee) + if (mFollowTarget && distBetween > rangeMelee) { //Close-up combat: just run up on target mMovement.mPosition[1] = 1; @@ -216,7 +214,7 @@ namespace MWMechanics mTimerCombatMove = 0.1f + 0.1f * static_cast(rand())/RAND_MAX; mCombatMove = true; } - else if(!distantCombat || (distantCombat && rangeMelee/5)) + else if(actor.getClass().isNpc() && (!distantCombat || (distantCombat && rangeMelee/5))) { //apply sideway movement (kind of dodging) with some probability if(static_cast(rand())/RAND_MAX < 0.25) @@ -234,13 +232,19 @@ namespace MWMechanics mReadyToAttack = true; //only once got in melee combat, actor is allowed to use close-up shortcutting - mCloseUp = true; + mFollowTarget = true; } } else { - //target is at far distance: build & follow the path - mCloseUp = false; + //target is at far distance: build path to target OR follow target (if previously actor had reached it once) + + /* + //apply when AIFOLLOW package implementation will be existent + if(mFollowTarget) + actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiFollow(mTarget));*/ + + mFollowTarget = false; buildNewPath(actor); @@ -342,7 +346,8 @@ namespace MWMechanics //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - mPathFinder = newPathFinder; + newPathFinder.syncStart(mPathFinder.getPath()); + mPathFinder = newPathFinder; } } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index cb9eee973..27f7f5d95 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -37,7 +37,7 @@ namespace MWMechanics float mTimerCombatMove; bool mReadyToAttack, mStrike; - bool mCloseUp; + bool mFollowTarget; bool mCombatMove; MWMechanics::Movement mMovement; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0ffeb52e5..967f7413a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -504,7 +504,7 @@ bool CharacterController::updateCreatureState() } mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, + MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); mUpperBodyState = UpperCharState_StartToMinAttack; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index c8bc9b49c..fd44fcc4c 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -4,6 +4,7 @@ #include "../mwbase/environment.hpp" #include "OgreMath.h" +#include "OgreVector3.h" #include #include @@ -233,5 +234,21 @@ namespace MWMechanics return false; } + + void PathFinder::syncStart(const std::list &path) + { + std::list::const_iterator oldStart = path.begin(); + std::list::iterator iter = ++mPath.begin(); + + if( (*iter).mX == oldStart->mX + && (*iter).mY == oldStart->mY + && (*iter).mZ == oldStart->mZ + && (*iter).mAutogenerated == oldStart->mAutogenerated + && (*iter).mConnectionNum == oldStart->mConnectionNum ) + { + mPath.pop_front(); + } + + } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 916df850b..de58f5db8 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -37,6 +37,11 @@ namespace MWMechanics return mPath; } + //When first point of newly created path is the nearest to actor point, then + //the cituation can occure when this point is undesirable (if the 2nd point of new path == the 1st point of old path) + //This functions deletes that point. + void syncStart(const std::list &path); + private: std::list mPath; bool mIsPathConstructed; From fec26342cd57269bc0801e5d901e00419fbc7cec Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 21:11:48 +0100 Subject: [PATCH 126/301] Query the number of animation groups for death/hit animations, since they can vary a lot (argonian/khajiit, creatures, first person..) --- apps/openmw/mwmechanics/character.cpp | 64 +++++++++------------------ apps/openmw/mwmechanics/character.hpp | 4 ++ 2 files changed, 24 insertions(+), 44 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ba6cb6449..3f29cbb7c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -62,26 +62,6 @@ struct StateInfo { const char groupname[32]; }; -static const std::string sDeathList[] = { - "death1" , - "death2" , - "death3" , - "death4" , - "death5" , - "swimdeath", -}; -static const int sDeathListSize = sizeof(sDeathList)/sizeof(sDeathList[0]); - -static const std::string sHitList[] = { - "hit1" , - "hit2" , - "hit3" , - "hit4" , - "hit5" , - "knockdown" , -}; -static const int sHitListSize = sizeof(sHitList)/sizeof(sHitList[0]); - static const StateInfo sMovementList[] = { { CharState_WalkForward, "walkforward" }, { CharState_WalkBack, "walkback" }, @@ -154,6 +134,17 @@ public: { return weap.type == type; } }; +std::string CharacterController::chooseRandomGroup (const std::string& prefix, int* num) +{ + int numAnims=0; + while (mAnimation->hasAnimation(prefix + Ogre::StringConverter::toString(numAnims+1))) + ++numAnims; + + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * numAnims + 1; // [1, numAnims] + if (num) + *num = roll; + return prefix + Ogre::StringConverter::toString(roll); +} void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { @@ -167,20 +158,13 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(knockdown) { mHitState = CharState_KnockDown; - mCurrentHit = sHitList[sHitListSize-1]; + mCurrentHit = "knockdown"; mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } else if (recovery) { mHitState = CharState_Hit; - int iHit = rand() % (sHitListSize-1); - mCurrentHit = sHitList[iHit]; - if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) - { - //only 3 different hit animations if player is in 1st person - int iHit = rand() % (sHitListSize-3); - mCurrentHit = sHitList[iHit]; - } + mCurrentHit = chooseRandomGroup("hit"); mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } @@ -387,28 +371,20 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I void CharacterController::playRandomDeath(float startpoint) { - if(MWWorld::Class::get(mPtr).isNpc()) + if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath")) { - if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) - { - mDeathState = CharState_SwimDeath; - mCurrentDeath = sDeathList[sDeathListSize-1]; //last in the list is 'swimdeath' - } - else - { - int num = rand() % (sDeathListSize-1); - mDeathState = static_cast(CharState_Death1 + num); - mCurrentDeath = sDeathList[num]; - } + mDeathState = CharState_SwimDeath; + mCurrentDeath = "swimdeath"; } else { - mDeathState = CharState_Death1; - mCurrentDeath = "death1"; + int selected=0; + mCurrentDeath = chooseRandomGroup("death", &selected); + mDeathState = static_cast(CharState_Death1 + (selected-1)); } mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, 1.0f, "start", "stop", 0.0f, 0); + false, 1.0f, "start", "stop", startpoint, 0); } CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 1b73670f9..ea12d9b94 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -177,6 +177,10 @@ class CharacterController void playRandomDeath(float startpoint = 0.0f); + /// choose a random animation group with \a prefix and numeric suffix + /// @param num if non-NULL, the chosen animation number will be written here + std::string chooseRandomGroup (const std::string& prefix, int* num = NULL); + public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); From 5dd3cfa09acdad237e507fd2063db5db9c9383b9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 11:50:52 +0100 Subject: [PATCH 127/301] Loop projectile sounds --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 46a1e9fda..2c825caa6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2177,7 +2177,7 @@ namespace MWWorld state.mStack = stack; MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f); + sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); mProjectiles[ptr] = state; } From ba5300b071b5e5e0f7ea8c9e1c8e06074d8601fd Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 12:05:13 +0100 Subject: [PATCH 128/301] Update the Ptr in SoundManager for references moved to a different cell. Fixes looping sounds not stopping after a moved object was already deleted. --- apps/openmw/mwbase/soundmanager.hpp | 2 ++ apps/openmw/mwsound/soundmanagerimp.cpp | 9 +++++++++ apps/openmw/mwsound/soundmanagerimp.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 1 + 4 files changed, 14 insertions(+) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 4d764597c..98b733c41 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 updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated) = 0; }; } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index bdd03a8b4..d39602516 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -640,6 +640,15 @@ namespace MWSound mListenerUp = up; } + void SoundManager::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated) + { + for (SoundMap::iterator snditer = mActiveSounds.begin(); snditer != mActiveSounds.end(); ++snditer) + { + if (snditer->second.first == old) + snditer->second.first = updated; + } + } + // Default readAll implementation, for decoders that can't do anything // better void Sound_Decoder::readAll(std::vector &output) diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index f62e62d50..4fd1d3d48 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 updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2c825caa6..9be7830cf 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -905,6 +905,7 @@ namespace MWWorld MWWorld::Class::get(ptr).copyToCell(ptr, newCell, pos); mRendering->updateObjectCell(ptr, copy); + MWBase::Environment::get().getSoundManager()->updatePtr (ptr, copy); MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->updateCell(ptr, copy); From 205e8aa4e95129db49032db48b2a8fc1c7cc34f1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:00:43 +0100 Subject: [PATCH 129/301] Feature #957: Implement area magic --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 + apps/openmw/mwmechanics/actors.cpp | 9 +++ apps/openmw/mwmechanics/actors.hpp | 2 + .../mwmechanics/mechanicsmanagerimp.cpp | 6 ++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 + apps/openmw/mwmechanics/objects.cpp | 9 +++ apps/openmw/mwmechanics/objects.hpp | 2 + apps/openmw/mwrender/effectmanager.cpp | 6 +- apps/openmw/mwrender/effectmanager.hpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 6 +- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 75 +++++++++++++++---- 12 files changed, 102 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 726c8cf04..914fbded2 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -157,6 +157,8 @@ namespace MWBase virtual void toggleAI() = 0; virtual bool isAIActive() = 0; + + virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& objects) = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index f7d5d3a1b..35783bd9f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -919,4 +919,13 @@ namespace MWMechanics return iter->second->isAnimPlaying(groupName); return false; } + + void Actors::getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out) + { + for (PtrControllerMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) + { + if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(position) <= radius*radius) + out.push_back(iter->first); + } + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index b7544dad4..78aae6861 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -94,6 +94,8 @@ namespace MWMechanics void skipAnimation(const MWWorld::Ptr& ptr); bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName); + void getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out); + private: PtrControllerMap mActors; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fff3db8a9..a0183e973 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -950,4 +950,10 @@ namespace MWMechanics return (roll >= target); } + + void MechanicsManager::getObjectsInRange(const Ogre::Vector3 &position, float radius, std::vector &objects) + { + mActors.getObjectsInRange(position, radius, objects); + mObjects.getObjectsInRange(position, radius, objects); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 469123df9..012de2e32 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -135,6 +135,8 @@ namespace MWMechanics virtual void toggleAI(); virtual bool isAIActive(); + + virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& objects); }; } diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 41d6b4ffa..b09574923 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -92,4 +92,13 @@ void Objects::skipAnimation(const MWWorld::Ptr& ptr) iter->second->skipAnim(); } +void Objects::getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out) +{ + for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter) + { + if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(position) <= radius*radius) + out.push_back(iter->first); + } +} + } diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp index 32432c130..373a2a105 100644 --- a/apps/openmw/mwmechanics/objects.hpp +++ b/apps/openmw/mwmechanics/objects.hpp @@ -40,6 +40,8 @@ namespace MWMechanics void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); + + void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& out); }; } diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index eb4525a4f..7d41525b7 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -26,9 +26,10 @@ EffectManager::EffectManager(Ogre::SceneManager *sceneMgr) { } -void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition) +void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition, float scale) { Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition); + sceneNode->setScale(scale,scale,scale); // fix texture extension to .dds if (textureOverride.size() > 4) @@ -78,7 +79,7 @@ void EffectManager::addEffect(const std::string &model, std::string textureOverr mEffects.push_back(std::make_pair(sceneNode, scene)); } -void EffectManager::update(float dt) +void EffectManager::update(float dt, Ogre::Camera* camera) { for (std::vector >::iterator it = mEffects.begin(); it != mEffects.end(); ) { @@ -91,6 +92,7 @@ void EffectManager::update(float dt) objects->mControllers[i].update(); } + objects->rotateBillboardNodes(camera); // Finished playing? if (objects->mControllers[0].getSource()->getValue() >= objects->mMaxControllerLength) diff --git a/apps/openmw/mwrender/effectmanager.hpp b/apps/openmw/mwrender/effectmanager.hpp index 0c8bc3857..bc9e68d26 100644 --- a/apps/openmw/mwrender/effectmanager.hpp +++ b/apps/openmw/mwrender/effectmanager.hpp @@ -14,9 +14,9 @@ namespace MWRender ~EffectManager() { clear(); } /// Add an effect. When it's finished playing, it will be removed automatically. - void addEffect (const std::string& model, std::string textureOverride, const Ogre::Vector3& worldPosition); + void addEffect (const std::string& model, std::string textureOverride, const Ogre::Vector3& worldPosition, float scale); - void update(float dt); + void update(float dt, Ogre::Camera* camera); /// Remove all effects void clear(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 572d2abdb..515112668 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -378,7 +378,7 @@ void RenderingManager::update (float duration, bool paused) if(paused) return; - mEffectManager->update(duration); + mEffectManager->update(duration, mRendering.getCamera()); mActors->update (mRendering.getCamera()); mPlayerAnimation->preRender(mRendering.getCamera()); @@ -1024,9 +1024,9 @@ float RenderingManager::getCameraDistance() const return mCamera->getCameraDistance(); } -void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition) +void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition, float scale) { - mEffectManager->addEffect(model, texture, worldPosition); + mEffectManager->addEffect(model, "", worldPosition, scale); } } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b6379bee4..ea9a64120 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -207,7 +207,7 @@ public: void stopVideo(); void frameStarted(float dt, bool paused); - void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition); + void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition, float scale=1.f); protected: virtual void windowResized(int x, int y); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9be7830cf..ccb16d5fb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2174,9 +2174,16 @@ namespace MWWorld state.mId = id; state.mActorHandle = actor.getRefData().getHandle(); state.mSpeed = speed; - state.mEffects = effects; state.mStack = stack; + // Only interested in "on target" effects + for (std::vector::const_iterator iter (effects.mList.begin()); + iter!=effects.mList.end(); ++iter) + { + if (iter->mRange == ESM::RT_Target) + state.mEffects.mList.push_back(*iter); + } + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); @@ -2223,30 +2230,68 @@ namespace MWWorld continue; explode = true; + } - MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); - if (caster.isEmpty()) - caster = obstacle; - if (obstacle.isEmpty()) + if (explode) + { + std::map > toApply; + for (std::vector::const_iterator effectIt = it->second.mEffects.mList.begin(); + effectIt != it->second.mEffects.mList.end(); ++effectIt) { - // Terrain + const ESM::MagicEffect* effect = getStore().get().find(effectIt->mEffectID); + + // Spawn the explosion orb effect + const ESM::Static* areaStatic; + if (!effect->mCasting.empty()) + areaStatic = getStore().get().find (effect->mArea); + else + areaStatic = getStore().get().find ("VFX_DefaultArea"); + + mRendering->spawnEffect("meshes\\" + areaStatic->mModel, "", Ogre::Vector3(ptr.getRefData().getPosition().pos), effectIt->mArea); + + // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now) + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mAreaSound.empty()) + sndMgr->playSound3D(ptr, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + else + sndMgr->playSound3D(ptr, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + + // Get the actors in range of the effect + std::vector objects; + MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( + Ogre::Vector3(ptr.getRefData().getPosition().pos), feetToGameUnits(effectIt->mArea), objects); + + for (std::vector::iterator affected = objects.begin(); affected != objects.end(); ++affected) + toApply[*affected].push_back(*effectIt); } - else + + // Now apply the appropriate effects to each actor in range + for (std::map >::iterator apply = toApply.begin(); apply != toApply.end(); ++apply) { - MWMechanics::CastSpell cast(caster, obstacle); - cast.mStack = it->second.mStack; + MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + + // Vanilla-compatible behaviour of never applying the spell to the caster + // (could be changed by mods later) + if (apply->first == caster) + continue; + + if (caster.isEmpty()) + caster = apply->first; + + MWMechanics::CastSpell cast(caster, apply->first); cast.mId = it->second.mId; cast.mSourceName = it->second.mSourceName; - cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target, false); + cast.mStack = it->second.mStack; + ESM::EffectList effects; + effects.mList = apply->second; + cast.inflict(apply->first, caster, effects, ESM::RT_Target); } deleteObject(ptr); mProjectiles.erase(it++); - } - - if (explode) - { - // TODO: Explode continue; } From 9cedd3bb2ee8c67b42b57103ef40997ff54e271f Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:05:38 +0100 Subject: [PATCH 130/301] Fix spell area not displaying in tooltip --- apps/openmw/mwgui/tooltips.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index b52c8e3dd..0fe500879 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -211,6 +211,7 @@ namespace MWGui params.mMagnMin = it->mMagnMin; params.mMagnMax = it->mMagnMax; params.mRange = it->mRange; + params.mArea = it->mArea; params.mIsConstant = (spell->mData.mType == ESM::Spell::ST_Ability); params.mNoTarget = false; effects.push_back(params); From bd34b61f2ae2037b4d701cbb4ad0a4cf83d56e7a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:37:34 +0100 Subject: [PATCH 131/301] Set all keyframe-controlled bones as manually controlled --- components/nifogre/ogrenifloader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 5a76b702e..d036844fc 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -917,6 +917,8 @@ class NIFObjectLoader { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + // The keyframe controller will control this bone manually + trgtbone->setManuallyControlled(true); Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); From 42284603f7624337ad900a338e69b1fe89b50dae Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:43:21 +0100 Subject: [PATCH 132/301] Properly handle on target effects with mArea=0 --- apps/openmw/mwworld/worldimp.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ccb16d5fb..419558387 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2229,6 +2229,23 @@ namespace MWWorld if (obstacle == ptr) continue; + MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + if (caster.isEmpty()) + caster = obstacle; + + if (obstacle.isEmpty()) + { + // Terrain + } + else + { + MWMechanics::CastSpell cast(caster, obstacle); + cast.mId = it->second.mId; + cast.mSourceName = it->second.mSourceName; + cast.mStack = it->second.mStack; + cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target); + } + explode = true; } @@ -2240,6 +2257,9 @@ namespace MWWorld { const ESM::MagicEffect* effect = getStore().get().find(effectIt->mEffectID); + if (effectIt->mArea <= 0) + continue; // Not an area effect + // Spawn the explosion orb effect const ESM::Static* areaStatic; if (!effect->mCasting.empty()) From c8a9e9f7fa467d54971237f653c5e095b1cc6978 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:54:10 +0100 Subject: [PATCH 133/301] Stop AiCombat when the target is dead --- apps/openmw/mwmechanics/aicombat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4053e640f..c01c4096b 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -52,7 +52,7 @@ namespace MWMechanics //General description if(!actor.getClass().getCreatureStats(actor).isHostile()) return true; - if(actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0) + if (mTarget.getClass().getCreatureStats(mTarget).isDead()) return true; //Update every frame From 33620a001b802ed8c8ae8c61db9afcc4619ad20b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 13:59:00 +0100 Subject: [PATCH 134/301] Cloning works for single record type tables. Well, kinda. --- apps/opencs/model/world/collection.hpp | 27 ++++- apps/opencs/model/world/collectionbase.hpp | 5 + apps/opencs/model/world/commands.cpp | 124 +++++++++++--------- apps/opencs/model/world/commands.hpp | 6 +- apps/opencs/model/world/idtable.cpp | 5 +- apps/opencs/model/world/idtable.hpp | 5 +- apps/opencs/model/world/refidcollection.cpp | 8 ++ apps/opencs/model/world/refidcollection.hpp | 5 + apps/opencs/view/world/creator.hpp | 2 +- apps/opencs/view/world/genericcreator.cpp | 15 ++- apps/opencs/view/world/genericcreator.hpp | 6 +- apps/opencs/view/world/tablebottombox.cpp | 6 +- apps/opencs/view/world/tablebottombox.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 6 +- apps/opencs/view/world/tablesubview.hpp | 4 +- 15 files changed, 149 insertions(+), 77 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index df144ba7b..0b9b44fa9 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -97,7 +97,12 @@ namespace CSMWorld virtual void appendBlankRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types - + + virtual void cloneRecord(const std::string& origin, + const std::string& destination, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType); + virtual int searchId (const std::string& id) const; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) @@ -193,6 +198,26 @@ namespace CSMWorld return true; } + template + void Collection::cloneRecord(const std::string& origin, + const std::string& destination, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType) + { + Record copy = getRecord(origin); + if (copy.isDeleted()) + { + return; + } + + if (argumentType == UniversalId::ArgumentType_Id) + { + copy.get().mId = Misc::StringUtils::lowerCase(destination); + } + + insertRecord(copy, getAppendIndex(destination, type)); + } + template Collection::Collection() {} diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index ab7a9ebec..d32847490 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -74,6 +74,11 @@ namespace CSMWorld UniversalId::Type type = UniversalId::Type_None) = 0; ///< If the record type does not match, an exception is thrown. + virtual void cloneRecord(const std::string& origin, + const std::string& destination, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType) = 0; + virtual const RecordBase& getRecord (const std::string& id) const = 0; virtual const RecordBase& getRecord (int index) const = 0; diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 281afb15f..75b88766a 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -6,81 +6,88 @@ #include "idtable.hpp" #include "idtable.hpp" -CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, - const QVariant& new_, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) +CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, + const QVariant& new_, QUndoCommand* parent) + : QUndoCommand(parent), mModel(model), mIndex(index), mNew(new_) { - mOld = mModel.data (mIndex, Qt::EditRole); + mOld = mModel.data(mIndex, Qt::EditRole); - setText ("Modify " + mModel.headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); + setText("Modify " + mModel.headerData(mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); } void CSMWorld::ModifyCommand::redo() { - mModel.setData (mIndex, mNew); + mModel.setData(mIndex, mNew); } void CSMWorld::ModifyCommand::undo() { - mModel.setData (mIndex, mOld); + mModel.setData(mIndex, mOld); } -CSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model, - const std::string& idOrigin, - const std::string& IdDestination, - CSMWorld::UniversalId::Type type, - QUndoCommand* parent) : - QUndoCommand(parent), - mModel(model), - mIdOrigin(idOrigin), +CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, + const std::string& idOrigin, + const std::string& IdDestination, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType, + QUndoCommand* parent) : + QUndoCommand(parent), + mModel(model), + mIdOrigin(idOrigin), mIdDestination(IdDestination), + mArgumentType(argumentType), mType(type) { - setText (("Clone record " + idOrigin + " to the " + IdDestination).c_str()); + setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); } void CSMWorld::CloneCommand::redo() { - mModel.cloneRecord(mIdOrigin, mIdDestination, mType); + mModel.cloneRecord(mIdOrigin, mIdDestination, mArgumentType, mType); } +void CSMWorld::CloneCommand::undo() +{ + mModel.removeRow(mModel.getModelIndex(mIdDestination, 0).row()); +} -CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) + +CSMWorld::CreateCommand::CreateCommand(IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand(parent), mModel(model), mId(id), mType(UniversalId::Type_None) { - setText (("Create record " + id).c_str()); + setText(("Create record " + id).c_str()); } -void CSMWorld::CreateCommand::addValue (int column, const QVariant& value) +void CSMWorld::CreateCommand::addValue(int column, const QVariant& value) { mValues[column] = value; } -void CSMWorld::CreateCommand::setType (UniversalId::Type type) +void CSMWorld::CreateCommand::setType(UniversalId::Type type) { mType = type; } void CSMWorld::CreateCommand::redo() { - mModel.addRecord (mId, mType); + mModel.addRecord(mId, mType); - for (std::map::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) - mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); + for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) + mModel.setData(mModel.getModelIndex(mId, iter->first), iter->second); } void CSMWorld::CreateCommand::undo() { - mModel.removeRow (mModel.getModelIndex (mId, 0).row()); + mModel.removeRow(mModel.getModelIndex(mId, 0).row()); } -CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mId (id), mOld (0) +CSMWorld::RevertCommand::RevertCommand(IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand(parent), mModel(model), mId(id), mOld(0) { - setText (("Revert record " + id).c_str()); + setText(("Revert record " + id).c_str()); - mOld = model.getRecord (id).clone(); + mOld = model.getRecord(id).clone(); } CSMWorld::RevertCommand::~RevertCommand() @@ -90,32 +97,32 @@ CSMWorld::RevertCommand::~RevertCommand() void CSMWorld::RevertCommand::redo() { - int column = mModel.findColumnIndex (Columns::ColumnId_Modification); + int column = mModel.findColumnIndex(Columns::ColumnId_Modification); - QModelIndex index = mModel.getModelIndex (mId, column); - RecordBase::State state = static_cast (mModel.data (index).toInt()); + QModelIndex index = mModel.getModelIndex(mId, column); + RecordBase::State state = static_cast(mModel.data(index).toInt()); - if (state==RecordBase::State_ModifiedOnly) + if (state == RecordBase::State_ModifiedOnly) { - mModel.removeRows (index.row(), 1); + mModel.removeRows(index.row(), 1); } else { - mModel.setData (index, static_cast (RecordBase::State_BaseOnly)); + mModel.setData(index, static_cast(RecordBase::State_BaseOnly)); } } void CSMWorld::RevertCommand::undo() { - mModel.setRecord (mId, *mOld); + mModel.setRecord(mId, *mOld); } -CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mId (id), mOld (0) +CSMWorld::DeleteCommand::DeleteCommand(IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand(parent), mModel(model), mId(id), mOld(0) { - setText (("Delete record " + id).c_str()); + setText(("Delete record " + id).c_str()); - mOld = model.getRecord (id).clone(); + mOld = model.getRecord(id).clone(); } CSMWorld::DeleteCommand::~DeleteCommand() @@ -125,44 +132,45 @@ CSMWorld::DeleteCommand::~DeleteCommand() void CSMWorld::DeleteCommand::redo() { - int column = mModel.findColumnIndex (Columns::ColumnId_Modification); + int column = mModel.findColumnIndex(Columns::ColumnId_Modification); - QModelIndex index = mModel.getModelIndex (mId, column); - RecordBase::State state = static_cast (mModel.data (index).toInt()); + QModelIndex index = mModel.getModelIndex(mId, column); + RecordBase::State state = static_cast(mModel.data(index).toInt()); - if (state==RecordBase::State_ModifiedOnly) + if (state == RecordBase::State_ModifiedOnly) { - mModel.removeRows (index.row(), 1); + mModel.removeRows(index.row(), 1); } else { - mModel.setData (index, static_cast (RecordBase::State_Deleted)); + mModel.setData(index, static_cast(RecordBase::State_Deleted)); } } void CSMWorld::DeleteCommand::undo() { - mModel.setRecord (mId, *mOld); + mModel.setRecord(mId, *mOld); } -CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex, - const std::vector& newOrder) -: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) +CSMWorld::ReorderRowsCommand::ReorderRowsCommand(IdTable& model, int baseIndex, + const std::vector& newOrder) + : mModel(model), mBaseIndex(baseIndex), mNewOrder(newOrder) {} void CSMWorld::ReorderRowsCommand::redo() { - mModel.reorderRows (mBaseIndex, mNewOrder); + mModel.reorderRows(mBaseIndex, mNewOrder); } void CSMWorld::ReorderRowsCommand::undo() { - int size = static_cast (mNewOrder.size()); - std::vector reverse (size); + int size = static_cast(mNewOrder.size()); + std::vector reverse(size); - for (int i=0; i mValues; public: CloneCommand (IdTable& model, const std::string& idOrigin, const std::string& IdDestination, - UniversalId::Type type, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType, QUndoCommand* parent = 0); virtual void redo(); -// virtual void undo(); + virtual void undo(); }; class CreateCommand : public QUndoCommand diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index b90bf1a9b..daa6bc51b 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -124,11 +124,12 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type endInsertRows(); } -void CSMWorld::IdTable::cloneRecord(const std::string& origin, +void CSMWorld::IdTable::cloneRecord(const std::string& origin, const std::string& destination, + CSMWorld::UniversalId::ArgumentType argumentType, CSMWorld::UniversalId::Type type) { - + mIdCollection->cloneRecord(origin, destination, type, argumentType); } diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index dfbc04134..ce47307e3 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -63,7 +63,10 @@ namespace CSMWorld void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types - void cloneRecord(const std::string& origin, const std::string& destination, UniversalId::Type type = UniversalId::Type_None); + void cloneRecord(const std::string& origin, + const std::string& destination, + UniversalId::ArgumentType argumentType, + UniversalId::Type type = UniversalId::Type_None); QModelIndex getModelIndex (const std::string& id, int column) const; diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 86a542c5c..896ee9010 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -449,6 +449,14 @@ void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) mData.getRecord (mData.globalToLocalIndex (index)).assign (record); } +void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, + const std::string& destination, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType) +{ + +} + void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, UniversalId::Type type) { diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 5ff4a70bf..4ad181004 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -69,6 +69,11 @@ namespace CSMWorld virtual void removeRows (int index, int count); + virtual void cloneRecord(const std::string& origin, + const std::string& destination, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType); + virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 58a5e21ad..5eaf3e178 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -25,7 +25,7 @@ namespace CSVWorld virtual void reset() = 0; - virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type) = 0; + virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) = 0; virtual void setEditLock (bool locked) = 0; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index f21a62d97..efdd9ecaf 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -59,7 +59,8 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, bool relaxedIdRules) -: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mCloneMode(false), mClonedType(CSMWorld::UniversalId::Type_None) +: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mCloneMode(false), mClonedType(CSMWorld::UniversalId::Type_None), +mArgumentType(CSMWorld::UniversalId::ArgumentType_None) { mLayout = new QHBoxLayout; mLayout->setContentsMargins (0, 0, 0, 0); @@ -126,7 +127,12 @@ void CSVWorld::GenericCreator::create() { std::string id = getId(); std::auto_ptr command (new CSMWorld::CloneCommand ( - dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType)); + dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType, mArgumentType)); + + mUndoStack.push(command.release()); + + emit done(); + emit requestFocus(id); } else { std::string id = getId(); @@ -143,11 +149,14 @@ void CSVWorld::GenericCreator::create() } } -void CSVWorld::GenericCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type) +void CSVWorld::GenericCreator::cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType) { mCloneMode = true; mClonedId = originid; mClonedType = type; + mArgumentType = argumentType; mId->setText(QString::fromStdString(mClonedId)); } diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index a1acb2e79..867595bb5 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -32,6 +32,7 @@ namespace CSVWorld bool mCloneMode; std::string mClonedId; CSMWorld::UniversalId::Type mClonedType; + CSMWorld::UniversalId::ArgumentType mArgumentType; protected: @@ -61,13 +62,14 @@ namespace CSVWorld virtual void reset(); - virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type); + virtual void cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. - private slots: void textChanged (const QString& text); diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index b89f5180b..60a206d0c 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -158,10 +158,12 @@ void CSVWorld::TableBottomBox::createRequest() mCreating = true; } -void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type) +void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumnetType) { mCreator->reset(); - mCreator->cloneMode(id, type); + mCreator->cloneMode(id, type, argumnetType); mLayout->setCurrentWidget(mCreator); setVisible (true); mCreating = true; diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 58a6a5a2f..29afa2277 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -77,7 +77,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); - void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); + void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType); }; } diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 36200dbbc..7bb852cbe 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -50,8 +50,8 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); connect (mTable, SIGNAL (cloneRequest(int)), this, SLOT(cloneRequest(int))); - connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), - mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType)), + mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType))); } connect (mBottom, SIGNAL (requestFocus (const std::string&)), mTable, SLOT (requestFocus (const std::string&))); @@ -85,5 +85,5 @@ void CSVWorld::TableSubView::setStatusBar (bool show) void CSVWorld::TableSubView::cloneRequest(int row) { const CSMWorld::UniversalId& toClone(mTable->getUniversalId(row)); - emit cloneRequest(toClone.getId(), toClone.getType()); + emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); } diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 5b19110b6..01df204de 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -35,7 +35,9 @@ namespace CSVWorld virtual void setStatusBar (bool show); signals: - void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); + void cloneRequest(const std::string& id, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType); private slots: From a45339bbe6ecd68ab2d5704649582ad3e3cd06b1 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 14:14:59 +0100 Subject: [PATCH 135/301] lower case in command, not in the collection --- apps/opencs/model/world/collection.hpp | 2 +- apps/opencs/model/world/commands.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 0b9b44fa9..091496926 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -212,7 +212,7 @@ namespace CSMWorld if (argumentType == UniversalId::ArgumentType_Id) { - copy.get().mId = Misc::StringUtils::lowerCase(destination); + copy.get().mId = destination; } insertRecord(copy, getAppendIndex(destination, type)); diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 75b88766a..f16117019 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -2,7 +2,7 @@ #include "commands.hpp" #include - +#include #include "idtable.hpp" #include "idtable.hpp" @@ -34,7 +34,7 @@ CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, QUndoCommand(parent), mModel(model), mIdOrigin(idOrigin), - mIdDestination(IdDestination), + mIdDestination(Misc::StringUtils::lowerCase(IdDestination)), mArgumentType(argumentType), mType(type) { From 38636fab9a60a442fce8f04de170827675194f6f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 14:29:47 +0100 Subject: [PATCH 136/301] added setId to adapter --- apps/opencs/model/world/commands.cpp | 3 +++ apps/opencs/model/world/refidadapter.hpp | 1 + apps/opencs/model/world/refidadapterimp.hpp | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index f16117019..b7c88e872 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -45,6 +45,9 @@ CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, void CSMWorld::CloneCommand::redo() { mModel.cloneRecord(mIdOrigin, mIdDestination, mArgumentType, mType); + + for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) + mModel.setData(mModel.getModelIndex(mIdDestination, iter->first), iter->second); } void CSMWorld::CloneCommand::undo() diff --git a/apps/opencs/model/world/refidadapter.hpp b/apps/opencs/model/world/refidadapter.hpp index df0ceae70..0870a2d3e 100644 --- a/apps/opencs/model/world/refidadapter.hpp +++ b/apps/opencs/model/world/refidadapter.hpp @@ -31,6 +31,7 @@ namespace CSMWorld ///< If the data type does not match an exception is thrown. virtual std::string getId (const RecordBase& record) const = 0; + virtual void setId(RecordBase& record, const std::string& id) = 0; }; } diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index d5d84a8f7..bd509a86b 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -34,6 +34,8 @@ namespace CSMWorld BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base); virtual std::string getId (const RecordBase& record) const; + + virtual void setId (RecordBase& record, const std::string& id); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; @@ -50,6 +52,12 @@ namespace CSMWorld : mType (type), mBase (base) {} + template + void BaseRefIdAdapter::setId (RecordBase& record, const std::string& id) + { + (dynamic_cast&> (record).get().mId) = id; + } + template std::string BaseRefIdAdapter::getId (const RecordBase& record) const { From 5a527157013f97bec71790f7bbbedc29e92779c5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 15:58:19 +0100 Subject: [PATCH 137/301] implemented check for deletion. But it seems flawed. --- apps/opencs/model/world/data.cpp | 21 +++++++++++++++++++++ apps/opencs/model/world/data.hpp | 2 ++ apps/opencs/view/world/genericcreator.cpp | 7 +++++-- apps/opencs/view/world/table.cpp | 3 ++- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 04e35cdaa..8a7dc3971 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -657,6 +657,27 @@ int CSMWorld::Data::count (RecordBase::State state) const count (state, mReferenceables); } +bool CSMWorld::Data::recordDeleted(const std::string& id) const +{ + return + getGlobals().getRecord(id).isDeleted() || + getGmsts().getRecord(id).isDeleted() || + getSkills().getRecord(id).isDeleted() || + getClasses().getRecord(id).isDeleted() || + getFactions().getRecord(id).isDeleted() || + getRaces().getRecord(id).isDeleted() || + getSounds().getRecord(id).isDeleted() || + getScripts().getRecord(id).isDeleted() || + getRegions().getRecord(id).isDeleted() || + getBirthsigns().getRecord(id).isDeleted() || + getSpells().getRecord(id).isDeleted() || + getTopics().getRecord(id).isDeleted() || + getJournals().getRecord(id).isDeleted() || + getCells().getRecord(id).isDeleted() || + getReferenceables().getRecord(id).isDeleted(); +} + + void CSMWorld::Data::setDescription (const std::string& description) { mDescription = description; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 152c3ac41..df51c936a 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -189,6 +189,8 @@ namespace CSMWorld void setAuthor (const std::string& author); std::string getAuthor() const; + + bool recordDeleted(const std::string& id) const; signals: diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index efdd9ecaf..16bd04ece 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -125,10 +125,13 @@ void CSVWorld::GenericCreator::create() { if (mCloneMode) { - std::string id = getId(); + const std::string id = getId(); + if (mData.recordDeleted(id)) + { + return; + } std::auto_ptr command (new CSMWorld::CloneCommand ( dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType, mArgumentType)); - mUndoStack.push(command.release()); emit done(); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 31a7d8b58..4577c40dc 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -308,9 +308,10 @@ void CSVWorld::Table::cloneRecord() if (!mEditLock) { QModelIndexList selectedRows = selectionModel()->selectedRows(); - if (selectedRows.size()==1) + { emit cloneRequest (selectedRows.begin()->row()); + } } } From d82f272e0546c9cb270dcc7f0951a7ead0489903 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 16:17:49 +0100 Subject: [PATCH 138/301] Properly check if clone is deleted. --- apps/opencs/view/world/table.cpp | 2 +- apps/opencs/view/world/table.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 11 +++++++---- apps/opencs/view/world/tablesubview.hpp | 7 ++++++- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 4577c40dc..02a93161c 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -310,7 +310,7 @@ void CSVWorld::Table::cloneRecord() QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) { - emit cloneRequest (selectedRows.begin()->row()); + emit cloneRequest (selectedRows.begin()->row(), mModel); } } } diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index f4a5d3c9e..118bd3cf2 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -74,7 +74,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); - void cloneRequest(int row); + void cloneRequest(int row, const CSMWorld::IdTable* table); private slots: diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 7bb852cbe..0cdc57f5f 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -6,7 +6,7 @@ #include "../../model/doc/document.hpp" #include "../filter/filterbox.hpp" - +#include "../../model/world/idtable.hpp" #include "table.hpp" #include "tablebottombox.hpp" #include "creator.hpp" @@ -49,7 +49,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - connect (mTable, SIGNAL (cloneRequest(int)), this, SLOT(cloneRequest(int))); + connect (mTable, SIGNAL (cloneRequest(int, CSMWorld::IdTable*)), this, SLOT(cloneRequest(int, CSMWorld::IdTable*))); connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType))); } @@ -82,8 +82,11 @@ void CSVWorld::TableSubView::setStatusBar (bool show) mBottom->setStatusBar (show); } -void CSVWorld::TableSubView::cloneRequest(int row) +void CSVWorld::TableSubView::cloneRequest(int row, const CSMWorld::IdTable* table) { const CSMWorld::UniversalId& toClone(mTable->getUniversalId(row)); - emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); + if (!(table->getRecord(toClone.getId()).isDeleted())) + { + emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); + } } diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 01df204de..650a098f6 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -5,6 +5,11 @@ class QModelIndex; +namespace CSMWorld +{ + class IdTable; +} + namespace CSMDoc { class Document; @@ -42,7 +47,7 @@ namespace CSVWorld private slots: void editRequest (int row); - void cloneRequest (int row); + void cloneRequest (int row, const CSMWorld::IdTable* table); }; } From 92ee252eef54b0e9072a39803087e81ccd7bff62 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 16:23:34 +0100 Subject: [PATCH 139/301] Small correction. --- apps/opencs/model/world/data.cpp | 21 --------------------- apps/opencs/model/world/data.hpp | 2 -- apps/opencs/view/world/genericcreator.cpp | 7 ++----- apps/opencs/view/world/table.cpp | 1 + apps/opencs/view/world/table.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 2 +- 6 files changed, 5 insertions(+), 30 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 8a7dc3971..04e35cdaa 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -657,27 +657,6 @@ int CSMWorld::Data::count (RecordBase::State state) const count (state, mReferenceables); } -bool CSMWorld::Data::recordDeleted(const std::string& id) const -{ - return - getGlobals().getRecord(id).isDeleted() || - getGmsts().getRecord(id).isDeleted() || - getSkills().getRecord(id).isDeleted() || - getClasses().getRecord(id).isDeleted() || - getFactions().getRecord(id).isDeleted() || - getRaces().getRecord(id).isDeleted() || - getSounds().getRecord(id).isDeleted() || - getScripts().getRecord(id).isDeleted() || - getRegions().getRecord(id).isDeleted() || - getBirthsigns().getRecord(id).isDeleted() || - getSpells().getRecord(id).isDeleted() || - getTopics().getRecord(id).isDeleted() || - getJournals().getRecord(id).isDeleted() || - getCells().getRecord(id).isDeleted() || - getReferenceables().getRecord(id).isDeleted(); -} - - void CSMWorld::Data::setDescription (const std::string& description) { mDescription = description; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index df51c936a..152c3ac41 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -189,8 +189,6 @@ namespace CSMWorld void setAuthor (const std::string& author); std::string getAuthor() const; - - bool recordDeleted(const std::string& id) const; signals: diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 16bd04ece..efdd9ecaf 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -125,13 +125,10 @@ void CSVWorld::GenericCreator::create() { if (mCloneMode) { - const std::string id = getId(); - if (mData.recordDeleted(id)) - { - return; - } + std::string id = getId(); std::auto_ptr command (new CSMWorld::CloneCommand ( dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType, mArgumentType)); + mUndoStack.push(command.release()); emit done(); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 02a93161c..cc1f814dd 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -308,6 +308,7 @@ void CSVWorld::Table::cloneRecord() if (!mEditLock) { QModelIndexList selectedRows = selectionModel()->selectedRows(); + if (selectedRows.size()==1) { emit cloneRequest (selectedRows.begin()->row(), mModel); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 118bd3cf2..af2d7877f 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -74,7 +74,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); - void cloneRequest(int row, const CSMWorld::IdTable* table); + void cloneRequest(int row, const CSMWorld::IdTable*); private slots: diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 0cdc57f5f..765055610 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -49,7 +49,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - connect (mTable, SIGNAL (cloneRequest(int, CSMWorld::IdTable*)), this, SLOT(cloneRequest(int, CSMWorld::IdTable*))); + connect (mTable, SIGNAL (cloneRequest(int, const CSMWorld::IdTable*)), this, SLOT(cloneRequest(int, const CSMWorld::IdTable*))); connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType))); } From d0a52b7b242e114d39a5fd25720a62a972eebcbe Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 18:28:06 +0100 Subject: [PATCH 140/301] mostly refreshing view. --- apps/opencs/model/world/idtable.cpp | 5 +++++ apps/opencs/model/world/refidcollection.cpp | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index daa6bc51b..d4008fb87 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -129,7 +129,12 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin, CSMWorld::UniversalId::ArgumentType argumentType, CSMWorld::UniversalId::Type type) { + int index = mIdCollection->getAppendIndex (destination); + beginInsertRows (QModelIndex(), index, index); mIdCollection->cloneRecord(origin, destination, type, argumentType); + endInsertRows(); + emit dataChanged (CSMWorld::IdTable::index (0, 0), + CSMWorld::IdTable::index (mIdCollection->getSize()-1, mIdCollection->getColumns()-1)); } diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 896ee9010..74f6f1359 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -454,7 +454,11 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) { - + RecordBase *newRecord = mData.getRecord(mData.searchId(origin)).clone(); + newRecord->mState = RecordBase::State_ModifiedOnly; + mAdapters.find(type)->second->setId(*newRecord, destination); + mData.getAppendIndex(type); + //WIP } void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, From 7594bcf97aa0813d3517f2f53f2d1d633c22aee6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 21 Jan 2014 08:27:29 +0100 Subject: [PATCH 141/301] Small refactoring. --- apps/opencs/view/world/genericcreator.cpp | 2 +- apps/opencs/view/world/table.cpp | 7 ++++--- apps/opencs/view/world/table.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 10 +++------- apps/opencs/view/world/tablesubview.hpp | 8 ++++---- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index efdd9ecaf..163842ab8 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -149,7 +149,7 @@ void CSVWorld::GenericCreator::create() } } -void CSVWorld::GenericCreator::cloneMode(const std::string& originid, +void CSVWorld::GenericCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) { diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index cc1f814dd..d343f9986 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -16,6 +16,7 @@ #include "recordstatusdelegate.hpp" #include "util.hpp" +#include void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { @@ -308,10 +309,10 @@ void CSVWorld::Table::cloneRecord() if (!mEditLock) { QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size()==1) + const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); + if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) { - emit cloneRequest (selectedRows.begin()->row(), mModel); + emit cloneRequest (toClone); } } } diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index af2d7877f..d30083333 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -74,7 +74,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); - void cloneRequest(int row, const CSMWorld::IdTable*); + void cloneRequest(const CSMWorld::UniversalId&); private slots: diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 765055610..3c71d1370 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -49,7 +49,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - connect (mTable, SIGNAL (cloneRequest(int, const CSMWorld::IdTable*)), this, SLOT(cloneRequest(int, const CSMWorld::IdTable*))); + connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, SLOT(cloneRequest(const CSMWorld::UniversalId&))); connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType))); } @@ -82,11 +82,7 @@ void CSVWorld::TableSubView::setStatusBar (bool show) mBottom->setStatusBar (show); } -void CSVWorld::TableSubView::cloneRequest(int row, const CSMWorld::IdTable* table) +void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) { - const CSMWorld::UniversalId& toClone(mTable->getUniversalId(row)); - if (!(table->getRecord(toClone.getId()).isDeleted())) - { - emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); - } + emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); } diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 650a098f6..b3a0a5162 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -40,14 +40,14 @@ namespace CSVWorld virtual void setStatusBar (bool show); signals: - void cloneRequest(const std::string& id, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType); + void cloneRequest(const std::string&, + const CSMWorld::UniversalId::Type, + const CSMWorld::UniversalId::ArgumentType); private slots: void editRequest (int row); - void cloneRequest (int row, const CSMWorld::IdTable* table); + void cloneRequest (const CSMWorld::UniversalId& toClone); }; } From 9b56b6585be31d6d8af9573d3e53b3aa4766cc2f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 21 Jan 2014 09:43:02 +0100 Subject: [PATCH 142/301] Cloning works for referencables as well. --- apps/opencs/model/world/refidcollection.cpp | 3 ++- apps/opencs/model/world/refiddata.cpp | 17 ++++++++++++++++- apps/opencs/model/world/refiddata.hpp | 13 +++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 74f6f1359..75a1b681d 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -457,7 +457,8 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, RecordBase *newRecord = mData.getRecord(mData.searchId(origin)).clone(); newRecord->mState = RecordBase::State_ModifiedOnly; mAdapters.find(type)->second->setId(*newRecord, destination); - mData.getAppendIndex(type); + mData.insertRecord(*newRecord, type, destination); + delete newRecord; //WIP } diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 8f59b0fe7..d4f08eb91 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -230,4 +230,19 @@ void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const throw std::logic_error ("invalid local index type"); iter->second->save (localIndex.first, writer); -} \ No newline at end of file +} + + +void CSMWorld::RefIdData::insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id) +{ + std::map::iterator iter = + mRecordContainers.find (type); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + iter->second->insertRecord(record); + + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), + LocalIndex (iter->second->getSize()-1, type))); +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 9595ab23b..a982f2f97 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -46,6 +46,8 @@ namespace CSMWorld virtual RecordBase& getRecord (int index)= 0; virtual void appendRecord (const std::string& id) = 0; + + virtual void insertRecord (RecordBase& record) = 0; virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; @@ -68,6 +70,8 @@ namespace CSMWorld virtual RecordBase& getRecord (int index); virtual void appendRecord (const std::string& id); + + virtual void insertRecord (RecordBase& record); virtual void load (int index, ESM::ESMReader& reader, bool base); @@ -78,6 +82,13 @@ namespace CSMWorld virtual void save (int index, ESM::ESMWriter& writer) const; }; + template + void RefIdDataContainer::insertRecord(RecordBase& record) + { + Record& newRecord = dynamic_cast& >(record); + mContainer.push_back(newRecord); + } + template int RefIdDataContainer::getSize() const { @@ -200,6 +211,8 @@ namespace CSMWorld LocalIndex searchId (const std::string& id) const; void erase (int index, int count); + + void insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id); const RecordBase& getRecord (const LocalIndex& index) const; From bc0130f8d8451ff75ba73a622033ac0e82499cc6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 21 Jan 2014 10:35:08 +0100 Subject: [PATCH 143/301] do not double check if record is deleted --- apps/opencs/model/world/collection.hpp | 5 ----- apps/opencs/view/world/genericcreator.cpp | 5 ++++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 091496926..7cc283ce8 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -205,11 +205,6 @@ namespace CSMWorld const UniversalId::ArgumentType argumentType) { Record copy = getRecord(origin); - if (copy.isDeleted()) - { - return; - } - if (argumentType == UniversalId::ArgumentType_Id) { copy.get().mId = destination; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 163842ab8..2fcce9186 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -158,5 +158,8 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originid, mClonedType = type; mArgumentType = argumentType; - mId->setText(QString::fromStdString(mClonedId)); + if (argumentType == CSMWorld::UniversalId::ArgumentType_Id) + { + mId->setText(QString::fromStdString(mClonedId)); + } } From 851a7d50144588f6cb2ceeb49a78e832324ae764 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 15:48:06 +0100 Subject: [PATCH 144/301] Feature #957: Handle area effects for "on touch" range --- apps/openmw/mwbase/world.hpp | 3 + apps/openmw/mwclass/creature.cpp | 20 +--- apps/openmw/mwclass/npc.cpp | 26 +---- apps/openmw/mwmechanics/spellcasting.cpp | 25 +++-- apps/openmw/mwmechanics/spellcasting.hpp | 5 +- apps/openmw/mwscript/miscextensions.cpp | 2 + apps/openmw/mwworld/actiontrap.cpp | 1 + apps/openmw/mwworld/worldimp.cpp | 128 ++++++++++++----------- apps/openmw/mwworld/worldimp.hpp | 3 + 9 files changed, 104 insertions(+), 109 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3ce98c0bf..92a1a4143 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -466,6 +466,9 @@ namespace MWBase /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0; + + virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, + const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 4a244f6a9..b0caccc73 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -316,23 +316,9 @@ namespace MWClass enchantmentName); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { - // Check if we have enough charges - const float enchantCost = enchantment->mData.mCost; - int eSkill = getSkill(ptr, ESM::Skill::Enchant); - const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); - - if (weapon.getCellRef().mEnchantmentCharge == -1) - weapon.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; - if (weapon.getCellRef().mEnchantmentCharge < castCost) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); - } - else - { - weapon.getCellRef().mEnchantmentCharge -= castCost; - MWMechanics::CastSpell cast(ptr, victim); - cast.cast(weapon); - } + MWMechanics::CastSpell cast(ptr, victim); + cast.mHitPosition = hitPosition; + cast.cast(weapon); } } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 7f75d910b..d7cd42489 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -576,28 +576,12 @@ namespace MWClass enchantmentName); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { - // Check if we have enough charges - const float enchantCost = enchantment->mData.mCost; - int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); - const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); - - if (weapon.getCellRef().mEnchantmentCharge == -1) - weapon.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; - if (weapon.getCellRef().mEnchantmentCharge < castCost) - { - if (ptr.getRefData().getHandle() == "player") - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); - } - else - { - weapon.getCellRef().mEnchantmentCharge -= castCost; - - MWMechanics::CastSpell cast(ptr, victim); - cast.cast(weapon); + MWMechanics::CastSpell cast(ptr, victim); + cast.mHitPosition = hitPosition; + bool success = cast.cast(weapon); - if (ptr.getRefData().getHandle() == "player") - skillUsageSucceeded (ptr, ESM::Skill::Enchant, 3); - } + if (ptr.getRefData().getHandle() == "player" && success) + skillUsageSucceeded (ptr, ESM::Skill::Enchant, 3); } } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index f70050628..749a5d7b1 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -181,11 +181,12 @@ namespace MWMechanics : mCaster(caster) , mTarget(target) , mStack(false) + , mHitPosition(0,0,0) { } void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, - const ESM::EffectList &effects, ESM::RangeType range, bool reflected) + const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded) { // If none of the effects need to apply, we can early-out bool found = false; @@ -375,11 +376,12 @@ namespace MWMechanics if (anim) anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); } - - // TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World. } } + if (!exploded) + MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, mTarget, effects, caster, mId, mSourceName); + if (reflectedEffects.mList.size()) inflict(caster, target, reflectedEffects, range, true); @@ -521,12 +523,11 @@ namespace MWMechanics mStack = (enchantment->mData.mType == ESM::Enchantment::CastOnce); - if (enchantment->mData.mType == ESM::Enchantment::WhenUsed) + // Check if there's enough charge left + if (enchantment->mData.mType == ESM::Enchantment::WhenUsed || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { - // Check if there's enough charge left const float enchantCost = enchantment->mData.mCost; - MWMechanics::NpcStats &stats = MWWorld::Class::get(mCaster).getNpcStats(mCaster); - int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); + int eSkill = mCaster.getClass().getSkill(mCaster, ESM::Skill::Enchant); const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); if (item.getCellRef().mEnchantmentCharge == -1) @@ -539,10 +540,15 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); return false; } - // Reduce charge item.getCellRef().mEnchantmentCharge -= castCost; } + + if (enchantment->mData.mType == ESM::Enchantment::WhenUsed) + { + if (mCaster.getRefData().getHandle() == "player") + mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1); + } if (enchantment->mData.mType == ESM::Enchantment::CastOnce) item.getContainerStore()->remove(item, 1, mCaster); else if (enchantment->mData.mType != ESM::Enchantment::WhenStrikes) @@ -551,9 +557,6 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); // Set again to show the modified charge } - if (mCaster.getRefData().getHandle() == "player") - mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1); - inflict(mCaster, mCaster, enchantment->mEffects, ESM::RT_Self); if (!mTarget.isEmpty()) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index b58e7bb09..74dc490ea 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -3,6 +3,8 @@ #include "../mwworld/ptr.hpp" +#include + namespace MWMechanics { class EffectKey; @@ -36,6 +38,7 @@ namespace MWMechanics bool mStack; std::string mId; // ID of spell, potion, item etc std::string mSourceName; // Display name for spell, potion, etc + Ogre::Vector3 mHitPosition; // Used for spawning area orb public: CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target); @@ -49,7 +52,7 @@ namespace MWMechanics bool cast (const std::string& id); void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, - const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false); + const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false); void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); }; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 637159475..27730767f 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -745,6 +745,7 @@ namespace MWScript MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false); MWMechanics::CastSpell cast(ptr, target); + cast.mHitPosition = Ogre::Vector3(target.getRefData().getPosition().pos); cast.cast(spell); } }; @@ -761,6 +762,7 @@ namespace MWScript runtime.pop(); MWMechanics::CastSpell cast(ptr, ptr); + cast.mHitPosition = Ogre::Vector3(ptr.getRefData().getPosition().pos); cast.cast(spell); } }; diff --git a/apps/openmw/mwworld/actiontrap.cpp b/apps/openmw/mwworld/actiontrap.cpp index d723b9823..bcefb0181 100644 --- a/apps/openmw/mwworld/actiontrap.cpp +++ b/apps/openmw/mwworld/actiontrap.cpp @@ -8,6 +8,7 @@ namespace MWWorld void ActionTrap::executeImp(const Ptr &actor) { MWMechanics::CastSpell cast(mTrapSource, actor); + cast.mHitPosition = Ogre::Vector3(actor.getRefData().getPosition().pos); cast.cast(mSpellId); mTrapSource.getCellRef().mTrap = ""; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 419558387..57c85317f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2106,6 +2106,8 @@ namespace MWWorld std::string selectedSpell = stats.getSpells().getSelectedSpell(); MWMechanics::CastSpell cast(actor, target); + if (!target.isEmpty()) + cast.mHitPosition = Ogre::Vector3(target.getRefData().getPosition().pos); if (!selectedSpell.empty()) { @@ -2240,10 +2242,11 @@ namespace MWWorld else { MWMechanics::CastSpell cast(caster, obstacle); + cast.mHitPosition = pos; cast.mId = it->second.mId; cast.mSourceName = it->second.mSourceName; cast.mStack = it->second.mStack; - cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target); + cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target, false, true); } explode = true; @@ -2251,64 +2254,8 @@ namespace MWWorld if (explode) { - std::map > toApply; - for (std::vector::const_iterator effectIt = it->second.mEffects.mList.begin(); - effectIt != it->second.mEffects.mList.end(); ++effectIt) - { - const ESM::MagicEffect* effect = getStore().get().find(effectIt->mEffectID); - - if (effectIt->mArea <= 0) - continue; // Not an area effect - - // Spawn the explosion orb effect - const ESM::Static* areaStatic; - if (!effect->mCasting.empty()) - areaStatic = getStore().get().find (effect->mArea); - else - areaStatic = getStore().get().find ("VFX_DefaultArea"); - - mRendering->spawnEffect("meshes\\" + areaStatic->mModel, "", Ogre::Vector3(ptr.getRefData().getPosition().pos), effectIt->mArea); - - // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now) - static const std::string schools[] = { - "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" - }; - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(!effect->mAreaSound.empty()) - sndMgr->playSound3D(ptr, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); - else - sndMgr->playSound3D(ptr, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); - - // Get the actors in range of the effect - std::vector objects; - MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( - Ogre::Vector3(ptr.getRefData().getPosition().pos), feetToGameUnits(effectIt->mArea), objects); - - for (std::vector::iterator affected = objects.begin(); affected != objects.end(); ++affected) - toApply[*affected].push_back(*effectIt); - } - - // Now apply the appropriate effects to each actor in range - for (std::map >::iterator apply = toApply.begin(); apply != toApply.end(); ++apply) - { - MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); - - // Vanilla-compatible behaviour of never applying the spell to the caster - // (could be changed by mods later) - if (apply->first == caster) - continue; - - if (caster.isEmpty()) - caster = apply->first; - - MWMechanics::CastSpell cast(caster, apply->first); - cast.mId = it->second.mId; - cast.mSourceName = it->second.mSourceName; - cast.mStack = it->second.mStack; - ESM::EffectList effects; - effects.mList = apply->second; - cast.inflict(apply->first, caster, effects, ESM::RT_Target); - } + MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + explodeSpell(Ogre::Vector3(ptr.getRefData().getPosition().pos), ptr, it->second.mEffects, caster, it->second.mId, it->second.mSourceName); deleteObject(ptr); mProjectiles.erase(it++); @@ -2707,4 +2654,67 @@ namespace MWWorld mRendering->spawnEffect(model, texture, worldPosition); } + + void World::explodeSpell(const Vector3 &origin, const MWWorld::Ptr& object, const ESM::EffectList &effects, const Ptr &caster, + const std::string& id, const std::string& sourceName) + { + std::map > toApply; + for (std::vector::const_iterator effectIt = effects.mList.begin(); + effectIt != effects.mList.end(); ++effectIt) + { + const ESM::MagicEffect* effect = getStore().get().find(effectIt->mEffectID); + + if (effectIt->mArea <= 0) + continue; // Not an area effect + + // Spawn the explosion orb effect + const ESM::Static* areaStatic; + if (!effect->mCasting.empty()) + areaStatic = getStore().get().find (effect->mArea); + else + areaStatic = getStore().get().find ("VFX_DefaultArea"); + + mRendering->spawnEffect("meshes\\" + areaStatic->mModel, "", origin, effectIt->mArea); + + // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now) + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mAreaSound.empty()) + sndMgr->playSound3D(object, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + else + sndMgr->playSound3D(object, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + + // Get the actors in range of the effect + std::vector objects; + MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( + origin, feetToGameUnits(effectIt->mArea), objects); + + for (std::vector::iterator affected = objects.begin(); affected != objects.end(); ++affected) + toApply[*affected].push_back(*effectIt); + } + + // Now apply the appropriate effects to each actor in range + for (std::map >::iterator apply = toApply.begin(); apply != toApply.end(); ++apply) + { + MWWorld::Ptr source = caster; + // Vanilla-compatible behaviour of never applying the spell to the caster + // (could be changed by mods later) + if (apply->first == caster) + continue; + + if (source.isEmpty()) + source = apply->first; + + MWMechanics::CastSpell cast(source, apply->first); + cast.mHitPosition = origin; + cast.mId = id; + cast.mSourceName = sourceName; + cast.mStack = false; + ESM::EffectList effects; + effects.mList = apply->second; + cast.inflict(apply->first, caster, effects, ESM::RT_Target, false, true); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 38766e74f..8b1bd9538 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -552,6 +552,9 @@ namespace MWWorld /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition); + + virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, + const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); }; } From 5de8c7fe293da2fc2fe54408ace81da3f388e2f5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 15:56:01 +0100 Subject: [PATCH 145/301] Feature #1130: Add auto-calculated NPC spells --- apps/openmw/mwclass/npc.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index d7cd42489..6b1be1b8f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -141,7 +141,7 @@ namespace * * and by adding class, race, specialization bonus. */ - void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats) + void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats, const MWWorld::Ptr& ptr) { const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().get().find(npc->mClass); @@ -190,6 +190,18 @@ namespace majorMultiplier = 1.0f; break; } + if (class_->mData.mSkills[k][1] == skillIndex) + { + // Major skill -> add starting spells for this skill if existing + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + MWWorld::Store::iterator it = store.get().begin(); + for (; it != store.get().end(); ++it) + { + if (it->mData.mFlags & ESM::Spell::F_Autocalc + && MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(&*it, ptr)) == skillIndex) + npcStats.getSpells().add(it->mId); + } + } } // is this skill in the same Specialization as the class? @@ -302,7 +314,7 @@ namespace MWClass data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); autoCalculateAttributes(ref->mBase, data->mNpcStats); - autoCalculateSkills(ref->mBase, data->mNpcStats); + autoCalculateSkills(ref->mBase, data->mNpcStats, ptr); } if (data->mNpcStats.getFactionRanks().size()) From a4404054bbe79699d82d32227ff659e5c2e90069 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 16:02:39 +0100 Subject: [PATCH 146/301] Feature #1130: Add race power spells for NPCs as well, not just the player --- apps/openmw/mwclass/npc.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 6b1be1b8f..ef45fa28a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -317,6 +317,14 @@ namespace MWClass autoCalculateSkills(ref->mBase, data->mNpcStats, ptr); } + // race powers + const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); + for (std::vector::const_iterator iter (race->mPowers.mList.begin()); + iter!=race->mPowers.mList.end(); ++iter) + { + data->mNpcStats.getSpells().add (*iter); + } + if (data->mNpcStats.getFactionRanks().size()) { static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get() From 1617a4f7d94bd53be80be3d50323b9f972c5e076 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 17:31:08 +0100 Subject: [PATCH 147/301] Use fCombatDistance --- apps/openmw/mwclass/creature.cpp | 6 ++++-- apps/openmw/mwclass/npc.cpp | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index b0caccc73..20b37833a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -225,8 +225,10 @@ namespace MWClass // TODO: where is the distance defined? float dist = 200.f; if (!weapon.isEmpty()) - dist = 100.f * weapon.get()->mBase->mData.mReach; - + { + const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); + dist = fCombatDistance * weapon.get()->mBase->mData.mReach; + } std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist); if (result.first.isEmpty()) return; // Didn't hit anything diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ef45fa28a..4b955373c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -467,10 +467,11 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); getCreatureStats(ptr).setFatigue(fatigue); - - float dist = 100.0f * (!weapon.isEmpty() ? + const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); + float dist = fCombatDistance * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : gmst.find("fHandToHandReach")->getFloat()); + // TODO: Use second to work out the hit angle std::pair result = world->getHitContact(ptr, dist); MWWorld::Ptr victim = result.first; From 82a07af72cfc2829d0ac83195e60e82c6f0604bc Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 17:44:39 +0100 Subject: [PATCH 148/301] Fix typo (should be Strength, not Luck). Use GMSTs for strength damage. --- apps/openmw/mwclass/npc.cpp | 8 +++++++- apps/openmw/mwclass/npc.hpp | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4b955373c..96cb4832d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -252,6 +252,8 @@ namespace MWClass fKnockDownMult = gmst.find("fKnockDownMult"); iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); + fDamageStrengthBase = gmst.find("fDamageStrengthBase"); + fDamageStrengthMult = gmst.find("fDamageStrengthMult"); inited = true; } @@ -524,7 +526,8 @@ namespace MWClass if(attack) { damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); - damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); + damage *= fDamageStrengthBase->getFloat() + + (stats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult->getFloat() * 0.1); if(weaphashealth) { int weapmaxhealth = weapon.get()->mBase->mData.mHealth; @@ -1254,4 +1257,7 @@ namespace MWClass const ESM::GameSetting *Npc::fKnockDownMult; const ESM::GameSetting *Npc::iKnockDownOddsMult; const ESM::GameSetting *Npc::iKnockDownOddsBase; + const ESM::GameSetting *Npc::fDamageStrengthBase; + const ESM::GameSetting *Npc::fDamageStrengthMult; + } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index e658dde5f..157e3afd9 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -36,6 +36,8 @@ namespace MWClass static const ESM::GameSetting *fKnockDownMult; static const ESM::GameSetting *iKnockDownOddsMult; static const ESM::GameSetting *iKnockDownOddsBase; + static const ESM::GameSetting *fDamageStrengthBase; + static const ESM::GameSetting *fDamageStrengthMult; public: From 0f6089851759129daca3ce9995df6a170f7cf077 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 21 Jan 2014 14:13:13 +0100 Subject: [PATCH 149/301] 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 4d764597c..1b3719e60 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 304c87191..95a4cd1dc 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 f62e62d50..bc8ef1db7 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 2eb54a125..8571e93ff 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 150/301] 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 04461fef9..552489bc4 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 9e57f5041..27d37eacf 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 f97d7bebf..55ead476b 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 95a4cd1dc..827ecd643 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 9704239d7..706701fa8 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 b3b51992ef8ac49e292787704cdf7ab466f1fbb4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 21 Jan 2014 21:37:21 +0100 Subject: [PATCH 151/301] copying references. --- apps/opencs/model/world/collection.hpp | 6 +++--- apps/opencs/model/world/commands.cpp | 1 - apps/opencs/model/world/refcollection.cpp | 15 +++++++++++++++ apps/opencs/model/world/refcollection.hpp | 6 ++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 7cc283ce8..8ecad816b 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -200,9 +200,9 @@ namespace CSMWorld template void Collection::cloneRecord(const std::string& origin, - const std::string& destination, - const UniversalId::Type type, - const UniversalId::ArgumentType argumentType) + const std::string& destination, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType) { Record copy = getRecord(origin); if (argumentType == UniversalId::ArgumentType_Id) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index b7c88e872..762e4809c 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -4,7 +4,6 @@ #include #include #include "idtable.hpp" -#include "idtable.hpp" CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 696aeefaa..2ecd3b663 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -5,6 +5,8 @@ #include "ref.hpp" #include "cell.hpp" +#include "universalid.hpp" +#include "record.hpp" void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base) { @@ -34,4 +36,17 @@ std::string CSMWorld::RefCollection::getNewId() std::ostringstream stream; stream << "ref#" << mNextId++; return stream.str(); +} + +void CSMWorld::RefCollection::cloneRecord(const std::string& origin, + const std::string& destination, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType) +{ + Record originRecord = getRecord(origin); + Record *copy = dynamic_cast* >(originRecord.clone()); + copy->mState = CSMWorld::RecordBase::State_ModifiedOnly; + copy->get().mId = getNewId(); + insertRecord(*copy, getAppendIndex(destination, type)); + delete copy; } \ No newline at end of file diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index b5f8c8064..dcfd2036c 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -8,6 +8,7 @@ namespace CSMWorld { struct Cell; + struct UniversalId; /// \brief References in cells class RefCollection : public Collection @@ -25,6 +26,11 @@ namespace CSMWorld ///< Load a sequence of references. std::string getNewId(); + + void cloneRecord(const std::string& origin, + const std::string& destination, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType); }; } From 16f5f5862de0921bc213ae366e1d93939f223944 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 21 Jan 2014 01:01:21 +0100 Subject: [PATCH 152/301] Feature #956: Implement blocking melee attacks --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwclass/creature.cpp | 72 ++++++++++---- apps/openmw/mwclass/creature.hpp | 2 + apps/openmw/mwclass/npc.cpp | 31 +++++- apps/openmw/mwclass/npc.hpp | 2 + apps/openmw/mwmechanics/character.cpp | 9 ++ apps/openmw/mwmechanics/character.hpp | 3 +- apps/openmw/mwmechanics/combat.cpp | 111 ++++++++++++++++++++++ apps/openmw/mwmechanics/combat.hpp | 14 +++ apps/openmw/mwmechanics/creaturestats.cpp | 12 ++- apps/openmw/mwmechanics/creaturestats.hpp | 3 + apps/openmw/mwrender/animation.cpp | 3 + apps/openmw/mwworld/class.cpp | 5 + apps/openmw/mwworld/class.hpp | 4 + 14 files changed, 247 insertions(+), 26 deletions(-) create mode 100644 apps/openmw/mwmechanics/combat.cpp create mode 100644 apps/openmw/mwmechanics/combat.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 3b533b416..41cc320ad 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting - disease pickpocket levelledlist + disease pickpocket levelledlist combat ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 20b37833a..d0a913760 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -30,6 +30,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/combat.hpp" namespace { @@ -325,8 +326,11 @@ namespace MWClass } } - // TODO: do not do this if the attack is blocked - MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) + damage = 0; + + if (damage > 0) + MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); victim.getClass().onHit(victim, damage, true, MWWorld::Ptr(), ptr, true); } @@ -355,31 +359,57 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } - // Check for knockdown - float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); - float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() - * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); - int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) + if (damage > 0.f) { - getCreatureStats(ptr).setKnockedDown(true); + // Check for knockdown + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() + * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) + { + getCreatureStats(ptr).setKnockedDown(true); - } - else - getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? + } + else + getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? - if(ishealth) - { - if(damage > 0.0f) + if(ishealth) + { MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); - float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; - setActorHealth(ptr, health, attacker); + float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; + setActorHealth(ptr, health, attacker); + } + else + { + MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); + fatigue.setCurrent(fatigue.getCurrent() - damage, true); + getCreatureStats(ptr).setFatigue(fatigue); + } } - else + } + + void Creature::block(const MWWorld::Ptr &ptr) const + { + MWWorld::InventoryStore& inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (shield == inv.end()) + return; + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + switch(shield->getClass().getEquipmentSkill(*shield)) { - MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); - fatigue.setCurrent(fatigue.getCurrent() - damage, true); - getCreatureStats(ptr).setFatigue(fatigue); + case ESM::Skill::LightArmor: + sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::MediumArmor: + sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::HeavyArmor: + sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f); + break; + default: + return; } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index cfa06ed62..ca8abc040 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -56,6 +56,8 @@ namespace MWClass virtual void hit(const MWWorld::Ptr& ptr, int type) const; + virtual void block(const MWWorld::Ptr &ptr) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 96cb4832d..f4ccd3c5f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -20,6 +20,7 @@ #include "../mwmechanics/movement.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/disease.hpp" +#include "../mwmechanics/combat.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -609,8 +610,10 @@ namespace MWClass } } - // TODO: do not do this if the attack is blocked - if (healthdmg) + if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) + damage = 0; + + if (healthdmg && damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); @@ -751,6 +754,30 @@ namespace MWClass } } + void Npc::block(const MWWorld::Ptr &ptr) const + { + MWWorld::InventoryStore& inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (shield == inv.end()) + return; + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + switch(shield->getClass().getEquipmentSkill(*shield)) + { + case ESM::Skill::LightArmor: + sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::MediumArmor: + sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::HeavyArmor: + sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f); + break; + default: + return; + } + } + void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const { MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 157e3afd9..2bd91d198 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -79,6 +79,8 @@ namespace MWClass virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + virtual void block(const MWWorld::Ptr &ptr) const; + virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 3f29cbb7c..2f5b0ca6c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -153,6 +153,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat { bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); + bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock(); if(mHitState == CharState_None) { if(knockdown) @@ -167,6 +168,12 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mCurrentHit = chooseRandomGroup("hit"); mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } + else if (block) + { + mHitState = CharState_Block; + mCurrentHit = "shield"; + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "block start", "block stop", 0.0f, 0); + } } else if(!mAnimation->isPlaying(mCurrentHit)) { @@ -175,6 +182,8 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(false); if (recovery) mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false); + if (block) + mPtr.getClass().getCreatureStats(mPtr).setBlock(false); mHitState = CharState_None; } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index ea12d9b94..915de93eb 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -92,7 +92,8 @@ enum CharacterState { CharState_SwimDeath, CharState_Hit, - CharState_KnockDown + CharState_KnockDown, + CharState_Block }; enum WeaponType { diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp new file mode 100644 index 000000000..d54736857 --- /dev/null +++ b/apps/openmw/mwmechanics/combat.cpp @@ -0,0 +1,111 @@ +#include "combat.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/movement.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" + +namespace +{ + +Ogre::Radian signedAngle(Ogre::Vector3 v1, Ogre::Vector3 v2, Ogre::Vector3 n) +{ + return Ogre::Math::ATan2( + n.dotProduct( v1.crossProduct(v2) ), + v1.dotProduct(v2) + ); +} + +} + +namespace MWMechanics +{ + + bool blockMeleeAttack(const MWWorld::Ptr &attacker, const MWWorld::Ptr &blocker, const MWWorld::Ptr &weapon, float damage) + { + if (!blocker.getClass().hasInventoryStore(blocker)) + return false; + + if (blocker.getClass().getCreatureStats(blocker).getKnockedDown() + || blocker.getClass().getCreatureStats(blocker).getHitRecovery()) + return false; + + MWWorld::InventoryStore& inv = blocker.getClass().getInventoryStore(blocker); + MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (shield == inv.end() || shield->getTypeName() != typeid(ESM::Armor).name()) + return false; + + Ogre::Degree angle = signedAngle (Ogre::Vector3(attacker.getRefData().getPosition().pos) - Ogre::Vector3(blocker.getRefData().getPosition().pos), + blocker.getRefData().getBaseNode()->getOrientation().yAxis(), Ogre::Vector3(0,0,1)); + + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + if (angle.valueDegrees() < gmst.find("fCombatBlockLeftAngle")->getFloat()) + return false; + if (angle.valueDegrees() > gmst.find("fCombatBlockRightAngle")->getFloat()) + return false; + + MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker); + if (blockerStats.getDrawState() == DrawState_Spell) + return false; + + MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); + + float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2 * blockerStats.getAttribute(ESM::Attribute::Agility).getModified() + + 0.1 * blockerStats.getAttribute(ESM::Attribute::Luck).getModified(); + float enemySwing = attackerStats.getAttackStrength(); + float swingTerm = enemySwing * gmst.find("fSwingBlockMult")->getFloat() + gmst.find("fSwingBlockBase")->getFloat(); + + float blockerTerm = blockTerm * swingTerm; + if (blocker.getClass().getMovementSettings(blocker).mPosition[1] <= 0) + blockerTerm *= gmst.find("fBlockStillBonus")->getFloat(); + blockerTerm *= blockerStats.getFatigueTerm(); + + float attackerSkill = attacker.getClass().getSkill(attacker, weapon.getClass().getEquipmentSkill(weapon)); + float attackerTerm = attackerSkill + 0.2 * attackerStats.getAttribute(ESM::Attribute::Agility).getModified() + + 0.1 * attackerStats.getAttribute(ESM::Attribute::Luck).getModified(); + attackerTerm *= attackerStats.getFatigueTerm(); + + int x = int(blockerTerm - attackerTerm); + int iBlockMaxChance = gmst.find("iBlockMaxChance")->getInt(); + int iBlockMinChance = gmst.find("iBlockMinChance")->getInt(); + x = std::min(iBlockMaxChance, std::max(iBlockMinChance, x)); + + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < x) + { + // Reduce shield durability by incoming damage + if (shield->getCellRef().mCharge == -1) + shield->getCellRef().mCharge = shield->getClass().getItemMaxHealth(*shield); + shield->getCellRef().mCharge -= std::min(shield->getCellRef().mCharge, int(damage)); + if (!shield->getCellRef().mCharge) + inv.unequipItem(*shield, blocker); + + // Reduce blocker fatigue + const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->getFloat(); + const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat(); + const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->getFloat(); + MWMechanics::DynamicStat fatigue = blockerStats.getFatigue(); + float normalizedEncumbrance = blocker.getClass().getEncumbrance(blocker) / blocker.getClass().getCapacity(blocker); + normalizedEncumbrance = std::min(1.f, normalizedEncumbrance); + float fatigueLoss = fFatigueBlockBase + normalizedEncumbrance * fFatigueBlockMult; + fatigueLoss += weapon.getClass().getWeight(weapon) * attackerStats.getAttackStrength() * fWeaponFatigueBlockMult; + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); + blockerStats.setFatigue(fatigue); + + blockerStats.setBlock(true); + + if (blocker.getClass().isNpc()) + blocker.getClass().skillUsageSucceeded(blocker, ESM::Skill::Block, 0); + + return true; + } + return false; + } + +} diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp new file mode 100644 index 000000000..d666905f2 --- /dev/null +++ b/apps/openmw/mwmechanics/combat.hpp @@ -0,0 +1,14 @@ +#ifndef OPENMW_MECHANICS_COMBAT_H +#define OPENMW_MECHANICS_COMBAT_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + +/// @return can we block the attack? +bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker, const MWWorld::Ptr& weapon, float damage); + +} + +#endif diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 62bae8ca8..8f84a7979 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -15,7 +15,7 @@ namespace MWMechanics mAttacked (false), mHostile (false), mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), - mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), + mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) { for (int i=0; i<4; ++i) @@ -427,6 +427,16 @@ namespace MWMechanics return mHitRecovery; } + void CreatureStats::setBlock(bool value) + { + mBlock = value; + } + + bool CreatureStats::getBlock() const + { + return mBlock; + } + bool CreatureStats::getMovementFlag (Flag flag) const { return mMovementFlags & flag; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 6893f385e..0501eb286 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -39,6 +39,7 @@ namespace MWMechanics bool mAttackingOrSpell; bool mKnockdown; bool mHitRecovery; + bool mBlock; unsigned int mMovementFlags; float mAttackStrength; // Note only some creatures attack with weapons @@ -204,6 +205,8 @@ namespace MWMechanics bool getKnockedDown() const; void setHitRecovery(bool value); bool getHitRecovery() const; + void setBlock(bool value); + bool getBlock() const; enum Flag { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a7623efea..cd2d9a47a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -679,6 +679,9 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") MWBase::Environment::get().getWorld()->castSpell(mPtr); + + else if (groupname == "shield" && evt.compare(off, len, "block hit") == 0) + mPtr.getClass().block(mPtr); } void Animation::changeGroups(const std::string &groupname, int groups) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index c7b0d2e1f..9771ffde3 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -92,6 +92,11 @@ namespace MWWorld throw std::runtime_error("class cannot hit"); } + void Class::block(const Ptr &ptr) const + { + throw std::runtime_error("class cannot block"); + } + void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, bool successful) const { throw std::runtime_error("class cannot be hit"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 823538cf8..0dee8b292 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -128,6 +128,10 @@ namespace MWWorld /// actor responsible for the attack, and \a successful specifies if the hit is /// successful or not. + virtual void block (const Ptr& ptr) const; + ///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield + /// (default implementation: throw an exception) + virtual void setActorHealth(const Ptr& ptr, float health, const Ptr& attacker=Ptr()) const; ///< Sets a new current health value for the actor, optionally specifying the object causing /// the change. Use this instead of using CreatureStats directly as this will make sure the From f89c400305a8f0563e4ba5156a0977f142c8492f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 00:02:43 +0100 Subject: [PATCH 153/301] Don't complain about greetings with no sound --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 ++- components/esm/loadinfo.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 263c101e5..7fbebb9d7 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -586,7 +586,8 @@ namespace MWDialogue MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); if(winMgr->getSubtitlesEnabled()) winMgr->messageBox(info->mResponse); - sndMgr->say(actor, info->mSound); + if (!info->mSound.empty()) + sndMgr->say(actor, info->mSound); } } diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 737494f6c..0c0d662a8 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -70,7 +70,7 @@ struct DialInfo // Sound and text associated with this item std::string mSound, mResponse; - // Result script (uncomiled) to run whenever this dialog item is + // Result script (uncompiled) to run whenever this dialog item is // selected std::string mResultScript; From 194413c9558d4fa0d9238d0e57054c26c015cff2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 12:09:44 +0100 Subject: [PATCH 154/301] Feature #1119: Implement Resistance/Weakness to normal weapons magic effect. Handle fWereWolfSilverWeaponDamageMult. --- apps/openmw/mwclass/creature.cpp | 3 +++ apps/openmw/mwclass/npc.cpp | 3 +++ apps/openmw/mwmechanics/combat.cpp | 28 +++++++++++++++++++++++++++- apps/openmw/mwmechanics/combat.hpp | 2 ++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index d0a913760..c23b9e23a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -359,6 +359,9 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } + if (damage > 0.0f && !object.isEmpty()) + MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); + if (damage > 0.f) { // Check for knockdown diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f4ccd3c5f..8c6e89544 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -652,6 +652,9 @@ namespace MWClass if (!attacker.isEmpty()) MWMechanics::diseaseContact(ptr, attacker); + if (damage > 0.0f && !object.isEmpty()) + MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); + if(damage > 0.0f) { // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index d54736857..204264106 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -5,12 +5,14 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwbase/windowmanager.hpp" + namespace { @@ -108,4 +110,28 @@ namespace MWMechanics return false; } + void resistNormalWeapon(const MWWorld::Ptr &actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr &weapon, float &damage) + { + MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + float resistance = std::min(100.f, stats.getMagicEffects().get(ESM::MagicEffect::ResistNormalWeapons).mMagnitude + - stats.getMagicEffects().get(ESM::MagicEffect::WeaknessToNormalWeapons).mMagnitude); + + float multiplier = 0; + if (resistance >= 0) + multiplier = 1 - resistance / 100.f; + else + multiplier = -(resistance-100) / 100.f; + + if (!(weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver + || weapon.get()->mBase->mData.mFlags & ESM::Weapon::Magical)) + damage *= multiplier; + + if (weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver + & actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) + damage *= MWBase::Environment::get().getWorld()->getStore().get().find("fWereWolfSilverWeaponDamageMult")->getFloat(); + + if (damage == 0 && attacker.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}"); + } + } diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp index d666905f2..7f2415697 100644 --- a/apps/openmw/mwmechanics/combat.hpp +++ b/apps/openmw/mwmechanics/combat.hpp @@ -9,6 +9,8 @@ namespace MWMechanics /// @return can we block the attack? bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker, const MWWorld::Ptr& weapon, float damage); +void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage); + } #endif From 264226dfe3e28054b4afe1ca3827211b1b85e898 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 12:33:47 +0100 Subject: [PATCH 155/301] Handle iWereWolfBounty --- apps/openmw/mwmechanics/npcstats.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 4fff6eac8..e41ce2078 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -267,12 +267,16 @@ bool MWMechanics::NpcStats::hasBeenUsed (const std::string& id) const int MWMechanics::NpcStats::getBounty() const { - return mBounty; + if (mIsWerewolf) + return MWBase::Environment::get().getWorld()->getStore().get().find("iWereWolfBounty")->getInt(); + else + return mBounty; } void MWMechanics::NpcStats::setBounty (int bounty) { - mBounty = bounty; + if (!mIsWerewolf) + mBounty = bounty; } int MWMechanics::NpcStats::getFactionReputation (const std::string& faction) const From f27701cbe896503fc308f9320d4d0e510e9f6fb0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 13:04:36 +0100 Subject: [PATCH 156/301] Detect selling stolen items or enchanting with stolen items --- apps/openmw/mwgui/enchantingdialog.cpp | 24 ++++++++++++++++++++++++ apps/openmw/mwgui/tradewindow.cpp | 19 +++++++++++++++++++ apps/openmw/mwmechanics/enchanting.hpp | 2 ++ 3 files changed, 45 insertions(+) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index d9d2a2ea8..92205c3e9 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -5,6 +5,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -296,6 +298,28 @@ namespace MWGui return; } + // check if the player is attempting to use a soulstone or item that was stolen from this actor + if (mPtr != player) + { + for (int i=0; i<2; ++i) + { + MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem(); + if (Misc::StringUtils::ciEqual(item.getCellRef().mOwner, mPtr.getCellRef().mRefID)) + { + std::string msg = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage49")->getString(); + if (msg.find("%s") != std::string::npos) + msg.replace(msg.find("%s"), 2, item.getClass().getName(item)); + MWBase::Environment::get().getWindowManager()->messageBox(msg); + MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); + MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, + item.getClass().getValue(item)); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); + return; + } + } + } + int result = mEnchanting.create(); if(result==1) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index d0c10bd30..92ba9470d 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -271,6 +271,25 @@ namespace MWGui return; } + // check if the player is attempting to sell back an item stolen from this actor + for (std::vector::iterator it = merchantBought.begin(); it != merchantBought.end(); ++it) + { + if (Misc::StringUtils::ciEqual(it->mBase.getCellRef().mOwner, mPtr.getCellRef().mRefID)) + { + std::string msg = gmst.find("sNotifyMessage49")->getString(); + if (msg.find("%s") != std::string::npos) + msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase)); + MWBase::Environment::get().getWindowManager()->messageBox(msg); + MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); + MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, + it->mBase.getClass().getValue(it->mBase) + * it->mCount); + onCancelButtonClicked(mCancelButton); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + return; + } + } + if(mCurrentBalance > mCurrentMerchantOffer) { //if npc is a creature: reject (no haggle) diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index 988ce41fc..ae0b25a4a 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -28,6 +28,8 @@ namespace MWMechanics void setEnchanter(MWWorld::Ptr enchanter); void setSelfEnchanting(bool selfEnchanting); void setOldItem(MWWorld::Ptr oldItem); + MWWorld::Ptr getOldItem() { return mOldItemPtr; } + MWWorld::Ptr getGem() { return mSoulGemPtr; } void setNewItemName(const std::string& s); void setEffect(ESM::EffectList effectList); void setSoulGem(MWWorld::Ptr soulGem); From 4c94289b1f5b7d8e1bda849e7c47f4b440f2080e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 13:30:07 +0100 Subject: [PATCH 157/301] Fix PC Health Percent function --- apps/openmw/mwdialogue/filter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index f132d13a3..bd64dcf9c 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -216,7 +216,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c float ratio = MWWorld::Class::get (player).getCreatureStats (player).getHealth().getCurrent() / MWWorld::Class::get (player).getCreatureStats (player).getHealth().getModified(); - return select.selectCompare (ratio); + return select.selectCompare (static_cast(ratio*100)); } case SelectWrapper::Function_PcDynamicStat: From ea21d8fec327bf448ef06e2eb1680e5d4c81ed18 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 13:30:45 +0100 Subject: [PATCH 158/301] Fix CreatureTargetted function --- apps/openmw/mwdialogue/filter.cpp | 2 +- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- apps/openmw/mwmechanics/aicombat.hpp | 2 +- apps/openmw/mwmechanics/aisequence.cpp | 7 +++---- apps/openmw/mwmechanics/aisequence.hpp | 5 ++--- apps/openmw/mwmechanics/creaturestats.cpp | 4 +++- apps/openmw/mwscript/aiextensions.cpp | 12 +++--------- 7 files changed, 15 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index bd64dcf9c..731fafbf6 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -536,7 +536,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_CreatureTargetted: - return MWWorld::Class::get (mActor).getCreatureStats (mActor).getCreatureTargetted(); + return mActor.getClass().getCreatureStats (mActor).getCreatureTargetted(); case SelectWrapper::Function_Werewolf: diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index c01c4096b..283c7f042 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -360,9 +360,9 @@ namespace MWMechanics return 1; } - const std::string &AiCombat::getTargetId() const + MWWorld::Ptr AiCombat::getTarget() const { - return mTarget.getRefData().getHandle(); + return mTarget; } AiCombat *MWMechanics::AiCombat::clone() const diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 27f7f5d95..a24183c65 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -25,7 +25,7 @@ namespace MWMechanics virtual unsigned int getPriority() const; - const std::string &getTargetId() const; + MWWorld::Ptr getTarget() const; private: PathFinder mPathFinder; diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 73caa6ca7..3902b9186 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -55,13 +55,12 @@ int MWMechanics::AiSequence::getTypeId() const return mPackages.front()->getTypeId(); } -bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const +MWWorld::Ptr MWMechanics::AiSequence::getCombatTarget() const { if (getTypeId() != AiPackage::TypeIdCombat) - return false; + return MWWorld::Ptr(); const AiCombat *combat = static_cast(mPackages.front()); - targetActorId = combat->getTargetId(); - return true; + return combat->getTarget(); } void MWMechanics::AiSequence::stopCombat() diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index d65c31616..075c43ff7 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -36,9 +36,8 @@ namespace MWMechanics int getTypeId() const; ///< @see enum AiPackage::TypeId - bool getCombatTarget (std::string &targetActorId) const; - ///< Return true and assign target if combat package is currently - /// active, return false otherwise + MWWorld::Ptr getCombatTarget () const; + ///< Return the combat target if a combat package is active, or an empty Ptr otherwise void stopCombat(); ///< Removes all combat packages until first non-combat or stack empty. diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 8f84a7979..ec300f6f0 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -348,7 +348,9 @@ namespace MWMechanics bool CreatureStats::getCreatureTargetted() const { - return false; + if (mAiSequence.getCombatTarget().isEmpty()) + return false; + return mAiSequence.getCombatTarget().getTypeName() == typeid(ESM::Creature).name(); } float CreatureStats::getEvasion() const diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 759d0ba94..75c635103 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -412,16 +412,10 @@ namespace MWScript std::string testedTargetId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - const MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); - std::string currentTargetId; + const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - bool targetsAreEqual = false; - if (creatureStats.getAiSequence().getCombatTarget (currentTargetId)) - { - if (currentTargetId == testedTargetId) - targetsAreEqual = true; - } - runtime.push(int(targetsAreEqual)); + MWWorld::Ptr target = creatureStats.getAiSequence().getCombatTarget(); + runtime.push(Misc::StringUtils::ciEqual(target.getCellRef().mRefID, testedTargetId)); } }; From 2e6e0fd0a05363bba558aaf0b5d9bc9b2001e9dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 14:45:36 +0100 Subject: [PATCH 159/301] Fix GetPcCell bug --- apps/openmw/mwscript/cellextensions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 5de95d1d4..1834c5651 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -115,6 +115,7 @@ namespace MWScript current = region->mName; } + Misc::StringUtils::toLower(current); bool match = current.length()>=name.length() && current.substr (0, name.length())==name; From cf378ec31ef28a0a967b02dc19266fd1d91a9d9e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 15:25:03 +0100 Subject: [PATCH 160/301] Support optional volume and pitch arguments for soundgen events (e.g. moan 0.5 1.0) as required for some actors --- apps/openmw/mwrender/animation.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index cd2d9a47a..e8d215f5a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -616,6 +616,13 @@ bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const s return true; } +void split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } +} void Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key) { @@ -630,14 +637,29 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co } if(evt.compare(0, 10, "soundgen: ") == 0) { - std::string sound = MWWorld::Class::get(mPtr).getSoundIdFromSndGen(mPtr, evt.substr(10)); + std::string soundgen = evt.substr(10); + + // The event can optionally contain volume and pitch modifiers + float volume=1.f, pitch=1.f; + if (soundgen.find(" ") != std::string::npos) + { + std::vector tokens; + split(soundgen, ' ', tokens); + soundgen = tokens[0]; + if (tokens.size() >= 2) + volume = Ogre::StringConverter::parseReal(tokens[1]); + if (tokens.size() >= 3) + pitch = Ogre::StringConverter::parseReal(tokens[2]); + } + + std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager::PlayType type = MWBase::SoundManager::Play_TypeSfx; if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0) type = MWBase::SoundManager::Play_TypeFoot; - sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f, type); + sndMgr->playSound3D(mPtr, sound, volume, pitch, type); } return; } From 43bc223e683e9059b6b89653f91d10f8ac0a9978 Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 22 Jan 2014 16:57:18 +0100 Subject: [PATCH 161/301] Added version info retrieval from git tags --- CMakeLists.txt | 26 +++- cmake/GetGitRevisionDescription.cmake | 159 +++++++++++++++++++++++ cmake/GetGitRevisionDescription.cmake.in | 38 ++++++ 3 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 cmake/GetGitRevisionDescription.cmake create mode 100644 cmake/GetGitRevisionDescription.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eb8b44c2..9bd2cdddc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,15 +14,29 @@ endif (APPLE) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) -include (OpenMWMacros) +include(OpenMWMacros) # Version -set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 27) -set (OPENMW_VERSION_RELEASE 0) +include(GetGitRevisionDescription) -set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") +get_git_tag_revision(taghash --tags --max-count=1) +get_git_head_revision(refspec commithash) +git_describe(version --tags ${taghash}) + +string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" match "${version}") +if (match) + string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${version}") + string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${version}") + string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${version}") + + set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") + set(OPENMW_VERSION_COMMIT "${commithash}") + + message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...") +else (match) + message(FATAL_ERROR "Failed to get valid version information from Git") +endif (match) # doxygen main page @@ -319,7 +333,7 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg "${OpenMW_BINARY_DIR}/opencs.cfg") - + configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 000000000..f70f64261 --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,159 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + return() + endif() + + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) + endif() + + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) + set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + + #get_git_head_revision(refspec hash) + + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + + #if(NOT hash) + # set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + # return() + #endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe + #${hash} + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(get_git_tag_revision _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + rev-list + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + + diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 000000000..888ce13aa --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,38 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") + configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + set(HEAD_HASH "${HEAD_REF}") + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() From c95b8bcb391f32ba92458c14b27d20f90a6e8397 Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 22 Jan 2014 17:33:55 +0100 Subject: [PATCH 162/301] Moved the generated version header stuff into components --- apps/openmw/CMakeLists.txt | 5 ----- apps/openmw/config.hpp.cmake | 9 --------- apps/openmw/main.cpp | 3 +-- components/CMakeLists.txt | 12 ++++++++++-- 4 files changed, 11 insertions(+), 18 deletions(-) delete mode 100644 apps/openmw/config.hpp.cmake diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 3b533b416..ff0ff65d2 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -1,7 +1,3 @@ - -# config file -configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/config.hpp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/config.hpp") - # local files set(GAME main.cpp @@ -12,7 +8,6 @@ if(NOT WIN32) endif() set(GAME_HEADER engine.hpp - config.hpp ) source_group(game FILES ${GAME} ${GAME_HEADER}) diff --git a/apps/openmw/config.hpp.cmake b/apps/openmw/config.hpp.cmake deleted file mode 100644 index 848fbe0eb..000000000 --- a/apps/openmw/config.hpp.cmake +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H - -#define OPENMW_VERSION_MAJOR @OPENMW_VERSION_MAJOR@ -#define OPENMW_VERSION_MINOR @OPENMW_VERSION_MINOR@ -#define OPENMW_VERSION_RELEASE @OPENMW_VERSION_RELEASE@ -#define OPENMW_VERSION "@OPENMW_VERSION@" - -#endif diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 3129e6bd3..260a35a9b 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -30,8 +31,6 @@ extern int is_debugger_attached(void); #include #endif -#include "config.hpp" - #include /** * Workaround for problems with whitespaces in paths in older versions of Boost library diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index a037fd5fa..9c37d2e62 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -1,5 +1,9 @@ project (Components) set (CMAKE_BUILD_TYPE DEBUG) + +# Version file +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version/version.hpp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/version/version.hpp") + # source files add_component_dir (settings @@ -75,8 +79,12 @@ add_component_dir (loadinglistener ) add_component_dir (ogreinit - ogreinit ogreplugin - ) + ogreinit ogreplugin + ) + +add_component_dir (version + version + ) set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ) From fa186f5ec1430c6b75ffbcf2bf0848b6498e90fe Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 22 Jan 2014 17:51:10 +0100 Subject: [PATCH 163/301] Added version information to the launcher --- apps/launcher/maindialog.cpp | 9 ++++++++ files/ui/mainwindow.ui | 42 +++++++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 9b3c4e1b0..4aa8a4816 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -1,5 +1,8 @@ #include "maindialog.hpp" +#include + +#include #include #include #include @@ -67,6 +70,12 @@ Launcher::MainDialog::MainDialog(QWidget *parent) // Remove what's this? button setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); + // Add version information to bottom of the window + QString revision(OPENMW_VERSION_COMMIT); + revision = revision.left(10); + + versionLabel->setText(tr("OpenMW %0 revision %1").arg(OPENMW_VERSION, revision)); + createIcons(); } diff --git a/files/ui/mainwindow.ui b/files/ui/mainwindow.ui index a1dfb172b..5f2be05a2 100644 --- a/files/ui/mainwindow.ui +++ b/files/ui/mainwindow.ui @@ -2,6 +2,14 @@ MainWindow + + + 0 + 0 + 575 + 535 + + 575 @@ -56,11 +64,35 @@ - - - QDialogButtonBox::Close - - + + + + + OpenMW version + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QDialogButtonBox::Close + + + + From c354cd52be15d92940e97dcaee9f7d3de0a86024 Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 22 Jan 2014 18:37:49 +0100 Subject: [PATCH 164/301] Added compile date and time to the version info in the launcher --- apps/launcher/maindialog.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 4aa8a4816..42d3d1754 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include #include @@ -74,7 +76,12 @@ Launcher::MainDialog::MainDialog(QWidget *parent) QString revision(OPENMW_VERSION_COMMIT); revision = revision.left(10); + QDate date(QDate::fromString(__DATE__, QLatin1String("MMM dd yyyy"))); + QTime time(QTime::fromString(__TIME__, QLatin1String("hh:m:ss"))); + versionLabel->setText(tr("OpenMW %0 revision %1").arg(OPENMW_VERSION, revision)); + versionLabel->setToolTip(tr("Compiled on %0 %1").arg(date.toString(Qt::SystemLocaleShortDate), + time.toString(Qt::SystemLocaleShortDate))); createIcons(); } From d92ded3bcd37af3a5bfd56b05e3f3e58903efe1e Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 22 Jan 2014 19:30:41 +0100 Subject: [PATCH 165/301] Forgot adding the version header CMake file --- components/version/version.hpp.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 components/version/version.hpp.cmake diff --git a/components/version/version.hpp.cmake b/components/version/version.hpp.cmake new file mode 100644 index 000000000..4adb0d8c2 --- /dev/null +++ b/components/version/version.hpp.cmake @@ -0,0 +1,12 @@ +#ifndef VERSION_HPP +#define VERSION_HPP + +#define OPENMW_VERSION_MAJOR @OPENMW_VERSION_MAJOR@ +#define OPENMW_VERSION_MINOR @OPENMW_VERSION_MINOR@ +#define OPENMW_VERSION_RELEASE @OPENMW_VERSION_RELEASE@ +#define OPENMW_VERSION "@OPENMW_VERSION@" + +#define OPENMW_VERSION_COMMIT "@OPENMW_VERSION_COMMIT@" + +#endif // VERSION_HPP + From 4d9d31b25e9ad56bd35580b35134118e88ea3053 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 23 Jan 2014 09:40:32 +0100 Subject: [PATCH 166/301] refrences cloning does not work --- apps/opencs/model/world/collection.hpp | 1 + apps/opencs/model/world/data.cpp | 1 + apps/opencs/model/world/idtable.cpp | 2 -- apps/opencs/model/world/refcollection.cpp | 10 ++++------ apps/opencs/view/world/genericcreator.cpp | 7 +------ apps/opencs/view/world/referencecreator.cpp | 2 +- 6 files changed, 8 insertions(+), 15 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 8ecad816b..74019dda7 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -205,6 +205,7 @@ namespace CSMWorld const UniversalId::ArgumentType argumentType) { Record copy = getRecord(origin); + copy.mState = RecordBase::State_ModifiedOnly; if (argumentType == UniversalId::ArgumentType_Id) { copy.get().mId = destination; diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 04e35cdaa..69fb4ab1e 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -194,6 +194,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mRefs.addColumn (new StringIdColumn (true)); mRefs.addColumn (new RecordStateColumn); + mRefs.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Reference)); mRefs.addColumn (new CellColumn); mRefs.addColumn (new IdColumn); mRefs.addColumn (new PosColumn (&CellRef::mPos, 0, false)); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index d4008fb87..f131f7087 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -133,8 +133,6 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin, beginInsertRows (QModelIndex(), index, index); mIdCollection->cloneRecord(origin, destination, type, argumentType); endInsertRows(); - emit dataChanged (CSMWorld::IdTable::index (0, 0), - CSMWorld::IdTable::index (mIdCollection->getSize()-1, mIdCollection->getColumns()-1)); } diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 2ecd3b663..6b0081258 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -43,10 +43,8 @@ void CSMWorld::RefCollection::cloneRecord(const std::string& origin, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) { - Record originRecord = getRecord(origin); - Record *copy = dynamic_cast* >(originRecord.clone()); - copy->mState = CSMWorld::RecordBase::State_ModifiedOnly; - copy->get().mId = getNewId(); - insertRecord(*copy, getAppendIndex(destination, type)); - delete copy; + Record clone(getRecord(origin)); + clone.mState = CSMWorld::RecordBase::State_ModifiedOnly; + clone.get().mId = destination; + insertRecord(clone, getAppendIndex(destination, type), type); } \ No newline at end of file diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 2fcce9186..ab387393b 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -157,9 +157,4 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originid, mClonedId = originid; mClonedType = type; mArgumentType = argumentType; - - if (argumentType == CSMWorld::UniversalId::ArgumentType_Id) - { - mId->setText(QString::fromStdString(mClonedId)); - } -} +} \ No newline at end of file diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index aef25a81d..06b3eb76d 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -40,9 +40,9 @@ CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack& void CSVWorld::ReferenceCreator::reset() { + GenericCreator::reset(); mCell->setText (""); mId = getData().getReferences().getNewId(); - GenericCreator::reset(); } std::string CSVWorld::ReferenceCreator::getErrors() const From 22cb4784b5079bb8d3d453bdb076dfc4b438d1d4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 23 Jan 2014 11:29:40 +0100 Subject: [PATCH 167/301] store cell state in saved game files (no references yet) --- apps/openmw/mwstate/statemanagerimp.cpp | 131 +++++++++++++----------- 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, 263 insertions(+), 69 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 8571e93ff..7020678d0 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(); - - switch (n.val) - { - case ESM::REC_SAVE: - - // don't need to read that here - reader.skipRecord(); - break; - - case ESM::REC_JOUR: - case ESM::REC_QUES: - - MWBase::Environment::get().getJournal()->readRecord (reader, n.val); - break; - - case ESM::REC_ALCH: - case ESM::REC_ARMO: - case ESM::REC_BOOK: - case ESM::REC_CLAS: - case ESM::REC_CLOT: - case ESM::REC_ENCH: - case ESM::REC_NPC_: - case ESM::REC_SPEL: - case ESM::REC_WEAP: - case ESM::REC_GLOB: - case ESM::REC_PLAY: + cleanup(); - MWBase::Environment::get().getWorld()->readRecord (reader, n.val); - break; + mTimePlayed = slot->mProfile.mTimePlayed; - case ESM::REC_GSCR: + ESM::ESMReader reader; + reader.open (slot->mPath.string()); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); - break; - - default: - - // ignore invalid records - /// \todo log error - reader.skipRecord(); + while (reader.hasMoreRecs()) + { + ESM::NAME n = reader.getRecName(); + reader.getRecHeader(); + + switch (n.val) + { + case ESM::REC_SAVE: + + // don't need to read that here + reader.skipRecord(); + break; + + case ESM::REC_JOUR: + case ESM::REC_QUES: + + MWBase::Environment::get().getJournal()->readRecord (reader, n.val); + break; + + case ESM::REC_ALCH: + case ESM::REC_ARMO: + case ESM::REC_BOOK: + case ESM::REC_CLAS: + case ESM::REC_CLOT: + case ESM::REC_ENCH: + case ESM::REC_NPC_: + case ESM::REC_SPEL: + case ESM::REC_WEAP: + case ESM::REC_GLOB: + case ESM::REC_PLAY: + case ESM::REC_CSTA: + + MWBase::Environment::get().getWorld()->readRecord (reader, n.val); + break; + + case ESM::REC_GSCR: + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); + break; + + default: + + // ignore invalid records + /// \todo log error + reader.skipRecord(); + } } - } - mCharacterManager.setCurrentCharacter(character); + mCharacterManager.setCurrentCharacter(character); - mState = State_Running; + mState = State_Running; - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); - MWBase::Environment::get().getWorld()->setupPlayer(); - MWBase::Environment::get().getWorld()->renderPlayer(); - MWBase::Environment::get().getWindowManager()->updatePlayer(); - MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + MWBase::Environment::get().getWorld()->setupPlayer(); + MWBase::Environment::get().getWorld()->renderPlayer(); + MWBase::Environment::get().getWindowManager()->updatePlayer(); + MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); + ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); - MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); + MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); + } + catch (const std::exception& e) + { + std::cerr << "failed to load saved game: " << e.what() << std::endl; + cleanup (true); + } } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index a2abcfd1b..d6bb7575d 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 c844b689e..ead12567f 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 31de2f60e..27e107646 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 cffa0537a..3cbf85e8e 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 1da219a9e..64843e7a4 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 706701fa8..f8321d74e 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 4c0bff59d..d9ab8129d 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 000000000..1f7e8197e --- /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 000000000..cd0db3067 --- /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 2b956d216..1ca6e88fc 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 358cc3c62febd25a28f7e81156e0da3177153d6e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 23 Jan 2014 12:24:06 +0100 Subject: [PATCH 168/301] Closes #1060: Fix incorrect message box size --- apps/openmw/mwgui/messagebox.cpp | 33 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 644b8f66a..74231c008 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -287,24 +287,6 @@ namespace MWGui else { mainWidgetSize.width = textSize.width + 3*textPadding; } - mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding; - - mMainWidget->setSize(mainWidgetSize); - - MyGUI::IntCoord absCoord; - absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; - absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; - - mMainWidget->setCoord(absCoord); - mMainWidget->setSize(mainWidgetSize); - - - MyGUI::IntCoord messageWidgetCoord; - messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; - messageWidgetCoord.top = textPadding; - mMessageWidget->setCoord(messageWidgetCoord); - - mMessageWidget->setSize(textSize); MyGUI::IntCoord buttonCord; MyGUI::IntSize buttonSize(0, buttonHeight); @@ -326,6 +308,21 @@ namespace MWGui top += buttonSize.height + 2*buttonTopPadding; } + mainWidgetSize.height = top + buttonMainPadding; + mMainWidget->setSize(mainWidgetSize); + + MyGUI::IntPoint absPos; + absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2; + + mMainWidget->setPosition(absPos); + + MyGUI::IntCoord messageWidgetCoord; + messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; + messageWidgetCoord.top = textPadding; + messageWidgetCoord.width = textSize.width; + messageWidgetCoord.height = textSize.height; + mMessageWidget->setCoord(messageWidgetCoord); } // Set key focus to "Ok" button From dd7d80ffbc6e5b7eca0cef25d18c1d71fcc83742 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 23 Jan 2014 12:51:25 +0100 Subject: [PATCH 169/301] 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 bbbc4798f..c599bb973 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 170/301] 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 ead12567f..77ea3856c 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 3cbf85e8e..b8cd15f53 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 64843e7a4..feebfe90a 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 1ca6e88fc..74d987df8 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 171/301] 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 b8cd15f53..aea4e5b6a 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 849ee54399486406b2961c58a92efe857b1a5732 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 23 Jan 2014 12:54:37 +0100 Subject: [PATCH 172/301] Feature #764: Show quest names in quest book if present --- apps/openmw/mwgui/journalviewmodel.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 79a77070a..ba6552225 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -206,10 +206,12 @@ struct JournalViewModelImpl : JournalViewModel if (active_only && i->second.isFinished ()) continue; - /// \todo quest.getName() is broken? returns empty string - //const MWDialogue::Quest& quest = i->second; - - visitor (reinterpret_cast (&i->second), toUtf8Span (i->first)); + const MWDialogue::Quest& quest = i->second; + // Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal. + if (quest.getName().empty()) + visitor (reinterpret_cast (&i->second), toUtf8Span (i->first)); + else + visitor (reinterpret_cast (&i->second), toUtf8Span (quest.getName())); } } From 70264eae3b64811c90ece2dbebb256f70f9af41f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 23 Jan 2014 13:08:56 +0100 Subject: [PATCH 173/301] Feature #764: Quest completetion status in quest book --- apps/openmw/mwdialogue/quest.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 5e2739be1..520331bc1 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -39,21 +39,24 @@ namespace MWDialogue const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); + bool found=false; for (std::vector::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mData.mDisposition==index && iter->mQuestStatus!=ESM::DialInfo::QS_Name) { - mIndex = index; - if (iter->mQuestStatus==ESM::DialInfo::QS_Finished) mFinished = true; else if (iter->mQuestStatus==ESM::DialInfo::QS_Restart) mFinished = false; - return; + found = true; + // Don't return here. Quest status may actually be in a different info record, since we don't merge these (yet?) } - throw std::runtime_error ("unknown journal index for topic " + mTopic); + if (found) + mIndex = index; + else + throw std::runtime_error ("unknown journal index for topic " + mTopic); } bool Quest::isFinished() const From 19bef4fce8cf490be89c40057ab498fe30d0394f Mon Sep 17 00:00:00 2001 From: pvdk Date: Thu, 23 Jan 2014 13:18:05 +0100 Subject: [PATCH 174/301] Distinguish between release and development builds --- CMakeLists.txt | 23 ++++++++++++----------- apps/launcher/maindialog.cpp | 12 +++++++++--- components/version/version.hpp.cmake | 3 ++- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bd2cdddc..01ac9b23f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,23 +20,24 @@ include(OpenMWMacros) include(GetGitRevisionDescription) -get_git_tag_revision(taghash --tags --max-count=1) -get_git_head_revision(refspec commithash) -git_describe(version --tags ${taghash}) +get_git_tag_revision(TAGHASH --tags --max-count=1) +get_git_head_revision(REFSPEC COMMITHASH) +git_describe(VERSION --tags ${TAGHASH}) -string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" match "${version}") -if (match) - string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${version}") - string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${version}") - string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${version}") +string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}") +if (MATCH) + string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}") set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") - set(OPENMW_VERSION_COMMIT "${commithash}") + set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") + set(OPENMW_VERSION_TAGHASH "${TAGHASH}") message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...") -else (match) +else (MATCH) message(FATAL_ERROR "Failed to get valid version information from Git") -endif (match) +endif (MATCH) # doxygen main page diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 42d3d1754..67665adf0 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -73,13 +73,19 @@ Launcher::MainDialog::MainDialog(QWidget *parent) setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); // Add version information to bottom of the window - QString revision(OPENMW_VERSION_COMMIT); - revision = revision.left(10); + QString revision(OPENMW_VERSION_COMMITHASH); + QString tag(OPENMW_VERSION_TAGHASH); + if (revision == tag) { + versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); + } else { + versionLabel->setText(tr("OpenMW unstable, revision %0").arg(revision.left(10))); + } + + // Add the compile date and time QDate date(QDate::fromString(__DATE__, QLatin1String("MMM dd yyyy"))); QTime time(QTime::fromString(__TIME__, QLatin1String("hh:m:ss"))); - versionLabel->setText(tr("OpenMW %0 revision %1").arg(OPENMW_VERSION, revision)); versionLabel->setToolTip(tr("Compiled on %0 %1").arg(date.toString(Qt::SystemLocaleShortDate), time.toString(Qt::SystemLocaleShortDate))); diff --git a/components/version/version.hpp.cmake b/components/version/version.hpp.cmake index 4adb0d8c2..4cdfa32f0 100644 --- a/components/version/version.hpp.cmake +++ b/components/version/version.hpp.cmake @@ -6,7 +6,8 @@ #define OPENMW_VERSION_RELEASE @OPENMW_VERSION_RELEASE@ #define OPENMW_VERSION "@OPENMW_VERSION@" -#define OPENMW_VERSION_COMMIT "@OPENMW_VERSION_COMMIT@" +#define OPENMW_VERSION_COMMITHASH "@OPENMW_VERSION_COMMITHASH@" +#define OPENMW_VERSION_TAGHASH "@OPENMW_VERSION_TAGHASH@" #endif // VERSION_HPP From 1b1ecafdd8c68672dd51898ca9f8ccc3878b4f0b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 23 Jan 2014 15:13:37 +0100 Subject: [PATCH 175/301] introduced missing columns to data --- apps/opencs/model/world/collection.hpp | 7 ++----- apps/opencs/model/world/data.cpp | 5 +++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 74019dda7..f97b5f529 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -204,12 +204,9 @@ namespace CSMWorld const UniversalId::Type type, const UniversalId::ArgumentType argumentType) { - Record copy = getRecord(origin); + Record copy(getRecord(origin)); copy.mState = RecordBase::State_ModifiedOnly; - if (argumentType == UniversalId::ArgumentType_Id) - { - copy.get().mId = destination; - } + copy.get().mId = destination; insertRecord(copy, getAppendIndex(destination, type)); } diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 69fb4ab1e..a52821036 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -154,14 +154,17 @@ CSMWorld::Data::Data() : mRefs (mCells) mTopics.addColumn (new StringIdColumn); mTopics.addColumn (new RecordStateColumn); + mTopics.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Topic)); mTopics.addColumn (new DialogueTypeColumn); mJournals.addColumn (new StringIdColumn); mJournals.addColumn (new RecordStateColumn); + mJournals.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Journal)); mJournals.addColumn (new DialogueTypeColumn (true)); mTopicInfos.addColumn (new StringIdColumn (true)); mTopicInfos.addColumn (new RecordStateColumn); + mTopicInfos.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Journal)); mTopicInfos.addColumn (new TopicColumn (false)); mTopicInfos.addColumn (new ActorColumn); mTopicInfos.addColumn (new RaceColumn); @@ -178,6 +181,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mJournalInfos.addColumn (new StringIdColumn (true)); mJournalInfos.addColumn (new RecordStateColumn); + mJournalInfos.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Journal)); mJournalInfos.addColumn (new TopicColumn (true)); mJournalInfos.addColumn (new QuestStatusTypeColumn); mJournalInfos.addColumn (new QuestIndexColumn); @@ -225,6 +229,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mFilters.addColumn (new StringIdColumn); mFilters.addColumn (new RecordStateColumn); + mFilters.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Filter)); mFilters.addColumn (new FilterColumn); mFilters.addColumn (new DescriptionColumn); mFilters.addColumn (new ScopeColumn); From dda7ddb6f8628b30632833ef2f486b8678aa4351 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 23 Jan 2014 16:00:44 +0100 Subject: [PATCH 176/301] Disable not needed referencable creator widget when in cloning mode. --- apps/opencs/view/world/creator.hpp | 2 ++ apps/opencs/view/world/genericcreator.cpp | 6 +++++- apps/opencs/view/world/genericcreator.hpp | 4 +++- apps/opencs/view/world/referenceablecreator.cpp | 8 +++++++- apps/opencs/view/world/referenceablecreator.hpp | 1 + apps/opencs/view/world/tablebottombox.cpp | 2 ++ 6 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 5eaf3e178..5174232bc 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -28,6 +28,8 @@ namespace CSVWorld virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) = 0; virtual void setEditLock (bool locked) = 0; + + virtual void toggleWidgets(bool active = true) = 0; signals: diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index ab387393b..2a93f0e0f 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -157,4 +157,8 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originid, mClonedId = originid; mClonedType = type; mArgumentType = argumentType; -} \ No newline at end of file +} + +void CSVWorld::GenericCreator::toggleWidgets(bool active) +{ +} diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 867595bb5..e4813594b 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -48,7 +48,7 @@ namespace CSVWorld virtual std::string getId() const; virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; - + CSMWorld::Data& getData() const; const CSMWorld::UniversalId& getCollectionId() const; @@ -61,6 +61,8 @@ namespace CSVWorld virtual void setEditLock (bool locked); virtual void reset(); + + virtual void toggleWidgets (bool active = true); virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, diff --git a/apps/opencs/view/world/referenceablecreator.cpp b/apps/opencs/view/world/referenceablecreator.cpp index 718fe9ca7..7a5fca853 100644 --- a/apps/opencs/view/world/referenceablecreator.cpp +++ b/apps/opencs/view/world/referenceablecreator.cpp @@ -40,4 +40,10 @@ void CSVWorld::ReferenceableCreator::reset() { mType->setCurrentIndex (0); GenericCreator::reset(); -} \ No newline at end of file +} + +void CSVWorld::ReferenceableCreator::toggleWidgets(bool active) +{ + CSVWorld::GenericCreator::toggleWidgets(active); + mType->setEnabled(active); +} diff --git a/apps/opencs/view/world/referenceablecreator.hpp b/apps/opencs/view/world/referenceablecreator.hpp index 06e0e582b..88545575e 100644 --- a/apps/opencs/view/world/referenceablecreator.hpp +++ b/apps/opencs/view/world/referenceablecreator.hpp @@ -23,6 +23,7 @@ namespace CSVWorld const CSMWorld::UniversalId& id); virtual void reset(); + virtual void toggleWidgets(bool active = true); }; } diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index 60a206d0c..ef20cf1d4 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -153,6 +153,7 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi void CSVWorld::TableBottomBox::createRequest() { mCreator->reset(); + mCreator->toggleWidgets(true); mLayout->setCurrentWidget (mCreator); setVisible (true); mCreating = true; @@ -163,6 +164,7 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, const CSMWorld::UniversalId::ArgumentType argumnetType) { mCreator->reset(); + mCreator->toggleWidgets(false); mCreator->cloneMode(id, type, argumnetType); mLayout->setCurrentWidget(mCreator); setVisible (true); From c87d9ff38d67d698f60d0f438b45b99c546a869a Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 23 Jan 2014 16:17:04 +0100 Subject: [PATCH 177/301] Disable reference creator widget when in the clone mode. --- apps/opencs/view/world/genericcreator.hpp | 6 ++++-- apps/opencs/view/world/referencecreator.cpp | 21 +++++++++++++++++++-- apps/opencs/view/world/referencecreator.hpp | 5 +++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index e4813594b..4870ba46c 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -29,11 +29,13 @@ namespace CSVWorld std::string mErrors; QHBoxLayout *mLayout; bool mLocked; - bool mCloneMode; std::string mClonedId; CSMWorld::UniversalId::Type mClonedType; CSMWorld::UniversalId::ArgumentType mArgumentType; - + + protected: + bool mCloneMode; + protected: void update(); diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index 06b3eb76d..6991ecf36 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -49,8 +49,13 @@ std::string CSVWorld::ReferenceCreator::getErrors() const { std::string errors = GenericCreator::getErrors(); + if (mCloneMode) + { + return errors; + } + std::string cell = mCell->text().toUtf8().constData(); - + if (cell.empty()) { if (!errors.empty()) @@ -72,4 +77,16 @@ std::string CSVWorld::ReferenceCreator::getErrors() const void CSVWorld::ReferenceCreator::cellChanged() { update(); -} \ No newline at end of file +} + +void CSVWorld::ReferenceCreator::toggleWidgets(bool active) +{ + CSVWorld::GenericCreator::toggleWidgets(active); + mCell->setEnabled(active); +} + +void CSVWorld::ReferenceCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) +{ + CSVWorld::GenericCreator::cloneMode(originid, type, argumentType); + cellChanged(); //otherwise ok button will remain disabled +} diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp index 27f81564f..2f0897026 100644 --- a/apps/opencs/view/world/referencecreator.hpp +++ b/apps/opencs/view/world/referencecreator.hpp @@ -25,7 +25,12 @@ namespace CSVWorld ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); + virtual void cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType); + virtual void reset(); + virtual void toggleWidgets(bool active = true); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty From f390c7f4b0279cf64981e78dca3bfefe6316cf5b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 23 Jan 2014 16:24:03 +0100 Subject: [PATCH 178/301] Disable widgets in the cell creator. --- apps/opencs/view/world/cellcreator.cpp | 8 +++++++- apps/opencs/view/world/cellcreator.hpp | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 74fb06870..43af16c82 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -78,4 +78,10 @@ void CSVWorld::CellCreator::setType (int index) void CSVWorld::CellCreator::valueChanged (int index) { update(); -} \ No newline at end of file +} + +void CSVWorld::CellCreator::toggleWidgets(bool active) +{ + CSVWorld::GenericCreator::toggleWidgets(active); + mType->setEnabled(active); +} diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index a5473e2c9..4e8705cb0 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -28,6 +28,8 @@ namespace CSVWorld CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); virtual void reset(); + + virtual void toggleWidgets(bool active = true); private slots: From 87e83a92f84e011ad9be71c3dfb993e1c80e80c5 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 23 Jan 2014 23:14:20 +0200 Subject: [PATCH 179/301] refactoring of setting an attack type --- apps/openmw/mwinput/inputmanagerimp.cpp | 14 ---- apps/openmw/mwmechanics/aicombat.cpp | 38 +++++----- apps/openmw/mwmechanics/character.cpp | 92 ++++++++++++++++------- apps/openmw/mwmechanics/character.hpp | 3 +- apps/openmw/mwmechanics/creaturestats.hpp | 6 +- apps/openmw/mwmechanics/pathfinding.cpp | 6 ++ apps/openmw/mwmechanics/pathfinding.hpp | 2 + 7 files changed, 96 insertions(+), 65 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f0feba89f..2ad667d3f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -159,20 +159,6 @@ namespace MWInput if (action == A_Use) { MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).setAttackingOrSpell(currentValue); - if (currentValue == 1) - { - int type = MWMechanics::CreatureStats::AT_Chop; - bool forward = (mInputBinder->getChannel(A_MoveForward)->getValue() > 0 - || mInputBinder->getChannel(A_MoveBackward)->getValue() > 0); - bool side = (mInputBinder->getChannel(A_MoveLeft)->getValue() > 0 - || mInputBinder->getChannel(A_MoveRight)->getValue() > 0); - if (side && !forward) - type = MWMechanics::CreatureStats::AT_Slash; - if (forward && !side) - type = MWMechanics::CreatureStats::AT_Thrust; - - MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).setAttackType(type); - } } if (currentValue == 1) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index f12a10f9b..8f2408587 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -28,7 +28,6 @@ namespace return -1.0; } - void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); //chooses an attack depending on probability to avoid uniformity void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } @@ -57,9 +56,6 @@ namespace MWMechanics return true; //Update every frame - if(mReadyToAttack) - determineAttackType(actor, mMovement); - if(mCombatMove) { mTimerCombatMove -= duration; @@ -155,7 +151,7 @@ namespace MWMechanics else //is creature { weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon - weapRange = 100; //TODO: use true attack range (the same problem in Creature::hit) + weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) } //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); @@ -167,7 +163,9 @@ namespace MWMechanics float rangeMelee; float rangeCloseUp; bool distantCombat = false; - if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) // || WeapType_Spell_OnTarget + int attackType = actor.getClass().getCreatureStats(actor).getAttackType(); + if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon + || attackType==MWMechanics::CreatureStats::AT_Target ) { rangeMelee = 1000; // TODO: should depend on archer skill rangeCloseUp = 0; //doesn't needed when attacking from distance @@ -251,7 +249,11 @@ namespace MWMechanics //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + //try shortcut + if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) + zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + else + zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); @@ -375,24 +377,12 @@ namespace MWMechanics namespace { -void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) -{ - if (movement.mPosition[0] && !movement.mPosition[1]) //sideway - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); - else if (movement.mPosition[1]) //forward - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - else - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); -} void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) { - //the more damage attackType deals the more probability it has - if (weapon == NULL) { - //hand-to-hand and creatures' attacks handled here - //hand-to-hand deals equal damage + //hand-to-hand and creatures' attacks deal equal damage for each type float roll = static_cast(rand())/RAND_MAX; if(roll <= 0.333f) //side punch { @@ -401,10 +391,15 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement } else if(roll <= 0.666f) //forward punch movement.mPosition[1] = 1; + else + { + movement.mPosition[1] = movement.mPosition[0] = 0; + } return; } + //the more damage attackType deals the more probability it has int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; @@ -419,7 +414,8 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement } else if(roll <= (static_cast(slash) + static_cast(thrust))/total) movement.mPosition[1] = 1; - //else chop + else + movement.mPosition[1] = movement.mPosition[0] = 0; } } \ No newline at end of file diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 967f7413a..05c4e1f2a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -490,18 +490,7 @@ bool CharacterController::updateCreatureState() { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); - switch (stats.getAttackType()) - { - case CreatureStats::AT_Chop: - mCurrentWeapon = "attack1"; - break; - case CreatureStats::AT_Slash: - mCurrentWeapon = "attack2"; - break; - case CreatureStats::AT_Thrust: - mCurrentWeapon = "attack3"; - break; - } + determineAttackType(); mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_All, true, @@ -517,7 +506,7 @@ bool CharacterController::updateCreatureState() return false; } -bool CharacterController::updateNpcState(bool inwater, bool isrunning) +bool CharacterController::updateNpcState() { const MWWorld::Class &cls = MWWorld::Class::get(mPtr); NpcStats &stats = cls.getNpcStats(mPtr); @@ -586,7 +575,9 @@ bool CharacterController::updateNpcState(bool inwater, bool isrunning) if(isWerewolf) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(isrunning && !inwater && mWeaponType == WeapType_None) + if(cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) + && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) + && mWeaponType == WeapType_None) { if(!sndMgr->getSoundPlaying(mPtr, "WolfRun")) sndMgr->playSound3D(mPtr, "WolfRun", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, @@ -653,12 +644,7 @@ bool CharacterController::updateNpcState(bool inwater, bool isrunning) mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Left Hand", effect->mParticle); mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Right Hand", effect->mParticle); - switch(effectentry.mRange) - { - case 0: mAttackType = "self"; break; - case 1: mAttackType = "touch"; break; - case 2: mAttackType = "target"; break; - } + determineAttackType(effectentry.mRange); mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, @@ -715,13 +701,8 @@ bool CharacterController::updateNpcState(bool inwater, bool isrunning) int attackType = stats.getAttackType(); if(isWeapon && Settings::Manager::getBool("best attack", "Game")) attackType = getBestAttack(weapon->get()->mBase); - - if (attackType == MWMechanics::CreatureStats::AT_Chop) - mAttackType = "chop"; - else if (attackType == MWMechanics::CreatureStats::AT_Slash) - mAttackType = "slash"; else - mAttackType = "thrust"; + determineAttackType(); } mAnimation->play(mCurrentWeapon, Priority_Weapon, @@ -902,7 +883,8 @@ void CharacterController::update(float duration) bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool flying = world->isFlying(mPtr); - Ogre::Vector3 vec = cls.getMovementVector(mPtr); + //Ogre::Vector3 vec = cls.getMovementVector(mPtr); + Ogre::Vector3 vec(cls.getMovementSettings(mPtr).mPosition); vec.normalise(); if(mHitState != CharState_None && mJumpState == JumpState_None) vec = Ogre::Vector3(0.0f); @@ -1129,7 +1111,7 @@ void CharacterController::update(float duration) } if(cls.isNpc()) - forcestateupdate = updateNpcState(inwater, isrunning) || forcestateupdate; + forcestateupdate = updateNpcState() || forcestateupdate; else forcestateupdate = updateCreatureState() || forcestateupdate; @@ -1149,6 +1131,7 @@ void CharacterController::update(float duration) } movement = vec; + cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = cls.getMovementSettings(mPtr).mPosition[2] = 0; } else if(cls.getCreatureStats(mPtr).isDead()) { @@ -1319,4 +1302,57 @@ void CharacterController::updateVisibility() mAnimation->setAlpha(alpha); } +void CharacterController::determineAttackType(int spellRange) +{ + if(spellRange == -1) + { + float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition; + + if (move[0] && !move[1]) //sideway + { + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Slash); + if(mPtr.getClass().isNpc()) + mAttackType = "slash"; + else + mCurrentWeapon = "attack2"; + } + else if (move[1]) //forward + { + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Thrust); + if(mPtr.getClass().isNpc()) + mAttackType = "thrust"; + else + mCurrentWeapon = "attack3"; + } + else + { + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Chop); + if(mPtr.getClass().isNpc()) + mAttackType = "chop"; + else + mCurrentWeapon = "attack1"; + } + + } + else + { + switch(spellRange) + { + case 0: + mAttackType = "self"; + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Self); + break; + case 1: + mAttackType = "touch"; + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Touch); + break; + case 2: + mAttackType = "target"; + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Target); + break; + } + } + } + +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 01985f38e..37fc42f1e 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -165,12 +165,13 @@ class CharacterController float mSecondsOfRunning; std::string mAttackType; // slash, chop or thrust + void determineAttackType(int spellRange = -1); void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); void clearAnimQueue(); - bool updateNpcState(bool inwater, bool isrunning); + bool updateNpcState(); bool updateCreatureState(); void updateVisibility(); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 308883fc5..f9b52abc0 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -118,7 +118,11 @@ namespace MWMechanics { AT_Chop, AT_Slash, - AT_Thrust + AT_Thrust, + + AT_Self, + AT_Touch, + AT_Target, }; void setAttackType(int attackType) { mAttackType = attackType; } int getAttackType() { return mAttackType; } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index fd44fcc4c..b9c8c5c4f 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -201,6 +201,12 @@ namespace MWMechanics return Ogre::Radian(Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult))).valueDegrees(); } + float PathFinder::getDistToNext(float x, float y, float z) + { + ESM::Pathgrid::Point nextPoint = *mPath.begin(); + return distance(nextPoint, x, y, z); + } + bool PathFinder::checkWaypoint(float x, float y, float z) { if(mPath.empty()) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index de58f5db8..6f836dac7 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -22,6 +22,8 @@ namespace MWMechanics ///< \Returns true if a way point was reached float getZAngleToNext(float x, float y) const; + float getDistToNext(float x, float y, float z); + bool isPathConstructed() const { return mIsPathConstructed; From 01be9386d6521f2878daa7a6b89a319820baaddb Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 24 Jan 2014 11:22:20 +0100 Subject: [PATCH 180/301] Id to the coordinates with the boost and explicit specialisations of template member functions. --- apps/opencs/model/world/collection.hpp | 27 ++++++++++++++++++++++- apps/opencs/view/world/cellcreator.cpp | 14 +++++++++++- apps/opencs/view/world/cellcreator.hpp | 5 +++++ apps/opencs/view/world/tablebottombox.cpp | 2 +- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index f97b5f529..0a8dc5a9d 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -7,10 +7,13 @@ #include #include #include +#include +#include #include #include +#include #include "columnbase.hpp" @@ -50,6 +53,8 @@ namespace CSMWorld // not implemented Collection (const Collection&); Collection& operator= (const Collection&); + + void idToCoordiantes(Record& record, const std::string& destination) {} //dose nothing protected: @@ -150,7 +155,24 @@ namespace CSMWorld void setRecord (int index, const Record& record); ///< \attention This function must not change the ID. }; - + + template<> + class Collection > : public CollectionBase //explicit specialisation + { + void idToCoordiantes(Record& record, const std::string& destination) + { + if (record.get().isExterior()) + { + unsigned separator = destination.find(' '); + assert(separator != std::string::npos); + std::string xPos(destination.substr(0, separator)); + std::string yPos(destination.substr(separator+1, destination.size())); + record.get().mData.mX = boost::lexical_cast(xPos); + record.get().mData.mY = boost::lexical_cast(yPos); + } + } + }; + template const std::map& Collection::getIdMap() const { @@ -208,6 +230,9 @@ namespace CSMWorld copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; + //the below function has explicit specialization for cells, and does nothing fo other records + idToCoordiantes(copy, destination); + insertRecord(copy, getAppendIndex(destination, type)); } diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 43af16c82..6298e3ba5 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -22,7 +22,7 @@ std::string CSVWorld::CellCreator::getId() const CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) -: GenericCreator (data, undoStack, id) +: GenericCreator (data, undoStack, id), mCloningExterior(false) { mY = new QSpinBox (this); mY->setVisible (false); @@ -80,6 +80,18 @@ void CSVWorld::CellCreator::valueChanged (int index) update(); } +void CSVWorld::CellCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) +{ + CSVWorld::GenericCreator::cloneMode(originid, type, argumentType); + if (*(originid.begin()) == '#') //if originid points to the exterior cell + { + setType(1); //enable x and y controls + } else { + setType(0); + } +} + + void CSVWorld::CellCreator::toggleWidgets(bool active) { CSVWorld::GenericCreator::toggleWidgets(active); diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index 4e8705cb0..521abd173 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -19,6 +19,7 @@ namespace CSVWorld QLabel *mYLabel; QSpinBox *mY; + bool mCloningExterior; protected: virtual std::string getId() const; @@ -30,6 +31,10 @@ namespace CSVWorld virtual void reset(); virtual void toggleWidgets(bool active = true); + + virtual void cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType); private slots: diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index ef20cf1d4..a6801b6fc 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -164,9 +164,9 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, const CSMWorld::UniversalId::ArgumentType argumnetType) { mCreator->reset(); - mCreator->toggleWidgets(false); mCreator->cloneMode(id, type, argumnetType); mLayout->setCurrentWidget(mCreator); + mCreator->toggleWidgets(false); setVisible (true); mCreating = true; } From ac4c904e62c5172d9847a2141ef1d7b4aa0f01e9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 24 Jan 2014 13:22:30 +0100 Subject: [PATCH 181/301] Fix projectile bug --- apps/openmw/mwworld/worldimp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 57c85317f..0394a2edd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2199,6 +2199,7 @@ namespace MWWorld { if (!mWorldScene->isCellActive(*it->first.getCell())) { + deleteObject(it->first); mProjectiles.erase(it++); continue; } From 9b9c39af7af02a96db04b34c9c08bb8f1f8bf179 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 24 Jan 2014 13:22:39 +0100 Subject: [PATCH 182/301] Fix being able to activate magic bolts mid-flight --- apps/openmw/engine.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5d04b985f..f18c8a8b8 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -509,6 +509,9 @@ void OMW::Engine::activate() if (ptr.isEmpty()) return; + if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated + return; + MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); boost::shared_ptr action = From 786ed6ca5b462f3e28f6db2a647f6ca2fba361d3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 20:56:24 +0100 Subject: [PATCH 183/301] 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 c8b37e9bb..620586bd2 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 643225515..b1ec2d5ff 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 a69511acd..246103471 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 21bbe51b6..9ebb0ab08 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 0a4db30e9..9e3105168 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 63e905766..b97d1dbe9 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 9e5ec5414..c86697497 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 184/301] 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 eaf411d20..99346bc6f 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 552489bc4..91993b0be 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 55ead476b..84cc0ac23 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 5631c9470..f62ca8b3c 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 7020678d0..6251c49c4 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 f8321d74e..46ed31217 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 58a6111c5..0e0bb2814 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 55b17289c..d6887f170 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 6c11d318f..9c7bf551d 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 032c542396758e6f71bccdd968476ab8dfa30652 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 24 Jan 2014 20:34:33 +0100 Subject: [PATCH 185/301] improving the cell cloning. --- apps/opencs/view/world/cellcreator.cpp | 9 +++++++-- apps/opencs/view/world/cellcreator.hpp | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 6298e3ba5..ebf88122b 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -22,7 +22,7 @@ std::string CSVWorld::CellCreator::getId() const CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) -: GenericCreator (data, undoStack, id), mCloningExterior(false) +: GenericCreator (data, undoStack, id) { mY = new QSpinBox (this); mY->setVisible (false); @@ -61,6 +61,7 @@ void CSVWorld::CellCreator::reset() mX->setValue (0); mY->setValue (0); mType->setCurrentIndex (0); + setType(0); GenericCreator::reset(); } @@ -80,14 +81,18 @@ void CSVWorld::CellCreator::valueChanged (int index) update(); } -void CSVWorld::CellCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) +void CSVWorld::CellCreator::cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType) { CSVWorld::GenericCreator::cloneMode(originid, type, argumentType); if (*(originid.begin()) == '#') //if originid points to the exterior cell { setType(1); //enable x and y controls + mType->setCurrentIndex(1); } else { setType(0); + mType->setCurrentIndex(0); } } diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index 521abd173..3130c1328 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -19,7 +19,6 @@ namespace CSVWorld QLabel *mYLabel; QSpinBox *mY; - bool mCloningExterior; protected: virtual std::string getId() const; From a1ac99463fff5440235d01e041111d053b2efff6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 24 Jan 2014 22:52:04 +0100 Subject: [PATCH 186/301] Fix an uninitialized value --- components/terrain/quadtreenode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 02225cb02..7fc452fbf 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -433,6 +433,7 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) LayerInfo info; info.mDiffuseMap = "textures\\_land_default.dds"; info.mParallax = false; + info.mSpecular = false; layer.push_back(info); matGen.setLayerList(layer); makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, matGen.generateForCompositeMapRTT(Ogre::MaterialPtr())); From f09328ca845306f2a033157ee924ba7d91b5ef48 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 13:34:56 +0100 Subject: [PATCH 187/301] 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 c39de4400..4d47e7eb7 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 ba6114262..c09b4fea0 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 7df2105dc..dec27199a 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 e11d1afcc..8e49d6614 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 9838a667f..68dc947af 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 120a83fae..522bbb321 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 aad9adcc4..20f40de99 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 56096b2f5..f9dd4e566 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 6251c49c4..e94f790c7 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 9d07d0119..e5138eaea 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 188/301] 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 b9f630290..19427af0c 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 189/301] 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 b2c0c1968..66dea4f1d 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 51e91a954..34cfe16fa 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 ee0d07731..40e6cde69 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 190/301] 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 e5138eaea..076fcfdb6 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 191/301] 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 df957408d..7bd00d6bf 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 192/301] 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 dbea10e77..8caea770e 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 193/301] 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 4d47e7eb7..fa0fe888b 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 c09b4fea0..9ed3bf80f 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 dec27199a..6ace7dc0f 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 8e49d6614..66bd805af 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 68dc947af..bc440d818 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 522bbb321..6fbcfdc6b 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 20f40de99..5fe878cd4 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 e94f790c7..a396d78c5 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 d9ab8129d..d73bcaf74 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 74d987df8..40ef7ecb6 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 000000000..1fa5f907e --- /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 000000000..5d036c736 --- /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 a1fbd1fcc8ada98cfb1c8c0583cdfe3669c87cd6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 00:14:49 +0100 Subject: [PATCH 194/301] Revert "Merge remote-tracking branch 'mrcheko/master'" This reverts commit df7c139e2f614f8a951aae1b4889b920af0013fe, reversing changes made to fec26342cd57269bc0801e5d901e00419fbc7cec. --- apps/openmw/mwmechanics/aicombat.cpp | 27 ++++++++++--------------- apps/openmw/mwmechanics/aicombat.hpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 17 ---------------- apps/openmw/mwmechanics/pathfinding.hpp | 5 ----- 4 files changed, 12 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 283c7f042..f24618510 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -1,5 +1,4 @@ #include "aicombat.hpp" -#include "aifollow.hpp" #include "movement.hpp" @@ -39,7 +38,7 @@ namespace MWMechanics mTimerAttack(0), mTimerReact(0), mTimerCombatMove(0), - mFollowTarget(false), + mCloseUp(false), mReadyToAttack(false), mStrike(false), mCombatMove(false), @@ -183,7 +182,7 @@ namespace MWMechanics Ogre::Vector3 vDir = vDest - vStart; float distBetween = vDir.length(); - if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mFollowTarget) ) + if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mCloseUp) ) { //Melee and Close-up combat vDir.z = 0; @@ -193,10 +192,13 @@ namespace MWMechanics // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + if(mPathFinder.isPathConstructed()) + mPathFinder.clearPath(); + //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); - if (mFollowTarget && distBetween > rangeMelee) + if (mCloseUp && distBetween > rangeMelee) { //Close-up combat: just run up on target mMovement.mPosition[1] = 1; @@ -212,7 +214,7 @@ namespace MWMechanics mTimerCombatMove = 0.1f + 0.1f * static_cast(rand())/RAND_MAX; mCombatMove = true; } - else if(actor.getClass().isNpc() && (!distantCombat || (distantCombat && rangeMelee/5))) + else if(!distantCombat || (distantCombat && rangeMelee/5)) { //apply sideway movement (kind of dodging) with some probability if(static_cast(rand())/RAND_MAX < 0.25) @@ -230,19 +232,13 @@ namespace MWMechanics mReadyToAttack = true; //only once got in melee combat, actor is allowed to use close-up shortcutting - mFollowTarget = true; + mCloseUp = true; } } else { - //target is at far distance: build path to target OR follow target (if previously actor had reached it once) - - /* - //apply when AIFOLLOW package implementation will be existent - if(mFollowTarget) - actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiFollow(mTarget));*/ - - mFollowTarget = false; + //target is at far distance: build & follow the path + mCloseUp = false; buildNewPath(actor); @@ -344,8 +340,7 @@ namespace MWMechanics //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - newPathFinder.syncStart(mPathFinder.getPath()); - mPathFinder = newPathFinder; + mPathFinder = newPathFinder; } } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index a24183c65..ab9b9a821 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -37,7 +37,7 @@ namespace MWMechanics float mTimerCombatMove; bool mReadyToAttack, mStrike; - bool mFollowTarget; + bool mCloseUp; bool mCombatMove; MWMechanics::Movement mMovement; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index fd44fcc4c..c8bc9b49c 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -4,7 +4,6 @@ #include "../mwbase/environment.hpp" #include "OgreMath.h" -#include "OgreVector3.h" #include #include @@ -234,21 +233,5 @@ namespace MWMechanics return false; } - - void PathFinder::syncStart(const std::list &path) - { - std::list::const_iterator oldStart = path.begin(); - std::list::iterator iter = ++mPath.begin(); - - if( (*iter).mX == oldStart->mX - && (*iter).mY == oldStart->mY - && (*iter).mZ == oldStart->mZ - && (*iter).mAutogenerated == oldStart->mAutogenerated - && (*iter).mConnectionNum == oldStart->mConnectionNum ) - { - mPath.pop_front(); - } - - } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index de58f5db8..916df850b 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -37,11 +37,6 @@ namespace MWMechanics return mPath; } - //When first point of newly created path is the nearest to actor point, then - //the cituation can occure when this point is undesirable (if the 2nd point of new path == the 1st point of old path) - //This functions deletes that point. - void syncStart(const std::list &path); - private: std::list mPath; bool mIsPathConstructed; From b1066de81de13f493ce2b94207bcbbf7e7d0d722 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 00:16:56 +0100 Subject: [PATCH 195/301] Revert "Merge remote-tracking branch 'mrcheko/master'" This reverts commit 4e360136b10061e29fd60492544aad59850513e7, reversing changes made to 047bbe43b2ca08a63c8e18d2b4af52e0dbefb37b. Conflicts: apps/openmw/mwmechanics/aicombat.cpp --- apps/openmw/mwmechanics/actors.cpp | 3 +- apps/openmw/mwmechanics/aicombat.cpp | 402 +++++----------------- apps/openmw/mwmechanics/aicombat.hpp | 26 +- apps/openmw/mwmechanics/aisequence.cpp | 7 +- apps/openmw/mwmechanics/aisequence.hpp | 5 +- apps/openmw/mwmechanics/character.cpp | 4 +- apps/openmw/mwmechanics/character.hpp | 10 +- apps/openmw/mwmechanics/creaturestats.cpp | 11 +- apps/openmw/mwscript/aiextensions.cpp | 23 +- 9 files changed, 123 insertions(+), 368 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 35783bd9f..272b9a0d0 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -13,7 +13,6 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/actionequip.hpp" -#include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -209,7 +208,7 @@ namespace MWMechanics && LOS ) { - creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayer().getPlayer())); + creatureStats.getAiSequence().stack(AiCombat("player")); creatureStats.setHostile(true); } } diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index f24618510..700cea2f3 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -4,18 +4,15 @@ #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" - +#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "character.hpp" -#include "../mwworld/inventorystore.hpp" - #include "creaturestats.hpp" +#include "npcstats.hpp" #include -#include namespace { @@ -25,324 +22,130 @@ namespace return 1.0; return -1.0; } - - void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); - //chooses an attack depending on probability to avoid uniformity - void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } namespace MWMechanics { - AiCombat::AiCombat(const MWWorld::Ptr& actor) : - mTarget(actor), - mTimerAttack(0), - mTimerReact(0), - mTimerCombatMove(0), - mCloseUp(false), - mReadyToAttack(false), - mStrike(false), - mCombatMove(false), - mMovement() + + AiCombat::AiCombat(const std::string &targetId) + :mTargetId(targetId),mTimer(0),mTimer2(0) { } bool AiCombat::execute (const MWWorld::Ptr& actor,float duration) { - //General description - if(!actor.getClass().getCreatureStats(actor).isHostile()) - return true; - if (mTarget.getClass().getCreatureStats(mTarget).isDead()) - return true; + if(!MWWorld::Class::get(actor).getCreatureStats(actor).isHostile()) return true; - //Update every frame - if(mReadyToAttack) - determineAttackType(actor, mMovement); + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mTargetId, false); - if(mCombatMove) - { - mTimerCombatMove -= duration; - if( mTimerCombatMove <= 0) - { - mTimerCombatMove = 0; - mMovement.mPosition[1] = mMovement.mPosition[0] = 0; - mCombatMove = false; - } - } - actor.getClass().getMovementSettings(actor) = mMovement; - - //actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mReadyToAttack); - mTimerAttack -= duration; - actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); - - float tReaction = 0.25f; - if(mTimerReact < tReaction) - { - mTimerReact += duration; - return false; - } - - //Update with period = tReaction + if (target.getClass().getCreatureStats(target).isDead()) + return true; - mTimerReact = 0; - - //actual attacking logic - //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f - float attackPeriod = 1.0f; - if(mReadyToAttack) - { - if(mTimerAttack <= -attackPeriod) - { - //TODO: should depend on time between 'start' to 'min attack' - //for better controlling of NPCs' attack strength. - //Also it seems that this time is different for slash/thrust/chop - mTimerAttack = 0.35f * static_cast(rand())/RAND_MAX; - mStrike = true; - - //say a provoking combat phrase - if (actor.getClass().isNpc()) - { - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - int chance = store.get().find("iVoiceAttackOdds")->getInt(); - int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - if (roll < chance) - { - MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); - } - } - } - else if (mTimerAttack <= 0) - mStrike = false; - } - else - { - mTimerAttack = -attackPeriod; - mStrike = false; - } - - const MWWorld::Class &cls = actor.getClass(); - const ESM::Weapon *weapon = NULL; - MWMechanics::WeaponType weaptype; - float weapRange, weapSpeed = 1.0f; + if(MWWorld::Class::get(actor).getCreatureStats(actor).getHealth().getCurrent() <= 0) return true; actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); - if(actor.getClass().hasInventoryStore(actor)) + if (actor.getClass().hasInventoryStore(actor)) { MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon); - - //Get weapon speed and range - MWWorld::ContainerStoreIterator weaponSlot = - MWMechanics::getActiveWeapon(cls.getCreatureStats(actor), cls.getInventoryStore(actor), &weaptype); - if (weaptype == WeapType_HandToHand) - { - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - weapRange = gmst.find("fHandToHandReach")->getFloat(); - } - else - { - weapon = weaponSlot->get()->mBase; - weapRange = weapon->mData.mReach; - weapSpeed = weapon->mData.mSpeed; - } - weapRange *= 100.0f; - } - else //is creature - { - weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon - weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) + //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); } - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - ESM::Position pos = actor.getRefData().getPosition(); - - float zAngle; + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); - float rangeMelee; - float rangeCloseUp; - bool distantCombat = false; - if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) // || WeapType_Spell_OnTarget - { - rangeMelee = 1000; // TODO: should depend on archer skill - rangeCloseUp = 0; //doesn't needed when attacking from distance - distantCombat = true; - } - else - { - rangeMelee = weapRange; - rangeCloseUp = 300; - } - - Ogre::Vector3 vStart(pos.pos[0], pos.pos[1], pos.pos[2]); - ESM::Position targetPos = mTarget.getRefData().getPosition(); - Ogre::Vector3 vDest(targetPos.pos[0], targetPos.pos[1], targetPos.pos[2]); - Ogre::Vector3 vDir = vDest - vStart; - float distBetween = vDir.length(); + float xCell = 0; + float yCell = 0; - if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mCloseUp) ) + if (actor.getCell()->mCell->isExterior()) { - //Melee and Close-up combat - vDir.z = 0; - float dirLen = vDir.length(); - zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); + xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + } - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + ESM::Pathgrid::Point dest; + dest.mX = target.getRefData().getPosition().pos[0]; + dest.mY = target.getRefData().getPosition().pos[1]; + dest.mZ = target.getRefData().getPosition().pos[2]; - if(mPathFinder.isPathConstructed()) - mPathFinder.clearPath(); + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; - //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + mTimer2 = mTimer2 + duration; - //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); - if (mCloseUp && distBetween > rangeMelee) + if(!mPathFinder.isPathConstructed()) + mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); + else + { + mPathFinder2.buildPath(start, dest, pathgrid, xCell, yCell, true); + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + if((mTimer2 > 0.25)&&(mPathFinder2.getPathSize() < mPathFinder.getPathSize() || + (dest.mX - lastPt.mX)*(dest.mX - lastPt.mX)+(dest.mY - lastPt.mY)*(dest.mY - lastPt.mY)+(dest.mZ - lastPt.mZ)*(dest.mZ - lastPt.mZ) > 200*200)) { - //Close-up combat: just run up on target - mMovement.mPosition[1] = 1; + mTimer2 = 0; + mPathFinder = mPathFinder2; } - else - { - //Melee: stop running and attack - mMovement.mPosition[1] = 0; - chooseBestAttack(weapon, mMovement); - - if(mMovement.mPosition[0] != 0 || mMovement.mPosition[1]) - { - mTimerCombatMove = 0.1f + 0.1f * static_cast(rand())/RAND_MAX; - mCombatMove = true; - } - else if(!distantCombat || (distantCombat && rangeMelee/5)) - { - //apply sideway movement (kind of dodging) with some probability - if(static_cast(rand())/RAND_MAX < 0.25) - { - mMovement.mPosition[0] = static_cast(rand())/RAND_MAX < 0.5? 1: -1; - mTimerCombatMove = 0.05f + 0.15f * static_cast(rand())/RAND_MAX; - mCombatMove = true; - } - } + } - if(distantCombat && distBetween < rangeMelee/4) - { - mMovement.mPosition[1] = -1; - } + mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - mReadyToAttack = true; - //only once got in melee combat, actor is allowed to use close-up shortcutting - mCloseUp = true; - } - } - else - { - //target is at far distance: build & follow the path - mCloseUp = false; + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - buildNewPath(actor); + // TODO: use movement settings instead of rotating directly + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - //delete visited path node - mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + float range = 100; + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); + if((dest.mX - start.mX)*(dest.mX - start.mX)+(dest.mY - start.mY)*(dest.mY - start.mY)+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) + < range*range) + { + float directionX = dest.mX - start.mX; + float directionY = dest.mY - start.mY; + float directionResult = sqrt(directionX * directionX + directionY * directionY); + zAngle = Ogre::Radian( Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult)) ).valueDegrees(); // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - mMovement.mPosition[1] = 1; - mReadyToAttack = false; - } - if(distBetween > rangeMelee) - { - //special run attack; it shouldn't affect melee combat tactics - if(actor.getClass().getMovementSettings(actor).mPosition[1] == 1) + mPathFinder.clearPath(); + + if(mTimer == 0) + { + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); + //mTimer = mTimer + duration; + } + if( mTimer > 1) { - //check if actor can overcome the distance = distToTarget - attackerWeapRange - //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) - //then start attacking - float speed1 = cls.getSpeed(actor); - float speed2 = mTarget.getClass().getSpeed(mTarget); - if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0 - && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0) - speed2 = 0; - - float s1 = distBetween - weapRange; - float t = s1/speed1; - float s2 = speed2 * t; - float t_swing = 0.17f/weapSpeed;//0.17 should be the time of playing weapon anim from 'start' to 'hit' tags - if (t + s2/speed1 <= t_swing) + if (actor.getClass().isNpc()) { - mReadyToAttack = true; - if(mTimerAttack <= -attackPeriod) + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + int chance = store.get().find("iVoiceAttackOdds")->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < chance) { - mTimerAttack = 0.3f*static_cast(rand())/RAND_MAX; - mStrike = true; + MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); } } - } - } - - return false; - } - void AiCombat::buildNewPath(const MWWorld::Ptr& actor) - { - //Construct path to target - ESM::Pathgrid::Point dest; - dest.mX = mTarget.getRefData().getPosition().pos[0]; - dest.mY = mTarget.getRefData().getPosition().pos[1]; - dest.mZ = mTarget.getRefData().getPosition().pos[2]; - Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); - - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); - float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); - - float targetPosThreshold; - bool isOutside = actor.getCell()->mCell->isExterior(); - if (isOutside) - targetPosThreshold = 300; - else - targetPosThreshold = 100; - - if(dist > targetPosThreshold) - { - //construct new path only if target has moved away more than on - ESM::Position pos = actor.getRefData().getPosition(); - - ESM::Pathgrid::Point start; - start.mX = pos.pos[0]; - start.mY = pos.pos[1]; - start.mZ = pos.pos[2]; - - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); - - float xCell = 0; - float yCell = 0; - - if (actor.getCell()->mCell->isExterior()) - { - xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; - yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); + mTimer = 0; } - - if(!mPathFinder.isPathConstructed()) - mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside); else { - PathFinder newPathFinder; - newPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside); - - //TO EXPLORE: - //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, - //not the actual path length. Here we should know if the new path is actually more effective. - //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - mPathFinder = newPathFinder; + mTimer = mTimer + duration; } + + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(!MWWorld::Class::get(actor).getCreatureStats(actor).getAttackingOrSpell()); } + return false; } int AiCombat::getTypeId() const @@ -355,64 +158,15 @@ namespace MWMechanics return 1; } - MWWorld::Ptr AiCombat::getTarget() const + const std::string &AiCombat::getTargetId() const { - return mTarget; + return mTargetId; } + AiCombat *MWMechanics::AiCombat::clone() const { return new AiCombat(*this); } } -namespace -{ -void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) -{ - if (movement.mPosition[0] && !movement.mPosition[1]) //sideway - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); - else if (movement.mPosition[1]) //forward - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - else - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); -} - -void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) -{ - //the more damage attackType deals the more probability it has - - if (weapon == NULL) - { - //hand-to-hand and creatures' attacks handled here - //hand-to-hand deals equal damage - float roll = static_cast(rand())/RAND_MAX; - if(roll <= 0.333f) //side punch - { - movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; - movement.mPosition[1] = 0; - } - else if(roll <= 0.666f) //forward punch - movement.mPosition[1] = 1; - - return; - } - - int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; - int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; - int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; - - float total = slash + chop + thrust; - - float roll = static_cast(rand())/RAND_MAX; - if(roll <= static_cast(slash)/total) - { - movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; - movement.mPosition[1] = 0; - } - else if(roll <= (static_cast(slash) + static_cast(thrust))/total) - movement.mPosition[1] = 1; - //else chop -} - -} diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index ab9b9a821..82efc043b 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -7,14 +7,12 @@ #include "movement.hpp" -#include "../mwbase/world.hpp" - namespace MWMechanics { class AiCombat : public AiPackage { public: - AiCombat(const MWWorld::Ptr& actor); + AiCombat(const std::string &targetId); virtual AiCombat *clone() const; @@ -25,25 +23,15 @@ namespace MWMechanics virtual unsigned int getPriority() const; - MWWorld::Ptr getTarget() const; + const std::string &getTargetId() const; private: - PathFinder mPathFinder; - //controls duration of the actual strike - float mTimerAttack; - float mTimerReact; - //controls duration of the sideway & forward moves - //when mCombatMove is true - float mTimerCombatMove; - - bool mReadyToAttack, mStrike; - bool mCloseUp; - bool mCombatMove; + std::string mTargetId; - MWMechanics::Movement mMovement; - MWWorld::Ptr mTarget; - - void buildNewPath(const MWWorld::Ptr& actor); + PathFinder mPathFinder; + PathFinder mPathFinder2; + float mTimer; + float mTimer2; }; } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 3902b9186..73caa6ca7 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -55,12 +55,13 @@ int MWMechanics::AiSequence::getTypeId() const return mPackages.front()->getTypeId(); } -MWWorld::Ptr MWMechanics::AiSequence::getCombatTarget() const +bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const { if (getTypeId() != AiPackage::TypeIdCombat) - return MWWorld::Ptr(); + return false; const AiCombat *combat = static_cast(mPackages.front()); - return combat->getTarget(); + targetActorId = combat->getTargetId(); + return true; } void MWMechanics::AiSequence::stopCombat() diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 075c43ff7..d65c31616 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -36,8 +36,9 @@ namespace MWMechanics int getTypeId() const; ///< @see enum AiPackage::TypeId - MWWorld::Ptr getCombatTarget () const; - ///< Return the combat target if a combat package is active, or an empty Ptr otherwise + bool getCombatTarget (std::string &targetActorId) const; + ///< Return true and assign target if combat package is currently + /// active, return false otherwise void stopCombat(); ///< Removes all combat packages until first non-combat or stack empty. diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2f5b0ca6c..64df7becc 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -310,7 +310,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat } -void getWeaponGroup(WeaponType weaptype, std::string &group) +void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group) { const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); if(info != sWeaponTypeListEnd) @@ -318,7 +318,7 @@ void getWeaponGroup(WeaponType weaptype, std::string &group) } -MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) +MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) { if(stats.getDrawState() == DrawState_Spell) { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 915de93eb..112ea18ce 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -169,6 +169,12 @@ class CharacterController void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); + static void getWeaponGroup(WeaponType weaptype, std::string &group); + + static MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, + MWWorld::InventoryStore &inv, + WeaponType *weaptype); + void clearAnimQueue(); bool updateWeaponState(bool inwater, bool isrunning); @@ -205,10 +211,6 @@ public: void forceStateUpdate(); }; - void getWeaponGroup(WeaponType weaptype, std::string &group); - MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, - MWWorld::InventoryStore &inv, - WeaponType *weaptype); } #endif /* GAME_MWMECHANICS_CHARACTER_HPP */ diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index ec300f6f0..c6522f08f 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -348,9 +348,14 @@ namespace MWMechanics bool CreatureStats::getCreatureTargetted() const { - if (mAiSequence.getCombatTarget().isEmpty()) - return false; - return mAiSequence.getCombatTarget().getTypeName() == typeid(ESM::Creature).name(); + std::string target; + if (mAiSequence.getCombatTarget(target)) + { + MWWorld::Ptr targetPtr; + targetPtr = MWBase::Environment::get().getWorld()->getPtr(target, true); + return targetPtr.getTypeName() == typeid(ESM::Creature).name(); + } + return false; } float CreatureStats::getEvasion() const diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 75c635103..09b1ed447 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -412,10 +412,16 @@ namespace MWScript std::string testedTargetId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); + const MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + std::string currentTargetId; - MWWorld::Ptr target = creatureStats.getAiSequence().getCombatTarget(); - runtime.push(Misc::StringUtils::ciEqual(target.getCellRef().mRefID, testedTargetId)); + bool targetsAreEqual = false; + if (creatureStats.getAiSequence().getCombatTarget (currentTargetId)) + { + if (currentTargetId == testedTargetId) + targetsAreEqual = true; + } + runtime.push(int(targetsAreEqual)); } }; @@ -426,14 +432,13 @@ namespace MWScript virtual void execute (Interpreter::Runtime &runtime) { MWWorld::Ptr actor = R()(runtime); - std::string targetID = runtime.getStringLiteral (runtime[0].mInteger); + std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - - creatureStats.setHostile(true); - creatureStats.getAiSequence().stack( - MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(targetID, true) )); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + creatureStats.getAiSequence().stack(MWMechanics::AiCombat(actorID)); + if (actorID == "player") + creatureStats.setHostile(true); } }; From eba6c9a8fd9b8083ce52b3b2a02689e411c0176f Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 02:49:01 +0100 Subject: [PATCH 196/301] Fix massive console spam regarding the scrib's idle3 animation --- apps/openmw/mwrender/animation.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e8d215f5a..c0e4eebda 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -584,7 +584,11 @@ bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const s const std::string stoptag = groupname+": "+stop; NifOgre::TextKeyMap::const_iterator stopkey(groupstart); - while(stopkey != keys.end() && stopkey->second != stoptag) + while(stopkey != keys.end() + // We have to ignore extra garbage at the end. + // The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop". + // Why, just why? :( + && (stopkey->second.size() < stoptag.size() || stopkey->second.substr(0,stoptag.size()) != stoptag)) stopkey++; if(stopkey == keys.end()) return false; From 45b3aa3d934b644436843e9ff305ab5aa1d4f3dd Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 26 Jan 2014 23:32:57 +0200 Subject: [PATCH 197/301] corrected things connected to determining attack type --- apps/openmw/mwmechanics/aicombat.cpp | 4 +- apps/openmw/mwmechanics/character.cpp | 74 +++++++++-------------- apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 6 +- 4 files changed, 31 insertions(+), 55 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 8f2408587..5ae2a0ca1 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -163,9 +163,7 @@ namespace MWMechanics float rangeMelee; float rangeCloseUp; bool distantCombat = false; - int attackType = actor.getClass().getCreatureStats(actor).getAttackType(); - if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon - || attackType==MWMechanics::CreatureStats::AT_Target ) + if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) { rangeMelee = 1000; // TODO: should depend on archer skill rangeCloseUp = 0; //doesn't needed when attacking from distance diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 05c4e1f2a..954eb18b3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -644,7 +644,12 @@ bool CharacterController::updateNpcState() mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Left Hand", effect->mParticle); mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Right Hand", effect->mParticle); - determineAttackType(effectentry.mRange); + switch(effectentry.mRange) + { + case 0: mAttackType = "self"; break; + case 1: mAttackType = "touch"; break; + case 2: mAttackType = "target"; break; + } mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, @@ -698,7 +703,7 @@ bool CharacterController::updateNpcState() mAttackType = "shoot"; else { - int attackType = stats.getAttackType(); + int attackType; if(isWeapon && Settings::Manager::getBool("best attack", "Game")) attackType = getBestAttack(weapon->get()->mBase); else @@ -1302,57 +1307,34 @@ void CharacterController::updateVisibility() mAnimation->setAlpha(alpha); } -void CharacterController::determineAttackType(int spellRange) +void CharacterController::determineAttackType() { - if(spellRange == -1) - { - float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition; + float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition; - if (move[0] && !move[1]) //sideway - { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Slash); - if(mPtr.getClass().isNpc()) - mAttackType = "slash"; - else - mCurrentWeapon = "attack2"; - } - else if (move[1]) //forward - { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - if(mPtr.getClass().isNpc()) - mAttackType = "thrust"; - else - mCurrentWeapon = "attack3"; - } + if (move[0] && !move[1]) //sideway + { + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Slash); + if(mPtr.getClass().isNpc()) + mAttackType = "slash"; else - { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Chop); - if(mPtr.getClass().isNpc()) - mAttackType = "chop"; - else - mCurrentWeapon = "attack1"; - } - + mCurrentWeapon = "attack2"; + } + else if (move[1]) //forward + { + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Thrust); + if(mPtr.getClass().isNpc()) + mAttackType = "thrust"; + else + mCurrentWeapon = "attack3"; } else { - switch(spellRange) - { - case 0: - mAttackType = "self"; - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Self); - break; - case 1: - mAttackType = "touch"; - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Touch); - break; - case 2: - mAttackType = "target"; - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Target); - break; - } + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Chop); + if(mPtr.getClass().isNpc()) + mAttackType = "chop"; + else + mCurrentWeapon = "attack1"; } - } } \ No newline at end of file diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 37fc42f1e..6950854c8 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -165,7 +165,7 @@ class CharacterController float mSecondsOfRunning; std::string mAttackType; // slash, chop or thrust - void determineAttackType(int spellRange = -1); + void determineAttackType(); void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index f9b52abc0..308883fc5 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -118,11 +118,7 @@ namespace MWMechanics { AT_Chop, AT_Slash, - AT_Thrust, - - AT_Self, - AT_Touch, - AT_Target, + AT_Thrust }; void setAttackType(int attackType) { mAttackType = attackType; } int getAttackType() { return mAttackType; } From 62c2259c871de8826b83fd239bb4a229f8cc7db3 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 10:10:23 +0100 Subject: [PATCH 198/301] removing coordinates handling --- apps/opencs/model/world/collection.hpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 0a8dc5a9d..d91260a5f 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -8,7 +8,6 @@ #include #include #include -#include #include @@ -54,8 +53,6 @@ namespace CSMWorld Collection (const Collection&); Collection& operator= (const Collection&); - void idToCoordiantes(Record& record, const std::string& destination) {} //dose nothing - protected: const std::map& getIdMap() const; @@ -156,23 +153,6 @@ namespace CSMWorld ///< \attention This function must not change the ID. }; - template<> - class Collection > : public CollectionBase //explicit specialisation - { - void idToCoordiantes(Record& record, const std::string& destination) - { - if (record.get().isExterior()) - { - unsigned separator = destination.find(' '); - assert(separator != std::string::npos); - std::string xPos(destination.substr(0, separator)); - std::string yPos(destination.substr(separator+1, destination.size())); - record.get().mData.mX = boost::lexical_cast(xPos); - record.get().mData.mY = boost::lexical_cast(yPos); - } - } - }; - template const std::map& Collection::getIdMap() const { @@ -230,9 +210,6 @@ namespace CSMWorld copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; - //the below function has explicit specialization for cells, and does nothing fo other records - idToCoordiantes(copy, destination); - insertRecord(copy, getAppendIndex(destination, type)); } From be8b978a89b182ada92eb46ae94ac0970c8038c4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 12:23:03 +0100 Subject: [PATCH 199/301] corrected mistake in data --- apps/opencs/model/world/commands.cpp | 1 - apps/opencs/model/world/data.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 762e4809c..789e65a21 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,4 +1,3 @@ - #include "commands.hpp" #include diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index a52821036..8d53c4e53 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -164,7 +164,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mTopicInfos.addColumn (new StringIdColumn (true)); mTopicInfos.addColumn (new RecordStateColumn); - mTopicInfos.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Journal)); + mTopicInfos.addColumn (new FixedRecordTypeColumn (UniversalId::Type_TopicInfo)); mTopicInfos.addColumn (new TopicColumn (false)); mTopicInfos.addColumn (new ActorColumn); mTopicInfos.addColumn (new RaceColumn); From aa6d1ff4c3b4fa5d490c0a2468212ef9412bf928 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 13:08:14 +0100 Subject: [PATCH 200/301] removed needless argument for cloning --- apps/opencs/model/world/collection.hpp | 6 ++---- apps/opencs/model/world/collectionbase.hpp | 3 +-- apps/opencs/model/world/commands.cpp | 4 +--- apps/opencs/model/world/commands.hpp | 2 -- apps/opencs/model/world/idtable.cpp | 3 +-- apps/opencs/model/world/idtable.hpp | 1 - apps/opencs/model/world/refidcollection.cpp | 4 +--- apps/opencs/model/world/refidcollection.hpp | 3 +-- apps/opencs/view/world/cellcreator.cpp | 5 ++--- apps/opencs/view/world/cellcreator.hpp | 3 +-- apps/opencs/view/world/creator.hpp | 3 ++- apps/opencs/view/world/genericcreator.cpp | 6 ++---- apps/opencs/view/world/genericcreator.hpp | 3 +-- apps/opencs/view/world/referencecreator.cpp | 5 +++-- apps/opencs/view/world/referencecreator.hpp | 3 +-- apps/opencs/view/world/tablebottombox.cpp | 5 ++--- apps/opencs/view/world/tablebottombox.hpp | 3 ++- apps/opencs/view/world/tablesubview.cpp | 10 ++++++---- apps/opencs/view/world/tablesubview.hpp | 3 +-- 19 files changed, 30 insertions(+), 45 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index d91260a5f..4bb10c1d8 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -102,8 +102,7 @@ namespace CSMWorld virtual void cloneRecord(const std::string& origin, const std::string& destination, - const UniversalId::Type type, - const UniversalId::ArgumentType argumentType); + const UniversalId::Type type); virtual int searchId (const std::string& id) const; ////< Search record with \a id. @@ -203,8 +202,7 @@ namespace CSMWorld template void Collection::cloneRecord(const std::string& origin, const std::string& destination, - const UniversalId::Type type, - const UniversalId::ArgumentType argumentType) + const UniversalId::Type type) { Record copy(getRecord(origin)); copy.mState = RecordBase::State_ModifiedOnly; diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index d32847490..2473ae741 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -76,8 +76,7 @@ namespace CSMWorld virtual void cloneRecord(const std::string& origin, const std::string& destination, - const UniversalId::Type type, - const UniversalId::ArgumentType argumentType) = 0; + const UniversalId::Type type) = 0; virtual const RecordBase& getRecord (const std::string& id) const = 0; diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 789e65a21..5eb2e9414 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -27,13 +27,11 @@ CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, const std::string& idOrigin, const std::string& IdDestination, const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType, QUndoCommand* parent) : QUndoCommand(parent), mModel(model), mIdOrigin(idOrigin), mIdDestination(Misc::StringUtils::lowerCase(IdDestination)), - mArgumentType(argumentType), mType(type) { setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); @@ -42,7 +40,7 @@ CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, void CSMWorld::CloneCommand::redo() { - mModel.cloneRecord(mIdOrigin, mIdDestination, mArgumentType, mType); + mModel.cloneRecord(mIdOrigin, mIdDestination, mType); for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) mModel.setData(mModel.getModelIndex(mIdDestination, iter->first), iter->second); diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 04eb0afbf..0dedea012 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -45,7 +45,6 @@ namespace CSMWorld std::string mIdOrigin; std::string mIdDestination; UniversalId::Type mType; - UniversalId::ArgumentType mArgumentType; std::map mValues; public: @@ -53,7 +52,6 @@ namespace CSMWorld CloneCommand (IdTable& model, const std::string& idOrigin, const std::string& IdDestination, const UniversalId::Type type, - const UniversalId::ArgumentType argumentType, QUndoCommand* parent = 0); virtual void redo(); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index f131f7087..bea307aa2 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -126,12 +126,11 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type void CSMWorld::IdTable::cloneRecord(const std::string& origin, const std::string& destination, - CSMWorld::UniversalId::ArgumentType argumentType, CSMWorld::UniversalId::Type type) { int index = mIdCollection->getAppendIndex (destination); beginInsertRows (QModelIndex(), index, index); - mIdCollection->cloneRecord(origin, destination, type, argumentType); + mIdCollection->cloneRecord(origin, destination, type); endInsertRows(); } diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index ce47307e3..2d620004c 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -65,7 +65,6 @@ namespace CSMWorld void cloneRecord(const std::string& origin, const std::string& destination, - UniversalId::ArgumentType argumentType, UniversalId::Type type = UniversalId::Type_None); QModelIndex getModelIndex (const std::string& id, int column) const; diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 75a1b681d..5e8d38117 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -451,15 +451,13 @@ void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, const std::string& destination, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType) + const CSMWorld::UniversalId::Type type) { RecordBase *newRecord = mData.getRecord(mData.searchId(origin)).clone(); newRecord->mState = RecordBase::State_ModifiedOnly; mAdapters.find(type)->second->setId(*newRecord, destination); mData.insertRecord(*newRecord, type, destination); delete newRecord; - //WIP } void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 4ad181004..ebbf2100d 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -71,8 +71,7 @@ namespace CSMWorld virtual void cloneRecord(const std::string& origin, const std::string& destination, - const UniversalId::Type type, - const UniversalId::ArgumentType argumentType); + const UniversalId::Type type); virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index ebf88122b..e65501e5f 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -82,10 +82,9 @@ void CSVWorld::CellCreator::valueChanged (int index) } void CSVWorld::CellCreator::cloneMode(const std::string& originid, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType) + const CSMWorld::UniversalId::Type type) { - CSVWorld::GenericCreator::cloneMode(originid, type, argumentType); + CSVWorld::GenericCreator::cloneMode(originid, type); if (*(originid.begin()) == '#') //if originid points to the exterior cell { setType(1); //enable x and y controls diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index 3130c1328..472be43b9 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -32,8 +32,7 @@ namespace CSVWorld virtual void toggleWidgets(bool active = true); virtual void cloneMode(const std::string& originid, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType); + const CSMWorld::UniversalId::Type type); private slots: diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 5174232bc..9be619915 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -25,7 +25,8 @@ namespace CSVWorld virtual void reset() = 0; - virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) = 0; + virtual void cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type) = 0; virtual void setEditLock (bool locked) = 0; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 2a93f0e0f..1f74b9954 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -127,7 +127,7 @@ void CSVWorld::GenericCreator::create() { std::string id = getId(); std::auto_ptr command (new CSMWorld::CloneCommand ( - dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType, mArgumentType)); + dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType)); mUndoStack.push(command.release()); @@ -150,13 +150,11 @@ void CSVWorld::GenericCreator::create() } void CSVWorld::GenericCreator::cloneMode(const std::string& originid, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType) + const CSMWorld::UniversalId::Type type) { mCloneMode = true; mClonedId = originid; mClonedType = type; - mArgumentType = argumentType; } void CSVWorld::GenericCreator::toggleWidgets(bool active) diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 4870ba46c..06b110b11 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -67,8 +67,7 @@ namespace CSVWorld virtual void toggleWidgets (bool active = true); virtual void cloneMode(const std::string& originid, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType); + const CSMWorld::UniversalId::Type type); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index 6991ecf36..069300f42 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -85,8 +85,9 @@ void CSVWorld::ReferenceCreator::toggleWidgets(bool active) mCell->setEnabled(active); } -void CSVWorld::ReferenceCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) +void CSVWorld::ReferenceCreator::cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type) { - CSVWorld::GenericCreator::cloneMode(originid, type, argumentType); + CSVWorld::GenericCreator::cloneMode(originid, type); cellChanged(); //otherwise ok button will remain disabled } diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp index 2f0897026..96c1bec27 100644 --- a/apps/opencs/view/world/referencecreator.hpp +++ b/apps/opencs/view/world/referencecreator.hpp @@ -26,8 +26,7 @@ namespace CSVWorld const CSMWorld::UniversalId& id); virtual void cloneMode(const std::string& originid, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType); + const CSMWorld::UniversalId::Type type); virtual void reset(); virtual void toggleWidgets(bool active = true); diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index a6801b6fc..239c7410f 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -160,11 +160,10 @@ void CSVWorld::TableBottomBox::createRequest() } void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumnetType) + const CSMWorld::UniversalId::Type type) { mCreator->reset(); - mCreator->cloneMode(id, type, argumnetType); + mCreator->cloneMode(id, type); mLayout->setCurrentWidget(mCreator); mCreator->toggleWidgets(false); setVisible (true); diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 29afa2277..8f0d85163 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -77,7 +77,8 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); - void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType); + void cloneRequest(const std::string& id, + const CSMWorld::UniversalId::Type type); }; } diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 3c71d1370..e4dbb64ed 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -49,9 +49,11 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, SLOT(cloneRequest(const CSMWorld::UniversalId&))); - connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType)), - mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType))); + connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, + SLOT(cloneRequest(const CSMWorld::UniversalId&))); + + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), + mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); } connect (mBottom, SIGNAL (requestFocus (const std::string&)), mTable, SLOT (requestFocus (const std::string&))); @@ -84,5 +86,5 @@ void CSVWorld::TableSubView::setStatusBar (bool show) void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) { - emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); + emit cloneRequest(toClone.getId(), toClone.getType()); } diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index b3a0a5162..9d2d005a8 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -41,8 +41,7 @@ namespace CSVWorld signals: void cloneRequest(const std::string&, - const CSMWorld::UniversalId::Type, - const CSMWorld::UniversalId::ArgumentType); + const CSMWorld::UniversalId::Type); private slots: From 71d63647546db1bf0e2b8adb189cbacb1f6ba1d7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 13:13:39 +0100 Subject: [PATCH 201/301] Chaninging variables name to follow our policy. --- apps/opencs/view/world/cellcreator.cpp | 6 +++--- apps/opencs/view/world/cellcreator.hpp | 2 +- apps/opencs/view/world/creator.hpp | 2 +- apps/opencs/view/world/genericcreator.cpp | 4 ++-- apps/opencs/view/world/genericcreator.hpp | 2 +- apps/opencs/view/world/referencecreator.cpp | 4 ++-- apps/opencs/view/world/referencecreator.hpp | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index e65501e5f..cdeee5655 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -81,11 +81,11 @@ void CSVWorld::CellCreator::valueChanged (int index) update(); } -void CSVWorld::CellCreator::cloneMode(const std::string& originid, +void CSVWorld::CellCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) { - CSVWorld::GenericCreator::cloneMode(originid, type); - if (*(originid.begin()) == '#') //if originid points to the exterior cell + CSVWorld::GenericCreator::cloneMode(originId, type); + if (*(originId.begin()) == '#') //if originid points to the exterior cell { setType(1); //enable x and y controls mType->setCurrentIndex(1); diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index 472be43b9..74bf5f76b 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -31,7 +31,7 @@ namespace CSVWorld virtual void toggleWidgets(bool active = true); - virtual void cloneMode(const std::string& originid, + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); private slots: diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 9be619915..2d5e014e2 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -25,7 +25,7 @@ namespace CSVWorld virtual void reset() = 0; - virtual void cloneMode(const std::string& originid, + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) = 0; virtual void setEditLock (bool locked) = 0; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 1f74b9954..9fb0b4b21 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -149,11 +149,11 @@ void CSVWorld::GenericCreator::create() } } -void CSVWorld::GenericCreator::cloneMode(const std::string& originid, +void CSVWorld::GenericCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) { mCloneMode = true; - mClonedId = originid; + mClonedId = originId; mClonedType = type; } diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 06b110b11..98dd12ac2 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -66,7 +66,7 @@ namespace CSVWorld virtual void toggleWidgets (bool active = true); - virtual void cloneMode(const std::string& originid, + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); virtual std::string getErrors() const; diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index 069300f42..384b59fc4 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -85,9 +85,9 @@ void CSVWorld::ReferenceCreator::toggleWidgets(bool active) mCell->setEnabled(active); } -void CSVWorld::ReferenceCreator::cloneMode(const std::string& originid, +void CSVWorld::ReferenceCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) { - CSVWorld::GenericCreator::cloneMode(originid, type); + CSVWorld::GenericCreator::cloneMode(originId, type); cellChanged(); //otherwise ok button will remain disabled } diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp index 96c1bec27..73b0899e5 100644 --- a/apps/opencs/view/world/referencecreator.hpp +++ b/apps/opencs/view/world/referencecreator.hpp @@ -25,7 +25,7 @@ namespace CSVWorld ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); - virtual void cloneMode(const std::string& originid, + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); virtual void reset(); From 29c3a288e3525ca29cef275e66536dded60a3683 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 27 Jan 2014 13:27:42 +0100 Subject: [PATCH 202/301] 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 66dea4f1d..08f9cdb5c 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 64104f2ba..cbf363581 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 a396d78c5..fb2d10bbf 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 d6bb7575d..46ade236b 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 26ec14a6d..965c9fc5d 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 1b0fe6724..7ee8a3f6c 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 aea4e5b6a..88c241e1a 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 feebfe90a..a4f219013 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 076fcfdb6..26cb34977 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 0fb4ed1ea..b99509322 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 40ef7ecb6..1b0125e78 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 bb62efc3d844171b1b77b517424871fb3f6de73b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 13:36:01 +0100 Subject: [PATCH 203/301] Removed pointless includes. --- apps/opencs/model/world/commands.cpp | 1 + apps/opencs/view/world/genericcreator.cpp | 1 - apps/opencs/view/world/table.cpp | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5eb2e9414..789c5d9b8 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,3 +1,4 @@ + #include "commands.hpp" #include diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 9fb0b4b21..c1faa741d 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -2,7 +2,6 @@ #include "genericcreator.hpp" #include -#include #include #include diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index d343f9986..f166171fe 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -16,7 +16,6 @@ #include "recordstatusdelegate.hpp" #include "util.hpp" -#include void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { From 6a0b5defd78e2e8e38a05c2c16c68c6ba37403d7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 13:41:37 +0100 Subject: [PATCH 204/301] removed needless member value. --- apps/opencs/model/world/commands.cpp | 3 +-- apps/opencs/view/world/genericcreator.cpp | 12 +++++++++--- apps/opencs/view/world/genericcreator.hpp | 1 - 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 789c5d9b8..d3ef7128f 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,9 +1,8 @@ #include "commands.hpp" - #include -#include #include "idtable.hpp" +#include CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index c1faa741d..447c07424 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -57,9 +57,15 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const } CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id, bool relaxedIdRules) -: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mCloneMode(false), mClonedType(CSMWorld::UniversalId::Type_None), -mArgumentType(CSMWorld::UniversalId::ArgumentType_None) + const CSMWorld::UniversalId& id, bool relaxedIdRules): + + mData (data), + mUndoStack (undoStack), + mListId (id), + mLocked (false), + mCloneMode(false), + mClonedType(CSMWorld::UniversalId::Type_None) + { mLayout = new QHBoxLayout; mLayout->setContentsMargins (0, 0, 0, 0); diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 98dd12ac2..0f426e1a7 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -31,7 +31,6 @@ namespace CSVWorld bool mLocked; std::string mClonedId; CSMWorld::UniversalId::Type mClonedType; - CSMWorld::UniversalId::ArgumentType mArgumentType; protected: bool mCloneMode; From 2899f04a3f11ab4821143e69ccbd9807c54b0302 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:43:12 +0100 Subject: [PATCH 205/301] reformatting --- apps/opencs/model/world/collection.hpp | 3 ++- apps/opencs/model/world/commands.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 4bb10c1d8..290558928 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -204,7 +204,8 @@ namespace CSMWorld const std::string& destination, const UniversalId::Type type) { - Record copy(getRecord(origin)); + Record copy; + copy = getRecord(origin); copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index d3ef7128f..5ca169fcd 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,7 +1,8 @@ #include "commands.hpp" -#include + #include "idtable.hpp" +#include #include CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, From 4d6fb31610cd65dcf9d7d07d0dfea4118fd299e5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:45:07 +0100 Subject: [PATCH 206/301] reformating --- apps/opencs/model/world/commands.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5ca169fcd..3b70f6940 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,8 +1,10 @@ #include "commands.hpp" -#include "idtable.hpp" #include + +#include "idtable.hpp" + #include CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, @@ -38,7 +40,6 @@ CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); } - void CSMWorld::CloneCommand::redo() { mModel.cloneRecord(mIdOrigin, mIdDestination, mType); From 316debb8277d6e4b851dc8a85be6efb1c0d248bc Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:46:58 +0100 Subject: [PATCH 207/301] reformating --- apps/opencs/model/world/commands.cpp | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 3b70f6940..9159b1f16 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -26,34 +26,6 @@ void CSMWorld::ModifyCommand::undo() mModel.setData(mIndex, mOld); } -CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, - const std::string& idOrigin, - const std::string& IdDestination, - const CSMWorld::UniversalId::Type type, - QUndoCommand* parent) : - QUndoCommand(parent), - mModel(model), - mIdOrigin(idOrigin), - mIdDestination(Misc::StringUtils::lowerCase(IdDestination)), - mType(type) -{ - setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); -} - -void CSMWorld::CloneCommand::redo() -{ - mModel.cloneRecord(mIdOrigin, mIdDestination, mType); - - for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) - mModel.setData(mModel.getModelIndex(mIdDestination, iter->first), iter->second); -} - -void CSMWorld::CloneCommand::undo() -{ - mModel.removeRow(mModel.getModelIndex(mIdDestination, 0).row()); -} - - CSMWorld::CreateCommand::CreateCommand(IdTable& model, const std::string& id, QUndoCommand* parent) : QUndoCommand(parent), mModel(model), mId(id), mType(UniversalId::Type_None) { @@ -174,4 +146,32 @@ void CSMWorld::ReorderRowsCommand::undo() mModel.reorderRows(mBaseIndex, reverse); } + +CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, + const std::string& idOrigin, + const std::string& IdDestination, + const CSMWorld::UniversalId::Type type, + QUndoCommand* parent) : + QUndoCommand(parent), + mModel(model), + mIdOrigin(idOrigin), + mIdDestination(Misc::StringUtils::lowerCase(IdDestination)), + mType(type) +{ + setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); +} + +void CSMWorld::CloneCommand::redo() +{ + mModel.cloneRecord(mIdOrigin, mIdDestination, mType); + + for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) + mModel.setData(mModel.getModelIndex(mIdDestination, iter->first), iter->second); +} + +void CSMWorld::CloneCommand::undo() +{ + mModel.removeRow(mModel.getModelIndex(mIdDestination, 0).row()); +} + // kate: indent-mode cstyle; indent-width 4; replace-tabs on; From 2b71568bb65b9e7f80cb4d43c813dbeb50bb0cb6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:50:36 +0100 Subject: [PATCH 208/301] still reformating --- apps/opencs/model/world/commands.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 9159b1f16..9a9e246f5 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -3,6 +3,7 @@ #include + #include "idtable.hpp" #include From 52176d6435baee3b092fe21e72be85dcdbe089cf Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:51:57 +0100 Subject: [PATCH 209/301] reforfucking --- apps/opencs/model/world/commands.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 9a9e246f5..80a751ac6 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -3,9 +3,7 @@ #include - #include "idtable.hpp" - #include CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, From c91ae86084df5f86af4734ec7617e5d09dc180dd Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:53:39 +0100 Subject: [PATCH 210/301] is there something that can generate diff from two git branches? Using github for this is kinda annoying. --- apps/opencs/model/world/commands.cpp | 118 +++++++++++++-------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 80a751ac6..4b36b1b63 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -6,60 +6,60 @@ #include "idtable.hpp" #include -CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, - const QVariant& new_, QUndoCommand* parent) - : QUndoCommand(parent), mModel(model), mIndex(index), mNew(new_) +CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, + const QVariant& new_, QUndoCommand* parent) + : QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) { - mOld = mModel.data(mIndex, Qt::EditRole); + mOld = mModel.data (mIndex, Qt::EditRole); - setText("Modify " + mModel.headerData(mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); + setText ("Modify " + mModel.headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); } void CSMWorld::ModifyCommand::redo() { - mModel.setData(mIndex, mNew); + mModel.setData (mIndex, mNew); } void CSMWorld::ModifyCommand::undo() { - mModel.setData(mIndex, mOld); + mModel.setData (mIndex, mOld); } -CSMWorld::CreateCommand::CreateCommand(IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand(parent), mModel(model), mId(id), mType(UniversalId::Type_None) +CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) { - setText(("Create record " + id).c_str()); + setText ( ("Create record " + id).c_str()); } -void CSMWorld::CreateCommand::addValue(int column, const QVariant& value) +void CSMWorld::CreateCommand::addValue (int column, const QVariant& value) { mValues[column] = value; } -void CSMWorld::CreateCommand::setType(UniversalId::Type type) +void CSMWorld::CreateCommand::setType (UniversalId::Type type) { mType = type; } void CSMWorld::CreateCommand::redo() { - mModel.addRecord(mId, mType); + mModel.addRecord (mId, mType); - for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) - mModel.setData(mModel.getModelIndex(mId, iter->first), iter->second); + for (std::map::const_iterator iter (mValues.begin()); iter != mValues.end(); ++iter) + mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); } void CSMWorld::CreateCommand::undo() { - mModel.removeRow(mModel.getModelIndex(mId, 0).row()); + mModel.removeRow (mModel.getModelIndex (mId, 0).row()); } -CSMWorld::RevertCommand::RevertCommand(IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand(parent), mModel(model), mId(id), mOld(0) +CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand (parent), mModel (model), mId (id), mOld (0) { - setText(("Revert record " + id).c_str()); + setText ( ("Revert record " + id).c_str()); - mOld = model.getRecord(id).clone(); + mOld = model.getRecord (id).clone(); } CSMWorld::RevertCommand::~RevertCommand() @@ -69,32 +69,32 @@ CSMWorld::RevertCommand::~RevertCommand() void CSMWorld::RevertCommand::redo() { - int column = mModel.findColumnIndex(Columns::ColumnId_Modification); + int column = mModel.findColumnIndex (Columns::ColumnId_Modification); - QModelIndex index = mModel.getModelIndex(mId, column); - RecordBase::State state = static_cast(mModel.data(index).toInt()); + QModelIndex index = mModel.getModelIndex (mId, column); + RecordBase::State state = static_cast (mModel.data (index).toInt()); if (state == RecordBase::State_ModifiedOnly) { - mModel.removeRows(index.row(), 1); + mModel.removeRows (index.row(), 1); } else { - mModel.setData(index, static_cast(RecordBase::State_BaseOnly)); + mModel.setData (index, static_cast (RecordBase::State_BaseOnly)); } } void CSMWorld::RevertCommand::undo() { - mModel.setRecord(mId, *mOld); + mModel.setRecord (mId, *mOld); } -CSMWorld::DeleteCommand::DeleteCommand(IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand(parent), mModel(model), mId(id), mOld(0) +CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand (parent), mModel (model), mId (id), mOld (0) { - setText(("Delete record " + id).c_str()); + setText ( ("Delete record " + id).c_str()); - mOld = model.getRecord(id).clone(); + mOld = model.getRecord (id).clone(); } CSMWorld::DeleteCommand::~DeleteCommand() @@ -104,73 +104,73 @@ CSMWorld::DeleteCommand::~DeleteCommand() void CSMWorld::DeleteCommand::redo() { - int column = mModel.findColumnIndex(Columns::ColumnId_Modification); + int column = mModel.findColumnIndex (Columns::ColumnId_Modification); - QModelIndex index = mModel.getModelIndex(mId, column); - RecordBase::State state = static_cast(mModel.data(index).toInt()); + QModelIndex index = mModel.getModelIndex (mId, column); + RecordBase::State state = static_cast (mModel.data (index).toInt()); if (state == RecordBase::State_ModifiedOnly) { - mModel.removeRows(index.row(), 1); + mModel.removeRows (index.row(), 1); } else { - mModel.setData(index, static_cast(RecordBase::State_Deleted)); + mModel.setData (index, static_cast (RecordBase::State_Deleted)); } } void CSMWorld::DeleteCommand::undo() { - mModel.setRecord(mId, *mOld); + mModel.setRecord (mId, *mOld); } -CSMWorld::ReorderRowsCommand::ReorderRowsCommand(IdTable& model, int baseIndex, +CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector& newOrder) - : mModel(model), mBaseIndex(baseIndex), mNewOrder(newOrder) + : mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) {} void CSMWorld::ReorderRowsCommand::redo() { - mModel.reorderRows(mBaseIndex, mNewOrder); + mModel.reorderRows (mBaseIndex, mNewOrder); } void CSMWorld::ReorderRowsCommand::undo() { - int size = static_cast(mNewOrder.size()); - std::vector reverse(size); + int size = static_cast (mNewOrder.size()); + std::vector reverse (size); for (int i = 0; i < size; ++i) - reverse.at(mNewOrder[i]) = i; + reverse.at (mNewOrder[i]) = i; - mModel.reorderRows(mBaseIndex, reverse); + mModel.reorderRows (mBaseIndex, reverse); } -CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, - const std::string& idOrigin, - const std::string& IdDestination, - const CSMWorld::UniversalId::Type type, - QUndoCommand* parent) : - QUndoCommand(parent), - mModel(model), - mIdOrigin(idOrigin), - mIdDestination(Misc::StringUtils::lowerCase(IdDestination)), - mType(type) +CSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model, + const std::string& idOrigin, + const std::string& IdDestination, + const CSMWorld::UniversalId::Type type, + QUndoCommand* parent) : + QUndoCommand (parent), + mModel (model), + mIdOrigin (idOrigin), + mIdDestination (Misc::StringUtils::lowerCase (IdDestination)), + mType (type) { - setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); + setText ( ("Clone record " + idOrigin + " to the " + IdDestination).c_str()); } void CSMWorld::CloneCommand::redo() { - mModel.cloneRecord(mIdOrigin, mIdDestination, mType); - - for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) - mModel.setData(mModel.getModelIndex(mIdDestination, iter->first), iter->second); + mModel.cloneRecord (mIdOrigin, mIdDestination, mType); + + for (std::map::const_iterator iter (mValues.begin()); iter != mValues.end(); ++iter) + mModel.setData (mModel.getModelIndex (mIdDestination, iter->first), iter->second); } void CSMWorld::CloneCommand::undo() { - mModel.removeRow(mModel.getModelIndex(mIdDestination, 0).row()); + mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row()); } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; From 51115fa5be719a1ad02e03ee7d4cc6b78f665382 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:56:57 +0100 Subject: [PATCH 211/301] spaces around operators --- apps/opencs/model/world/commands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 4b36b1b63..1bb1cf2b2 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -170,7 +170,7 @@ void CSMWorld::CloneCommand::redo() void CSMWorld::CloneCommand::undo() { - mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row()); + mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row()); //should be enough } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; From 84e07c95b1d375d0b03d68c6fa1076d39f5e5bf7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 15:02:29 +0100 Subject: [PATCH 212/301] formatting --- apps/opencs/model/world/commands.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 1bb1cf2b2..a544baee3 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -8,7 +8,7 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) - : QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) +: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) { mOld = mModel.data (mIndex, Qt::EditRole); @@ -26,9 +26,9 @@ void CSMWorld::ModifyCommand::undo() } CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) +: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) { - setText ( ("Create record " + id).c_str()); + setText (("Create record " + id).c_str()); } void CSMWorld::CreateCommand::addValue (int column, const QVariant& value) @@ -45,7 +45,7 @@ void CSMWorld::CreateCommand::redo() { mModel.addRecord (mId, mType); - for (std::map::const_iterator iter (mValues.begin()); iter != mValues.end(); ++iter) + for (std::map::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); } @@ -55,9 +55,9 @@ void CSMWorld::CreateCommand::undo() } CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand (parent), mModel (model), mId (id), mOld (0) +: QUndoCommand (parent), mModel (model), mId (id), mOld (0) { - setText ( ("Revert record " + id).c_str()); + setText (("Revert record " + id).c_str()); mOld = model.getRecord (id).clone(); } @@ -74,7 +74,7 @@ void CSMWorld::RevertCommand::redo() QModelIndex index = mModel.getModelIndex (mId, column); RecordBase::State state = static_cast (mModel.data (index).toInt()); - if (state == RecordBase::State_ModifiedOnly) + if (state==RecordBase::State_ModifiedOnly) { mModel.removeRows (index.row(), 1); } @@ -90,9 +90,9 @@ void CSMWorld::RevertCommand::undo() } CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand (parent), mModel (model), mId (id), mOld (0) +: QUndoCommand (parent), mModel (model), mId (id), mOld (0) { - setText ( ("Delete record " + id).c_str()); + setText (("Delete record " + id).c_str()); mOld = model.getRecord (id).clone(); } @@ -109,7 +109,7 @@ void CSMWorld::DeleteCommand::redo() QModelIndex index = mModel.getModelIndex (mId, column); RecordBase::State state = static_cast (mModel.data (index).toInt()); - if (state == RecordBase::State_ModifiedOnly) + if (state==RecordBase::State_ModifiedOnly) { mModel.removeRows (index.row(), 1); } @@ -127,7 +127,7 @@ void CSMWorld::DeleteCommand::undo() CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector& newOrder) - : mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) +: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) {} void CSMWorld::ReorderRowsCommand::redo() @@ -140,7 +140,7 @@ void CSMWorld::ReorderRowsCommand::undo() int size = static_cast (mNewOrder.size()); std::vector reverse (size); - for (int i = 0; i < size; ++i) + for (int i=0; i < size; ++i) reverse.at (mNewOrder[i]) = i; mModel.reorderRows (mBaseIndex, reverse); From 9c579dbd6c4b4fea3dd72bf9e446d6d74d00bfb1 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 15:03:29 +0100 Subject: [PATCH 213/301] ok, that should be enough --- apps/opencs/model/world/commands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index a544baee3..5526d7418 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -140,7 +140,7 @@ void CSMWorld::ReorderRowsCommand::undo() int size = static_cast (mNewOrder.size()); std::vector reverse (size); - for (int i=0; i < size; ++i) + for (int i=0; i< size; ++i) reverse.at (mNewOrder[i]) = i; mModel.reorderRows (mBaseIndex, reverse); From ed0ba906cfbfd72a7b41b3dbd48db12b9cc05023 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 15:05:29 +0100 Subject: [PATCH 214/301] removed needless include --- apps/opencs/view/world/tablesubview.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index e4dbb64ed..5495c708f 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -6,7 +6,6 @@ #include "../../model/doc/document.hpp" #include "../filter/filterbox.hpp" -#include "../../model/world/idtable.hpp" #include "table.hpp" #include "tablebottombox.hpp" #include "creator.hpp" From d0b07de7efd4b77c105112cc495d1040b33261af Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 16:59:18 +0100 Subject: [PATCH 215/301] Corrected bug mentioned by zini. --- apps/opencs/model/world/collection.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 290558928..64b950e60 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -205,7 +205,7 @@ namespace CSMWorld const UniversalId::Type type) { Record copy; - copy = getRecord(origin); + copy.mModified = getRecord(origin).get(); copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; From c82db915f17922d03b68a33de3a85b4e427b0c01 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 19:32:51 +0100 Subject: [PATCH 216/301] Removed needless includes --- apps/opencs/model/world/collection.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 64b950e60..9fc97d580 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -7,12 +7,10 @@ #include #include #include -#include #include #include -#include #include "columnbase.hpp" From 62ea0bb06685aefff34329c19486222714a94934 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 19:40:05 +0100 Subject: [PATCH 217/301] Cleared whitespaces. --- apps/opencs/model/world/collection.hpp | 12 ++++++------ apps/opencs/model/world/collectionbase.hpp | 2 +- apps/opencs/model/world/commands.hpp | 4 ++-- apps/opencs/model/world/idtable.hpp | 2 +- apps/opencs/model/world/refidcollection.hpp | 2 +- apps/opencs/view/world/cellcreator.hpp | 4 ++-- apps/opencs/view/world/creator.hpp | 4 ++-- apps/opencs/view/world/genericcreator.cpp | 6 +++--- apps/opencs/view/world/genericcreator.hpp | 6 +++--- apps/opencs/view/world/referencecreator.cpp | 4 ++-- apps/opencs/view/world/referencecreator.hpp | 2 +- apps/opencs/view/world/table.cpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 6 +++--- apps/opencs/view/world/tablesubview.hpp | 4 ++-- 14 files changed, 30 insertions(+), 30 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 9fc97d580..d342e88a4 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -50,7 +50,7 @@ namespace CSMWorld // not implemented Collection (const Collection&); Collection& operator= (const Collection&); - + protected: const std::map& getIdMap() const; @@ -97,11 +97,11 @@ namespace CSMWorld virtual void appendBlankRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types - + virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type); - + virtual int searchId (const std::string& id) const; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) @@ -149,7 +149,7 @@ namespace CSMWorld void setRecord (int index, const Record& record); ///< \attention This function must not change the ID. }; - + template const std::map& Collection::getIdMap() const { @@ -206,10 +206,10 @@ namespace CSMWorld copy.mModified = getRecord(origin).get(); copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; - + insertRecord(copy, getAppendIndex(destination, type)); } - + template Collection::Collection() {} diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index 2473ae741..408a89d92 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -77,7 +77,7 @@ namespace CSMWorld virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type) = 0; - + virtual const RecordBase& getRecord (const std::string& id) const = 0; virtual const RecordBase& getRecord (int index) const = 0; diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 0dedea012..ec6350658 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -53,12 +53,12 @@ namespace CSMWorld const std::string& IdDestination, const UniversalId::Type type, QUndoCommand* parent = 0); - + virtual void redo(); virtual void undo(); }; - + class CreateCommand : public QUndoCommand { IdTable& mModel; diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 2d620004c..74923867d 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -66,7 +66,7 @@ namespace CSMWorld void cloneRecord(const std::string& origin, const std::string& destination, UniversalId::Type type = UniversalId::Type_None); - + QModelIndex getModelIndex (const std::string& id, int column) const; void setRecord (const std::string& id, const RecordBase& record); diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index ebbf2100d..5e973b447 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -72,7 +72,7 @@ namespace CSMWorld virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type); - + virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index 74bf5f76b..db9fbf8a3 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -28,9 +28,9 @@ namespace CSVWorld CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); virtual void reset(); - + virtual void toggleWidgets(bool active = true); - + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 2d5e014e2..88da70330 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -24,12 +24,12 @@ namespace CSVWorld virtual ~Creator(); virtual void reset() = 0; - + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) = 0; virtual void setEditLock (bool locked) = 0; - + virtual void toggleWidgets(bool active = true) = 0; signals: diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 447c07424..cd7a5fa18 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -57,15 +57,15 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const } CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id, bool relaxedIdRules): - + const CSMWorld::UniversalId& id, bool relaxedIdRules): + mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mCloneMode(false), mClonedType(CSMWorld::UniversalId::Type_None) - + { mLayout = new QHBoxLayout; mLayout->setContentsMargins (0, 0, 0, 0); diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 0f426e1a7..6cc39cf23 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -31,10 +31,10 @@ namespace CSVWorld bool mLocked; std::string mClonedId; CSMWorld::UniversalId::Type mClonedType; - + protected: bool mCloneMode; - + protected: void update(); @@ -49,7 +49,7 @@ namespace CSVWorld virtual std::string getId() const; virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; - + CSMWorld::Data& getData() const; const CSMWorld::UniversalId& getCollectionId() const; diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index 384b59fc4..6b8e02da0 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -53,9 +53,9 @@ std::string CSVWorld::ReferenceCreator::getErrors() const { return errors; } - + std::string cell = mCell->text().toUtf8().constData(); - + if (cell.empty()) { if (!errors.empty()) diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp index 73b0899e5..12fb12dd9 100644 --- a/apps/opencs/view/world/referencecreator.hpp +++ b/apps/opencs/view/world/referencecreator.hpp @@ -27,7 +27,7 @@ namespace CSVWorld virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); - + virtual void reset(); virtual void toggleWidgets(bool active = true); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index f166171fe..8f6fc46a8 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -203,7 +203,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q mCreateAction = new QAction (tr ("Add Record"), this); connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); addAction (mCreateAction); - + mCloneAction = new QAction (tr ("Clone Record"), this); connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); addAction(mCloneAction); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 5495c708f..981faaf59 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -47,11 +47,11 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D if (mBottom->canCreateAndDelete()) { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - + connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, SLOT(cloneRequest(const CSMWorld::UniversalId&))); - - connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), + + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); } connect (mBottom, SIGNAL (requestFocus (const std::string&)), diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 9d2d005a8..d728dc2f3 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -38,9 +38,9 @@ namespace CSVWorld virtual void updateEditorSetting (const QString& key, const QString& value); virtual void setStatusBar (bool show); - + signals: - void cloneRequest(const std::string&, + void cloneRequest(const std::string&, const CSMWorld::UniversalId::Type); private slots: From d3000ce0990a05f89fd212b138107edfb10b20ba Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 19:47:54 +0100 Subject: [PATCH 218/301] whitespaces removed again. --- apps/opencs/view/world/genericcreator.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 6cc39cf23..714853f98 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -62,12 +62,12 @@ namespace CSVWorld virtual void setEditLock (bool locked); virtual void reset(); - + virtual void toggleWidgets (bool active = true); virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); - + virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. From e50e94af0b639634760cc8feda3f0728e3781019 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 27 Jan 2014 23:05:17 +0200 Subject: [PATCH 219/301] remove CreatureStats::mAttackType, set/getAttackType() --- apps/openmw/mwmechanics/character.cpp | 33 +++++++++-------------- apps/openmw/mwmechanics/creaturestats.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 12 --------- apps/openmw/mwrender/animation.cpp | 12 ++++----- components/esm/loadweap.hpp | 7 +++++ 5 files changed, 27 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4acdb4462..a54f2365d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -39,17 +39,17 @@ namespace { -int getBestAttack (const ESM::Weapon* weapon) +std::string getBestAttack (const ESM::Weapon* weapon) { int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; if (slash >= chop && slash >= thrust) - return MWMechanics::CreatureStats::AT_Slash; + return "slash"; else if (chop >= slash && chop >= thrust) - return MWMechanics::CreatureStats::AT_Chop; + return "chop"; else - return MWMechanics::CreatureStats::AT_Thrust; + return "thrust"; } } @@ -691,9 +691,8 @@ bool CharacterController::updateWeaponState() mAttackType = "shoot"; else { - int attackType; if(isWeapon && Settings::Manager::getBool("best attack", "Game")) - attackType = getBestAttack(weapon->get()->mBase); + mAttackType = getBestAttack(weapon->get()->mBase); else determineAttackType(); } @@ -1299,27 +1298,21 @@ void CharacterController::determineAttackType() { float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition; - if (move[0] && !move[1]) //sideway + if(mPtr.getClass().hasInventoryStore(mPtr)) { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Slash); - if(mPtr.getClass().hasInventoryStore(mPtr)) + if (move[0] && !move[1]) //sideway mAttackType = "slash"; - else - mCurrentWeapon = "attack2"; - } - else if (move[1]) //forward - { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - if(mPtr.getClass().hasInventoryStore(mPtr)) + else if (move[1]) //forward mAttackType = "thrust"; else - mCurrentWeapon = "attack3"; + mAttackType = "chop"; } else { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Chop); - if(mPtr.getClass().hasInventoryStore(mPtr)) - mAttackType = "chop"; + if (move[0] && !move[1]) //sideway + mCurrentWeapon = "attack2"; + else if (move[1]) //forward + mCurrentWeapon = "attack3"; else mCurrentWeapon = "attack1"; } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index c6522f08f..30db59311 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -13,7 +13,7 @@ namespace MWMechanics : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mDied (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false), - mAttackingOrSpell(false), mAttackType(AT_Chop), + mAttackingOrSpell(false), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 0501eb286..67d925a19 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -45,14 +45,11 @@ namespace MWMechanics float mFallHeight; - int mAttackType; - std::string mLastHitObject; // The last object to hit this actor // Do we need to recalculate stats derived from attributes or other factors? bool mRecalcDynamicStats; - std::map mUsedPowers; protected: bool mIsWerewolf; @@ -125,15 +122,6 @@ namespace MWMechanics void setAttackingOrSpell(bool attackingOrSpell); - enum AttackType - { - AT_Chop, - AT_Slash, - AT_Thrust - }; - void setAttackType(int attackType) { mAttackType = attackType; } - int getAttackType() { return mAttackType; } - void setLevel(int level); enum AiSetting diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index c0e4eebda..3f45ce769 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -686,19 +686,19 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co else if(evt.compare(off, len, "unequip detach") == 0) showWeapons(false); else if(evt.compare(off, len, "chop hit") == 0) - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); else if(evt.compare(off, len, "slash hit") == 0) - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); else if(evt.compare(off, len, "thrust hit") == 0) - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); else if(evt.compare(off, len, "hit") == 0) { if (groupname == "attack1") - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); else if (groupname == "attack2") - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); else if (groupname == "attack3") - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); else mPtr.getClass().hit(mPtr); } diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index fde716b91..14ddb4708 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -35,6 +35,13 @@ struct Weapon Bolt = 13 }; + enum AttackType + { + AT_Chop, + AT_Slash, + AT_Thrust + }; + enum Flags { Magical = 0x01, From 2bd98a69ab487062ec9d54efccde89ce9b8e6929 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 28 Jan 2014 11:16:48 +0100 Subject: [PATCH 220/301] Corrections, according to the comments. Thanks for the review. :-) --- apps/opencs/model/world/commands.cpp | 6 ++---- apps/opencs/model/world/refidcollection.cpp | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5526d7418..b60ffeb29 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -170,7 +170,5 @@ void CSMWorld::CloneCommand::redo() void CSMWorld::CloneCommand::undo() { - mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row()); //should be enough -} - -// kate: indent-mode cstyle; indent-width 4; replace-tabs on; + mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row()); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 5e8d38117..5d310208a 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -2,6 +2,7 @@ #include "refidcollection.hpp" #include +#include #include @@ -453,11 +454,10 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, const std::string& destination, const CSMWorld::UniversalId::Type type) { - RecordBase *newRecord = mData.getRecord(mData.searchId(origin)).clone(); + std::auto_ptr newRecord(mData.getRecord(mData.searchId(origin)).clone()); newRecord->mState = RecordBase::State_ModifiedOnly; mAdapters.find(type)->second->setId(*newRecord, destination); mData.insertRecord(*newRecord, type, destination); - delete newRecord; } void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, From a473c3f6193b311ae84cf963034f144e20795c72 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Jan 2014 12:36:10 +0100 Subject: [PATCH 221/301] some cleanup --- apps/openmw/mwmechanics/aitravel.cpp | 8 -------- apps/openmw/mwmechanics/pathfinding.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index ba75cb418..9a1b98d8f 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -66,14 +66,6 @@ namespace MWMechanics { cellX = cell->mData.mX; cellY = cell->mData.mY; - float xCell = 0; - float yCell = 0; - - if(cell->isExterior()) - { - xCell = cell->mData.mX * ESM::Land::REAL_SIZE; - yCell = cell->mData.mY * ESM::Land::REAL_SIZE; - } ESM::Pathgrid::Point dest; dest.mX = mX; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 668b4f2b1..1ead89040 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -117,7 +117,7 @@ namespace if(current == goal) break; closedset.push_back(current); - + for(int j = 0;j (mGraph.size());i++) { mGraph[i].parent = -1; mGScore[i] = -1; @@ -225,8 +225,8 @@ namespace MWMechanics if(current == goal) break; closedset.push_back(current); - - for(int j = 0;j (mGraph[current].edges.size());j++) { //int next = mGraph[current].edges[j].destination if(std::find(closedset.begin(),closedset.end(),mGraph[current].edges[j].destination) == closedset.end()) From a623f038504232f152b04a79d7bd790f0565f1db Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Jan 2014 13:49:59 +0100 Subject: [PATCH 222/301] reduced object state for objects in containers --- components/esm/cellref.cpp | 29 ++++++++++++++++------------- components/esm/cellref.hpp | 2 +- components/esm/objectstate.cpp | 14 ++++++++------ components/esm/objectstate.hpp | 2 +- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 19427af0c..00b15f4a3 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) { + + if (mFltv != 0 && !inInventory) esm.writeHNT("FLTV", mFltv); - } - esm.writeHNT("DATA", mPos, 24); - if (mNam0 != 0) { + 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 0cd6a3673..16f6603a2 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 56289acae..21585e09d 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 c599bb973..34226ea91 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 223/301] 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 21585e09d..6aa820599 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 34226ea91..9c9ca5f2e 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 5ee1dc6be83d0859fc5b48d3ba73f2066b7c9422 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Tue, 28 Jan 2014 21:07:26 +0200 Subject: [PATCH 224/301] attacktypes error fix --- apps/openmw/mwclass/creature.cpp | 6 +++--- apps/openmw/mwclass/npc.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index c23b9e23a..812cd16f7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -282,11 +282,11 @@ namespace MWClass { const bool weaphashealth = get(weapon).hasItemHealth(weapon); const unsigned char *attack = NULL; - if(type == MWMechanics::CreatureStats::AT_Chop) + if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; - else if(type == MWMechanics::CreatureStats::AT_Slash) + else if(type == ESM::Weapon::AT_Slash) attack = weapon.get()->mBase->mData.mSlash; - else if(type == MWMechanics::CreatureStats::AT_Thrust) + else if(type == ESM::Weapon::AT_Thrust) attack = weapon.get()->mBase->mData.mThrust; if(attack) { diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8c6e89544..4ee02d73e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -518,11 +518,11 @@ namespace MWClass { const bool weaphashealth = get(weapon).hasItemHealth(weapon); const unsigned char *attack = NULL; - if(type == MWMechanics::CreatureStats::AT_Chop) + if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; - else if(type == MWMechanics::CreatureStats::AT_Slash) + else if(type == ESM::Weapon::AT_Slash) attack = weapon.get()->mBase->mData.mSlash; - else if(type == MWMechanics::CreatureStats::AT_Thrust) + else if(type == ESM::Weapon::AT_Thrust) attack = weapon.get()->mBase->mData.mThrust; if(attack) { From 365ae155326af422ef471fa6f82aea25ef3c7856 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 14:47:01 +0100 Subject: [PATCH 225/301] Drag&Drop: auto-hide windows that can't be drop targets on mouseover --- apps/openmw/mwgui/mapwindow.cpp | 5 ++-- apps/openmw/mwgui/mapwindow.hpp | 6 +++-- apps/openmw/mwgui/spellwindow.cpp | 3 ++- apps/openmw/mwgui/spellwindow.hpp | 6 +++-- apps/openmw/mwgui/statswindow.cpp | 7 ++++-- apps/openmw/mwgui/statswindow.hpp | 6 ++--- apps/openmw/mwgui/windowbase.cpp | 34 ++++++++++++++++++++++++++ apps/openmw/mwgui/windowbase.hpp | 16 ++++++++++++ apps/openmw/mwgui/windowmanagerimp.cpp | 10 +++++--- 9 files changed, 77 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index ba6114262..d5b243ca5 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -373,8 +373,9 @@ namespace MWGui // ------------------------------------------------------------------------------------------ - MapWindow::MapWindow(const std::string& cacheDir) - : MWGui::WindowPinnableBase("openmw_map_window.layout") + MapWindow::MapWindow(DragAndDrop* drag, const std::string& cacheDir) + : WindowPinnableBase("openmw_map_window.layout") + , NoDrop(drag, mMainWidget) , mGlobal(false) , mGlobalMap(0) , mGlobalMapRender(0) diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 7df2105dc..88c53f06f 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -75,10 +75,10 @@ namespace MWGui float mLastDirectionY; }; - class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase + class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase, public NoDrop { public: - MapWindow(const std::string& cacheDir); + MapWindow(DragAndDrop* drag, const std::string& cacheDir); virtual ~MapWindow(); void setCellName(const std::string& cellName); @@ -92,6 +92,8 @@ namespace MWGui virtual void open(); + void onFrame(float dt) { NoDrop::onFrame(dt); } + 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/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 07076159c..6b261a799 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -42,8 +42,9 @@ namespace namespace MWGui { - SpellWindow::SpellWindow() + SpellWindow::SpellWindow(DragAndDrop* drag) : WindowPinnableBase("openmw_spell_window.layout") + , NoDrop(drag, mMainWidget) , mHeight(0) , mWidth(0) { diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index 521e73d76..38a761931 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -7,14 +7,16 @@ namespace MWGui { class SpellIcons; - class SpellWindow : public WindowPinnableBase + class SpellWindow : public WindowPinnableBase, public NoDrop { public: - SpellWindow(); + SpellWindow(DragAndDrop* drag); virtual ~SpellWindow(); void updateSpells(); + void onFrame(float dt) { NoDrop::onFrame(dt); } + protected: MyGUI::ScrollView* mSpellView; MyGUI::Widget* mEffectBox; diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 40eb2d3b1..abaca165e 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -18,8 +18,9 @@ namespace MWGui const int StatsWindow::sLineHeight = 18; - StatsWindow::StatsWindow () + StatsWindow::StatsWindow (DragAndDrop* drag) : WindowPinnableBase("openmw_stats_window.layout") + , NoDrop(drag, mMainWidget) , mSkillView(NULL) , mMajorSkills() , mMinorSkills() @@ -219,11 +220,13 @@ namespace MWGui updateSkillArea(); } - void StatsWindow::onFrame () + void StatsWindow::onFrame (float dt) { if (!mMainWidget->getVisible()) return; + NoDrop::onFrame(dt); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &PCstats = MWWorld::Class::get(player).getNpcStats(player); diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 28d96ca90..d90c16be9 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -10,17 +10,17 @@ namespace MWGui { class WindowManager; - class StatsWindow : public WindowPinnableBase + class StatsWindow : public WindowPinnableBase, public NoDrop { public: typedef std::map FactionList; typedef std::vector SkillList; - StatsWindow(); + StatsWindow(DragAndDrop* drag); /// automatically updates all the data in the stats window, but only if it has changed. - void onFrame(); + void onFrame(float dt); void setBar(const std::string& name, const std::string& tname, int val, int max); void setPlayerName(const std::string& playerName); diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index cc74579ab..87b26b814 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -1,6 +1,7 @@ #include "windowbase.hpp" #include "../mwbase/windowmanager.hpp" +#include "container.hpp" using namespace MWGui; @@ -50,3 +51,36 @@ void WindowModal::close() { MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget); } + +NoDrop::NoDrop(DragAndDrop *drag, MyGUI::Widget *widget) + : mDrag(drag), mWidget(widget), mTransparent(false) +{ +} + +void NoDrop::onFrame(float dt) +{ + MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance().getMousePosition(); + + if (mDrag->mIsOnDragAndDrop) + { + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); + while (focus && focus != mWidget) + focus = focus->getParent(); + + if (focus == mWidget) + mTransparent = true; + } + if (!mWidget->getAbsoluteCoord().inside(mousePos)) + mTransparent = false; + + if (mTransparent) + { + mWidget->setNeedMouseFocus(false); // Allow click-through + mWidget->setAlpha(std::max(0.13f, mWidget->getAlpha() - dt*5)); + } + else + { + mWidget->setNeedMouseFocus(true); + mWidget->setAlpha(std::min(1.0f, mWidget->getAlpha() + dt*5)); + } +} diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 2c014baf0..48de9ea87 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -11,6 +11,7 @@ namespace MWBase namespace MWGui { class WindowManager; + class DragAndDrop; class WindowBase: public OEngine::GUI::Layout { @@ -42,6 +43,21 @@ namespace MWGui virtual void open(); virtual void close(); }; + + /// A window that cannot be the target of a drag&drop action. + /// When hovered with a drag item, the window will become transparent and allow click-through. + class NoDrop + { + public: + NoDrop(DragAndDrop* drag, MyGUI::Widget* widget); + + void onFrame(float dt); + + private: + MyGUI::Widget* mWidget; + DragAndDrop* mDrag; + bool mTransparent; + }; } #endif diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index cda146e8c..1c48e1d9b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -200,9 +200,9 @@ namespace MWGui mRecharge = new Recharge(); mMenu = new MainMenu(w,h); - mMap = new MapWindow(""); + mMap = new MapWindow(mDragAndDrop, ""); trackWindow(mMap, "map"); - mStatsWindow = new StatsWindow(); + mStatsWindow = new StatsWindow(mDragAndDrop); trackWindow(mStatsWindow, "stats"); mConsole = new Console(w,h, mConsoleOnlyScripts); trackWindow(mConsole, "console"); @@ -227,7 +227,7 @@ namespace MWGui mConfirmationDialog = new ConfirmationDialog(); mAlchemyWindow = new AlchemyWindow(); trackWindow(mAlchemyWindow, "alchemy"); - mSpellWindow = new SpellWindow(); + mSpellWindow = new SpellWindow(mDragAndDrop); trackWindow(mSpellWindow, "spells"); mQuickKeysMenu = new QuickKeysMenu(); mLevelupDialog = new LevelupDialog(); @@ -709,7 +709,9 @@ namespace MWGui mInventoryWindow->onFrame(); - mStatsWindow->onFrame(); + mStatsWindow->onFrame(frameDuration); + mMap->onFrame(frameDuration); + mSpellWindow->onFrame(frameDuration); mWaitDialog->onFrame(frameDuration); From ba67bf45f837e7296c9e55c47237f669ba233140 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 20:15:22 +0100 Subject: [PATCH 226/301] Fix an issue with InventoryStore copy constructor. Don't copy the iterator directly - mContainer will be wrong and comparisons against end() will always fail. This caused an exception when looting a creature that had moved cells. --- apps/openmw/mwworld/inventorystore.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index ea43314e6..5da871d9d 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -34,6 +34,13 @@ void MWWorld::InventoryStore::copySlots (const InventoryStore& store) mSlots.push_back (slot); } + + // some const-trickery, required because of a flaw in the handling of MW-references and the + // resulting workarounds + std::size_t distance = std::distance (const_cast (store).begin(), const_cast (store).mSelectedEnchantItem); + ContainerStoreIterator slot = begin(); + std::advance (slot, distance); + mSelectedEnchantItem = slot; } void MWWorld::InventoryStore::initSlots (TSlots& slots_) @@ -54,18 +61,19 @@ MWWorld::InventoryStore::InventoryStore() MWWorld::InventoryStore::InventoryStore (const InventoryStore& store) : ContainerStore (store) , mSelectedEnchantItem(end()) - , mListener(NULL) - , mUpdatesEnabled(true) { mMagicEffects = store.mMagicEffects; mFirstAutoEquip = store.mFirstAutoEquip; - mSelectedEnchantItem = store.mSelectedEnchantItem; + mListener = store.mListener; + mUpdatesEnabled = store.mUpdatesEnabled; + mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes; copySlots (store); } MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStore& store) { + mListener = store.mListener; mMagicEffects = store.mMagicEffects; mFirstAutoEquip = store.mFirstAutoEquip; mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes; From cc40cec39577fb79e163436deaee2b277beb577d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 21:08:11 +0100 Subject: [PATCH 227/301] Move levelup to NpcStats The code came from back in the days where NpcStats did not derive from CreatureStats. --- apps/openmw/mwgui/charactercreation.cpp | 7 +++-- apps/openmw/mwgui/levelupdialog.cpp | 6 ++--- apps/openmw/mwmechanics/creaturestats.cpp | 31 +---------------------- apps/openmw/mwmechanics/creaturestats.hpp | 11 +------- apps/openmw/mwmechanics/npcstats.cpp | 22 ++++++++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 6 +++++ 6 files changed, 35 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 1a3226074..5526bd26d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -10,7 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" @@ -47,9 +47,8 @@ namespace void updatePlayerHealth() { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats(player); - - creatureStats.updateHealth(); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player); + npcStats.updateHealth(); } } diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index e55d9d8ca..f56d80883 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -155,7 +155,6 @@ namespace MWGui void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) { MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); if (mSpentAttributes.size() < 3) @@ -165,15 +164,14 @@ namespace MWGui // increase attributes for (int i=0; i<3; ++i) { - MWMechanics::AttributeValue attribute = creatureStats.getAttribute(mSpentAttributes[i]); + MWMechanics::AttributeValue attribute = pcStats.getAttribute(mSpentAttributes[i]); attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); if (attribute.getBase() >= 100) attribute.setBase(100); - creatureStats.setAttribute(mSpentAttributes[i], attribute); + pcStats.setAttribute(mSpentAttributes[i], attribute); } - creatureStats.levelUp(); pcStats.levelUp (); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 30db59311..c862c0ab4 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -10,7 +10,7 @@ namespace MWMechanics { CreatureStats::CreatureStats() - : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mDied (false), mFriendlyHits (0), + : mLevel (0), mDead (false), mDied (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false), mAttackingOrSpell(false), @@ -22,35 +22,6 @@ namespace MWMechanics mAiSettings[i] = 0; } - float CreatureStats::getLevelHealthBonus () const - { - return mLevelHealthBonus; - } - - void CreatureStats::levelUp() - { - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - - const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); - - // "When you gain a level, in addition to increasing three primary attributes, your Health - // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, - // the Health increase is calculated from the increased Endurance" - mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat(); - updateHealth(); - - mLevel++; - } - - void CreatureStats::updateHealth() - { - const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); - const int strength = getAttribute(ESM::Attribute::Strength).getBase(); - - setHealth(static_cast (0.5 * (strength + endurance)) + mLevelHealthBonus); - } - const AiSequence& CreatureStats::getAiSequence() const { return mAiSequence; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 67d925a19..bb9583301 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -22,13 +22,11 @@ namespace MWMechanics DrawState_ mDrawState; AttributeValue mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue - int mLevel; Spells mSpells; ActiveSpells mActiveSpells; MagicEffects mMagicEffects; Stat mAiSettings[4]; AiSequence mAiSequence; - float mLevelHealthBonus; bool mDead; bool mDied; int mFriendlyHits; @@ -54,6 +52,7 @@ namespace MWMechanics protected: bool mIsWerewolf; AttributeValue mWerewolfAttributes[8]; + int mLevel; public: CreatureStats(); @@ -142,14 +141,6 @@ namespace MWMechanics float getFatigueTerm() const; ///< Return effective fatigue - float getLevelHealthBonus() const; - - void levelUp(); - - void updateHealth(); - ///< Calculate health based on endurance and strength. - /// Called at character creation and at level up. - bool isDead() const; bool hasDied() const; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index e41ce2078..df59328c2 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -30,6 +30,7 @@ MWMechanics::NpcStats::NpcStats() , mProfit(0) , mTimeToStartDrowning(20.0) , mLastDrowningHit(0) +, mLevelHealthBonus(0) { mSkillIncreases.resize (ESM::Attribute::Length, 0); } @@ -237,6 +238,27 @@ void MWMechanics::NpcStats::levelUp() mLevelProgress -= 10; for (int i=0; i &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); + + // "When you gain a level, in addition to increasing three primary attributes, your Health + // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, + // the Health increase is calculated from the increased Endurance" + mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat(); + updateHealth(); + + setLevel(getLevel()+1); +} + +void MWMechanics::NpcStats::updateHealth() +{ + const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); + const int strength = getAttribute(ESM::Attribute::Strength).getBase(); + + setHealth(static_cast (0.5 * (strength + endurance)) + mLevelHealthBonus); } int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 8cdeeea5d..d7db999e4 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -51,6 +51,8 @@ namespace MWMechanics /// time since last hit from drowning float mLastDrowningHit; + float mLevelHealthBonus; + public: NpcStats(); @@ -98,6 +100,10 @@ namespace MWMechanics void levelUp(); + void updateHealth(); + ///< Calculate health based on endurance and strength. + /// Called at character creation and at level up. + void flagAsUsed (const std::string& id); bool hasBeenUsed (const std::string& id) const; From 82146e7f8d865f931c388f39a4129d0bec189d90 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 21:50:40 +0100 Subject: [PATCH 228/301] Use GMSTs for levelup --- apps/openmw/mwgui/statswindow.cpp | 5 ++- apps/openmw/mwgui/waitdialog.cpp | 4 ++- apps/openmw/mwmechanics/npcstats.cpp | 53 ++++++++++++++++------------ files/mygui/openmw_tooltips.layout | 3 -- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index abaca165e..3d4c741a3 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -234,9 +234,12 @@ namespace MWGui MyGUI::Widget* levelWidget; for (int i=0; i<2; ++i) { + int max = MWBase::Environment::get().getWorld()->getStore().get().find("iLevelUpTotal")->getInt(); getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast(PCstats.getLevelProgress())); - levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/10"); + levelWidget->setUserString("Range_LevelProgress", boost::lexical_cast(max)); + levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/" + + boost::lexical_cast(max)); } setFactions(PCstats.getFactionRanks()); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 0ead54d9d..484462043 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -271,7 +271,9 @@ namespace MWGui const MWMechanics::NpcStats &pcstats = MWWorld::Class::get(player).getNpcStats(player); // trigger levelup if possible - if (mSleeping && pcstats.getLevelProgress () >= 10) + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + if (mSleeping && pcstats.getLevelProgress () >= gmst.find("iLevelUpTotal")->getInt()) { MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index df59328c2..e642ffc5a 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -190,22 +190,31 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas base += 1; - // if this is a major or minor skill of the class, increase level progress - bool levelProgress = false; - for (int i=0; i<2; ++i) - for (int j=0; j<5; ++j) + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + // is this a minor or major skill? + int increase = gmst.find("iLevelupMiscMultAttriubte")->getInt(); // Note: GMST has a typo + for (int k=0; k<5; ++k) + { + if (class_.mData.mSkills[k][0] == skillIndex) { - int skill = class_.mData.mSkills[j][i]; - if (skill == skillIndex) - levelProgress = true; + mLevelProgress += gmst.find("iLevelUpMinorMult")->getInt(); + increase = gmst.find("iLevelUpMajorMultAttribute")->getInt(); } + } + for (int k=0; k<5; ++k) + { + if (class_.mData.mSkills[k][1] == skillIndex) + { + mLevelProgress += gmst.find("iLevelUpMajorMult")->getInt(); + increase = gmst.find("iLevelUpMinorMultAttribute")->getInt(); + } + } - mLevelProgress += levelProgress; - - // check the attribute this skill belongs to const ESM::Skill* skill = MWBase::Environment::get().getWorld ()->getStore ().get().find(skillIndex); - ++mSkillIncreases[skill->mData.mAttribute]; + mSkillIncreases[skill->mData.mAttribute] += increase; // Play sound & skill progress notification /// \todo check if character is the player, if levelling is ever implemented for NPCs @@ -217,7 +226,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas % static_cast (base); MWBase::Environment::get().getWindowManager ()->messageBox(message.str()); - if (mLevelProgress >= 10) + if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) { // levelup is possible now MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}"); @@ -263,18 +272,18 @@ void MWMechanics::NpcStats::updateHealth() int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const { - // Source: http://www.uesp.net/wiki/Morrowind:Level#How_to_Level_Up int num = mSkillIncreases[attribute]; - if (num <= 1) + + if (num == 0) return 1; - else if (num <= 4) - return 2; - else if (num <= 7) - return 3; - else if (num <= 9) - return 4; - else - return 5; + + num = std::min(10, num); + + // iLevelUp01Mult - iLevelUp10Mult + std::stringstream gmst; + gmst << "iLevelUp" << std::setfill('0') << std::setw(2) << num << "Mult"; + + return MWBase::Environment::get().getWorld()->getStore().get().find(gmst.str())->getInt(); } void MWMechanics::NpcStats::flagAsUsed (const std::string& id) diff --git a/files/mygui/openmw_tooltips.layout b/files/mygui/openmw_tooltips.layout index 624c133f2..3f4fec59f 100644 --- a/files/mygui/openmw_tooltips.layout +++ b/files/mygui/openmw_tooltips.layout @@ -184,11 +184,8 @@ - - - From b8db151da75457de35c412269ca390245315ddb6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 22:00:36 +0100 Subject: [PATCH 229/301] Add missing skill increases for armor hits --- apps/openmw/mwclass/npc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4ee02d73e..9e54f2856 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -726,6 +726,9 @@ namespace MWClass if (armorref.mCharge == 0) inv.unequipItem(armor, ptr); + if (ptr.getRefData().getHandle() == "player") + skillUsageSucceeded(ptr, get(armor).getEquipmentSkill(armor), 0); + switch(get(armor).getEquipmentSkill(armor)) { case ESM::Skill::LightArmor: @@ -739,6 +742,8 @@ namespace MWClass break; } } + else if(ptr.getRefData().getHandle() == "player") + skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0); } } From ea7e0abdc23b778b43e917e3f590792bdbb7b91d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 22:07:20 +0100 Subject: [PATCH 230/301] Move WhenStrikes skill success to a more appropriate place --- apps/openmw/mwclass/npc.cpp | 5 +---- apps/openmw/mwmechanics/spellcasting.cpp | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 9e54f2856..512a5279c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -603,10 +603,7 @@ namespace MWClass { MWMechanics::CastSpell cast(ptr, victim); cast.mHitPosition = hitPosition; - bool success = cast.cast(weapon); - - if (ptr.getRefData().getHandle() == "player" && success) - skillUsageSucceeded (ptr, ESM::Skill::Enchant, 3); + cast.cast(weapon); } } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 749a5d7b1..0dec49f13 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -554,7 +554,10 @@ namespace MWMechanics else if (enchantment->mData.mType != ESM::Enchantment::WhenStrikes) { if (mCaster.getRefData().getHandle() == "player") + { MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); // Set again to show the modified charge + mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 3); + } } inflict(mCaster, mCaster, enchantment->mEffects, ESM::RT_Self); From 292d692b40102dd5c28cdbfebea1176176bb2129 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 23:32:46 +0100 Subject: [PATCH 231/301] Loosen window size restrictions --- files/mygui/openmw_inventory_window.layout | 2 +- files/mygui/openmw_map_window.layout | 2 +- files/mygui/openmw_spell_window.layout | 2 +- files/mygui/openmw_stats_window.layout | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index ecccd995b..ba6bf820e 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -2,7 +2,7 @@ - + diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index 232f31b75..6e0efce7e 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -2,7 +2,7 @@ - + diff --git a/files/mygui/openmw_spell_window.layout b/files/mygui/openmw_spell_window.layout index ab924da6d..ec655ace8 100644 --- a/files/mygui/openmw_spell_window.layout +++ b/files/mygui/openmw_spell_window.layout @@ -2,7 +2,7 @@ - + diff --git a/files/mygui/openmw_stats_window.layout b/files/mygui/openmw_stats_window.layout index 36c28c450..efec0ab37 100644 --- a/files/mygui/openmw_stats_window.layout +++ b/files/mygui/openmw_stats_window.layout @@ -2,7 +2,7 @@ - + From 190512156d02a15f65e6f54801582c753eda98fa Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Jan 2014 01:58:04 +0100 Subject: [PATCH 232/301] Use some more GMSTs for enchanting --- apps/openmw/mwclass/armor.cpp | 4 ++-- apps/openmw/mwclass/armor.hpp | 2 +- apps/openmw/mwclass/book.cpp | 4 ++-- apps/openmw/mwclass/book.hpp | 2 +- apps/openmw/mwclass/clothing.cpp | 4 ++-- apps/openmw/mwclass/clothing.hpp | 2 +- apps/openmw/mwclass/weapon.cpp | 4 ++-- apps/openmw/mwclass/weapon.hpp | 2 +- apps/openmw/mwmechanics/enchanting.cpp | 25 ++++++++++++------------- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 2 +- 11 files changed, 26 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 83bda25d1..e3974f243 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -366,12 +366,12 @@ namespace MWClass return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell); } - float Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mEnchant/10.f; + return ref->mBase->mData.mEnchant; } bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index d8d09d5bb..17cfca453 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -79,7 +79,7 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; - virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 429d91259..0e6506514 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -189,12 +189,12 @@ namespace MWClass return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell); } - float Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mEnchant/10.f; + return ref->mBase->mData.mEnchant; } bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index 7fb8a9507..79b823fa9 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -58,7 +58,7 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; - virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index a135585eb..ab98d05ae 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -279,12 +279,12 @@ namespace MWClass return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell); } - float Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mEnchant/10.f; + return ref->mBase->mData.mEnchant; } bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index e2e1188a1..a73b2c190 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -71,7 +71,7 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; - virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 82935673c..af0234cd5 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -429,12 +429,12 @@ namespace MWClass return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell); } - float Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mEnchant/10.f; + return ref->mBase->mData.mEnchant; } bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 181c637db..db44cd2b7 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -84,7 +84,7 @@ namespace MWClass virtual float getWeight (const MWWorld::Ptr& ptr) const; - virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 6c765aa41..87337cdd7 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -250,7 +250,10 @@ namespace MWMechanics { if (itemEmpty()) return 0; - return MWWorld::Class::get(mOldItemPtr).getEnchantmentPoints(mOldItemPtr); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + + return mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get().find("fEnchantmentMult")->getFloat(); } bool Enchanting::soulEmpty() const { @@ -274,22 +277,18 @@ namespace MWMechanics float Enchanting::getEnchantChance() const { - /* - Formula from http://www.uesp.net/wiki/Morrowind:Enchant - */ - const CreatureStats& creatureStats = MWWorld::Class::get (mEnchanter).getCreatureStats (mEnchanter); const NpcStats& npcStats = MWWorld::Class::get (mEnchanter).getNpcStats (mEnchanter); float chance1 = (npcStats.getSkill (ESM::Skill::Enchant).getModified() + - (0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified()) - + (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())); + (0.25 * npcStats.getAttribute (ESM::Attribute::Intelligence).getModified()) + + (0.125 * npcStats.getAttribute (ESM::Attribute::Luck).getModified())); + + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + + float chance2 = 7.5 / (gmst.find("fEnchantmentChanceMult")->getFloat() * ((mCastStyle == ESM::Enchantment::ConstantEffect) ? + gmst.find("fEnchantmentConstantChanceMult")->getFloat() : 1 )) + * getEnchantPoints(); - float chance2 = 2.5 * getEnchantPoints(); - if(mCastStyle==ESM::Enchantment::ConstantEffect) - { - float constantChance = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentConstantChanceMult")->getFloat(); - chance2 /= constantChance; - } return (chance1-chance2); } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 9771ffde3..12676b43c 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -167,7 +167,7 @@ namespace MWWorld return 0; } - float Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { throw std::runtime_error ("class does not support enchanting"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 0dee8b292..ad292df5c 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -259,7 +259,7 @@ namespace MWWorld ///< @return the enchantment ID if the object is enchanted, otherwise an empty string /// (default implementation: return empty string) - virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; ///< @return the number of enchantment points available for possible enchanting virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; From a8a09762ce536b49b0d5ee4b701cac0b32fa2d46 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jan 2014 19:03:20 +0100 Subject: [PATCH 233/301] Don't crash when deleting or disabling a moving door --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0394a2edd..340c2fa58 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1153,7 +1153,7 @@ namespace MWWorld std::map::iterator it = mDoorStates.begin(); while (it != mDoorStates.end()) { - if (!mWorldScene->isCellActive(*it->first.getCell())) + if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode()) mDoorStates.erase(it++); else { From edff88542be04be60c02e0fc575eb782276b8875 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jan 2014 20:12:37 +0100 Subject: [PATCH 234/301] Allow double click / Enter key for accepting race/class/birthsign --- apps/openmw/mwgui/birth.cpp | 11 +++++++++-- apps/openmw/mwgui/birth.hpp | 1 + apps/openmw/mwgui/class.cpp | 11 +++++++++-- apps/openmw/mwgui/class.hpp | 1 + apps/openmw/mwgui/race.cpp | 11 +++++++++-- apps/openmw/mwgui/race.hpp | 1 + 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 9c8e07f3e..7f58309ba 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -33,8 +33,7 @@ namespace MWGui getWidget(mBirthList, "BirthsignList"); mBirthList->setScrollVisible(true); - mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onAccept); mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); MyGUI::Button* backButton; @@ -97,6 +96,14 @@ namespace MWGui eventDone(this); } + void BirthDialog::onAccept(MyGUI::ListBox *_sender, size_t _index) + { + onSelectBirth(_sender, _index); + if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } + void BirthDialog::onBackClicked(MyGUI::Widget* _sender) { eventBack(); diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index cc958ddca..20a64c78c 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -38,6 +38,7 @@ namespace MWGui protected: void onSelectBirth(MyGUI::ListBox* _sender, size_t _index); + void onAccept(MyGUI::ListBox* _sender, size_t index); void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 1e1aebd95..1c8cc7840 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -82,8 +82,7 @@ namespace MWGui getWidget(mClassList, "ClassList"); mClassList->setScrollVisible(true); - mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onAccept); mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); getWidget(mClassImage, "ClassImage"); @@ -152,6 +151,14 @@ namespace MWGui eventBack(); } + void PickClassDialog::onAccept(MyGUI::ListBox* _sender, size_t _index) + { + onSelectClass(_sender, _index); + if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } + void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) { if (_index == MyGUI::ITEM_NONE) diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index e74370a4c..f78f7541b 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -111,6 +111,7 @@ namespace MWGui protected: void onSelectClass(MyGUI::ListBox* _sender, size_t _index); + void onAccept(MyGUI::ListBox* _sender, size_t _index); void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 299b34b51..3dff1b7e4 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -70,8 +70,7 @@ namespace MWGui setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race")); getWidget(mRaceList, "RaceList"); mRaceList->setScrollVisible(true); - mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); + mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onAccept); mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus")); @@ -241,6 +240,14 @@ namespace MWGui updateSpellPowers(); } + void RaceDialog::onAccept(MyGUI::ListBox *_sender, size_t _index) + { + onSelectRace(_sender, _index); + if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } + void RaceDialog::getBodyParts (int part, std::vector& out) { out.clear(); diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 914ae8096..340dcfa27 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -67,6 +67,7 @@ namespace MWGui void onSelectNextHair(MyGUI::Widget* _sender); void onSelectRace(MyGUI::ListBox* _sender, size_t _index); + void onAccept(MyGUI::ListBox* _sender, size_t _index); void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); From 434fd215848762c0582fdf45bfc3798f99074709 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jan 2014 20:24:25 +0100 Subject: [PATCH 235/301] Don't set journal index if a higher index is currently set This is vanilla behaviour, and required for the Mehra Milo vivec informants quest. --- apps/openmw/mwdialogue/quest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 520331bc1..5de5b1cf1 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -82,7 +82,8 @@ namespace MWDialogue if (index==-1) throw std::runtime_error ("unknown journal entry for topic " + mTopic); - setIndex (index); + if (index > mIndex) + setIndex (index); for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter) if (*iter==entry.mInfoId) From 23ffb8a4dc0f393f01272a97255606353ebe683f Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jan 2014 20:57:37 +0100 Subject: [PATCH 236/301] Fixes #1143: Make getCurrentAiPackage return the package that was run last, not the package that will be run in the next frame. This makes the Mehra Milo script work properly. --- apps/openmw/mwmechanics/aisequence.cpp | 5 ++++- apps/openmw/mwmechanics/aisequence.hpp | 7 +++++++ apps/openmw/mwscript/aiextensions.cpp | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 989d9c6a2..2110393fd 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -23,7 +23,7 @@ void MWMechanics::AiSequence::copy (const AiSequence& sequence) mPackages.push_back ((*iter)->clone()); } -MWMechanics::AiSequence::AiSequence() : mDone (false) {} +MWMechanics::AiSequence::AiSequence() : mDone (false), mLastAiPackage(-1) {} MWMechanics::AiSequence::AiSequence (const AiSequence& sequence) : mDone (false) { @@ -84,6 +84,7 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration) { if (!mPackages.empty()) { + mLastAiPackage = mPackages.front()->getTypeId(); if (mPackages.front()->execute (actor,duration)) { delete *mPackages.begin(); @@ -91,7 +92,9 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration) mDone = true; } else + { mDone = false; + } } } } diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 351e04480..62f48f981 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -23,6 +23,9 @@ namespace MWMechanics void copy (const AiSequence& sequence); + // The type of AI package that ran last + int mLastAiPackage; + public: AiSequence(); @@ -36,6 +39,10 @@ namespace MWMechanics int getTypeId() const; ///< @see enum AiPackage::TypeId + int getLastRunTypeId() const { return mLastAiPackage; } + ///< Get the typeid of the Ai package that ran last, NOT the currently "active" Ai package that will be run in the next frame. + /// This difference is important when an Ai package has just finished and been removed. + bool getCombatTarget (std::string &targetActorId) const; ///< Return true and assign target if combat package is currently /// active, return false otherwise diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 05886c51c..8314d011a 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -352,7 +352,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().getTypeId (); + Interpreter::Type_Integer value = MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().getLastRunTypeId(); runtime.push (value); } From e717694b3670c7ffff81a0b403c0fb5f84b2e087 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jan 2014 22:14:36 +0100 Subject: [PATCH 237/301] Reset Run stance in AiWander --- apps/openmw/mwmechanics/aiwander.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 2f8f1cbd4..c6ea2ee91 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -68,6 +68,7 @@ namespace MWMechanics bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); + actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); MWBase::World *world = MWBase::Environment::get().getWorld(); if(mDuration) { From 305e78c9819201a85581d072b69652caee9c83bc Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 00:05:32 +0100 Subject: [PATCH 238/301] Fix creature attack bug --- apps/openmw/mwclass/creature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 812cd16f7..b1bd80596 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -332,7 +332,7 @@ namespace MWClass if (damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); - victim.getClass().onHit(victim, damage, true, MWWorld::Ptr(), ptr, true); + victim.getClass().onHit(victim, damage, true, weapon, ptr, true); } void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const From 2b15b8b4848469a9462f3cec765c55303c0dbb29 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 15:22:07 +0100 Subject: [PATCH 239/301] Fix gold bugs (Fixes #1145, Fixes #1146) --- apps/openmw/mwclass/misc.cpp | 10 ++++------ apps/openmw/mwworld/containerstore.cpp | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index a8a6c55ec..e58716f1c 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -155,11 +155,8 @@ namespace MWClass int count = ptr.getRefData().getCount(); bool gold = isGold(ptr); - - if (gold && ptr.getCellRef().mGoldValue != 1) - count = ptr.getCellRef().mGoldValue; - else if (gold) - count *= ref->mBase->mData.mValue; + if (gold) + count *= getValue(ptr); std::string countString; if (!gold) @@ -204,7 +201,7 @@ namespace MWClass MWBase::Environment::get().getWorld()->getStore(); if (isGold(ptr)) { - int goldAmount = ptr.getRefData().getCount(); + int goldAmount = getValue(ptr) * ptr.getRefData().getCount(); std::string base = "Gold_001"; if (goldAmount >= 100) @@ -223,6 +220,7 @@ namespace MWClass newRef.getPtr().get(); newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); newPtr.getCellRef().mGoldValue = goldAmount; + newPtr.getRefData().setCount(1); } else { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 0c4226f9b..71eb9145e 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -192,13 +192,11 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { - int realCount = ptr.getRefData().getCount(); - if (ptr.getCellRef().mGoldValue > 1 && realCount == 1) - realCount = ptr.getCellRef().mGoldValue; + int realCount = count * ptr.getClass().getValue(ptr); for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { - if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, MWWorld::ContainerStore::sGoldId)) + if (Misc::StringUtils::ciEqual((*iter).getCellRef().mRefID, MWWorld::ContainerStore::sGoldId)) { iter->getRefData().setCount(iter->getRefData().getCount() + realCount); flagAsModified(); @@ -206,8 +204,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, } } - MWWorld::ManualRef ref(esmStore, MWWorld::ContainerStore::sGoldId, count); - return addNewStack(ref.getPtr(), count); + MWWorld::ManualRef ref(esmStore, MWWorld::ContainerStore::sGoldId, realCount); + return addNewStack(ref.getPtr(), realCount); } // determine whether to stack or not From ec7cb90ca466f11bcb67ee34271af5f46f896e3c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 30 Jan 2014 11:50:13 +0100 Subject: [PATCH 240/301] 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 6c00b949c..6e43b8dc2 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 ec22d0306..6e3a56907 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 a12d20e6a..d71704fd7 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 241/301] 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 cc56ec4c8..ddb2c16d6 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 c15228a6a..5568e1727 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 88c241e1a..77fdc971d 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 d73bcaf74..f37a537c5 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 000000000..1ef040823 --- /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 000000000..a22735e07 --- /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 80d8aa4030f771b48fc612c4716a0e891d5fe12a Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 30 Jan 2014 23:54:26 +0200 Subject: [PATCH 242/301] bug with sequence of knockouts; giving sense to some hit state code --- apps/openmw/mwmechanics/character.cpp | 1 + apps/openmw/mwmechanics/creaturestats.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index a54f2365d..252271d32 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -764,6 +764,7 @@ bool CharacterController::updateWeaponState() //commenting out following 2 lines will give a bit different combat dynamics(slower) mHitState = CharState_None; mCurrentHit.clear(); + mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false); } } else if(mUpperBodyState == UpperCharState_UnEquipingWeap) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 30db59311..943c13a18 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -209,7 +209,10 @@ namespace MWMechanics mDynamic[index] = value; if (index == 2 && value.getCurrent() < 0) + { setKnockedDown(true); + mDynamic[2].setCurrent(0); + } if (index==0 && mDynamic[index].getCurrent()<1) { From 39d86a946803b2494c93934742c73e79e27c915e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 20:29:07 +0100 Subject: [PATCH 243/301] Improvements to smooth NPC steering --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 6 +-- apps/openmw/mwmechanics/aicombat.cpp | 57 ++++++++++------------- apps/openmw/mwmechanics/aicombat.hpp | 11 +++-- apps/openmw/mwmechanics/aiescort.cpp | 19 ++++---- apps/openmw/mwmechanics/aifollow.cpp | 62 ++++++++++++------------- apps/openmw/mwmechanics/aitravel.cpp | 9 ++-- apps/openmw/mwmechanics/aiwander.cpp | 13 ++++-- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.hpp | 3 +- apps/openmw/mwmechanics/steering.cpp | 43 +++++++++++++++++ apps/openmw/mwmechanics/steering.hpp | 19 ++++++++ 12 files changed, 151 insertions(+), 95 deletions(-) create mode 100644 apps/openmw/mwmechanics/steering.cpp create mode 100644 apps/openmw/mwmechanics/steering.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 41cc320ad..ce7700ddd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting - disease pickpocket levelledlist combat + disease pickpocket levelledlist combat steering ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 2ad667d3f..44ed59bb4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -574,8 +574,6 @@ namespace MWInput double x = arg.xrel * mCameraSensitivity * (1.0f/256.f); double y = arg.yrel * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; - float scale = MWBase::Environment::get().getFrameDuration(); - if(scale <= 0.0f) scale = 1.0f; float rot[3]; rot[0] = -y; @@ -585,8 +583,8 @@ namespace MWInput // Only actually turn player when we're not in vanity mode if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) { - mPlayer->yaw(x/scale); - mPlayer->pitch(-y/scale); + mPlayer->yaw(x); + mPlayer->pitch(-y); } if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index ab39e8f0f..cf08dabf8 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -1,23 +1,22 @@ #include "aicombat.hpp" -#include "aifollow.hpp" -#include "movement.hpp" +#include +#include + #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "character.hpp" -#include "../mwworld/inventorystore.hpp" -#include "creaturestats.hpp" #include "npcstats.hpp" - -#include -#include +#include "steering.hpp" +#include "movement.hpp" +#include "character.hpp" // fixme: for getActiveWeapon namespace { @@ -43,7 +42,9 @@ namespace MWMechanics mReadyToAttack(false), mStrike(false), mCombatMove(false), - mMovement() + mRotate(false), + mMovement(), + mTargetAngle(0) { } @@ -68,10 +69,16 @@ namespace MWMechanics mCombatMove = false; } } + actor.getClass().getMovementSettings(actor) = mMovement; + + if (mRotate) + { + if (zTurn(actor, Ogre::Degree(mTargetAngle))) + mRotate = false; + } - //actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mReadyToAttack); mTimerAttack -= duration; actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); @@ -156,12 +163,7 @@ namespace MWMechanics weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) } - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - ESM::Position pos = actor.getRefData().getPosition(); - - float zAngle; - float rangeMelee; float rangeCloseUp; @@ -189,12 +191,8 @@ namespace MWMechanics //Melee and Close-up combat vDir.z = 0; float dirLen = vDir.length(); - zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); - - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - - //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); + mRotate = true; //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); if (mFollowTarget && distBetween > rangeMelee) @@ -237,12 +235,6 @@ namespace MWMechanics else { //target is at far distance: build path to target OR follow target (if previously actor had reached it once) - - /* - //apply when AIFOLLOW package implementation will be existent - if(mFollowTarget) - actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiFollow(mTarget));*/ - mFollowTarget = false; buildNewPath(actor); @@ -252,13 +244,10 @@ namespace MWMechanics //try shortcut if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) - zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); else - zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - //mMovement.mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]); + mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + mRotate = true; mMovement.mPosition[1] = 1; mReadyToAttack = false; @@ -294,6 +283,8 @@ namespace MWMechanics } } + actor.getClass().getMovementSettings(actor) = mMovement; + return false; } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 27f7f5d95..767a36292 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -29,16 +29,21 @@ namespace MWMechanics private: PathFinder mPathFinder; - //controls duration of the actual strike + // controls duration of the actual strike float mTimerAttack; float mTimerReact; - //controls duration of the sideway & forward moves - //when mCombatMove is true + // controls duration of the sideway & forward moves + // when mCombatMove is true float mTimerCombatMove; + // the z rotation angle (degrees) we want to reach + // used every frame when mRotate is true + float mTargetAngle; + bool mReadyToAttack, mStrike; bool mFollowTarget; bool mCombatMove; + bool mRotate; MWMechanics::Movement mMovement; MWWorld::Ptr mTarget; diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 901b8c31d..bac258425 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -8,6 +8,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "steering.hpp" + namespace { float sgn(float a) @@ -33,7 +35,7 @@ namespace MWMechanics { mMaxDist = 470; - // The CS Help File states that if a duration is givin, the AI package will run for that long + // The CS Help File states that if a duration is given, the AI package will run for that long // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. if(mX != 0 || mY != 0 || mZ != 0) mDuration = 0; @@ -52,7 +54,7 @@ namespace MWMechanics { mMaxDist = 470; - // The CS Help File states that if a duration is givin, the AI package will run for that long + // The CS Help File states that if a duration is given, the AI package will run for that long // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. if(mX != 0 || mY != 0 || mZ != 0) mDuration = 0; @@ -89,25 +91,23 @@ namespace MWMechanics if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) { int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); - // Check if actor is near the border of an inactive cell. If so, disable AiEscort. - // FIXME: This *should* pause the AiEscort package instead of terminating it. + // Check if actor is near the border of an inactive cell. If so, pause walking. if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2.0 - 200)) { MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - return true; + return false; } } if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) { int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); - // Check if actor is near the border of an inactive cell. If so, disable AiEscort. - // FIXME: This *should* pause the AiEscort package instead of terminating it. + // Check if actor is near the border of an inactive cell. If so, pause walking. if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2.0 - 200)) { MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - return true; + return false; } } @@ -151,8 +151,7 @@ namespace MWMechanics if(distanceBetweenResult <= mMaxDist * mMaxDist) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + zTurn(actor, Ogre::Degree(zAngle)); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; mMaxDist = 470; } diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 10bff8356..cf5291fd3 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -6,15 +6,17 @@ #include "movement.hpp" #include - + +#include "steering.hpp" + MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0) -{ -} -MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) -: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0) -{ -} +{ +} +MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) +: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0) +{ +} bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { @@ -45,14 +47,14 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) } } - ESM::Pathgrid::Point dest; - dest.mX = target.getRefData().getPosition().pos[0]; - dest.mY = target.getRefData().getPosition().pos[1]; + ESM::Pathgrid::Point dest; + dest.mX = target.getRefData().getPosition().pos[0]; + dest.mY = target.getRefData().getPosition().pos[1]; dest.mZ = target.getRefData().getPosition().pos[2]; - ESM::Pathgrid::Point start; - start.mX = pos.pos[0]; - start.mY = pos.pos[1]; + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; start.mZ = pos.pos[2]; if(mPathFinder.getPath().empty()) @@ -88,18 +90,14 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) { - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - //MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - MWWorld::Class::get(actor).getMovementSettings(actor).mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]); - //std::cout << Ogre::Degree(zAngle).valueDegrees()-Ogre::Radian(actor.getRefData().getPosition().rot[2]).valueDegrees() << " "<< pos.rot[2] << " " << zAngle << "\n"; - //MWWorld::Class::get(actor).get - } - - if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2]) - < 100*100) - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + } + + if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2]) + < 100*100) + actor.getClass().getMovementSettings(actor).mPosition[1] = 0; else - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; return false; } @@ -109,12 +107,12 @@ std::string MWMechanics::AiFollow::getFollowedActor() return mActorId; } -MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const -{ - return new AiFollow(*this); -} - - int MWMechanics::AiFollow::getTypeId() const -{ - return TypeIdFollow; +MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const +{ + return new AiFollow(*this); +} + + int MWMechanics::AiFollow::getTypeId() const +{ + return TypeIdFollow; } diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 9a1b98d8f..8a0b2ebd0 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -1,11 +1,12 @@ #include "aitravel.hpp" -#include "movement.hpp" - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" +#include "steering.hpp" +#include "movement.hpp" + namespace { float sgn(float a) @@ -86,9 +87,7 @@ namespace MWMechanics return true; } - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - // TODO: use movement settings instead of rotating directly - world->rotateObject(actor, 0, 0, zAngle, false); + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); movement.mPosition[1] = 1; return false; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index c6ea2ee91..5be604ab1 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -11,6 +11,8 @@ #include "creaturestats.hpp" #include +#include "steering.hpp" + namespace { float sgn(float a) @@ -282,11 +284,6 @@ namespace MWMechanics if(mWalking) { - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - // TODO: use movement settings instead of rotating directly - world->rotateObject(actor, 0, 0, zAngle, false); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) { stopWalking(actor); @@ -294,6 +291,12 @@ namespace MWMechanics mWalking = false; mChooseAction = true; } + else + { + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + } } return false; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index a54f2365d..32a05832c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1111,9 +1111,9 @@ void CharacterController::update(float duration) if (!mSkipAnim) { + rot *= Ogre::Math::RadiansToDegrees(1.0f); if(mHitState != CharState_KnockDown) { - rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); } else //avoid z-rotating for knockdown diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index a3ac22012..8771ef0ca 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -3,7 +3,6 @@ #include #include -#include namespace MWWorld { @@ -26,8 +25,10 @@ namespace MWMechanics bool checkPathCompleted(float x, float y, float z); ///< \Returns true if the last point of the path has been reached. + bool checkWaypoint(float x, float y, float z); ///< \Returns true if a way point was reached + float getZAngleToNext(float x, float y) const; float getDistToNext(float x, float y, float z); diff --git a/apps/openmw/mwmechanics/steering.cpp b/apps/openmw/mwmechanics/steering.cpp new file mode 100644 index 000000000..d911fd81b --- /dev/null +++ b/apps/openmw/mwmechanics/steering.cpp @@ -0,0 +1,43 @@ +#include "steering.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/ptr.hpp" + +#include "../mwbase/environment.hpp" + +#include "movement.hpp" + +namespace MWMechanics +{ + +bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle) +{ + Ogre::Radian currentAngle (actor.getRefData().getPosition().rot[2]); + Ogre::Radian diff (targetAngle - currentAngle); + if (diff >= Ogre::Degree(180)) + { + // Turning the other way would be a better idea + diff = diff-Ogre::Degree(360); + } + else if (diff <= Ogre::Degree(-180)) + { + diff = Ogre::Degree(360)-diff; + } + Ogre::Radian absDiff = Ogre::Math::Abs(diff); + + // The turning animation actually moves you slightly, so the angle will be wrong again. + // Use epsilon to prevent jerkiness. + const Ogre::Degree epsilon (0.5); + if (absDiff < epsilon) + return true; + + // Max. speed of 10 radian per sec + Ogre::Radian limit = Ogre::Radian(10) * MWBase::Environment::get().getFrameDuration(); + if (absDiff > limit) + diff = Ogre::Math::Sign(diff) * limit; + + actor.getClass().getMovementSettings(actor).mRotation[2] = diff.valueRadians(); + return false; +} + +} diff --git a/apps/openmw/mwmechanics/steering.hpp b/apps/openmw/mwmechanics/steering.hpp new file mode 100644 index 000000000..504dc3ac3 --- /dev/null +++ b/apps/openmw/mwmechanics/steering.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_MECHANICS_STEERING_H + +#include + +namespace MWWorld +{ +class Ptr; +} + +namespace MWMechanics +{ + +/// configure rotation settings for an actor to reach this target angle (eventually) +/// @return have we reached the target angle? +bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle); + +} + +#endif From 52f89e9a1754bc7a61d2df32e0bc61bc5803d090 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 20:50:40 +0100 Subject: [PATCH 244/301] Don't play "idle" voice entries for NPCs with Hello 0 Not entirely sure if this is correct, but it prevents the NPCs in the starting boat from incorrectly playing them. --- apps/openmw/mwmechanics/aiwander.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 5be604ab1..77316fedf 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -191,15 +191,18 @@ namespace MWMechanics mIdleNow = true; // Play idle voiced dialogue entries randomly - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - float chance = store.get().find("fVoiceIdleOdds")->getFloat(); - int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - - // Don't bother if the player is out of hearing range - if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(actor.getRefData().getPosition().pos)) < 1500) - MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); + int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); + if (hello > 0) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + float chance = store.get().find("fVoiceIdleOdds")->getFloat(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + // Don't bother if the player is out of hearing range + if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(actor.getRefData().getPosition().pos)) < 1500) + MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); + } } } @@ -208,7 +211,7 @@ namespace MWMechanics // Play a random voice greeting if the player gets too close const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - float hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); + int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); float helloDistance = hello; int iGreetDistanceMultiplier = store.get().find("iGreetDistanceMultiplier")->getInt(); helloDistance *= iGreetDistanceMultiplier; From d0c6ecd03ad8440f9fc5390a4c036ecdeaab5d2d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 21:04:52 +0100 Subject: [PATCH 245/301] Bug #1148: Workaround for broken images in some MW versions --- apps/openmw/mwgui/formatting.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index bd75c078c..4d3d04ced 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -12,6 +12,8 @@ #include #include +#include + namespace { int convertFromHex(std::string hex) @@ -288,6 +290,16 @@ namespace MWGui MyGUI::ImageBox* box = mParent->createWidget ("ImageBox", MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, mParent->getName() + boost::lexical_cast(mParent->getChildCount())); + + // Apparently a bug with some morrowind versions, they reference the image without the size suffix. + // So if the image isn't found, try appending the size. + if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("bookart\\"+image)) + { + std::stringstream str; + str << image.substr(0, image.rfind(".")) << "_" << width << "_" << height << image.substr(image.rfind(".")); + image = str.str(); + } + box->setImageTexture("bookart\\" + image); box->setProperty("NeedMouse", "false"); } From 7820ea5806f95065dbbb02cda5873203c4228004 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 30 Jan 2014 03:14:22 +0100 Subject: [PATCH 246/301] "Always best attack" should only affect the player --- apps/openmw/mwmechanics/character.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 32a05832c..63f363045 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -691,7 +691,8 @@ bool CharacterController::updateWeaponState() mAttackType = "shoot"; else { - if(isWeapon && Settings::Manager::getBool("best attack", "Game")) + if(isWeapon && mPtr.getRefData().getHandle() == "player" && + Settings::Manager::getBool("best attack", "Game")) mAttackType = getBestAttack(weapon->get()->mBase); else determineAttackType(); From 067c2bc2ec0d67fe3d05afba72d9c7f047944935 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 30 Jan 2014 03:18:34 +0100 Subject: [PATCH 247/301] Disable combat movements for creatures without weapons --- apps/openmw/mwmechanics/aicombat.cpp | 7 +++++-- apps/openmw/mwmechanics/character.cpp | 18 ++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index cf08dabf8..2ff7ec229 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -204,7 +204,10 @@ namespace MWMechanics { //Melee: stop running and attack mMovement.mPosition[1] = 0; - chooseBestAttack(weapon, mMovement); + + // When attacking with a weapon, choose between slash, thrust or chop + if (actor.getClass().hasInventoryStore(actor)) + chooseBestAttack(weapon, mMovement); if(mMovement.mPosition[0] || mMovement.mPosition[1]) { @@ -365,7 +368,7 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement { if (weapon == NULL) { - //hand-to-hand and creatures' attacks deal equal damage for each type + //hand-to-hand deal equal damage for each type float roll = static_cast(rand())/RAND_MAX; if(roll <= 0.333f) //side punch { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 63f363045..74cda0a70 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -478,7 +478,14 @@ bool CharacterController::updateCreatureState() { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); - determineAttackType(); + // These are unique animations and not linked to movement type. Just pick one randomly. + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 3; // [0, 2] + if (roll == 0) + mCurrentWeapon = "attack1"; + else if (roll == 1) + mCurrentWeapon = "attack2"; + else + mCurrentWeapon = "attack3"; mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_All, true, @@ -1308,15 +1315,6 @@ void CharacterController::determineAttackType() else mAttackType = "chop"; } - else - { - if (move[0] && !move[1]) //sideway - mCurrentWeapon = "attack2"; - else if (move[1]) //forward - mCurrentWeapon = "attack3"; - else - mCurrentWeapon = "attack1"; - } } } From da34f8bda129df330cd58acd553023bf89d7f3e5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 00:15:59 +0100 Subject: [PATCH 248/301] Fix LastHitObject --- apps/openmw/mwmechanics/actors.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index b80fa9d7c..02adf3c16 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -776,12 +776,19 @@ namespace MWMechanics { if (!paused) { + for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + { + // Reset last hit object, which is only valid for one frame + // Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation + // (below) + iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string()); + } + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) { const MWWorld::Class &cls = MWWorld::Class::get(iter->first); CreatureStats &stats = cls.getCreatureStats(iter->first); - stats.setLastHitObject(std::string()); if(!stats.isDead()) { if(iter->second->isDead()) From 305a471c5ae72a68269c7dd104699587914e8bce Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 01:17:51 +0100 Subject: [PATCH 249/301] Fix VisController in ascended sleeper's death animation --- components/nifogre/ogrenifloader.cpp | 38 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index d036844fc..9fca4d2fb 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -311,25 +311,26 @@ public: static void setVisible(Ogre::Node *node, int vis) { + // Skinned meshes are attached to the scene node, not the bone. + // We use the Node's user data to connect it with the mesh. + Ogre::Any customData = node->getUserObjectBindings().getUserAny(); + + if (!customData.isEmpty()) + Ogre::any_cast(customData)->setVisible(vis); + + Ogre::TagPoint *tag = dynamic_cast(node); + if(tag != NULL) + { + Ogre::MovableObject *obj = tag->getChildObject(); + if(obj != NULL) + obj->setVisible(vis); + } + Ogre::Node::ChildNodeIterator iter = node->getChildIterator(); while(iter.hasMoreElements()) { node = iter.getNext(); setVisible(node, vis); - - // Skinned meshes and particle systems are attached to the scene node, not the bone. - // We use the Node's user data to connect it with the mesh / particle system. - Ogre::Any customData = node->getUserObjectBindings().getUserAny(); - if (!customData.isEmpty()) - Ogre::any_cast(customData)->setVisible(vis); - - Ogre::TagPoint *tag = dynamic_cast(node); - if(tag != NULL) - { - Ogre::MovableObject *obj = tag->getChildObject(); - if(obj != NULL) - obj->setVisible(vis); - } } } @@ -622,15 +623,14 @@ class NIFObjectLoader scene->mEntities.push_back(entity); if(scene->mSkelBase) { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); + Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(entity))); + if(entity->hasSkeleton()) entity->shareSkeletonInstanceWith(scene->mSkelBase); else - { - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); - Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); - trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(entity))); scene->mSkelBase->attachObjectToBone(trgtbone->getName(), entity); - } } Nif::ControllerPtr ctrl = node->controller; From 60bbab52fecf580bf18db0c173de848a6d829c85 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 03:23:47 +0100 Subject: [PATCH 250/301] Support keyframe controllers for bones that aren't in the skeleton base --- apps/openmw/mwrender/animation.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3f45ce769..ba729e3da 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -532,12 +532,6 @@ static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bo bone->setScale(Ogre::Vector3::UNIT_SCALE); } } - else - { - // No matching bone in the source. Make sure it stays properly offset - // from its parent. - bone->resetToInitialState(); - } Ogre::Node::ChildNodeIterator boneiter = bone->getChildIterator(); while(boneiter.hasMoreElements()) From 5b300c1052d541bf05bde80319f1361c694a4b00 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 03:24:09 +0100 Subject: [PATCH 251/301] Handle controllers for the root node. Fixes #1147 (incorrect dwemer crossbow rotation) --- components/nifogre/ogrenifloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 9fca4d2fb..644a6df6b 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -1034,7 +1034,7 @@ class NIFObjectLoader e = e->extra; } - if(!node->controller.empty() && (node->parent || node->recType != Nif::RC_NiNode)) + if(!node->controller.empty()) createNodeControllers(name, node->controller, scene, animflags); if(node->recType == Nif::RC_NiCamera) From 4ec86d1c68accd42d0c24b23ee7d50fbafba4c73 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 03:28:18 +0100 Subject: [PATCH 252/301] Bug #1147: Fix the inventory preview pose used for ranged weapons --- apps/openmw/mwrender/characterpreview.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 32145928e..08749fee7 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -160,11 +160,14 @@ namespace MWRender if(type == ESM::Weapon::ShortBladeOneHand || type == ESM::Weapon::LongBladeOneHand || type == ESM::Weapon::BluntOneHand || - type == ESM::Weapon::AxeOneHand) + type == ESM::Weapon::AxeOneHand || + type == ESM::Weapon::MarksmanThrown) groupname = "inventoryweapononehand"; else if(type == ESM::Weapon::LongBladeTwoHand || type == ESM::Weapon::BluntTwoClose || - type == ESM::Weapon::AxeTwoHand) + type == ESM::Weapon::AxeTwoHand || + type == ESM::Weapon::MarksmanCrossbow || + type == ESM::Weapon::MarksmanBow) groupname = "inventoryweapontwohand"; else if(type == ESM::Weapon::BluntTwoWide || type == ESM::Weapon::SpearTwoWide) From bbd15b185d5df067e1a64429f977392642b02ca9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 05:59:58 +0100 Subject: [PATCH 253/301] Skip inactive controllers. Fixes those bloody bone boots again. --- components/nif/node.hpp | 3 + components/nifogre/mesh.cpp | 2 +- components/nifogre/ogrenifloader.cpp | 105 +++++++++++++++------------ components/nifogre/skeleton.cpp | 2 +- 4 files changed, 62 insertions(+), 50 deletions(-) diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 6816a79a2..eebcd8be8 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -140,6 +140,9 @@ struct NiNode : Node ParticleFlag_AutoPlay = 0x0020, ParticleFlag_LocalSpace = 0x0080 }; + enum ControllerFlags { + ControllerFlag_Active = 0x8 + }; void read(NIFStream *nif) { diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index 80e377a49..43622cb9a 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -341,7 +341,7 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape Nif::ControllerPtr ctrl = shape->controller; do { // Load GeomMorpherController into an Ogre::Pose and Animation - if(ctrl->recType == Nif::RC_NiGeomMorpherController) + if(ctrl->recType == Nif::RC_NiGeomMorpherController && ctrl->flags & Nif::NiNode::ControllerFlag_Active) { const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 644a6df6b..4f755a85a 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -636,36 +636,39 @@ class NIFObjectLoader Nif::ControllerPtr ctrl = node->controller; while(!ctrl.empty()) { - if(ctrl->recType == Nif::RC_NiUVController) + if (ctrl->flags & Nif::NiNode::ControllerFlag_Active) { - const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); + if(ctrl->recType == Nif::RC_NiUVController) + { + const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); - Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? - Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(entity, uv->data.getPtr(), &scene->mMaterialControllerMgr)); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(entity, uv->data.getPtr(), &scene->mMaterialControllerMgr)); - UVController::Function* function = OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); - scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); - Ogre::ControllerFunctionRealPtr func(function); + UVController::Function* function = OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); - scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); - } - else if(ctrl->recType == Nif::RC_NiGeomMorpherController) - { - const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + else if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); - Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? - Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value( - entity, geom->data.getPtr(), geom->recIndex)); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value( + entity, geom->data.getPtr(), geom->recIndex)); - GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); - scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); - Ogre::ControllerFunctionRealPtr func(function); + GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); - scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } } ctrl = ctrl->next; } @@ -852,7 +855,7 @@ class NIFObjectLoader Nif::ControllerPtr ctrl = partnode->controller; while(!ctrl.empty()) { - if(ctrl->recType == Nif::RC_NiParticleSystemController) + if(ctrl->recType == Nif::RC_NiParticleSystemController && ctrl->flags & Nif::NiNode::ControllerFlag_Active) { const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); @@ -893,42 +896,45 @@ class NIFObjectLoader static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectScenePtr scene, int animflags) { do { - if(ctrl->recType == Nif::RC_NiVisController) + if (ctrl->flags & Nif::NiNode::ControllerFlag_Active) { - const Nif::NiVisController *vis = static_cast(ctrl.getPtr()); - - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); - Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); - Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? - Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); - - VisController::Function* function = OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); - scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); - Ogre::ControllerFunctionRealPtr func(function); - - scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); - } - else if(ctrl->recType == Nif::RC_NiKeyframeController) - { - const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if(!key->data.empty()) + if(ctrl->recType == Nif::RC_NiVisController) { + const Nif::NiVisController *vis = static_cast(ctrl.getPtr()); + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); - // The keyframe controller will control this bone manually - trgtbone->setManuallyControlled(true); Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); - KeyframeController::Function* function = OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); + + VisController::Function* function = OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); Ogre::ControllerFunctionRealPtr func(function); scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } + else if(ctrl->recType == Nif::RC_NiKeyframeController) + { + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + if(!key->data.empty()) + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); + Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + // The keyframe controller will control this bone manually + trgtbone->setManuallyControlled(true); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + KeyframeController::Function* function = OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); + + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + } } ctrl = ctrl->next; } while(!ctrl.empty()); @@ -1151,6 +1157,9 @@ public: continue; } + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index e01ae22ef..c0482cf5e 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -97,7 +97,7 @@ bool NIFSkeletonLoader::needSkeleton(const Nif::Node *node) { Nif::ControllerPtr ctrl = node->controller; do { - if(ctrl->recType == Nif::RC_NiKeyframeController) + if(ctrl->recType == Nif::RC_NiKeyframeController && ctrl->flags & Nif::NiNode::ControllerFlag_Active) return true; } while(!(ctrl=ctrl->next).empty()); } From dd674566a2aa85b4ed62dc4438777a97868e7ec8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 31 Jan 2014 13:25:32 +0100 Subject: [PATCH 254/301] 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 f89a6bce0..546d6538c 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 006e4bd22..c97867d35 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 77fdc971d..154a2d1e7 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 0c4226f9b..2d5a9bbd3 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 0a1728740..3bdefb1ec 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 46f49df78..b2089fa7a 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 384bd71b1..67bfe4900 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 8b70382d0..1212619d0 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 f37a537c5..854d1f1ae 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 000000000..5dcf17733 --- /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 000000000..1ecf2b46e --- /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 000000000..4d8cbc622 --- /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 000000000..3cfffbccc --- /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 6aa820599..be00f3ef6 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 255/301] changed implementation functions for container serialisation from free functions to member functions (will need some polymorphism later) --- apps/openmw/mwworld/containerstore.cpp | 59 +++++++++++++------------- apps/openmw/mwworld/containerstore.hpp | 11 +++++ 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 2d5a9bbd3..7d12c7b6e 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) - { - if (!MWWorld::LiveCellRef::checkState (state)) - return; // not valid anymore with current content files -> skip +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); + const T *record = MWBase::Environment::get().getWorld()->getStore(). + get().search (state.mRef.mRefID); - if (!record) - return; + if (!record) + return; - MWWorld::LiveCellRef ref (record); - ref.load (state); - ref.mRef.mRefNum.mContentFile = -1; - collection.mList.push_back (ref); - } + 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 MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::ObjectState& state) + const +{ + ref.save (state); +} - template - void storeStates (const MWWorld::CellRefList& collection, - std::vector > >& states) +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) { - 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 3bdefb1ec..5e305d408 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 256/301] 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 7d12c7b6e..86cf3bbde 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 5e305d408..2e01eb856 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 82b827e75..93573b401 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 257/301] 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 a97268318..1174f1bd2 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 d518d0056..484afdaf5 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 f93a3e342..7c0f0b6ea 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 497d0ced8..237746de8 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 154a2d1e7..42c954afb 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 2e01eb856..68bad4b9b 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 854d1f1ae..f2b16d4d5 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 000000000..43cde3025 --- /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 000000000..f7f9b8038 --- /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 000000000..c452611a0 --- /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 000000000..ceb18b88b --- /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 bd618457e..0d70ee090 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; From 49e26415be5838e6d08198979ed1cc64e6f740e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 22:20:46 +0100 Subject: [PATCH 258/301] Don't call loadGame() from within the MWMechanics::Actors update sequence --- apps/openmw/engine.cpp | 6 ++--- apps/openmw/mwmechanics/character.cpp | 1 - apps/openmw/mwstate/statemanagerimp.cpp | 36 +++++++++++++------------ 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 874fad267..bf1cca5e0 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -91,6 +91,9 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); + // update game state + MWBase::Environment::get().getStateManager()->update (frametime); + if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) { @@ -110,9 +113,6 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (!paused) MWBase::Environment::get().getWorld()->advanceTime( frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); - - // update game state - MWBase::Environment::get().getStateManager()->update (frametime); } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 15bb137fc..d58dee9ad 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1244,7 +1244,6 @@ bool CharacterController::kill() //player's death animation is over if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) ) { - MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); MWBase::Environment::get().getStateManager()->askLoadRecent(); } return false; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index fb2d10bbf..f68a01bf4 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -111,23 +111,6 @@ void MWState::StateManager::askLoadRecent() 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 @@ -340,4 +323,23 @@ MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() void MWState::StateManager::update (float duration) { mTimePlayed += duration; + + // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. + if (mAskLoadRecent) + { + 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); + } + } } From 37ef8ec908bdf0e31aafb1236f169bfe31db40c2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 22:30:25 +0100 Subject: [PATCH 259/301] Savegame dialog: support loading saves using Enter key or double-click --- apps/openmw/mwgui/savegamedialog.cpp | 8 ++++++++ apps/openmw/mwgui/savegamedialog.hpp | 1 + 2 files changed, 9 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 91993b0be..17aaa1189 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -34,7 +34,13 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); + mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); + } + void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos) + { + onSlotSelected(sender, pos); + onOkButtonClicked(mOkButton); } void SaveGameDialog::open() @@ -103,6 +109,8 @@ namespace MWGui void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) { + MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL); + // Get the selected slot, if any unsigned int i=0; const MWState::Slot* slot = NULL; diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 2a188061c..bf6fab73a 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -24,6 +24,7 @@ namespace MWGui void onOkButtonClicked (MyGUI::Widget* sender); void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); void onSlotSelected (MyGUI::ListBox* sender, size_t pos); + void onSlotActivated (MyGUI::ListBox* sender, size_t pos); void fillSaveList(); From a988a0d6dcdb6a5ef853a4fb7bf756bee397690c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 23:07:52 +0100 Subject: [PATCH 260/301] Don't add duplicate topic responses to the journal --- apps/openmw/mwdialogue/topic.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index 0e546f43b..f7df305c7 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -24,6 +24,13 @@ namespace MWDialogue if (entry.mTopic!=mTopic) throw std::runtime_error ("topic does not match: " + mTopic); + // bail out if we already have heard this + for (Topic::TEntryIter it = mEntries.begin(); it != mEntries.end(); ++it) + { + if (it->mInfoId == entry.mInfoId) + return; + } + mEntries.push_back (entry); // we want slicing here } From f89b3cac020c8bd45b6b952d9073212513ed210e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 23:53:50 +0100 Subject: [PATCH 261/301] Feature #764: Store the actor that gave the dialog response --- apps/openmw/mwbase/journal.hpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 4 ++-- apps/openmw/mwdialogue/journalentry.cpp | 5 +++-- apps/openmw/mwdialogue/journalentry.hpp | 1 + apps/openmw/mwdialogue/journalimp.cpp | 6 ++++-- apps/openmw/mwdialogue/journalimp.hpp | 2 +- apps/openmw/mwgui/journalviewmodel.cpp | 6 +----- components/esm/journalentry.cpp | 6 +++++- components/esm/journalentry.hpp | 1 + 9 files changed, 19 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index 81b4ba0b4..56d9601fc 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -54,7 +54,7 @@ namespace MWBase virtual int getJournalIndex (const std::string& id) const = 0; ///< Get the journal index. - virtual void addTopic (const std::string& topicId, const std::string& infoId) = 0; + virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) = 0; virtual TEntryIter begin() const = 0; ///< Iterator pointing to the begin of the main journal. diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 7fbebb9d7..845c3c07b 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -286,7 +286,7 @@ namespace MWDialogue MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title); - MWBase::Environment::get().getJournal()->addTopic (topic, info->mId); + MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor.getClass().getName(mActor)); executeScript (info->mResultScript); @@ -451,7 +451,7 @@ namespace MWDialogue MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext)); - MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); + MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor.getClass().getName(mActor)); executeScript (info->mResultScript); } } diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 7828d18ad..9463e4c45 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -32,7 +32,7 @@ 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) {} + Entry::Entry (const ESM::JournalEntry& record) : mInfoId (record.mInfo), mText (record.mText), mActorName(record.mActorName) {} std::string Entry::getText() const { @@ -43,6 +43,7 @@ namespace MWDialogue { entry.mInfo = mInfoId; entry.mText = mText; + entry.mActorName = mActorName; } @@ -53,7 +54,7 @@ namespace MWDialogue {} JournalEntry::JournalEntry (const ESM::JournalEntry& record) - : Entry (record), mTopic (record.mTopic) + : Entry (record), mTopic (record.mTopic) {} void JournalEntry::write (ESM::JournalEntry& entry) const diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 18d022aab..a77ba4f7c 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -15,6 +15,7 @@ namespace MWDialogue { std::string mInfoId; std::string mText; + std::string mActorName; // optional Entry(); diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index f24a93356..26383b3a7 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -103,11 +103,13 @@ namespace MWDialogue quest.setIndex (index); } - void Journal::addTopic (const std::string& topicId, const std::string& infoId) + void Journal::addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) { Topic& topic = getTopic (topicId); - topic.addEntry (JournalEntry (topicId, infoId)); + JournalEntry entry(topicId, infoId); + entry.mActorName = actorName; + topic.addEntry (entry); } int Journal::getJournalIndex (const std::string& id) const diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index 86091a12d..1b4803ba2 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -38,7 +38,7 @@ namespace MWDialogue virtual int getJournalIndex (const std::string& id) const; ///< Get the journal index. - virtual void addTopic (const std::string& topicId, const std::string& infoId); + virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName); virtual TEntryIter begin() const; ///< Iterator pointing to the begin of the main journal. diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 3464f283d..a0d67b025 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -311,8 +311,6 @@ struct JournalViewModelImpl : JournalViewModel { MWDialogue::Topic const & mTopic; - mutable std::string source_buffer; - TopicEntryImpl (JournalViewModelImpl const * model, MWDialogue::Topic const & topic, iterator_t itr) : BaseEntry (model, itr), mTopic (topic) {} @@ -324,9 +322,7 @@ struct JournalViewModelImpl : JournalViewModel Utf8Span source () const { - if (source_buffer.empty ()) - source_buffer = "someone"; - return toUtf8Span (source_buffer); + return toUtf8Span (itr->mActorName); } }; diff --git a/components/esm/journalentry.cpp b/components/esm/journalentry.cpp index 514bf3597..445213de4 100644 --- a/components/esm/journalentry.cpp +++ b/components/esm/journalentry.cpp @@ -17,6 +17,8 @@ void ESM::JournalEntry::load (ESMReader &esm) esm.getHNT (mMonth, "JEMO"); esm.getHNT (mDayOfMonth, "JEDM"); } + else if (mType==Type_Topic) + mActorName = esm.getHNOString("ACT_"); } void ESM::JournalEntry::save (ESMWriter &esm) const @@ -32,4 +34,6 @@ void ESM::JournalEntry::save (ESMWriter &esm) const esm.writeHNT ("JEMO", mMonth); esm.writeHNT ("JEDM", mDayOfMonth); } -} \ No newline at end of file + else if (mType==Type_Topic) + esm.writeHNString ("ACT_", mActorName); +} diff --git a/components/esm/journalentry.hpp b/components/esm/journalentry.hpp index 94808dde6..76901a4b6 100644 --- a/components/esm/journalentry.hpp +++ b/components/esm/journalentry.hpp @@ -23,6 +23,7 @@ namespace ESM std::string mTopic; std::string mInfo; std::string mText; + std::string mActorName; // Could also be Actor ID to allow switching of localisation, but since mText is plaintext anyway... int mDay; // time stamp int mMonth; int mDayOfMonth; From dea9d21db65fdd4edb1e25e58b796ced5f45b798 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 15:18:31 +0100 Subject: [PATCH 262/301] Some additional safety checks for global map loading --- apps/openmw/mwrender/globalmap.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 6fbcfdc6b..4a87d9f1a 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -265,6 +265,10 @@ namespace MWRender if (bounds.mMaxY-bounds.mMinY <= 0) return; + if (bounds.mMinX > bounds.mMaxX + || bounds.mMinY > bounds.mMaxY) + throw std::runtime_error("invalid map bounds"); + Ogre::Image image; Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&map.mImageData[0], map.mImageData.size())); image.load(stream, "png"); @@ -297,9 +301,15 @@ namespace MWRender // 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; + // Completely off-screen? -> no need to blit anything + if (bounds.mMaxX < mMinX + || bounds.mMaxY < mMinY + || bounds.mMinX > mMaxX + || bounds.mMinY > mMaxY) + return; + int leftDiff = (mMinX - bounds.mMinX); int topDiff = (bounds.mMaxY - mMaxY); int rightDiff = (bounds.mMaxX - mMaxX); From 63284d21a0bd58297321786bde2a39e5af9f1726 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 15:22:31 +0100 Subject: [PATCH 263/301] Savegame dialog: 'Enter' while editing a name is equivalent to pressing the ok button --- apps/openmw/mwgui/savegamedialog.cpp | 6 ++++++ apps/openmw/mwgui/savegamedialog.hpp | 1 + 2 files changed, 7 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 17aaa1189..da0417e7b 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -35,6 +35,7 @@ namespace MWGui mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); + mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept); } void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos) @@ -43,6 +44,11 @@ namespace MWGui onOkButtonClicked(mOkButton); } + void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender) + { + onOkButtonClicked(mOkButton); + } + void SaveGameDialog::open() { WindowModal::open(); diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index bf6fab73a..6743b00b9 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -25,6 +25,7 @@ namespace MWGui void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); void onSlotSelected (MyGUI::ListBox* sender, size_t pos); void onSlotActivated (MyGUI::ListBox* sender, size_t pos); + void onEditSelectAccept (MyGUI::EditBox* sender); void fillSaveList(); From 6d37cd7e86fcf8bcbdac2ca970b7e5915ec7ada8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 15:28:05 +0100 Subject: [PATCH 264/301] Savegame dialog: Don't allow empty save names --- apps/openmw/mwgui/savegamedialog.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index da0417e7b..d7c5c3a94 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -132,6 +132,11 @@ namespace MWGui if (mSaving) { + if (mSaveNameEdit->getCaption().empty()) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage65}"); + return; + } MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), slot); } else From ec46575671fbe4fb69b572cb63b93b5102f0d494 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 15:43:26 +0100 Subject: [PATCH 265/301] Overwriting saves fixes - copy description of overwritten slot, ask for confirmation --- apps/openmw/mwgui/savegamedialog.cpp | 39 +++++++++++++++++++++++++--- apps/openmw/mwgui/savegamedialog.hpp | 7 +++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index d7c5c3a94..731795a82 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -15,6 +15,8 @@ #include "../mwstate/character.hpp" +#include "confirmationdialog.hpp" + namespace MWGui { SaveGameDialog::SaveGameDialog() @@ -36,17 +38,25 @@ namespace MWGui mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept); + mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged); } void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos) { onSlotSelected(sender, pos); - onOkButtonClicked(mOkButton); + accept(); + } + + void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender) + { + // This might have previously been a save slot from the list. If so, that is no longer the case + mSaveList->setIndexSelected(MyGUI::ITEM_NONE); + onSlotSelected(mSaveList, MyGUI::ITEM_NONE); } void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender) { - onOkButtonClicked(mOkButton); + accept(); } void SaveGameDialog::open() @@ -113,7 +123,12 @@ namespace MWGui setVisible(false); } - void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) + void SaveGameDialog::onConfirmationGiven() + { + accept(true); + } + + void SaveGameDialog::accept(bool reallySure) { MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL); @@ -132,6 +147,16 @@ namespace MWGui if (mSaving) { + // If overwriting an existing slot, ask for confirmation first + if (slot != NULL && !reallySure) + { + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); + dialog->open("#{sMessage4}"); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationGiven); + dialog->eventCancelClicked.clear(); + return; + } if (mSaveNameEdit->getCaption().empty()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage65}"); @@ -154,6 +179,11 @@ namespace MWGui } } + void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) + { + accept(); + } + void SaveGameDialog::onCharacterSelected(MyGUI::ComboBox *sender, size_t pos) { MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); @@ -192,6 +222,9 @@ namespace MWGui return; } + if (mSaving) + mSaveNameEdit->setCaption(sender->getItemNameAt(pos)); + const MWState::Slot* slot = NULL; unsigned int i=0; for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i) diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 6743b00b9..8d09a1cbc 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -20,17 +20,20 @@ namespace MWGui void setLoadOrSave(bool load); + private: 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 onSlotActivated (MyGUI::ListBox* sender, size_t pos); void onEditSelectAccept (MyGUI::EditBox* sender); + void onSaveNameChanged (MyGUI::EditBox* sender); + void onConfirmationGiven(); - void fillSaveList(); + void accept(bool reallySure=false); + void fillSaveList(); - private: MyGUI::ImageBox* mScreenshot; bool mSaving; From 1deb0a7cdfab0348a84ddcaf504343fca3ba01ce Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 17:26:18 +0100 Subject: [PATCH 266/301] Savegame dialog: Set key focus to editbox --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwgui/messagebox.cpp | 2 +- apps/openmw/mwgui/savegamedialog.cpp | 4 +++- apps/openmw/mwgui/windowmanagerimp.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 2 -- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index fa0fe888b..069e6311a 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -281,6 +281,7 @@ namespace MWBase virtual const Translation::Storage& getTranslationDataStorage() const = 0; + /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; virtual Loading::Listener* getLoadingScreen() = 0; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 858378c03..7e7ad5ec2 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -235,7 +235,7 @@ namespace MWGui mItemView->setModel (mSortModel); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); // Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last // or we end up using a possibly invalid model. diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 74231c008..b52aee706 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -332,7 +332,7 @@ namespace MWGui { if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok)) { - MyGUI::InputManager::getInstance().setKeyFocusWidget(*button); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(*button); (*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed); break; } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 731795a82..77ad98121 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -64,6 +64,8 @@ namespace MWGui WindowModal::open(); mSaveNameEdit->setCaption (""); + if (mSaving) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit); center(); @@ -130,7 +132,7 @@ namespace MWGui void SaveGameDialog::accept(bool reallySure) { - MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); // Get the selected slot, if any unsigned int i=0; diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index bc440d818..db52d9f79 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -105,6 +105,7 @@ namespace MWGui */ virtual void update(); + /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. virtual void setKeyFocusWidget (MyGUI::Widget* widget); virtual void setNewGame(bool newgame); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9f1ccb2df..5385ab388 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -251,8 +251,6 @@ namespace MWInput mInputManager->capture(loading); // inject some fake mouse movement to force updating MyGUI's widget states - // this shouldn't do any harm since we're moving back to the original position afterwards - MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX+1), int(mMouseY+1), mMouseWheel); MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); // update values of channels (as a result of pressed keys) From e3d85af70ac4a3515470351beb9064c7c435ba3e Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sun, 2 Feb 2014 13:57:19 +0100 Subject: [PATCH 267/301] Fix an issue with int32_t being unknown on windows. --- apps/openmw/mwgui/mapwindow.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 2f5ded012..a07adc1f3 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -2,6 +2,7 @@ #define MWGUI_MAPWINDOW_H #include "windowpinnablebase.hpp" +#include namespace MWRender { From b85a4dd35e595ec0dd29f5354348ae95244ff2f9 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 2 Feb 2014 15:01:49 +0200 Subject: [PATCH 268/301] pos accum without conformity with animation bug/creature speed --- apps/openmw/mwmechanics/character.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 252271d32..eff48ca08 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -301,8 +301,9 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(!mCurrentMovement.empty()) { float vel, speedmult = 1.0f; - if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) + if(mPtr.getClass().isNpc() && mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) speedmult = mMovementSpeed / vel; + mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); } @@ -1120,7 +1121,13 @@ void CharacterController::update(float duration) else //avoid z-rotating for knockdown world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true); - world->queueMovement(mPtr, vec); + // all actual movement in 3rd person controlled by animations, except for jump + // !mAnimation->hasAnimation("death1") identifies 1st person mode + if(mJumpState != JumpState_None || vec.z > 0 + || (mPtr.getRefData().getHandle() == "player" && !mAnimation->hasAnimation("death1"))) + { + world->queueMovement(mPtr, vec); + } } movement = vec; From 761f13d3cef95cf2dcdba32b0da3b047157e9aa2 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 2 Feb 2014 16:29:51 +0200 Subject: [PATCH 269/301] activate whole-body attack animations --- apps/openmw/mwmechanics/character.cpp | 23 +++++++++++++++++++++++ apps/openmw/mwrender/animation.cpp | 5 ++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index eff48ca08..6c1a40249 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -781,6 +781,13 @@ bool CharacterController::updateWeaponState() stop = mAttackType+" max attack"; mUpperBodyState = UpperCharState_MinAttackToMaxAttack; break; + case UpperCharState_MinAttackToMaxAttack: + //hack to avoid body pos desync when jumping/sneaking in 'max attack' state + if(!mAnimation->isPlaying(mCurrentWeapon)) + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + 0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0); + break; case UpperCharState_MaxAttackToMinHit: if(mAttackType == "shoot") { @@ -830,6 +837,22 @@ bool CharacterController::updateWeaponState() } } + //if playing combat animation and lowerbody is not busy switch to whole body animation + if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) + { + if( mMovementState != CharState_None || + mJumpState != JumpState_None || + mHitState != CharState_None || + MWBase::Environment::get().getWorld()->isSwimming(mPtr) || + cls.getCreatureStats(mPtr).getMovementFlag(CreatureStats::Flag_Sneak)) + { + mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody); + } + else + { + mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All); + } + } MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3f45ce769..c62515c8c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1050,9 +1050,8 @@ bool Animation::allowSwitchViewMode() const { for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter) { - if(stateiter->second.mGroups == Group_UpperBody - || (stateiter->first.size()==4 && stateiter->first.find("hit") != std::string::npos) - || (stateiter->first.find("knock") != std::string::npos) ) + if(stateiter->second.mPriority > MWMechanics::Priority_Movement + && stateiter->second.mPriority < MWMechanics::Priority_Torch) return false; } return true; From 4e03e9cf87998129c432e833a186b77b863dcfbd Mon Sep 17 00:00:00 2001 From: pvdk Date: Sun, 2 Feb 2014 20:10:47 +0100 Subject: [PATCH 270/301] Changed development version info text and the tooltip now works on all platforms --- apps/launcher/maindialog.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 67665adf0..56b3186ff 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -79,15 +79,14 @@ Launcher::MainDialog::MainDialog(QWidget *parent) if (revision == tag) { versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); } else { - versionLabel->setText(tr("OpenMW unstable, revision %0").arg(revision.left(10))); + versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10))); } // Add the compile date and time - QDate date(QDate::fromString(__DATE__, QLatin1String("MMM dd yyyy"))); - QTime time(QTime::fromString(__TIME__, QLatin1String("hh:m:ss"))); - - versionLabel->setToolTip(tr("Compiled on %0 %1").arg(date.toString(Qt::SystemLocaleShortDate), - time.toString(Qt::SystemLocaleShortDate))); + versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), + QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), + QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), + QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); createIcons(); } From e50a393de485f2425ed15fb82c2cc317090c37a4 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sun, 2 Feb 2014 23:59:57 +0100 Subject: [PATCH 271/301] Fix linking issues on Windows using MSVC. --- apps/openmw/mwworld/livecellref.hpp | 2 +- apps/openmw/mwworld/refdata.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index b2089fa7a..b2e4d6d56 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -9,7 +9,7 @@ namespace ESM { - class ObjectState; + struct ObjectState; } namespace MWWorld diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index d9f5697bd..19e3d4882 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -14,7 +14,7 @@ namespace ESM { class Script; class CellRef; - class ObjectState; + struct ObjectState; } namespace MWWorld From 21f502df349107029273300ba5db4ecdc5722b62 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 3 Feb 2014 11:20:55 +0100 Subject: [PATCH 272/301] compatibility fix --- apps/openmw/mwgui/mapwindow.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index a07adc1f3..1e52ff26a 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -1,8 +1,9 @@ #ifndef MWGUI_MAPWINDOW_H #define MWGUI_MAPWINDOW_H +#include + #include "windowpinnablebase.hpp" -#include namespace MWRender { From 688f359a333420753dd5fc1a6341b55d8edb7519 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 3 Feb 2014 23:09:26 +0200 Subject: [PATCH 273/301] discard creatures speed and negative fatique changes --- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6c1a40249..7647a940e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -301,7 +301,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(!mCurrentMovement.empty()) { float vel, speedmult = 1.0f; - if(mPtr.getClass().isNpc() && mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) + if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) speedmult = mMovementSpeed / vel; mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 943c13a18..30db59311 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -209,10 +209,7 @@ namespace MWMechanics mDynamic[index] = value; if (index == 2 && value.getCurrent() < 0) - { setKnockedDown(true); - mDynamic[2].setCurrent(0); - } if (index==0 && mDynamic[index].getCurrent()<1) { From df78357e0512e05c444ac41f98b7f3118e5c4bf9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 2 Feb 2014 03:24:40 +0100 Subject: [PATCH 274/301] Handle knockout separately (Closes #1151) --- apps/openmw/mwmechanics/character.cpp | 17 +++++++++++++++-- apps/openmw/mwmechanics/character.hpp | 1 + apps/openmw/mwmechanics/creaturestats.cpp | 3 --- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d58dee9ad..2f234db7b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -157,7 +157,14 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock(); if(mHitState == CharState_None) { - if(knockdown) + if (mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0) + { + mHitState = CharState_KnockOut; + mCurrentHit = "knockout"; + mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, false, 1, "start", "stop", 0.0f, ~0ul); + mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true); + } + else if(knockdown) { mHitState = CharState_KnockDown; mCurrentHit = "knockdown"; @@ -187,6 +194,12 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mPtr.getClass().getCreatureStats(mPtr).setBlock(false); mHitState = CharState_None; } + else if (mHitState == CharState_KnockOut && mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() > 0) + { + mHitState = CharState_KnockDown; + mAnimation->disable(mCurrentHit); + mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "loop stop", "stop", 0.0f, 0); + } } const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); @@ -1121,7 +1134,7 @@ void CharacterController::update(float duration) if (!mSkipAnim) { rot *= Ogre::Math::RadiansToDegrees(1.0f); - if(mHitState != CharState_KnockDown) + if(mHitState != CharState_KnockDown && mHitState != CharState_KnockOut) { world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 8b6dc6c94..0c022c462 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -93,6 +93,7 @@ enum CharacterState { CharState_Hit, CharState_KnockDown, + CharState_KnockOut, CharState_Block }; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index c862c0ab4..8f890befb 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -179,9 +179,6 @@ namespace MWMechanics mDynamic[index] = value; - if (index == 2 && value.getCurrent() < 0) - setKnockedDown(true); - if (index==0 && mDynamic[index].getCurrent()<1) { if (!mDead) From cf3812188f73984c0e7c53a829c440e8da6717bb Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Feb 2014 20:56:17 +0100 Subject: [PATCH 275/301] Fix bounding box assertions due to negative particle life time Other parts of the code could not deal with negative life times and produced negative particle sizes as a result (which Ogre could not handle) --- components/nifogre/ogrenifloader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 4f755a85a..adf379af0 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -749,8 +749,8 @@ class NIFObjectLoader emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f, partctrl->velocity + partctrl->velocityRandom*0.5f); emitter->setEmissionRate(partctrl->emitRate); - emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f, - partctrl->lifetime + partctrl->lifetimeRandom*0.5f); + emitter->setTimeToLive(partctrl->lifetime, + partctrl->lifetime + partctrl->lifetimeRandom); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z)); From 2c0d46525b74672b3f68a44abddc2efb22b67b49 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Feb 2014 20:57:46 +0100 Subject: [PATCH 276/301] Fast-forward particle systems to make cell loads less obvious. --- components/nifogre/ogrenifloader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index adf379af0..43e8934c8 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -882,6 +882,9 @@ class NIFObjectLoader Ogre::ControllerFunctionRealPtr func(function); scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + + if (partflags&Nif::NiNode::ParticleFlag_AutoPlay) + partsys->fastForward(1, 0.1); } ctrl = ctrl->next; } From 5b076aa570bd1e28838b6045201bb5d83aadbdc9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Feb 2014 21:04:30 +0100 Subject: [PATCH 277/301] Revert "Bug #1074: Inventory paperdoll obscures armour rating" This reverts commit b017a3be3e4e88d8aea6150287893f3677d6fb69. --- apps/openmw/mwrender/characterpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 08749fee7..f46389850 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -128,7 +128,7 @@ namespace MWRender InventoryPreview::InventoryPreview(MWWorld::Ptr character) - : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 62, -200), Ogre::Vector3(0, 62, 0)) + : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0)) , mSelectionBuffer(NULL) { } From 8aed4fcfa408c45ba99b91cabc0f53f550470605 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Feb 2014 22:24:03 +0100 Subject: [PATCH 278/301] Correction for marksman weapon inventory preview --- apps/openmw/mwrender/characterpreview.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index f46389850..280828652 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -161,13 +161,13 @@ namespace MWRender type == ESM::Weapon::LongBladeOneHand || type == ESM::Weapon::BluntOneHand || type == ESM::Weapon::AxeOneHand || - type == ESM::Weapon::MarksmanThrown) + type == ESM::Weapon::MarksmanThrown || + type == ESM::Weapon::MarksmanCrossbow || + type == ESM::Weapon::MarksmanBow) groupname = "inventoryweapononehand"; else if(type == ESM::Weapon::LongBladeTwoHand || type == ESM::Weapon::BluntTwoClose || - type == ESM::Weapon::AxeTwoHand || - type == ESM::Weapon::MarksmanCrossbow || - type == ESM::Weapon::MarksmanBow) + type == ESM::Weapon::AxeTwoHand) groupname = "inventoryweapontwohand"; else if(type == ESM::Weapon::BluntTwoWide || type == ESM::Weapon::SpearTwoWide) From f608ceeffcbaab71b50a053513e5dfd11eccf403 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Feb 2014 22:32:50 +0100 Subject: [PATCH 279/301] Fixes #1158 (Armor rating label issues) --- apps/openmw/mwgui/inventorywindow.cpp | 36 +++++++++++----------- apps/openmw/mwgui/inventorywindow.hpp | 2 -- apps/openmw/mwgui/tooltips.cpp | 2 +- files/mygui/openmw_inventory_window.layout | 7 ++--- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 7139c1b2c..2ea09db61 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -55,7 +55,7 @@ namespace MWGui getWidget(mRightPane, "RightPane"); getWidget(mArmorRating, "ArmorRating"); - mAvatar->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); + mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected); @@ -76,11 +76,12 @@ namespace MWGui void InventoryWindow::adjustPanes() { - const float aspect = 0.5; // fixed aspect ratio for the left pane - mLeftPane->setSize( (mMainWidget->getSize().height-44) * aspect, mMainWidget->getSize().height-44 ); - mRightPane->setCoord( mLeftPane->getPosition().left + (mMainWidget->getSize().height-44) * aspect + 4, + const float aspect = 0.5; // fixed aspect ratio for the avatar image + float leftPaneWidth = (mMainWidget->getSize().height-44-mArmorRating->getHeight()) * aspect; + mLeftPane->setSize( leftPaneWidth, mMainWidget->getSize().height-44 ); + mRightPane->setCoord( mLeftPane->getPosition().left + leftPaneWidth + 4, mRightPane->getPosition().top, - mMainWidget->getSize().width - 12 - (mMainWidget->getSize().height-44) * aspect - 15, + mMainWidget->getSize().width - 12 - leftPaneWidth - 15, mMainWidget->getSize().height-44 ); } @@ -418,9 +419,9 @@ namespace MWGui else { MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance ().getLastPressedPosition (MyGUI::MouseButton::Left); - MyGUI::IntPoint relPos = mousePos - mAvatar->getAbsolutePosition (); - int realX = int(float(relPos.left) / float(mAvatar->getSize().width) * 512.f ); - int realY = int(float(relPos.top) / float(mAvatar->getSize().height) * 1024.f ); + MyGUI::IntPoint relPos = mousePos - mAvatarImage->getAbsolutePosition (); + int realX = int(float(relPos.left) / float(mAvatarImage->getSize().width) * 512.f ); + int realY = int(float(relPos.top) / float(mAvatarImage->getSize().height) * 1024.f ); MWWorld::Ptr itemSelected = getAvatarSelectedItem (realX, realY); if (itemSelected.isEmpty ()) @@ -487,11 +488,18 @@ namespace MWGui if (mPreviewDirty) { mPreviewDirty = false; - MyGUI::IntSize size = mAvatar->getSize(); + MyGUI::IntSize size = mAvatarImage->getSize(); mPreview.update (size.width, size.height); - mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); + mAvatarImage->setImageTexture("CharacterPreview"); + mAvatarImage->setImageCoord(MyGUI::IntCoord(0, 0, std::min(512, size.width), std::min(1024, size.height))); + mAvatarImage->setImageTile(MyGUI::IntSize(std::min(512, size.width), std::min(1024, size.height))); + + mArmorRating->setCaptionWithReplacing ("#{sArmor}: " + + boost::lexical_cast(static_cast(MWWorld::Class::get(mPtr).getArmorRating(mPtr)))); + if (mArmorRating->getTextSize().width > mArmorRating->getSize().width) + mArmorRating->setCaptionWithReplacing (boost::lexical_cast(static_cast(MWWorld::Class::get(mPtr).getArmorRating(mPtr)))); } } @@ -502,9 +510,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->getSpellWindow()->updateSpells(); mPreviewDirty = true; - - mArmorRating->setCaptionWithReplacing ("#{sArmor}: " - + boost::lexical_cast(static_cast(MWWorld::Class::get(mPtr).getArmorRating(mPtr)))); } void InventoryWindow::pickUpObject (MWWorld::Ptr object) @@ -551,9 +556,4 @@ namespace MWGui MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, count); } - - MyGUI::IntCoord InventoryWindow::getAvatarScreenCoord () - { - return mAvatar->getAbsoluteCoord (); - } } diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 7e5a0fe10..7ef168e98 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -31,8 +31,6 @@ namespace MWGui void pickUpObject (MWWorld::Ptr object); - MyGUI::IntCoord getAvatarScreenCoord(); - MWWorld::Ptr getAvatarSelectedItem(int x, int y); void rebuildAvatar() { diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 0fe500879..8716c4dea 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -182,7 +182,7 @@ namespace MWGui } else if (type == "AvatarItemSelection") { - MyGUI::IntCoord avatarPos = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarScreenCoord (); + MyGUI::IntCoord avatarPos = focus->getAbsoluteCoord(); MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top); int realX = int(float(relMousePos.left) / float(avatarPos.width) * 512.f ); int realY = int(float(relMousePos.top) / float(avatarPos.height) * 1024.f ); diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index ba6bf820e..09e5ed9c7 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -12,11 +12,10 @@ - - - + + - + From e1e7a492e200b517b730b06546c5089741d8d64c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 00:14:06 +0100 Subject: [PATCH 280/301] Fix movement speed formula for flying creatures --- apps/openmw/mwclass/creature.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 1f07ede7e..6af8373c5 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -529,8 +529,8 @@ namespace MWClass float moveSpeed; if(normalizedEncumbrance >= 1.0f) moveSpeed = 0.0f; - else if(mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && - world->isLevitationEnabled()) + else if(isFlying(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && + world->isLevitationEnabled())) { float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); From 975f0e05b48450aa2606a179ef32217bfeef7c92 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 01:07:27 +0100 Subject: [PATCH 281/301] Fixes #1157 (attribute layout issue) --- files/mygui/openmw_stats_window.layout | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/files/mygui/openmw_stats_window.layout b/files/mygui/openmw_stats_window.layout index efec0ab37..6cdd4c02a 100644 --- a/files/mygui/openmw_stats_window.layout +++ b/files/mygui/openmw_stats_window.layout @@ -100,7 +100,9 @@ - + + + @@ -114,7 +116,7 @@ - + @@ -128,7 +130,7 @@ - + @@ -142,7 +144,7 @@ - + @@ -156,7 +158,7 @@ - + @@ -170,7 +172,7 @@ - + @@ -184,7 +186,7 @@ - + @@ -198,7 +200,7 @@ - + @@ -212,6 +214,7 @@ + From 7cf22391a5343bf82228803eeeaa756c0e3436de Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 03:46:15 +0100 Subject: [PATCH 282/301] Feature #50: Handle weapon controllers (i.e. bowstring animations, etc) --- apps/openmw/mwmechanics/character.cpp | 1 + apps/openmw/mwrender/animation.cpp | 21 +++++++++++++++++++ apps/openmw/mwrender/animation.hpp | 8 ++++++++ apps/openmw/mwrender/npcanimation.cpp | 29 ++++++++++++++++++++++++--- apps/openmw/mwrender/npcanimation.hpp | 20 ++++++++++++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2f234db7b..b73283e7c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -547,6 +547,7 @@ bool CharacterController::updateWeaponState() { getWeaponGroup(weaptype, weapgroup); mAnimation->showWeapons(false); + mAnimation->setWeaponGroup(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ba729e3da..5417f1820 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -870,6 +870,27 @@ bool Animation::getInfo(const std::string &groupname, float *complete, float *sp return true; } +float Animation::getStartTime(const std::string &groupname) const +{ + AnimSourceList::const_iterator iter(mAnimSources.begin()); + for(;iter != mAnimSources.end();iter++) + { + const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys; + NifOgre::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); + if(found != keys.end()) + return found->first; + } + return -1.f; +} + +float Animation::getCurrentTime(const std::string &groupname) const +{ + AnimStateMap::const_iterator iter = mStates.find(groupname); + if(iter == mStates.end()) + return -1.f; + + return iter->second.mTime; +} void Animation::disable(const std::string &groupname) { diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index da1c1628c..27778d373 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -271,12 +271,20 @@ public: */ bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; + /// Get the absolute position in the animation track of the first text key with the given group. + float getStartTime(const std::string &groupname) const; + + /// Get the current absolute position in the animation track for the animation that is currently playing from the given group. + float getCurrentTime(const std::string& groupname) const; + /** Disables the specified animation group; * \param groupname Animation group to disable. */ void disable(const std::string &groupname); void changeGroups(const std::string &groupname, int group); + virtual void setWeaponGroup(const std::string& group) {} + /** Retrieves the velocity (in units per second) that the animation will move. */ float getVelocity(const std::string &groupname) const; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index bcb6a374c..531d90162 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -71,6 +71,27 @@ float HeadAnimationTime::getValue() const return 1; } +float WeaponAnimationTime::getValue() const +{ + if (mWeaponGroup.empty()) + return 0; + float current = mAnimation->getCurrentTime(mWeaponGroup); + if (current == -1) + return 0; + return current - mStartTime; +} + +void WeaponAnimationTime::setGroup(const std::string &group) +{ + mWeaponGroup = group; + mStartTime = mAnimation->getStartTime(mWeaponGroup); +} + +void WeaponAnimationTime::updateStartTime() +{ + setGroup(mWeaponGroup); +} + static NpcAnimation::PartBoneMap createPartListMap() { NpcAnimation::PartBoneMap result; @@ -126,6 +147,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mNpc = mPtr.get()->mBase; mHeadAnimationTime = Ogre::SharedPtr(new HeadAnimationTime(mPtr)); + mWeaponAnimationTime = Ogre::SharedPtr(new WeaponAnimationTime(this)); for(size_t i = 0;i < ESM::PRT_Count;i++) { @@ -223,6 +245,8 @@ void NpcAnimation::updateNpcBase() for(size_t i = 0;i < ESM::PRT_Count;i++) removeIndividualPart((ESM::PartReferenceType)i); updateParts(); + + mWeaponAnimationTime->updateStartTime(); } void NpcAnimation::updateParts() @@ -588,9 +612,6 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g updateSkeletonInstance(mSkelBase->getSkeleton(), skel); } - // TODO: - // type == ESM::PRT_Weapon should get an animation source based on the current offset - // of the weapon attack animation (from its beginning, or start marker?) std::vector >::iterator ctrl(mObjectParts[type]->mControllers.begin()); for(;ctrl != mObjectParts[type]->mControllers.end();ctrl++) { @@ -600,6 +621,8 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g if (type == ESM::PRT_Head) ctrl->setSource(mHeadAnimationTime); + else if (type == ESM::PRT_Weapon) + ctrl->setSource(mWeaponAnimationTime); } } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index e86ec7d4e..1dd0b41e6 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -25,6 +25,23 @@ public: { } }; +class WeaponAnimationTime : public Ogre::ControllerValue +{ +private: + Animation* mAnimation; + std::string mWeaponGroup; + float mStartTime; +public: + WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {} + void setGroup(const std::string& group); + void updateStartTime(); + + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value) + { } +}; + + class NpcAnimation : public Animation, public MWWorld::InventoryStoreListener { public: @@ -71,6 +88,7 @@ private: Ogre::Vector3 mFirstPersonOffset; Ogre::SharedPtr mHeadAnimationTime; + Ogre::SharedPtr mWeaponAnimationTime; float mAlpha; @@ -105,6 +123,8 @@ public: ViewMode viewMode=VM_Normal); virtual ~NpcAnimation(); + virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); } + virtual Ogre::Vector3 runAnimation(float timepassed); virtual void showWeapons(bool showWeapon); From 5ee105c8120b6c864ca73d52183991fa39e6aed7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 03:55:40 +0100 Subject: [PATCH 283/301] Fix typo --- apps/openmw/mwmechanics/aicombat.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 6 +++--- apps/openmw/mwmechanics/character.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 2ff7ec229..3653587f8 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -168,7 +168,7 @@ namespace MWMechanics float rangeMelee; float rangeCloseUp; bool distantCombat = false; - if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) + if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_Thrown) { rangeMelee = 1000; // TODO: should depend on archer skill rangeCloseUp = 0; //doesn't needed when attacking from distance diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b73283e7c..9ea159e03 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -119,7 +119,7 @@ static const struct WeaponInfo { { WeapType_TwoWide, "2w", "weapontwowide" }, { WeapType_BowAndArrow, "1h", "bowandarrow" }, { WeapType_Crossbow, "crossbow", "crossbow" }, - { WeapType_ThowWeapon, "1h", "throwweapon" }, + { WeapType_Thrown, "1h", "throwweapon" }, { WeapType_PickProbe, "1h", "pickprobe" }, { WeapType_Spell, "spell", "spellcast" }, }; @@ -380,7 +380,7 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I *weaptype = WeapType_Crossbow; break; case ESM::Weapon::MarksmanThrown: - *weaptype = WeapType_ThowWeapon; + *weaptype = WeapType_Thrown; break; } } @@ -709,7 +709,7 @@ bool CharacterController::updateWeaponState() else { if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || - mWeaponType == WeapType_ThowWeapon) + mWeaponType == WeapType_Thrown) mAttackType = "shoot"; else { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 0c022c462..cf684d320 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -106,7 +106,7 @@ enum WeaponType { WeapType_TwoWide, WeapType_BowAndArrow, WeapType_Crossbow, - WeapType_ThowWeapon, + WeapType_Thrown, WeapType_PickProbe, WeapType_Spell From a07eaa0c0ddd237529b357acfd59e2bc85d4e23a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 04:00:52 +0100 Subject: [PATCH 284/301] Feature #50: Allow body pitch in third person for ranged weapon aiming --- apps/openmw/mwmechanics/character.cpp | 44 +++++++++++++++++++++++++++ apps/openmw/mwrender/animation.cpp | 1 - apps/openmw/mwrender/animation.hpp | 8 ++--- apps/openmw/mwrender/camera.cpp | 2 -- apps/openmw/mwrender/npcanimation.cpp | 18 ++++++++--- apps/openmw/mwrender/npcanimation.hpp | 5 +++ 6 files changed, 67 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9ea159e03..df6e7938f 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -773,6 +773,50 @@ bool CharacterController::updateWeaponState() } } + mAnimation->setPitchFactor(0.f); + if (mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) + { + switch (mUpperBodyState) + { + case UpperCharState_StartToMinAttack: + mAnimation->setPitchFactor(complete); + break; + case UpperCharState_MinAttackToMaxAttack: + case UpperCharState_MaxAttackToMinHit: + case UpperCharState_MinHitToHit: + mAnimation->setPitchFactor(1.f); + break; + case UpperCharState_FollowStartToFollowStop: + if (animPlaying) + mAnimation->setPitchFactor(1.f-complete); + break; + default: + break; + } + } + else if (mWeaponType == WeapType_Crossbow) + { + switch (mUpperBodyState) + { + case UpperCharState_EquipingWeap: + mAnimation->setPitchFactor(complete); + break; + case UpperCharState_UnEquipingWeap: + mAnimation->setPitchFactor(1.f-complete); + break; + case UpperCharState_WeapEquiped: + case UpperCharState_StartToMinAttack: + case UpperCharState_MinAttackToMaxAttack: + case UpperCharState_MaxAttackToMinHit: + case UpperCharState_MinHitToHit: + case UpperCharState_FollowStartToFollowStop: + mAnimation->setPitchFactor(1.f); + break; + default: + break; + } + } + if(!animPlaying) { if(mUpperBodyState == UpperCharState_EquipingWeap || diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 5417f1820..66153a7e0 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -53,7 +53,6 @@ void Animation::EffectAnimationTime::setValue(Ogre::Real) Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) : mPtr(ptr) - , mCamera(NULL) , mInsert(node) , mSkelBase(NULL) , mAccumRoot(NULL) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 27778d373..ed7ed819f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -121,7 +121,6 @@ protected: std::vector mEffects; MWWorld::Ptr mPtr; - Camera *mCamera; Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; @@ -288,6 +287,10 @@ public: /** Retrieves the velocity (in units per second) that the animation will move. */ float getVelocity(const std::string &groupname) const; + /// A relative factor (0-1) that decides if and how much the skeleton should be pitched + /// to indicate the facing orientation of the character. + virtual void setPitchFactor(float factor) {} + virtual Ogre::Vector3 runAnimation(float duration); virtual void showWeapons(bool showWeapon); @@ -297,9 +300,6 @@ public: Ogre::AxisAlignedBox getWorldBounds(); - void setCamera(Camera *cam) - { mCamera = cam; } - Ogre::Node *getNode(const std::string &name); // Attaches the given object to a bone on this object's base skeleton. If the bone doesn't diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9c8387b83..9ae9c5878 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -338,11 +338,9 @@ namespace MWRender if(mAnimation && mAnimation != anim) { mAnimation->setViewMode(NpcAnimation::VM_Normal); - mAnimation->setCamera(NULL); mAnimation->detachObjectFromBone(mCamera); } mAnimation = anim; - mAnimation->setCamera(this); processViewChange(); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 531d90162..d051bde52 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -142,7 +142,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mShowCarriedLeft(true), mFirstPersonOffset(0.f, 0.f, 0.f), mAlpha(1.f), - mNpcType(Type_Normal) + mNpcType(Type_Normal), + mPitchFactor(0) { mNpc = mPtr.get()->mBase; @@ -522,16 +523,25 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) Ogre::Vector3 ret = Animation::runAnimation(timepassed); Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); - if(mViewMode == VM_FirstPerson && mCamera) + if(mViewMode == VM_FirstPerson) { - float pitch = mCamera->getPitch(); + float pitch = mPtr.getRefData().getPosition().rot[0]; Ogre::Node *node = baseinst->getBone("Bip01 Neck"); - node->pitch(Ogre::Radian(pitch*0.75f), Ogre::Node::TS_WORLD); + node->pitch(Ogre::Radian(pitch), Ogre::Node::TS_WORLD); // This has to be done before this function ends; // updateSkeletonInstance, below, touches the hands. node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD); } + else if (mPitchFactor > 0) + { + // In third person mode we may still need pitch for ranged weapon targeting + float pitch = mPtr.getRefData().getPosition().rot[0] * mPitchFactor; + Ogre::Node *node = baseinst->getBone("Bip01 Spine2"); + node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD); + node = baseinst->getBone("Bip01 Spine1"); + node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD); + } mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame. for(size_t i = 0;i < ESM::PRT_Count;i++) diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 1dd0b41e6..9baf975f1 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -91,6 +91,7 @@ private: Ogre::SharedPtr mWeaponAnimationTime; float mAlpha; + float mPitchFactor; void updateNpcBase(); @@ -127,6 +128,10 @@ public: virtual Ogre::Vector3 runAnimation(float timepassed); + /// A relative factor (0-1) that decides if and how much the skeleton should be pitched + /// to indicate the facing orientation of the character. + virtual void setPitchFactor(float factor) { mPitchFactor = factor; } + virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool showa); From 8b8fb931a009a87b874c73a6f87a756bad73b0bf Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 04:05:01 +0100 Subject: [PATCH 285/301] Feature #50: Don't allow ranged weapon attack when ammunition is empty --- apps/openmw/mwmechanics/character.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index df6e7938f..5d7794e66 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -602,6 +602,19 @@ bool CharacterController::updateWeaponState() if(isWeapon) weapSpeed = weapon->get()->mBase->mData.mSpeed; + // Cancel attack if we no longer have ammunition + bool ammunition = true; + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (mWeaponType == WeapType_Crossbow) + ammunition = (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt); + else if (mWeaponType == WeapType_BowAndArrow) + ammunition = (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Arrow); + if (!ammunition && mUpperBodyState > UpperCharState_WeapEquiped) + { + mAnimation->disable(mCurrentWeapon); + mUpperBodyState = UpperCharState_WeapEquiped; + } + float complete; bool animPlaying; if(stats.getAttackingOrSpell()) @@ -706,7 +719,7 @@ bool CharacterController::updateWeaponState() if(item.getRefData().getCount()) MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item); } - else + else if (ammunition) { if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) From ffe19e7a523cc061d3bde7210023aeded6b1b5dd Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 04:11:46 +0100 Subject: [PATCH 286/301] Feature #50: Handle attach & release of projectiles --- apps/openmw/mwmechanics/character.cpp | 3 ++ apps/openmw/mwrender/animation.cpp | 6 +++ apps/openmw/mwrender/animation.hpp | 3 +- apps/openmw/mwrender/npcanimation.cpp | 57 ++++++++++++++++++++++++++ apps/openmw/mwrender/npcanimation.hpp | 5 +++ apps/openmw/mwworld/inventorystore.cpp | 44 ++++++++++---------- apps/openmw/mwworld/inventorystore.hpp | 4 +- 7 files changed, 96 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5d7794e66..0a94e4ed4 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -836,6 +836,9 @@ bool CharacterController::updateWeaponState() mUpperBodyState == UpperCharState_FollowStartToFollowStop || mUpperBodyState == UpperCharState_CastingSpell) { + if (ammunition && mWeaponType == WeapType_Crossbow) + mAnimation->attachArrow(); + mUpperBodyState = UpperCharState_WeapEquiped; //don't allow to continue playing hit animation on UpperBody after actor had attacked during it if(mHitState == CharState_Hit) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 66153a7e0..ba662be88 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -695,6 +695,12 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co else mPtr.getClass().hit(mPtr); } + else if (evt.compare(off, len, "shoot attach") == 0) + attachArrow(); + else if (evt.compare(off, len, "shoot release") == 0) + releaseArrow(); + else if (evt.compare(off, len, "shoot follow attach") == 0) + attachArrow(); else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") MWBase::Environment::get().getWorld()->castSpell(mPtr); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index ed7ed819f..c0cb18010 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -295,7 +295,8 @@ public: virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool show) {} - + virtual void attachArrow() {} + virtual void releaseArrow() {} void enableLights(bool enable); Ogre::AxisAlignedBox getWorldBounds(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d051bde52..bcbcbc737 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -696,6 +696,17 @@ void NpcAnimation::showWeapons(bool showWeapon) std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon); addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); + + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + { + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt) + attachArrow(); + else + mAmmunition.setNull(); + } + else + mAmmunition.setNull(); } } else @@ -726,6 +737,52 @@ void NpcAnimation::showCarriedLeft(bool show) removeIndividualPart(ESM::PRT_Shield); } +void NpcAnimation::attachArrow() +{ + MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weaponSlot != inv.end() && weaponSlot->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + showWeapons(true); + else + { + NifOgre::ObjectScenePtr weapon = mObjectParts[ESM::PRT_Weapon]; + + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo == inv.end()) + return; + std::string model = ammo->getClass().getModel(*ammo); + + mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", mInsert, model); + Ogre::Vector3 glowColor = getEnchantmentColor(*ammo); + setRenderProperties(mAmmunition, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0, + !ammo->getClass().getEnchantment(*ammo).empty(), &glowColor); + + std::for_each(mAmmunition->mEntities.begin(), mAmmunition->mEntities.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition)); + std::for_each(mAmmunition->mParticles.begin(), mAmmunition->mParticles.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition)); + } +} + +void NpcAnimation::releaseArrow() +{ + // Thrown weapons get detached now + MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weapon != inv.end() && weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + { + showWeapons(false); + inv.remove(*weapon, 1, mPtr); + } + else + { + // With bows and crossbows only the used arrow/bolt gets detached + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo == inv.end()) + return; + inv.remove(*ammo, 1, mPtr); + mAmmunition.setNull(); + } +} + void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) { // During first auto equip, we don't play any sounds. diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 9baf975f1..725fde01d 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -135,6 +135,11 @@ public: virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool showa); + virtual void attachArrow(); + virtual void releaseArrow(); + + NifOgre::ObjectScenePtr mAmmunition; + void setViewMode(ViewMode viewMode); void updateParts(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index e6dfe7a3e..e00276293 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -462,21 +462,23 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem( int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor) { - for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - if (mSlots[slot] == end()) - continue; + int retCount = ContainerStore::remove(item, count, actor); - if (*mSlots[slot] == item) + if (!item.getRefData().getCount()) + { + for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) { - // restacking is disabled cause it may break removal - unequipSlot(slot, actor, false); - break; + if (mSlots[slot] == end()) + continue; + + if (*mSlots[slot] == item) + { + unequipSlot(slot, actor); + break; + } } } - int retCount = ContainerStore::remove(item, count, actor); - // If an armor/clothing item is removed, try to find a replacement, // but not for the player nor werewolves. if ((actor.getRefData().getHandle() != "player") @@ -500,9 +502,9 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor return retCount; } -MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor, bool restack) +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) { - ContainerStoreIterator it = getSlot(slot); + ContainerStoreIterator it = mSlots[slot]; if (it != end()) { @@ -511,17 +513,15 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c // empty this slot mSlots[slot] = end(); - if (restack) { - // restack item previously in this slot - for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + // restack the previously equipped item with other (non-equipped) items + for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + { + if (stacks(*iter, *it)) { - if (stacks(*iter, *it)) - { - iter->getRefData().setCount(iter->getRefData().getCount() + it->getRefData().getCount()); - it->getRefData().setCount(0); - retval = iter; - break; - } + iter->getRefData().setCount(iter->getRefData().getCount() + it->getRefData().getCount()); + it->getRefData().setCount(0); + retval = iter; + break; } } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 868f3e517..714ba47da 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -168,12 +168,10 @@ namespace MWWorld /// /// @return the number of items actually removed - ContainerStoreIterator unequipSlot(int slot, const Ptr& actor, bool restack = true); + ContainerStoreIterator unequipSlot(int slot, const Ptr& actor); ///< Unequip \a slot. /// /// @return an iterator to the item that was previously in the slot - /// (if \a restack is true, the item can be re-stacked so its count - /// may differ from when it was equipped). ContainerStoreIterator unequipItem(const Ptr& item, const Ptr& actor); ///< Unequip an item identified by its Ptr. An exception is thrown From 7907181c0cc0009937a9705d0c4f12079ceb720a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 07:51:18 +0100 Subject: [PATCH 287/301] Fix uninitialized member --- apps/openmw/mwmechanics/pathfinding.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 70fa197d0..4407363a6 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -158,7 +158,9 @@ namespace namespace MWMechanics { PathFinder::PathFinder() - :mIsPathConstructed(false),mIsGraphConstructed(false) + : mIsPathConstructed(false), + mIsGraphConstructed(false), + mCell(NULL) { } From 0cc1cd8f7ec02882130d49d7b9f25794fcf0d50a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 23:52:13 +0100 Subject: [PATCH 288/301] Fix message box formatting bug --- apps/openmw/mwgui/messagebox.cpp | 8 ++++---- components/interpreter/miscopcodes.hpp | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index b52aee706..1ce167c33 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -245,11 +245,11 @@ namespace MWGui } mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding; - MyGUI::IntCoord absCoord; - absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; - absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; + MyGUI::IntPoint absPos; + absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2; - mMainWidget->setCoord(absCoord); + mMainWidget->setPosition(absPos); mMainWidget->setSize(mainWidgetSize); MyGUI::IntCoord messageWidgetCoord; diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index 1b4c823a0..1da8cf695 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -48,9 +48,10 @@ namespace Interpreter } else if (c=='f' || c=='F' || c=='.') { - while (c!='f' && i Date: Wed, 5 Feb 2014 08:48:15 +0100 Subject: [PATCH 289/301] Add generated version.hpp to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c061ca637..3975c4521 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ resources ## generated objects apps/openmw/config.hpp +components/version/version.hpp Docs/mainpage.hpp moc_*.cxx *.cxx_parameters From 677fc84223d03a462cbd1a6d4878c9e35eac6f54 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 5 Feb 2014 09:50:21 +0100 Subject: [PATCH 290/301] Refactor actors update --- apps/openmw/mwmechanics/actors.cpp | 38 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 71b1702e8..1fb22ce63 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -776,6 +776,27 @@ namespace MWMechanics { if(!paused) { + // Reset data from previous frame + for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + { + // Reset last hit object, which is only valid for one frame + // Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation + // (below) + iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string()); + } + + // AI and magic effects update + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) + { + updateActor(iter->first, duration); + if(iter->first.getTypeName() == typeid(ESM::NPC).name()) + updateNpc(iter->first, duration, paused); + } + } + + // Looping magic VFX update // 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. @@ -783,6 +804,7 @@ namespace MWMechanics for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) iter->second->updateContinuousVfx(); + // Animation/movement update for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( @@ -790,18 +812,8 @@ namespace MWMechanics iter->second->skipAnim(); iter->second->update(duration); } - } - - if (!paused) - { - for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) - { - // Reset last hit object, which is only valid for one frame - // Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation - // (below) - iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string()); - } + // Kill dead actors for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) { const MWWorld::Class &cls = MWWorld::Class::get(iter->first); @@ -812,10 +824,6 @@ namespace MWMechanics if(iter->second->isDead()) iter->second->resurrect(); - updateActor(iter->first, duration); - if(iter->first.getTypeName() == typeid(ESM::NPC).name()) - updateNpc(iter->first, duration, paused); - if(!stats.isDead()) continue; } From 4a26909172d0584acd344590410b6d1ebac05f7f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 5 Feb 2014 12:15:07 +0100 Subject: [PATCH 291/301] Fixes #1159: Don't allow quick keys menu in chargen --- apps/openmw/mwinput/inputmanagerimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 5385ab388..c4b8d0a89 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -748,7 +748,8 @@ namespace MWInput void InputManager::showQuickKeysMenu() { - if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()) + if (!MWBase::Environment::get().getWindowManager()->isGuiMode () + && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1) MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu); else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_QuickKeysMenu); From ff11d85a62cc72b10ad5cbe2f5a666a05c42a571 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 6 Feb 2014 03:15:01 +0100 Subject: [PATCH 292/301] Fix wrong assertion --- apps/openmw/mwrender/globalmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 4a87d9f1a..018dc082a 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -291,7 +291,7 @@ namespace MWRender unsigned int imageY = (yLength - (y + 1)) * cellImageSizeSrc; assert(imageX < image.getWidth()); - assert(imageY < image.getWidth()); + assert(imageY < image.getHeight()); if (image.getColourAt(imageX, imageY, 0).a > 0) exploredCells.push_back(std::make_pair(x+bounds.mMinX,y+bounds.mMinY)); From 6ce499f0e6df8994444cf3116065db806574f432 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 6 Feb 2014 03:58:07 +0100 Subject: [PATCH 293/301] Fixes #848: Use hardcoded animation velocity for first person movement --- apps/openmw/mwmechanics/character.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0a94e4ed4..f280f3be1 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -315,8 +315,18 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(!mCurrentMovement.empty()) { float vel, speedmult = 1.0f; + + bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); + if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) speedmult = mMovementSpeed / vel; + else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight) + speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed + else if (mMovementSpeed > 0.0f) + // The first person anims don't have any velocity to calculate a speed multiplier from. + // We use the third person velocities instead. + // FIXME: should be pulled from the actual animation, but it is not presently loaded. + speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f); mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); } From 8f949c6ae217c662e61f926fda0c5f9812b7fe09 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 8 Feb 2014 07:35:34 +0100 Subject: [PATCH 294/301] Fix lockpicks --- apps/openmw/mwrender/npcanimation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index bcbcbc737..d07aad31d 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -697,7 +697,8 @@ void NpcAnimation::showWeapons(bool showWeapon) addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); - if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + if (weapon->getTypeName() == typeid(ESM::Weapon).name() && + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) { MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt) From 8c8f4cd420f0c192fb3092955b88048820a41bfd Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Mon, 10 Feb 2014 22:53:16 +0100 Subject: [PATCH 295/301] Fix typo --- components/compiler/extensions.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp index 53ebfa31b..34551374a 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -1,5 +1,5 @@ #ifndef COMPILER_EXTENSIONS_H_INCLUDED -#define COMPILER_EXTENSINOS_H_INCLUDED +#define COMPILER_EXTENSIONS_H_INCLUDED #include #include From 6a4820c0f7fa21def9bb90989a57b79acb5893ec Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 11 Feb 2014 14:51:46 +0100 Subject: [PATCH 296/301] Show a message when the player attempts to cast a disabled spell --- apps/openmw/mwmechanics/spellcasting.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 0dec49f13..fe395e566 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -232,6 +232,24 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getStore().get().find ( effectIt->mEffectID); + if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate) + { + if (caster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); + continue; + } + + if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled() && + (effectIt->mEffectID == ESM::MagicEffect::AlmsiviIntervention || + effectIt->mEffectID == ESM::MagicEffect::DivineIntervention || + effectIt->mEffectID == ESM::MagicEffect::Mark || + effectIt->mEffectID == ESM::MagicEffect::Recall)) + { + if (caster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); + continue; + } + // If player is healing someone, show the target's HP bar if (caster.getRefData().getHandle() == "player" && target != caster && effectIt->mEffectID == ESM::MagicEffect::RestoreHealth From 70d35da1160090cd9dd3e338dbf498f49deca356 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 11 Feb 2014 16:34:23 +0100 Subject: [PATCH 297/301] Unset selected spell when removed --- apps/openmw/mwscript/statsextensions.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 7a59e9689..c4f672dc7 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -17,6 +17,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" @@ -451,6 +452,14 @@ namespace MWScript runtime.pop(); MWWorld::Class::get (ptr).getCreatureStats (ptr).getSpells().remove (id); + + MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); + + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr() && + id == wm->getSelectedSpell()) + { + wm->unsetSelectedSpell(); + } } }; From a3eea4f6b61932caf20e1ab856fa683be2253e5f Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 11 Feb 2014 16:34:51 +0100 Subject: [PATCH 298/301] Do not allow spellcasting stance without spell selected --- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwinput/inputmanagerimp.cpp | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index bd17cb317..878c2b95c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1035,6 +1035,11 @@ namespace MWGui { mSelectedSpell = ""; mHud->unsetSelectedSpell(); + + MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer(); + if (player->getDrawState() == MWMechanics::DrawState_Spell) + player->setDrawState(MWMechanics::DrawState_Nothing); + mSpellWindow->setTitle("#{sNone}"); } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index c4b8d0a89..7ed3007ff 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -646,6 +646,10 @@ namespace MWInput if (!mControlSwitch["playermagic"]) return; + // Not allowed if no spell selected + if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty()) + return; + MWMechanics::DrawState_ state = mPlayer->getDrawState(); if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) mPlayer->setDrawState(MWMechanics::DrawState_Spell); From 8824af30b4d58ef0b34929a96521182c01d5d785 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 11 Feb 2014 18:26:57 +0100 Subject: [PATCH 299/301] Allow to display message box outside of dialogue window --- apps/openmw/mwbase/windowmanager.hpp | 8 +++++++- apps/openmw/mwgui/windowmanagerimp.cpp | 13 ++++--------- apps/openmw/mwgui/windowmanagerimp.hpp | 2 +- apps/openmw/mwmechanics/npcstats.cpp | 6 ++++-- apps/openmw/mwscript/containerextensions.cpp | 4 ++-- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 069e6311a..4fce19e33 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -57,6 +57,12 @@ namespace MWGui class InventoryWindow; class ContainerWindow; class DialogueWindow; + + enum ShowInDialogueMode { + ShowInDialogueMode_IfPossible, + ShowInDialogueMode_Only, + ShowInDialogueMode_Never + }; } namespace SFO @@ -226,7 +232,7 @@ namespace MWBase virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; ///< Hides dialog and schedules dialog to be deleted. - virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false) = 0; + virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0; virtual void staticMessageBox(const std::string& message) = 0; virtual void removeStaticMessageBox() = 0; virtual int readPressedButton() = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 878c2b95c..5448bc3c4 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -648,19 +648,14 @@ namespace MWGui mGarbageDialogs.push_back(dialog); } - void WindowManager::messageBox (const std::string& message, const std::vector& buttons, bool showInDialogueModeOnly) + void WindowManager::messageBox (const std::string& message, const std::vector& buttons, enum MWGui::ShowInDialogueMode showInDialogueMode) { if (buttons.empty()) { /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ - if (getMode() == GM_Dialogue) { + if (getMode() == GM_Dialogue && showInDialogueMode != MWGui::ShowInDialogueMode_Never) { mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); - } else { - if (showInDialogueModeOnly) { - if (getMode() == GM_Dialogue) - mMessageBoxManager->createMessageBox(message); - } else { - mMessageBoxManager->createMessageBox(message); - } + } else if (showInDialogueMode != MWGui::ShowInDialogueMode_Only) { + mMessageBoxManager->createMessageBox(message); } } else { mMessageBoxManager->createInteractiveMessageBox(message, buttons); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index db52d9f79..dafb65e47 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -220,7 +220,7 @@ namespace MWGui virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false); + virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible); virtual void staticMessageBox(const std::string& message); virtual void removeStaticMessageBox(); virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index e642ffc5a..63b4467f6 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -220,16 +220,18 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas /// \todo check if character is the player, if levelling is ever implemented for NPCs MWBase::Environment::get().getSoundManager ()->playSound ("skillraise", 1, 1); + std::vector noButtons; + std::stringstream message; message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") % static_cast (base); - MWBase::Environment::get().getWindowManager ()->messageBox(message.str()); + MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), noButtons, MWGui::ShowInDialogueMode_Never); if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) { // levelup is possible now - MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}"); + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", noButtons, MWGui::ShowInDialogueMode_Never); } getSkill (skillIndex).setBase (base); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index e06505e86..fc21c5712 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -70,7 +70,7 @@ namespace MWScript msgBox = boost::str(boost::format(msgBox) % count % itemName); } std::vector noButtons; - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, /*showInDialogueModeOnly*/ true); + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, MWGui::ShowInDialogueMode_Only); } } }; @@ -142,7 +142,7 @@ namespace MWScript msgBox = boost::str (boost::format(msgBox) % itemName); } std::vector noButtons; - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, /*showInDialogueModeOnly*/ true); + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, MWGui::ShowInDialogueMode_Only); } } }; From fb0c5be536de767a14deb5a2a34c7744269db826 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 10:13:03 +0100 Subject: [PATCH 300/301] Don't suppress git error output --- cmake/GetGitRevisionDescription.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake index f70f64261..fecd1654d 100644 --- a/cmake/GetGitRevisionDescription.cmake +++ b/cmake/GetGitRevisionDescription.cmake @@ -122,7 +122,6 @@ function(git_describe _var) res OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) @@ -147,7 +146,6 @@ function(get_git_tag_revision _var) res OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") From c6fb0f2d9bf2540d8c23817174d8066485a7f5f1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 10:13:53 +0100 Subject: [PATCH 301/301] Removed terrain component's dependency on ESM --- apps/openmw/mwrender/terrainstorage.cpp | 514 ++++++++++++++++++++++++ apps/openmw/mwrender/terrainstorage.hpp | 69 +++- components/terrain/chunk.cpp | 6 +- components/terrain/quadtreenode.cpp | 6 +- components/terrain/quadtreenode.hpp | 2 +- components/terrain/storage.cpp | 509 ----------------------- components/terrain/storage.hpp | 37 +- components/terrain/world.cpp | 101 ++--- 8 files changed, 650 insertions(+), 594 deletions(-) diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 318627fc7..197572db9 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -1,5 +1,14 @@ #include "terrainstorage.hpp" +#include +#include +#include +#include +#include +#include + +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/esmstore.hpp" @@ -53,4 +62,509 @@ namespace MWRender return esmStore.get().find(index, plugin); } + bool TerrainStorage::getMinMaxHeights(float size, const Ogre::Vector2 ¢er, float &min, float &max) + { + assert (size <= 1 && "TerrainStorage::getMinMaxHeights, chunk size should be <= 1 cell"); + + /// \todo investigate if min/max heights should be stored at load time in ESM::Land instead + + Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); + + assert(origin.x == (int) origin.x); + assert(origin.y == (int) origin.y); + + int cellX = origin.x; + int cellY = origin.y; + + const ESM::Land* land = getLand(cellX, cellY); + if (!land) + return false; + + min = std::numeric_limits().max(); + max = -std::numeric_limits().max(); + for (int row=0; rowmLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; + if (h > max) + max = h; + if (h < min) + min = h; + } + } + return true; + } + + void TerrainStorage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row) + { + while (col >= ESM::Land::LAND_SIZE-1) + { + ++cellY; + col -= ESM::Land::LAND_SIZE-1; + } + while (row >= ESM::Land::LAND_SIZE-1) + { + ++cellX; + row -= ESM::Land::LAND_SIZE-1; + } + while (col < 0) + { + --cellY; + col += ESM::Land::LAND_SIZE-1; + } + while (row < 0) + { + --cellX; + row += ESM::Land::LAND_SIZE-1; + } + ESM::Land* land = getLand(cellX, cellY); + if (land && land->mHasData) + { + normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalise(); + } + else + normal = Ogre::Vector3(0,0,1); + } + + void TerrainStorage::averageNormal(Ogre::Vector3 &normal, int cellX, int cellY, int col, int row) + { + Ogre::Vector3 n1,n2,n3,n4; + fixNormal(n1, cellX, cellY, col+1, row); + fixNormal(n2, cellX, cellY, col-1, row); + fixNormal(n3, cellX, cellY, col, row+1); + fixNormal(n4, cellX, cellY, col, row-1); + normal = (n1+n2+n3+n4); + normal.normalise(); + } + + void TerrainStorage::fixColour (Ogre::ColourValue& color, int cellX, int cellY, int col, int row) + { + if (col == ESM::Land::LAND_SIZE-1) + { + ++cellY; + col = 0; + } + if (row == ESM::Land::LAND_SIZE-1) + { + ++cellX; + row = 0; + } + ESM::Land* land = getLand(cellX, cellY); + if (land && land->mLandData->mUsingColours) + { + color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; + color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; + color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; + } + else + { + color.r = 1; + color.g = 1; + color.b = 1; + } + + } + + void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + Ogre::HardwareVertexBufferSharedPtr vertexBuffer, + Ogre::HardwareVertexBufferSharedPtr normalBuffer, + Ogre::HardwareVertexBufferSharedPtr colourBuffer) + { + // LOD level n means every 2^n-th vertex is kept + size_t increment = 1 << lodLevel; + + Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); + assert(origin.x == (int) origin.x); + assert(origin.y == (int) origin.y); + + int startX = origin.x; + int startY = origin.y; + + size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1; + + std::vector colors; + colors.resize(numVerts*numVerts*4); + std::vector positions; + positions.resize(numVerts*numVerts*3); + std::vector normals; + normals.resize(numVerts*numVerts*3); + + Ogre::Vector3 normal; + Ogre::ColourValue color; + + float vertY; + float vertX; + + float vertY_ = 0; // of current cell corner + for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY) + { + float vertX_ = 0; // of current cell corner + for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX) + { + ESM::Land* land = getLand(cellX, cellY); + if (land && !land->mHasData) + land = NULL; + bool hasColors = land && land->mLandData->mUsingColours; + + int rowStart = 0; + int colStart = 0; + // Skip the first row / column unless we're at a chunk edge, + // since this row / column is already contained in a previous cell + if (colStart == 0 && vertY_ != 0) + colStart += increment; + if (rowStart == 0 && vertX_ != 0) + rowStart += increment; + + vertY = vertY_; + for (int col=colStart; colmLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; + else + positions[vertX*numVerts*3 + vertY*3 + 2] = -2048; + + if (land) + { + normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalise(); + } + else + normal = Ogre::Vector3(0,0,1); + + // Normals apparently don't connect seamlessly between cells + if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) + fixNormal(normal, cellX, cellY, col, row); + + // some corner normals appear to be complete garbage (z < 0) + if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) + averageNormal(normal, cellX, cellY, col, row); + + assert(normal.z > 0); + + normals[vertX*numVerts*3 + vertY*3] = normal.x; + normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y; + normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z; + + if (hasColors) + { + color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; + color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; + color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; + } + else + { + color.r = 1; + color.g = 1; + color.b = 1; + } + + // Unlike normals, colors mostly connect seamlessly between cells, but not always... + if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) + fixColour(color, cellX, cellY, col, row); + + color.a = 1; + Ogre::uint32 rsColor; + Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor); + memcpy(&colors[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32)); + + ++vertX; + } + ++vertY; + } + vertX_ = vertX; + } + vertY_ = vertY; + + assert(vertX_ == numVerts); // Ensure we covered whole area + } + assert(vertY_ == numVerts); // Ensure we covered whole area + + vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true); + normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true); + colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colors[0], true); + } + + TerrainStorage::UniqueTextureId TerrainStorage::getVtexIndexAt(int cellX, int cellY, + int x, int y) + { + // For the first/last row/column, we need to get the texture from the neighbour cell + // to get consistent blending at the borders + --x; + if (x < 0) + { + --cellX; + x += ESM::Land::LAND_TEXTURE_SIZE; + } + if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? + { + ++cellY; + y -= ESM::Land::LAND_TEXTURE_SIZE; + } + + assert(xisDataLoaded(ESM::Land::DATA_VTEX)) + land->loadData(ESM::Land::DATA_VTEX); + + int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; + if (tex == 0) + return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin + return std::make_pair(tex, land->mPlugin); + } + else + return std::make_pair(0,0); + } + + std::string TerrainStorage::getTextureName(UniqueTextureId id) + { + if (id.first == 0) + return "_land_default.dds"; // Not sure if the default texture floatly is hardcoded? + + // NB: All vtex ids are +1 compared to the ltex ids + const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); + + std::string texture = ltex->mTexture; + //TODO this is needed due to MWs messed up texture handling + texture = texture.substr(0, texture.rfind(".")) + ".dds"; + + return texture; + } + + void TerrainStorage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter, + bool pack, std::vector &blendmaps, std::vector &layerList) + { + // TODO - blending isn't completely right yet; the blending radius appears to be + // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap + // and interpolate the rest of the cell by hand? :/ + + Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f); + int cellX = origin.x; + int cellY = origin.y; + + // Save the used texture indices so we know the total number of textures + // and number of required blend maps + std::set textureIndices; + // Due to the way the blending works, the base layer will always shine through in between + // blend transitions (eg halfway between two texels, both blend values will be 0.5, so 25% of base layer visible). + // To get a consistent look, we need to make sure to use the same base layer in all cells. + // So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell. + textureIndices.insert(std::make_pair(0,0)); + + for (int y=0; y textureIndicesMap; + for (std::set::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it) + { + int size = textureIndicesMap.size(); + textureIndicesMap[*it] = size; + layerList.push_back(getLayerInfo(getTextureName(*it))); + } + + int numTextures = textureIndices.size(); + // numTextures-1 since the base layer doesn't need blending + int numBlendmaps = pack ? std::ceil((numTextures-1) / 4.f) : (numTextures-1); + + int channels = pack ? 4 : 1; + + // Second iteration - create and fill in the blend maps + const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1; + std::vector data; + data.resize(blendmapSize * blendmapSize * channels, 0); + + for (int i=0; isecond; + int blendIndex = (pack ? std::floor((layerIndex-1)/4.f) : layerIndex-1); + int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0; + + if (blendIndex == i) + data[y*blendmapSize*channels + x*channels + channel] = 255; + else + data[y*blendmapSize*channels + x*channels + channel] = 0; + } + } + + // All done, upload to GPU + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); + map->loadRawData(stream, blendmapSize, blendmapSize, format); + blendmaps.push_back(map); + } + } + + float TerrainStorage::getHeightAt(const Ogre::Vector3 &worldPos) + { + int cellX = std::floor(worldPos.x / 8192.f); + int cellY = std::floor(worldPos.y / 8192.f); + + ESM::Land* land = getLand(cellX, cellY); + if (!land) + return -2048; + + // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition + + // Normalized position in the cell + float nX = (worldPos.x - (cellX * 8192))/8192.f; + float nY = (worldPos.y - (cellY * 8192))/8192.f; + + // get left / bottom points (rounded down) + float factor = ESM::Land::LAND_SIZE - 1.0f; + float invFactor = 1.0f / factor; + + int startX = static_cast(nX * factor); + int startY = static_cast(nY * factor); + int endX = startX + 1; + int endY = startY + 1; + + assert(endX < ESM::Land::LAND_SIZE); + assert(endY < ESM::Land::LAND_SIZE); + + // now get points in terrain space (effectively rounding them to boundaries) + float startXTS = startX * invFactor; + float startYTS = startY * invFactor; + float endXTS = endX * invFactor; + float endYTS = endY * invFactor; + + // get parametric from start coord to next point + float xParam = (nX - startXTS) * factor; + float yParam = (nY - startYTS) * factor; + + /* For even / odd tri strip rows, triangles are this shape: + even odd + 3---2 3---2 + | / | | \ | + 0---1 0---1 + */ + + // Build all 4 positions in normalized cell space, using point-sampled height + Ogre::Vector3 v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); + Ogre::Vector3 v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); + Ogre::Vector3 v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); + Ogre::Vector3 v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); + // define this plane in terrain space + Ogre::Plane plane; + // (At the moment, all rows have the same triangle alignment) + if (true) + { + // odd row + bool secondTri = ((1.0 - yParam) > xParam); + if (secondTri) + plane.redefine(v0, v1, v3); + else + plane.redefine(v1, v2, v3); + } + else + { + // even row + bool secondTri = (yParam > xParam); + if (secondTri) + plane.redefine(v0, v2, v3); + else + plane.redefine(v0, v1, v2); + } + + // Solve plane equation for z + return (-plane.normal.x * nX + -plane.normal.y * nY + - plane.d) / plane.normal.z * 8192; + + } + + float TerrainStorage::getVertexHeight(const ESM::Land *land, int x, int y) + { + assert(x < ESM::Land::LAND_SIZE); + assert(y < ESM::Land::LAND_SIZE); + return land->mLandData->mHeights[y * ESM::Land::LAND_SIZE + x]; + } + + Terrain::LayerInfo TerrainStorage::getLayerInfo(const std::string& texture) + { + // Already have this cached? + if (mLayerInfoMap.find(texture) != mLayerInfoMap.end()) + return mLayerInfoMap[texture]; + + Terrain::LayerInfo info; + info.mParallax = false; + info.mSpecular = false; + info.mDiffuseMap = "textures\\" + texture; + std::string texture_ = texture; + boost::replace_last(texture_, ".", "_nh."); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) + { + info.mNormalMap = "textures\\" + texture_; + info.mParallax = true; + } + else + { + texture_ = texture; + boost::replace_last(texture_, ".", "_n."); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) + info.mNormalMap = "textures\\" + texture_; + } + + texture_ = texture; + boost::replace_last(texture_, ".", "_diffusespec."); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) + { + info.mDiffuseMap = "textures\\" + texture_; + info.mSpecular = true; + } + + mLayerInfoMap[texture] = info; + + return info; + } + + Terrain::LayerInfo TerrainStorage::getDefaultLayer() + { + Terrain::LayerInfo info; + info.mDiffuseMap = "textures\\_land_default.dds"; + info.mParallax = false; + info.mSpecular = false; + return info; + } + + float TerrainStorage::getCellWorldSize() + { + return ESM::Land::REAL_SIZE; + } + + int TerrainStorage::getCellVertices() + { + return ESM::Land::LAND_SIZE; + } + } diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index ebf5e26ab..5c2035952 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -12,8 +12,75 @@ namespace MWRender virtual ESM::Land* getLand (int cellX, int cellY); virtual const ESM::LandTexture* getLandTexture(int index, short plugin); public: + + /// Get bounds of the whole terrain in cell units virtual Ogre::AxisAlignedBox getBounds(); - ///< Get bounds in cell units + + /// Get the minimum and maximum heights of a terrain chunk. + /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. + /// Larger chunks can simply merge AABB of children. + /// @param size size of the chunk in cell units + /// @param center center of the chunk in cell units + /// @param min min height will be stored here + /// @param max max height will be stored here + /// @return true if there was data available for this terrain chunk + virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max); + + /// Fill vertex buffers for a terrain chunk. + /// @param lodLevel LOD level, 0 = most detailed + /// @param size size of the terrain chunk in cell units + /// @param center center of the chunk in cell units + /// @param vertexBuffer buffer to write vertices + /// @param normalBuffer buffer to write vertex normals + /// @param colourBuffer buffer to write vertex colours + virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + Ogre::HardwareVertexBufferSharedPtr vertexBuffer, + Ogre::HardwareVertexBufferSharedPtr normalBuffer, + Ogre::HardwareVertexBufferSharedPtr colourBuffer); + + /// Create textures holding layer blend values for a terrain chunk. + /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might + /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @param chunkSize size of the terrain chunk in cell units + /// @param chunkCenter center of the chunk in cell units + /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - + /// otherwise, each texture contains blend values for one layer only. Shader-based rendering + /// can utilize packing, FFP can't. + /// @param blendmaps created blendmaps will be written here + /// @param layerList names of the layer textures used will be written here + virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, + std::vector& blendmaps, + std::vector& layerList); + + virtual float getHeightAt (const Ogre::Vector3& worldPos); + + virtual Terrain::LayerInfo getDefaultLayer(); + + /// Get the transformation factor for mapping cell units to world units. + virtual float getCellWorldSize(); + + /// Get the number of vertices on one side for each cell. Should be (power of two)+1 + virtual int getCellVertices(); + + private: + void fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); + void fixColour (Ogre::ColourValue& colour, int cellX, int cellY, int col, int row); + void averageNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); + + float getVertexHeight (const ESM::Land* land, int x, int y); + + // Since plugins can define new texture palettes, we need to know the plugin index too + // in order to retrieve the correct texture name. + // pair + typedef std::pair UniqueTextureId; + + UniqueTextureId getVtexIndexAt(int cellX, int cellY, + int x, int y); + std::string getTextureName (UniqueTextureId id); + + std::map mLayerInfoMap; + + Terrain::LayerInfo getLayerInfo(const std::string& texture); }; } diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index ce2118cdb..a5c629088 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -18,11 +18,13 @@ namespace Terrain mVertexData = OGRE_NEW Ogre::VertexData; mVertexData->vertexStart = 0; + unsigned int verts = mNode->getTerrain()->getStorage()->getCellVertices(); + // Set the total number of vertices - size_t numVertsOneSide = mNode->getSize() * (ESM::Land::LAND_SIZE-1); + size_t numVertsOneSide = mNode->getSize() * (verts-1); numVertsOneSide /= 1 << lodLevel; numVertsOneSide += 1; - assert((int)numVertsOneSide == ESM::Land::LAND_SIZE); + assert(numVertsOneSide == verts); mVertexData->vertexCount = numVertsOneSide * numVertsOneSide; // Set up the vertex declaration, which specifies the info for each vertex (normals, colors, UVs, etc) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 7fc452fbf..a4fdd5e13 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -430,11 +430,7 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) // TODO - store this default material somewhere instead of creating one for each empty cell MaterialGenerator matGen(mTerrain->getShadersEnabled()); std::vector layer; - LayerInfo info; - info.mDiffuseMap = "textures\\_land_default.dds"; - info.mParallax = false; - info.mSpecular = false; - layer.push_back(info); + layer.push_back(mTerrain->getStorage()->getDefaultLayer()); matGen.setLayerList(layer); makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, matGen.generateForCompositeMapRTT(Ogre::MaterialPtr())); return; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 9f6fb5d24..ea299c096 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -108,7 +108,7 @@ namespace Terrain void destroyChunks(bool children); /// Get the effective LOD level if this node was rendered in one chunk - /// with ESM::Land::LAND_SIZE^2 vertices + /// with Storage::getCellVertices^2 vertices size_t getNativeLodLevel() { return mLodLevel; } /// Get the effective current LOD level used by the chunk rendering this node diff --git a/components/terrain/storage.cpp b/components/terrain/storage.cpp index 398ebac01..e69de29bb 100644 --- a/components/terrain/storage.cpp +++ b/components/terrain/storage.cpp @@ -1,509 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include -#include -#include -#include - -#include - -namespace Terrain -{ - - struct VertexElement - { - Ogre::Vector3 pos; - Ogre::Vector3 normal; - Ogre::ColourValue colour; - }; - - bool Storage::getMinMaxHeights(float size, const Ogre::Vector2 ¢er, float &min, float &max) - { - assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); - - /// \todo investigate if min/max heights should be stored at load time in ESM::Land instead - - Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); - - assert(origin.x == (int) origin.x); - assert(origin.y == (int) origin.y); - - int cellX = origin.x; - int cellY = origin.y; - - const ESM::Land* land = getLand(cellX, cellY); - if (!land) - return false; - - min = std::numeric_limits().max(); - max = -std::numeric_limits().max(); - for (int row=0; rowmLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; - if (h > max) - max = h; - if (h < min) - min = h; - } - } - return true; - } - - void Storage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row) - { - while (col >= ESM::Land::LAND_SIZE-1) - { - ++cellY; - col -= ESM::Land::LAND_SIZE-1; - } - while (row >= ESM::Land::LAND_SIZE-1) - { - ++cellX; - row -= ESM::Land::LAND_SIZE-1; - } - while (col < 0) - { - --cellY; - col += ESM::Land::LAND_SIZE-1; - } - while (row < 0) - { - --cellX; - row += ESM::Land::LAND_SIZE-1; - } - ESM::Land* land = getLand(cellX, cellY); - if (land && land->mHasData) - { - normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; - normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; - normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; - normal.normalise(); - } - else - normal = Ogre::Vector3(0,0,1); - } - - void Storage::averageNormal(Ogre::Vector3 &normal, int cellX, int cellY, int col, int row) - { - Ogre::Vector3 n1,n2,n3,n4; - fixNormal(n1, cellX, cellY, col+1, row); - fixNormal(n2, cellX, cellY, col-1, row); - fixNormal(n3, cellX, cellY, col, row+1); - fixNormal(n4, cellX, cellY, col, row-1); - normal = (n1+n2+n3+n4); - normal.normalise(); - } - - void Storage::fixColour (Ogre::ColourValue& color, int cellX, int cellY, int col, int row) - { - if (col == ESM::Land::LAND_SIZE-1) - { - ++cellY; - col = 0; - } - if (row == ESM::Land::LAND_SIZE-1) - { - ++cellX; - row = 0; - } - ESM::Land* land = getLand(cellX, cellY); - if (land && land->mLandData->mUsingColours) - { - color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; - color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; - color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; - } - else - { - color.r = 1; - color.g = 1; - color.b = 1; - } - - } - - void Storage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, - Ogre::HardwareVertexBufferSharedPtr vertexBuffer, - Ogre::HardwareVertexBufferSharedPtr normalBuffer, - Ogre::HardwareVertexBufferSharedPtr colourBuffer) - { - // LOD level n means every 2^n-th vertex is kept - size_t increment = 1 << lodLevel; - - Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); - assert(origin.x == (int) origin.x); - assert(origin.y == (int) origin.y); - - int startX = origin.x; - int startY = origin.y; - - size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1; - - std::vector colors; - colors.resize(numVerts*numVerts*4); - std::vector positions; - positions.resize(numVerts*numVerts*3); - std::vector normals; - normals.resize(numVerts*numVerts*3); - - Ogre::Vector3 normal; - Ogre::ColourValue color; - - float vertY; - float vertX; - - float vertY_ = 0; // of current cell corner - for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY) - { - float vertX_ = 0; // of current cell corner - for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX) - { - ESM::Land* land = getLand(cellX, cellY); - if (land && !land->mHasData) - land = NULL; - bool hasColors = land && land->mLandData->mUsingColours; - - int rowStart = 0; - int colStart = 0; - // Skip the first row / column unless we're at a chunk edge, - // since this row / column is already contained in a previous cell - if (colStart == 0 && vertY_ != 0) - colStart += increment; - if (rowStart == 0 && vertX_ != 0) - rowStart += increment; - - vertY = vertY_; - for (int col=colStart; colmLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; - else - positions[vertX*numVerts*3 + vertY*3 + 2] = -2048; - - if (land) - { - normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; - normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; - normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; - normal.normalise(); - } - else - normal = Ogre::Vector3(0,0,1); - - // Normals apparently don't connect seamlessly between cells - if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) - fixNormal(normal, cellX, cellY, col, row); - - // some corner normals appear to be complete garbage (z < 0) - if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) - averageNormal(normal, cellX, cellY, col, row); - - assert(normal.z > 0); - - normals[vertX*numVerts*3 + vertY*3] = normal.x; - normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y; - normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z; - - if (hasColors) - { - color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; - color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; - color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; - } - else - { - color.r = 1; - color.g = 1; - color.b = 1; - } - - // Unlike normals, colors mostly connect seamlessly between cells, but not always... - if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) - fixColour(color, cellX, cellY, col, row); - - color.a = 1; - Ogre::uint32 rsColor; - Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor); - memcpy(&colors[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32)); - - ++vertX; - } - ++vertY; - } - vertX_ = vertX; - } - vertY_ = vertY; - - assert(vertX_ == numVerts); // Ensure we covered whole area - } - assert(vertY_ == numVerts); // Ensure we covered whole area - - vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true); - normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true); - colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colors[0], true); - } - - Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY, - int x, int y) - { - // For the first/last row/column, we need to get the texture from the neighbour cell - // to get consistent blending at the borders - --x; - if (x < 0) - { - --cellX; - x += ESM::Land::LAND_TEXTURE_SIZE; - } - if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? - { - ++cellY; - y -= ESM::Land::LAND_TEXTURE_SIZE; - } - - assert(xisDataLoaded(ESM::Land::DATA_VTEX)) - land->loadData(ESM::Land::DATA_VTEX); - - int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; - if (tex == 0) - return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin - return std::make_pair(tex, land->mPlugin); - } - else - return std::make_pair(0,0); - } - - std::string Storage::getTextureName(UniqueTextureId id) - { - if (id.first == 0) - return "_land_default.dds"; // Not sure if the default texture floatly is hardcoded? - - // NB: All vtex ids are +1 compared to the ltex ids - const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); - - std::string texture = ltex->mTexture; - //TODO this is needed due to MWs messed up texture handling - texture = texture.substr(0, texture.rfind(".")) + ".dds"; - - return texture; - } - - void Storage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter, - bool pack, std::vector &blendmaps, std::vector &layerList) - { - // TODO - blending isn't completely right yet; the blending radius appears to be - // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap - // and interpolate the rest of the cell by hand? :/ - - Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f); - int cellX = origin.x; - int cellY = origin.y; - - // Save the used texture indices so we know the total number of textures - // and number of required blend maps - std::set textureIndices; - // Due to the way the blending works, the base layer will always shine through in between - // blend transitions (eg halfway between two texels, both blend values will be 0.5, so 25% of base layer visible). - // To get a consistent look, we need to make sure to use the same base layer in all cells. - // So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell. - textureIndices.insert(std::make_pair(0,0)); - - for (int y=0; y textureIndicesMap; - for (std::set::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it) - { - int size = textureIndicesMap.size(); - textureIndicesMap[*it] = size; - layerList.push_back(getLayerInfo(getTextureName(*it))); - } - - int numTextures = textureIndices.size(); - // numTextures-1 since the base layer doesn't need blending - int numBlendmaps = pack ? std::ceil((numTextures-1) / 4.f) : (numTextures-1); - - int channels = pack ? 4 : 1; - - // Second iteration - create and fill in the blend maps - const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1; - std::vector data; - data.resize(blendmapSize * blendmapSize * channels, 0); - - for (int i=0; isecond; - int blendIndex = (pack ? std::floor((layerIndex-1)/4.f) : layerIndex-1); - int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0; - - if (blendIndex == i) - data[y*blendmapSize*channels + x*channels + channel] = 255; - else - data[y*blendmapSize*channels + x*channels + channel] = 0; - } - } - - // All done, upload to GPU - Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); - map->loadRawData(stream, blendmapSize, blendmapSize, format); - blendmaps.push_back(map); - } - } - - float Storage::getHeightAt(const Ogre::Vector3 &worldPos) - { - int cellX = std::floor(worldPos.x / 8192.f); - int cellY = std::floor(worldPos.y / 8192.f); - - ESM::Land* land = getLand(cellX, cellY); - if (!land) - return -2048; - - // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition - - // Normalized position in the cell - float nX = (worldPos.x - (cellX * 8192))/8192.f; - float nY = (worldPos.y - (cellY * 8192))/8192.f; - - // get left / bottom points (rounded down) - float factor = ESM::Land::LAND_SIZE - 1.0f; - float invFactor = 1.0f / factor; - - int startX = static_cast(nX * factor); - int startY = static_cast(nY * factor); - int endX = startX + 1; - int endY = startY + 1; - - assert(endX < ESM::Land::LAND_SIZE); - assert(endY < ESM::Land::LAND_SIZE); - - // now get points in terrain space (effectively rounding them to boundaries) - float startXTS = startX * invFactor; - float startYTS = startY * invFactor; - float endXTS = endX * invFactor; - float endYTS = endY * invFactor; - - // get parametric from start coord to next point - float xParam = (nX - startXTS) * factor; - float yParam = (nY - startYTS) * factor; - - /* For even / odd tri strip rows, triangles are this shape: - even odd - 3---2 3---2 - | / | | \ | - 0---1 0---1 - */ - - // Build all 4 positions in normalized cell space, using point-sampled height - Ogre::Vector3 v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); - Ogre::Vector3 v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); - Ogre::Vector3 v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); - Ogre::Vector3 v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); - // define this plane in terrain space - Ogre::Plane plane; - // (At the moment, all rows have the same triangle alignment) - if (true) - { - // odd row - bool secondTri = ((1.0 - yParam) > xParam); - if (secondTri) - plane.redefine(v0, v1, v3); - else - plane.redefine(v1, v2, v3); - } - else - { - // even row - bool secondTri = (yParam > xParam); - if (secondTri) - plane.redefine(v0, v2, v3); - else - plane.redefine(v0, v1, v2); - } - - // Solve plane equation for z - return (-plane.normal.x * nX - -plane.normal.y * nY - - plane.d) / plane.normal.z * 8192; - - } - - float Storage::getVertexHeight(const ESM::Land *land, int x, int y) - { - assert(x < ESM::Land::LAND_SIZE); - assert(y < ESM::Land::LAND_SIZE); - return land->mLandData->mHeights[y * ESM::Land::LAND_SIZE + x]; - } - - LayerInfo Storage::getLayerInfo(const std::string& texture) - { - // Already have this cached? - if (mLayerInfoMap.find(texture) != mLayerInfoMap.end()) - return mLayerInfoMap[texture]; - - LayerInfo info; - info.mParallax = false; - info.mSpecular = false; - info.mDiffuseMap = "textures\\" + texture; - std::string texture_ = texture; - boost::replace_last(texture_, ".", "_nh."); - if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) - { - info.mNormalMap = "textures\\" + texture_; - info.mParallax = true; - } - else - { - texture_ = texture; - boost::replace_last(texture_, ".", "_n."); - if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) - info.mNormalMap = "textures\\" + texture_; - } - - texture_ = texture; - boost::replace_last(texture_, ".", "_diffusespec."); - if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) - { - info.mDiffuseMap = "textures\\" + texture_; - info.mSpecular = true; - } - - mLayerInfoMap[texture] = info; - - return info; - } - - -} diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 18d05b100..021e01c7e 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -24,9 +24,6 @@ namespace Terrain { public: virtual ~Storage() {} - private: - virtual ESM::Land* getLand (int cellX, int cellY) = 0; - virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; public: /// Get bounds of the whole terrain in cell units @@ -40,7 +37,7 @@ namespace Terrain /// @param min min height will be stored here /// @param max max height will be stored here /// @return true if there was data available for this terrain chunk - bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max); + virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max) = 0; /// Fill vertex buffers for a terrain chunk. /// @param lodLevel LOD level, 0 = most detailed @@ -49,10 +46,10 @@ namespace Terrain /// @param vertexBuffer buffer to write vertices /// @param normalBuffer buffer to write vertex normals /// @param colourBuffer buffer to write vertex colours - void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Ogre::HardwareVertexBufferSharedPtr vertexBuffer, Ogre::HardwareVertexBufferSharedPtr normalBuffer, - Ogre::HardwareVertexBufferSharedPtr colourBuffer); + Ogre::HardwareVertexBufferSharedPtr colourBuffer) = 0; /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might @@ -64,31 +61,19 @@ namespace Terrain /// can utilize packing, FFP can't. /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here - void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, + virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, std::vector& blendmaps, - std::vector& layerList); + std::vector& layerList) = 0; - float getHeightAt (const Ogre::Vector3& worldPos); + virtual float getHeightAt (const Ogre::Vector3& worldPos) = 0; - private: - void fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); - void fixColour (Ogre::ColourValue& colour, int cellX, int cellY, int col, int row); - void averageNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); + virtual LayerInfo getDefaultLayer() = 0; - float getVertexHeight (const ESM::Land* land, int x, int y); + /// Get the transformation factor for mapping cell units to world units. + virtual float getCellWorldSize() = 0; - // Since plugins can define new texture palettes, we need to know the plugin index too - // in order to retrieve the correct texture name. - // pair - typedef std::pair UniqueTextureId; - - UniqueTextureId getVtexIndexAt(int cellX, int cellY, - int x, int y); - std::string getTextureName (UniqueTextureId id); - - std::map mLayerInfoMap; - - LayerInfo getLayerInfo(const std::string& texture); + /// Get the number of vertices on one side for each cell. Should be (power of two)+1 + virtual int getCellVertices() = 0; }; } diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 711ebbc8f..dac960fbb 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include "storage.hpp" @@ -208,6 +207,8 @@ namespace Terrain Ogre::HardwareIndexBufferSharedPtr World::getIndexBuffer(int flags, size_t& numIndices) { + unsigned int verts = mStorage->getCellVertices(); + if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) { numIndices = mIndexBufferMap[flags]->getNumIndexes(); @@ -224,11 +225,11 @@ namespace Terrain bool anyDeltas = (lodDeltas[North] || lodDeltas[South] || lodDeltas[West] || lodDeltas[East]); size_t increment = 1 << lodLevel; - assert((int)increment < ESM::Land::LAND_SIZE); + assert(increment < verts); std::vector indices; - indices.reserve((ESM::Land::LAND_SIZE-1)*(ESM::Land::LAND_SIZE-1)*2*3 / increment); + indices.reserve((verts-1)*(verts-1)*2*3 / increment); - size_t rowStart = 0, colStart = 0, rowEnd = ESM::Land::LAND_SIZE-1, colEnd = ESM::Land::LAND_SIZE-1; + size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; // If any edge needs stitching we'll skip all edges at this point, // mainly because stitching one edge would have an effect on corners and on the adjacent edges if (anyDeltas) @@ -242,13 +243,13 @@ namespace Terrain { for (size_t col = colStart; col < colEnd; col += increment) { - indices.push_back(ESM::Land::LAND_SIZE*col+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row+increment); - indices.push_back(ESM::Land::LAND_SIZE*col+row+increment); + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row+increment); + indices.push_back(verts*col+row+increment); - indices.push_back(ESM::Land::LAND_SIZE*col+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row+increment); + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row); + indices.push_back(verts*(col+increment)+row+increment); } } @@ -261,96 +262,96 @@ namespace Terrain // South size_t row = 0; size_t outerStep = 1 << (lodDeltas[South] + lodLevel); - for (size_t col = 0; col < ESM::Land::LAND_SIZE-1; col += outerStep) + for (size_t col = 0; col < verts-1; col += outerStep) { - indices.push_back(ESM::Land::LAND_SIZE*col+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row); + indices.push_back(verts*col+row); + indices.push_back(verts*(col+outerStep)+row); // Make sure not to touch the right edge - if (col+outerStep == ESM::Land::LAND_SIZE-1) - indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep-innerStep)+row+innerStep); + if (col+outerStep == verts-1) + indices.push_back(verts*(col+outerStep-innerStep)+row+innerStep); else - indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row+innerStep); + indices.push_back(verts*(col+outerStep)+row+innerStep); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == ESM::Land::LAND_SIZE-1-innerStep) + if (col+i == 0 || col+i == verts-1-innerStep) continue; - indices.push_back(ESM::Land::LAND_SIZE*(col)+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+i+innerStep)+row+innerStep); - indices.push_back(ESM::Land::LAND_SIZE*(col+i)+row+innerStep); + indices.push_back(verts*(col)+row); + indices.push_back(verts*(col+i+innerStep)+row+innerStep); + indices.push_back(verts*(col+i)+row+innerStep); } } // North - row = ESM::Land::LAND_SIZE-1; + row = verts-1; outerStep = 1 << (lodDeltas[North] + lodLevel); - for (size_t col = 0; col < ESM::Land::LAND_SIZE-1; col += outerStep) + for (size_t col = 0; col < verts-1; col += outerStep) { - indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row); - indices.push_back(ESM::Land::LAND_SIZE*col+row); + indices.push_back(verts*(col+outerStep)+row); + indices.push_back(verts*col+row); // Make sure not to touch the left edge if (col == 0) - indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row-innerStep); + indices.push_back(verts*(col+innerStep)+row-innerStep); else - indices.push_back(ESM::Land::LAND_SIZE*col+row-innerStep); + indices.push_back(verts*col+row-innerStep); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == ESM::Land::LAND_SIZE-1-innerStep) + if (col+i == 0 || col+i == verts-1-innerStep) continue; - indices.push_back(ESM::Land::LAND_SIZE*(col+i)+row-innerStep); - indices.push_back(ESM::Land::LAND_SIZE*(col+i+innerStep)+row-innerStep); - indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row); + indices.push_back(verts*(col+i)+row-innerStep); + indices.push_back(verts*(col+i+innerStep)+row-innerStep); + indices.push_back(verts*(col+outerStep)+row); } } // West size_t col = 0; outerStep = 1 << (lodDeltas[West] + lodLevel); - for (size_t row = 0; row < ESM::Land::LAND_SIZE-1; row += outerStep) + for (size_t row = 0; row < verts-1; row += outerStep) { - indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep); - indices.push_back(ESM::Land::LAND_SIZE*col+row); + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*col+row); // Make sure not to touch the top edge - if (row+outerStep == ESM::Land::LAND_SIZE-1) - indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+outerStep-innerStep); + if (row+outerStep == verts-1) + indices.push_back(verts*(col+innerStep)+row+outerStep-innerStep); else - indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+outerStep); + indices.push_back(verts*(col+innerStep)+row+outerStep); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep) + if (row+i == 0 || row+i == verts-1-innerStep) continue; - indices.push_back(ESM::Land::LAND_SIZE*col+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+i); - indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+i+innerStep); + indices.push_back(verts*col+row); + indices.push_back(verts*(col+innerStep)+row+i); + indices.push_back(verts*(col+innerStep)+row+i+innerStep); } } // East - col = ESM::Land::LAND_SIZE-1; + col = verts-1; outerStep = 1 << (lodDeltas[East] + lodLevel); - for (size_t row = 0; row < ESM::Land::LAND_SIZE-1; row += outerStep) + for (size_t row = 0; row < verts-1; row += outerStep) { - indices.push_back(ESM::Land::LAND_SIZE*col+row); - indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep); + indices.push_back(verts*col+row); + indices.push_back(verts*col+row+outerStep); // Make sure not to touch the bottom edge if (row == 0) - indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+innerStep); + indices.push_back(verts*(col-innerStep)+row+innerStep); else - indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row); + indices.push_back(verts*(col-innerStep)+row); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep) + if (row+i == 0 || row+i == verts-1-innerStep) continue; - indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep); - indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+i+innerStep); - indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+i); + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*(col-innerStep)+row+i+innerStep); + indices.push_back(verts*(col-innerStep)+row+i); } } }