From 8d7de7d1ece744356fee88f7e8613089725fcd67 Mon Sep 17 00:00:00 2001 From: dteviot Date: Tue, 17 Feb 2015 22:14:25 +1300 Subject: [PATCH 1/2] Telekinesis allows safe opening of traps (Fixes #1916) When trap activated at beyond normal activation distance, assume telekinesis used and detonate trap at trapped object's location. Also some minor code refactoring of spellcasting. 1. Corrected parameter passed to explodeSpell(). 2. For loop now correctly does an early exit. 3. Removed duplicated tests. --- apps/openmw/mwbase/world.hpp | 4 +++- apps/openmw/mwmechanics/spellcasting.cpp | 16 ++++++------- apps/openmw/mwworld/actiontrap.cpp | 29 +++++++++++++++++++++--- apps/openmw/mwworld/worldimp.cpp | 4 ++-- apps/openmw/mwworld/worldimp.hpp | 2 +- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f58ef08095..f719754337 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -269,6 +269,8 @@ namespace MWBase virtual MWWorld::Ptr getFacedObject() = 0; ///< Return pointer to the object the player is looking at, if it is within activation range + virtual float getMaxActivationDistance() = 0; + /// Returns a pointer to the object the provided object would hit (if within the /// specified distance), and the point where the hit occurs. This will attempt to /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis. @@ -548,7 +550,7 @@ namespace MWBase virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos) = 0; virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, - const MWWorld::Ptr& caster, int rangeType, const std::string& id, const std::string& sourceName) = 0; + const MWWorld::Ptr& caster, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName) = 0; virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 49887e560f..4ce3fcaf5a 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -309,9 +309,11 @@ namespace MWMechanics for (std::vector::const_iterator iter (effects.mList.begin()); iter!=effects.mList.end(); ++iter) { - if (iter->mRange != range) - continue; - found = true; + if (iter->mRange == range) + { + found = true; + break; + } } if (!found) return; @@ -766,8 +768,7 @@ namespace MWMechanics if (!mTarget.isEmpty()) { - if (!mTarget.getClass().isActor() || !mTarget.getClass().getCreatureStats(mTarget).isDead()) - inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch); + inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch); } std::string projectileModel; @@ -851,10 +852,7 @@ namespace MWMechanics if (!mTarget.isEmpty()) { - if (!mTarget.getClass().isActor() || !mTarget.getClass().getCreatureStats(mTarget).isDead()) - { - inflict(mTarget, mCaster, spell->mEffects, ESM::RT_Touch); - } + inflict(mTarget, mCaster, spell->mEffects, ESM::RT_Touch); } diff --git a/apps/openmw/mwworld/actiontrap.cpp b/apps/openmw/mwworld/actiontrap.cpp index 1472afc087..d153b7e618 100644 --- a/apps/openmw/mwworld/actiontrap.cpp +++ b/apps/openmw/mwworld/actiontrap.cpp @@ -1,16 +1,39 @@ #include "actiontrap.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" namespace MWWorld { void ActionTrap::executeImp(const Ptr &actor) { - MWMechanics::CastSpell cast(mTrapSource, actor); - cast.mHitPosition = Ogre::Vector3(actor.getRefData().getPosition().pos); - cast.cast(mSpellId); + Ogre::Vector3 actorPosition(actor.getRefData().getPosition().pos); + Ogre::Vector3 trapPosition(mTrapSource.getRefData().getPosition().pos); + float activationDistance = MWBase::Environment::get().getWorld()->getMaxActivationDistance(); + // GUI calcs if object in activation distance include object and player geometry + const float fudgeFactor = 1.25f; + + // Hack: if actor is beyond activation range, then assume actor is using telekinesis + // to open door/container. + // Note, can't just detonate the trap at the trapped object's location and use the blast + // radius, because for most trap spells this is 1 foot, much less than the activation distance. + if (trapPosition.distance(actorPosition) < (activationDistance * fudgeFactor)) + { + // assume actor touched trap + MWMechanics::CastSpell cast(mTrapSource, actor); + cast.mHitPosition = actorPosition; + cast.cast(mSpellId); + } + else + { + // assume telekinesis used + MWMechanics::CastSpell cast(mTrapSource, mTrapSource); + cast.mHitPosition = trapPosition; + cast.cast(mSpellId); + } mTrapSource.getCellRef().setTrap(""); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8bcfcb4211..333f172b91 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3128,7 +3128,7 @@ namespace MWWorld mRendering->spawnEffect(model, textureOverride, worldPos); } - void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster, int rangeType, + void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName) { std::map > toApply; @@ -3187,7 +3187,7 @@ namespace MWWorld cast.mStack = false; ESM::EffectList effects; effects.mList = apply->second; - cast.inflict(apply->first, caster, effects, (ESM::RangeType)rangeType, false, true); + cast.inflict(apply->first, caster, effects, rangeType, false, true); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 9834015ac6..84da984f85 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -627,7 +627,7 @@ namespace MWWorld virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos); virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, - const MWWorld::Ptr& caster, int rangeType, const std::string& id, const std::string& sourceName); + const MWWorld::Ptr& caster, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName); virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor); From 6e2d6a028200df830b2ca90c95987f873e7c5199 Mon Sep 17 00:00:00 2001 From: dteviot Date: Tue, 17 Feb 2015 22:51:30 +1300 Subject: [PATCH 2/2] Minor correction, MWWorld::getMaxActivationDistance() is now public. --- apps/openmw/mwworld/worldimp.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 84da984f85..1636980cb6 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -114,7 +114,6 @@ namespace MWWorld void performUpdateSceneQueries (); void getFacedHandle(std::string& facedHandle, float maxDistance, bool ignorePlayer=true); - float getMaxActivationDistance (); float getNpcActivationDistance (); float getObjectActivationDistance (); @@ -361,6 +360,8 @@ namespace MWWorld virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos); ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. + virtual float getMaxActivationDistance(); + virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; ///< Convert cell numbers to position.