// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "GameFramework/Actor.h" #include "SmartObjectTypes.h" #include "SmartObjectPersistentCollection.generated.h" #define UE_API SMARTOBJECTSMODULE_API namespace EEndPlayReason { enum Type : int; } class ASmartObjectPersistentCollection; class UBillboardComponent; class USmartObjectComponent; class USmartObjectContainerRenderingComponent; class USmartObjectDefinition; class USmartObjectSubsystem; struct FSmartObjectContainer; struct FSmartObjectDefinitionReference; /** Struct representing a unique registered component in the collection actor */ USTRUCT() struct FSmartObjectCollectionEntry { GENERATED_BODY() PRAGMA_DISABLE_DEPRECATION_WARNINGS FSmartObjectCollectionEntry() = default; FSmartObjectCollectionEntry(const FSmartObjectCollectionEntry& Other) = default; FSmartObjectCollectionEntry(FSmartObjectCollectionEntry&& Other) = default; FSmartObjectCollectionEntry& operator=(const FSmartObjectCollectionEntry& Other) = default; FSmartObjectCollectionEntry& operator=(FSmartObjectCollectionEntry&& Other) = default; UE_API FSmartObjectCollectionEntry(const FSmartObjectHandle SmartObjectHandle, TNotNull SmartObjectComponent, const uint32 DefinitionIndex); UE_DEPRECATED(5.6, "Use the constructor taking a pointer to the component instead.") UE_API FSmartObjectCollectionEntry(const FSmartObjectHandle SmartObjectHandle, const USmartObjectComponent& SmartObjectComponent, const uint32 DefinitionIndex); PRAGMA_ENABLE_DEPRECATION_WARNINGS const FSmartObjectHandle& GetHandle() const { return Handle; } #if WITH_EDITORONLY_DATA PRAGMA_DISABLE_DEPRECATION_WARNINGS UE_DEPRECATED(5.6, "Use GetComponent instead.") const FSoftObjectPath& GetPath() const { return Path_DEPRECATED; } UE_API PRAGMA_ENABLE_DEPRECATION_WARNINGS #endif// WITH_EDITORONLY_DATA USmartObjectComponent* GetComponent() const; FTransform GetTransform() const { return Transform; } const FBox& GetBounds() const { return Bounds; } FBox GetWorldBounds() const { return Bounds.MoveTo(Transform.GetLocation()); } uint32 GetDefinitionIndex() const { return DefinitionIdx; } const FGameplayTagContainer& GetTags() const { return Tags; } friend FString LexToString(const FSmartObjectCollectionEntry& CollectionEntry); protected: // Only the collection can access the path since the way we reference the component // might change to better support streaming so keeping this as encapsulated as possible friend FSmartObjectContainer; #if WITH_EDITORONLY_DATA void SetDefinitionIndex(const uint32 InDefinitionIndex) { DefinitionIdx = InDefinitionIndex; } UE_DEPRECATED(5.6, "Use Component weak pointer instead.") UPROPERTY() FSoftObjectPath Path_DEPRECATED; #endif // WITH_EDITORONLY_DATA UPROPERTY(VisibleAnywhere, Category = SmartObject) FGameplayTagContainer Tags; TWeakObjectPtr Component; UPROPERTY() FTransform Transform; UPROPERTY() FBox Bounds = FBox(ForceInitToZero); UPROPERTY(VisibleAnywhere, Category = SmartObject, meta = (ShowOnlyInnerProperties)) FSmartObjectHandle Handle; UPROPERTY(VisibleAnywhere, Category = SmartObject) uint32 DefinitionIdx = INDEX_NONE; }; USTRUCT() struct FSmartObjectContainer { GENERATED_BODY() UE_API explicit FSmartObjectContainer(UObject* InOwner = nullptr); UE_API ~FSmartObjectContainer(); UE_API FSmartObjectContainer& operator=(const FSmartObjectContainer& Other); UE_API FSmartObjectContainer& operator=(FSmartObjectContainer&& Other); /** * Creates a new entry for a given component. * @param SOComponent SmartObject Component for which a new entry must be created * @param bOutAlreadyInCollection Output parameter to indicate if an existing entry was returned instead of a newly created one. * @return Pointer to the created or existing entry. An unset value indicates a registration error. */ UE_API FSmartObjectCollectionEntry* AddSmartObject(TNotNull SOComponent, bool& bOutAlreadyInCollection); UE_DEPRECATED(5.6, "Use the overload taking a pointer to the component instead.") FSmartObjectCollectionEntry* AddSmartObject(USmartObjectComponent& SOComponent, bool& bOutAlreadyInCollection) { return AddSmartObject(&SOComponent, bOutAlreadyInCollection); } UE_API bool RemoveSmartObject(TNotNull SOComponent); UE_DEPRECATED(5.6, "Use the overload taking a pointer to the component instead.") bool RemoveSmartObject(USmartObjectComponent& SOComponent) { return RemoveSmartObject(&SOComponent); } #if WITH_EDITORONLY_DATA /** * If SOComponent is already contained by this FSmartObjectContainer instance then data relating to it will get updated * @return whether this container instance contains SOComponent */ UE_API bool UpdateSmartObject(TNotNull SOComponent); UE_DEPRECATED(5.6, "Use the overload taking a pointer to the component instead.") bool UpdateSmartObject(const USmartObjectComponent& SOComponent) { return UpdateSmartObject(&SOComponent); } #endif // WITH_EDITORONLY_DATA UE_API USmartObjectComponent* GetSmartObjectComponent(const FSmartObjectHandle SmartObjectHandle) const; UE_DEPRECATED(5.6, "Use the overload taking a UWorld as parameter.") const USmartObjectDefinition* GetDefinitionForEntry(const FSmartObjectCollectionEntry& Entry) const { return nullptr; } UE_API const USmartObjectDefinition* GetDefinitionForEntry(const FSmartObjectCollectionEntry& Entry, TNotNull World) const; TConstArrayView GetEntries() const { return CollectionEntries; } void SetBounds(const FBox& InBounds) { Bounds = InBounds; } const FBox& GetBounds() const { return Bounds; } bool IsEmpty() const { return CollectionEntries.Num() == 0; } UE_API void Append(const FSmartObjectContainer& Other); UE_API int32 Remove(const FSmartObjectContainer& Other); UE_API void ValidateDefinitions(); /** Note that this implementation is only expected to be used in the editor - it's pretty slow */ friend SMARTOBJECTSMODULE_API uint32 GetTypeHash(const FSmartObjectContainer& Instance); protected: friend USmartObjectSubsystem; friend ASmartObjectPersistentCollection; // assumes SOComponent to not be part of the collection yet UE_API FSmartObjectCollectionEntry* AddSmartObjectInternal(const FSmartObjectHandle Handle, TNotNull SOComponent); UE_DEPRECATED(5.6, "Use the overload taking a pointer to the component instead") FSmartObjectCollectionEntry* AddSmartObjectInternal(const FSmartObjectHandle Handle, const USmartObjectDefinition&, const USmartObjectComponent& SOComponent) { return AddSmartObjectInternal(Handle, const_cast(&SOComponent)); } UPROPERTY(VisibleAnywhere, Category = SmartObject) FBox Bounds = FBox(ForceInitToZero); UPROPERTY(VisibleAnywhere, Category = SmartObject) TArray CollectionEntries; UE_DEPRECATED(5.6, "Use HandleToComponentMappings instead.") UPROPERTY() TMap RegisteredIdToObjectMap_DEPRECATED; UPROPERTY() TMap> HandleToComponentMappings; UPROPERTY(VisibleAnywhere, Category = SmartObject) TArray DefinitionReferences; // used for reporting and debugging UPROPERTY() TObjectPtr Owner; #if WITH_EDITORONLY_DATA FString GetFullName() const { return Owner ? Owner->GetFullName() : TEXT("None"); } UE_API void ConvertDeprecatedDefinitionsToReferences(); UE_API void ConvertDeprecatedEntries(); UE_DEPRECATED(5.6, "Use DefinitionReferences instead.") UPROPERTY() TArray> Definitions_DEPRECATED; #endif // WITH_EDITORONLY_DATA }; /** Actor holding smart object persistent data */ UCLASS(MinimalAPI, NotBlueprintable, hidecategories = (Rendering, Replication, Collision, Input, HLOD, Actor, LOD, Cooking, WorldPartition)) class ASmartObjectPersistentCollection : public AActor { GENERATED_BODY() public: const TArray& GetEntries() const { return SmartObjectContainer.CollectionEntries; } void SetBounds(const FBox& InBounds) { SmartObjectContainer.Bounds = InBounds; } const FBox& GetBounds() const { return SmartObjectContainer.Bounds; } const FSmartObjectContainer& GetSmartObjectContainer() const { return SmartObjectContainer; }; FSmartObjectContainer& GetMutableSmartObjectContainer() { return SmartObjectContainer; }; bool IsEmpty() const { return SmartObjectContainer.IsEmpty(); } #if WITH_EDITORONLY_DATA UE_API void ResetCollection(const int32 ExpectedNumElements = 0); bool ShouldDebugDraw() const { return bEnableDebugDrawing; } #endif // WITH_EDITORONLY_DATA protected: friend class USmartObjectSubsystem; UE_API explicit ASmartObjectPersistentCollection(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); UE_API virtual void PostLoad() override; UE_API virtual void PostActorCreated() override; UE_API virtual void Destroyed() override; UE_API virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; UE_API virtual void PreRegisterAllComponents() override; UE_API virtual void PostUnregisterAllComponents() override; #if WITH_EDITOR UE_API virtual void PostEditUndo() override; /** Removes all entries from the collection. */ UFUNCTION(CallInEditor, Category = SmartObject) UE_API void ClearCollection(); /** Rebuild entries in the collection using all the SmartObjectComponents currently loaded in the level. */ UFUNCTION(CallInEditor, Category = SmartObject) UE_API void RebuildCollection(); /** Adds contents of InComponents to the stored SmartObjectContainer. Note that function does not clear out * the existing contents of SmartObjectContainer. Call ClearCollection or RebuildCollection if that is required. */ UE_API void AppendToCollection(const TConstArrayView InComponents); UE_API void OnSmartObjectComponentChanged(TNotNull Instance); UE_DEPRECATED(5.6, "Use the overload taking a pointer to the component instead.") void OnSmartObjectComponentChanged(const USmartObjectComponent& Instance) { OnSmartObjectComponentChanged(&Instance); } #endif // WITH_EDITOR UE_API virtual bool RegisterWithSubsystem(const FString& Context); UE_API virtual bool UnregisterWithSubsystem(const FString& Context); UE_API void OnRegistered(); bool IsRegistered() const { return bRegistered; } UE_API void OnUnregistered(); UPROPERTY(VisibleAnywhere, Category = SmartObject) FSmartObjectContainer SmartObjectContainer; #if WITH_EDITORONLY_DATA UPROPERTY(transient) TObjectPtr SpriteComponent; UPROPERTY(transient) TObjectPtr RenderingComponent; private: FDelegateHandle OnSmartObjectChangedDelegateHandle; protected: UPROPERTY(EditAnywhere, Category = SmartObject, AdvancedDisplay) bool bUpdateCollectionOnSmartObjectsChange = true; UPROPERTY(EditAnywhere, Category = SmartObject, AdvancedDisplay) bool bEnableDebugDrawing = true; #endif // WITH_EDITORONLY_DATA protected: bool bRegistered = false; }; #undef UE_API