void TacticalSpotsDetector::CheckAreasReachFromOrigin(const OriginParams &originParams, const CandidateAreas &candidateAreas, ReachCheckedAreas &result) { AiAasRouteCache *routeCache = originParams.routeCache; const aas_area_t *areas = AiAasWorld::Instance()->Areas(); int originAreaNum = originParams.originAreaNum; const float *origin = originParams.origin; float searchRadius = originParams.searchRadius; // Do not more than result.capacity() iterations. // Some feasible areas in candidateAreas tai that pass test may be skipped, // but this is intended to reduce performance drops (do not more than result.capacity() pathfinder calls). for (unsigned i = 0, end = std::min(candidateAreas.size(), result.capacity()); i < end; ++i) { int areaNum = candidateAreas[i].areaNum; int travelTime = routeCache->TravelTimeToGoalArea(originAreaNum, areaNum, Bot::ALLOWED_TRAVEL_FLAGS); if (!travelTime) continue; // AAS time is in seconds^-2 float travelTimeFactor = 1.0f - ComputeTravelTimeFactor(travelTime * 10); float distanceFactor = ComputeDistanceFactor(areas[areaNum].center, origin, searchRadius); float newScore = candidateAreas[i].score; newScore = ApplyFactor(newScore, distanceFactor, distanceInfluence); newScore = ApplyFactor(newScore, travelTimeFactor, travelTimeInfluence); result.push_back(AreaAndScore(areaNum, newScore)); } // Sort result so best score areas are first std::sort(result.begin(), result.end()); }
static int FindGoalAASArea(edict_t *ent) { AiAasWorld *aasWorld = AiAasWorld::Instance(); if (!aasWorld->IsLoaded()) return 0; Vec3 mins(ent->r.mins), maxs(ent->r.maxs); // Extend AABB XY dimensions ExtendDimension(mins.Data(), maxs.Data(), 0); ExtendDimension(mins.Data(), maxs.Data(), 1); // Z needs special extension rules float presentHeight = maxs.Z() - mins.Z(); float playerHeight = playerbox_stand_maxs[2] - playerbox_stand_mins[2]; if (playerHeight > presentHeight) maxs.Z() += playerHeight - presentHeight; // Find all areas in bounds int areas[16]; // Convert bounds to absolute ones mins += ent->s.origin; maxs += ent->s.origin; const int numAreas = aasWorld->BBoxAreas(mins, maxs, areas, 16); // Find hub areas (or use cached) FindHubAreas(); int bestArea = 0; int bestAreaReachCount = 0; AiAasRouteCache *routeCache = AiAasRouteCache::Shared(); for (int i = 0; i < numAreas; ++i) { const int areaNum = areas[i]; int areaReachCount = 0; for (const int hubAreaNum: hubAreas) { const aas_area_t &hubArea = aasWorld->Areas()[hubAreaNum]; Vec3 hubAreaPoint(hubArea.center); hubAreaPoint.Z() = hubArea.mins[2] + std::min(24.0f, hubArea.maxs[2] - hubArea.mins[2]); // Do not waste pathfinder cycles testing for preferred flags that may fail. constexpr int travelFlags = Bot::ALLOWED_TRAVEL_FLAGS; if (routeCache->ReachabilityToGoalArea(hubAreaNum, hubAreaPoint.Data(), areaNum, travelFlags)) { areaReachCount++; // Thats't enough, do not waste CPU cycles if (areaReachCount == 4) return areaNum; } } if (areaReachCount > bestAreaReachCount) { bestArea = areaNum; bestAreaReachCount = areaReachCount; } } if (bestArea) return bestArea; // Fall back to a default method and hope it succeeds return aasWorld->FindAreaNum(ent); }
void BotTacticalSpotsCache::FindReachableClassEntities( const Vec3 &origin, float radius, const char *classname, BotTacticalSpotsCache::ReachableEntities &result ) { int *triggerEntities; int numEntities = FindNearbyEntities( origin, radius, &triggerEntities ); ReachableEntities candidateEntities; // Copy to locals for faster access (a compiler might be paranoid about aliasing) edict_t *gameEdicts = game.edicts; if( numEntities > (int)candidateEntities.capacity() ) { for( int i = 0; i < numEntities; ++i ) { edict_t *ent = gameEdicts + triggerEntities[i]; // Specify expected strcmp() result explicitly to avoid misinterpreting the condition // (Strings are equal if an strcmp() result is zero) if( strcmp( ent->classname, classname ) != 0 ) { continue; } float distance = DistanceFast( origin.Data(), ent->s.origin ); candidateEntities.push_back( EntAndScore( triggerEntities[i], radius - distance ) ); if( candidateEntities.size() == candidateEntities.capacity() ) { break; } } } else { for( int i = 0; i < numEntities; ++i ) { edict_t *ent = gameEdicts + triggerEntities[i]; if( strcmp( ent->classname, classname ) != 0 ) { continue; } float distance = DistanceFast( origin.Data(), ent->s.origin ); candidateEntities.push_back( EntAndScore( triggerEntities[i], radius - distance ) ); } } const AiAasWorld *aasWorld = AiAasWorld::Instance(); AiAasRouteCache *routeCache = self->ai->botRef->routeCache; bool testTwoCurrAreas = false; int fromAreaNum = 0; // If an origin matches actual bot origin if( ( origin - self->s.origin ).SquaredLength() < WorldState::OriginVar::MAX_ROUNDING_SQUARE_DISTANCE_ERROR ) { // Try testing both areas if( self->ai->botRef->CurrAreaNum() != self->ai->botRef->DroppedToFloorAreaNum() ) { testTwoCurrAreas = true; } else { fromAreaNum = self->ai->botRef->CurrAreaNum(); } } else { fromAreaNum = aasWorld->FindAreaNum( origin ); } if( testTwoCurrAreas ) { int fromAreaNums[2] = { self->ai->botRef->CurrAreaNum(), self->ai->botRef->DroppedToFloorAreaNum() }; for( EntAndScore &candidate: candidateEntities ) { edict_t *ent = gameEdicts + candidate.entNum; int toAreaNum = FindMostFeasibleEntityAasArea( ent, aasWorld ); if( !toAreaNum ) { continue; } int travelTime = 0; for( int i = 0; i < 2; ++i ) { travelTime = routeCache->TravelTimeToGoalArea( fromAreaNums[i], toAreaNum, Bot::ALLOWED_TRAVEL_FLAGS ); if( travelTime ) { break; } } if( !travelTime ) { continue; } // AAS travel time is in seconds^-2 float factor = 1.0f / Q_RSqrt( 1.0001f - BoundedFraction( travelTime, 200 ) ); result.push_back( EntAndScore( candidate.entNum, candidate.score * factor ) ); } } else { for( EntAndScore &candidate: candidateEntities ) { edict_t *ent = gameEdicts + candidate.entNum; int toAreaNum = FindMostFeasibleEntityAasArea( ent, aasWorld ); if( !toAreaNum ) { continue; } int travelTime = routeCache->TravelTimeToGoalArea( fromAreaNum, toAreaNum, Bot::ALLOWED_TRAVEL_FLAGS ); if( !travelTime ) { continue; } float factor = 1.0f / Q_RSqrt( 1.0001f - BoundedFraction( travelTime, 200 ) ); result.push_back( EntAndScore( candidate.entNum, candidate.score * factor ) ); } } // Sort entities so best entities are first std::sort( result.begin(), result.end() ); }