From aa855e9524115d27c0dda59c69a7d0166f95e396 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Thu, 9 Jan 2014 20:56:24 +0100
Subject: [PATCH 01/23] Include some required Ogre headers explicitely

---
 apps/opencs/view/render/scenewidget.cpp   | 1 +
 apps/openmw/mwrender/characterpreview.cpp | 1 +
 apps/openmw/mwrender/occlusionquery.cpp   | 1 +
 apps/openmw/mwrender/shadows.cpp          | 1 +
 apps/openmw/mwrender/water.cpp            | 1 +
 components/nifogre/ogrenifloader.cpp      | 4 +++-
 libs/openengine/ogre/renderer.cpp         | 1 +
 7 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp
index c8b37e9bb..620586bd2 100644
--- a/apps/opencs/view/render/scenewidget.cpp
+++ b/apps/opencs/view/render/scenewidget.cpp
@@ -6,6 +6,7 @@
 #include <OgreRoot.h>
 #include <OgreRenderWindow.h>
 #include <OgreEntity.h>
+#include <OgreCamera.h>
 
 namespace CSVRender
 {
diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp
index f7333db35..2dbc72e26 100644
--- a/apps/openmw/mwrender/characterpreview.cpp
+++ b/apps/openmw/mwrender/characterpreview.cpp
@@ -4,6 +4,7 @@
 #include <OgreSceneManager.h>
 #include <OgreRoot.h>
 #include <OgreHardwarePixelBuffer.h>
+#include <OgreCamera.h>
 
 #include <libs/openengine/ogre/selectionbuffer.hpp>
 
diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp
index a69511acd..246103471 100644
--- a/apps/openmw/mwrender/occlusionquery.cpp
+++ b/apps/openmw/mwrender/occlusionquery.cpp
@@ -8,6 +8,7 @@
 #include <OgreSubEntity.h>
 #include <OgreMeshManager.h>
 #include <OgreMaterialManager.h>
+#include <OgreCamera.h>
 
 #include "renderconst.hpp"
 
diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp
index 21bbe51b6..9ebb0ab08 100644
--- a/apps/openmw/mwrender/shadows.cpp
+++ b/apps/openmw/mwrender/shadows.cpp
@@ -8,6 +8,7 @@
 #include <OgreShadowCameraSetupLiSPSM.h>
 #include <OgreShadowCameraSetupPSSM.h>
 #include <OgreHardwarePixelBuffer.h>
+#include <OgreCamera.h>
 
 #include <extern/shiny/Main/Factory.hpp>
 
diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp
index 0a4db30e9..9e3105168 100644
--- a/apps/openmw/mwrender/water.cpp
+++ b/apps/openmw/mwrender/water.cpp
@@ -5,6 +5,7 @@
 #include <OgreMeshManager.h>
 #include <OgreHardwarePixelBuffer.h>
 #include <OgreRoot.h>
+#include <OgreCamera.h>
 
 #include "sky.hpp"
 #include "renderingmanager.hpp"
diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp
index 0c1fdfbee..5a76b702e 100644
--- a/components/nifogre/ogrenifloader.cpp
+++ b/components/nifogre/ogrenifloader.cpp
@@ -26,7 +26,6 @@
 #include <algorithm>
 
 #include <OgreTechnique.h>
-#include <OgreRoot.h>
 #include <OgreEntity.h>
 #include <OgreSubEntity.h>
 #include <OgreTagPoint.h>
@@ -36,6 +35,9 @@
 #include <OgreMeshManager.h>
 #include <OgreSkeletonManager.h>
 #include <OgreControllerManager.h>
+#include <OgreMaterialManager.h>
+#include <OgreCamera.h>
+#include <OgreSceneManager.h>
 
 #include <extern/shiny/Main/Factory.hpp>
 
diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp
index 9e5ec5414..c86697497 100644
--- a/libs/openengine/ogre/renderer.cpp
+++ b/libs/openengine/ogre/renderer.cpp
@@ -8,6 +8,7 @@
 #include <OgreTextureManager.h>
 #include <OgreTexture.h>
 #include <OgreHardwarePixelBuffer.h>
+#include <OgreCamera.h>
 
 #include <extern/sdl4ogre/sdlwindowhelper.hpp>
 

From 546b0cee76477aff7409d90a6059d1591cad46e7 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Thu, 9 Jan 2014 22:17:51 +0100
Subject: [PATCH 02/23] Closes #1077: Replace tags before trying to get the
 message length

---
 apps/openmw/mwgui/messagebox.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp
index 8e518668b..72a5bd0b0 100644
--- a/apps/openmw/mwgui/messagebox.cpp
+++ b/apps/openmw/mwgui/messagebox.cpp
@@ -63,7 +63,8 @@ namespace MWGui
     {
         MessageBox *box = new MessageBox(*this, message);
         box->mCurrentTime = 0;
-        box->mMaxTime = message.length()*mMessageBoxSpeed;
+        std::string realMessage = MyGUI::LanguageManager::getInstance().replaceTags(message);
+        box->mMaxTime = realMessage.length()*mMessageBoxSpeed;
 
         if(stat)
             mStaticMessageBox = box;

From bfdca3b73816831d45e86c2e806462c04fab5be2 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Thu, 9 Jan 2014 23:13:31 +0100
Subject: [PATCH 03/23] Fix needTangents not being set for cached/shared
 materials

---
 components/nifogre/material.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp
index be6ccbed6..e2cc3712b 100644
--- a/components/nifogre/material.cpp
+++ b/components/nifogre/material.cpp
@@ -277,6 +277,8 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
         if (itr != sMaterialMap.end())
         {
             // a suitable material exists already - use it
+            sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(itr->second);
+            needTangents = !sh::retrieveValue<sh::StringValue>(instance->getProperty("normalMap"), instance).get().empty();
             return itr->second;
         }
         // not found, create a new one

From 627dcddb333a2d8e881184bf1f6a1dd72d8d15b6 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Fri, 10 Jan 2014 20:53:53 +0100
Subject: [PATCH 04/23] Do not allow training skills above their governing
 attribute

---
 apps/openmw/mwgui/trainingwindow.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp
index 709bc0fb9..24be5363d 100644
--- a/apps/openmw/mwgui/trainingwindow.cpp
+++ b/apps/openmw/mwgui/trainingwindow.cpp
@@ -130,6 +130,14 @@ namespace MWGui
             return;
         }
 
+        // You can not train a skill above its governing attribute
+        const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(skillId);
+        if (pcStats.getSkill(skillId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase())
+        {
+            MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage17}");
+            return;
+        }
+
         // increase skill
         MWWorld::LiveCellRef<ESM::NPC> *playerRef = player.get<ESM::NPC>();
 

From f5a70dccf0f65269fa992503f9d809d8ba56f155 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Fri, 10 Jan 2014 21:13:03 +0100
Subject: [PATCH 05/23] Fix title of buttons in generate class result dialog

---
 apps/openmw/mwgui/class.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp
index 6c46f2176..7965669f1 100644
--- a/apps/openmw/mwgui/class.cpp
+++ b/apps/openmw/mwgui/class.cpp
@@ -29,11 +29,12 @@ namespace MWGui
 
         MyGUI::Button* backButton;
         getWidget(backButton, "BackButton");
+        backButton->setCaptionWithReplacing("#{sMessageQuestionAnswer3}");
         backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked);
 
         MyGUI::Button* okButton;
         getWidget(okButton, "OKButton");
-        okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
+        okButton->setCaptionWithReplacing("#{sMessageQuestionAnswer2}");
         okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked);
     }
 

From 3bf36515d52dcd7cd632a37f29d603eedf662259 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Fri, 10 Jan 2014 21:26:24 +0100
Subject: [PATCH 06/23] Implement Trespassing crime

---
 apps/openmw/mwbase/mechanicsmanager.hpp       |  2 ++
 .../mwmechanics/mechanicsmanagerimp.cpp       | 26 ++++++++++++-------
 .../mwmechanics/mechanicsmanagerimp.hpp       |  2 ++
 apps/openmw/mwmechanics/security.cpp          |  3 +++
 apps/openmw/mwmechanics/spellcasting.cpp      | 12 +++++----
 apps/openmw/mwmechanics/spellcasting.hpp      |  2 +-
 apps/openmw/mwworld/inventorystore.cpp        |  2 +-
 7 files changed, 32 insertions(+), 17 deletions(-)

diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp
index 24e955cdf..85fcfc75b 100644
--- a/apps/openmw/mwbase/mechanicsmanager.hpp
+++ b/apps/openmw/mwbase/mechanicsmanager.hpp
@@ -112,6 +112,8 @@ namespace MWBase
                                       OffenseType type, int arg=0) = 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;
+            /// 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.
             /// @return was it illegal, and someone saw you doing it?
             virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0;
diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
index 511fe6544..002cd561d 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
@@ -19,7 +19,7 @@
 namespace
 {
     /// @return is \a ptr allowed to take/use \a item or is it a crime?
-    bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item)
+    bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim)
     {
         const std::string& owner = item.getCellRef().mOwner;
         bool isOwned = !owner.empty();
@@ -33,6 +33,9 @@ namespace
                 isFactionOwned = true;
         }
 
+        if (!item.getCellRef().mOwner.empty())
+            victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true);
+
         return (!isOwned && !isFactionOwned);
     }
 }
@@ -752,11 +755,9 @@ namespace MWMechanics
 
     bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed)
     {
-        if (isAllowedToUse(ptr, bed))
-            return false;
         MWWorld::Ptr victim;
-        if (!bed.getCellRef().mOwner.empty())
-            victim = MWBase::Environment::get().getWorld()->getPtr(bed.getCellRef().mOwner, true);
+        if (isAllowedToUse(ptr, bed, victim))
+            return false;
 
         if(commitCrime(ptr, victim, OT_SleepingInOwnedBed))
         {
@@ -767,14 +768,19 @@ namespace MWMechanics
             return false;
     }
 
+    void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item)
+    {
+        MWWorld::Ptr victim;
+        if (isAllowedToUse(ptr, item, victim))
+            return;
+        commitCrime(ptr, victim, OT_Trespassing);
+    }
+
     void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count)
     {
-        if (isAllowedToUse(ptr, item))
-            return;
         MWWorld::Ptr victim;
-        if (!item.getCellRef().mOwner.empty())
-            victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true);
-
+        if (isAllowedToUse(ptr, item, victim))
+            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 cec08fa92..569cd2fca 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp
@@ -113,6 +113,8 @@ namespace MWMechanics
                                       OffenseType type, int arg=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);
+            /// 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.
             /// @return was it illegal, and someone saw you doing it?
             virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed);
diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp
index 0769e13df..2e5eaecfd 100644
--- a/apps/openmw/mwmechanics/security.cpp
+++ b/apps/openmw/mwmechanics/security.cpp
@@ -6,6 +6,7 @@
 #include "../mwbase/world.hpp"
 #include "../mwbase/environment.hpp"
 #include "../mwbase/windowmanager.hpp"
+#include "../mwbase/mechanicsmanager.hpp"
 
 #include "npcstats.hpp"
 #include "creaturestats.hpp"
@@ -45,6 +46,7 @@ namespace MWMechanics
             resultMessage = "#{sLockImpossible}";
         else
         {
+            MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, lock);
             int roll = static_cast<float> (std::rand()) / RAND_MAX * 100;
             if (roll <= x)
             {
@@ -86,6 +88,7 @@ namespace MWMechanics
             resultMessage = "#{sTrapImpossible}";
         else
         {
+            MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, trap);
             int roll = static_cast<float> (std::rand()) / RAND_MAX * 100;
             if (roll <= x)
             {
diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp
index a1cdb8b61..8c4c4d292 100644
--- a/apps/openmw/mwmechanics/spellcasting.cpp
+++ b/apps/openmw/mwmechanics/spellcasting.cpp
@@ -4,7 +4,7 @@
 
 #include "../mwbase/windowmanager.hpp"
 #include "../mwbase/soundmanager.hpp"
-
+#include "../mwbase/mechanicsmanager.hpp"
 
 #include "../mwworld/containerstore.hpp"
 #include "../mwworld/actionteleport.hpp"
@@ -167,7 +167,7 @@ namespace MWMechanics
                     }
                 }
                 else
-                    applyInstantEffect(target, EffectKey(*effectIt), magnitude);
+                    applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude);
 
                 // HACK: Damage attribute/skill actually has a duration, even though the actual effect is instant and permanent.
                 // This was probably just done to have the effect visible in the magic menu for a while
@@ -177,7 +177,7 @@ namespace MWMechanics
                         || effectIt->mEffectID == ESM::MagicEffect::RestoreAttribute
                         || effectIt->mEffectID == ESM::MagicEffect::RestoreSkill
                         )
-                    applyInstantEffect(target, EffectKey(*effectIt), magnitude);
+                    applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude);
 
                 if (target.getClass().isActor() || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)
                 {
@@ -220,7 +220,7 @@ namespace MWMechanics
                                                                                   mSourceName, caster.getRefData().getHandle());
     }
 
-    void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, MWMechanics::EffectKey effect, float magnitude)
+    void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, MWMechanics::EffectKey effect, float magnitude)
     {
         short effectId = effect.mId;
         if (!target.getClass().isActor())
@@ -232,11 +232,13 @@ namespace MWMechanics
             }
             else if (effectId == ESM::MagicEffect::Open)
             {
-                // TODO: This is a crime
                 if (target.getCellRef().mLockLevel <= magnitude)
                 {
                     if (target.getCellRef().mLockLevel > 0)
+                    {
                         MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f);
+                        MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target);
+                    }
                     target.getCellRef().mLockLevel = 0;
                 }
                 else
diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp
index a55c45fd1..438b65575 100644
--- a/apps/openmw/mwmechanics/spellcasting.hpp
+++ b/apps/openmw/mwmechanics/spellcasting.hpp
@@ -203,7 +203,7 @@ namespace MWMechanics
         void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster,
                       const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false);
 
-        void applyInstantEffect (const MWWorld::Ptr& target, MWMechanics::EffectKey effect, float magnitude);
+        void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, MWMechanics::EffectKey effect, float magnitude);
     };
 
 }
diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp
index 2aee74eee..b11c22c72 100644
--- a/apps/openmw/mwworld/inventorystore.cpp
+++ b/apps/openmw/mwworld/inventorystore.cpp
@@ -392,7 +392,7 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
                     // Apply instant effects
                     MWMechanics::CastSpell cast(actor, actor);
                     if (magnitude)
-                        cast.applyInstantEffect(actor, effectIt->mEffectID, magnitude);
+                        cast.applyInstantEffect(actor, actor, effectIt->mEffectID, magnitude);
                 }
 
                 if (magnitude)

From 2744cde40fea2ff24f009740fd64512900b752b9 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Fri, 10 Jan 2014 22:27:31 +0100
Subject: [PATCH 07/23] Use a few additional GMSTs

---
 apps/openmw/mwgui/enchantingdialog.cpp        | 2 +-
 apps/openmw/mwgui/messagebox.cpp              | 5 ++---
 apps/openmw/mwgui/messagebox.hpp              | 2 +-
 apps/openmw/mwgui/windowmanagerimp.cpp        | 3 ++-
 apps/openmw/mwmechanics/actors.cpp            | 8 ++++++--
 files/mygui/openmw_spell_buying_window.layout | 2 +-
 files/mygui/openmw_travel_window.layout       | 4 ++--
 7 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp
index 4d79875a2..d9d2a2ea8 100644
--- a/apps/openmw/mwgui/enchantingdialog.cpp
+++ b/apps/openmw/mwgui/enchantingdialog.cpp
@@ -257,7 +257,7 @@ namespace MWGui
     {
         if (mEffects.size() <= 0)
         {
-            MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}");
+            MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu11}");
             return;
         }
 
diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp
index 72a5bd0b0..b0abaef14 100644
--- a/apps/openmw/mwgui/messagebox.cpp
+++ b/apps/openmw/mwgui/messagebox.cpp
@@ -8,13 +8,12 @@
 namespace MWGui
 {
 
-    MessageBoxManager::MessageBoxManager ()
+    MessageBoxManager::MessageBoxManager (float timePerChar)
     {
-        // TODO: fMessageTimePerChar
-        mMessageBoxSpeed = 0.1;
         mInterMessageBoxe = NULL;
         mStaticMessageBox = NULL;
         mLastButtonPressed = -1;
+        mMessageBoxSpeed = timePerChar;
     }
 
     MessageBoxManager::~MessageBoxManager ()
diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp
index 0288f366c..e82a51642 100644
--- a/apps/openmw/mwgui/messagebox.hpp
+++ b/apps/openmw/mwgui/messagebox.hpp
@@ -22,7 +22,7 @@ namespace MWGui
     class MessageBoxManager
     {
         public:
-            MessageBoxManager ();
+            MessageBoxManager (float timePerChar);
             ~MessageBoxManager ();
             void onFrame (float frameDuration);
             void createMessageBox (const std::string& message, bool stat = false);
diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp
index 3accd925f..e4297f8fc 100644
--- a/apps/openmw/mwgui/windowmanagerimp.cpp
+++ b/apps/openmw/mwgui/windowmanagerimp.cpp
@@ -207,7 +207,8 @@ namespace MWGui
         mConsole = new Console(w,h, mConsoleOnlyScripts);
         trackWindow(mConsole, "console");
         mJournal = JournalWindow::create(JournalViewModel::create ());
-        mMessageBoxManager = new MessageBoxManager();
+        mMessageBoxManager = new MessageBoxManager(
+                    MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fMessageTimePerChar")->getFloat());
         mInventoryWindow = new InventoryWindow(mDragAndDrop);
         mTradeWindow = new TradeWindow();
         trackWindow(mTradeWindow, "barter");
diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp
index 0a4adb6e2..afc94255b 100644
--- a/apps/openmw/mwmechanics/actors.cpp
+++ b/apps/openmw/mwmechanics/actors.cpp
@@ -581,7 +581,8 @@ namespace MWMechanics
             if(timeLeft == 0.0f)
             {
                 // If drowning, apply 3 points of damage per second
-                ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - 3.0f*duration);
+                static const float fSuffocationDamage = world->getStore().get<ESM::GameSetting>().find("fSuffocationDamage")->getFloat();
+                ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - fSuffocationDamage*duration);
 
                 // Play a drowning sound as necessary for the player
                 if(ptr == world->getPlayerPtr())
@@ -593,7 +594,10 @@ namespace MWMechanics
             }
         }
         else
-            stats.setTimeToStartDrowning(20);
+        {
+            static const float fHoldBreathTime = world->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->getFloat();
+            stats.setTimeToStartDrowning(fHoldBreathTime);
+        }
     }
 
     void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration)
diff --git a/files/mygui/openmw_spell_buying_window.layout b/files/mygui/openmw_spell_buying_window.layout
index 1510372dd..b24a476c4 100644
--- a/files/mygui/openmw_spell_buying_window.layout
+++ b/files/mygui/openmw_spell_buying_window.layout
@@ -11,7 +11,7 @@
         </Widget>
         <Widget type="TextBox" skin="NormalText" position="0 0 450 18" align="Right Top">
             <Property key="TextAlign" value="Center"/>
-            <Property key="Caption" value="#{sSpells}"/>
+            <Property key="Caption" value="#{sServiceSpellsTitle}"/>
         </Widget>
 
 
diff --git a/files/mygui/openmw_travel_window.layout b/files/mygui/openmw_travel_window.layout
index db3fa24a0..683d47fe7 100644
--- a/files/mygui/openmw_travel_window.layout
+++ b/files/mygui/openmw_travel_window.layout
@@ -11,7 +11,7 @@
         </Widget>
         <Widget type="TextBox" skin="SandText" position="0 0 24 24" name="Travel" align="Right Top">
             <Property key="TextAlign" value="Right"/>
-            <Property key="Caption" value="#D8C09A#{sTravel}"/>
+            <Property key="Caption" value="#D8C09A#{sServiceTravelTitle}"/>
         </Widget>
 
 
@@ -32,4 +32,4 @@
 
     </Widget>
 
-</MyGUI>
\ No newline at end of file
+</MyGUI>

From 15e48107f7b5ee62e47be7d6d2ab17759395065e Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Fri, 10 Jan 2014 22:39:01 +0100
Subject: [PATCH 08/23] Use i1stPersonSneakDelta + some cleanup

---
 apps/openmw/mwmechanics/spellcasting.hpp  | 12 ++++++------
 apps/openmw/mwrender/camera.cpp           |  5 ++---
 apps/openmw/mwrender/camera.hpp           |  2 +-
 apps/openmw/mwrender/renderingmanager.cpp |  4 +++-
 4 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp
index 438b65575..a1ae254f6 100644
--- a/apps/openmw/mwmechanics/spellcasting.hpp
+++ b/apps/openmw/mwmechanics/spellcasting.hpp
@@ -19,12 +19,12 @@ namespace MWMechanics
     inline int spellSchoolToSkill(int school)
     {
         std::map<int, int> schoolSkillMap; // maps spell school to skill id
-        schoolSkillMap[0] = 11; // alteration
-        schoolSkillMap[1] = 13; // conjuration
-        schoolSkillMap[3] = 12; // illusion
-        schoolSkillMap[2] = 10; // destruction
-        schoolSkillMap[4] = 14; // mysticism
-        schoolSkillMap[5] = 15; // restoration
+        schoolSkillMap[0] = ESM::Skill::Alteration;
+        schoolSkillMap[1] = ESM::Skill::Conjuration;
+        schoolSkillMap[3] = ESM::Skill::Illusion;
+        schoolSkillMap[2] = ESM::Skill::Destruction;
+        schoolSkillMap[4] = ESM::Skill::Mysticism;
+        schoolSkillMap[5] = ESM::Skill::Restoration;
         assert(schoolSkillMap.find(school) != schoolSkillMap.end());
         return schoolSkillMap[school];
     }
diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp
index dda9797ef..d579c9793 100644
--- a/apps/openmw/mwrender/camera.cpp
+++ b/apps/openmw/mwrender/camera.cpp
@@ -226,11 +226,10 @@ namespace MWRender
         mCamera->setPosition(0.f, 0.f, offset);
     }
 
-    void Camera::setSneakOffset()
+    void Camera::setSneakOffset(float offset)
     {
-        // TODO: iFirstPersonSneakDelta
         if(mAnimation)
-            mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -9.8f));
+            mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -offset));
     }
 
     float Camera::getYaw()
diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp
index d31d9e56c..808f817cf 100644
--- a/apps/openmw/mwrender/camera.hpp
+++ b/apps/openmw/mwrender/camera.hpp
@@ -87,7 +87,7 @@ namespace MWRender
         /// As animation is tied to the camera, this needs
         /// to be set each frame after the animation is
         /// applied.
-        void setSneakOffset();
+        void setSneakOffset(float offset);
 
         bool isFirstPerson() const
         { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); }
diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp
index 11d06704e..0c5e053c9 100644
--- a/apps/openmw/mwrender/renderingmanager.cpp
+++ b/apps/openmw/mwrender/renderingmanager.cpp
@@ -351,8 +351,10 @@ void RenderingManager::update (float duration, bool paused)
     bool isInAir = !world->isOnGround(player);
     bool isSwimming = world->isSwimming(player);
 
+    static const int i1stPersonSneakDelta = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
+            .find("i1stPersonSneakDelta")->getInt();
     if(isSneaking && !(isSwimming || isInAir))
-        mCamera->setSneakOffset();
+        mCamera->setSneakOffset(i1stPersonSneakDelta);
 
 
     mOcclusionQuery->update(duration);

From d9d6f376193700f51688251f354887cf07641222 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Fri, 10 Jan 2014 22:44:40 +0100
Subject: [PATCH 09/23] Use iVoiceHitOdds

---
 apps/openmw/mwclass/npc.cpp | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp
index 01fe61472..1c5c302d8 100644
--- a/apps/openmw/mwclass/npc.cpp
+++ b/apps/openmw/mwclass/npc.cpp
@@ -615,7 +615,13 @@ namespace MWClass
             // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
             // something, alert the character controller, scripts, etc.
 
-            MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
+            const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
+            int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->getInt();
+            int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
+            if (roll < chance)
+            {
+                MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
+            }
             getCreatureStats(ptr).setAttacked(true);//used in CharacterController
 
             if(object.isEmpty())

From 621e52f09d47758cb41b3e081e0d2b4069e3b53b Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Fri, 10 Jan 2014 22:48:42 +0100
Subject: [PATCH 10/23] Play "attack" voiced dialogue entries randomly based on
 iVoiceAttackOdds.

---
 apps/openmw/mwmechanics/aicombat.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp
index 32b0063b6..34b721755 100644
--- a/apps/openmw/mwmechanics/aicombat.cpp
+++ b/apps/openmw/mwmechanics/aicombat.cpp
@@ -7,6 +7,7 @@
 #include "../mwbase/world.hpp"
 #include "../mwbase/environment.hpp"
 #include "../mwbase/mechanicsmanager.hpp"
