diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp
index 0178a3f2b..74bc0b7fd 100644
--- a/apps/openmw/mwbase/world.hpp
+++ b/apps/openmw/mwbase/world.hpp
@@ -560,6 +560,12 @@ namespace MWBase
             virtual void removeContainerScripts(const MWWorld::Ptr& reference) = 0;
 
             virtual bool isPlayerInJail() const = 0;
+
+            /// Return terrain height at \a worldPos position.
+            virtual float getTerrainHeightAt(const osg::Vec3f& worldPos) const = 0;
+
+            /// Return physical or rendering half extents of the given actor.
+            virtual osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering=false) const = 0;
     };
 }
 
diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp
index 2e1dfbac5..80b343d4f 100644
--- a/apps/openmw/mwmechanics/aicombat.cpp
+++ b/apps/openmw/mwmechanics/aicombat.cpp
@@ -18,6 +18,7 @@
 #include "character.hpp"
 #include "aicombataction.hpp"
 #include "combat.hpp"
+#include "coordinateconverter.hpp"
 
 namespace
 {
@@ -50,6 +51,19 @@ namespace MWMechanics
         bool mForceNoShortcut;
         ESM::Position mShortcutFailPos;
         MWMechanics::Movement mMovement;
+
+        enum FleeState
+        {
+            FleeState_None,
+            FleeState_Idle,
+            FleeState_RunBlindly,
+            FleeState_RunToDestination
+        };
+        FleeState mFleeState;
+        bool mFleeLOS;
+        float mFleeUpdateLOSTimer;
+        float mFleeBlindRunTimer;
+        ESM::Pathgrid::Point mFleeDest;
         
         AiCombatStorage():
         mAttackCooldown(0),
@@ -66,7 +80,11 @@ namespace MWMechanics
         mStrength(),
         mForceNoShortcut(false),
         mShortcutFailPos(),
-        mMovement()
+        mMovement(),
+        mFleeState(FleeState_None),
+        mFleeLOS(false),
+        mFleeUpdateLOSTimer(0.0f),
+        mFleeBlindRunTimer(0.0f)
         {}
 
         void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
@@ -76,6 +94,10 @@ namespace MWMechanics
             const ESM::Weapon* weapon, bool distantCombat);
         void updateAttack(CharacterController& characterController);
         void stopAttack();
+
+        void startFleeing();
+        void stopFleeing();
+        bool isFleeing();
     };
     
     AiCombat::AiCombat(const MWWorld::Ptr& actor) :
@@ -157,16 +179,23 @@ namespace MWMechanics
                 || target.getClass().getCreatureStats(target).isDead())
             return true;
 
-        if (storage.mCurrentAction.get()) // need to wait to init action with it's attack range
+        if (!storage.isFleeing())
         {
-            //Update every frame
-            bool is_target_reached = pathTo(actor, target.getRefData().getPosition().pos, duration, storage.mAttackRange);
-            if (is_target_reached) storage.mReadyToAttack = true;
-        }
+            if (storage.mCurrentAction.get()) // need to wait to init action with it's attack range
+            {
+                //Update every frame
+                bool is_target_reached = pathTo(actor, target.getRefData().getPosition().pos, duration, storage.mAttackRange);
+                if (is_target_reached) storage.mReadyToAttack = true;
+            }
 
-        storage.updateCombatMove(duration);
-        if (storage.mReadyToAttack) updateActorsMovement(actor, duration, storage);
-        storage.updateAttack(characterController);
+            storage.updateCombatMove(duration);
+            if (storage.mReadyToAttack) updateActorsMovement(actor, duration, storage);
+            storage.updateAttack(characterController);
+        }
+        else
+        {
+            updateFleeing(actor, target, duration, storage);
+        }
         storage.mActionCooldown -= duration;
 
         float& timerReact = storage.mTimerReact;
@@ -185,12 +214,6 @@ namespace MWMechanics
 
     void AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController)
     {
-        if (isTargetMagicallyHidden(target))
-        {
-            storage.stopAttack();
-            return; // TODO: run away instead of doing nothing
-        }
-
         const MWWorld::CellStore*& currentCell = storage.mCell;
         bool cellChange = currentCell && (actor.getCell() != currentCell);
         if(!currentCell || cellChange)
@@ -198,30 +221,61 @@ namespace MWMechanics
             currentCell = actor.getCell();
         }
 
