bool UBTCompositeNode::DoDecoratorsAllowExecution(class UBehaviorTreeComponent* OwnerComp, int32 InstanceIdx, int32 ChildIdx) const { const FBTCompositeChild& ChildInfo = Children[ChildIdx]; bool bResult = true; if (ChildInfo.Decorators.Num() == 0) { return bResult; } FBehaviorTreeInstance& MyInstance = OwnerComp->InstanceStack[InstanceIdx]; if (ChildInfo.DecoratorOps.Num() == 0) { // simple check: all decorators must agree for (int32 i = 0; i < ChildInfo.Decorators.Num(); i++) { const UBTDecorator* TestDecorator = ChildInfo.Decorators[i]; const bool bIsAllowed = TestDecorator->CanExecute(OwnerComp, TestDecorator->GetNodeMemory<uint8>(MyInstance)); OwnerComp->StoreDebuggerSearchStep(TestDecorator, InstanceIdx, bIsAllowed); if (!bIsAllowed) { UE_VLOG(OwnerComp->GetOwner(), LogBehaviorTree, Verbose, TEXT("Child[%d] execution forbidden by %s"), ChildIdx, *UBehaviorTreeTypes::DescribeNodeHelper(TestDecorator)); bResult = false; break; } } } else { // advanced check: follow decorator logic operations (composite decorator on child link) UE_VLOG(OwnerComp->GetOwner(), LogBehaviorTree, Verbose, TEXT("Child[%d] execution test with logic operations"), ChildIdx); TArray<FOperationStackInfo> OperationStack; FString Indent; // debugger data collection: // - get index of each decorator from main AND test, they will match graph nodes // - if first operator is not AND it means, that there's only single composite decorator on line // - while updating top level stack, grab index of first failed node int32 NodeDecoratorIdx = INDEX_NONE; int32 FailedDecoratorIdx = INDEX_NONE; bool bShouldStoreNodeIndex = true; for (int32 i = 0; i < ChildInfo.DecoratorOps.Num(); i++) { const FBTDecoratorLogic& DecoratorOp = ChildInfo.DecoratorOps[i]; if (IsLogicOp(DecoratorOp)) { OperationStack.Add(FOperationStackInfo(DecoratorOp)); Indent += TEXT(" "); UE_VLOG(OwnerComp->GetOwner(), LogBehaviorTree, Verbose, TEXT("%spushed %s:%d"), *Indent, *DescribeLogicOp(DecoratorOp.Operation), DecoratorOp.Number); } else if (DecoratorOp.Operation == EBTDecoratorLogic::Test) { const bool bHasOverride = OperationStack.Num() ? OperationStack.Last().bHasForcedResult : false; const bool bCurrentOverride = OperationStack.Num() ? OperationStack.Last().bForcedResult : false; // debugger: store first decorator of graph node if (bShouldStoreNodeIndex) { bShouldStoreNodeIndex = false; NodeDecoratorIdx = DecoratorOp.Number; } UBTDecorator* TestDecorator = ChildInfo.Decorators[DecoratorOp.Number]; const bool bIsAllowed = bHasOverride ? bCurrentOverride : TestDecorator->CanExecute(OwnerComp, TestDecorator->GetNodeMemory<uint8>(MyInstance)); UE_VLOG(OwnerComp->GetOwner(), LogBehaviorTree, Verbose, TEXT("%s%s %s: %s"), *Indent, bHasOverride ? TEXT("skipping") : TEXT("testing"), *UBehaviorTreeTypes::DescribeNodeHelper(TestDecorator), bIsAllowed ? TEXT("allowed") : TEXT("forbidden")); bResult = UpdateOperationStack(OwnerComp, Indent, OperationStack, bIsAllowed, FailedDecoratorIdx, NodeDecoratorIdx, bShouldStoreNodeIndex); if (OperationStack.Num() == 0) { UE_VLOG(OwnerComp->GetOwner(), LogBehaviorTree, Verbose, TEXT("finished execution test: %s"), bResult ? TEXT("allowed") : TEXT("forbidden")); OwnerComp->StoreDebuggerSearchStep(ChildInfo.Decorators[FMath::Max(0, FailedDecoratorIdx)], InstanceIdx, bResult); break; } } } } return bResult; }