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());
}
Esempio n. 2
0
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() );
}