2021-09-28 13:33:00 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "SmartObjectSubsystem.h"
2023-01-19 00:48:07 -05:00
# include "Math/ColorList.h"
2021-09-28 13:33:00 -04:00
# include "SmartObjectComponent.h"
2023-03-27 08:22:19 -04:00
# include "SmartObjectUserComponent.h"
2023-03-14 06:26:29 -04:00
# include "SmartObjectTypes.h"
2021-09-28 13:33:00 -04:00
# include "EngineUtils.h"
2022-02-21 01:10:34 -05:00
# include "SmartObjectHashGrid.h"
2023-02-02 18:43:13 -05:00
# include "WorldConditionContext.h"
2021-09-28 13:33:00 -04:00
# include "VisualLogger/VisualLogger.h"
2022-11-24 14:53:52 -05:00
# include "Engine/LevelStreaming.h"
2023-03-02 05:58:30 -05:00
# include "NavigationSystem.h"
# include "AI/Navigation/NavigationTypes.h"
2024-01-15 13:31:49 -05:00
# include "NavFilters/NavigationQueryFilter.h"
2023-03-14 06:26:29 -04:00
# include "Annotations/SmartObjectSlotEntranceAnnotation.h"
2023-03-21 07:37:13 -04:00
# include "Annotations/SmartObjectAnnotation_SlotUserCollision.h"
2023-03-17 07:41:24 -04:00
# include "Misc/EnumerateRange.h"
2023-09-05 16:59:33 -04:00
# include "Types/TargetingSystemTypes.h"
2021-09-28 13:33:00 -04:00
2022-09-28 01:06:15 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(SmartObjectSubsystem)
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
# include "SmartObjectSubsystemRenderingActor.h"
# endif
2022-01-26 17:33:02 -05:00
# if WITH_SMARTOBJECT_DEBUG
# endif
2021-11-02 11:12:43 -04:00
# if WITH_EDITOR
# include "Engine/LevelBounds.h"
# include "WorldPartition/WorldPartition.h"
# endif
2022-11-24 14:53:52 -05:00
# if WITH_EDITORONLY_DATA
# include "SmartObjectCollection.h"
# endif // WITH_EDITORONLY_DATA
2022-04-04 13:22:01 -04:00
namespace UE : : SmartObject
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
// Indicates that runtime shouldn't be initialized.
// This flag must be set BEFORE launching the game and not toggled after.
bool bDisableRuntime = false ;
FAutoConsoleVariableRef CVarDisableRuntime (
TEXT ( " ai.smartobject.DisableRuntime " ) ,
bDisableRuntime ,
TEXT ( " If enabled, runtime instances won't be created for baked collection entries or runtime added ones from component registration. " ) ,
ECVF_Default ) ;
2022-04-04 13:22:01 -04:00
2021-09-28 13:33:00 -04:00
# if WITH_SMARTOBJECT_DEBUG
2022-11-24 14:53:52 -05:00
namespace Debug
{
static FAutoConsoleCommandWithWorld RegisterAllSmartObjectsCmd
(
TEXT ( " ai.debug.so.RegisterAllSmartObjects " ) ,
TEXT ( " Force register all objects registered in the subsystem to simulate & debug runtime flows (will ignore already registered components). " ) ,
FConsoleCommandWithWorldDelegate : : CreateLambda ( [ ] ( const UWorld * InWorld )
{
if ( USmartObjectSubsystem * Subsystem = USmartObjectSubsystem : : GetCurrent ( InWorld ) )
{
Subsystem - > DebugRegisterAllSmartObjects ( ) ;
}
} )
) ;
2021-09-28 13:33:00 -04:00
2022-11-24 14:53:52 -05:00
static FAutoConsoleCommandWithWorld UnregisterAllSmartObjectsCmd
(
TEXT ( " ai.debug.so.UnregisterAllSmartObjects " ) ,
TEXT ( " Force unregister all objects registered in the subsystem to simulate & debug runtime flows (will ignore already unregistered components). " ) ,
FConsoleCommandWithWorldDelegate : : CreateLambda ( [ ] ( const UWorld * InWorld )
{
if ( USmartObjectSubsystem * Subsystem = USmartObjectSubsystem : : GetCurrent ( InWorld ) )
{
Subsystem - > DebugUnregisterAllSmartObjects ( ) ;
}
} )
) ;
} // UE::SmartObject::Debug
2022-04-04 13:22:01 -04:00
# endif // WITH_SMARTOBJECT_DEBUG
2023-03-02 05:58:30 -05:00
2024-02-08 13:21:26 -05:00
FString DebugGetComponentName ( const USmartObjectComponent & SmartObjectComponent )
{
const AActor * Owner = SmartObjectComponent . GetOwner ( ) ;
return SmartObjectComponent . GetFullName ( Owner ! = nullptr
? Owner - > GetOwner ( ) // Get path relative to the owner's owner to get a good compromise between not enough and too many details
: nullptr ) ; // Get fully qualified pathname
}
2022-04-04 13:22:01 -04:00
} // UE::SmartObject
2021-09-28 13:33:00 -04:00
2023-08-10 07:02:20 -04:00
/**
* Internal helper struct for all the data needed for smart object entrance validation .
*/
struct FSmartObjectValidationContext
{
const ANavigationData * NavigationData = nullptr ;
FSharedConstNavQueryFilter NavigationFilter = nullptr ;
FVector NavigationSearchExtents = FVector : : ZeroVector ;
const USmartObjectSlotValidationFilter * ValidationFilter = nullptr ;
const FSmartObjectSlotValidationParams * ValidationParams = nullptr ;
FSmartObjectUserCapsuleParams UserCapsuleParams ;
FSmartObjectTraceParams GroundTraceParams ;
FSmartObjectTraceParams TransitionTraceParams ;
FCollisionQueryParams GroundTraceQueryParams ;
FCollisionQueryParams TransitionTraceQueryParams ;
2023-08-14 03:40:42 -04:00
bool Init ( const UWorld * World , const FSmartObjectSlotEntranceLocationRequest & Request , const AActor * SmartObjectActor )
2023-08-10 07:02:20 -04:00
{
2023-08-14 03:40:42 -04:00
const UObject * LogOwner = USmartObjectSubsystem : : GetCurrent ( World ) ;
if ( ! LogOwner )
{
LogOwner = World ;
}
2023-08-10 07:02:20 -04:00
TSubclassOf < USmartObjectSlotValidationFilter > ValidationFilterClass = Request . ValidationFilter ;
NavigationData = Request . NavigationData ;
if ( Request . UserActor )
{
// If user actor is present, try to query some data automatically from interfaces and components.
if ( ! ValidationFilterClass . Get ( ) )
{
if ( const USmartObjectUserComponent * UserComponent = Request . UserActor - > GetComponentByClass < USmartObjectUserComponent > ( ) )
{
ValidationFilterClass = UserComponent - > GetValidationFilter ( ) ;
}
}
if ( ! NavigationData )
{
NavigationData = UE : : SmartObject : : Annotations : : GetNavDataForActor ( * World , Request . UserActor ) ;
}
}
if ( ! ValidationFilterClass . Get ( ) )
{
2023-08-14 03:40:42 -04:00
UE_VLOG_UELOG ( LogOwner , LogSmartObject , Warning ,
2023-08-10 07:02:20 -04:00
TEXT ( " %hs: Invalid validation filter for user actor %s. " ) ,
__FUNCTION__ , * GetNameSafe ( Request . UserActor ) ) ;
return false ;
}
ValidationFilter = ValidationFilterClass . GetDefaultObject ( ) ;
check ( ValidationFilter ) ;
ValidationParams = & ValidationFilter - > GetValidationParams ( Request . LocationType ) ;
const bool bRequiresValidUserCapsule = Request . bCheckSlotLocationOverlap | | Request . bCheckEntranceLocationOverlap ;
if ( bRequiresValidUserCapsule )
{
if ( Request . UserCapsuleParams . IsValid ( ) )
{
UserCapsuleParams = ValidationParams - > GetUserCapsule ( Request . UserCapsuleParams ) ;
}
else if ( Request . UserActor )
{
if ( ! ValidationParams - > GetUserCapsuleForActor ( * Request . UserActor , UserCapsuleParams ) )
{
2023-08-14 03:40:42 -04:00
UE_VLOG_UELOG ( LogOwner , LogSmartObject , Error ,
2023-08-10 07:02:20 -04:00
TEXT ( " %hs: Could not resolve user capsule size. Failed to access navigation parameters for user actor %s. " ) ,
__FUNCTION__ , * GetNameSafe ( Request . UserActor ) ) ;
return false ;
}
}
else
{
// Fallback to the capsule size from validation params.
UserCapsuleParams = ValidationParams - > GetUserCapsule ( ) ;
}
}
// Navdata must be valid when testing for navigable.
if ( Request . bProjectNavigationLocation )
{
if ( ! NavigationData )
{
2023-08-14 03:40:42 -04:00
UE_VLOG_UELOG ( LogOwner , LogSmartObject , Error ,
2023-08-10 07:02:20 -04:00
TEXT ( " %hs: ProjectNavigationLocation is requested, expecting valid navigation data, NavigationData is not set. " ) ,
__FUNCTION__ ) ;
return false ;
}
// Filter must be valid if specified.
if ( ValidationParams - > GetNavigationFilter ( ) . Get ( ) )
{
NavigationFilter = UNavigationQueryFilter : : GetQueryFilter ( * NavigationData , Request . UserActor , ValidationParams - > GetNavigationFilter ( ) ) ;
if ( ! NavigationFilter . IsValid ( ) )
{
2023-08-14 03:40:42 -04:00
UE_VLOG_UELOG ( LogOwner , LogSmartObject , Error ,
2023-08-10 07:02:20 -04:00
TEXT ( " %hs: Navigation filter was specified was failed to resolve it. " ) ,
__FUNCTION__ ) ;
return false ;
}
}
}
NavigationSearchExtents = FVector ( ValidationParams - > GetSearchExtents ( ) ) ;
GroundTraceParams = ValidationParams - > GetGroundTraceParameters ( ) ;
TransitionTraceParams = ValidationParams - > GetTransitionTraceParameters ( ) ;
GroundTraceQueryParams = FCollisionQueryParams ( SCENE_QUERY_STAT ( SmartObjectTrace ) , GroundTraceParams . bTraceComplex ) ;
TransitionTraceQueryParams = FCollisionQueryParams ( SCENE_QUERY_STAT ( SmartObjectTrace ) , TransitionTraceParams . bTraceComplex ) ;
GroundTraceQueryParams . bIgnoreTouches = true ;
TransitionTraceQueryParams . bIgnoreTouches = true ;
if ( SmartObjectActor )
{
GroundTraceQueryParams . AddIgnoredActor ( SmartObjectActor ) ;
TransitionTraceQueryParams . AddIgnoredActor ( SmartObjectActor ) ;
}
if ( Request . UserActor )
{
GroundTraceQueryParams . AddIgnoredActor ( Request . UserActor ) ;
TransitionTraceQueryParams . AddIgnoredActor ( Request . UserActor ) ;
}
return true ;
}
} ;
2021-09-28 13:33:00 -04:00
//----------------------------------------------------------------------//
// USmartObjectSubsystem
//----------------------------------------------------------------------//
2024-02-08 13:21:26 -05:00
/*__________________________________________________________________________________________________________________________________________________________________________________
[ Registration flows ]
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| CreateSmartObject ( ) | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > | CreateRuntimeInstance ( ) |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + / + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ( no comp ) /
| RegisterCollection ( ) | - - - - > | AddContainerToSimulation ( ) | - - - - - - - - - - - - - /
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
\ - > | AddCollectionEntryToSimulation ( ) |
( comp ) \ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ( 1 ) / + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
- > | AddComponentToSimulation ( ) | - - - -
( not registered & not in collection ) / + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ( 2 ) \
/ \
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| RegisterSmartObjectActor ( ) | - - - - > | RegisterSmartObject ( ) | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > | BindComponentToSimulationInternal ( ) |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ( registered | already in collection ) + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
____________________________________________________________________________________________________________________________________________________________________________________
[ Unregistration flows ]
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| UnregisterCollection ( ) | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + \
| UnregisterSmartObjectActor ( ) | - - - - > | UnregisterSmartObject ( ) | \
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + \
\ \
\ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ( keep runtime ) \ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
- > | UnregisterSmartObjectInternal | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > | UnbindComponentFromSimulationInternal ( ) |
/ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + / + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
/ \ ( destroy runtime ) /
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + \ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + /
| RemoveSmartObjectActor ( ) | - - - - > | RemoveSmartObject ( ) | - > | RemoveComponentFromSimulation ( ) | /
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + / + - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + /
/ ( comp ) \ /
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + / \ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| DestroySmartObject ( ) | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > | RemoveRuntimeInstanceFromSimulation ( ) | - - - - > | DestroyRuntimeInstanceInternal ( ) |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ( no comp ) + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
__________________________________________________________________________________________________________________________________________________________________________________ */
2022-11-24 14:53:52 -05:00
USmartObjectSubsystem : : USmartObjectSubsystem ( )
: SmartObjectContainer ( this )
{
}
2021-09-28 13:33:00 -04:00
2021-11-02 11:12:43 -04:00
void USmartObjectSubsystem : : OnWorldComponentsUpdated ( UWorld & World )
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
# if WITH_EDITORONLY_DATA
bIsPartitionedWorld = World . IsPartitionedWorld ( ) ;
# endif // WITH_EDITORONLY_DATA
2022-02-21 01:10:34 -05:00
// Load class required to instantiate the space partition structure
UE_CVLOG_UELOG ( ! SpacePartitionClassName . IsValid ( ) , this , LogSmartObject , Error , TEXT ( " A valid space partition class name is required. " ) ) ;
if ( SpacePartitionClassName . IsValid ( ) )
{
2023-03-01 15:43:52 -05:00
SpacePartitionClass = LoadClass < USmartObjectSpacePartition > ( nullptr , * SpacePartitionClassName . ToString ( ) ) ;
2022-02-21 01:10:34 -05:00
UE_CVLOG_UELOG ( * SpacePartitionClass = = nullptr , this , LogSmartObject , Error , TEXT ( " Unable to load class %s " ) , * SpacePartitionClassName . ToString ( ) ) ;
2022-04-26 14:39:26 -04:00
}
2022-02-21 01:10:34 -05:00
// Class not specified or invalid, use some default
if ( SpacePartitionClass . Get ( ) = = nullptr )
2022-04-26 14:39:26 -04:00
{
2022-02-21 01:10:34 -05:00
SpacePartitionClassName = FSoftClassPath ( USmartObjectHashGrid : : StaticClass ( ) ) ;
SpacePartitionClass = USmartObjectHashGrid : : StaticClass ( ) ;
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Using default class %s " ) , * SpacePartitionClassName . ToString ( ) ) ;
}
# if UE_ENABLE_DEBUG_DRAWING
// Spawn the rendering actor
2022-05-10 16:35:49 -04:00
if ( RenderingActor = = nullptr )
{
FActorSpawnParameters SpawnInfo ;
SpawnInfo . SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod : : AlwaysSpawn ;
RenderingActor = World . SpawnActor < ASmartObjectSubsystemRenderingActor > ( SpawnInfo ) ;
}
2022-02-21 01:10:34 -05:00
# endif // UE_ENABLE_DEBUG_DRAWING
2021-11-02 11:12:43 -04:00
// Register collections that were unable to register since they got loaded before the subsystem got created/initialized.
2021-09-28 13:33:00 -04:00
RegisterCollectionInstances ( ) ;
2021-11-02 11:12:43 -04:00
# if WITH_EDITOR
2022-12-08 11:50:57 -05:00
if ( ! World . IsGameWorld ( ) & & bAutoInitializeEditorInstances )
2021-11-02 11:12:43 -04:00
{
2022-11-24 14:53:52 -05:00
// calculating world bounds first since InitializeRuntime is using that data to create the USmartObjectSpacePartition
// instance. Note that we use the World-calculated bounds only for editor worlds, since Runtime SmartObjectContainer's
// bounds will rely on existing SmartObjectCollections. In editor we use world's size to not resize the
// USmartObjectSpacePartition with SO operations
2022-12-05 08:19:29 -05:00
SmartObjectContainer . SetBounds ( ComputeBounds ( World ) ) ;
2022-11-24 14:53:52 -05:00
InitializeRuntime ( ) ;
2021-11-02 11:12:43 -04:00
}
2022-02-21 01:10:34 -05:00
# endif // WITH_EDITOR
2021-09-28 13:33:00 -04:00
}
USmartObjectSubsystem * USmartObjectSubsystem : : GetCurrent ( const UWorld * World )
{
return UWorld : : GetSubsystem < USmartObjectSubsystem > ( World ) ;
}
2023-02-02 18:43:13 -05:00
FSmartObjectRuntime * USmartObjectSubsystem : : AddComponentToSimulation (
USmartObjectComponent & SmartObjectComponent ,
2023-07-18 08:42:51 -04:00
const FSmartObjectCollectionEntry & NewEntry
2023-02-02 18:43:13 -05:00
)
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
checkf ( SmartObjectComponent . GetDefinition ( ) ! = nullptr , TEXT ( " Shouldn't reach this point with an invalid definition asset " ) ) ;
2023-02-02 18:43:13 -05:00
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = AddCollectionEntryToSimulation ( NewEntry , * SmartObjectComponent . GetDefinition ( ) , & SmartObjectComponent ) ;
2023-02-02 18:43:13 -05:00
if ( SmartObjectRuntime ! = nullptr )
{
BindComponentToSimulationInternal ( SmartObjectComponent , * SmartObjectRuntime ) ;
}
return SmartObjectRuntime ;
2022-03-16 03:47:02 -04:00
}
2023-11-22 04:49:00 -05:00
bool USmartObjectSubsystem : : UpdateSmartObjectTransform ( const FSmartObjectHandle Handle , const FTransform & NewTransform )
{
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Handle ) ;
if ( ! SmartObjectRuntime )
{
return false ;
}
check ( SpacePartition ) ;
// Remove from old location in spatial partition.
if ( SmartObjectRuntime - > SpatialEntryData . IsValid ( ) )
{
SpacePartition - > Remove ( Handle , SmartObjectRuntime - > SpatialEntryData ) ;
}
// Set transform and register back to spatial partition.
SmartObjectRuntime - > SetTransform ( NewTransform ) ;
const FBox Bounds = SmartObjectRuntime - > GetDefinition ( ) . GetBounds ( ) . TransformBy ( NewTransform ) ;
SpacePartition - > Add ( Handle , Bounds , SmartObjectRuntime - > SpatialEntryData ) ;
# if UE_ENABLE_DEBUG_DRAWING
// Refresh debug draw
SmartObjectRuntime - > Bounds = Bounds ;
if ( RenderingActor ! = nullptr )
{
RenderingActor - > MarkComponentsRenderStateDirty ( ) ;
}
# endif // UE_ENABLE_DEBUG_DRAWING
return true ;
}
2024-02-08 13:21:26 -05:00
void USmartObjectSubsystem : : BindComponentToSimulationInternal ( USmartObjectComponent & SmartObjectComponent , FSmartObjectRuntime & SmartObjectRuntime ) const
2023-02-02 18:43:13 -05:00
{
2024-02-08 13:21:26 -05:00
ensureMsgf ( SmartObjectComponent . GetRegisteredHandle ( ) . IsValid ( ) , TEXT ( " %hs expects parameter SmartObjectComponent to be already registered. " ) , __FUNCTION__ ) ;
2023-02-02 18:43:13 -05:00
// It is possible that the component is already linked to the runtime instance when the collection entry was initially added.
2024-02-08 13:21:26 -05:00
const USmartObjectComponent * CurrentComponent = SmartObjectRuntime . GetOwnerComponent ( ) ;
ensureMsgf ( CurrentComponent = = nullptr | | CurrentComponent = = & SmartObjectComponent ,
2023-02-02 18:43:13 -05:00
TEXT ( " Different OwnerComponent (was %s) when binding SmartObjectComponent %s. This might indicate multiple objects using the same handle. " ) ,
2024-02-08 13:21:26 -05:00
* GetFullNameSafe ( CurrentComponent ) , * SmartObjectComponent . GetFullName ( ) ) ;
2023-02-02 18:43:13 -05:00
SmartObjectRuntime . OwnerComponent = & SmartObjectComponent ;
2024-02-08 13:21:26 -05:00
// Set the component's owner as the runtime owner if it is not already set (e.g. instance created by an instanced actor)
if ( ! SmartObjectRuntime . OwnerData . IsValid ( ) )
{
SmartObjectRuntime . OwnerData = FConstStructView : : Make ( FSmartObjectActorUserData ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
// Notify the component to bind to its runtime counterpart
2023-02-02 18:43:13 -05:00
SmartObjectComponent . OnRuntimeInstanceBound ( SmartObjectRuntime ) ;
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " '%s' using definition '%s' bound to simulation instance with handle '%s'. " ) ,
* UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ,
* SmartObjectComponent . GetDefinition ( ) - > GetPathName ( ) ,
* LexToString ( SmartObjectComponent . GetRegisteredHandle ( ) ) ) ;
2023-08-18 03:54:54 -04:00
if ( SmartObjectRuntime . OnEvent . IsBound ( ) )
{
FSmartObjectEventData Data ;
Data . SmartObjectHandle = SmartObjectRuntime . GetRegisteredHandle ( ) ;
Data . Reason = ESmartObjectChangeReason : : OnComponentBound ;
SmartObjectRuntime . OnEvent . Broadcast ( Data ) ;
}
2023-02-02 18:43:13 -05:00
}
2024-02-08 13:21:26 -05:00
void USmartObjectSubsystem : : UnbindComponentFromSimulationInternal ( USmartObjectComponent & SmartObjectComponent , FSmartObjectRuntime & SmartObjectRuntime ) const
2022-11-24 14:53:52 -05:00
{
2023-08-18 03:54:54 -04:00
if ( SmartObjectRuntime . OnEvent . IsBound ( ) )
{
FSmartObjectEventData Data ;
Data . SmartObjectHandle = SmartObjectRuntime . GetRegisteredHandle ( ) ;
Data . Reason = ESmartObjectChangeReason : : OnComponentUnbound ;
SmartObjectRuntime . OnEvent . Broadcast ( Data ) ;
}
2023-02-02 18:43:13 -05:00
SmartObjectComponent . OnRuntimeInstanceUnbound ( SmartObjectRuntime ) ;
2023-01-18 10:52:51 -05:00
SmartObjectRuntime . OwnerComponent = nullptr ;
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " '%s' unbound from simulation instance '%s'. " ) ,
* GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ,
* LexToString ( SmartObjectRuntime . GetRegisteredHandle ( ) ) ) ;
2022-11-24 14:53:52 -05:00
}
2023-02-02 18:43:13 -05:00
FSmartObjectRuntime * USmartObjectSubsystem : : AddCollectionEntryToSimulation (
const FSmartObjectCollectionEntry & Entry ,
const USmartObjectDefinition & Definition ,
2023-07-18 08:42:51 -04:00
USmartObjectComponent * OwnerComponent
2023-02-02 18:43:13 -05:00
)
2022-03-16 03:47:02 -04:00
{
const FSmartObjectHandle Handle = Entry . GetHandle ( ) ;
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Creating SmartObject using handle '%s' from collection entry using definition '%s'%s. " ) ,
* LexToString ( Handle ) ,
* Definition . GetName ( ) ,
( OwnerComponent ! = nullptr ) ? * FString : : Printf ( TEXT ( " for '%s' " ) , * GetNameSafe ( OwnerComponent - > GetOwner ( ) ) ) : TEXT ( " " ) ) ;
2022-03-16 03:47:02 -04:00
2024-02-09 07:34:22 -05:00
FSmartObjectRuntime * Runtime = CreateRuntimeInstance ( Handle , Definition , Entry . GetBounds ( ) , OwnerComponent ) ;
2024-02-08 13:21:26 -05:00
if ( Runtime ! = nullptr )
{
Runtime - > SetTransform ( Entry . GetTransform ( ) ) ;
Runtime - > Tags = Entry . GetTags ( ) ;
}
return Runtime ;
}
2024-02-09 07:34:22 -05:00
FSmartObjectRuntime * USmartObjectSubsystem : : CreateRuntimeInstance ( const FSmartObjectHandle Handle , const USmartObjectDefinition & Definition , const FBox Bounds , USmartObjectComponent * OwnerComponent )
2024-02-08 13:21:26 -05:00
{
2024-03-25 14:42:27 -04:00
ensure ( IsInGameThread ( ) | | IsInParallelGameThread ( ) ) ;
2022-01-31 18:52:49 -05:00
if ( ! ensureMsgf ( Handle . IsValid ( ) , TEXT ( " SmartObject needs a valid Handle to be added to the simulation " ) ) )
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
return nullptr ;
2021-09-28 13:33:00 -04:00
}
2024-02-08 13:21:26 -05:00
if ( ! ensureMsgf ( RuntimeSmartObjects . Find ( Handle ) = = nullptr , TEXT ( " Handle '%s' already registered in runtime simulation " ) , * LexToString ( Handle ) ) )
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
return nullptr ;
2021-09-28 13:33:00 -04:00
}
2022-01-31 18:52:49 -05:00
FSmartObjectRuntime & Runtime = RuntimeSmartObjects . Emplace ( Handle , FSmartObjectRuntime ( Definition ) ) ;
2022-03-02 13:25:37 -05:00
Runtime . SetRegisteredHandle ( Handle ) ;
2024-02-09 07:34:22 -05:00
Runtime . OwnerComponent = OwnerComponent ;
2021-09-28 13:33:00 -04:00
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
Runtime . Bounds = Bounds ;
# endif
2022-11-17 07:44:24 -05:00
FWorldConditionContextData ConditionContextData ( * Definition . GetWorldConditionSchema ( ) ) ;
2023-01-18 10:52:51 -05:00
SetupConditionContextCommonData ( ConditionContextData , Runtime ) ;
2022-11-23 09:11:13 -05:00
2023-01-18 10:52:51 -05:00
// Always initialize state (handles empty conditions)
Runtime . PreconditionState . Initialize ( * this , Definition . GetPreconditions ( ) ) ;
2024-03-25 14:42:27 -04:00
// Activate preconditions only if associated actor is available, otherwise we wait on hydration since
// many world conditions relies on actor at the moment.
const bool bActivateConditions = Runtime . GetOwnerActor ( ETrySpawnActorIfDehydrated : : No ) ! = nullptr ;
if ( bActivateConditions )
2023-01-18 10:52:51 -05:00
{
2024-03-25 14:42:27 -04:00
ActivateObjectPreconditions ( ConditionContextData , Runtime ) ;
2023-01-18 10:52:51 -05:00
}
2022-01-19 14:16:16 -05:00
// Create runtime data and entity for each slot
2023-07-18 08:42:51 -04:00
Runtime . Slots . Reserve ( Definition . GetSlots ( ) . Num ( ) ) ;
2024-03-25 14:42:27 -04:00
int32 SlotIndex = 0 ;
2022-02-03 14:03:16 -05:00
for ( const FSmartObjectSlotDefinition & SlotDefinition : Definition . GetSlots ( ) )
2022-01-19 14:16:16 -05:00
{
2023-07-18 08:42:51 -04:00
FSmartObjectRuntimeSlot & Slot = Runtime . Slots . AddDefaulted_GetRef ( ) ;
2022-01-19 14:16:16 -05:00
2023-01-18 10:52:51 -05:00
// Setup initial state from slot definition and current object state
2023-07-18 08:42:51 -04:00
Slot . Offset = SlotDefinition . Offset ;
Slot . Rotation = SlotDefinition . Rotation ;
2023-01-18 10:52:51 -05:00
Slot . bSlotEnabled = SlotDefinition . bEnabled ;
2022-11-01 15:11:25 -04:00
Slot . Tags = SlotDefinition . RuntimeTags ;
2023-07-18 08:42:51 -04:00
Slot . bObjectEnabled = Runtime . IsEnabled ( ) ;
2022-11-17 07:44:24 -05:00
2023-01-18 10:52:51 -05:00
// Always initialize state (handles empty conditions)
Slot . PreconditionState . Initialize ( * this , SlotDefinition . SelectionPreconditions ) ;
2023-01-18 01:06:45 -05:00
2024-03-25 14:42:27 -04:00
if ( bActivateConditions )
2023-01-18 10:52:51 -05:00
{
2024-03-25 14:42:27 -04:00
ActivateSlotPreconditions ( ConditionContextData , Slot , FSmartObjectSlotHandle ( Handle , SlotIndex ) ) ;
2022-11-17 07:44:24 -05:00
}
2022-11-01 15:11:25 -04:00
2022-02-03 14:03:16 -05:00
SlotIndex + + ;
}
2021-09-28 13:33:00 -04:00
2022-02-21 01:10:34 -05:00
// Insert to the spatial representation structure and store associated data
checkfSlow ( SpacePartition ! = nullptr , TEXT ( " Space partition is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
2023-11-22 04:49:00 -05:00
SpacePartition - > Add ( Handle , Bounds , Runtime . SpatialEntryData ) ;
2022-03-16 03:47:02 -04:00
2023-08-10 07:02:20 -04:00
// Notify that the object became in use.
if ( Runtime . OnEvent . IsBound ( ) )
{
FSmartObjectEventData Data ;
Data . SmartObjectHandle = Runtime . GetRegisteredHandle ( ) ;
Data . Reason = ESmartObjectChangeReason : : OnObjectEnabled ;
Runtime . OnEvent . Broadcast ( Data ) ;
}
2022-03-16 03:47:02 -04:00
return & Runtime ;
2021-09-28 13:33:00 -04:00
}
2024-02-08 13:21:26 -05:00
bool USmartObjectSubsystem : : RemoveRuntimeInstanceFromSimulation ( FSmartObjectRuntime & SmartObjectRuntime , USmartObjectComponent * SmartObjectComponent )
2021-11-02 11:12:43 -04:00
{
2024-02-08 13:21:26 -05:00
const FSmartObjectHandle Handle = SmartObjectRuntime . GetRegisteredHandle ( ) ;
2021-11-02 11:12:43 -04:00
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Removing SmartObject '%s' using definition '%s' from runtime simulation%s. " ) ,
* LexToString ( Handle ) ,
* SmartObjectRuntime . GetDefinition ( ) . GetName ( ) ,
( SmartObjectComponent ! = nullptr ) ? * FString : : Printf ( TEXT ( " for '%s' " ) , * GetNameSafe ( SmartObjectComponent - > GetOwner ( ) ) ) : TEXT ( " " ) ) ;
2021-09-28 13:33:00 -04:00
2023-05-26 12:48:38 -04:00
if ( SmartObjectComponent ! = nullptr )
{
2024-02-08 13:21:26 -05:00
UnbindComponentFromSimulationInternal ( * SmartObjectComponent , SmartObjectRuntime ) ;
2023-05-26 12:48:38 -04:00
}
2024-02-08 13:21:26 -05:00
DestroyRuntimeInstanceInternal ( Handle , SmartObjectRuntime ) ;
2022-10-26 19:34:40 -04:00
// Remove object runtime data
RuntimeSmartObjects . Remove ( Handle ) ;
return true ;
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : DestroyRuntimeInstanceInternal (
const FSmartObjectHandle Handle ,
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime & SmartObjectRuntime
2023-02-02 18:43:13 -05:00
)
2022-10-26 19:34:40 -04:00
{
2022-01-26 17:33:02 -05:00
// Abort everything before removing since abort flow may require access to runtime data
2023-07-18 08:42:51 -04:00
AbortAll ( Handle , SmartObjectRuntime ) ;
2022-01-26 17:33:02 -05:00
2023-08-10 07:02:20 -04:00
// Notify that the object is not in use anymore.
if ( SmartObjectRuntime . OnEvent . IsBound ( ) )
{
FSmartObjectEventData Data ;
Data . SmartObjectHandle = SmartObjectRuntime . GetRegisteredHandle ( ) ;
Data . Reason = ESmartObjectChangeReason : : OnObjectDisabled ;
SmartObjectRuntime . OnEvent . Broadcast ( Data ) ;
}
2022-02-21 01:10:34 -05:00
// Remove from space partition
checkfSlow ( SpacePartition ! = nullptr , TEXT ( " Space partition is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
2022-10-26 19:34:40 -04:00
SpacePartition - > Remove ( Handle , SmartObjectRuntime . SpatialEntryData ) ;
2022-01-19 14:16:16 -05:00
2024-03-25 14:42:27 -04:00
if ( SmartObjectRuntime . PreconditionState . AreConditionsActivated ( ) )
2022-01-19 14:16:16 -05:00
{
2024-03-25 14:42:27 -04:00
FWorldConditionContextData ConditionContextData ( * SmartObjectRuntime . GetDefinition ( ) . GetWorldConditionSchema ( ) ) ;
SetupConditionContextCommonData ( ConditionContextData , SmartObjectRuntime ) ;
2023-01-18 01:06:45 -05:00
2024-03-25 14:42:27 -04:00
// Deactivate object and slot Preconditions
const FWorldConditionContext ObjectContext ( SmartObjectRuntime . PreconditionState , ConditionContextData ) ;
ObjectContext . Deactivate ( ) ;
const USmartObjectWorldConditionSchema * DefaultWorldConditionSchema = GetDefault < USmartObjectWorldConditionSchema > ( ) ;
for ( TConstEnumerateRef < FSmartObjectRuntimeSlot > RuntimeSlot : EnumerateRange ( SmartObjectRuntime . Slots ) )
{
const FSmartObjectSlotHandle SlotHandle ( Handle , RuntimeSlot . GetIndex ( ) ) ;
ensureMsgf ( ConditionContextData . SetContextData ( DefaultWorldConditionSchema - > GetSlotHandleRef ( ) , & SlotHandle ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::SlotHandleRef to be valid. " ) ) ;
// Deactivate slot Preconditions (if successfully initialized)
const FWorldConditionContext SlotContext ( RuntimeSlot - > PreconditionState , ConditionContextData ) ;
SlotContext . Deactivate ( ) ;
}
2022-01-19 14:16:16 -05:00
}
2021-09-28 13:33:00 -04:00
}
2022-10-26 19:34:40 -04:00
bool USmartObjectSubsystem : : RemoveCollectionEntryFromSimulation ( const FSmartObjectCollectionEntry & Entry )
2021-09-28 13:33:00 -04:00
{
2024-02-08 13:21:26 -05:00
return DestroySmartObject ( Entry . GetHandle ( ) ) ;
2021-11-02 11:12:43 -04:00
}
2023-05-26 12:48:38 -04:00
void USmartObjectSubsystem : : RemoveComponentFromSimulation ( USmartObjectComponent & SmartObjectComponent )
2021-11-02 11:12:43 -04:00
{
2024-02-08 13:21:26 -05:00
const FSmartObjectHandle Handle = SmartObjectComponent . GetRegisteredHandle ( ) ;
if ( FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Handle ) )
2022-10-26 19:34:40 -04:00
{
2024-02-08 13:21:26 -05:00
if ( RemoveRuntimeInstanceFromSimulation ( * SmartObjectRuntime , & SmartObjectComponent ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " %hs call succeeded for %s " ) , __FUNCTION__ , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
else
{
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " %hs call failed for %s " ) , __FUNCTION__ , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
2022-10-26 19:34:40 -04:00
}
else
{
2024-02-08 13:21:26 -05:00
# if WITH_SMARTOBJECT_DEBUG
ensureMsgf ( false , TEXT ( " RemoveComponentFromSimulation is an internal call and should only be used for objects still part of the simulation " ) ) ;
# endif // WITH_SMARTOBJECT_DEBUG
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " %hs called with %s handle and no corresponding SmartObjectRuntime " )
, __FUNCTION__
, Handle . IsValid ( ) ? * FString : : Printf ( TEXT ( " a VALID '%s' " ) , * LexToString ( Handle ) ) : TEXT ( " an INVALID " ) ) ;
2022-10-26 19:34:40 -04:00
}
2021-11-02 11:12:43 -04:00
}
2023-07-18 08:42:51 -04:00
void USmartObjectSubsystem : : AbortAll ( const FSmartObjectHandle Handle , FSmartObjectRuntime & SmartObjectRuntime ) const
2022-03-02 13:25:37 -05:00
{
2023-07-18 08:42:51 -04:00
for ( TEnumerateRef < FSmartObjectRuntimeSlot > RuntimeSlot : EnumerateRange ( SmartObjectRuntime . Slots ) )
2022-03-02 13:25:37 -05:00
{
2023-07-18 08:42:51 -04:00
const FSmartObjectSlotHandle SlotHandle ( Handle , RuntimeSlot . GetIndex ( ) ) ;
switch ( RuntimeSlot - > State )
2022-03-02 13:25:37 -05:00
{
case ESmartObjectSlotState : : Claimed :
case ESmartObjectSlotState : : Occupied :
{
2023-07-18 08:42:51 -04:00
const FSmartObjectClaimHandle ClaimHandle ( SmartObjectRuntime . GetRegisteredHandle ( ) , SlotHandle , RuntimeSlot - > User ) ;
2023-02-02 18:43:13 -05:00
// Keep user data to be used as payload in the notification event
// since it will be released by the following call to Slot.Release
2023-07-18 08:42:51 -04:00
const FInstancedStruct Payload ( MoveTemp ( RuntimeSlot - > UserData ) ) ;
if ( RuntimeSlot - > Release ( ClaimHandle , /* bAborted */ true ) )
2022-11-01 15:11:25 -04:00
{
2023-07-18 08:42:51 -04:00
OnSlotChanged ( SmartObjectRuntime , * RuntimeSlot , SlotHandle , ESmartObjectChangeReason : : OnReleased , Payload ) ;
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Slot %s released by an abort " ) , * LexToString ( ClaimHandle . SlotHandle ) ) ;
2024-02-08 13:21:26 -05:00
UE_VLOG_LOCATION ( this , LogSmartObject , Display , SmartObjectRuntime . Transform . TransformPosition ( FVector ( RuntimeSlot - > Offset ) ) , /*Radius*/ 50.f , FColor : : Red , TEXT ( " Released by abort " ) ) ;
2022-11-01 15:11:25 -04:00
}
2022-03-02 13:25:37 -05:00
break ;
}
case ESmartObjectSlotState : : Free : // falling through on purpose
2022-03-16 03:47:02 -04:00
default :
2023-07-18 08:42:51 -04:00
UE_CVLOG_UELOG ( RuntimeSlot - > User . IsValid ( ) , this , LogSmartObject , Warning ,
2024-02-08 13:21:26 -05:00
TEXT ( " SmartObject '%s' using definition '%s' used by %s while the slot it's assigned to is not marked Claimed nor Occupied " ) ,
* LexToString ( Handle ) ,
2022-03-16 03:47:02 -04:00
* LexToString ( SmartObjectRuntime . GetDefinition ( ) ) ,
2023-07-18 08:42:51 -04:00
* LexToString ( RuntimeSlot - > User ) ) ;
2022-03-02 13:25:37 -05:00
break ;
}
2023-07-18 08:42:51 -04:00
RuntimeSlot - > State = ESmartObjectSlotState : : Free ;
2022-03-02 13:25:37 -05:00
}
}
2021-11-02 11:12:43 -04:00
bool USmartObjectSubsystem : : RegisterSmartObject ( USmartObjectComponent & SmartObjectComponent )
2022-03-30 15:13:42 -04:00
{
2023-08-16 14:15:36 -04:00
const USmartObjectDefinition * Definition = SmartObjectComponent . GetDefinition ( ) ;
if ( Definition = = nullptr )
2022-10-13 02:49:16 -04:00
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Attempting to register '%s' while its DefinitionAsset is not set. Bailing out. " ) ,
* UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ) ;
2022-10-13 02:49:16 -04:00
return false ;
}
2023-08-16 14:15:36 -04:00
TOptional < bool > bIsValid = Definition - > IsValid ( ) ;
2022-11-04 06:56:50 -04:00
if ( bIsValid . IsSet ( ) = = false )
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Attempting to register '%s' while its DefinitionAsset has not been Validated. Validating now. " ) ,
* UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ) ;
2023-08-16 14:15:36 -04:00
bIsValid = Definition - > Validate ( ) ;
2022-11-04 06:56:50 -04:00
}
2023-01-23 18:48:38 -05:00
if ( bIsValid . GetValue ( ) = = false )
2022-11-04 03:35:03 -04:00
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Attempting to register '%s' while its DefinitionAsset fails validation test. Bailing out. "
" Resave asset '%s' to see the errors and fix the problem. " ) ,
* UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ,
* GetPathNameSafe ( Definition ) ) ;
2023-08-16 14:15:36 -04:00
return false ;
}
if ( Definition - > GetSlots ( ) . IsEmpty ( ) )
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Attempting to register '%s' while its DefinitionAsset doesn't contain any slots. Bailing out. "
" Resave asset '%s' to see the errors and fix the problem. " ) ,
* UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ,
* GetPathNameSafe ( Definition ) ) ;
2022-11-04 03:35:03 -04:00
return false ;
}
2024-02-08 13:21:26 -05:00
if ( RegisteredSOComponents . Contains ( & SmartObjectComponent ) )
2022-03-30 15:13:42 -04:00
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Failed to register '%s'. Already registered " ) , * UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ) ;
return false ;
2022-03-30 15:13:42 -04:00
}
2022-03-16 03:47:02 -04:00
2022-12-05 08:19:29 -05:00
// until the runtime is initialized we're not ready to register SmartObject. We collect them in PendingSmartObjectRegistration
// and process them in InitializeRuntime call.
2022-11-24 14:53:52 -05:00
if ( bRuntimeInitialized )
2021-11-02 11:12:43 -04:00
{
2022-12-05 08:19:29 -05:00
if ( SmartObjectComponent . GetRegisteredHandle ( ) . IsValid ( ) )
2021-11-02 11:12:43 -04:00
{
2024-02-08 13:21:26 -05:00
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
if ( ensureAlwaysMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " Unable to bind '%s' with handle '%s' since an associated runtime doesn't exist. " ) ,
* SmartObjectComponent . GetFullName ( ) ,
* LexToString ( SmartObjectComponent . GetRegisteredHandle ( ) ) ) )
{
// Simply bind the newly available component to its active runtime instance
BindComponentToSimulationInternal ( SmartObjectComponent , * SmartObjectRuntime ) ;
}
2021-11-02 11:12:43 -04:00
}
2022-12-05 08:19:29 -05:00
else
2022-11-24 14:53:52 -05:00
{
2022-12-05 08:19:29 -05:00
bool bAlreadyInCollection = false ;
if ( const FSmartObjectCollectionEntry * Entry = SmartObjectContainer . AddSmartObject ( SmartObjectComponent , bAlreadyInCollection ) )
2022-11-24 14:53:52 -05:00
{
2022-12-07 13:05:23 -05:00
if ( bAlreadyInCollection )
2022-12-05 08:19:29 -05:00
{
2024-02-08 13:21:26 -05:00
SmartObjectComponent . SetRegisteredHandle ( Entry - > GetHandle ( ) , ESmartObjectRegistrationType : : BindToExistingInstance ) ;
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
if ( ensureAlwaysMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " Unable to bind '%s' with handle '%s' since an associated runtime doesn't exist. " ) ,
* SmartObjectComponent . GetFullName ( ) ,
* LexToString ( SmartObjectComponent . GetRegisteredHandle ( ) ) ) )
{
BindComponentToSimulationInternal ( SmartObjectComponent , * SmartObjectRuntime ) ;
}
2022-12-05 08:19:29 -05:00
}
2022-12-07 13:05:23 -05:00
else
{
2023-02-02 18:43:13 -05:00
SmartObjectComponent . SetRegisteredHandle ( Entry - > GetHandle ( ) , ESmartObjectRegistrationType : : Dynamic ) ;
2022-12-07 13:05:23 -05:00
AddComponentToSimulation ( SmartObjectComponent , * Entry ) ;
# if WITH_EDITOR
OnMainCollectionDirtied . Broadcast ( ) ;
2022-12-05 08:19:29 -05:00
# endif
2022-12-07 13:05:23 -05:00
}
2021-11-02 11:12:43 -04:00
}
}
2022-10-10 12:00:30 -04:00
ensureMsgf ( RegisteredSOComponents . Find ( & SmartObjectComponent ) = = INDEX_NONE
2024-02-08 13:21:26 -05:00
, TEXT ( " Adding '%s' to list of registered components, but it has already been added. Missing unregister call? " ) , * UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ) ;
2022-10-10 12:00:30 -04:00
RegisteredSOComponents . Add ( & SmartObjectComponent ) ;
2023-04-18 09:40:15 -04:00
# if UE_ENABLE_DEBUG_DRAWING
// Refresh debug draw
if ( RenderingActor ! = nullptr )
{
RenderingActor - > MarkComponentsRenderStateDirty ( ) ;
}
# endif // UE_ENABLE_DEBUG_DRAWING
2021-11-02 11:12:43 -04:00
}
2022-01-12 16:15:32 -05:00
else
{
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose ,
2024-02-08 13:21:26 -05:00
TEXT ( " '%s' not registered since InitializeRuntime has not been called yet. Storing component for registration during InitializeRuntime call. " )
, * UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ) ;
2022-11-24 14:53:52 -05:00
PendingSmartObjectRegistration . Add ( & SmartObjectComponent ) ;
2022-01-12 16:15:32 -05:00
}
2021-11-02 11:12:43 -04:00
2021-09-28 13:33:00 -04:00
return true ;
}
2024-02-08 13:21:26 -05:00
FSmartObjectHandle USmartObjectSubsystem : : CreateSmartObject ( const USmartObjectDefinition & Definition , const FTransform & Transform , const FConstStructView OwnerData )
{
if ( ! ensureMsgf ( bRuntimeInitialized , TEXT ( " " ) ) )
{
return FSmartObjectHandle : : Invalid ;
}
FSmartObjectHandle Handle = FSmartObjectHandleFactory : : CreateHandleForDynamicObject ( ) ;
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Creating SmartObject '%s' using definition '%s'. " ) ,
* LexToString ( Handle ) ,
* Definition . GetName ( ) ) ;
if ( FSmartObjectRuntime * Runtime = CreateRuntimeInstance ( Handle , Definition , Definition . GetBounds ( ) . TransformBy ( Transform ) ) )
{
Runtime - > SetTransform ( Transform ) ;
Runtime - > OwnerData = OwnerData ;
}
else
{
Handle . Invalidate ( ) ;
}
return Handle ;
}
bool USmartObjectSubsystem : : DestroySmartObject ( const FSmartObjectHandle Handle )
{
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Destroying SmartObject using handle '%s'. " ) , * LexToString ( Handle ) ) ;
if ( FSmartObjectRuntime * SmartObjectRuntime = GetRuntimeInstance ( Handle ) )
{
if ( USmartObjectComponent * Component = SmartObjectRuntime - > GetOwnerComponent ( ) )
{
RemoveSmartObject ( * Component ) ;
}
else
{
return RemoveRuntimeInstanceFromSimulation ( * SmartObjectRuntime ) ;
}
}
return false ;
}
2022-12-07 13:05:23 -05:00
bool USmartObjectSubsystem : : RemoveSmartObject ( USmartObjectComponent & SmartObjectComponent )
{
if ( RegisteredSOComponents . Contains ( & SmartObjectComponent ) )
{
return UnregisterSmartObjectInternal ( SmartObjectComponent , /*bDestroyRuntimeState=*/ true ) ;
}
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Failed to remove '%s' since it doesn't seem registered or has already been unregistered. " ) ,
* UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ) ;
2022-12-07 13:05:23 -05:00
return false ;
}
2021-11-02 11:12:43 -04:00
bool USmartObjectSubsystem : : UnregisterSmartObject ( USmartObjectComponent & SmartObjectComponent )
2022-03-30 15:13:42 -04:00
{
if ( RegisteredSOComponents . Contains ( & SmartObjectComponent ) )
{
2023-02-02 18:43:13 -05:00
return UnregisterSmartObjectInternal ( SmartObjectComponent ,
/*bDestroyRuntimeState=*/ SmartObjectComponent . GetRegistrationType ( ) = = ESmartObjectRegistrationType : : Dynamic ) ;
2022-03-30 15:13:42 -04:00
}
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Failed to unregister '%s' since it doesn't seem registered or has already been unregistered. " ) ,
* UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ) ;
2022-03-30 15:13:42 -04:00
return false ;
}
2022-12-07 13:05:23 -05:00
bool USmartObjectSubsystem : : UnregisterSmartObjectInternal ( USmartObjectComponent & SmartObjectComponent , const bool bDestroyRuntimeState )
2021-09-28 13:33:00 -04:00
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " Unregistering '%s' using definition '%s' associated to '%s'. " ) ,
* LexToString ( SmartObjectComponent . GetRegisteredHandle ( ) ) ,
* GetNameSafe ( SmartObjectComponent . GetDefinition ( ) ) ,
* GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
2022-03-16 03:47:02 -04:00
2022-11-24 14:53:52 -05:00
if ( bRuntimeInitialized )
2021-11-02 11:12:43 -04:00
{
2022-12-05 08:19:29 -05:00
ensure ( SmartObjectComponent . GetRegisteredHandle ( ) . IsValid ( ) ) ;
2021-11-02 11:12:43 -04:00
2023-05-26 12:48:38 -04:00
if ( SmartObjectComponent . IsBoundToSimulation ( ) )
2022-12-07 13:05:23 -05:00
{
2023-05-26 12:48:38 -04:00
if ( bDestroyRuntimeState )
{
RemoveComponentFromSimulation ( SmartObjectComponent ) ;
SmartObjectContainer . RemoveSmartObject ( SmartObjectComponent ) ;
}
// otherwise we keep all the runtime entries in place - those will be removed along with the collection that has added them
else
{
2024-02-08 13:21:26 -05:00
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
if ( ensureAlwaysMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " Unable to unbind '%s' using handle '%s' since an associated runtime doesn't exist. " ) ,
* SmartObjectComponent . GetFullName ( ) ,
* LexToString ( SmartObjectComponent . GetRegisteredHandle ( ) ) ) )
{
// Unbind the component from its associated runtime instance
UnbindComponentFromSimulationInternal ( SmartObjectComponent , * SmartObjectRuntime ) ;
}
2023-05-26 12:48:38 -04:00
}
2022-12-05 08:19:29 -05:00
}
2021-09-28 13:33:00 -04:00
2022-11-24 14:53:52 -05:00
RegisteredSOComponents . Remove ( & SmartObjectComponent ) ;
}
else
{
PendingSmartObjectRegistration . RemoveSingleSwap ( & SmartObjectComponent ) ;
}
2021-09-28 13:33:00 -04:00
return true ;
}
bool USmartObjectSubsystem : : RegisterSmartObjectActor ( const AActor & SmartObjectActor )
{
2022-03-30 15:13:42 -04:00
TArray < USmartObjectComponent * > Components ;
SmartObjectActor . GetComponents ( Components ) ;
UE_CVLOG_UELOG ( Components . Num ( ) = = 0 , & SmartObjectActor , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " Failed to register SmartObject components for '%s'. No components found. " ) , * SmartObjectActor . GetFullName ( SmartObjectActor . GetOwner ( ) ) ) ;
2021-09-28 13:33:00 -04:00
2022-03-30 15:13:42 -04:00
int32 NumSuccess = 0 ;
for ( USmartObjectComponent * SOComponent : Components )
{
if ( RegisterSmartObject ( * SOComponent ) )
{
NumSuccess + + ;
}
}
return NumSuccess > 0 & & NumSuccess = = Components . Num ( ) ;
2021-09-28 13:33:00 -04:00
}
bool USmartObjectSubsystem : : UnregisterSmartObjectActor ( const AActor & SmartObjectActor )
{
2022-03-30 15:13:42 -04:00
TArray < USmartObjectComponent * > Components ;
SmartObjectActor . GetComponents ( Components ) ;
UE_CVLOG_UELOG ( Components . Num ( ) = = 0 , & SmartObjectActor , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " Failed to unregister SmartObject components for '%s'. No components found. " ) , * SmartObjectActor . GetFullName ( SmartObjectActor . GetOwner ( ) ) ) ;
2021-09-28 13:33:00 -04:00
2022-03-30 15:13:42 -04:00
int32 NumSuccess = 0 ;
for ( USmartObjectComponent * SOComponent : Components )
{
if ( UnregisterSmartObject ( * SOComponent ) )
{
NumSuccess + + ;
}
}
return NumSuccess > 0 & & NumSuccess = = Components . Num ( ) ;
2021-09-28 13:33:00 -04:00
}
2022-12-07 13:05:23 -05:00
bool USmartObjectSubsystem : : RemoveSmartObjectActor ( const AActor & SmartObjectActor )
{
TArray < USmartObjectComponent * > Components ;
SmartObjectActor . GetComponents ( Components ) ;
UE_CVLOG_UELOG ( Components . Num ( ) = = 0 , & SmartObjectActor , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " Failed to remove SmartObject components runtime data for '%s'. No components found. " ) , * SmartObjectActor . GetFullName ( ) ) ;
2022-12-07 13:05:23 -05:00
int32 NumSuccess = 0 ;
for ( USmartObjectComponent * SOComponent : Components )
{
if ( RemoveSmartObject ( * SOComponent ) )
{
NumSuccess + + ;
}
}
return NumSuccess > 0 & & NumSuccess = = Components . Num ( ) ;
}
2023-01-18 10:52:51 -05:00
bool USmartObjectSubsystem : : SetSmartObjectActorEnabled ( const AActor & SmartObjectActor , const bool bEnabled )
{
TArray < USmartObjectComponent * > Components ;
SmartObjectActor . GetComponents ( Components ) ;
UE_CVLOG_UELOG ( Components . Num ( ) = = 0 , this , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " Failed to change SmartObject components enabled state for '%s'. No components found. " ) , * SmartObjectActor . GetFullName ( ) ) ;
2023-01-18 10:52:51 -05:00
int32 NumSuccess = 0 ;
for ( const USmartObjectComponent * SOComponent : Components )
{
2023-11-29 11:23:11 -05:00
if ( SetEnabledForReason ( SOComponent - > GetRegisteredHandle ( ) , UE : : SmartObject : : EnabledReason : : Gameplay , bEnabled ) )
2023-01-18 10:52:51 -05:00
{
NumSuccess + + ;
}
}
return NumSuccess > 0 & & NumSuccess = = Components . Num ( ) ;
}
bool USmartObjectSubsystem : : SetEnabled ( const FSmartObjectHandle Handle , const bool bEnabled )
{
2023-11-29 11:23:11 -05:00
return SetEnabledForReason ( Handle , UE : : SmartObject : : EnabledReason : : Gameplay , bEnabled ) ;
}
bool USmartObjectSubsystem : : SetEnabledForReason ( const FSmartObjectHandle Handle , const FGameplayTag ReasonTag , const bool bEnabled )
{
if ( ! ensureMsgf ( ReasonTag . IsValid ( ) , TEXT ( " All code paths are expected to provide a specific reason tag. " ) ) )
{
return false ;
}
2023-01-18 10:52:51 -05:00
FSmartObjectRuntime * SmartObjectRuntime = GetRuntimeInstance ( Handle ) ;
if ( SmartObjectRuntime = = nullptr )
{
UE_VLOG_UELOG ( this , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " Failed to change SmartObject enabled state for '%s'. No associated runtime instance found. " ) , * LexToString ( Handle ) ) ;
2023-01-18 10:52:51 -05:00
return false ;
}
2023-11-29 11:23:11 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose ,
TEXT ( " %s Tag %s " ) , bEnabled ? TEXT ( " Removing " ) : TEXT ( " Adding " ) , * ReasonTag . ToString ( ) ) ;
// Keep track of our previous state
const uint16 OldFlags = SmartObjectRuntime - > DisableFlags ;
const uint16 ReasonFlag = UE : : SmartObject : : GetMaskForEnabledReasonTag ( ReasonTag ) ;
const bool bWasEnabled = ! ( OldFlags & ReasonFlag ) ;
if ( bWasEnabled = = bEnabled )
2023-01-18 10:52:51 -05:00
{
// Already in the proper state, nothing to notify
2023-11-29 11:23:11 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log ,
TEXT ( " Object is already in the desired state for Tag %s. That might indicates assymetrical calls to SetEnabledForReason(..., ReasonX, true|false) " ) , * ReasonTag . ToString ( ) ) ;
2023-01-18 10:52:51 -05:00
return true ;
}
2023-11-29 11:23:11 -05:00
// Apply the mask
SmartObjectRuntime - > SetEnabled ( bEnabled , ReasonFlag ) ;
if ( ! OldFlags = = ! SmartObjectRuntime - > DisableFlags )
{
// Already in the proper state for other reasons, nothing to notify
return true ;
}
2023-01-18 10:52:51 -05:00
// Notify if needed
if ( SmartObjectRuntime - > OnEvent . IsBound ( ) )
{
FSmartObjectEventData Data ;
Data . SmartObjectHandle = SmartObjectRuntime - > GetRegisteredHandle ( ) ;
2023-02-02 18:43:13 -05:00
Data . Reason = bEnabled ? ESmartObjectChangeReason : : OnObjectEnabled : ESmartObjectChangeReason : : OnObjectDisabled ;
2023-01-18 10:52:51 -05:00
SmartObjectRuntime - > OnEvent . Broadcast ( Data ) ;
}
2023-11-29 11:23:11 -05:00
// Propagate object enabled state to slots and notify if needed.
2023-07-18 08:42:51 -04:00
for ( TEnumerateRef < FSmartObjectRuntimeSlot > RuntimeSlot : EnumerateRange ( SmartObjectRuntime - > Slots ) )
2023-01-18 10:52:51 -05:00
{
2023-07-18 08:42:51 -04:00
const FSmartObjectSlotHandle SlotHandle ( Handle , RuntimeSlot . GetIndex ( ) ) ;
2023-01-18 10:52:51 -05:00
// Using 'IsEnabled' to combine slot enable and smart object enable
2023-07-18 08:42:51 -04:00
const bool bSlotPreviousValue = RuntimeSlot - > IsEnabled ( ) ;
2023-01-18 10:52:51 -05:00
// Always set object enabled state even if combined result might not be affected
2023-07-18 08:42:51 -04:00
RuntimeSlot - > bObjectEnabled = bEnabled ;
2023-01-18 10:52:51 -05:00
// Using new combined value to detect changes
2023-07-18 08:42:51 -04:00
if ( RuntimeSlot - > IsEnabled ( ) ! = bSlotPreviousValue )
2023-01-18 10:52:51 -05:00
{
2023-07-18 08:42:51 -04:00
OnSlotChanged ( * SmartObjectRuntime , * RuntimeSlot , SlotHandle , RuntimeSlot - > IsEnabled ( ) ? ESmartObjectChangeReason : : OnSlotEnabled : ESmartObjectChangeReason : : OnSlotDisabled , RuntimeSlot - > UserData ) ;
2023-01-18 10:52:51 -05:00
}
}
return true ;
}
2023-02-28 10:24:32 -05:00
bool USmartObjectSubsystem : : IsEnabled ( const FSmartObjectHandle Handle ) const
{
const FSmartObjectRuntime * SmartObjectRuntime = GetRuntimeInstance ( Handle ) ;
UE_CVLOG_UELOG ( SmartObjectRuntime = = nullptr , this , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " Failed to get the SmartObject enabled state for '%s'. No associated runtime instance found. " ) , * LexToString ( Handle ) ) ;
2023-02-28 10:24:32 -05:00
2023-11-29 11:23:11 -05:00
return SmartObjectRuntime & & SmartObjectRuntime - > IsEnabled ( ) ;
}
bool USmartObjectSubsystem : : IsEnabledForReason ( const FSmartObjectHandle Handle , const FGameplayTag ReasonTag ) const
{
const FSmartObjectRuntime * SmartObjectRuntime = GetRuntimeInstance ( Handle ) ;
UE_CVLOG_UELOG ( SmartObjectRuntime = = nullptr , this , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " Failed to get the SmartObject enabled state for '%s'. No associated runtime instance found. " ) , * LexToString ( Handle ) ) ;
2023-11-29 11:23:11 -05:00
return SmartObjectRuntime & & SmartObjectRuntime - > IsEnabledForReason ( ReasonTag ) ;
2023-02-28 10:24:32 -05:00
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : SetupConditionContextCommonData ( FWorldConditionContextData & ContextData , const FSmartObjectRuntime & SmartObjectRuntime ) const
2023-01-18 10:52:51 -05:00
{
2024-02-08 13:21:26 -05:00
const USmartObjectWorldConditionSchema * DefaultSchema = GetDefault < USmartObjectWorldConditionSchema > ( ) ;
2023-02-02 18:43:13 -05:00
ensureMsgf ( ContextData . SetContextData ( DefaultSchema - > GetSmartObjectActorRef ( ) , SmartObjectRuntime . GetOwnerActor ( ) ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::GetSmartObjectActorRef to be valid. " ) ) ;
ensureMsgf ( ContextData . SetContextData ( DefaultSchema - > GetSmartObjectHandleRef ( ) , & SmartObjectRuntime . RegisteredHandle ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::SmartObjectHandleRef to be valid. " ) ) ;
ensureMsgf ( ContextData . SetContextData ( DefaultSchema - > GetSubsystemRef ( ) , this ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::SubsystemRef to be valid. " ) ) ;
}
void USmartObjectSubsystem : : BindPropertiesFromStruct ( FWorldConditionContextData & ContextData , const FConstStructView & UserData ) const
{
const UWorldConditionSchema * Schema = ContextData . GetSchema ( ) ;
check ( Schema ) ;
// @todo SO: could create a cache of layouts since user data types shouldn't vary much
// @todo SO: consider moving this into FWorldConditionContextData
2023-12-08 07:31:01 -05:00
for ( TFieldIterator < FProperty > It ( UserData . GetScriptStruct ( ) ) ; It ; + + It )
2023-02-02 18:43:13 -05:00
{
const FProperty * Property = * It ;
if ( const FStructProperty * StructProperty = CastField < FStructProperty > ( Property ) )
{
const FWorldConditionContextDataRef Ref = Schema - > GetContextDataRefByName ( Property - > GetFName ( ) , StructProperty - > Struct ) ;
if ( Ref . IsValid ( ) )
{
const FConstStructView StructView ( StructProperty - > Struct , UserData . GetMemory ( ) + Property - > GetOffset_ForInternal ( ) ) ;
ContextData . SetContextData ( Ref , StructView ) ;
}
}
else if ( const FObjectPropertyBase * ObjectProperty = CastField < FObjectPropertyBase > ( Property ) )
{
const FWorldConditionContextDataRef Ref = Schema - > GetContextDataRefByName ( Property - > GetFName ( ) , ObjectProperty - > PropertyClass ) ;
if ( Ref . IsValid ( ) )
{
const UObject * Object = ObjectProperty - > GetObjectPropertyValue ( UserData . GetMemory ( ) + Property - > GetOffset_ForInternal ( ) ) ;
ContextData . SetContextData ( Ref , Object ) ;
}
}
}
2023-01-18 10:52:51 -05:00
}
2024-03-25 14:42:27 -04:00
bool USmartObjectSubsystem : : ActivateObjectPreconditions ( const FWorldConditionContextData & ContextData , const FSmartObjectRuntime & SmartObjectRuntime ) const
{
if ( SmartObjectRuntime . PreconditionState . GetNumConditions ( ) = = 0 )
{
// Nothing to activate is considered a success
2024-03-25 16:38:26 -04:00
SmartObjectRuntime . PreconditionState . SetConditionsActivated ( true ) ;
2024-03-25 14:42:27 -04:00
return true ;
}
const FWorldConditionContext ObjectContext ( SmartObjectRuntime . PreconditionState , ContextData ) ;
if ( ! ObjectContext . Activate ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " Failed to activate Preconditions on SmartObject '%s'. " ) ,
* LexToString ( SmartObjectRuntime . GetRegisteredHandle ( ) ) ) ;
return false ;
}
return true ;
}
bool USmartObjectSubsystem : : ActivateSlotPreconditions ( FWorldConditionContextData & ContextData , const FSmartObjectRuntimeSlot & Slot , const FSmartObjectSlotHandle SlotHandle ) const
{
2024-03-25 15:21:45 -04:00
if ( Slot . PreconditionState . GetNumConditions ( ) = = 0 )
2024-03-25 14:42:27 -04:00
{
// Nothing to activate is considered a success
2024-03-25 16:38:26 -04:00
Slot . PreconditionState . SetConditionsActivated ( true ) ;
2024-03-25 14:42:27 -04:00
return true ;
}
// Activate slot Preconditions if any
ensureMsgf ( ContextData . SetContextData ( CastChecked < const USmartObjectWorldConditionSchema > ( ContextData . GetSchema ( ) ) - > GetSlotHandleRef ( ) , & SlotHandle ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::SlotHandleRef to be valid. " ) ) ;
const FWorldConditionContext SlotContext ( Slot . PreconditionState , ContextData ) ;
if ( ! SlotContext . Activate ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Error ,
TEXT ( " Failed to activate Preconditions on SmartObject '%s' slot '%s'. " ) , * LexToString ( SlotHandle . GetSmartObjectHandle ( ) ) , * LexToString ( SlotHandle ) ) ;
return false ;
}
return true ;
}
bool USmartObjectSubsystem : : TryActivatePreconditions ( const FSmartObjectRuntime & SmartObjectRuntime ) const
{
if ( SmartObjectRuntime . PreconditionState . AreConditionsActivated ( ) )
{
return true ;
}
if ( ! SmartObjectRuntime . ResolveOwnerActor ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Preconditions for owning SmartObject '%s' can't be activated: no owner actor " ) ,
* LexToString ( SmartObjectRuntime . GetRegisteredHandle ( ) ) ) ;
return false ;
}
FWorldConditionContextData ContextData ( * SmartObjectRuntime . GetDefinition ( ) . GetWorldConditionSchema ( ) ) ;
SetupConditionContextCommonData ( ContextData , SmartObjectRuntime ) ;
if ( ! ActivateObjectPreconditions ( ContextData , SmartObjectRuntime ) )
{
// No need to continue with slot preconditions, we already failed. Errors are reported by ActivateObjectPreconditions.
return false ;
}
int32 SlotIndex = 0 ;
for ( const FSmartObjectRuntimeSlot & Slot : SmartObjectRuntime . Slots )
{
if ( ! ActivateSlotPreconditions ( ContextData , Slot , FSmartObjectSlotHandle ( SmartObjectRuntime . GetRegisteredHandle ( ) , SlotIndex ) ) )
{
// No need to continue with other slots preconditions, we already failed. Errors are reported by ActivateSlotPreconditions.
return false ;
}
SlotIndex + + ;
}
return true ;
}
2023-01-18 10:52:51 -05:00
bool USmartObjectSubsystem : : EvaluateObjectConditions ( const FWorldConditionContextData & ConditionContextData , const FSmartObjectRuntime & SmartObjectRuntime ) const
{
// Evaluate object conditions. Note that unsuccessfully initialized conditions is supported (i.e. error during activation)
2023-09-05 11:29:58 -04:00
// We only want to evaluate the world condition on the server because, even if a client evaluates a false positive world condition,
// the server will reconcile that failure when the replication data gets updated anyway. At the moment it isn't worth the cost
// of replicating the world condition across clients to make it work.
// The world condition context's FWorldConditionQueryState will never be initialized on the client (bIsInitialized) will always be false
// because FWorldConditionQueryState::InitializeInternal is always going to be called with a null InSharedDefinition param.
2024-03-25 14:42:27 -04:00
if ( ! IsRunningOnServer ( ) | | SmartObjectRuntime . PreconditionState . GetNumConditions ( ) = = 0 )
2023-01-18 10:52:51 -05:00
{
2024-03-25 14:42:27 -04:00
return true ;
}
// Preconditions activation might have been delayed for dehydrated actors
if ( ! TryActivatePreconditions ( SmartObjectRuntime ) )
{
// Errors are reported by TryActivatePreconditions.
return false ;
}
const FWorldConditionContext Context ( SmartObjectRuntime . PreconditionState , ConditionContextData ) ;
if ( ! Context . IsTrue ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Preconditions for owning SmartObject '%s' failed. " ) , * LexToString ( SmartObjectRuntime . GetRegisteredHandle ( ) ) ) ;
return false ;
2023-01-18 10:52:51 -05:00
}
return true ;
}
2023-02-02 18:43:13 -05:00
bool USmartObjectSubsystem : : EvaluateSlotConditions (
FWorldConditionContextData & ConditionContextData ,
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime & SmartObjectRuntime ,
const FSmartObjectSlotHandle SlotHandle
2023-02-02 18:43:13 -05:00
) const
2023-01-18 10:52:51 -05:00
{
2024-03-25 14:42:27 -04:00
FWorldConditionQueryState & QueryState = SmartObjectRuntime . Slots [ SlotHandle . GetSlotIndex ( ) ] . PreconditionState ;
if ( ! IsRunningOnServer ( ) | | QueryState . GetNumConditions ( ) = = 0 )
{
return true ;
}
// Preconditions activation might have been delayed for dehydrated actors
// We try activate also for slots since the object might not have preconditions so it didn't need to activate any.
if ( ! TryActivatePreconditions ( SmartObjectRuntime ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Preconditions for owning SmartObject '%s' can't be activated. " ) , * LexToString ( SmartObjectRuntime . GetRegisteredHandle ( ) ) ) ;
return false ;
}
2023-01-18 10:52:51 -05:00
// Add slot data to the context
const USmartObjectWorldConditionSchema * DefaultSchema = GetDefault < USmartObjectWorldConditionSchema > ( ) ;
2023-02-02 18:43:13 -05:00
ensureMsgf ( ConditionContextData . SetContextData ( DefaultSchema - > GetSlotHandleRef ( ) , & SlotHandle ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::SlotHandleRef to be valid. " ) ) ;
2023-01-18 10:52:51 -05:00
// Evaluate slot conditions. Note that unsuccessfully initialized conditions is supported (i.e. error during activation)
2024-03-25 14:42:27 -04:00
const FWorldConditionContext Context ( QueryState , ConditionContextData ) ;
2023-01-18 10:52:51 -05:00
if ( ! Context . IsTrue ( ) )
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " Preconditions for slot '%s' failed. " ) , * LexToString ( SlotHandle ) ) ;
2023-01-18 10:52:51 -05:00
return false ;
}
return true ;
}
2022-01-31 18:52:49 -05:00
FSmartObjectClaimHandle USmartObjectSubsystem : : Claim ( const FSmartObjectHandle Handle , const FSmartObjectRequestFilter & Filter )
2021-09-28 13:33:00 -04:00
{
2024-02-08 13:21:26 -05:00
const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( Handle , __FUNCTION__ ) ;
2022-03-16 03:47:02 -04:00
if ( SmartObjectRuntime = = nullptr )
2021-09-28 13:33:00 -04:00
{
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2022-03-02 13:25:37 -05:00
TArray < FSmartObjectSlotHandle > SlotHandles ;
2023-07-18 08:42:51 -04:00
FindSlots ( Handle , * SmartObjectRuntime , Filter , SlotHandles , { } ) ;
2022-03-02 13:25:37 -05:00
if ( SlotHandles . IsEmpty ( ) )
2021-09-28 13:33:00 -04:00
{
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2023-06-08 10:34:40 -04:00
return MarkSlotAsClaimed ( SlotHandles . Top ( ) , { } ) ;
2021-09-28 13:33:00 -04:00
}
2023-06-08 10:34:40 -04:00
FSmartObjectClaimHandle USmartObjectSubsystem : : MarkSlotAsClaimed ( const FSmartObjectSlotHandle SlotHandle , const FConstStructView UserData )
2023-11-30 02:26:33 -05:00
{
return MarkSlotAsClaimed ( SlotHandle , ESmartObjectClaimPriority : : Normal , UserData ) ;
}
FSmartObjectClaimHandle USmartObjectSubsystem : : MarkSlotAsClaimed ( const FSmartObjectSlotHandle SlotHandle , ESmartObjectClaimPriority ClaimPriority , const FConstStructView UserData )
2022-01-19 14:16:16 -05:00
{
2023-02-02 18:43:13 -05:00
if ( ! SlotHandle . IsValid ( ) )
2022-03-18 12:31:49 -04:00
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Claiming using an unset SmartObject slot handle. Returning invalid FSmartObjectClaimHandle. " ) ) ;
2022-03-18 12:31:49 -04:00
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( ! GetValidatedMutableRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2022-03-16 03:47:02 -04:00
{
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2023-01-18 10:52:51 -05:00
// Fast test to see if slot can be claimed (Parent smart object is enabled AND slot is free and enabled)
2023-11-30 02:26:33 -05:00
if ( ! Slot - > CanBeClaimed ( ClaimPriority ) )
2023-01-18 10:52:51 -05:00
{
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " Can't claim slot handle '%s' since it is, or its owning SmartObject '%s', disabled or not free. " ) , * LexToString ( SlotHandle ) , * LexToString ( SlotHandle . GetSmartObjectHandle ( ) ) ) ;
2023-01-18 10:52:51 -05:00
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2023-11-30 02:26:33 -05:00
// We're overriding a claim, notify current listeners about the release.
bool bIsClaimOverridden = false ;
if ( Slot - > GetState ( ) = = ESmartObjectSlotState : : Claimed )
{
const FInstancedStruct Payload ( MoveTemp ( Slot - > UserData ) ) ;
const FSmartObjectClaimHandle ExistingClaim ( SlotHandle . SmartObjectHandle , SlotHandle , Slot - > User ) ;
ensureMsgf ( Slot - > Release ( ExistingClaim , /*bAborted*/ true ) , TEXT ( " Expecting the release to always succeed, since the slot can be claimed based on earlier check. " ) ) ;
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Released using handle '%s' due to claim override " ) , * LexToString ( ExistingClaim ) ) ;
2023-11-30 02:26:33 -05:00
UE_VLOG_LOCATION ( this , LogSmartObject , Display , GetSlotLocation ( ExistingClaim ) . GetValue ( ) , 50.f , FColor : : White , TEXT ( " Released (Override) " ) ) ;
OnSlotChanged ( * SmartObjectRuntime , * Slot , ExistingClaim . SlotHandle , ESmartObjectChangeReason : : OnReleased , Payload ) ;
bIsClaimOverridden = true ;
}
2022-03-18 12:31:49 -04:00
const FSmartObjectUserHandle User ( NextFreeUserID + + ) ;
2023-11-30 02:26:33 -05:00
const bool bClaimed = Slot - > Claim ( User , ClaimPriority ) ;
2022-03-18 12:31:49 -04:00
2023-07-18 08:42:51 -04:00
const FSmartObjectClaimHandle ClaimHandle ( SlotHandle . GetSmartObjectHandle ( ) , SlotHandle , User ) ;
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Claim %s for handle '%s'. Slot State is '%s' " ) ,
2022-03-18 12:31:49 -04:00
bClaimed ? TEXT ( " SUCCEEDED " ) : TEXT ( " FAILED " ) ,
* LexToString ( ClaimHandle ) ,
2023-07-18 08:42:51 -04:00
* UEnum : : GetValueAsString ( Slot - > GetState ( ) ) ) ;
2023-11-30 02:26:33 -05:00
UE_CVLOG_LOCATION ( bClaimed , this , LogSmartObject , Display , GetSlotLocation ( ClaimHandle ) . GetValue ( ) , 50.f , FColor : : Yellow , TEXT ( " Claim %s " ) , bIsClaimOverridden ? TEXT ( " [Override] " ) : TEXT ( " " ) ) ;
2022-03-18 12:31:49 -04:00
if ( bClaimed )
2022-03-16 03:47:02 -04:00
{
2023-02-02 18:43:13 -05:00
Slot - > UserData = UserData ;
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle , ESmartObjectChangeReason : : OnClaimed , Slot - > UserData ) ;
2022-03-18 12:31:49 -04:00
return ClaimHandle ;
2021-09-28 13:33:00 -04:00
}
2022-01-19 14:16:16 -05:00
return FSmartObjectClaimHandle : : InvalidHandle ;
2021-09-28 13:33:00 -04:00
}
2023-11-30 02:26:33 -05:00
bool USmartObjectSubsystem : : CanBeClaimed ( const FSmartObjectSlotHandle SlotHandle , ESmartObjectClaimPriority ClaimPriority ) const
2022-09-01 09:03:19 -04:00
{
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
const FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2023-07-18 08:42:51 -04:00
{
2023-11-30 02:26:33 -05:00
return Slot - > CanBeClaimed ( ClaimPriority ) ;
2023-07-18 08:42:51 -04:00
}
return false ;
2022-09-01 09:03:19 -04:00
}
2022-03-18 12:31:49 -04:00
bool USmartObjectSubsystem : : IsSmartObjectValid ( const FSmartObjectHandle SmartObjectHandle ) const
{
return SmartObjectHandle . IsValid ( ) & & RuntimeSmartObjects . Find ( SmartObjectHandle ) ! = nullptr ;
}
bool USmartObjectSubsystem : : IsClaimedSmartObjectValid ( const FSmartObjectClaimHandle & ClaimHandle ) const
2022-03-16 03:47:02 -04:00
{
return ClaimHandle . IsValid ( ) & & RuntimeSmartObjects . Find ( ClaimHandle . SmartObjectHandle ) ! = nullptr ;
}
2024-02-08 13:21:26 -05:00
bool USmartObjectSubsystem : : IsSlotValidVerbose ( const FSmartObjectSlotHandle SlotHandle , const ANSICHAR * LogContext ) const
2022-03-16 03:47:02 -04:00
{
2022-03-18 12:31:49 -04:00
UE_CVLOG_UELOG ( ! SlotHandle . IsValid ( ) , this , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " %hs failed. SlotHandle is not set. " ) , LogContext ) ;
2022-03-18 12:31:49 -04:00
return IsSmartObjectSlotValid ( SlotHandle ) ;
2022-03-16 03:47:02 -04:00
}
2023-02-02 18:43:13 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : GetBehaviorDefinition (
const FSmartObjectClaimHandle & ClaimHandle ,
2023-04-21 16:47:40 -04:00
TSubclassOf < USmartObjectBehaviorDefinition > DefinitionClass
2023-02-02 18:43:13 -05:00
)
2022-01-26 17:33:02 -05:00
{
2024-02-08 13:21:26 -05:00
const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( ClaimHandle . SmartObjectHandle , __FUNCTION__ ) ;
2022-09-16 12:46:45 -04:00
return SmartObjectRuntime ! = nullptr ? GetBehaviorDefinition ( * SmartObjectRuntime , ClaimHandle . SlotHandle , DefinitionClass ) : nullptr ;
2022-01-26 17:33:02 -05:00
}
2023-02-02 18:43:13 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : GetBehaviorDefinitionByRequestResult (
const FSmartObjectRequestResult & RequestResult ,
2023-04-21 16:47:40 -04:00
TSubclassOf < USmartObjectBehaviorDefinition > DefinitionClass
2023-02-02 18:43:13 -05:00
)
2022-09-16 12:46:45 -04:00
{
2024-02-08 13:21:26 -05:00
const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( RequestResult . SmartObjectHandle , __FUNCTION__ ) ;
2022-09-16 12:46:45 -04:00
return SmartObjectRuntime ! = nullptr ? GetBehaviorDefinition ( * SmartObjectRuntime , RequestResult . SlotHandle , DefinitionClass ) : nullptr ;
}
2023-02-02 18:43:13 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : GetBehaviorDefinition (
const FSmartObjectRuntime & SmartObjectRuntime ,
const FSmartObjectSlotHandle SlotHandle ,
2023-04-21 16:47:40 -04:00
TSubclassOf < USmartObjectBehaviorDefinition > DefinitionClass
2023-02-02 18:43:13 -05:00
)
2022-01-26 17:33:02 -05:00
{
const USmartObjectDefinition & Definition = SmartObjectRuntime . GetDefinition ( ) ;
2023-07-18 08:42:51 -04:00
return Definition . GetBehaviorDefinition ( SlotHandle . GetSlotIndex ( ) , DefinitionClass ) ;
2022-01-26 17:33:02 -05:00
}
2023-06-08 10:34:40 -04:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : MarkSlotAsOccupied (
2023-02-02 18:43:13 -05:00
const FSmartObjectClaimHandle & ClaimHandle ,
2023-04-21 16:47:40 -04:00
TSubclassOf < USmartObjectBehaviorDefinition > DefinitionClass
2023-02-02 18:43:13 -05:00
)
2021-09-28 13:33:00 -04:00
{
2024-02-08 13:21:26 -05:00
FSmartObjectRuntime * SmartObjectRuntime = GetValidatedMutableRuntime ( ClaimHandle . SmartObjectHandle , __FUNCTION__ ) ;
2023-06-08 10:34:40 -04:00
return SmartObjectRuntime ! = nullptr ? MarkSlotAsOccupied ( * SmartObjectRuntime , ClaimHandle , DefinitionClass ) : nullptr ;
2021-09-28 13:33:00 -04:00
}
2023-06-08 10:34:40 -04:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : MarkSlotAsOccupied (
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime & SmartObjectRuntime ,
2023-02-02 18:43:13 -05:00
const FSmartObjectClaimHandle & ClaimHandle ,
2023-06-08 10:34:40 -04:00
const TSubclassOf < USmartObjectBehaviorDefinition > DefinitionClass
2023-02-02 18:43:13 -05:00
)
2021-09-28 13:33:00 -04:00
{
2022-03-18 12:31:49 -04:00
checkf ( ClaimHandle . IsValid ( ) , TEXT ( " This is an internal method that should only be called with an assigned claim handle " ) ) ;
2022-03-16 03:47:02 -04:00
2022-11-01 15:11:25 -04:00
if ( ! SmartObjectRuntime . IsEnabled ( ) )
2021-09-28 13:33:00 -04:00
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Can't use handle '%s' since associated object is disabled. " ) , * LexToString ( ClaimHandle ) ) ;
2021-09-28 13:33:00 -04:00
return nullptr ;
}
2022-09-16 12:46:45 -04:00
const USmartObjectBehaviorDefinition * BehaviorDefinition = GetBehaviorDefinition ( SmartObjectRuntime , ClaimHandle . SlotHandle , DefinitionClass ) ;
2022-01-19 14:16:16 -05:00
if ( BehaviorDefinition = = nullptr )
{
const UClass * ClassPtr = DefinitionClass . Get ( ) ;
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Unable to find a behavior definition of type '%s' in '%s' " ) ,
ClassPtr ! = nullptr ? * ClassPtr - > GetName ( ) : TEXT ( " Null " ) , * SmartObjectRuntime . GetDefinition ( ) . GetPathName ( ) ) ;
2022-01-19 14:16:16 -05:00
return nullptr ;
}
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Start using handle '%s' " ) , * LexToString ( ClaimHandle ) ) ;
2023-02-02 18:43:13 -05:00
UE_VLOG_LOCATION ( this , LogSmartObject , Display , GetSlotLocation ( ClaimHandle ) . GetValue ( ) , 50.f , FColor : : Green , TEXT ( " Use " ) ) ;
2022-01-19 14:16:16 -05:00
2023-07-18 08:42:51 -04:00
FSmartObjectRuntimeSlot & Slot = SmartObjectRuntime . Slots [ ClaimHandle . SlotHandle . GetSlotIndex ( ) ] ;
2022-01-19 14:16:16 -05:00
2022-11-01 15:11:25 -04:00
if ( ensureMsgf ( Slot . GetState ( ) = = ESmartObjectSlotState : : Claimed , TEXT ( " Should have been claimed first: %s " ) , * LexToString ( ClaimHandle ) ) & &
2024-02-08 13:21:26 -05:00
ensureMsgf ( Slot . User = = ClaimHandle . UserHandle , TEXT ( " Attempt to use slot '%s' from handle '%s' but already assigned to '%s' " ) ,
2022-11-01 15:11:25 -04:00
* LexToString ( Slot ) , * LexToString ( ClaimHandle ) , * LexToString ( Slot . User ) ) )
2022-01-19 14:16:16 -05:00
{
2022-11-01 15:11:25 -04:00
Slot . State = ESmartObjectSlotState : : Occupied ;
2023-02-02 18:43:13 -05:00
OnSlotChanged ( SmartObjectRuntime , Slot , ClaimHandle . SlotHandle , ESmartObjectChangeReason : : OnOccupied , Slot . UserData ) ;
2022-01-19 14:16:16 -05:00
return BehaviorDefinition ;
}
return nullptr ;
2021-09-28 13:33:00 -04:00
}
2023-06-08 10:34:40 -04:00
bool USmartObjectSubsystem : : MarkSlotAsFree ( const FSmartObjectClaimHandle & ClaimHandle )
2021-09-28 13:33:00 -04:00
{
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( ! GetValidatedMutableRuntimeAndSlot ( ClaimHandle . SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2021-09-28 13:33:00 -04:00
{
return false ;
}
2023-02-02 18:43:13 -05:00
// Keep user data to be used as payload in the notification event
// since it will be released by the following call to Slot.Release
const FInstancedStruct Payload ( MoveTemp ( Slot - > UserData ) ) ;
2022-11-01 15:11:25 -04:00
const bool bSuccess = Slot - > Release ( ClaimHandle , /*bAborted*/ false ) ;
if ( bSuccess )
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Released using handle '%s' " ) , * LexToString ( ClaimHandle ) ) ;
2023-02-02 18:43:13 -05:00
UE_VLOG_LOCATION ( this , LogSmartObject , Display , GetSlotLocation ( ClaimHandle ) . GetValue ( ) , 50.f , FColor : : White , TEXT ( " Released " ) ) ;
2023-07-18 08:42:51 -04:00
OnSlotChanged ( * SmartObjectRuntime , * Slot , ClaimHandle . SlotHandle , ESmartObjectChangeReason : : OnReleased , Payload ) ;
2022-11-01 15:11:25 -04:00
}
2021-09-28 13:33:00 -04:00
return bSuccess ;
}
2022-01-19 14:16:16 -05:00
ESmartObjectSlotState USmartObjectSubsystem : : GetSlotState ( const FSmartObjectSlotHandle SlotHandle ) const
2021-09-28 13:33:00 -04:00
{
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
const FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2023-07-18 08:42:51 -04:00
{
return Slot - > GetState ( ) ;
}
return ESmartObjectSlotState : : Invalid ;
2022-01-19 14:16:16 -05:00
}
2021-11-26 15:48:13 -05:00
bool USmartObjectSubsystem : : GetSlotLocation ( const FSmartObjectClaimHandle & ClaimHandle , FVector & OutSlotLocation ) const
{
const TOptional < FVector > OptionalLocation = GetSlotLocation ( ClaimHandle ) ;
OutSlotLocation = OptionalLocation . Get ( FVector : : ZeroVector ) ;
return OptionalLocation . IsSet ( ) ;
}
2022-03-18 12:31:49 -04:00
TOptional < FVector > USmartObjectSubsystem : : GetSlotLocation ( const FSmartObjectSlotHandle SlotHandle ) const
2021-09-28 13:33:00 -04:00
{
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
const FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2023-07-18 08:42:51 -04:00
{
return SmartObjectRuntime - > Transform . TransformPosition ( FVector ( Slot - > Offset ) ) ;
}
return { } ;
2022-03-18 12:31:49 -04:00
}
bool USmartObjectSubsystem : : GetSlotTransform ( const FSmartObjectClaimHandle & ClaimHandle , FTransform & OutSlotTransform ) const
{
const TOptional < FTransform > OptionalTransform = GetSlotTransform ( ClaimHandle ) ;
OutSlotTransform = OptionalTransform . Get ( FTransform : : Identity ) ;
return OptionalTransform . IsSet ( ) ;
2021-09-28 13:33:00 -04:00
}
2022-09-15 15:11:53 -04:00
bool USmartObjectSubsystem : : GetSlotTransformFromRequestResult ( const FSmartObjectRequestResult & RequestResult , FTransform & OutSlotTransform ) const
{
const TOptional < FTransform > OptionalTransform = GetSlotTransform ( RequestResult ) ;
OutSlotTransform = OptionalTransform . Get ( FTransform : : Identity ) ;
return OptionalTransform . IsSet ( ) ;
}
2022-01-19 14:16:16 -05:00
TOptional < FTransform > USmartObjectSubsystem : : GetSlotTransform ( const FSmartObjectSlotHandle SlotHandle ) const
2021-09-28 13:33:00 -04:00
{
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
const FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2021-09-28 13:33:00 -04:00
{
2023-07-18 08:42:51 -04:00
return Slot - > GetSlotWorldTransform ( SmartObjectRuntime - > Transform ) ;
2021-09-28 13:33:00 -04:00
}
2023-07-18 08:42:51 -04:00
return { } ;
2021-09-28 13:33:00 -04:00
}
2023-07-18 08:42:51 -04:00
FTransform USmartObjectSubsystem : : GetSlotTransformChecked ( const FSmartObjectSlotHandle SlotHandle ) const
2022-09-01 09:03:19 -04:00
{
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
const FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
verify ( GetValidatedRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) ) ;
2023-07-18 08:42:51 -04:00
return Slot - > GetSlotLocalTransform ( ) * SmartObjectRuntime - > Transform ;
}
2024-02-08 13:21:26 -05:00
bool USmartObjectSubsystem : : GetValidatedMutableRuntimeAndSlot ( const FSmartObjectSlotHandle SlotHandle , FSmartObjectRuntime * & OutSmartObjectRuntime , FSmartObjectRuntimeSlot * & OutSlot , const ANSICHAR * Context )
2023-07-18 08:42:51 -04:00
{
const FSmartObjectRuntime * ConstSmartObjectRuntime = nullptr ;
const FSmartObjectRuntimeSlot * ConstSlot = nullptr ;
if ( GetValidatedRuntimeAndSlot ( SlotHandle , ConstSmartObjectRuntime , ConstSlot , Context ) )
{
OutSmartObjectRuntime = const_cast < FSmartObjectRuntime * > ( ConstSmartObjectRuntime ) ;
OutSlot = const_cast < FSmartObjectRuntimeSlot * > ( ConstSlot ) ;
return true ;
}
OutSmartObjectRuntime = nullptr ;
OutSlot = nullptr ;
return false ;
}
2024-02-08 13:21:26 -05:00
bool USmartObjectSubsystem : : GetValidatedRuntimeAndSlot ( const FSmartObjectSlotHandle SlotHandle , const FSmartObjectRuntime * & OutSmartObjectRuntime , const FSmartObjectRuntimeSlot * & OutSlot , const ANSICHAR * Context ) const
2023-07-18 08:42:51 -04:00
{
if ( SlotHandle . IsValid ( ) )
{
if ( const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SlotHandle . GetSmartObjectHandle ( ) ) )
{
if ( SmartObjectRuntime - > Slots . IsValidIndex ( SlotHandle . GetSlotIndex ( ) ) )
{
OutSmartObjectRuntime = SmartObjectRuntime ;
OutSlot = SmartObjectRuntime ! = nullptr ? & SmartObjectRuntime - > Slots [ SlotHandle . GetSlotIndex ( ) ] : nullptr ;
return true ;
}
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " %hs Invalid slot index %d (%d slots). " ) , Context , SlotHandle . GetSlotIndex ( ) , SmartObjectRuntime - > Slots . Num ( ) ) ;
2023-07-18 08:42:51 -04:00
}
else
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " %hs failed using handle '%s'. SmartObject is no longer part of the simulation. " ) , Context , * LexToString ( SlotHandle ) ) ;
2023-07-18 08:42:51 -04:00
}
}
else
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " %hs failed. Handle is not set. " ) , Context ) ;
2023-07-18 08:42:51 -04:00
}
OutSmartObjectRuntime = nullptr ;
OutSlot = nullptr ;
return false ;
2022-09-01 09:03:19 -04:00
}
2024-02-08 13:21:26 -05:00
FSmartObjectRuntime * USmartObjectSubsystem : : GetValidatedMutableRuntime ( const FSmartObjectHandle Handle , const ANSICHAR * Context ) const
2022-03-16 03:47:02 -04:00
{
return const_cast < FSmartObjectRuntime * > ( GetValidatedRuntime ( Handle , Context ) ) ;
}
2024-02-08 13:21:26 -05:00
const FSmartObjectRuntime * USmartObjectSubsystem : : GetValidatedRuntime ( const FSmartObjectHandle Handle , const ANSICHAR * Context ) const
2022-03-16 03:47:02 -04:00
{
const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Handle ) ;
2024-02-08 13:21:26 -05:00
UE_CVLOG_UELOG ( ! Handle . IsValid ( ) , this , LogSmartObject , Log , TEXT ( " %hs failed. Handle is not set. " ) , Context ) ;
2022-03-18 12:31:49 -04:00
UE_CVLOG_UELOG ( Handle . IsValid ( ) & & SmartObjectRuntime = = nullptr , this , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " %hs failed using handle '%s'. SmartObject is no longer part of the simulation. " ) , Context , * LexToString ( Handle ) ) ;
2022-03-18 12:31:49 -04:00
2022-03-16 03:47:02 -04:00
return SmartObjectRuntime ;
}
2023-01-18 10:52:51 -05:00
FOnSmartObjectEvent * USmartObjectSubsystem : : GetEventDelegate ( const FSmartObjectHandle SmartObjectHandle )
{
2024-02-08 13:21:26 -05:00
if ( FSmartObjectRuntime * SmartObjectRuntime = GetValidatedMutableRuntime ( SmartObjectHandle , __FUNCTION__ ) )
2023-01-18 10:52:51 -05:00
{
return & SmartObjectRuntime - > GetMutableEventDelegate ( ) ;
}
return nullptr ;
}
2022-03-16 03:47:02 -04:00
const FGameplayTagContainer & USmartObjectSubsystem : : GetInstanceTags ( const FSmartObjectHandle Handle ) const
{
2024-02-08 13:21:26 -05:00
const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( Handle , __FUNCTION__ ) ;
2022-03-16 03:47:02 -04:00
return SmartObjectRuntime ! = nullptr ? SmartObjectRuntime - > GetTags ( ) : FGameplayTagContainer : : EmptyContainer ;
}
void USmartObjectSubsystem : : AddTagToInstance ( const FSmartObjectHandle Handle , const FGameplayTag & Tag )
{
2024-02-08 13:21:26 -05:00
if ( FSmartObjectRuntime * SmartObjectRuntime = GetValidatedMutableRuntime ( Handle , __FUNCTION__ ) )
2022-03-16 03:47:02 -04:00
{
AddTagToInstance ( * SmartObjectRuntime , Tag ) ;
}
}
void USmartObjectSubsystem : : RemoveTagFromInstance ( const FSmartObjectHandle Handle , const FGameplayTag & Tag )
{
2024-02-08 13:21:26 -05:00
if ( FSmartObjectRuntime * SmartObjectRuntime = GetValidatedMutableRuntime ( Handle , __FUNCTION__ ) )
2022-03-16 03:47:02 -04:00
{
RemoveTagFromInstance ( * SmartObjectRuntime , Tag ) ;
}
}
2022-11-01 15:11:25 -04:00
const FGameplayTagContainer & USmartObjectSubsystem : : GetSlotTags ( const FSmartObjectSlotHandle SlotHandle ) const
{
static const FGameplayTagContainer EmptyTags ;
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
const FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2022-11-01 15:11:25 -04:00
{
return Slot - > Tags ;
}
return EmptyTags ;
}
void USmartObjectSubsystem : : AddTagToSlot ( const FSmartObjectSlotHandle SlotHandle , const FGameplayTag & Tag )
{
if ( ! Tag . IsValid ( ) )
{
return ;
}
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedMutableRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2022-11-01 15:11:25 -04:00
{
if ( ! Slot - > Tags . HasTag ( Tag ) )
{
Slot - > Tags . AddTagFast ( Tag ) ;
2023-07-18 08:42:51 -04:00
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle , ESmartObjectChangeReason : : OnTagAdded , Slot - > GetUserData ( ) , Tag ) ;
2022-11-01 15:11:25 -04:00
}
}
}
bool USmartObjectSubsystem : : RemoveTagFromSlot ( const FSmartObjectSlotHandle SlotHandle , const FGameplayTag & Tag )
{
if ( ! Tag . IsValid ( ) )
{
return false ;
}
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedMutableRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2022-11-01 15:11:25 -04:00
{
if ( Slot - > Tags . RemoveTag ( Tag ) )
{
2023-07-18 08:42:51 -04:00
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle , ESmartObjectChangeReason : : OnTagRemoved , Slot - > GetUserData ( ) , Tag ) ;
2022-11-01 15:11:25 -04:00
return true ;
}
}
return false ;
}
2022-11-04 09:18:52 -04:00
bool USmartObjectSubsystem : : SetSlotEnabled ( const FSmartObjectSlotHandle SlotHandle , const bool bEnabled )
2022-11-01 15:11:25 -04:00
{
2022-11-04 09:18:52 -04:00
bool bPreviousValue = false ;
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedMutableRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2022-11-01 15:11:25 -04:00
{
2023-01-18 10:52:51 -05:00
// Using 'IsEnabled' that combines both slot and smart object enabled state
bPreviousValue = Slot - > IsEnabled ( ) ;
// Always set slot enabled state even if combined result might not be affected
Slot - > bSlotEnabled = bEnabled ;
// Using new combined value to detect changes
if ( Slot - > IsEnabled ( ) ! = bPreviousValue )
2022-11-01 15:11:25 -04:00
{
2023-07-18 08:42:51 -04:00
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle ,
Slot - > IsEnabled ( ) ? ESmartObjectChangeReason : : OnSlotEnabled : ESmartObjectChangeReason : : OnSlotDisabled , Slot - > UserData ) ;
2022-11-01 15:11:25 -04:00
}
}
2022-11-04 09:18:52 -04:00
return bPreviousValue ;
2022-11-01 15:11:25 -04:00
}
bool USmartObjectSubsystem : : SendSlotEvent ( const FSmartObjectSlotHandle SlotHandle , const FGameplayTag EventTag , const FConstStructView Payload )
{
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedMutableRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2022-11-01 15:11:25 -04:00
{
2023-02-02 18:43:13 -05:00
// Runtime slot lifetime is bound to the runtime smart object so it should always be available.
2023-07-18 08:42:51 -04:00
if ( SmartObjectRuntime - > GetEventDelegate ( ) . IsBound ( ) )
2022-11-01 15:11:25 -04:00
{
FSmartObjectEventData Data ;
2023-07-18 08:42:51 -04:00
Data . SmartObjectHandle = SlotHandle . GetSmartObjectHandle ( ) ;
2022-11-01 15:11:25 -04:00
Data . SlotHandle = SlotHandle ;
Data . Reason = ESmartObjectChangeReason : : OnEvent ;
Data . Tag = EventTag ;
Data . EventPayload = Payload ;
2023-07-18 08:42:51 -04:00
SmartObjectRuntime - > GetEventDelegate ( ) . Broadcast ( Data ) ;
2022-11-01 15:11:25 -04:00
return true ;
}
}
return false ;
}
2022-03-16 03:47:02 -04:00
void USmartObjectSubsystem : : AddTagToInstance ( FSmartObjectRuntime & SmartObjectRuntime , const FGameplayTag & Tag )
{
if ( ! SmartObjectRuntime . Tags . HasTag ( Tag ) )
{
SmartObjectRuntime . Tags . AddTagFast ( Tag ) ;
2023-01-18 10:52:51 -05:00
FSmartObjectEventData Data ;
Data . SmartObjectHandle = SmartObjectRuntime . GetRegisteredHandle ( ) ;
Data . Reason = ESmartObjectChangeReason : : OnTagAdded ;
Data . Tag = Tag ;
SmartObjectRuntime . OnEvent . Broadcast ( Data ) ;
2022-03-16 03:47:02 -04:00
}
}
void USmartObjectSubsystem : : RemoveTagFromInstance ( FSmartObjectRuntime & SmartObjectRuntime , const FGameplayTag & Tag )
{
if ( SmartObjectRuntime . Tags . RemoveTag ( Tag ) )
{
2023-01-18 10:52:51 -05:00
FSmartObjectEventData Data ;
Data . SmartObjectHandle = SmartObjectRuntime . GetRegisteredHandle ( ) ;
Data . Reason = ESmartObjectChangeReason : : OnTagRemoved ;
Data . Tag = Tag ;
SmartObjectRuntime . OnEvent . Broadcast ( Data ) ;
2022-03-16 03:47:02 -04:00
}
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : OnSlotChanged (
const FSmartObjectRuntime & SmartObjectRuntime ,
const FSmartObjectRuntimeSlot & Slot ,
const FSmartObjectSlotHandle SlotHandle ,
const ESmartObjectChangeReason Reason ,
const FConstStructView Payload ,
const FGameplayTag ChangedTag
)
2021-09-28 13:33:00 -04:00
{
2023-02-02 18:43:13 -05:00
if ( SmartObjectRuntime . GetEventDelegate ( ) . IsBound ( ) )
2022-11-01 15:11:25 -04:00
{
FSmartObjectEventData Data ;
2023-07-18 08:42:51 -04:00
Data . SmartObjectHandle = SlotHandle . GetSmartObjectHandle ( ) ;
2022-11-01 15:11:25 -04:00
Data . SlotHandle = SlotHandle ;
Data . Reason = Reason ;
Data . Tag = ChangedTag ;
2023-02-02 18:43:13 -05:00
Data . EventPayload = Payload ;
SmartObjectRuntime . GetEventDelegate ( ) . Broadcast ( Data ) ;
2022-11-01 15:11:25 -04:00
}
}
2021-09-28 13:33:00 -04:00
void USmartObjectSubsystem : : RegisterSlotInvalidationCallback ( const FSmartObjectClaimHandle & ClaimHandle , const FOnSlotInvalidated & Callback )
{
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedMutableRuntimeAndSlot ( ClaimHandle . SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2021-09-28 13:33:00 -04:00
{
Slot - > OnSlotInvalidatedDelegate = Callback ;
}
}
void USmartObjectSubsystem : : UnregisterSlotInvalidationCallback ( const FSmartObjectClaimHandle & ClaimHandle )
{
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedMutableRuntimeAndSlot ( ClaimHandle . SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2021-09-28 13:33:00 -04:00
{
Slot - > OnSlotInvalidatedDelegate . Unbind ( ) ;
}
}
2022-11-01 15:11:25 -04:00
FOnSmartObjectEvent * USmartObjectSubsystem : : GetSlotEventDelegate ( const FSmartObjectSlotHandle SlotHandle )
{
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedMutableRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2022-11-01 15:11:25 -04:00
{
2023-07-18 08:42:51 -04:00
return & SmartObjectRuntime - > GetMutableEventDelegate ( ) ;
2022-11-01 15:11:25 -04:00
}
return nullptr ;
}
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
void USmartObjectSubsystem : : DebugDraw ( FDebugRenderSceneProxy * DebugProxy ) const
{
2022-11-24 14:53:52 -05:00
if ( ! bRuntimeInitialized )
2022-02-21 01:10:34 -05:00
{
return ;
}
checkfSlow ( SpacePartition ! = nullptr , TEXT ( " Space partition is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
SpacePartition - > Draw ( DebugProxy ) ;
for ( auto It ( RuntimeSmartObjects . CreateConstIterator ( ) ) ; It ; + + It )
{
const FSmartObjectRuntime & Runtime = It . Value ( ) ;
DebugProxy - > Boxes . Emplace ( Runtime . Bounds , GColorList . Blue ) ;
}
}
# endif // UE_ENABLE_DEBUG_DRAWING
2023-07-18 08:42:51 -04:00
void USmartObjectSubsystem : : AddSlotData ( const FSmartObjectClaimHandle & ClaimHandle , const FConstStructView InData )
2021-09-28 13:33:00 -04:00
{
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( ! GetValidatedMutableRuntimeAndSlot ( ClaimHandle . SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2022-01-19 14:16:16 -05:00
{
2023-07-18 08:42:51 -04:00
return ;
}
2022-11-01 15:11:25 -04:00
2023-07-18 08:42:51 -04:00
// If we have a data of same type, override, else add.
bool bFound = false ;
for ( FStructView Data : Slot - > StateData )
{
if ( Data . GetScriptStruct ( ) = = InData . GetScriptStruct ( ) )
{
Data . GetScriptStruct ( ) - > CopyScriptStruct ( Data . GetMemory ( ) , InData . GetMemory ( ) ) ;
bFound = true ;
break ; ;
2022-03-18 12:31:49 -04:00
}
2022-01-19 14:16:16 -05:00
}
2023-07-18 08:42:51 -04:00
if ( ! bFound )
{
Slot - > StateData . Append ( { InData } ) ;
}
2022-01-19 14:16:16 -05:00
}
2022-03-17 19:07:54 -04:00
FSmartObjectSlotView USmartObjectSubsystem : : GetSlotView ( const FSmartObjectSlotHandle SlotHandle ) const
2022-01-19 14:16:16 -05:00
{
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
const FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2022-01-19 14:16:16 -05:00
{
2023-07-18 08:42:51 -04:00
return FSmartObjectSlotView ( SlotHandle , * const_cast < FSmartObjectRuntime * > ( SmartObjectRuntime ) , * const_cast < FSmartObjectRuntimeSlot * > ( Slot ) ) ;
2022-01-19 14:16:16 -05:00
}
2023-07-18 08:42:51 -04:00
return { } ;
2022-01-19 14:16:16 -05:00
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : FindSlots ( const FSmartObjectHandle Handle , const FSmartObjectRequestFilter & Filter , TArray < FSmartObjectSlotHandle > & OutSlots , const FConstStructView UserData ) const
2022-01-19 14:16:16 -05:00
{
2024-02-08 13:21:26 -05:00
if ( const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( Handle , __FUNCTION__ ) )
2022-03-02 13:25:37 -05:00
{
2023-07-18 08:42:51 -04:00
FindSlots ( Handle , * SmartObjectRuntime , Filter , OutSlots , UserData ) ;
2022-03-02 13:25:37 -05:00
}
2022-01-31 18:52:49 -05:00
}
2022-11-01 15:11:25 -04:00
void USmartObjectSubsystem : : GetAllSlots ( const FSmartObjectHandle Handle , TArray < FSmartObjectSlotHandle > & OutSlots ) const
{
TRACE_CPUPROFILER_EVENT_SCOPE_STR ( " SmartObject_FilterSlots " ) ;
OutSlots . Reset ( ) ;
2024-02-08 13:21:26 -05:00
if ( const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( Handle , __FUNCTION__ ) )
2022-11-01 15:11:25 -04:00
{
2023-07-18 08:42:51 -04:00
OutSlots . Reserve ( SmartObjectRuntime - > Slots . Num ( ) ) ;
for ( int32 Index = 0 ; Index < SmartObjectRuntime - > Slots . Num ( ) ; Index + + )
{
OutSlots . Add ( FSmartObjectSlotHandle ( Handle , Index ) ) ;
}
2022-11-01 15:11:25 -04:00
}
}
2023-02-02 18:43:13 -05:00
bool USmartObjectSubsystem : : EvaluateConditionsForFiltering (
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime & SmartObjectRuntime ,
2023-02-02 18:43:13 -05:00
const FSmartObjectSlotHandle SlotHandle ,
FWorldConditionContextData & ContextData ,
const FConstStructView UserData ,
TPair < const FSmartObjectRuntime * , bool > & LastEvaluatedRuntime
) const
{
// Evaluate preconditions on the parent object only once if all slots have the same one (usual case)
2023-07-18 08:42:51 -04:00
if ( & SmartObjectRuntime ! = LastEvaluatedRuntime . Key )
2023-02-02 18:43:13 -05:00
{
2023-07-18 08:42:51 -04:00
LastEvaluatedRuntime . Key = & SmartObjectRuntime ;
2023-02-02 18:43:13 -05:00
// Set context schema and bind user data only if not set or changed
const UWorldConditionSchema * PrevSchema = ContextData . GetSchema ( ) ;
2023-07-18 08:42:51 -04:00
if ( PrevSchema = = nullptr | | PrevSchema ! = SmartObjectRuntime . GetDefinition ( ) . GetWorldConditionSchema ( ) )
2023-02-02 18:43:13 -05:00
{
2023-07-18 08:42:51 -04:00
ContextData . SetSchema ( * SmartObjectRuntime . GetDefinition ( ) . GetWorldConditionSchema ( ) ) ;
2023-02-02 18:43:13 -05:00
// Setup some context data using user data
BindPropertiesFromStruct ( ContextData , UserData ) ;
}
// Setup system related data (object runtime, slot, subsystem, etc.)
2023-07-18 08:42:51 -04:00
SetupConditionContextCommonData ( ContextData , SmartObjectRuntime ) ;
2023-02-02 18:43:13 -05:00
// Evaluate object conditions.
2023-07-18 08:42:51 -04:00
LastEvaluatedRuntime . Value = EvaluateObjectConditions ( ContextData , SmartObjectRuntime ) ;
2023-02-02 18:43:13 -05:00
}
// Evaluate slot conditions only if parent runtime passed its own selection conditions
2023-07-18 08:42:51 -04:00
return LastEvaluatedRuntime . Value ? EvaluateSlotConditions ( ContextData , SmartObjectRuntime , SlotHandle ) : false ;
2023-02-02 18:43:13 -05:00
}
TArray < FSmartObjectSlotHandle > USmartObjectSubsystem : : FilterSlotsBySelectionConditions (
const TConstArrayView < FSmartObjectSlotHandle > & SlotsToFilter ,
const FConstStructView UserData
) const
{
TArray < FSmartObjectSlotHandle > Result ;
Result . Reserve ( SlotsToFilter . Num ( ) ) ;
FWorldConditionContextData ContextData ;
TPair < const FSmartObjectRuntime * , bool > LastEvaluatedSmartObjectRuntime = { nullptr , false } ;
2023-04-20 12:37:29 -04:00
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * CurrentRuntime = nullptr ;
FSmartObjectHandle CurrentRuntimeHandle = { } ;
2023-04-20 12:37:29 -04:00
bool bSlotIsValid = false ;
2023-02-02 18:43:13 -05:00
for ( const FSmartObjectSlotHandle SlotHandle : SlotsToFilter )
{
2023-07-18 08:42:51 -04:00
if ( ! CurrentRuntime | | CurrentRuntimeHandle ! = SlotHandle . GetSmartObjectHandle ( ) )
{
2024-02-08 13:21:26 -05:00
CurrentRuntime = GetValidatedRuntime ( SlotHandle . GetSmartObjectHandle ( ) , __FUNCTION__ ) ;
2023-07-18 08:42:51 -04:00
CurrentRuntimeHandle = SlotHandle . GetSmartObjectHandle ( ) ;
}
if ( ! CurrentRuntime | | ! CurrentRuntime - > Slots . IsValidIndex ( SlotHandle . GetSlotIndex ( ) ) )
{
bSlotIsValid = false ;
}
2023-04-20 12:37:29 -04:00
UE_CVLOG_UELOG ( bSlotIsValid = = false , this , LogSmartObject , Log ,
TEXT ( " %hs failed using handle '%s'. Slot is no longer part of the simulation. Consider calling IsSmartObjectSlotValid to avoid this message. " ) ,
__FUNCTION__ , * LexToString ( SlotHandle ) ) ;
2023-07-18 08:42:51 -04:00
if ( bSlotIsValid & & EvaluateConditionsForFiltering ( * CurrentRuntime , SlotHandle , ContextData , UserData , LastEvaluatedSmartObjectRuntime ) )
2023-02-02 18:43:13 -05:00
{
Result . Add ( SlotHandle ) ;
}
}
Result . Shrink ( ) ;
return MoveTemp ( Result ) ;
}
TArray < FSmartObjectRequestResult > USmartObjectSubsystem : : FilterResultsBySelectionConditions (
const TConstArrayView < FSmartObjectRequestResult > & ResultsToFilter ,
const FConstStructView UserData
) const
{
TArray < FSmartObjectRequestResult > Result ;
Result . Reserve ( ResultsToFilter . Num ( ) ) ;
FWorldConditionContextData ContextData ;
TPair < const FSmartObjectRuntime * , bool > LastEvaluatedSmartObjectRuntime = { nullptr , false } ;
2023-04-20 12:37:29 -04:00
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * CurrentRuntime = nullptr ;
FSmartObjectHandle CurrentRuntimeHandle = { } ;
2023-04-20 12:37:29 -04:00
bool bSlotIsValid = false ;
2023-02-02 18:43:13 -05:00
for ( const FSmartObjectRequestResult RequestResult : ResultsToFilter )
{
2023-07-18 08:42:51 -04:00
if ( ! CurrentRuntime | | CurrentRuntimeHandle ! = RequestResult . SlotHandle . GetSmartObjectHandle ( ) )
{
2024-02-08 13:21:26 -05:00
CurrentRuntime = GetValidatedRuntime ( RequestResult . SlotHandle . GetSmartObjectHandle ( ) , __FUNCTION__ ) ;
2023-07-18 08:42:51 -04:00
CurrentRuntimeHandle = RequestResult . SlotHandle . GetSmartObjectHandle ( ) ;
}
if ( ! CurrentRuntime | | ! CurrentRuntime - > Slots . IsValidIndex ( RequestResult . SlotHandle . GetSlotIndex ( ) ) )
{
bSlotIsValid = false ;
}
2023-04-20 12:37:29 -04:00
UE_CVLOG_UELOG ( bSlotIsValid = = false , this , LogSmartObject , Log ,
TEXT ( " %hs failed using handle '%s'. Slot is no longer part of the simulation. Consider calling IsSmartObjectSlotValid to avoid this message. " ) ,
__FUNCTION__ , * LexToString ( RequestResult . SlotHandle ) ) ;
2023-07-18 08:42:51 -04:00
if ( bSlotIsValid & & EvaluateConditionsForFiltering ( * CurrentRuntime , RequestResult . SlotHandle , ContextData , UserData , LastEvaluatedSmartObjectRuntime ) )
2023-02-02 18:43:13 -05:00
{
Result . Add ( RequestResult ) ;
}
}
Result . Shrink ( ) ;
return MoveTemp ( Result ) ;
}
bool USmartObjectSubsystem : : EvaluateSelectionConditions ( const FSmartObjectSlotHandle SlotHandle , const FConstStructView UserData ) const
{
FWorldConditionContextData ContextData ;
TPair < const FSmartObjectRuntime * , bool > LastEvaluatedSmartObjectRuntime = { nullptr , false } ;
2023-04-20 12:37:29 -04:00
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
const FSmartObjectRuntimeSlot * Slot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( GetValidatedRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , Slot , __FUNCTION__ ) )
2023-07-18 08:42:51 -04:00
{
return EvaluateConditionsForFiltering ( * SmartObjectRuntime , SlotHandle , ContextData , UserData , LastEvaluatedSmartObjectRuntime ) ;
}
2023-04-20 12:37:29 -04:00
2023-07-18 08:42:51 -04:00
return false ;
2023-02-02 18:43:13 -05:00
}
2023-03-17 07:41:24 -04:00
bool USmartObjectSubsystem : : FindEntranceLocationForSlot ( const FSmartObjectSlotHandle SlotHandle , const FSmartObjectSlotEntranceLocationRequest & Request , FSmartObjectSlotEntranceLocationResult & Result ) const
{
return FindEntranceLocationInternal ( SlotHandle , FSmartObjectSlotEntranceHandle ( ) , Request , Result ) ;
}
bool USmartObjectSubsystem : : UpdateEntranceLocation ( const FSmartObjectSlotEntranceHandle EntranceHandle , const FSmartObjectSlotEntranceLocationRequest & Request , FSmartObjectSlotEntranceLocationResult & Result ) const
{
return FindEntranceLocationInternal ( EntranceHandle . GetSlotHandle ( ) , EntranceHandle , Request , Result ) ;
}
bool USmartObjectSubsystem : : FindEntranceLocationInternal (
const FSmartObjectSlotHandle SlotHandle ,
const FSmartObjectSlotEntranceHandle SlotEntranceHandle ,
const FSmartObjectSlotEntranceLocationRequest & Request ,
FSmartObjectSlotEntranceLocationResult & Result
) const
2023-03-02 05:58:30 -05:00
{
Result = { } ;
2023-03-14 06:26:29 -04:00
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = nullptr ;
2023-08-29 07:48:32 -04:00
const FSmartObjectRuntimeSlot * RuntimeSlot = nullptr ;
2024-02-08 13:21:26 -05:00
if ( ! GetValidatedRuntimeAndSlot ( SlotHandle , SmartObjectRuntime , RuntimeSlot , __FUNCTION__ ) )
2023-03-02 05:58:30 -05:00
{
return false ;
}
2023-03-27 08:22:19 -04:00
UWorld * World = GetWorld ( ) ;
2023-08-10 07:02:20 -04:00
FSmartObjectValidationContext ValidationContext ;
2023-08-14 03:40:42 -04:00
if ( ! ValidationContext . Init ( World , Request , SmartObjectRuntime - > GetOwnerActor ( ) ) )
2023-03-27 08:22:19 -04:00
{
2023-08-10 07:02:20 -04:00
return false ;
2023-03-27 08:22:19 -04:00
}
2023-08-10 07:02:20 -04:00
const FSmartObjectSlotDefinition & SlotDefinition = SmartObjectRuntime - > GetDefinition ( ) . GetSlot ( SlotHandle . GetSlotIndex ( ) ) ;
2023-08-29 07:48:32 -04:00
const FTransform & SlotTransform = RuntimeSlot - > GetSlotWorldTransform ( SmartObjectRuntime - > Transform ) ;
2023-03-27 08:22:19 -04:00
2023-08-10 07:02:20 -04:00
bool bHasResult = false ;
QueryValidatedSlotEntranceLocationsInternal (
2023-08-14 03:40:42 -04:00
World , ValidationContext , Request , SlotHandle , SlotDefinition , SlotTransform , SlotEntranceHandle ,
2023-08-10 07:02:20 -04:00
[ & OutResult = Result , & bHasResult ] ( const FSmartObjectSlotEntranceLocationResult & Result )
2023-06-09 06:52:59 -04:00
{
2023-08-10 07:02:20 -04:00
if ( Result . bIsValid )
2023-06-09 06:52:59 -04:00
{
2023-08-10 07:02:20 -04:00
OutResult = Result ;
bHasResult = true ;
return false ; // Stop iterating
2023-06-09 06:52:59 -04:00
}
2023-08-10 07:02:20 -04:00
return true ; // Continue
} ) ;
2023-03-27 08:22:19 -04:00
2023-08-10 07:02:20 -04:00
return bHasResult ;
}
bool USmartObjectSubsystem : : QueryAllValidatedEntranceLocations (
2023-08-14 03:40:42 -04:00
const UWorld * World ,
2023-08-10 07:02:20 -04:00
const USmartObjectDefinition & SmartObjectDefinition ,
const FTransform & SmartObjectTransform ,
const AActor * SkipActor ,
const FSmartObjectSlotEntranceLocationRequest & Request ,
TArray < FSmartObjectSlotEntranceLocationResult > & Results
2023-08-14 03:40:42 -04:00
)
2023-08-10 07:02:20 -04:00
{
FSmartObjectValidationContext ValidationContext ;
2023-08-14 03:40:42 -04:00
if ( ! ValidationContext . Init ( World , Request , SkipActor ) )
2023-03-27 08:22:19 -04:00
{
2023-03-21 07:37:13 -04:00
return false ;
}
2023-08-10 07:02:20 -04:00
TConstArrayView < FSmartObjectSlotDefinition > SlotDefinitions = SmartObjectDefinition . GetSlots ( ) ;
for ( TConstEnumerateRef < const FSmartObjectSlotDefinition > SlotDefinition : EnumerateRange ( SlotDefinitions ) )
2023-03-27 08:22:19 -04:00
{
2023-08-10 07:02:20 -04:00
const FTransform & SlotTransform = SmartObjectDefinition . GetSlotWorldTransform ( SlotDefinition . GetIndex ( ) , SmartObjectTransform ) ;
const FSmartObjectSlotHandle SlotHandle ( { } , SlotDefinition . GetIndex ( ) ) ;
2023-03-21 07:37:13 -04:00
2023-08-10 07:02:20 -04:00
QueryValidatedSlotEntranceLocationsInternal (
2023-08-14 03:40:42 -04:00
World , ValidationContext , Request , SlotHandle , * SlotDefinition , SlotTransform , { } ,
2023-08-10 07:02:20 -04:00
[ & Results ] ( const FSmartObjectSlotEntranceLocationResult & Result )
{
Results . Add ( Result ) ;
return true ; // Continue
} ) ;
}
return Results . Num ( ) > 0 ;
}
void USmartObjectSubsystem : : QueryValidatedSlotEntranceLocationsInternal (
2023-08-14 03:40:42 -04:00
const UWorld * World ,
2023-08-10 07:02:20 -04:00
FSmartObjectValidationContext & ValidationContext ,
const FSmartObjectSlotEntranceLocationRequest & Request ,
const FSmartObjectSlotHandle SlotHandle ,
const FSmartObjectSlotDefinition & SlotDefinition ,
const FTransform & SlotTransform ,
const FSmartObjectSlotEntranceHandle SlotEntranceHandle ,
TFunctionRef < bool ( const FSmartObjectSlotEntranceLocationResult & ) > ResultFunc
2023-08-14 03:40:42 -04:00
)
2023-08-10 07:02:20 -04:00
{
struct FSmartObjectSlotEntranceCandidate
2023-03-02 05:58:30 -05:00
{
FVector Location ;
FRotator Rotation ;
NavNodeRef NodeRef ;
FVector : : FReal DistanceSqr = 0.0 ;
2023-03-14 06:26:29 -04:00
const FSmartObjectSlotEntranceAnnotation * EntranceAnnotation = nullptr ;
2023-03-17 07:41:24 -04:00
ESmartObjectEntrancePriority SelectionPriority = ESmartObjectEntrancePriority : : Normal ;
2023-03-14 06:26:29 -04:00
bool bTraceGroundLocation = false ;
bool bCheckTransitionTrajectory = false ;
2023-03-17 07:41:24 -04:00
FSmartObjectSlotEntranceHandle Handle ;
2023-03-02 05:58:30 -05:00
} ;
2023-08-10 07:02:20 -04:00
TArray < FSmartObjectAnnotationCollider > SlotColliders ;
TArray < FSmartObjectSlotEntranceCandidate , TInlineAllocator < 8 > > Candidates ;
2023-03-02 05:58:30 -05:00
2023-03-14 06:26:29 -04:00
const bool bIncludeEntries = Request . LocationType = = ESmartObjectSlotNavigationLocationType : : Entry ;
const bool bIncludeExits = Request . LocationType = = ESmartObjectSlotNavigationLocationType : : Exit ;
2023-12-04 07:36:52 -05:00
for ( TConstEnumerateRef < const FSmartObjectDefinitionDataProxy > DataProxy : EnumerateRange ( SlotDefinition . DefinitionData ) )
2023-03-02 05:58:30 -05:00
{
2023-08-29 07:48:32 -04:00
if ( const FSmartObjectSlotEntranceAnnotation * EntranceAnnotation = DataProxy - > Data . GetPtr < FSmartObjectSlotEntranceAnnotation > ( ) )
2023-03-02 05:58:30 -05:00
{
2023-03-22 08:23:20 -04:00
// If specific entry location was requested and this is not the one, skip it.
if ( SlotEntranceHandle . Type = = FSmartObjectSlotEntranceHandle : : EType : : Entrance
2023-08-29 07:48:32 -04:00
& & SlotEntranceHandle . Index ! = DataProxy . GetIndex ( ) )
2023-03-22 08:23:20 -04:00
{
continue ;
}
2023-07-18 08:42:51 -04:00
if ( ( EntranceAnnotation - > bIsEntry = = bIncludeEntries
| | EntranceAnnotation - > bIsExit = = bIncludeExits )
& & EntranceAnnotation - > HasTransform ( ) )
2023-03-02 05:58:30 -05:00
{
2023-07-18 08:42:51 -04:00
const FTransform EntryTransform = EntranceAnnotation - > GetAnnotationWorldTransform ( SlotTransform ) ;
2023-08-10 07:02:20 -04:00
FSmartObjectSlotEntranceCandidate & Candidate = Candidates . AddDefaulted_GetRef ( ) ;
2023-07-18 08:42:51 -04:00
Candidate . Location = EntryTransform . GetLocation ( ) ;
Candidate . Rotation = EntryTransform . GetRotation ( ) . Rotator ( ) ;
Candidate . EntranceAnnotation = EntranceAnnotation ;
Candidate . bTraceGroundLocation = EntranceAnnotation - > bTraceGroundLocation ;
Candidate . bCheckTransitionTrajectory = EntranceAnnotation - > bCheckTransitionTrajectory ;
Candidate . SelectionPriority = EntranceAnnotation - > SelectionPriority ;
2023-08-29 07:48:32 -04:00
Candidate . Handle = FSmartObjectSlotEntranceHandle ( SlotHandle , FSmartObjectSlotEntranceHandle : : EType : : Entrance , DataProxy . GetIndex ( ) ) ;
2023-03-02 05:58:30 -05:00
}
}
2023-08-29 07:48:32 -04:00
else if ( const FSmartObjectAnnotation_SlotUserCollision * UserCollisionAnnotation = DataProxy - > Data . GetPtr < FSmartObjectAnnotation_SlotUserCollision > ( ) )
2023-03-21 07:37:13 -04:00
{
2023-08-10 07:02:20 -04:00
UserCollisionAnnotation - > GetColliders ( ValidationContext . UserCapsuleParams , SlotTransform , SlotColliders ) ;
2023-03-21 07:37:13 -04:00
}
2023-03-02 05:58:30 -05:00
}
2023-03-17 07:41:24 -04:00
if ( ( Candidates . IsEmpty ( ) & & Request . bUseSlotLocationAsFallback )
| | SlotEntranceHandle . Type = = FSmartObjectSlotEntranceHandle : : EType : : Slot )
2023-03-02 05:58:30 -05:00
{
2023-08-10 07:02:20 -04:00
FSmartObjectSlotEntranceCandidate & Candidate = Candidates . AddDefaulted_GetRef ( ) ;
2023-03-02 05:58:30 -05:00
Candidate . Location = SlotTransform . GetLocation ( ) ;
Candidate . Rotation = SlotTransform . GetRotation ( ) . Rotator ( ) ;
2023-08-16 07:26:50 -04:00
Candidate . bTraceGroundLocation = true ; // Use ground project by default on slots (this seems to match the users expectation). Entrances have specific bool to turn it off.
2023-03-17 07:41:24 -04:00
Candidate . Handle = FSmartObjectSlotEntranceHandle ( SlotHandle , FSmartObjectSlotEntranceHandle : : EType : : Slot ) ;
2023-03-02 05:58:30 -05:00
}
2023-08-10 07:02:20 -04:00
// Early out if nothing to report.
2023-03-02 05:58:30 -05:00
if ( Candidates . IsEmpty ( ) )
{
2023-08-10 07:02:20 -04:00
return ;
2023-03-02 05:58:30 -05:00
}
2023-03-14 06:26:29 -04:00
// Sort candidates so that the best candidate is first.
2023-03-17 07:41:24 -04:00
if ( Candidates . Num ( ) > 1 )
2023-03-02 05:58:30 -05:00
{
2023-03-17 07:41:24 -04:00
if ( Request . SelectMethod = = FSmartObjectSlotEntrySelectionMethod : : NearestToSearchLocation )
2023-03-02 05:58:30 -05:00
{
2023-08-10 07:02:20 -04:00
for ( FSmartObjectSlotEntranceCandidate & Candidate : Candidates )
2023-03-17 07:41:24 -04:00
{
Candidate . DistanceSqr = FVector : : DistSquared ( Request . SearchLocation , Candidate . Location ) ;
}
2023-08-10 07:02:20 -04:00
Candidates . Sort ( [ ] ( const FSmartObjectSlotEntranceCandidate & A , const FSmartObjectSlotEntranceCandidate & B )
2023-03-17 07:41:24 -04:00
{
if ( A . SelectionPriority = = B . SelectionPriority )
{
return A . DistanceSqr < B . DistanceSqr ;
}
return A . SelectionPriority > B . SelectionPriority ;
} ) ;
2023-03-02 05:58:30 -05:00
}
2023-03-17 07:41:24 -04:00
else
2023-03-02 05:58:30 -05:00
{
2023-03-17 07:41:24 -04:00
// Use stable sort to keep initial order.
2023-08-10 07:02:20 -04:00
Candidates . StableSort ( [ ] ( const FSmartObjectSlotEntranceCandidate & A , const FSmartObjectSlotEntranceCandidate & B )
2023-03-17 07:41:24 -04:00
{
return A . SelectionPriority > B . SelectionPriority ;
} ) ;
}
2023-03-02 05:58:30 -05:00
}
check ( Candidates . Num ( ) > 0 ) ;
2023-03-21 07:37:13 -04:00
// If the slot location should be free of collisions, check it now since it's shared for all entries.
2023-08-10 07:02:20 -04:00
bool bIsSlotCollisionsValid = true ;
2023-03-21 07:37:13 -04:00
if ( Request . bCheckSlotLocationOverlap
2023-08-10 07:02:20 -04:00
& & ! SlotColliders . IsEmpty ( ) )
2023-03-21 07:37:13 -04:00
{
2023-08-10 07:02:20 -04:00
if ( UE : : SmartObject : : Annotations : : TestCollidersOverlap ( * World , SlotColliders , ValidationContext . TransitionTraceParams , ValidationContext . TransitionTraceQueryParams ) )
2023-03-21 07:37:13 -04:00
{
2023-08-10 07:02:20 -04:00
bIsSlotCollisionsValid = false ;
2023-03-21 07:37:13 -04:00
}
}
2023-03-02 05:58:30 -05:00
2023-08-10 07:02:20 -04:00
// Candidates are now in order of preference, validate each for hard requirements.
// In order to save performance, we stop validating as soon as the first hard test fails.
// Results are generated for both valid and invalid results, which allows the callback to decide
// to pick first valid result or all results (e.g. for visualization).
for ( FSmartObjectSlotEntranceCandidate & Candidate : Candidates )
2023-03-14 06:26:29 -04:00
{
2023-08-10 07:02:20 -04:00
const FBox SearchBounds ( Candidate . Location - ValidationContext . NavigationSearchExtents , Candidate . Location + ValidationContext . NavigationSearchExtents ) ;
2023-03-14 06:26:29 -04:00
2023-08-10 07:02:20 -04:00
bool bIsValid = bIsSlotCollisionsValid ;
2023-03-14 06:26:29 -04:00
// Check and adjust the location on navigable space.
2023-08-10 07:02:20 -04:00
if ( bIsValid
& & Request . bProjectNavigationLocation )
2023-03-14 06:26:29 -04:00
{
FNavLocation NavLocation ;
2023-08-10 07:02:20 -04:00
if ( ! UE : : SmartObject : : Annotations : : ProjectNavigationLocation ( * ValidationContext . NavigationData , Candidate . Location , SearchBounds , ValidationContext . NavigationFilter , Request . UserActor , NavLocation ) )
2023-03-14 06:26:29 -04:00
{
2023-03-17 07:41:24 -04:00
// If no navigable area found, skip the candidate.
2023-08-10 07:02:20 -04:00
bIsValid = false ;
}
else
{
Candidate . Location = NavLocation . Location ;
Candidate . NodeRef = NavLocation . NodeRef ;
2023-03-14 06:26:29 -04:00
}
}
2023-03-21 07:37:13 -04:00
// Check that the entry location is free of collisions if requested.
2023-08-10 07:02:20 -04:00
if ( bIsValid
& & Request . bCheckEntranceLocationOverlap )
2023-03-21 07:37:13 -04:00
{
2023-08-10 07:02:20 -04:00
const FSmartObjectAnnotationCollider Collider = ValidationContext . UserCapsuleParams . GetAsCollider ( Candidate . Location , Candidate . Rotation . Quaternion ( ) ) ;
if ( UE : : SmartObject : : Annotations : : TestCollidersOverlap ( * World , { Collider } , ValidationContext . TransitionTraceParams , ValidationContext . TransitionTraceQueryParams ) )
2023-03-21 07:37:13 -04:00
{
// If the colliders overlap, skip the candidate.
2023-08-10 07:02:20 -04:00
bIsValid = false ;
2023-03-21 07:37:13 -04:00
}
}
2023-03-14 06:26:29 -04:00
// Check and adjust the location on ground.
2023-08-10 07:02:20 -04:00
if ( bIsValid
& & Request . bTraceGroundLocation
2023-03-14 06:26:29 -04:00
& & Candidate . bTraceGroundLocation )
{
FVector GroundLocation ;
2023-08-10 07:02:20 -04:00
if ( ! UE : : SmartObject : : Annotations : : TraceGroundLocation ( * World , Candidate . Location , SearchBounds , ValidationContext . GroundTraceParams , ValidationContext . GroundTraceQueryParams , GroundLocation ) )
2023-03-14 06:26:29 -04:00
{
// If not ground location found, skip the candidate.
2023-08-10 07:02:20 -04:00
bIsValid = false ;
}
else
{
Candidate . Location = GroundLocation ;
2023-03-14 06:26:29 -04:00
}
}
// Check that there's no collision during transition to slot location.
2023-08-10 07:02:20 -04:00
if ( bIsValid
& & Request . bCheckTransitionTrajectory
& & Candidate . bCheckTransitionTrajectory
2023-03-14 06:26:29 -04:00
& & Candidate . EntranceAnnotation )
{
2023-08-10 07:02:20 -04:00
// @todo: we're currently _not_ using the adjusted location (Candidate.Location), consider if we should.
2023-03-14 06:26:29 -04:00
TArray < FSmartObjectAnnotationCollider > Colliders ;
Candidate . EntranceAnnotation - > GetTrajectoryColliders ( SlotTransform , Colliders ) ;
2023-08-10 07:02:20 -04:00
if ( UE : : SmartObject : : Annotations : : TestCollidersOverlap ( * World , Colliders , ValidationContext . TransitionTraceParams , ValidationContext . TransitionTraceQueryParams ) )
{
2023-03-14 06:26:29 -04:00
// If the colliders overlap, skip the candidate.
2023-08-10 07:02:20 -04:00
bIsValid = false ;
}
2023-03-14 06:26:29 -04:00
}
2023-08-10 07:02:20 -04:00
// Make result for the validated data, the callback will decide to use the data or not, or to keep on validating the next entrances.
FSmartObjectSlotEntranceLocationResult Result ;
2023-03-14 06:26:29 -04:00
Result . Location = Candidate . Location ;
Result . Rotation = Candidate . Rotation ;
Result . NodeRef = INVALID_NAVNODEREF ;
if ( Candidate . EntranceAnnotation )
{
2023-06-06 09:07:47 -04:00
Result . Tags = Candidate . EntranceAnnotation - > Tags ;
PRAGMA_DISABLE_DEPRECATION_WARNINGS
Result . Tag = Result . Tags . First ( ) ;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2023-03-14 06:26:29 -04:00
}
if ( Request . LocationType = = ESmartObjectSlotNavigationLocationType : : Exit )
{
// Reverse direction for exits.
Result . Rotation = Result . Rotation . Add ( 0.0 , 180.0 , 0.0 ) . Clamp ( ) ;
}
2023-03-22 08:23:20 -04:00
Result . EntranceHandle = Candidate . Handle ;
2023-08-10 07:02:20 -04:00
Result . bIsValid = bIsValid ;
2023-03-14 06:26:29 -04:00
2023-08-10 07:02:20 -04:00
const bool bShouldContinue = ResultFunc ( Result ) ;
if ( ! bShouldContinue )
{
break ;
}
}
2023-03-02 05:58:30 -05:00
}
2023-07-18 08:42:51 -04:00
void USmartObjectSubsystem : : FindSlots ( const FSmartObjectHandle Handle , const FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectRequestFilter & Filter , TArray < FSmartObjectSlotHandle > & OutResults , const FConstStructView UserData ) const
2022-01-31 18:52:49 -05:00
{
2022-02-17 03:40:43 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE_STR ( " SmartObject_FilterSlots " ) ;
2022-03-16 03:47:02 -04:00
// Use the high level flag, no need to dig into each slot state since they are also all disabled.
2022-11-01 15:11:25 -04:00
if ( ! SmartObjectRuntime . IsEnabled ( ) )
2022-03-16 03:47:02 -04:00
{
return ;
}
2021-11-26 15:48:13 -05:00
const USmartObjectDefinition & Definition = SmartObjectRuntime . GetDefinition ( ) ;
2022-03-02 13:25:37 -05:00
const int32 NumSlots = Definition . GetSlots ( ) . Num ( ) ;
checkf ( NumSlots > 0 , TEXT ( " Definition should contain slot definitions at this point " ) ) ;
2023-07-18 08:42:51 -04:00
checkf ( SmartObjectRuntime . Slots . Num ( ) = = NumSlots , TEXT ( " Number of runtime slot handles should match number of slot definitions " ) ) ;
2022-03-02 13:25:37 -05:00
// Applying caller's predicate
if ( Filter . Predicate & & ! Filter . Predicate ( SmartObjectRuntime . GetRegisteredHandle ( ) ) )
2021-09-28 13:33:00 -04:00
{
2022-01-31 18:52:49 -05:00
return ;
2021-09-28 13:33:00 -04:00
}
2022-03-02 13:25:37 -05:00
// Apply definition level filtering (Tags and BehaviorDefinition)
// This could be improved to cache results between a single query against multiple instances of the same definition
TArray < int32 > ValidSlotIndices ;
FindMatchingSlotDefinitionIndices ( Definition , Filter , ValidSlotIndices ) ;
2021-09-28 13:33:00 -04:00
2023-02-02 18:43:13 -05:00
FWorldConditionContextData ConditionContextData ;
ConditionContextData . SetSchema ( * Definition . GetWorldConditionSchema ( ) ) ;
2022-11-17 07:44:24 -05:00
2023-01-18 10:52:51 -05:00
// Setup default data
SetupConditionContextCommonData ( ConditionContextData , SmartObjectRuntime ) ;
2023-02-02 18:43:13 -05:00
2023-01-18 10:52:51 -05:00
// Setup additional data related to requester
2023-02-02 18:43:13 -05:00
BindPropertiesFromStruct ( ConditionContextData , UserData ) ;
2023-01-18 10:52:51 -05:00
// Check object conditions.
2023-03-22 08:23:20 -04:00
if ( Filter . bShouldEvaluateConditions & & ! EvaluateObjectConditions ( ConditionContextData , SmartObjectRuntime ) )
2023-01-18 10:52:51 -05:00
{
return ;
}
2022-11-17 07:44:24 -05:00
2022-11-04 11:28:57 -04:00
// Build list of available slot indices (filter out occupied or reserved slots or disabled slots)
2022-03-02 13:25:37 -05:00
for ( const int32 SlotIndex : ValidSlotIndices )
2021-09-28 13:33:00 -04:00
{
2023-07-18 08:42:51 -04:00
const FSmartObjectRuntimeSlot & RuntimeSlot = SmartObjectRuntime . GetSlot ( SlotIndex ) ;
2023-06-29 17:44:56 -04:00
if ( ! Filter . bShouldIncludeDisabledSlots & & ! RuntimeSlot . IsEnabled ( ) )
{
continue ;
}
2023-01-18 10:52:51 -05:00
2023-11-30 02:26:33 -05:00
if ( Filter . bShouldIncludeClaimedSlots
| | RuntimeSlot . State = = ESmartObjectSlotState : : Free
| | ( RuntimeSlot . State = = ESmartObjectSlotState : : Claimed
& & RuntimeSlot . ClaimedPriority < Filter . ClaimPriority ) )
{
const FSmartObjectSlotHandle SlotHandle ( Handle , SlotIndex ) ;
// Check slot conditions.
if ( Filter . bShouldEvaluateConditions & & ! EvaluateSlotConditions ( ConditionContextData , SmartObjectRuntime , SlotHandle ) )
{
continue ;
}
OutResults . Add ( SlotHandle ) ;
}
2021-09-28 13:33:00 -04:00
}
}
2022-03-02 13:25:37 -05:00
void USmartObjectSubsystem : : FindMatchingSlotDefinitionIndices ( const USmartObjectDefinition & Definition , const FSmartObjectRequestFilter & Filter , TArray < int32 > & OutValidIndices )
2021-09-28 13:33:00 -04:00
{
2022-03-02 22:33:53 -05:00
const ESmartObjectTagFilteringPolicy UserTagsFilteringPolicy = Definition . GetUserTagsFilteringPolicy ( ) ;
2022-03-02 13:25:37 -05:00
// Define our Tags filtering predicate
auto MatchesTagQueryFunc = [ ] ( const FGameplayTagQuery & Query , const FGameplayTagContainer & Tags ) { return Query . IsEmpty ( ) | | Query . Matches ( Tags ) ; } ;
// When filter policy is to use combined we can validate the user tag query of the parent object first
// since they can't be merge so we need to apply them one after the other.
// For activity requirements we have to merge parent and slot tags together before testing.
2022-03-02 22:33:53 -05:00
if ( UserTagsFilteringPolicy = = ESmartObjectTagFilteringPolicy : : Combine
2022-03-02 13:25:37 -05:00
& & ! MatchesTagQueryFunc ( Definition . GetUserTagFilter ( ) , Filter . UserTags ) )
2021-09-28 13:33:00 -04:00
{
2022-03-02 13:25:37 -05:00
return ;
}
// Apply filter to individual slots
const TConstArrayView < FSmartObjectSlotDefinition > SlotDefinitions = Definition . GetSlots ( ) ;
OutValidIndices . Reserve ( SlotDefinitions . Num ( ) ) ;
for ( int i = 0 ; i < SlotDefinitions . Num ( ) ; + + i )
{
const FSmartObjectSlotDefinition & Slot = SlotDefinitions [ i ] ;
2022-09-08 19:27:10 -04:00
// Filter out mismatching behavior type (if specified)
if ( ! Filter . BehaviorDefinitionClasses . IsEmpty ( ) )
{
bool bMatchesAny = false ;
for ( const TSubclassOf < USmartObjectBehaviorDefinition > & BehaviorDefinitionClass : Filter . BehaviorDefinitionClasses )
{
2023-07-18 08:42:51 -04:00
if ( Definition . GetBehaviorDefinition ( i , BehaviorDefinitionClass ) ! = nullptr )
2022-09-08 19:27:10 -04:00
{
bMatchesAny = true ;
break ;
}
}
if ( ! bMatchesAny )
{
continue ;
}
}
2022-03-02 13:25:37 -05:00
2022-03-02 22:33:53 -05:00
// Filter out slots based on their activity tags
FGameplayTagContainer ActivityTags ;
Definition . GetSlotActivityTags ( Slot , ActivityTags ) ;
if ( ! MatchesTagQueryFunc ( Filter . ActivityRequirements , ActivityTags ) )
2022-03-02 13:25:37 -05:00
{
2022-03-02 22:33:53 -05:00
continue ;
2022-03-02 13:25:37 -05:00
}
2022-03-02 22:33:53 -05:00
// Filter out slots based on their TagQuery applied on provided User Tags
// - override: we only run query from the slot if provided otherwise we run the one from the parent object
// - combine: we run slot query (parent query was applied before processing individual slots)
if ( UserTagsFilteringPolicy = = ESmartObjectTagFilteringPolicy : : Combine
& & ! MatchesTagQueryFunc ( Slot . UserTagFilter , Filter . UserTags ) )
{
continue ;
}
if ( UserTagsFilteringPolicy = = ESmartObjectTagFilteringPolicy : : Override
& & ! MatchesTagQueryFunc ( ( Slot . UserTagFilter . IsEmpty ( ) ? Definition . GetUserTagFilter ( ) : Slot . UserTagFilter ) , Filter . UserTags ) )
{
continue ;
2022-03-02 13:25:37 -05:00
}
OutValidIndices . Add ( i ) ;
2021-09-28 13:33:00 -04:00
}
}
2023-02-02 18:43:13 -05:00
FSmartObjectRequestResult USmartObjectSubsystem : : FindSmartObject ( const FSmartObjectRequest & Request , const FConstStructView UserData ) const
2021-09-28 13:33:00 -04:00
{
2022-02-21 01:10:34 -05:00
TArray < FSmartObjectRequestResult > Results ;
2023-02-02 18:43:13 -05:00
FindSmartObjects ( Request , Results , UserData ) ;
2022-02-17 03:40:43 -05:00
2022-02-21 01:10:34 -05:00
return Results . Num ( ) ? Results . Top ( ) : FSmartObjectRequestResult ( ) ;
2021-09-28 13:33:00 -04:00
}
2023-02-02 18:43:13 -05:00
bool USmartObjectSubsystem : : FindSmartObjects ( const FSmartObjectRequest & Request , TArray < FSmartObjectRequestResult > & OutResults , const FConstStructView UserData ) const
2021-09-28 13:33:00 -04:00
{
2022-02-17 03:40:43 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE_STR ( " SmartObject_FindAllResults " ) ;
2022-11-24 14:53:52 -05:00
if ( ! bRuntimeInitialized )
2022-02-21 01:10:34 -05:00
{
2022-04-04 13:22:01 -04:00
// Do not report warning if runtime was explicitly disabled by CVar
UE_CVLOG_UELOG ( ! UE : : SmartObject : : bDisableRuntime , this , LogSmartObject , Warning ,
TEXT ( " Can't find smart objet before runtime gets initialized (i.e. InitializeRuntime gets called). " ) ) ;
2022-02-21 01:10:34 -05:00
return false ;
}
2021-09-28 13:33:00 -04:00
const FSmartObjectRequestFilter & Filter = Request . Filter ;
2022-02-21 01:10:34 -05:00
TArray < FSmartObjectHandle > QueryResults ;
checkfSlow ( SpacePartition ! = nullptr , TEXT ( " Space partition is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
SpacePartition - > Find ( Request . QueryBox , QueryResults ) ;
for ( const FSmartObjectHandle SmartObjectHandle : QueryResults )
{
const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectHandle ) ;
2022-03-16 03:47:02 -04:00
checkf ( SmartObjectRuntime ! = nullptr , TEXT ( " Results returned by the space partition are expected to be valid. " ) ) ;
if ( ! Request . QueryBox . IsInside ( SmartObjectRuntime - > GetTransform ( ) . GetLocation ( ) ) )
2021-09-28 13:33:00 -04:00
{
2022-02-21 01:10:34 -05:00
continue ;
}
2022-03-18 12:31:49 -04:00
TArray < FSmartObjectSlotHandle > SlotHandles ;
2023-07-18 08:42:51 -04:00
FindSlots ( SmartObjectHandle , * SmartObjectRuntime , Filter , SlotHandles , UserData ) ;
2022-02-21 01:10:34 -05:00
OutResults . Reserve ( OutResults . Num ( ) + SlotHandles . Num ( ) ) ;
2022-03-18 12:31:49 -04:00
for ( FSmartObjectSlotHandle SlotHandle : SlotHandles )
{
2022-02-21 01:10:34 -05:00
OutResults . Emplace ( SmartObjectHandle , SlotHandle ) ;
2022-03-18 12:31:49 -04:00
}
2022-02-21 01:10:34 -05:00
}
2021-11-26 15:48:13 -05:00
return ( OutResults . Num ( ) > 0 ) ;
2021-09-28 13:33:00 -04:00
}
2023-09-05 11:29:58 -04:00
bool USmartObjectSubsystem : : FindSmartObjectsInList ( const FSmartObjectRequestFilter & Filter , TConstArrayView < AActor * > ActorList , TArray < FSmartObjectRequestResult > & OutResults , const FConstStructView UserData ) const
{
// Iterate the actor list, if it has a Smart Object Component in it, then find all the slots and populate our results
// We don't want to use a Query Box here because that could include smart objects from outside of this ActorList.
for ( const AActor * SearchActor : ActorList )
{
if ( ! SearchActor )
{
continue ;
}
const USmartObjectComponent * FoundComponent = SearchActor - > GetComponentByClass < USmartObjectComponent > ( ) ;
if ( ! FoundComponent )
{
continue ;
}
const FSmartObjectHandle SmartObjectHandle = FoundComponent - > GetRegisteredHandle ( ) ;
const FSmartObjectRuntime * SmartObjectRuntime = SmartObjectHandle . IsValid ( ) ? RuntimeSmartObjects . Find ( SmartObjectHandle ) : nullptr ;
if ( ! SmartObjectRuntime )
{
continue ;
}
// We found a valid smart object runtime, populate our results with it's slots
TArray < FSmartObjectSlotHandle > SlotHandles ;
FindSlots ( SmartObjectHandle , * SmartObjectRuntime , Filter , SlotHandles , UserData ) ;
OutResults . Reserve ( OutResults . Num ( ) + SlotHandles . Num ( ) ) ;
for ( FSmartObjectSlotHandle SlotHandle : SlotHandles )
{
OutResults . Emplace ( SmartObjectHandle , SlotHandle ) ;
}
}
// Successful if we found some smart objects
return ( OutResults . Num ( ) > 0 ) ;
}
2023-09-05 16:59:33 -04:00
bool USmartObjectSubsystem : : FindSmartObjectsInTargetingRequest ( const FSmartObjectRequestFilter & Filter , const FTargetingRequestHandle TargetingHandle , TArray < FSmartObjectRequestResult > & OutResults , const FConstStructView UserData ) const
{
if ( FTargetingDefaultResultsSet * Results = FTargetingDefaultResultsSet : : Find ( TargetingHandle ) )
{
for ( const FTargetingDefaultResultData & Data : Results - > TargetResults )
{
AActor * ResultActor = Data . HitResult . GetActor ( ) ;
if ( ! ResultActor )
{
continue ;
}
const USmartObjectComponent * FoundComponent = ResultActor - > GetComponentByClass < USmartObjectComponent > ( ) ;
if ( ! FoundComponent )
{
continue ;
}
const FSmartObjectHandle SmartObjectHandle = FoundComponent - > GetRegisteredHandle ( ) ;
const FSmartObjectRuntime * SmartObjectRuntime = SmartObjectHandle . IsValid ( ) ? RuntimeSmartObjects . Find ( SmartObjectHandle ) : nullptr ;
if ( ! SmartObjectRuntime )
{
continue ;
}
// We found a valid smart object runtime, populate our results with it's slots
TArray < FSmartObjectSlotHandle > SlotHandles ;
FindSlots ( SmartObjectHandle , * SmartObjectRuntime , Filter , SlotHandles , UserData ) ;
OutResults . Reserve ( OutResults . Num ( ) + SlotHandles . Num ( ) ) ;
for ( FSmartObjectSlotHandle SlotHandle : SlotHandles )
{
OutResults . Emplace ( SmartObjectHandle , SlotHandle ) ;
}
}
}
// Successful if we found some smart objects
return ( OutResults . Num ( ) > 0 ) ;
}
2021-09-28 13:33:00 -04:00
void USmartObjectSubsystem : : RegisterCollectionInstances ( )
{
2022-11-24 14:53:52 -05:00
for ( TActorIterator < ASmartObjectPersistentCollection > It ( GetWorld ( ) ) ; It ; + + It )
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
ASmartObjectPersistentCollection * Collection = ( * It ) ;
2021-09-28 13:33:00 -04:00
if ( IsValid ( Collection ) & & Collection - > IsRegistered ( ) = = false )
{
2022-01-12 16:15:32 -05:00
const ESmartObjectCollectionRegistrationResult Result = RegisterCollection ( * Collection ) ;
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( Collection , LogSmartObject , Log ,
2024-02-08 13:21:26 -05:00
TEXT ( " Collection '%s' registration from USmartObjectSubsystem initialization - %s " ) , * Collection - > GetPathName ( ) , * UEnum : : GetValueAsString ( Result ) ) ;
2021-09-28 13:33:00 -04:00
}
}
}
2023-09-05 16:59:33 -04:00
2022-11-24 14:53:52 -05:00
ESmartObjectCollectionRegistrationResult USmartObjectSubsystem : : RegisterCollection ( ASmartObjectPersistentCollection & InCollection )
2021-09-28 13:33:00 -04:00
{
if ( ! IsValid ( & InCollection ) )
{
2022-01-12 16:15:32 -05:00
return ESmartObjectCollectionRegistrationResult : : Failed_InvalidCollection ;
2021-09-28 13:33:00 -04:00
}
if ( InCollection . IsRegistered ( ) )
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Error , TEXT ( " Trying to register collection '%s' more than once " ) , * InCollection . GetPathName ( ) ) ;
2022-01-12 16:15:32 -05:00
return ESmartObjectCollectionRegistrationResult : : Failed_AlreadyRegistered ;
2021-09-28 13:33:00 -04:00
}
2022-01-12 16:15:32 -05:00
ESmartObjectCollectionRegistrationResult Result = ESmartObjectCollectionRegistrationResult : : Succeeded ;
2021-09-28 13:33:00 -04:00
2022-11-24 14:53:52 -05:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Log , TEXT ( " Adding collection '%s' registered with %d entries " ) , * InCollection . GetName ( ) , InCollection . GetEntries ( ) . Num ( ) ) ;
InCollection . GetMutableSmartObjectContainer ( ) . ValidateDefinitions ( ) ;
SmartObjectContainer . Append ( InCollection . GetSmartObjectContainer ( ) ) ;
RegisteredCollections . Add ( & InCollection ) ;
2022-12-05 08:19:29 -05:00
// We want to add the new collection to the "simulation" only if the Runtime part of the subsystem has been initialized.
// SmartObjectContainer is added to simulation in one go in InitializeRuntime.
2023-07-18 08:42:51 -04:00
if ( bRuntimeInitialized )
2022-11-24 14:53:52 -05:00
{
AddContainerToSimulation ( InCollection . GetSmartObjectContainer ( ) ) ;
}
2022-10-10 12:00:30 -04:00
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2022-11-24 14:53:52 -05:00
// Broadcast after rebuilding so listeners will be able to access up-to-date data
OnMainCollectionChanged . Broadcast ( ) ;
2021-09-28 13:33:00 -04:00
# endif // WITH_EDITOR
2021-11-02 11:12:43 -04:00
2022-11-24 14:53:52 -05:00
InCollection . OnRegistered ( ) ;
Result = ESmartObjectCollectionRegistrationResult : : Succeeded ;
2022-01-12 16:15:32 -05:00
return Result ;
2021-09-28 13:33:00 -04:00
}
2022-11-24 14:53:52 -05:00
void USmartObjectSubsystem : : UnregisterCollection ( ASmartObjectPersistentCollection & InCollection )
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
if ( RegisteredCollections . Remove ( & InCollection ) )
{
SmartObjectContainer . Remove ( InCollection . GetSmartObjectContainer ( ) ) ;
2023-07-18 08:42:51 -04:00
for ( const FSmartObjectCollectionEntry & Entry : InCollection . GetSmartObjectContainer ( ) . GetEntries ( ) )
2022-11-24 14:53:52 -05:00
{
2023-07-18 08:42:51 -04:00
FSmartObjectRuntime SORuntime ;
// even though we did add this entry to RuntimeSmartObjects at some point it could have been removed
// when the smart object in question got disabled or removed
if ( RuntimeSmartObjects . RemoveAndCopyValue ( Entry . GetHandle ( ) , SORuntime ) )
2022-11-24 14:53:52 -05:00
{
2023-07-18 08:42:51 -04:00
if ( USmartObjectComponent * SOComponent = Entry . GetComponent ( ) )
2022-11-24 14:53:52 -05:00
{
2023-07-18 08:42:51 -04:00
UnbindComponentFromSimulationInternal ( * SOComponent , SORuntime ) ;
2022-11-24 14:53:52 -05:00
}
2023-07-18 08:42:51 -04:00
DestroyRuntimeInstanceInternal ( Entry . GetHandle ( ) , SORuntime ) ;
2022-11-24 14:53:52 -05:00
}
}
InCollection . OnUnregistered ( ) ;
}
else
2021-09-28 13:33:00 -04:00
{
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Verbose ,
2024-02-08 13:21:26 -05:00
TEXT ( " Ignoring unregistration of collection '%s' since this is not one of the previously registered collections. " ) , * InCollection . GetPathName ( ) ) ;
2021-09-28 13:33:00 -04:00
return ;
}
2022-11-24 14:53:52 -05:00
}
2021-09-28 13:33:00 -04:00
2022-11-24 14:53:52 -05:00
void USmartObjectSubsystem : : AddContainerToSimulation ( const FSmartObjectContainer & InSmartObjectContainer )
{
2024-02-08 13:21:26 -05:00
if ( ! ensureMsgf ( bRuntimeInitialized , TEXT ( " %hs called before InitializeRuntime, this is not expected to happen. " ) , __FUNCTION__ ) )
2022-12-05 08:19:29 -05:00
{
return ;
}
2022-11-24 14:53:52 -05:00
for ( const FSmartObjectCollectionEntry & Entry : InSmartObjectContainer . GetEntries ( ) )
2022-03-16 03:47:02 -04:00
{
2022-11-24 14:53:52 -05:00
const USmartObjectDefinition * Definition = InSmartObjectContainer . GetDefinitionForEntry ( Entry ) ;
USmartObjectComponent * Component = Entry . GetComponent ( ) ;
if ( Definition = = nullptr | | Definition - > IsValid ( ) = = false )
{
UE_CVLOG_UELOG ( Component ! = nullptr , Component - > GetOwner ( ) , LogSmartObject , Error ,
TEXT ( " Skipped runtime data creation for SmartObject %s: Invalid definition " ) , * GetNameSafe ( Component - > GetOwner ( ) ) ) ;
continue ;
}
if ( Component ! = nullptr )
{
2024-02-08 13:21:26 -05:00
if ( const USmartObjectDefinition * ComponentDefinition = Component - > GetDefinition ( ) )
{
UE_CVLOG_UELOG ( ComponentDefinition ! = Definition , this , LogSmartObject , Warning ,
TEXT ( " Definition '%s' specified in component for '%s' differs from '%s' specified in the collection entry. Collection should be rebuild. " ) ,
* ComponentDefinition - > GetPathName ( ) ,
* UE : : SmartObject : : DebugGetComponentName ( * Component ) ,
* Definition - > GetFullName ( ) ) ;
// When component is available we add it to the simulation along with its collection entry to create the runtime instance and bound them together.
Component - > SetRegisteredHandle ( Entry . GetHandle ( ) , ESmartObjectRegistrationType : : BindToExistingInstance ) ;
AddComponentToSimulation ( * Component , Entry ) ;
continue ;
}
UE_VLOG_UELOG ( Component - > GetOwner ( ) , LogSmartObject , Error ,
TEXT ( " Component in '%s' doesn't have a valid definition. Adding based on the collection entry but collection should be rebuild. " ) ,
* UE : : SmartObject : : DebugGetComponentName ( * Component ) ) ;
2022-11-24 14:53:52 -05:00
}
2024-02-08 13:21:26 -05:00
// Otherwise we create the runtime instance based on the information from the collection and component will be bound later (e.g. on load)
AddCollectionEntryToSimulation ( Entry , * Definition , nullptr ) ;
2022-03-16 03:47:02 -04:00
}
2021-09-28 13:33:00 -04:00
}
2024-02-08 13:21:26 -05:00
USmartObjectComponent * USmartObjectSubsystem : : GetSmartObjectComponent ( const FSmartObjectClaimHandle & ClaimHandle , const ETrySpawnActorIfDehydrated TrySpawnActorIfDehydrated ) const
2021-09-28 13:33:00 -04:00
{
2024-01-30 10:11:23 -05:00
const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( ClaimHandle . SmartObjectHandle ) ;
2024-02-08 13:21:26 -05:00
return SmartObjectRuntime ! = nullptr ? SmartObjectRuntime - > GetOwnerComponent ( TrySpawnActorIfDehydrated ) : nullptr ;
2021-09-28 13:33:00 -04:00
}
2024-02-08 13:21:26 -05:00
USmartObjectComponent * USmartObjectSubsystem : : GetSmartObjectComponentByRequestResult ( const FSmartObjectRequestResult & Result , const ETrySpawnActorIfDehydrated TrySpawnActorIfDehydrated ) const
2022-09-12 19:23:55 -04:00
{
2024-01-30 10:11:23 -05:00
const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Result . SmartObjectHandle ) ;
2024-02-08 13:21:26 -05:00
return SmartObjectRuntime ! = nullptr ? SmartObjectRuntime - > GetOwnerComponent ( TrySpawnActorIfDehydrated ) : nullptr ;
2022-09-12 19:23:55 -04:00
}
2022-02-17 03:40:43 -05:00
void USmartObjectSubsystem : : InitializeRuntime ( )
2021-09-28 13:33:00 -04:00
{
2022-04-04 13:22:01 -04:00
if ( UE : : SmartObject : : bDisableRuntime )
{
2024-02-08 13:21:26 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Runtime explicitly disabled by CVar. Initialization skipped in %hs. " ) , __FUNCTION__ ) ;
2022-04-04 13:22:01 -04:00
return ;
}
2022-02-21 01:10:34 -05:00
// Initialize spatial representation structure
checkfSlow ( * SpacePartitionClass ! = nullptr , TEXT ( " Partition class is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
SpacePartition = NewObject < USmartObjectSpacePartition > ( this , SpacePartitionClass ) ;
2022-12-05 08:19:29 -05:00
SpacePartition - > SetBounds ( SmartObjectContainer . GetBounds ( ) ) ;
2021-11-02 11:12:43 -04:00
// Note that we use our own flag instead of relying on World.HasBegunPlay() since world might not be marked
// as BegunPlay immediately after subsystem OnWorldBeingPlay gets called (e.g. waiting game mode to be ready on clients)
2022-12-05 08:19:29 -05:00
// Setting bRuntimeInitialized at this point since the following code assumes the SpatialPartition has been created
// and EntityManager cached.
bRuntimeInitialized = true ;
AddContainerToSimulation ( SmartObjectContainer ) ;
2022-11-24 14:53:52 -05:00
2023-02-02 18:43:13 -05:00
UE_CVLOG_UELOG ( PendingSmartObjectRegistration . Num ( ) > 0 , this , LogSmartObject , VeryVerbose ,
TEXT ( " SmartObjectSubsystem: Handling %d pending registrations during runtime initialization. " ) , PendingSmartObjectRegistration . Num ( ) ) ;
2022-11-30 07:17:21 -05:00
2022-11-24 14:53:52 -05:00
for ( TObjectPtr < USmartObjectComponent > & SOComponent : PendingSmartObjectRegistration )
{
2023-02-02 18:43:13 -05:00
// ensure the SOComponent is still valid - things could have happened to it between adding to PendingSmartObjectRegistration and it being processed here
2022-11-24 14:53:52 -05:00
if ( SOComponent & & IsValid ( SOComponent ) )
{
RegisterSmartObject ( * SOComponent ) ;
}
}
PendingSmartObjectRegistration . Empty ( ) ;
2022-02-03 14:03:16 -05:00
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
// Refresh debug draw
if ( RenderingActor ! = nullptr )
{
RenderingActor - > MarkComponentsRenderStateDirty ( ) ;
2022-03-18 12:31:49 -04:00
}
2022-02-21 01:10:34 -05:00
# endif // UE_ENABLE_DEBUG_DRAWING
2021-09-28 13:33:00 -04:00
}
2022-02-17 03:40:43 -05:00
void USmartObjectSubsystem : : CleanupRuntime ( )
{
2022-03-16 03:47:02 -04:00
// Process component list first so they can be notified before we destroy their associated runtime instance
2023-05-26 12:48:38 -04:00
for ( USmartObjectComponent * Component : RegisteredSOComponents )
2022-03-16 03:47:02 -04:00
{
2022-04-27 16:53:50 -04:00
// Make sure component was registered to simulation (e.g. Valid associated definition)
2023-05-26 12:48:38 -04:00
if ( Component ! = nullptr & & Component - > IsBoundToSimulation ( ) )
2022-03-16 03:47:02 -04:00
{
RemoveComponentFromSimulation ( * Component ) ;
}
}
2022-10-26 19:34:40 -04:00
// Cleanup all remaining entries (e.g. associated to unloaded SmartObjectComponents)
2023-07-18 08:42:51 -04:00
for ( auto It ( RuntimeSmartObjects . CreateIterator ( ) ) ; It ; + + It )
2022-02-17 03:40:43 -05:00
{
2023-07-18 08:42:51 -04:00
DestroyRuntimeInstanceInternal ( It . Key ( ) , It . Value ( ) ) ;
2022-11-24 14:53:52 -05:00
}
2023-07-18 08:42:51 -04:00
2022-10-26 19:34:40 -04:00
RuntimeSmartObjects . Reset ( ) ;
2022-02-17 03:40:43 -05:00
2022-11-24 14:53:52 -05:00
bRuntimeInitialized = false ;
2022-02-17 03:40:43 -05:00
2022-11-24 14:53:52 -05:00
RegisteredCollections . Reset ( ) ;
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
// Refresh debug draw
if ( RenderingActor ! = nullptr )
{
RenderingActor - > MarkComponentsRenderStateDirty ( ) ;
}
# endif // UE_ENABLE_DEBUG_DRAWING
2022-02-17 03:40:43 -05:00
}
void USmartObjectSubsystem : : OnWorldBeginPlay ( UWorld & World )
{
Super : : OnWorldBeginPlay ( World ) ;
InitializeRuntime ( ) ;
}
2022-08-29 11:12:18 -04:00
void USmartObjectSubsystem : : Deinitialize ( )
{
2022-11-24 14:53:52 -05:00
CleanupRuntime ( ) ;
2022-08-29 11:12:18 -04:00
Super : : Deinitialize ( ) ;
}
2022-11-23 08:17:20 -05:00
bool USmartObjectSubsystem : : ShouldCreateSubsystem ( UObject * Outer ) const
{
if ( Super : : ShouldCreateSubsystem ( Outer ) )
{
2023-01-18 10:52:51 -05:00
if ( const UWorld * OuterWorld = Cast < UWorld > ( Outer ) )
2022-11-23 08:17:20 -05:00
{
return OuterWorld - > IsNetMode ( NM_Client ) = = false ;
}
}
return false ;
}
2023-09-05 11:29:58 -04:00
bool USmartObjectSubsystem : : IsRunningOnServer ( ) const
{
if ( const UWorld * World = GetWorld ( ) )
{
return World - > GetNetMode ( ) < NM_Client ;
}
return false ;
}
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2022-11-24 14:53:52 -05:00
FBox USmartObjectSubsystem : : ComputeBounds ( const UWorld & World ) const
2021-11-02 11:12:43 -04:00
{
FBox Bounds ( ForceInitToZero ) ;
if ( const UWorldPartition * WorldPartition = World . GetWorldPartition ( ) )
{
2022-06-13 10:49:55 -04:00
Bounds = WorldPartition - > GetRuntimeWorldBounds ( ) ;
2021-11-02 11:12:43 -04:00
}
else if ( const ULevel * PersistentLevel = World . PersistentLevel . Get ( ) )
{
if ( PersistentLevel - > LevelBoundsActor . IsValid ( ) )
{
Bounds = PersistentLevel - > LevelBoundsActor . Get ( ) - > GetComponentsBoundingBox ( ) ;
}
else
{
Bounds = ALevelBounds : : CalculateLevelBounds ( PersistentLevel ) ;
}
}
else
{
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " Unable to determine world bounds: no world partition or persistent level. " ) ) ;
}
2022-11-24 14:53:52 -05:00
return Bounds ;
2021-11-02 11:12:43 -04:00
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : PopulateCollection ( ASmartObjectPersistentCollection & InCollection ) const
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
TArray < USmartObjectComponent * > RelevantComponents ;
if ( GetRegisteredSmartObjectsCompatibleWithCollection ( InCollection , RelevantComponents ) > 0 )
{
InCollection . AppendToCollection ( RelevantComponents ) ;
}
2021-09-28 13:33:00 -04:00
}
2023-02-02 18:43:13 -05:00
int32 USmartObjectSubsystem : : GetRegisteredSmartObjectsCompatibleWithCollection (
const ASmartObjectPersistentCollection & InCollection ,
TArray < USmartObjectComponent * > & OutRelevantComponents
) const
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
const int32 InitialCount = OutRelevantComponents . Num ( ) ;
if ( bIsPartitionedWorld = = false )
{
const ULevel * MyLevel = InCollection . GetLevel ( ) ;
const ULevelStreaming * MyLevelStreaming = ULevelStreaming : : FindStreamingLevel ( MyLevel ) ;
const bool bCollectionShouldAlwaysBeLoaded = ( MyLevelStreaming = = nullptr ) | | MyLevelStreaming - > ShouldBeAlwaysLoaded ( ) ;
const ULevel * PreviousLevel = nullptr ;
bool bPreviousLevelValid = false ;
for ( const TObjectPtr < USmartObjectComponent > & Component : RegisteredSOComponents )
{
check ( Component ) ;
if ( Component - > GetCanBePartOfCollection ( ) = = false )
{
continue ;
}
const ULevel * OwnerLevel = Component - > GetComponentLevel ( ) ;
bool bValid = bPreviousLevelValid ;
if ( OwnerLevel ! = PreviousLevel )
{
const ULevelStreaming * LevelStreaming = ULevelStreaming : : FindStreamingLevel ( OwnerLevel ) ;
bValid = ( MyLevelStreaming = = LevelStreaming )
| | ( bCollectionShouldAlwaysBeLoaded & & LevelStreaming & & LevelStreaming - > ShouldBeAlwaysLoaded ( ) ) ;
}
if ( bValid )
{
OutRelevantComponents . Add ( Component ) ;
}
bPreviousLevelValid = bValid ;
PreviousLevel = OwnerLevel ;
}
}
else
{
TArray < const UDataLayerInstance * > DataLayers = InCollection . GetDataLayerInstances ( ) ;
const bool bPersistentLevelCollection = ( DataLayers . Num ( ) = = 0 ) ;
for ( const TObjectPtr < USmartObjectComponent > & Component : RegisteredSOComponents )
{
check ( Component ) ;
if ( Component - > GetCanBePartOfCollection ( ) = = false )
{
continue ;
}
2023-01-18 10:52:51 -05:00
if ( const AActor * Owner = Component - > GetOwner ( ) )
2022-11-24 14:53:52 -05:00
{
const bool bInPersistentLayer = ( Owner - > HasDataLayers ( ) = = false ) ;
if ( bPersistentLevelCollection = = bInPersistentLayer )
{
if ( bPersistentLevelCollection )
{
OutRelevantComponents . Add ( Component ) ;
}
else
{
for ( const UDataLayerInstance * DataLayerInstance : DataLayers )
{
if ( Owner - > ContainsDataLayer ( DataLayerInstance ) )
{
OutRelevantComponents . Add ( Component ) ;
// breaking here since at the moment we only support registering smart objects only
// with a single collection
break ;
}
}
}
}
}
}
}
return ( OutRelevantComponents . Num ( ) - InitialCount ) ;
}
void USmartObjectSubsystem : : IterativelyBuildCollections ( )
{
2024-02-08 13:21:26 -05:00
ensureMsgf ( bIsPartitionedWorld , TEXT ( " %hs expected to be called in World Partitioned worlds " ) , __FUNCTION__ ) ;
2022-11-24 14:53:52 -05:00
if ( RegisteredSOComponents . Num ( ) = = 0 )
2021-09-28 13:33:00 -04:00
{
return ;
}
2023-04-18 09:40:15 -04:00
TArray < USmartObjectComponent * > ComponentsToRestore = RegisteredSOComponents ;
2022-11-24 14:53:52 -05:00
TArray < USmartObjectComponent * > RelevantComponents ;
for ( TWeakObjectPtr < ASmartObjectPersistentCollection > & WeakCollection : RegisteredCollections )
{
if ( ASmartObjectPersistentCollection * Collection = WeakCollection . Get ( ) )
{
RelevantComponents . Reset ( ) ;
2021-09-28 13:33:00 -04:00
2022-11-24 14:53:52 -05:00
if ( GetRegisteredSmartObjectsCompatibleWithCollection ( * Collection , RelevantComponents ) > 0 )
{
Collection - > AppendToCollection ( RelevantComponents ) ;
2021-09-28 13:33:00 -04:00
2023-04-18 09:40:15 -04:00
// A component can belong to only a single collection.
// We remove objects added to the collection so that they do not get added to another collection.
// Also, the subsequent GetRegisteredSmartObjectsCompatibleWithCollection calls get less data to consider.
2022-11-24 14:53:52 -05:00
for ( USmartObjectComponent * SOComponent : RelevantComponents )
{
RegisteredSOComponents . RemoveSingleSwap ( SOComponent ) ;
}
}
}
}
2023-04-18 09:40:15 -04:00
// Restore registered components so they can be unregistered properly by the normal streaming flow (i.e. not reporting any warnings/errors)
RegisteredSOComponents = MoveTemp ( ComponentsToRestore ) ;
2021-09-28 13:33:00 -04:00
}
# endif // WITH_EDITOR
2022-11-24 14:53:52 -05:00
# if WITH_EDITORONLY_DATA
void USmartObjectSubsystem : : CreatePersistentCollectionFromDeprecatedData ( UWorld & World , const ADEPRECATED_SmartObjectCollection & DeprecatedCollection )
{
2022-12-05 08:19:29 -05:00
if ( DeprecatedCollection . CollectionEntries . Num ( ) = = 0 )
{
// we ignore the empty deprecated collections - we used to always create these even if no smart objects were being used
// and an empty collection is an indication of such a case. No point in creating a replacement for such a collection.
return ;
}
2022-11-24 14:53:52 -05:00
FActorSpawnParameters SpawnParams ;
SpawnParams . OverrideLevel = DeprecatedCollection . GetLevel ( ) ;
if ( ASmartObjectPersistentCollection * NewCollection = World . SpawnActor < ASmartObjectPersistentCollection > ( SpawnParams ) )
{
NewCollection - > SmartObjectContainer . Bounds = DeprecatedCollection . Bounds ;
NewCollection - > SmartObjectContainer . CollectionEntries = DeprecatedCollection . CollectionEntries ;
NewCollection - > SmartObjectContainer . RegisteredIdToObjectMap = DeprecatedCollection . RegisteredIdToObjectMap ;
NewCollection - > SmartObjectContainer . Definitions = DeprecatedCollection . Definitions ;
NewCollection - > bUpdateCollectionOnSmartObjectsChange = DeprecatedCollection . bBuildCollectionAutomatically ;
}
}
# endif // WITH_EDITORONLY_DATA
2021-09-28 13:33:00 -04:00
# if WITH_SMARTOBJECT_DEBUG
void USmartObjectSubsystem : : DebugUnregisterAllSmartObjects ( )
{
2023-05-26 12:48:38 -04:00
for ( USmartObjectComponent * Cmp : RegisteredSOComponents )
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
if ( Cmp ! = nullptr & & RuntimeSmartObjects . Find ( Cmp - > GetRegisteredHandle ( ) ) ! = nullptr )
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
RemoveComponentFromSimulation ( * Cmp ) ;
2021-09-28 13:33:00 -04:00
}
}
}
void USmartObjectSubsystem : : DebugRegisterAllSmartObjects ( )
{
2022-03-16 03:47:02 -04:00
for ( USmartObjectComponent * Cmp : RegisteredSOComponents )
{
if ( Cmp ! = nullptr )
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
const FSmartObjectCollectionEntry * Entry = SmartObjectContainer . GetEntries ( ) . FindByPredicate (
2022-03-16 03:47:02 -04:00
[ Handle = Cmp - > GetRegisteredHandle ( ) ] ( const FSmartObjectCollectionEntry & CollectionEntry )
{
return CollectionEntry . GetHandle ( ) = = Handle ;
} ) ;
// In this debug command we register back components that were already part of the simulation but
// removed using debug command 'ai.debug.so.UnregisterAllSmartObjects'.
// We need to find associated collection entry and pass it back so the callbacks can be bound properly
if ( Entry & & RuntimeSmartObjects . Find ( Entry - > GetHandle ( ) ) = = nullptr )
{
AddComponentToSimulation ( * Cmp , * Entry ) ;
}
2021-09-28 13:33:00 -04:00
}
}
}
2022-02-17 03:40:43 -05:00
void USmartObjectSubsystem : : DebugInitializeRuntime ( )
{
// do not initialize more than once or on a GameWorld
2022-11-24 14:53:52 -05:00
if ( bRuntimeInitialized | | GetWorldRef ( ) . IsGameWorld ( ) )
2022-02-17 03:40:43 -05:00
{
return ;
}
InitializeRuntime ( ) ;
}
void USmartObjectSubsystem : : DebugCleanupRuntime ( )
{
// do not cleanup more than once or on a GameWorld
2022-11-24 14:53:52 -05:00
if ( ! bRuntimeInitialized | | GetWorldRef ( ) . IsGameWorld ( ) )
2022-02-17 03:40:43 -05:00
{
return ;
}
CleanupRuntime ( ) ;
}
2022-09-28 01:06:15 -04:00
# endif // WITH_SMARTOBJECT_DEBUG
2022-12-07 13:05:23 -05:00
//----------------------------------------------------------------------//
// deprecated functions implementations
//----------------------------------------------------------------------//
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2024-02-08 13:21:26 -05:00
bool USmartObjectSubsystem : : RegisterSmartObjectInternal ( USmartObjectComponent & SmartObjectComponent )
2022-12-07 13:05:23 -05:00
{
2024-02-08 13:21:26 -05:00
return RegisterSmartObject ( ( SmartObjectComponent ) ) ;
2022-12-07 13:05:23 -05:00
}
2024-02-08 13:21:26 -05:00
void USmartObjectSubsystem : : BindComponentToSimulation ( USmartObjectComponent & SmartObjectComponent )
{
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
if ( ensureAlwaysMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " Unable to bind %s using handle '%s' since an associated runtime doesn't exist. " ) ,
* UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ,
* LexToString ( SmartObjectComponent . GetRegisteredHandle ( ) ) ) )
{
// Simply bind the newly available component to its active runtime instance
BindComponentToSimulationInternal ( SmartObjectComponent , * SmartObjectRuntime ) ;
}
}
void USmartObjectSubsystem : : UnbindComponentFromSimulation ( USmartObjectComponent & SmartObjectComponent )
{
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
if ( ensureAlwaysMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " Unable to bind %s using handle '%s' since an associated runtime doesn't exist. " ) ,
* UE : : SmartObject : : DebugGetComponentName ( SmartObjectComponent ) ,
* LexToString ( SmartObjectComponent . GetRegisteredHandle ( ) ) ) )
{
UnbindComponentFromSimulationInternal ( SmartObjectComponent , * SmartObjectRuntime ) ;
}
}
bool USmartObjectSubsystem : : RemoveRuntimeInstanceFromSimulation ( const FSmartObjectHandle Handle , USmartObjectComponent * SmartObjectComponent )
{
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Removing SmartObject '%s' from runtime simulation%s. " ) ,
* LexToString ( Handle ) ,
SmartObjectComponent ? * FString : : Printf ( TEXT ( " associated to %s " ) , * SmartObjectComponent - > GetName ( ) ) : TEXT ( " " ) ) ;
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Handle ) ;
# if WITH_SMARTOBJECT_DEBUG
ensureMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " RemoveFromSimulation is an internal call and should only be used for objects still part of the simulation " ) ) ;
# endif // WITH_SMARTOBJECT_DEBUG
if ( SmartObjectRuntime = = nullptr )
{
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " %hs called with %s handle and no corresponding SmartObjectRuntime " )
, __FUNCTION__
, Handle . IsValid ( ) ? * FString : : Printf ( TEXT ( " a VALID '%s' " ) , * LexToString ( Handle ) ) : TEXT ( " an INVALID " ) ) ;
return false ;
}
return RemoveRuntimeInstanceFromSimulation ( * SmartObjectRuntime , SmartObjectComponent ) ;
}
2023-01-18 10:52:51 -05:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS