diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 82d5ddd5e..549931b9a 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -385,6 +385,7 @@ namespace MWBase ///Is the head of the creature underwater? virtual bool isSubmerged(const MWWorld::ConstPtr &object) const = 0; virtual bool isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const = 0; + virtual bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const = 0; virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 61365be97..cdf8a2a67 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -259,7 +259,7 @@ namespace MWMechanics } /// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure. - bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, bool castByPlayer) + bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer) { switch (effectId) { @@ -291,8 +291,20 @@ namespace MWMechanics return false; } break; - } + case ESM::MagicEffect::WaterWalking: + if (target.getClass().isPureWaterCreature(target) && MWBase::Environment::get().getWorld()->isSwimming(target)) + return false; + MWBase::World *world = MWBase::Environment::get().getWorld(); + + if (!world->isWaterWalkingCastableOnTarget(target)) + { + if (castByPlayer && caster == target) + MWBase::Environment::get().getWindowManager()->messageBox ("#{sMagicInvalidEffect}"); + return false; + } + break; + } return true; } @@ -384,7 +396,7 @@ namespace MWMechanics else canCastAnEffect = true; - if (!checkEffectTarget(effectIt->mEffectID, target, castByPlayer)) + if (!checkEffectTarget(effectIt->mEffectID, target, caster, castByPlayer)) continue; // caster needs to be an actor for linked effects (e.g. Absorb) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b178e6e52..5652b2d78 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2077,6 +2077,19 @@ namespace MWWorld return pos.z() < cell->getWaterLevel(); } + bool World::isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const + { + const MWWorld::CellStore* cell = target.getCell(); + if (!cell->getCell()->hasWater()) + return true; + + // Based on observations from the original engine, the depth + // limit at which water walking can still be cast on a target + // in water appears to be the same as what the highest swimmable + // z position would be with SwimHeightScale + 1. + return !isUnderwater(target, mSwimHeightScale + 1); + } + bool World::isOnGround(const MWWorld::Ptr &ptr) const { return mPhysics->isOnGround(ptr); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index aa67c9ea8..da665e27c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -481,6 +481,7 @@ namespace MWWorld virtual bool isSwimming(const MWWorld::ConstPtr &object) const; virtual bool isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const; virtual bool isWading(const MWWorld::ConstPtr &object) const; + virtual bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const; virtual bool isOnGround(const MWWorld::Ptr &ptr) const; virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const;