FHitResult AGameplayAbilityTargetActor_GroundTrace::PerformTrace(AActor* InSourceActor)
{
    static const FName LineTraceSingleName(TEXT("AGameplayAbilityTargetActor_GroundTrace"));
    bool bTraceComplex = false;

    FCollisionQueryParams Params(LineTraceSingleName, bTraceComplex);
    Params.bReturnPhysicalMaterial = true;
    Params.bTraceAsyncScene = true;
    Params.AddIgnoredActor(InSourceActor);

    FVector TraceStart = StartLocation.GetTargetingTransform().GetLocation();// InSourceActor->GetActorLocation();
    FVector TraceEnd;
    AimWithPlayerController(InSourceActor, Params, TraceStart, TraceEnd);		//Effective on server and launching client only

    // ------------------------------------------------------

    FHitResult ReturnHitResult;
    //Use a line trace initially to see where the player is actually pointing
    LineTraceWithFilter(ReturnHitResult, InSourceActor->GetWorld(), Filter, TraceStart, TraceEnd, TraceProfile.Name, Params);
    //Default to end of trace line if we don't hit anything.
    if (!ReturnHitResult.bBlockingHit)
    {
        ReturnHitResult.Location = TraceEnd;
    }

    //Second trace, straight down. Consider using InSourceActor->GetWorld()->NavigationSystem->ProjectPointToNavigation() instead of just going straight down in the case of movement abilities (flag/bool).
    TraceStart = ReturnHitResult.Location - (TraceEnd - TraceStart).GetSafeNormal();		//Pull back very slightly to avoid scraping down walls
    TraceEnd = TraceStart;
    TraceStart.Z += CollisionHeightOffset;
    TraceEnd.Z -= 99999.0f;
    LineTraceWithFilter(ReturnHitResult, InSourceActor->GetWorld(), Filter, TraceStart, TraceEnd, TraceProfile.Name, Params);
    //if (!ReturnHitResult.bBlockingHit) then our endpoint may be off the map. Hopefully this is only possible in debug maps.

    bLastTraceWasGood = true;		//So far, we're good. If we need a ground spot and can't find one, we'll come back.

    //Use collision shape to find a valid ground spot, if appropriate
    if (CollisionShape.ShapeType != ECollisionShape::Line)
    {
        ReturnHitResult.Location.Z += CollisionHeightOffset;		//Rise up out of the ground
        TraceStart = InSourceActor->GetActorLocation();
        TraceEnd = ReturnHitResult.Location;
        TraceStart.Z += CollisionHeightOffset;
        bLastTraceWasGood = AdjustCollisionResultForShape(TraceStart, TraceEnd, Params, ReturnHitResult);
        if (bLastTraceWasGood)
        {
            ReturnHitResult.Location.Z -= CollisionHeightOffset;	//Undo the artificial height adjustment
        }
    }

    if (AGameplayAbilityWorldReticle* LocalReticleActor = ReticleActor.Get())
    {
        LocalReticleActor->SetIsTargetValid(bLastTraceWasGood);
        LocalReticleActor->SetActorLocation(ReturnHitResult.Location);
    }

    // Reset the trace start so the target data uses the correct origin
    ReturnHitResult.TraceStart = StartLocation.GetTargetingTransform().GetLocation();

    return ReturnHitResult;
}
void AGameplayAbilityTargetActor_Trace::AimWithPlayerController(const AActor* InSourceActor, FCollisionQueryParams Params, const FVector& TraceStart, FVector& OutTraceEnd, bool bIgnorePitch) const
{
	if (!OwningAbility) // Server and launching client only
	{
		return;
	}

	APlayerController* PC = OwningAbility->GetCurrentActorInfo()->PlayerController.Get();
	check(PC);

	FVector ViewStart;
	FRotator ViewRot;
	PC->GetPlayerViewPoint(ViewStart, ViewRot);

	const FVector ViewDir = ViewRot.Vector();
	FVector ViewEnd = ViewStart + (ViewDir * MaxRange);

	ClipCameraRayToAbilityRange(ViewStart, ViewDir, TraceStart, MaxRange, ViewEnd);

	FHitResult HitResult;
	LineTraceWithFilter(HitResult, InSourceActor->GetWorld(), Filter, ViewStart, ViewEnd, TraceProfile.Name, Params);

	const bool bUseTraceResult = HitResult.bBlockingHit && (FVector::DistSquared(TraceStart, HitResult.Location) <= (MaxRange * MaxRange));

	const FVector AdjustedEnd = (bUseTraceResult) ? HitResult.Location : ViewEnd;

	FVector AdjustedAimDir = (AdjustedEnd - TraceStart).GetSafeNormal();
	if (AdjustedAimDir.IsZero())
	{
		AdjustedAimDir = ViewDir;
	}

	if (!bTraceAffectsAimPitch && bUseTraceResult)
	{
		FVector OriginalAimDir = (ViewEnd - TraceStart).GetSafeNormal();

		if (!OriginalAimDir.IsZero())
		{
			// Convert to angles and use original pitch
			const FRotator OriginalAimRot = OriginalAimDir.Rotation();

			FRotator AdjustedAimRot = AdjustedAimDir.Rotation();
			AdjustedAimRot.Pitch = OriginalAimRot.Pitch;

			AdjustedAimDir = AdjustedAimRot.Vector();
		}
	}

	OutTraceEnd = TraceStart + (AdjustedAimDir * MaxRange);
}
FHitResult AGameplayAbilityTargetActor_SingleLineTrace::PerformTrace(AActor* InSourceActor)
{
	static const FName LineTraceSingleName(TEXT("AGameplayAbilityTargetActor_SingleLineTrace"));
	bool bTraceComplex = false;
	TArray<AActor*> ActorsToIgnore;

	ActorsToIgnore.Add(InSourceActor);

	FCollisionQueryParams Params(LineTraceSingleName, bTraceComplex);
	Params.bReturnPhysicalMaterial = true;
	Params.bTraceAsyncScene = true;
	Params.AddIgnoredActors(ActorsToIgnore);

	FVector TraceStart = StartLocation.GetTargetingTransform().GetLocation();// InSourceActor->GetActorLocation();
	FVector TraceEnd;
	AimWithPlayerController(InSourceActor, Params, TraceStart, TraceEnd);		//Effective on server and launching client only

	// ------------------------------------------------------

	FHitResult ReturnHitResult;
	LineTraceWithFilter(ReturnHitResult, InSourceActor->GetWorld(), Filter, TraceStart, TraceEnd, TraceProfile.Name, Params);
	//Default to end of trace line if we don't hit anything.
	if (!ReturnHitResult.bBlockingHit)
	{
		ReturnHitResult.Location = TraceEnd;
	}
	if (AGameplayAbilityWorldReticle* LocalReticleActor = ReticleActor.Get())
	{
		LocalReticleActor->SetActorLocation(ReturnHitResult.Location);
		LocalReticleActor->SetIsTargetAnActor(ReturnHitResult.bBlockingHit && (ReturnHitResult.Actor != NULL));
	}

	if (bDebug)
	{
		DrawDebugLine(GetWorld(), TraceStart, TraceEnd, FColor::Green);
		DrawDebugSphere(GetWorld(), TraceEnd, 100.0f, 16, FColor::Green);
	}
	return ReturnHitResult;
}