Merge pull request #983 from Allofich/telekinesis

Don't allow telekinesis on actors or teleport doors
This commit is contained in:
scrawl 2016-07-08 23:47:43 +02:00 committed by GitHub
commit 6f376bd499
14 changed files with 87 additions and 31 deletions

View file

@ -250,6 +250,8 @@ namespace MWBase
virtual MWWorld::Ptr getFacedObject() = 0; virtual MWWorld::Ptr getFacedObject() = 0;
///< Return pointer to the object the player is looking at, if it is within activation range ///< Return pointer to the object the player is looking at, if it is within activation range
virtual float getDistanceToFacedObject() = 0;
virtual float getMaxActivationDistance() = 0; virtual float getMaxActivationDistance() = 0;
/// Returns a pointer to the object the provided object would hit (if within the /// Returns a pointer to the object the provided object would hit (if within the

View file

@ -81,6 +81,10 @@ namespace MWClass
return (ref->mBase->mName != ""); 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 MWGui::ToolTipInfo Activator::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>(); const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();

View file

@ -24,6 +24,9 @@ namespace MWClass
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false) ///< @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; 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. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -79,4 +79,8 @@ namespace MWClass
weight += effects.get(MWMechanics::EffectKey(ESM::MagicEffect::Burden)).getMagnitude(); weight += effects.get(MWMechanics::EffectKey(ESM::MagicEffect::Burden)).getMagnitude();
return (weight < 0) ? 0.0f : weight; return (weight < 0) ? 0.0f : weight;
} }
bool Actor::allowTelekinesis(const MWWorld::ConstPtr &ptr) const {
return false;
}
} }

View file

@ -35,6 +35,9 @@ namespace MWClass
///< Returns total weight of objects inside this object (including modifications from magic ///< Returns total weight of objects inside this object (including modifications from magic
/// effects). Throws an exception, if the object can't hold other objects. /// 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 // not implemented
Actor(const Actor&); Actor(const Actor&);
Actor& operator= (const Actor&); Actor& operator= (const Actor&);

View file

