1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-04-01 13:06:43 +00:00

Drop items at location

This commit is contained in:
Mads Buvik Sandvei 2020-03-10 22:39:11 +01:00
parent 08c08646cd
commit 31f5b76394
11 changed files with 286 additions and 113 deletions

View file

@ -53,7 +53,7 @@ namespace MWWorld
namespace MWGui
{
class Layout;
class DragAndDrop;
class Console;
class SpellWindow;
class TradeWindow;
@ -226,6 +226,7 @@ namespace MWBase
virtual void showCrosshair(bool show) = 0;
virtual bool getSubtitlesEnabled() = 0;
virtual bool toggleHud() = 0;
virtual MWGui::DragAndDrop& getDragAndDrop(void) = 0;
virtual void disallowMouse() = 0;
virtual void allowMouse() = 0;

View file

@ -54,6 +54,7 @@ namespace MWRender
{
class Animation;
class RenderingManager;
struct RayResult;
}
namespace MWMechanics
@ -98,12 +99,6 @@ namespace MWBase
ESM::CellId dest;
};
using IntersectedObject = std::tuple<
MWWorld::Ptr,
osg::Node*,
osg::Vec3f
>;
World() {}
virtual ~World() {}
@ -268,11 +263,7 @@ namespace MWBase
virtual MWWorld::Ptr getFacedObject() = 0;
///< Return pointer to the object the player is looking at, if it is within activation range
virtual IntersectedObject getPointedAtObject() = 0;
///< Return pointer to the object and/or node the player is currently pointing at
virtual float getDistanceToFacedObject() = 0;
virtual float getDistanceToPointedAtObject() = 0;
virtual float getMaxActivationDistance() = 0;
@ -638,6 +629,27 @@ namespace MWBase
virtual osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const = 0;
virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;
#ifdef USE_OPENXR
using IntersectedObject = std::tuple<
MWWorld::Ptr,
osg::Node*,
osg::Vec3f
>;
virtual float getDistanceToPointedAtObject() = 0;
///< Return the distance to the object and/or node the player is currently pointing at
virtual void getPointedAtObject(MWRender::RayResult& result) = 0;
///< Return pointer to the object and/or node the player is currently pointing at
virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& object, int amount) = 0;
///< copy and place an object into the gameworld where the player is currently pointing
/// @param object
/// @param number of objects to place
virtual bool canPlaceObject() = 0;
///< @return true if it is possible to place on object where the player is currently pointing
#endif
};
}

View file

