void CAI_PlaneSolver::GenerateObstacleNpcs( const AILocalMoveGoal_t &goal, float probeDist )
{
	if ( !ProbeForNpcs() )
	{
		CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
		Vector minsSelf, maxsSelf;
		m_pNpc->CollisionProp()->WorldSpaceSurroundingBounds( &minsSelf, &maxsSelf );
		float radiusSelf = (minsSelf.AsVector2D() - maxsSelf.AsVector2D()).Length() * 0.5;

		for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
		{
			CAI_BaseNPC *pAI = ppAIs[i];
			if ( pAI != m_pNpc && pAI->IsAlive() && ( !goal.pPath || pAI != goal.pPath->GetTarget() ) )
			{
				Vector mins, maxs;
				
				pAI->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs );
				if ( mins.z < maxsSelf.z + 12.0 && maxs.z > minsSelf.z - 12.0 )
				{
					float radius = (mins.AsVector2D() - maxs.AsVector2D()).Length() * 0.5;
					float distance = ( pAI->GetAbsOrigin().AsVector2D() - m_pNpc->GetAbsOrigin().AsVector2D() ).Length();
					if ( distance - radius < radiusSelf + probeDist )
					{
						AddObstacle( pAI->WorldSpaceCenter(), radius, pAI, AIMST_AVOID_NPC );
					}
				}
			}
		}

		#ifdef SecobMod__Enable_Fixed_Multiplayer_AI
			CBaseEntity *pPlayer = UTIL_GetNearestPlayer(m_pNpc->GetAbsOrigin()); 
		#else
			CBaseEntity *pPlayer = UTIL_PlayerByIndex( 1 );
		#endif //SecobMod__Enable_Fixed_Multiplayer_AI

		if ( pPlayer )
		{
			Vector mins, maxs;
			
			pPlayer->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs );
			if ( mins.z < maxsSelf.z + 12.0 && maxs.z > minsSelf.z - 12.0 )
			{
				float radius = (mins.AsVector2D() - maxs.AsVector2D()).Length();
				float distance = ( pPlayer->GetAbsOrigin().AsVector2D() - m_pNpc->GetAbsOrigin().AsVector2D() ).Length();
				if ( distance - radius < radiusSelf + probeDist )
				{
					AddObstacle( pPlayer->WorldSpaceCenter(), radius, pPlayer, AIMST_AVOID_NPC );
				}
			}
		}

	}
}
AI_SuggestorResult_t CAI_PlaneSolver::GenerateObstacleSuggestion( const AILocalMoveGoal_t &goal, float yawScanCenter, 
																  float probeDist, float spanPerProbe, int probeOffset)
{
	AIMoveTrace_t moveTrace;
	float		  yawTest;
	float		  arcCenter;

	CalcYawsFromOffset( yawScanCenter, spanPerProbe, probeOffset, &yawTest, &arcCenter );

	Vector probeDir = UTIL_YawToVector( yawTest );
	float requiredMovement = goal.speed * GetMotor()->GetMoveInterval();

	// Probe immediate move with footing, then look further out ignoring footing
	bool fTraceClear = true;
	if ( probeDist > requiredMovement )
	{
		if ( !MoveLimit( goal.navType, GetLocalOrigin() + probeDir * requiredMovement, !ProbeForNpcs(), true, &moveTrace ) )
		{
			fTraceClear = false;
			moveTrace.flDistObstructed = (probeDist - requiredMovement) + moveTrace.flDistObstructed;
		}
	}

	if ( fTraceClear )
	{
		fTraceClear = MoveLimit( goal.navType, GetLocalOrigin() + probeDir * probeDist, !ProbeForNpcs(), false, &moveTrace );
	}
	

	if ( !fTraceClear )
	{
		GenerateSuggestionFromTrace( goal, moveTrace, probeDist, arcCenter, spanPerProbe, probeOffset );
		return SR_OK;
	}
	
	return SR_NONE;
}
bool CAI_PlaneSolver::MoveLimit( Navigation_t navType, const Vector &target, bool ignoreTransients, bool fCheckStep, int contents, AIMoveTrace_t *pMoveTrace )
{
	AI_PROFILE_SCOPE( CAI_PlaneSolver_MoveLimit );

	int flags = ( navType == NAV_GROUND ) ? AIMLF_2D : AIMLF_DEFAULT;

	if ( ignoreTransients )
	{
		Assert( !ProbeForNpcs() );
		flags |= AIMLF_IGNORE_TRANSIENTS;
	}

	CAI_MoveProbe *pProbe = m_pNpc->GetMoveProbe();
	return pProbe->MoveLimit( navType, GetLocalOrigin(), target, contents, 
		m_pNpc->GetNavTargetEntity(), (fCheckStep) ? 100 : 0, 
							  flags, 
							  pMoveTrace );
}
AI_SuggestorResult_t CAI_PlaneSolver::GenerateObstacleSuggestions( const AILocalMoveGoal_t &goal, const AIMoveTrace_t &directTrace, 
																   float distClear, float probeDist, float degreesToProbe, int nProbes )
{
	Assert( nProbes % 2 == 1 );
	
	PLANESOLVER_PROFILE_SCOPE( CAI_PlaneSolver_GenerateObstacleSuggestions );
	
	AI_SuggestorResult_t seekResult = SR_NONE;
	bool				 fNewTarget = ( !m_fSolvedPrev || m_PrevTarget != goal.target );
	
	if ( fNewTarget )
		m_RefreshSamplesTimer.Force();

	if ( PLANE_SOLVER_THINK_FREQUENCY[AIStrongOpt()] == 0.0 || m_RefreshSamplesTimer.Expired() )
	{
		m_Solver.ClearRegulations();
	
		if ( !ProbeForNpcs() )
			GenerateObstacleNpcs( goal, probeDist );
			
		if ( GenerateCircleObstacleSuggestions( goal, probeDist ) )
			seekResult = SR_OK;
		
		float spanPerProbe = degreesToProbe / nProbes;
		int   nSideProbes  = (nProbes - 1) / 2;
		float yawGoalDir   = UTIL_VecToYaw( goal.dir );
		
		Vector 		  probeTarget;
		AIMoveTrace_t moveTrace;
		int			  i;
		
		// Generate suggestion from direct trace, or probe if direct trace doesn't match
		if ( fabs( probeDist - ( distClear + directTrace.flDistObstructed ) ) < 0.1 && 
			 ( ProbeForNpcs() || directTrace.fStatus != AIMR_BLOCKED_NPC ) )
		{
			if ( directTrace.fStatus != AIMR_OK )
			{
				seekResult = SR_OK;
				GenerateSuggestionFromTrace( goal, directTrace, probeDist, yawGoalDir, spanPerProbe, 0 );
			}
		}
		else if ( GenerateObstacleSuggestion( goal, yawGoalDir, probeDist, spanPerProbe, 0 ) == SR_OK )
		{
			seekResult = SR_OK;
		}
		
		// Scan left. Note that in the left and right scan, the algorithm stops as soon
		// as there is a clear path. This is an optimization in anticipation of the
		// behavior of the underlying solver. This will break more often the higher
		// PLANE_SOLVER_THINK_FREQUENCY becomes
		bool foundClear = false;

		for ( i = 1; i <= nSideProbes; i++ )
		{
			if ( !foundClear )
			{
				AI_SuggestorResult_t curSeekResult = GenerateObstacleSuggestion( goal, yawGoalDir, probeDist, 
																				 spanPerProbe, i );
				if ( curSeekResult == SR_OK )
				{
					seekResult = SR_OK;
				}
				else
					foundClear = true;
			}
			else
			{
				float ignored;
				float arcCenter;
				CalcYawsFromOffset( yawGoalDir, spanPerProbe, i, &ignored, &arcCenter );
				m_Solver.AddRegulation( AI_MoveSuggestion_t( AIMST_NO_KNOWLEDGE, 1, arcCenter, spanPerProbe ) );
			}
		}

		// Scan right
		foundClear = false;

		for ( i = -1; i >= -nSideProbes; i-- )
		{
			if ( !foundClear )
			{
				AI_SuggestorResult_t curSeekResult = GenerateObstacleSuggestion( goal, yawGoalDir, probeDist, 
																				 spanPerProbe, i );
				if ( curSeekResult == SR_OK )
				{
					seekResult = SR_OK;
				}
				else
					foundClear = true;
			}
			else
			{
				float ignored;
				float arcCenter;
				CalcYawsFromOffset( yawGoalDir, spanPerProbe, i, &ignored, &arcCenter );
				m_Solver.AddRegulation( AI_MoveSuggestion_t( AIMST_NO_KNOWLEDGE, 1, arcCenter, spanPerProbe ) );
			}
		}

		if ( seekResult == SR_OK )
		{
			float arcCenter = yawGoalDir - 180;
			if ( arcCenter < 0 )
				arcCenter += 360;
				
			// Since these are not sampled every think, place a negative arc in all directions not sampled
			m_Solver.AddRegulation( AI_MoveSuggestion_t( AIMST_NO_KNOWLEDGE, 1, arcCenter, 360 - degreesToProbe ) );

		}

		m_RefreshSamplesTimer.Reset( PLANE_SOLVER_THINK_FREQUENCY[AIStrongOpt()] );
	}
	else if ( m_Solver.HaveRegulations() )
		seekResult = SR_OK;

	return seekResult;
}