1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-27 00:40:21 +00:00

Merge pull request #2907 from elsid/navmesh_area_cost

Use actors stats to define navmesh area cost
This commit is contained in:
Bret Curtis 2020-06-13 10:43:01 +02:00 committed by GitHub
commit 3f489b3774
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 203 additions and 79 deletions

View File

@ -507,23 +507,16 @@ namespace MWClass
float Creature::getSpeed(const MWWorld::Ptr &ptr) const float Creature::getSpeed(const MWWorld::Ptr &ptr) const
{ {
MWMechanics::CreatureStats& stats = getCreatureStats(ptr); const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
return 0.f; return 0.f;
const GMST& gmst = getGmst(); const GMST& gmst = getGmst();
float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat());
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);
// The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp)
float runSpeed = walkSpeed;
float moveSpeed; float moveSpeed;
if(getEncumbrance(ptr) > getCapacity(ptr)) if(getEncumbrance(ptr) > getCapacity(ptr))
@ -540,19 +533,9 @@ namespace MWClass
moveSpeed = flySpeed; moveSpeed = flySpeed;
} }
else if(world->isSwimming(ptr)) else if(world->isSwimming(ptr))
{ moveSpeed = getSwimSpeed(ptr);
float swimSpeed = walkSpeed;
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
gmst.fSwimRunAthleticsMult->mValue.getFloat();
moveSpeed = swimSpeed;
}
else if(running)
moveSpeed = runSpeed;
else else
moveSpeed = walkSpeed; moveSpeed = getWalkSpeed(ptr);
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f; moveSpeed *= 0.75f;
@ -889,4 +872,31 @@ namespace MWClass
{ {
MWMechanics::setBaseAISetting<ESM::Creature>(id, setting, value); MWMechanics::setBaseAISetting<ESM::Creature>(id, setting, value);
} }
float Creature::getWalkSpeed(const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const GMST& gmst = getGmst();
return gmst.fMinWalkSpeedCreature->mValue.getFloat()
+ 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat());
}
float Creature::getRunSpeed(const MWWorld::Ptr& ptr) const
{
return getWalkSpeed(ptr);
}
float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const GMST& gmst = getGmst();
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
return getWalkSpeed(ptr)
* (1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude())
* (gmst.fSwimRunBase->mValue.getFloat()
+ 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat());
}
} }

View File

@ -131,6 +131,12 @@ namespace MWClass
/// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
float getWalkSpeed(const MWWorld::Ptr& ptr) const final;
float getRunSpeed(const MWWorld::Ptr& ptr) const final;
float getSwimSpeed(const MWWorld::Ptr& ptr) const final;
}; };
} }

View File

@ -947,16 +947,6 @@ namespace MWClass
bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
(gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat());
walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed);
if(sneaking)
walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat();
float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) *
gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat());
float moveSpeed; float moveSpeed;
if(getEncumbrance(ptr) > getCapacity(ptr)) if(getEncumbrance(ptr) > getCapacity(ptr))
moveSpeed = 0.0f; moveSpeed = 0.0f;
@ -971,19 +961,11 @@ namespace MWClass
moveSpeed = flySpeed; moveSpeed = flySpeed;
} }
else if (swimming) else if (swimming)
{ moveSpeed = getSwimSpeed(ptr);
float swimSpeed = walkSpeed;
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics)*
gmst.fSwimRunAthleticsMult->mValue.getFloat();
moveSpeed = swimSpeed;
}
else if (running && !sneaking) else if (running && !sneaking)
moveSpeed = runSpeed; moveSpeed = getRunSpeed(ptr);
else else
moveSpeed = walkSpeed; moveSpeed = getWalkSpeed(ptr);
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f; moveSpeed *= 0.75f;
@ -1448,4 +1430,56 @@ namespace MWClass
{ {
MWMechanics::setBaseAISetting<ESM::NPC>(id, setting, value); MWMechanics::setBaseAISetting<ESM::NPC>(id, setting, value);
} }
float Npc::getWalkSpeed(const MWWorld::Ptr& ptr) const
{
const GMST& gmst = getGmst();
const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
const bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);
float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat()
+ 0.01f * npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat());
walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed);
if(sneaking)
walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat();
return walkSpeed;
}
float Npc::getRunSpeed(const MWWorld::Ptr& ptr) const
{
const GMST& gmst = getGmst();
return getWalkSpeed(ptr)
* (0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat()
+ gmst.fBaseRunMultiplier->mValue.getFloat());
}
float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const
{
const GMST& gmst = getGmst();
const MWBase::World* world = MWBase::Environment::get().getWorld();
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects& mageffects = npcdata->mNpcStats.getMagicEffects();
const bool swimming = world->isSwimming(ptr);
const bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
const bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run)
&& (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
float swimSpeed;
if (running)
swimSpeed = getRunSpeed(ptr);
else
swimSpeed = getWalkSpeed(ptr);
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->mValue.getFloat()
+ 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat();
return swimSpeed;
}
} }

