bool UTIL_ASW_BrushBlockingRoute( AI_Waypoint_t *pRoute, const int nCollisionMask, const int nCollisionGroup )
{
    if ( !pRoute )
        return false;

    AI_Waypoint_t *pLastPoint = pRoute;
    pRoute = pRoute->GetNext();
    trace_t tr;
    Vector vecShiftUp = Vector( 0, 0, 20 );

    while ( pRoute )
    {
        CTraceFilterSimple traceFilter( NULL, nCollisionGroup );
        UTIL_TraceLine( pLastPoint->GetPos() + vecShiftUp, pRoute->GetPos() + vecShiftUp,
                        nCollisionMask, &traceFilter, &tr );

        if ( asw_blink_debug.GetBool() )
        {
            DebugDrawLine( pLastPoint->GetPos() + vecShiftUp, pRoute->GetPos() + vecShiftUp, 255, 0, 0, true, 30.0f );
        }

        if ( tr.DidHit() )
        {
            return true;
        }

        pLastPoint = pRoute;
        pRoute = pRoute->GetNext();
    }

    return false;
}
// traces along an AI network route to see if a door is blocking the way
CASW_Door* UTIL_ASW_DoorBlockingRoute( AI_Waypoint_t *pRoute, bool bRequireLockedOrSealed )
{
    if ( !pRoute )
        return NULL;

    AI_Waypoint_t *pLastPoint = pRoute;
    pRoute = pRoute->GetNext();
    trace_t tr;
    Vector vecShiftUp = Vector( 0, 0, 20 );

    while ( pRoute )
    {
        CASW_Trace_Filter_Doors traceFilter( NULL, COLLISION_GROUP_NONE, bRequireLockedOrSealed );
        UTIL_TraceLine( pLastPoint->GetPos() + vecShiftUp, pRoute->GetPos() + vecShiftUp,
                        MASK_NPCSOLID, &traceFilter, &tr );

        if ( asw_blink_debug.GetBool() )
        {
            DebugDrawLine( pLastPoint->GetPos() + vecShiftUp, pRoute->GetPos() + vecShiftUp, 0, 0, 255, true, 30.0f );
        }

        if ( tr.DidHit() )
        {
            return dynamic_cast<CASW_Door*>(tr.m_pEnt);
        }

        pLastPoint = pRoute;
        pRoute = pRoute->GetNext();
    }

    return NULL;
}
int CASW_Marine_Hint_Manager::FindHints(const Vector &position, CASW_Marine *pLeader, const float flMinDistance, const float flMaxDistance, CUtlVector<HintData_t *> &result)
{
	VPROF_BUDGET("CASW_Marine_Hint_Manager::FindHints", "SquadFormation");
	Assert(pLeader);
	const float flMinDistSqr = Square(flMinDistance);
	const float flMaxDistSqr = Square(flMaxDistance);
	int nCount = m_Hints.Count();
	for ( int i = 0; i < nCount; i++ )
	{
		HintData_t *pHint = m_Hints[i];
		float flDistSqr = position.DistToSqr(pHint->m_vecPosition);
		if (flDistSqr < flMinDistSqr || flDistSqr > flMaxDistSqr)
		{
			continue;
		}

		AI_Waypoint_t *pPath = pLeader->GetPathfinder()->BuildRoute(position, pHint->m_vecPosition, NULL, 0, NAV_GROUND, bits_BUILD_NO_LOCAL_NAV);
		if (!pPath)
		{
			continue;
		}
		pHint->m_flDistance = position.DistTo(pPath->GetPos());
		AI_Waypoint_t *pCur = pPath;
		while (pCur->GetNext())
		{
			AI_Waypoint_t *pNext = pCur->GetNext();
			pHint->m_flDistance += pCur->GetPos().DistTo(pNext->GetPos());
			pCur = pNext;
		}
		DeleteAll(pPath);

		if (pHint->m_flDistance > flMaxDistance)
		{
			continue;
		}

		result.AddToTail( pHint );
	}
	return result.Count();
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CAI_LeadBehavior::GetClosestPointOnRoute( const Vector &targetPos, Vector *pVecClosestPoint )
{
	AI_Waypoint_t *waypoint = GetOuter()->GetNavigator()->GetPath()->GetCurWaypoint();
	AI_Waypoint_t *builtwaypoints = NULL;
	if ( !waypoint )
	{
		// We arrive here twice when lead behaviour starts:
		//	- When the lead behaviour is first enabled. We have no schedule. We want to know if the player is ahead of us.
		//	- A frame later when we've chosen to lead the player, but we still haven't built our route. We know that the
		//	  the player isn't lagging, so it's safe to go ahead and simply say he's ahead of us. This avoids building 
		//	  the temp route twice.
		if ( IsCurSchedule( SCHED_LEAD_PLAYER, false ) )
			return true;

		// Build a temp route to the gold and use that
		builtwaypoints = GetOuter()->GetPathfinder()->BuildRoute( GetOuter()->GetAbsOrigin(), m_goal, NULL, GetOuter()->GetDefaultNavGoalTolerance(), GetOuter()->GetNavType(), true );
		if ( !builtwaypoints )
			return false;

		GetOuter()->GetPathfinder()->UnlockRouteNodes( builtwaypoints );
		waypoint = builtwaypoints;
	}

	// Find the nearest node to the target (going forward)
	float		flNearestDist2D	= 999999999;
	float		flNearestDist	= 999999999;
	float		flPathDist, flPathDist2D;

	Vector vecNearestPoint(0, 0, 0);
	Vector vecPrevPos = GetOuter()->GetAbsOrigin();
	for ( ; (waypoint != NULL) ; waypoint = waypoint->GetNext() )
	{
		// Find the closest point on the line segment on the path
		Vector vecClosest;
		CalcClosestPointOnLineSegment( targetPos, vecPrevPos, waypoint->GetPos(), vecClosest );
		/*
		if ( builtwaypoints )
		{
			NDebugOverlay::Line( vecPrevPos, waypoint->GetPos(), 0,0,255,true, 10.0 );
		}
		*/
		vecPrevPos = waypoint->GetPos();

		// Find the distance between this test point and our goal point
		flPathDist2D = vecClosest.AsVector2D().DistToSqr( targetPos.AsVector2D() );
		if ( flPathDist2D > flNearestDist2D )
			continue;

		flPathDist = vecClosest.z - targetPos.z;
		flPathDist *= flPathDist;
		flPathDist += flPathDist2D;
		if (( flPathDist2D == flNearestDist2D ) && ( flPathDist >= flNearestDist ))
			continue;

		flNearestDist2D	= flPathDist2D;
		flNearestDist	= flPathDist;
		vecNearestPoint	= vecClosest;
	}

	if ( builtwaypoints )
	{
		//NDebugOverlay::Line( vecNearestPoint, targetPos, 0,255,0,true, 10.0 );
		DeleteAll( builtwaypoints );
	}

	*pVecClosestPoint = vecNearestPoint;
	return true;
}
bool CASW_Weapon_Blink::SetBlinkDestination()
{
	CASW_Player *pPlayer = GetCommander();
	if ( !pPlayer )
		return false;

	CASW_Marine *pMarine = GetMarine();
	if ( !pMarine )
		return false;

	Vector vecStart = pPlayer->GetCrosshairTracePos() + Vector( 0, 0, 30 );
	Vector vecEnd = pPlayer->GetCrosshairTracePos() - Vector( 0, 0, 30 );
	trace_t tr;
	UTIL_TraceHull( vecStart, vecEnd, pMarine->WorldAlignMins(), pMarine->WorldAlignMaxs(), MASK_PLAYERSOLID_BRUSHONLY, pMarine, COLLISION_GROUP_PLAYER_MOVEMENT, &tr );
	if ( tr.startsolid || tr.allsolid )
	{
		m_vecInvalidDestination = vecStart;
		return false;
	}

	if ( pMarine->GetAbsOrigin().DistTo( tr.endpos ) > asw_blink_range.GetFloat() )
	{
		m_vecInvalidDestination = tr.endpos;
		return false;
	}

	Vector vecDest = tr.endpos;

	// now see if we can build an AI path from the marine to this spot
	bool bValidRoute = false;

	if ( !pMarine->GetPathfinder() )
	{
		m_vecInvalidDestination = vecDest;
		return false;
	}

	AI_Waypoint_t *pRoute = pMarine->GetPathfinder()->BuildRoute( pMarine->GetAbsOrigin(), vecDest, NULL, 30, NAV_GROUND, bits_BUILD_GROUND | bits_BUILD_IGNORE_NPCS );
	if ( pRoute && !UTIL_ASW_DoorBlockingRoute( pRoute, true ) )
	{
		if ( !UTIL_ASW_BrushBlockingRoute( pRoute, MASK_PLAYERSOLID_BRUSHONLY, COLLISION_GROUP_PLAYER_MOVEMENT ) )
		{
			// if end node of the route is too Z different, then abort, to stop people jumping on top of walls
			AI_Waypoint_t *pLast = pRoute->GetLast();
			if ( pLast )
			{
				AI_Waypoint_t *pNode = pLast->GetPrev();
				if ( !pNode || fabs( pNode->GetPos().z - pLast->GetPos().z ) < 80.0f )
				{
					bValidRoute = true;
				}
			}
		}
	}
	
	if ( !bValidRoute )
	{
		// find the closest node to the dest and try to path there instead
		CAI_Network *pNetwork = pMarine->GetNavigator() ? pMarine->GetNavigator()->GetNetwork() : NULL;
		if ( pNetwork )
		{
			int nNode = pNetwork->NearestNodeToPoint( vecDest, false );
			if ( nNode != NO_NODE )
			{
				CAI_Node *pNode = pNetwork->GetNode( nNode );
				if ( pNode && pNode->GetType() == NODE_GROUND )
				{
					vecDest = pNode->GetOrigin();
					if ( pRoute )
					{
						ASWPathUtils()->DeleteRoute( pRoute );
						pRoute = NULL;
					}
					pRoute = pMarine->GetPathfinder()->BuildRoute( pMarine->GetAbsOrigin(), vecDest, NULL, 30, NAV_GROUND, bits_BUILD_GROUND | bits_BUILD_IGNORE_NPCS );
					if ( pRoute && !UTIL_ASW_DoorBlockingRoute( pRoute, true ) )
					{
						if ( !UTIL_ASW_BrushBlockingRoute( pRoute, MASK_PLAYERSOLID_BRUSHONLY, COLLISION_GROUP_PLAYER_MOVEMENT ) )
						{
							bValidRoute = true;
						}
					}
					if ( !bValidRoute )
					{
						m_vecInvalidDestination = vecDest;
					}
				}
			}
		}
	}

	if ( !bValidRoute )
	{
		if ( pRoute )
		{
			ASWPathUtils()->DeleteRoute( pRoute );
			pRoute = NULL;
		}
		return false;
	}

	if ( asw_blink_debug.GetBool() )
	{
		ASWPathUtils()->DebugDrawRoute( pMarine->GetAbsOrigin(), pRoute );
	}

	m_vecAbilityDestination = vecDest;

	if ( pRoute )
	{
		ASWPathUtils()->DeleteRoute( pRoute );
		pRoute = NULL;
	}

	return true;
}