CAI_BaseNPC * CASW_Sentry_Top::SelectOptimalEnemy()
{
	// search through all npcs, any that are in LOS and have health
	CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();

	for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
	{
		if (ppAIs[i]->GetHealth() > 0 && CanSee(ppAIs[i]))
		{
			// don't shoot marines
			if ( !asw_sentry_friendly_target.GetBool() && (ppAIs[i]->Classify() == CLASS_ASW_MARINE || ppAIs[i]->Classify() == CLASS_ASW_COLONIST) )
				continue;

			if ( ppAIs[i]->Classify() == CLASS_SCANNER )
				continue;

			if ( !IsValidEnemy( ppAIs[i] ) )
				continue;

			return ppAIs[i];
			break;

		}
	}
	// todo: should evaluate valid targets and pick the best one?
	//   (didn't do this for ASv1 and it was fine...)

	return NULL;
}
void CASW_Sentry_Top::FindEnemy()
{
	bool bFindNewEnemy = true;
	bool bHadEnemy = (m_hEnemy.IsValid() && m_hEnemy.Get());
	// if have an enemy and it is alive
	if (m_hEnemy.IsValid() && m_hEnemy.Get() && m_hEnemy->GetHealth() > 0 )
	{		
		// check for LOS to enemy
		if (CanSee(m_hEnemy))
			bFindNewEnemy = false;
		// reject if enemy is somehow invalid now
		CAI_BaseNPC *pNPC = dynamic_cast<CAI_BaseNPC *>(m_hEnemy.Get());
		if ( pNPC && !IsValidEnemy(pNPC) )
			bFindNewEnemy = true;
	}

	if (bFindNewEnemy)
	{
		if ( g_vecTargetDummies.Count() > 0 )
		{
			m_hEnemy = g_vecTargetDummies[0];
		}
		else
		{
			m_hEnemy = SelectOptimalEnemy();
		}
	}
	// acquired a new enemy
	if (!bHadEnemy && m_hEnemy.IsValid() && m_hEnemy.Get())
	{
		PlayTurnSound();
	}
}
예제 #3
0
CAI_BaseNPC *CASW_Sentry_Top_Cannon::SelectOptimalEnemy()
{
	// prioritize unfrozen aliens who are going to leave the cone soon.
	// prioritize aliens less the more frozen they get.
	CUtlVectorFixedGrowable< CAI_BaseNPC *,16 > candidates;
	CUtlVectorFixedGrowable< float, 16 > candidatescores;

	// search through all npcs, any that are in LOS and have health
	CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
	for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
	{
		if (ppAIs[i]->GetHealth() > 0 && CanSee(ppAIs[i]))
		{
			// don't shoot marines
			if ( !asw_sentry_friendly_target.GetBool() && ppAIs[i]->Classify() == CLASS_ASW_MARINE )
				continue;

			if ( ppAIs[i]->Classify() == CLASS_SCANNER )
				continue;

			if ( !IsValidEnemy( ppAIs[i] ) )
				continue;

			candidates.AddToTail( ppAIs[i] );
		}
	}

	// bail out if we don't have anyone
	if ( candidates.Count() < 1 )
		return NULL;

	else if ( candidates.Count() == 1 ) // just one candidate is an obvious result
		return candidates[0];

	// score each of the candidates
	candidatescores.EnsureCount( candidates.Count() );
	for ( int i = candidates.Count() - 1; i >= 0 ; --i )
	{
		CAI_BaseNPC * RESTRICT pCandidate = candidates[i];

		// is the candidate moving into or out of the cone?
		Vector vCandVel = GetEnemyVelocity(pCandidate);
		Vector vMeToTarget = pCandidate->GetAbsOrigin() - GetFiringPosition();
		Vector vBaseForward = UTIL_YawToVector( m_fDeployYaw );

		// crush everything to 2d for simplicity
		vMeToTarget.z = 0.0f;
		vCandVel.z = 0.0f;
		vBaseForward.z = 0.0f;

		Vector velCross = vBaseForward.Cross(vCandVel); // this encodes also some info on perpendicularity
		Vector vAimCross = vBaseForward.Cross(vMeToTarget);
		bool bTargetHeadedOutOfCone = !vCandVel.IsZero() && velCross.z * vAimCross.z >= 0; // true if same sign
		float flConeLeavingUrgency;

		if ( bTargetHeadedOutOfCone )
		{
			flConeLeavingUrgency = fabs( velCross.z / vCandVel.Length2D() ); 
			// just the sin; varies 0..1 where 1 means moving perpendicular to my aim
		}
		else
		{
			flConeLeavingUrgency = 0; // not at threat of leaving just yet
		}

		// the angle between my current yaw and what's needed to hit the target
		float flSwivelNeeded = fabs( UTIL_AngleDiff(  // i wish we weren't storing euler angles
			UTIL_VecToYaw( vMeToTarget ), m_fDeployYaw ) );
		flSwivelNeeded /= ASW_SENTRY_ANGLE; // normalize to 0..2

		float fBigness = 0.0f;

		int nClassify = pCandidate->Classify();
		switch( nClassify )
		{
		case CLASS_ASW_SHIELDBUG:
		case CLASS_ASW_MORTAR_BUG:
			fBigness = 4.0f;
			break;

		case CLASS_ASW_HARVESTER:
		case CLASS_ASW_RANGER:
			fBigness = 2.0f;
			break;
		}

		candidatescores[i] = Vector( 3.0f, -1.5f, 4.0f ).Dot( Vector( flConeLeavingUrgency, flSwivelNeeded, fBigness ) );
	}
	// find the highest scoring candidate
	int best = 0;
	for ( int i = 1 ; i < candidatescores.Count() ; ++i )
	{
		if ( candidatescores[i] > candidatescores[best] )
			best = i;
	}

	// NDebugOverlay::EntityBounds(candidates[best], 255, 255, 0, 255, 0.2f );

	return candidates[best];
}
예제 #4
0
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseEntity *CAI_FuncTankBehavior::BestEnemy( void )
{
	// Only use this BestEnemy call when we are on the manned gun.
	if ( !m_hFuncTank ||!IsMounted() )
		return BaseClass::BestEnemy();

	CBaseEntity *pBestEnemy	= NULL;
	int	iBestDistSq	= MAX_COORD_RANGE * MAX_COORD_RANGE;	// so first visible entity will become the closest.
	int	iBestPriority = -1000;
	bool bBestUnreachable = false;							// Forces initial check
	bool bBestSeen = false;
	bool bUnreachable = false;
	int	iDistSq;

	AIEnemiesIter_t iter;

	// Get the current npc for checking from.
	CAI_BaseNPC *pNPC = GetOuter();
	if ( !pNPC )
		return NULL;

	for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst( &iter ); pEMemory != NULL; pEMemory = GetEnemies()->GetNext( &iter ) )
	{
		CBaseEntity *pEnemy = pEMemory->hEnemy;
		if ( !pEnemy || !pEnemy->IsAlive() )
			continue;
		
		// UNDONE: Move relationship checks into IsValidEnemy?
		if ( ( pEnemy->GetFlags() & FL_NOTARGET ) || 
			 ( pNPC->IRelationType( pEnemy ) != D_HT && pNPC->IRelationType( pEnemy ) != D_FR ) ||
			 !IsValidEnemy( pEnemy ) )
			continue;

		if ( pEMemory->timeLastSeen < pNPC->GetAcceptableTimeSeenEnemy() )
			continue;

		if ( pEMemory->timeValidEnemy > gpGlobals->curtime )
			continue;

		// Skip enemies that have eluded me to prevent infinite loops
		if ( GetEnemies()->HasEludedMe( pEnemy ) )
			continue;

		// Establish the reachability of this enemy
		bUnreachable = pNPC->IsUnreachable( pEnemy );

		// Check view cone of the view tank here.
		bUnreachable = !m_hFuncTank->IsEntityInViewCone( pEnemy );
		if ( !bUnreachable )
		{
			// It's in the viewcone. Now make sure we have LOS to it.
			bUnreachable = !m_hFuncTank->HasLOSTo( pEnemy );
		}

		// If best is reachable and current is unreachable, skip the unreachable enemy regardless of priority
		if ( !bBestUnreachable && bUnreachable )
			continue;

		//  If best is unreachable and current is reachable, always pick the current regardless of priority
		if ( bBestUnreachable && !bUnreachable )
		{
			bBestSeen = ( pNPC->GetSenses()->DidSeeEntity( pEnemy ) || pNPC->FVisible( pEnemy ) ); // @TODO (toml 04-02-03): Need to optimize CanSeeEntity() so multiple calls in frame do not recalculate, rather cache
			iBestPriority = pNPC->IRelationPriority( pEnemy );
			iBestDistSq = static_cast<int>((pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr());
			pBestEnemy = pEnemy;
			bBestUnreachable = bUnreachable;
		}
		// If both are unreachable or both are reachable, chose enemy based on priority and distance
		else if ( pNPC->IRelationPriority( pEnemy ) > iBestPriority )
		{
			// this entity is disliked MORE than the entity that we
			// currently think is the best visible enemy. No need to do
			// a distance check, just get mad at this one for now.
			iBestPriority = pNPC->IRelationPriority ( pEnemy );
			iBestDistSq = static_cast<int>(( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr());
			pBestEnemy = pEnemy;
			bBestUnreachable = bUnreachable;
		}
		else if ( pNPC->IRelationPriority( pEnemy ) == iBestPriority )
		{
			// this entity is disliked just as much as the entity that
			// we currently think is the best visible enemy, so we only
			// get mad at it if it is closer.
			iDistSq = static_cast<int>(( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr());

			bool bCloser = ( iDistSq < iBestDistSq ) ;

			if ( bCloser || !bBestSeen )
			{
				// @TODO (toml 04-02-03): Need to optimize FVisible() so multiple calls in frame do not recalculate, rather cache
				bool fSeen = ( pNPC->GetSenses()->DidSeeEntity( pEnemy ) || pNPC->FVisible( pEnemy ) );
				if ( ( bCloser && ( fSeen || !bBestSeen ) ) || ( !bCloser && !bBestSeen && fSeen ) )
				{
					bBestSeen = fSeen;
					iBestDistSq = iDistSq;
					iBestPriority = pNPC->IRelationPriority( pEnemy );
					pBestEnemy = pEnemy;
					bBestUnreachable = bUnreachable;
				}
			}
		}
	}
	return pBestEnemy;
}