+        bool forceFlee = false;
+        if (!canFight(actor, target))
+        {
+            storage.stopAttack();
+            characterController.setAttackingOrSpell(false);
+            storage.mActionCooldown = 0.f;
+            forceFlee = true;
+        }
+
         const MWWorld::Class& actorClass = actor.getClass();
         actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
 
         float& actionCooldown = storage.mActionCooldown;
-        if (actionCooldown > 0)
-            return;
-
-        float &rangeAttack = storage.mAttackRange;
         boost::shared_ptr<Action>& currentAction = storage.mCurrentAction;
-        if (characterController.readyToPrepareAttack())
+
+        if (!forceFlee)
         {
-            currentAction = prepareNextAction(actor, target);
+            if (actionCooldown > 0)
+                return;
+
+            if (characterController.readyToPrepareAttack())
+            {
+                currentAction = prepareNextAction(actor, target);
+                actionCooldown = currentAction->getActionCooldown();
+            }
+        }
+        else
+        {
+            currentAction.reset(new ActionFlee());
             actionCooldown = currentAction->getActionCooldown();
         }
 
-        const ESM::Weapon *weapon = NULL;
-        bool isRangedCombat = false;
-        if (currentAction.get())
+        if (!currentAction)
+            return;
+
+        if (storage.isFleeing() != currentAction->isFleeing())
         {
-            rangeAttack = currentAction->getCombatRange(isRangedCombat);
-            // Get weapon characteristics
-            weapon = currentAction->getWeapon();
+            if (currentAction->isFleeing())
+            {
+                storage.startFleeing();
+                MWBase::Environment::get().getDialogueManager()->say(actor, "flee");
+                return;
+            }
+            else
+                storage.stopFleeing();
         }
 
+        bool isRangedCombat = false;
+        float &rangeAttack = storage.mAttackRange;
+
+        rangeAttack = currentAction->getCombatRange(isRangedCombat);
+
+        // Get weapon characteristics
+        const ESM::Weapon* weapon = currentAction->getWeapon();
+
         ESM::Position pos = actor.getRefData().getPosition();
         osg::Vec3f vActorPos(pos.asVec3());
         osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3());
@@ -229,19 +283,7 @@ namespace MWMechanics
         osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target);
         float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target);
 
-        if (!currentAction)
-            return;
-
         storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack);
-        
-        // can't fight if attacker can't go where target is.  E.g. A fish can't attack person on land.
-        if (distToTarget > rangeAttack
-                && !actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target))
-        {
-            // TODO: start fleeing?
-            storage.stopAttack();
-            return;
-        }
 
         if (storage.mReadyToAttack)
         {
@@ -267,6 +309,106 @@ namespace MWMechanics
         }
     }
 
+    void MWMechanics::AiCombat::updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage)
+    {
+        static const float LOS_UPDATE_DURATION = 0.5f;
+        static const float BLIND_RUN_DURATION = 1.0f;
+
+        if (storage.mFleeUpdateLOSTimer <= 0.f)
+        {
+            storage.mFleeLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target);
+            storage.mFleeUpdateLOSTimer = LOS_UPDATE_DURATION;
+        }
+        else
+            storage.mFleeUpdateLOSTimer -= duration;
+
+        AiCombatStorage::FleeState& state = storage.mFleeState;
+        switch (state)
+        {
+            case AiCombatStorage::FleeState_None:
+                return;
+
+            case AiCombatStorage::FleeState_Idle:
+                {
+                    float triggerDist = getMaxAttackDistance(target);
+
+                    if (storage.mFleeLOS &&
+                            (triggerDist >= 1000 || getDistanceMinusHalfExtents(actor, target) <= triggerDist))
+                    {
+                        const ESM::Pathgrid* pathgrid =
+                                MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*storage.mCell->getCell());
+
+                        bool runFallback = true;
+
+                        if (pathgrid && !actor.getClass().isPureWaterCreature(actor))
+                        {
+                            ESM::Pathgrid::PointList points;
+                            CoordinateConverter coords(storage.mCell->getCell());
+
+                            osg::Vec3f localPos = actor.getRefData().getPosition().asVec3();
+                            coords.toLocal(localPos);
+
+                            int closestPointIndex = PathFinder::GetClosestPoint(pathgrid, localPos);
+                            for (int i = 0; i < static_cast<int>(pathgrid->mPoints.size()); i++)
+                            {
+                                if (i != closestPointIndex && storage.mCell->isPointConnected(closestPointIndex, i))
+                                {
+                                    points.push_back(pathgrid->mPoints[static_cast<size_t>(i)]);
+                                }
+                            }
+
+                            if (!points.empty())
+                            {
+                                ESM::Pathgrid::Point dest = points[Misc::Rng::rollDice(points.size())];
+                                coords.toWorld(dest);
+
+                                state = AiCombatStorage::FleeState_RunToDestination;
+                                storage.mFleeDest = ESM::Pathgrid::Point(dest.mX, dest.mY, dest.mZ);
+
+                                runFallback = false;
+                            }
+                        }
+
+                        if (runFallback)
+                        {
+                            state = AiCombatStorage::FleeState_RunBlindly;
+                            storage.mFleeBlindRunTimer = 0.0f;
+                        }
+                    }
+                }
+                break;
+
+            case AiCombatStorage::FleeState_RunBlindly:
+                {
+                    // timer to prevent twitchy movement that can be observed in vanilla MW
+                    if (storage.mFleeBlindRunTimer < BLIND_RUN_DURATION)
+                    {
+                        storage.mFleeBlindRunTimer += duration;
+
+                        storage.mMovement.mRotation[2] = osg::PI + getZAngleToDir(target.getRefData().getPosition().asVec3()-actor.getRefData().getPosition().asVec3());
+                        storage.mMovement.mPosition[1] = 1;
+                        updateActorsMovement(actor, duration, storage);
+                    }
+                    else
+                        state = AiCombatStorage::FleeState_Idle;
+                }
+                break;
+
+            case AiCombatStorage::FleeState_RunToDestination:
+                {
+                    static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fFleeDistance")->getFloat();
+
+                    float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length();
+                    if ((dist > fFleeDistance && !storage.mFleeLOS)
+                            || pathTo(actor, storage.mFleeDest, duration))
+                    {
+                        state = AiCombatStorage::FleeState_Idle;
+                    }
+                }
+                break;
+        };
+    }
+
     void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage)
     {
         // apply combat movement
@@ -446,6 +588,29 @@ namespace MWMechanics
         mReadyToAttack = false;
         mAttack = false;
     }
+
+    void AiCombatStorage::startFleeing()
+    {
+        stopFleeing();
+        mFleeState = FleeState_Idle;
+    }
+
+    void AiCombatStorage::stopFleeing()
+    {
+        mMovement.mPosition[0] = 0;
+        mMovement.mPosition[1] = 0;
+        mMovement.mPosition[2] = 0;
+        mFleeState = FleeState_None;
+        mFleeDest = ESM::Pathgrid::Point(0, 0, 0);
+        mFleeLOS = false;
+        mFleeUpdateLOSTimer = 0.0f;
+        mFleeUpdateLOSTimer = 0.0f;
+    }
+
+    bool AiCombatStorage::isFleeing()
+    {
+        return mFleeState != FleeState_None;
+    }
 }
 
 
diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp
index 4be2ac9da..3f2bde776 100644
--- a/apps/openmw/mwmechanics/aicombat.hpp
+++ b/apps/openmw/mwmechanics/aicombat.hpp
@@ -61,6 +61,8 @@ namespace MWMechanics
 
             void attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController);
 
+            void updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage);
+
             /// Transfer desired movement (from AiCombatStorage) to Actor
             void updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage);
             void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, 
diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp
index 094df1db3..437aae277 100644
--- a/apps/openmw/mwmechanics/aicombataction.cpp
+++ b/apps/openmw/mwmechanics/aicombataction.cpp
@@ -5,14 +5,17 @@
 
 #include "../mwbase/environment.hpp"
 #include "../mwbase/world.hpp"
