From aad13d315c4a19d8dc55be4ca4f7f1483373ee18 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 18 Apr 2014 14:25:08 +1000 Subject: [PATCH] Fixed issue where allowed nodes were being erased. PathFinder was returning an empty path if the closest pathgrid point to the start was also the closest pathgrid point to the goal. Still need to clean up and remove logging statements. --- apps/openmw/mwmechanics/aiwander.cpp | 101 ++++++++++++++---------- apps/openmw/mwmechanics/pathfinding.cpp | 42 +++++++++- apps/openmw/mwmechanics/pathgrid.cpp | 14 ++++ 3 files changed, 115 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 029be2fb8..c25821b4e 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -112,11 +112,13 @@ namespace MWMechanics * allowed set. The issue is when the door opens the allowed set is not * re-calculated. Normally this would not be an issue since hostile actors will * enter combat (i.e. no longer wandering) - * - * FIXME: Sometimes allowed nodes that shouldn't be deleted are deleted. */ bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { + MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor); + if(cStats.isDead() || cStats.getHealth().getCurrent() <= 0) + return true; // Don't bother with dead actors + bool cellChange = mCell && (actor.getCell() != mCell); if(!mCell || cellChange) { @@ -125,7 +127,6 @@ namespace MWMechanics } const ESM::Cell *cell = mCell->getCell(); - MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor); cStats.setDrawState(DrawState_Nothing); cStats.setMovementFlag(CreatureStats::Flag_Run, false); MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -188,45 +189,56 @@ namespace MWMechanics mYCell = mCellY * ESM::Land::REAL_SIZE; } - // convert actorPos to local (i.e. cell) co-ordinates - Ogre::Vector3 actorPos(pos.pos); - actorPos[0] = actorPos[0] - mXCell; - actorPos[1] = actorPos[1] - mYCell; - - // mAllowedNodes for this actor with pathgrid point indexes - // based on mDistance + // FIXME: There might be a bug here. The allowed node points are + // based on the actor's current position rather than the actor's + // spawn point. As a result the allowed nodes for wander can change + // between saves, for example. + // + // convert npcPos to local (i.e. cell) co-ordinates + Ogre::Vector3 npcPos(pos.pos); + npcPos[0] = npcPos[0] - mXCell; + npcPos[1] = npcPos[1] - mYCell; + + // mAllowedNodes for this actor with pathgrid point indexes based on mDistance // NOTE: mPoints and mAllowedNodes are in local co-ordinates - float closestNodeDist = -1; - unsigned int closestIndex = 0; - unsigned int indexAllowedNodes = 0; for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) { - float sqrDist = actorPos.squaredDistance(Ogre::Vector3( - pathgrid->mPoints[counter].mX, - pathgrid->mPoints[counter].mY, - pathgrid->mPoints[counter].mZ)); - if(sqrDist <= (mDistance * mDistance)) - { + Ogre::Vector3 nodePos(pathgrid->mPoints[counter].mX, pathgrid->mPoints[counter].mY, + pathgrid->mPoints[counter].mZ); + if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) mAllowedNodes.push_back(pathgrid->mPoints[counter]); - // keep track of the closest node - if(closestNodeDist == -1 || sqrDist < closestNodeDist) - { - closestNodeDist = sqrDist; - closestIndex = indexAllowedNodes; - } - indexAllowedNodes++; - } } if(!mAllowedNodes.empty()) { - // Start with the closest node and remove it from the allowed set - // so that it does not get selected again. The removed node will - // later be put in the back of the queue, unless it gets removed - // due to inaccessibility (e.g. a closed door) - mCurrentNode = mAllowedNodes[closestIndex]; - mAllowedNodes.erase(mAllowedNodes.begin() + closestIndex); - // set only if successful in finding allowed nodes - mStoredAvailableNodes = true; + Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ); + float closestNode = npcPos.squaredDistance(firstNodePos); + unsigned int index = 0; + for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++) + { + Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, + mAllowedNodes[counterThree].mZ); + float tempDist = npcPos.squaredDistance(nodePos); + if(tempDist < closestNode) + index = counterThree; + } +#if 0 + if(actor.getClass().getName(actor) == "Rat") + { + std::cout << "rat allowed "<< std::to_string(mAllowedNodes.size()) + +" mDist "+std::to_string(mDistance) + +" pos "+std::to_string(static_cast(npcPos[0])) + +", "+std::to_string(static_cast(npcPos[1])) + < 1) { mTrimCurrentNode = false; -#if 0 +//#if 0 std::cout << "deleted "<< std::to_string(mCurrentNode.mX) +", "+std::to_string(mCurrentNode.mY) << std::endl; -#endif +//#endif +//#if 0 + std::cout << "allowed size "<< + std::to_string(mAllowedNodes.size()) << std::endl; +//#endif } else mAllowedNodes.push_back(mCurrentNode); @@ -412,7 +428,15 @@ namespace MWMechanics } // Choose a different node and delete this one from possible nodes because it is uncreachable: else + { mAllowedNodes.erase(mAllowedNodes.begin() + randNode); +//#if 0 + //std::cout << "actor \""<< actor.getClass().getName(actor) << "\"" << std::endl; + if(actor.getClass().getName(actor) == "Rat") + std::cout << "erase no path "<< std::to_string(mAllowedNodes[randNode].mX) + +", "+std::to_string(mAllowedNodes[randNode].mY) << std::endl; +//#endif + } } } @@ -477,9 +501,6 @@ namespace MWMechanics void AiWander::trimAllowedNodes(std::vector& nodes, const PathFinder& pathfinder) { -//#if 0 - std::cout << "allowed size "<< std::to_string(nodes.size()) << std::endl; -//#endif // TODO: how to add these back in once the door opens? std::list paths = pathfinder.getPath(); while(paths.size() >= 2) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 730b8cb92..3a67a9b72 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -100,8 +100,11 @@ namespace } } } - if(start == closestReachableIndex) - closestReachableIndex = -1; // couldn't find anyting other than start + // AiWander has logic that depends on whether a path was created, deleting + // allowed nodes if not. Hence a path needs to be created even if the start + // and the end points are the same. + //if(start == closestReachableIndex) + //closestReachableIndex = -1; // couldn't find anyting other than start return std::pair (closestReachableIndex, closestReachableIndex == closestIndex); @@ -224,6 +227,18 @@ namespace MWMechanics // this shouldn't really happen, but just in case if(endNode.first != -1) { + // AiWander has logic that depends on whether a path was created, + // deleting allowed nodes if not. Hence a path needs to be created + // even if the start and the end points are the same. + // NOTE: aStarSearch will return an empty path if the start and end + // nodes are the same + if(startNode == endNode.first) + { + mPath.push_back(endPoint); + mIsPathConstructed = true; + return; + } + mPath = mCell->aStarSearch(startNode, endNode.first); if(!mPath.empty()) @@ -243,9 +258,32 @@ namespace MWMechanics } else mIsPathConstructed = false; +#if 0 + { + mIsPathConstructed = false; + std::cout << "no path "<< + std::to_string(startPoint.mX-xCell) + +", "+std::to_string(startPoint.mY-yCell) + +", "+std::to_string(startNode) + +", "+std::to_string(endPoint.mX-xCell) + +", "+std::to_string(endPoint.mY-yCell) + +", "+std::to_string(endNode.first)<mName == "Gnisis, Arvs-Drelen") + for(unsigned int v = 0; v < mPathgrid->mPoints.size(); v++) + { + std::cout << "SCC \"X:" << + std::to_string(mPathgrid->mPoints[v].mX) + +", Y:"+std::to_string(mPathgrid->mPoints[v].mY) + +", Num:"+std::to_string(mSCCId) + +", Point:"+std::to_string(v) + +", Group:"+std::to_string(mGraph[v].componentId) + <<"\""<