Exemplo n.º 1
0
void TacticalSpotsDetector::SelectCandidateAreas(const OriginParams &originParams, const int *areas, int numAreas,
                                                 CandidateAreas &result)
{
    const aas_area_t *worldAreas = AiAasWorld::Instance()->Areas();
    const aas_areasettings_t *worldAreaSettings = AiAasWorld::Instance()->AreaSettings();

    int badAreaContents = 0;
    badAreaContents |= AREACONTENTS_LAVA | AREACONTENTS_SLIME | AREACONTENTS_DONOTENTER;
    badAreaContents |= AREACONTENTS_JUMPPAD | AREACONTENTS_TELEPORTER | AREACONTENTS_MOVER | AREACONTENTS_WATER;

    for (int i = 0; i < numAreas && result.size() < result.capacity(); ++i)
    {
        const aas_areasettings_t &areaSettings = worldAreaSettings[areas[i]];
        if (!(areaSettings.areaflags & AREA_GROUNDED))
            continue;
        if (areaSettings.areaflags & (AREA_DISABLED|AREA_JUNK))
            continue;
        if (areaSettings.contents & badAreaContents)
            continue;
        if (!areaSettings.numreachableareas)
            continue;

        const aas_area_t &area = worldAreas[areas[i]];

        float height = area.mins[2] - originParams.origin[2];
        if (height < minHeightAdvantage)
            continue;

        float dx = area.maxs[0] - area.mins[0];
        float dy = area.maxs[1] - area.mins[1];
        // Skip small areas. If an area did not qualify as "junk" its not enough.
        if (dx < 24.0f || dy < 24.0f)
            continue;

        float score = 1.0f;

        // Increase score for higher areas
        float heightFactor = BoundedFraction(height - minHeightAdvantage, originParams.searchRadius);
        score = ApplyFactor(score, heightFactor, heightInfluence);
        // Increase scores for larger areas
        score *= 1.0f + 2.0f * BoundedFraction(dx - 24.0f, 96.0f);
        score *= 1.0f + 2.0f * BoundedFraction(dy - 24.0f, 96.0f);

        if ((areaSettings.areaflags & AREA_LEDGE))
            score *= ledgePenalty;
        if ((areaSettings.areaflags & AREA_WALL))
            score *= wallPenalty;

        result.push_back(AreaAndScore(areas[i], score));
    }

    // Sort result so best score areas are first
    std::sort(result.begin(), result.end());
}
Exemplo n.º 2
0
void TacticalSpotsDetector::SelectAreasForCover(const OriginParams &originParams,
                                                const CoverProblemParams &problemParams,
                                                ReachCheckedAreas &candidateAreas,
                                                TraceCheckedAreas &result)
{
    const aas_area_t *worldAreas = AiAasWorld::Instance()->Areas();

    // Do not more result.capacity() iterations
    for (unsigned i = 0, end = std::min(candidateAreas.size(), result.capacity()); i < end; ++i)
    {
        const aas_area_t &area = worldAreas[candidateAreas[i].areaNum];
        if (!LooksLikeACoverArea(area, originParams, problemParams))
            continue;

        // Prefer larger areas
        float dimensionFactor = 1.0f;
        dimensionFactor *= BoundedFraction(area.maxs[0] - area.mins[0], 64);
        dimensionFactor *= BoundedFraction(area.maxs[1] - area.mins[1], 64);
        result.push_back(AreaAndScore(candidateAreas[i].areaNum, candidateAreas[i].score * dimensionFactor));
    };

    // Sort result so best score areas are first
    std::sort(result.begin(), result.end());
}
Exemplo n.º 3
0
void TacticalSpotsDetector::SortByVisAndOtherFactors(const OriginParams &params, TraceCheckedAreas &areas)
{
    const aas_area_t *worldAreas = AiAasWorld::Instance()->Areas();
    const aas_areasettings_t *worldAreaSettings = AiAasWorld::Instance()->AreaSettings();

    const float originZ = params.origin[2];
    const float searchRadius = params.searchRadius;

    // A matrix of trace results:
    // -1 means that trace has not been computed
    // 0 means that trace.fraction < 1.0f
    // 1 means that trace.fraction == 1.0f
    signed char traceResultCache[areas.capacity() * areas.capacity()];
    std::fill(traceResultCache, traceResultCache + areas.capacity() * areas.capacity(), -1);

    // Compute area points to avoid doing it in the trace loop.
    vec3_t areaPoints[areas.capacity()];
    for (unsigned i = 0; i < areas.size(); ++i)
    {
        const aas_area_t &area = worldAreas[areas[i].areaNum];
        VectorCopy(area.center, areaPoints[i]);
        areaPoints[i][2] = area.mins[2] + PLAYER_VIEW_GROUND_OFFSET;
    }

    trace_t trace;
    for (unsigned i = 0; i < areas.size(); ++i)
    {
        // Avoid fp <-> int conversions in a loop
        int numVisAreas = 0;
        for (unsigned j = 0; j < i; ++j)
        {
            int cachedTraceResult = traceResultCache[areas.capacity() * i + j];
            if (cachedTraceResult >= 0)
            {
                numVisAreas += cachedTraceResult;
                continue;
            }
            G_Trace(&trace, areaPoints[i], nullptr, nullptr, areaPoints[j], nullptr, MASK_AISOLID);
            // Omit fractional part by intention
            signed char visibility = (signed char)trace.fraction;
            traceResultCache[areas.capacity() * i + j] = visibility;
            traceResultCache[areas.capacity() * j + i] = visibility;
            numVisAreas += visibility;
        }

        // Skip a trace from an area to itself

        for (unsigned j = i + 1; j < areas.size(); ++j)
        {
            int cachedTraceResult = traceResultCache[areas.capacity() * i + j];
            if (cachedTraceResult >= 0)
            {
                numVisAreas += cachedTraceResult;
                continue;
            }
            G_Trace(&trace, areaPoints[i], nullptr, nullptr, areaPoints[j], nullptr, MASK_AISOLID);
            // Omit fractional part by intention
            signed char visibility = (signed char)trace.fraction;
            traceResultCache[areas.capacity() * i + j] = visibility;
            traceResultCache[areas.capacity() * j + i] = visibility;
            numVisAreas += visibility;
        }

        float visFactor = numVisAreas / (float)areas.size();
        visFactor = 1.0f / Q_RSqrt(visFactor);
        areas[i].score *= 0.1f + 0.9f * visFactor;

        // We should modify the final score by following factors.
        // These factors should be checked in earlier calls but mainly for early rejection of non-suitable areas.

        const int areaNum = areas[i].areaNum;

        float height = (worldAreas[areaNum].mins[2] - originZ - minHeightAdvantage);
        float heightFactor = BoundedFraction(height, searchRadius - minHeightAdvantage);
        areas[i].score = ApplyFactor(areas[i].score, heightFactor, heightInfluence);

        const aas_areasettings_t &areaSettings = worldAreaSettings[areaNum];
        if (areaSettings.areaflags & AREA_WALL)
            areas[i].score *= wallPenalty;
        if (areaSettings.areaflags & AREA_LEDGE)
            areas[i].score *= ledgePenalty;
    }

    // Sort results so best score areas are first
    std::sort(areas.begin(), areas.end());
}
Exemplo n.º 4
0
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() );
}