@@ -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
44344435void 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