├── Resources └── Icon128.png ├── .gitignore ├── Config └── DefaultAIFlow.ini ├── Source ├── AIFlow │ ├── Private │ │ ├── AIFlowLogChannels.cpp │ │ ├── AIFlowTags.cpp │ │ ├── AIFlowModule.h │ │ ├── Types │ │ │ ├── FlowBlackboardEntry.cpp │ │ │ └── ConfigurableEnumProperty.cpp │ │ ├── AIFlowModule.cpp │ │ ├── Interfaces │ │ │ ├── FlowPerSpawnedActorInterface.cpp │ │ │ ├── FlowSpawnedActorInterface.cpp │ │ │ └── FlowBlackboardInterface.cpp │ │ ├── AddOns │ │ │ ├── AIFlowNodeAddOn.cpp │ │ │ ├── FlowNodeAddOn_InjectComponentsBase.cpp │ │ │ ├── FlowNodeAddOn_InjectComponents.cpp │ │ │ └── FlowNodeAddOn_ConfigureSpawnedActorBlackboard.cpp │ │ ├── Nodes │ │ │ ├── AIFlowNode.cpp │ │ │ ├── AIFlowNode_ExecutionRollGuaranteed.cpp │ │ │ ├── AIFlowNode_ExecutionRollWeighted.cpp │ │ │ ├── FlowNode_EnsureActorHasBlackboard.cpp │ │ │ ├── FlowNode_SetBlackboardValuesOnActor.cpp │ │ │ └── FlowNode_GetBlackboardValues.cpp │ │ ├── Blackboard │ │ │ ├── FlowBlackboardEntryValue_String.cpp │ │ │ ├── FlowBlackboardEntryValue_Vector.cpp │ │ │ ├── FlowBlackboardEntryValue_Rotator.cpp │ │ │ ├── FlowBlackboardEntryValue_Name.cpp │ │ │ ├── FlowBlackboardEntryValue_Bool.cpp │ │ │ ├── FlowBlackboardEntryValue.cpp │ │ │ ├── FlowBlackboardEntryValue_Int.cpp │ │ │ ├── FlowBlackboardEntryValue_Float.cpp │ │ │ ├── FlowBlackboardEntryValue_Class.cpp │ │ │ └── FlowBlackboardEntryValue_Object.cpp │ │ └── AIFlowAsset.cpp │ ├── Public │ │ ├── AIFlowLogChannels.h │ │ ├── AIFlowTags.h │ │ ├── Interfaces │ │ │ ├── AIFlowOwnerInterface.h │ │ │ ├── FlowPerSpawnedActorInterface.h │ │ │ ├── FlowBlackboardAssetProvider.h │ │ │ ├── FlowSpawnedActorInterface.h │ │ │ └── FlowBlackboardInterface.h │ │ ├── AddOns │ │ │ ├── AIFlowNodeAddOn.h │ │ │ ├── FlowNodeAddOn_InjectComponents.h │ │ │ ├── FlowNodeAddOn_InjectComponentsBase.h │ │ │ ├── FlowNodeAddOn_ConfigureSpawnedActorBlackboard.h │ │ │ └── FlowNodeAddOn_PredicateCompareBlackboardValue.h │ │ ├── Nodes │ │ │ ├── AIFlowNode.h │ │ │ ├── AIFlowNode_ExecutionRollGuaranteed.h │ │ │ ├── AIFlowNode_ExecutionRollWeighted.h │ │ │ ├── FlowNode_SetBlackboardValuesOnActor.h │ │ │ ├── FlowNode_EnsureActorHasBlackboard.h │ │ │ ├── FlowNode_GetBlackboardValues.h │ │ │ ├── FlowNode_SetBlackboardValues.h │ │ │ └── FlowNode_SetBlackboardValuesV2.h │ │ ├── Types │ │ │ ├── ConfigurableEnumProperty.h │ │ │ └── FlowBlackboardEntry.h │ │ ├── Blackboard │ │ │ ├── FlowBlackboardEntryValue_Bool.h │ │ │ ├── FlowBlackboardEntryValue_Name.h │ │ │ ├── FlowBlackboardEntryValue_String.h │ │ │ ├── FlowBlackboardEntryValue_Vector.h │ │ │ ├── FlowBlackboardEntryValue_Rotator.h │ │ │ ├── FlowBlackboardEntryValue_Int.h │ │ │ ├── FlowBlackboardEntryValue_Float.h │ │ │ ├── FlowBlackboardEntryValue_Class.h │ │ │ ├── FlowBlackboardEntryValue_Enum.h │ │ │ ├── FlowBlackboardEntryValue_Object.h │ │ │ └── FlowBlackboardEntryValue.h │ │ ├── AIFlowAsset.h │ │ └── AIFlowActorBlackboardHelper.h │ └── AIFlow.Build.cs └── AIFlowEditor │ ├── Private │ ├── AIFlowEditorLogChannels.cpp │ ├── Asset │ │ ├── AIFlowAssetFactory.h │ │ ├── AssetDefinition_AIFlowAsset.cpp │ │ └── AIFlowAssetFactory.cpp │ ├── AIFlowEditorModule.h │ ├── DetailCustomizations │ │ ├── FlowBlackboardEntryCustomization.h │ │ ├── ConfigurableEnumPropertyCustomization.h │ │ ├── ConfigurableEnumPropertyCustomization.cpp │ │ └── FlowBlackboardEntryCustomization.cpp │ └── AIFlowEditorModule.cpp │ ├── Public │ ├── AIFlowEditorLogChannels.h │ └── Asset │ │ └── AssetDefinition_AIFlowAsset.h │ └── AIFlowEditor.Build.cs ├── README.md ├── AIFlow.uplugin └── LICENSE /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LindyHopperGT/AIFlowGraph/HEAD/Resources/Icon128.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.vs 3 | /_ReSharper.Caches 4 | /Binaries 5 | /Intermediate 6 | 7 | *.sln 8 | 9 | *.exe 10 | *.exp 11 | *.lib 12 | *.patch_* 13 | *.pdb 14 | *.target 15 | -------------------------------------------------------------------------------- /Config/DefaultAIFlow.ini: -------------------------------------------------------------------------------- 1 | [CoreRedirects] 2 | +ClassRedirects=(OldName="/Script/AIFlow.FlowNode_SetBlackboardValuesOnActorByKey",NewName="/Script/AIFlow.FlowNode_SetBlackboardValuesOnActor") 3 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/AIFlowLogChannels.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AIFlowLogChannels.h" 4 | 5 | DEFINE_LOG_CATEGORY(LogAIFlow); 6 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Private/AIFlowEditorLogChannels.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AIFlowEditorLogChannels.h" 4 | 5 | DEFINE_LOG_CATEGORY(LogAIFlowEditor); 6 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/AIFlowTags.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AIFlowTags.h" 4 | 5 | UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Blackboard, "Flow.NodeStyle.Node.Blackboard"); 6 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/AIFlowLogChannels.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "Logging/LogMacros.h" 6 | 7 | AIFLOW_API DECLARE_LOG_CATEGORY_EXTERN(LogAIFlow, Log, All) 8 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Public/AIFlowEditorLogChannels.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "Logging/LogMacros.h" 6 | 7 | DECLARE_LOG_CATEGORY_EXTERN(LogAIFlowEditor, Log, All) 8 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/AIFlowTags.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "NativeGameplayTags.h" 6 | 7 | namespace FlowNodeStyle 8 | { 9 | AIFLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Blackboard); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/AIFlowModule.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleInterface.h" 6 | 7 | class FAIFlowModule final : public IModuleInterface 8 | { 9 | public: 10 | virtual void StartupModule() override; 11 | virtual void ShutdownModule() override; 12 | }; 13 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Types/FlowBlackboardEntry.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Types/FlowBlackboardEntry.h" 4 | 5 | // UFlowBlackboardFunctionLibrary Implementation 6 | 7 | FName UFlowBlackboardFunctionLibrary::AutoConvert_FlowBlackboardEntryKeyToName(const FFlowBlackboardEntry& FlowBlackboardEntry) 8 | { 9 | return FlowBlackboardEntry.GetKeyName(); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/AIFlowModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AIFlowModule.h" 4 | 5 | #include "Modules/ModuleManager.h" 6 | 7 | #define LOCTEXT_NAMESPACE "AIFlow" 8 | 9 | void FAIFlowModule::StartupModule() 10 | { 11 | } 12 | 13 | void FAIFlowModule::ShutdownModule() 14 | { 15 | } 16 | 17 | #undef LOCTEXT_NAMESPACE 18 | 19 | IMPLEMENT_MODULE(FAIFlowModule, AIFlow) 20 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Types/ConfigurableEnumProperty.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Types/ConfigurableEnumProperty.h" 4 | 5 | // UConfigurableEnumPropertyFunctionLibrary Implementation 6 | 7 | uint8 UConfigurableEnumPropertyFunctionLibrary::AutoConvert_ConfigurableEnumPropertyToEnum(const FConfigurableEnumProperty& EnumValue) 8 | { 9 | const uint64 EnumValueAsInt = EnumValue.EnumClass->GetValueByName(EnumValue.Value); 10 | 11 | // At least For Now(tm) Blueprint Enums want to be uint8's 12 | return static_cast(EnumValueAsInt); 13 | } 14 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Private/Asset/AIFlowAssetFactory.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Asset/FlowAssetFactory.h" 7 | #include "AIFlowAssetFactory.generated.h" 8 | 9 | UCLASS(HideCategories = Object) 10 | class AIFLOWEDITOR_API UAIFlowAssetFactory : public UFlowAssetFactory 11 | { 12 | GENERATED_UCLASS_BODY() 13 | virtual bool ConfigureProperties() override; 14 | virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; 15 | }; 16 | -------------------------------------------------------------------------------- /Source/AIFlow/AIFlow.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | using UnrealBuildTool; 4 | 5 | public class AIFlow : ModuleRules 6 | { 7 | public AIFlow(ReadOnlyTargetRules target) : base(target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new[] 12 | { 13 | "AIModule", 14 | "Flow", 15 | // #UE56Fix 16 | //"StructUtils", 17 | // 18 | }); 19 | 20 | PrivateDependencyModuleNames.AddRange(new[] 21 | { 22 | "Core", 23 | "CoreUObject", 24 | "Engine", 25 | "GameplayTags", 26 | }); 27 | } 28 | } -------------------------------------------------------------------------------- /Source/AIFlowEditor/Public/Asset/AssetDefinition_AIFlowAsset.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "Asset/AssetDefinition_FlowAsset.h" 6 | #include "AssetDefinition_AIFlowAsset.generated.h" 7 | 8 | /** 9 | * AI Flow Asset - subclass of FlowAsset that adds support for AI Blackboards 10 | */ 11 | UCLASS() 12 | class AIFLOWEDITOR_API UAssetDefinition_AIFlowAsset : public UAssetDefinition_FlowAsset 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | virtual FText GetAssetDisplayName() const override; 18 | virtual TSoftClassPtr GetAssetClass() const override; 19 | }; 20 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/AIFlowEditor.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | using UnrealBuildTool; 4 | 5 | public class AIFlowEditor : ModuleRules 6 | { 7 | public AIFlowEditor(ReadOnlyTargetRules target) : base(target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new[] 12 | { 13 | "AIFlow", 14 | "AssetDefinition", 15 | "Flow", 16 | "FlowEditor", 17 | }); 18 | 19 | PrivateDependencyModuleNames.AddRange(new[] 20 | { 21 | "AIModule", 22 | "Core", 23 | "CoreUObject", 24 | "DetailCustomizations", 25 | "UnrealEd" 26 | }); 27 | } 28 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AIFlowGraph 2 | Flow plug-in for Unreal Engine is a design-agnostic event node editor. It provides a graph editor tailored for scripting flow of events in virtual worlds. It's based on a decade of experience with designing and implementing narrative in video games. All we need here is simplicity. 3 | 4 | **AI Flow** plug-in is an extension to the Flow plug-in that adds support for commonly-used AI features like the Environment Query System (EQS) and Blackboards. 5 | 6 | See the Flow plug-in documentation for more information. 7 | 8 | This is a "Preview" release, requires this version of Flow graph (not yet integrated into the mainline Flow): https://github.com/LindyHopperGT/FlowGraph/tree/5.x 9 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Private/Asset/AssetDefinition_AIFlowAsset.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Asset/AssetDefinition_AIFlowAsset.h" 4 | 5 | #include "AIFlowAsset.h" 6 | 7 | #include UE_INLINE_GENERATED_CPP_BY_NAME(AssetDefinition_AIFlowAsset) 8 | 9 | #define LOCTEXT_NAMESPACE "AssetDefinition_AIFlowAsset" 10 | 11 | FText UAssetDefinition_AIFlowAsset::GetAssetDisplayName() const 12 | { 13 | return LOCTEXT("AssetTypeActions_AIFlowAsset", "AI Flow Asset"); 14 | } 15 | 16 | TSoftClassPtr UAssetDefinition_AIFlowAsset::GetAssetClass() const 17 | { 18 | return UAIFlowAsset::StaticClass(); 19 | } 20 | 21 | #undef LOCTEXT_NAMESPACE 22 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Interfaces/FlowPerSpawnedActorInterface.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Interfaces/FlowPerSpawnedActorInterface.h" 4 | #include "AddOns/FlowNodeAddOn.h" 5 | 6 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPerSpawnedActorInterface) 7 | 8 | bool IFlowPerSpawnedActorInterface::ImplementsInterfaceSafe(const UFlowNodeAddOn* AddOnTemplate) 9 | { 10 | if (!IsValid(AddOnTemplate)) 11 | { 12 | return false; 13 | } 14 | 15 | UClass* AddOnClass = AddOnTemplate->GetClass(); 16 | if (AddOnClass->ImplementsInterface(UFlowPerSpawnedActorInterface::StaticClass())) 17 | { 18 | return true; 19 | } 20 | 21 | return false; 22 | } 23 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Interfaces/AIFlowOwnerInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "UObject/Interface.h" 6 | 7 | #include "AIFlowOwnerInterface.generated.h" 8 | 9 | // Forward Declarations 10 | class UAIFlowAsset; 11 | 12 | UINTERFACE(MinimalAPI, BlueprintType, DisplayName = "AI Flow Owner Interface") 13 | class UAIFlowOwnerInterface : public UInterface 14 | { 15 | GENERATED_BODY() 16 | }; 17 | 18 | class AIFLOW_API IAIFlowOwnerInterface 19 | { 20 | GENERATED_BODY() 21 | 22 | public: 23 | 24 | // Called at the end of InitializeInstance for a new AIFlowAsset 25 | UFUNCTION(BlueprintNativeEvent, Category = "FlowAsset") 26 | void PostFlowAssetInitializeInstance(UAIFlowAsset* AIFlowAsset); 27 | virtual void PostFlowAssetInitializeInstance_Implementation(UAIFlowAsset* AIFlowAsset) { } 28 | }; 29 | -------------------------------------------------------------------------------- /AIFlow.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion" : 3, 3 | "Version" : 1.6, 4 | "FriendlyName" : "AI Flow", 5 | "Description" : "An extension to the Flow plugin which introduces AI utilities including Blackboard and EQS.", 6 | "Category" : "Gameplay", 7 | "CreatedByURL" : "https://github.com/MothCocoon/FlowGraph/graphs/contributors", 8 | "DocsURL" : "https://github.com/MothCocoon/FlowGraph/wiki", 9 | "MarketplaceURL" : "", 10 | "SupportURL": "https://discord.gg/zMtMQ2vUUa", 11 | "EnabledByDefault" : false, 12 | "CanContainContent" : false, 13 | "IsBetaVersion" : false, 14 | "Installed" : false, 15 | "Modules" : 16 | [ 17 | { 18 | "Name" : "AIFlow", 19 | "Type" : "Runtime", 20 | "LoadingPhase" : "PreDefault" 21 | }, 22 | { 23 | "Name" : "AIFlowEditor", 24 | "Type" : "Editor", 25 | "LoadingPhase" : "Default" 26 | } 27 | ], 28 | "Plugins": [ 29 | { 30 | "Name": "Flow", 31 | "Enabled": true 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /Source/AIFlow/Public/AddOns/AIFlowNodeAddOn.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AddOns/FlowNodeAddOn.h" 6 | #include "Interfaces/FlowBlackboardAssetProvider.h" 7 | #include "Interfaces/FlowBlackboardInterface.h" 8 | 9 | #include "AIFlowNodeAddOn.generated.h" 10 | 11 | UCLASS(Abstract, EditInlineNew, Blueprintable) 12 | class AIFLOW_API UAIFlowNodeAddOn 13 | : public UFlowNodeAddOn 14 | , public IFlowBlackboardAssetProvider 15 | , public IFlowBlackboardInterface 16 | { 17 | GENERATED_BODY() 18 | 19 | public: 20 | 21 | // IFlowBlackboardInterface 22 | virtual UBlackboardComponent* GetBlackboardComponent() const override; 23 | // -- 24 | 25 | // IBlackboardAssetProvider 26 | virtual UBlackboardData* GetBlackboardAsset() const override; 27 | // -- 28 | 29 | #if WITH_EDITOR 30 | // IFlowBlackboardAssetProvider 31 | virtual UBlackboardData* GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const override; 32 | // -- 33 | #endif // WITH_EDITOR 34 | }; 35 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/AddOns/FlowNodeAddOn_InjectComponents.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowNodeAddOn_InjectComponentsBase.h" 6 | 7 | #include "FlowNodeAddOn_InjectComponents.generated.h" 8 | 9 | class UActorComponent; 10 | class UFlowInjectComponentsManager; 11 | 12 | // Inject a component on a spawned actor 13 | UCLASS(Blueprintable, meta = (DisplayName = "Inject Components")) 14 | class AIFLOW_API UFlowNodeAddOn_InjectComponents : public UFlowNodeAddOn_InjectComponentsBase 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | 20 | UFlowNodeAddOn_InjectComponents(); 21 | 22 | // IFlowPerSpawnedActorInterface 23 | void FinishedSpawningActor_Implementation(AActor* SpawnedActor, UFlowNodeBase* SpawningFlowNodeBase) override; 24 | // -- 25 | 26 | // UFlowNodeBase 27 | virtual void UpdateNodeConfigText_Implementation() override; 28 | // -- 29 | 30 | protected: 31 | 32 | UPROPERTY(EditAnywhere, Category = Configuration, meta = (ShowOnlyInnerProperties)) 33 | FFlowInjectComponentsHelper InjectComponentsHelper; 34 | }; 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Greg Taylor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Interfaces/FlowPerSpawnedActorInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "UObject/Interface.h" 6 | 7 | #include "FlowPerSpawnedActorInterface.generated.h" 8 | 9 | class AActor; 10 | class UFlowNodeAddOn; 11 | 12 | UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Per-Spawned Actor Interface") 13 | class UFlowPerSpawnedActorInterface : public UInterface 14 | { 15 | GENERATED_BODY() 16 | }; 17 | 18 | class AIFLOW_API IFlowPerSpawnedActorInterface 19 | { 20 | GENERATED_BODY() 21 | 22 | public: 23 | 24 | // SpawningFlowNodeBase is the Node or AddOn that did the spawning or is most relevant to the spawning; 25 | // this is often (but not always) the FlowNode itself. 26 | UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "FlowNode") 27 | void FinishedSpawningActor(AActor* SpawnedActor, UFlowNodeBase* SpawningNodeOrAddOn); 28 | virtual void FinishedSpawningActor_Implementation(AActor* SpawnedActor, UFlowNodeBase* SpawningNodeOrAddOn) { } 29 | 30 | static bool ImplementsInterfaceSafe(const UFlowNodeAddOn* AddOnTemplate); 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Interfaces/FlowBlackboardAssetProvider.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "UObject/Interface.h" 6 | #include "BehaviorTree/BlackboardAssetProvider.h" 7 | 8 | #include "FlowBlackboardAssetProvider.generated.h" 9 | 10 | class UBlackboardData; 11 | class IPropertyHandle; 12 | 13 | // Extension of the IBlackboardAssetProvider interface for AIFlow blackboard asset providers. 14 | UINTERFACE(MinimalAPI, meta = (CannotImplementInterfaceInBlueprint)) 15 | class UFlowBlackboardAssetProvider : public UBlackboardAssetProvider 16 | { 17 | GENERATED_BODY() 18 | }; 19 | 20 | class IFlowBlackboardAssetProvider : public IBlackboardAssetProvider 21 | { 22 | GENERATED_BODY() 23 | 24 | public: 25 | 26 | #if WITH_EDITOR 27 | // Alternate GetBlackboardAsset for editor-use that will allow the supplier to determine which UBlackboardData to return, 28 | // based on the PropertyHandle being serviced. 29 | AIFLOW_API virtual UBlackboardData* GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const { return GetBlackboardAsset(); } 30 | #endif 31 | }; 32 | 33 | 34 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Private/Asset/AIFlowAssetFactory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AIFlowAssetFactory.h" 4 | #include "AIFlowAsset.h" 5 | #include "Graph/FlowGraphSchema.h" 6 | #include "Graph/FlowGraph.h" 7 | 8 | #define LOCTEXT_NAMESPACE "AIFlowAssetFactory" 9 | 10 | UAIFlowAssetFactory::UAIFlowAssetFactory(const FObjectInitializer& ObjectInitializer) 11 | : Super(ObjectInitializer) 12 | { 13 | SupportedClass = UAIFlowAsset::StaticClass(); 14 | } 15 | 16 | bool UAIFlowAssetFactory::ConfigureProperties() 17 | { 18 | const FText TitleText = LOCTEXT("CreateEncounterFlowAssetOptions", "Pick AI Flow Asset Class"); 19 | 20 | return ConfigurePropertiesInternal(TitleText); 21 | } 22 | 23 | UObject* UAIFlowAssetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, 24 | UObject* Context, FFeedbackContext* Warn) 25 | { 26 | UAIFlowAsset* NewFlowAsset = NewObject(InParent, Class, Name, Flags | RF_Transactional, Context); 27 | 28 | UFlowGraph::CreateGraph(NewFlowAsset, UFlowGraphSchema::StaticClass()); 29 | 30 | return NewFlowAsset; 31 | } 32 | 33 | #undef LOCTEXT_NAMESPACE 34 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Private/AIFlowEditorModule.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AssetTypeCategories.h" 6 | #include "IAssetTypeActions.h" 7 | #include "Modules/ModuleInterface.h" 8 | #include "PropertyEditorDelegates.h" 9 | 10 | class AIFLOWEDITOR_API FAIFlowEditorModule : public IModuleInterface 11 | { 12 | public: 13 | static EAssetTypeCategories::Type FlowAssetCategory; 14 | 15 | private: 16 | TArray> RegisteredAssetActions; 17 | TSet CustomClassLayouts; 18 | TSet CustomStructLayouts; 19 | 20 | public: 21 | virtual void StartupModule() override; 22 | virtual void ShutdownModule() override; 23 | 24 | private: 25 | void TrySetFlowNodeDisplayStyleDefaults() const; 26 | 27 | void RegisterAssets(); 28 | void UnregisterAssets(); 29 | 30 | void RegisterDetailCustomizations(); 31 | void UnregisterDetailCustomizations(); 32 | 33 | void RegisterCustomClassLayout(const TSubclassOf Class, const FOnGetDetailCustomizationInstance DetailLayout); 34 | void RegisterCustomStructLayout(const UScriptStruct& Struct, const FOnGetPropertyTypeCustomizationInstance DetailLayout); 35 | }; 36 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Nodes/AIFlowNode.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "Interfaces/FlowBlackboardAssetProvider.h" 6 | #include "Nodes/FlowNode.h" 7 | 8 | #include "Interfaces/FlowBlackboardInterface.h" 9 | 10 | #include "AIFlowNode.generated.h" 11 | 12 | /** 13 | * A base flow node class for Flow Nodes that access Blackboards 14 | */ 15 | UCLASS(Abstract, Blueprintable, BlueprintType, DisplayName = "AI Flow Node") 16 | class AIFLOW_API UAIFlowNode 17 | : public UFlowNode 18 | , public IFlowBlackboardAssetProvider 19 | , public IFlowBlackboardInterface 20 | { 21 | GENERATED_BODY() 22 | 23 | public: 24 | 25 | // IFlowBlackboardInterface 26 | virtual UBlackboardComponent* GetBlackboardComponent() const override; 27 | // -- 28 | 29 | // IBlackboardAssetProvider 30 | virtual UBlackboardData* GetBlackboardAsset() const override; 31 | // -- 32 | 33 | // UFlowNode 34 | virtual int32 GetRandomSeed() const override; 35 | // -- 36 | 37 | #if WITH_EDITOR 38 | // IFlowBlackboardAssetProvider 39 | virtual UBlackboardData* GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const override; 40 | // -- 41 | #endif // WITH_EDITOR 42 | }; 43 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Interfaces/FlowSpawnedActorInterface.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Interfaces/FlowSpawnedActorInterface.h" 4 | #include "GameFramework/Actor.h" 5 | 6 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowSpawnedActorInterface) 7 | 8 | bool IFlowSpawnedActorInterface::ImplementsInterfaceSafe(const UObject* Object) 9 | { 10 | if (!IsValid(Object)) 11 | { 12 | return false; 13 | } 14 | 15 | UClass* ObjectClass = Object->GetClass(); 16 | if (ObjectClass->ImplementsInterface(UFlowSpawnedActorInterface::StaticClass())) 17 | { 18 | return true; 19 | } 20 | 21 | return false; 22 | } 23 | 24 | void IFlowSpawnedActorInterface::DispatchFinishedSpawningActorFromFlowToActorAndComponents(AActor* SpawnedActor, UFlowNodeBase* SpawningNodeOrAddOn) 25 | { 26 | check(SpawnedActor); 27 | 28 | if (ImplementsInterfaceSafe(SpawnedActor)) 29 | { 30 | Execute_FinishedSpawningActorFromFlow(SpawnedActor, SpawnedActor, SpawningNodeOrAddOn); 31 | } 32 | 33 | const TArray Components = SpawnedActor->GetComponentsByInterface(UFlowSpawnedActorInterface::StaticClass()); 34 | for (UActorComponent* Component : Components) 35 | { 36 | Execute_FinishedSpawningActorFromFlow(Component, SpawnedActor, SpawningNodeOrAddOn); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/AddOns/AIFlowNodeAddOn.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AddOns/AIFlowNodeAddOn.h" 4 | #include "FlowAsset.h" 5 | 6 | #include UE_INLINE_GENERATED_CPP_BY_NAME(AIFlowNodeAddOn) 7 | 8 | UBlackboardComponent* UAIFlowNodeAddOn::GetBlackboardComponent() const 9 | { 10 | if (IFlowBlackboardInterface* FlowBlackboardInterface = Cast(GetFlowAsset())) 11 | { 12 | return FlowBlackboardInterface->GetBlackboardComponent(); 13 | } 14 | 15 | return nullptr; 16 | } 17 | 18 | UBlackboardData* UAIFlowNodeAddOn::GetBlackboardAsset() const 19 | { 20 | if (const IFlowBlackboardAssetProvider* BlackboardAssetProvider = Cast(GetFlowAsset())) 21 | { 22 | return BlackboardAssetProvider->GetBlackboardAsset(); 23 | } 24 | 25 | return nullptr; 26 | } 27 | 28 | #if WITH_EDITOR 29 | UBlackboardData* UAIFlowNodeAddOn::GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const 30 | { 31 | if (const IFlowBlackboardAssetProvider* BlackboardAssetProvider = Cast(GetFlowAsset())) 32 | { 33 | return BlackboardAssetProvider->GetBlackboardAssetForPropertyHandle(PropertyHandle); 34 | } 35 | 36 | return nullptr; 37 | } 38 | #endif // WITH_EDITOR 39 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Interfaces/FlowSpawnedActorInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "UObject/Interface.h" 6 | 7 | #include "FlowSpawnedActorInterface.generated.h" 8 | 9 | // Interface for actors that were spawned by a Flow Node/AddOn 10 | // May be implemented by the Actor that is spawned and/or its Components, to receive the FinishedSpawningActor call. 11 | UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow-Spawned Actor Interface") 12 | class UFlowSpawnedActorInterface : public UInterface 13 | { 14 | GENERATED_BODY() 15 | }; 16 | 17 | class AIFLOW_API IFlowSpawnedActorInterface 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | 23 | // Function called for Actors spawned by Flow nodes, after all of Flow-based post-spawn setup has completed. 24 | UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "FlowNode") 25 | void FinishedSpawningActorFromFlow(AActor* SpawnedActor, UFlowNodeBase* SpawningNodeOrAddOn); 26 | virtual void FinishedSpawningActorFromFlow_Implementation(AActor* SpawnedActor, UFlowNodeBase* SpawningNodeOrAddOn) { } 27 | 28 | // Function called by the flow node or addon that spawned an actor to dispatch the FinishedSpawningActorFromFlow function to 29 | // the SpawnedActor (if it implements this interface) and/or its Components (if they implement this interface). 30 | static void DispatchFinishedSpawningActorFromFlowToActorAndComponents(AActor* SpawnedActor, UFlowNodeBase* SpawningNodeOrAddOn); 31 | 32 | static bool ImplementsInterfaceSafe(const UObject* Object); 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Nodes/AIFlowNode.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Nodes/AIFlowNode.h" 4 | #include "AIFlowAsset.h" 5 | 6 | #include UE_INLINE_GENERATED_CPP_BY_NAME(AIFlowNode) 7 | 8 | UBlackboardData* UAIFlowNode::GetBlackboardAsset() const 9 | { 10 | if (const IFlowBlackboardAssetProvider* BlackboardAssetProvider = Cast(GetFlowAsset())) 11 | { 12 | return BlackboardAssetProvider->GetBlackboardAsset(); 13 | } 14 | 15 | return nullptr; 16 | } 17 | 18 | int32 UAIFlowNode::GetRandomSeed() const 19 | { 20 | const int32 SuperRandomSeed = Super::GetRandomSeed(); 21 | 22 | if (const UAIFlowAsset* AIFlowAsset = Cast(GetFlowAsset())) 23 | { 24 | return HashCombine(AIFlowAsset->GetRandomSeed(), SuperRandomSeed); 25 | } 26 | 27 | return SuperRandomSeed; 28 | } 29 | 30 | #if WITH_EDITOR 31 | UBlackboardData* UAIFlowNode::GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const 32 | { 33 | if (const IFlowBlackboardAssetProvider* BlackboardAssetProvider = Cast(GetFlowAsset())) 34 | { 35 | return BlackboardAssetProvider->GetBlackboardAssetForPropertyHandle(PropertyHandle); 36 | } 37 | 38 | return nullptr; 39 | } 40 | #endif // WITH_EDITOR 41 | 42 | UBlackboardComponent* UAIFlowNode::GetBlackboardComponent() const 43 | { 44 | IFlowBlackboardInterface* FlowBlackboardInterface = Cast(GetFlowAsset()); 45 | if (FlowBlackboardInterface) 46 | { 47 | return FlowBlackboardInterface->GetBlackboardComponent(); 48 | } 49 | 50 | return nullptr; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/AddOns/FlowNodeAddOn_InjectComponentsBase.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AddOns/AIFlowNodeAddOn.h" 6 | #include "Interfaces/FlowPerSpawnedActorInterface.h" 7 | #include "Types/FlowInjectComponentsHelper.h" 8 | 9 | #include "GameplayTagContainer.h" 10 | 11 | #include "FlowNodeAddOn_InjectComponentsBase.generated.h" 12 | 13 | class UActorComponent; 14 | class UFlowInjectComponentsManager; 15 | 16 | // Inject a component on a spawned actor 17 | UCLASS(Abstract, Blueprintable, meta = (DisplayName = "Inject Components (base class)")) 18 | class AIFLOW_API UFlowNodeAddOn_InjectComponentsBase 19 | : public UAIFlowNodeAddOn 20 | , public IFlowPerSpawnedActorInterface 21 | { 22 | GENERATED_BODY() 23 | 24 | public: 25 | 26 | UFlowNodeAddOn_InjectComponentsBase(); 27 | 28 | // IFlowPerSpawnedActorInterface 29 | void FinishedSpawningActor_Implementation(AActor* SpawnedActor, UFlowNodeBase* SpawningFlowNodeBase) override; 30 | // -- 31 | 32 | // IFlowCoreExecutableInterface 33 | virtual void DeinitializeInstance() override; 34 | // -- 35 | 36 | protected: 37 | 38 | UFUNCTION() 39 | void OnBeforeActorRemoved(AActor* Actor); 40 | 41 | void EnsureInjectComponentsManager(); 42 | void CleanupInjectComponentsManager(); 43 | 44 | // Functions for subclasses to apply additional monitoring to Actors 45 | virtual void OnStartMonitoringActor(AActor& Actor) { } 46 | virtual void OnStopMonitoringActor(AActor& Actor) { } 47 | 48 | protected: 49 | 50 | // Manager object to inject and remove components from actors 51 | UPROPERTY() 52 | TObjectPtr InjectComponentsManager = nullptr; 53 | }; 54 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Nodes/AIFlowNode_ExecutionRollGuaranteed.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AIFlowNode.h" 6 | #include "AIFlowNode_ExecutionRollGuaranteed.generated.h" 7 | 8 | /** 9 | * Executes a random roll that is eventually guaranteed 10 | * Our odds of the Guaranteed Output increase by 1 / MaximumAttempts every failed attempt 11 | * MaximumAttempts(4) - odds for success would be 25%, 50%, 75%, 100% on each subsequent roll 12 | */ 13 | UCLASS(NotBlueprintable, meta = (DisplayName = "Roll Guaranteed", Keywords = "random")) 14 | class AIFLOW_API UAIFlowNode_ExecutionRollGuaranteed final : public UAIFlowNode 15 | { 16 | GENERATED_UCLASS_BODY() 17 | 18 | // Initial odds of our "guaranteed output" rout. ( 1 / MaximumAttempts ) 19 | UPROPERTY(EditAnywhere, Category = "RollGuaranteed", meta = (ClampMin = 2, UIMin = 2)) 20 | int32 MaximumAttempts = 2; 21 | 22 | // After a successful output should we reset? Otherwise we fail until we make MaximumAttempts. 23 | UPROPERTY(EditAnywhere, Category = "RollGuaranteed") 24 | bool bResetOnSuccess = false; 25 | 26 | private: 27 | UPROPERTY(SaveGame) 28 | int32 RollAttempts = 0; 29 | 30 | UPROPERTY(Transient) 31 | FRandomStream RandomStream; 32 | 33 | UPROPERTY(SaveGame) 34 | bool bHasSuccessfullyRolled = false; 35 | 36 | public: 37 | #if WITH_EDITOR 38 | virtual bool CanUserAddOutput() const override { return false; } 39 | #endif 40 | 41 | virtual void OnActivate() override; 42 | virtual void ExecuteInput(const FName& PinName) override; 43 | virtual void Cleanup() override; 44 | 45 | #if WITH_EDITOR 46 | virtual FString GetNodeDescription() const override; 47 | #endif 48 | 49 | static const FName INPIN_Reset; 50 | static const FName OUTPIN_GuaranteedOut; 51 | static const FName OUTPIN_FailureOut; 52 | }; 53 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Private/DetailCustomizations/FlowBlackboardEntryCustomization.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" 6 | 7 | #include "Types/FlowBlackboardEntry.h" 8 | 9 | // Forward Declaration 10 | class IFlowBlackboardAssetProvider; 11 | class UBlackboardData; 12 | 13 | // Details customization for FFlowBlackboardEntry 14 | class FFlowBlackboardEntryCustomization : public IFlowCuratedNamePropertyCustomization 15 | { 16 | private: 17 | typedef IFlowCuratedNamePropertyCustomization Super; 18 | 19 | public: 20 | static TSharedRef MakeInstance() { return MakeShareable(new FFlowBlackboardEntryCustomization()); } 21 | 22 | protected: 23 | 24 | // IPropertyTypeCustomization 25 | virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; 26 | // -- 27 | 28 | // ICuratedNamePropertyCustomization 29 | virtual TSharedPtr GetCuratedNamePropertyHandle() const override; 30 | virtual void SetCuratedName(const FName& NewName) override; 31 | virtual bool TryGetCuratedName(FName& OutName) const override; 32 | virtual TArray GetCuratedNameOptions() const override; 33 | // -- 34 | 35 | // Accessor to return the actual struct being edited 36 | FORCEINLINE FFlowBlackboardEntry* GetFlowBlackboardEntry() const 37 | { return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); } 38 | 39 | const IFlowBlackboardAssetProvider* TryGetBlackboardAssetProviderFromOuters() const; 40 | const UBlackboardData* GetBlackboardData() const; 41 | 42 | static TArray GetFlowBlackboardEntries(const UBlackboardData& BlackboardAsset, const FFlowBlackboardEntry& FlowBlackboardEntry); 43 | }; 44 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Types/ConfigurableEnumProperty.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Enum.h" 6 | #include "Kismet/BlueprintFunctionLibrary.h" 7 | #include "Templates/SubclassOf.h" 8 | 9 | #include "ConfigurableEnumProperty.generated.h" 10 | 11 | // Similar to FAnimNodeFunctionRef, providing a configurable enum property 12 | USTRUCT(BlueprintType) 13 | struct FConfigurableEnumProperty 14 | { 15 | GENERATED_BODY() 16 | 17 | // For GET_MEMBER_NAME_CHECKED access 18 | friend class FConfigurableEnumPropertyCustomization; 19 | 20 | public: 21 | 22 | // The selected enum Value 23 | UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Blackboard) 24 | FName Value = NAME_None; 25 | 26 | // Class for this enum 27 | UPROPERTY(EditAnywhere, Category = Blackboard) 28 | UEnum* EnumClass = nullptr; 29 | 30 | #if WITH_EDITORONLY_DATA 31 | // name of enum defined in c++ code, will take priority over asset from EnumType property 32 | // (this is a work-around because EnumClass cannot find C++ Enums, 33 | // so you need to type the name of the enum in here, manually) 34 | // See also: UBlackboardKeyType_Enum::PostEditChangeProperty() 35 | UPROPERTY(EditAnywhere, Category = Blackboard) 36 | FString EnumName; 37 | #endif // WITH_EDITORONLY_DATA 38 | }; 39 | 40 | UCLASS() 41 | class UConfigurableEnumPropertyFunctionLibrary : public UBlueprintFunctionLibrary 42 | { 43 | GENERATED_BODY() 44 | 45 | public: 46 | // Auto-cast function to convert the FConfigurableEnumProperty into an Enum in Blueprint 47 | // (so we don't need to Break the struct every time we want the key) 48 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Enum", CompactNodeTitle = "->", BlueprintAutocast), Category = Blackboard) 49 | static uint8 AutoConvert_ConfigurableEnumPropertyToEnum(const FConfigurableEnumProperty& ConfigurableEnumProperty); 50 | }; 51 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Private/DetailCustomizations/ConfigurableEnumPropertyCustomization.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" 6 | 7 | #include "Types/ConfigurableEnumProperty.h" 8 | 9 | // NOTE (gtaylor) this is nearly identical to FFlowDataPinOutputProperty_EnumCustomization, can we combine them? 10 | 11 | // Details customization for FConfigurableEnumProperty 12 | class FConfigurableEnumPropertyCustomization : public IFlowCuratedNamePropertyCustomization 13 | { 14 | private: 15 | typedef IFlowCuratedNamePropertyCustomization Super; 16 | 17 | public: 18 | static TSharedRef MakeInstance() { return MakeShareable(new FConfigurableEnumPropertyCustomization()); } 19 | 20 | protected: 21 | 22 | //~Begin IPropertyTypeCustomization 23 | virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; 24 | //~End IPropertyTypeCustomization 25 | 26 | //~Begin ICuratedNamePropertyCustomization 27 | virtual TSharedPtr GetCuratedNamePropertyHandle() const override; 28 | virtual void SetCuratedName(const FName& NewName) override; 29 | virtual bool TryGetCuratedName(FName& OutName) const override; 30 | virtual TArray GetCuratedNameOptions() const override; 31 | virtual bool AllowNameNoneIfOtherOptionsExist() const override { return false; } 32 | //~End ICuratedNamePropertyCustomization 33 | 34 | // Accessor to return the actual struct being edited 35 | FORCEINLINE FConfigurableEnumProperty* GetConfigurableEnumProperty() const 36 | { return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); } 37 | 38 | void OnEnumNameChanged(); 39 | 40 | const UEnum* GetEnumClass() const; 41 | 42 | static TArray GetEnumValues(const UEnum& Enum); 43 | }; 44 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Types/FlowBlackboardEntry.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "Templates/SubclassOf.h" 6 | #include "Kismet/BlueprintFunctionLibrary.h" 7 | 8 | #include "FlowBlackboardEntry.generated.h" 9 | 10 | // Forward Declarations 11 | class UBlackboardEntry; 12 | class UBlackboardKeyType; 13 | class UBlackboardComponent; 14 | 15 | // Similar to FAnimNodeFunctionRef, providing a FName-based BlackboardEntry binding 16 | // that is resolved at runtime 17 | USTRUCT(BlueprintType) 18 | struct FFlowBlackboardEntry 19 | { 20 | GENERATED_BODY() 21 | 22 | // For GET_MEMBER_NAME_CHECKED access 23 | friend class FFlowBlackboardEntryCustomization; 24 | 25 | public: 26 | 27 | const FName& GetKeyName() const { return KeyName; } 28 | 29 | bool operator ==(const FFlowBlackboardEntry& Other) const { return KeyName == Other.KeyName; } 30 | bool operator !=(const FFlowBlackboardEntry& Other) const { return !(*this == Other); } 31 | 32 | public: 33 | 34 | // The blackboard Key's name 35 | UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Blackboard) 36 | FName KeyName = NAME_None; 37 | 38 | #if WITH_EDITORONLY_DATA 39 | public: 40 | // array of allowed types with additional properties (e.g. uobject's base class) 41 | UPROPERTY(EditDefaultsOnly, Instanced, Category = Blackboard) 42 | TArray> AllowedTypes; 43 | #endif // WITH_EDITORONLY_DATA 44 | }; 45 | 46 | UCLASS() 47 | class UFlowBlackboardFunctionLibrary : public UBlueprintFunctionLibrary 48 | { 49 | GENERATED_BODY() 50 | 51 | public: 52 | // Auto-cast function to convert the FlowBlackboardEntry into an FName in Blueprint 53 | // (so we don't need to Break the struct every time we want the key) 54 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = Blackboard) 55 | static FName AutoConvert_FlowBlackboardEntryKeyToName(const FFlowBlackboardEntry& FlowBlackboardEntry); 56 | }; -------------------------------------------------------------------------------- /Source/AIFlow/Private/AddOns/FlowNodeAddOn_InjectComponentsBase.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AddOns/FlowNodeAddOn_InjectComponentsBase.h" 4 | #include "Components/ActorComponent.h" 5 | #include "Types/FlowInjectComponentsManager.h" 6 | #include "GameFramework/Actor.h" 7 | #include "AIFlowLogChannels.h" 8 | 9 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeAddOn_InjectComponentsBase) 10 | 11 | UFlowNodeAddOn_InjectComponentsBase::UFlowNodeAddOn_InjectComponentsBase() 12 | : Super() 13 | { 14 | #if WITH_EDITOR 15 | NodeDisplayStyle = FlowNodeStyle::AddOn_PerSpawnedActor; 16 | Category = TEXT("Per-Spawned Actor"); 17 | #endif 18 | } 19 | 20 | void UFlowNodeAddOn_InjectComponentsBase::EnsureInjectComponentsManager() 21 | { 22 | if (IsValid(InjectComponentsManager)) 23 | { 24 | return; 25 | } 26 | 27 | InjectComponentsManager = NewObject(this); 28 | 29 | InjectComponentsManager->InitializeRuntime(); 30 | InjectComponentsManager->BeforeActorRemovedDelegate.AddDynamic(this, &UFlowNodeAddOn_InjectComponentsBase::OnBeforeActorRemoved); 31 | } 32 | 33 | void UFlowNodeAddOn_InjectComponentsBase::CleanupInjectComponentsManager() 34 | { 35 | if (IsValid(InjectComponentsManager)) 36 | { 37 | InjectComponentsManager->ShutdownRuntime(); 38 | 39 | InjectComponentsManager->BeforeActorRemovedDelegate.RemoveDynamic(this, &UFlowNodeAddOn_InjectComponentsBase::OnBeforeActorRemoved); 40 | } 41 | 42 | InjectComponentsManager = nullptr; 43 | } 44 | 45 | void UFlowNodeAddOn_InjectComponentsBase::DeinitializeInstance() 46 | { 47 | CleanupInjectComponentsManager(); 48 | 49 | Super::DeinitializeInstance(); 50 | } 51 | 52 | void UFlowNodeAddOn_InjectComponentsBase::OnBeforeActorRemoved(AActor* RemovedActor) 53 | { 54 | if (IsValid(RemovedActor)) 55 | { 56 | OnStopMonitoringActor(*RemovedActor); 57 | } 58 | } 59 | 60 | void UFlowNodeAddOn_InjectComponentsBase::FinishedSpawningActor_Implementation(AActor* SpawnedActor, UFlowNodeBase* SpawningFlowNodeBase) 61 | { 62 | } 63 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Nodes/AIFlowNode_ExecutionRollWeighted.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AIFlowNode.h" 6 | #include "AIFlowNode_ExecutionRollWeighted.generated.h" 7 | 8 | USTRUCT(BlueprintType, meta = (ShowOnlyInnerProperties)) 9 | struct FAIFlowNode_RollWeightedOption 10 | { 11 | GENERATED_BODY() 12 | 13 | public: 14 | 15 | FAIFlowNode_RollWeightedOption() = default; 16 | FAIFlowNode_RollWeightedOption(const FName InOutputName, int32 InWeight); 17 | 18 | public: 19 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "RollWeightedOption") 20 | FName OutputName; 21 | 22 | // Weight for this option (higher is better, zero weight will not spawn at all) 23 | UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (ClampMin = 0, UIMin = 0), Category = "RollWeightedOption") 24 | int32 Weight = 1; 25 | }; 26 | 27 | /** 28 | * Executes a weighted roll 29 | */ 30 | UCLASS(NotBlueprintable, meta = (DisplayName = "Roll Weighted", Keywords = "random")) 31 | class AIFLOW_API UAIFlowNode_ExecutionRollWeighted final : public UAIFlowNode 32 | { 33 | GENERATED_UCLASS_BODY() 34 | 35 | public: 36 | UPROPERTY(EditAnywhere, Category = "RollWeighted", meta = (TitleProperty = "{OutputName} {Weight}")) 37 | TArray OutputPinOptions; 38 | 39 | // Random stream to use for all random numbers 40 | UPROPERTY(Transient) 41 | FRandomStream RandomStream; 42 | 43 | UPROPERTY(Transient) 44 | int32 TotalWeight = 0; 45 | 46 | public: 47 | virtual void OnActivate() override; 48 | virtual void ExecuteInput(const FName& PinName) override; 49 | 50 | #if WITH_EDITOR 51 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; 52 | 53 | // IFlowContextPinSupplierInterface 54 | virtual bool SupportsContextPins() const override { return true; } 55 | virtual TArray GetContextOutputs() const override; 56 | // -- 57 | #endif 58 | 59 | private: 60 | int32 GetWeightedRandomChoice(); 61 | void CalculateTotalWeight(); 62 | }; 63 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Interfaces/FlowBlackboardInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "UObject/Interface.h" 6 | #include "Templates/SubclassOf.h" 7 | 8 | #include "FlowBlackboardInterface.generated.h" 9 | 10 | // Forward Declarations 11 | class UBlackboardKeyType; 12 | class UBlackboardComponent; 13 | class UBlackboardData; 14 | 15 | // Implemented by objects that can access a Flow Blackboard 16 | // (eg, UAIFlowAsset, UAIFlowNode and UAIFlowNodeAddOn implement this) 17 | UINTERFACE(MinimalAPI, BlueprintType, DisplayName = "Flow Blackboard Interface", meta = (CannotImplementInterfaceInBlueprint)) 18 | class UFlowBlackboardInterface : public UInterface 19 | { 20 | GENERATED_BODY() 21 | }; 22 | 23 | class AIFLOW_API IFlowBlackboardInterface 24 | { 25 | GENERATED_BODY() 26 | 27 | public: 28 | 29 | // Get the blackboard component 30 | UFUNCTION(BlueprintCallable, Category = "FlowNode") 31 | virtual UBlackboardComponent* GetBlackboardComponent() const; 32 | 33 | // Is the given blackboard key valid? 34 | UFUNCTION(BlueprintCallable, Category = "FlowNode") 35 | virtual bool IsValidBlackboardKey(const FName& KeyName) const; 36 | static bool IsValidBlackboardKey(const UBlackboardComponent& BlackboardComponent, const FName& KeyName); 37 | 38 | // Gather all blackboard keys of a given type 39 | UFUNCTION(BlueprintCallable, Category = "FlowNode") 40 | virtual TArray GatherAllBlackboardKeysOfType(UBlackboardKeyType* KeyType = nullptr) const; 41 | static TArray GatherAllBlackboardKeysOfType(const UBlackboardComponent& BlackboardComponent, UBlackboardKeyType* KeyType = nullptr); 42 | 43 | // Get the KeyType of a blackboard key 44 | UFUNCTION(BlueprintCallable, Category = "FlowNode") 45 | virtual UBlackboardKeyType* GetBlackboardKeyType(const FName& KeyName) const; 46 | static UBlackboardKeyType* GetBlackboardKeyType(const UBlackboardComponent& BlackboardComponent, const FName& KeyName); 47 | static UBlackboardKeyType* GetBlackboardKeyType(const UBlackboardData* BlackboardAsset, const FName& KeyName); 48 | }; 49 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/AddOns/FlowNodeAddOn_InjectComponents.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AddOns/FlowNodeAddOn_InjectComponents.h" 4 | #include "Components/ActorComponent.h" 5 | #include "Types/FlowInjectComponentsManager.h" 6 | #include "GameFramework/Actor.h" 7 | 8 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeAddOn_InjectComponents) 9 | 10 | UFlowNodeAddOn_InjectComponents::UFlowNodeAddOn_InjectComponents() 11 | : Super() 12 | { 13 | } 14 | 15 | void UFlowNodeAddOn_InjectComponents::FinishedSpawningActor_Implementation(AActor* SpawnedActor, UFlowNodeBase* SpawningFlowNodeBase) 16 | { 17 | // It is possible to fail spawning, and we will still get a nullptr call to this function 18 | // (in case the AddOn is simply counting spawn attempts, etc.) 19 | 20 | if (IsValid(SpawnedActor)) 21 | { 22 | const TArray ComponentInstances = InjectComponentsHelper.CreateComponentInstancesForActor(*SpawnedActor); 23 | if (!ComponentInstances.IsEmpty()) 24 | { 25 | EnsureInjectComponentsManager(); 26 | 27 | InjectComponentsManager->InjectComponentsOnActor(*SpawnedActor, ComponentInstances); 28 | 29 | // Inform subclasses that we are monitoring this Actor 30 | OnStartMonitoringActor(*SpawnedActor); 31 | } 32 | } 33 | 34 | Super::FinishedSpawningActor_Implementation(SpawnedActor, SpawningFlowNodeBase); 35 | } 36 | 37 | void UFlowNodeAddOn_InjectComponents::UpdateNodeConfigText_Implementation() 38 | { 39 | #if WITH_EDITOR 40 | FTextBuilder TextBuilder; 41 | 42 | for (UActorComponent* ComponentTemplate : InjectComponentsHelper.ComponentTemplates) 43 | { 44 | if (IsValid(ComponentTemplate)) 45 | { 46 | TextBuilder.AppendLine(FText::FromString(*ComponentTemplate->GetName())); 47 | } 48 | } 49 | 50 | for (TSubclassOf ComponentClass : InjectComponentsHelper.ComponentClasses) 51 | { 52 | if (IsValid(ComponentClass)) 53 | { 54 | TextBuilder.AppendLine(FText::FromString(*ComponentClass->GetName())); 55 | } 56 | } 57 | 58 | SetNodeConfigText(TextBuilder.ToText()); 59 | #endif // WITH_EDITOR 60 | } 61 | 62 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue_Bool.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowBlackboardEntryValue.h" 6 | 7 | #include "FlowBlackboardEntryValue_Bool.generated.h" 8 | 9 | /** 10 | * Configuration object for setting blackboard entries for UBlackboardKeyType_Bool entries 11 | */ 12 | UCLASS(BlueprintType, DisplayName = "Bool Blackboard Value") 13 | class AIFLOW_API UFlowBlackboardEntryValue_Bool : public UFlowBlackboardEntryValue 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | 19 | //~Begin UFlowBlackboardEntryValue 20 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const override; 21 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const override; 22 | virtual TSubclassOf GetSupportedBlackboardKeyType() const override; 23 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) override; 24 | #if WITH_EDITOR 25 | virtual FString GetEditorValueString() const override; 26 | #endif // WITH_EDITOR 27 | //~End UFlowBlackboardEntryValue 28 | 29 | // IFlowDataPinPropertyProviderInterface 30 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const override; 31 | // -- 32 | 33 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 34 | const FName& BlackboardKeyName, 35 | const UBlackboardKeyType& BlackboardKeyType, 36 | UBlackboardComponent* OptionalBlackboardComponent, 37 | TInstancedStruct& OutFlowDataPinProperty) const override; 38 | 39 | #if WITH_EDITOR 40 | public: 41 | //~Begin UFlowNodeBase 42 | virtual FText BuildNodeConfigText() const override; 43 | //~End UFlowNodeBase 44 | 45 | //~Begin UObject 46 | virtual void PostInitProperties() override; 47 | //~End UObject 48 | #endif // WITH_EDITOR 49 | 50 | protected: 51 | 52 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (DisplayAfter = Key)) 53 | bool bBoolValue = false; 54 | }; 55 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue_Name.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowBlackboardEntryValue.h" 6 | 7 | #include "FlowBlackboardEntryValue_Name.generated.h" 8 | 9 | /** 10 | * Configuration object for setting blackboard entries for UBlackboardKeyType_Name entries 11 | */ 12 | UCLASS(BlueprintType, DisplayName = "Name Blackboard Value") 13 | class AIFLOW_API UFlowBlackboardEntryValue_Name : public UFlowBlackboardEntryValue 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | 19 | //~Begin UFlowBlackboardEntryValue 20 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const override; 21 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const override; 22 | virtual TSubclassOf GetSupportedBlackboardKeyType() const override; 23 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) override; 24 | #if WITH_EDITOR 25 | virtual FString GetEditorValueString() const override; 26 | #endif // WITH_EDITOR 27 | //~End UFlowBlackboardEntryValue 28 | 29 | // IFlowDataPinPropertyProviderInterface 30 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const override; 31 | // -- 32 | 33 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 34 | const FName& BlackboardKeyName, 35 | const UBlackboardKeyType& BlackboardKeyType, 36 | UBlackboardComponent* OptionalBlackboardComponent, 37 | TInstancedStruct& OutFlowDataPinProperty) const override; 38 | 39 | #if WITH_EDITOR 40 | public: 41 | //~Begin UFlowNodeBase 42 | virtual FText BuildNodeConfigText() const override; 43 | //~End UFlowNodeBase 44 | 45 | //~Begin UObject 46 | virtual void PostInitProperties() override; 47 | //~End UObject 48 | #endif // WITH_EDITOR 49 | 50 | protected: 51 | 52 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (DisplayAfter = Key)) 53 | FName NameValue = NAME_None; 54 | }; 55 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue_String.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowBlackboardEntryValue.h" 6 | 7 | #include "FlowBlackboardEntryValue_String.generated.h" 8 | 9 | /** 10 | * Configuration object for setting blackboard entries for UBlackboardKeyType_String entries 11 | */ 12 | UCLASS(BlueprintType, DisplayName = "String Blackboard Value") 13 | class AIFLOW_API UFlowBlackboardEntryValue_String : public UFlowBlackboardEntryValue 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | 19 | //~Begin UFlowBlackboardEntryValue 20 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const override; 21 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const override; 22 | virtual TSubclassOf GetSupportedBlackboardKeyType() const override; 23 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) override; 24 | #if WITH_EDITOR 25 | virtual FString GetEditorValueString() const override { return StringValue; } 26 | #endif // WITH_EDITOR 27 | //~End UFlowBlackboardEntryValue 28 | 29 | // IFlowDataPinPropertyProviderInterface 30 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const override; 31 | // -- 32 | 33 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 34 | const FName& BlackboardKeyName, 35 | const UBlackboardKeyType& BlackboardKeyType, 36 | UBlackboardComponent* OptionalBlackboardComponent, 37 | TInstancedStruct& OutFlowDataPinProperty) const override; 38 | 39 | #if WITH_EDITOR 40 | public: 41 | //~Begin UFlowNodeBase 42 | virtual FText BuildNodeConfigText() const override; 43 | //~End UFlowNodeBase 44 | 45 | //~Begin UObject 46 | virtual void PostInitProperties() override; 47 | //~End UObject 48 | #endif // WITH_EDITOR 49 | 50 | protected: 51 | 52 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (DisplayAfter = Key)) 53 | FString StringValue; 54 | }; 55 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue_Vector.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowBlackboardEntryValue.h" 6 | 7 | #include "FlowBlackboardEntryValue_Vector.generated.h" 8 | 9 | /** 10 | * Configuration object for setting blackboard entries for UBlackboardKeyType_Vector entries 11 | */ 12 | UCLASS(BlueprintType, DisplayName = "Vector Blackboard Value") 13 | class AIFLOW_API UFlowBlackboardEntryValue_Vector : public UFlowBlackboardEntryValue 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | 19 | //~Begin UFlowBlackboardEntryValue 20 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const override; 21 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const override; 22 | virtual TSubclassOf GetSupportedBlackboardKeyType() const override; 23 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) override; 24 | #if WITH_EDITOR 25 | virtual FString GetEditorValueString() const override { return VectorValue.ToCompactString(); } 26 | #endif // WITH_EDITOR 27 | //~End UFlowBlackboardEntryValue 28 | 29 | // IFlowDataPinPropertyProviderInterface 30 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const override; 31 | // -- 32 | 33 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 34 | const FName& BlackboardKeyName, 35 | const UBlackboardKeyType& BlackboardKeyType, 36 | UBlackboardComponent* OptionalBlackboardComponent, 37 | TInstancedStruct& OutFlowDataPinProperty) const override; 38 | 39 | #if WITH_EDITOR 40 | public: 41 | //~Begin UFlowNodeBase 42 | virtual FText BuildNodeConfigText() const override; 43 | //~End UFlowNodeBase 44 | 45 | //~Begin UObject 46 | virtual void PostInitProperties() override; 47 | //~End UObject 48 | #endif // WITH_EDITOR 49 | 50 | protected: 51 | 52 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (DisplayAfter = Key)) 53 | FVector VectorValue = FVector::ZeroVector; 54 | }; 55 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue_Rotator.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowBlackboardEntryValue.h" 6 | 7 | #include "FlowBlackboardEntryValue_Rotator.generated.h" 8 | 9 | /** 10 | * Configuration object for setting blackboard entries for UBlackboardKeyType_Rotator entries 11 | */ 12 | UCLASS(BlueprintType, DisplayName = "Rotator Blackboard Value") 13 | class AIFLOW_API UFlowBlackboardEntryValue_Rotator : public UFlowBlackboardEntryValue 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | 19 | //~Begin UFlowBlackboardEntryValue 20 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const override; 21 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const override; 22 | virtual TSubclassOf GetSupportedBlackboardKeyType() const override; 23 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) override; 24 | #if WITH_EDITOR 25 | virtual FString GetEditorValueString() const override { return RotatorValue.ToCompactString(); } 26 | #endif // WITH_EDITOR 27 | //~End UFlowBlackboardEntryValue 28 | 29 | // IFlowDataPinPropertyProviderInterface 30 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const override; 31 | // -- 32 | 33 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 34 | const FName& BlackboardKeyName, 35 | const UBlackboardKeyType& BlackboardKeyType, 36 | UBlackboardComponent* OptionalBlackboardComponent, 37 | TInstancedStruct& OutFlowDataPinProperty) const override; 38 | 39 | #if WITH_EDITOR 40 | public: 41 | //~Begin UFlowNodeBase 42 | virtual FText BuildNodeConfigText() const override; 43 | //~End UFlowNodeBase 44 | 45 | //~Begin UObject 46 | virtual void PostInitProperties() override; 47 | //~End UObject 48 | #endif // WITH_EDITOR 49 | 50 | protected: 51 | 52 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (DisplayAfter = Key)) 53 | FRotator RotatorValue = FRotator::ZeroRotator; 54 | }; 55 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Nodes/FlowNode_SetBlackboardValuesOnActor.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "Nodes/FlowNode_SetBlackboardValues.h" 6 | #include "Types/FlowBlackboardEntry.h" 7 | 8 | #include "FlowNode_SetBlackboardValuesOnActor.generated.h" 9 | 10 | /** 11 | * Set blackboard values to the values defined in the Entries array 12 | * on an actor's blackboard. 13 | * The actor is found via a blackboard key on the flow graph's blackboard. 14 | */ 15 | UCLASS(DisplayName = "Set Blackboard Values") 16 | class AIFLOW_API UFlowNode_SetBlackboardValuesOnActor : public UFlowNode_SetBlackboardValues 17 | { 18 | GENERATED_BODY() 19 | 20 | public: 21 | UFlowNode_SetBlackboardValuesOnActor(); 22 | 23 | // UObject 24 | #if WITH_EDITOR 25 | virtual void PostInitProperties() override; 26 | #endif // WITH_EDITOR 27 | // -- 28 | 29 | // UFlowNodeBase 30 | virtual void UpdateNodeConfigText_Implementation() override; 31 | // -- 32 | 33 | // IFlowBlackboardAssetProvider 34 | #if WITH_EDITOR 35 | virtual UBlackboardData* GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const override; 36 | #endif // WITH_EDITOR 37 | // -- 38 | 39 | protected: 40 | 41 | // UFlowNode_SetBlackboardValues 42 | virtual TArray TryResolveActorsForBlackboard() const override; 43 | // -- 44 | 45 | bool TryAddSpecificActor(TArray& InOutResolvedActors) const; 46 | bool TryAddBlackboardKeyedActor(TArray& InOutResolvedActors) const; 47 | 48 | protected: 49 | 50 | // TODO (update the naming for SpecificActor to array 51 | 52 | // Optional specific actor to use to look for (or inject) the blackboard. 53 | // If not specified, will use the flow graph's owning actor. 54 | UPROPERTY(Transient, meta = (DefaultForInputFlowPin, FlowPinType = "Object", DisplayPriority = 1)) 55 | TObjectPtr SpecificActor = nullptr; 56 | 57 | // BlackboardKey (in this flow graph's blackboard) 58 | // to source the Actors to Set Blackboard Values On (on their blackboards) 59 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Key for Actor to Set Blackboard Values On", meta = (DisplayPriority = 1)) 60 | FFlowBlackboardEntry BlackboardActorKey; 61 | 62 | static FName INPIN_SpecificActor; 63 | }; 64 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue_Int.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowBlackboardEntryValue.h" 6 | 7 | #include "FlowBlackboardEntryValue_Int.generated.h" 8 | 9 | /** 10 | * Configuration object for setting blackboard entries for UBlackboardKeyType_Int entries 11 | */ 12 | UCLASS(BlueprintType, DisplayName = "Int Blackboard Value") 13 | class AIFLOW_API UFlowBlackboardEntryValue_Int : public UFlowBlackboardEntryValue 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | 19 | //~Begin UFlowBlackboardEntryValue 20 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const override; 21 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const override; 22 | virtual TSubclassOf GetSupportedBlackboardKeyType() const override; 23 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) override; 24 | virtual bool TryGetNumericalValuesForArithmeticOperation(int32* OutIntValue, float* OutFloatValue) const override; 25 | #if WITH_EDITOR 26 | virtual bool SupportsArithmeticOperations() const override { return true; } 27 | virtual FString GetEditorValueString() const override; 28 | #endif // WITH_EDITOR 29 | //~End UFlowBlackboardEntryValue 30 | 31 | // IFlowDataPinPropertyProviderInterface 32 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const override; 33 | // -- 34 | 35 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 36 | const FName& BlackboardKeyName, 37 | const UBlackboardKeyType& BlackboardKeyType, 38 | UBlackboardComponent* OptionalBlackboardComponent, 39 | TInstancedStruct& OutFlowDataPinProperty) const override; 40 | 41 | #if WITH_EDITOR 42 | public: 43 | //~Begin UFlowNodeBase 44 | virtual FText BuildNodeConfigText() const override; 45 | //~End UFlowNodeBase 46 | 47 | //~Begin UObject 48 | virtual void PostInitProperties() override; 49 | //~End UObject 50 | #endif // WITH_EDITOR 51 | 52 | protected: 53 | 54 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (DisplayAfter = Key)) 55 | int32 IntValue = 0; 56 | }; 57 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue_Float.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowBlackboardEntryValue.h" 6 | 7 | #include "FlowBlackboardEntryValue_Float.generated.h" 8 | 9 | /** 10 | * Configuration object for setting blackboard entries for UBlackboardKeyType_Float entries 11 | */ 12 | UCLASS(BlueprintType, DisplayName = "Float Blackboard Value") 13 | class AIFLOW_API UFlowBlackboardEntryValue_Float : public UFlowBlackboardEntryValue 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | 19 | //~Begin UFlowBlackboardEntryValue 20 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const override; 21 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const override; 22 | virtual TSubclassOf GetSupportedBlackboardKeyType() const override; 23 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) override; 24 | virtual bool TryGetNumericalValuesForArithmeticOperation(int32* OutIntValue, float* OutFloatValue) const override; 25 | #if WITH_EDITOR 26 | virtual bool SupportsArithmeticOperations() const override { return true; } 27 | virtual FString GetEditorValueString() const override; 28 | #endif // WITH_EDITOR 29 | //~End UFlowBlackboardEntryValue 30 | 31 | // IFlowDataPinPropertyProviderInterface 32 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const override; 33 | // -- 34 | 35 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 36 | const FName& BlackboardKeyName, 37 | const UBlackboardKeyType& BlackboardKeyType, 38 | UBlackboardComponent* OptionalBlackboardComponent, 39 | TInstancedStruct& OutFlowDataPinProperty) const override; 40 | 41 | #if WITH_EDITOR 42 | public: 43 | //~Begin UFlowNodeBase 44 | virtual FText BuildNodeConfigText() const override; 45 | //~End UFlowNodeBase 46 | 47 | //~Begin UObject 48 | virtual void PostInitProperties() override; 49 | //~End UObject 50 | #endif // WITH_EDITOR 51 | 52 | protected: 53 | 54 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (DisplayAfter = Key)) 55 | float FloatValue = 0.0f; 56 | }; 57 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue_Class.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowBlackboardEntryValue.h" 6 | 7 | #include "FlowBlackboardEntryValue_Class.generated.h" 8 | 9 | /** 10 | * Configuration object for setting blackboard entries for UBlackboardKeyType_Class entries 11 | */ 12 | UCLASS(BlueprintType, DisplayName = "Class Blackboard Value") 13 | class UFlowBlackboardEntryValue_Class : public UFlowBlackboardEntryValue 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | 19 | //~Begin UFlowBlackboardEntryValue 20 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const override; 21 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const override; 22 | virtual TSubclassOf GetSupportedBlackboardKeyType() const override; 23 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) override; 24 | #if WITH_EDITOR 25 | virtual bool TryReconfigureFromBlackboardKeyType(const UBlackboardKeyType& KeyType) override; 26 | virtual FString GetEditorValueString() const override; 27 | #endif // WITH_EDITOR 28 | //~End UFlowBlackboardEntryValue 29 | 30 | // IFlowDataPinPropertyProviderInterface 31 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const override; 32 | // -- 33 | 34 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 35 | const FName& BlackboardKeyName, 36 | const UBlackboardKeyType& BlackboardKeyType, 37 | UBlackboardComponent* OptionalBlackboardComponent, 38 | TInstancedStruct& OutFlowDataPinProperty) const override; 39 | 40 | #if WITH_EDITOR 41 | //~Begin UFlowNodeBase 42 | virtual FText BuildNodeConfigText() const override; 43 | //~End UFlowNodeBase 44 | 45 | //~Begin UObject 46 | virtual void PostInitProperties() override; 47 | virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; 48 | //~End UObject 49 | #endif // WITH_EDITOR 50 | 51 | protected: 52 | #if WITH_EDITOR 53 | void EnsureClassInstanceIsCompatibleWithBaseClass(); 54 | #endif // WITH_EDITOR 55 | 56 | protected: 57 | #if WITH_EDITORONLY_DATA 58 | // The BaseClass for the Object to set 59 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (AllowAbstract = "true")) 60 | TObjectPtr BaseClass = nullptr; 61 | #endif // WITH_EDITORONLY_DATA 62 | 63 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (DisplayAfter = Key, AllowAbstract = "true", EditCondition = "BaseClass != nullptr")) 64 | TObjectPtr ClassInstance = nullptr; 65 | }; 66 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue_Enum.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "Types/ConfigurableEnumProperty.h" 6 | #include "FlowBlackboardEntryValue.h" 7 | 8 | #include "FlowBlackboardEntryValue_Enum.generated.h" 9 | 10 | struct FFlowDataPinResult_Enum; 11 | class UBlackboardComponent; 12 | 13 | /** 14 | * Configuration object class for setting blackboard entries for UBlackboardKeyType_Enum 15 | */ 16 | UCLASS(BlueprintType, DisplayName = "Enum Blackboard Value") 17 | class UFlowBlackboardEntryValue_Enum : public UFlowBlackboardEntryValue 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | 23 | //~Begin UFlowBlackboardEntryValue 24 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const override; 25 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const override; 26 | virtual TSubclassOf GetSupportedBlackboardKeyType() const override; 27 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) override; 28 | virtual bool TryGetNumericalValuesForArithmeticOperation(int32* OutIntValue, float* OutFloatValue) const override; 29 | #if WITH_EDITOR 30 | virtual bool SupportsArithmeticOperations() const override { return true; } 31 | virtual bool TryReconfigureFromBlackboardKeyType(const UBlackboardKeyType& KeyType) override; 32 | virtual FString GetEditorValueString() const override; 33 | #endif // WITH_EDITOR 34 | //~End UFlowBlackboardEntryValue 35 | 36 | // IFlowDataPinPropertyProviderInterface 37 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const override; 38 | // -- 39 | 40 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 41 | const FName& BlackboardKeyName, 42 | const UBlackboardKeyType& BlackboardKeyType, 43 | UBlackboardComponent* OptionalBlackboardComponent, 44 | TInstancedStruct& OutFlowDataPinProperty) const override; 45 | 46 | static FFlowDataPinResult_Enum TryBuildDataPinResultFromBlackboardEnumEntry(const FName& KeyName, const UBlackboardComponent& BlackboardComponent); 47 | 48 | #if WITH_EDITOR 49 | public: 50 | //~Begin UFlowNodeBase 51 | virtual FText BuildNodeConfigText() const override; 52 | //~End UFlowNodeBase 53 | 54 | //~Begin UObject 55 | virtual void PostInitProperties() override; 56 | virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; 57 | //~End UObject 58 | 59 | protected: 60 | void EnsureValueIsCompatibleWithEnumClass(); 61 | bool TryUpdateEnumTypesFromKey(); 62 | #endif // WITH_EDITOR 63 | 64 | protected: 65 | 66 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (DisplayAfter = Key)) 67 | FConfigurableEnumProperty EnumValue; 68 | }; -------------------------------------------------------------------------------- /Source/AIFlow/Private/Nodes/AIFlowNode_ExecutionRollGuaranteed.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Nodes/AIFlowNode_ExecutionRollGuaranteed.h" 4 | 5 | #include UE_INLINE_GENERATED_CPP_BY_NAME(AIFlowNode_ExecutionRollGuaranteed) 6 | 7 | const FName UAIFlowNode_ExecutionRollGuaranteed::INPIN_Reset(TEXT("Reset")); 8 | 9 | const FName UAIFlowNode_ExecutionRollGuaranteed::OUTPIN_GuaranteedOut(TEXT("Guaranteed Out")); 10 | const FName UAIFlowNode_ExecutionRollGuaranteed::OUTPIN_FailureOut(TEXT("Failure Out")); 11 | 12 | UAIFlowNode_ExecutionRollGuaranteed::UAIFlowNode_ExecutionRollGuaranteed(const FObjectInitializer& ObjectInitializer) 13 | : Super(ObjectInitializer) 14 | { 15 | #if WITH_EDITOR 16 | NodeDisplayStyle = FlowNodeStyle::Default; 17 | Category = TEXT("Flow|Routing"); 18 | #endif 19 | 20 | FString ResetPinTooltip = TEXT("Finish work of this node."); 21 | ResetPinTooltip += LINE_TERMINATOR; 22 | ResetPinTooltip += TEXT("Calling In input will start triggering output pins once again."); 23 | 24 | InputPins.Add(FFlowPin(INPIN_Reset.ToString(), ResetPinTooltip)); 25 | 26 | FString GuaranteedPinTooltip = TEXT("Roll for a chance to enter this rout sequentially increasing our chances."); 27 | FString FailurePinTooltip = TEXT("On failure we take this rout"); 28 | OutputPins.Empty(); 29 | OutputPins.Add(FFlowPin(OUTPIN_GuaranteedOut.ToString(), GuaranteedPinTooltip)); 30 | OutputPins.Add(FFlowPin(OUTPIN_FailureOut.ToString(), FailurePinTooltip)); 31 | 32 | AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; 33 | } 34 | 35 | void UAIFlowNode_ExecutionRollGuaranteed::OnActivate() 36 | { 37 | Super::OnActivate(); 38 | 39 | const int32 RandomSeed = GetRandomSeed(); 40 | RandomStream.Initialize(RandomSeed); 41 | } 42 | 43 | void UAIFlowNode_ExecutionRollGuaranteed::ExecuteInput(const FName& PinName) 44 | { 45 | if (PinName == DefaultInputPin.PinName) 46 | { 47 | ++RollAttempts; 48 | ensure(RollAttempts <= MaximumAttempts); 49 | 50 | const bool bResetOnMaxAttempts = RollAttempts == MaximumAttempts; 51 | 52 | if (bHasSuccessfullyRolled) 53 | { 54 | TriggerOutput(OUTPIN_FailureOut, bResetOnMaxAttempts); 55 | } 56 | else 57 | { 58 | const int32 Random = RandomStream.RandRange(0, MaximumAttempts - RollAttempts); 59 | 60 | if (Random == 0) 61 | { 62 | bHasSuccessfullyRolled = true; 63 | TriggerOutput(OUTPIN_GuaranteedOut, bResetOnSuccess || bResetOnMaxAttempts); 64 | } 65 | else 66 | { 67 | TriggerOutput(OUTPIN_FailureOut, false); 68 | } 69 | } 70 | } 71 | else if (PinName == INPIN_Reset) 72 | { 73 | Finish(); 74 | } 75 | } 76 | 77 | void UAIFlowNode_ExecutionRollGuaranteed::Cleanup() 78 | { 79 | RollAttempts = 0; 80 | bHasSuccessfullyRolled = false; 81 | } 82 | 83 | #if WITH_EDITOR 84 | FString UAIFlowNode_ExecutionRollGuaranteed::GetNodeDescription() const 85 | { 86 | FString Result; 87 | Result.Reserve(128); 88 | 89 | if (bResetOnSuccess) 90 | { 91 | Result.Append(TEXT("Reset On Success")); 92 | } 93 | 94 | return Result; 95 | } 96 | #endif 97 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Nodes/FlowNode_EnsureActorHasBlackboard.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AIFlowNode.h" 6 | #include "AIFlowActorBlackboardHelper.h" 7 | 8 | #include "FlowNode_EnsureActorHasBlackboard.generated.h" 9 | 10 | class UBlackboardComponent; 11 | 12 | /** 13 | * Ensure an Actor has a blackboard. 14 | * Can optionally specify the actor and the blackboard. 15 | * The desired blackboard component will be injected if it cannot be found on the actor 16 | */ 17 | UCLASS(Blueprintable, meta = (DisplayName = "Ensure Actor Has Blackboard")) 18 | class AIFLOW_API UFlowNode_EnsureActorHasBlackboard : public UAIFlowNode 19 | { 20 | GENERATED_BODY() 21 | 22 | public: 23 | 24 | UFlowNode_EnsureActorHasBlackboard(); 25 | 26 | // IFlowCoreExecutableInterface 27 | virtual void ExecuteInput(const FName& PinName) override; 28 | virtual void DeinitializeInstance() override; 29 | // -- 30 | 31 | protected: 32 | 33 | void EnsureInjectComponentsManager(); 34 | void CleanupInjectComponentsManager(); 35 | 36 | UBlackboardComponent* EnsureActorHasBlackboard(AActor& ResolvedActor); 37 | 38 | UFUNCTION() 39 | void OnBeforeActorRemoved(AActor* RemovedActor); 40 | 41 | // Functions for subclasses to apply additional monitoring to Actors 42 | virtual void OnStartMonitoringActor(AActor& Actor) { } 43 | virtual void OnStopMonitoringActor(AActor& Actor) { } 44 | 45 | protected: 46 | 47 | // Optional specific actor to use to look for (or inject) the blackboard. 48 | // If not specified, will use the flow graph's owning actor. 49 | UPROPERTY(Transient, meta = (DefaultForInputFlowPin, FlowPinType = "Object", DisplayPriority = 2)) 50 | TObjectPtr SpecificActor = nullptr; 51 | 52 | // Specific blackboard to use (optional, defaults to the flow asset's blackboard) 53 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Specific Blackboard", meta = (DisplayPriority = 2)) 54 | TObjectPtr SpecificBlackboardAsset = nullptr; 55 | 56 | // Injection rule (generally inject onto the Actor, but occasionally you may wish to inject onto the actor's controller) 57 | UPROPERTY(EditAnywhere, AdvancedDisplay, Category = Configuration, DisplayName = "Blackboard Injection Rule", meta = (DisplayPriority = 3)) 58 | EActorBlackboardInjectRule InjectRule = EActorBlackboardInjectRule::InjectOntoActorIfMissing; 59 | 60 | // Search rule to use to find the Blackboard 61 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Blackboard Search Rule", meta = (DisplayPriority = 2)) 62 | EActorBlackboardSearchRule BlackboardSearchRule = EActorBlackboardSearchRule::ActorOnly; 63 | 64 | // Specific blackboard component subclass to use (optional, defaults to the flow asset's blackboard defined component class) 65 | UPROPERTY(EditAnywhere, AdvancedDisplay, Category = Configuration, DisplayName = "Specific Blackboard Component", meta = (DisplayPriority = 2)) 66 | TSubclassOf SpecificBlackboardComponentClass = nullptr; 67 | 68 | // Manager object to inject and remove components from actors 69 | UPROPERTY() 70 | TObjectPtr InjectComponentsManager = nullptr; 71 | 72 | public: 73 | 74 | static const FName OUTPIN_Success; 75 | static const FName OUTPIN_Failed; 76 | }; 77 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/AIFlowAsset.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowAsset.h" 6 | #include "Interfaces/FlowBlackboardAssetProvider.h" 7 | #include "Interfaces/FlowBlackboardInterface.h" 8 | 9 | #include "AIFlowAsset.generated.h" 10 | 11 | // Forward Declarations 12 | class UBlackboardData; 13 | class UBlackboardComponent; 14 | class UFlowInjectComponentsManager; 15 | 16 | /** 17 | * Flow Asset subclass to add AI utility (specifically blackboard) capabilities 18 | */ 19 | UCLASS(BlueprintType, DisplayName = "AI Flow Asset") 20 | class AIFLOW_API UAIFlowAsset 21 | : public UFlowAsset 22 | , public IFlowBlackboardAssetProvider 23 | , public IFlowBlackboardInterface 24 | { 25 | GENERATED_UCLASS_BODY() 26 | 27 | public: 28 | 29 | // UFlowAsset 30 | virtual void InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset& InTemplateAsset) override; 31 | virtual void DeinitializeInstance() override; 32 | // -- 33 | 34 | // IFlowBlackboardInterface 35 | virtual UBlackboardComponent* GetBlackboardComponent() const override; 36 | // -- 37 | 38 | // IBlackboardAssetProvider 39 | virtual UBlackboardData* GetBlackboardAsset() const override; 40 | // -- 41 | 42 | UFUNCTION(BlueprintCallable, Category = "AI Flow", DisplayName = TryFindBlackboardComponentOnActor) 43 | static UBlackboardComponent* BP_TryFindBlackboardComponentOnActor(AActor* Actor, UBlackboardData* OptionalBlackboardData = nullptr); 44 | static UBlackboardComponent* TryFindBlackboardComponentOnActor(AActor& Actor, UBlackboardData* OptionalBlackboardData = nullptr); 45 | 46 | TSubclassOf GetBlackboardComponentClass() const { return BlackboardComponentClass; } 47 | 48 | // Allow setting a specific RandomSeed for flownodes in this flowgraph to use 49 | UFUNCTION(BlueprintCallable, Category = "AI Flow") 50 | void SetRandomSeed(int32 Seed) { RandomSeed = Seed; } 51 | 52 | UFUNCTION(BlueprintCallable, BlueprintPure, Category = "AI Flow") 53 | int32 GetRandomSeed() const { return RandomSeed; } 54 | 55 | protected: 56 | 57 | virtual void CreateAndRegisterBlackboardComponent(); 58 | virtual void DestroyAndUnregisterBlackboardComponent(); 59 | virtual void SetKeySelfOnBlackboardComponent(UBlackboardComponent* BlackboardComp) const; 60 | 61 | // Return the BlackboardData to use at runtime 62 | // (subclasses may want to instance this class For Reasons) 63 | virtual UBlackboardData* EnsureRuntimeBlackboardData() const { return BlackboardAsset; } 64 | 65 | protected: 66 | 67 | // Blackboard asset for this FlowAsset 68 | UPROPERTY(EditAnywhere, Category = "AI Flow") 69 | TObjectPtr BlackboardAsset = nullptr; 70 | 71 | // Cached blackboard component (on the owning actor) 72 | UPROPERTY(Transient) 73 | TWeakObjectPtr BlackboardComponent = nullptr; 74 | 75 | // Manager object to inject and remove blackboard components from the Flow owning Actor 76 | UPROPERTY(Transient) 77 | TObjectPtr InjectComponentsManager = nullptr; 78 | 79 | // Subclass-configurable Blackboard component class to use 80 | UPROPERTY(Transient) 81 | TSubclassOf BlackboardComponentClass; 82 | 83 | // Random seed for anything that requires one in this FlowAsset 84 | UPROPERTY(Transient) 85 | int32 RandomSeed = 0; 86 | }; 87 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Blackboard/FlowBlackboardEntryValue_String.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Blackboard/FlowBlackboardEntryValue_String.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType_String.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "AIFlowLogChannels.h" 7 | #include "Nodes/FlowNode.h" 8 | #include "Types/FlowDataPinValuesStandard.h" 9 | #include "Types/FlowDataPinResults.h" 10 | 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardEntryValue_String) 12 | 13 | #if WITH_EDITOR 14 | void UFlowBlackboardEntryValue_String::PostInitProperties() 15 | { 16 | Super::PostInitProperties(); 17 | 18 | if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) 19 | { 20 | if (Key.AllowedTypes.IsEmpty()) 21 | { 22 | Key.AllowedTypes.Add(NewObject(this)); 23 | } 24 | } 25 | } 26 | 27 | FText UFlowBlackboardEntryValue_String::BuildNodeConfigText() const 28 | { 29 | return FText::FromString(FString::Printf(TEXT("Set %s to \"%s\""), *Key.GetKeyName().ToString(), *GetEditorValueString())); 30 | } 31 | #endif // WITH_EDITOR 32 | 33 | bool UFlowBlackboardEntryValue_String::TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const 34 | { 35 | OutFlowDataPinProperty.InitializeAs(StringValue); 36 | return true; 37 | } 38 | 39 | bool UFlowBlackboardEntryValue_String::TryProvideFlowDataPinPropertyFromBlackboardEntry( 40 | const FName& BlackboardKeyName, 41 | const UBlackboardKeyType& BlackboardKeyType, 42 | UBlackboardComponent* OptionalBlackboardComponent, 43 | TInstancedStruct& OutFlowDataPinProperty) const 44 | { 45 | return 46 | TryProvideFlowDataPinPropertyFromBlackboardEntryTemplate( 47 | BlackboardKeyName, 48 | BlackboardKeyType, 49 | OptionalBlackboardComponent, 50 | OutFlowDataPinProperty); 51 | } 52 | 53 | bool UFlowBlackboardEntryValue_String::TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) 54 | { 55 | const EFlowDataPinResolveResult ResolveResult = PinOwnerFlowNode.TryResolveDataPinValue(PinName, StringValue); 56 | return FlowPinType::IsSuccess(ResolveResult); 57 | } 58 | 59 | void UFlowBlackboardEntryValue_String::SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const 60 | { 61 | if (IsValid(BlackboardComponent)) 62 | { 63 | BlackboardComponent->SetValueAsString(Key.GetKeyName(), StringValue); 64 | } 65 | } 66 | 67 | EBlackboardCompare::Type UFlowBlackboardEntryValue_String::CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const 68 | { 69 | if (!IsValid(BlackboardComponent)) 70 | { 71 | UE_LOG(LogAIFlow, Error, TEXT("Cannot CompareKeyValues without a Blackboard!")); 72 | return EBlackboardCompare::NotEqual; 73 | } 74 | 75 | const FString OtherValueAsString = BlackboardComponent->GetValueAsString(OtherKeyName); 76 | 77 | if (StringValue == OtherValueAsString) 78 | { 79 | return EBlackboardCompare::Equal; 80 | } 81 | else 82 | { 83 | return EBlackboardCompare::NotEqual; 84 | } 85 | } 86 | 87 | TSubclassOf UFlowBlackboardEntryValue_String::GetSupportedBlackboardKeyType() const 88 | { 89 | return UBlackboardKeyType_String::StaticClass(); 90 | } 91 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Blackboard/FlowBlackboardEntryValue_Vector.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Blackboard/FlowBlackboardEntryValue_Vector.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Vector.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "AIFlowLogChannels.h" 7 | #include "Nodes/FlowNode.h" 8 | #include "Types/FlowDataPinValuesStandard.h" 9 | #include "Types/FlowDataPinResults.h" 10 | 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardEntryValue_Vector) 12 | 13 | #if WITH_EDITOR 14 | void UFlowBlackboardEntryValue_Vector::PostInitProperties() 15 | { 16 | Super::PostInitProperties(); 17 | 18 | if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) 19 | { 20 | if (Key.AllowedTypes.IsEmpty()) 21 | { 22 | Key.AllowedTypes.Add(NewObject(this)); 23 | } 24 | } 25 | } 26 | 27 | FText UFlowBlackboardEntryValue_Vector::BuildNodeConfigText() const 28 | { 29 | return FText::FromString(FString::Printf(TEXT("Set %s to \"%s\""), *Key.GetKeyName().ToString(), *GetEditorValueString())); 30 | } 31 | #endif // WITH_EDITOR 32 | 33 | bool UFlowBlackboardEntryValue_Vector::TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const 34 | { 35 | OutFlowDataPinProperty.InitializeAs(VectorValue); 36 | return true; 37 | } 38 | 39 | bool UFlowBlackboardEntryValue_Vector::TryProvideFlowDataPinPropertyFromBlackboardEntry( 40 | const FName& BlackboardKeyName, 41 | const UBlackboardKeyType& BlackboardKeyType, 42 | UBlackboardComponent* OptionalBlackboardComponent, 43 | TInstancedStruct& OutFlowDataPinProperty) const 44 | { 45 | return 46 | TryProvideFlowDataPinPropertyFromBlackboardEntryTemplate( 47 | BlackboardKeyName, 48 | BlackboardKeyType, 49 | OptionalBlackboardComponent, 50 | OutFlowDataPinProperty); 51 | } 52 | 53 | bool UFlowBlackboardEntryValue_Vector::TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) 54 | { 55 | const EFlowDataPinResolveResult ResolveResult = PinOwnerFlowNode.TryResolveDataPinValue(PinName, VectorValue); 56 | return FlowPinType::IsSuccess(ResolveResult); 57 | } 58 | 59 | void UFlowBlackboardEntryValue_Vector::SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const 60 | { 61 | if (IsValid(BlackboardComponent)) 62 | { 63 | BlackboardComponent->SetValueAsVector(Key.GetKeyName(), VectorValue); 64 | } 65 | } 66 | 67 | EBlackboardCompare::Type UFlowBlackboardEntryValue_Vector::CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const 68 | { 69 | if (!IsValid(BlackboardComponent)) 70 | { 71 | UE_LOG(LogAIFlow, Error, TEXT("Cannot CompareKeyValues without a Blackboard!")); 72 | return EBlackboardCompare::NotEqual; 73 | } 74 | 75 | const FVector OtherValueAsVector = BlackboardComponent->GetValueAsVector(OtherKeyName); 76 | 77 | if (VectorValue.Equals(OtherValueAsVector)) 78 | { 79 | return EBlackboardCompare::Equal; 80 | } 81 | else 82 | { 83 | return EBlackboardCompare::NotEqual; 84 | } 85 | } 86 | 87 | TSubclassOf UFlowBlackboardEntryValue_Vector::GetSupportedBlackboardKeyType() const 88 | { 89 | return UBlackboardKeyType_Vector::StaticClass(); 90 | } 91 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Nodes/FlowNode_GetBlackboardValues.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AIFlowActorBlackboardHelper.h" 6 | #include "Nodes/AIFlowNode.h" 7 | 8 | #include "FlowNode_GetBlackboardValues.generated.h" 9 | 10 | // Forward Declarations 11 | class UFlowBlackboardEntryValue; 12 | 13 | /** 14 | * Get blackboard values and provide them as output data pins 15 | */ 16 | UCLASS(DisplayName = "Get Blackboard Values") 17 | class AIFLOW_API UFlowNode_GetBlackboardValues : public UAIFlowNode 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | 23 | UFlowNode_GetBlackboardValues(); 24 | 25 | // UFlowNodeBase 26 | virtual void UpdateNodeConfigText_Implementation() override; 27 | // -- 28 | 29 | // IFlowDataPinValueSupplierInterface 30 | virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const override; 31 | // -- 32 | 33 | #if WITH_EDITOR 34 | public: 35 | 36 | // IFlowContextPinSupplierInterface 37 | virtual bool SupportsContextPins() const override { return Super::SupportsContextPins() || !BlackboardEntries.IsEmpty(); } 38 | // -- 39 | 40 | // UObject 41 | virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; 42 | // -- 43 | 44 | // IFlowDataPinGeneratorInterface 45 | virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; 46 | // -- 47 | 48 | // IFlowBlackboardAssetProvider 49 | virtual UBlackboardData* GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const override; 50 | // -- 51 | 52 | protected: 53 | 54 | void AppendEntriesForEveryActor(FTextBuilder& InOutTextBuilder) const; 55 | 56 | UBlackboardData* GetBlackboardAssetForEditor() const; 57 | #endif // WITH_EDITOR 58 | 59 | protected: 60 | 61 | static UBlackboardKeyType* GetBlackboardKeyTypeFromBlackboardKeyName(const UBlackboardData* BlackboardAsset, const FName& KeyName); 62 | 63 | UBlackboardComponent* GetBlackboardComponentToApplyTo() const; 64 | AActor* TryResolveActorForBlackboard() const; 65 | 66 | protected: 67 | 68 | // Optional specific actor to use for the blackboard query. 69 | // If not specified, will use the flow graph's owning actor. 70 | UPROPERTY(Transient, meta = (DefaultForInputFlowPin, FlowPinType = "Object", DisplayPriority = 2)) 71 | TObjectPtr SpecificActor = nullptr; 72 | 73 | // Specific blackboard to use (optional, defaults to the flow asset's blackboard) 74 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Specific Blackboard", meta = (DisplayPriority = 2)) 75 | TObjectPtr SpecificBlackboardAsset = nullptr; 76 | 77 | // Blackboard entries to get from the blackboard and output as data pins 78 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Blackboard Entries", meta = (DisplayPriority = 3)) 79 | TArray BlackboardEntries; 80 | 81 | // Search rule to use to find the "Specific Blackboard" (if specified) 82 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Specific Blackboard Search Rule", meta = (EditCondition = "SpecificBlackboardAsset", DisplayAfter = SpecificBlackboardAsset)) 83 | EActorBlackboardSearchRule SpecificBlackboardSearchRule = EActorBlackboardSearchRule::ActorAndControllerAndGameState; 84 | 85 | static FName INPIN_SpecificActor; 86 | }; 87 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Blackboard/FlowBlackboardEntryValue_Rotator.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Blackboard/FlowBlackboardEntryValue_Rotator.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Rotator.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "AIFlowLogChannels.h" 7 | #include "Nodes/FlowNode.h" 8 | #include "Types/FlowDataPinValuesStandard.h" 9 | #include "Types/FlowDataPinResults.h" 10 | 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardEntryValue_Rotator) 12 | 13 | #if WITH_EDITOR 14 | void UFlowBlackboardEntryValue_Rotator::PostInitProperties() 15 | { 16 | Super::PostInitProperties(); 17 | 18 | if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) 19 | { 20 | if (Key.AllowedTypes.IsEmpty()) 21 | { 22 | Key.AllowedTypes.Add(NewObject(this)); 23 | } 24 | } 25 | } 26 | 27 | FText UFlowBlackboardEntryValue_Rotator::BuildNodeConfigText() const 28 | { 29 | return FText::FromString(FString::Printf(TEXT("Set %s to \"%s\""), *Key.GetKeyName().ToString(), *GetEditorValueString())); 30 | } 31 | #endif // WITH_EDITOR 32 | 33 | bool UFlowBlackboardEntryValue_Rotator::TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const 34 | { 35 | OutFlowDataPinProperty.InitializeAs(RotatorValue); 36 | return false; 37 | } 38 | 39 | bool UFlowBlackboardEntryValue_Rotator::TryProvideFlowDataPinPropertyFromBlackboardEntry( 40 | const FName& BlackboardKeyName, 41 | const UBlackboardKeyType& BlackboardKeyType, 42 | UBlackboardComponent* OptionalBlackboardComponent, 43 | TInstancedStruct& OutFlowDataPinProperty) const 44 | { 45 | return 46 | TryProvideFlowDataPinPropertyFromBlackboardEntryTemplate( 47 | BlackboardKeyName, 48 | BlackboardKeyType, 49 | OptionalBlackboardComponent, 50 | OutFlowDataPinProperty); 51 | } 52 | 53 | bool UFlowBlackboardEntryValue_Rotator::TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) 54 | { 55 | const EFlowDataPinResolveResult ResolveResult = PinOwnerFlowNode.TryResolveDataPinValue(PinName, RotatorValue); 56 | return FlowPinType::IsSuccess(ResolveResult); 57 | } 58 | 59 | void UFlowBlackboardEntryValue_Rotator::SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const 60 | { 61 | if (IsValid(BlackboardComponent)) 62 | { 63 | BlackboardComponent->SetValueAsRotator(Key.GetKeyName(), RotatorValue); 64 | } 65 | } 66 | 67 | EBlackboardCompare::Type UFlowBlackboardEntryValue_Rotator::CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const 68 | { 69 | if (!IsValid(BlackboardComponent)) 70 | { 71 | UE_LOG(LogAIFlow, Error, TEXT("Cannot CompareKeyValues without a Blackboard!")); 72 | return EBlackboardCompare::NotEqual; 73 | } 74 | 75 | const FRotator OtherValueAsRotator = BlackboardComponent->GetValueAsRotator(OtherKeyName); 76 | 77 | if (RotatorValue.Equals(OtherValueAsRotator)) 78 | { 79 | return EBlackboardCompare::Equal; 80 | } 81 | else 82 | { 83 | return EBlackboardCompare::NotEqual; 84 | } 85 | } 86 | 87 | TSubclassOf UFlowBlackboardEntryValue_Rotator::GetSupportedBlackboardKeyType() const 88 | { 89 | return UBlackboardKeyType_Rotator::StaticClass(); 90 | } 91 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Blackboard/FlowBlackboardEntryValue_Name.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Blackboard/FlowBlackboardEntryValue_Name.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Name.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "AIFlowLogChannels.h" 7 | #include "Nodes/FlowNode.h" 8 | #include "Types/FlowDataPinValuesStandard.h" 9 | #include "Types/FlowDataPinResults.h" 10 | 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardEntryValue_Name) 12 | 13 | #if WITH_EDITOR 14 | void UFlowBlackboardEntryValue_Name::PostInitProperties() 15 | { 16 | Super::PostInitProperties(); 17 | 18 | if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) 19 | { 20 | if (Key.AllowedTypes.IsEmpty()) 21 | { 22 | Key.AllowedTypes.Add(NewObject(this)); 23 | } 24 | } 25 | } 26 | 27 | FString UFlowBlackboardEntryValue_Name::GetEditorValueString() const 28 | { 29 | return *NameValue.ToString(); 30 | } 31 | 32 | FText UFlowBlackboardEntryValue_Name::BuildNodeConfigText() const 33 | { 34 | return FText::FromString(FString::Printf(TEXT("Set %s to \"%s\""), *Key.GetKeyName().ToString(), *GetEditorValueString())); 35 | } 36 | #endif // WITH_EDITOR 37 | 38 | bool UFlowBlackboardEntryValue_Name::TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const 39 | { 40 | OutFlowDataPinProperty.InitializeAs(NameValue); 41 | return true; 42 | } 43 | 44 | bool UFlowBlackboardEntryValue_Name::TryProvideFlowDataPinPropertyFromBlackboardEntry( 45 | const FName& BlackboardKeyName, 46 | const UBlackboardKeyType& BlackboardKeyType, 47 | UBlackboardComponent* OptionalBlackboardComponent, 48 | TInstancedStruct& OutFlowDataPinProperty) const 49 | { 50 | return 51 | TryProvideFlowDataPinPropertyFromBlackboardEntryTemplate( 52 | BlackboardKeyName, 53 | BlackboardKeyType, 54 | OptionalBlackboardComponent, 55 | OutFlowDataPinProperty); 56 | } 57 | 58 | bool UFlowBlackboardEntryValue_Name::TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) 59 | { 60 | const EFlowDataPinResolveResult ResolveResult = PinOwnerFlowNode.TryResolveDataPinValue(PinName, NameValue); 61 | return FlowPinType::IsSuccess(ResolveResult); 62 | } 63 | 64 | void UFlowBlackboardEntryValue_Name::SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const 65 | { 66 | if (IsValid(BlackboardComponent)) 67 | { 68 | BlackboardComponent->SetValueAsName(Key.GetKeyName(), NameValue); 69 | } 70 | } 71 | 72 | EBlackboardCompare::Type UFlowBlackboardEntryValue_Name::CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const 73 | { 74 | if (!IsValid(BlackboardComponent)) 75 | { 76 | UE_LOG(LogAIFlow, Error, TEXT("Cannot CompareKeyValues without a Blackboard!")); 77 | return EBlackboardCompare::NotEqual; 78 | } 79 | 80 | const FName OtherValueAsName = BlackboardComponent->GetValueAsName(OtherKeyName); 81 | 82 | if (NameValue == OtherValueAsName) 83 | { 84 | return EBlackboardCompare::Equal; 85 | } 86 | else 87 | { 88 | return EBlackboardCompare::NotEqual; 89 | } 90 | } 91 | 92 | TSubclassOf UFlowBlackboardEntryValue_Name::GetSupportedBlackboardKeyType() const 93 | { 94 | return UBlackboardKeyType_Name::StaticClass(); 95 | } 96 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Blackboard/FlowBlackboardEntryValue_Bool.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Blackboard/FlowBlackboardEntryValue_Bool.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Bool.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "AIFlowLogChannels.h" 7 | #include "Nodes/FlowNode.h" 8 | #include "Types/FlowDataPinValuesStandard.h" 9 | #include "Types/FlowDataPinResults.h" 10 | 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardEntryValue_Bool) 12 | 13 | #if WITH_EDITOR 14 | void UFlowBlackboardEntryValue_Bool::PostInitProperties() 15 | { 16 | Super::PostInitProperties(); 17 | 18 | if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) 19 | { 20 | if (Key.AllowedTypes.IsEmpty()) 21 | { 22 | Key.AllowedTypes.Add(NewObject(this)); 23 | } 24 | } 25 | } 26 | 27 | FString UFlowBlackboardEntryValue_Bool::GetEditorValueString() const 28 | { 29 | return bBoolValue ? TEXT("true") : TEXT("false"); 30 | } 31 | 32 | FText UFlowBlackboardEntryValue_Bool::BuildNodeConfigText() const 33 | { 34 | return FText::FromString(FString::Printf(TEXT("Set %s to \"%s\""), *Key.GetKeyName().ToString(), *GetEditorValueString())); 35 | } 36 | #endif // WITH_EDITOR 37 | 38 | bool UFlowBlackboardEntryValue_Bool::TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const 39 | { 40 | OutFlowDataPinProperty.InitializeAs(bBoolValue); 41 | return true; 42 | } 43 | 44 | bool UFlowBlackboardEntryValue_Bool::TryProvideFlowDataPinPropertyFromBlackboardEntry( 45 | const FName& BlackboardKeyName, 46 | const UBlackboardKeyType& BlackboardKeyType, 47 | UBlackboardComponent* OptionalBlackboardComponent, 48 | TInstancedStruct& OutFlowDataPinProperty) const 49 | { 50 | return 51 | TryProvideFlowDataPinPropertyFromBlackboardEntryTemplate( 52 | BlackboardKeyName, 53 | BlackboardKeyType, 54 | OptionalBlackboardComponent, 55 | OutFlowDataPinProperty); 56 | } 57 | 58 | bool UFlowBlackboardEntryValue_Bool::TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) 59 | { 60 | const EFlowDataPinResolveResult ResolveResult = PinOwnerFlowNode.TryResolveDataPinValue(PinName, bBoolValue); 61 | return FlowPinType::IsSuccess(ResolveResult); 62 | } 63 | 64 | void UFlowBlackboardEntryValue_Bool::SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const 65 | { 66 | if (IsValid(BlackboardComponent)) 67 | { 68 | BlackboardComponent->SetValueAsBool(Key.GetKeyName(), bBoolValue); 69 | } 70 | } 71 | 72 | EBlackboardCompare::Type UFlowBlackboardEntryValue_Bool::CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const 73 | { 74 | if (!IsValid(BlackboardComponent)) 75 | { 76 | UE_LOG(LogAIFlow, Error, TEXT("Cannot CompareKeyValues without a Blackboard!")); 77 | return EBlackboardCompare::NotEqual; 78 | } 79 | 80 | const bool bOtherKeyValue = BlackboardComponent->GetValueAsBool(OtherKeyName); 81 | 82 | if (bBoolValue == bOtherKeyValue) 83 | { 84 | return EBlackboardCompare::Equal; 85 | } 86 | else 87 | { 88 | return EBlackboardCompare::NotEqual; 89 | } 90 | } 91 | 92 | TSubclassOf UFlowBlackboardEntryValue_Bool::GetSupportedBlackboardKeyType() const 93 | { 94 | return UBlackboardKeyType_Bool::StaticClass(); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/AddOns/FlowNodeAddOn_ConfigureSpawnedActorBlackboard.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AIFlowActorBlackboardHelper.h" 6 | #include "Interfaces/FlowPerSpawnedActorInterface.h" 7 | #include "FlowNodeAddOn_InjectComponentsBase.h" 8 | 9 | #include "FlowNodeAddOn_ConfigureSpawnedActorBlackboard.generated.h" 10 | 11 | class UFlowInjectComponentsManager; 12 | 13 | /** 14 | * Set blackboard values to the values defined in the Entries array 15 | */ 16 | UCLASS(DisplayName = "Configure Spawned Actor Blackboard") 17 | class AIFLOW_API UFlowNodeAddOn_ConfigureSpawnedActorBlackboard : public UFlowNodeAddOn_InjectComponentsBase 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | 23 | UFlowNodeAddOn_ConfigureSpawnedActorBlackboard(); 24 | 25 | // IFlowPerSpawnedActorInterface 26 | virtual void FinishedSpawningActor_Implementation(AActor* SpawnedActor, UFlowNodeBase* SpawningNodeOrAddOn) override; 27 | // -- 28 | 29 | // UFlowNodeBase 30 | virtual void UpdateNodeConfigText_Implementation() override; 31 | // -- 32 | 33 | // IBlackboardAssetProvider 34 | virtual UBlackboardData* GetBlackboardAsset() const override; 35 | // -- 36 | 37 | // IFlowBlackboardAssetProvider 38 | #if WITH_EDITOR 39 | virtual UBlackboardData* GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const override; 40 | #endif // WITH_EDITOR 41 | // -- 42 | 43 | protected: 44 | 45 | virtual UBlackboardComponent* TryEnsureBlackboardComponentToApplyTo(AActor* SpawnedActor, UFlowNodeBase* SpawningNodeOrAddOn); 46 | 47 | protected: 48 | 49 | // Specify an explicit blackboard asset to write to 50 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Expected Blackboard Asset for Actors", meta = (DisplayPriority = 1)) 51 | UBlackboardData* ExpectedBlackboardData = nullptr; 52 | 53 | // Blackboard properties to set on every spawned actor 54 | UPROPERTY(EditAnywhere, Category = Configuration, meta = (DisplayPriority = 3)) 55 | FAIFlowConfigureBlackboardOption EntriesForEveryActor; 56 | 57 | // Where to search for the desired blackboard: the Actor, their Controller (if Pawn) or both. 58 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Blackboard Component Search Rule", meta = (DisplayPriority = 2)) 59 | EActorBlackboardSearchRule SearchRule = EActorBlackboardSearchRule::ActorAndController; 60 | 61 | // Injection rule, if desired blackboard is not found 62 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Blackboard Injection Rule", meta = (DisplayPriority = 2)) 63 | EActorBlackboardInjectRule InjectRule = EActorBlackboardInjectRule::InjectOntoActorIfMissing; 64 | 65 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Per-Actor Options Assignment Method", meta = (DisplayPriority = 4)) 66 | EPerActorOptionsAssignmentMethod PerActorOptionsAssignmentMethod = EPerActorOptionsAssignmentMethod::InOrderWithWrapping; 67 | 68 | // Configured blackboard entry option sets to apply to actors according to the application method 69 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Per-Actor Options", meta = (DisplayPriority = 4)) 70 | TArray PerActorOptions; 71 | 72 | // Helper struct that shared functionality for manipulating Actor blackboards 73 | UPROPERTY(EditAnywhere, Category = Configuration, meta = (ShowOnlyInnerProperties, DisplayPriority = 4)) 74 | FAIFlowActorBlackboardHelper ActorBlackboardHelper; 75 | }; 76 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue_Object.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "FlowBlackboardEntryValue.h" 6 | 7 | #include "FlowBlackboardEntryValue_Object.generated.h" 8 | 9 | // Selector enum to control the EditCondition for ObjectInstance/ObjectAsset switching 10 | UENUM() 11 | enum class EObjectInstanceTypeSelector : uint8 12 | { 13 | Unknown, 14 | Instanced, 15 | Asset, 16 | }; 17 | 18 | /** 19 | * Configuration object for setting blackboard entries for UBlackboardKeyType_Object entries 20 | */ 21 | UCLASS(BlueprintType, DisplayName = "Object Blackboard Value") 22 | class UFlowBlackboardEntryValue_Object : public UFlowBlackboardEntryValue 23 | { 24 | GENERATED_BODY() 25 | 26 | public: 27 | 28 | //~Begin UFlowBlackboardEntryValue 29 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const override; 30 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const override; 31 | virtual TSubclassOf GetSupportedBlackboardKeyType() const override; 32 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) override; 33 | #if WITH_EDITOR 34 | virtual bool TryReconfigureFromBlackboardKeyType(const UBlackboardKeyType& KeyType) override; 35 | virtual FString GetEditorValueString() const override; 36 | #endif // WITH_EDITOR 37 | //~End UFlowBlackboardEntryValue 38 | 39 | // IFlowDataPinPropertyProviderInterface 40 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const override; 41 | // -- 42 | 43 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 44 | const FName& BlackboardKeyName, 45 | const UBlackboardKeyType& BlackboardKeyType, 46 | UBlackboardComponent* OptionalBlackboardComponent, 47 | TInstancedStruct& OutFlowDataPinProperty) const override; 48 | 49 | UObject* GetObjectValue() const; 50 | void SetObjectValue(UObject* InValue); 51 | 52 | #if WITH_EDITOR 53 | public: 54 | //~Begin UFlowNodeBase 55 | virtual FText BuildNodeConfigText() const override; 56 | //~End UFlowNodeBase 57 | 58 | //~Begin UObject 59 | virtual void PostInitProperties() override; 60 | virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; 61 | //~End UObject 62 | 63 | protected: 64 | void EnsureObjectsAreCompatibleWithBaseClass(); 65 | void RefreshObjectTypeFromBaseClass(); 66 | #endif // WITH_EDITOR 67 | 68 | protected: 69 | #if WITH_EDITORONLY_DATA 70 | // The BaseClass for the Object to set 71 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (AllowAbstract = "true")) 72 | TObjectPtr BaseClass = nullptr; 73 | #endif // WITH_EDITORONLY_DATA 74 | 75 | // If an inline Instanced object, use this property to configure it 76 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category = Configuration, meta = (DisplayAfter = Key, EditCondition = "ObjectTypeSelector == EObjectInstanceTypeSelector::Instanced")) 77 | TObjectPtr ObjectInstance = nullptr; 78 | 79 | // If the object is to be created from an asset, use this property to configure it 80 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (DisplayAfter = ObjectInstance, EditCondition = "ObjectTypeSelector == EObjectInstanceTypeSelector::Asset")) 81 | TObjectPtr ObjectAsset = nullptr; 82 | 83 | #if WITH_EDITORONLY_DATA 84 | UPROPERTY() 85 | EObjectInstanceTypeSelector ObjectTypeSelector = EObjectInstanceTypeSelector::Unknown; 86 | #endif // WITH_EDITORONLY_DATA 87 | }; 88 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Nodes/AIFlowNode_ExecutionRollWeighted.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Nodes/AIFlowNode_ExecutionRollWeighted.h" 4 | 5 | #include UE_INLINE_GENERATED_CPP_BY_NAME(AIFlowNode_ExecutionRollWeighted) 6 | 7 | FAIFlowNode_RollWeightedOption::FAIFlowNode_RollWeightedOption(const FName InOutputName, int32 InWeight) 8 | : OutputName(InOutputName) 9 | , Weight(InWeight) 10 | { 11 | } 12 | 13 | UAIFlowNode_ExecutionRollWeighted::UAIFlowNode_ExecutionRollWeighted(const FObjectInitializer& ObjectInitializer) 14 | : Super(ObjectInitializer) 15 | { 16 | #if WITH_EDITOR 17 | NodeDisplayStyle = FlowNodeStyle::Default; 18 | Category = TEXT("Flow|Routing"); 19 | #endif 20 | 21 | OutputPins.Empty(); 22 | 23 | AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; 24 | } 25 | 26 | int32 UAIFlowNode_ExecutionRollWeighted::GetWeightedRandomChoice() 27 | { 28 | CalculateTotalWeight(); 29 | if (TotalWeight <= 0) 30 | { 31 | return INDEX_NONE; 32 | } 33 | 34 | const int32 ChosenWeight = RandomStream.RandHelper(TotalWeight); 35 | check(ChosenWeight <= TotalWeight); 36 | check(ChosenWeight >= 0); 37 | 38 | int32 CumulativeWeight = 0; 39 | 40 | for (int32 OptionIndex = 0; OptionIndex < OutputPinOptions.Num(); ++OptionIndex) 41 | { 42 | const FAIFlowNode_RollWeightedOption& Option = OutputPinOptions[OptionIndex]; 43 | const int32 Weight = Option.OutputName != NAME_None ? Option.Weight : 0; 44 | check(Weight >= 0); 45 | 46 | CumulativeWeight += Weight; 47 | 48 | if (ChosenWeight < CumulativeWeight) 49 | { 50 | return OptionIndex; 51 | } 52 | } 53 | 54 | const int32 FinalIndex = OutputPinOptions.Num() - 1; 55 | return FinalIndex; 56 | } 57 | 58 | void UAIFlowNode_ExecutionRollWeighted::OnActivate() 59 | { 60 | Super::OnActivate(); 61 | 62 | const int32 RandomSeed = GetRandomSeed(); 63 | RandomStream.Initialize(RandomSeed); 64 | } 65 | 66 | void UAIFlowNode_ExecutionRollWeighted::ExecuteInput(const FName& PinName) 67 | { 68 | const int32 ChosenOptionIndex = GetWeightedRandomChoice(); 69 | if(OutputPins.IsValidIndex(ChosenOptionIndex)) 70 | { 71 | TriggerOutput(OutputPins[ChosenOptionIndex].PinName, true); 72 | } 73 | } 74 | 75 | void UAIFlowNode_ExecutionRollWeighted::CalculateTotalWeight() 76 | { 77 | TotalWeight = 0; 78 | for (int32 OptionIndex = 0; OptionIndex < OutputPinOptions.Num(); ++OptionIndex) 79 | { 80 | const FAIFlowNode_RollWeightedOption& Option = OutputPinOptions[OptionIndex]; 81 | const int32 Weight = Option.OutputName != NAME_None ? Option.Weight : 0; 82 | check(Weight >= 0); 83 | 84 | TotalWeight += Weight; 85 | } 86 | } 87 | 88 | #if WITH_EDITOR 89 | 90 | TArray UAIFlowNode_ExecutionRollWeighted::GetContextOutputs() const 91 | { 92 | TArray ContextPins; 93 | 94 | for (const FAIFlowNode_RollWeightedOption Output : OutputPinOptions) 95 | { 96 | if (Output.OutputName != NAME_None) 97 | { 98 | ContextPins.Add(FFlowPin(Output.OutputName.ToString())); 99 | } 100 | } 101 | 102 | return ContextPins; 103 | } 104 | 105 | void UAIFlowNode_ExecutionRollWeighted::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) 106 | { 107 | Super::PostEditChangeProperty(PropertyChangedEvent); 108 | 109 | const FName MemberPropertyName = (PropertyChangedEvent.MemberProperty != nullptr) ? PropertyChangedEvent.MemberProperty->GetFName() : NAME_None; 110 | 111 | if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAIFlowNode_ExecutionRollWeighted, OutputPinOptions)) 112 | { 113 | OnReconstructionRequested.ExecuteIfBound(); 114 | } 115 | } 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Blackboard/FlowBlackboardEntryValue.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Blackboard/FlowBlackboardEntryValue.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType.h" 5 | 6 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardEntryValue) 7 | 8 | TArray> UFlowBlackboardEntryValue::CachedBlackboardEntryValueSubclassArray; 9 | 10 | UBlackboardData* UFlowBlackboardEntryValue::GetBlackboardAsset() const 11 | { 12 | if (IFlowBlackboardAssetProvider* OuterProvider = Cast(GetOuter())) 13 | { 14 | return OuterProvider->GetBlackboardAsset(); 15 | } 16 | 17 | return nullptr; 18 | } 19 | 20 | const TArray>& UFlowBlackboardEntryValue::EnsureBlackboardEntryValueSubclassArray() 21 | { 22 | // NOTE (gtaylor) Potentially vulnerable to modules loading in subclasses after this initial caching 23 | 24 | if (CachedBlackboardEntryValueSubclassArray.IsEmpty()) 25 | { 26 | TArray Subclasses; 27 | GetDerivedClasses(UFlowBlackboardEntryValue::StaticClass(), Subclasses); 28 | 29 | for (UClass* Subclass : Subclasses) 30 | { 31 | CachedBlackboardEntryValueSubclassArray.Add(Subclass); 32 | } 33 | } 34 | 35 | return CachedBlackboardEntryValueSubclassArray; 36 | } 37 | 38 | #if WITH_EDITOR 39 | UBlackboardData* UFlowBlackboardEntryValue::GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const 40 | { 41 | if (IFlowBlackboardAssetProvider* OuterProvider = Cast(GetOuter())) 42 | { 43 | return OuterProvider->GetBlackboardAssetForPropertyHandle(PropertyHandle); 44 | } 45 | 46 | return nullptr; 47 | } 48 | 49 | bool UFlowBlackboardEntryValue::TryReconfigureFromBlackboardKeyType(const UBlackboardKeyType& KeyType) 50 | { 51 | TSubclassOf SupportedKeyClass = GetSupportedBlackboardKeyType(); 52 | check(SupportedKeyClass && KeyType.IsA(SupportedKeyClass)); 53 | check(Key.AllowedTypes.IsEmpty() || Key.AllowedTypes.Num() == 1); 54 | 55 | if (Key.AllowedTypes.Num() == 1 && Key.AllowedTypes[0]->IsA(SupportedKeyClass)) 56 | { 57 | // No change to the supported class 58 | return false; 59 | } 60 | else 61 | { 62 | // Reconfigure the AllowedTypes to match the passed-in KeyType 63 | Key.AllowedTypes.Reset(); 64 | 65 | UBlackboardKeyType* InstancedKeyType = NewObject(this, KeyType.GetClass(), NAME_None, RF_NoFlags, const_cast(&KeyType)); 66 | 67 | Key.AllowedTypes.Add(InstancedKeyType); 68 | 69 | return true; 70 | } 71 | } 72 | 73 | TSubclassOf UFlowBlackboardEntryValue::GetFlowBlackboardEntryValueClassForKeyType(TSubclassOf KeyTypeClass) 74 | { 75 | if (!KeyTypeClass) 76 | { 77 | return nullptr; 78 | } 79 | 80 | TArray Subclasses; 81 | GetDerivedClasses(UFlowBlackboardEntryValue::StaticClass(), Subclasses); 82 | 83 | for (UClass* Subclass : Subclasses) 84 | { 85 | const UFlowBlackboardEntryValue* TypedSubclassCDO = Cast(Subclass->GetDefaultObject()); 86 | 87 | TSubclassOf SupportedType = TypedSubclassCDO->GetSupportedBlackboardKeyType(); 88 | if (SupportedType && KeyTypeClass->IsChildOf(SupportedType)) 89 | { 90 | // NOTE (gtaylor) Taking the first class found that supports the KeyTypeClass. 91 | // We could, instead, keep searching and resolve between multiple possible choices with a "best fit" algorithm of some sort. 92 | // But that's overkill at this point, since we don't have any expectation that that is a case we will need to support. 93 | 94 | return Subclass; 95 | } 96 | } 97 | 98 | return nullptr; 99 | } 100 | 101 | #endif // WITH_EDITOR -------------------------------------------------------------------------------- /Source/AIFlow/Private/Blackboard/FlowBlackboardEntryValue_Int.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Blackboard/FlowBlackboardEntryValue_Int.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Int.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "AIFlowLogChannels.h" 7 | #include "Nodes/FlowNode.h" 8 | #include "Types/FlowDataPinValuesStandard.h" 9 | #include "Types/FlowDataPinResults.h" 10 | 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardEntryValue_Int) 12 | 13 | #if WITH_EDITOR 14 | void UFlowBlackboardEntryValue_Int::PostInitProperties() 15 | { 16 | Super::PostInitProperties(); 17 | 18 | if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) 19 | { 20 | if (Key.AllowedTypes.IsEmpty()) 21 | { 22 | Key.AllowedTypes.Add(NewObject(this)); 23 | } 24 | } 25 | } 26 | 27 | FString UFlowBlackboardEntryValue_Int::GetEditorValueString() const 28 | { 29 | return FString::FromInt(IntValue); 30 | } 31 | 32 | FText UFlowBlackboardEntryValue_Int::BuildNodeConfigText() const 33 | { 34 | return FText::FromString(FString::Printf(TEXT("Set %s to \"%d\""), *Key.GetKeyName().ToString(), IntValue)); 35 | } 36 | #endif // WITH_EDITOR 37 | 38 | bool UFlowBlackboardEntryValue_Int::TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const 39 | { 40 | OutFlowDataPinProperty.InitializeAs(IntValue); 41 | return true; 42 | } 43 | 44 | bool UFlowBlackboardEntryValue_Int::TryProvideFlowDataPinPropertyFromBlackboardEntry( 45 | const FName& BlackboardKeyName, 46 | const UBlackboardKeyType& BlackboardKeyType, 47 | UBlackboardComponent* OptionalBlackboardComponent, 48 | TInstancedStruct& OutFlowDataPinProperty) const 49 | { 50 | return 51 | TryProvideFlowDataPinPropertyFromBlackboardEntryTemplate( 52 | BlackboardKeyName, 53 | BlackboardKeyType, 54 | OptionalBlackboardComponent, 55 | OutFlowDataPinProperty); 56 | } 57 | 58 | bool UFlowBlackboardEntryValue_Int::TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) 59 | { 60 | const EFlowDataPinResolveResult ResolveResult = PinOwnerFlowNode.TryResolveDataPinValue(PinName, IntValue); 61 | return FlowPinType::IsSuccess(ResolveResult); 62 | } 63 | 64 | void UFlowBlackboardEntryValue_Int::SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const 65 | { 66 | if (IsValid(BlackboardComponent)) 67 | { 68 | BlackboardComponent->SetValueAsInt(Key.GetKeyName(), IntValue); 69 | } 70 | } 71 | 72 | EBlackboardCompare::Type UFlowBlackboardEntryValue_Int::CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const 73 | { 74 | if (!IsValid(BlackboardComponent)) 75 | { 76 | UE_LOG(LogAIFlow, Error, TEXT("Cannot CompareKeyValues without a Blackboard!")); 77 | return EBlackboardCompare::NotEqual; 78 | } 79 | 80 | const int32 OtherValueAsInt = BlackboardComponent->GetValueAsInt(OtherKeyName); 81 | 82 | if (IntValue == OtherValueAsInt) 83 | { 84 | return EBlackboardCompare::Equal; 85 | } 86 | else 87 | { 88 | return EBlackboardCompare::NotEqual; 89 | } 90 | } 91 | 92 | TSubclassOf UFlowBlackboardEntryValue_Int::GetSupportedBlackboardKeyType() const 93 | { 94 | return UBlackboardKeyType_Int::StaticClass(); 95 | } 96 | 97 | bool UFlowBlackboardEntryValue_Int::TryGetNumericalValuesForArithmeticOperation(int32* OutIntValue, float* OutFloatValue) const 98 | { 99 | if (OutIntValue) 100 | { 101 | *OutIntValue = IntValue; 102 | } 103 | 104 | if (OutFloatValue) 105 | { 106 | *OutFloatValue = static_cast(IntValue); 107 | } 108 | 109 | return true; 110 | } 111 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Interfaces/FlowBlackboardInterface.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Interfaces/FlowBlackboardInterface.h" 4 | #include "BehaviorTree/BlackboardData.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "BehaviorTree/Blackboard/BlackboardKeyType.h" 7 | 8 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardInterface) 9 | 10 | bool IFlowBlackboardInterface::IsValidBlackboardKey(const FName& KeyName) const 11 | { 12 | UBlackboardComponent* BlackboardComp = GetBlackboardComponent(); 13 | if (!IsValid(BlackboardComp)) 14 | { 15 | return false; 16 | } 17 | 18 | return IsValidBlackboardKey(*BlackboardComp, KeyName); 19 | } 20 | 21 | bool IFlowBlackboardInterface::IsValidBlackboardKey(const UBlackboardComponent& BlackboardComp, const FName& KeyName) 22 | { 23 | const FBlackboard::FKey KeyId = BlackboardComp.GetKeyID(KeyName); 24 | const bool bIsValidKey = BlackboardComp.IsValidKey(KeyId); 25 | return bIsValidKey; 26 | } 27 | 28 | TArray IFlowBlackboardInterface::GatherAllBlackboardKeysOfType(UBlackboardKeyType* AllowedType) const 29 | { 30 | UBlackboardComponent* BlackboardComp = GetBlackboardComponent(); 31 | if (!IsValid(BlackboardComp)) 32 | { 33 | return TArray(); 34 | } 35 | 36 | return GatherAllBlackboardKeysOfType(*BlackboardComp, AllowedType); 37 | } 38 | 39 | TArray IFlowBlackboardInterface::GatherAllBlackboardKeysOfType(const UBlackboardComponent& BlackboardComp, UBlackboardKeyType* AllowedType) 40 | { 41 | TArray MatchingKeys; 42 | 43 | UBlackboardData* BlackboardAsset = BlackboardComp.GetBlackboardAsset(); 44 | if (!IsValid(BlackboardAsset)) 45 | { 46 | return MatchingKeys; 47 | } 48 | 49 | MatchingKeys.Reserve(BlackboardComp.GetNumKeys()); 50 | 51 | // Get matching keys from all blackboards 52 | for (const UBlackboardData* It = BlackboardAsset; It; It = It->Parent) 53 | { 54 | for (int32 KeyIndex = 0; KeyIndex < It->Keys.Num(); KeyIndex++) 55 | { 56 | const FBlackboardEntry& EntryInfo = It->Keys[KeyIndex]; 57 | if (!EntryInfo.KeyType) 58 | { 59 | continue; 60 | } 61 | 62 | const bool bFilterPassed = !IsValid(AllowedType) || EntryInfo.KeyType->IsAllowedByFilter(AllowedType); 63 | 64 | if (bFilterPassed) 65 | { 66 | MatchingKeys.Add(EntryInfo.EntryName); 67 | } 68 | } 69 | } 70 | 71 | return MatchingKeys; 72 | } 73 | 74 | UBlackboardKeyType* IFlowBlackboardInterface::GetBlackboardKeyType(const FName& KeyName) const 75 | { 76 | UBlackboardComponent* BlackboardComp = GetBlackboardComponent(); 77 | if (!IsValid(BlackboardComp)) 78 | { 79 | return nullptr; 80 | } 81 | 82 | return GetBlackboardKeyType(*BlackboardComp, KeyName); 83 | } 84 | 85 | UBlackboardKeyType* IFlowBlackboardInterface::GetBlackboardKeyType(const UBlackboardComponent& BlackboardComp, const FName& KeyName) 86 | { 87 | return GetBlackboardKeyType(BlackboardComp.GetBlackboardAsset(), KeyName); 88 | } 89 | 90 | UBlackboardKeyType* IFlowBlackboardInterface::GetBlackboardKeyType(const UBlackboardData* BlackboardAsset, const FName& KeyName) 91 | { 92 | if (!BlackboardAsset) 93 | { 94 | return nullptr; 95 | } 96 | 97 | const FBlackboard::FKey KeyId = BlackboardAsset->GetKeyID(KeyName); 98 | 99 | // Look for the key on all matching blackboards 100 | for (const UBlackboardData* It = BlackboardAsset; It; It = It->Parent) 101 | { 102 | if (const FBlackboardEntry* BlackboardEntry = It->GetKey(KeyId)) 103 | { 104 | return BlackboardEntry->KeyType; 105 | } 106 | } 107 | 108 | return nullptr; 109 | } 110 | 111 | UBlackboardComponent* IFlowBlackboardInterface::GetBlackboardComponent() const 112 | { 113 | // Must be overridden by implementing classes to return the appropriate blackboard component 114 | return nullptr; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Blackboard/FlowBlackboardEntryValue_Float.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Blackboard/FlowBlackboardEntryValue_Float.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Float.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "AIFlowLogChannels.h" 7 | #include "Nodes/FlowNode.h" 8 | #include "Types/FlowDataPinValuesStandard.h" 9 | #include "Types/FlowDataPinResults.h" 10 | 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardEntryValue_Float) 12 | 13 | #if WITH_EDITOR 14 | void UFlowBlackboardEntryValue_Float::PostInitProperties() 15 | { 16 | Super::PostInitProperties(); 17 | 18 | if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) 19 | { 20 | if (Key.AllowedTypes.IsEmpty()) 21 | { 22 | Key.AllowedTypes.Add(NewObject(this)); 23 | } 24 | } 25 | } 26 | 27 | FString UFlowBlackboardEntryValue_Float::GetEditorValueString() const 28 | { 29 | return FString::Printf(TEXT("%f"), FloatValue); 30 | } 31 | 32 | FText UFlowBlackboardEntryValue_Float::BuildNodeConfigText() const 33 | { 34 | return FText::FromString(FString::Printf(TEXT("Set %s to \"%f\""), *Key.GetKeyName().ToString(), FloatValue)); 35 | } 36 | #endif // WITH_EDITOR 37 | 38 | bool UFlowBlackboardEntryValue_Float::TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const 39 | { 40 | OutFlowDataPinProperty.InitializeAs(FloatValue); 41 | return true; 42 | } 43 | 44 | bool UFlowBlackboardEntryValue_Float::TryProvideFlowDataPinPropertyFromBlackboardEntry( 45 | const FName& BlackboardKeyName, 46 | const UBlackboardKeyType& BlackboardKeyType, 47 | UBlackboardComponent* OptionalBlackboardComponent, 48 | TInstancedStruct& OutFlowDataPinProperty) const 49 | { 50 | return 51 | TryProvideFlowDataPinPropertyFromBlackboardEntryTemplate( 52 | BlackboardKeyName, 53 | BlackboardKeyType, 54 | OptionalBlackboardComponent, 55 | OutFlowDataPinProperty); 56 | } 57 | 58 | bool UFlowBlackboardEntryValue_Float::TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) 59 | { 60 | const EFlowDataPinResolveResult ResolveResult = PinOwnerFlowNode.TryResolveDataPinValue(PinName, FloatValue); 61 | return FlowPinType::IsSuccess(ResolveResult); 62 | } 63 | 64 | void UFlowBlackboardEntryValue_Float::SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const 65 | { 66 | if (IsValid(BlackboardComponent)) 67 | { 68 | BlackboardComponent->SetValueAsFloat(Key.GetKeyName(), FloatValue); 69 | } 70 | } 71 | 72 | EBlackboardCompare::Type UFlowBlackboardEntryValue_Float::CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const 73 | { 74 | if (!IsValid(BlackboardComponent)) 75 | { 76 | UE_LOG(LogAIFlow, Error, TEXT("Cannot CompareKeyValues without a Blackboard!")); 77 | return EBlackboardCompare::NotEqual; 78 | } 79 | 80 | const float OtherValueAsFloat = BlackboardComponent->GetValueAsFloat(OtherKeyName); 81 | 82 | if (FMath::IsNearlyEqual(FloatValue, OtherValueAsFloat)) 83 | { 84 | return EBlackboardCompare::Equal; 85 | } 86 | else 87 | { 88 | return EBlackboardCompare::NotEqual; 89 | } 90 | } 91 | 92 | TSubclassOf UFlowBlackboardEntryValue_Float::GetSupportedBlackboardKeyType() const 93 | { 94 | return UBlackboardKeyType_Float::StaticClass(); 95 | } 96 | 97 | bool UFlowBlackboardEntryValue_Float::TryGetNumericalValuesForArithmeticOperation(int32* OutIntValue, float* OutFloatValue) const 98 | { 99 | if (OutIntValue) 100 | { 101 | *OutIntValue = FMath::FloorToInt32(FloatValue); 102 | } 103 | 104 | if (OutFloatValue) 105 | { 106 | *OutFloatValue = FloatValue; 107 | } 108 | 109 | return true; 110 | } 111 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Private/DetailCustomizations/ConfigurableEnumPropertyCustomization.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "ConfigurableEnumPropertyCustomization.h" 4 | #include "Types/ConfigurableEnumProperty.h" 5 | #include "Nodes/FlowPin.h" 6 | 7 | #include "IDetailChildrenBuilder.h" 8 | #include "UObject/UnrealType.h" 9 | 10 | // FConfigurableEnumPropertyCustomization Implementation 11 | 12 | void FConfigurableEnumPropertyCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) 13 | { 14 | if (TSharedPtr EnumClassHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FConfigurableEnumProperty, EnumClass))) 15 | { 16 | StructBuilder.AddProperty(EnumClassHandle.ToSharedRef()); 17 | } 18 | 19 | if (TSharedPtr EnumNameHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FConfigurableEnumProperty, EnumName))) 20 | { 21 | StructBuilder.AddProperty(EnumNameHandle.ToSharedRef()); 22 | 23 | EnumNameHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FConfigurableEnumPropertyCustomization::OnEnumNameChanged)); 24 | } 25 | } 26 | 27 | TSharedPtr FConfigurableEnumPropertyCustomization::GetCuratedNamePropertyHandle() const 28 | { 29 | check(StructPropertyHandle->IsValidHandle()); 30 | 31 | TSharedPtr FoundHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FConfigurableEnumProperty, Value)); 32 | check(FoundHandle); 33 | 34 | return FoundHandle; 35 | } 36 | 37 | TArray FConfigurableEnumPropertyCustomization::GetCuratedNameOptions() const 38 | { 39 | TArray Results; 40 | 41 | const UEnum* Enum = GetEnumClass(); 42 | 43 | if (IsValid(Enum)) 44 | { 45 | Results = GetEnumValues(*Enum); 46 | } 47 | 48 | return Results; 49 | } 50 | 51 | TArray FConfigurableEnumPropertyCustomization::GetEnumValues(const UEnum& Enum) 52 | { 53 | TArray EnumValues; 54 | 55 | for (int Index = 0; Index < Enum.GetMaxEnumValue(); Index++) 56 | { 57 | if (!Enum.IsValidEnumValue(Index)) 58 | { 59 | continue; 60 | } 61 | 62 | static const TCHAR* MetaDataKey_Hidden = TEXT("Hidden"); 63 | if (!Enum.HasMetaData(MetaDataKey_Hidden, Index)) 64 | { 65 | EnumValues.Add(*Enum.GetDisplayNameTextByIndex(Index).ToString()); 66 | } 67 | } 68 | 69 | return EnumValues; 70 | } 71 | 72 | void FConfigurableEnumPropertyCustomization::SetCuratedName(const FName& NewValue) 73 | { 74 | TSharedPtr ValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FConfigurableEnumProperty, Value)); 75 | 76 | check(ValueHandle); 77 | 78 | ValueHandle->SetPerObjectValue(0, NewValue.ToString()); 79 | } 80 | 81 | bool FConfigurableEnumPropertyCustomization::TryGetCuratedName(FName& OutName) const 82 | { 83 | const FConfigurableEnumProperty* ConfigurableEnumProperty = GetConfigurableEnumProperty(); 84 | if (ConfigurableEnumProperty) 85 | { 86 | OutName = ConfigurableEnumProperty->Value; 87 | 88 | return true; 89 | } 90 | else 91 | { 92 | return false; 93 | } 94 | } 95 | 96 | void FConfigurableEnumPropertyCustomization::OnEnumNameChanged() 97 | { 98 | FConfigurableEnumProperty* ConfigurableEnumProperty = GetConfigurableEnumProperty(); 99 | if (!ConfigurableEnumProperty) 100 | { 101 | return; 102 | } 103 | 104 | if (!ConfigurableEnumProperty->EnumName.IsEmpty()) 105 | { 106 | ConfigurableEnumProperty->EnumClass = UClass::TryFindTypeSlow(ConfigurableEnumProperty->EnumName, EFindFirstObjectOptions::ExactClass); 107 | 108 | if (ConfigurableEnumProperty->EnumClass != nullptr && !FFlowPin::ValidateEnum(*ConfigurableEnumProperty->EnumClass)) 109 | { 110 | ConfigurableEnumProperty->EnumClass = nullptr; 111 | } 112 | } 113 | } 114 | 115 | const UEnum* FConfigurableEnumPropertyCustomization::GetEnumClass() const 116 | { 117 | const FConfigurableEnumProperty* ConfigurableEnumProperty = GetConfigurableEnumProperty(); 118 | if (!ConfigurableEnumProperty) 119 | { 120 | return nullptr; 121 | } 122 | 123 | return ConfigurableEnumProperty->EnumClass; 124 | } 125 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Nodes/FlowNode_EnsureActorHasBlackboard.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Nodes/FlowNode_EnsureActorHasBlackboard.h" 4 | #include "BehaviorTree/BlackboardComponent.h" 5 | #include "Types/FlowInjectComponentsManager.h" 6 | #include "AIFlowAsset.h" 7 | #include "AIFlowTags.h" 8 | 9 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_EnsureActorHasBlackboard) 10 | 11 | const FName UFlowNode_EnsureActorHasBlackboard::OUTPIN_Success("Success"); 12 | const FName UFlowNode_EnsureActorHasBlackboard::OUTPIN_Failed("Failed"); 13 | 14 | UFlowNode_EnsureActorHasBlackboard::UFlowNode_EnsureActorHasBlackboard() 15 | : Super() 16 | { 17 | #if WITH_EDITOR 18 | NodeDisplayStyle = FlowNodeStyle::Blackboard; 19 | Category = TEXT("Blackboard"); 20 | #endif 21 | 22 | OutputPins.Reset(); 23 | OutputPins.Add(FFlowPin(OUTPIN_Success)); 24 | OutputPins.Add(FFlowPin(OUTPIN_Failed)); 25 | } 26 | 27 | void UFlowNode_EnsureActorHasBlackboard::ExecuteInput(const FName& PinName) 28 | { 29 | // Use the SpecificActor if provided, otherwise use the Flow Owner Actor 30 | TObjectPtr ResolvedObject = nullptr; 31 | const EFlowDataPinResolveResult ResolveResult = TryResolveDataPinValue(GET_MEMBER_NAME_CHECKED(ThisClass, SpecificActor), ResolvedObject); 32 | AActor* ResolvedActor = Cast(ResolvedObject); 33 | 34 | if (!IsValid(ResolvedActor)) 35 | { 36 | ResolvedActor = TryGetRootFlowActorOwner(); 37 | 38 | if (!IsValid(ResolvedActor)) 39 | { 40 | LogError(TEXT("Could not inject a blackboard onto a null actor")); 41 | 42 | return; 43 | } 44 | } 45 | 46 | UBlackboardComponent* BlackboardComponent = EnsureActorHasBlackboard(*ResolvedActor); 47 | 48 | constexpr bool bFinish = true; 49 | if (IsValid(BlackboardComponent)) 50 | { 51 | TriggerOutput(OUTPIN_Success); 52 | } 53 | else 54 | { 55 | TriggerOutput(OUTPIN_Failed); 56 | } 57 | } 58 | 59 | UBlackboardComponent* UFlowNode_EnsureActorHasBlackboard::EnsureActorHasBlackboard(AActor& ResolvedActor) 60 | { 61 | const bool bMayInjectComponent = EActorBlackboardInjectRule_Classifiers::NeedsInjectComponentsManager(InjectRule); 62 | if (bMayInjectComponent) 63 | { 64 | EnsureInjectComponentsManager(); 65 | } 66 | 67 | TSubclassOf BlackboardComponentClass = SpecificBlackboardComponentClass; 68 | if (!IsValid(BlackboardComponentClass)) 69 | { 70 | // If no specific one provided, the BlackboardComponentClass to use from the FlowAsset 71 | UAIFlowAsset* AIFlowAsset = Cast(GetFlowAsset()); 72 | if (IsValid(AIFlowAsset)) 73 | { 74 | BlackboardComponentClass = AIFlowAsset->GetBlackboardComponentClass(); 75 | } 76 | else 77 | { 78 | BlackboardComponentClass = UBlackboardComponent::StaticClass(); 79 | } 80 | } 81 | 82 | // Find or inject the blackboard 83 | return FAIFlowActorBlackboardHelper::FindOrAddBlackboardComponentOnActor( 84 | ResolvedActor, 85 | InjectComponentsManager, 86 | BlackboardComponentClass, 87 | SpecificBlackboardAsset, 88 | BlackboardSearchRule, 89 | InjectRule); 90 | } 91 | 92 | void UFlowNode_EnsureActorHasBlackboard::DeinitializeInstance() 93 | { 94 | CleanupInjectComponentsManager(); 95 | 96 | Super::DeinitializeInstance(); 97 | } 98 | 99 | void UFlowNode_EnsureActorHasBlackboard::EnsureInjectComponentsManager() 100 | { 101 | if (IsValid(InjectComponentsManager)) 102 | { 103 | return; 104 | } 105 | 106 | InjectComponentsManager = NewObject(this); 107 | 108 | InjectComponentsManager->InitializeRuntime(); 109 | InjectComponentsManager->BeforeActorRemovedDelegate.AddDynamic(this, &ThisClass::OnBeforeActorRemoved); 110 | } 111 | 112 | void UFlowNode_EnsureActorHasBlackboard::CleanupInjectComponentsManager() 113 | { 114 | if (IsValid(InjectComponentsManager)) 115 | { 116 | InjectComponentsManager->ShutdownRuntime(); 117 | InjectComponentsManager->BeforeActorRemovedDelegate.RemoveDynamic(this, &ThisClass::OnBeforeActorRemoved); 118 | } 119 | 120 | InjectComponentsManager = nullptr; 121 | } 122 | 123 | void UFlowNode_EnsureActorHasBlackboard::OnBeforeActorRemoved(AActor* RemovedActor) 124 | { 125 | if (IsValid(RemovedActor)) 126 | { 127 | OnStopMonitoringActor(*RemovedActor); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/AddOns/FlowNodeAddOn_ConfigureSpawnedActorBlackboard.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AddOns/FlowNodeAddOn_ConfigureSpawnedActorBlackboard.h" 4 | #include "BehaviorTree/BlackboardComponent.h" 5 | #include "Blackboard/FlowBlackboardEntryValue.h" 6 | #include "Types/FlowInjectComponentsManager.h" 7 | #include "AIFlowAsset.h" 8 | 9 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeAddOn_ConfigureSpawnedActorBlackboard) 10 | 11 | UFlowNodeAddOn_ConfigureSpawnedActorBlackboard::UFlowNodeAddOn_ConfigureSpawnedActorBlackboard() 12 | : Super() 13 | { 14 | } 15 | 16 | void UFlowNodeAddOn_ConfigureSpawnedActorBlackboard::FinishedSpawningActor_Implementation(AActor* SpawnedActor, UFlowNodeBase* SpawningNodeOrAddOn) 17 | { 18 | UBlackboardComponent* BlackboardComponent = TryEnsureBlackboardComponentToApplyTo(SpawnedActor, SpawningNodeOrAddOn); 19 | 20 | if (IsValid(BlackboardComponent)) 21 | { 22 | ActorBlackboardHelper.ApplyBlackboardOptionsToBlackboardComponent( 23 | *BlackboardComponent, 24 | PerActorOptionsAssignmentMethod, 25 | EntriesForEveryActor, 26 | &PerActorOptions); 27 | } 28 | 29 | Super::FinishedSpawningActor_Implementation(SpawnedActor, SpawningNodeOrAddOn); 30 | } 31 | 32 | UBlackboardComponent* UFlowNodeAddOn_ConfigureSpawnedActorBlackboard::TryEnsureBlackboardComponentToApplyTo(AActor* SpawnedActor, UFlowNodeBase* SpawningNodeOrAddOn) 33 | { 34 | if (!IsValid(SpawnedActor)) 35 | { 36 | return nullptr; 37 | } 38 | 39 | const bool bMayInjectComponent = EActorBlackboardInjectRule_Classifiers::NeedsInjectComponentsManager(InjectRule); 40 | if (bMayInjectComponent) 41 | { 42 | EnsureInjectComponentsManager(); 43 | } 44 | 45 | // Get the BlackboardComponentClass to use from the FlowAsset 46 | TSubclassOf BlackboardComponentClass = UBlackboardComponent::StaticClass(); 47 | UAIFlowAsset* AIFlowAsset = Cast(GetFlowAsset()); 48 | if (IsValid(AIFlowAsset)) 49 | { 50 | BlackboardComponentClass = AIFlowAsset->GetBlackboardComponentClass(); 51 | } 52 | 53 | // Find or add the blackboard component 54 | UBlackboardComponent* BlackboardComponent = 55 | FAIFlowActorBlackboardHelper::FindOrAddBlackboardComponentOnActor( 56 | *SpawnedActor, 57 | InjectComponentsManager, 58 | BlackboardComponentClass, 59 | ExpectedBlackboardData, 60 | SearchRule, 61 | InjectRule); 62 | 63 | // Start monitoring the actor, if we (potentially) injected a blackboard component 64 | if (bMayInjectComponent) 65 | { 66 | // Inform subclasses that we are monitoring this Actor 67 | OnStartMonitoringActor(*SpawnedActor); 68 | } 69 | 70 | return BlackboardComponent; 71 | } 72 | 73 | void UFlowNodeAddOn_ConfigureSpawnedActorBlackboard::UpdateNodeConfigText_Implementation() 74 | { 75 | #if WITH_EDITOR 76 | FTextBuilder TextBuilder; 77 | 78 | for (const UFlowBlackboardEntryValue* Entry : EntriesForEveryActor.Entries) 79 | { 80 | if (IsValid(Entry)) 81 | { 82 | TextBuilder.AppendLine(Entry->BuildNodeConfigText()); 83 | } 84 | } 85 | 86 | FAIFlowActorBlackboardHelper::AppendBlackboardOptions(PerActorOptions, TextBuilder); 87 | 88 | SetNodeConfigText(TextBuilder.ToText()); 89 | #endif // WITH_EDITOR 90 | } 91 | 92 | UBlackboardData* UFlowNodeAddOn_ConfigureSpawnedActorBlackboard::GetBlackboardAsset() const 93 | { 94 | // For the purposes of this function, we will substitute the expected blackboard rather than our own. 95 | // (so that the blackboard entry keys are sourced from the ExpectedBlackboardData) 96 | if (ExpectedBlackboardData) 97 | { 98 | return ExpectedBlackboardData; 99 | } 100 | 101 | return Super::GetBlackboardAsset(); 102 | } 103 | 104 | #if WITH_EDITOR 105 | UBlackboardData* UFlowNodeAddOn_ConfigureSpawnedActorBlackboard::GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const 106 | { 107 | // For the purposes of this function, we will substitute the expected blackboard rather than our own. 108 | // (so that the blackboard entry keys are sourced from the ExpectedBlackboardData) 109 | if (ExpectedBlackboardData) 110 | { 111 | return ExpectedBlackboardData; 112 | } 113 | 114 | return Super::GetBlackboardAssetForPropertyHandle(PropertyHandle); 115 | } 116 | #endif // WITH_EDITOR 117 | 118 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Nodes/FlowNode_SetBlackboardValuesOnActor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Nodes/FlowNode_SetBlackboardValuesOnActor.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #if WITH_EDITOR 7 | #include "PropertyHandle.h" 8 | #endif // WITH_EDITOR 9 | 10 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_SetBlackboardValuesOnActor) 11 | 12 | FName UFlowNode_SetBlackboardValuesOnActor::INPIN_SpecificActor; 13 | 14 | UFlowNode_SetBlackboardValuesOnActor::UFlowNode_SetBlackboardValuesOnActor() 15 | : Super() 16 | { 17 | INPIN_SpecificActor = GET_MEMBER_NAME_CHECKED(ThisClass, SpecificActor); 18 | #if WITH_EDITOR 19 | bNodeDeprecated = true; 20 | #endif 21 | } 22 | 23 | void UFlowNode_SetBlackboardValuesOnActor::UpdateNodeConfigText_Implementation() 24 | { 25 | #if WITH_EDITOR 26 | 27 | FTextBuilder TextBuilder; 28 | 29 | if (!BlackboardActorKey.KeyName.IsNone()) 30 | { 31 | TextBuilder.AppendLine(FString::Printf(TEXT("Actor Key: %s"), *BlackboardActorKey.KeyName.ToString())); 32 | } 33 | 34 | AppendEntriesForEveryActor(TextBuilder); 35 | 36 | SetNodeConfigText(TextBuilder.ToText()); 37 | #endif // WITH_EDITOR 38 | } 39 | 40 | TArray UFlowNode_SetBlackboardValuesOnActor::TryResolveActorsForBlackboard() const 41 | { 42 | TArray ResolvedActors; 43 | 44 | const bool bAddedSpecificActor = TryAddSpecificActor(ResolvedActors); 45 | const bool bAddedBlackboardKeyedActor = TryAddBlackboardKeyedActor(ResolvedActors); 46 | 47 | if (bAddedSpecificActor || bAddedBlackboardKeyedActor) 48 | { 49 | return ResolvedActors; 50 | } 51 | 52 | // Default to the flow actor owner, if not overridden 53 | return Super::TryResolveActorsForBlackboard(); 54 | } 55 | 56 | bool UFlowNode_SetBlackboardValuesOnActor::TryAddSpecificActor(TArray& InOutResolvedActors) const 57 | { 58 | // Resolve the Specific Actor(s) Pin first and add it to the ResolvedActors array 59 | TArray> PinSuppliedObjects; 60 | const EFlowDataPinResolveResult ResolveResult = TryResolveDataPinValues(INPIN_SpecificActor, PinSuppliedObjects); 61 | if (FlowPinType::IsSuccess(ResolveResult)) 62 | { 63 | for (TObjectPtr PinSuppliedObject : PinSuppliedObjects) 64 | { 65 | AActor* PinSuppliedActor = Cast(PinSuppliedObject); 66 | 67 | if (IsValid(PinSuppliedActor)) 68 | { 69 | InOutResolvedActors.Add(PinSuppliedActor); 70 | } 71 | } 72 | 73 | return true; 74 | } 75 | 76 | return false; 77 | } 78 | 79 | bool UFlowNode_SetBlackboardValuesOnActor::TryAddBlackboardKeyedActor(TArray& InOutResolvedActors) const 80 | { 81 | // Add the actor specified by the given blackboard key 82 | if (BlackboardActorKey.KeyName == NAME_None) 83 | { 84 | return false; 85 | } 86 | 87 | UBlackboardComponent* FlowBlackboardComponent = GetBlackboardComponent(); 88 | if (!FlowBlackboardComponent) 89 | { 90 | return false; 91 | } 92 | 93 | AActor* FoundActor = Cast(FlowBlackboardComponent->GetValueAsObject(BlackboardActorKey.KeyName)); 94 | if (!IsValid(FoundActor)) 95 | { 96 | // Counts as true, because we intended to add an actor from the blackboard, 97 | // it was just a null actor 98 | return true; 99 | } 100 | 101 | InOutResolvedActors.Add(FoundActor); 102 | 103 | return true; 104 | } 105 | 106 | #if WITH_EDITOR 107 | void UFlowNode_SetBlackboardValuesOnActor::PostInitProperties() 108 | { 109 | Super::PostInitProperties(); 110 | 111 | if (HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) 112 | { 113 | return; 114 | } 115 | 116 | if (BlackboardActorKey.AllowedTypes.IsEmpty()) 117 | { 118 | UBlackboardKeyType_Object* ActorObjectType = NewObject(this); 119 | 120 | // Limit the BlackboardActorKey to AActor 121 | ActorObjectType->BaseClass = AActor::StaticClass(); 122 | 123 | BlackboardActorKey.AllowedTypes.Add(ActorObjectType); 124 | } 125 | } 126 | 127 | UBlackboardData* UFlowNode_SetBlackboardValuesOnActor::GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const 128 | { 129 | if (SpecificBlackboardAsset && PropertyHandle) 130 | { 131 | // Use the ExpectedBlackboardData for all properties except the BlackboardActorKey, 132 | // which we should use the owning Flow Graph's blackboard instead. 133 | 134 | const FName PropertyName = PropertyHandle->GetProperty()->GetFName(); 135 | if (PropertyName != GET_MEMBER_NAME_CHECKED(UFlowNode_SetBlackboardValuesOnActor, BlackboardActorKey)) 136 | { 137 | return SpecificBlackboardAsset; 138 | } 139 | } 140 | 141 | return GetBlackboardAsset(); 142 | } 143 | #endif // WITH_EDITOR -------------------------------------------------------------------------------- /Source/AIFlow/Public/Nodes/FlowNode_SetBlackboardValues.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AIFlowActorBlackboardHelper.h" 6 | #include "Nodes/AIFlowNode.h" 7 | 8 | #include "FlowNode_SetBlackboardValues.generated.h" 9 | 10 | // Forward Declarations 11 | class UFlowBlackboardEntryValue; 12 | 13 | /** 14 | * Set blackboard values base class to the values defined in the Entries array 15 | * "DEPRECATED" in that it will be made Abstract once all of the node instances have been replaced with the subclass 16 | */ 17 | UCLASS(DisplayName = "Set Blackboard Values") 18 | class AIFLOW_API UFlowNode_SetBlackboardValues : public UAIFlowNode 19 | { 20 | GENERATED_BODY() 21 | 22 | public: 23 | 24 | UFlowNode_SetBlackboardValues(); 25 | 26 | // IFlowCoreExecutableInterface 27 | virtual void ExecuteInput(const FName& PinName) override; 28 | virtual void DeinitializeInstance() override; 29 | // -- 30 | 31 | // UFlowNodeBase 32 | virtual void UpdateNodeConfigText_Implementation() override; 33 | // -- 34 | 35 | #if WITH_EDITOR 36 | public: 37 | 38 | // IFlowContextPinSupplierInterface 39 | virtual bool SupportsContextPins() const override { return Super::SupportsContextPins() || !EntriesForEveryActor.Entries.IsEmpty(); } 40 | // -- 41 | 42 | // UObject 43 | virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; 44 | // -- 45 | 46 | // IFlowDataPinGeneratorInterface 47 | virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; 48 | // -- 49 | 50 | // IFlowBlackboardAssetProvider 51 | virtual UBlackboardData* GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const override; 52 | // -- 53 | 54 | protected: 55 | 56 | void AppendEntriesForEveryActor(FTextBuilder& InOutTextBuilder) const; 57 | 58 | UBlackboardData* GetBlackboardAssetForEditor() const; 59 | #endif // WITH_EDITOR 60 | 61 | protected: 62 | 63 | TArray GetBlackboardComponentsToApplyTo() const; 64 | 65 | void EnsureInjectComponentsManager(); 66 | void CleanupInjectComponentsManager(); 67 | 68 | UFUNCTION() 69 | void OnBeforeActorRemoved(AActor* RemovedActor); 70 | 71 | // Functions for subclasses to apply additional monitoring to Actors 72 | virtual void OnStartMonitoringActor(AActor& Actor) { } 73 | virtual void OnStopMonitoringActor(AActor& Actor) { } 74 | 75 | virtual TArray TryResolveActorsForBlackboard() const; 76 | virtual bool TryGetPerActorOptions( 77 | const TArray*& OutPerActorOptions, 78 | EPerActorOptionsAssignmentMethod& OutPerActorOptionsAssignmentMethod) const { return false; } 79 | 80 | virtual bool TryFindPropertyByPinName( 81 | const UObject& PropertyOwnerObject, 82 | const FName& PinName, 83 | const FProperty*& OutFoundProperty, 84 | TInstancedStruct& OutFoundInstancedStruct) const override; 85 | 86 | protected: 87 | 88 | // Blackboard properties to set on each actor this node is applied to 89 | UPROPERTY(EditAnywhere, Category = Configuration, meta = (ShowOnlyInnerProperties, DisplayPriority = 4)) 90 | FAIFlowConfigureBlackboardOption EntriesForEveryActor; 91 | 92 | // Specific blackboard to use (optional, defaults to the flow asset's blackboard) 93 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Specific Blackboard", meta = (DisplayPriority = 2)) 94 | TObjectPtr SpecificBlackboardAsset = nullptr; 95 | 96 | // Search rule to use to find the "Specific Blackboard" (if specified) 97 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Blackboard Search Rule", meta = (EditCondition = "SpecificBlackboardAsset", DisplayAfter = SpecificBlackboardAsset)) 98 | EActorBlackboardSearchRule SpecificBlackboardSearchRule = EActorBlackboardSearchRule::ActorAndControllerAndGameState; 99 | 100 | // Injection rule for a missing blackboard on the actor 101 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Blackboard Injection Rule", meta = (EditCondition = "SpecificBlackboardAsset", DisplayAfter = SpecificBlackboardAsset)) 102 | EActorBlackboardInjectRule InjectRule = EActorBlackboardInjectRule::DoNotInjectIfMissing; 103 | 104 | // Specific blackboard component subclass to use (optional, defaults to the flow asset's blackboard defined component class) 105 | UPROPERTY(EditAnywhere, AdvancedDisplay, Category = Configuration, DisplayName = "Specific Blackboard Component", meta = (EditCondition = "SpecificBlackboardAsset", DisplayAfter = SpecificBlackboardAsset)) 106 | TSubclassOf SpecificBlackboardComponentClass = nullptr; 107 | 108 | // Helper struct that shared functionality for manipulating Actor blackboards 109 | UPROPERTY() 110 | FAIFlowActorBlackboardHelper ActorBlackboardHelper; 111 | 112 | // Manager object to inject and remove components from actors 113 | UPROPERTY(Transient) 114 | TObjectPtr InjectComponentsManager = nullptr; 115 | }; 116 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Private/AIFlowEditorModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AIFlowEditorModule.h" 4 | 5 | #include "DetailCustomizations/FlowBlackboardEntryCustomization.h" 6 | #include "DetailCustomizations/ConfigurableEnumPropertyCustomization.h" 7 | #include "AIFlowTags.h" 8 | #include "Graph/FlowGraphSettings.h" 9 | 10 | #include "FlowModule.h" 11 | #include "AssetToolsModule.h" 12 | #include "Modules/ModuleManager.h" 13 | #include "PropertyEditorModule.h" 14 | 15 | #define LOCTEXT_NAMESPACE "AIFlowEditorModule" 16 | 17 | EAssetTypeCategories::Type FAIFlowEditorModule::FlowAssetCategory = static_cast(0); 18 | 19 | void FAIFlowEditorModule::StartupModule() 20 | { 21 | TrySetFlowNodeDisplayStyleDefaults(); 22 | 23 | RegisterAssets(); 24 | 25 | RegisterDetailCustomizations(); 26 | } 27 | 28 | void FAIFlowEditorModule::ShutdownModule() 29 | { 30 | UnregisterDetailCustomizations(); 31 | 32 | UnregisterAssets(); 33 | } 34 | 35 | void FAIFlowEditorModule::TrySetFlowNodeDisplayStyleDefaults() const 36 | { 37 | // Force the flow module to be loaded before we try to access the Settings 38 | FModuleManager::LoadModuleChecked("Flow"); 39 | 40 | UFlowGraphSettings& Settings = *UFlowGraphSettings::Get(); 41 | (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Blackboard, FLinearColor(-0.7f, 0.58f, 1.0f, 1.0f))); 42 | } 43 | 44 | void FAIFlowEditorModule::RegisterAssets() 45 | { 46 | IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); 47 | 48 | // try to merge asset category with a built-in one 49 | { 50 | const FText AssetCategoryText = UFlowGraphSettings::Get()->FlowAssetCategoryName; 51 | 52 | // Find matching built-in category 53 | if (!AssetCategoryText.IsEmpty()) 54 | { 55 | TArray AllCategories; 56 | AssetTools.GetAllAdvancedAssetCategories(AllCategories); 57 | for (const FAdvancedAssetCategory& ExistingCategory : AllCategories) 58 | { 59 | if (ExistingCategory.CategoryName.EqualTo(AssetCategoryText)) 60 | { 61 | FlowAssetCategory = ExistingCategory.CategoryType; 62 | break; 63 | } 64 | } 65 | } 66 | 67 | if (FlowAssetCategory == EAssetTypeCategories::None) 68 | { 69 | FlowAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Flow")), AssetCategoryText); 70 | } 71 | } 72 | } 73 | 74 | void FAIFlowEditorModule::UnregisterAssets() 75 | { 76 | if (FModuleManager::Get().IsModuleLoaded("AssetTools")) 77 | { 78 | IAssetTools& AssetTools = FModuleManager::GetModuleChecked("AssetTools").Get(); 79 | for (const TSharedRef& TypeAction : RegisteredAssetActions) 80 | { 81 | AssetTools.UnregisterAssetTypeActions(TypeAction); 82 | } 83 | } 84 | 85 | RegisteredAssetActions.Empty(); 86 | } 87 | 88 | void FAIFlowEditorModule::RegisterCustomClassLayout(const TSubclassOf Class, const FOnGetDetailCustomizationInstance DetailLayout) 89 | { 90 | if (Class) 91 | { 92 | FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); 93 | PropertyModule.RegisterCustomClassLayout(Class->GetFName(), DetailLayout); 94 | 95 | CustomClassLayouts.Add(Class->GetFName()); 96 | } 97 | } 98 | 99 | void FAIFlowEditorModule::RegisterCustomStructLayout(const UScriptStruct& Struct, const FOnGetPropertyTypeCustomizationInstance DetailLayout) 100 | { 101 | if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) 102 | { 103 | FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); 104 | PropertyModule.RegisterCustomPropertyTypeLayout(Struct.GetFName(), DetailLayout); 105 | 106 | CustomStructLayouts.Add(Struct.GetFName()); 107 | } 108 | } 109 | 110 | void FAIFlowEditorModule::RegisterDetailCustomizations() 111 | { 112 | if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) 113 | { 114 | FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); 115 | 116 | RegisterCustomStructLayout(*FConfigurableEnumProperty::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FConfigurableEnumPropertyCustomization::MakeInstance)); 117 | RegisterCustomStructLayout(*FFlowBlackboardEntry::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowBlackboardEntryCustomization::MakeInstance)); 118 | 119 | PropertyModule.NotifyCustomizationModuleChanged(); 120 | } 121 | } 122 | 123 | void FAIFlowEditorModule::UnregisterDetailCustomizations() 124 | { 125 | if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) 126 | { 127 | FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); 128 | 129 | for (auto It = CustomClassLayouts.CreateConstIterator(); It; ++It) 130 | { 131 | if (It->IsValid()) 132 | { 133 | PropertyModule.UnregisterCustomClassLayout(*It); 134 | } 135 | } 136 | 137 | for (auto It = CustomStructLayouts.CreateConstIterator(); It; ++It) 138 | { 139 | if (It->IsValid()) 140 | { 141 | PropertyModule.UnregisterCustomPropertyTypeLayout(*It); 142 | } 143 | } 144 | 145 | PropertyModule.NotifyCustomizationModuleChanged(); 146 | } 147 | } 148 | 149 | #undef LOCTEXT_NAMESPACE 150 | 151 | IMPLEMENT_MODULE(FAIFlowEditorModule, AIFlowEditor) 152 | -------------------------------------------------------------------------------- /Source/AIFlowEditor/Private/DetailCustomizations/FlowBlackboardEntryCustomization.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "FlowBlackboardEntryCustomization.h" 4 | #include "Interfaces/FlowBlackboardAssetProvider.h" 5 | #include "Types/FlowBlackboardEntry.h" 6 | 7 | #include "BehaviorTree/BlackboardData.h" 8 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Enum.h" 9 | #include "IDetailChildrenBuilder.h" 10 | #include "UObject/UnrealType.h" 11 | 12 | // FFlowBlackboardEntryCustomization Implementation 13 | 14 | void FFlowBlackboardEntryCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) 15 | { 16 | TSharedPtr PropertyHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowBlackboardEntry, AllowedTypes)); 17 | if (PropertyHandle) 18 | { 19 | StructBuilder.AddProperty(PropertyHandle.ToSharedRef()); 20 | } 21 | } 22 | 23 | TSharedPtr FFlowBlackboardEntryCustomization::GetCuratedNamePropertyHandle() const 24 | { 25 | check(StructPropertyHandle->IsValidHandle()); 26 | 27 | TSharedPtr FoundHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowBlackboardEntry, KeyName)); 28 | check(FoundHandle); 29 | 30 | return FoundHandle; 31 | } 32 | 33 | TArray FFlowBlackboardEntryCustomization::GetCuratedNameOptions() const 34 | { 35 | TArray Results; 36 | 37 | const UBlackboardData* BlackboardAsset = GetBlackboardData(); 38 | if (!IsValid(BlackboardAsset)) 39 | { 40 | return Results; 41 | } 42 | 43 | const FFlowBlackboardEntry* FlowBlackboardEntry = GetFlowBlackboardEntry(); 44 | if (!FlowBlackboardEntry) 45 | { 46 | return Results; 47 | } 48 | 49 | Results = GetFlowBlackboardEntries(*BlackboardAsset, *FlowBlackboardEntry); 50 | 51 | return Results; 52 | } 53 | 54 | TArray FFlowBlackboardEntryCustomization::GetFlowBlackboardEntries( 55 | const UBlackboardData& BlackboardAsset, 56 | const FFlowBlackboardEntry& FlowBlackboardEntry) 57 | { 58 | TArray ValidBlackboardEntries; 59 | 60 | const TArray>& AllowedTypes = FlowBlackboardEntry.AllowedTypes; 61 | 62 | for (const UBlackboardData* It = &BlackboardAsset; It; It = It->Parent) 63 | { 64 | for (int32 KeyIndex = 0; KeyIndex < It->Keys.Num(); KeyIndex++) 65 | { 66 | const FBlackboardEntry& EntryInfo = It->Keys[KeyIndex]; 67 | if (!EntryInfo.KeyType) 68 | { 69 | continue; 70 | } 71 | 72 | // Add all BlackboardKeys that pass the AllowedTypes filters 73 | bool bFilterPassed = true; 74 | if (AllowedTypes.Num()) 75 | { 76 | bFilterPassed = false; 77 | 78 | for (int32 FilterIndex = 0; FilterIndex < AllowedTypes.Num(); FilterIndex++) 79 | { 80 | UBlackboardKeyType* AllowedType = AllowedTypes[FilterIndex]; 81 | 82 | if (EntryInfo.KeyType->IsAllowedByFilter(AllowedType)) 83 | { 84 | bFilterPassed = true; 85 | 86 | break; 87 | } 88 | 89 | // Special-case for enum filter without an EnumType set 90 | // (that is, show all enum type blackboard entries when no specific enum type filter is set) 91 | if (UBlackboardKeyType_Enum* AllowedTypeEnum = Cast(AllowedType)) 92 | { 93 | if (EntryInfo.KeyType->IsA() && !AllowedTypeEnum->EnumType) 94 | { 95 | bFilterPassed = true; 96 | 97 | break; 98 | } 99 | } 100 | } 101 | } 102 | 103 | if (bFilterPassed) 104 | { 105 | ValidBlackboardEntries.Add(EntryInfo.EntryName); 106 | } 107 | } 108 | } 109 | 110 | return ValidBlackboardEntries; 111 | } 112 | 113 | void FFlowBlackboardEntryCustomization::SetCuratedName(const FName& NewKeyName) 114 | { 115 | TSharedPtr KeyHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowBlackboardEntry, KeyName)); 116 | 117 | check(KeyHandle); 118 | 119 | KeyHandle->SetPerObjectValue(0, NewKeyName.ToString()); 120 | } 121 | 122 | bool FFlowBlackboardEntryCustomization::TryGetCuratedName(FName& OutName) const 123 | { 124 | const FFlowBlackboardEntry* FlowBlackboardEntry = GetFlowBlackboardEntry(); 125 | if (FlowBlackboardEntry) 126 | { 127 | OutName = FlowBlackboardEntry->KeyName; 128 | 129 | return true; 130 | } 131 | else 132 | { 133 | return false; 134 | } 135 | } 136 | 137 | const IFlowBlackboardAssetProvider* FFlowBlackboardEntryCustomization::TryGetBlackboardAssetProviderFromOuters() const 138 | { 139 | check(StructPropertyHandle->IsValidHandle()); 140 | 141 | TArray OuterObjects; 142 | StructPropertyHandle->GetOuterObjects(OuterObjects); 143 | 144 | if (OuterObjects.Num() != 1) 145 | { 146 | // We don't support multiselect. Peace out. 147 | return nullptr; 148 | } 149 | 150 | UObject* CurrentOuter = OuterObjects[0]; 151 | 152 | while (IsValid(CurrentOuter)) 153 | { 154 | const IFlowBlackboardAssetProvider* FlowNodeOuter = Cast(CurrentOuter); 155 | if (FlowNodeOuter) 156 | { 157 | return FlowNodeOuter; 158 | } 159 | 160 | CurrentOuter = CurrentOuter->GetOuter(); 161 | } 162 | 163 | return nullptr; 164 | } 165 | 166 | const UBlackboardData* FFlowBlackboardEntryCustomization::GetBlackboardData() const 167 | { 168 | const IFlowBlackboardAssetProvider* FlowBlackboardProvider = TryGetBlackboardAssetProviderFromOuters(); 169 | if (!FlowBlackboardProvider) 170 | { 171 | return nullptr; 172 | } 173 | 174 | const UBlackboardData* BlackboardData = FlowBlackboardProvider->GetBlackboardAssetForPropertyHandle(StructPropertyHandle); 175 | if (!IsValid(BlackboardData)) 176 | { 177 | return nullptr; 178 | } 179 | 180 | return BlackboardData; 181 | } 182 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Nodes/FlowNode_SetBlackboardValuesV2.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AIFlowActorBlackboardHelper.h" 6 | #include "Nodes/AIFlowNode.h" 7 | #include "Types/FlowBlackboardEntry.h" 8 | #include "Types/FlowInjectComponentsManager.h" 9 | #include "BehaviorTree/BlackboardComponent.h" 10 | #include "BehaviorTree/Blackboard/BlackboardKeyWrappers.h" 11 | 12 | #include "FlowNode_SetBlackboardValuesV2.generated.h" 13 | 14 | // Forward Declarations 15 | class UFlowBlackboardEntryValue; 16 | 17 | /** 18 | * Set blackboard values to the values defined in the Entries array 19 | * on one or more actors' blackboards. 20 | * 21 | * Actors can be: 22 | * • Specific actors supplied via the SpecificActors pin 23 | * • The flow graph's owning actor (fallback) 24 | */ 25 | UCLASS(DisplayName = "Set Blackboard Values V2") 26 | class AIFLOW_API UFlowNode_SetBlackboardValuesV2 : public UAIFlowNode 27 | { 28 | GENERATED_BODY() 29 | 30 | public: 31 | UFlowNode_SetBlackboardValuesV2(); 32 | 33 | // IFlowCoreExecutableInterface 34 | virtual void ExecuteInput(const FName& PinName) override; 35 | virtual void DeinitializeInstance() override; 36 | // -- 37 | 38 | // UFlowNodeBase 39 | virtual void UpdateNodeConfigText_Implementation() override; 40 | // -- 41 | 42 | #if WITH_EDITOR 43 | // UObject 44 | virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; 45 | // -- 46 | 47 | // IFlowContextPinSupplierInterface 48 | virtual bool SupportsContextPins() const override { return !EntriesForEveryActor.Entries.IsEmpty(); } 49 | // -- 50 | 51 | // IFlowDataPinGeneratorInterface 52 | virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; 53 | // -- 54 | 55 | // IFlowBlackboardAssetProvider 56 | virtual UBlackboardData* GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const override; 57 | // -- 58 | #endif 59 | 60 | protected: 61 | // Actor resolution 62 | virtual TArray TryResolveActorsForBlackboard() const; 63 | bool TryAddSpecificActors(TArray& InOutResolvedActors) const; 64 | // -- 65 | 66 | // Blackboard handling helpers (from base class) 67 | TArray GetBlackboardComponentsToApplyTo() const; 68 | void EnsureInjectComponentsManager(); 69 | void CleanupInjectComponentsManager(); 70 | // -- 71 | 72 | UFUNCTION() 73 | void OnBeforeActorRemoved(AActor* RemovedActor); 74 | 75 | // Functions for subclasses to apply additional monitoring to Actors 76 | virtual void OnStartMonitoringActor(AActor& Actor) { } 77 | virtual void OnStopMonitoringActor(AActor& Actor) { } 78 | 79 | virtual bool TryFindPropertyByPinName( 80 | const UObject& PropertyOwnerObject, 81 | const FName& PinName, 82 | const FProperty*& OutFoundProperty, 83 | TInstancedStruct& OutFoundInstancedStruct) const override; 84 | // -- 85 | 86 | // Editor helpers 87 | #if WITH_EDITOR 88 | void AppendEntriesForEveryActor(FTextBuilder& InOutTextBuilder) const; 89 | UBlackboardData* GetBlackboardAssetForEditor() const; 90 | #endif // WITH_EDITOR 91 | // -- 92 | 93 | protected: 94 | // Blackboard properties to set on each actor this node is applied to 95 | UPROPERTY(EditAnywhere, Category = BlackboardEntriesToSet, meta = (ShowOnlyInnerProperties, DisplayPriority = 0)) 96 | FAIFlowConfigureBlackboardOption EntriesForEveryActor; 97 | 98 | // Configured blackboard entry option sets to apply to actors 99 | UPROPERTY(EditAnywhere, Category = BlackboardEntriesToSet, DisplayName = "Per-Actor Blackboard Entries", meta = (DisplayPriority = 1)) 100 | TArray PerActorOptions; 101 | 102 | // Method to use when applying the Per-Actor Options 103 | UPROPERTY(EditAnywhere, Category = BlackboardEntriesToSet, DisplayName = "Per-Actor Assignment Method", meta = (DisplayPriority = 1)) 104 | EPerActorOptionsAssignmentMethod PerActorOptionsAssignmentMethod = EPerActorOptionsAssignmentMethod::InOrderWithWrapping; 105 | 106 | // Optional specific actors to use to look for (or inject) the blackboard. 107 | UPROPERTY(Transient, meta = (DefaultForInputFlowPin, FlowPinType = "Object", DisplayPriority = 1)) 108 | TArray SpecificActors; 109 | 110 | // Specific blackboard to use (optional, defaults to the flow asset's blackboard) 111 | UPROPERTY(EditAnywhere, Category = BlackboardAsset, DisplayName = "Specific Blackboard", meta = (DisplayPriority = 2)) 112 | TObjectPtr SpecificBlackboardAsset = nullptr; 113 | 114 | // Search rule to use to find the "Specific Blackboard" (if specified) 115 | UPROPERTY(EditAnywhere, Category = BlackboardAsset, DisplayName = "Blackboard Search Rule", meta = (EditCondition = "SpecificBlackboardAsset", DisplayAfter = SpecificBlackboardAsset)) 116 | EActorBlackboardSearchRule SpecificBlackboardSearchRule = EActorBlackboardSearchRule::ActorAndControllerAndGameState; 117 | 118 | // Injection rule for a missing blackboard on the actor 119 | UPROPERTY(EditAnywhere, Category = BlackboardAsset, DisplayName = "Blackboard Injection Rule", meta = (EditCondition = "SpecificBlackboardAsset", DisplayAfter = SpecificBlackboardAsset)) 120 | EActorBlackboardInjectRule InjectRule = EActorBlackboardInjectRule::DoNotInjectIfMissing; 121 | 122 | // Specific blackboard component subclass to use (optional, defaults to the flow asset's blackboard defined component class) 123 | UPROPERTY(EditAnywhere, AdvancedDisplay, Category = BlackboardAsset, DisplayName = "Specific Blackboard Component", meta = (EditCondition = "SpecificBlackboardAsset", DisplayAfter = SpecificBlackboardAsset)) 124 | TSubclassOf SpecificBlackboardComponentClass = nullptr; 125 | 126 | // Helper struct that shared functionality for manipulating Actor blackboards 127 | UPROPERTY() 128 | FAIFlowActorBlackboardHelper ActorBlackboardHelper; 129 | 130 | // Manager object to inject and remove components from actors 131 | UPROPERTY(Transient) 132 | TObjectPtr InjectComponentsManager = nullptr; 133 | 134 | static FName INPIN_SpecificActors; 135 | }; -------------------------------------------------------------------------------- /Source/AIFlow/Private/Blackboard/FlowBlackboardEntryValue_Class.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Blackboard/FlowBlackboardEntryValue_Class.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Class.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "AIFlowLogChannels.h" 7 | #include "Nodes/FlowNode.h" 8 | #include "Types/FlowDataPinValuesStandard.h" 9 | #include "Types/FlowDataPinResults.h" 10 | 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardEntryValue_Class) 12 | 13 | #if WITH_EDITOR 14 | void UFlowBlackboardEntryValue_Class::PostInitProperties() 15 | { 16 | Super::PostInitProperties(); 17 | 18 | if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) 19 | { 20 | if (Key.AllowedTypes.IsEmpty()) 21 | { 22 | UBlackboardKeyType_Class* ClassType = NewObject(this); 23 | ClassType->BaseClass = BaseClass; 24 | Key.AllowedTypes.Add(ClassType); 25 | } 26 | } 27 | } 28 | 29 | bool UFlowBlackboardEntryValue_Class::TryReconfigureFromBlackboardKeyType(const UBlackboardKeyType& KeyType) 30 | { 31 | const bool bSuperMadeChanges = Super::TryReconfigureFromBlackboardKeyType(KeyType); 32 | 33 | // Superclass is responsible for ensuring the UBlackboardKeyType_Class is correct, 34 | // but the subtype (BaseClass) may be mismatched, so we will need to ensure that here. 35 | 36 | check(Key.AllowedTypes.Num() == 1 && Key.AllowedTypes[0]->IsA(GetSupportedBlackboardKeyType())); 37 | 38 | const UBlackboardKeyType_Class* ClassKeyType = CastChecked(&KeyType); 39 | if (BaseClass != ClassKeyType->BaseClass) 40 | { 41 | BaseClass = ClassKeyType->BaseClass; 42 | 43 | EnsureClassInstanceIsCompatibleWithBaseClass(); 44 | 45 | return true; 46 | } 47 | 48 | return bSuperMadeChanges; 49 | } 50 | 51 | FString UFlowBlackboardEntryValue_Class::GetEditorValueString() const 52 | { 53 | if (IsValid(ClassInstance)) 54 | { 55 | return *ClassInstance->GetName(); 56 | } 57 | else 58 | { 59 | return TEXT(""); 60 | } 61 | } 62 | 63 | FText UFlowBlackboardEntryValue_Class::BuildNodeConfigText() const 64 | { 65 | return FText::FromString(FString::Printf(TEXT("Set %s to \"%s\""), *Key.GetKeyName().ToString(), *GetEditorValueString())); 66 | } 67 | 68 | void UFlowBlackboardEntryValue_Class::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) 69 | { 70 | Super::PostEditChangeProperty(PropertyChangedEvent); 71 | 72 | const FName MemberPropertyName = PropertyChangedEvent.MemberProperty->GetFName(); 73 | 74 | if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowBlackboardEntryValue_Class, BaseClass)) 75 | { 76 | if (ensure(Key.AllowedTypes.Num() == 1)) 77 | { 78 | UBlackboardKeyType_Class* ClassType = CastChecked(Key.AllowedTypes[0]); 79 | 80 | ClassType->BaseClass = BaseClass; 81 | } 82 | } 83 | 84 | EnsureClassInstanceIsCompatibleWithBaseClass(); 85 | } 86 | 87 | void UFlowBlackboardEntryValue_Class::EnsureClassInstanceIsCompatibleWithBaseClass() 88 | { 89 | if (ClassInstance && !ClassInstance->IsChildOf(BaseClass)) 90 | { 91 | // Clear the ClassInstance if it is not compatible with the BaseClass 92 | ClassInstance = nullptr; 93 | } 94 | } 95 | 96 | #endif // WITH_EDITOR 97 | 98 | bool UFlowBlackboardEntryValue_Class::TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const 99 | { 100 | FSoftClassPath ClassInstancePath = FSoftClassPath(ClassInstance); 101 | 102 | UClass* ClassFilter = nullptr; 103 | 104 | #if WITH_EDITOR 105 | // Only the editor data has the BaseClass (and also FFlowDataPinOutputProperty_Object::ClassFilter) 106 | // so we only can supply (or use) that information in editor builds 107 | ClassFilter = BaseClass; 108 | #endif // WITH_EDITOR 109 | 110 | OutFlowDataPinProperty.InitializeAs(ClassInstancePath, ClassFilter); 111 | 112 | return true; 113 | } 114 | 115 | bool UFlowBlackboardEntryValue_Class::TryProvideFlowDataPinPropertyFromBlackboardEntry( 116 | const FName& BlackboardKeyName, 117 | const UBlackboardKeyType& BlackboardKeyType, 118 | UBlackboardComponent* OptionalBlackboardComponent, 119 | TInstancedStruct& OutFlowDataPinProperty) const 120 | { 121 | if (TryProvideFlowDataPinPropertyFromBlackboardEntryTemplate( 122 | BlackboardKeyName, 123 | BlackboardKeyType, 124 | OptionalBlackboardComponent, 125 | OutFlowDataPinProperty)) 126 | { 127 | #if WITH_EDITOR 128 | const UBlackboardKeyType_Class* TypedKeyType = CastChecked(&BlackboardKeyType); 129 | FFlowDataPinValue_Class* MutableProperty = OutFlowDataPinProperty.GetMutablePtr(); 130 | 131 | // Only the editor data has the BaseClass or ClassFilter 132 | // so we only can supply (or use) that information in editor builds 133 | MutableProperty->ClassFilter = TypedKeyType->BaseClass; 134 | #endif // WITH_EDITOR 135 | 136 | return true; 137 | } 138 | 139 | return false; 140 | } 141 | 142 | bool UFlowBlackboardEntryValue_Class::TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) 143 | { 144 | TObjectPtr ResolvedClass = nullptr; 145 | const EFlowDataPinResolveResult ResolveResult = PinOwnerFlowNode.TryResolveDataPinValue(PinName, ResolvedClass); 146 | ClassInstance = ResolvedClass; 147 | return FlowPinType::IsSuccess(ResolveResult); 148 | } 149 | 150 | void UFlowBlackboardEntryValue_Class::SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const 151 | { 152 | if (IsValid(BlackboardComponent)) 153 | { 154 | BlackboardComponent->SetValueAsClass(Key.GetKeyName(), ClassInstance); 155 | } 156 | } 157 | 158 | EBlackboardCompare::Type UFlowBlackboardEntryValue_Class::CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const 159 | { 160 | if (!IsValid(BlackboardComponent)) 161 | { 162 | UE_LOG(LogAIFlow, Error, TEXT("Cannot CompareKeyValues without a Blackboard!")); 163 | return EBlackboardCompare::NotEqual; 164 | } 165 | 166 | const UClass* OtherValueAsClass = BlackboardComponent->GetValueAsClass(OtherKeyName); 167 | 168 | if (ClassInstance == OtherValueAsClass) 169 | { 170 | return EBlackboardCompare::Equal; 171 | } 172 | else 173 | { 174 | return EBlackboardCompare::NotEqual; 175 | } 176 | } 177 | 178 | TSubclassOf UFlowBlackboardEntryValue_Class::GetSupportedBlackboardKeyType() const 179 | { 180 | return UBlackboardKeyType_Class::StaticClass(); 181 | } 182 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/AIFlowAsset.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "AIFlowAsset.h" 4 | #include "AIFlowLogChannels.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "GameFramework/Actor.h" 7 | #include "GameFramework/Controller.h" 8 | #include "GameFramework/Pawn.h" 9 | #include "Interfaces/AIFlowOwnerInterface.h" 10 | #include "Misc/DataValidation.h" 11 | #include "Types/FlowInjectComponentsHelper.h" 12 | #include "Types/FlowInjectComponentsManager.h" 13 | 14 | #include UE_INLINE_GENERATED_CPP_BY_NAME(AIFlowAsset) 15 | 16 | UAIFlowAsset::UAIFlowAsset(const FObjectInitializer& ObjectInitializer) 17 | : Super(ObjectInitializer) 18 | { 19 | BlackboardComponentClass = UBlackboardComponent::StaticClass(); 20 | } 21 | 22 | void UAIFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset& InTemplateAsset) 23 | { 24 | Super::InitializeInstance(InOwner, InTemplateAsset); 25 | 26 | check(Owner == InOwner.Get()); 27 | 28 | // Start with a 'random' random seed, 29 | // this could be overridden with a stable random seed if subclasses wish. 30 | const FDateTime& CurrentTime = FDateTime::Now(); 31 | FRandomStream RandomStream(static_cast(CurrentTime.GetTicks())); 32 | RandomSeed = RandomStream.RandHelper(INT32_MAX); 33 | 34 | if (UObject* FlowOwnerActor = TryFindActorOwner()) 35 | { 36 | CreateAndRegisterBlackboardComponent(); 37 | 38 | SetKeySelfOnBlackboardComponent(BlackboardComponent.Get()); 39 | } 40 | 41 | UObject* OwnerObject = Owner.Get(); 42 | if (IsValid(OwnerObject) && OwnerObject->Implements()) 43 | { 44 | IAIFlowOwnerInterface::Execute_PostFlowAssetInitializeInstance(OwnerObject, this); 45 | } 46 | } 47 | 48 | void UAIFlowAsset::DeinitializeInstance() 49 | { 50 | Super::DeinitializeInstance(); 51 | 52 | // We want to keep the blackboard around until we have deinitialized everything else. 53 | DestroyAndUnregisterBlackboardComponent(); 54 | } 55 | 56 | UBlackboardData* UAIFlowAsset::GetBlackboardAsset() const 57 | { 58 | return BlackboardAsset; 59 | } 60 | 61 | UBlackboardComponent* UAIFlowAsset::GetBlackboardComponent() const 62 | { 63 | return BlackboardComponent.Get(); 64 | } 65 | 66 | UBlackboardComponent* UAIFlowAsset::BP_TryFindBlackboardComponentOnActor(AActor* Actor, UBlackboardData* OptionalBlackboardData) 67 | { 68 | if (IsValid(Actor)) 69 | { 70 | return TryFindBlackboardComponentOnActor(*Actor, OptionalBlackboardData); 71 | } 72 | 73 | return nullptr; 74 | } 75 | 76 | UBlackboardComponent* UAIFlowAsset::TryFindBlackboardComponentOnActor(AActor& Actor, UBlackboardData* OptionalBlackboardData) 77 | { 78 | TArray BlackboardComponents; 79 | Actor.GetComponents(BlackboardComponents); 80 | 81 | if (IsValid(OptionalBlackboardData)) 82 | { 83 | // Find the desired blackboard component, if it exists, on the ActorOwner 84 | const UPackage* BlackboardAssetPackage = OptionalBlackboardData->GetPackage(); 85 | for (UBlackboardComponent* BlackboardComp : BlackboardComponents) 86 | { 87 | if (const UBlackboardData* BlackboardCompAsset = BlackboardComp->GetBlackboardAsset()) 88 | { 89 | const UPackage* BlackboardCompAssetPackage = BlackboardCompAsset->GetPackage(); 90 | 91 | if (BlackboardCompAssetPackage == BlackboardAssetPackage) 92 | { 93 | return BlackboardComp; 94 | } 95 | } 96 | } 97 | 98 | return nullptr; 99 | } 100 | else if (BlackboardComponents.Num() > 0) 101 | { 102 | if (BlackboardComponents.Num() > 1) 103 | { 104 | UE_LOG( 105 | LogAIFlow, 106 | Error, 107 | TEXT("UAIFlowAsset::TryFindBlackboardComponentOnActor found multiple blackboard components (%d) on actor %s, but no OptionalBlackboardData was specified to filter which is desired. Returning the 0th, but this may not be the desired blackboard."), 108 | BlackboardComponents.Num(), 109 | *Actor.GetName()); 110 | } 111 | 112 | return BlackboardComponents[0]; 113 | } 114 | 115 | return nullptr; 116 | } 117 | 118 | void UAIFlowAsset::CreateAndRegisterBlackboardComponent() 119 | { 120 | if (!IsValid(BlackboardAsset)) 121 | { 122 | return; 123 | } 124 | 125 | AActor* ActorOwner = TryFindActorOwner(); 126 | if (!IsValid(ActorOwner)) 127 | { 128 | return; 129 | } 130 | 131 | BlackboardComponent = TryFindBlackboardComponentOnActor(*ActorOwner, BlackboardAsset); 132 | 133 | if (BlackboardComponent.IsValid()) 134 | { 135 | // Blackboard component has already been setup 136 | // (presumably by some other flow graph, or it was built-in to the Actor) 137 | 138 | return; 139 | } 140 | 141 | // If the desired blackboard component does not already exist, add it to the ActorOwner 142 | const FName InstanceBaseName = FName(FString(TEXT("Comp_") + BlackboardAsset->GetName())); 143 | UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromClass(*ActorOwner, BlackboardComponentClass, InstanceBaseName); 144 | BlackboardComponent = CastChecked(ComponentInstance); 145 | 146 | // Create the manager object if we're injecting a component 147 | InjectComponentsManager = NewObject(this); 148 | InjectComponentsManager->InitializeRuntime(); 149 | 150 | // Inject the desired component 151 | InjectComponentsManager->InjectComponentOnActor(*ActorOwner, *ComponentInstance); 152 | 153 | // Ensure the Runtime BlackboardData is instanced (if subclasses need to instance it) 154 | UBlackboardData* RuntimeBlackboard = EnsureRuntimeBlackboardData(); 155 | 156 | BlackboardComponent->InitializeBlackboard(*RuntimeBlackboard); 157 | } 158 | 159 | void UAIFlowAsset::DestroyAndUnregisterBlackboardComponent() 160 | { 161 | if (IsValid(InjectComponentsManager)) 162 | { 163 | InjectComponentsManager->ShutdownRuntime(); 164 | } 165 | 166 | InjectComponentsManager = nullptr; 167 | 168 | BlackboardComponent = nullptr; 169 | } 170 | 171 | void UAIFlowAsset::SetKeySelfOnBlackboardComponent(UBlackboardComponent* BlackboardComp) const 172 | { 173 | if (!IsValid(BlackboardComp)) 174 | { 175 | return; 176 | } 177 | 178 | // Set the SelfActor key to the ActorOwner 179 | AActor* ActorSelf = TryFindActorOwner();; 180 | 181 | AController* ControllerOwner = Cast(ActorSelf); 182 | if (IsValid(ControllerOwner)) 183 | { 184 | // If the ActorOwner is a Controller, use the PossessedPawn 185 | // (if available) instead of the Controller for KeySelf 186 | APawn* PossessedPawn = ControllerOwner->GetPawn(); 187 | if (IsValid(PossessedPawn)) 188 | { 189 | ActorSelf = PossessedPawn; 190 | } 191 | } 192 | 193 | BlackboardComp->SetValueAsObject(FBlackboard::KeySelf, ActorSelf); 194 | } 195 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/Blackboard/FlowBlackboardEntryValue.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "BehaviorTree/Blackboard/BlackboardKeyEnums.h" 6 | #include "Interfaces/FlowDataPinPropertyProviderInterface.h" 7 | #include "Interfaces/FlowBlackboardAssetProvider.h" 8 | #include "Templates/SubclassOf.h" 9 | #include "UObject/Object.h" 10 | #include "BehaviorTree/Blackboard/BlackboardKeyType.h" 11 | #include "BehaviorTree/BlackboardComponent.h" 12 | 13 | #include "Types/FlowBlackboardEntry.h" 14 | 15 | #include "FlowBlackboardEntryValue.generated.h" 16 | 17 | // Forward Declarations 18 | class UBlackboardKeyType; 19 | class UBlackboardComponent; 20 | class UFlowBlackboardEntryValue; 21 | class UFlowNode; 22 | struct FFlowDataPinValue; 23 | 24 | // Enum to control visibility of the UFlowBlackboardEntryValue's Key in EditCondition 25 | UENUM() 26 | enum class EFlowBlackboardEntryValueKeyVisibility : uint8 27 | { 28 | Visible, 29 | NotVisible, 30 | }; 31 | 32 | /** 33 | * Configuration base class for setting blackboard entries for UBlackboardKeyType entries 34 | */ 35 | UCLASS(Abstract, BlueprintType, EditInlineNew, DisplayName = "Blackboard Value") 36 | class AIFLOW_API UFlowBlackboardEntryValue 37 | : public UObject 38 | , public IFlowBlackboardAssetProvider 39 | , public IFlowDataPinPropertyProviderInterface 40 | { 41 | GENERATED_BODY() 42 | 43 | public: 44 | 45 | // UFlowBlackboardEntryValue 46 | 47 | // Uses the data in this UFlowBlackboardEntryValue to set the matching key's value on the given blackboard 48 | virtual void SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const PURE_VIRTUAL(SetOnBlackboardComponent); 49 | 50 | // Compares the value contained in this object vs. the given key's value on the blackboard, 51 | // similar to UBlackboardComponent::CompareKeyValues() 52 | virtual EBlackboardCompare::Type CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const PURE_VIRTUAL(CompareKeyValues, return EBlackboardCompare::NotEqual;); 53 | 54 | // Returns the UBlackboardKeyType subclass that this UFlowBlackboardKeyValue is built for 55 | virtual TSubclassOf GetSupportedBlackboardKeyType() const PURE_VIRTUAL(GetSupportedBlackboardKeyType, return nullptr;); 56 | 57 | // Worker function for arithmetic compare operations (of the form EArithmeticCompare) that are done on blackboard entries. 58 | // Only subclasses that SupportArithmeticOperations need to implement this function. 59 | virtual bool TryGetNumericalValuesForArithmeticOperation(int32 * OutIntValue = nullptr, float* OutFloatValue = nullptr) const { return false; } 60 | 61 | // Set this UFlowBlackboardEntryValue's value to the value from a Data Pin, resolved by PinOwnerFlowNode 62 | virtual bool TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) PURE_VIRTUAL(TrySetValueFromInputDataPin, return false;); 63 | 64 | #if WITH_EDITOR 65 | // Does this class support arithmetic compare operations (of the form EArithmeticCompare) and 66 | // thus supports TryGetNumericalValuesForArithmeticOperation ? 67 | virtual bool SupportsArithmeticOperations() const { return false; } 68 | 69 | // Tries to reconfigure this object to match the given UBlackboardKeyType. 70 | // Must be a supported UBlackboardKeyType subclass. 71 | // This is used when procedurally reconfiguring EnumClass and other subtype changes etc. in editor 72 | virtual bool TryReconfigureFromBlackboardKeyType(const UBlackboardKeyType& KeyType); 73 | 74 | // Returns the Value of this object in string form, for editor use 75 | virtual FString GetEditorValueString() const { return FString(); } 76 | 77 | // Returns the NodeConfigText, used in populating the "NodeConfig" area of FlowNode & AddOns in the flow editor 78 | virtual FText BuildNodeConfigText() const PURE_VIRTUAL(BuildNodeConfigText, return FText();) 79 | 80 | // Returns the first found UFlowBlackboardEntryValue subclass that supports the given UBlackboardKeyType, 81 | // from the available subclasses. 82 | static TSubclassOf GetFlowBlackboardEntryValueClassForKeyType(TSubclassOf KeyTypeClass); 83 | #endif // WITH_EDITOR 84 | // -- 85 | 86 | // IBlackboardAssetProvider 87 | virtual UBlackboardData* GetBlackboardAsset() const override; 88 | // -- 89 | 90 | #if WITH_EDITOR 91 | // IFlowBlackboardAssetProvider 92 | virtual UBlackboardData* GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const override; 93 | // -- 94 | #endif // WITH_EDITOR 95 | 96 | // IFlowDataPinPropertyProviderInterface 97 | virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const { return false; } 98 | // -- 99 | 100 | // Try to provide a blackboard entry value for a key, if the type is supported by this class, in the form of a FFlowDataPinProperty 101 | virtual bool TryProvideFlowDataPinPropertyFromBlackboardEntry( 102 | const FName& BlackboardKeyName, 103 | const UBlackboardKeyType& BlackboardKeyType, 104 | UBlackboardComponent* OptionalBlackboardComponent, 105 | TInstancedStruct& OutFlowDataPinProperty) const PURE_VIRTUAL(TryProvideFlowDataPinPropertyFromBlackboardEntry, return false;); 106 | 107 | // Ensures the CachedBlackboardEntryValueSubclassArray has been cached and returns its value 108 | static const TArray>& EnsureBlackboardEntryValueSubclassArray(); 109 | 110 | protected: 111 | 112 | // Template worker function for TryProvideFlowDataPinPropertyFromBlackboardEntry() 113 | template 114 | static bool TryProvideFlowDataPinPropertyFromBlackboardEntryTemplate( 115 | const FName& BlackboardKeyName, 116 | const UBlackboardKeyType& BlackboardKeyType, 117 | UBlackboardComponent* OptionalBlackboardComponent, 118 | TInstancedStruct& OutFlowDataPinProperty) 119 | { 120 | if (BlackboardKeyType.IsA()) 121 | { 122 | const typename TBlackboardEntryType::FDataType Value = 123 | OptionalBlackboardComponent ? 124 | OptionalBlackboardComponent->GetValue(BlackboardKeyName) : 125 | TBlackboardEntryType::InvalidValue; 126 | 127 | OutFlowDataPinProperty.InitializeAs(Value); 128 | 129 | return true; 130 | } 131 | 132 | return false; 133 | } 134 | 135 | public: 136 | 137 | // Target blackboard key for this entry to set 138 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Configuration, meta = (EditCondition = "KeyVisibility == EFlowBlackboardEntryValueKeyVisibility::Visible", EditConditionHides)) 139 | FFlowBlackboardEntry Key; 140 | 141 | // Cached array of all of the subclasses of UFlowBlackboardEntryValue 142 | static TArray> CachedBlackboardEntryValueSubclassArray; 143 | 144 | #if WITH_EDITORONLY_DATA 145 | // Used to control visibility of Key property 146 | // (in some use-cases, we only want to use the value portion of the FFlowBlackboardEntry, 147 | // and do not want to show the Key in the editor). 148 | UPROPERTY(EditDefaultsOnly, Category = Configuration) 149 | EFlowBlackboardEntryValueKeyVisibility KeyVisibility = EFlowBlackboardEntryValueKeyVisibility::Visible; 150 | #endif // WITH_EDITORONLY_DATA 151 | }; 152 | 153 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/AddOns/FlowNodeAddOn_PredicateCompareBlackboardValue.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "AddOns/AIFlowNodeAddOn.h" 6 | #include "AIFlowActorBlackboardHelper.h" 7 | #include "Interfaces/FlowPredicateInterface.h" 8 | #include "Types/FlowBlackboardEntry.h" 9 | 10 | #include "GameplayTagContainer.h" 11 | #include "BehaviorTree/Blackboard/BlackboardKey.h" 12 | #include "BehaviorTree/Blackboard/BlackboardKeyEnums.h" 13 | #include "Templates/SubclassOf.h" 14 | 15 | #include "FlowNodeAddOn_PredicateCompareBlackboardValue.generated.h" 16 | 17 | // TODO (gtaylor) Add specific actor sourcing options (data pins?) for comparing from other actors' blackboards 18 | 19 | // Forward Declarations 20 | class UFlowBlackboardEntryValue; 21 | class UBlackboardKeyType; 22 | struct FAIFlowCachedBlackboardReference; 23 | struct FBlackboardEntry; 24 | 25 | // Operator for UFlowNodeAddOn_PredicateCompareBlackboardValue's compare operation 26 | UENUM(BlueprintType) 27 | enum class EPredicateCompareOperatorType : uint8 28 | { 29 | // Supported by all UBlackboardKeyItem subclasses 30 | 31 | Equal UMETA(DisplayName = "Is Equal To"), 32 | NotEqual UMETA(DisplayName = "Is Not Equal To"), 33 | 34 | // Supported by UBlackboardKeyItem _Int, _Float and _Enum subclasses only 35 | 36 | Less UMETA(DisplayName = "Is Less Than"), 37 | LessOrEqual UMETA(DisplayName = "Is Less Than Or Equal To"), 38 | Greater UMETA(DisplayName = "Is Greater Than"), 39 | GreaterOrEqual UMETA(DisplayName = "Is Greater Than Or Equal To"), 40 | 41 | Max UMETA(Hidden), 42 | Min = 0 UMETA(Hidden), 43 | 44 | // Subrange for equality operations 45 | EqualityFirst = Equal UMETA(Hidden), 46 | EqualityLast = NotEqual UMETA(Hidden), 47 | 48 | // Subrange for Arithmetic-only operations 49 | ArithmeticFirst = Less UMETA(Hidden), 50 | ArithmeticLast = GreaterOrEqual UMETA(Hidden), 51 | }; 52 | 53 | FORCEINLINE_DEBUGGABLE FString GetOperatorSymbolString(const EPredicateCompareOperatorType OperatorType) 54 | { 55 | static_assert(static_cast(EPredicateCompareOperatorType::Max) == 6, TEXT("This should be kept up to date with the enum")); 56 | switch(OperatorType) 57 | { 58 | case EPredicateCompareOperatorType::Equal: 59 | return TEXT("=="); 60 | case EPredicateCompareOperatorType::NotEqual: 61 | return TEXT("!="); 62 | case EPredicateCompareOperatorType::Less: 63 | return TEXT("<"); 64 | case EPredicateCompareOperatorType::LessOrEqual: 65 | return TEXT("<="); 66 | case EPredicateCompareOperatorType::Greater: 67 | return TEXT(">"); 68 | case EPredicateCompareOperatorType::GreaterOrEqual: 69 | return TEXT(">="); 70 | default: 71 | return TEXT("[Invalid Operator]"); 72 | } 73 | } 74 | 75 | 76 | UCLASS(MinimalApi, NotBlueprintable, meta = (DisplayName = "Compare Blackboard Value")) 77 | class UFlowNodeAddOn_PredicateCompareBlackboardValue 78 | : public UAIFlowNodeAddOn 79 | , public IFlowPredicateInterface 80 | { 81 | GENERATED_BODY() 82 | 83 | public: 84 | 85 | UFlowNodeAddOn_PredicateCompareBlackboardValue(); 86 | 87 | // IFlowPredicateInterface 88 | virtual bool EvaluatePredicate_Implementation() const override; 89 | // -- 90 | 91 | #if WITH_EDITOR 92 | // UObject 93 | virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; 94 | // -- 95 | 96 | // UFlowNodeBase 97 | virtual FText K2_GetNodeTitle_Implementation() const override; 98 | // -- 99 | 100 | // IFlowBlackboardAssetProvider 101 | virtual UBlackboardData* GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const override; 102 | // -- 103 | #endif // WITH_EDITOR 104 | 105 | protected: 106 | #if WITH_EDITOR 107 | bool TryRefreshIsKeyLeftSelected(); 108 | bool TryRefreshKeyEntriesAndExplicitValues(const UBlackboardData& BlackboardData); 109 | 110 | bool TryRefreshSelectedKeyType(const UBlackboardData& BlackboardData, bool bEnableKeyEntry, FFlowBlackboardEntry& InOutKeyProperty); 111 | bool TryRefreshExplicitValue(const UBlackboardData& BlackboardData, bool bEnableExplicitValue, TObjectPtr& InOutExplicitValue); 112 | 113 | TSubclassOf TryGetKeyTypeClass(const UBlackboardData& BlackboardData) const; 114 | UBlackboardKeyType const* TryGetKeyType(const UBlackboardData& BlackboardData) const; 115 | 116 | UBlackboardData* GetBlackboardAssetForEditor() const; 117 | #endif // WITH_EDITOR 118 | 119 | bool TryGetBlackboardKeyInfo( 120 | const UBlackboardData& BlackboardData, 121 | const FFlowBlackboardEntry& KeyEntry, 122 | FBlackboard::FKey& OutKeyID, 123 | FBlackboardEntry const*& OutKeyTypeEntry, 124 | bool bWarnIfMissing) const; 125 | 126 | bool ComputeCompareResultWithExplicitValue( 127 | const UBlackboardComponent& BlackboardComponent, 128 | const TSubclassOf KeyType, 129 | const FBlackboard::FKey LeftKeyID, 130 | const UFlowBlackboardEntryValue& RightExplicitValue) const; 131 | bool ComputeCompareResult( 132 | const UBlackboardComponent& BlackboardComponent, 133 | const TSubclassOf KeyType, 134 | const FBlackboard::FKey LeftKeyID, 135 | const FBlackboard::FKey RightKeyID) const; 136 | 137 | FORCEINLINE static bool IsEqualityOperation(EPredicateCompareOperatorType Operation) 138 | { 139 | return 140 | Operation >= EPredicateCompareOperatorType::EqualityFirst && 141 | Operation <= EPredicateCompareOperatorType::EqualityLast; 142 | } 143 | 144 | FORCEINLINE static bool IsArithmeticOperation(EPredicateCompareOperatorType Operation) 145 | { 146 | return 147 | Operation >= EPredicateCompareOperatorType::ArithmeticFirst && 148 | Operation <= EPredicateCompareOperatorType::ArithmeticLast; 149 | } 150 | 151 | static EArithmeticKeyOperation::Type ConvertPredicateCompareOperatorTypeToArithmeticKeyOperation(EPredicateCompareOperatorType OperatorType); 152 | 153 | protected: 154 | 155 | // Blackboard key for the Gameplay Tag or Tag Container to test with the Query 156 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Key (left)") 157 | FFlowBlackboardEntry KeyLeft; 158 | 159 | // Note - &&-ing the bIsKeyLeftSelected bool with itself in EditCondition is to prevent an implicit InlineEditConditionToggle 160 | // from being introduced on these properties. We don't want the embedded checkbox for bIsKeyLeftSelected, so I am making the 161 | // EditCondition harmlessly more complicated so that UE doesn't imply that I want the InlineEditConditionToggle. 162 | 163 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Operator", meta = (EditCondition = "bIsKeyLeftSelected && bIsKeyLeftSelected")) 164 | EPredicateCompareOperatorType OperatorType = EPredicateCompareOperatorType::Equal; 165 | 166 | // Search rule to use to find the "Specific Blackboard" (if specified) 167 | UPROPERTY(EditAnywhere, Category = Configuration, AdvancedDisplay, DisplayName = "Specific Blackboard Search Rule", meta = (EditCondition = "SpecificBlackboardAsset", DisplayAfter = SpecificBlackboardAsset)) 168 | EActorBlackboardSearchRule SpecificBlackboardSearchRule = EActorBlackboardSearchRule::ActorAndControllerAndGameState; 169 | 170 | // Blackboard key for the Gameplay Tag or Tag Container to test with the Query 171 | UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Key (right)", meta = (EditCondition = "!bUseExplicitValueForRightHandSide && bIsKeyLeftSelected == true")) 172 | FFlowBlackboardEntry KeyRight; 173 | 174 | UPROPERTY(EditAnywhere, Instanced, Category = Configuration, DisplayName = "Explicit Value (right)", meta = (EditCondition = "bUseExplicitValueForRightHandSide && bIsKeyLeftSelected")) 175 | TObjectPtr ExplicitValueRight = nullptr; 176 | 177 | // Specific blackboard to use for the comparison 178 | UPROPERTY(EditAnywhere, Category = Configuration, AdvancedDisplay, DisplayName = "Specific Blackboard") 179 | TObjectPtr SpecificBlackboardAsset = nullptr; 180 | 181 | #if WITH_EDITORONLY_DATA 182 | UPROPERTY(EditAnywhere, Category = Configuration, meta = (EditCondition = "bIsKeyLeftSelected && bIsKeyLeftSelected")) 183 | bool bUseExplicitValueForRightHandSide = false; 184 | 185 | UPROPERTY() 186 | bool bIsKeyLeftSelected = false; 187 | #endif // WITH_EDITORONLY_DATA 188 | }; 189 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Blackboard/FlowBlackboardEntryValue_Object.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Blackboard/FlowBlackboardEntryValue_Object.h" 4 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "AIFlowLogChannels.h" 7 | #include "Nodes/FlowNode.h" 8 | #include "Types/FlowDataPinValuesStandard.h" 9 | #include "Types/FlowDataPinResults.h" 10 | 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowBlackboardEntryValue_Object) 12 | 13 | #if WITH_EDITOR 14 | void UFlowBlackboardEntryValue_Object::PostInitProperties() 15 | { 16 | Super::PostInitProperties(); 17 | 18 | if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) 19 | { 20 | if (Key.AllowedTypes.IsEmpty()) 21 | { 22 | UBlackboardKeyType_Object* ObjectType = NewObject(this); 23 | ObjectType->BaseClass = BaseClass; 24 | Key.AllowedTypes.Add(ObjectType); 25 | } 26 | } 27 | } 28 | 29 | bool UFlowBlackboardEntryValue_Object::TryReconfigureFromBlackboardKeyType(const UBlackboardKeyType& KeyType) 30 | { 31 | const bool bSuperMadeChanges = Super::TryReconfigureFromBlackboardKeyType(KeyType); 32 | 33 | // Superclass is responsible for ensuring the UBlackboardKeyType_Object is correct, 34 | // but the subtype (eg BaseClass) may be mismatched, so we will need to ensure that here. 35 | 36 | check(Key.AllowedTypes.Num() == 1 && Key.AllowedTypes[0]->IsA(GetSupportedBlackboardKeyType())); 37 | 38 | const UBlackboardKeyType_Object* ObjectKeyType = CastChecked(&KeyType); 39 | if (BaseClass != ObjectKeyType->BaseClass) 40 | { 41 | BaseClass = ObjectKeyType->BaseClass; 42 | 43 | RefreshObjectTypeFromBaseClass(); 44 | EnsureObjectsAreCompatibleWithBaseClass(); 45 | 46 | return true; 47 | } 48 | 49 | return bSuperMadeChanges; 50 | } 51 | 52 | FString UFlowBlackboardEntryValue_Object::GetEditorValueString() const 53 | { 54 | if (IsValid(ObjectInstance)) 55 | { 56 | return *ObjectInstance->GetName(); 57 | } 58 | else if (IsValid(ObjectAsset)) 59 | { 60 | return *ObjectAsset->GetName(); 61 | } 62 | else 63 | { 64 | return TEXT(""); 65 | } 66 | } 67 | 68 | FText UFlowBlackboardEntryValue_Object::BuildNodeConfigText() const 69 | { 70 | return FText::FromString(FString::Printf(TEXT("Set %s to \"%s\""), *Key.GetKeyName().ToString(), *GetEditorValueString())); 71 | } 72 | 73 | void UFlowBlackboardEntryValue_Object::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) 74 | { 75 | Super::PostEditChangeProperty(PropertyChangedEvent); 76 | 77 | const FName MemberPropertyName = PropertyChangedEvent.MemberProperty->GetFName(); 78 | 79 | if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowBlackboardEntryValue_Object, BaseClass)) 80 | { 81 | if (ensure(Key.AllowedTypes.Num() == 1)) 82 | { 83 | // Update the BaseClass on the Key whenever it changes 84 | UBlackboardKeyType_Object* ObjectType = CastChecked(Key.AllowedTypes[0]); 85 | 86 | ObjectType->BaseClass = BaseClass; 87 | } 88 | } 89 | 90 | RefreshObjectTypeFromBaseClass(); 91 | EnsureObjectsAreCompatibleWithBaseClass(); 92 | } 93 | 94 | void UFlowBlackboardEntryValue_Object::EnsureObjectsAreCompatibleWithBaseClass() 95 | { 96 | if (ObjectInstance && !ObjectInstance->IsA(BaseClass)) 97 | { 98 | // Clear the ObjectInstance if it is not compatible with the BaseClass 99 | ObjectInstance = nullptr; 100 | } 101 | 102 | if (ObjectAsset && !ObjectAsset->IsA(BaseClass)) 103 | { 104 | // Clear the ObjectAsset if it is not compatible with the BaseClass 105 | ObjectAsset = nullptr; 106 | } 107 | } 108 | 109 | void UFlowBlackboardEntryValue_Object::RefreshObjectTypeFromBaseClass() 110 | { 111 | if (BaseClass) 112 | { 113 | const bool bIsInstanced = (BaseClass->ClassFlags & CLASS_EditInlineNew) != 0; 114 | if (bIsInstanced) 115 | { 116 | ObjectTypeSelector = EObjectInstanceTypeSelector::Instanced; 117 | } 118 | else 119 | { 120 | ObjectTypeSelector = EObjectInstanceTypeSelector::Asset; 121 | } 122 | } 123 | else 124 | { 125 | ObjectTypeSelector = EObjectInstanceTypeSelector::Unknown; 126 | } 127 | } 128 | 129 | #endif // WITH_EDITOR 130 | 131 | bool UFlowBlackboardEntryValue_Object::TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const 132 | { 133 | UObject* ObjectValue = GetObjectValue(); 134 | 135 | UClass* ClassFilter = nullptr; 136 | 137 | #if WITH_EDITOR 138 | // Only the editor data has the BaseClass (and also FFlowDataPinOutputProperty_Object::ClassFilter) 139 | // so we only can supply (or use) that information in editor builds 140 | ClassFilter = BaseClass; 141 | #endif // WITH_EDITOR 142 | 143 | OutFlowDataPinProperty.InitializeAs(ObjectValue, ClassFilter); 144 | return true; 145 | } 146 | 147 | bool UFlowBlackboardEntryValue_Object::TryProvideFlowDataPinPropertyFromBlackboardEntry( 148 | const FName& BlackboardKeyName, 149 | const UBlackboardKeyType& BlackboardKeyType, 150 | UBlackboardComponent* OptionalBlackboardComponent, 151 | TInstancedStruct& OutFlowDataPinProperty) const 152 | { 153 | if (TryProvideFlowDataPinPropertyFromBlackboardEntryTemplate( 154 | BlackboardKeyName, 155 | BlackboardKeyType, 156 | OptionalBlackboardComponent, 157 | OutFlowDataPinProperty)) 158 | { 159 | #if WITH_EDITOR 160 | const UBlackboardKeyType_Object* TypedKeyType = CastChecked(&BlackboardKeyType); 161 | FFlowDataPinValue_Object* MutableProperty = OutFlowDataPinProperty.GetMutablePtr(); 162 | 163 | // Only the editor data has the BaseClass or ClassFilter 164 | // so we only can supply (or use) that information in editor builds 165 | MutableProperty->ClassFilter = TypedKeyType->BaseClass; 166 | #endif // WITH_EDITOR 167 | 168 | return true; 169 | } 170 | 171 | return false; 172 | } 173 | 174 | UObject* UFlowBlackboardEntryValue_Object::GetObjectValue() const 175 | { 176 | return ObjectAsset ? ObjectAsset : ObjectInstance; 177 | } 178 | 179 | void UFlowBlackboardEntryValue_Object::SetObjectValue(UObject* InValue) 180 | { 181 | UClass* ObjectClass = IsValid(InValue) ? InValue->GetClass() : nullptr; 182 | if (IsValid(ObjectClass)) 183 | { 184 | const bool bIsInstanced = (ObjectClass->ClassFlags & CLASS_EditInlineNew) != 0; 185 | 186 | if (bIsInstanced) 187 | { 188 | ObjectInstance = InValue; 189 | ObjectAsset = nullptr; 190 | } 191 | else 192 | { 193 | ObjectInstance = nullptr; 194 | ObjectAsset = InValue; 195 | } 196 | } 197 | else 198 | { 199 | ObjectInstance = nullptr; 200 | ObjectAsset = nullptr; 201 | } 202 | } 203 | 204 | bool UFlowBlackboardEntryValue_Object::TrySetValueFromInputDataPin(const FName& PinName, UFlowNode& PinOwnerFlowNode) 205 | { 206 | TObjectPtr ResolvedObject = nullptr; 207 | const EFlowDataPinResolveResult ResolveResult = PinOwnerFlowNode.TryResolveDataPinValue(PinName, ResolvedObject); 208 | SetObjectValue(ResolvedObject); 209 | return FlowPinType::IsSuccess(ResolveResult); 210 | } 211 | 212 | void UFlowBlackboardEntryValue_Object::SetOnBlackboardComponent(UBlackboardComponent* BlackboardComponent) const 213 | { 214 | if (IsValid(BlackboardComponent)) 215 | { 216 | if (ObjectInstance) 217 | { 218 | BlackboardComponent->SetValueAsObject(Key.GetKeyName(), ObjectInstance); 219 | } 220 | else 221 | { 222 | BlackboardComponent->SetValueAsObject(Key.GetKeyName(), ObjectAsset); 223 | } 224 | } 225 | } 226 | 227 | EBlackboardCompare::Type UFlowBlackboardEntryValue_Object::CompareKeyValues(const UBlackboardComponent* BlackboardComponent, const FName& OtherKeyName) const 228 | { 229 | if (!IsValid(BlackboardComponent)) 230 | { 231 | UE_LOG(LogAIFlow, Error, TEXT("Cannot CompareKeyValues without a Blackboard!")); 232 | return EBlackboardCompare::NotEqual; 233 | } 234 | 235 | const UObject* OtherValueAsObject = BlackboardComponent->GetValueAsObject(OtherKeyName); 236 | 237 | if ((ObjectInstance && ObjectInstance == OtherValueAsObject) || 238 | (ObjectAsset == OtherValueAsObject)) 239 | { 240 | return EBlackboardCompare::Equal; 241 | } 242 | else 243 | { 244 | return EBlackboardCompare::NotEqual; 245 | } 246 | } 247 | 248 | TSubclassOf UFlowBlackboardEntryValue_Object::GetSupportedBlackboardKeyType() const 249 | { 250 | return UBlackboardKeyType_Object::StaticClass(); 251 | } 252 | -------------------------------------------------------------------------------- /Source/AIFlow/Public/AIFlowActorBlackboardHelper.h: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #pragma once 4 | 5 | #include "Templates/SubclassOf.h" 6 | #include "Types/FlowEnumUtils.h" 7 | #include "StructUtils/InstancedStruct.h" 8 | #include "Types/FlowPinEnums.h" 9 | 10 | #include "AIFlowActorBlackboardHelper.generated.h" 11 | 12 | // Forward Declarations 13 | class AActor; 14 | class FTextBuilder; 15 | class UBlackboardComponent; 16 | class UBlackboardData; 17 | class UFlowBlackboardEntryValue; 18 | class UFlowNodeBase; 19 | class UFlowInjectComponentsManager; 20 | struct FFlowBlackboardEntry; 21 | struct FFlowDataPinValue; 22 | class UBlackboardKeyType; 23 | 24 | // Rule enum for injecting missing blackboards on Actors 25 | UENUM() 26 | enum class EActorBlackboardInjectRule : uint8 27 | { 28 | InjectOntoActorIfMissing, 29 | InjectOntoControllerIfMissing, 30 | DoNotInjectIfMissing, 31 | 32 | Max UMETA(Hidden), 33 | Invalid UMETA(Hidden), 34 | Min = 0 UMETA(Hidden), 35 | 36 | NeedsInjectComponentsManagerFirst = InjectOntoActorIfMissing UMETA(Hidden), 37 | NeedsInjectComponentsManagerLast = InjectOntoControllerIfMissing UMETA(Hidden), 38 | }; 39 | FLOW_ENUM_RANGE_VALUES(EActorBlackboardInjectRule); 40 | 41 | namespace EActorBlackboardInjectRule_Classifiers 42 | { 43 | FORCEINLINE bool NeedsInjectComponentsManager(EActorBlackboardInjectRule Rule) { return FLOW_IS_ENUM_IN_SUBRANGE(Rule, EActorBlackboardInjectRule::NeedsInjectComponentsManager); } 44 | } 45 | 46 | // Rule enum for searching for Actor blackboards 47 | UENUM() 48 | enum class EActorBlackboardSearchRule : uint8 49 | { 50 | // Search the Actor only for the BlackboardComponent 51 | ActorOnly UMETA(DisplayName = "Actor Only"), 52 | 53 | // Search the Actor's Controller only for the BlackboardComponent 54 | ControllerOnly UMETA(DisplayName = "Controller Only"), 55 | 56 | // Search the GameState actor for the BlackboardComponent 57 | GameStateOnly UMETA(DisplayName = "GameState Only"), 58 | 59 | // Search both the Actor and the Controller for the BlackboardComponent 60 | ActorAndController UMETA(DisplayName = "Actor & Controller"), 61 | 62 | // Search the Actor, its Controller and the GameState actor for the BlackboardComponent 63 | ActorAndControllerAndGameState UMETA(DisplayName = "Actor, Controller & GameState"), 64 | 65 | Max UMETA(Hidden), 66 | Invalid UMETA(Hidden), 67 | Min = 0 UMETA(Hidden), 68 | }; 69 | FLOW_ENUM_RANGE_VALUES(EActorBlackboardSearchRule); 70 | 71 | namespace EActorBlackboardSearchRule_Classifiers 72 | { 73 | FORCEINLINE bool CanSearchActor(EActorBlackboardSearchRule Rule) { return Rule == EActorBlackboardSearchRule::ActorOnly || Rule == EActorBlackboardSearchRule::ActorAndController || Rule == EActorBlackboardSearchRule::ActorAndControllerAndGameState; } 74 | FORCEINLINE bool CanSearchController(EActorBlackboardSearchRule Rule) { return Rule == EActorBlackboardSearchRule::ControllerOnly || Rule == EActorBlackboardSearchRule::ActorAndController || Rule == EActorBlackboardSearchRule::ActorAndControllerAndGameState; } 75 | FORCEINLINE bool CanSearchGameState(EActorBlackboardSearchRule Rule) { return Rule == EActorBlackboardSearchRule::GameStateOnly || Rule == EActorBlackboardSearchRule::ActorAndControllerAndGameState; } 76 | } 77 | 78 | // Method to apply Per-Actor Options to Actors 79 | UENUM(BlueprintType) 80 | enum class EPerActorOptionsAssignmentMethod : uint8 81 | { 82 | // Options applied to actors in-order, 83 | // wrapping if the PerActorOptions are insufficient for the number of actors 84 | InOrderWithWrapping, 85 | 86 | // Options are shuffled and then applied to actors, 87 | // wrapping if the PerActorOptions are insufficient for the number of actors 88 | ShuffledWithWrapping, 89 | 90 | // Options are shuffled and then applied to actors, 91 | // reshuffling if the PerActorOptions are insufficient for the number of actors 92 | ShuffledWithReshuffling, 93 | 94 | Max UMETA(Hidden), 95 | Invalid UMETA(Hidden), 96 | Min = 0 UMETA(Hidden), 97 | }; 98 | FLOW_ENUM_RANGE_VALUES(EPerActorOptionsAssignmentMethod); 99 | 100 | // A bundle of Blackboard Entries to set on an actor(s) 101 | USTRUCT(BlueprintType) 102 | struct FAIFlowConfigureBlackboardOption 103 | { 104 | GENERATED_BODY() 105 | 106 | public: 107 | 108 | // Entries to set on the blackboard 109 | UPROPERTY(EditAnywhere, Instanced, Category = BlackboardEntriesToSet, DisplayName = "Blackboard Entries") 110 | TArray Entries; 111 | }; 112 | 113 | // Helper struct to handle the shared functionality of setting blackboard values for actors. 114 | // Used by UFlowNodeAddOn_ConfigureSpawnedActorBlackboard and UFlowNode_SetBlackboardValues. 115 | USTRUCT() 116 | struct FAIFlowActorBlackboardHelper 117 | { 118 | GENERATED_BODY() 119 | 120 | public: 121 | 122 | // Apply groups of blackboard entry options to a blackboard component. 123 | // Handles the incrementing and other management of AssignmentMethod state data. 124 | void ApplyBlackboardOptionsToBlackboardComponent( 125 | UBlackboardComponent& BlackboardComponent, 126 | EPerActorOptionsAssignmentMethod AssignmentMethod, 127 | const FAIFlowConfigureBlackboardOption& EntriesForEveryActor, 128 | const TArray* PerActorOptions); 129 | 130 | // Find or add (if the InjectRule allows) the desired BlackboardComponent on Actors. 131 | // If no OptionalBlackboardData is specified, it uses the first blackboard component that can be found, 132 | // otherwise, it restricts the result to a blackboard component that uses the blackboard data specified. 133 | static TArray FindOrAddBlackboardComponentOnActors( 134 | const TArray& Actors, 135 | UFlowInjectComponentsManager* InjectComponentsManager, 136 | TSubclassOf BlackboardComponentClass, 137 | UBlackboardData* OptionalBlackboardData, 138 | EActorBlackboardSearchRule SearchRule, 139 | EActorBlackboardInjectRule InjectRule); 140 | 141 | // Find or add (if the InjectRule allows) the desired BlackboardComponent on an Actor. 142 | // If no OptionalBlackboardData is specified, it uses the first blackboard component that can be found, 143 | // otherwise, it restricts the result to a blackboard component that uses the blackboard data specified. 144 | static UBlackboardComponent* FindOrAddBlackboardComponentOnActor( 145 | AActor& Actor, 146 | UFlowInjectComponentsManager* InjectComponentsManager, 147 | TSubclassOf BlackboardComponentClass, 148 | UBlackboardData* OptionalBlackboardData, 149 | EActorBlackboardSearchRule SearchRule, 150 | EActorBlackboardInjectRule InjectRule); 151 | 152 | // Try to find the blackboard on either the Actor, their Controller or the GameState, as directed by the supplied parameters 153 | static UBlackboardComponent* TryFindBlackboardComponent(UWorld& World, EActorBlackboardSearchRule SearchRule, AActor* OptionalActor, UBlackboardData* OptionalBlackboardData); 154 | 155 | AIFLOW_API static EFlowDataPinResolveResult TryProvideFlowDataPinPropertyFromBlackboardEntry( 156 | const FName& BlackboardKeyName, 157 | const UBlackboardKeyType* BlackboardKeyType, 158 | UBlackboardComponent* OptionalBlackboardComponent, 159 | TInstancedStruct& OutFlowDataPinProperty); 160 | 161 | #if WITH_EDITOR 162 | // Helper function to append text for Flow Node/AddOn Configuration display 163 | AIFLOW_API static void AppendBlackboardOptions( 164 | const TArray& PerActorOptions, 165 | FTextBuilder& InOutTextBuilder); 166 | #endif // WITH_EDITOR 167 | 168 | protected: 169 | 170 | // Apply the Blackboard Entries's value changes to the specified blackboard 171 | static void ApplyBlackboardEntries(UBlackboardComponent& BlackboardComponent, const TArray& EntriesToApply); 172 | 173 | // Helper function to setup and maintain the OrderedOptionIndices and OrderedOptionIndex according to the AssignmentMethod. 174 | int32 ChooseNextBlackboardOptionIndex( 175 | EPerActorOptionsAssignmentMethod AssignmentMethod, 176 | const TArray& InOutPerActorOptions); 177 | 178 | // Builds the OrderedOptionIndices array (if empty) 179 | void EnsureOrderedOptionIndices(int32 OptionNum); 180 | 181 | protected: 182 | 183 | // Most recently used Index in the OrderedOptionIndices array. 184 | UPROPERTY(Transient) 185 | int32 OrderedOptionIndex = INDEX_NONE; 186 | 187 | // Array of indices into the PerActorOptions. 188 | // May be in-order, or shuffled, based on the AssignmentMethod used to generate the array. 189 | UPROPERTY(Transient) 190 | TArray OrderedOptionIndices; 191 | }; 192 | 193 | // Helper struct to cache the blackboard component and runtime data reference 194 | USTRUCT() 195 | struct FAIFlowCachedBlackboardReference 196 | { 197 | GENERATED_BODY() 198 | 199 | public: 200 | 201 | AIFLOW_API FAIFlowCachedBlackboardReference() = default; 202 | AIFLOW_API FAIFlowCachedBlackboardReference(const UFlowNodeBase& FlowNodeBase, UBlackboardData* OptionalSpecificBlackboardData = nullptr, EActorBlackboardSearchRule SpecificBlackboardSearchRule = EActorBlackboardSearchRule::ActorAndControllerAndGameState) 203 | { (void) TryCacheBlackboardReference(FlowNodeBase, OptionalSpecificBlackboardData, SpecificBlackboardSearchRule); } 204 | 205 | AIFLOW_API bool TryCacheBlackboardReference(const UFlowNodeBase& FlowNodeBase, UBlackboardData* OptionalSpecificBlackboardData = nullptr, EActorBlackboardSearchRule SpecificBlackboardSearchRule = EActorBlackboardSearchRule::ActorAndControllerAndGameState); 206 | AIFLOW_API bool IsValid() const; 207 | 208 | public: 209 | 210 | UPROPERTY(Transient) 211 | TObjectPtr BlackboardComponent = nullptr; 212 | 213 | UPROPERTY(Transient) 214 | TObjectPtr BlackboardData = nullptr; 215 | }; 216 | -------------------------------------------------------------------------------- /Source/AIFlow/Private/Nodes/FlowNode_GetBlackboardValues.cpp: -------------------------------------------------------------------------------- 1 | // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors 2 | 3 | #include "Nodes/FlowNode_GetBlackboardValues.h" 4 | #include "Blackboard/FlowBlackboardEntryValue.h" 5 | #include "BehaviorTree/BlackboardComponent.h" 6 | #include "AIFlowAsset.h" 7 | #include "AIFlowTags.h" 8 | #include "BehaviorTree/Blackboard/BlackboardKeyType.h" 9 | #include "Blackboard/FlowBlackboardEntryValue_Enum.h" 10 | #include "Types/FlowAutoDataPinsWorkingData.h" 11 | #include "Types/FlowDataPinValuesStandard.h" 12 | #include "StructUtils/InstancedStruct.h" 13 | 14 | #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_GetBlackboardValues) 15 | 16 | FName UFlowNode_GetBlackboardValues::INPIN_SpecificActor; 17 | 18 | UFlowNode_GetBlackboardValues::UFlowNode_GetBlackboardValues() 19 | : Super() 20 | { 21 | #if WITH_EDITOR 22 | NodeDisplayStyle = FlowNodeStyle::Blackboard; 23 | Category = TEXT("Blackboard"); 24 | #endif 25 | 26 | InputPins.Empty(); 27 | OutputPins.Empty(); 28 | 29 | INPIN_SpecificActor = GET_MEMBER_NAME_CHECKED(ThisClass, SpecificActor); 30 | } 31 | 32 | AActor* UFlowNode_GetBlackboardValues::TryResolveActorForBlackboard() const 33 | { 34 | // TODO (gtaylor) Cache this result for better lookup perf + an exec pin to provoke a recache operation 35 | 36 | // Use the SpecificActor if provided, otherwise use the Flow Owner Actor 37 | TObjectPtr ResolvedObject = nullptr; 38 | const EFlowDataPinResolveResult ResolveResult = TryResolveDataPinValue(INPIN_SpecificActor, ResolvedObject); 39 | 40 | if (FlowPinType::IsSuccess(ResolveResult) && ResolvedObject) 41 | { 42 | AActor* ResolvedSpecificActor = Cast(ResolvedObject); 43 | if (IsValid(ResolvedSpecificActor)) 44 | { 45 | return ResolvedSpecificActor; 46 | } 47 | 48 | LogError(TEXT("Specific actor could not be resolved to an actor."), EFlowOnScreenMessageType::Temporary); 49 | 50 | return nullptr; 51 | } 52 | 53 | // Default to the Flow graph's actor. 54 | AActor* ActorOwner = GetFlowAsset()->TryFindActorOwner(); 55 | return ActorOwner; 56 | } 57 | 58 | UBlackboardComponent* UFlowNode_GetBlackboardValues::GetBlackboardComponentToApplyTo() const 59 | { 60 | // TODO (gtaylor) Consider consolidating with UFlowNode_SetBlackboardValuesV2::GetBlackboardComponentsToApplyTo() 61 | UBlackboardData* DesiredBlackboardAsset = SpecificBlackboardAsset; 62 | 63 | TSubclassOf BlackboardComponentClass = UBlackboardComponent::StaticClass(); 64 | if (UAIFlowAsset* AIFlowAsset = Cast(GetFlowAsset())) 65 | { 66 | BlackboardComponentClass = AIFlowAsset->GetBlackboardComponentClass(); 67 | 68 | // Default to the Flow Asset's default blackboard, if no SpecificBlackboardAsset was specified. 69 | if (!DesiredBlackboardAsset) 70 | { 71 | DesiredBlackboardAsset = AIFlowAsset->GetBlackboardAsset(); 72 | } 73 | } 74 | 75 | AActor* ActorSourceForBlackboard = TryResolveActorForBlackboard(); 76 | if (!IsValid(ActorSourceForBlackboard)) 77 | { 78 | return nullptr; 79 | } 80 | 81 | constexpr UFlowInjectComponentsManager* InjectComponentsManager = nullptr; 82 | 83 | UBlackboardComponent* BlackboardComponent = 84 | FAIFlowActorBlackboardHelper::FindOrAddBlackboardComponentOnActor( 85 | *ActorSourceForBlackboard, 86 | InjectComponentsManager, 87 | BlackboardComponentClass, 88 | DesiredBlackboardAsset, 89 | SpecificBlackboardSearchRule, 90 | EActorBlackboardInjectRule::DoNotInjectIfMissing); 91 | 92 | return BlackboardComponent; 93 | } 94 | 95 | #if WITH_EDITOR 96 | void UFlowNode_GetBlackboardValues::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChainEvent) 97 | { 98 | Super::PostEditChangeChainProperty(PropertyChainEvent); 99 | 100 | if (PropertyChainEvent.PropertyChain.Num() == 0) 101 | { 102 | return; 103 | } 104 | 105 | auto& Property = PropertyChainEvent.PropertyChain.GetActiveMemberNode()->GetValue(); 106 | 107 | constexpr EPropertyChangeType::Type RelevantChangeTypesForReconstructionMask = 108 | EPropertyChangeType::Unspecified | 109 | EPropertyChangeType::ArrayAdd | 110 | EPropertyChangeType::ArrayRemove | 111 | EPropertyChangeType::ArrayClear | 112 | EPropertyChangeType::ValueSet | 113 | EPropertyChangeType::Redirected | 114 | EPropertyChangeType::ArrayMove; 115 | 116 | const uint32 PropertyChangedTypeFlags = (PropertyChainEvent.ChangeType & RelevantChangeTypesForReconstructionMask); 117 | const bool bIsRelevantChangeTypeForReconstruction = PropertyChangedTypeFlags != 0; 118 | const bool bChangedOutputProperties = Property->GetFName() == GET_MEMBER_NAME_CHECKED(ThisClass, BlackboardEntries); 119 | if (bIsRelevantChangeTypeForReconstruction && bChangedOutputProperties) 120 | { 121 | OnReconstructionRequested.ExecuteIfBound(); 122 | } 123 | } 124 | 125 | UBlackboardData* UFlowNode_GetBlackboardValues::GetBlackboardAssetForEditor() const 126 | { 127 | if (IsValid(SpecificBlackboardAsset)) 128 | { 129 | return SpecificBlackboardAsset; 130 | } 131 | 132 | return GetBlackboardAsset(); 133 | } 134 | 135 | UBlackboardData* UFlowNode_GetBlackboardValues::GetBlackboardAssetForPropertyHandle(const TSharedPtr& PropertyHandle) const 136 | { 137 | if (UBlackboardData* BlackboardAssetForEditor = GetBlackboardAssetForEditor()) 138 | { 139 | return BlackboardAssetForEditor; 140 | } 141 | 142 | return Super::GetBlackboardAssetForPropertyHandle(PropertyHandle); 143 | } 144 | 145 | void UFlowNode_GetBlackboardValues::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const 146 | { 147 | Super::AutoGenerateDataPins(InOutWorkingData); 148 | 149 | // TODO (gtaylor) Consider combining/merging with UFlowNode_GetBlackboardValues::AutoGenerateDataPins() version 150 | const UBlackboardData* BlackboardAssetForEditor = GetBlackboardAssetForEditor(); 151 | 152 | if (!IsValid(BlackboardAssetForEditor)) 153 | { 154 | LogError(TEXT("Could not auto-generate pins: no blackboard asset found for use in the editor."), EFlowOnScreenMessageType::Temporary); 155 | 156 | return; 157 | } 158 | 159 | for (const FFlowBlackboardEntry& BlackboardEntry : BlackboardEntries) 160 | { 161 | const FName& PinName = BlackboardEntry.KeyName; 162 | if (PinName.IsNone()) 163 | { 164 | continue; 165 | } 166 | 167 | // Source BlackboardKeyType from the expected blackboard 168 | UBlackboardKeyType* BlackboardKeyType = GetBlackboardKeyTypeFromBlackboardKeyName(BlackboardAssetForEditor, BlackboardEntry.KeyName); 169 | if (!BlackboardKeyType) 170 | { 171 | LogError(FString::Printf(TEXT("Could not auto-generate pins: blackboard asset could not provide a type for pin name %s."), *PinName.ToString()), EFlowOnScreenMessageType::Temporary); 172 | 173 | continue; 174 | } 175 | 176 | TInstancedStruct InstancedFlowDataPinProperty; 177 | 178 | constexpr UBlackboardComponent* BlackboardComponent = nullptr; 179 | const EFlowDataPinResolveResult ProvidedResult = 180 | FAIFlowActorBlackboardHelper::TryProvideFlowDataPinPropertyFromBlackboardEntry( 181 | PinName, 182 | BlackboardKeyType, 183 | BlackboardComponent, 184 | InstancedFlowDataPinProperty); 185 | 186 | if (FlowPinType::IsSuccess(ProvidedResult)) 187 | { 188 | const FFlowDataPinValue& FlowDataPinValuePtr = InstancedFlowDataPinProperty.Get(); 189 | if (const FFlowPinType* FlowPinType = FFlowPinType::LookupPinType(FlowDataPinValuePtr.GetPinTypeName())) 190 | { 191 | FFlowPin NewFlowPin = FlowPinType->CreateFlowPinFromValueWrapper(PinName, FlowDataPinValuePtr); 192 | InOutWorkingData.AutoOutputDataPinsNext.AddUnique(NewFlowPin); 193 | } 194 | else 195 | { 196 | LogError(FString::Printf(TEXT("Could not auto-generate pin %s: Could not find pin type %s."), *PinName.ToString(), *FlowDataPinValuePtr.GetPinTypeName().ToString()), EFlowOnScreenMessageType::Temporary); 197 | } 198 | } 199 | else 200 | { 201 | LogError(FString::Printf(TEXT("Could not auto-generate pins: blackboard asset could not provide a value for pin name %s."), *PinName.ToString()), EFlowOnScreenMessageType::Temporary); 202 | } 203 | } 204 | } 205 | 206 | #endif // WITH_EDITOR 207 | 208 | void UFlowNode_GetBlackboardValues::UpdateNodeConfigText_Implementation() 209 | { 210 | #if WITH_EDITOR 211 | FTextBuilder TextBuilder; 212 | 213 | // TODO (gtaylor) Include some additional configuration notes here 214 | 215 | SetNodeConfigText(TextBuilder.ToText()); 216 | #endif // WITH_EDITOR 217 | } 218 | 219 | UBlackboardKeyType* UFlowNode_GetBlackboardValues::GetBlackboardKeyTypeFromBlackboardKeyName(const UBlackboardData* BlackboardAsset, const FName& KeyName) 220 | { 221 | if (!IsValid(BlackboardAsset)) 222 | { 223 | return nullptr; 224 | } 225 | 226 | const FBlackboard::FKey KeyID = BlackboardAsset->GetKeyID(KeyName); 227 | 228 | if (KeyID != FBlackboard::InvalidKey) 229 | { 230 | if (const FBlackboardEntry* BlackboardKey = BlackboardAsset->GetKey(KeyID)) 231 | { 232 | return BlackboardKey->KeyType; 233 | } 234 | } 235 | 236 | return nullptr; 237 | } 238 | 239 | FFlowDataPinResult UFlowNode_GetBlackboardValues::TrySupplyDataPin_Implementation(FName PinName) const 240 | { 241 | if (PinName == INPIN_SpecificActor) 242 | { 243 | return Super::TrySupplyDataPin_Implementation(PinName); 244 | } 245 | 246 | if (UBlackboardComponent* BlackboardComponent = GetBlackboardComponentToApplyTo()) 247 | { 248 | const UBlackboardKeyType* BlackboardKeyType = BlackboardComponent->GetBlackboardKeyType(PinName); 249 | 250 | FFlowDataPinResult SuppliedResult; 251 | 252 | SuppliedResult.Result = 253 | FAIFlowActorBlackboardHelper::TryProvideFlowDataPinPropertyFromBlackboardEntry( 254 | PinName, 255 | BlackboardKeyType, 256 | BlackboardComponent, 257 | SuppliedResult.ResultValue); 258 | 259 | return SuppliedResult; 260 | } 261 | 262 | return Super::TrySupplyDataPin_Implementation(PinName); 263 | } 264 | --------------------------------------------------------------------------------