bool UAblAbilityTaskDependencyValidator::HasCircularDependency(const TWeakObjectPtr<UAblAbilityTaskValidatorContext>& Context, const UAblAbilityTask* TaskToCheck, const UAblAbilityTask* CurrentTask) const
{
	static TArray<const UAblAbilityTask*> Stack;
	Stack.Push(CurrentTask);

	bool Result = false;
	if (CurrentTask->HasDependencies())
	{
		for (const UAblAbilityTask* Dependency : CurrentTask->GetTaskDependencies())
		{
			if (Dependency == TaskToCheck)
			{
				FText ErrorMessage = FText::FormatOrdered(LOCTEXT("AbilityValidatorTaskCirculeDependency", "Circular Dependency Detected for Task {0}, dependency stack:"), TaskToCheck->GetTaskName());
				Context->AddError(ErrorMessage);

				Result = true;
				break;
			}
			else
			{
				if (!Stack.Contains(Dependency) && HasCircularDependency(Context, TaskToCheck, Dependency))
				{
					Context->AddError(FText::FormatOrdered(LOCTEXT("AbilityValidatorTaskCircularDependencyStack", "Task {0} depends on {1}"), CurrentTask->GetTaskName(), Dependency->GetTaskName()));

					Result = true;
					break;
				}
			}
		}
	}

	Stack.Pop();
	return Result;
}
void UAblAbilityTaskOrderValidator::Validate(const TWeakObjectPtr<UAblAbilityTaskValidatorContext>& Context, const TWeakObjectPtr<const UAblAbilityTask>& Task) const
{
	const float AbilityLength = Context->GetAbility()->GetLength();
	const TArray<UAblAbilityTask*>& AllTasks = Context->GetAbility()->GetTasks();
	int j = 0;
	float TimeDifference = 0.0f;
	for (int i = 0; i < AllTasks.Num() - 1; ++i)
	{
		for (j = i + 1; j < AllTasks.Num(); ++j)
		{
			TimeDifference = AllTasks[j]->GetStartTime() - AllTasks[i]->GetStartTime();
			// The 0.0 check is just there to catch any constants people may be using.
			if (TimeDifference != 0.0f && TimeDifference < KINDA_SMALL_NUMBER)
			{
				Context->AddError(LOCTEXT("AbilityValidatorTasksOutOfOrder", "Tasks aren't in sequential order. Please resave the ability."));
				return; // We can early out.
			}
		}
	}
}
void UAblAbilityTaskPlayAnimationAssetValidator::Validate(const TWeakObjectPtr<UAblAbilityTaskValidatorContext>& Context, const TWeakObjectPtr<const UAblAbilityTask>& Task) const
{
	const UAblPlayAnimationTask* PlayAnimationTask = CastChecked<UAblPlayAnimationTask>(Task.Get());

	const UAnimationAsset* AnimationAsset = PlayAnimationTask->GetAnimationAsset();
	if (AnimationAsset == nullptr)
	{
		Context->AddError(LOCTEXT("AbilityValidatorPlayAnimationNoAsset", "Play Animation Task does not have a animation asset specified."));
	}
	else if (!AnimationAsset->IsA<UAnimSequenceBase>() && !AnimationAsset->IsA<UAnimMontage>())
	{
		Context->AddError(LOCTEXT("AbilityValidatorPlayAnimationWrongAssetType", "Play Animation Task has an invalid asset specified. Only Sequences and Montages are supported."));
	}

	if (PlayAnimationTask->GetAnimationMode() == EAblPlayAnimationTaskAnimMode::AbilityAnimationNode)
	{
		// None is our default value, so if that hasn't changed we need to tell the user.
		if (PlayAnimationTask->GetStateMachineName().IsNone())
		{
			Context->AddError(LOCTEXT("AbilityValidatorPlayAnimationInvalidStateMachine", "Play Animation Task is set to use Ability Animation Mode, but doesn't have a State Machine Name specified."));
		}

		if (PlayAnimationTask->GetAbilityStateName().IsNone())
		{
			Context->AddError(LOCTEXT("AbilityValidatorPlayAnimationInvalidAbilityNode", "Play Animation Task is set to use Ability Animation Mode, but doesn't have a Ability State Name specified."));
		}
	}
}