2021-11-26 15:48:13 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "SmartObjectDefinition.h"
2022-03-02 13:25:37 -05:00
# include "SmartObjectSettings.h"
2022-11-01 15:11:25 -04:00
# if WITH_EDITOR
# include "UObject/ObjectSaveContext.h"
2023-01-18 10:52:51 -05:00
# include "WorldConditions/WorldCondition_SmartObjectActorTagQuery.h"
# include "WorldConditions/SmartObjectWorldConditionObjectTagQuery.h"
2022-11-01 15:11:25 -04:00
# endif
2021-11-26 15:48:13 -05:00
2022-09-28 01:06:15 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(SmartObjectDefinition)
2023-01-23 18:48:38 -05:00
# define LOCTEXT_NAMESPACE "SmartObjectDefinition"
2022-01-07 14:28:06 -05:00
namespace UE : : SmartObject
{
const FVector DefaultSlotSize ( 40 , 40 , 90 ) ;
}
2022-03-02 13:25:37 -05:00
USmartObjectDefinition : : USmartObjectDefinition ( const FObjectInitializer & ObjectInitializer ) : UDataAsset ( ObjectInitializer )
{
2023-01-23 18:48:38 -05:00
# if WITH_EDITORONLY_DATA
Slots . AddDefaulted ( ) ;
# endif
2022-03-02 22:33:53 -05:00
UserTagsFilteringPolicy = GetDefault < USmartObjectSettings > ( ) - > DefaultUserTagsFilteringPolicy ;
ActivityTagsMergingPolicy = GetDefault < USmartObjectSettings > ( ) - > DefaultActivityTagsMergingPolicy ;
2022-11-17 07:44:24 -05:00
WorldConditionSchemaClass = GetDefault < USmartObjectSettings > ( ) - > DefaultWorldConditionSchemaClass ;
2022-03-02 13:25:37 -05:00
}
2023-01-23 18:48:38 -05:00
# if WITH_EDITOR
EDataValidationResult USmartObjectDefinition : : IsDataValid ( TArray < FText > & ValidationErrors )
{
const EDataValidationResult Result = Super : : IsDataValid ( ValidationErrors ) ;
Validate ( & ValidationErrors ) ;
return CombineDataValidationResults ( Result , bValid ? EDataValidationResult : : Valid : EDataValidationResult : : Invalid ) ;
}
# endif // WITH_EDITOR
bool USmartObjectDefinition : : Validate ( TArray < FText > * ErrorsToReport ) const
2021-11-26 15:48:13 -05:00
{
bValid = false ;
if ( Slots . Num ( ) = = 0 )
{
2023-01-23 18:48:38 -05:00
if ( ErrorsToReport )
{
ErrorsToReport - > Emplace ( LOCTEXT ( " MissingSlotError " , " Need to provide at least one slot definition " ) ) ;
}
else
{
return false ;
}
2021-11-26 15:48:13 -05:00
}
// Detect null entries in default definitions
int32 NullEntryIndex ;
if ( DefaultBehaviorDefinitions . Find ( nullptr , NullEntryIndex ) )
{
2023-01-23 18:48:38 -05:00
if ( ErrorsToReport )
{
ErrorsToReport - > Emplace ( FText : : Format ( LOCTEXT ( " NullDefaultBehaviorEntryError " , " Null entry found at index {0} in default behavior definition list " ) , NullEntryIndex ) ) ;
}
else
{
return false ;
}
2021-11-26 15:48:13 -05:00
}
// Detect null entries in slot definitions
for ( int i = 0 ; i < Slots . Num ( ) ; + + i )
{
2022-01-19 14:16:16 -05:00
const FSmartObjectSlotDefinition & Slot = Slots [ i ] ;
2021-11-26 15:48:13 -05:00
if ( Slot . BehaviorDefinitions . Find ( nullptr , NullEntryIndex ) )
{
2023-01-23 18:48:38 -05:00
if ( ErrorsToReport )
{
ErrorsToReport - > Emplace ( FText : : Format ( LOCTEXT ( " NullSlotBehaviorEntryError " , " Null entry found at index {0} in default behavior definition list " ) , NullEntryIndex ) ) ;
}
else
{
return false ;
}
2021-11-26 15:48:13 -05:00
}
}
// Detect missing definitions in slots if no default one are provided
if ( DefaultBehaviorDefinitions . Num ( ) = = 0 )
{
for ( int i = 0 ; i < Slots . Num ( ) ; + + i )
{
2022-01-19 14:16:16 -05:00
const FSmartObjectSlotDefinition & Slot = Slots [ i ] ;
2021-11-26 15:48:13 -05:00
if ( Slot . BehaviorDefinitions . Num ( ) = = 0 )
{
2023-01-23 18:48:38 -05:00
if ( ErrorsToReport )
{
ErrorsToReport - > Emplace ( FText : : Format ( LOCTEXT ( " MissingSlotBehaviorError " , " Slot at index {0} needs to provide a behavior definition since there is no default one in the SmartObject definition " ) , i ) ) ;
}
else
{
return false ;
}
2021-11-26 15:48:13 -05:00
}
}
}
bValid = true ;
return true ;
}
2022-03-02 22:33:53 -05:00
FBox USmartObjectDefinition : : GetBounds ( ) const
{
FBox BoundingBox ( ForceInitToZero ) ;
for ( const FSmartObjectSlotDefinition & Slot : GetSlots ( ) )
{
BoundingBox + = Slot . Offset + UE : : SmartObject : : DefaultSlotSize ;
BoundingBox + = Slot . Offset - UE : : SmartObject : : DefaultSlotSize ;
}
return BoundingBox ;
}
void USmartObjectDefinition : : GetSlotActivityTags ( const FSmartObjectSlotIndex & SlotIndex , FGameplayTagContainer & OutActivityTags ) const
{
if ( ensureMsgf ( Slots . IsValidIndex ( SlotIndex ) , TEXT ( " Requesting activity tags for an out of range slot index: %s " ) , * LexToString ( SlotIndex ) ) )
{
GetSlotActivityTags ( Slots [ SlotIndex ] , OutActivityTags ) ;
}
}
void USmartObjectDefinition : : GetSlotActivityTags ( const FSmartObjectSlotDefinition & SlotDefinition , FGameplayTagContainer & OutActivityTags ) const
{
OutActivityTags = ActivityTags ;
if ( ActivityTagsMergingPolicy = = ESmartObjectTagMergingPolicy : : Combine )
{
OutActivityTags . AppendTags ( SlotDefinition . ActivityTags ) ;
}
else if ( ActivityTagsMergingPolicy = = ESmartObjectTagMergingPolicy : : Override & & ! SlotDefinition . ActivityTags . IsEmpty ( ) )
{
OutActivityTags = SlotDefinition . ActivityTags ;
}
}
TOptional < FTransform > USmartObjectDefinition : : GetSlotTransform ( const FTransform & OwnerTransform , const FSmartObjectSlotIndex SlotIndex ) const
{
TOptional < FTransform > Transform ;
if ( ensureMsgf ( Slots . IsValidIndex ( SlotIndex ) , TEXT ( " Requesting slot transform for an out of range index: %s " ) , * LexToString ( SlotIndex ) ) )
{
const FSmartObjectSlotDefinition & Slot = Slots [ SlotIndex ] ;
Transform = FTransform ( Slot . Rotation , Slot . Offset ) * OwnerTransform ;
}
return Transform ;
}
2021-11-26 15:48:13 -05:00
const USmartObjectBehaviorDefinition * USmartObjectDefinition : : GetBehaviorDefinition ( const FSmartObjectSlotIndex & SlotIndex ,
2022-03-02 13:25:37 -05:00
const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass ) const
2021-11-26 15:48:13 -05:00
{
const USmartObjectBehaviorDefinition * Definition = nullptr ;
if ( Slots . IsValidIndex ( SlotIndex ) )
{
Definition = GetBehaviorDefinitionByType ( Slots [ SlotIndex ] . BehaviorDefinitions , DefinitionClass ) ;
}
if ( Definition = = nullptr )
{
Definition = GetBehaviorDefinitionByType ( DefaultBehaviorDefinitions , DefinitionClass ) ;
}
return Definition ;
}
const USmartObjectBehaviorDefinition * USmartObjectDefinition : : GetBehaviorDefinitionByType ( const TArray < USmartObjectBehaviorDefinition * > & BehaviorDefinitions ,
const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass )
{
2023-01-18 10:52:51 -05:00
USmartObjectBehaviorDefinition * const * BehaviorDefinition = BehaviorDefinitions . FindByPredicate ( [ & DefinitionClass ] ( const USmartObjectBehaviorDefinition * SlotBehaviorDefinition )
2021-11-26 15:48:13 -05:00
{
return SlotBehaviorDefinition ! = nullptr & & SlotBehaviorDefinition - > GetClass ( ) - > IsChildOf ( * DefinitionClass ) ;
} ) ;
return BehaviorDefinition ! = nullptr ? * BehaviorDefinition : nullptr ;
}
2022-11-01 15:11:25 -04:00
# if WITH_EDITOR
int32 USmartObjectDefinition : : FindSlotByID ( const FGuid ID ) const
{
const int32 Slot = Slots . IndexOfByPredicate ( [ & ID ] ( const FSmartObjectSlotDefinition & Slot ) { return Slot . ID = = ID ; } ) ;
return Slot ;
}
2022-09-28 01:06:15 -04:00
2022-11-01 15:11:25 -04:00
void USmartObjectDefinition : : PostEditChangeChainProperty ( FPropertyChangedChainEvent & PropertyChangedEvent )
{
Super : : PostEditChangeChainProperty ( PropertyChangedEvent ) ;
const FProperty * Property = PropertyChangedEvent . Property ;
if ( Property = = nullptr )
{
return ;
}
const FProperty * MemberProperty = nullptr ;
if ( PropertyChangedEvent . PropertyChain . GetActiveMemberNode ( ) )
{
MemberProperty = PropertyChangedEvent . PropertyChain . GetActiveMemberNode ( ) - > GetValue ( ) ;
}
if ( MemberProperty = = nullptr )
{
return ;
}
// Ensure unique Slot ID on added or duplicated items.
if ( PropertyChangedEvent . ChangeType = = EPropertyChangeType : : ArrayAdd
| | PropertyChangedEvent . ChangeType = = EPropertyChangeType : : Duplicate )
{
if ( Property - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( USmartObjectDefinition , Slots ) )
{
const int32 ArrayIndex = PropertyChangedEvent . GetArrayIndex ( MemberProperty - > GetFName ( ) . ToString ( ) ) ;
if ( Slots . IsValidIndex ( ArrayIndex ) )
{
FSmartObjectSlotDefinition & Slot = Slots [ ArrayIndex ] ;
Slot . ID = FGuid : : NewGuid ( ) ;
2023-01-26 06:15:02 -05:00
Slot . SelectionPreconditions . SetSchemaClass ( WorldConditionSchemaClass ) ;
2022-11-01 15:11:25 -04:00
}
}
}
// Anything in the slots changed, update references.
if ( MemberProperty - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( USmartObjectDefinition , Slots ) )
{
UpdateSlotReferences ( ) ;
}
2022-11-04 06:56:50 -04:00
2022-11-17 07:44:24 -05:00
// If schema changes, update preconditions too.
if ( MemberProperty - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( USmartObjectDefinition , WorldConditionSchemaClass ) )
2022-11-01 15:11:25 -04:00
{
2022-11-17 07:44:24 -05:00
for ( FSmartObjectSlotDefinition & Slot : Slots )
2022-11-01 15:11:25 -04:00
{
2023-01-26 06:15:02 -05:00
Slot . SelectionPreconditions . SetSchemaClass ( WorldConditionSchemaClass ) ;
2022-11-30 07:17:21 -05:00
Slot . SelectionPreconditions . Initialize ( * this ) ;
2022-11-01 15:11:25 -04:00
}
}
2022-11-04 06:56:50 -04:00
Validate ( ) ;
2022-11-01 15:11:25 -04:00
}
void USmartObjectDefinition : : PreSave ( FObjectPreSaveContext SaveContext )
{
2022-11-17 07:44:24 -05:00
for ( FSmartObjectSlotDefinition & Slot : Slots )
{
2022-11-30 07:17:21 -05:00
Slot . SelectionPreconditions . Initialize ( * this ) ;
2022-11-17 07:44:24 -05:00
}
2022-11-01 15:11:25 -04:00
UpdateSlotReferences ( ) ;
Super : : PreSave ( SaveContext ) ;
}
void USmartObjectDefinition : : UpdateSlotReferences ( )
{
for ( FSmartObjectSlotDefinition & Slot : Slots )
{
for ( FInstancedStruct & Data : Slot . Data )
{
if ( ! Data . IsValid ( ) )
{
continue ;
}
const UScriptStruct * ScriptStruct = Data . GetScriptStruct ( ) ;
uint8 * Memory = Data . GetMutableMemory ( ) ;
for ( TFieldIterator < FProperty > It ( ScriptStruct ) ; It ; + + It )
{
2023-01-18 10:52:51 -05:00
if ( const FStructProperty * StructProp = CastField < FStructProperty > ( * It ) )
2022-11-01 15:11:25 -04:00
{
if ( StructProp - > Struct = = TBaseStructure < FSmartObjectSlotReference > : : Get ( ) )
{
FSmartObjectSlotReference & Ref = * StructProp - > ContainerPtrToValuePtr < FSmartObjectSlotReference > ( Memory ) ;
const int32 Index = FindSlotByID ( Ref . GetSlotID ( ) ) ;
Ref . SetIndex ( Index ) ;
}
}
}
}
}
}
2022-11-17 07:44:24 -05:00
# endif // WITH_EDITOR
void USmartObjectDefinition : : PostLoad ( )
{
Super : : PostLoad ( ) ;
// Fill in missing world condition schema for old data.
if ( ! WorldConditionSchemaClass )
{
WorldConditionSchemaClass = GetDefault < USmartObjectSettings > ( ) - > DefaultWorldConditionSchemaClass ;
}
2023-01-18 10:52:51 -05:00
2023-01-26 06:15:02 -05:00
if ( Preconditions . GetSchemaClass ( ) . Get ( ) = = nullptr )
2023-01-18 10:52:51 -05:00
{
2023-01-26 06:15:02 -05:00
Preconditions . SetSchemaClass ( WorldConditionSchemaClass ) ;
2023-01-18 10:52:51 -05:00
}
# if WITH_EDITOR
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if ( ! ObjectTagFilter . IsEmpty ( ) )
{
FWorldCondition_SmartObjectActorTagQuery NewActorTagQueryCondition ;
NewActorTagQueryCondition . TagQuery = ObjectTagFilter ;
2023-01-26 06:15:02 -05:00
Preconditions . AddCondition ( FWorldConditionEditable ( 0 , EWorldConditionOperator : : And , FConstStructView : : Make ( NewActorTagQueryCondition ) ) ) ;
2023-01-18 10:52:51 -05:00
ObjectTagFilter . Clear ( ) ;
UE_ASSET_LOG ( LogSmartObject , Log , this , TEXT ( " Deprecated object tag filter has been replaced by a %s precondition to validate tags on the smart object actor. "
" If the intent was to validate against instance runtime tags then the condition should be replaced by %s. " ) ,
* FWorldCondition_SmartObjectActorTagQuery : : StaticStruct ( ) - > GetName ( ) ,
* FSmartObjectWorldConditionObjectTagQuery : : StaticStruct ( ) - > GetName ( ) ) ;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
# endif
Preconditions . Initialize ( * this ) ;
2022-11-17 07:44:24 -05:00
for ( FSmartObjectSlotDefinition & Slot : Slots )
{
# if WITH_EDITOR
// Fill in missing slot ID for old data.
if ( ! Slot . ID . IsValid ( ) )
{
Slot . ID = FGuid : : NewGuid ( ) ;
}
# endif
// Fill in missing world condition schema for old data.
2023-01-26 06:15:02 -05:00
if ( Slot . SelectionPreconditions . GetSchemaClass ( ) . Get ( ) = = nullptr )
2022-11-17 07:44:24 -05:00
{
2023-01-26 06:15:02 -05:00
Slot . SelectionPreconditions . SetSchemaClass ( WorldConditionSchemaClass ) ;
2022-11-17 07:44:24 -05:00
}
2022-11-30 07:17:21 -05:00
Slot . SelectionPreconditions . Initialize ( * this ) ;
2022-11-17 07:44:24 -05:00
}
# if WITH_EDITOR
UpdateSlotReferences ( ) ;
Validate ( ) ;
# endif
}
2023-01-23 18:48:38 -05:00
# undef LOCTEXT_NAMESPACE