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.")); } } }