// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/UnrealType.h" #include "BehaviorTree/BehaviorTreeTypes.h" #include "BehaviorTree/BlackboardData.h" #include "BehaviorTree/BehaviorTree.h" #include "BehaviorTree/Blackboard/BlackboardKeyType_Bool.h" #include "BehaviorTree/Blackboard/BlackboardKeyType_Int.h" #include "BehaviorTree/Composites/BTComposite_Selector.h" #include "BehaviorTree/Composites/BTComposite_Sequence.h" #include "BehaviorTree/Composites/BTComposite_SimpleParallel.h" #include "BehaviorTree/Decorators/BTDecorator_Loop.h" #include "BehaviorTree/Tasks/BTTask_RunBehavior.h" #include "BehaviorTree/TestBTDecorator_Blackboard.h" #include "BehaviorTree/TestBTDecorator_Blueprint.h" #include "BehaviorTree/TestBTDecorator_DelayedAbort.h" #include "BehaviorTree/TestBTDecorator_LoopUntil.h" #include "BehaviorTree/TestBTDecorator_NodeMemoryValidator.h" #include "BehaviorTree/TestBTService_Log.h" #include "BehaviorTree/TestBTService_BTStopAction.h" #include "BehaviorTree/TestBTTask_LatentWithFlags.h" #include "BehaviorTree/TestBTTask_Log.h" #include "BehaviorTree/TestBTTask_SetFlag.h" #include "BehaviorTree/TestBTTask_SetValue.h" #include "BehaviorTree/TestBTTask_SetValuesWithLogs.h" #include "BehaviorTree/TestBTTask_BTStopAction.h" #include "BehaviorTree/TestBTTask_ToggleFlag.h" struct FBTBuilder { static UBehaviorTree& CreateBehaviorTree() { UBlackboardData* BB = NewObject(); FBlackboardEntry KeyData; KeyData.EntryName = TEXT("Bool1"); KeyData.KeyType = NewObject(); BB->Keys.Add(KeyData); KeyData.EntryName = TEXT("Bool2"); KeyData.KeyType = NewObject(); BB->Keys.Add(KeyData); KeyData.EntryName = TEXT("Bool3"); KeyData.KeyType = NewObject(); BB->Keys.Add(KeyData); KeyData.EntryName = TEXT("Bool4"); KeyData.KeyType = NewObject(); BB->Keys.Add(KeyData); KeyData.EntryName = TEXT("Int"); KeyData.KeyType = NewObject(); BB->Keys.Add(KeyData); KeyData.EntryName = TEXT("Int2"); KeyData.KeyType = NewObject(); BB->Keys.Add(KeyData); BB->UpdateParentKeys(); UBehaviorTree* TreeOb = NewObject(); TreeOb->BlackboardAsset = BB; return *TreeOb; } static UBehaviorTree& CreateBehaviorTree(UBehaviorTree& ParentTree) { UBehaviorTree* TreeOb = NewObject(); TreeOb->BlackboardAsset = ParentTree.BlackboardAsset; return *TreeOb; } static UBTComposite_Selector& AddSelector(UBehaviorTree& TreeOb) { UBTComposite_Selector* NodeOb = NewObject(&TreeOb); NodeOb->InitializeFromAsset(TreeOb); TreeOb.RootNode = NodeOb; return *NodeOb; } static UBTComposite_Selector& AddSelector(UBTCompositeNode& ParentNode) { UBTComposite_Selector* NodeOb = NewObject(ParentNode.GetTreeAsset()); NodeOb->InitializeFromAsset(*ParentNode.GetTreeAsset()); const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildComposite = NodeOb; return *NodeOb; } static UBTComposite_Sequence& AddSequence(UBehaviorTree& TreeOb) { UBTComposite_Sequence* NodeOb = NewObject(&TreeOb); NodeOb->InitializeFromAsset(TreeOb); TreeOb.RootNode = NodeOb; return *NodeOb; } static UBTComposite_Sequence& AddSequence(UBTCompositeNode& ParentNode) { UBTComposite_Sequence* NodeOb = NewObject(ParentNode.GetTreeAsset()); NodeOb->InitializeFromAsset(*ParentNode.GetTreeAsset()); const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildComposite = NodeOb; return *NodeOb; } static UBTComposite_SimpleParallel& AddParallel(UBehaviorTree& TreeOb, EBTParallelMode::Type Mode) { UBTComposite_SimpleParallel* NodeOb = NewObject(&TreeOb); NodeOb->FinishMode = Mode; NodeOb->InitializeFromAsset(TreeOb); TreeOb.RootNode = NodeOb; return *NodeOb; } static UBTComposite_SimpleParallel& AddParallel(UBTCompositeNode& ParentNode, EBTParallelMode::Type Mode) { UBTComposite_SimpleParallel* NodeOb = NewObject(ParentNode.GetTreeAsset()); NodeOb->FinishMode = Mode; NodeOb->InitializeFromAsset(*ParentNode.GetTreeAsset()); const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildComposite = NodeOb; return *NodeOb; } template static T& AddTask(UBTCompositeNode& ParentNode, UClass* TaskClass = T::StaticClass()) { T* TaskNode = NewObject(ParentNode.GetTreeAsset()); ParentNode.Children.Emplace_GetRef().ChildTask = TaskNode; return *TaskNode; } static void AddTask(UBTCompositeNode& ParentNode, int32 LogIndex, EBTNodeResult::Type NodeResult, int32 ExecutionTicks = 0, int32 LogTickIndex = -1) { UTestBTTask_Log* TaskNode = NewObject(ParentNode.GetTreeAsset()); TaskNode->LogIndex = LogIndex; TaskNode->LogResult = NodeResult; TaskNode->ExecutionTicks = ExecutionTicks; TaskNode->LogTickIndex = LogTickIndex; const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildTask = TaskNode; } static void AddTaskLogFinish(UBTCompositeNode& ParentNode, int32 LogIndex, int32 FinishIndex, EBTNodeResult::Type NodeResult, int32 ExecutionTicks = 0) { UTestBTTask_Log* TaskNode = NewObject(ParentNode.GetTreeAsset()); TaskNode->LogIndex = LogIndex; TaskNode->LogFinished = FinishIndex; TaskNode->LogResult = NodeResult; TaskNode->ExecutionTicks = ExecutionTicks; const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildTask = TaskNode; } static void AddTaskFlagChange(UBTCompositeNode& ParentNode, bool bValue, EBTNodeResult::Type NodeResult, FName BoolKeyName = TEXT("Bool1"), FName BoolOnAbortKeyName = FName(), bool bOnAbortValue = false) { UTestBTTask_SetFlag* TaskNode = NewObject(ParentNode.GetTreeAsset()); TaskNode->bValue = bValue; TaskNode->TaskResult = NodeResult; TaskNode->KeyName = BoolKeyName; TaskNode->OnAbortKeyName = BoolOnAbortKeyName; TaskNode->bOnAbortValue = bOnAbortValue; const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildTask = TaskNode; } static void AddTaskToggleFlag(UBTCompositeNode& ParentNode, EBTNodeResult::Type NodeResult, FName BoolKeyName, int32 NumToggles) { UTestBTTask_ToggleFlag* TaskNode = NewObject(ParentNode.GetTreeAsset()); TaskNode->TaskResult = NodeResult; TaskNode->KeyName = BoolKeyName; TaskNode->NumToggles = NumToggles; const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildTask = TaskNode; } static void AddTaskValueChange(UBTCompositeNode& ParentNode, int32 Value, EBTNodeResult::Type NodeResult, FName IntKeyName = TEXT("Int"), FName IntOnAbortKeyName = FName(), int32 OnAbortValue = 0) { UTestBTTask_SetValue* TaskNode = NewObject(ParentNode.GetTreeAsset()); TaskNode->Value = Value; TaskNode->TaskResult = NodeResult; TaskNode->KeyName = IntKeyName; TaskNode->OnAbortKeyName = IntOnAbortKeyName; TaskNode->OnAbortValue = OnAbortValue; const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildTask = TaskNode; } static void AddTaskValuesChangedWithLogs(UBTCompositeNode& ParentNode, int32 LogIndex, EBTNodeResult::Type NodeResult, int32 Value1, int32 Value2, FName IntKeyName1 = TEXT("Int"), FName IntKeyName2 = TEXT("Int2"), int32 ExecutionTicks1 = 0, int32 ExecutionTicks2 = 0, int32 LogTickIndex = -1, int32 LogFinished = -1, FName IntOnAbortKeyName = FName(), int32 OnAbortValue = 0) { UTestBTTask_SetValuesWithLogs* TaskNode = NewObject(ParentNode.GetTreeAsset()); TaskNode->LogIndex = LogIndex; TaskNode->LogFinished = LogFinished; TaskNode->ExecutionTicks1 = ExecutionTicks1; TaskNode->ExecutionTicks2 = ExecutionTicks2; TaskNode->LogTickIndex = LogTickIndex; TaskNode->KeyName1 = IntKeyName1; TaskNode->Value1 = Value1; TaskNode->KeyName2 = IntKeyName2; TaskNode->Value2 = Value2; TaskNode->OnAbortKeyName = IntOnAbortKeyName; TaskNode->OnAbortValue = OnAbortValue; TaskNode->TaskResult = NodeResult; const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildTask = TaskNode; } static void AddTaskSubtree(UBTCompositeNode& ParentNode, UBehaviorTree* TreeAsset) { UBTTask_RunBehavior* TaskNode = NewObject(ParentNode.GetTreeAsset()); FObjectProperty* SubtreeProp = FindFProperty(UBTTask_RunBehavior::StaticClass(), TEXT("BehaviorAsset")); uint8* SubtreePropData = SubtreeProp->ContainerPtrToValuePtr(TaskNode); SubtreeProp->SetObjectPropertyValue(SubtreePropData, TreeAsset); const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildTask = TaskNode; } static void AddTaskLatentFlags(UBTCompositeNode& ParentNode, EBTNodeResult::Type NodeResult, int32 ExecuteHalfTicks, /** Num of ticks before 'execute start' and `set execute flag` and then the same num of ticks before `execute finish` */ FName ExecuteKeyName, int32 ExecuteLogStart, int32 ExecuteLogFinish, int32 AbortHalfTicks = 0, /** Num of ticks before 'abort start' and `set abort flag` and then the same num of ticks before `abort finish` */ FName AbortKeyName = NAME_None, int32 AbortLogStart = 0, int32 AbortLogFinish = 0, EBTTestChangeFlagBehavior ChangeFlagBehavior = EBTTestChangeFlagBehavior::Set) { UTestBTTask_LatentWithFlags* TaskNode = NewObject(ParentNode.GetTreeAsset()); TaskNode->ExecuteHalfTicks = ExecuteHalfTicks; TaskNode->KeyNameExecute = ExecuteKeyName; TaskNode->LogIndexExecuteStart = ExecuteLogStart; TaskNode->LogIndexExecuteFinish = ExecuteLogFinish; TaskNode->AbortHalfTicks = AbortHalfTicks; TaskNode->KeyNameAbort = AbortKeyName; TaskNode->LogIndexAbortStart = AbortLogStart; TaskNode->LogIndexAbortFinish = AbortLogFinish; TaskNode->ChangeFlagBehavior = ChangeFlagBehavior; const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildTask = TaskNode; } static void AddTaskBTStopAction(UBTCompositeNode& ParentNode, int32 LogIndex, EBTNodeResult::Type NodeResult, EBTTestTaskStopTiming StopTiming, EBTTestStopAction StopAction) { UTestBTTask_BTStopAction* TaskNode = NewObject(ParentNode.GetTreeAsset()); TaskNode->LogIndex = LogIndex; TaskNode->LogResult = NodeResult; TaskNode->StopTiming = StopTiming; TaskNode->StopAction = StopAction; const int32 ChildIdx = ParentNode.Children.AddZeroed(1); ParentNode.Children[ChildIdx].ChildTask = TaskNode; } template static T& WithDecorator(UBTCompositeNode& ParentNode, UClass* DecoratorClass = T::StaticClass()) { T* DecoratorOb = NewObject(ParentNode.GetTreeAsset()); ParentNode.Children.Last().Decorators.Add(DecoratorOb); // Push additional memory validation decorator for extra coverage ParentNode.Children.Last().Decorators.Add(NewObject(ParentNode.GetTreeAsset())); return *DecoratorOb; } static void WithDecoratorBlackboard(UBTCompositeNode& ParentNode, EBasicKeyOperation::Type Condition, EBTFlowAbortMode::Type Observer, FName BoolKeyName = TEXT("Bool1"), int32 LogIndexBecomeRelevant = -1, int32 LogIndexCeaseRelevant = -1, int32 LogIndexCalculate = -1) { UTestBTDecorator_Blackboard& BBDecorator = WithDecorator(ParentNode); BBDecorator.LogIndexBecomeRelevant= LogIndexBecomeRelevant; BBDecorator.LogIndexCeaseRelevant= LogIndexCeaseRelevant; BBDecorator.LogIndexCalculate = LogIndexCalculate; FByteProperty* ConditionProp = FindFProperty(UBTDecorator_Blackboard::StaticClass(), TEXT("OperationType")); uint8* ConditionPropData = ConditionProp->ContainerPtrToValuePtr(&BBDecorator); ConditionProp->SetIntPropertyValue(ConditionPropData, (uint64)Condition); FByteProperty* ObserverProp = FindFProperty(UBTDecorator_Blackboard::StaticClass(), TEXT("FlowAbortMode")); uint8* ObserverPropData = ObserverProp->ContainerPtrToValuePtr(&BBDecorator); ObserverProp->SetIntPropertyValue(ObserverPropData, (uint64)Observer); FStructProperty* KeyProp = FindFProperty(UBTDecorator_Blackboard::StaticClass(), TEXT("BlackboardKey")); FBlackboardKeySelector* KeyPropData = KeyProp->ContainerPtrToValuePtr(&BBDecorator); KeyPropData->SelectedKeyName = BoolKeyName; } static void WithDecoratorBlackboard(UBTCompositeNode& ParentNode, EArithmeticKeyOperation::Type Condition, int32 Value, EBTFlowAbortMode::Type Observer, EBTBlackboardRestart::Type NotifyMode, FName IntKeyName = TEXT("Int"), int32 LogIndexBecomeRelevant = -1, int32 LogIndexCeaseRelevant = -1, int32 LogIndexCalculate = -1) { UTestBTDecorator_Blackboard& BBDecorator = WithDecorator(ParentNode); BBDecorator.LogIndexBecomeRelevant = LogIndexBecomeRelevant; BBDecorator.LogIndexCeaseRelevant = LogIndexCeaseRelevant; BBDecorator.LogIndexCalculate = LogIndexCalculate; FByteProperty* ConditionProp = FindFProperty(UBTDecorator_Blackboard::StaticClass(), TEXT("OperationType")); uint8* ConditionPropData = ConditionProp->ContainerPtrToValuePtr(&BBDecorator); ConditionProp->SetIntPropertyValue(ConditionPropData, (uint64)Condition); FByteProperty* ObserverProp = FindFProperty(UBTDecorator_Blackboard::StaticClass(), TEXT("FlowAbortMode")); uint8* ObserverPropData = ObserverProp->ContainerPtrToValuePtr(&BBDecorator); ObserverProp->SetIntPropertyValue(ObserverPropData, (uint64)Observer); FByteProperty* NotifyModeProp = FindFProperty(UBTDecorator_Blackboard::StaticClass(), TEXT("NotifyObserver")); uint8* NotifyModePropData = NotifyModeProp->ContainerPtrToValuePtr(&BBDecorator); NotifyModeProp->SetIntPropertyValue(NotifyModePropData, (uint64)NotifyMode); FIntProperty* ConditionValueProp = FindFProperty(UBTDecorator_Blackboard::StaticClass(), TEXT("IntValue")); uint8* ConditionValuePropData = ConditionValueProp->ContainerPtrToValuePtr(&BBDecorator); ConditionValueProp->SetIntPropertyValue(ConditionValuePropData, (uint64)Value); FStructProperty* KeyProp = FindFProperty(UBTDecorator_Blackboard::StaticClass(), TEXT("BlackboardKey")); FBlackboardKeySelector* KeyPropData = KeyProp->ContainerPtrToValuePtr(&BBDecorator); KeyPropData->SelectedKeyName = IntKeyName; } static void WithDecoratorDelayedAbort(UBTCompositeNode& ParentNode, int32 NumTicks, bool bAbortOnlyOnce = true) { UTestBTDecorator_DelayedAbort& AbortDecorator = WithDecorator(ParentNode); AbortDecorator.DelayTicks = NumTicks; AbortDecorator.bOnlyOnce = bAbortOnlyOnce; } static void WithDecoratorBlueprint(UBTCompositeNode& ParentNode, EBTFlowAbortMode::Type Observer, EBPConditionType BPConditionType = EBPConditionType::TrueCondition, int32 LogIndexBecomeRelevant = -1, int32 LogIndexCeaseRelevant = -1, int32 LogIndexCalculate = -1, FName ObservingKeyName = NAME_None ) { UTestBTDecorator_Blueprint& BPDecorator = WithDecorator(ParentNode); BPDecorator.LogIndexBecomeRelevant = LogIndexBecomeRelevant; BPDecorator.LogIndexCeaseRelevant = LogIndexCeaseRelevant; BPDecorator.LogIndexCalculate = LogIndexCalculate; BPDecorator.BPConditionType = BPConditionType; BPDecorator.ObservingKeyName = ObservingKeyName; FByteProperty* ObserverProp = FindFProperty(UBTDecorator_Blackboard::StaticClass(), TEXT("FlowAbortMode")); uint8* ObserverPropData = ObserverProp->ContainerPtrToValuePtr(&BPDecorator); ObserverProp->SetIntPropertyValue(ObserverPropData, (uint64)Observer); } static void WithDecoratorLoop(UBTCompositeNode& ParentNode, int32 NumLoops = 2) { UBTDecorator_Loop& LoopDecorator = WithDecorator(ParentNode); LoopDecorator.NumLoops = NumLoops; } static void WithDecoratorLoopUntil(UBTCompositeNode& ParentNode, EBTNodeResult::Type RequiredResult) { UTestBTDecorator_LoopUntil& LoopUntilDecorator = WithDecorator(ParentNode); LoopUntilDecorator.SetRequiredResult(RequiredResult); } template static T& WithService(UBTCompositeNode& ParentNode, UClass* ServiceClass = T::StaticClass()) { T* ServiceOb = NewObject(ParentNode.GetTreeAsset()); ParentNode.Services.Add(ServiceOb); return *ServiceOb; } static void WithServiceLog(UBTCompositeNode& ParentNode, int32 ActivationIndex, int32 DeactivationIndex, int32 TickIndex = INDEX_NONE, FName TickBoolKeyName = NAME_None, bool bCallTickOnSearchStart = false, FName BecomeRelevantBoolKeyName = NAME_None, FName CeaseRelevantBoolKeyName = NAME_None, bool bToggleValue = false, int32 TicksDelaySetKeyNameTick = 0) { UTestBTService_Log& LogService = WithService(ParentNode); LogService.LogActivation = ActivationIndex; LogService.LogDeactivation = DeactivationIndex; LogService.LogTick = TickIndex; LogService.SetFlagOnTick(TickBoolKeyName, bCallTickOnSearchStart); LogService.KeyNameBecomeRelevant = BecomeRelevantBoolKeyName; LogService.KeyNameCeaseRelevant = CeaseRelevantBoolKeyName; LogService.bToggleValue = bToggleValue; LogService.TicksDelaySetKeyNameTick = TicksDelaySetKeyNameTick; } static void WithServiceBTStopAction(UBTCompositeNode& ParentNode, int32 LogIndex, EBTTestServiceStopTiming StopTiming, EBTTestStopAction StopAction) { UTestBTService_BTStopAction& Service = WithService(ParentNode); Service.LogIndex = LogIndex; Service.StopTiming = StopTiming; Service.StopAction = StopAction; } template static T& WithTaskService(UBTCompositeNode& ParentNode, UClass* ServiceClass = T::StaticClass()) { UBTTaskNode* TaskNode = ParentNode.Children.Last().ChildTask; check(TaskNode); T* ServiceOb = NewObject(ParentNode.GetTreeAsset()); TaskNode->Services.Add(ServiceOb); return *ServiceOb; } static void WithTaskServiceLog(UBTCompositeNode& ParentNode, int32 ActivationIndex, int32 DeactivationIndex, int32 TickIndex = INDEX_NONE, FName TickBoolKeyName = NAME_None, bool bCallTickOnSearchStart = false, FName BecomeRelevantBoolKeyName = NAME_None, FName CeaseRelevantBoolKeyName = NAME_None, bool bToggleValue = false) { UTestBTService_Log& LogService = WithTaskService(ParentNode); LogService.LogActivation = ActivationIndex; LogService.LogDeactivation = DeactivationIndex; LogService.LogTick = TickIndex; LogService.SetFlagOnTick(TickBoolKeyName, bCallTickOnSearchStart); LogService.KeyNameBecomeRelevant = BecomeRelevantBoolKeyName; LogService.KeyNameCeaseRelevant = CeaseRelevantBoolKeyName; LogService.bToggleValue = bToggleValue; } static void WithTaskServiceBTStopAction(UBTCompositeNode& ParentNode, int32 LogIndex, EBTTestServiceStopTiming StopTiming, EBTTestStopAction StopAction) { UTestBTService_BTStopAction& Service = WithTaskService(ParentNode); Service.LogIndex = LogIndex; Service.StopTiming = StopTiming; Service.StopAction = StopAction; } };