+#include "../mwbase/dialoguemanager.hpp"
 
 #include "creaturestats.hpp"
 #include "npcstats.hpp"
@@ -118,6 +119,17 @@ namespace MWMechanics
             }
             if( mTimer > 1)
             {
+                if (actor.getClass().isNpc())
+                {
+                    const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
+                    int chance = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->getInt();
+                    int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
+                    if (roll < chance)
+                    {
+                        MWBase::Environment::get().getDialogueManager()->say(actor, "attack");
+                    }
+                }
+
                 MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true);
                 mTimer = 0;
             }

From e9f63270d95b85dc2aca449e64f6ba71a0eb9628 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Fri, 10 Jan 2014 22:51:42 +0100
Subject: [PATCH 11/23] Speed fix for "on target" spells

---
 apps/openmw/mwworld/worldimp.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp
index f61938f92..738c71a7d 100644
--- a/apps/openmw/mwworld/worldimp.cpp
+++ b/apps/openmw/mwworld/worldimp.cpp
@@ -2174,14 +2174,13 @@ namespace MWWorld
 
             Ogre::Vector3 rot(ptr.getRefData().getPosition().rot);
 
-            // TODO: Why -rot.z, but not -rot.x?
+            // TODO: Why -rot.z, but not -rot.x? (note: same issue in MovementSolver::move)
             Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z);
             orient = orient * Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X);
 
-            // This is just a guess, probably wrong
-            static float fProjectileMinSpeed = getStore().get<ESM::GameSetting>().find("fProjectileMinSpeed")->getFloat();
-            static float fProjectileMaxSpeed = getStore().get<ESM::GameSetting>().find("fProjectileMaxSpeed")->getFloat();
-            float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * it->second.mSpeed;
+
+            static float fTargetSpellMaxSpeed = getStore().get<ESM::GameSetting>().find("fTargetSpellMaxSpeed")->getFloat();
+            float speed = fTargetSpellMaxSpeed * it->second.mSpeed;
 
             Ogre::Vector3 direction = orient.yAxis();
             direction.normalise();

From d01f89b153921ecc8729ccf9777fbb8c3e785fd7 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 00:24:21 +0100
Subject: [PATCH 12/23] Rewrite some awful code

---
 apps/openmw/mwbase/windowmanager.hpp    |  3 ---
 apps/openmw/mwgui/container.cpp         | 11 +++++++++
 apps/openmw/mwgui/container.hpp         |  1 +
 apps/openmw/mwgui/messagebox.cpp        | 20 +++++++---------
 apps/openmw/mwgui/messagebox.hpp        |  3 +--
 apps/openmw/mwgui/windowmanagerimp.cpp  | 11 ---------
 apps/openmw/mwgui/windowmanagerimp.hpp  |  2 --
 apps/openmw/mwinput/inputmanagerimp.cpp | 31 +------------------------
 apps/openmw/mwinput/inputmanagerimp.hpp |  1 -
 9 files changed, 22 insertions(+), 61 deletions(-)

diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp
index 6c85be5fd..c39de4400 100644
--- a/apps/openmw/mwbase/windowmanager.hpp
+++ b/apps/openmw/mwbase/windowmanager.hpp
@@ -227,9 +227,6 @@ namespace MWBase
             virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons = std::vector<std::string>(), bool showInDialogueModeOnly = false) = 0;
             virtual void staticMessageBox(const std::string& message) = 0;
             virtual void removeStaticMessageBox() = 0;
-
-            virtual void enterPressed () = 0;
-            virtual void activateKeyPressed () = 0;
             virtual int readPressedButton() = 0;
             ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
 
diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp
index bccc7120f..d22842b57 100644
--- a/apps/openmw/mwgui/container.cpp
+++ b/apps/openmw/mwgui/container.cpp
@@ -139,6 +139,7 @@ namespace MWGui
 
         mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked);
         mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked);
+        mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ContainerWindow::onKeyPressed);
         mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked);
 
         setCoord(200,0,600,300);
@@ -234,11 +235,21 @@ namespace MWGui
 
         mItemView->setModel (mSortModel);
 
+        MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton);
+
         // Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last
         // or we end up using a possibly invalid model.
         setTitle(MWWorld::Class::get(container).getName(container));
     }
 
+    void ContainerWindow::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)
+    {
+        if (_key == MyGUI::KeyCode::Space)
+            onCloseButtonClicked(mCloseButton);
+        if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter)
+            onTakeAllButtonClicked(mTakeButton);
+    }
+
     void ContainerWindow::close()
     {
         WindowBase::close();
diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp
index f934d8828..ce4707af6 100644
--- a/apps/openmw/mwgui/container.hpp
+++ b/apps/openmw/mwgui/container.hpp
@@ -75,6 +75,7 @@ namespace MWGui
         void onCloseButtonClicked(MyGUI::Widget* _sender);
         void onTakeAllButtonClicked(MyGUI::Widget* _sender);
         void onDisposeCorpseButtonClicked(MyGUI::Widget* sender);
+        void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char);
 
         /// @return is taking the item allowed?
         bool onTakeItem(const ItemStack& item, int count);
diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp
index b0abaef14..ae01f4293 100644
--- a/apps/openmw/mwgui/messagebox.cpp
+++ b/apps/openmw/mwgui/messagebox.cpp
@@ -127,12 +127,6 @@ namespace MWGui
         mMessageBoxSpeed = speed;
     }
 
-    void MessageBoxManager::okayPressed ()
-    {
-        if(mInterMessageBoxe != NULL)
-            mInterMessageBoxe->okayPressed();
-    }
-
     int MessageBoxManager::readPressedButton ()
     {
         int pressed = mLastButtonPressed;
@@ -333,23 +327,25 @@ namespace MWGui
             }
 
         }
-    }
-
-    void InteractiveMessageBox::okayPressed()
-    {
 
+        // Set key focus to "Ok" button
         std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}"));
         std::vector<MyGUI::Button*>::const_iterator button;
         for(button = mButtons.begin(); button != mButtons.end(); ++button)
         {
             if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok)
             {
-                buttonActivated(*button);
-                MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
+                MyGUI::InputManager::getInstance().setKeyFocusWidget(*button);
+                (*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed);
                 break;
             }
         }
+    }
 
+    void InteractiveMessageBox::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)
+    {
+        if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter || _key == MyGUI::KeyCode::Space)
+            buttonActivated(_sender);
     }
 
     void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed)
diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp
index e82a51642..caa37008c 100644
--- a/apps/openmw/mwgui/messagebox.hpp
+++ b/apps/openmw/mwgui/messagebox.hpp
@@ -33,7 +33,6 @@ namespace MWGui
             bool removeMessageBox (MessageBox *msgbox);
             void setMessageBoxSpeed (int speed);
 
-            void okayPressed();
             int readPressedButton ();
 
             typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int;
@@ -74,7 +73,6 @@ namespace MWGui
     {
         public:
             InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons);
-            void okayPressed ();
             void mousePressed (MyGUI::Widget* _widget);
             int readPressedButton ();
 
@@ -82,6 +80,7 @@ namespace MWGui
 
         private:
             void buttonActivated (MyGUI::Widget* _widget);
+            void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char);
 
             MessageBoxManager& mMessageBoxManager;
             MyGUI::EditBox* mMessageWidget;
diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp
index e4297f8fc..cda146e8c 100644
--- a/apps/openmw/mwgui/windowmanagerimp.cpp
+++ b/apps/openmw/mwgui/windowmanagerimp.cpp
@@ -677,17 +677,6 @@ namespace MWGui
         mMessageBoxManager->removeStaticMessageBox();
     }
 
-    void WindowManager::enterPressed ()
-    {
-        mMessageBoxManager->okayPressed();
-    }
-
-    void WindowManager::activateKeyPressed ()
-    {
-        mMessageBoxManager->okayPressed();
-        mCountDialog->cancel();
-    }
-
     int WindowManager::readPressedButton ()
     {
         return mMessageBoxManager->readPressedButton();
diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp
index a3b135ad4..9838a667f 100644
--- a/apps/openmw/mwgui/windowmanagerimp.hpp
+++ b/apps/openmw/mwgui/windowmanagerimp.hpp
@@ -222,8 +222,6 @@ namespace MWGui
     virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons = std::vector<std::string>(), bool showInDialogueModeOnly = false);
     virtual void staticMessageBox(const std::string& message);
     virtual void removeStaticMessageBox();