+#include "../mwbase/mechanicsmanager.hpp"
 
 #include "../mwworld/class.hpp"
 #include "../mwworld/esmstore.hpp"
 #include "../mwworld/inventorystore.hpp"
 #include "../mwworld/actionequip.hpp"
+#include "../mwworld/cellstore.hpp"
 
 #include "npcstats.hpp"
 #include "spellcasting.hpp"
+#include "combat.hpp"
 
 namespace
 {
@@ -517,6 +520,7 @@ namespace MWMechanics
         Spells& spells = actor.getClass().getCreatureStats(actor).getSpells();
 
         float bestActionRating = 0.f;
+        float antiFleeRating = 0.f;
         // Default to hand-to-hand combat
         boost::shared_ptr<Action> bestAction (new ActionWeapon(MWWorld::Ptr()));
         if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
@@ -536,6 +540,7 @@ namespace MWMechanics
                 {
                     bestActionRating = rating;
                     bestAction.reset(new ActionPotion(*it));
+                    antiFleeRating = std::numeric_limits<float>::max();
                 }
             }
 
@@ -546,6 +551,7 @@ namespace MWMechanics
                 {
                     bestActionRating = rating;
                     bestAction.reset(new ActionEnchantedItem(it));
+                    antiFleeRating = std::numeric_limits<float>::max();
                 }
             }
 
@@ -593,6 +599,7 @@ namespace MWMechanics
 
                     bestActionRating = rating;
                     bestAction.reset(new ActionWeapon(*it, ammo));
+                    antiFleeRating = vanillaRateWeaponAndAmmo(*it, ammo, actor, enemy);
                 }
             }
         }
@@ -606,13 +613,308 @@ namespace MWMechanics
             {
                 bestActionRating = rating;
                 bestAction.reset(new ActionSpell(spell->mId));
+                antiFleeRating = vanillaRateSpell(spell, actor, enemy);
             }
         }
 
+        if (makeFleeDecision(actor, enemy, antiFleeRating))
+            bestAction.reset(new ActionFlee());
+
         if (bestAction.get())
             bestAction->prepare(actor);
 
         return bestAction;
     }
 
