mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-09-10 12:59:58 -04:00
Store allowed positions as osg::Vec3f
There is no need to store pathgrid points.
This commit is contained in:
parent
7c46635a5a
commit
50f5bc51c6
@ -109,24 +109,25 @@ namespace MWMechanics
|
|||||||
return std::vector<unsigned char>(std::begin(idle), std::end(idle));
|
return std::vector<unsigned char>(std::begin(idle), std::end(idle));
|
||||||
}
|
}
|
||||||
|
|
||||||
void trimAllowedNodes(const std::deque<osg::Vec3f>& path, std::vector<ESM::Pathgrid::Point>& nodes)
|
void trimAllowedPositions(const std::deque<osg::Vec3f>& path, std::vector<osg::Vec3f>& allowedPositions)
|
||||||
{
|
{
|
||||||
// TODO: how to add these back in once the door opens?
|
// TODO: how to add these back in once the door opens?
|
||||||
// Idea: keep a list of detected closed doors (see aicombat.cpp)
|
// Idea: keep a list of detected closed doors (see aicombat.cpp)
|
||||||
// Every now and then check whether one of the doors is opened. (maybe
|
// Every now and then check whether one of the doors is opened. (maybe
|
||||||
// at the end of playing idle?) If the door is opened then re-calculate
|
// at the end of playing idle?) If the door is opened then re-calculate
|
||||||
// allowed nodes starting from the spawn point.
|
// allowed positions starting from the spawn point.
|
||||||
std::vector<osg::Vec3f> points(path.begin(), path.end());
|
std::vector<osg::Vec3f> points(path.begin(), path.end());
|
||||||
while (points.size() >= 2)
|
while (points.size() >= 2)
|
||||||
{
|
{
|
||||||
const osg::Vec3f point = points.back();
|
const osg::Vec3f point = points.back();
|
||||||
for (std::size_t j = 0; j < nodes.size(); j++)
|
for (std::size_t j = 0; j < allowedPositions.size(); j++)
|
||||||
{
|
{
|
||||||
// FIXME: doesn't handle a door with the same X/Y
|
// FIXME: doesn't handle a door with the same X/Y
|
||||||
// coordinates but with a different Z
|
// coordinates but with a different Z
|
||||||
if (std::abs(nodes[j].mX - point.x()) <= 0.5 && std::abs(nodes[j].mY - point.y()) <= 0.5)
|
if (std::abs(allowedPositions[j].x() - point.x()) <= 0.5
|
||||||
|
&& std::abs(allowedPositions[j].y() - point.y()) <= 0.5)
|
||||||
{
|
{
|
||||||
nodes.erase(nodes.begin() + j);
|
allowedPositions.erase(allowedPositions.begin() + j);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,9 +144,9 @@ namespace MWMechanics
|
|||||||
, mCanWanderAlongPathGrid(true)
|
, mCanWanderAlongPathGrid(true)
|
||||||
, mIdleAnimation(0)
|
, mIdleAnimation(0)
|
||||||
, mBadIdles()
|
, mBadIdles()
|
||||||
, mPopulateAvailableNodes(true)
|
, mPopulateAvailablePositions(true)
|
||||||
, mAllowedNodes()
|
, mAllowedPositions()
|
||||||
, mTrimCurrentNode(false)
|
, mTrimCurrentPosition(false)
|
||||||
, mCheckIdlePositionTimer(0)
|
, mCheckIdlePositionTimer(0)
|
||||||
, mStuckCount(0)
|
, mStuckCount(0)
|
||||||
{
|
{
|
||||||
@ -298,10 +299,10 @@ namespace MWMechanics
|
|||||||
mStoredInitialActorPosition = true;
|
mStoredInitialActorPosition = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialization to discover & store allowed node points for this actor.
|
// Initialization to discover & store allowed positions points for this actor.
|
||||||
if (storage.mPopulateAvailableNodes)
|
if (storage.mPopulateAvailablePositions)
|
||||||
{
|
{
|
||||||
getAllowedNodes(actor, storage);
|
fillAllowedPositions(actor, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWBase::World& world = *MWBase::Environment::get().getWorld();
|
MWBase::World& world = *MWBase::Environment::get().getWorld();
|
||||||
@ -319,7 +320,7 @@ namespace MWMechanics
|
|||||||
}
|
}
|
||||||
// If the package has a wander distance but no pathgrid is available,
|
// If the package has a wander distance but no pathgrid is available,
|
||||||
// randomly idle or wander near spawn point
|
// randomly idle or wander near spawn point
|
||||||
else if (storage.mAllowedNodes.empty() && mDistance > 0 && !storage.mIsWanderingManually)
|
else if (storage.mAllowedPositions.empty() && mDistance > 0 && !storage.mIsWanderingManually)
|
||||||
{
|
{
|
||||||
// Typically want to idle for a short time before the next wander
|
// Typically want to idle for a short time before the next wander
|
||||||
if (Misc::Rng::rollDice(100, prng) >= 96)
|
if (Misc::Rng::rollDice(100, prng) >= 96)
|
||||||
@ -331,7 +332,7 @@ namespace MWMechanics
|
|||||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually)
|
else if (storage.mAllowedPositions.empty() && !storage.mIsWanderingManually)
|
||||||
{
|
{
|
||||||
storage.mCanWanderAlongPathGrid = false;
|
storage.mCanWanderAlongPathGrid = false;
|
||||||
}
|
}
|
||||||
@ -347,9 +348,9 @@ namespace MWMechanics
|
|||||||
// Construct a new path if there isn't one
|
// Construct a new path if there isn't one
|
||||||
if (!mPathFinder.isPathConstructed())
|
if (!mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
if (!storage.mAllowedNodes.empty())
|
if (!storage.mAllowedPositions.empty())
|
||||||
{
|
{
|
||||||
setPathToAnAllowedNode(actor, storage, pos);
|
setPathToAnAllowedPosition(actor, storage, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,17 +516,17 @@ namespace MWMechanics
|
|||||||
|
|
||||||
void AiWander::onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage)
|
void AiWander::onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage)
|
||||||
{
|
{
|
||||||
// Check if an idle actor is too far from all allowed nodes or too close to a door - if so start walking.
|
// Check if an idle actor is too far from all allowed positions or too close to a door - if so start walking.
|
||||||
storage.mCheckIdlePositionTimer += duration;
|
storage.mCheckIdlePositionTimer += duration;
|
||||||
|
|
||||||
if (storage.mCheckIdlePositionTimer >= idlePositionCheckInterval && !isStationary())
|
if (storage.mCheckIdlePositionTimer >= idlePositionCheckInterval && !isStationary())
|
||||||
{
|
{
|
||||||
storage.mCheckIdlePositionTimer = 0; // restart timer
|
storage.mCheckIdlePositionTimer = 0; // restart timer
|
||||||
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance() * 1.6f;
|
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance() * 1.6f;
|
||||||
if (proximityToDoor(actor, distance) || !isNearAllowedNode(actor, storage, distance))
|
if (proximityToDoor(actor, distance) || !isNearAllowedPosition(actor, storage, distance))
|
||||||
{
|
{
|
||||||
storage.setState(AiWanderStorage::Wander_MoveNow);
|
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||||
storage.mTrimCurrentNode = false; // just in case
|
storage.mTrimCurrentPosition = false; // just in case
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -541,16 +542,14 @@ namespace MWMechanics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AiWander::isNearAllowedNode(const MWWorld::Ptr& actor, const AiWanderStorage& storage, float distance) const
|
bool AiWander::isNearAllowedPosition(
|
||||||
|
const MWWorld::Ptr& actor, const AiWanderStorage& storage, float distance) const
|
||||||
{
|
{
|
||||||
const osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
const osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
||||||
for (const ESM::Pathgrid::Point& node : storage.mAllowedNodes)
|
const float squaredDistance = distance * distance;
|
||||||
{
|
return std::ranges::find_if(storage.mAllowedPositions, [&](const osg::Vec3& v) {
|
||||||
osg::Vec3f point(node.mX, node.mY, node.mZ);
|
return (actorPos - v).length2() < squaredDistance;
|
||||||
if ((actorPos - point).length2() < distance * distance)
|
}) != storage.mAllowedPositions.end();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration,
|
void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration,
|
||||||
@ -610,8 +609,8 @@ namespace MWMechanics
|
|||||||
if (proximityToDoor(actor, distance))
|
if (proximityToDoor(actor, distance))
|
||||||
{
|
{
|
||||||
// remove allowed points then select another random destination
|
// remove allowed points then select another random destination
|
||||||
storage.mTrimCurrentNode = true;
|
storage.mTrimCurrentPosition = true;
|
||||||
trimAllowedNodes(mPathFinder.getPath(), storage.mAllowedNodes);
|
trimAllowedPositions(mPathFinder.getPath(), storage.mAllowedPositions);
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
stopWalking(actor);
|
stopWalking(actor);
|
||||||
storage.setState(AiWanderStorage::Wander_MoveNow);
|
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||||
@ -630,42 +629,41 @@ namespace MWMechanics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::setPathToAnAllowedNode(
|
void AiWander::setPathToAnAllowedPosition(
|
||||||
const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)
|
const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)
|
||||||
{
|
{
|
||||||
auto world = MWBase::Environment::get().getWorld();
|
auto world = MWBase::Environment::get().getWorld();
|
||||||
auto& prng = world->getPrng();
|
auto& prng = world->getPrng();
|
||||||
unsigned int randNode = Misc::Rng::rollDice(storage.mAllowedNodes.size(), prng);
|
const std::size_t randomAllowedPositionIndex
|
||||||
const ESM::Pathgrid::Point& dest = storage.mAllowedNodes[randNode];
|
= static_cast<std::size_t>(Misc::Rng::rollDice(storage.mAllowedPositions.size(), prng));
|
||||||
|
const osg::Vec3f randomAllowedPosition = storage.mAllowedPositions[randomAllowedPositionIndex];
|
||||||
|
|
||||||
const osg::Vec3f start = actorPos.asVec3();
|
const osg::Vec3f start = actorPos.asVec3();
|
||||||
|
|
||||||
// don't take shortcuts for wandering
|
// don't take shortcuts for wandering
|
||||||
const ESM::Pathgrid* pathgrid = world->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->getCell());
|
const ESM::Pathgrid* pathgrid = world->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->getCell());
|
||||||
const osg::Vec3f destVec3f = PathFinder::makeOsgVec3(dest);
|
mPathFinder.buildPathByPathgrid(start, randomAllowedPosition, actor.getCell(), getPathGridGraph(pathgrid));
|
||||||
mPathFinder.buildPathByPathgrid(start, destVec3f, actor.getCell(), getPathGridGraph(pathgrid));
|
|
||||||
|
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
mDestination = destVec3f;
|
mDestination = randomAllowedPosition;
|
||||||
mHasDestination = true;
|
mHasDestination = true;
|
||||||
mUsePathgrid = true;
|
mUsePathgrid = true;
|
||||||
// Remove this node as an option and add back the previously used node (stops NPC from picking the same
|
// Remove this position as an option and add back the previously used position (stops NPC from picking the
|
||||||
// node):
|
// same position):
|
||||||
ESM::Pathgrid::Point temp = storage.mAllowedNodes[randNode];
|
storage.mAllowedPositions.erase(storage.mAllowedPositions.begin() + randomAllowedPositionIndex);
|
||||||
storage.mAllowedNodes.erase(storage.mAllowedNodes.begin() + randNode);
|
// check if mCurrentPosition was taken out of mAllowedPositions
|
||||||
// check if mCurrentNode was taken out of mAllowedNodes
|
if (storage.mTrimCurrentPosition && storage.mAllowedPositions.size() > 1)
|
||||||
if (storage.mTrimCurrentNode && storage.mAllowedNodes.size() > 1)
|
storage.mTrimCurrentPosition = false;
|
||||||
storage.mTrimCurrentNode = false;
|
|
||||||
else
|
else
|
||||||
storage.mAllowedNodes.push_back(storage.mCurrentNode);
|
storage.mAllowedPositions.push_back(storage.mCurrentPosition);
|
||||||
storage.mCurrentNode = temp;
|
storage.mCurrentPosition = randomAllowedPosition;
|
||||||
|
|
||||||
storage.setState(AiWanderStorage::Wander_Walking);
|
storage.setState(AiWanderStorage::Wander_Walking);
|
||||||
}
|
}
|
||||||
// Choose a different node and delete this one from possible nodes because it is uncreachable:
|
// Choose a different position and delete this one from possible positions because it is uncreachable:
|
||||||
else
|
else
|
||||||
storage.mAllowedNodes.erase(storage.mAllowedNodes.begin() + randNode);
|
storage.mAllowedPositions.erase(storage.mAllowedPositions.begin() + randomAllowedPositionIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::stopWalking(const MWWorld::Ptr& actor)
|
void AiWander::stopWalking(const MWWorld::Ptr& actor)
|
||||||
@ -741,20 +739,20 @@ namespace MWMechanics
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
AiWanderStorage& storage = state.get<AiWanderStorage>();
|
AiWanderStorage& storage = state.get<AiWanderStorage>();
|
||||||
if (storage.mPopulateAvailableNodes)
|
if (storage.mPopulateAvailablePositions)
|
||||||
getAllowedNodes(actor, storage);
|
fillAllowedPositions(actor, storage);
|
||||||
|
|
||||||
if (storage.mAllowedNodes.empty())
|
if (storage.mAllowedPositions.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
int index = Misc::Rng::rollDice(storage.mAllowedNodes.size(), prng);
|
int index = Misc::Rng::rollDice(storage.mAllowedPositions.size(), prng);
|
||||||
ESM::Pathgrid::Point worldDest = storage.mAllowedNodes[index];
|
const osg::Vec3f worldDest = storage.mAllowedPositions[index];
|
||||||
const Misc::CoordinateConverter converter = Misc::makeCoordinateConverter(*actor.getCell()->getCell());
|
const Misc::CoordinateConverter converter = Misc::makeCoordinateConverter(*actor.getCell()->getCell());
|
||||||
ESM::Pathgrid::Point dest = converter.toLocalPoint(worldDest);
|
osg::Vec3f dest = converter.toLocalVec3(worldDest);
|
||||||
|
|
||||||
bool isPathGridOccupied = MWBase::Environment::get().getMechanicsManager()->isAnyActorInRange(
|
const bool isPathGridOccupied
|
||||||
PathFinder::makeOsgVec3(worldDest), 60);
|
= MWBase::Environment::get().getMechanicsManager()->isAnyActorInRange(worldDest, 60);
|
||||||
|
|
||||||
// add offset only if the selected pathgrid is occupied by another actor
|
// add offset only if the selected pathgrid is occupied by another actor
|
||||||
if (isPathGridOccupied)
|
if (isPathGridOccupied)
|
||||||
@ -774,19 +772,17 @@ namespace MWMechanics
|
|||||||
const ESM::Pathgrid::Point& connDest = points[randomIndex];
|
const ESM::Pathgrid::Point& connDest = points[randomIndex];
|
||||||
|
|
||||||
// add an offset towards random neighboring node
|
// add an offset towards random neighboring node
|
||||||
osg::Vec3f dir = PathFinder::makeOsgVec3(connDest) - PathFinder::makeOsgVec3(dest);
|
osg::Vec3f dir = PathFinder::makeOsgVec3(connDest) - dest;
|
||||||
float length = dir.length();
|
const float length = dir.length();
|
||||||
dir.normalize();
|
dir.normalize();
|
||||||
|
|
||||||
for (int j = 1; j <= 3; j++)
|
for (int j = 1; j <= 3; j++)
|
||||||
{
|
{
|
||||||
// move for 5-15% towards random neighboring node
|
// move for 5-15% towards random neighboring node
|
||||||
dest
|
dest = dest + dir * (j * 5 * length / 100.f);
|
||||||
= PathFinder::makePathgridPoint(PathFinder::makeOsgVec3(dest) + dir * (j * 5 * length / 100.f));
|
|
||||||
worldDest = converter.toWorldPoint(dest);
|
|
||||||
|
|
||||||
isOccupied = MWBase::Environment::get().getMechanicsManager()->isAnyActorInRange(
|
isOccupied = MWBase::Environment::get().getMechanicsManager()->isAnyActorInRange(
|
||||||
PathFinder::makeOsgVec3(worldDest), 60);
|
converter.toWorldVec3(dest), 60);
|
||||||
|
|
||||||
if (!isOccupied)
|
if (!isOccupied)
|
||||||
break;
|
break;
|
||||||
@ -806,19 +802,18 @@ namespace MWMechanics
|
|||||||
|
|
||||||
// place above to prevent moving inside objects, e.g. stairs, because a vector between pathgrids can be
|
// place above to prevent moving inside objects, e.g. stairs, because a vector between pathgrids can be
|
||||||
// underground. Adding 20 in adjustPosition() is not enough.
|
// underground. Adding 20 in adjustPosition() is not enough.
|
||||||
dest.mZ += 60;
|
dest.z() += 60;
|
||||||
|
|
||||||
converter.toWorld(dest);
|
converter.toWorld(dest);
|
||||||
|
|
||||||
state.reset();
|
state.reset();
|
||||||
|
|
||||||
osg::Vec3f pos(static_cast<float>(dest.mX), static_cast<float>(dest.mY), static_cast<float>(dest.mZ));
|
MWBase::Environment::get().getWorld()->moveObject(actor, dest);
|
||||||
MWBase::Environment::get().getWorld()->moveObject(actor, pos);
|
|
||||||
actor.getClass().adjustPosition(actor, false);
|
actor.getClass().adjustPosition(actor, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::getNeighbouringNodes(
|
void AiWander::getNeighbouringNodes(
|
||||||
ESM::Pathgrid::Point dest, const MWWorld::CellStore* currentCell, ESM::Pathgrid::PointList& points)
|
const osg::Vec3f& dest, const MWWorld::CellStore* currentCell, ESM::Pathgrid::PointList& points)
|
||||||
{
|
{
|
||||||
const ESM::Pathgrid* pathgrid
|
const ESM::Pathgrid* pathgrid
|
||||||
= MWBase::Environment::get().getESMStore()->get<ESM::Pathgrid>().search(*currentCell->getCell());
|
= MWBase::Environment::get().getESMStore()->get<ESM::Pathgrid>().search(*currentCell->getCell());
|
||||||
@ -826,19 +821,19 @@ namespace MWMechanics
|
|||||||
if (pathgrid == nullptr || pathgrid->mPoints.empty())
|
if (pathgrid == nullptr || pathgrid->mPoints.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size_t index = PathFinder::getClosestPoint(pathgrid, PathFinder::makeOsgVec3(dest));
|
const size_t index = PathFinder::getClosestPoint(pathgrid, dest);
|
||||||
|
|
||||||
getPathGridGraph(pathgrid).getNeighbouringPoints(index, points);
|
getPathGridGraph(pathgrid).getNeighbouringPoints(index, points);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
void AiWander::fillAllowedPositions(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
||||||
{
|
{
|
||||||
// infrequently used, therefore no benefit in caching it as a member
|
// infrequently used, therefore no benefit in caching it as a member
|
||||||
const MWWorld::CellStore* cellStore = actor.getCell();
|
const MWWorld::CellStore* cellStore = actor.getCell();
|
||||||
const ESM::Pathgrid* pathgrid
|
const ESM::Pathgrid* pathgrid
|
||||||
= MWBase::Environment::get().getESMStore()->get<ESM::Pathgrid>().search(*cellStore->getCell());
|
= MWBase::Environment::get().getESMStore()->get<ESM::Pathgrid>().search(*cellStore->getCell());
|
||||||
|
|
||||||
storage.mAllowedNodes.clear();
|
storage.mAllowedPositions.clear();
|
||||||
|
|
||||||
// If there is no path this actor doesn't go anywhere. See:
|
// If there is no path this actor doesn't go anywhere. See:
|
||||||
// https://forum.openmw.org/viewtopic.php?t=1556
|
// https://forum.openmw.org/viewtopic.php?t=1556
|
||||||
@ -861,32 +856,33 @@ namespace MWMechanics
|
|||||||
// Find closest pathgrid point
|
// Find closest pathgrid point
|
||||||
size_t closestPointIndex = PathFinder::getClosestPoint(pathgrid, npcPos);
|
size_t closestPointIndex = PathFinder::getClosestPoint(pathgrid, npcPos);
|
||||||
|
|
||||||
// mAllowedNodes for this actor with pathgrid point indexes based on mDistance
|
// mAllowedPositions for this actor with pathgrid point indexes based on mDistance
|
||||||
// and if the point is connected to the closest current point
|
// and if the point is connected to the closest current point
|
||||||
// NOTE: mPoints is in local coordinates
|
// NOTE: mPoints is in local coordinates
|
||||||
size_t pointIndex = 0;
|
size_t pointIndex = 0;
|
||||||
for (size_t counter = 0; counter < pathgrid->mPoints.size(); counter++)
|
for (size_t counter = 0; counter < pathgrid->mPoints.size(); counter++)
|
||||||
{
|
{
|
||||||
osg::Vec3f nodePos(PathFinder::makeOsgVec3(pathgrid->mPoints[counter]));
|
const osg::Vec3f nodePos = PathFinder::makeOsgVec3(pathgrid->mPoints[counter]);
|
||||||
if ((npcPos - nodePos).length2() <= mDistance * mDistance
|
if ((npcPos - nodePos).length2() <= mDistance * mDistance
|
||||||
&& getPathGridGraph(pathgrid).isPointConnected(closestPointIndex, counter))
|
&& getPathGridGraph(pathgrid).isPointConnected(closestPointIndex, counter))
|
||||||
{
|
{
|
||||||
storage.mAllowedNodes.push_back(converter.toWorldPoint(pathgrid->mPoints[counter]));
|
storage.mAllowedPositions.push_back(
|
||||||
|
Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid->mPoints[counter])));
|
||||||
pointIndex = counter;
|
pointIndex = counter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (storage.mAllowedNodes.size() == 1)
|
if (storage.mAllowedPositions.size() == 1)
|
||||||
{
|
{
|
||||||
storage.mAllowedNodes.push_back(PathFinder::makePathgridPoint(mInitialActorPosition));
|
storage.mAllowedPositions.push_back(mInitialActorPosition);
|
||||||
addNonPathGridAllowedPoints(pathgrid, pointIndex, storage, converter);
|
addNonPathGridAllowedPoints(pathgrid, pointIndex, storage, converter);
|
||||||
}
|
}
|
||||||
if (!storage.mAllowedNodes.empty())
|
if (!storage.mAllowedPositions.empty())
|
||||||
{
|
{
|
||||||
setCurrentNodeToClosestAllowedNode(storage);
|
setCurrentPositionToClosestAllowedPosition(storage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.mPopulateAvailableNodes = false;
|
storage.mPopulateAvailablePositions = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When only one path grid point in wander distance,
|
// When only one path grid point in wander distance,
|
||||||
@ -900,13 +896,13 @@ namespace MWMechanics
|
|||||||
{
|
{
|
||||||
if (edge.mV0 == pointIndex)
|
if (edge.mV0 == pointIndex)
|
||||||
{
|
{
|
||||||
AddPointBetweenPathGridPoints(converter.toWorldPoint(pathGrid->mPoints[edge.mV0]),
|
addPositionBetweenPathgridPoints(converter.toWorldPoint(pathGrid->mPoints[edge.mV0]),
|
||||||
converter.toWorldPoint(pathGrid->mPoints[edge.mV1]), storage);
|
converter.toWorldPoint(pathGrid->mPoints[edge.mV1]), storage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::AddPointBetweenPathGridPoints(
|
void AiWander::addPositionBetweenPathgridPoints(
|
||||||
const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end, AiWanderStorage& storage)
|
const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end, AiWanderStorage& storage)
|
||||||
{
|
{
|
||||||
osg::Vec3f vectorStart = PathFinder::makeOsgVec3(start);
|
osg::Vec3f vectorStart = PathFinder::makeOsgVec3(start);
|
||||||
@ -919,25 +915,25 @@ namespace MWMechanics
|
|||||||
// must not travel longer than distance between waypoints or NPC goes past waypoint
|
// must not travel longer than distance between waypoints or NPC goes past waypoint
|
||||||
distance = std::min(distance, static_cast<unsigned>(length));
|
distance = std::min(distance, static_cast<unsigned>(length));
|
||||||
delta *= distance;
|
delta *= distance;
|
||||||
storage.mAllowedNodes.push_back(PathFinder::makePathgridPoint(vectorStart + delta));
|
storage.mAllowedPositions.push_back(vectorStart + delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::setCurrentNodeToClosestAllowedNode(AiWanderStorage& storage)
|
void AiWander::setCurrentPositionToClosestAllowedPosition(AiWanderStorage& storage)
|
||||||
{
|
{
|
||||||
float distanceToClosestNode = std::numeric_limits<float>::max();
|
float distanceToClosestPosition = std::numeric_limits<float>::max();
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
for (size_t i = 0; i < storage.mAllowedNodes.size(); ++i)
|
for (size_t i = 0; i < storage.mAllowedPositions.size(); ++i)
|
||||||
{
|
{
|
||||||
osg::Vec3f nodePos(PathFinder::makeOsgVec3(storage.mAllowedNodes[i]));
|
const osg::Vec3f position = storage.mAllowedPositions[i];
|
||||||
float tempDist = (mInitialActorPosition - nodePos).length2();
|
const float tempDist = (mInitialActorPosition - position).length2();
|
||||||
if (tempDist < distanceToClosestNode)
|
if (tempDist < distanceToClosestPosition)
|
||||||
{
|
{
|
||||||
index = i;
|
index = i;
|
||||||
distanceToClosestNode = tempDist;
|
distanceToClosestPosition = tempDist;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
storage.mCurrentNode = storage.mAllowedNodes[index];
|
storage.mCurrentPosition = storage.mAllowedPositions[index];
|
||||||
storage.mAllowedNodes.erase(storage.mAllowedNodes.begin() + index);
|
storage.mAllowedPositions.erase(storage.mAllowedPositions.begin() + index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::writeState(ESM::AiSequence::AiSequence& sequence) const
|
void AiWander::writeState(ESM::AiSequence::AiSequence& sequence) const
|
||||||
|
@ -51,14 +51,13 @@ namespace MWMechanics
|
|||||||
unsigned short mIdleAnimation;
|
unsigned short mIdleAnimation;
|
||||||
std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors
|
std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors
|
||||||
|
|
||||||
// do we need to calculate allowed nodes based on mDistance
|
bool mPopulateAvailablePositions;
|
||||||
bool mPopulateAvailableNodes;
|
|
||||||
|
|
||||||
// allowed pathgrid nodes based on mDistance from the spawn point
|
// allowed destination positions based on mDistance from the spawn point
|
||||||
std::vector<ESM::Pathgrid::Point> mAllowedNodes;
|
std::vector<osg::Vec3f> mAllowedPositions;
|
||||||
|
|
||||||
ESM::Pathgrid::Point mCurrentNode;
|
osg::Vec3f mCurrentPosition;
|
||||||
bool mTrimCurrentNode;
|
bool mTrimCurrentPosition;
|
||||||
|
|
||||||
float mCheckIdlePositionTimer;
|
float mCheckIdlePositionTimer;
|
||||||
int mStuckCount;
|
int mStuckCount;
|
||||||
@ -132,7 +131,8 @@ namespace MWMechanics
|
|||||||
bool playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
bool playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||||
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||||
int getRandomIdle() const;
|
int getRandomIdle() const;
|
||||||
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
void setPathToAnAllowedPosition(
|
||||||
|
const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
||||||
void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||||
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration,
|
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration,
|
||||||
MWWorld::MovementDirectionFlags supportedMovementDirections, AiWanderStorage& storage);
|
MWWorld::MovementDirectionFlags supportedMovementDirections, AiWanderStorage& storage);
|
||||||
@ -145,26 +145,27 @@ namespace MWMechanics
|
|||||||
void wanderNearStart(const MWWorld::Ptr& actor, AiWanderStorage& storage, int wanderDistance);
|
void wanderNearStart(const MWWorld::Ptr& actor, AiWanderStorage& storage, int wanderDistance);
|
||||||
bool destinationIsAtWater(const MWWorld::Ptr& actor, const osg::Vec3f& destination);
|
bool destinationIsAtWater(const MWWorld::Ptr& actor, const osg::Vec3f& destination);
|
||||||
void completeManualWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
void completeManualWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||||
bool isNearAllowedNode(const MWWorld::Ptr& actor, const AiWanderStorage& storage, float distance) const;
|
bool isNearAllowedPosition(const MWWorld::Ptr& actor, const AiWanderStorage& storage, float distance) const;
|
||||||
|
|
||||||
const unsigned mDistance; // how far the actor can wander from the spawn point
|
// how far the actor can wander from the spawn point
|
||||||
|
const unsigned mDistance;
|
||||||
const unsigned mDuration;
|
const unsigned mDuration;
|
||||||
float mRemainingDuration;
|
float mRemainingDuration;
|
||||||
const int mTimeOfDay;
|
const int mTimeOfDay;
|
||||||
const std::vector<unsigned char> mIdle;
|
const std::vector<unsigned char> mIdle;
|
||||||
|
|
||||||
bool mStoredInitialActorPosition;
|
bool mStoredInitialActorPosition;
|
||||||
osg::Vec3f
|
// Note: an original engine does not reset coordinates even when actor changes a cell
|
||||||
mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell
|
osg::Vec3f mInitialActorPosition;
|
||||||
|
|
||||||
bool mHasDestination;
|
bool mHasDestination;
|
||||||
osg::Vec3f mDestination;
|
osg::Vec3f mDestination;
|
||||||
bool mUsePathgrid;
|
bool mUsePathgrid;
|
||||||
|
|
||||||
void getNeighbouringNodes(
|
void getNeighbouringNodes(
|
||||||
ESM::Pathgrid::Point dest, const MWWorld::CellStore* currentCell, ESM::Pathgrid::PointList& points);
|
const osg::Vec3f& dest, const MWWorld::CellStore* currentCell, ESM::Pathgrid::PointList& points);
|
||||||
|
|
||||||
void getAllowedNodes(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
void fillAllowedPositions(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||||
|
|
||||||
// constants for converting idleSelect values into groupNames
|
// constants for converting idleSelect values into groupNames
|
||||||
enum GroupIndex
|
enum GroupIndex
|
||||||
@ -173,12 +174,12 @@ namespace MWMechanics
|
|||||||
GroupIndex_MaxIdle = 9
|
GroupIndex_MaxIdle = 9
|
||||||
};
|
};
|
||||||
|
|
||||||
void setCurrentNodeToClosestAllowedNode(AiWanderStorage& storage);
|
void setCurrentPositionToClosestAllowedPosition(AiWanderStorage& storage);
|
||||||
|
|
||||||
void addNonPathGridAllowedPoints(const ESM::Pathgrid* pathGrid, size_t pointIndex, AiWanderStorage& storage,
|
void addNonPathGridAllowedPoints(const ESM::Pathgrid* pathGrid, size_t pointIndex, AiWanderStorage& storage,
|
||||||
const Misc::CoordinateConverter& converter);
|
const Misc::CoordinateConverter& converter);
|
||||||
|
|
||||||
void AddPointBetweenPathGridPoints(
|
void addPositionBetweenPathgridPoints(
|
||||||
const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end, AiWanderStorage& storage);
|
const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end, AiWanderStorage& storage);
|
||||||
|
|
||||||
/// lookup table for converting idleSelect value to groupName
|
/// lookup table for converting idleSelect value to groupName
|
||||||
|
@ -145,19 +145,6 @@ namespace MWMechanics
|
|||||||
mPath.push_back(point);
|
mPath.push_back(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// utility function to convert a osg::Vec3f to a Pathgrid::Point
|
|
||||||
static ESM::Pathgrid::Point makePathgridPoint(const osg::Vec3f& v)
|
|
||||||
{
|
|
||||||
return ESM::Pathgrid::Point(static_cast<int>(v[0]), static_cast<int>(v[1]), static_cast<int>(v[2]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// utility function to convert an ESM::Position to a Pathgrid::Point
|
|
||||||
static ESM::Pathgrid::Point makePathgridPoint(const ESM::Position& p)
|
|
||||||
{
|
|
||||||
return ESM::Pathgrid::Point(
|
|
||||||
static_cast<int>(p.pos[0]), static_cast<int>(p.pos[1]), static_cast<int>(p.pos[2]));
|
|
||||||
}
|
|
||||||
|
|
||||||
static osg::Vec3f makeOsgVec3(const ESM::Pathgrid::Point& p)
|
static osg::Vec3f makeOsgVec3(const ESM::Pathgrid::Point& p)
|
||||||
{
|
{
|
||||||
return osg::Vec3f(static_cast<float>(p.mX), static_cast<float>(p.mY), static_cast<float>(p.mZ));
|
return osg::Vec3f(static_cast<float>(p.mX), static_cast<float>(p.mY), static_cast<float>(p.mZ));
|
||||||
|
@ -59,10 +59,18 @@ namespace Misc
|
|||||||
point.y() -= static_cast<float>(mCellY);
|
point.y() -= static_cast<float>(mCellY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::Vec3f toWorldVec3(const osg::Vec3f& point) const
|
||||||
|
{
|
||||||
|
osg::Vec3f result = point;
|
||||||
|
toWorld(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
osg::Vec3f toLocalVec3(const osg::Vec3f& point) const
|
osg::Vec3f toLocalVec3(const osg::Vec3f& point) const
|
||||||
{
|
{
|
||||||
return osg::Vec3f(
|
osg::Vec3f result = point;
|
||||||
point.x() - static_cast<float>(mCellX), point.y() - static_cast<float>(mCellY), point.z());
|
toLocal(result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user