-    virtual void enterPressed ();
-    virtual void activateKeyPressed ();
     virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
 
     virtual void onFrame (float frameDuration);
diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp
index c2efa0c33..f0feba89f 100644
--- a/apps/openmw/mwinput/inputmanagerimp.cpp
+++ b/apps/openmw/mwinput/inputmanagerimp.cpp
@@ -195,14 +195,7 @@ namespace MWInput
             case A_Activate:
                 resetIdleTime();
 
-                if (MWBase::Environment::get().getWindowManager()->isGuiMode())
-                {
-                    if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container)
-                        toggleContainer ();
-                    else
-                        MWBase::Environment::get().getWindowManager()->activateKeyPressed();
-                }
-                else
+                if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
                     activate();
                 break;
             case A_Journal:
@@ -511,13 +504,6 @@ namespace MWInput
 
         mInputBinder->keyPressed (arg);
 
-        if((arg.keysym.sym == SDLK_RETURN || arg.keysym.sym == SDLK_KP_ENTER)
-            && MWBase::Environment::get().getWindowManager()->isGuiMode())
-        {
-            // Pressing enter when a messagebox is prompting for "ok" will activate the ok button
-            MWBase::Environment::get().getWindowManager()->enterPressed();
-        }
-
         OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym);
 
         if (kc != OIS::KC_UNASSIGNED)
@@ -730,21 +716,6 @@ namespace MWInput
         // .. but don't touch any other mode, except container.
     }
 
-    void InputManager::toggleContainer()
-    {
-        if (MyGUI::InputManager::getInstance ().isModalAny())
-            return;
-
-        if(MWBase::Environment::get().getWindowManager()->isGuiMode())
-        {
-            if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container)
-                MWBase::Environment::get().getWindowManager()->popGuiMode();
-            else
-                MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container);
-        }
-
-    }
-
     void InputManager::toggleConsole()
     {
         if (MyGUI::InputManager::getInstance ().isModalAny())
diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp
index 4eaee9b69..d41b4c3f3 100644
--- a/apps/openmw/mwinput/inputmanagerimp.hpp
+++ b/apps/openmw/mwinput/inputmanagerimp.hpp
@@ -173,7 +173,6 @@ namespace MWInput
         void toggleSpell();
         void toggleWeapon();
         void toggleInventory();
-        void toggleContainer();
         void toggleConsole();
         void screenshot();
         void toggleJournal();

From c64dc2c8312d1177fdeb38b11adea7c776e40b9e Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 00:51:03 +0100
Subject: [PATCH 13/23] Terrain specular mapping: use a "_diffusespec" postfix
 to indicate specular information is present in the alpha channel. Use alpha
 directly instead of 1-alpha.

---
 components/terrain/material.cpp |  3 +++
 components/terrain/storage.cpp  |  9 +++++++++
 components/terrain/storage.hpp  |  1 +
 files/materials/terrain.shader  | 26 +++++++++++++-------------
 4 files changed, 26 insertions(+), 13 deletions(-)

diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp
index 5dcdb7fed..be468866b 100644
--- a/components/terrain/material.cpp
+++ b/components/terrain/material.cpp
@@ -281,6 +281,7 @@ namespace Terrain
                         // normal map (optional)
                         bool useNormalMap = mNormalMapping && !mLayerList[layerOffset+i].mNormalMap.empty() && !renderCompositeMap;
                         bool useParallax = useNormalMap && mParallaxMapping && layer.mParallax;
+                        bool useSpecular = layer.mSpecular;
                         if (useNormalMap)
                         {
                             anyNormalMaps = true;
@@ -292,6 +293,8 @@ namespace Terrain
                                                           sh::makeProperty (new sh::BooleanValue(useNormalMap)));
                         p->mShaderProperties.setProperty ("use_parallax_" + Ogre::StringConverter::toString(i),
                                                           sh::makeProperty (new sh::BooleanValue(useParallax)));
+                        p->mShaderProperties.setProperty ("use_specular_" + Ogre::StringConverter::toString(i),
+                                                          sh::makeProperty (new sh::BooleanValue(useSpecular)));
                         boost::hash_combine(normalMaps, useNormalMap);
                         boost::hash_combine(normalMaps, useNormalMap && layer.mParallax);
 
diff --git a/components/terrain/storage.cpp b/components/terrain/storage.cpp
index 9d6b44de8..398ebac01 100644
--- a/components/terrain/storage.cpp
+++ b/components/terrain/storage.cpp
@@ -475,6 +475,7 @@ namespace Terrain
 
         LayerInfo info;
         info.mParallax = false;
+        info.mSpecular = false;
         info.mDiffuseMap = "textures\\" + texture;
         std::string texture_ = texture;
         boost::replace_last(texture_, ".", "_nh.");
@@ -491,6 +492,14 @@ namespace Terrain
                 info.mNormalMap = "textures\\" + texture_;
         }
 
+        texture_ = texture;
+        boost::replace_last(texture_, ".", "_diffusespec.");
+        if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_))
+        {
+            info.mDiffuseMap = "textures\\" + texture_;
+            info.mSpecular = true;
+        }
+
         mLayerInfoMap[texture] = info;
 
         return info;
diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp
index 68fae01af..18d05b100 100644
--- a/components/terrain/storage.hpp
+++ b/components/terrain/storage.hpp
@@ -16,6 +16,7 @@ namespace Terrain
         std::string mDiffuseMap;
         std::string mNormalMap;
         bool mParallax; // Height info in normal map alpha channel?
+        bool mSpecular; // Specular info in diffuse map alpha channel?
     };
 
     /// We keep storage of terrain data abstract here since we need different implementations for game and editor
diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader
index 86eef36ff..1436de0c3 100644
--- a/files/materials/terrain.shader
+++ b/files/materials/terrain.shader
@@ -337,6 +337,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5;
         float2 layerUV = float2(UV.x, 1.f-UV.y) * 16; // Reverse Y, required to get proper tangents
         float2 thisLayerUV;
         float4 normalTex;
+        float4 diffuseTex;
 
         float3 eyeDir = normalize(cameraPos.xyz - worldPos);
 #if PARALLAX
@@ -358,19 +359,18 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5;
         thisLayerUV += TSeyeDir.xy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS );
 #endif
 
-#if IS_FIRST_PASS
-        #if @shIterator == 0
-        // first layer of first pass is the base layer and doesn't need a blend map
-        albedo = shSample(diffuseMap0, layerUV);
-        #else
-        albedo = shLerp(albedo, shSample(diffuseMap@shIterator, thisLayerUV), blendValues@shPropertyString(blendmap_component_@shIterator));
-        #endif
+        diffuseTex = shSample(diffuseMap@shIterator, layerUV);
+#if !@shPropertyBool(use_specular_@shIterator)
+        diffuseTex.a = 0;
+#endif
+
+#if @shIterator == 0
+albedo = diffuseTex;
 #else
-        #if @shIterator == 0
-        albedo = shSample(diffuseMap@shIterator, layerUV);
-        #else
-        albedo = shLerp(albedo, shSample(diffuseMap@shIterator, thisLayerUV), blendValues@shPropertyString(blendmap_component_@shIterator));
-        #endif
+albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_component_@shIterator));
+#endif
+
+#if !IS_FIRST_PASS
         previousAlpha *= 1.f-blendValues@shPropertyString(blendmap_component_@shIterator);
 #endif
 
@@ -448,7 +448,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5;
         float3 halfVec = normalize (light0Dir + eyeDir);
 
         float3 specular = pow(max(dot(normal, halfVec), 0), 32) * lightSpec0;
-        shOutputColour(0).xyz += specular * (1.f-albedo.a) * shadow;
+        shOutputColour(0).xyz += specular * (albedo.a) * shadow;
 #endif
 
 #if FOG

From 727aa30347a345e7a61bebffa3e05d94586622ea Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 02:06:54 +0100
Subject: [PATCH 14/23] Dead actors are unaware of everything

---
 apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
index 002cd561d..2ce196fdd 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
@@ -857,6 +857,9 @@ namespace MWMechanics
 
     bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer)
     {
+        if (observer.getClass().getCreatureStats(observer).isDead())
+            return false;
+
         const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
 
         CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);

From d4a98ffc270e6e19504c4da1615bf26d4b352ef6 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 02:27:44 +0100
Subject: [PATCH 15/23] Increase fight rating when being the victim of a crime

---
 .../mwmechanics/mechanicsmanagerimp.cpp       | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
index 2ce196fdd..57b876723 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
@@ -840,6 +840,25 @@ namespace MWMechanics
         ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty()
                                                   + arg);
 