+
+    float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool minusZDist)
+    {
+        osg::Vec3f actor1Pos = actor1.getRefData().getPosition().asVec3();
+        osg::Vec3f actor2Pos = actor2.getRefData().getPosition().asVec3();
+
+        float dist = (actor1Pos - actor2Pos).length();
+
+        if (minusZDist)
+            dist -= std::abs(actor1Pos.z() - actor2Pos.z());
+
+        return (dist
+                - MWBase::Environment::get().getWorld()->getHalfExtents(actor1).y()
+                - MWBase::Environment::get().getWorld()->getHalfExtents(actor2).y());
+    }
+
+    float getMaxAttackDistance(const MWWorld::Ptr& actor)
+    {
+        const CreatureStats& stats = actor.getClass().getCreatureStats(actor);
+        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
+
+        std::string selectedSpellId = stats.getSpells().getSelectedSpell();
+        MWWorld::Ptr selectedEnchItem;
+
+        MWWorld::Ptr activeWeapon, activeAmmo;
+        if (actor.getClass().hasInventoryStore(actor))
+        {
+            MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor);
+
+            MWWorld::ContainerStoreIterator item = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
+            if (item != invStore.end() && item.getType() == MWWorld::ContainerStore::Type_Weapon)
+                activeWeapon = *item;
+
+            item = invStore.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
+            if (item != invStore.end() && item.getType() == MWWorld::ContainerStore::Type_Weapon)
+                activeAmmo = *item;
+
+            if (invStore.getSelectedEnchantItem() != invStore.end())
+                selectedEnchItem = *invStore.getSelectedEnchantItem();
+        }
+
+        float dist = 1.0f;
+        if (activeWeapon.isEmpty() && !selectedSpellId.empty() && !selectedEnchItem.isEmpty())
+        {
+            static const float fHandToHandReach = gmst.find("fHandToHandReach")->getFloat();
+            dist = fHandToHandReach;
+        }
+        else if (stats.getDrawState() == MWMechanics::DrawState_Spell)
+        {
+            dist = 1.0f;
+            if (!selectedSpellId.empty())
+            {
+                const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(selectedSpellId);
+                for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =
+                     spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)
+                {
+                    if (effectIt->mArea == ESM::RT_Target)
+                    {
+                        const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
+                        dist = effect->mData.mSpeed;
+                        break;
+                    }
+                }
+            }
+            else if (!selectedEnchItem.isEmpty())
+            {
+                std::string enchId = selectedEnchItem.getClass().getEnchantment(selectedEnchItem);
+                if (!enchId.empty())
+                {
+                    const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(enchId);
+                    for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =
+                         ench->mEffects.mList.begin(); effectIt != ench->mEffects.mList.end(); ++effectIt)
+                    {
+                        if (effectIt->mArea == ESM::RT_Target)
+                        {
+                            const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
+                            dist = effect->mData.mSpeed;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            static const float fTargetSpellMaxSpeed = gmst.find("fTargetSpellMaxSpeed")->getFloat();
+            dist *= std::max(1000.0f, fTargetSpellMaxSpeed);
+        }
+        else if (!activeWeapon.isEmpty())
+        {
+            const ESM::Weapon* esmWeap = activeWeapon.get<ESM::Weapon>()->mBase;
+            if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow)
+            {
+                static const float fTargetSpellMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat();
+                dist = fTargetSpellMaxSpeed;
+                if (!activeAmmo.isEmpty())
+                {
+                    const ESM::Weapon* esmAmmo = activeAmmo.get<ESM::Weapon>()->mBase;
+                    dist *= esmAmmo->mData.mSpeed;
+                }
+            }
+            else if (esmWeap->mData.mReach > 1)
+            {
+                dist = esmWeap->mData.mReach;
+            }
+        }
+
+        dist = (dist > 0.f) ? dist : 1.0f;
+
+        static const float fCombatDistance = gmst.find("fCombatDistance")->getFloat();
+        static const float fCombatDistanceWerewolfMod = gmst.find("fCombatDistanceWerewolfMod")->getFloat();
+
+        float combatDistance = fCombatDistance;
+        if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
+            combatDistance *= (fCombatDistanceWerewolfMod + 1.0f);
+
+        if (dist < combatDistance)
+            dist *= combatDistance;
+
+        return dist;
+    }
+
+    bool canFight(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
+    {
+        ESM::Position actorPos = actor.getRefData().getPosition();
+        ESM::Position enemyPos = enemy.getRefData().getPosition();
+
+        const CreatureStats& enemyStats = enemy.getClass().getCreatureStats(enemy);
+        if (enemyStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0
+                || enemyStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 0)
+        {
+            if (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(enemy, actor))
+                return false;
+        }
+
+        if (actor.getClass().isPureWaterCreature(actor))
+        {
+            if (!MWBase::Environment::get().getWorld()->isWading(enemy))
+                return false;
+        }
+
+        float atDist = getMaxAttackDistance(actor);
+        if (atDist > getDistanceMinusHalfExtents(actor, enemy)
+                && atDist > std::abs(actorPos.pos[2] - enemyPos.pos[2]))
+        {
+            if (MWBase::Environment::get().getWorld()->getLOS(actor, enemy))
+                return true;
+        }
+
+        if (actor.getClass().isPureFlyingCreature(actor) || actor.getClass().isPureLandCreature(actor))
+        {
+            if (MWBase::Environment::get().getWorld()->isSwimming(enemy))
+                return false;
+        }
+
+        if (actor.getClass().isBipedal(actor) || !actor.getClass().canFly(actor))
+        {
+            if (enemy.getClass().getCreatureStats(enemy).getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0)
+            {
+                float attackDistance = getMaxAttackDistance(actor);
+                if ((attackDistance + actorPos.pos[2]) < enemyPos.pos[2])
+                {
+                    if (enemy.getCell()->isExterior())
+                    {
+                        if (attackDistance < (enemyPos.pos[2] - MWBase::Environment::get().getWorld()->getTerrainHeightAt(enemyPos.asVec3())))
+                            return false;
+                    }
+                }
+            }
+        }
+
+        if (!actor.getClass().canWalk(actor) && !actor.getClass().isBipedal(actor))
+            return true;
+
+        if (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0)
+            return true;
+
+        if (MWBase::Environment::get().getWorld()->isSwimming(actor))
+            return true;
+
+        if (getDistanceMinusHalfExtents(actor, enemy, true) <= 0.0f)
+            return false;
+
+        return true;
+    }
+
+    float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
+    {
+        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
+
+        static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->getFloat();
+        static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->getFloat();
+
+        float mult = fAIMagicSpellMult;
+
+        for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =
+             spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)
+        {
+            if (effectIt->mArea == ESM::RT_Target)
+            {
+                if (!MWBase::Environment::get().getWorld()->isSwimming(enemy))
+                    mult = fAIRangeMagicSpellMult;
+                else
+                    mult = 0.0f;
+                break;
+            }
+        }
+
+        return MWMechanics::getSpellSuccessChance(spell, actor) * mult;
+    }
+
+    float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
+    {
+        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
+
+        static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->getFloat();
+        static const float fAIMeleeArmorMult = gmst.find("fAIMeleeArmorMult")->getFloat();
+        static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->getFloat();
+
+        if (weapon.isEmpty())
+            return 0.f;
+
+        float skillMult = actor.getClass().getSkill(actor, weapon.getClass().getEquipmentSkill(weapon)) * 0.01f;
+        float chopMult = fAIMeleeWeaponMult;
+        float bonusDamage = 0.f;
+
+        const ESM::Weapon* esmWeap = weapon.get<ESM::Weapon>()->mBase;
+
+        if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow)
+        {
+            if (!ammo.isEmpty() && !MWBase::Environment::get().getWorld()->isSwimming(enemy))
+            {
+                bonusDamage = ammo.get<ESM::Weapon>()->mBase->mData.mChop[1];
+                chopMult = fAIRangeMeleeWeaponMult;
+            }
+            else
+                chopMult = 0.f;
+        }
+
+        float chopRating = (esmWeap->mData.mChop[1] + bonusDamage) * skillMult * chopMult;
+        float slashRating = esmWeap->mData.mSlash[1] * skillMult * fAIMeleeWeaponMult;
+        float thrustRating = esmWeap->mData.mThrust[1] * skillMult * fAIMeleeWeaponMult;
+
+        return actor.getClass().getArmorRating(actor) * fAIMeleeArmorMult
+                    + std::max(std::max(chopRating, slashRating), thrustRating);
+    }
+
+    float vanillaRateFlee(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
+    {
+        const CreatureStats& stats = actor.getClass().getCreatureStats(actor);
+        const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
+
+        int flee = stats.getAiSetting(CreatureStats::AI_Flee).getModified();
+        if (flee >= 100)
+            return flee;
+
+        static const float fAIFleeHealthMult = gmst.find("fAIFleeHealthMult")->getFloat();
+        static const float fAIFleeFleeMult = gmst.find("fAIFleeFleeMult")->getFloat();
+
+        float healthPercentage = (stats.getHealth().getModified() == 0.0f)
+                                    ? 1.0f : stats.getHealth().getCurrent() / stats.getHealth().getModified();
+        float rating = (1.0f - healthPercentage) * fAIFleeHealthMult + flee * fAIFleeFleeMult;
+
+        static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->getInt();
+
+        if (enemy.getClass().isNpc() && enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack)
+        {
+            static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt();
+            rating = iWereWolfFleeMod;
+        }
+
+        if (rating != 0.0f)
+            rating += getFightDistanceBias(actor, enemy);
+
+        return rating;
+    }
+
+    bool makeFleeDecision(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, float antiFleeRating)
+    {
+        float fleeRating = vanillaRateFlee(actor, enemy);
+        if (fleeRating < 100.0f)
+            fleeRating = 0.0f;
+
+        if (fleeRating > antiFleeRating)
+            return true;
+
+        // Run away after summoning a creature if we have nothing to use but fists.
+        if (antiFleeRating == 0.0f && !actor.getClass().getCreatureStats(actor).getSummonedCreatureMap().empty())
+            return true;
+
+        return false;
+    }
+
 }
