FVector UTKMathFunctionLibrary::GetVelocityAtPoint(UPrimitiveComponent* Target, FVector Point, FName BoneName, bool DrawDebugInfo)
{
	
	//FTransform Transform = Target->GetComponentTransform();
	//FVector LocalLinearVelocity = Transform.InverseTransformVectorNoScale(Target->GetPhysicsLinearVelocity());
	//FVector LocalAngularVelocity = Transform.InverseTransformVectorNoScale(Target->GetPhysicsAngularVelocity());
	//FVector ResultPointVelocity = LocalLinearVelocity + FVector::CrossProduct(FVector::DegreesToRadians(LocalAngularVelocity), Transform.InverseTransformVectorNoScale(Point - Target->GetCenterOfMass()));
	

	if (!Target) return FVector::ZeroVector;

	//You can actually get it from the physx body instance instead.
	FBodyInstance* BI = Target->GetBodyInstance(BoneName);
	if (BI && BI->IsValidBodyInstance())
	{
		FVector PointVelocity = BI->GetUnrealWorldVelocityAtPoint(Point);

		UWorld* TheWorld = Target->GetWorld();
		if (DrawDebugInfo && TheWorld)
		{ 
			FColor DefaultColor(255,200,0);
			DrawDebugPoint(TheWorld, Point, 10, DefaultColor);
			DrawDebugString(TheWorld, Point, FString::SanitizeFloat(PointVelocity.Size()), NULL, FColor::White, 0.0f);
		}

		return PointVelocity;
	}
	return FVector::ZeroVector;
}
void FSoundSource::DrawDebugInfo()
{
	// Draw 3d Debug information about this source, if enabled
	FAudioDeviceManager* DeviceManager = GEngine->GetAudioDeviceManager();

	if (DeviceManager && DeviceManager->IsVisualizeDebug3dEnabled())
	{
		const uint32 AudioComponentID = WaveInstance->ActiveSound->GetAudioComponentID();

		if (AudioComponentID > 0)
		{
			DECLARE_CYCLE_STAT(TEXT("FAudioThreadTask.DrawSourceDebugInfo"), STAT_AudioDrawSourceDebugInfo, STATGROUP_TaskGraphTasks);

			USoundBase* Sound = WaveInstance->ActiveSound->GetSound();
			const FVector Location = WaveInstance->Location;

			const bool bSpatialized = Buffer->NumChannels == 2 && WaveInstance->bUseSpatialization;
			const FVector LeftChannelSourceLoc = LeftChannelSourceLocation;
			const FVector RightChannelSourceLoc = RightChannelSourceLocation;

			FAudioThread::RunCommandOnGameThread([AudioComponentID, Sound, bSpatialized, Location, LeftChannelSourceLoc, RightChannelSourceLoc]()
			{
				UAudioComponent* AudioComponent = UAudioComponent::GetAudioComponentFromID(AudioComponentID);
				if (AudioComponent)
				{
					UWorld* SoundWorld = AudioComponent->GetWorld();
					if (SoundWorld)
					{
						FRotator SoundRotation = AudioComponent->GetComponentRotation();
						DrawDebugCrosshairs(SoundWorld, Location, SoundRotation, 20.0f, FColor::White, false, -1.0f, SDPG_Foreground);

						if (bSpatialized)
						{
							DrawDebugCrosshairs(SoundWorld, LeftChannelSourceLoc, SoundRotation, 20.0f, FColor::Red, false, -1.0f, SDPG_Foreground);
							DrawDebugCrosshairs(SoundWorld, RightChannelSourceLoc, SoundRotation, 20.0f, FColor::Green, false, -1.0f, SDPG_Foreground);
						}

						const FString Name = Sound->GetName();
						DrawDebugString(SoundWorld, AudioComponent->GetComponentLocation() + FVector(0, 0, 32), *Name, nullptr, FColor::White, 0.033, false);
					}
				}
			}, GET_STATID(STAT_AudioDrawSourceDebugInfo));
		}
	}
}
void UGameplayCueManager::HandleGameplayCue(AActor* TargetActor, FGameplayTag GameplayCueTag, EGameplayCueEvent::Type EventType, const FGameplayCueParameters& Parameters)
{
	if (DisableGameplayCues)
	{
		return;
	}

	if (GameplayCueRunOnDedicatedServer == 0 && IsDedicatedServerForGameplayCue())
	{
		return;
	}

#if WITH_EDITOR
	if (GIsEditor && TargetActor == nullptr && UGameplayCueManager::PreviewComponent)
	{
		TargetActor = Cast<AActor>(AActor::StaticClass()->GetDefaultObject());
	}
#endif

	if (TargetActor == nullptr)
	{
		ABILITY_LOG(Warning, TEXT("UGameplayCueManager::HandleGameplayCue called on null TargetActor. GameplayCueTag: %s."), *GameplayCueTag.ToString());
		return;
	}

	IGameplayCueInterface* GameplayCueInterface = Cast<IGameplayCueInterface>(TargetActor);
	bool bAcceptsCue = true;
	if (GameplayCueInterface)
	{
		bAcceptsCue = GameplayCueInterface->ShouldAcceptGameplayCue(TargetActor, GameplayCueTag, EventType, Parameters);
	}

	if (DisplayGameplayCues)
	{
		FString DebugStr = FString::Printf(TEXT("%s - %s"), *GameplayCueTag.ToString(), *EGameplayCueEventToString(EventType) );
		FColor DebugColor = FColor::Green;
		DrawDebugString(TargetActor->GetWorld(), FVector(0.f, 0.f, 100.f), DebugStr, TargetActor, DebugColor, DisplayGameplayCueDuration);
	}

	CurrentWorld = TargetActor->GetWorld();

	// Don't handle gameplay cues when world is tearing down
	if (!GetWorld() || GetWorld()->bIsTearingDown)
	{
		return;
	}

	// Give the global set a chance
	check(GlobalCueSet);
	if (bAcceptsCue)
	{
		GlobalCueSet->HandleGameplayCue(TargetActor, GameplayCueTag, EventType, Parameters);
	}

	// Use the interface even if it's not in the map
	if (GameplayCueInterface && bAcceptsCue)
	{
		GameplayCueInterface->HandleGameplayCue(TargetActor, GameplayCueTag, EventType, Parameters);
	}

	CurrentWorld = nullptr;
}
// Runs calculations on friction component, applies friction force to effected component and returns reaction forces(forces that can effect track or a wheel)
void UMMTFrictionComponent::ApplyFriction(const FVector& ContactPointLocation, const FVector& ContactPointNormal, const FVector& InducedVelocity, const FVector& PreNormalForceAtPoint,
        const EPhysicalSurface& PhysicalSurface, const float& NumberOfContactPoints, const float& DeltaTime, FVector& NormalizedReactionForce, FVector& RollingFrictionForce)
{
    // Gather stats
    SCOPE_CYCLE_COUNTER(STAT_MMTFrictionApply);

    float NormalForceAtContactPoint = PreNormalForceAtPoint.ProjectOnTo(ContactPointNormal).Size();

    // Check if Effected Component Mesh reference is valid and escape early otherwise
    if (!IsValid(EffectedComponentMesh))
    {
        GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, FString::Printf(TEXT("%s->%s component's EffectedComponentMesh reference is invalid!"), *GetOwner()->GetName(), *GetName()));
        UE_LOG(LogTemp, Warning, TEXT("%s->%s component's EffectedComponentMesh reference is invalid!"), *GetOwner()->GetName(), *GetName());
        NormalizedReactionForce = FVector::ZeroVector;
        RollingFrictionForce = FVector::ZeroVector;
        return;
    }

    //Find relative velocity of the friction surface and ground/another object at point
    FVector RelativeVelocityAtPoint = EffectedComponentMesh->GetPhysicsLinearVelocityAtPoint(ContactPointLocation) + InducedVelocity + FrictionSurfaceVelocity;
    RelativeVelocityAtPoint = RelativeVelocityAtPoint.VectorPlaneProject(RelativeVelocityAtPoint, ContactPointNormal);

    //filter out oscillations when vehicle is standing still but friction overshoots
    RelativeVelocityAtPoint = (PrevRelativeVelocityAtPoint + RelativeVelocityAtPoint) * 0.5;
    PrevRelativeVelocityAtPoint = RelativeVelocityAtPoint;

    // early exit if velocity is too low to consider as vehicle most likely standing still
    if (RelativeVelocityAtPoint.Size() < 1.0f)
    {
        NormalizedReactionForce = FVector::ZeroVector;
        RollingFrictionForce = FVector::ZeroVector;
        return;
    }

    //Calculate static and kinetic friction coefficients, taking into account velocity direction and friction ellipse
    float MuStatic;
    float MuKinetic;
    UMMTBPFunctionLibrary::GetMuFromFrictionElipse(RelativeVelocityAtPoint.GetSafeNormal(), ReferenceFrameTransform.TransformVector(FVector(1.0f, 1.0f, 1.0f)),
            MuXStatic, MuXKinetic, MuYStatic, MuYKinetic, MuStatic, MuKinetic);

    //Calculate "stopping force" which is amount of force necessary to completely remove velocity of the object
    FVector	StoppingForce = ((RelativeVelocityAtPoint * (-1.0f) * EffectedComponentMesh->GetMass()) / DeltaTime) / NumberOfContactPoints;

    //Static friction threshold
    float MuStaticByLoad = NormalForceAtContactPoint * MuStatic;

    //Friction Force that will be applied to effected mesh component
    FVector ApplicationForce;
    if (StoppingForce.Size() >= MuStaticByLoad)
    {
        ApplicationForce = StoppingForce.GetClampedToSize(0.0f, NormalForceAtContactPoint * MuKinetic);
        if (IsDebugMode)
        {
            DrawDebugString(GetWorld(), ContactPointLocation, FString("Kinetic Friction"), nullptr, FColor::Magenta, 0.0f, false);
        }
    }
    else
    {
        ApplicationForce = StoppingForce.GetClampedToSize(0.0f, MuStaticByLoad);
        if (IsDebugMode)
        {
            DrawDebugString(GetWorld(), ContactPointLocation, FString("Static Friction"), nullptr, FColor::Red, 0.0f, false);
        }
    }

    //Apply friction force
    UMMTBPFunctionLibrary::MMTAddForceAtLocationComponent(EffectedComponentMesh, ApplicationForce, ContactPointLocation);

    //Calculate Reaction force
    NormalizedReactionForce = (ApplicationForce * (-1.0f)) / EffectedComponentMesh->GetMass();


    //Calculate Rolling Friction
    float RollingFrictionCoefficient = FPhysicalSurfaceRollingFrictionCoefficient().RollingFrictionCoefficient;

    for (int32 i = 0; i < PhysicsSurfaceResponse.Num(); i++)
    {
        if (PhysicsSurfaceResponse[i].PhysicalSurface == PhysicalSurface)
        {
            RollingFrictionCoefficient = PhysicsSurfaceResponse[i].RollingFrictionCoefficient;
            break;
        }
    }

    RollingFrictionForce = RelativeVelocityAtPoint.GetSafeNormal() * NormalForceAtContactPoint * RollingFrictionCoefficient;

    if (IsDebugMode)
    {
        DrawDebugLine(GetWorld(), ContactPointLocation, ContactPointLocation + ApplicationForce * 0.005f, FColor::Yellow, false, 0.0f, 0, 3.0f);
        DrawDebugLine(GetWorld(), ContactPointLocation, ContactPointLocation + RollingFrictionForce * 0.005f, FColor::Green, false, 0.0f, 0, 3.0f);
        DrawDebugString(GetWorld(), ContactPointLocation+FVector(0.0f, 0.0f, 50.0f), (PhysicalSurfaceEnum ? PhysicalSurfaceEnum->GetEnumName(PhysicalSurface) : FString("<Invalid Enum>")), nullptr, FColor::Cyan, 0.0f, false);
        //DrawDebugString(GetWorld(), ContactPointLocation + FVector(0.0f, 0.0f, 25.0f), FString("Normal Force: ") + FString::SanitizeFloat(NormalForceAtContactPoint), nullptr, FColor::Turquoise, 0.0f, false);
    }
}