@ -270,6 +270,7 @@ namespace MWGui
}
}
// XR-TODO: Implement equivalent
void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y)
{
if (mDragAndDrop->mIsOnDragAndDrop)

View file

@ -113,6 +113,11 @@
#include "keyboardnavigation.hpp"
#include "resourceskin.hpp"
#ifdef USE_OPENXR
#include "../mwvr/openxrenvironment.hpp"
#include "../mwvr/openxrmenu.hpp"
#endif
namespace
{
@ -175,6 +180,7 @@ namespace MWGui
, mHudEnabled(true)
, mCursorVisible(true)
, mCursorActive(false)
, mVideoEnabled(false)
, mPlayerBounty(-1)
, mPlayerName()
, mPlayerRaceId()
@ -872,6 +878,8 @@ namespace MWGui
}
}
mVideoEnabled = false;
popGuiMode();
}
@ -1532,6 +1540,11 @@ namespace MWGui
updateVisible();
}
DragAndDrop& WindowManager::getDragAndDrop(void)
{
return *mDragAndDrop;
}
void WindowManager::forceHide(GuiWindow wnd)
{
mForceHidden = (GuiWindow)(mForceHidden | wnd);
@ -1549,7 +1562,8 @@ namespace MWGui
return
!mGuiModes.empty() ||
isConsoleMode() ||
(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox());
(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox()) ||
mVideoEnabled;
}
bool WindowManager::isConsoleMode() const
@ -1870,8 +1884,15 @@ namespace MWGui
void WindowManager::playVideo(const std::string &name, bool allowSkipping)
{
mVideoEnabled = true;
mVideoWidget->playVideo("video\\" + name);
#ifdef USE_OPENXR
// Temporary hack to force update of menu placement
// (Menu gets recreated next tick)
auto* xrMenuManager = MWVR::OpenXREnvironment::get().getMenuManager();
#endif
mVideoWidget->eventKeyButtonPressed.clear();
mVideoBackground->eventKeyButtonPressed.clear();
if (allowSkipping)
@ -1902,12 +1923,22 @@ namespace MWGui
~MWSound::Type::Movie & MWSound::Type::Mask
);
osg::Timer frameTimer;
while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
while (mVideoEnabled && mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
{
double dt = frameTimer.time_s();
frameTimer.setStartTick();
MWBase::Environment::get().getInputManager()->update(dt, true, false);
#ifdef USE_OPENXR
// Temporary hack to force update of menu placement
// (Menu gets recreated next tick)
if (xrMenuManager)
{
xrMenuManager->showMenus(false);
xrMenuManager = nullptr;
}
#endif
if (!MWBase::Environment::get().getInputManager()->isWindowVisible())
OpenThreads::Thread::microSleep(5000);
@ -1937,6 +1968,7 @@ namespace MWGui
mViewer->getCamera()->setCullMask(oldCullMask);
mVideoBackground->setVisible(false);
mVideoEnabled = false;
}
void WindowManager::sizeVideo(int screenWidth, int screenHeight)

View file

@ -168,6 +168,8 @@ namespace MWGui
virtual void toggleVisible(GuiWindow wnd);
virtual DragAndDrop& getDragAndDrop(void) override;
virtual void forceHide(MWGui::GuiWindow wnd);
virtual void unsetForceHide(MWGui::GuiWindow wnd);
@ -461,6 +463,7 @@ namespace MWGui
bool mHudEnabled;
bool mCursorVisible;
bool mCursorActive;
bool mVideoEnabled;
int mPlayerBounty;

View file

@ -1015,9 +1015,9 @@ namespace MWRender
return osg::Vec4f(min_x, min_y, max_x, max_y);
}
RenderingManager::RayResult getIntersectionResult (osgUtil::LineSegmentIntersector* intersector)
RayResult getIntersectionResult (osgUtil::LineSegmentIntersector* intersector)
{
RenderingManager::RayResult result;
RayResult result;
result.mHit = false;
result.mRatio = 0;
result.mHitNode = nullptr;
@ -1074,7 +1074,7 @@ namespace MWRender
return mIntersectionVisitor;
}
RenderingManager::RayResult RenderingManager::castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors)
RayResult RenderingManager::castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors)
{
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::MODEL,
origin, dest));
@ -1085,7 +1085,7 @@ namespace MWRender
return getIntersectionResult(intersector);
}
RenderingManager::RayResult RenderingManager::castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors)
RayResult RenderingManager::castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors)
{
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::PROJECTION,
nX * 2.f - 1.f, nY * (-2.f) + 1.f));

View file

@ -83,6 +83,19 @@ namespace MWRender
class NavMesh;
class ActorsPaths;
// Result data of ray cast methods.
// Needs to be declared outside the RenderingManager class to be forward declarable
struct RayResult
{
bool mHit;
osg::Vec3f mHitNormalWorld;
osg::Vec3f mHitPointWorld;
osg::Vec3f mHitPointLocal;
MWWorld::Ptr mHitObject;
osg::Node* mHitNode;
float mRatio;
};
class RenderingManager : public MWRender::RenderingInterface
{
public:
@ -146,17 +159,6 @@ namespace MWRender
void screenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd());
bool screenshot360(osg::Image* image, std::string settingStr);
struct RayResult
{
bool mHit;
osg::Vec3f mHitNormalWorld;
osg::Vec3f mHitPointWorld;
osg::Vec3f mHitPointLocal;
MWWorld::Ptr mHitObject;
osg::Node* mHitNode;
float mRatio;
};
RayResult castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors=false);
/// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen coordinates,

View file

@ -189,7 +189,8 @@ void FingerController::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
// TODO: Using the cached value from the input manager makes this off by one frame
// So do one otherwise redundant intersection here.
world->getPointedAtObject();
MWRender::RayResult result;
world->getPointedAtObject(result);
float intersected_distance = world->getDistanceToPointedAtObject();
// Stretch beam to point of intersection.

View file