diff --git a/apps/openmw/mwmechanics/aicombataction.hpp b/apps/openmw/mwmechanics/aicombataction.hpp
index b36413587..0f1f7dd5b 100644
--- a/apps/openmw/mwmechanics/aicombataction.hpp
+++ b/apps/openmw/mwmechanics/aicombataction.hpp
@@ -20,6 +20,18 @@ namespace MWMechanics
         virtual float getActionCooldown() { return 0.f; }
         virtual const ESM::Weapon* getWeapon() const { return NULL; };
         virtual bool isAttackingOrSpell() const { return true; }
+        virtual bool isFleeing() const { return false; }
+    };
+
+    class ActionFlee : public Action
+    {
+    public:
+        ActionFlee() {}
+        virtual void prepare(const MWWorld::Ptr& actor) {}
+        virtual float getCombatRange (bool& isRanged) const { return 0.0f; }
+        virtual float getActionCooldown() { return 3.0f; }
+        virtual bool isAttackingOrSpell() const { return false; }
+        virtual bool isFleeing() const { return true; }
     };
 
     class ActionSpell : public Action
@@ -89,6 +101,15 @@ namespace MWMechanics
     float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
 
     boost::shared_ptr<Action> prepareNextAction (const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
+
+    float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, bool minusZDist=false);
+    float getMaxAttackDistance(const MWWorld::Ptr& actor);
+    bool canFight(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
+
+    float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
+    float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
+    float vanillaRateFlee(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
+    bool makeFleeDecision(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, float antiFleeRating);
 }
 
 #endif
diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp
index b454f8e3a..41d8b9595 100644
--- a/apps/openmw/mwmechanics/combat.cpp
+++ b/apps/openmw/mwmechanics/combat.cpp
@@ -431,4 +431,19 @@ namespace MWMechanics
 
         return true;
     }