+        if (!victim.isEmpty())
+        {
+            int fight = 0;
+            // Increase in fight rating for each type of crime
+            if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
+                fight = store.find("iFightTrespass")->getFloat();
+            else if (type == OT_Pickpocket)
+                fight = store.find("iFightPickpocket")->getInt();
+            else if (type == OT_Assault)
+                fight = store.find("iFightAttack")->getInt();
+            else if (type == OT_Murder)
+                fight = store.find("iFightKilling")->getInt();
+            else if (type == OT_Theft)
+                fight = store.find("fFightStealing")->getFloat();
+            // Not sure if this should be permanent?
+            fight = victim.getClass().getCreatureStats(victim).getAiSetting(CreatureStats::AI_Fight).getBase() + fight;
+            victim.getClass().getCreatureStats(victim).setAiSetting(CreatureStats::AI_Fight, fight);
+        }
+
         // If committing a crime against a faction member, expell from the faction
         if (!victim.isEmpty() && victim.getClass().isNpc())
         {

From 12944f2459a13296b71ee4c9ea2bafac2d549256 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 03:07:35 +0100
Subject: [PATCH 16/23] Fix an auto equipping bug that allowed twohanded weapon
 and shield at the same time

---
 apps/openmw/mwworld/inventorystore.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp
index b11c22c72..3e81da212 100644
--- a/apps/openmw/mwworld/inventorystore.cpp
+++ b/apps/openmw/mwworld/inventorystore.cpp
@@ -165,7 +165,6 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot)
 void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
 {
     const MWMechanics::NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor);
-    MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor);
 
     TSlots slots_;
     initSlots (slots_);
@@ -266,10 +265,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
                 case 0:
                     continue;
                 case 2:
-                    invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, actor);
+                    slots_[MWWorld::InventoryStore::Slot_CarriedLeft] = end();
                     break;
                 case 3:
-                    invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, actor);
+                    // Prefer keeping twohanded weapon
                     break;
             }
 

From 909494ff3524a93a450d63656fe450dd863cdda5 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 03:08:16 +0100
Subject: [PATCH 17/23] Implement Assault crimes. In other words, NPCs now
 fight back!

---
 apps/openmw/mwclass/npc.cpp                     |  4 ++++
 apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 11 ++++++-----
 apps/openmw/mwmechanics/spellcasting.cpp        |  7 +++++++
 3 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp
index 1c5c302d8..68b7d41f5 100644
--- a/apps/openmw/mwclass/npc.cpp
+++ b/apps/openmw/mwclass/npc.cpp
@@ -587,6 +587,10 @@ namespace MWClass
 
         // NOTE: 'object' and/or 'attacker' may be empty.
 
+        // Attacking peaceful NPCs is a crime
+        if (ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
+            MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
+
         if(!successful)
         {
             // TODO: Handle HitAttemptOnMe script function
diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
index 57b876723..901b6e414 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
@@ -786,7 +786,8 @@ namespace MWMechanics
 
     bool MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg)
     {
-        // TODO: expell from faction
+        if (ptr.getRefData().getHandle() != "player")
+            return false;
 
         bool reported=false;
         for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it)
@@ -803,10 +804,7 @@ namespace MWMechanics
 
                 // Actor has witnessed a crime. Will he report it?
                 // (not sure, is > 0 correct?)
-                if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0
-                        // This is a bit inconsistent, but AFAIK assaulted NPCs can not report if they are alone
-                        && (type != OT_Assault || it->first != victim)
-                )
+                if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0)
                 {
                     // TODO: stats.setAlarmed(true) on NPCs within earshot
                     // fAlarmRadius ?
@@ -836,6 +834,9 @@ namespace MWMechanics
         else if (type == OT_Theft)
             arg *= store.find("fCrimeStealing")->getFloat();
 
+        // TODO: In some cases (type == Assault), if no NPCs are within earshot, the report will have no effect.
+        // however other crime types seem to be always produce a bounty.
+
         MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
         ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty()
                                                   + arg);
diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp
index 8c4c4d292..b8c36d213 100644
--- a/apps/openmw/mwmechanics/spellcasting.cpp
+++ b/apps/openmw/mwmechanics/spellcasting.cpp
@@ -57,6 +57,7 @@ namespace MWMechanics
         ESM::EffectList reflectedEffects;
         std::vector<ActiveSpells::Effect> appliedLastingEffects;
         bool firstAppliedEffect = true;
+        bool anyHarmfulEffect = false;
 
         for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
             effectIt!=effects.mList.end(); ++effectIt)
@@ -77,6 +78,8 @@ namespace MWMechanics
             float magnitudeMult = 1;
             if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor())
             {
+                anyHarmfulEffect = true;
+
                 // If player is attempting to cast a harmful spell, show the target's HP bar
                 if (caster.getRefData().getHandle() == "player" && target != caster)
                     MWBase::Environment::get().getWindowManager()->setEnemy(target);
@@ -218,6 +221,10 @@ namespace MWMechanics
         if (appliedLastingEffects.size())
             target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
                                                                                   mSourceName, caster.getRefData().getHandle());
+
+        if (anyHarmfulEffect && target.getClass().isActor()
+                && target.getClass().getCreatureStats(target).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
+            MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault);
     }
 
     void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, MWMechanics::EffectKey effect, float magnitude)

From bf6d302fbadbfdf3399e12a2fbfaca6d06406bec Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 03:29:41 +0100
Subject: [PATCH 18/23] Confiscate stolen items when caught

---
 apps/openmw/mwbase/world.hpp            |  3 ++
 apps/openmw/mwscript/miscextensions.cpp |  5 ++--
 apps/openmw/mwworld/cells.cpp           | 12 ++++++++
 apps/openmw/mwworld/cells.hpp           |  6 ++++
 apps/openmw/mwworld/worldimp.cpp        | 40 +++++++++++++++++++++++++
 apps/openmw/mwworld/worldimp.hpp        |  3 ++
 6 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp
index 43e526ecb..10e25b376 100644
--- a/apps/openmw/mwbase/world.hpp
+++ b/apps/openmw/mwbase/world.hpp
@@ -451,6 +451,9 @@ namespace MWBase
             /// Update the value of some globals according to the world state, which may be used by dialogue entries.
             /// This should be called when initiating a dialogue.
             virtual void updateDialogueGlobals() = 0;
+
+            /// Moves all stolen items from \a ptr to the closest evidence chest.
+            virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0;
     };
 }
 
diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp
index a403d76ed..f69f2e7cb 100644
--- a/apps/openmw/mwscript/miscextensions.cpp
+++ b/apps/openmw/mwscript/miscextensions.cpp
@@ -769,8 +769,8 @@ namespace MWScript
                 MWWorld::Ptr player = world->getPlayerPtr();
                 world->teleportToClosestMarker(player, "prisonmarker");
                 player.getClass().getNpcStats(player).setBounty(0);
+                world->confiscateStolenItems(player);
                 // TODO: pass time, change skills, show messagebox
-                // TODO: move stolen items to closest evidence chest
                 // iDaysinPrisonMod
             }
         };
@@ -782,8 +782,7 @@ namespace MWScript
             {
                 MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
                 player.getClass().getNpcStats(player).setBounty(0);
-
-                // TODO: move stolen items to closest evidence chest
+                MWBase::Environment::get().getWorld()->confiscateStolenItems(player);
             }
         };
 
diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp
index 621ff3b31..575e10f04 100644
--- a/apps/openmw/mwworld/cells.cpp
+++ b/apps/openmw/mwworld/cells.cpp
@@ -271,3 +271,15 @@ void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector<MWWorl
     }
 
 }
+
+void MWWorld::Cells::getInteriorPtrs(const std::string &name, std::vector<MWWorld::Ptr> &out)
+{
+    for (std::map<std::string, Ptr::CellStore>::iterator iter = mInteriors.begin();
+        iter!=mInteriors.end(); ++iter)
+    {
+        Ptr ptr = getPtrAndCache (name, iter->second);
+        if (!ptr.isEmpty())
+            out.push_back(ptr);
+    }
+
+}
diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp
index 31de2f60e..afa7c03f2 100644
--- a/apps/openmw/mwworld/cells.hpp
+++ b/apps/openmw/mwworld/cells.hpp
@@ -56,6 +56,12 @@ namespace MWWorld
             /// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
             /// @note name must be lower case
             void getExteriorPtrs (const std::string& name, std::vector<MWWorld::Ptr>& out);
