2021-09-28 13:33:00 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# pragma once
2021-11-02 11:12:43 -04:00
# include "SmartObjectCollection.h"
2021-09-28 13:33:00 -04:00
# include "Templates/SubclassOf.h"
# include "SmartObjectOctree.h"
# include "SmartObjectTypes.h"
# include "SmartObjectRuntime.h"
# include "Subsystems/WorldSubsystem.h"
# include "SmartObjectSubsystem.generated.h"
class USmartObjectComponent ;
2021-11-03 15:47:51 -04:00
# if WITH_EDITOR
/** Called when main collection changed. */
DECLARE_MULTICAST_DELEGATE ( FOnMainCollectionChanged ) ;
# endif
2021-09-28 13:33:00 -04:00
/**
* Struct that can be used to filter results of a smart object request when trying to find or claim a smart object
*/
2021-11-26 15:48:13 -05:00
USTRUCT ( BlueprintType )
2021-09-28 13:33:00 -04:00
struct SMARTOBJECTSMODULE_API FSmartObjectRequestFilter
{
2021-11-26 15:48:13 -05:00
GENERATED_BODY ( )
2021-09-28 13:33:00 -04:00
FSmartObjectRequestFilter ( const FGameplayTagContainer & InUserTags , const FGameplayTagQuery & InRequirements )
: UserTags ( InUserTags )
, ActivityRequirements ( InRequirements )
{ }
2021-11-02 11:12:43 -04:00
explicit FSmartObjectRequestFilter ( const FGameplayTagContainer & InUserTags )
2021-09-28 13:33:00 -04:00
: UserTags ( InUserTags )
{ }
2021-11-02 11:12:43 -04:00
explicit FSmartObjectRequestFilter ( const FGameplayTagQuery & InRequirements )
2021-09-28 13:33:00 -04:00
: ActivityRequirements ( InRequirements )
{ }
2021-11-26 15:48:13 -05:00
explicit FSmartObjectRequestFilter ( const TSubclassOf < USmartObjectBehaviorDefinition > DefinitionClass )
: BehaviorDefinitionClass ( DefinitionClass )
2021-09-28 13:33:00 -04:00
{ }
FSmartObjectRequestFilter ( ) = default ;
2021-11-26 15:48:13 -05:00
UPROPERTY ( EditAnywhere , BlueprintReadWrite , Category = SmartObject )
2021-09-28 13:33:00 -04:00
FGameplayTagContainer UserTags ;
2021-11-26 15:48:13 -05:00
UPROPERTY ( EditAnywhere , BlueprintReadWrite , Category = SmartObject )
2021-09-28 13:33:00 -04:00
FGameplayTagQuery ActivityRequirements ;
2021-11-26 15:48:13 -05:00
UPROPERTY ( EditAnywhere , BlueprintReadWrite , Category = SmartObject )
TSubclassOf < USmartObjectBehaviorDefinition > BehaviorDefinitionClass ;
2021-09-28 13:33:00 -04:00
TFunction < bool ( FSmartObjectID ) > Predicate ;
} ;
/**
* Struct used to find a smart object within a specific search range and with optional filtering
*/
2021-11-26 15:48:13 -05:00
USTRUCT ( BlueprintType )
2021-09-28 13:33:00 -04:00
struct SMARTOBJECTSMODULE_API FSmartObjectRequest
{
2021-11-26 15:48:13 -05:00
GENERATED_BODY ( )
FSmartObjectRequest ( ) = default ;
2021-09-28 13:33:00 -04:00
FSmartObjectRequest ( const FBox & InQueryBox , const FSmartObjectRequestFilter & InFilter )
: QueryBox ( InQueryBox )
, Filter ( InFilter )
{ }
/** Box defining the search range */
2021-11-26 15:48:13 -05:00
UPROPERTY ( EditAnywhere , BlueprintReadWrite , Category = SmartObject )
FBox QueryBox = FBox ( ForceInitToZero ) ;
2021-09-28 13:33:00 -04:00
/** Struct used to filter out some results (all results allowed by default) */
2021-11-26 15:48:13 -05:00
UPROPERTY ( EditAnywhere , BlueprintReadWrite , Category = SmartObject )
2021-09-28 13:33:00 -04:00
FSmartObjectRequestFilter Filter ;
} ;
/**
* Struct that holds the object and slot selected by processing a smart object request .
*/
2021-11-26 15:48:13 -05:00
USTRUCT ( BlueprintType )
2021-09-28 13:33:00 -04:00
struct SMARTOBJECTSMODULE_API FSmartObjectRequestResult
{
2021-11-26 15:48:13 -05:00
GENERATED_BODY ( )
2021-09-28 13:33:00 -04:00
FSmartObjectRequestResult ( const FSmartObjectID & InSmartObjectID , const FSmartObjectSlotIndex & InSlotIndex )
: SmartObjectID ( InSmartObjectID )
, SlotIndex ( InSlotIndex )
{ }
FSmartObjectRequestResult ( ) = default ;
bool IsValid ( ) const { return SmartObjectID . IsValid ( ) & & SlotIndex . IsValid ( ) ; }
2021-11-26 15:48:13 -05:00
bool operator = = ( const FSmartObjectRequestResult & Other ) const
{
return IsValid ( ) & & Other . IsValid ( )
& & SmartObjectID = = Other . SmartObjectID
& & SlotIndex = = Other . SlotIndex ;
}
bool operator ! = ( const FSmartObjectRequestResult & Other ) const
{
return ! ( * this = = Other ) ;
}
2021-09-28 13:33:00 -04:00
FString Describe ( ) const
{
return FString : : Printf ( TEXT ( " Object:%s Use:%s " ) , * SmartObjectID . Describe ( ) , * SlotIndex . Describe ( ) ) ;
}
2021-11-26 15:48:13 -05:00
UPROPERTY ( VisibleAnywhere , BlueprintReadOnly , Category = SmartObject )
2021-09-28 13:33:00 -04:00
FSmartObjectID SmartObjectID ;
2021-11-26 15:48:13 -05:00
UPROPERTY ( VisibleAnywhere , BlueprintReadOnly , Category = SmartObject )
2021-09-28 13:33:00 -04:00
FSmartObjectSlotIndex SlotIndex ;
} ;
/**
* Subsystem that holds all registered smart object instances and offers the API for spatial queries and reservations .
*/
UCLASS ( config = Game )
class SMARTOBJECTSMODULE_API USmartObjectSubsystem : public UWorldSubsystem
{
GENERATED_BODY ( )
public :
USmartObjectSubsystem ( ) ;
static USmartObjectSubsystem * GetCurrent ( const UWorld * World ) ;
void RegisterCollection ( ASmartObjectCollection & InCollection ) ;
void UnregisterCollection ( ASmartObjectCollection & InCollection ) ;
ASmartObjectCollection * GetMainCollection ( ) const { return MainCollection ; }
bool RegisterSmartObject ( USmartObjectComponent & SmartObjectComponent ) ;
bool UnregisterSmartObject ( USmartObjectComponent & SmartObjectComponent ) ;
/**
* Returns the component associated to the claim handle if still
* accessible . In some scenarios the component may no longer exist
* but its smart object data could ( e . g . streaming )
* @ param ClaimHandle Valid handle to a claimed smart object slot
* @ return A pointer to the USmartObjectComponent * associated to the handle .
*/
2021-11-26 15:48:13 -05:00
UFUNCTION ( BlueprintCallable , Category = " SmartObject " )
2021-09-28 13:33:00 -04:00
USmartObjectComponent * GetSmartObjectComponent ( const FSmartObjectClaimHandle & ClaimHandle ) const ;
bool RegisterSmartObjectActor ( const AActor & SmartObjectActor ) ;
bool UnregisterSmartObjectActor ( const AActor & SmartObjectActor ) ;
/**
* Spatial lookup
* @ return First valid smart object in range . Not the closest one , just the one
* that happens to be retrieved first from the octree
*/
2021-11-26 15:48:13 -05:00
UFUNCTION ( BlueprintCallable , Category = " SmartObject " )
FSmartObjectRequestResult FindSmartObject ( const FSmartObjectRequest & Request ) ;
2021-09-28 13:33:00 -04:00
/**
* Spatial lookup
* @ return All valid smart objects in range .
*/
2021-11-26 15:48:13 -05:00
UFUNCTION ( BlueprintCallable , Category = " SmartObject " )
bool FindSmartObjects ( const FSmartObjectRequest & Request , TArray < FSmartObjectRequestResult > & OutResults ) ;
2021-09-28 13:33:00 -04:00
/**
* Goes through all defined slots of a given smart object and finds the first one matching the filter .
* @ return Identifier of a valid slot to use . Call IsValid on it to check if the search was successful .
*/
2021-11-02 11:12:43 -04:00
UE_NODISCARD FSmartObjectRequestResult FindSlot ( const FSmartObjectID ID , const FSmartObjectRequestFilter & Filter ) const ;
2021-09-28 13:33:00 -04:00
/**
* Claim smart object from a valid request result .
* @ param RequestResult Valid request result for given smart object and slot index . Ensure when called with an invalid result .
* @ return A claim handle binding the claimed smart object , its use index and a user id .
*/
2021-11-26 15:48:13 -05:00
UFUNCTION ( BlueprintCallable , Category = " SmartObject " )
FSmartObjectClaimHandle Claim ( const FSmartObjectRequestResult & RequestResult ) ;
2021-09-28 13:33:00 -04:00
2021-11-02 11:12:43 -04:00
UE_NODISCARD FSmartObjectClaimHandle Claim ( FSmartObjectID ID , const FSmartObjectRequestFilter & Filter = { } ) ;
2021-09-28 13:33:00 -04:00
/**
* Start using a claimed smart object slot .
* @ param ClaimHandle Handle for given pair of user and smart object . Error will be reported if the handle is invalid .
2021-11-26 15:48:13 -05:00
* @ param DefinitionClass The type of behavior definition the user wants to use .
* @ return The base class pointer of the requested behavior definition class associated to the slot
2021-09-28 13:33:00 -04:00
*/
2021-11-26 15:48:13 -05:00
UFUNCTION ( BlueprintCallable , Category = " SmartObject " )
const USmartObjectBehaviorDefinition * Use ( const FSmartObjectClaimHandle & ClaimHandle , const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass ) ;
2021-09-28 13:33:00 -04:00
/**
* Start using a claimed smart object slot .
* @ param ClaimHandle Handle for given pair of user and smart object . Error will be reported if the handle is invalid .
2021-11-26 15:48:13 -05:00
* @ return The requested behavior definition class pointer associated to the slot
2021-09-28 13:33:00 -04:00
*/
2021-11-26 15:48:13 -05:00
template < typename DefinitionType >
const DefinitionType * Use ( const FSmartObjectClaimHandle & ClaimHandle )
2021-09-28 13:33:00 -04:00
{
2021-11-26 15:48:13 -05:00
static_assert ( TIsDerivedFrom < DefinitionType , USmartObjectBehaviorDefinition > : : IsDerived , " DefinitionType must derive from USmartObjectBehaviorDefinition " ) ;
return Cast < const DefinitionType > ( Use ( ClaimHandle , DefinitionType : : StaticClass ( ) ) ) ;
2021-09-28 13:33:00 -04:00
}
/**
* Release claim on a smart object .
* @ param ClaimHandle Handle for given pair of user and smart object . Does nothing if the handle is invalid .
* @ return Whether the claim was successfully released or not
*/
2021-11-26 15:48:13 -05:00
UFUNCTION ( BlueprintCallable , Category = " SmartObject " )
2021-09-28 13:33:00 -04:00
bool Release ( const FSmartObjectClaimHandle & ClaimHandle ) ;
/**
* Returns the position ( in world space ) of the slot associated to the given claim handle .
* @ param ClaimHandle A valid handle ( ClaimHandle . IsValid ( ) returns true ) returned by ClaimUse or ClaimSmartObject .
* @ return Position ( in world space ) of the slot associated to ClaimHandle .
* @ note Method will ensure on invalid FSmartObjectClaimHandle .
*/
TOptional < FVector > GetSlotLocation ( const FSmartObjectClaimHandle & ClaimHandle ) const ;
2021-11-26 15:48:13 -05:00
/**
* Returns the position ( in world space ) of the slot associated to the given claim handle .
* @ param ClaimHandle A valid handle ( ClaimHandle . IsValid ( ) returns true ) returned by ClaimUse or ClaimSmartObject .
* @ param OutSlotLocation Position ( in world space ) of the slot associated to ClaimHandle .
* @ return Whether the location was found and assigned to ' OutSlotLocation '
* @ note Method will ensure on invalid FSmartObjectClaimHandle .
*/
UFUNCTION ( BlueprintCallable , Category = " SmartObject " )
bool GetSlotLocation ( const FSmartObjectClaimHandle & ClaimHandle , FVector & OutSlotLocation ) const ;
2021-09-28 13:33:00 -04:00
/**
* Returns the position ( in world space ) of the slot associated to the given request result .
* @ param Result A valid request result ( Result . IsValid ( ) returns true ) returned by FindValidUse or FindSmartObject .
* @ return Position ( in world space ) of the slot associated to ClaimHandle .
* @ note Method will ensure on invalid FSmartObjectRequestResult .
*/
TOptional < FVector > GetSlotLocation ( const FSmartObjectRequestResult & Result ) const ;
/**
* Returns the position ( in world space ) of the slot represented by the provided object id and slot index .
* @ param SmartObjectID Identifier of the smart object .
* @ param SlotIndex Index within the list of available slots in the smart object represented by SmartObjectID .
* @ return Position ( in world space ) of the slot represented by SmartObjectID and SlotIndex .
* @ note Method will ensure on invalid FSmartObjectID or an invalid index .
*/
TOptional < FVector > GetSlotLocation ( const FSmartObjectID SmartObjectID , const FSmartObjectSlotIndex SlotIndex ) const ;
/**
* Returns the transform ( in world space ) of the slot associated to the given claim handle .
* @ param ClaimHandle A valid handle ( ClaimHandle . IsValid ( ) returns true ) returned by ClaimUse or ClaimSmartObject .
* @ return Transform ( in world space ) of the slot associated to ClaimHandle .
* @ note Method will ensure on invalid FSmartObjectClaimHandle .
*/
TOptional < FTransform > GetSlotTransform ( const FSmartObjectClaimHandle & ClaimHandle ) const ;
2021-11-26 15:48:13 -05:00
/**
* Returns the transform ( in world space ) of the slot associated to the given claim handle .
* @ param ClaimHandle A valid handle ( ClaimHandle . IsValid ( ) returns true ) returned by ClaimUse or ClaimSmartObject .
* @ param OutSlotTransform Transform ( in world space ) of the slot associated to ClaimHandle .
* @ return Whether the transform was found and assigned to ' OutSlotTransform '
* @ note Method will ensure on invalid FSmartObjectClaimHandle .
*/
UFUNCTION ( BlueprintCallable , Category = " SmartObject " )
bool GetSlotTransform ( const FSmartObjectClaimHandle & ClaimHandle , FTransform & OutSlotTransform ) const ;
2021-09-28 13:33:00 -04:00
/**
* Returns the transform ( in world space ) of the slot associated to the given request result .
* @ param Result A valid request result ( Result . IsValid ( ) returns true ) returned by FindValidUse or FindSmartObject .
* @ return Transform ( in world space ) of the slot associated to ClaimHandle .
* @ note Method will ensure on invalid FSmartObjectRequestResult .
*/
TOptional < FTransform > GetSlotTransform ( const FSmartObjectRequestResult & Result ) const ;
/**
* Returns the transform ( in world space ) of the slot represented by the provided object id and slot index .
* @ param SmartObjectID Identifier of the smart object .
* @ param SlotIndex Index within the list of available slots in the smart object represented by SmartObjectID .
* @ return Transform ( in world space ) of the slot represented by SmartObjectID and SlotIndex .
* @ note Method will ensure on invalid FSmartObjectID or an invalid index .
*/
TOptional < FTransform > GetSlotTransform ( const FSmartObjectID SmartObjectID , const FSmartObjectSlotIndex SlotIndex ) const ;
/** @return The octree used by the subsystem to store all registered smart objects. */
const FSmartObjectOctree & GetOctree ( ) const ;
/**
* Register a callback to be notified if the claimed slot is no longer available and user need to perform cleanup .
* @ param ClaimHandle Handle to identify the object and slot . Error will be reported if the handle is invalid .
* @ param Callback Delegate that will be called to notify that a slot gets invalidated and can no longer be used .
*/
void RegisterSlotInvalidationCallback ( const FSmartObjectClaimHandle & ClaimHandle , const FOnSlotInvalidated & Callback ) ;
/**
* Unregisters a callback to be notified if the claimed slot is no longer available and user need to perform cleanup .
* @ param ClaimHandle Handle to identify the object and slot . Error will be reported if the handle is invalid .
*/
void UnregisterSlotInvalidationCallback ( const FSmartObjectClaimHandle & ClaimHandle ) ;
2021-11-03 15:47:51 -04:00
# if WITH_EDITOR
mutable FOnMainCollectionChanged OnMainCollectionChanged ;
# endif
2021-09-28 13:33:00 -04:00
protected :
2021-11-02 11:12:43 -04:00
/**
* Callback overriden to gather loaded collections , spawn missing one and set the main collection .
* @ note we use this method instead of ` Initialize ` or ` PostInitialize ` so active level is set and actors registered .
*/
virtual void OnWorldComponentsUpdated ( UWorld & World ) override ;
/**
* BeginPlay will push all objects stored in the collection to the runtime simulation
* and initialize octree using collection bounds .
*/
virtual void OnWorldBeginPlay ( UWorld & World ) override ;
2021-09-28 13:33:00 -04:00
/**
* Goes through all defined slots of smart object represented by SmartObjectRuntime
* and finds the first one given actor can use .
* @ return identifier indicating valid slot to use . Call IsValid on it to check if the search was successful .
*/
FSmartObjectSlotIndex FindSlot ( const FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectRequestFilter & Filter ) const ;
2021-11-26 15:48:13 -05:00
const USmartObjectBehaviorDefinition * Use ( FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectClaimHandle & ClaimHandle , const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass ) ;
2021-09-28 13:33:00 -04:00
void AbortAll ( FSmartObjectRuntime & SmartObjectRuntime ) ;
FSmartObjectSlotRuntimeData * GetMutableRuntimeSlot ( const FSmartObjectClaimHandle & ClaimHandle ) ;
/** Make sure that all SmartObjectCollection actors from our associated world are registered. */
void RegisterCollectionInstances ( ) ;
2021-11-26 15:48:13 -05:00
void AddToSimulation ( const FSmartObjectID ID , const USmartObjectDefinition & Definition , const FTransform & Transform , const FBox & Bounds ) ;
void AddToSimulation ( const FSmartObjectCollectionEntry & Entry , const USmartObjectDefinition & Definition ) ;
2021-11-02 11:12:43 -04:00
void AddToSimulation ( const USmartObjectComponent & ) ;
void RemoveFromSimulation ( const FSmartObjectID ID ) ;
void RemoveFromSimulation ( const FSmartObjectCollectionEntry & Entry ) ;
void RemoveFromSimulation ( const USmartObjectComponent & SmartObjectComponent ) ;
2021-09-28 13:33:00 -04:00
protected :
UPROPERTY ( )
ASmartObjectCollection * MainCollection ;
FSmartObjectOctree SmartObjectOctree ;
2021-11-02 11:12:43 -04:00
TMap < FSmartObjectID , FSmartObjectRuntime > RuntimeSmartObjects ;
/** Keep track of Ids associated to objects entirely created at runtime (i.e. not part of the initial collection) */
TArray < FSmartObjectID > RuntimeCreatedEntries ;
2021-09-28 13:33:00 -04:00
UE : : SmartObject : : ID NextFreeUserID ;
2021-11-02 11:12:43 -04:00
/** Flag to indicate that all entries from the baked collection are registered and new registrations will be considered runtime entries (i.e. no persistence) */
bool bInitialCollectionAddedToSimulation = false ;
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
friend class ASmartObjectCollection ;
void RebuildCollection ( ASmartObjectCollection & InCollection ) ;
void SpawnMissingCollection ( ) ;
2021-11-02 11:12:43 -04:00
/**
* Compute bounds from given world and store result in provided collection
* @ param World World from which the bounds must be computed
* @ param Collection Collection that will store computed bounds
*/
void ComputeBounds ( const UWorld & World , ASmartObjectCollection & Collection ) const ;
2021-09-28 13:33:00 -04:00
# endif // WITH_EDITOR
# if WITH_EDITORONLY_DATA
2021-11-02 11:12:43 -04:00
/** List of registered used to rebuild collection on demand */
UPROPERTY ( Transient )
2021-09-28 13:33:00 -04:00
TArray < USmartObjectComponent * > RegisteredSOComponents ;
# endif // WITH_EDITORONLY_DATA
# if WITH_SMARTOBJECT_DEBUG
public :
2021-11-02 11:12:43 -04:00
uint32 DebugGetNumRuntimeObjects ( ) const { return RuntimeSmartObjects . Num ( ) ; }
const TMap < FSmartObjectID , FSmartObjectRuntime > & DebugGetRuntimeObjects ( ) const { return RuntimeSmartObjects ; }
uint32 DebugGetNumRegisteredComponents ( ) const { return DebugRegisteredComponents . Num ( ) ; }
/** Debugging helper to remove all registered smart objects from the simulation */
2021-09-28 13:33:00 -04:00
void DebugUnregisterAllSmartObjects ( ) ;
2021-11-02 11:12:43 -04:00
/** Debugging helpers to add all registered smart objects to the simulation */
2021-09-28 13:33:00 -04:00
void DebugRegisterAllSmartObjects ( ) ;
private :
TArray < TWeakObjectPtr < USmartObjectComponent > > DebugRegisteredComponents ;
# endif // WITH_SMARTOBJECT_DEBUG
} ;