From 28c3f65bab353bee754b3e2a3cd373da00e5a881 Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Tue, 28 May 2024 09:21:29 +0200 Subject: [PATCH 1/4] Fixed code --- .../GAS/Components/GBFAbilitySystemComponent.cpp | 16 ++++++++-------- .../GBFAT_WaitUseSmartObjectGameplayBehavior.cpp | 8 ++++---- .../GBFAT_WaitUseSmartObjectGameplayBehavior.h | 5 +++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Source/GameBaseFramework/Private/GAS/Components/GBFAbilitySystemComponent.cpp b/Source/GameBaseFramework/Private/GAS/Components/GBFAbilitySystemComponent.cpp index 453ebf8c..16e75f98 100644 --- a/Source/GameBaseFramework/Private/GAS/Components/GBFAbilitySystemComponent.cpp +++ b/Source/GameBaseFramework/Private/GAS/Components/GBFAbilitySystemComponent.cpp @@ -491,7 +491,7 @@ float UGBFAbilitySystemComponent::PlayMontageForMesh( UGameplayAbility * animati auto & ability_rep_montage_info = GetGameplayAbilityRepAnimMontageForMesh( mesh ); const auto current_rep_play_instance_id = ability_rep_montage_info.RepMontageInfo.PlayInstanceId; - ability_rep_montage_info.RepMontageInfo.AnimMontage = new_anim_montage; + ability_rep_montage_info.RepMontageInfo.Animation = new_anim_montage; ability_rep_montage_info.RepMontageInfo.PlayInstanceId = current_rep_play_instance_id < UINT8_MAX ? current_rep_play_instance_id + 1 : 0; ability_rep_montage_info.RepMontageInfo.SectionIdToPlay = 0; @@ -1085,7 +1085,7 @@ void UGBFAbilitySystemComponent::AnimMontage_UpdateReplicatedDataForMesh( FGamep if ( anim_instance != nullptr && anim_montage_info.LocalMontageInfo.AnimMontage ) { - rep_anim_montage_info.RepMontageInfo.AnimMontage = anim_montage_info.LocalMontageInfo.AnimMontage; + rep_anim_montage_info.RepMontageInfo.Animation = anim_montage_info.LocalMontageInfo.AnimMontage; // Compressed Flags const auto is_stopped = anim_instance->Montage_GetIsStopped( anim_montage_info.LocalMontageInfo.AnimMontage ); @@ -1176,23 +1176,23 @@ void UGBFAbilitySystemComponent::OnRep_ReplicatedAnimMontageForMesh() if ( must_debug_montage ) { ABILITY_LOG( Warning, TEXT( "\n\nOnRep_ReplicatedAnimMontage, %s" ), *GetNameSafe( this ) ); - ABILITY_LOG( Warning, TEXT( "\tAnimMontage: %s\n\tPlayRate: %f\n\tPosition: %f\n\tBlendTime: %f\n\tNextSectionID: %d\n\tIsStopped: %d" ), *GetNameSafe( new_rep_montage_info_for_mesh.RepMontageInfo.AnimMontage ), new_rep_montage_info_for_mesh.RepMontageInfo.PlayRate, new_rep_montage_info_for_mesh.RepMontageInfo.Position, new_rep_montage_info_for_mesh.RepMontageInfo.BlendTime, new_rep_montage_info_for_mesh.RepMontageInfo.NextSectionID, new_rep_montage_info_for_mesh.RepMontageInfo.IsStopped ); + ABILITY_LOG( Warning, TEXT( "\tAnimMontage: %s\n\tPlayRate: %f\n\tPosition: %f\n\tBlendTime: %f\n\tNextSectionID: %d\n\tIsStopped: %d" ), *GetNameSafe( new_rep_montage_info_for_mesh.RepMontageInfo.GetAnimMontage() ), new_rep_montage_info_for_mesh.RepMontageInfo.PlayRate, new_rep_montage_info_for_mesh.RepMontageInfo.Position, new_rep_montage_info_for_mesh.RepMontageInfo.BlendTime, new_rep_montage_info_for_mesh.RepMontageInfo.NextSectionID, new_rep_montage_info_for_mesh.RepMontageInfo.IsStopped ); ABILITY_LOG( Warning, TEXT( "\tLocalAnimMontageInfo.AnimMontage: %s\n\tPosition: %f" ), *GetNameSafe( anim_montage_info.LocalMontageInfo.AnimMontage ), anim_instance->Montage_GetPosition( anim_montage_info.LocalMontageInfo.AnimMontage ) ); } - if ( new_rep_montage_info_for_mesh.RepMontageInfo.AnimMontage ) + if ( new_rep_montage_info_for_mesh.RepMontageInfo.Animation ) { // New Montage to play - if ( ( anim_montage_info.LocalMontageInfo.AnimMontage != new_rep_montage_info_for_mesh.RepMontageInfo.AnimMontage ) || + if ( ( anim_montage_info.LocalMontageInfo.AnimMontage != new_rep_montage_info_for_mesh.RepMontageInfo.Animation ) || ( anim_montage_info.LocalMontageInfo.PlayInstanceId != new_rep_montage_info_for_mesh.RepMontageInfo.PlayInstanceId ) ) { anim_montage_info.LocalMontageInfo.PlayInstanceId = new_rep_montage_info_for_mesh.RepMontageInfo.PlayInstanceId; - PlayMontageSimulatedForMesh( new_rep_montage_info_for_mesh.Mesh, new_rep_montage_info_for_mesh.RepMontageInfo.AnimMontage, new_rep_montage_info_for_mesh.RepMontageInfo.PlayRate ); + PlayMontageSimulatedForMesh( new_rep_montage_info_for_mesh.Mesh, new_rep_montage_info_for_mesh.RepMontageInfo.GetAnimMontage(), new_rep_montage_info_for_mesh.RepMontageInfo.PlayRate ); } if ( anim_montage_info.LocalMontageInfo.AnimMontage == nullptr ) { - ABILITY_LOG( Warning, TEXT( "OnRep_ReplicatedAnimMontage: PlayMontageSimulated failed. Name: %s, AnimMontage: %s" ), *GetNameSafe( this ), *GetNameSafe( new_rep_montage_info_for_mesh.RepMontageInfo.AnimMontage ) ); + ABILITY_LOG( Warning, TEXT( "OnRep_ReplicatedAnimMontage: PlayMontageSimulated failed. Name: %s, AnimMontage: %s" ), *GetNameSafe( this ), *GetNameSafe( new_rep_montage_info_for_mesh.RepMontageInfo.GetAnimMontage() ) ); return; } @@ -1250,7 +1250,7 @@ void UGBFAbilitySystemComponent::OnRep_ReplicatedAnimMontageForMesh() if ( ( current_section_id == rep_section_id ) && ( FMath::Abs( delta_position ) > montage_rep_pos_err_threshold ) && ( new_rep_montage_info_for_mesh.RepMontageInfo.IsStopped == 0 ) ) { // fast forward to server position and trigger notifies - if ( auto * montage_instance = anim_instance->GetActiveInstanceForMontage( new_rep_montage_info_for_mesh.RepMontageInfo.AnimMontage ) ) + if ( auto * montage_instance = anim_instance->GetActiveInstanceForMontage( new_rep_montage_info_for_mesh.RepMontageInfo.GetAnimMontage() ) ) { // Skip triggering notifies if we're going backwards in time, we've already triggered them. const auto delta_time = !FMath::IsNearlyZero( new_rep_montage_info_for_mesh.RepMontageInfo.PlayRate ) diff --git a/Source/GameBaseFramework/Private/GAS/Tasks/GBFAT_WaitUseSmartObjectGameplayBehavior.cpp b/Source/GameBaseFramework/Private/GAS/Tasks/GBFAT_WaitUseSmartObjectGameplayBehavior.cpp index 197fb4cf..a44eefde 100644 --- a/Source/GameBaseFramework/Private/GAS/Tasks/GBFAT_WaitUseSmartObjectGameplayBehavior.cpp +++ b/Source/GameBaseFramework/Private/GAS/Tasks/GBFAT_WaitUseSmartObjectGameplayBehavior.cpp @@ -67,7 +67,7 @@ void UGBFAT_WaitUseSmartObjectGameplayBehavior::Activate() } } -UGBFAT_WaitUseSmartObjectGameplayBehavior * UGBFAT_WaitUseSmartObjectGameplayBehavior::WaitUseSmartObjectGameplayBehaviorWithSmartObjectComponent( UGameplayAbility * owning_ability, USmartObjectComponent * smart_object_component, EGBFATSmartObjectComponentSlotSelection slot_selection, const TArray< TSubclassOf< USmartObjectBehaviorDefinition > > behavior_definition_classes, const FGameplayTagQuery activity_tags ) +UGBFAT_WaitUseSmartObjectGameplayBehavior * UGBFAT_WaitUseSmartObjectGameplayBehavior::WaitUseSmartObjectGameplayBehaviorWithSmartObjectComponent( UGameplayAbility * owning_ability, USmartObjectComponent * smart_object_component, EGBFATSmartObjectComponentSlotSelection slot_selection, const TArray< TSubclassOf< USmartObjectBehaviorDefinition > > behavior_definition_classes, const FGameplayTagQuery activity_tags, ESmartObjectClaimPriority claim_priority ) { auto * smart_object_subsystem = USmartObjectSubsystem::GetCurrent( owning_ability->GetWorld() ); @@ -134,16 +134,16 @@ UGBFAT_WaitUseSmartObjectGameplayBehavior * UGBFAT_WaitUseSmartObjectGameplayBeh } } - claim_handle = smart_object_subsystem->MarkSlotAsClaimed( selected_slot ); + claim_handle = smart_object_subsystem->MarkSlotAsClaimed( selected_slot, claim_priority ); } return WaitUseSmartObjectGameplayBehaviorWithClaimHandle( owning_ability, claim_handle ); } -UGBFAT_WaitUseSmartObjectGameplayBehavior * UGBFAT_WaitUseSmartObjectGameplayBehavior::WaitUseSmartObjectGameplayBehaviorWithSlotHandle( UGameplayAbility * owning_ability, FSmartObjectSlotHandle slot_handle ) +UGBFAT_WaitUseSmartObjectGameplayBehavior * UGBFAT_WaitUseSmartObjectGameplayBehavior::WaitUseSmartObjectGameplayBehaviorWithSlotHandle( UGameplayAbility * owning_ability, FSmartObjectSlotHandle slot_handle, ESmartObjectClaimPriority claim_priority ) { auto * smart_object_subsystem = USmartObjectSubsystem::GetCurrent( owning_ability->GetWorld() ); - const auto claim_handle = smart_object_subsystem->MarkSlotAsClaimed( slot_handle ); + const auto claim_handle = smart_object_subsystem->MarkSlotAsClaimed( slot_handle, claim_priority ); return WaitUseSmartObjectGameplayBehaviorWithClaimHandle( owning_ability, claim_handle ); } diff --git a/Source/GameBaseFramework/Public/GAS/Tasks/GBFAT_WaitUseSmartObjectGameplayBehavior.h b/Source/GameBaseFramework/Public/GAS/Tasks/GBFAT_WaitUseSmartObjectGameplayBehavior.h index 8dba8b41..8db95772 100644 --- a/Source/GameBaseFramework/Public/GAS/Tasks/GBFAT_WaitUseSmartObjectGameplayBehavior.h +++ b/Source/GameBaseFramework/Public/GAS/Tasks/GBFAT_WaitUseSmartObjectGameplayBehavior.h @@ -45,10 +45,10 @@ class GAMEBASEFRAMEWORK_API UGBFAT_WaitUseSmartObjectGameplayBehavior final : pu void Activate() override; UFUNCTION( BlueprintCallable, Category = "Ability|Tasks", meta = ( HidePin = "owning_ability", DefaultToSelf = "owning_ability", BlueprintInternalUseOnly = "TRUE" ) ) - static UGBFAT_WaitUseSmartObjectGameplayBehavior * WaitUseSmartObjectGameplayBehaviorWithSmartObjectComponent( UGameplayAbility * owning_ability, USmartObjectComponent * smart_object_component, EGBFATSmartObjectComponentSlotSelection slot_selection, const TArray< TSubclassOf< USmartObjectBehaviorDefinition > > behavior_definition_classes, const FGameplayTagQuery activity_tags ); + static UGBFAT_WaitUseSmartObjectGameplayBehavior * WaitUseSmartObjectGameplayBehaviorWithSmartObjectComponent( UGameplayAbility * owning_ability, USmartObjectComponent * smart_object_component, EGBFATSmartObjectComponentSlotSelection slot_selection, const TArray< TSubclassOf< USmartObjectBehaviorDefinition > > behavior_definition_classes, const FGameplayTagQuery activity_tags, ESmartObjectClaimPriority claim_priority = ESmartObjectClaimPriority::Normal ); UFUNCTION( BlueprintCallable, Category = "Ability|Tasks", meta = ( HidePin = "owning_ability", DefaultToSelf = "owning_ability", BlueprintInternalUseOnly = "TRUE" ) ) - static UGBFAT_WaitUseSmartObjectGameplayBehavior * WaitUseSmartObjectGameplayBehaviorWithSlotHandle( UGameplayAbility * owning_ability, FSmartObjectSlotHandle slot_handle ); + static UGBFAT_WaitUseSmartObjectGameplayBehavior * WaitUseSmartObjectGameplayBehaviorWithSlotHandle( UGameplayAbility * owning_ability, FSmartObjectSlotHandle slot_handle, ESmartObjectClaimPriority claim_priority = ESmartObjectClaimPriority::Normal ); UFUNCTION( BlueprintCallable, Category = "Ability|Tasks", meta = ( HidePin = "owning_ability", DefaultToSelf = "owning_ability", BlueprintInternalUseOnly = "TRUE" ) ) static UGBFAT_WaitUseSmartObjectGameplayBehavior * WaitUseSmartObjectGameplayBehaviorWithClaimHandle( UGameplayAbility * owning_ability, FSmartObjectClaimHandle claim_handle ); @@ -80,4 +80,5 @@ class GAMEBASEFRAMEWORK_API UGBFAT_WaitUseSmartObjectGameplayBehavior final : pu FSmartObjectClaimHandle ClaimedHandle; FDelegateHandle OnBehaviorFinishedNotifyHandle; bool bInteractionCompleted; + ESmartObjectClaimPriority ClaimPriority; }; From 6df2c4d071e8971c921bdc82f3cf29ca4029f996 Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Fri, 31 May 2024 15:10:32 +0200 Subject: [PATCH 2/4] Fixed warnings --- .../Public/Interaction/GBFInteractionOption.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h b/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h index a62afc3c..cfa77dec 100644 --- a/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h +++ b/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h @@ -8,6 +8,7 @@ class UInputAction; class UInputMappingContext; class UGBFInputConfig; +class UUserWidget; class UAbilitySystemComponent; class IGBFInteractableTarget; @@ -65,7 +66,7 @@ struct FGBFInteractionOption FText SubText; UPROPERTY( EditAnywhere ) - EGBFInteractionAbilityTarget AbilityTarget; + EGBFInteractionAbilityTarget AbilityTarget = EGBFInteractionAbilityTarget::InteractableTarget; /** The ability to grant the avatar when they get near interactable objects. */ UPROPERTY( EditAnywhere, BlueprintReadOnly ) @@ -109,7 +110,7 @@ struct FGBFInteractionOptionContainer TSoftObjectPtr< UInputMappingContext > InputMappingContext; UPROPERTY( EditAnywhere ) - EGBFInteractionGroup InteractionGroup; + EGBFInteractionGroup InteractionGroup = EGBFInteractionGroup::Exclusive; UPROPERTY( EditAnywhere ) FGameplayTagRequirements InteractableTargetTagRequirements; From bb96ab9d9a99e5b991c21478eaf6898694bb4e0c Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Fri, 31 May 2024 17:38:12 +0200 Subject: [PATCH 3/4] Used wrong object for the cast --- .../GameFeatures/GBFGameFeatureAction_AddGameplayCuePath.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/GameBaseFramework/Private/GameFeatures/GBFGameFeatureAction_AddGameplayCuePath.cpp b/Source/GameBaseFramework/Private/GameFeatures/GBFGameFeatureAction_AddGameplayCuePath.cpp index 7a798a2a..ef360f37 100644 --- a/Source/GameBaseFramework/Private/GameFeatures/GBFGameFeatureAction_AddGameplayCuePath.cpp +++ b/Source/GameBaseFramework/Private/GameFeatures/GBFGameFeatureAction_AddGameplayCuePath.cpp @@ -80,7 +80,7 @@ void UGBFGameFeatureObserver_AddGameplayCuePath::OnGameFeatureUnregistering( con const auto plugin_root_path = TEXT( "/" ) + plugin_name; for ( const UGameFeatureAction * action : game_feature_data->GetActions() ) { - if ( const auto * game_feature_action_add_gameplay_cue_path = Cast< UGBFGameFeatureAction_AddGameplayCuePath >( game_feature_data ) ) + if ( const auto * game_feature_action_add_gameplay_cue_path = Cast< UGBFGameFeatureAction_AddGameplayCuePath >( action ) ) { const auto & dirs_to_add = game_feature_action_add_gameplay_cue_path->GetDirectoryPathsToAdd(); From f621c304069bdc1f323909a4e98f67cbc349142c Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Tue, 4 Jun 2024 15:03:24 +0200 Subject: [PATCH 4/4] Fixed clang warnings --- Source/GameBaseFramework/Private/Camera/GBFCameraMode.cpp | 2 +- .../Private/Characters/GBFSimpleCharacterWithAbilities.cpp | 4 ++-- .../Private/Equipment/GBFEquipmentManagerComponent.cpp | 2 +- .../Feedback/ContextEffects/GBFContextEffectsLibrary.cpp | 2 +- .../GameBaseFramework/Private/GAS/GBFActorWithAbilities.cpp | 4 ++-- .../Private/Inventory/GBFInventoryItemDefinition.cpp | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/GameBaseFramework/Private/Camera/GBFCameraMode.cpp b/Source/GameBaseFramework/Private/Camera/GBFCameraMode.cpp index eeae2ec6..708c173a 100644 --- a/Source/GameBaseFramework/Private/Camera/GBFCameraMode.cpp +++ b/Source/GameBaseFramework/Private/Camera/GBFCameraMode.cpp @@ -477,7 +477,7 @@ void UGBFCameraModeStack::DrawDebug( UCanvas * canvas ) const void UGBFCameraModeStack::Reset() { - for ( const auto camera_mode : CameraModeStack ) + for ( const auto & camera_mode : CameraModeStack ) { camera_mode->Reset(); } diff --git a/Source/GameBaseFramework/Private/Characters/GBFSimpleCharacterWithAbilities.cpp b/Source/GameBaseFramework/Private/Characters/GBFSimpleCharacterWithAbilities.cpp index a0610485..d5407b7c 100644 --- a/Source/GameBaseFramework/Private/Characters/GBFSimpleCharacterWithAbilities.cpp +++ b/Source/GameBaseFramework/Private/Characters/GBFSimpleCharacterWithAbilities.cpp @@ -36,7 +36,7 @@ void AGBFSimpleCharacterWithAbilities::BeginPlay() return; } - for ( const auto ability_set : AbilitySets ) + for ( const auto & ability_set : AbilitySets ) { ability_set->GiveToAbilitySystem( AbilitySystemComponent, nullptr ); } @@ -49,7 +49,7 @@ void AGBFSimpleCharacterWithAbilities::BeginPlay() AbilitySystemComponent->ApplyGameplayEffectSpecToSelf( *spec_handle.Data.Get() ); } - for ( const auto attribute_set_class : AttributeSetClasses ) + for ( const auto & attribute_set_class : AttributeSetClasses ) { auto * attribute_set = NewObject< UAttributeSet >( this, attribute_set_class, NAME_None, RF_Transient ); AbilitySystemComponent->AddSpawnedAttribute( attribute_set ); diff --git a/Source/GameBaseFramework/Private/Equipment/GBFEquipmentManagerComponent.cpp b/Source/GameBaseFramework/Private/Equipment/GBFEquipmentManagerComponent.cpp index 1c2731b3..1fedaeb8 100644 --- a/Source/GameBaseFramework/Private/Equipment/GBFEquipmentManagerComponent.cpp +++ b/Source/GameBaseFramework/Private/Equipment/GBFEquipmentManagerComponent.cpp @@ -99,7 +99,7 @@ UGBFEquipmentInstance * FGBFEquipmentList::AddEntryInternal( UGBFEquipmentInstan if ( auto * asc = GetAbilitySystemComponent() ) { - for ( const auto ability_set : equipment_cdo->AbilitySetsToGrant ) + for ( const auto & ability_set : equipment_cdo->AbilitySetsToGrant ) { ability_set->GiveToAbilitySystem( asc, /*inout*/ &new_entry.GrantedHandles, new_entry.Instance ); } diff --git a/Source/GameBaseFramework/Private/Feedback/ContextEffects/GBFContextEffectsLibrary.cpp b/Source/GameBaseFramework/Private/Feedback/ContextEffects/GBFContextEffectsLibrary.cpp index aefd69c8..e29a6ec1 100644 --- a/Source/GameBaseFramework/Private/Feedback/ContextEffects/GBFContextEffectsLibrary.cpp +++ b/Source/GameBaseFramework/Private/Feedback/ContextEffects/GBFContextEffectsLibrary.cpp @@ -9,7 +9,7 @@ void UGBFContextEffectsLibrary::GetEffects( TArray< USoundBase * > & sounds, TAr if ( effect.IsValid() && context.IsValid() && EffectsLoadState == EGBFContextEffectsLibraryLoadState::Loaded ) { // Loop through Context Effects - for ( const auto active_context_effect : ActiveContextEffects ) + for ( const auto & active_context_effect : ActiveContextEffects ) { // Make sure the Effect is an exact Tag Match and ensure the Context has all tags in the Effect (and neither or both are empty) if ( effect.MatchesTagExact( active_context_effect->EffectTag ) && context.HasAllExact( active_context_effect->Context ) && ( active_context_effect->Context.IsEmpty() == context.IsEmpty() ) ) diff --git a/Source/GameBaseFramework/Private/GAS/GBFActorWithAbilities.cpp b/Source/GameBaseFramework/Private/GAS/GBFActorWithAbilities.cpp index 7e12af06..45fcee5f 100644 --- a/Source/GameBaseFramework/Private/GAS/GBFActorWithAbilities.cpp +++ b/Source/GameBaseFramework/Private/GAS/GBFActorWithAbilities.cpp @@ -36,7 +36,7 @@ void AGBFActorWithAbilities::BeginPlay() return; } - for ( const auto ability_set : AbilitySets ) + for ( const auto & ability_set : AbilitySets ) { ability_set->GiveToAbilitySystem( AbilitySystemComponent, nullptr ); } @@ -49,7 +49,7 @@ void AGBFActorWithAbilities::BeginPlay() AbilitySystemComponent->ApplyGameplayEffectSpecToSelf( *spec_handle.Data.Get() ); } - for ( const auto attribute_set_class : AttributeSetClasses ) + for ( const auto & attribute_set_class : AttributeSetClasses ) { auto * attribute_set = NewObject< UAttributeSet >( this, attribute_set_class, NAME_None, RF_Transient ); AbilitySystemComponent->AddSpawnedAttribute( attribute_set ); diff --git a/Source/GameBaseFramework/Private/Inventory/GBFInventoryItemDefinition.cpp b/Source/GameBaseFramework/Private/Inventory/GBFInventoryItemDefinition.cpp index 1fd5c3f4..c39d6f19 100644 --- a/Source/GameBaseFramework/Private/Inventory/GBFInventoryItemDefinition.cpp +++ b/Source/GameBaseFramework/Private/Inventory/GBFInventoryItemDefinition.cpp @@ -6,7 +6,7 @@ const UGBFInventoryItemFragment * UGBFInventoryItemDefinition::FindFragmentByCla { if ( fragment_class != nullptr ) { - for ( const auto fragment : Fragments ) + for ( const auto & fragment : Fragments ) { if ( fragment && fragment->IsA( fragment_class ) ) {