+
+            /// Get all Ptrs referencing \a name in interior cells
+            /// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
+            /// @note name must be lower case
+            void getInteriorPtrs (const std::string& name, std::vector<MWWorld::Ptr>& out);
+
     };
 }
 
diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp
index 738c71a7d..c5315afe6 100644
--- a/apps/openmw/mwworld/worldimp.cpp
+++ b/apps/openmw/mwworld/worldimp.cpp
@@ -2446,4 +2446,44 @@ namespace MWWorld
         mGlobalVariables->setInt("crimegoldturnin", turnIn);
         mGlobalVariables->setInt("pchasturnin", (turnIn <= playerGold) ? 1 : 0);
     }
+
+    void World::confiscateStolenItems(const Ptr &ptr)
+    {
+        Ogre::Vector3 playerPos;
+        if (!findInteriorPositionInWorldSpace(ptr.getCell(), playerPos))
+            playerPos = mPlayer->getLastKnownExteriorPosition();
+
+        MWWorld::Ptr closestChest;
+        float closestDistance = FLT_MAX;
+
+        std::vector<MWWorld::Ptr> chests;
+        mCells.getInteriorPtrs("stolen_goods", chests);
+
+        Ogre::Vector3 chestPos;
+        for (std::vector<MWWorld::Ptr>::iterator it = chests.begin(); it != chests.end(); ++it)
+        {
+            if (!findInteriorPositionInWorldSpace(it->getCell(), chestPos))
+                continue;
+
+            float distance = playerPos.squaredDistance(chestPos);
+            if (distance < closestDistance)
+            {
+                closestDistance = distance;
+                closestChest = *it;
+            }
+        }
+
+        if (!closestChest.isEmpty())
+        {
+            ContainerStore& store = ptr.getClass().getContainerStore(ptr);
+            for (ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
+            {
+                if (!it->getCellRef().mOwner.empty() && it->getCellRef().mOwner != "player")
+                {
+                    closestChest.getClass().getContainerStore(closestChest).add(*it, it->getRefData().getCount(), closestChest);
+                    store.remove(*it, it->getRefData().getCount(), ptr);
+                }
+            }
+        }
+    }
 }
diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp
index cc087a89f..8c091de50 100644
--- a/apps/openmw/mwworld/worldimp.hpp
+++ b/apps/openmw/mwworld/worldimp.hpp
@@ -536,6 +536,9 @@ namespace MWWorld
             /// Update the value of some globals according to the world state, which may be used by dialogue entries.
             /// This should be called when initiating a dialogue.
             virtual void updateDialogueGlobals();
+
+            /// Moves all stolen items from \a ptr to the closest evidence chest.
+            virtual void confiscateStolenItems(const MWWorld::Ptr& ptr);
     };
 }
 

From 9127839cc1b9e2c71aede268b1e2c0a1220a019a Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 03:33:17 +0100
Subject: [PATCH 19/23] Add a searchPtr method as required for getting an
 owner, which may already be dead and disposed of.

---
 apps/openmw/mwbase/world.hpp                    |  4 ++++
 apps/openmw/mwmechanics/mechanicsmanagerimp.cpp |  2 +-
 apps/openmw/mwworld/worldimp.cpp                | 15 ++++++++++-----
 apps/openmw/mwworld/worldimp.hpp                |  4 ++++
 4 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp
index 10e25b376..2fb5f02eb 100644
--- a/apps/openmw/mwbase/world.hpp
+++ b/apps/openmw/mwbase/world.hpp
@@ -157,6 +157,10 @@ namespace MWBase
             ///< Return a pointer to a liveCellRef with the given name.
             /// \param activeOnly do non search inactive cells.
 
+            virtual MWWorld::Ptr searchPtr (const std::string& name, bool activeOnly) = 0;
+            ///< Return a pointer to a liveCellRef with the given name.
+            /// \param activeOnly do non search inactive cells.
+
             virtual MWWorld::Ptr getPtrViaHandle (const std::string& handle) = 0;
             ///< Return a pointer to a liveCellRef with the given Ogre handle.
 
diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
index 901b6e414..76ba2ff16 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
@@ -34,7 +34,7 @@ namespace
         }
 
         if (!item.getCellRef().mOwner.empty())
-            victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true);
+            victim = MWBase::Environment::get().getWorld()->searchPtr(item.getCellRef().mOwner, true);
 
         return (!isOwned && !isFactionOwned);
     }
diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp
index c5315afe6..ca85d7a7f 100644
--- a/apps/openmw/mwworld/worldimp.cpp
+++ b/apps/openmw/mwworld/worldimp.cpp
@@ -485,8 +485,9 @@ namespace MWWorld
         mLocalScripts.remove (ref);
     }
 
-    Ptr World::getPtr (const std::string& name, bool activeOnly)
+    Ptr World::searchPtr (const std::string& name, bool activeOnly)
     {
+        Ptr ret;
         // the player is always in an active cell.
         if (name=="player")
         {
@@ -514,12 +515,16 @@ namespace MWWorld
 
         if (!activeOnly)
         {
-            Ptr ptr = mCells.getPtr (lowerCaseName);
-
-            if (!ptr.isEmpty())
-                return ptr;
+            ret = mCells.getPtr (lowerCaseName);
         }
+        return ret;
+    }
 
+    Ptr World::getPtr (const std::string& name, bool activeOnly)
+    {
+        Ptr ret = searchPtr(name, activeOnly);
+        if (!ret.isEmpty())
+            return ret;
         throw std::runtime_error ("unknown ID: " + name);
     }
 
diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp
index 8c091de50..d5ccd7625 100644
--- a/apps/openmw/mwworld/worldimp.hpp
+++ b/apps/openmw/mwworld/worldimp.hpp
@@ -232,6 +232,10 @@ namespace MWWorld
             ///< Return a pointer to a liveCellRef with the given name.
             /// \param activeOnly do non search inactive cells.
 
+            virtual Ptr searchPtr (const std::string& name, bool activeOnly);
+            ///< Return a pointer to a liveCellRef with the given name.
+            /// \param activeOnly do non search inactive cells.
+
             virtual Ptr getPtrViaHandle (const std::string& handle);
             ///< Return a pointer to a liveCellRef with the given Ogre handle.
 

From 9ddee8fd8c6b00e7ff5f4904118056b02bdf30d2 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 04:03:13 +0100
Subject: [PATCH 20/23] Autocalculate NPC reputation as according to research
 wiki

---
 apps/openmw/mwclass/npc.cpp | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp
index 68b7d41f5..12ed2dd09 100644
--- a/apps/openmw/mwclass/npc.cpp
+++ b/apps/openmw/mwclass/npc.cpp
@@ -307,6 +307,17 @@ namespace MWClass
                 autoCalculateSkills(ref->mBase, data->mNpcStats);
             }
 
+            if (data->mNpcStats.getFactionRanks().size())
+            {
+                static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
+                        .find("iAutoRepFacMod")->getInt();
+                static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
+                        .find("iAutoRepLevMod")->getInt();
+                int rank = data->mNpcStats.getFactionRanks().begin()->second;
+
+                data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1));
+            }
+
             data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage);
 
             data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello);

From ce6aab89cf9d7dde54f994aba21c7fda228ff269 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 04:58:30 +0100
Subject: [PATCH 21/23] Fix a possible permutation issue

---
 components/terrain/material.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp
index be468866b..8e78d2216 100644
--- a/components/terrain/material.cpp
+++ b/components/terrain/material.cpp
@@ -297,6 +297,7 @@ namespace Terrain
                                                           sh::makeProperty (new sh::BooleanValue(useSpecular)));
                         boost::hash_combine(normalMaps, useNormalMap);
                         boost::hash_combine(normalMaps, useNormalMap && layer.mParallax);
+                        boost::hash_combine(normalMaps, useSpecular);
 
                         if (i+layerOffset > 0)
                         {

From 3896c88403c71cfef013fa9cc713fe5a8a910910 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 05:58:05 +0100
Subject: [PATCH 22/23] Use VFX_DefaultCast / VFX_DefaultHit if the magic
 effect does not specify any

---
 apps/openmw/mwmechanics/actors.cpp       |  2 +-
 apps/openmw/mwmechanics/character.cpp    |  7 ++++++-
 apps/openmw/mwmechanics/spellcasting.cpp | 18 ++++++++++--------
 3 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp
index afc94255b..468a21892 100644
--- a/apps/openmw/mwmechanics/actors.cpp
+++ b/apps/openmw/mwmechanics/actors.cpp
@@ -525,7 +525,7 @@ namespace MWMechanics
                         ref.getPtr().getCellRef().mPos = ipos;
 
                         // TODO: Add AI to follow player and fight for him
-
+                        // TODO: VFX_SummonStart, VFX_SummonEnd
                         creatureStats.mSummonedCreatures.insert(std::make_pair(it->first,
                             MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle()));
                     }
diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp
index 99067a6b7..fed3f485f 100644
--- a/apps/openmw/mwmechanics/character.cpp
+++ b/apps/openmw/mwmechanics/character.cpp
@@ -592,7 +592,12 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
                     const ESM::MagicEffect *effect;
                     effect = store.get<ESM::MagicEffect>().find(effectentry.mEffectID);
 
-                    const ESM::Static* castStatic = store.get<ESM::Static>().find (effect->mCasting);
+                    const ESM::Static* castStatic;
+                    if (!effect->mCasting.empty())
+                        castStatic = store.get<ESM::Static>().find (effect->mCasting);
+                    else
+                        castStatic = store.get<ESM::Static>().find ("VFX_DefaultCast");
+
                     mAnimation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex);
 
                     castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_Hands");
diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp
index b8c36d213..850c4dcf5 100644
--- a/apps/openmw/mwmechanics/spellcasting.cpp
+++ b/apps/openmw/mwmechanics/spellcasting.cpp
@@ -200,15 +200,17 @@ namespace MWMechanics
                     }
 
                     // Add VFX
+                    const ESM::Static* castStatic;
                     if (!magicEffect->mHit.empty())
-                    {
-                        const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);
-                        bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
-                        // Note: in case of non actor, a free effect should be fine as well
-                        MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
-                        if (anim)
-                            anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "");
-                    }
+                        castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);
+                    else
+                        castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit");
+
+                    bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
+                    // Note: in case of non actor, a free effect should be fine as well
+                    MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
+                    if (anim)
+                        anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "");
                 }
 
                 // TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World.

From a2ba0dde31036bb45646488a6d4bc6348c65a443 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Sat, 11 Jan 2014 06:47:58 +0100
Subject: [PATCH 23/23] Implemented GoToJail

---
 apps/openmw/mwbase/world.hpp            |  2 +
 apps/openmw/mwscript/miscextensions.cpp |  7 +--
 apps/openmw/mwworld/worldimp.cpp        | 79 ++++++++++++++++++++++++-
 apps/openmw/mwworld/worldimp.hpp        |  3 +
 4 files changed, 84 insertions(+), 7 deletions(-)

diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp
index 2fb5f02eb..fe40fab24 100644
--- a/apps/openmw/mwbase/world.hpp
+++ b/apps/openmw/mwbase/world.hpp
@@ -458,6 +458,8 @@ namespace MWBase
 
             /// Moves all stolen items from \a ptr to the closest evidence chest.
             virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0;
+
+            virtual void goToJail () = 0;
     };
 }
 
diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp
index f69f2e7cb..bb3600a27 100644
--- a/apps/openmw/mwscript/miscextensions.cpp
+++ b/apps/openmw/mwscript/miscextensions.cpp
@@ -766,12 +766,7 @@ namespace MWScript
             virtual void execute (Interpreter::Runtime& runtime)
             {
                 MWBase::World* world = MWBase::Environment::get().getWorld();
-                MWWorld::Ptr player = world->getPlayerPtr();
-                world->teleportToClosestMarker(player, "prisonmarker");
-                player.getClass().getNpcStats(player).setBounty(0);
-                world->confiscateStolenItems(player);
-                // TODO: pass time, change skills, show messagebox
-                // iDaysinPrisonMod
+                world->goToJail();
             }
         };
 
diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp
index ca85d7a7f..620058c65 100644
--- a/apps/openmw/mwworld/worldimp.cpp
+++ b/apps/openmw/mwworld/worldimp.cpp
@@ -216,7 +216,7 @@ namespace MWWorld
       mSky (true), mCells (mStore, mEsm),
       mActivationDistanceOverride (mActivationDistanceOverride),
       mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), mLevitationEnabled(false),
-      mFacedDistance(FLT_MAX), mGodMode(false)
+      mFacedDistance(FLT_MAX), mGodMode(false), mGoToJail(false)
     {
         mPhysics = new PhysicsSystem(renderer);
         mPhysEngine = mPhysics->getEngine();
@@ -259,6 +259,9 @@ namespace MWWorld
 
     void World::startNewGame()
     {
+        mGoToJail = false;
+        mLevitationEnabled = true;
+        mTeleportEnabled = true;
         mWorldScene->changeToVoid();
 
         mStore.clearDynamic();
@@ -1272,6 +1275,9 @@ namespace MWWorld
                 mRendering->playVideo(mFallback.getFallbackString("Movies_New_Game"), true);
         }
 
+        if (mGoToJail && !paused)
+            goToJail();
+
         updateWeather(duration);
 
         mWorldScene->update (duration, paused);
@@ -2491,4 +2497,75 @@ namespace MWWorld
             }
         }
     }
+
+    void World::goToJail()
+    {
+        if (!mGoToJail)
+        {
+            // Save for next update, since the player should be able to read the dialog text first
+            mGoToJail = true;
+            return;
+        }
+        else
+        {
+            mGoToJail = false;
+
+            MWWorld::Ptr player = getPlayerPtr();
+            teleportToClosestMarker(player, "prisonmarker");
+            int bounty = player.getClass().getNpcStats(player).getBounty();
+            player.getClass().getNpcStats(player).setBounty(0);
+            confiscateStolenItems(player);
+
+            int iDaysinPrisonMod = getStore().get<ESM::GameSetting>().find("iDaysinPrisonMod")->getInt();
+            int days = std::max(1, bounty / iDaysinPrisonMod);
+
+            advanceTime(days * 24);
+
+            std::set<int> skills;
+            for (int day=0; day<days; ++day)
+            {
+                int skill = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * ESM::Skill::Length;
+                skills.insert(skill);
+
+                MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill);
+                if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak)
+                    value.setBase(std::min(100, value.getBase()+1));
+                else
+                    value.setBase(value.getBase()-1);
+            }
+
+            const Store<ESM::GameSetting>& gmst = getStore().get<ESM::GameSetting>();
+
+            std::string message;
+            if (days == 1)
+                message = gmst.find("sNotifyMessage42")->getString();
+            else
+                message = gmst.find("sNotifyMessage43")->getString();
+
+            std::stringstream dayStr;
+            dayStr << days;
+            if (message.find("%d") != std::string::npos)
+                message.replace(message.find("%d"), 2, dayStr.str());
+
+            for (std::set<int>::iterator it = skills.begin(); it != skills.end(); ++it)
+            {
+                std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[*it])->getString();
+                std::stringstream skillValue;
+                skillValue << player.getClass().getNpcStats(player).getSkill(*it).getBase();
+                std::string skillMsg = gmst.find("sNotifyMessage44")->getString();
+                if (*it == ESM::Skill::Sneak || *it == ESM::Skill::Security)
+                    skillMsg = gmst.find("sNotifyMessage39")->getString();
+
+                if (skillMsg.find("%s") != std::string::npos)
+                    skillMsg.replace(skillMsg.find("%s"), 2, skillName);
+                if (skillMsg.find("%d") != std::string::npos)
+                    skillMsg.replace(skillMsg.find("%d"), 2, skillValue.str());
+                message += "\n" + skillMsg;
+            }
+
+            std::vector<std::string> buttons;
+            buttons.push_back("#{sOk}");
+            MWBase::Environment::get().getWindowManager()->messageBox(message, buttons);
+        }
+    }
 }
diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp
index d5ccd7625..634cc8d6b 100644
--- a/apps/openmw/mwworld/worldimp.hpp
+++ b/apps/openmw/mwworld/worldimp.hpp
@@ -151,6 +151,7 @@ namespace MWWorld
 
             bool mTeleportEnabled;
             bool mLevitationEnabled;
+            bool mGoToJail;
 
             /// Called when \a object is moved to an inactive cell
             void objectLeftActiveCell (MWWorld::Ptr object, MWWorld::Ptr movedPtr);
@@ -543,6 +544,8 @@ namespace MWWorld
 
             /// Moves all stolen items from \a ptr to the closest evidence chest.
             virtual void confiscateStolenItems(const MWWorld::Ptr& ptr);
+
+            virtual void goToJail ();
     };
 }