importonly abstract class gameISpatialQueriesSystem extends IGameSystem
{
}

importonly final class SpatialQueriesSystem extends gameISpatialQueriesSystem
{
	public import final function GetPlayerObstacleSystem() : PlayerObstacleSystem;
	public import final function GetGeometryDescriptionSystem() : GeometryDescriptionSystem;
	public import final function Overlap( primitiveDimension : Vector4, position : Vector4, rotation : EulerAngles, optional collisionGroup : CName, out result : TraceResult ) : Bool;
	public import final function OverlapByQueryFilter( primitiveDimension : Vector4, position : Vector4, rotation : Quaternion, queryFilter : QueryFilter, out result : TraceResult ) : Bool;
	public import final function SyncRaycastByQueryFilter( start : Vector4, end : Vector4, queryFilter : QueryFilter, out result : TraceResult, optional staticOnly : Bool, optional dynamicOnly : Bool ) : Bool;
	public import final function SyncRaycastByQueryPreset( start : Vector4, end : Vector4, optional queryPreset : CName, out result : TraceResult, optional staticOnly : Bool, optional dynamicOnly : Bool ) : Bool;
	public import final function SyncRaycastByCollisionPreset( start : Vector4, end : Vector4, optional collisionPreset : CName, out result : TraceResult, optional staticOnly : Bool, optional dynamicOnly : Bool ) : Bool;
	public import final function SyncRaycastByCollisionGroup( start : Vector4, end : Vector4, optional collisionGroup : CName, out result : TraceResult, optional staticOnly : Bool, optional dynamicOnly : Bool ) : Bool;
	public import final function GetDebugPreviewDuration() : Float;
}

class SpatialQueriesHelper
{

	public static function HasSpaceInFront( const sourceObject : weak< GameObject >, groundClearance : Float, areaWidth : Float, areaLength : Float, areaHeight : Float ) : Bool
	{
		var hasSpace : Bool;
		hasSpace = HasSpaceInFront( sourceObject, sourceObject.GetWorldForward(), groundClearance, areaWidth, areaLength, areaHeight );
		return hasSpace;
	}

	public static function HasSpaceInFront( const sourceObject : weak< GameObject >, queryDirection : Vector4, groundClearance : Float, areaWidth : Float, areaLength : Float, areaHeight : Float ) : Bool
	{
		var overlapSuccessStatic : Bool;
		var overlapSuccessVehicle : Bool;
		var fitTestOvelap : TraceResult;
		var queryPosition : Vector4;
		var boxDimensions : Vector4;
		var boxOrientation : EulerAngles;
		queryDirection.Z = 0.0;
		queryDirection = Vector4.Normalize( queryDirection );
		boxDimensions.X = areaWidth * 0.5;
		boxDimensions.Y = areaLength * 0.5;
		boxDimensions.Z = areaHeight * 0.5;
		queryPosition = sourceObject.GetWorldPosition();
		queryPosition.Z += ( boxDimensions.Z + groundClearance );
		queryPosition += ( boxDimensions.Y * queryDirection );
		boxOrientation = Quaternion.ToEulerAngles( Quaternion.BuildFromDirectionVector( queryDirection ) );
		overlapSuccessStatic = GameInstance.GetSpatialQueriesSystem( sourceObject.GetGame() ).Overlap( boxDimensions, queryPosition, boxOrientation, 'Static', fitTestOvelap );
		overlapSuccessVehicle = GameInstance.GetSpatialQueriesSystem( sourceObject.GetGame() ).Overlap( boxDimensions, queryPosition, boxOrientation, 'Vehicle', fitTestOvelap );
		return !( overlapSuccessStatic ) && !( overlapSuccessVehicle );
	}

	public static function GetFloorAngle( const sourceObject : weak< GameObject >, out floorAngle : Float ) : Bool
	{
		var startPosition, endPosition : Vector4;
		var raycastResult : TraceResult;
		startPosition = sourceObject.GetWorldPosition() + Vector4( 0.0, 0.0, 0.1, 0.0 );
		endPosition = sourceObject.GetWorldPosition() + Vector4( 0.0, 0.0, -0.30000001, 0.0 );
		if( GameInstance.GetSpatialQueriesSystem( sourceObject.GetGame() ).SyncRaycastByCollisionGroup( startPosition, endPosition, 'Static', raycastResult, true, false ) )
		{
			floorAngle = Vector4.GetAngleBetween( ( ( Vector4 )( raycastResult.normal ) ), sourceObject.GetWorldUp() );
			return true;
		}
		return false;
	}