@ -26,6 +26,9 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwgui/itemmodel.hpp"
#include "../mwgui/draganddrop.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
@ -764,14 +767,51 @@ void OpenXRInputManager::updateActivationIndication(void)
playerAnimation->setPointForward(show);
}
/**
* Makes it possible to use ItemModel::moveItem to move an item from an inventory to the world.
*/
class DropItemAtPointModel: public MWGui::ItemModel
{
public:
DropItemAtPointModel(){}
virtual ~DropItemAtPointModel() {}
virtual MWWorld::Ptr copyItem(const MWGui::ItemStack& item, size_t count, bool /*allowAutoEquip*/)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
MWWorld::Ptr dropped;
if (world->canPlaceObject())
dropped = world->placeObject(item.mBase, count);
else
dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count);
dropped.getCellRef().setOwner("");
return dropped;
}
virtual void removeItem(const MWGui::ItemStack& item, size_t count) { throw std::runtime_error("removeItem not implemented"); }
virtual ModelIndex getIndex(MWGui::ItemStack item) { throw std::runtime_error("getIndex not implemented"); }
virtual void update() {}
virtual size_t getItemCount() { return 0; }
virtual MWGui::ItemStack getItem(ModelIndex index) { throw std::runtime_error("getItem not implemented"); }
private:
// Where to drop the item
MWRender::RayResult mIntersection;
};
void OpenXRInputManager::pointActivation(bool onPress)
{
auto* world = MWBase::Environment::get().getWorld();
if (world)
{
auto pointedAt = world->getPointedAtObject();
auto* node = std::get<1>(pointedAt);
MWWorld::Ptr ptr = std::get<0>(pointedAt);
MWRender::RayResult pointedAt;
world->getPointedAtObject(pointedAt);
auto* node = pointedAt.mHitNode;
MWWorld::Ptr ptr = pointedAt.mHitObject;
auto& dnd = MWBase::Environment::get().getWindowManager()->getDragAndDrop();
if (node && node->getName() == "XR Menu Geometry")
{
// Interseceted with the menu
@ -782,8 +822,21 @@ void OpenXRInputManager::updateActivationIndication(void)
else
mouseReleased(arg, SDL_BUTTON_LEFT);
}
if (!ptr.isEmpty())
else if (onPress)
{
// Other actions should only happen on release;
return;
}
else if (dnd.mIsOnDragAndDrop)
{
// Intersected with the world while drag and drop is active
// Drop item into the world
MWBase::Environment::get().getWorld()->breakInvisibility(
MWMechanics::getPlayer());
DropItemAtPointModel drop;
dnd.drop(&drop, nullptr);
}
else if (!ptr.isEmpty())
{
if (mPlayer)
{
@ -840,18 +893,18 @@ void OpenXRInputManager::updateActivationIndication(void)
{
mXRInput->updateControls();
auto* world = MWBase::Environment::get().getWorld();
if (world)
{
auto pointedAt = world->getPointedAtObject();
auto* node = std::get<1>(pointedAt);
MWRender::RayResult pointedAt;
world->getPointedAtObject(pointedAt);
auto* node = pointedAt.mHitNode;
if (node)
{
int w, h;
SDL_GetWindowSize(mWindow, &w, &h);
osg::Vec3 local = std::get<2>(pointedAt);
osg::Vec3 local = pointedAt.mHitPointLocal;
local.x() = (local.x() + 1.f) / 2.f;
local.y() = 1.f - (local.y() + 1.f) / 2.f;
@ -1027,7 +1080,7 @@ void OpenXRInputManager::updateActivationIndication(void)
mAttemptJump = true;
break;
case A_Use:
if (mActivationIndication | MWBase::Environment::get().getWindowManager()->isGuiMode())
if (mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode())
pointActivation(event.onPress);
else
mInputBinder->getChannel(A_Use)->setValue(event.onPress);

View file

@ -1113,34 +1113,11 @@ namespace MWWorld
return facedObject;
}
World::IntersectedObject World::getPointedAtObject()
{
if (MWBase::Environment::get().getWindowManager()->isGuiMode() &&
MWBase::Environment::get().getWindowManager()->isConsoleMode())
{
return getPointedAtObject(getMaxActivationDistance() * 50, false);
}
else
{
auto pointedAtObject = getPointedAtObject(getActivationDistancePlusTelekinesis(), true);
auto ptr = std::get<0>(pointedAtObject);
if (!ptr.isEmpty() && !ptr.getClass().allowTelekinesis(ptr)
&& mDistanceToFacedObject > getMaxActivationDistance() && !MWBase::Environment::get().getWindowManager()->isGuiMode())
return IntersectedObject();
return pointedAtObject;
}
}
float World::getDistanceToFacedObject()
{
return mDistanceToFacedObject;
}
float World::getDistanceToPointedAtObject()
{
return mDistanceToPointedAtObject;
}
osg::Matrixf World::getActorHeadTransform(const MWWorld::ConstPtr& actor) const
{
const MWRender::Animation *anim = mRendering->getAnimation(actor);
@ -2051,7 +2028,7 @@ namespace MWWorld
const float camDist = mRendering->getCameraDistance();
maxDistance += camDist;
MWWorld::Ptr facedObject;
MWRender::RenderingManager::RayResult rayToObject;
MWRender::RayResult rayToObject;
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
@ -2070,45 +2047,6 @@ namespace MWWorld
return facedObject;
}
World::IntersectedObject World::getPointedAtObject(float maxDistance, bool ignorePlayer)
{
auto sceneRoot = mRendering->getLightRoot();
// Find the transform giving the finger's pointing direction.
SceneUtil::FindByNameVisitor findPointerVisitor("Pointer Transform", osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
sceneRoot->accept(findPointerVisitor);
auto pointerTransform = findPointerVisitor.mFoundNode;
if (pointerTransform)
{
auto pat = pointerTransform->asTransform()->asPositionAttitudeTransform();
// Discover the world position of the finger
// (This is actually the base of the last joint bone but so long as it is pointing forward it serves the same purpose).
osg::Matrix worldMatrix = osg::computeLocalToWorld(pointerTransform->getParentalNodePaths()[0]);
pat->computeLocalToWorldMatrix(worldMatrix, nullptr);
osg::Vec3 translate;
osg::Quat rotation;
osg::Vec3 scale;
osg::Quat scaleRotation;
worldMatrix.decompose(translate, rotation, scale, scaleRotation);
osg::Vec3f direction = rotation * osg::Vec3f(-1, 0, 0);
direction.normalize();
osg::Vec3f raySource = translate;
osg::Vec3f rayTarget = translate + direction * maxDistance;
auto rayToObject = mRendering->castRay(raySource, rayTarget, ignorePlayer, false);
if (rayToObject.mHit)
mDistanceToPointedAtObject = (rayToObject.mHitPointWorld - raySource).length();
else
// Leave a very large but not too large number to permit visualizing a beam going into "infinity"
mDistanceToPointedAtObject = 10000.f;
return { rayToObject.mHitObject, rayToObject.mHitNode, rayToObject.mHitPointLocal };
}
return IntersectedObject();
}
bool World::isCellExterior() const
{
const CellStore *currentCell = mWorldScene->getCurrentCell();
@ -2255,7 +2193,7 @@ namespace MWWorld
{
const float maxDist = 200.f;
MWRender::RenderingManager::RayResult result = mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true);
MWRender::RayResult result = mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true);
CellStore* cell = getPlayerPtr().getCell();
@ -2282,7 +2220,7 @@ namespace MWWorld
bool World::canPlaceObject(float cursorX, float cursorY)
{
const float maxDist = 200.f;
MWRender::RenderingManager::RayResult result = mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true);
MWRender::RayResult result = mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true);
if (result.mHit)
{
@ -2366,7 +2304,7 @@ namespace MWWorld
float len = 1000000.0;
MWRender::RenderingManager::RayResult result = mRendering->castRay(orig, orig+dir*len, true, true);
MWRender::RayResult result = mRendering->castRay(orig, orig+dir*len, true, true);
if (result.mHit)
pos.pos[2] = result.mHitPointWorld.z();
@ -3148,7 +3086,7 @@ namespace MWWorld
float distance = getMaxActivationDistance();
osg::Vec3f dest = origin + direction * distance;
MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);
MWRender::RayResult result2 = mRendering->castRay(origin, dest, true, true);
float dist1 = std::numeric_limits<float>::max();
float dist2 = std::numeric_limits<float>::max();
@ -3977,4 +3915,122 @@ namespace MWWorld
btVector3 hitNormal;
return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal);
}
#ifdef USE_OPENXR
void World::getPointedAtObject(MWRender::RayResult& result)
{
result = {};
result.mHit = false;
if (MWBase::Environment::get().getWindowManager()->isGuiMode() &&
MWBase::Environment::get().getWindowManager()->isConsoleMode())
{
return getPointedAtObject(result, getMaxActivationDistance() * 50, false);
}
else
{
MWRender::RayResult pointedAtObject;
getPointedAtObject(pointedAtObject, getActivationDistancePlusTelekinesis(), true);
auto ptr = pointedAtObject.mHitObject;
if (!ptr.isEmpty() && !ptr.getClass().allowTelekinesis(ptr)
&& mDistanceToFacedObject > getMaxActivationDistance() && !MWBase::Environment::get().getWindowManager()->isGuiMode())
return;
result = pointedAtObject;
}
}
float World::getDistanceToPointedAtObject()
{
return mDistanceToPointedAtObject;
}
void World::getPointedAtObject(MWRender::RayResult& result, float maxDistance, bool ignorePlayer)
{
auto sceneRoot = mRendering->getLightRoot();
result = {};
result.mHit = false;
// Find the transform giving the finger's pointing direction.
SceneUtil::FindByNameVisitor findPointerVisitor("Pointer Transform", osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
sceneRoot->accept(findPointerVisitor);
auto pointerTransform = findPointerVisitor.mFoundNode;
if (pointerTransform)
{
auto pat = pointerTransform->asTransform()->asPositionAttitudeTransform();
// Discover the world position of the finger
// (This is actually the base of the last joint bone but so long as it is pointing forward it serves the same purpose).
osg::Matrix worldMatrix = osg::computeLocalToWorld(pointerTransform->getParentalNodePaths()[0]);
pat->computeLocalToWorldMatrix(worldMatrix, nullptr);
osg::Vec3 translate;
osg::Quat rotation;
osg::Vec3 scale;
osg::Quat scaleRotation;
worldMatrix.decompose(translate, rotation, scale, scaleRotation);
osg::Vec3f direction = rotation * osg::Vec3f(-1, 0, 0);
direction.normalize();
osg::Vec3f raySource = translate;
osg::Vec3f rayTarget = translate + direction * maxDistance;
auto rayToObject = mRendering->castRay(raySource, rayTarget, ignorePlayer, false);
if (rayToObject.mHit)
mDistanceToPointedAtObject = (rayToObject.mHitPointWorld - raySource).length();
else
// Leave a very large but not too large number to permit visualizing a beam going into "infinity"
mDistanceToPointedAtObject = 10000.f;
result = rayToObject;
}
}
MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& object, int amount)
{
const float maxDist = 200.f;
MWRender::RayResult pointedAt;
getPointedAtObject(pointedAt);
CellStore* cell = getPlayerPtr().getCell();
ESM::Position pos = getPlayerPtr().getRefData().getPosition();
if (pointedAt.mHit)
{
pos.pos[0] = pointedAt.mHitPointWorld.x();
pos.pos[1] = pointedAt.mHitPointWorld.y();
pos.pos[2] = pointedAt.mHitPointWorld.z();
}
// We want only the Z part of the player's rotation
// TODO: Use the hand's orientation?
pos.rot[0] = 0;
pos.rot[1] = 0;
// copy the object and set its count
Ptr dropped = copyObjectToCell(object, cell, pos, amount, true);
// only the player place items in the world, so no need to check actor
PCDropped(dropped);
return dropped;
}
bool World::canPlaceObject()
{
const float maxDist = 200.f;
MWRender::RayResult pointedAt;
getPointedAtObject(pointedAt);
if (pointedAt.mHit)
{
// check if the wanted position is on a flat surface, and not e.g. against a vertical wall
if (std::acos((pointedAt.mHitNormalWorld / pointedAt.mHitNormalWorld.length()) * osg::Vec3f(0, 0, 1)) >= osg::DegreesToRadians(30.f))
return false;
return true;
}
else
return false;
}
#endif
}

View file

@ -140,8 +140,6 @@ namespace MWWorld
MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true);
IntersectedObject getPointedAtObject(float maxDistance, bool ignorePlayer=true);
public: // FIXME
void addContainerScripts(const Ptr& reference, CellStore* cell) override;
void removeContainerScripts(const Ptr& reference) override;
@ -176,7 +174,6 @@ namespace MWWorld
float mSwimHeightScale;
float mDistanceToFacedObject;
float mDistanceToPointedAtObject;
bool mTeleportEnabled;
bool mLevitationEnabled;
@ -377,11 +374,7 @@ namespace MWWorld
MWWorld::Ptr getFacedObject() override;
///< Return pointer to the object the player is looking at, if it is within activation range
IntersectedObject getPointedAtObject() override;
///< Return pointer to the object and/or node the player is currently pointing at
float getDistanceToFacedObject() override;
float getDistanceToPointedAtObject() override;
/// 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
@ -735,6 +728,25 @@ namespace MWWorld
osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const override;
bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override;
#ifdef USE_OPENXR
private:
float mDistanceToPointedAtObject;
void getPointedAtObject(MWRender::RayResult& result, float maxDistance, bool ignorePlayer = true);
public:
void getPointedAtObject(MWRender::RayResult& result) override;
///< Return pointer to the object and/or node the player is currently pointing at
float getDistanceToPointedAtObject() override;
///< Return the distance to the object and/or node the player is currently pointing at
MWWorld::Ptr placeObject(const MWWorld::ConstPtr& object, int amount) override;
///< copy and place an object into the gameworld where the player is currently pointing
/// @param object
/// @param number of objects to place
bool canPlaceObject() override;
///< @return true if it is possible to place on object where the player is currently pointing
#endif
};
}