diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 7376e6b51..82d5ddd5e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -250,6 +250,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 getDistanceToFacedObject() = 0; + virtual float getMaxActivationDistance() = 0; /// Returns a pointer to the object the provided object would hit (if within the diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 9785eef1e..b3747f916 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -81,6 +81,10 @@ namespace MWClass return (ref->mBase->mName != ""); } + bool Activator::allowTelekinesis(const MWWorld::ConstPtr &ptr) const { + return false; + } + MWGui::ToolTipInfo Activator::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 15dbf5767..e90620cea 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -24,6 +24,9 @@ namespace MWClass virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) + virtual bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const; + ///< Return whether this class of object can be activated with telekinesis + virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. diff --git a/apps/openmw/mwclass/actor.cpp b/apps/openmw/mwclass/actor.cpp index 4e89c7282..56de5e3f8 100644 --- a/apps/openmw/mwclass/actor.cpp +++ b/apps/openmw/mwclass/actor.cpp @@ -79,4 +79,8 @@ namespace MWClass weight += effects.get(MWMechanics::EffectKey(ESM::MagicEffect::Burden)).getMagnitude(); return (weight < 0) ? 0.0f : weight; } + + bool Actor::allowTelekinesis(const MWWorld::ConstPtr &ptr) const { + return false; + } } diff --git a/apps/openmw/mwclass/actor.hpp b/apps/openmw/mwclass/actor.hpp index 88a3f1a32..1aca5e660 100644 --- a/apps/openmw/mwclass/actor.hpp +++ b/apps/openmw/mwclass/actor.hpp @@ -35,6 +35,9 @@ namespace MWClass ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. + virtual bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const; + ///< Return whether this class of object can be activated with telekinesis + // not implemented Actor(const Actor&); Actor& operator= (const Actor&); diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 8d54dff5d..d8a9efba5 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -146,11 +146,18 @@ namespace MWClass if (ptr.getCellRef().getTeleport()) { - boost::shared_ptr action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest(), true)); - - action->setSound(openSound); - - return action; + if (actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getDistanceToFacedObject() > MWBase::Environment::get().getWorld()->getMaxActivationDistance()) + { + // player activated teleport door with telekinesis + boost::shared_ptr action(new MWWorld::FailedAction); + return action; + } + else + { + boost::shared_ptr action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest(), true)); + action->setSound(openSound); + return action; + } } else { @@ -213,6 +220,14 @@ namespace MWClass return true; } + bool Door::allowTelekinesis(const MWWorld::ConstPtr &ptr) const + { + if (ptr.getCellRef().getTeleport() && ptr.getCellRef().getLockLevel() <= 0 && ptr.getCellRef().getTrap().empty()) + return false; + else + return true; + } + std::string Door::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 42aa6d64d..906b18511 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -45,6 +45,9 @@ namespace MWClass virtual bool canLock(const MWWorld::ConstPtr &ptr) const; + virtual bool allowTelekinesis(const MWWorld::ConstPtr &ptr) const; + ///< Return whether this class of object can be activated with telekinesis + virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 3c5504228..a79adbc8b 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -39,7 +39,7 @@ namespace MWMechanics //Set the target desition from the actor ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; - if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 200) { //Stop when you get close + if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < MWBase::Environment::get().getWorld()->getMaxActivationDistance()) { //Stop when you get in activation range actor.getClass().getMovementSettings(actor).mPosition[1] = 0; MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 33384a521..ab76b86f1 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -661,6 +661,7 @@ namespace MWRender { RenderingManager::RayResult result; result.mHit = false; + result.mRatio = 0; if (intersector->containsIntersections()) { result.mHit = true; @@ -668,6 +669,7 @@ namespace MWRender result.mHitPointWorld = intersection.getWorldIntersectPoint(); result.mHitNormalWorld = intersection.getWorldIntersectNormal(); + result.mRatio = intersection.ratio; PtrHolder* ptrHolder = NULL; for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 306ae9676..f5245be98 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -120,6 +120,7 @@ namespace MWRender osg::Vec3f mHitNormalWorld; osg::Vec3f mHitPointWorld; MWWorld::Ptr mHitObject; + float mRatio; }; RayResult castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors=false); diff --git a/apps/openmw/mwworld/actiontrap.cpp b/apps/openmw/mwworld/actiontrap.cpp index 68d7c69e9..cdabaf8c8 100644 --- a/apps/openmw/mwworld/actiontrap.cpp +++ b/apps/openmw/mwworld/actiontrap.cpp @@ -1,40 +1,34 @@ #include "actiontrap.hpp" #include "../mwmechanics/spellcasting.hpp" + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" namespace MWWorld { - void ActionTrap::executeImp(const Ptr &actor) { osg::Vec3f actorPosition(actor.getRefData().getPosition().asVec3()); osg::Vec3f trapPosition(mTrapSource.getRefData().getPosition().asVec3()); - float activationDistance = MWBase::Environment::get().getWorld()->getMaxActivationDistance(); + float trapRange = 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 + // 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 - actorPosition).length() < (activationDistance * fudgeFactor)) + // Using activation distance as the trap range. + + if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr() && MWBase::Environment::get().getWorld()->getDistanceToFacedObject() > trapRange) // player activated object outside range of trap { - // 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); } + else // player activated object within range of trap, or NPC activated trap + { + MWMechanics::CastSpell cast(mTrapSource, actor); + cast.mHitPosition = actorPosition; + cast.cast(mSpellId); + } mTrapSource.getCellRef().setTrap(""); } - } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 0bb3edbee..23128ea9d 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -279,6 +279,9 @@ namespace MWWorld virtual bool isKey (const MWWorld::ConstPtr& ptr) const { return false; } virtual bool isGold(const MWWorld::ConstPtr& ptr) const { return false; } + + virtual bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const { return true; } + ///< Return whether this class of object can be activated with telekinesis /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1706f7681..cb5756c93 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -150,7 +150,7 @@ namespace MWWorld mSky (true), mCells (mStore, mEsm), mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), - mStartCell (startCell), mTeleportEnabled(true), + mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0) { mPhysics = new MWPhysics::PhysicsSystem(resourceSystem, rootNode); @@ -1027,12 +1027,20 @@ namespace MWWorld float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; - facedObject = getFacedObject(activationDistance); - } + facedObject = getFacedObject(activationDistance, true); + if (!facedObject.isEmpty() && !facedObject.getClass().allowTelekinesis(facedObject) + && mDistanceToFacedObject > getMaxActivationDistance()) + return 0; + } return facedObject; } + float World::getDistanceToFacedObject() + { + return mDistanceToFacedObject; + } + osg::Matrixf World::getActorHeadTransform(const MWWorld::ConstPtr& actor) const { const MWRender::Animation *anim = mRendering->getAnimation(actor); @@ -1715,17 +1723,24 @@ namespace MWWorld MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer) { maxDistance += mRendering->getCameraDistance(); + MWWorld::Ptr facedObject; + MWRender::RenderingManager::RayResult rayToObject; if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - return mRendering->castCameraToViewportRay(x, y, maxDistance, ignorePlayer).mHitObject; + rayToObject = mRendering->castCameraToViewportRay(x, y, maxDistance, ignorePlayer); } else - { - return mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer).mHitObject; - } + rayToObject = mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer); + + facedObject = rayToObject.mHitObject; + if (rayToObject.mHit) + mDistanceToFacedObject = rayToObject.mRatio * maxDistance; + else + mDistanceToFacedObject = -1; + return facedObject; } bool World::isCellExterior() const diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7af632d23..aa67c9ea8 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -127,7 +127,9 @@ namespace MWWorld void updateSoundListener(); void updateWindowManager (); void updatePlayer(bool paused); + MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true); + public: // FIXME void removeContainerScripts(const Ptr& reference); private: @@ -154,6 +156,9 @@ namespace MWWorld const std::vector& content, ContentLoader& contentLoader); float mSwimHeightScale; + + float mDistanceToFacedObject; + bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const; ///< helper function for implementing isSwimming(), isSubmerged(), isWading() @@ -343,6 +348,8 @@ namespace MWWorld virtual MWWorld::Ptr getFacedObject(); ///< Return pointer to the object the player is looking at, if it is within activation range + virtual float getDistanceToFacedObject(); + /// 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 as a basis.