	public static function IsTargetReachable( owner : GameObject, target : GameObject, targetPos : Vector4, depthTestForObstacle : Bool, out isObstacleVaultable : Bool ) : Bool
	{
		var scriptedPuppet : ScriptedPuppet;
		var geometryDescription : GeometryDescriptionQuery;
		var queryFilter : QueryFilter;
		var geometryDescriptionResult : GeometryDescriptionResult;
		var cameraWorldTransform : Transform;
		var cameraWorldTransformPos : Vector4;
		var playerPos : Vector4;
		var vecToTarget : Vector4;
		var refPosition : Vector4;
		var heightDifference : Float;
		var spatialQueriesSystem : SpatialQueriesSystem;
		var isTargetReachable : Bool;
		var maxObstacleExtent : Float;
		var topPointFromPlayer : Vector4;
		var topPointFromTarget : Vector4;
		scriptedPuppet = ( ( ScriptedPuppet )( target ) );
		spatialQueriesSystem = GameInstance.GetSpatialQueriesSystem( owner.GetGame() );
		if( !( target ) || ( scriptedPuppet && scriptedPuppet.IsCivilian() ) )
		{
			return false;
		}
		GameInstance.GetCameraSystem( owner.GetGame() ).GetActiveCameraWorldTransform( cameraWorldTransform );
		cameraWorldTransformPos = Transform.GetPosition( cameraWorldTransform );
		QueryFilter.AddGroup( queryFilter, 'Static' );
		QueryFilter.AddGroup( queryFilter, 'PlayerBlocker' );
		playerPos = owner.GetWorldPosition();
		if( Vector4.IsZero( targetPos ) )
		{
			targetPos = target.GetWorldPosition();
		}
		if( !( depthTestForObstacle ) && ( targetPos.Z > cameraWorldTransformPos.Z ) )
		{
			heightDifference = targetPos.Z - cameraWorldTransformPos.Z;
		}
		targetPos.Z += ( cameraWorldTransformPos.Z - playerPos.Z );
		refPosition = cameraWorldTransformPos;
		refPosition.Z += heightDifference;
		if( depthTestForObstacle )
		{
			targetPos.Z -= 0.5;
			refPosition.Z -= 0.5;
		}
		vecToTarget = targetPos - refPosition;
		vecToTarget -= ( Vector4.Normalize( vecToTarget ) * 0.03 );
		geometryDescription = new GeometryDescriptionQuery;
		geometryDescription.refPosition = refPosition;
		geometryDescription.refDirection = vecToTarget;
		geometryDescription.filter = queryFilter;
		geometryDescription.primitiveDimension = Vector4( 0.1, 0.1, 0.1, 0.0 );
		geometryDescription.primitiveRotation = Quaternion.BuildFromDirectionVector( vecToTarget, Transform.GetUp( cameraWorldTransform ) );
		geometryDescription.maxDistance = Vector4.Length( vecToTarget );
		geometryDescription.maxExtent = 0.01;
		geometryDescription.probingPrecision = 0.01;
		geometryDescription.probingMaxDistanceDiff = 0.01;
		geometryDescription.probingMaxHeight = 0.30000001;
		geometryDescription.maxProbes = 1;
		geometryDescription.AddFlag( worldgeometryDescriptionQueryFlags.DistanceVector );
		if( depthTestForObstacle )
		{
			geometryDescription.primitiveDimension.X = 0.30000001;
			geometryDescription.probingMaxHeight = 0.89999998;
		}
		geometryDescriptionResult = spatialQueriesSystem.GetGeometryDescriptionSystem().QueryExtents( geometryDescription );
		isTargetReachable = geometryDescriptionResult.queryStatus == worldgeometryDescriptionQueryStatus.NoGeometry;
		if( ( !( depthTestForObstacle ) || ( depthTestForObstacle && isTargetReachable ) ) && ( ( scriptedPuppet && scriptedPuppet.IsHumanoid() ) || target.IsTurret() ) )
		{
			heightDifference = 0.15000001;
			targetPos = target.GetWorldPosition();
			targetPos.Z += heightDifference;
			refPosition = playerPos;
			refPosition.Z += heightDifference;
			vecToTarget = targetPos - refPosition;
			geometryDescription.refPosition = refPosition;
			geometryDescription.refDirection = vecToTarget;
			geometryDescription.maxDistance = Vector4.Length( vecToTarget );
			geometryDescription.primitiveDimension.X = 0.1;
			geometryDescription.probingMaxHeight = ( cameraWorldTransformPos.Z - playerPos.Z ) / 2.0;
			if( depthTestForObstacle )
			{
				geometryDescription.AddFlag( worldgeometryDescriptionQueryFlags.TopPoint );
			}
			geometryDescriptionResult = spatialQueriesSystem.GetGeometryDescriptionSystem().QueryExtents( geometryDescription );
			isObstacleVaultable = geometryDescriptionResult.queryStatus != worldgeometryDescriptionQueryStatus.NoGeometry;
			if( isObstacleVaultable && depthTestForObstacle )
			{
				topPointFromPlayer = geometryDescriptionResult.topPoint;
				geometryDescription.refPosition = targetPos;
				geometryDescription.refDirection = -( vecToTarget );
				geometryDescriptionResult = spatialQueriesSystem.GetGeometryDescriptionSystem().QueryExtents( geometryDescription );
				topPointFromTarget = geometryDescriptionResult.topPoint;
				maxObstacleExtent = TDB.GetFloat( T"playerStateMachineLocomotion.vault.maxExtent" );
				isObstacleVaultable = Vector4.Length( topPointFromTarget - topPointFromPlayer ) <= maxObstacleExtent;
				return isObstacleVaultable && isTargetReachable;
			}
		}
		return isTargetReachable;
	}

}

