void FGameplayDebuggerCategory_AI::DrawPawnIcons(UWorld* World, AActor* DebugActor, APawn* SkipPawn, FGameplayDebuggerCanvasContext& CanvasContext)
{
	FString FailsafeIcon = TEXT("/Engine/EngineResources/AICON-Green.AICON-Green");
	for (FConstPawnIterator It = World->GetPawnIterator(); It; ++It)
	{
		const APawn* ItPawn = *It;
		if (IsValid(ItPawn) && SkipPawn != ItPawn)
		{
			const FVector IconLocation = ItPawn->GetActorLocation() + FVector(0, 0, ItPawn->GetSimpleCollisionHalfHeight());
			const AAIController* ItAI = Cast<const AAIController>(ItPawn->GetController());

			FString DebugIconPath = IsValid(ItAI) ? ItAI->GetDebugIcon() : FailsafeIcon;
			if (CanvasContext.IsLocationVisible(IconLocation) && DebugIconPath.Len())
			{
				UTexture2D* IconTexture = (UTexture2D*)StaticLoadObject(UTexture2D::StaticClass(), NULL, *DebugIconPath, NULL, LOAD_NoWarn | LOAD_Quiet, NULL);
				FCanvasIcon CanvasIcon = UCanvas::MakeIcon(IconTexture);
				if (CanvasIcon.Texture)
				{
					const FVector2D ScreenLoc = CanvasContext.ProjectLocation(IconLocation);
					const float IconSize = (DebugActor == ItPawn) ? 32.0f : 16.0f;

					CanvasContext.DrawIcon(FColor::White, CanvasIcon, ScreenLoc.X, ScreenLoc.Y - IconSize, IconSize / CanvasIcon.Texture->GetSurfaceWidth());
				}
			}
		}
	}
}
void FGameplayDebuggerCategory_AI::DrawData(APlayerController* OwnerPC, FGameplayDebuggerCanvasContext& CanvasContext)
{
	const bool bReducedMode = IsSimulateInEditor();
	bShowCategoryName = !bReducedMode;

	UWorld* MyWorld = OwnerPC->GetWorld();
	AActor* SelectedActor = FindLocalDebugActor();

	DrawPawnIcons(MyWorld, SelectedActor, OwnerPC ? OwnerPC->GetPawn() : nullptr, CanvasContext);
	if (bReducedMode)
	{
		if (DataPack.bHasController)
		{
			DrawPath(MyWorld);
		}
	}
	else
	{
		if (SelectedActor)
		{
			DrawOverheadInfo(*SelectedActor, CanvasContext);
		}

		DrawPath(MyWorld);
	}

	const bool bShowClassNames = !bReducedMode || DataPack.bHasController;
	if (bShowClassNames)
	{
		CanvasContext.Printf(TEXT("Controller Name: {yellow}%s"), *DataPack.ControllerName);
		CanvasContext.Printf(TEXT("Pawn Name: {yellow}%s"), *DataPack.PawnName);
	}

	if (DataPack.bIsUsingCharacter)
	{
		CanvasContext.Printf(TEXT("Movement Mode: {yellow}%s{white}, Base: {yellow}%s"), *DataPack.MovementModeInfo, *DataPack.MovementBaseInfo);
		CanvasContext.Printf(TEXT("NavData: {yellow}%s{white}, Path following: {yellow}%s"), *DataPack.NavDataInfo, *DataPack.PathFollowingInfo);
	}

	if (DataPack.bIsUsingBehaviorTree)
	{
		CanvasContext.Printf(TEXT("Behavior: {yellow}%s{white}, Tree: {yellow}%s"), *DataPack.CurrentAIState, *DataPack.CurrentAIAssets);
		CanvasContext.Printf(TEXT("Active task: {yellow}%s"), *DataPack.CurrentAITask);
	}

	if (DataPack.bIsUsingGameplayTasks)
	{
		if (DataPack.NumTickingTasks > 0)
		{
			CanvasContext.Printf(TEXT("Ticking tasks: {yellow}%d%s"), DataPack.NumTickingTasks, *DataPack.TickingTaskInfo);
		}

		CanvasContext.Printf(TEXT("Gameplay tasks: {yellow}%d%s"), DataPack.NumTasksInQueue, *DataPack.TaskQueueInfo);
	}

	if (DataPack.bIsUsingCharacter)
	{
		CanvasContext.Printf(TEXT("Montage: {yellow}%s"), *DataPack.MontageInfo);
	}
}
void FGameplayDebuggerCategory_AI::DrawOverheadInfo(AActor& DebugActor, FGameplayDebuggerCanvasContext& CanvasContext)
{
	const FVector OverheadLocation = DebugActor.GetActorLocation() + FVector(0, 0, DebugActor.GetSimpleCollisionHalfHeight());
	if (CanvasContext.IsLocationVisible(OverheadLocation))
	{
		FGameplayDebuggerCanvasContext OverheadContext(CanvasContext);
		OverheadContext.Font = GEngine->GetSmallFont();
		OverheadContext.FontRenderInfo.bEnableShadow = true;

		const FVector2D ScreenLoc = OverheadContext.ProjectLocation(OverheadLocation);
		FString ActorDesc = FString::Printf(TEXT("{yellow}%s {white}%s"), *DataPack.ControllerName, *DataPack.PawnName);

		float SizeX = 0.0f, SizeY = 0.0f;
		OverheadContext.MeasureString(ActorDesc, SizeX, SizeY);
		OverheadContext.PrintAt(ScreenLoc.X - (SizeX * 0.5f), ScreenLoc.Y - (SizeY * 1.2f), ActorDesc);
	}
}
void FGameplayDebuggerShape::Draw(UWorld* World, FGameplayDebuggerCanvasContext& Context)
{
	FVector DescLocation;
	switch (Type)
	{
	case EGameplayDebuggerShape::Point:
		if (ShapeData.Num() == 2 && ShapeData[1].X > 0)
		{
			DrawDebugSphere(World, ShapeData[0], ShapeData[1].X, 16, Color);
			DescLocation = ShapeData[0];
		}
		break;

	case EGameplayDebuggerShape::Segment:
		if (ShapeData.Num() == 3 && ShapeData[2].X > 0)
		{
			DrawDebugLine(World, ShapeData[0], ShapeData[1], Color, false, -1.0f, 0, ShapeData[2].X);
			DescLocation = (ShapeData[0] + ShapeData[1]) * 0.5f;
		}
		break;

	case EGameplayDebuggerShape::Box:
		if (ShapeData.Num() == 2)
		{
			DrawDebugBox(World, ShapeData[0], ShapeData[1], Color);
			DescLocation = ShapeData[0];
		}
		break;

	case EGameplayDebuggerShape::Cone:
		if (ShapeData.Num() == 3 && ShapeData[2].X > 0)
		{
			DrawDebugCone(World, ShapeData[0], ShapeData[1], ShapeData[2].X, PI * 0.5f, PI * 0.5f, 16, Color);
			DescLocation = ShapeData[0];
		}
		break;

	case EGameplayDebuggerShape::Cylinder:
		if (ShapeData.Num() == 2)
		{
			DrawDebugCylinder(World, ShapeData[0] - FVector(0, 0, ShapeData[1].Z), ShapeData[0] + FVector(0, 0, ShapeData[1].Z), ShapeData[1].X, 16, Color);
			DescLocation = ShapeData[0];
		}
		break;

	case EGameplayDebuggerShape::Capsule:
		if (ShapeData.Num() == 2)
		{
			DrawDebugCapsule(World, ShapeData[0], ShapeData[1].Z, ShapeData[1].X, FQuat::Identity, Color);
			DescLocation = ShapeData[0];
		}
		break;

	case EGameplayDebuggerShape::Polygon:
		if (ShapeData.Num() > 0)
		{
			FVector MidPoint = FVector::ZeroVector;
			TArray<int32> Indices;
			for (int32 Idx = 0; Idx < ShapeData.Num(); Idx++)
			{
				Indices.Add(Idx);
				MidPoint += ShapeData[Idx];
			}

			DrawDebugMesh(World, ShapeData, Indices, Color);
			DescLocation = MidPoint / ShapeData.Num();
		}
		break;

	default:
		break;
	}

	if (Description.Len() && Context.IsLocationVisible(DescLocation))
	{
		const FVector2D ScreenLoc = Context.ProjectLocation(DescLocation);
		Context.PrintAt(ScreenLoc.X, ScreenLoc.Y, Color, Description);
	}
}