From 0c6e2170dbb5c880b4c3c8df3ff43566849cd804 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 31 Jan 2015 23:27:34 +0100 Subject: [PATCH 01/26] Include cleanup --- apps/launcher/graphicspage.cpp | 2 +- apps/openmw/mwbase/environment.hpp | 4 ++-- apps/openmw/mwbase/inputmanager.hpp | 4 +--- apps/openmw/mwbase/soundmanager.hpp | 4 +--- apps/openmw/mwbase/windowmanager.hpp | 18 +++++++++++------- apps/openmw/mwbase/world.hpp | 14 +++++--------- apps/openmw/mwdialogue/scripttest.cpp | 2 ++ apps/openmw/mwgui/hud.cpp | 1 + apps/openmw/mwgui/inventorywindow.cpp | 2 ++ apps/openmw/mwgui/itemmodel.cpp | 2 ++ apps/openmw/mwgui/journalviewmodel.cpp | 2 ++ apps/openmw/mwgui/loadingscreen.cpp | 2 ++ apps/openmw/mwgui/mainmenu.cpp | 1 + apps/openmw/mwgui/mapwindow.cpp | 1 + apps/openmw/mwgui/settingswindow.cpp | 1 + apps/openmw/mwgui/tooltips.cpp | 1 + apps/openmw/mwgui/waitdialog.cpp | 1 + apps/openmw/mwgui/windowbase.cpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwgui/windowmanagerimp.hpp | 7 ++++--- apps/openmw/mwmechanics/actors.hpp | 1 + apps/openmw/mwmechanics/character.cpp | 2 ++ apps/openmw/mwrender/animation.cpp | 1 + apps/openmw/mwrender/globalmap.cpp | 1 + apps/openmw/mwscript/animationextensions.cpp | 1 + apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/livecellref.cpp | 3 ++- extern/sdl4ogre/events.h | 6 +++--- extern/sdl4ogre/sdlcursormanager.cpp | 3 +++ extern/sdl4ogre/sdlcursormanager.hpp | 5 +++-- 30 files changed, 66 insertions(+), 34 deletions(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 1f85bb5e7b..dccf8bb8ce 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -10,7 +10,7 @@ #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #endif // MAC_OS_X_VERSION_MIN_REQUIRED -#include +#include #include #include diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index eb636ea2fb..7f7919f813 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_BASE_INVIRONMENT_H -#define GAME_BASE_INVIRONMENT_H +#ifndef GAME_BASE_ENVIRONMENT_H +#define GAME_BASE_ENVIRONMENT_H namespace MWBase { diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index d44da4974d..11eb1721f2 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -3,8 +3,6 @@ #include -#include - namespace MWBase { /// \brief Interface for input manager (implemented in MWInput) @@ -29,7 +27,7 @@ namespace MWBase virtual void changeInputMode(bool guiMode) = 0; - virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; + virtual void processChangedSettings(const std::vector< std::pair >& changed) = 0; virtual void setDragDrop(bool dragDrop) = 0; diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index bc2f3f1c61..a4c08f06fe 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -5,8 +5,6 @@ #include -#include - #include "../mwworld/ptr.hpp" namespace Ogre @@ -74,7 +72,7 @@ namespace MWBase virtual ~SoundManager() {} - virtual void processChangedSettings(const Settings::CategorySettingVector& settings) = 0; + virtual void processChangedSettings(const std::vector< std::pair >& settings) = 0; virtual void stopMusic() = 0; ///< Stops music if it's playing diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 69027d7341..fc745d3e68 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -5,16 +5,20 @@ #include #include -#include - -#include - -#include - #include "../mwmechanics/stat.hpp" #include "../mwgui/mode.hpp" +namespace Loading +{ + class Listener; +} + +namespace Translation +{ + class Storage; +} + namespace MyGUI { class Gui; @@ -267,7 +271,7 @@ namespace MWBase */ virtual std::string getGameSettingString(const std::string &id, const std::string &default_) = 0; - virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; + virtual void processChangedSettings(const std::vector< std::pair >& changed) = 0; virtual void windowResized(int x, int y) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 9d8fc90a1f..3af2a27752 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -4,25 +4,21 @@ #include #include -#include - #include -#include "../mwworld/globals.hpp" #include "../mwworld/ptr.hpp" namespace Ogre { class Vector2; class Vector3; + class Quaternion; + class Image; } -namespace OEngine +namespace Loading { - namespace Physic - { - class PhysicEngine; - } + class Listener; } namespace ESM @@ -390,7 +386,7 @@ namespace MWBase virtual bool canPlaceObject (float cursorX, float cursorY) = 0; ///< @return true if it is possible to place on object at specified cursor location - virtual void processChangedSettings (const Settings::CategorySettingVector& settings) = 0; + virtual void processChangedSettings (const std::vector< std::pair >& settings) = 0; virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const = 0; diff --git a/apps/openmw/mwdialogue/scripttest.cpp b/apps/openmw/mwdialogue/scripttest.cpp index b2c8f536a2..fd477365f0 100644 --- a/apps/openmw/mwdialogue/scripttest.cpp +++ b/apps/openmw/mwdialogue/scripttest.cpp @@ -1,5 +1,7 @@ #include "scripttest.hpp" +#include + #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 13a5c559c1..1f7ead6f56 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index ed0f505f53..ae7d60d442 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index f5135ce80e..8224fd55b0 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -1,5 +1,7 @@ #include "itemmodel.hpp" +#include + #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/store.hpp" diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index add2ac62c7..3a86613f61 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -8,6 +8,8 @@ #include +#include + #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 270066c97a..9e3343c782 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -15,6 +15,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/statemanager.hpp" diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index f1c4c40885..6ad4da3bfb 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 61712a470e..13e2e39049 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f8741a67a2..2810934522 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 2849688c2b..2750717372 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 718e04e52e..fedb0324e9 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index 7f2eaec2ec..8fdcf6b207 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -2,6 +2,8 @@ #include +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 975ee6c29f..a35498deaa 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include + #include #include @@ -26,6 +29,8 @@ #include +#include + #include #include diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index a08c59a2fb..b3eb9cd0a9 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -5,13 +5,14 @@ This class owns and controls all the MW specific windows in the GUI. It can enable/disable Gui mode, and is responsible for sending and retrieving information from the Gui. - - MyGUI should be initialized separately before creating instances of - this class. **/ +#include + #include "../mwbase/windowmanager.hpp" +#include + #include "mapwindow.hpp" #include diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 39598b2a25..70f1b47d91 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "movement.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 91b3757146..449c030f45 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -27,6 +27,8 @@ #include "creaturestats.hpp" #include "security.hpp" +#include + #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ec53983092..50afc5afdd 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index f0f6158ed4..de78748b57 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -11,6 +11,7 @@ #include #include +#include #include diff --git a/apps/openmw/mwscript/animationextensions.cpp b/apps/openmw/mwscript/animationextensions.cpp index 613cf7d24e..00b8a9620f 100644 --- a/apps/openmw/mwscript/animationextensions.cpp +++ b/apps/openmw/mwscript/animationextensions.cpp @@ -2,6 +2,7 @@ #include "animationextensions.hpp" #include +#include #include #include diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a282f152ad..d623ba4654 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp index dd313632b8..bfc708185d 100644 --- a/apps/openmw/mwworld/livecellref.cpp +++ b/apps/openmw/mwworld/livecellref.cpp @@ -1,6 +1,7 @@ - #include "livecellref.hpp" +#include + #include #include "../mwbase/environment.hpp" diff --git a/extern/sdl4ogre/events.h b/extern/sdl4ogre/events.h index 0fb4d6f060..6961e81fc8 100644 --- a/extern/sdl4ogre/events.h +++ b/extern/sdl4ogre/events.h @@ -1,8 +1,8 @@ #ifndef _SFO_EVENTS_H #define _SFO_EVENTS_H -#include - +#include +#include //////////// // Events // @@ -65,7 +65,7 @@ public: virtual ~WindowListener() {} /** @remarks The window's visibility changed */ - virtual void windowVisibilityChange( bool visible ) {}; + virtual void windowVisibilityChange( bool visible ) {} /** @remarks The window got / lost input focus */ virtual void windowFocusChange( bool have_focus ) {} diff --git a/extern/sdl4ogre/sdlcursormanager.cpp b/extern/sdl4ogre/sdlcursormanager.cpp index 5ef274b7e7..7623d57dbf 100644 --- a/extern/sdl4ogre/sdlcursormanager.cpp +++ b/extern/sdl4ogre/sdlcursormanager.cpp @@ -4,6 +4,9 @@ #include #include +#include +#include + #include namespace SFO diff --git a/extern/sdl4ogre/sdlcursormanager.hpp b/extern/sdl4ogre/sdlcursormanager.hpp index 7e3e59b4a5..58324fc01c 100644 --- a/extern/sdl4ogre/sdlcursormanager.hpp +++ b/extern/sdl4ogre/sdlcursormanager.hpp @@ -1,11 +1,12 @@ #ifndef SDL4OGRE_CURSORMANAGER_H #define SDL4OGRE_CURSORMANAGER_H -#include - #include "cursormanager.hpp" #include +struct SDL_Cursor; +struct SDL_Surface; + namespace SFO { class SDLCursorManager : From 6d097fbfbd8f051de27bfcd81bb0746f1509bf47 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 1 Feb 2015 00:26:06 +0100 Subject: [PATCH 02/26] Normalise spelling of "levelled" throughout the code --- apps/esmtool/record.cpp | 4 ++-- apps/essimporter/importcellref.cpp | 4 ++-- apps/openmw/mwmechanics/levelledlist.hpp | 8 ++++---- apps/openmw/mwmechanics/npcstats.cpp | 2 +- apps/openmw/mwscript/miscextensions.cpp | 10 +++++----- components/esm/loadlevlist.cpp | 6 +++--- components/esm/loadlevlist.hpp | 12 ++++++------ 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index df2631c13f..e167278f3d 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -837,7 +837,7 @@ void Record::print() std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; - std::vector::iterator iit; + std::vector::iterator iit; for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit) std::cout << " Creature: Level: " << iit->mLevel << " Creature: " << iit->mId << std::endl; @@ -849,7 +849,7 @@ void Record::print() std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; - std::vector::iterator iit; + std::vector::iterator iit; for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit) std::cout << " Inventory: Level: " << iit->mLevel << " Item: " << iit->mId << std::endl; diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 906b2ed242..cca356b2a8 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -22,7 +22,7 @@ namespace ESSImport ActorData::load(esm); if (esm.isNextSub("LVCR")) { - // occurs on leveled creature spawner references + // occurs on levelled creature spawner references // probably some identifier for the creature that has been spawned? unsigned char lvcr; esm.getHT(lvcr); @@ -32,7 +32,7 @@ namespace ESSImport mEnabled = true; esm.getHNOT(mEnabled, "ZNAM"); - // DATA should occur for all references, except leveled creature spawners + // DATA should occur for all references, except levelled creature spawners // I've seen DATA *twice* on a creature record, and with the exact same content too! weird // alarmvoi0000.ess esm.getHNOT(mPos, "DATA", 24); diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp index 96410c35d7..de652a9c84 100644 --- a/apps/openmw/mwmechanics/levelledlist.hpp +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -12,9 +12,9 @@ namespace MWMechanics { /// @return ID of resulting item, or empty if none - inline std::string getLevelledItem (const ESM::LeveledListBase* levItem, bool creature, unsigned char failChance=0) + inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature, unsigned char failChance=0) { - const std::vector& items = levItem->mList; + const std::vector& items = levItem->mList; const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int playerLevel = player.getClass().getCreatureStats(player).getLevel(); @@ -27,7 +27,7 @@ namespace MWMechanics std::vector candidates; int highestLevel = 0; - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { if (it->mLevel > highestLevel && it->mLevel <= playerLevel) highestLevel = it->mLevel; @@ -39,7 +39,7 @@ namespace MWMechanics allLevels = levItem->mFlags & ESM::CreatureLevList::AllLevels; std::pair highest = std::make_pair(-1, ""); - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { if (playerLevel >= it->mLevel && (allLevels || it->mLevel == highestLevel)) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index c517b4df8a..7219d6185a 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -200,7 +200,7 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, if (value.getProgress()>=1) { - // skill leveled up + // skill levelled up increaseSkill(skillIndex, class_, false); } } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 37bb4235e2..bffeef768c 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -34,24 +34,24 @@ namespace { - void addToLevList(ESM::LeveledListBase* list, const std::string& itemId, int level) + void addToLevList(ESM::LevelledListBase* list, const std::string& itemId, int level) { - for (std::vector::iterator it = list->mList.begin(); it != list->mList.end();) + for (std::vector::iterator it = list->mList.begin(); it != list->mList.end();) { if (it->mLevel == level && itemId == it->mId) return; } - ESM::LeveledListBase::LevelItem item; + ESM::LevelledListBase::LevelItem item; item.mId = itemId; item.mLevel = level; list->mList.push_back(item); } - void removeFromLevList(ESM::LeveledListBase* list, const std::string& itemId, int level) + void removeFromLevList(ESM::LevelledListBase* list, const std::string& itemId, int level) { // level of -1 removes all items with that itemId - for (std::vector::iterator it = list->mList.begin(); it != list->mList.end();) + for (std::vector::iterator it = list->mList.begin(); it != list->mList.end();) { if (level != -1 && it->mLevel != level) { diff --git a/components/esm/loadlevlist.cpp b/components/esm/loadlevlist.cpp index 6385b9a718..f2c695c1f1 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -7,7 +7,7 @@ namespace ESM { -void LeveledListBase::load(ESMReader &esm) +void LevelledListBase::load(ESMReader &esm) { esm.getHNT(mFlags, "DATA"); esm.getHNT(mChanceNone, "NNAM"); @@ -34,7 +34,7 @@ void LeveledListBase::load(ESMReader &esm) esm.getHNT(li.mLevel, "INTV"); } } -void LeveledListBase::save(ESMWriter &esm) const +void LevelledListBase::save(ESMWriter &esm) const { esm.writeHNT("DATA", mFlags); esm.writeHNT("NNAM", mChanceNone); @@ -47,7 +47,7 @@ void LeveledListBase::save(ESMWriter &esm) const } } - void LeveledListBase::blank() + void LevelledListBase::blank() { mFlags = 0; mChanceNone = 0; diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index a4e1b85c2d..bcea2b234c 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -11,14 +11,14 @@ class ESMReader; class ESMWriter; /* - * Leveled lists. Since these have identical layout, I only bothered + * Levelled lists. Since these have identical layout, I only bothered * to implement it once. * - * We should later implement the ability to merge leveled lists from + * We should later implement the ability to merge levelled lists from * several files. */ -struct LeveledListBase +struct LevelledListBase { int mFlags; unsigned char mChanceNone; // Chance that none are selected (0-100) @@ -43,7 +43,7 @@ struct LeveledListBase ///< Set record to default state (does not touch the ID). }; -struct CreatureLevList: LeveledListBase +struct CreatureLevList: LevelledListBase { static unsigned int sRecordId; @@ -61,7 +61,7 @@ struct CreatureLevList: LeveledListBase } }; -struct ItemLevList: LeveledListBase +struct ItemLevList: LevelledListBase { static unsigned int sRecordId; @@ -72,7 +72,7 @@ struct ItemLevList: LeveledListBase // list is instantiated, instead of // giving several identical items // (used when a container has more - // than one instance of one leveled + // than one instance of one levelled // list.) AllLevels = 0x02 // Calculate from all levels <= player // level, not just the closest below From 9825b68dfb6d5ab015d283c6bb858246b40ba9b2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 1 Feb 2015 16:19:05 +0100 Subject: [PATCH 03/26] Fix SDL eating SIGINT signals in the launcher (Fixes #2328) --- apps/launcher/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index e4fae74f4d..5c3f384581 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -26,6 +27,8 @@ int main(int argc, char *argv[]) qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError()); return 0; } + signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher, + // so reset SIGINT which SDL wants to redirect to an SDL_Quit event. QApplication app(argc, argv); From 6ea59c93abf46402f505c8dd8519b42a7249dc15 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 2 Feb 2015 04:20:33 +0100 Subject: [PATCH 04/26] Ignore the object scale in Move instruction (Fixes #2275) --- apps/openmw/mwscript/transformationextensions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 3bbde20036..fcfc36995f 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -691,10 +691,10 @@ namespace MWScript if (!ptr.getRefData().getBaseNode()) return; - Ogre::Vector3 worldPos = ptr.getRefData().getBaseNode()->convertLocalToWorldPosition(posChange); - - dynamic_cast(runtime.getContext()).updatePtr( - MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x, worldPos.y, worldPos.z)); + Ogre::Vector3 diff = ptr.getRefData().getBaseNode()->getOrientation() * posChange; + Ogre::Vector3 worldPos(ptr.getRefData().getPosition().pos); + worldPos += diff; + MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x, worldPos.y, worldPos.z); } }; From 27dc49a1357c2078d2305f0859efd3ccc3de8639 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 3 Feb 2015 00:53:30 +0100 Subject: [PATCH 05/26] Rewrite game settings manager Removes the abhorrent dependency on Ogre for this code and improves the error handling. --- apps/openmw/engine.cpp | 6 +- apps/openmw/mwbase/inputmanager.hpp | 4 +- apps/openmw/mwbase/soundmanager.hpp | 4 +- apps/openmw/mwbase/windowmanager.hpp | 2 +- apps/openmw/mwbase/world.hpp | 3 +- components/settings/settings.cpp | 224 +++++++++++++++------------ components/settings/settings.hpp | 13 +- 7 files changed, 137 insertions(+), 119 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 859baebe4a..a9a7f06324 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -292,14 +292,10 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) else throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed."); - // load user settings if they exist, otherwise just load the default settings as user settings + // load user settings if they exist const std::string settingspath = mCfgMgr.getUserConfigPath().string() + "/settings.cfg"; if (boost::filesystem::exists(settingspath)) settings.loadUser(settingspath); - else if (boost::filesystem::exists(localdefault)) - settings.loadUser(localdefault); - else if (boost::filesystem::exists(globaldefault)) - settings.loadUser(globaldefault); mFpsLevel = settings.getInt("fps", "HUD"); diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 11eb1721f2..a53d8d9dcd 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -2,6 +2,8 @@ #define GAME_MWBASE_INPUTMANAGER_H #include +#include +#include namespace MWBase { @@ -27,7 +29,7 @@ namespace MWBase virtual void changeInputMode(bool guiMode) = 0; - virtual void processChangedSettings(const std::vector< std::pair >& changed) = 0; + virtual void processChangedSettings(const std::set< std::pair >& changed) = 0; virtual void setDragDrop(bool dragDrop) = 0; diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index a4c08f06fe..f3381a8fda 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -2,7 +2,7 @@ #define GAME_MWBASE_SOUNDMANAGER_H #include - +#include #include #include "../mwworld/ptr.hpp" @@ -72,7 +72,7 @@ namespace MWBase virtual ~SoundManager() {} - virtual void processChangedSettings(const std::vector< std::pair >& settings) = 0; + virtual void processChangedSettings(const std::set< std::pair >& settings) = 0; virtual void stopMusic() = 0; ///< Stops music if it's playing diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index fc745d3e68..c984b5e541 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -271,7 +271,7 @@ namespace MWBase */ virtual std::string getGameSettingString(const std::string &id, const std::string &default_) = 0; - virtual void processChangedSettings(const std::vector< std::pair >& changed) = 0; + virtual void processChangedSettings(const std::set< std::pair >& changed) = 0; virtual void windowResized(int x, int y) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3af2a27752..154d96f7d4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -386,7 +387,7 @@ namespace MWBase virtual bool canPlaceObject (float cursorX, float cursorY) = 0; ///< @return true if it is possible to place on object at specified cursor location - virtual void processChangedSettings (const std::vector< std::pair >& settings) = 0; + virtual void processChangedSettings (const std::set< std::pair >& settings) = 0; virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const = 0; diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index 5fc2ca3c11..a9a78d0351 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -1,95 +1,144 @@ #include "settings.hpp" #include -#include -#include -#include #include -#include -#include - -using namespace Settings; +#include +#include +#include -namespace bfs = boost::filesystem; +namespace Settings +{ -Ogre::ConfigFile Manager::mFile = Ogre::ConfigFile(); -Ogre::ConfigFile Manager::mDefaultFile = Ogre::ConfigFile(); +CategorySettingValueMap Manager::mDefaultSettings = CategorySettingValueMap(); +CategorySettingValueMap Manager::mUserSettings = CategorySettingValueMap(); CategorySettingVector Manager::mChangedSettings = CategorySettingVector(); -CategorySettingValueMap Manager::mNewSettings = CategorySettingValueMap(); - -void Manager::loadUser (const std::string& file) -{ - Ogre::DataStreamPtr stream = openConstrainedFileDataStream(file.c_str()); - mFile.load(stream); -} -void Manager::loadDefault (const std::string& file) -{ - Ogre::DataStreamPtr stream = openConstrainedFileDataStream(file.c_str()); - mDefaultFile.load(stream); -} -void Manager::saveUser(const std::string& file) +class SettingsFileParser { - bfs::ofstream fout((bfs::path(file))); +public: + SettingsFileParser() : mLine(0) {} - Ogre::ConfigFile::SectionIterator seci = mFile.getSectionIterator(); - - while (seci.hasMoreElements()) + void loadSettingsFile (const std::string& file, CategorySettingValueMap& settings) { - Ogre::String sectionName = seci.peekNextKey(); + mFile = file; + boost::filesystem::ifstream stream; + stream.open(boost::filesystem::path(file)); + std::string currentCategory; + mLine = 0; + while (!stream.eof() && !stream.fail()) + { + ++mLine; + std::string line; + std::getline( stream, line ); - if (sectionName.length() > 0) - fout << '\n' << '[' << seci.peekNextKey() << ']' << '\n'; + size_t i = 0; + if (!skipWhiteSpace(i, line)) + continue; - Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext(); - Ogre::ConfigFile::SettingsMultiMap::iterator i; - for (i = settings->begin(); i != settings->end(); ++i) - { - fout << i->first.c_str() << " = " << i->second.c_str() << '\n'; - } + if (line[i] == '#') // skip comment + continue; - CategorySettingValueMap::iterator it = mNewSettings.begin(); - while (it != mNewSettings.end()) - { - if (it->first.first == sectionName) + if (line[i] == '[') { - fout << it->first.second << " = " << it->second << '\n'; - mNewSettings.erase(it++); + size_t end = line.find(']', i); + if (end == std::string::npos) + fail("unterminated category"); + + currentCategory = line.substr(i+1, end - (i+1)); + boost::algorithm::trim(currentCategory); + i = end+1; } - else - ++it; + + if (!skipWhiteSpace(i, line)) + continue; + + if (currentCategory.empty()) + fail("empty category name"); + + size_t settingEnd = line.find('=', i); + if (settingEnd == std::string::npos) + fail("unterminated setting name"); + + std::string setting = line.substr(i, (settingEnd-i)); + boost::algorithm::trim(setting); + + size_t valueBegin = settingEnd+1; + std::string value = line.substr(valueBegin); + boost::algorithm::trim(value); + + if (settings.insert(std::make_pair(std::make_pair(currentCategory, setting), value)).second == false) + fail(std::string("duplicate setting: [" + currentCategory + "] " + setting)); } } - std::string category = ""; - for (CategorySettingValueMap::iterator it = mNewSettings.begin(); - it != mNewSettings.end(); ++it) +private: + /// Increment i until it longer points to a whitespace character + /// in the string or has reached the end of the string. + /// @return false if we have reached the end of the string + bool skipWhiteSpace(size_t& i, std::string& str) { - if (category != it->first.first) + while (i < str.size() && std::isspace(str[i], std::locale::classic())) { - category = it->first.first; - fout << '\n' << '[' << category << ']' << '\n'; + ++i; } - fout << it->first.second << " = " << it->second << '\n'; + return i < str.size(); } - fout.close(); + void fail(const std::string& message) + { + std::stringstream error; + error << "Error on line " << mLine << " in " << mFile << ":\n" << message; + throw std::runtime_error(error.str()); + } + + std::string mFile; + int mLine; +}; + +void Manager::loadDefault(const std::string &file) +{ + SettingsFileParser parser; + parser.loadSettingsFile(file, mDefaultSettings); } -std::string Manager::getString (const std::string& setting, const std::string& category) +void Manager::loadUser(const std::string &file) { - if (mNewSettings.find(std::make_pair(category, setting)) != mNewSettings.end()) - return mNewSettings[std::make_pair(category, setting)]; + SettingsFileParser parser; + parser.loadSettingsFile(file, mUserSettings); +} + +void Manager::saveUser(const std::string &file) +{ + boost::filesystem::ofstream stream; + stream.open(boost::filesystem::path(file)); + std::string currentCategory; + for (CategorySettingValueMap::iterator it = mUserSettings.begin(); it != mUserSettings.end(); ++it) + { + if (it->first.first != currentCategory) + { + currentCategory = it->first.first; + stream << "\n[" << currentCategory << "]\n"; + } + stream << it->first.second << " = " << it->second << "\n"; + } +} + +std::string Manager::getString(const std::string &setting, const std::string &category) +{ + CategorySettingValueMap::key_type key = std::make_pair(category, setting); + CategorySettingValueMap::iterator it = mUserSettings.find(key); + if (it != mUserSettings.end()) + return it->second; - std::string defaultval = mDefaultFile.getSetting(setting, category, "NOTFOUND"); - std::string val = mFile.getSetting(setting, category, defaultval); + it = mDefaultSettings.find(key); + if (it != mDefaultSettings.end()) + return it->second; - if (val == "NOTFOUND") - throw std::runtime_error("Trying to retrieve a non-existing setting: " + setting + " Make sure the settings-default.cfg file was properly installed."); - return val; + throw std::runtime_error(std::string("Trying to retrieve a non-existing setting: ") + setting + + ".\nMake sure the settings-default.cfg file file was properly installed."); } float Manager::getFloat (const std::string& setting, const std::string& category) @@ -107,51 +156,20 @@ bool Manager::getBool (const std::string& setting, const std::string& category) return Ogre::StringConverter::parseBool( getString(setting, category) ); } -void Manager::setString (const std::string& setting, const std::string& category, const std::string& value) +void Manager::setString(const std::string &setting, const std::string &category, const std::string &value) { - CategorySetting s = std::make_pair(category, setting); + CategorySettingValueMap::key_type key = std::make_pair(category, setting); - bool found=false; - try + CategorySettingValueMap::iterator found = mUserSettings.find(key); + if (found != mUserSettings.end()) { - Ogre::ConfigFile::SettingsIterator it = mFile.getSettingsIterator(category); - while (it.hasMoreElements()) - { - Ogre::ConfigFile::SettingsMultiMap::iterator i = it.current(); - - if ((*i).first == setting) - { - if ((*i).second != value) - { - mChangedSettings.push_back(std::make_pair(category, setting)); - (*i).second = value; - } - found = true; - } - - it.getNext(); - } + if (found->second == value) + return; } - catch (Ogre::Exception&) - {} - if (!found) - { - if (mNewSettings.find(s) != mNewSettings.end()) - { - if (mNewSettings[s] != value) - { - mChangedSettings.push_back(std::make_pair(category, setting)); - mNewSettings[s] = value; - } - } - else - { - if (mDefaultFile.getSetting(setting, category) != value) - mChangedSettings.push_back(std::make_pair(category, setting)); - mNewSettings[s] = value; - } - } + mUserSettings[key] = value; + + mChangedSettings.insert(key); } void Manager::setInt (const std::string& setting, const std::string& category, const int value) @@ -159,12 +177,12 @@ void Manager::setInt (const std::string& setting, const std::string& category, c setString(setting, category, Ogre::StringConverter::toString(value)); } -void Manager::setFloat (const std::string& setting, const std::string& category, const float value) +void Manager::setFloat (const std::string &setting, const std::string &category, const float value) { setString(setting, category, Ogre::StringConverter::toString(value)); } -void Manager::setBool (const std::string& setting, const std::string& category, const bool value) +void Manager::setBool(const std::string &setting, const std::string &category, const bool value) { setString(setting, category, Ogre::StringConverter::toString(value)); } @@ -175,3 +193,5 @@ const CategorySettingVector Manager::apply() mChangedSettings.clear(); return vec; } + +} diff --git a/components/settings/settings.hpp b/components/settings/settings.hpp index 03b0b517ca..c16ff5a1ef 100644 --- a/components/settings/settings.hpp +++ b/components/settings/settings.hpp @@ -1,12 +1,14 @@ #ifndef COMPONENTS_SETTINGS_H #define COMPONENTS_SETTINGS_H -#include +#include +#include +#include namespace Settings { typedef std::pair < std::string, std::string > CategorySetting; - typedef std::vector< std::pair > CategorySettingVector; + typedef std::set< std::pair > CategorySettingVector; typedef std::map < CategorySetting, std::string > CategorySettingValueMap; /// @@ -15,15 +17,12 @@ namespace Settings class Manager { public: - static Ogre::ConfigFile mFile; - static Ogre::ConfigFile mDefaultFile; + static CategorySettingValueMap mDefaultSettings; + static CategorySettingValueMap mUserSettings; static CategorySettingVector mChangedSettings; ///< tracks all the settings that were changed since the last apply() call - static CategorySettingValueMap mNewSettings; - ///< tracks all the settings that are in the default file, but not in user file yet - void loadDefault (const std::string& file); ///< load file as the default settings (can be overridden by user settings) From 955505c167800109590f4c56f40c679cef344611 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 3 Feb 2015 01:05:40 +0100 Subject: [PATCH 06/26] Remove unused function --- apps/openmw/engine.cpp | 10 +--------- apps/openmw/engine.hpp | 4 ---- apps/openmw/mwgui/windowmanagerimp.cpp | 5 ++--- apps/openmw/mwgui/windowmanagerimp.hpp | 3 +-- 4 files changed, 4 insertions(+), 18 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index a9a7f06324..c70acaba8d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -170,7 +170,6 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) : mOgre (0) - , mFpsLevel(0) , mVerboseScripts (false) , mSkipMenu (false) , mUseSound (true) @@ -297,8 +296,6 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) if (boost::filesystem::exists(settingspath)) settings.loadUser(settingspath); - mFpsLevel = settings.getInt("fps", "HUD"); - // load nif overrides NifOverrides::Overrides nifOverrides; std::string transparencyOverrides = "/transparency-overrides.cfg"; @@ -373,7 +370,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.setInputManager (input); MWGui::WindowManager* window = new MWGui::WindowManager( - mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), + mExtensions, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap); mEnvironment.setWindowManager (window); @@ -574,11 +571,6 @@ void OMW::Engine::setSoundUsage(bool soundUsage) mUseSound = soundUsage; } -void OMW::Engine::showFPS(int level) -{ - mFpsLevel = level; -} - void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding) { mEncoding = encoding; diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 079dd922cc..3b088595c0 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -71,7 +71,6 @@ namespace OMW OEngine::Render::OgreRenderer *mOgre; std::string mCellName; std::vector mContentFiles; - int mFpsLevel; bool mVerboseScripts; bool mSkipMenu; bool mUseSound; @@ -151,9 +150,6 @@ namespace OMW */ void addContentFile(const std::string& file); - /// Enable fps counter - void showFPS(int level); - /// Enable or disable verbose script output void setScriptsVerbosity(bool scriptsVerbosity); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index a35498deaa..715f47c46e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -96,7 +96,7 @@ namespace MWGui { WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *ogre, + const Compiler::Extensions& extensions, OEngine::Render::OgreRenderer *ogre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap) : mConsoleOnlyScripts(consoleOnlyScripts) @@ -167,7 +167,6 @@ namespace MWGui , mForceHidden(GW_None) , mAllowed(GW_ALL) , mRestAllowed(true) - , mShowFPSLevel(fpsLevel) , mFPS(0.0f) , mTriangleCount(0) , mBatchCount(0) @@ -269,7 +268,7 @@ namespace MWGui trackWindow(mDialogueWindow, "dialogue"); mContainerWindow = new ContainerWindow(mDragAndDrop); trackWindow(mContainerWindow, "container"); - mHud = new HUD(mCustomMarkers, mShowFPSLevel, mDragAndDrop); + mHud = new HUD(mCustomMarkers, Settings::Manager::getInt("fps", "HUD"), mDragAndDrop); mToolTips = new ToolTips(); mScrollWindow = new ScrollWindow(); mBookWindow = new BookWindow(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index b3eb9cd0a9..9f01b136cf 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -97,7 +97,7 @@ namespace MWGui typedef std::pair Faction; typedef std::vector FactionList; - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, + WindowManager(const Compiler::Extensions& extensions, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap); @@ -451,7 +451,6 @@ namespace MWGui void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings - int mShowFPSLevel; float mFPS; unsigned int mTriangleCount; unsigned int mBatchCount; From ab9100fa904898606cc41f6df706b85a583bc2dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 3 Feb 2015 13:12:58 +0100 Subject: [PATCH 07/26] Prevent dropping item on itself in ContentModel (Fixes #2334) Also improves the drop indicator, it is now shown in between items instead of on an item. --- components/contentselector/model/contentmodel.cpp | 7 +++---- components/contentselector/model/contentmodel.hpp | 1 - components/contentselector/view/contentselector.cpp | 3 ++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index b81b1cbf6a..198bbffbb1 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -15,8 +15,7 @@ ContentSelectorModel::ContentModel::ContentModel(QObject *parent, QIcon warningI mMimeType ("application/omwcontent"), mMimeTypes (QStringList() << mMimeType), mColumnCount (1), - mDragDropFlags (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled), - mDropActions (Qt::CopyAction | Qt::MoveAction) + mDropActions (Qt::MoveAction) { setEncoding ("win1252"); uncheckAll(); @@ -104,7 +103,7 @@ QModelIndex ContentSelectorModel::ContentModel::indexFromItem(const EsmFile *ite Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index) const { if (!index.isValid()) - return Qt::NoItemFlags; + return Qt::ItemIsDropEnabled; const EsmFile *file = item(index.row()); @@ -152,7 +151,7 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index if (gamefileChecked) { if (allDependenciesFound) - returnFlags = returnFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | mDragDropFlags; + returnFlags = returnFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled; else returnFlags = Qt::ItemIsSelectable; } diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index 2ced40f047..6af5425c77 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -89,7 +89,6 @@ namespace ContentSelectorModel QString mMimeType; QStringList mMimeTypes; int mColumnCount; - Qt::ItemFlags mDragDropFlags; Qt::DropActions mDropActions; }; } diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 2d560e054b..ad023e5b22 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -15,7 +15,8 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : QObject(parent) { - ui.setupUi (parent); + ui.setupUi(parent); + ui.addonView->setDragDropMode(QAbstractItemView::InternalMove); buildContentModel(); buildGameFileView(); From 2bf1dd8f46fa3bf19fb5416bc6b269631495a84f Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 3 Feb 2015 14:01:31 +0100 Subject: [PATCH 08/26] Make the status badges consistent, again --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1698a7ebc3..ed21b852f5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ OpenMW ====== -[![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) +[![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg?style=plastic)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. From aa6ebcd75cd95c5423b7216759f19cfc6d65224a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 3 Feb 2015 23:43:56 +0100 Subject: [PATCH 09/26] Change stolen items handling to match MW (Fixes #1443, Fixes #2290) --- apps/openmw/mwbase/mechanicsmanager.hpp | 4 +++- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 24 ++++++++++++++++--- .../mwmechanics/mechanicsmanagerimp.hpp | 4 +++- apps/openmw/mwworld/actiontake.cpp | 2 +- 6 files changed, 30 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 33a2ef1eaa..58f2f6ee53 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -129,7 +129,9 @@ namespace MWBase /// @return false if the attack was considered a "friendly hit" and forgiven virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0; /// Utility to check if taking this item is illegal and calling commitCrime if so - virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0; + /// @param container The container the item is in; may be empty for an item in the world + virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container, + int count) = 0; /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0; /// Attempt sleeping in a bed. If this is illegal, call commitCrime. diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 53b1e679a0..579730f42f 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -285,7 +285,7 @@ namespace MWGui if (mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead()) return true; else - MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, count); + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, mPtr, count); } return true; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index ae7d60d442..5cb67c048d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -624,7 +624,7 @@ namespace MWGui throw std::runtime_error("Added item not found"); mDragAndDrop->startDrag(i, mSortModel, mTradeModel, mItemView, count); - MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, count); + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, MWWorld::Ptr(), count); if (MWBase::Environment::get().getWindowManager()->getSpellWindow()) MWBase::Environment::get().getWindowManager()->getSpellWindow()->updateSpells(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index d52dcb43c2..caa242338c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -936,11 +936,29 @@ namespace MWMechanics commitCrime(ptr, victim, OT_Trespassing); } - void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count) + void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, const MWWorld::Ptr& container, + int count) { MWWorld::Ptr victim; - if (isAllowedToUse(ptr, item, victim)) - return; + + if (!container.isEmpty()) + { + // Inherit the owner of the container + if (isAllowedToUse(ptr, container, victim)) + return; + } + else + { + if (isAllowedToUse(ptr, item, victim)) + return; + + if (!item.getCellRef().hasContentFile()) + { + // this is a manually placed item, which means it was already stolen + return; + } + } + commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 2f443ad592..79580c0502 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -121,7 +121,9 @@ namespace MWMechanics /// @return false if the attack was considered a "friendly hit" and forgiven virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); /// Utility to check if taking this item is illegal and calling commitCrime if so - virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count); + /// @param container The container the item is in; may be empty for an item in the world + virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container, + int count); /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item); /// Attempt sleeping in a bed. If this is illegal, call commitCrime. diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 548a949819..269d941dc6 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -16,7 +16,7 @@ namespace MWWorld void ActionTake::executeImp (const Ptr& actor) { MWBase::Environment::get().getMechanicsManager()->itemTaken( - actor, getTarget(), getTarget().getRefData().getCount()); + actor, getTarget(), MWWorld::Ptr(), getTarget().getRefData().getCount()); actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor); MWBase::Environment::get().getWorld()->deleteObject (getTarget()); } From 8087a7d920ebd20d63f0b820856a45e795b9ab2c Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Feb 2015 00:21:35 +0100 Subject: [PATCH 10/26] Add possibility to dress up corpses (Fixes #2221) --- apps/openmw/mwworld/inventorystore.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 4950be9126..020f9561a5 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -133,12 +133,11 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner); // Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves - if ((actorPtr.getRefData().getHandle() != "player") - && !(actorPtr.getClass().isNpc() && actorPtr.getClass().getNpcStats(actorPtr).isWerewolf()) - && !actorPtr.getClass().getCreatureStats(actorPtr).isDead()) + if (actorPtr.getRefData().getHandle() != "player" + && !(actorPtr.getClass().isNpc() && actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())) { std::string type = itemPtr.getTypeName(); - if ((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name()) || (type == typeid(ESM::Weapon).name())) + if (type == typeid(ESM::Armor).name() || type == typeid(ESM::Clothing).name()) autoEquip(actorPtr); } @@ -234,7 +233,9 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) // ...unless this is a companion, he should always equip items given to him. if (!Misc::StringUtils::ciEqual(test.getCellRef().getOwner(), actor.getCellRef().getRefId()) && (actor.getClass().getScript(actor).empty() || - !actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))) + !actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion")) + && !actor.getClass().getCreatureStats(actor).isDead() // Corpses can be dressed up by the player as desired + ) { continue; } @@ -506,8 +507,7 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor && !(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())) { std::string type = item.getTypeName(); - if (((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name())) - && !actor.getClass().getCreatureStats(actor).isDead()) + if (type == typeid(ESM::Armor).name() || type == typeid(ESM::Clothing).name()) autoEquip(actor); } From f8b4ff300412344c652a104b8d77ecc7aa281b0f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Feb 2015 01:25:37 +0100 Subject: [PATCH 11/26] Documentation fix --- components/terrain/quadtreenode.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 6265727011..e8392b8e40 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -98,7 +98,6 @@ namespace Terrain DefaultWorld* getTerrain() { return mTerrain; } /// Adjust LODs for the given camera position, possibly splitting up chunks or merging them. - /// @param force Always choose to render this node, even if not the perfect LOD. /// @return Did we (or all of our children) choose to render? bool update (const Ogre::Vector3& cameraPos); @@ -124,7 +123,6 @@ namespace Terrain /// call this method on their children. /// @note Do not call this before World::areLayersLoaded() == true /// @param area area in image space to put the quad - /// @param quads collect quads here so they can be deleted later void prepareForCompositeMap(Ogre::TRect area); /// Create a chunk for this node from the given data. From c1862cbfc28063be922c98f39e2a7489596a7211 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Feb 2015 02:39:47 +0100 Subject: [PATCH 12/26] Clarify documentation for --load-savegame --- README.md | 4 +++- apps/openmw/main.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed21b852f5..9207c5728d 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,9 @@ Command line options of the blacklist is enabled) --script-blacklist-use [=arg(=1)] (=1) enable script blacklisting - --load-savegame arg load a save game file on game startup + --load-savegame arg load a save game file on game startup + (specify an absolute or relative + filename for this option) --skip-menu [=arg(=1)] (=0) skip main menu on game startup --new-game [=arg(=1)] (=0) run new game sequence (ignored if skip-menu=0) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index b96b90989c..f4875e68a1 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -153,7 +153,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ->default_value(true), "enable script blacklisting") ("load-savegame", bpo::value()->default_value(""), - "load a save game file on game startup") + "load a save game file on game startup (specify an absolute or relative filename for this option)") ("skip-menu", bpo::value()->implicit_value(true) ->default_value(false), "skip main menu on game startup") From bea88c3643b78ddf81dbb50a367688b2b16589f9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Feb 2015 21:18:43 +0100 Subject: [PATCH 13/26] Stolen item tracking overhaul part 2 (Fixes #2338) --- apps/openmw/mwbase/mechanicsmanager.hpp | 12 +- apps/openmw/mwclass/container.cpp | 9 +- apps/openmw/mwclass/creature.cpp | 5 +- apps/openmw/mwclass/npc.cpp | 6 +- apps/openmw/mwgui/enchantingdialog.cpp | 3 +- apps/openmw/mwgui/inventoryitemmodel.cpp | 11 +- apps/openmw/mwgui/itemmodel.hpp | 13 +- apps/openmw/mwgui/tooltips.cpp | 10 ++ apps/openmw/mwgui/tradeitemmodel.cpp | 4 +- apps/openmw/mwgui/tradewindow.cpp | 5 +- .../mwmechanics/mechanicsmanagerimp.cpp | 120 +++++++++++++++--- .../mwmechanics/mechanicsmanagerimp.hpp | 20 ++- apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/cellref.cpp | 9 ++ apps/openmw/mwworld/cellref.hpp | 2 + apps/openmw/mwworld/class.hpp | 2 + apps/openmw/mwworld/containerstore.cpp | 53 ++++---- apps/openmw/mwworld/containerstore.hpp | 8 +- apps/openmw/mwworld/worldimp.cpp | 14 +- components/CMakeLists.txt | 2 +- components/esm/defs.hpp | 1 + components/esm/stolenitems.cpp | 47 +++++++ components/esm/stolenitems.hpp | 24 ++++ 23 files changed, 289 insertions(+), 92 deletions(-) create mode 100644 components/esm/stolenitems.cpp create mode 100644 components/esm/stolenitems.hpp diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 58f2f6ee53..f7fc515f57 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -138,9 +138,6 @@ namespace MWBase /// @return was it illegal, and someone saw you doing it? virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0; - /// @return is \a ptr allowed to take/use \a item or is it a crime? - virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim) = 0; - enum PersuasionType { PT_Admire, @@ -205,6 +202,15 @@ namespace MWBase virtual void keepPlayerAlive() = 0; virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0; + + virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0; + + /// List the owners that the player has stolen this item from (the owner can be an NPC or a faction). + /// + virtual std::vector > getStolenItemOwners(const std::string& itemid) = 0; + + /// Has the player stolen this item from the given owner? + virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0; }; } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index c6a7bbf741..5f49a74b6c 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -59,8 +59,10 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); + // setting ownership not needed, since taking items from a container inherits the + // container's owner automatically data->mContainerStore.fill( - ref->mBase->mInventory, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank(), MWBase::Environment::get().getWorld()->getStore()); + ref->mBase->mInventory, ""); // store ptr.getRefData().setCustomData (data.release()); @@ -82,7 +84,10 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; MWWorld::ContainerStore& store = getContainerStore(ptr); - store.restock(list, ptr, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank()); + + // setting ownership not needed, since taking items from a container inherits the + // container's owner automatically + store.restock(list, ptr, ""); } void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index df3f9c0d30..e44f4ffc12 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -139,8 +139,7 @@ namespace MWClass // store ptr.getRefData().setCustomData(data.release()); - getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", -1, - MWBase::Environment::get().getWorld()->getStore()); + getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr)); if (ref->mBase->mFlags & ESM::Creature::Weapon) getInventoryStore(ptr).autoEquip(ptr); @@ -886,7 +885,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; MWWorld::ContainerStore& store = getContainerStore(ptr); - store.restock(list, ptr, ptr.getCellRef().getRefId(), "", -1); + store.restock(list, ptr, ptr.getCellRef().getRefId()); } int Creature::getBaseFightRating(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c55f81d91c..cedff12918 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -384,8 +384,8 @@ namespace MWClass } // inventory - data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", -1, - MWBase::Environment::get().getWorld()->getStore()); + // setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items + data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr)); data->mNpcStats.setGoldPool(gold); @@ -1328,7 +1328,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; MWWorld::ContainerStore& store = getContainerStore(ptr); - store.restock(list, ptr, ptr.getCellRef().getRefId(), "", -1); + store.restock(list, ptr, ptr.getCellRef().getRefId()); } int Npc::getBaseFightRating (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 3376858c1c..43f2493a98 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -339,7 +339,8 @@ namespace MWGui for (int i=0; i<2; ++i) { MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem(); - if (Misc::StringUtils::ciEqual(item.getCellRef().getOwner(), mPtr.getCellRef().getRefId())) + if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(), + mPtr.getCellRef().getRefId())) { std::string msg = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage49")->getString(); if (msg.find("%s") != std::string::npos) diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index cee0dc6eed..e8354b7406 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -65,16 +65,7 @@ MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack &item, size_t count, I if (item.mFlags & ItemStack::Flag_Bound) return MWWorld::Ptr(); - bool setNewOwner = false; - - // Are you dead? Then you wont need that anymore - if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead() - // Make sure that the item is actually owned by the dead actor - // Prevents a potential exploit for resetting the owner of any item, by placing the item in a corpse - && Misc::StringUtils::ciEqual(item.mBase.getCellRef().getOwner(), mActor.getCellRef().getRefId())) - setNewOwner = true; - - MWWorld::Ptr ret = otherModel->copyItem(item, count, setNewOwner); + MWWorld::Ptr ret = otherModel->copyItem(item, count, false); removeItem(item, count); return ret; } diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index c1b243a761..53c7018e22 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -46,20 +46,27 @@ namespace MWGui ItemModel(); virtual ~ItemModel() {} - typedef int ModelIndex; + typedef int ModelIndex; // -1 means invalid index + /// Throws for invalid index or out of range index virtual ItemStack getItem (ModelIndex index) = 0; - ///< throws for invalid index + + /// The number of items in the model, this specifies the range of indices you can pass to + /// the getItem function (but this range is only valid until the next call to update()) virtual size_t getItemCount() = 0; + /// Returns an invalid index if the item was not found virtual ModelIndex getIndex (ItemStack item) = 0; + /// Rebuild the item model, this will invalidate existing model indices virtual void update() = 0; /// Move items from this model to \a otherModel. + /// @note Derived implementations may return an empty Ptr if the move was unsuccessful. virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); - /// @param setNewOwner Set the copied item's owner to the actor we are copying to, or keep the original owner? + /// @param setNewOwner If true, set the copied item's owner to the actor we are copying to, + /// otherwise reset owner to "" virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0; virtual void removeItem (const ItemStack& item, size_t count) = 0; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 2750717372..c6210ac642 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -13,6 +13,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" @@ -586,6 +587,15 @@ namespace MWGui ret += getMiscString(cellref.getFaction(), "Faction"); if (cellref.getFactionRank() > 0) ret += getValueString(cellref.getFactionRank(), "Rank"); + + std::vector > itemOwners = + MWBase::Environment::get().getMechanicsManager()->getStolenItemOwners(cellref.getRefId()); + + for (std::vector >::const_iterator it = itemOwners.begin(); it != itemOwners.end(); ++it) + { + ret += std::string("\nStolen ") + MyGUI::utility::toString(it->second) + " from " + it->first; + } + ret += getMiscString(cellref.getGlobalVariable(), "Global"); return ret; } diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index df0cbcac95..a5a2b3e881 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -135,11 +135,9 @@ namespace MWGui if (i == sourceModel->getItemCount()) throw std::runtime_error("The borrowed item disappeared"); - // reset owner while copying, but only for items bought by the player - bool setNewOwner = (mMerchant.isEmpty()); const ItemStack& item = sourceModel->getItem(i); // copy the borrowed items to our model - copyItem(item, it->mCount, setNewOwner); + copyItem(item, it->mCount); // then remove them from the source model sourceModel->removeItem(item, it->mCount); } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index fab7c63a6c..87b2362efa 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -300,7 +300,8 @@ namespace MWGui // 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().getOwner(), mPtr.getCellRef().getRefId())) + if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(it->mBase.getCellRef().getRefId(), + mPtr.getCellRef().getRefId())) { std::string msg = gmst.find("sNotifyMessage49")->getString(); if (msg.find("%s") != std::string::npos) @@ -315,6 +316,8 @@ namespace MWGui } } + // TODO: move to mwmechanics + // Is the player buying? bool buying = (mCurrentMerchantOffer < 0); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index caa242338c..4e4a1a8a69 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -2,6 +2,8 @@ #include "mechanicsmanagerimp.hpp" #include "npcstats.hpp" +#include + #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" @@ -873,31 +875,31 @@ namespace MWMechanics mAI = true; } - bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim) + bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim) { - const std::string& owner = item.getCellRef().getOwner(); + const std::string& owner = cellref.getOwner(); bool isOwned = !owner.empty() && owner != "player"; - const std::string& faction = item.getCellRef().getFaction(); + const std::string& faction = cellref.getFaction(); bool isFactionOwned = false; if (!faction.empty() && ptr.getClass().isNpc()) { const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); std::map::const_iterator found = factions.find(Misc::StringUtils::lowerCase(faction)); if (found == factions.end() - || found->second < item.getCellRef().getFactionRank()) + || found->second < cellref.getFactionRank()) isFactionOwned = true; } - const std::string& globalVariable = item.getCellRef().getGlobalVariable(); + const std::string& globalVariable = cellref.getGlobalVariable(); if (!globalVariable.empty() && MWBase::Environment::get().getWorld()->getGlobalInt(Misc::StringUtils::lowerCase(globalVariable)) == 1) { isOwned = false; isFactionOwned = false; } - if (!item.getCellRef().getOwner().empty()) - victim = MWBase::Environment::get().getWorld()->searchPtr(item.getCellRef().getOwner(), true); + if (!cellref.getOwner().empty()) + victim = MWBase::Environment::get().getWorld()->searchPtr(cellref.getOwner(), true); return (!isOwned && !isFactionOwned); } @@ -916,7 +918,7 @@ namespace MWMechanics } MWWorld::Ptr victim; - if (isAllowedToUse(ptr, bed, victim)) + if (isAllowedToUse(ptr, bed.getCellRef(), victim)) return false; if(commitCrime(ptr, victim, OT_SleepingInOwnedBed)) @@ -931,27 +933,82 @@ namespace MWMechanics void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item) { MWWorld::Ptr victim; - if (isAllowedToUse(ptr, item, victim)) + if (isAllowedToUse(ptr, item.getCellRef(), victim)) return; commitCrime(ptr, victim, OT_Trespassing); } + std::vector > MechanicsManager::getStolenItemOwners(const std::string& itemid) + { + std::vector > result; + StolenItemsMap::const_iterator it = mStolenItems.find(Misc::StringUtils::lowerCase(itemid)); + if (it == mStolenItems.end()) + return result; + else + { + const OwnerMap& owners = it->second; + for (OwnerMap::const_iterator ownerIt = owners.begin(); ownerIt != owners.end(); ++ownerIt) + result.push_back(std::make_pair(ownerIt->first.first, ownerIt->second)); + return result; + } + } + + bool MechanicsManager::isItemStolenFrom(const std::string &itemid, const std::string &ownerid) + { + StolenItemsMap::const_iterator it = mStolenItems.find(Misc::StringUtils::lowerCase(itemid)); + if (it == mStolenItems.end()) + return false; + const OwnerMap& owners = it->second; + OwnerMap::const_iterator ownerFound = owners.find(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false)); + return ownerFound != owners.end(); + } + + void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer) + { + MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + StolenItemsMap::iterator stolenIt = mStolenItems.find(Misc::StringUtils::lowerCase(it->getClass().getId(*it))); + if (stolenIt == mStolenItems.end()) + continue; + OwnerMap& owners = stolenIt->second; + int itemCount = it->getRefData().getCount(); + for (OwnerMap::iterator ownerIt = owners.begin(); ownerIt != owners.end();) + { + int toRemove = std::min(itemCount, ownerIt->second); + itemCount -= toRemove; + ownerIt->second -= toRemove; + if (ownerIt->second == 0) + owners.erase(ownerIt++); + else + ++ownerIt; + } + + int toMove = it->getRefData().getCount() - itemCount; + + targetContainer.getClass().getContainerStore(targetContainer).add(*it, toMove, targetContainer); + store.remove(*it, toMove, player); + } + // TODO: unhardcode the locklevel + targetContainer.getClass().lock(targetContainer,50); + } + void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, const MWWorld::Ptr& container, int count) { + if (ptr != MWBase::Environment::get().getWorld()->getPlayerPtr()) + return; + MWWorld::Ptr victim; + const MWWorld::CellRef* ownerCellRef = &item.getCellRef(); if (!container.isEmpty()) { // Inherit the owner of the container - if (isAllowedToUse(ptr, container, victim)) - return; + ownerCellRef = &container.getCellRef(); } else { - if (isAllowedToUse(ptr, item, victim)) - return; - if (!item.getCellRef().hasContentFile()) { // this is a manually placed item, which means it was already stolen @@ -959,6 +1016,20 @@ namespace MWMechanics } } + if (isAllowedToUse(ptr, *ownerCellRef, victim)) + return; + + Owner owner; + owner.first = ownerCellRef->getOwner(); + owner.second = false; + if (owner.first.empty()) + { + owner.first = ownerCellRef->getFaction(); + owner.second = true; + } + Misc::StringUtils::toLower(owner.first); + mStolenItems[Misc::StringUtils::lowerCase(item.getClass().getId(item))][owner] += count; + commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } @@ -967,7 +1038,7 @@ namespace MWMechanics // NOTE: victim may be empty // Only player can commit crime - if (player.getRefData().getHandle() != "player") + if (player != MWBase::Environment::get().getWorld()->getPlayerPtr()) return false; // Find all the actors within the alarm radius @@ -1369,22 +1440,37 @@ namespace MWMechanics int MechanicsManager::countSavedGameRecords() const { - return 1; // Death counter + return 1 // Death counter + +1; // Stolen items } void MechanicsManager::write(ESM::ESMWriter &writer, Loading::Listener &listener) const { mActors.write(writer, listener); + + ESM::StolenItems items; + items.mStolenItems = mStolenItems; + writer.startRecord(ESM::REC_STLN); + items.write(writer); + writer.endRecord(ESM::REC_STLN); } void MechanicsManager::readRecord(ESM::ESMReader &reader, uint32_t type) { - mActors.readRecord(reader, type); + if (type == ESM::REC_STLN) + { + ESM::StolenItems items; + items.load(reader); + mStolenItems = items.mStolenItems; + } + else + mActors.readRecord(reader, type); } void MechanicsManager::clear() { mActors.clear(); + mStolenItems.clear(); } bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 79580c0502..d08334ae8c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -35,6 +35,11 @@ namespace MWMechanics Objects mObjects; Actors mActors; + typedef std::pair Owner; // < Owner id, bool isFaction > + typedef std::map OwnerMap; // < Owner, number of stolen items with this id from this owner > + typedef std::map StolenItemsMap; + StolenItemsMap mStolenItems; + public: void buildPlayer(); @@ -130,9 +135,6 @@ namespace MWMechanics /// @return was it illegal, and someone saw you doing it? Also returns fail when enemies are nearby virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed); - /// @return is \a ptr allowed to take/use \a item or is it a crime? - virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim); - virtual void forceStateUpdate(const MWWorld::Ptr &ptr); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); @@ -170,9 +172,21 @@ namespace MWMechanics virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const; + virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer); + + /// List the owners that the player has stolen this item from (the owner can be an NPC or a faction). + /// + virtual std::vector > getStolenItemOwners(const std::string& itemid); + + /// Has the player stolen this item from the given owner? + virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid); + private: void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0); + + /// @return is \a ptr allowed to take/use \a cellref or is it a crime? + virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index d623ba4654..0883bc63b3 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -421,6 +421,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str break; case ESM::REC_DCOU: + case ESM::REC_STLN: MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.val); break; diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index d2cced3b86..0d81e06368 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -117,6 +117,15 @@ namespace MWWorld return mCellRef.mGlobalVariable; } + void CellRef::resetGlobalVariable() + { + if (!mCellRef.mGlobalVariable.empty()) + { + mChanged = true; + mCellRef.mGlobalVariable.erase(); + } + } + void CellRef::setFactionRank(int factionRank) { if (factionRank != mCellRef.mFactionRank) diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index f063b27272..b8e85f286e 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -75,6 +75,8 @@ namespace MWWorld // Used by bed rent scripts to allow the player to use the bed for the duration of the rent. std::string getGlobalVariable() const; + void resetGlobalVariable(); + // ID of creature trapped in this soul gem std::string getSoul() const; void setSoul(const std::string& soul); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 751375c556..380d2a34b5 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -80,6 +80,8 @@ namespace MWWorld virtual std::string getId (const Ptr& ptr) const; ///< Return ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) + /// @note This function is currently redundant; the same ID can be retrieved by CellRef::getRefId. + /// Leaving it here for now in case we want to optimize later. virtual void insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObject(const Ptr& ptr, const std::string& mesh, MWWorld::PhysicsSystem& physics) const; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index b21486c2cc..c2906e4474 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -222,29 +222,22 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr MWWorld::ContainerStoreIterator it = end(); - if (setOwner && actorPtr.getClass().isActor()) + // HACK: Set owner on the original item, then reset it after we have copied it + // If we set the owner on the copied item, it would not stack correctly... + std::string oldOwner = itemPtr.getCellRef().getOwner(); + if (!setOwner || actorPtr == MWBase::Environment::get().getWorld()->getPlayerPtr()) // No point in setting owner to the player - NPCs will not respect this anyway { - // HACK: Set owner on the original item, then reset it after we have copied it - // If we set the owner on the copied item, it would not stack correctly... - std::string oldOwner = itemPtr.getCellRef().getOwner(); - if (actorPtr == player) - { - // No point in setting owner to the player - NPCs will not respect this anyway - // Additionally, setting it to "player" would make those items not stack with items that don't have an owner - itemPtr.getCellRef().setOwner(""); - } - else - itemPtr.getCellRef().setOwner(actorPtr.getCellRef().getRefId()); - - it = addImp(itemPtr, count); - - itemPtr.getCellRef().setOwner(oldOwner); + itemPtr.getCellRef().setOwner(""); } else { - it = addImp(itemPtr, count); + itemPtr.getCellRef().setOwner(actorPtr.getCellRef().getRefId()); } + it = addImp(itemPtr, count); + + itemPtr.getCellRef().setOwner(oldOwner); + // The copy of the original item we just made MWWorld::Ptr item = *it; @@ -258,6 +251,14 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr pos.pos[1] = 0; pos.pos[2] = 0; item.getCellRef().setPosition(pos); + + // reset ownership stuff, owner was already handled above + item.getCellRef().resetGlobalVariable(); + item.getCellRef().setFaction(""); + item.getCellRef().setFactionRank(-1); + + // must reset the RefNum on the copied item, so that the RefNum on the original item stays unique + // maybe we should do this in the copy constructor instead? item.getCellRef().unsetRefNum(); // destroy link to content file std::string script = item.getClass().getScript(item); @@ -399,19 +400,19 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor return count - toRemove; } -void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, int factionRank, const MWWorld::ESMStore& store) +void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner) { for (std::vector::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); ++iter) { std::string id = Misc::StringUtils::lowerCase(iter->mItem.toString()); - addInitialItem(id, owner, faction, factionRank, iter->mCount); + addInitialItem(id, owner, iter->mCount); } flagAsModified(); } -void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int factionRank, +void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel, const std::string& levItem) { ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); @@ -423,7 +424,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: if (topLevel && std::abs(count) > 1 && levItem->mFlags & ESM::ItemLevList::Each) { for (int i=0; i 0 ? 1 : -1, true, levItem->mId); + addInitialItem(id, owner, count > 0 ? 1 : -1, true, levItem->mId); return; } else @@ -431,7 +432,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: std::string id = MWMechanics::getLevelledItem(ref.getPtr().get()->mBase, false); if (id.empty()) return; - addInitialItem(id, owner, faction, factionRank, count, false, levItem->mId); + addInitialItem(id, owner, count, false, levItem->mId); } } else @@ -447,13 +448,11 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: count = std::abs(count); ref.getPtr().getCellRef().setOwner(owner); - ref.getPtr().getCellRef().setFaction(faction); - ref.getPtr().getCellRef().setFactionRank(factionRank); addImp (ref.getPtr(), count); } } -void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner, const std::string& faction, int factionRank) +void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner) { // Remove the items already spawned by levelled items that will restock for (std::map::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) @@ -472,13 +471,13 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW if (MWBase::Environment::get().getWorld()->getStore().get().search(it->mItem.toString())) { - addInitialItem(item, owner, faction, factionRank, it->mCount, true); + addInitialItem(item, owner, it->mCount, true); } else { int currentCount = count(item); if (currentCount < std::abs(it->mCount)) - addInitialItem(item, owner, faction, factionRank, std::abs(it->mCount) - currentCount, true); + addInitialItem(item, owner, std::abs(it->mCount) - currentCount, true); } } flagAsModified(); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index d909a98ccf..e9750a6228 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -75,7 +75,7 @@ namespace MWWorld mutable float mCachedWeight; mutable bool mWeightUpToDate; ContainerStoreIterator addImp (const Ptr& ptr, int count); - void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int factionRank, int count, bool topLevel=true, const std::string& levItem = ""); + void addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = ""); template ContainerStoreIterator getState (CellRefList& collection, @@ -112,7 +112,7 @@ namespace MWWorld /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! /// - /// @param setOwner Set the owner of the added item to \a actorPtr? + /// @param setOwner Set the owner of the added item to \a actorPtr? If false, the owner is reset to "". /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. @@ -151,10 +151,10 @@ namespace MWWorld virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); ///< @return true if the two specified objects can stack with each other - void fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, int factionRank, const MWWorld::ESMStore& store); + void fill (const ESM::InventoryList& items, const std::string& owner); ///< Insert items into *this. - void restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner, const std::string& faction, int factionRank); + void restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner); virtual void clear(); ///< Empty container. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1036d8ce6a..933a1fa428 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2980,18 +2980,10 @@ namespace MWWorld if (!closestChest.isEmpty()) //Found a close chest { - ContainerStore& store = ptr.getClass().getContainerStore(ptr); - for (ContainerStoreIterator it = store.begin(); it != store.end(); ++it) //Move all stolen stuff into chest - { - MWWorld::Ptr dummy; - if (!MWBase::Environment::get().getMechanicsManager()->isAllowedToUse(getPlayerPtr(), *it, dummy)) - { - closestChest.getClass().getContainerStore(closestChest).add(*it, it->getRefData().getCount(), closestChest); - store.remove(*it, it->getRefData().getCount(), ptr); - } - } - closestChest.getClass().lock(closestChest,50); + MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest); } + else + std::cerr << "Failed to confiscate items: no stolen_goods container found" << std::endl; } void World::goToJail() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 814dde6a29..6f8352ddbb 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -63,7 +63,7 @@ add_component_dir (esm loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile - aisequence magiceffects util custommarkerstate + aisequence magiceffects util custommarkerstate stolenitems ) add_component_dir (esmterrain diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 60926d562d..7ef8102c29 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -117,6 +117,7 @@ enum RecNameInts REC_MARK = FourCC<'M','A','R','K'>::value, REC_ENAB = FourCC<'E','N','A','B'>::value, REC_CAM_ = FourCC<'C','A','M','_'>::value, + REC_STLN = FourCC<'S','T','L','N'>::value, // format 1 REC_FILT = FourCC<'F','I','L','T'>::value, diff --git a/components/esm/stolenitems.cpp b/components/esm/stolenitems.cpp new file mode 100644 index 0000000000..c51b0b99b0 --- /dev/null +++ b/components/esm/stolenitems.cpp @@ -0,0 +1,47 @@ +#include "stolenitems.hpp" + +#include +#include + +namespace ESM +{ + + void StolenItems::write(ESMWriter &esm) const + { + for (StolenItemsMap::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it) + { + esm.writeHNString("NAME", it->first); + for (std::map, int>::const_iterator ownerIt = it->second.begin(); + ownerIt != it->second.end(); ++ownerIt) + { + if (ownerIt->first.second) + esm.writeHNString("FNAM", ownerIt->first.first); + else + esm.writeHNString("ONAM", ownerIt->first.first); + esm.writeHNT("COUN", ownerIt->second); + } + } + } + + void StolenItems::load(ESMReader &esm) + { + while (esm.isNextSub("NAME")) + { + std::string itemid = esm.getHString(); + + std::map, int> ownerMap; + while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM")) + { + std::string subname = esm.retSubName().toString(); + std::string owner = esm.getHString(); + bool isFaction = (subname == "FNAM"); + int count; + esm.getHNT(count, "COUN"); + ownerMap.insert(std::make_pair(std::make_pair(owner, isFaction), count)); + } + + mStolenItems[itemid] = ownerMap; + } + } + +} diff --git a/components/esm/stolenitems.hpp b/components/esm/stolenitems.hpp new file mode 100644 index 0000000000..928fbbf757 --- /dev/null +++ b/components/esm/stolenitems.hpp @@ -0,0 +1,24 @@ +#ifndef OPENMW_COMPONENTS_ESM_STOLENITEMS_H +#define OPENMW_COMPONENTS_ESM_STOLENITEMS_H + +#include +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + struct StolenItems + { + typedef std::map, int> > StolenItemsMap; + StolenItemsMap mStolenItems; + + void load(ESM::ESMReader& esm); + void write(ESM::ESMWriter& esm) const; + }; + +} + +#endif From 356d1c7657124ccad4987a78fb0672e379909070 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Feb 2015 22:10:58 +0100 Subject: [PATCH 14/26] ESSImport: convert stolen item tracker --- apps/essimporter/converter.hpp | 35 ++++++++++++++++++++++++++++++---- apps/openmw/mwgui/tooltips.cpp | 2 +- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 7759956058..c23083f8e9 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "importcrec.hpp" #include "importcntc.hpp" @@ -387,24 +388,50 @@ public: virtual void read(ESM::ESMReader &esm) { std::string itemid = esm.getHNString("NAME"); + Misc::StringUtils::toLower(itemid); while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM")) { if (esm.retSubName().toString() == "FNAM") { std::string factionid = esm.getHString(); - mFactionStolenItems.insert(std::make_pair(itemid, factionid)); + mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(factionid), true)); } else { std::string ownerid = esm.getHString(); - mStolenItems.insert(std::make_pair(itemid, ownerid)); + mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false)); } } } + virtual void write(ESM::ESMWriter &esm) + { + ESM::StolenItems items; + for (std::map >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it) + { + std::map, int> owners; + for (std::set::const_iterator ownerIt = it->second.begin(); ownerIt != it->second.end(); ++ownerIt) + { + owners.insert(std::make_pair(std::make_pair(ownerIt->first, ownerIt->second) + // Since OpenMW doesn't suffer from the owner contamination bug, + // it needs a count argument. But for legacy savegames, we don't know + // this count, so must assume all items of that ID are stolen, + // like vanilla MW did. + ,std::numeric_limits::max())); + } + + items.mStolenItems.insert(std::make_pair(it->first, owners)); + } + + esm.startRecord(ESM::REC_STLN); + items.write(esm); + esm.endRecord(ESM::REC_STLN); + } + private: - std::multimap mStolenItems; - std::multimap mFactionStolenItems; + typedef std::pair Owner; // + + std::map > mStolenItems; }; /// Seen responses for a dialogue topic? diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index c6210ac642..2c10004a63 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -593,7 +593,7 @@ namespace MWGui for (std::vector >::const_iterator it = itemOwners.begin(); it != itemOwners.end(); ++it) { - ret += std::string("\nStolen ") + MyGUI::utility::toString(it->second) + " from " + it->first; + ret += std::string("\nStolen from ") + it->first; } ret += getMiscString(cellref.getGlobalVariable(), "Global"); From dd8aab2a7ff2597d1665dfefc2d2773a2fa71d23 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Feb 2015 22:17:30 +0100 Subject: [PATCH 15/26] Adjust help for --load-savegame again --- README.md | 5 +++-- apps/openmw/main.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9207c5728d..63a3138960 100644 --- a/README.md +++ b/README.md @@ -69,8 +69,9 @@ Command line options --script-blacklist-use [=arg(=1)] (=1) enable script blacklisting --load-savegame arg load a save game file on game startup - (specify an absolute or relative - filename for this option) + (specify an absolute filename or a + filename relative to the current + working directory) --skip-menu [=arg(=1)] (=0) skip main menu on game startup --new-game [=arg(=1)] (=0) run new game sequence (ignored if skip-menu=0) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index f4875e68a1..475fe63a2d 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -153,7 +153,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ->default_value(true), "enable script blacklisting") ("load-savegame", bpo::value()->default_value(""), - "load a save game file on game startup (specify an absolute or relative filename for this option)") + "load a save game file on game startup (specify an absolute filename or a filename relative to the current working directory)") ("skip-menu", bpo::value()->implicit_value(true) ->default_value(false), "skip main menu on game startup") From ae77eacd7ea5f3ef5766138178d73345256f2b6e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Feb 2015 23:15:12 +0100 Subject: [PATCH 16/26] Skill progress refactoring --- apps/openmw/mwgui/statswindow.cpp | 6 ++- apps/openmw/mwmechanics/npcstats.cpp | 54 +++++++++++------------- apps/openmw/mwmechanics/npcstats.hpp | 6 +-- apps/openmw/mwscript/statsextensions.cpp | 17 -------- 4 files changed, 31 insertions(+), 52 deletions(-) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index a407aedc72..f624d751d9 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -383,11 +383,15 @@ namespace MWGui const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; int base = stat.getBase(); int modified = stat.getModified(); - int progressPercent = stat.getProgress() * 100; + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); + int progressRequirement = player.getClass().getNpcStats(player).getSkillProgressRequirement(skillId, + *esmStore.get().find(player.get()->mBase->mClass)); + int progressPercent = int(int(stat.getProgress()) / float(progressRequirement) * 100.f); + const ESM::Skill* skill = esmStore.get().find(skillId); std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 7219d6185a..bd1cd55721 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -125,32 +125,9 @@ bool MWMechanics::NpcStats::isInFaction (const std::string& faction) const return (mFactionRank.find(Misc::StringUtils::lowerCase(faction)) != mFactionRank.end()); } -float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& class_, int usageType, - int level, float extraFactor) const +float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const ESM::Class& class_) const { - if (level<0) - level = static_cast (getSkill (skillIndex).getBase()); - - const ESM::Skill *skill = - MWBase::Environment::get().getWorld()->getStore().get().find (skillIndex); - - float skillFactor = 1; - - if (usageType>=4) - throw std::runtime_error ("skill usage type out of range"); - - if (usageType>=0) - { - skillFactor = skill->mData.mUseValue[usageType]; - - if (skillFactor<0) - throw std::runtime_error ("invalid skill gain factor"); - - if (skillFactor==0) - return 0; - } - - skillFactor *= extraFactor; + float progressRequirement = 1 + getSkill (skillIndex).getBase(); const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); @@ -173,11 +150,15 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla break; } + progressRequirement *= typeFactor; + if (typeFactor<=0) throw std::runtime_error ("invalid skill type factor"); float specialisationFactor = 1; + const ESM::Skill *skill = + MWBase::Environment::get().getWorld()->getStore().get().find (skillIndex); if (skill->mData.mSpecialization==class_.mData.mSpecialization) { specialisationFactor = gmst.find ("fSpecialSkillBonus")->getFloat(); @@ -185,7 +166,9 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla if (specialisationFactor<=0) throw std::runtime_error ("invalid skill specialisation factor"); } - return 1.0 / ((level+1) * (1.0/skillFactor) * typeFactor * specialisationFactor); + progressRequirement *= specialisationFactor; + + return progressRequirement; } void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType, float extraFactor) @@ -194,11 +177,24 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, if(mIsWerewolf) return; + const ESM::Skill *skill = + MWBase::Environment::get().getWorld()->getStore().get().find (skillIndex); + float skillGain = 1; + if (usageType>=4) + throw std::runtime_error ("skill usage type out of range"); + if (usageType>=0) + { + skillGain = skill->mData.mUseValue[usageType]; + if (skillGain<0) + throw std::runtime_error ("invalid skill gain factor"); + } + skillGain *= extraFactor; + MWMechanics::SkillValue& value = getSkill (skillIndex); - value.setProgress(value.getProgress() + getSkillGain (skillIndex, class_, usageType, -1, extraFactor)); + value.setProgress(value.getProgress() + skillGain); - if (value.getProgress()>=1) + if (int(value.getProgress())>=int(getSkillProgressRequirement(skillIndex, class_))) { // skill levelled up increaseSkill(skillIndex, class_, false); @@ -256,7 +252,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", MWGui::ShowInDialogueMode_Never); } - getSkill (skillIndex).setBase (base); + getSkill(skillIndex).setBase (base); if (!preserveProgress) getSkill(skillIndex).setProgress(0); } diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index fc4f297db0..1cb62276fe 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -74,11 +74,7 @@ namespace MWMechanics bool isInFaction (const std::string& faction) const; - float getSkillGain (int skillIndex, const ESM::Class& class_, int usageType = -1, - int level = -1, float extraFactor=1.f) const; - ///< \param usageType: Usage specific factor, specified in the respective skill record; - /// -1: use a factor of 1.0 instead. - /// \param level Level to base calculation on; -1: use current level. + float getSkillProgressRequirement (int skillIndex, const ESM::Class& class_) const; void useSkill (int skillIndex, const ESM::Class& class_, int usageType = -1, float extraFactor=1.f); ///< Increase skill by usage. diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index f0cf12e5ea..abef636ccf 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -348,29 +348,12 @@ namespace MWScript MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr); - MWWorld::LiveCellRef *ref = ptr.get(); - - assert (ref); - - const ESM::Class& class_ = - *MWBase::Environment::get().getWorld()->getStore().get().find (ref->mBase->mClass); - - float level = stats.getSkill(mIndex).getBase(); - float progress = stats.getSkill(mIndex).getProgress(); - int newLevel = value - (stats.getSkill(mIndex).getModified() - stats.getSkill(mIndex).getBase()); if (newLevel<0) newLevel = 0; - progress = (progress / stats.getSkillGain (mIndex, class_, -1, level)) - * stats.getSkillGain (mIndex, class_, -1, newLevel); - - if (progress>=1) - progress = 0.999999999; - stats.getSkill (mIndex).setBase (newLevel); - stats.getSkill (mIndex).setProgress(progress); } }; From f0b1d889c0f9f054207b07194f583f6a355d336e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Feb 2015 23:24:49 +0100 Subject: [PATCH 17/26] ESSImport: import player skill progress --- apps/essimporter/convertplayer.cpp | 5 ++--- apps/openmw/mwmechanics/npcstats.hpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/essimporter/convertplayer.cpp b/apps/essimporter/convertplayer.cpp index ef22660f56..532efa7f5b 100644 --- a/apps/essimporter/convertplayer.cpp +++ b/apps/essimporter/convertplayer.cpp @@ -17,6 +17,8 @@ namespace ESSImport } for (int i=0; i<8; ++i) out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i]; + for (int i=0; i<27; ++i) + out.mObject.mNpcStats.mSkills[i].mRegular.mProgress = pcdt.mPNAM.mSkillProgress[i]; out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress; if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Weapon) @@ -24,9 +26,6 @@ namespace ESSImport if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell) out.mObject.mCreatureStats.mDrawState = 2; - // TODO: convert PNAM.mSkillProgress, needs to be converted to uniform scale - // (or change openmw to accept non-uniform skill progress) - firstPersonCam = (pcdt.mPNAM.mCameraState == PCDT::CameraState_FirstPerson); for (std::vector::const_iterator it = pcdt.mKnownDialogueTopics.begin(); diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 1cb62276fe..224366a3e3 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -23,7 +23,7 @@ namespace MWMechanics class NpcStats : public CreatureStats { int mDisposition; - SkillValue mSkill[ESM::Skill::Length]; + SkillValue mSkill[ESM::Skill::Length]; // SkillValue.mProgress used by the player only SkillValue mWerewolfSkill[ESM::Skill::Length]; int mReputation; int mCrimeId; From 5096e5dc0c1cadad9d9b14ec9175a30e210411e6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Feb 2015 23:45:01 +0100 Subject: [PATCH 18/26] esmtool minor/major skills fix --- apps/esmtool/record.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index e167278f3d..97b0635fe5 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -535,10 +535,10 @@ void Record::print() std::cout << " Specialization: " << specializationLabel(mData.mData.mSpecialization) << " (" << mData.mData.mSpecialization << ")" << std::endl; for (int i = 0; i != 5; i++) - std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][0]) + std::cout << " Minor Skill: " << skillLabel(mData.mData.mSkills[i][0]) << " (" << mData.mData.mSkills[i][0] << ")" << std::endl; for (int i = 0; i != 5; i++) - std::cout << " Minor Skill: " << skillLabel(mData.mData.mSkills[i][1]) + std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][1]) << " (" << mData.mData.mSkills[i][1] << ")" << std::endl; } From 55cd9b526c93a44e22ccd784df93e590c61b1b04 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 5 Feb 2015 00:56:32 +0100 Subject: [PATCH 19/26] Skill progress bar changed to match MW --- apps/essimporter/importacdt.hpp | 2 +- apps/openmw/mwgui/statswindow.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index 1e0317049d..47055092de 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -61,7 +61,7 @@ namespace ESSImport bool mHasACSC; ACSC mACSC; - int mSkills[27][2]; + int mSkills[27][2]; // skills, base and modified // creature combat stats, base and modified // I think these can be ignored in the conversion, because it is not possible diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index f624d751d9..2a22f42393 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -376,9 +376,8 @@ namespace MWGui for (SkillList::const_iterator it = skills.begin(); it != end; ++it) { int skillId = *it; - if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + if (skillId < 0 || skillId >= ESM::Skill::Length) // Skip unknown skill indexes continue; - assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; int base = stat.getBase(); @@ -388,9 +387,14 @@ namespace MWGui const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); - int progressRequirement = player.getClass().getNpcStats(player).getSkillProgressRequirement(skillId, + float progressRequirement = player.getClass().getNpcStats(player).getSkillProgressRequirement(skillId, *esmStore.get().find(player.get()->mBase->mClass)); - int progressPercent = int(int(stat.getProgress()) / float(progressRequirement) * 100.f); + + // This is how vanilla MW displays the progress bar (I think). Note it's slightly inaccurate, + // due to the int casting in the skill levelup logic. Also the progress label could in rare cases + // reach 100% without the skill levelling up. + // Leaving the original display logic for now, for consistency with ess-imported savegames. + int progressPercent = int(float(stat.getProgress()) / float(progressRequirement) * 100.f + 0.5f); const ESM::Skill* skill = esmStore.get().find(skillId); From b5e0e45c78716e37dbeeaebb49c3d198a71ef140 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 5 Feb 2015 01:03:10 +0100 Subject: [PATCH 20/26] Fix iLevelUpTotal not being used in the levelUp logic --- apps/openmw/mwmechanics/npcstats.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index bd1cd55721..9d3d967b9e 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -264,13 +264,14 @@ int MWMechanics::NpcStats::getLevelProgress () const void MWMechanics::NpcStats::levelUp() { - mLevelProgress -= 10; - for (int i=0; i &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + mLevelProgress -= gmst.find("iLevelUpTotal")->getInt(); + + for (int i=0; i Date: Thu, 5 Feb 2015 01:08:26 +0100 Subject: [PATCH 21/26] Cap the displayed attribute multiplier in levelup to attribute points left to 100 --- apps/openmw/mwgui/levelupdialog.cpp | 1 + apps/openmw/mwmechanics/npcstats.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 8553811aac..6759824f98 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -176,6 +176,7 @@ namespace MWGui availableAttributes++; int mult = pcStats.getLevelupAttributeMultiplier (i); + mult = std::min(mult, 100-pcStats.getAttribute(i).getBase()); text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult)); } else diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 9d3d967b9e..3aa034a3c4 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -268,6 +268,7 @@ void MWMechanics::NpcStats::levelUp() MWBase::Environment::get().getWorld()->getStore().get(); mLevelProgress -= gmst.find("iLevelUpTotal")->getInt(); + mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console for (int i=0; i Date: Thu, 5 Feb 2015 01:15:42 +0100 Subject: [PATCH 22/26] UI consistency fix in levelup dialog --- apps/openmw/mwgui/levelupdialog.cpp | 3 ++- files/mygui/openmw_levelup_dialog.layout | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 6759824f98..c392372ff3 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -139,7 +139,6 @@ namespace MWGui if(world->getStore().get().isDynamic(cls->mId)) { - // Vanilla uses thief.dds for custom classes. // Choosing Stealth specialization and Speed/Agility as attributes, if possible. Otherwise fall back to first class found. MWWorld::SharedIterator it = world->getStore().get().begin(); for(; it != world->getStore().get().end(); ++it) @@ -173,6 +172,7 @@ namespace MWGui if (pcStats.getAttribute(i).getBase() < 100) { mAttributes[i]->setEnabled(true); + mAttributeValues[i]->setEnabled(true); availableAttributes++; int mult = pcStats.getLevelupAttributeMultiplier (i); @@ -182,6 +182,7 @@ namespace MWGui else { mAttributes[i]->setEnabled(false); + mAttributeValues[i]->setEnabled(false); text->setCaption(""); } diff --git a/files/mygui/openmw_levelup_dialog.layout b/files/mygui/openmw_levelup_dialog.layout index 190f16e5d9..ead181d26a 100644 --- a/files/mygui/openmw_levelup_dialog.layout +++ b/files/mygui/openmw_levelup_dialog.layout @@ -50,7 +50,7 @@ - + @@ -64,7 +64,7 @@ - + @@ -78,7 +78,7 @@ - + @@ -92,7 +92,7 @@ - + @@ -107,7 +107,7 @@ - + @@ -121,7 +121,7 @@ - + @@ -135,7 +135,7 @@ - + @@ -149,7 +149,7 @@ - + From d18d2a1882a297e21364b8f7aaaba5b4abf7d77e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 5 Feb 2015 04:28:05 +0100 Subject: [PATCH 23/26] Skip rest of LEVI record if it has no items (Bug #2348) --- components/esm/loadlevlist.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/esm/loadlevlist.cpp b/components/esm/loadlevlist.cpp index f2c695c1f1..0d02677648 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -19,7 +19,10 @@ void LevelledListBase::load(ESMReader &esm) mList.resize(len); } else + { + esm.skipRecord(); return; + } // TODO: Merge with an existing lists here. This can be done // simply by adding the lists together, making sure that they are From 01b41778720b71dcefc4a26860861cebefd04429 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 5 Feb 2015 04:29:52 +0100 Subject: [PATCH 24/26] Make sure birthsign spells are added when loading savegame --- apps/openmw/mwworld/player.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index c1e176b910..8f3560a691 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -260,9 +260,20 @@ namespace MWWorld mCellStore = world.getExterior(0,0); } - if (!player.mBirthsign.empty() && - !world.getStore().get().search (player.mBirthsign)) - throw std::runtime_error ("invalid player state record (birthsign)"); + if (!player.mBirthsign.empty()) + { + const ESM::BirthSign* sign = world.getStore().get().search (player.mBirthsign); + if (!sign) + throw std::runtime_error ("invalid player state record (birthsign does not exist)"); + + // To handle the case where a birth sign was edited in between play sessions (does not yet handle removing the old spells) + // Also needed for ess-imported savegames which do not specify the birtsign spells in the player's spell list. + for (std::vector::const_iterator iter (sign->mPowers.mList.begin()); + iter!=sign->mPowers.mList.end(); ++iter) + { + getPlayer().getClass().getCreatureStats(getPlayer()).getSpells().add (*iter); + } + } mCurrentCrimeId = player.mCurrentCrimeId; mPaidCrimeId = player.mPaidCrimeId; From cff0127ce70d1eafb7474d0821adfd6aee7bdb77 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 5 Feb 2015 04:31:50 +0100 Subject: [PATCH 25/26] Update comment on merging levelled lists --- components/esm/loadlevlist.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/components/esm/loadlevlist.cpp b/components/esm/loadlevlist.cpp index 0d02677648..2cbfac0b40 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -24,11 +24,12 @@ void LevelledListBase::load(ESMReader &esm) return; } - // TODO: Merge with an existing lists here. This can be done - // simply by adding the lists together, making sure that they are - // sorted by level. A better way might be to exclude repeated - // items. Also, some times we don't want to merge lists, just - // overwrite. Figure out a way to give the user this option. + // If this levelled list was already loaded by a previous content file, + // we overwrite the list. Merging lists should probably be left to external tools, + // with the limited amount of information there is in the records, all merging methods + // will be flawed in some way. For a proper fix the ESM format would have to be changed + // to actually track list changes instead of including the whole list for every file + // that does something with that list. for (size_t i = 0; i < mList.size(); i++) { From 4c6b9f8266b67e34bc6c6552c3a3ff0a0c0e406b Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 5 Feb 2015 15:39:53 +0100 Subject: [PATCH 26/26] Remove redundant decoder in ContentModel --- components/contentselector/model/contentmodel.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 198bbffbb1..56a86f87f6 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -442,11 +442,6 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; dir.setNameFilters(filters); - QTextCodec *codec = QTextCodec::codecForName("UTF8"); - - // Create a decoder for non-latin characters in esx metadata - QTextDecoder *decoder = codec->makeDecoder(); - foreach (const QString &path, dir.entryList()) { QFileInfo info(dir.absoluteFilePath(path)); @@ -466,11 +461,11 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) foreach (const ESM::Header::MasterData &item, fileReader.getGameFiles()) file->addGameFile(QString::fromStdString(item.name)); - file->setAuthor (decoder->toUnicode(fileReader.getAuthor().c_str())); + file->setAuthor (QString::fromUtf8(fileReader.getAuthor().c_str())); file->setDate (info.lastModified()); file->setFormat (fileReader.getFormat()); file->setFilePath (info.absoluteFilePath()); - file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); + file->setDescription(QString::fromUtf8(fileReader.getDesc().c_str())); // Put the file in the table addFile(file); @@ -483,8 +478,6 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) } - delete decoder; - sortFiles(); }