View File

@ -166,6 +166,12 @@ namespace MWClass
virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const;
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
float getWalkSpeed(const MWWorld::Ptr& ptr) const final;
float getRunSpeed(const MWWorld::Ptr& ptr) const final;
float getSwimSpeed(const MWWorld::Ptr& ptr) const final;
}; };
} }

View File

@ -114,7 +114,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
{ {
const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor); const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor);
mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()), mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),
pathfindingHalfExtents, getNavigatorFlags(actor)); pathfindingHalfExtents, getNavigatorFlags(actor), getAreaCosts(actor));
mRotateOnTheRunChecks = 3; mRotateOnTheRunChecks = 3;
// give priority to go directly on target if there is minimal opportunity // give priority to go directly on target if there is minimal opportunity
@ -402,3 +402,27 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld::
return result; return result;
} }
DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::Ptr& actor) const
{
DetourNavigator::AreaCosts costs;
const DetourNavigator::Flags flags = getNavigatorFlags(actor);
const MWWorld::Class& actorClass = actor.getClass();
if (flags & DetourNavigator::Flag_swim)
costs.mWater = costs.mWater / actorClass.getSwimSpeed(actor);
if (flags & DetourNavigator::Flag_walk)
{
float walkCost;
if (getTypeId() == TypeIdWander)
walkCost = 1.0 / actorClass.getWalkSpeed(actor);
else
walkCost = 1.0 / actorClass.getRunSpeed(actor);
costs.mDoor = costs.mDoor * walkCost;
costs.mPathgrid = costs.mPathgrid * walkCost;
costs.mGround = costs.mGround * walkCost;
}
return costs;
}

View File

@ -4,6 +4,7 @@
#include <memory> #include <memory>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/detournavigator/areatype.hpp>
#include "pathfinding.hpp" #include "pathfinding.hpp"
#include "obstacle.hpp" #include "obstacle.hpp"
@ -167,6 +168,8 @@ namespace MWMechanics
DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const;
DetourNavigator::AreaCosts getAreaCosts(const MWWorld::Ptr& actor) const;
const TypeId mTypeId; const TypeId mTypeId;
const Options mOptions; const Options mOptions;

View File

@ -202,7 +202,7 @@ namespace MWMechanics
{ {
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(), mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(),
getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor), getAreaCosts(actor));
} }
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
@ -337,6 +337,7 @@ namespace MWMechanics
const auto halfExtents = world->getPathfindingHalfExtents(actor); const auto halfExtents = world->getPathfindingHalfExtents(actor);
const auto navigator = world->getNavigator(); const auto navigator = world->getNavigator();
const auto navigatorFlags = getNavigatorFlags(actor); const auto navigatorFlags = getNavigatorFlags(actor);
const auto areaCosts = getAreaCosts(actor);
do { do {
// Determine a random location within radius of original position // Determine a random location within radius of original position
@ -365,7 +366,8 @@ namespace MWMechanics
if (isWaterCreature || isFlyingCreature) if (isWaterCreature || isFlyingCreature)
mPathFinder.buildStraightPath(mDestination); mPathFinder.buildStraightPath(mDestination);
else else
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags); mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags,
areaCosts);
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
{ {
@ -496,7 +498,8 @@ namespace MWMechanics
if (mUsePathgrid) if (mUsePathgrid)
{ {
const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor)); mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor),
getAreaCosts(actor));
} }
if (mObstacleCheck.isEvading()) if (mObstacleCheck.isEvading())