@ -146,11 +146,18 @@ namespace MWClass
if (ptr.getCellRef().getTeleport()) if (ptr.getCellRef().getTeleport())
{ {
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest(), true)); if (actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getDistanceToFacedObject() > MWBase::Environment::get().getWorld()->getMaxActivationDistance())
{
action->setSound(openSound); // player activated teleport door with telekinesis
boost::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction);
return action; return action;
}
else
{
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest(), true));
action->setSound(openSound);
return action;
}
} }
else else
{ {
@ -213,6 +220,14 @@ namespace MWClass
return true; 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 std::string Door::getScript (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>(); const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();

View file

@ -45,6 +45,9 @@ namespace MWClass
virtual bool canLock(const MWWorld::ConstPtr &ptr) const; 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; virtual std::string getScript (const MWWorld::ConstPtr& ptr) const;
///< Return name of the script attached to ptr ///< Return name of the script attached to ptr

View file

@ -39,7 +39,7 @@ namespace MWMechanics
//Set the target desition from the actor //Set the target desition from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; 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; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);

View file

@ -661,6 +661,7 @@ namespace MWRender
{ {
RenderingManager::RayResult result; RenderingManager::RayResult result;
result.mHit = false; result.mHit = false;
result.mRatio = 0;
if (intersector->containsIntersections()) if (intersector->containsIntersections())
{ {
result.mHit = true; result.mHit = true;
@ -668,6 +669,7 @@ namespace MWRender
result.mHitPointWorld = intersection.getWorldIntersectPoint(); result.mHitPointWorld = intersection.getWorldIntersectPoint();
result.mHitNormalWorld = intersection.getWorldIntersectNormal(); result.mHitNormalWorld = intersection.getWorldIntersectNormal();
result.mRatio = intersection.ratio;
PtrHolder* ptrHolder = NULL; PtrHolder* ptrHolder = NULL;
for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it)

View file

@ -120,6 +120,7 @@ namespace MWRender
osg::Vec3f mHitNormalWorld; osg::Vec3f mHitNormalWorld;
osg::Vec3f mHitPointWorld; osg::Vec3f mHitPointWorld;
MWWorld::Ptr mHitObject; MWWorld::Ptr mHitObject;
float mRatio;
}; };
RayResult castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors=false); RayResult castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors=false);

View file

@ -1,40 +1,34 @@
#include "actiontrap.hpp" #include "actiontrap.hpp"
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
namespace MWWorld namespace MWWorld
{ {
void ActionTrap::executeImp(const Ptr &actor) void ActionTrap::executeImp(const Ptr &actor)
{ {
osg::Vec3f actorPosition(actor.getRefData().getPosition().asVec3()); osg::Vec3f actorPosition(actor.getRefData().getPosition().asVec3());
osg::Vec3f trapPosition(mTrapSource.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 // Note: can't just detonate the trap at the trapped object's location and use the blast
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. // 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); MWMechanics::CastSpell cast(mTrapSource, mTrapSource);
cast.mHitPosition = trapPosition; cast.mHitPosition = trapPosition;
cast.cast(mSpellId); 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(""); mTrapSource.getCellRef().setTrap("");
} }
} }

View file

@ -280,6 +280,9 @@ namespace MWWorld
virtual bool isGold(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) /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;

View file

@ -150,7 +150,7 @@ namespace MWWorld
mSky (true), mCells (mStore, mEsm), mSky (true), mCells (mStore, mEsm),
mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles),
mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript),
mStartCell (startCell), mTeleportEnabled(true), mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true),
mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0) mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0)
{ {
mPhysics = new MWPhysics::PhysicsSystem(resourceSystem, rootNode); mPhysics = new MWPhysics::PhysicsSystem(resourceSystem, rootNode);
@ -1027,12 +1027,20 @@ namespace MWWorld
float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus;
facedObject = getFacedObject(activationDistance); facedObject = getFacedObject(activationDistance, true);
}
if (!facedObject.isEmpty() && !facedObject.getClass().allowTelekinesis(facedObject)
&& mDistanceToFacedObject > getMaxActivationDistance())
return 0;
}
return facedObject; return facedObject;
} }
float World::getDistanceToFacedObject()
{
return mDistanceToFacedObject;
}
osg::Matrixf World::getActorHeadTransform(const MWWorld::ConstPtr& actor) const osg::Matrixf World::getActorHeadTransform(const MWWorld::ConstPtr& actor) const
{ {
const MWRender::Animation *anim = mRendering->getAnimation(actor); const MWRender::Animation *anim = mRendering->getAnimation(actor);
@ -1715,17 +1723,24 @@ namespace MWWorld
MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer) MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer)
{ {
maxDistance += mRendering->getCameraDistance(); maxDistance += mRendering->getCameraDistance();
MWWorld::Ptr facedObject;
MWRender::RenderingManager::RayResult rayToObject;
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{ {
float x, y; float x, y;
MWBase::Environment::get().getWindowManager()->getMousePosition(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 else
{ rayToObject = mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer);
return mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer).mHitObject;
} facedObject = rayToObject.mHitObject;
if (rayToObject.mHit)
mDistanceToFacedObject = rayToObject.mRatio * maxDistance;
else
mDistanceToFacedObject = -1;
return facedObject;
} }
bool World::isCellExterior() const bool World::isCellExterior() const

View file

@ -127,7 +127,9 @@ namespace MWWorld
void updateSoundListener(); void updateSoundListener();
void updateWindowManager (); void updateWindowManager ();
void updatePlayer(bool paused); void updatePlayer(bool paused);
MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true); MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true);
public: // FIXME public: // FIXME
void removeContainerScripts(const Ptr& reference); void removeContainerScripts(const Ptr& reference);
private: private:
@ -154,6 +156,9 @@ namespace MWWorld
const std::vector<std::string>& content, ContentLoader& contentLoader); const std::vector<std::string>& content, ContentLoader& contentLoader);
float mSwimHeightScale; float mSwimHeightScale;
float mDistanceToFacedObject;
bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const; bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const;
///< helper function for implementing isSwimming(), isSubmerged(), isWading() ///< helper function for implementing isSwimming(), isSubmerged(), isWading()
@ -343,6 +348,8 @@ namespace MWWorld
virtual MWWorld::Ptr getFacedObject(); virtual MWWorld::Ptr getFacedObject();
///< Return pointer to the object the player is looking at, if it is within activation range ///< 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 /// 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 /// specified distance), and the point where the hit occurs. This will attempt to
/// use the "Head" node as a basis. /// use the "Head" node as a basis.