Skip to content

Commit c08e6d0

Browse files
authored
CNavArea::ComputeApproachAreas(): fix hang on *.nav generating (#913)
1 parent 9f66264 commit c08e6d0

File tree

1 file changed

+108
-70
lines changed

1 file changed

+108
-70
lines changed

regamedll/game_shared/bot/nav_area.cpp

Lines changed: 108 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4431,6 +4431,7 @@ inline bool IsAreaVisible(const Vector *pos, const CNavArea *area)
44314431
// Determine the set of "approach areas".
44324432
// An approach area is an area representing a place where players
44334433
// move into/out of our local neighborhood of areas.
4434+
// @todo Optimize by search from eye outward and modifying pathfinder to treat all links as bi-directional
44344435
void CNavArea::ComputeApproachAreas()
44354436
{
44364437
m_approachCount = 0;
@@ -4452,95 +4453,132 @@ void CNavArea::ComputeApproachAreas()
44524453
enum { MAX_PATH_LENGTH = 256 };
44534454
CNavArea *path[MAX_PATH_LENGTH];
44544455

4455-
// In order to enumerate all of the approach areas, we need to
4456-
// run the algorithm many times, once for each "far away" area
4457-
// and keep the union of the approach area sets
4458-
for (auto farArea : goodSizedAreaList)
4456+
enum SearchType
44594457
{
4460-
BlockedIDCount = 0;
4458+
FROM_EYE, ///< start search from our eyepoint outward to farArea
4459+
TO_EYE, ///< start search from farArea beack towards our eye
4460+
SEARCH_FINISHED
4461+
};
44614462

4462-
// if we can see 'farArea', try again - the whole point is to go "around the bend", so to speak
4463-
if (IsAreaVisible(&eye, farArea))
4464-
continue;
4463+
// In order to *completely* enumerate all of the approach areas, we
4464+
// need to search from our eyepoint outward, as well as from outwards
4465+
// towards our eyepoint
4466+
for (int searchType = FROM_EYE; searchType != SEARCH_FINISHED; searchType++)
4467+
{
4468+
// In order to enumerate all of the approach areas, we need to
4469+
// run the algorithm many times, once for each "far away" area
4470+
// and keep the union of the approach area sets
4471+
for (auto farArea : goodSizedAreaList)
4472+
{
4473+
BlockedIDCount = 0;
44654474

4466-
// make first path to far away area
4467-
ApproachAreaCost cost;
4468-
if (NavAreaBuildPath(this, farArea, nullptr, cost) == false)
4469-
continue;
4475+
// if we can see 'farArea', try again - the whole point is to go "around the bend", so to speak
4476+
if (IsAreaVisible(&eye, farArea))
4477+
continue;
44704478

4471-
//
4472-
// Keep building paths to farArea and blocking them off until we
4473-
// cant path there any more.
4474-
// As areas are blocked off, all exits will be enumerated.
4475-
//
4476-
while (m_approachCount < MAX_APPROACH_AREAS)
4477-
{
4478-
// find number of areas on path
4479-
int count = 0;
4480-
CNavArea *area;
4481-
for (area = farArea; area; area = area->GetParent())
4482-
count++;
4483-
4484-
if (count > MAX_PATH_LENGTH)
4485-
count = MAX_PATH_LENGTH;
4486-
4487-
// build path in correct order - from eye outwards
4488-
int i = count;
4489-
for (area = farArea; i && area; area = area->GetParent())
4490-
{
4491-
path[--i] = area;
4492-
}
4479+
ApproachAreaCost cost;
44934480

4494-
// traverse path to find first area we cannot see (skip the first area)
4495-
for (i = 1; i < count; i++)
4481+
//
4482+
// Keep building paths to farArea and blocking them off until we
4483+
// cant path there any more.
4484+
// As areas are blocked off, all exits will be enumerated.
4485+
//
4486+
while (m_approachCount < MAX_APPROACH_AREAS)
44964487
{
4497-
// if we see this area, continue on
4498-
if (IsAreaVisible(&eye, path[i]))
4499-
continue;
4488+
CNavArea *from, *to;
45004489

4501-
// we can't see this area.
4502-
// mark this area as "blocked" and unusable by subsequent approach paths
4503-
if (BlockedIDCount == MAX_BLOCKED_AREAS)
4490+
if (searchType == FROM_EYE)
45044491
{
4505-
CONSOLE_ECHO("Overflow computing approach areas for area #%d.\n", m_id);
4506-
return;
4492+
// find another path *to* 'farArea'
4493+
// we must pathfind from us in order to pick up one-way paths OUT OF our area
4494+
from = this;
4495+
to = farArea;
4496+
}
4497+
else // TO_EYE
4498+
{
4499+
// find another path *from* 'farArea'
4500+
// we must pathfind to us in order to pick up one-way paths INTO our area
4501+
from = farArea;
4502+
to = this;
45074503
}
45084504

4509-
// if the area to be blocked is actually farArea, block the one just prior
4510-
// (blocking farArea will cause all subsequent pathfinds to fail)
4511-
int block = (path[i] == farArea) ? i - 1 : i;
4505+
// build the actual path
4506+
if (NavAreaBuildPath(from, to, NULL, cost) == false)
4507+
break;
45124508

4513-
BlockedID[BlockedIDCount++] = path[block]->GetID();
4509+
// find number of areas on path
4510+
int count = 0;
4511+
CNavArea *area;
4512+
for (area = to; area; area = area->GetParent())
4513+
count++;
45144514

4515-
if (block == 0)
4515+
if (count > MAX_PATH_LENGTH)
4516+
count = MAX_PATH_LENGTH;
4517+
4518+
// if the path is only two areas long, there can be no approach points
4519+
if (count <= 2)
45164520
break;
45174521

4518-
// store new approach area if not already in set
4519-
int a;
4520-
for (a = 0; a < m_approachCount; a++)
4521-
if (m_approach[a].here.area == path[block - 1])
4522-
break;
4522+
// build path starting from eye
4523+
int i = 0;
45234524

4524-
if (a == m_approachCount)
4525+
if (searchType == FROM_EYE)
45254526
{
4526-
m_approach[m_approachCount].prev.area = (block >= 2) ? path[block-2] : nullptr;
4527-
m_approach[m_approachCount].here.area = path[block - 1];
4528-
m_approach[m_approachCount].prevToHereHow = path[block - 1]->GetParentHow();
4529-
m_approach[m_approachCount].next.area = path[block];
4530-
m_approach[m_approachCount].hereToNextHow = path[block]->GetParentHow();
4531-
m_approachCount++;
4527+
for(area = to; i < count && area; area = area->GetParent())
4528+
{
4529+
path[count - i - 1] = area;
4530+
++i;
4531+
}
4532+
}
4533+
else // TO_EYE
4534+
{
4535+
for(area = to; i < count && area; area = area->GetParent())
4536+
path[i++] = area;
45324537
}
45334538

4534-
// we are done with this path
4535-
break;
4536-
}
4539+
// traverse path to find first area we cannot see (skip the first area)
4540+
for (i = 1; i < count; i++)
4541+
{
4542+
// if we see this area, continue on
4543+
if (IsAreaVisible(&eye, path[i]))
4544+
continue;
45374545

4538-
// find another path to 'farArea'
4539-
ApproachAreaCost cost;
4540-
if (NavAreaBuildPath(this, farArea, nullptr, cost) == false)
4541-
{
4542-
// can't find a path to 'farArea' means all exits have been already tested and blocked
4543-
break;
4546+
// we can't see this area.
4547+
// mark this area as "blocked" and unusable by subsequent approach paths
4548+
if (BlockedIDCount == MAX_BLOCKED_AREAS)
4549+
{
4550+
CONSOLE_ECHO("Overflow computing approach areas for area #%d.\n", m_id);
4551+
return;
4552+
}
4553+
4554+
// if the area to be blocked is actually farArea, block the one just prior
4555+
// (blocking farArea will cause all subsequent pathfinds to fail)
4556+
int block = (path[i] == farArea) ? i - 1 : i;
4557+
4558+
if (block == 0)
4559+
continue;
4560+
4561+
BlockedID[BlockedIDCount++] = path[block]->GetID();
4562+
4563+
// store new approach area if not already in set
4564+
int a;
4565+
for (a = 0; a < m_approachCount; a++)
4566+
if (m_approach[a].here.area == path[block-1])
4567+
break;
4568+
4569+
if (a == m_approachCount)
4570+
{
4571+
m_approach[m_approachCount].prev.area = (block >= 2) ? path[block-2] : nullptr;
4572+
m_approach[m_approachCount].here.area = path[block - 1];
4573+
m_approach[m_approachCount].prevToHereHow = path[block - 1]->GetParentHow();
4574+
m_approach[m_approachCount].next.area = path[block];
4575+
m_approach[m_approachCount].hereToNextHow = path[block]->GetParentHow();
4576+
m_approachCount++;
4577+
}
4578+
4579+
// we are done with this path
4580+
break;
4581+
}
45444582
}
45454583
}
45464584
}

0 commit comments

Comments
 (0)