View File

@ -309,12 +309,13 @@ namespace MWMechanics
} }
void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags) const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
const DetourNavigator::AreaCosts& areaCosts)
{ {
mPath.clear(); mPath.clear();
// If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path // If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path
if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath))) if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, areaCosts, std::back_inserter(mPath)))
mPath.push_back(endPoint); mPath.push_back(endPoint);
mConstructed = true; mConstructed = true;
@ -322,7 +323,7 @@ namespace MWMechanics
void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags) const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts)
{ {
mPath.clear(); mPath.clear();
mCell = cell; mCell = cell;
@ -330,11 +331,11 @@ namespace MWMechanics
bool hasNavMesh = false; bool hasNavMesh = false;
if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor)) if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor))
hasNavMesh = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); hasNavMesh = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, areaCosts, std::back_inserter(mPath));
if (hasNavMesh && mPath.empty()) if (hasNavMesh && mPath.empty())
buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents,
flags | DetourNavigator::Flag_usePathgrid, std::back_inserter(mPath)); flags | DetourNavigator::Flag_usePathgrid, areaCosts, std::back_inserter(mPath));
if (mPath.empty()) if (mPath.empty())
buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath)); buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath));
@ -347,12 +348,12 @@ namespace MWMechanics
bool PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, bool PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
std::back_insert_iterator<std::deque<osg::Vec3f>> out) const DetourNavigator::AreaCosts& areaCosts, std::back_insert_iterator<std::deque<osg::Vec3f>> out)
{ {
const auto world = MWBase::Environment::get().getWorld(); const auto world = MWBase::Environment::get().getWorld();
const auto stepSize = getPathStepSize(actor); const auto stepSize = getPathStepSize(actor);
const auto navigator = world->getNavigator(); const auto navigator = world->getNavigator();
const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out); const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, areaCosts, out);
if (status == DetourNavigator::Status::NavMeshNotFound) if (status == DetourNavigator::Status::NavMeshNotFound)
return false; return false;
@ -369,7 +370,7 @@ namespace MWMechanics
} }
void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags) const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts)
{ {
if (mPath.empty()) if (mPath.empty())
return; return;
@ -383,7 +384,7 @@ namespace MWMechanics
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
std::deque<osg::Vec3f> prePath; std::deque<osg::Vec3f> prePath;
auto prePathInserter = std::back_inserter(prePath); auto prePathInserter = std::back_inserter(prePath);
const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, areaCosts,
prePathInserter); prePathInserter);
if (status == DetourNavigator::Status::NavMeshNotFound) if (status == DetourNavigator::Status::NavMeshNotFound)

View File

@ -6,6 +6,7 @@
#include <iterator> #include <iterator>
#include <components/detournavigator/flags.hpp> #include <components/detournavigator/flags.hpp>
#include <components/detournavigator/areatype.hpp>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm/loadpgrd.hpp> #include <components/esm/loadpgrd.hpp>
@ -90,14 +91,15 @@ namespace MWMechanics
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph); const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags); const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
const DetourNavigator::AreaCosts& areaCosts);
void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags); const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags); const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
/// Remove front point if exist and within tolerance /// Remove front point if exist and within tolerance
void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance); void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance);
@ -203,7 +205,7 @@ namespace MWMechanics
bool buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, bool buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
std::back_insert_iterator<std::deque<osg::Vec3f>> out); const DetourNavigator::AreaCosts& areaCosts, std::back_insert_iterator<std::deque<osg::Vec3f>> out);
}; };
} }

View File

@ -521,4 +521,19 @@ namespace MWWorld
{ {
throw std::runtime_error ("class does not have creature stats"); throw std::runtime_error ("class does not have creature stats");
} }
float Class::getWalkSpeed(const Ptr& /*ptr*/) const
{
return 0;
}
float Class::getRunSpeed(const Ptr& /*ptr*/) const
{
return 0;
}
float Class::getSwimSpeed(const Ptr& /*ptr*/) const
{
return 0;
}
} }

View File

@ -362,6 +362,12 @@ namespace MWWorld
virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const;
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
virtual float getWalkSpeed(const Ptr& ptr) const;
virtual float getRunSpeed(const Ptr& ptr) const;
virtual float getSwimSpeed(const Ptr& ptr) const;
}; };
} }

View File

@ -37,6 +37,7 @@ namespace
std::deque<osg::Vec3f> mPath; std::deque<osg::Vec3f> mPath;
std::back_insert_iterator<std::deque<osg::Vec3f>> mOut; std::back_insert_iterator<std::deque<osg::Vec3f>> mOut;
float mStepSize; float mStepSize;
AreaCosts mAreaCosts;
DetourNavigatorNavigatorTest() DetourNavigatorNavigatorTest()
: mPlayerPosition(0, 0, 0) : mPlayerPosition(0, 0, 0)
@ -80,7 +81,7 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty) TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty)
{ {
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut),
Status::NavMeshNotFound); Status::NavMeshNotFound);
EXPECT_EQ(mPath, std::deque<osg::Vec3f>()); EXPECT_EQ(mPath, std::deque<osg::Vec3f>());
} }
@ -88,7 +89,7 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception) TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception)
{ {
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut),
Status::StartPolygonNotFound); Status::StartPolygonNotFound);
} }
@ -97,7 +98,7 @@ namespace
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->removeAgent(mAgentHalfExtents); mNavigator->removeAgent(mAgentHalfExtents);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut),
Status::StartPolygonNotFound); Status::StartPolygonNotFound);
} }
@ -118,7 +119,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(); mNavigator->wait();
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
Vec3fEq(-215, 215, 1.85963428020477294921875), Vec3fEq(-215, 215, 1.85963428020477294921875),
@ -168,7 +169,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(); mNavigator->wait();
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
Vec3fEq(-215, 215, 1.85963428020477294921875), Vec3fEq(-215, 215, 1.85963428020477294921875),
@ -202,7 +203,7 @@ namespace
mPath.clear(); mPath.clear();
mOut = std::back_inserter(mPath); mOut = std::back_inserter(mPath);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
Vec3fEq(-215, 215, 1.87826788425445556640625), Vec3fEq(-215, 215, 1.87826788425445556640625),
@ -253,7 +254,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(); mNavigator->wait();
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
Vec3fEq(-215, 215, 1.87826788425445556640625), Vec3fEq(-215, 215, 1.87826788425445556640625),
@ -289,7 +290,7 @@ namespace
mPath.clear(); mPath.clear();
mOut = std::back_inserter(mPath); mOut = std::back_inserter(mPath);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
Vec3fEq(-215, 215, 1.85963428020477294921875), Vec3fEq(-215, 215, 1.85963428020477294921875),
@ -346,7 +347,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(); mNavigator->wait();
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
Vec3fEq(-215, 215, 1.96328866481781005859375), Vec3fEq(-215, 215, 1.96328866481781005859375),
@ -402,7 +403,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(); mNavigator->wait();
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
Vec3fEq(-215, 215, 1.9393787384033203125), Vec3fEq(-215, 215, 1.9393787384033203125),
@ -455,7 +456,7 @@ namespace
mEnd.x() = 0; mEnd.x() = 0;
mEnd.z() = 300; mEnd.z() = 300;
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim, mAreaCosts, mOut), Status::Success);
EXPECT_EQ(mPath, std::deque<osg::Vec3f>({ EXPECT_EQ(mPath, std::deque<osg::Vec3f>({
osg::Vec3f(0, 215, 185.33331298828125), osg::Vec3f(0, 215, 185.33331298828125),
@ -501,7 +502,7 @@ namespace
mStart.x() = 0; mStart.x() = 0;
mEnd.x() = 0; mEnd.x() = 0;
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mOut), EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mOut),
Status::Success); Status::Success);
EXPECT_EQ(mPath, std::deque<osg::Vec3f>({ EXPECT_EQ(mPath, std::deque<osg::Vec3f>({
@ -548,7 +549,7 @@ namespace
mStart.x() = 0; mStart.x() = 0;
mEnd.x() = 0; mEnd.x() = 0;
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mOut), EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mOut),
Status::Success); Status::Success);
EXPECT_EQ(mPath, std::deque<osg::Vec3f>({ EXPECT_EQ(mPath, std::deque<osg::Vec3f>({
@ -595,7 +596,7 @@ namespace
mStart.x() = 0; mStart.x() = 0;
mEnd.x() = 0; mEnd.x() = 0;
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
Vec3fEq(0, 215, -94.75363922119140625), Vec3fEq(0, 215, -94.75363922119140625),
@ -644,7 +645,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(); mNavigator->wait();
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
Vec3fEq(-215, 215, 1.85963428020477294921875), Vec3fEq(-215, 215, 1.85963428020477294921875),
@ -739,7 +740,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(); mNavigator->wait();
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
EXPECT_THAT(mPath, ElementsAre( EXPECT_THAT(mPath, ElementsAre(
Vec3fEq(-215, 215, 1.8782780170440673828125), Vec3fEq(-215, 215, 1.8782780170440673828125),

View File

@ -13,6 +13,14 @@ namespace DetourNavigator
AreaType_pathgrid, AreaType_pathgrid,
AreaType_ground = RC_WALKABLE_AREA, AreaType_ground = RC_WALKABLE_AREA,
}; };
struct AreaCosts
{
float mWater = 1.0f;
float mDoor = 2.0f;
float mPathgrid = 1.0f;
float mGround = 1.0f;
};
} }
#endif #endif

View File

@ -8,6 +8,7 @@
#include "settingsutils.hpp" #include "settingsutils.hpp"
#include "debug.hpp" #include "debug.hpp"
#include "status.hpp" #include "status.hpp"
#include "areatype.hpp"
#include <DetourCommon.h> #include <DetourCommon.h>
#include <DetourNavMesh.h> #include <DetourNavMesh.h>
@ -269,7 +270,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 osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const AreaCosts& areaCosts,
const Settings& settings, OutputIterator& out) const Settings& settings, OutputIterator& out)
{ {
dtNavMeshQuery navMeshQuery; dtNavMeshQuery navMeshQuery;
@ -278,6 +279,10 @@ namespace DetourNavigator
dtQueryFilter queryFilter; dtQueryFilter queryFilter;
queryFilter.setIncludeFlags(includeFlags); queryFilter.setIncludeFlags(includeFlags);
queryFilter.setAreaCost(AreaType_water, areaCosts.mWater);
queryFilter.setAreaCost(AreaType_door, areaCosts.mDoor);
queryFilter.setAreaCost(AreaType_pathgrid, areaCosts.mPathgrid);
queryFilter.setAreaCost(AreaType_ground, areaCosts.mGround);
dtPolyRef startRef = 0; dtPolyRef startRef = 0;
osg::Vec3f startPolygonPosition; osg::Vec3f startPolygonPosition;

View File

@ -172,7 +172,8 @@ namespace DetourNavigator
*/ */
template <class OutputIterator> template <class OutputIterator>
Status findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start, Status findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start,
const osg::Vec3f& end, const Flags includeFlags, OutputIterator& out) const const osg::Vec3f& end, const Flags includeFlags, const DetourNavigator::AreaCosts& areaCosts,
OutputIterator& out) const
{ {
static_assert( static_assert(
std::is_same< std::is_same<
@ -187,7 +188,7 @@ namespace DetourNavigator
const auto settings = getSettings(); const auto settings = getSettings();
return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents), return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents),
toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start), toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start),
toNavMeshCoordinates(settings, end), includeFlags, settings, out); toNavMeshCoordinates(settings, end), includeFlags, areaCosts, settings, out);
} }
/** /**

View File

@ -64,7 +64,6 @@ namespace DetourNavigator
std::map<osg::Vec3f, std::size_t> mAgents; std::map<osg::Vec3f, std::size_t> mAgents;
std::unordered_map<ObjectId, ObjectId> mAvoidIds; std::unordered_map<ObjectId, ObjectId> mAvoidIds;
std::unordered_map<ObjectId, ObjectId> mWaterIds; std::unordered_map<ObjectId, ObjectId> mWaterIds;
std::multimap<ObjectId, ObjectId> mOffMeshConnectionIds;
void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId); void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId);
void updateWaterShapeId(const ObjectId id, const ObjectId waterId); void updateWaterShapeId(const ObjectId id, const ObjectId waterId);