diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index aaed6712e..5c5fc3899 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -223,7 +223,7 @@ namespace MWMechanics return 1 - resistance / 100.f; } - /// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure. + /// Check if the given effect can be applied to the target. If \a castByPlayer, emits a message box on failure. bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer) { switch (effectId) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 85277401f..852ae79dc 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -59,6 +59,8 @@ namespace MWMechanics float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); + bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer); + int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor); void effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude); diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 21867591c..cc3ba40e1 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -985,6 +985,18 @@ namespace MWPhysics } } + bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel) + { + const Actor* physicActor = getActor(actor); + const float halfZ = physicActor->getHalfExtents().z(); + const osg::Vec3f actorPosition = physicActor->getPosition(); + const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ); + const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ); + ActorTracer tracer; + tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld); + return (tracer.mFraction >= 1.0f); + } + osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); @@ -1326,25 +1338,10 @@ namespace MWPhysics { if (!world->isUnderwater(iter->first.getCell(), osg::Vec3f(iter->first.getRefData().getPosition().asVec3()))) waterCollision = true; - else if (physicActor->getCollisionMode()) + else if (physicActor->getCollisionMode() && canMoveToWaterSurface(iter->first, waterlevel)) { - const float halfZ = physicActor->getHalfExtents().z(); const osg::Vec3f actorPosition = physicActor->getPosition(); - const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ); - const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ); - ActorTracer tracer; - tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld); - if (tracer.mFraction >= 1.0f) - { - waterCollision = true; - physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel)); - } - else - { - //Remove the effect to remove the performance hit of casting in a weird spot - //probably makes that Tribunal quest where the water rises a bit safer - iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().purgeEffect(ESM::MagicEffect::WaterWalking); - } + physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel)); } } physicActor->setCanWaterWalk(waterCollision); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index c12672285..215355316 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -123,6 +123,8 @@ namespace MWPhysics bool isOnGround (const MWWorld::Ptr& actor); + bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel); + /// Get physical half extents (scaled) of the given actor. osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor) const; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 98cbad742..9f8bae280 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -515,8 +515,8 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) MWBase::Environment::get().getWorld()->getStore().get().find ( effectIt->mEffectID); - // Fully resisted? - if (params[i].mMultiplier == 0) + // Fully resisted or can't be applied to target? + if (params[i].mMultiplier == 0 || !MWMechanics::checkEffectTarget(effectIt->mEffectID, actor, actor, actor == MWMechanics::getPlayer())) continue; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; @@ -770,6 +770,9 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito for (std::vector::const_iterator effectIt (enchantment.mEffects.mList.begin()); effectIt!=enchantment.mEffects.mList.end(); ++effectIt) { + // Don't get spell icon display information for enchantments that weren't actually applied + if (mMagicEffects.get(MWMechanics::EffectKey(*effectIt)).getMagnitude() == 0) + continue; const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i]; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; magnitude *= params.mMultiplier; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cead4fc92..5f652650f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2084,11 +2084,16 @@ namespace MWWorld 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); + float waterlevel = cell->getWaterLevel(); + + // SwimHeightScale affects the upper z position an actor can swim to + // while in water. Based on observation from the original engine, + // the upper z position you get with a +1 SwimHeightScale is the depth + // limit for being able to cast water walking on an underwater target. + if (isUnderwater(target, mSwimHeightScale + 1) || (isUnderwater(cell, target.getRefData().getPosition().asVec3()) && !mPhysics->canMoveToWaterSurface(target, waterlevel))) + return false; // not castable if too deep or if not enough room to move actor to surface + else + return true; } bool World::isOnGround(const MWWorld::Ptr &ptr) const