// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "BehaviorTree/BehaviorTreeComponent.h" #include "BehaviorTree/Blackboard/BlackboardKey.h" #include "BehaviorTree/BlackboardComponent.h" #include "GameplayTagContainer.h" #include "Misc/Guid.h" #include "StructUtils/InstancedStruct.h" #include "StructUtils/StructView.h" #include "ValueOrBBKey.generated.h" class UBlackboardKeyType; class FValueOrBBKeyDetails; struct FPropertyTag; struct FConstStructView; // Drop in replacement for Property in bt nodes that allows easy binding to blackboard key. Replace the old property with the corresponding type and call the GetValue() function to retrieve the value. struct FValueOrBBKey_Bool; struct FValueOrBBKey_Class; struct FValueOrBBKey_Enum; struct FValueOrBBKey_Float; struct FValueOrBBKey_Int32; struct FValueOrBBKey_Name; struct FValueOrBBKey_String; struct FValueOrBBKey_Object; struct FValueOrBBKey_Rotator; struct FValueOrBBKey_Struct; struct FValueOrBBKey_Vector; namespace FBlackboard { template TOptional TryGetBlackboardKeyValue(const UBlackboardComponent& Blackboard, const FName& Name, FBlackboard::FKey& InOutCachedKey) { if (InOutCachedKey == FBlackboard::InvalidKey) { InOutCachedKey = Blackboard.GetKeyID(Name); } if (Blackboard.GetKeyType(InOutCachedKey) == T::StaticClass()) { return { Blackboard.GetValue(InOutCachedKey) }; } return {}; } template typename T::FDataType GetValue(const UBlackboardComponent& Blackboard, const FName& Name, FBlackboard::FKey& InOutCachedKey, const typename T::FDataType& DefaultValue) { if (!Name.IsNone()) { if (const TOptional KeyValue = FBlackboard::TryGetBlackboardKeyValue(Blackboard, Name, InOutCachedKey)) { return KeyValue.GetValue(); } } return DefaultValue; } template typename T::FDataType GetValue(const UBehaviorTreeComponent& BehaviorComp, const FName& Name, FBlackboard::FKey& InOutCachedKey, const typename T::FDataType& DefaultValue) { if (const UBlackboardComponent* Blackboard = BehaviorComp.GetBlackboardComponent()) { return GetValue(*Blackboard, Name, InOutCachedKey, DefaultValue); } return DefaultValue; } FConstStructView TryGetBlackboardKeyStruct(const UBlackboardComponent& Blackboard, const FName& Name, FBlackboard::FKey& InOutCachedKey, const UScriptStruct* TargetStruct); FConstStructView GetStructValue(const UBlackboardComponent& Blackboard, const FName& Name, FBlackboard::FKey& InOutCachedKey, const FConstStructView& DefaultValue); FConstStructView GetStructValue(const UBehaviorTreeComponent& BehaviorComp, const FName& Name, FBlackboard::FKey& InOutCachedKey, const FConstStructView& DefaultValue); } // namespace FBlackboard // Base struct to simplify edition in the editor, shouldn't be used elsewhere USTRUCT(BlueprintType) struct FValueOrBlackboardKeyBase { GENERATED_BODY() friend FValueOrBBKeyDetails; #if WITH_EDITOR virtual ~FValueOrBlackboardKeyBase() = default; virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const { return false; }; virtual FString ToString() const { return FString(); } AIMODULE_API void PreSave(const UObject* Outer, const UBlackboardData& Blackboard, FName PropertyName); #endif // WITH_EDITOR const FName& GetKey() const { return Key; } void SetKey(FName NewKey) { Key = NewKey; } AIMODULE_API FBlackboard::FKey GetKeyId(const UBehaviorTreeComponent& OwnerComp) const; protected: template FString ToStringInternal(const T& DefaultValue) const { if (!Key.IsNone()) { return ToStringKeyName(); } else { return FString::Format(TEXT("{0}"), { DefaultValue }); } } AIMODULE_API FString ToStringKeyName() const; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Value") FName Key; mutable FBlackboard::FKey KeyId = FBlackboard::InvalidKey; }; USTRUCT(BlueprintType) struct FValueOrBBKey_Bool : public FValueOrBlackboardKeyBase { GENERATED_BODY() FValueOrBBKey_Bool(bool Default = false) : DefaultValue(Default) {} AIMODULE_API bool GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API bool GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API bool GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API bool GetValue(const UBlackboardComponent* Blackboard) const; #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const override; #endif // WITH_EDITOR bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); FString ToString() const { return ToStringInternal(DefaultValue); } UE_DEPRECATED_FORGAME(5.5, "Implicit conversion will be removed next version. Call GetValue instead") operator bool() const { return DefaultValue; } protected: UPROPERTY(EditAnywhere, Category = "Value") bool DefaultValue = false; }; USTRUCT(BlueprintType) struct FValueOrBBKey_Class : public FValueOrBlackboardKeyBase { GENERATED_BODY() friend class FValueOrBBKeyDetails_Class; FValueOrBBKey_Class() = default; template FValueOrBBKey_Class(TSubclassOf Default) : DefaultValue(Default), BaseClass(T::StaticClass()) {} template TSubclassOf GetValue(const UBehaviorTreeComponent& BehaviorComp) const { return TSubclassOf(GetValue(BehaviorComp)); } AIMODULE_API UClass* GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API UClass* GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API UClass* GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API UClass* GetValue(const UBlackboardComponent* Blackboard) const; bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const override; #endif // WITH_EDITOR AIMODULE_API FString ToString() const; UE_DEPRECATED_FORGAME(5.5, "Implicit conversion will be removed next version. Call GetValue instead") operator UClass*() const { return DefaultValue; } AIMODULE_API void SetBaseClass(UClass* NewBaseClass); protected: UPROPERTY(EditAnywhere, Category = "Value") TObjectPtr DefaultValue = nullptr; UPROPERTY(EditDefaultsOnly, Category = "Value", meta = (AllowAbstract = "1")) TObjectPtr BaseClass = UObject::StaticClass(); }; USTRUCT(BlueprintType) struct FValueOrBBKey_Enum : public FValueOrBlackboardKeyBase { GENERATED_BODY() friend class FValueOrBBKeyDetails_Enum; FValueOrBBKey_Enum() = default; template FValueOrBBKey_Enum(T Default) : DefaultValue(static_cast(Default)), EnumType(StaticEnum()), NativeEnumTypeName(GetNameSafe(EnumType)) {} template T GetValue(const UBehaviorTreeComponent& BehaviorComp) const { return static_cast(GetValue(BehaviorComp)); } AIMODULE_API uint8 GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API uint8 GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API uint8 GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API uint8 GetValue(const UBlackboardComponent* Blackboard) const; bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const override; #endif // WITH_EDITOR AIMODULE_API FString ToString() const; UE_DEPRECATED_FORGAME(5.5, "Implicit conversion will be removed next version. Call GetValue instead") operator uint8() const { return DefaultValue; } AIMODULE_API void SetEnumType(UEnum* NewEnumType); protected: UPROPERTY(EditAnywhere, Category = "Value") uint8 DefaultValue = 0; UPROPERTY(EditDefaultsOnly, Category = "Value") TObjectPtr EnumType = nullptr; /** Name of enum type defined in C++ code, will take priority over asset from EnumType property */ UPROPERTY(EditDefaultsOnly, Category = "Value") FString NativeEnumTypeName = TEXT(""); }; USTRUCT(BlueprintType) struct FValueOrBBKey_Float : public FValueOrBlackboardKeyBase { GENERATED_BODY() FValueOrBBKey_Float(float Default = 0.f) : DefaultValue(Default) {} AIMODULE_API float GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API float GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API float GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API float GetValue(const UBlackboardComponent* Blackboard) const; bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const override; #endif // WITH_EDITOR AIMODULE_API FString ToString() const; AIMODULE_API bool IsBoundOrNonZero() const; UE_DEPRECATED_FORGAME(5.5, "Implicit conversion will be removed next version. Call GetValue instead") operator float() const { return DefaultValue; } protected: UPROPERTY(EditAnywhere, Category = "Value") float DefaultValue = 0.f; }; USTRUCT(BlueprintType) struct FValueOrBBKey_Int32 : public FValueOrBlackboardKeyBase { GENERATED_BODY() FValueOrBBKey_Int32(int32 Default = 0) : DefaultValue(Default) {} AIMODULE_API int32 GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API int32 GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API int32 GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API int32 GetValue(const UBlackboardComponent* Blackboard) const; bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const override; #endif // WITH_EDITOR FString ToString() const { return ToStringInternal(DefaultValue); } UE_DEPRECATED_FORGAME(5.5, "Implicit conversion will be removed next version. Call GetValue instead") operator int32() const { return DefaultValue; } protected: UPROPERTY(EditAnywhere, Category = "Value") int32 DefaultValue = 0; }; USTRUCT(BlueprintType) struct FValueOrBBKey_Name : public FValueOrBlackboardKeyBase { GENERATED_BODY() FValueOrBBKey_Name(FName Default = FName()) : DefaultValue(Default) {} AIMODULE_API FName GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API FName GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API FName GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API FName GetValue(const UBlackboardComponent* Blackboard) const; bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const override; #endif // WITH_EDITOR AIMODULE_API FString ToString() const; UE_DEPRECATED_FORGAME(5.5, "Implicit conversion will be removed next version. Call GetValue instead") operator FName() const { return DefaultValue; } protected: UPROPERTY(EditAnywhere, Category = "Value") FName DefaultValue; }; USTRUCT(BlueprintType) struct FValueOrBBKey_String : public FValueOrBlackboardKeyBase { GENERATED_BODY() FValueOrBBKey_String(FString Default = FString()) : DefaultValue(MoveTemp(Default)) {} AIMODULE_API FString GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API FString GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API FString GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API FString GetValue(const UBlackboardComponent* Blackboard) const; bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const override; #endif // WITH_EDITOR UE_DEPRECATED_FORGAME(5.5, "Implicit conversion will be removed next version. Call GetValue instead") operator FString() const { return DefaultValue; } FString ToString() const { return ToStringInternal(DefaultValue); } protected: UPROPERTY(EditAnywhere, Category = "Value") FString DefaultValue; }; USTRUCT(BlueprintType) struct FValueOrBBKey_Object : public FValueOrBlackboardKeyBase { GENERATED_BODY() friend class FValueOrBBKeyDetails_Object; FValueOrBBKey_Object() = default; template FValueOrBBKey_Object(TObjectPtr Default) : DefaultValue(Default), BaseClass(T::StaticClass()) {} template T* GetValue(const UBehaviorTreeComponent& BehaviorComp) const { return Cast(GetValue(BehaviorComp)); } AIMODULE_API UObject* GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API UObject* GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API UObject* GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API UObject* GetValue(const UBlackboardComponent* Blackboard) const; bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const override; #endif // WITH_EDITOR AIMODULE_API FString ToString() const; UE_DEPRECATED_FORGAME(5.5, "Implicit conversion will be removed next version. Call GetValue instead") operator UObject*() const { return DefaultValue; } AIMODULE_API void SetBaseClass(UClass* NewBaseClass); protected: UPROPERTY(EditAnywhere, Category = "Value") TObjectPtr DefaultValue = nullptr; UPROPERTY(EditDefaultsOnly, Category = "Value", meta = (AllowAbstract = "1")) TObjectPtr BaseClass = UObject::StaticClass(); }; USTRUCT(BlueprintType) struct FValueOrBBKey_Rotator : public FValueOrBlackboardKeyBase { GENERATED_BODY() FValueOrBBKey_Rotator(FRotator Default = FRotator::ZeroRotator) : DefaultValue(MoveTemp(Default)) {} AIMODULE_API FRotator GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API FRotator GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API FRotator GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API FRotator GetValue(const UBlackboardComponent* Blackboard) const; bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const override; #endif // WITH_EDITOR AIMODULE_API FString ToString() const; UE_DEPRECATED_FORGAME(5.5, "Implicit conversion will be removed next version. Call GetValue instead") operator FRotator() const { return DefaultValue; } protected: UPROPERTY(EditAnywhere, Category = "Value") FRotator DefaultValue; }; USTRUCT(BlueprintType) struct FValueOrBBKey_Vector : public FValueOrBlackboardKeyBase { GENERATED_BODY() FValueOrBBKey_Vector(FVector Default = FVector::ZeroVector) : DefaultValue(MoveTemp(Default)) {} AIMODULE_API FVector GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API FVector GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API FVector GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API FVector GetValue(const UBlackboardComponent* Blackboard) const; bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const override; #endif // WITH_EDITOR AIMODULE_API FString ToString() const; UE_DEPRECATED_FORGAME(5.5, "Implicit conversion will be removed next version. Call GetValue instead") operator FVector() const { return DefaultValue; } protected: UPROPERTY(EditAnywhere, Category = "Value") FVector DefaultValue; }; USTRUCT(BlueprintType) struct FValueOrBBKey_Struct : public FValueOrBlackboardKeyBase { GENERATED_BODY() public: template FValueOrBBKey_Struct(const T& Value) { DefaultValue.InitializeAs(Value); } FValueOrBBKey_Struct() = default; #if WITH_EDITOR AIMODULE_API virtual bool IsCompatibleType(const UBlackboardKeyType* KeyType) const; #endif // WITH_EDITOR AIMODULE_API FString ToString() const; template const T& GetValue(const UBehaviorTreeComponent& BehaviorComp) const { check(DefaultValue.GetScriptStruct() == TBaseStructure::Get()); return GetValue(BehaviorComp).Get(); } bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot); AIMODULE_API FConstStructView GetValue(const UBehaviorTreeComponent& BehaviorComp) const; AIMODULE_API FConstStructView GetValue(const UBehaviorTreeComponent* BehaviorComp) const; AIMODULE_API FConstStructView GetValue(const UBlackboardComponent& Blackboard) const; AIMODULE_API FConstStructView GetValue(const UBlackboardComponent* Blackboard) const; AIMODULE_API void SetStructType(UScriptStruct* NewStructType); protected: friend class FValueOrBBKeyDetails_Struct; UPROPERTY(EditAnywhere, Category = Value) FInstancedStruct DefaultValue; #if WITH_EDITORONLY_DATA /** Exist to give a way for the details to know if the DefaultValue type can be edited. */ UPROPERTY(EditDefaultsOnly, Category = Value) bool bCanEditDefaultValueType = false; #endif // WITH_EDITORONLY_DATA }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; }; template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithStructuredSerializeFromMismatchedTag = true, }; };