forked from teamnwah/openmw-tes3coop
Merge pull request #983 from Allofich/telekinesis
Don't allow telekinesis on actors or teleport doors
This commit is contained in:
commit
6f376bd499
14 changed files with 87 additions and 31 deletions
|
@ -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
|
||||
|
|
|
@ -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<ESM::Activator> *ref = ptr.get<ESM::Activator>();
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -146,13 +146,20 @@ namespace MWClass
|
|||
|
||||
if (ptr.getCellRef().getTeleport())
|
||||
{
|
||||
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest(), true));
|
||||
|
||||
action->setSound(openSound);
|
||||
|
||||
if (actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getDistanceToFacedObject() > MWBase::Environment::get().getWorld()->getMaxActivationDistance())
|
||||
{
|
||||
// player activated teleport door with telekinesis
|
||||
boost::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction);
|
||||
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
|
||||
{
|
||||
// animated door
|
||||
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr));
|
||||
|
@ -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<ESM::Door> *ref = ptr.get<ESM::Door>();
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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("");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -280,6 +280,9 @@ namespace MWWorld
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -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,10 +1027,18 @@ 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;
|
||||
}
|
||||
|
||||
return facedObject;
|
||||
float World::getDistanceToFacedObject()
|
||||
{
|
||||
return mDistanceToFacedObject;
|
||||
}
|
||||
|
||||
osg::Matrixf World::getActorHeadTransform(const MWWorld::ConstPtr& actor) const
|
||||
|
@ -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
|
||||
|
|
|
@ -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<std::string>& 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.
|
||||
|
|
Loading…
Reference in a new issue