diff --git a/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp b/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp index f235f082..477942d5 100644 --- a/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp +++ b/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp @@ -3,6 +3,7 @@ #include "Characters/Components/GBFHeroComponent.h" #include "Input/GBFInputComponent.h" #include "Interaction/GBFInteractableComponent.h" +#include "Interaction/GBFInteractionAllowCondition.h" #include "Interaction/GBFInteractionEventCustomization.h" #include "Interaction/GBFInteractionOption.h" #include "Interaction/GBFInteractionStatics.h" @@ -122,7 +123,14 @@ void UGBFGameplayAbility_Interact::UpdateInteractableOptions( const TArray< UGBF TActorToComponentMap previous_active_targets; fill_actor_to_component_map( previous_active_targets ); - ResetUnusedInteractions( target_infos ); + // Clear every interaction option and rebuild from scratch because some options can become invalid from frame to frame (tags added to the player, etc...) + for ( auto & [ actor, context ] : InteractableTargetContexts ) + { + context.Reset(); + } + + InteractableTargetContexts.Reset(); + RegisterInteractions( target_infos ); TActorToComponentMap new_active_targets; @@ -278,49 +286,6 @@ void UGBFGameplayAbility_Interact::GatherTargetInfos( TArray< InteractableTarget } ); } -void UGBFGameplayAbility_Interact::ResetUnusedInteractions( const TArray< InteractableTargetInfos > & target_infos ) -{ - TArray< TWeakObjectPtr< AActor >, TInlineAllocator< 8 > > actors_to_unregister; - InteractableTargetContexts.GetKeys( actors_to_unregister ); - - for ( auto index = 0; index < target_infos.Num(); ++index ) - { - const auto & infos = target_infos[ index ]; - - auto remove_actor = false; - - if ( auto * context = InteractableTargetContexts.Find( infos.Actor.Get() ) ) - { - if ( context->InteractionsId == infos.InteractableComponent->GetInteractableOptions().GetInteractionsId() ) - { - remove_actor = true; - } - } - else - { - remove_actor = true; - } - if ( remove_actor ) - { - actors_to_unregister.Remove( infos.Actor ); - } - - if ( infos.Group == EGBFInteractionGroup::Exclusive ) - { - break; - } - } - - for ( auto actor : actors_to_unregister ) - { - if ( auto * context = InteractableTargetContexts.Find( actor.Get() ) ) - { - context->Reset(); - InteractableTargetContexts.Remove( actor.Get() ); - } - } -} - void UGBFGameplayAbility_Interact::RegisterInteractions( const TArray< InteractableTargetInfos > & target_infos ) { for ( const auto & infos : target_infos ) @@ -336,13 +301,13 @@ void UGBFGameplayAbility_Interact::RegisterInteractions( const TArray< Interacta void UGBFGameplayAbility_Interact::RegisterInteraction( const InteractableTargetInfos & target_infos ) { - const auto * pawn = Cast< APawn >( GetAvatarActorFromActorInfo() ); + const auto actor_info = GetActorInfo(); + + const auto * pawn = Cast< APawn >( actor_info.AvatarActor ); - InteractableTargetContext context; auto interactable_component = target_infos.InteractableComponent; const auto & option_container = interactable_component->GetInteractableOptions(); - context.InteractionsId = option_container.GetInteractionsId(); auto * asc_from_actor_info = GetAbilitySystemComponentFromActorInfo_Ensured(); auto * asc_from_interactable_target = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent( interactable_component.Get()->GetOwner() ); @@ -356,39 +321,16 @@ void UGBFGameplayAbility_Interact::RegisterInteraction( const InteractableTarget asc_from_interactable_target->GetOwnedGameplayTags( interactable_target_tags ); } - if ( !option_container.InstigatorTagRequirements.RequirementsMet( actor_info_tags ) ) - { - return; - } - - if ( !option_container.InteractableTargetTagRequirements.RequirementsMet( interactable_target_tags ) ) + for ( auto condition : option_container.AllowConditions ) { - return; - } - - bool has_a_matching_sub_option = false; - - for ( auto sub_option : option_container.GetOptions() ) - { - if ( !sub_option.InstigatorTagRequirements.RequirementsMet( actor_info_tags ) ) + if ( !condition->AreAllInteractionsAllowed( interactable_component.Get(), actor_info ) ) { - continue; + return; } - - if ( !sub_option.InteractableTargetTagRequirements.RequirementsMet( interactable_target_tags ) ) - { - continue; - } - - has_a_matching_sub_option = true; - break; - } - - if ( !has_a_matching_sub_option ) - { - return; } + InteractableTargetContext context; + context.InteractionsId = option_container.GetInteractionsId(); context.WidgetInfosHandle = { interactable_component, option_container.CommonWidgetInfos }; if ( const auto * pc = Cast< APlayerController >( pawn->GetController() ) ) @@ -406,18 +348,20 @@ void UGBFGameplayAbility_Interact::RegisterInteraction( const InteractableTarget } } - for ( auto & option : option_container.GetOptions() ) - { - if ( !option.InstigatorTagRequirements.RequirementsMet( actor_info_tags ) ) + auto valid_options = option_container.GetOptions().FilterByPredicate( [ & ]( const FGBFInteractionOption & option ) { + for ( auto condition : option.AllowConditions ) { - continue; + if ( !condition->IsOptionAllowed( interactable_component.Get(), actor_info, option ) ) + { + return false; + } } - if ( !option.InteractableTargetTagRequirements.RequirementsMet( interactable_target_tags ) ) - { - continue; - } + return true; + } ); + for ( auto & option : valid_options ) + { OptionHandle option_handle; option_handle.InteractableComponent = interactable_component; option_handle.InitialInteractionOption = option; diff --git a/Source/GameBaseFramework/Private/Interaction/GBFInteractionAllowCondition.cpp b/Source/GameBaseFramework/Private/Interaction/GBFInteractionAllowCondition.cpp new file mode 100644 index 00000000..ed8c509c --- /dev/null +++ b/Source/GameBaseFramework/Private/Interaction/GBFInteractionAllowCondition.cpp @@ -0,0 +1,47 @@ +#include "Interaction/GBFInteractionAllowCondition.h" + +#include +#include + +bool UGBFInteractionAllowCondition::AreAllInteractionsAllowed_Implementation( UGBFInteractableComponent * /*interactable_component*/, const FGameplayAbilityActorInfo & /*instigator_infos*/ ) const +{ + return false; +} + +bool UGBFInteractionAllowCondition::IsOptionAllowed_Implementation( UGBFInteractableComponent * /*interactable_component*/, const FGameplayAbilityActorInfo & /*instigator_infos */, const FGBFInteractionOption & /*option*/ ) const +{ + return false; +} + +bool UGBFInteractionAllowCondition_TagRequirements::AreAllInteractionsAllowed_Implementation( UGBFInteractableComponent * interactable_component, const FGameplayAbilityActorInfo & instigator_infos ) const +{ + FGameplayTagContainer tags; + GetGameplayTags( tags, interactable_component, instigator_infos ); + + return TagRequirements.RequirementsMet( tags ); +} + +bool UGBFInteractionAllowCondition_TagRequirements::IsOptionAllowed_Implementation( UGBFInteractableComponent * interactable_component, const FGameplayAbilityActorInfo & instigator_infos, const FGBFInteractionOption & /*option*/ ) const +{ + FGameplayTagContainer tags; + GetGameplayTags( tags, interactable_component, instigator_infos ); + + return TagRequirements.RequirementsMet( tags ); +} + +void UGBFInteractionAllowCondition_TagRequirements::GetGameplayTags( FGameplayTagContainer & /*tags*/, UGBFInteractableComponent * /*interactable_component*/, const FGameplayAbilityActorInfo & /*instigator_infos*/ ) const +{ +} + +void UGBFInteractionAllowCondition_InstigatorTags::GetGameplayTags( FGameplayTagContainer & tags, UGBFInteractableComponent * /*interactable_component*/, const FGameplayAbilityActorInfo & instigator_infos ) const +{ + instigator_infos.AbilitySystemComponent->GetOwnedGameplayTags( tags ); +} + +void UGBFInteractionAllowCondition_InteractableActorTags::GetGameplayTags( FGameplayTagContainer & tags, UGBFInteractableComponent * interactable_component, const FGameplayAbilityActorInfo & /*instigator_infos*/ ) const +{ + if ( auto * asc = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent( interactable_component->GetOwner() ) ) + { + asc->GetOwnedGameplayTags( tags ); + } +} \ No newline at end of file diff --git a/Source/GameBaseFramework/Private/Interaction/GBFInteractionOption.cpp b/Source/GameBaseFramework/Private/Interaction/GBFInteractionOption.cpp index b2215d13..458a9364 100644 --- a/Source/GameBaseFramework/Private/Interaction/GBFInteractionOption.cpp +++ b/Source/GameBaseFramework/Private/Interaction/GBFInteractionOption.cpp @@ -36,8 +36,7 @@ FGBFInteractionOptionContainer::FGBFInteractionOptionContainer( const FGBFIntera InputMappingContext( other.InputMappingContext ), DefaultInputAction( other.DefaultInputAction ), InteractionGroup( other.InteractionGroup ), - InteractableTargetTagRequirements( other.InteractableTargetTagRequirements ), - InstigatorTagRequirements( other.InstigatorTagRequirements ), + AllowConditions( other.AllowConditions ), CommonWidgetInfos( other.CommonWidgetInfos ), Options( other.Options ), // :NOTE: Increment the id to make sure we invalidate this container and force a full refresh of the options diff --git a/Source/GameBaseFramework/Public/Interaction/Abilities/GBFGameplayAbility_Interact.h b/Source/GameBaseFramework/Public/Interaction/Abilities/GBFGameplayAbility_Interact.h index b61abd1f..4bd7c547 100644 --- a/Source/GameBaseFramework/Public/Interaction/Abilities/GBFGameplayAbility_Interact.h +++ b/Source/GameBaseFramework/Public/Interaction/Abilities/GBFGameplayAbility_Interact.h @@ -127,7 +127,6 @@ class GAMEBASEFRAMEWORK_API UGBFGameplayAbility_Interact : public UGBFGameplayAb void OnPressCallBack( OptionHandle interaction_option ); void UpdateIndicators(); void GatherTargetInfos( TArray< InteractableTargetInfos > & target_infos, const TArray< UGBFInteractableComponent * > & interactable_components ) const; - void ResetUnusedInteractions( const TArray< InteractableTargetInfos > & target_infos ); void RegisterInteractions( const TArray< InteractableTargetInfos > & target_infos ); void RegisterInteraction( const InteractableTargetInfos & target_infos ); diff --git a/Source/GameBaseFramework/Public/Interaction/GBFInteractionAllowCondition.h b/Source/GameBaseFramework/Public/Interaction/GBFInteractionAllowCondition.h new file mode 100644 index 00000000..6463f043 --- /dev/null +++ b/Source/GameBaseFramework/Public/Interaction/GBFInteractionAllowCondition.h @@ -0,0 +1,57 @@ +#pragma once + +#include "GBFInteractableComponent.h" + +#include + +#include "GBFInteractionAllowCondition.generated.h" + +UCLASS( Abstract, Const, Blueprintable, DefaultToInstanced, EditInlineNew ) +class GAMEBASEFRAMEWORK_API UGBFInteractionAllowCondition : public UObject +{ + GENERATED_BODY() + +public: + UFUNCTION( BlueprintPure, BlueprintNativeEvent ) + bool AreAllInteractionsAllowed( UGBFInteractableComponent * interactable_component, const FGameplayAbilityActorInfo & instigator_infos ) const; + + UFUNCTION( BlueprintPure, BlueprintNativeEvent ) + bool IsOptionAllowed( UGBFInteractableComponent * interactable_component, const FGameplayAbilityActorInfo & instigator_infos, const FGBFInteractionOption & option ) const; +}; + +UCLASS( abstract ) +class GAMEBASEFRAMEWORK_API UGBFInteractionAllowCondition_TagRequirements : public UGBFInteractionAllowCondition +{ +public: + GENERATED_BODY() + + bool AreAllInteractionsAllowed_Implementation( UGBFInteractableComponent * interactable_component, const FGameplayAbilityActorInfo & instigator_infos ) const override; + bool IsOptionAllowed_Implementation( UGBFInteractableComponent * interactable_component, const FGameplayAbilityActorInfo & instigator_infos, const FGBFInteractionOption & option ) const override; + +protected: + virtual void GetGameplayTags( FGameplayTagContainer & tags, UGBFInteractableComponent * interactable_component, const FGameplayAbilityActorInfo & instigator_infos ) const; + +private: + UPROPERTY( EditAnywhere ) + FGameplayTagRequirements TagRequirements; +}; + +UCLASS() +class GAMEBASEFRAMEWORK_API UGBFInteractionAllowCondition_InstigatorTags : public UGBFInteractionAllowCondition_TagRequirements +{ +public: + GENERATED_BODY() + +protected: + void GetGameplayTags( FGameplayTagContainer & tags, UGBFInteractableComponent * interactable_component, const FGameplayAbilityActorInfo & instigator_infos ) const override; +}; + +UCLASS() +class GAMEBASEFRAMEWORK_API UGBFInteractionAllowCondition_InteractableActorTags : public UGBFInteractionAllowCondition_TagRequirements +{ +public: + GENERATED_BODY() + +protected: + void GetGameplayTags( FGameplayTagContainer & tags, UGBFInteractableComponent * interactable_component, const FGameplayAbilityActorInfo & instigator_infos ) const override; +}; \ No newline at end of file diff --git a/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h b/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h index 0419935e..9f2758f7 100644 --- a/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h +++ b/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h @@ -6,6 +6,7 @@ #include "GBFInteractionOption.generated.h" +class UGBFInteractionAllowCondition; class UGBFInteractionEventCustomization; class UInputAction; class UInputMappingContext; @@ -115,11 +116,8 @@ struct FGBFInteractionOption UPROPERTY( EditAnywhere, Instanced ) TObjectPtr< UGBFInteractionEventCustomization > EventCustomization; - UPROPERTY( EditAnywhere, BlueprintReadOnly ) - FGameplayTagRequirements InteractableTargetTagRequirements; - - UPROPERTY( EditAnywhere, BlueprintReadOnly ) - FGameplayTagRequirements InstigatorTagRequirements; + UPROPERTY( EditAnywhere, Instanced ) + TArray< TObjectPtr< UGBFInteractionAllowCondition > > AllowConditions; UPROPERTY( EditAnywhere, BlueprintReadOnly ) TObjectPtr< const UInputAction > InputAction = nullptr; @@ -166,11 +164,8 @@ struct FGBFInteractionOptionContainer UPROPERTY( EditAnywhere, BlueprintReadOnly ) EGBFInteractionGroup InteractionGroup = EGBFInteractionGroup::Exclusive; - UPROPERTY( EditAnywhere, BlueprintReadOnly ) - FGameplayTagRequirements InteractableTargetTagRequirements; - - UPROPERTY( EditAnywhere, BlueprintReadOnly ) - FGameplayTagRequirements InstigatorTagRequirements; + UPROPERTY( EditAnywhere, Instanced ) + TArray< TObjectPtr< UGBFInteractionAllowCondition > > AllowConditions; UPROPERTY( EditAnywhere, BlueprintReadOnly ) FGBFInteractionWidgetInfos CommonWidgetInfos; @@ -199,8 +194,7 @@ FORCEINLINE FGBFInteractionOptionContainer & FGBFInteractionOptionContainer::ope InputMappingContext = other.InputMappingContext; DefaultInputAction = other.DefaultInputAction; InteractionGroup = other.InteractionGroup; - InteractableTargetTagRequirements = other.InteractableTargetTagRequirements; - InstigatorTagRequirements = other.InstigatorTagRequirements; + Options = other.Options; CommonWidgetInfos = other.CommonWidgetInfos;