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,95 +4453,132 @@ void CNavArea::ComputeApproachAreas()
enum { MAX_PATH_LENGTH = 256 }; enum { MAX_PATH_LENGTH = 256 };
CNavArea *path[MAX_PATH_LENGTH]; CNavArea *path[MAX_PATH_LENGTH];
// In order to enumerate all of the approach areas, we need to enum SearchType
// run the algorithm many times, once for each "far away" area
// and keep the union of the approach area sets
for (auto farArea : goodSizedAreaList)
{ {
BlockedIDCount = 0; FROM_EYE, ///< start search from our eyepoint outward to farArea
TO_EYE, ///< start search from farArea beack towards our eye
SEARCH_FINISHED
};
// if we can see 'farArea', try again - the whole point is to go "around the bend", so to speak // In order to *completely* enumerate all of the approach areas, we
if (IsAreaVisible(&eye, farArea)) // need to search from our eyepoint outward, as well as from outwards
continue; // towards our eyepoint
for (int searchType = FROM_EYE; searchType != SEARCH_FINISHED; searchType++)
// make first path to far away area {
ApproachAreaCost cost; // In order to enumerate all of the approach areas, we need to
if (NavAreaBuildPath(this, farArea, nullptr, cost) == false) // run the algorithm many times, once for each "far away" area
continue; // and keep the union of the approach area sets
for (auto farArea : goodSizedAreaList)
//
// Keep building paths to farArea and blocking them off until we
// cant path there any more.
// As areas are blocked off, all exits will be enumerated.
//
while (m_approachCount < MAX_APPROACH_AREAS)
{ {
// find number of areas on path BlockedIDCount = 0;
int count = 0;
CNavArea *area;
for (area = farArea; area; area = area->GetParent())
count++;
if (count > MAX_PATH_LENGTH) // if we can see 'farArea', try again - the whole point is to go "around the bend", so to speak
count = MAX_PATH_LENGTH; if (IsAreaVisible(&eye, farArea))
continue;
// build path in correct order - from eye outwards ApproachAreaCost cost;
int i = count;
for (area = farArea; i && area; area = area->GetParent()) //
// Keep building paths to farArea and blocking them off until we
// cant path there any more.
// As areas are blocked off, all exits will be enumerated.
//
while (m_approachCount < MAX_APPROACH_AREAS)
{ {
path[--i] = area; CNavArea *from, *to;
}
// traverse path to find first area we cannot see (skip the first area) if (searchType == FROM_EYE)
for (i = 1; i < count; i++)
{
// if we see this area, continue on
if (IsAreaVisible(&eye, path[i]))
continue;
// we can't see this area.
// mark this area as "blocked" and unusable by subsequent approach paths
if (BlockedIDCount == MAX_BLOCKED_AREAS)
{ {
CONSOLE_ECHO("Overflow computing approach areas for area #%d.\n", m_id); // find another path *to* 'farArea'
return; // 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;
} }
// if the area to be blocked is actually farArea, block the one just prior // build the actual path
// (blocking farArea will cause all subsequent pathfinds to fail) if (NavAreaBuildPath(from, to, NULL, cost) == false)
int block = (path[i] == farArea) ? i - 1 : i;
BlockedID[BlockedIDCount++] = path[block]->GetID();
if (block == 0)
break; break;
// store new approach area if not already in set // find number of areas on path
int a; int count = 0;
for (a = 0; a < m_approachCount; a++) CNavArea *area;
if (m_approach[a].here.area == path[block - 1]) for (area = to; area; area = area->GetParent())
break; count++;
if (a == m_approachCount) if (count > MAX_PATH_LENGTH)
count = MAX_PATH_LENGTH;
// if the path is only two areas long, there can be no approach points
if (count <= 2)
break;
// build path starting from eye
int i = 0;
if (searchType == FROM_EYE)
{ {
m_approach[m_approachCount].prev.area = (block >= 2) ? path[block-2] : nullptr; for(area = to; i < count && area; area = area->GetParent())
m_approach[m_approachCount].here.area = path[block - 1]; {
m_approach[m_approachCount].prevToHereHow = path[block - 1]->GetParentHow(); path[count - i - 1] = area;
m_approach[m_approachCount].next.area = path[block]; ++i;
m_approach[m_approachCount].hereToNextHow = path[block]->GetParentHow(); }
m_approachCount++; }
else // TO_EYE
{
for(area = to; i < count && area; area = area->GetParent())
path[i++] = area;
} }
// we are done with this path // traverse path to find first area we cannot see (skip the first area)
break; for (i = 1; i < count; i++)
} {
// if we see this area, continue on
if (IsAreaVisible(&eye, path[i]))
continue;
// find another path to 'farArea' // we can't see this area.
ApproachAreaCost cost; // mark this area as "blocked" and unusable by subsequent approach paths
if (NavAreaBuildPath(this, farArea, nullptr, cost) == false) if (BlockedIDCount == MAX_BLOCKED_AREAS)
{ {
// can't find a path to 'farArea' means all exits have been already tested and blocked CONSOLE_ECHO("Overflow computing approach areas for area #%d.\n", m_id);
break; return;
}
// if the area to be blocked is actually farArea, block the one just prior
// (blocking farArea will cause all subsequent pathfinds to fail)
int block = (path[i] == farArea) ? i - 1 : i;
if (block == 0)
continue;
BlockedID[BlockedIDCount++] = path[block]->GetID();
// store new approach area if not already in set
int a;
for (a = 0; a < m_approachCount; a++)
if (m_approach[a].here.area == path[block-1])
break;
if (a == m_approachCount)
{
m_approach[m_approachCount].prev.area = (block >= 2) ? path[block-2] : nullptr;
m_approach[m_approachCount].here.area = path[block - 1];
m_approach[m_approachCount].prevToHereHow = path[block - 1]->GetParentHow();
m_approach[m_approachCount].next.area = path[block];
m_approach[m_approachCount].hereToNextHow = path[block]->GetParentHow();
m_approachCount++;
}
// we are done with this path
break;
}
} }
} }
} }