mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 14:59:54 +00:00
Add bindings for navigator utils functions
This commit is contained in:
parent
f393fee9d1
commit
27cc901e76
11 changed files with 360 additions and 8 deletions
|
@ -54,7 +54,7 @@ namespace MWLua
|
||||||
{
|
{
|
||||||
auto* lua = context.mLua;
|
auto* lua = context.mLua;
|
||||||
sol::table api(lua->sol(), sol::create);
|
sol::table api(lua->sol(), sol::create);
|
||||||
api["API_REVISION"] = 27;
|
api["API_REVISION"] = 28;
|
||||||
api["quit"] = [lua]()
|
api["quit"] = [lua]()
|
||||||
{
|
{
|
||||||
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#include "luabindings.hpp"
|
#include "luabindings.hpp"
|
||||||
|
|
||||||
#include <components/lua/luastate.hpp>
|
#include <components/lua/luastate.hpp>
|
||||||
|
#include <components/detournavigator/navigator.hpp>
|
||||||
|
#include <components/detournavigator/navigatorutils.hpp>
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
@ -122,6 +125,144 @@ namespace MWLua
|
||||||
api["containers"] = LObjectList{worldView->getContainersInScene()};
|
api["containers"] = LObjectList{worldView->getContainersInScene()};
|
||||||
api["doors"] = LObjectList{worldView->getDoorsInScene()};
|
api["doors"] = LObjectList{worldView->getDoorsInScene()};
|
||||||
api["items"] = LObjectList{worldView->getItemsInScene()};
|
api["items"] = LObjectList{worldView->getItemsInScene()};
|
||||||
|
|
||||||
|
api["NAVIGATOR_FLAGS"] = LuaUtil::makeStrictReadOnly(
|
||||||
|
context.mLua->tableFromPairs<std::string_view, DetourNavigator::Flag>({
|
||||||
|
{"Walk", DetourNavigator::Flag_walk},
|
||||||
|
{"Swim", DetourNavigator::Flag_swim},
|
||||||
|
{"OpenDoor", DetourNavigator::Flag_openDoor},
|
||||||
|
{"UsePathgrid", DetourNavigator::Flag_usePathgrid},
|
||||||
|
}));
|
||||||
|
|
||||||
|
api["COLLISION_SHAPE_TYPE"] = LuaUtil::makeStrictReadOnly(
|
||||||
|
context.mLua->tableFromPairs<std::string_view, DetourNavigator::CollisionShapeType>({
|
||||||
|
{"Aabb", DetourNavigator::CollisionShapeType::Aabb},
|
||||||
|
{"RotatingBox", DetourNavigator::CollisionShapeType::RotatingBox},
|
||||||
|
}));
|
||||||
|
|
||||||
|
api["FIND_PATH_STATUS"] = LuaUtil::makeStrictReadOnly(
|
||||||
|
context.mLua->tableFromPairs<std::string_view, DetourNavigator::Status>({
|
||||||
|
{"Success", DetourNavigator::Status::Success},
|
||||||
|
{"PartialPath", DetourNavigator::Status::PartialPath},
|
||||||
|
{"NavMeshNotFound", DetourNavigator::Status::NavMeshNotFound},
|
||||||
|
{"StartPolygonNotFound", DetourNavigator::Status::StartPolygonNotFound},
|
||||||
|
{"EndPolygonNotFound", DetourNavigator::Status::EndPolygonNotFound},
|
||||||
|
{"MoveAlongSurfaceFailed", DetourNavigator::Status::MoveAlongSurfaceFailed},
|
||||||
|
{"FindPathOverPolygonsFailed", DetourNavigator::Status::FindPathOverPolygonsFailed},
|
||||||
|
{"GetPolyHeightFailed", DetourNavigator::Status::GetPolyHeightFailed},
|
||||||
|
{"InitNavMeshQueryFailed", DetourNavigator::Status::InitNavMeshQueryFailed},
|
||||||
|
}));
|
||||||
|
|
||||||
|
static const DetourNavigator::AgentBounds defaultAgentBounds {
|
||||||
|
DetourNavigator::defaultCollisionShapeType,
|
||||||
|
Settings::Manager::getVector3("default actor pathfind half extents", "Game"),
|
||||||
|
};
|
||||||
|
static const float defaultStepSize = 2 * std::max(defaultAgentBounds.mHalfExtents.x(), defaultAgentBounds.mHalfExtents.y());
|
||||||
|
static constexpr DetourNavigator::Flags defaultIncludeFlags = DetourNavigator::Flag_walk
|
||||||
|
| DetourNavigator::Flag_swim
|
||||||
|
| DetourNavigator::Flag_openDoor
|
||||||
|
| DetourNavigator::Flag_usePathgrid;
|
||||||
|
|
||||||
|
api["findPath"] = [] (const osg::Vec3f& source, const osg::Vec3f& destination,
|
||||||
|
const sol::optional<sol::table>& options)
|
||||||
|
{
|
||||||
|
DetourNavigator::AgentBounds agentBounds = defaultAgentBounds;
|
||||||
|
float stepSize = defaultStepSize;
|
||||||
|
DetourNavigator::Flags includeFlags = defaultIncludeFlags;
|
||||||
|
DetourNavigator::AreaCosts areaCosts {};
|
||||||
|
float destinationTolerance = 1;
|
||||||
|
|
||||||
|
if (options.has_value())
|
||||||
|
{
|
||||||
|
if (const auto& t = options->get<sol::optional<sol::table>>("agentBounds"))
|
||||||
|
{
|
||||||
|
if (const auto& v = t->get<sol::optional<DetourNavigator::CollisionShapeType>>("shapeType"))
|
||||||
|
agentBounds.mShapeType = *v;
|
||||||
|
if (const auto& v = t->get<sol::optional<osg::Vec3f>>("halfExtents"))
|
||||||
|
{
|
||||||
|
agentBounds.mHalfExtents = *v;
|
||||||
|
stepSize = 2 * std::max(v->x(), v->y());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const auto& v = options->get<sol::optional<float>>("stepSize"))
|
||||||
|
stepSize = *v;
|
||||||
|
if (const auto& v = options->get<sol::optional<DetourNavigator::Flags>>("includeFlags"))
|
||||||
|
includeFlags = *v;
|
||||||
|
if (const auto& t = options->get<sol::optional<sol::table>>("areaCosts"))
|
||||||
|
{
|
||||||
|
if (const auto& v = t->get<sol::optional<float>>("water"))
|
||||||
|
areaCosts.mWater = *v;
|
||||||
|
if (const auto& v = t->get<sol::optional<float>>("door"))
|
||||||
|
areaCosts.mDoor = *v;
|
||||||
|
if (const auto& v = t->get<sol::optional<float>>("pathgrid"))
|
||||||
|
areaCosts.mPathgrid = *v;
|
||||||
|
if (const auto& v = t->get<sol::optional<float>>("ground"))
|
||||||
|
areaCosts.mGround = *v;
|
||||||
|
}
|
||||||
|
if (const auto& v = options->get<sol::optional<float>>("destinationTolerance"))
|
||||||
|
destinationTolerance = *v;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<osg::Vec3f> result;
|
||||||
|
|
||||||
|
const DetourNavigator::Status status = DetourNavigator::findPath(
|
||||||
|
*MWBase::Environment::get().getWorld()->getNavigator(), agentBounds, stepSize, source,
|
||||||
|
destination, includeFlags, areaCosts, destinationTolerance, std::back_inserter(result));
|
||||||
|
|
||||||
|
return std::make_tuple(status, std::move(result));
|
||||||
|
};
|
||||||
|
|
||||||
|
api["findRandomPointAroundCircle"] = [] (const osg::Vec3f& position, float maxRadius,
|
||||||
|
const sol::optional<sol::table>& options)
|
||||||
|
{
|
||||||
|
DetourNavigator::AgentBounds agentBounds = defaultAgentBounds;
|
||||||
|
DetourNavigator::Flags includeFlags = defaultIncludeFlags;
|
||||||
|
|
||||||
|
if (options.has_value())
|
||||||
|
{
|
||||||
|
if (const auto& t = options->get<sol::optional<sol::table>>("agentBounds"))
|
||||||
|
{
|
||||||
|
if (const auto& v = t->get<sol::optional<DetourNavigator::CollisionShapeType>>("shapeType"))
|
||||||
|
agentBounds.mShapeType = *v;
|
||||||
|
if (const auto& v = t->get<sol::optional<osg::Vec3f>>("halfExtents"))
|
||||||
|
agentBounds.mHalfExtents = *v;
|
||||||
|
}
|
||||||
|
if (const auto& v = options->get<sol::optional<DetourNavigator::Flags>>("includeFlags"))
|
||||||
|
includeFlags = *v;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto getRandom = []
|
||||||
|
{
|
||||||
|
return Misc::Rng::rollProbability(MWBase::Environment::get().getWorld()->getPrng());
|
||||||
|
};
|
||||||
|
|
||||||
|
return DetourNavigator::findRandomPointAroundCircle(*MWBase::Environment::get().getWorld()->getNavigator(),
|
||||||
|
agentBounds, position, maxRadius, includeFlags, getRandom);
|
||||||
|
};
|
||||||
|
|
||||||
|
api["castNavigationRay"] = [] (const osg::Vec3f& from, const osg::Vec3f& to,
|
||||||
|
const sol::optional<sol::table>& options)
|
||||||
|
{
|
||||||
|
DetourNavigator::AgentBounds agentBounds = defaultAgentBounds;
|
||||||
|
DetourNavigator::Flags includeFlags = defaultIncludeFlags;
|
||||||
|
|
||||||
|
if (options.has_value())
|
||||||
|
{
|
||||||
|
if (const auto& t = options->get<sol::optional<sol::table>>("agentBounds"))
|
||||||
|
{
|
||||||
|
if (const auto& v = t->get<sol::optional<DetourNavigator::CollisionShapeType>>("shapeType"))
|
||||||
|
agentBounds.mShapeType = *v;
|
||||||
|
if (const auto& v = t->get<sol::optional<osg::Vec3f>>("halfExtents"))
|
||||||
|
agentBounds.mHalfExtents = *v;
|
||||||
|
}
|
||||||
|
if (const auto& v = options->get<sol::optional<DetourNavigator::Flags>>("includeFlags"))
|
||||||
|
includeFlags = *v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DetourNavigator::raycast(*MWBase::Environment::get().getWorld()->getNavigator(),
|
||||||
|
agentBounds, from, to, includeFlags);
|
||||||
|
};
|
||||||
|
|
||||||
return LuaUtil::makeReadOnly(api);
|
return LuaUtil::makeReadOnly(api);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
|
|
||||||
#include <components/lua/luastate.hpp>
|
#include <components/lua/luastate.hpp>
|
||||||
|
#include <components/detournavigator/agentbounds.hpp>
|
||||||
|
|
||||||
#include <apps/openmw/mwmechanics/drawstate.hpp>
|
#include <apps/openmw/mwmechanics/drawstate.hpp>
|
||||||
#include <apps/openmw/mwmechanics/creaturestats.hpp>
|
#include <apps/openmw/mwmechanics/creaturestats.hpp>
|
||||||
|
@ -243,6 +244,14 @@ namespace MWLua
|
||||||
}
|
}
|
||||||
context.mLuaManager->addAction(std::make_unique<SetEquipmentAction>(context.mLua, obj.id(), std::move(eqp)));
|
context.mLuaManager->addAction(std::make_unique<SetEquipmentAction>(context.mLua, obj.id(), std::move(eqp)));
|
||||||
};
|
};
|
||||||
|
actor["getPathfindingAgentBounds"] = [context](const LObject& o)
|
||||||
|
{
|
||||||
|
const DetourNavigator::AgentBounds agentBounds = MWBase::Environment::get().getWorld()->getPathfindingAgentBounds(o.ptr());
|
||||||
|
sol::table result = context.mLua->newTable();
|
||||||
|
result["shapeType"] = agentBounds.mShapeType;
|
||||||
|
result["halfExtents"] = agentBounds.mHalfExtents;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
addActorStatsBindings(actor, context);
|
addActorStatsBindings(actor, context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3931,7 +3931,7 @@ namespace MWWorld
|
||||||
DetourNavigator::AgentBounds World::getPathfindingAgentBounds(const MWWorld::ConstPtr& actor) const
|
DetourNavigator::AgentBounds World::getPathfindingAgentBounds(const MWWorld::ConstPtr& actor) const
|
||||||
{
|
{
|
||||||
const MWPhysics::Actor* physicsActor = mPhysics->getActor(actor);
|
const MWPhysics::Actor* physicsActor = mPhysics->getActor(actor);
|
||||||
if (physicsActor == nullptr || (actor.isInCell() && actor.getCell()->isExterior()))
|
if (physicsActor == nullptr || !actor.isInCell() || actor.getCell()->isExterior())
|
||||||
return DetourNavigator::AgentBounds {DetourNavigator::defaultCollisionShapeType, mDefaultHalfExtents};
|
return DetourNavigator::AgentBounds {DetourNavigator::defaultCollisionShapeType, mDefaultHalfExtents};
|
||||||
else
|
else
|
||||||
return DetourNavigator::AgentBounds {physicsActor->getCollisionShapeType(), physicsActor->getHalfExtents()};
|
return DetourNavigator::AgentBounds {physicsActor->getCollisionShapeType(), physicsActor->getHalfExtents()};
|
||||||
|
|
|
@ -256,7 +256,7 @@ namespace DetourNavigator
|
||||||
template <class OutputIterator>
|
template <class OutputIterator>
|
||||||
Status findSmoothPath(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, const float stepSize,
|
Status findSmoothPath(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, const float stepSize,
|
||||||
const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const AreaCosts& areaCosts,
|
const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const AreaCosts& areaCosts,
|
||||||
const Settings& settings, float endTolerance, OutputIterator& out)
|
const Settings& settings, float endTolerance, OutputIterator out)
|
||||||
{
|
{
|
||||||
dtNavMeshQuery navMeshQuery;
|
dtNavMeshQuery navMeshQuery;
|
||||||
if (!initNavMeshQuery(navMeshQuery, navMesh, settings.mDetour.mMaxNavMeshQueryNodes))
|
if (!initNavMeshQuery(navMeshQuery, navMesh, settings.mDetour.mMaxNavMeshQueryNodes))
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace DetourNavigator
|
||||||
template <class OutputIterator>
|
template <class OutputIterator>
|
||||||
inline Status findPath(const Navigator& navigator, const AgentBounds& agentBounds, const float stepSize,
|
inline Status findPath(const Navigator& navigator, const AgentBounds& agentBounds, const float stepSize,
|
||||||
const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const AreaCosts& areaCosts,
|
const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const AreaCosts& areaCosts,
|
||||||
float endTolerance, OutputIterator& out)
|
float endTolerance, OutputIterator out)
|
||||||
{
|
{
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_same<
|
std::is_same<
|
||||||
|
@ -45,7 +45,7 @@ namespace DetourNavigator
|
||||||
/**
|
/**
|
||||||
* @brief findRandomPointAroundCircle returns random location on navmesh within the reach of specified location.
|
* @brief findRandomPointAroundCircle returns random location on navmesh within the reach of specified location.
|
||||||
* @param agentBounds allows to find navmesh for given actor.
|
* @param agentBounds allows to find navmesh for given actor.
|
||||||
* @param start path from given point.
|
* @param start is a position where the search starts.
|
||||||
* @param maxRadius limit maximum distance from start.
|
* @param maxRadius limit maximum distance from start.
|
||||||
* @param includeFlags setup allowed surfaces for actor to walk.
|
* @param includeFlags setup allowed surfaces for actor to walk.
|
||||||
* @return not empty optional with position if point is found and empty optional if point is not found.
|
* @return not empty optional with position if point is found and empty optional if point is not found.
|
||||||
|
|
|
@ -88,5 +88,127 @@
|
||||||
-- @param openmw.util#Vector3 from Start point of the ray.
|
-- @param openmw.util#Vector3 from Start point of the ray.
|
||||||
-- @param openmw.util#Vector3 to End point of the ray.
|
-- @param openmw.util#Vector3 to End point of the ray.
|
||||||
|
|
||||||
return nil
|
---
|
||||||
|
-- @type NAVIGATOR_FLAGS
|
||||||
|
-- @field [parent=#NAVIGATOR_FLAGS] #number Walk allow agent to walk on the ground area;
|
||||||
|
-- @field [parent=#NAVIGATOR_FLAGS] #number Swim allow agent to swim on the water surface;
|
||||||
|
-- @field [parent=#NAVIGATOR_FLAGS] #number OpenDoor allow agent to open doors on the way;
|
||||||
|
-- @field [parent=#NAVIGATOR_FLAGS] #number UsePathgrid allow agent to use predefined pathgrid imported from ESM files.
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @type COLLISION_SHAPE_TYPE
|
||||||
|
-- @field [parent=#CCOLLISION_SHAPE_TYPE] #number Aabb Axis-Aligned Bounding Box is used for NPC and symmetric
|
||||||
|
-- Creatures;
|
||||||
|
-- @field [parent=#COLLISION_SHAPE_TYPE] #number RotatingBox is used for Creatures with big difference in width and
|
||||||
|
-- height.
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @type FIND_PATH_STATUS
|
||||||
|
-- @field [parent=#FIND_PATH_STATUS] #number Success Path is found;
|
||||||
|
-- @field [parent=#FIND_PATH_STATUS] #number PartialPath Last path point is not a destination but a nearest position
|
||||||
|
-- among found;
|
||||||
|
-- @field [parent=#FIND_PATH_STATUS] #number NavMeshNotFound Provided `agentBounds` don't have corresponding navigation
|
||||||
|
-- mesh. For interior cells it means an agent with such `agentBounds` is present on the scene. For exterior cells only
|
||||||
|
-- default `agentBounds` is supported;
|
||||||
|
-- @field [parent=#FIND_PATH_STATUS] #number StartPolygonNotFound `source` position is too far from available
|
||||||
|
-- navigation mesh. The status may appear when navigation mesh is not fully generated or position is outside of covered
|
||||||
|
-- area;
|
||||||
|
-- @field [parent=#FIND_PATH_STATUS] #number EndPolygonNotFound `destination` position is too far from available
|
||||||
|
-- navigation mesh. The status may appear when navigation mesh is not fully generated or position is outside of covered
|
||||||
|
-- area;
|
||||||
|
-- @field [parent=#FIND_PATH_STATUS] #number MoveAlongSurfaceFailed Found path couldn't be smoothed due to imperfect
|
||||||
|
-- algorithm implementation or bad navigation mesh data;
|
||||||
|
-- @field [parent=#FIND_PATH_STATUS] #number FindPathOverPolygonsFailed Path over navigation mesh from `source` to
|
||||||
|
-- `destination` does not exist or navigation mesh is not fully generated to provide the path;
|
||||||
|
-- @field [parent=#FIND_PATH_STATUS] #number GetPolyHeightFailed Found path couldn't be smoothed due to imperfect
|
||||||
|
-- algorithm implementation or bad navigation mesh data;
|
||||||
|
-- @field [parent=#FIND_PATH_STATUS] #number InitNavMeshQueryFailed Couldn't initialize required data due to bad input
|
||||||
|
-- or bad navigation mesh data.
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Find path over navigation mesh from source to destination with given options. Result is unstable since navigation
|
||||||
|
-- mesh generation is asynchronous.
|
||||||
|
-- @function [parent=#nearby] findPath
|
||||||
|
-- @param openmw.util#Vector3 source Initial path position.
|
||||||
|
-- @param openmw.util#Vector3 destination Final path position.
|
||||||
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
|
--
|
||||||
|
-- * `agentBounds` - a table identifying which navmesh to use, can contain:
|
||||||
|
--
|
||||||
|
-- * `shapeType` - one of @{#COLLISION_SHAPE_TYPE} values;
|
||||||
|
-- * `halfExtents` - @{openmw.util#Vector3} defining agent bounds size;
|
||||||
|
-- * `stepSize` - a floating point number to define frequency of path points
|
||||||
|
-- (default: `2 * math.max(halfExtents:x, halfExtents:y)`)
|
||||||
|
-- * `includeFlags` - allowed areas for agent to move, a sum of @{#NAVIGATOR_FLAGS} values
|
||||||
|
-- (default: @{#NAVIGATOR_FLAGS.Walk} + @{#NAVIGATOR_FLAGS.Swim} +
|
||||||
|
-- @{#NAVIGATOR_FLAGS.OpenDoor} + @{#NAVIGATOR_FLAGS.UsePathgrid});
|
||||||
|
-- * `areaCosts` - a table defining relative cost for each type of area, can contain:
|
||||||
|
--
|
||||||
|
-- * `ground` - a floating point number >= 0, used in combination with @{#NAVIGATOR_FLAGS.Walk} (default: 1);
|
||||||
|
-- * `water` - a floating point number >= 0, used in combination with @{#NAVIGATOR_FLAGS.Swim} (default: 1);
|
||||||
|
-- * `door` - a floating point number >= 0, used in combination with @{#NAVIGATOR_FLAGS.OpenDoor} (default: 2);
|
||||||
|
-- * `pathgrid` - a floating point number >= 0, used in combination with @{#NAVIGATOR_FLAGS.UsePathgrid}
|
||||||
|
-- (default: 1);
|
||||||
|
-- * `destinationTolerance` - a floating point number representing maximum allowed distance between destination and a
|
||||||
|
-- nearest point on the navigation mesh in addition to agent size (default: 1);
|
||||||
|
-- @return @{#FIND_PATH_STATUS}, a collection of @{openmw.util#Vector3}
|
||||||
|
-- @usage local status, path = nearby.findPath(source, destination)
|
||||||
|
-- @usage local status, path = nearby.findPath(source, destination, {
|
||||||
|
-- includeFlags = nearby.NAVIGATOR_FLAGS.Walk + nearby.NAVIGATOR_FLAGS.OpenDoor,
|
||||||
|
-- areaCosts = {
|
||||||
|
-- door = 1.5,
|
||||||
|
-- },
|
||||||
|
-- })
|
||||||
|
-- @usage local status, path = nearby.findPath(source, destination, {
|
||||||
|
-- agentBounds = Actor.getPathfindingAgentBounds(self),
|
||||||
|
-- })
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Returns random location on navigation mesh within the reach of specified location.
|
||||||
|
-- The location is not exactly constrained by the circle, but it limits the area.
|
||||||
|
-- @function [parent=#nearby] findRandomPointAroundCircle
|
||||||
|
-- @param openmw.util#Vector3 position Center of the search circle.
|
||||||
|
-- @param #number maxRadius Approximate maximum search distance.
|
||||||
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
|
--
|
||||||
|
-- * `agentBounds` - a table identifying which navmesh to use, can contain:
|
||||||
|
--
|
||||||
|
-- * `shapeType` - one of @{#COLLISION_SHAPE_TYPE} values;
|
||||||
|
-- * `halfExtents` - @{openmw.util#Vector3} defining agent bounds size;
|
||||||
|
-- * `includeFlags` - allowed areas for agent to move, a sum of @{#NAVIGATOR_FLAGS} values
|
||||||
|
-- (default: @{#NAVIGATOR_FLAGS.Walk} + @{#NAVIGATOR_FLAGS.Swim} +
|
||||||
|
-- @{#NAVIGATOR_FLAGS.OpenDoor} + @{#NAVIGATOR_FLAGS.UsePathgrid});
|
||||||
|
-- @return @{openmw.util#Vector3} or nil
|
||||||
|
-- @usage local position = nearby.findRandomPointAroundCircle(position, maxRadius)
|
||||||
|
-- @usage local position = nearby.findRandomPointAroundCircle(position, maxRadius, {
|
||||||
|
-- includeFlags = nearby.NAVIGATOR_FLAGS.Walk,
|
||||||
|
-- })
|
||||||
|
-- @usage local position = nearby.findRandomPointAroundCircle(position, maxRadius, {
|
||||||
|
-- agentBounds = Actor.getPathfindingAgentBounds(self),
|
||||||
|
-- })
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Finds a nearest to the ray target position starting from the initial position with resulting curve drawn on the
|
||||||
|
-- navigation mesh surface.
|
||||||
|
-- @function [parent=#nearby] castNavigationRay
|
||||||
|
-- @param openmw.util#Vector3 from Initial ray position.
|
||||||
|
-- @param openmw.util#Vector3 to Target ray position.
|
||||||
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
|
--
|
||||||
|
-- * `agentBounds` - a table identifying which navmesh to use, can contain:
|
||||||
|
--
|
||||||
|
-- * `shapeType` - one of @{#COLLISION_SHAPE_TYPE} values;
|
||||||
|
-- * `halfExtents` - @{openmw.util#Vector3} defining agent bounds size;
|
||||||
|
-- * `includeFlags` - allowed areas for agent to move, a sum of @{#NAVIGATOR_FLAGS} values
|
||||||
|
-- (default: @{#NAVIGATOR_FLAGS.Walk} + @{#NAVIGATOR_FLAGS.Swim} +
|
||||||
|
-- @{#NAVIGATOR_FLAGS.OpenDoor} + @{#NAVIGATOR_FLAGS.UsePathgrid});
|
||||||
|
-- @return @{openmw.util#Vector3} or nil
|
||||||
|
-- @usage local position = nearby.castNavigationRay(from, to)
|
||||||
|
-- @usage local position = nearby.castNavigationRay(from, to, {
|
||||||
|
-- includeFlags = nearby.NAVIGATOR_FLAGS.Swim,
|
||||||
|
-- })
|
||||||
|
-- @usage local position = nearby.castNavigationRay(from, to, {
|
||||||
|
-- agentBounds = Actor.getPathfindingAgentBounds(self),
|
||||||
|
-- })
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
|
@ -9,6 +9,12 @@
|
||||||
--- Common functions for Creature, NPC, and Player.
|
--- Common functions for Creature, NPC, and Player.
|
||||||
-- @type Actor
|
-- @type Actor
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Agent bounds to be used for pathfinding functions.
|
||||||
|
-- @function [parent=#Actor] getPathfindingAgentBounds
|
||||||
|
-- @param openmw.core#GameObject actor
|
||||||
|
-- @return #table with `shapeType` and `halfExtents`
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Whether the object is an actor.
|
-- Whether the object is an actor.
|
||||||
-- @function [parent=#Actor] objectIsInstance
|
-- @function [parent=#Actor] objectIsInstance
|
||||||
|
|
|
@ -4,6 +4,7 @@ local util = require('openmw.util')
|
||||||
local core = require('openmw.core')
|
local core = require('openmw.core')
|
||||||
local input = require('openmw.input')
|
local input = require('openmw.input')
|
||||||
local types = require('openmw.types')
|
local types = require('openmw.types')
|
||||||
|
local nearby = require('openmw.nearby')
|
||||||
|
|
||||||
input.setControlSwitch(input.CONTROL_SWITCH.Fighting, false)
|
input.setControlSwitch(input.CONTROL_SWITCH.Fighting, false)
|
||||||
input.setControlSwitch(input.CONTROL_SWITCH.Jumping, false)
|
input.setControlSwitch(input.CONTROL_SWITCH.Jumping, false)
|
||||||
|
@ -64,6 +65,51 @@ testing.registerLocalTest('playerDiagonalWalking',
|
||||||
testing.expectEqualWithDelta(direction.y, -0.707, 0.1, 'Walk diagonally, Y coord')
|
testing.expectEqualWithDelta(direction.y, -0.707, 0.1, 'Walk diagonally, Y coord')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
testing.registerLocalTest('findPath',
|
||||||
|
function()
|
||||||
|
local src = util.vector3(4096, 4096, 867.237)
|
||||||
|
local dst = util.vector3(4500, 4500, 700.216)
|
||||||
|
local options = {
|
||||||
|
agentBounds = types.Actor.getPathfindingAgentBounds(self),
|
||||||
|
stepSize = 50,
|
||||||
|
includeFlags = nearby.NAVIGATOR_FLAGS.Walk + nearby.NAVIGATOR_FLAGS.Swim,
|
||||||
|
areaCosts = {
|
||||||
|
water = 1,
|
||||||
|
door = 2,
|
||||||
|
ground = 1,
|
||||||
|
pathgrid = 1,
|
||||||
|
},
|
||||||
|
destinationTolerance = 1,
|
||||||
|
}
|
||||||
|
local status, path = nearby.findPath(src, dst)
|
||||||
|
testing.expectEqual(status, nearby.FIND_PATH_STATUS.Success, 'Status')
|
||||||
|
testing.expectLessOrEqual((path[path:size()] - dst):length(), 1, 'Last path point')
|
||||||
|
end)
|
||||||
|
|
||||||
|
testing.registerLocalTest('findRandomPointAroundCircle',
|
||||||
|
function()
|
||||||
|
local position = util.vector3(4096, 4096, 867.237)
|
||||||
|
local maxRadius = 100
|
||||||
|
local options = {
|
||||||
|
agentBounds = types.Actor.getPathfindingAgentBounds(self),
|
||||||
|
includeFlags = nearby.NAVIGATOR_FLAGS.Walk,
|
||||||
|
}
|
||||||
|
local result = nearby.findRandomPointAroundCircle(position, maxRadius, options)
|
||||||
|
testing.expectGreaterThan((result - position):length(), 1, 'Random point')
|
||||||
|
end)
|
||||||
|
|
||||||
|
testing.registerLocalTest('castNavigationRay',
|
||||||
|
function()
|
||||||
|
local src = util.vector3(4096, 4096, 867.237)
|
||||||
|
local dst = util.vector3(4500, 4500, 700.216)
|
||||||
|
local options = {
|
||||||
|
agentBounds = types.Actor.getPathfindingAgentBounds(self),
|
||||||
|
includeFlags = nearby.NAVIGATOR_FLAGS.Walk + nearby.NAVIGATOR_FLAGS.Swim,
|
||||||
|
}
|
||||||
|
local result = nearby.castNavigationRay(src, dst, options)
|
||||||
|
testing.expectLessOrEqual((result - dst):length(), 1, 'Navigation hit point')
|
||||||
|
end)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
engineHandlers = {
|
engineHandlers = {
|
||||||
onUpdate = testing.updateLocal,
|
onUpdate = testing.updateLocal,
|
||||||
|
|
|
@ -70,6 +70,18 @@ tests = {
|
||||||
initPlayer()
|
initPlayer()
|
||||||
testing.runLocalTest(player, 'playerDiagonalWalking')
|
testing.runLocalTest(player, 'playerDiagonalWalking')
|
||||||
end},
|
end},
|
||||||
|
{'findPath', function()
|
||||||
|
initPlayer()
|
||||||
|
testing.runLocalTest(player, 'findPath')
|
||||||
|
end},
|
||||||
|
{'findRandomPointAroundCircle', function()
|
||||||
|
initPlayer()
|
||||||
|
testing.runLocalTest(player, 'findRandomPointAroundCircle')
|
||||||
|
end},
|
||||||
|
{'castNavigationRay', function()
|
||||||
|
initPlayer()
|
||||||
|
testing.runLocalTest(player, 'castNavigationRay')
|
||||||
|
end},
|
||||||
{'teleport', testTeleport},
|
{'teleport', testTeleport},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,4 +92,3 @@ return {
|
||||||
},
|
},
|
||||||
eventHandlers = testing.eventHandlers,
|
eventHandlers = testing.eventHandlers,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,24 @@ function M.expectGreaterOrEqual(v1, v2, msg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M.expectGreaterThan(v1, v2, msg)
|
||||||
|
if not (v1 > v2) then
|
||||||
|
error(string.format('%s: %s > %s', msg or '', v1, v2), 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.expectLessOrEqual(v1, v2, msg)
|
||||||
|
if not (v1 <= v2) then
|
||||||
|
error(string.format('%s: %s <= %s', msg or '', v1, v2), 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.expectEqual(v1, v2, msg)
|
||||||
|
if not (v1 == v2) then
|
||||||
|
error(string.format('%s: %s ~= %s', msg or '', v1, v2), 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local localTests = {}
|
local localTests = {}
|
||||||
local localTestRunner = nil
|
local localTestRunner = nil
|
||||||
|
|
||||||
|
@ -96,4 +114,3 @@ M.eventHandlers = {
|
||||||
}
|
}
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue