CNavArea::ComputeApproachAreas(): fix hang on *.nav generating (#913)

This commit is contained in:
Sergey Shorokhov 2024-05-12 17:17:02 +03:00 committed by GitHub
parent 9f66264562
commit c08e6d0180
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -4431,6 +4431,7 @@ inline bool IsAreaVisible(const Vector *pos, const CNavArea *area)
// Determine the set of "approach areas". // Determine the set of "approach areas".
// An approach area is an area representing a place where players // An approach area is an area representing a place where players
// move into/out of our local neighborhood of areas. // move into/out of our local neighborhood of areas.
// @todo Optimize by search from eye outward and modifying pathfinder to treat all links as bi-directional
void CNavArea::ComputeApproachAreas() void CNavArea::ComputeApproachAreas()
{ {
m_approachCount = 0; m_approachCount = 0;
@ -4452,6 +4453,18 @@ void CNavArea::ComputeApproachAreas()
enum { MAX_PATH_LENGTH = 256 }; enum { MAX_PATH_LENGTH = 256 };
CNavArea *path[MAX_PATH_LENGTH]; CNavArea *path[MAX_PATH_LENGTH];
enum SearchType
{
FROM_EYE, ///< start search from our eyepoint outward to farArea
TO_EYE, ///< start search from farArea beack towards our eye
SEARCH_FINISHED
};
// In order to *completely* enumerate all of the approach areas, we
// need to search from our eyepoint outward, as well as from outwards
// towards our eyepoint
for (int searchType = FROM_EYE; searchType != SEARCH_FINISHED; searchType++)
{
// In order to enumerate all of the approach areas, we need to // In order to enumerate all of the approach areas, we need to
// run the algorithm many times, once for each "far away" area // run the algorithm many times, once for each "far away" area
// and keep the union of the approach area sets // and keep the union of the approach area sets
@ -4463,10 +4476,7 @@ void CNavArea::ComputeApproachAreas()
if (IsAreaVisible(&eye, farArea)) if (IsAreaVisible(&eye, farArea))
continue; continue;
// make first path to far away area
ApproachAreaCost cost; ApproachAreaCost cost;
if (NavAreaBuildPath(this, farArea, nullptr, cost) == false)
continue;
// //
// Keep building paths to farArea and blocking them off until we // Keep building paths to farArea and blocking them off until we
@ -4475,20 +4485,55 @@ void CNavArea::ComputeApproachAreas()
// //
while (m_approachCount < MAX_APPROACH_AREAS) while (m_approachCount < MAX_APPROACH_AREAS)
{ {
CNavArea *from, *to;
if (searchType == FROM_EYE)
{
// find another path *to* 'farArea'
// we must pathfind from us in order to pick up one-way paths OUT OF our area
from = this;
to = farArea;
}
else // TO_EYE
{
// find another path *from* 'farArea'
// we must pathfind to us in order to pick up one-way paths INTO our area
from = farArea;
to = this;
}
// build the actual path
if (NavAreaBuildPath(from, to, NULL, cost) == false)
break;
// find number of areas on path // find number of areas on path
int count = 0; int count = 0;
CNavArea *area; CNavArea *area;
for (area = farArea; area; area = area->GetParent()) for (area = to; area; area = area->GetParent())
count++; count++;
if (count > MAX_PATH_LENGTH) if (count > MAX_PATH_LENGTH)
count = MAX_PATH_LENGTH; count = MAX_PATH_LENGTH;
// build path in correct order - from eye outwards // if the path is only two areas long, there can be no approach points
int i = count; if (count <= 2)
for (area = farArea; i && area; area = area->GetParent()) break;
// build path starting from eye
int i = 0;
if (searchType == FROM_EYE)
{ {
path[--i] = area; for(area = to; i < count && area; area = area->GetParent())
{
path[count - i - 1] = area;
++i;
}
}
else // TO_EYE
{
for(area = to; i < count && area; area = area->GetParent())
path[i++] = area;
} }
// traverse path to find first area we cannot see (skip the first area) // traverse path to find first area we cannot see (skip the first area)
@ -4510,15 +4555,15 @@ void CNavArea::ComputeApproachAreas()
// (blocking farArea will cause all subsequent pathfinds to fail) // (blocking farArea will cause all subsequent pathfinds to fail)
int block = (path[i] == farArea) ? i - 1 : i; int block = (path[i] == farArea) ? i - 1 : i;
BlockedID[BlockedIDCount++] = path[block]->GetID();
if (block == 0) if (block == 0)
break; continue;
BlockedID[BlockedIDCount++] = path[block]->GetID();
// store new approach area if not already in set // store new approach area if not already in set
int a; int a;
for (a = 0; a < m_approachCount; a++) for (a = 0; a < m_approachCount; a++)
if (m_approach[a].here.area == path[block - 1]) if (m_approach[a].here.area == path[block-1])
break; break;
if (a == m_approachCount) if (a == m_approachCount)
@ -4534,13 +4579,6 @@ void CNavArea::ComputeApproachAreas()
// we are done with this path // we are done with this path
break; break;
} }
// find another path to 'farArea'
ApproachAreaCost cost;
if (NavAreaBuildPath(this, farArea, nullptr, cost) == false)
{
// can't find a path to 'farArea' means all exits have been already tested and blocked
break;
} }
} }
} }