+
+    float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2)
+    {
+        osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3());
+        osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3());
+
+        float d = (pos1 - pos2).length();
+
+        static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
+                    "iFightDistanceBase")->getInt();
+        static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
+                    "fFightDistanceMultiplier")->getFloat();
+
+        return (iFightDistanceBase - fFightDistanceMultiplier * d);
+    }
 }
diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp
index 7d0b3b78f..3f733763a 100644
--- a/apps/openmw/mwmechanics/combat.hpp
+++ b/apps/openmw/mwmechanics/combat.hpp
@@ -42,6 +42,7 @@ void applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon,
 /// e.g. If attacker is a fish, is victim in water? Or, if attacker can't swim, is victim on land?
 bool isEnvironmentCompatible(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);
 
+float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2);
 }
 
 #endif
diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
index b10127f74..9661a9b3a 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
@@ -25,25 +25,11 @@
 #include "autocalcspell.hpp"
 #include "npcstats.hpp"
 #include "actorutil.hpp"
+#include "combat.hpp"
 
 namespace
 {
 
-    float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2)
-    {
-        osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3());
-        osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3());
-
-        float d = (pos1 - pos2).length();
-
-        static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
-                    "iFightDistanceBase")->getInt();
-        static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
-                    "fFightDistanceMultiplier")->getFloat();
-
-        return (iFightDistanceBase - fFightDistanceMultiplier * d);
-    }
-
     float getFightDispositionBias(float disposition)
     {
         static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp
index d04252a2c..96a56fe39 100644
--- a/apps/openmw/mwworld/class.cpp
+++ b/apps/openmw/mwworld/class.cpp
@@ -393,7 +393,26 @@ namespace MWWorld
 
     bool Class::isPureWaterCreature(const MWWorld::Ptr& ptr) const
     {
-        return canSwim(ptr) && !canWalk(ptr);
+        return canSwim(ptr)
+                && !isBipedal(ptr)
+                && !canFly(ptr)
+                && !canWalk(ptr);
+    }
+
+    bool Class::isPureFlyingCreature(const Ptr& ptr) const
+    {
+        return canFly(ptr)
+                && !isBipedal(ptr)
+                && !canSwim(ptr)
+                && !canWalk(ptr);
+    }
+
+    bool Class::isPureLandCreature(const Ptr& ptr) const
+    {
+        return canWalk(ptr)
+                && !isBipedal(ptr)
+                && !canFly(ptr)
+                && !canSwim(ptr);
     }
 
     bool Class::isMobile(const MWWorld::Ptr& ptr) const
diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp
index 8d3f9b891..06deedc53 100644
--- a/apps/openmw/mwworld/class.hpp
+++ b/apps/openmw/mwworld/class.hpp
@@ -312,6 +312,8 @@ namespace MWWorld
             virtual bool canSwim(const MWWorld::ConstPtr& ptr) const;
             virtual bool canWalk(const MWWorld::ConstPtr& ptr) const;
             bool isPureWaterCreature(const MWWorld::Ptr& ptr) const;
+            bool isPureFlyingCreature(const MWWorld::Ptr& ptr) const;
+            bool isPureLandCreature(const MWWorld::Ptr& ptr) const;
             bool isMobile(const MWWorld::Ptr& ptr) const;
 
             virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp
index fce3d69db..028cc45c9 100644
--- a/apps/openmw/mwworld/worldimp.cpp
+++ b/apps/openmw/mwworld/worldimp.cpp
@@ -421,11 +421,16 @@ namespace MWWorld
         gmst["sBribeFail"] = ESM::Variant("Bribe Fail");
         gmst["fNPCHealthBarTime"] = ESM::Variant(5.f);
         gmst["fNPCHealthBarFade"] = ESM::Variant(1.f);
+        gmst["fFleeDistance"] = ESM::Variant(3000.f);
 
         // Werewolf (BM)
         gmst["fWereWolfRunMult"] = ESM::Variant(1.f);
         gmst["fWereWolfSilverWeaponDamageMult"] = ESM::Variant(1.f);
         gmst["iWerewolfFightMod"] = ESM::Variant(1);
+        gmst["iWereWolfFleeMod"] = ESM::Variant(100);
+        gmst["iWereWolfLevelToAttack"] = ESM::Variant(20);
+        gmst["iWereWolfBounty"] = ESM::Variant(10000);
+        gmst["fCombatDistanceWerewolfMod"] = ESM::Variant(0.3f);
 
         std::map<std::string, ESM::Variant> globals;
         // vanilla Morrowind does not define dayspassed.
@@ -1295,7 +1300,7 @@ namespace MWWorld
 
         float terrainHeight = -std::numeric_limits<float>::max();
         if (ptr.getCell()->isExterior())
-            terrainHeight = mRendering->getTerrainHeightAt(pos.asVec3());
+            terrainHeight = getTerrainHeightAt(pos.asVec3());
 
         if (pos.pos[2] < terrainHeight)
             pos.pos[2] = terrainHeight;
@@ -3121,6 +3126,19 @@ namespace MWWorld
         return MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail);
     }
 
+    float World::getTerrainHeightAt(const osg::Vec3f& worldPos) const
+    {
+        return mRendering->getTerrainHeightAt(worldPos);
+    }
+
+    osg::Vec3f World::getHalfExtents(const ConstPtr& actor, bool rendering) const
+    {
+        if (rendering)
+            return mPhysics->getRenderingHalfExtents(actor);
+        else
+            return mPhysics->getHalfExtents(actor);
+    }
+
     void World::spawnRandomCreature(const std::string &creatureList)
     {
         const ESM::CreatureLevList* list = getStore().get<ESM::CreatureLevList>().find(creatureList);
diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp
index a5e250452..a9d3f3397 100644
--- a/apps/openmw/mwworld/worldimp.hpp
+++ b/apps/openmw/mwworld/worldimp.hpp
@@ -661,6 +661,12 @@ namespace MWWorld
             virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target);
 
             virtual bool isPlayerInJail() const;
+
+            /// Return terrain height at \a worldPos position.
+            virtual float getTerrainHeightAt(const osg::Vec3f& worldPos) const;
+
+            /// Return physical or rendering half extents of the given actor.
+            virtual osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering=false) const;
     };
 }