第一次提交
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Interaction/Abilities/GGS_GameplayAbility_Interaction.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GGS_LogChannels.h"
|
||||
#include "SmartObjectBlueprintFunctionLibrary.h"
|
||||
#include "Interaction/GGS_InteractionDefinition.h"
|
||||
#include "Interaction/GGS_InteractionSystemComponent.h"
|
||||
#include "Misc/DataValidation.h"
|
||||
|
||||
UGGS_GameplayAbility_Interaction::UGGS_GameplayAbility_Interaction()
|
||||
{
|
||||
InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
|
||||
ReplicationPolicy = EGameplayAbilityReplicationPolicy::ReplicateYes;
|
||||
}
|
||||
|
||||
void UGGS_GameplayAbility_Interaction::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,
|
||||
const FGameplayEventData* TriggerEventData)
|
||||
{
|
||||
InteractionSystem = UGGS_InteractionSystemComponent::GetInteractionSystemComponent(ActorInfo->AvatarActor.Get());
|
||||
if (InteractionSystem == nullptr)
|
||||
{
|
||||
EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
|
||||
return;
|
||||
}
|
||||
InteractionSystem->OnInteractableActorChangedEvent.AddDynamic(this, &ThisClass::OnInteractActorChanged);
|
||||
|
||||
Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
|
||||
}
|
||||
|
||||
void UGGS_GameplayAbility_Interaction::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,
|
||||
bool bReplicateEndAbility, bool bWasCancelled)
|
||||
{
|
||||
if (UGGS_InteractionSystemComponent* UserComponent = UGGS_InteractionSystemComponent::GetInteractionSystemComponent(ActorInfo->AvatarActor.Get()))
|
||||
{
|
||||
UserComponent->OnInteractableActorChangedEvent.RemoveDynamic(this, &ThisClass::OnInteractActorChanged);
|
||||
}
|
||||
Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
|
||||
}
|
||||
|
||||
bool UGGS_GameplayAbility_Interaction::TryClaimInteraction(int32 Index, FSmartObjectClaimHandle& ClaimedHandle)
|
||||
{
|
||||
USmartObjectSubsystem* Subsystem = USmartObjectSubsystem::GetCurrent(GetWorld());
|
||||
|
||||
check(Subsystem!=nullptr)
|
||||
const TArray<FGGS_InteractionOption>& InteractionInstances = InteractionSystem->GetInteractionOptions();
|
||||
if (!InteractionInstances.IsValidIndex(Index))
|
||||
{
|
||||
GGS_CLOG(Error, "Interaction at index(%d) not exist!!", Index)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (InteractionInstances[Index].Definition == nullptr)
|
||||
{
|
||||
GGS_CLOG(Error, "Interaction at index(%d) has invalid definition!", Index)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (InteractionInstances[Index].SlotState != ESmartObjectSlotState::Free)
|
||||
{
|
||||
GGS_CLOG(Error, "Interaction(%s) was Claimed/Occupied!", *InteractionInstances[Index].Definition->Text.ToString())
|
||||
return false;
|
||||
}
|
||||
|
||||
const FGGS_InteractionOption& CurrentOption = InteractionInstances[Index];
|
||||
|
||||
FSmartObjectClaimHandle NewlyClaimedHandle = USmartObjectBlueprintFunctionLibrary::MarkSmartObjectSlotAsClaimed(GetWorld(), CurrentOption.RequestResult.SlotHandle, GetAvatarActorFromActorInfo());
|
||||
|
||||
// A valid claimed handle can point to an object that is no longer part of the simulation
|
||||
if (!Subsystem->IsClaimedSmartObjectValid(NewlyClaimedHandle))
|
||||
{
|
||||
GGS_CLOG(Error, "Interaction(%s) refers to an object that is no longer available.!", *InteractionInstances[Index].Definition->Text.ToString())
|
||||
return false;
|
||||
}
|
||||
|
||||
ClaimedHandle = NewlyClaimedHandle;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void UGGS_GameplayAbility_Interaction::OnInteractActorChanged_Implementation(AActor* OldActor, AActor* NewActor)
|
||||
{
|
||||
}
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
EDataValidationResult UGGS_GameplayAbility_Interaction::IsDataValid(class FDataValidationContext& Context) const
|
||||
{
|
||||
if (ReplicationPolicy != EGameplayAbilityReplicationPolicy::ReplicateYes)
|
||||
{
|
||||
Context.AddError(FText::FromString(TEXT("Core Interaction ability must be Replicated to allow client->server communications via RPC.")));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
if (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::LocalOnly || NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::ServerOnly)
|
||||
{
|
||||
Context.AddError(FText::FromString(TEXT("Core Interaction ability must not be Local/Server only.")));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
if (!AbilityTriggers.IsEmpty())
|
||||
{
|
||||
Context.AddError(FText::FromString(TEXT("Core Interaction ability doesn't allow event triggering!")));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
if (InstancingPolicy != EGameplayAbilityInstancingPolicy::InstancedPerActor)
|
||||
{
|
||||
Context.AddError(FText::FromString(TEXT("Core Interaction ability's instancing policy must be InstancedPerActor")));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
return Super::IsDataValid(Context);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Interaction/Behaviors/GGS_GameplayBehaviorConfig_InteractionWithAbility.h"
|
||||
#include "Interaction/Behaviors/GGS_GameplayBehavior_InteractionWithAbility.h"
|
||||
|
||||
UGGS_GameplayBehaviorConfig_InteractionWithAbility::UGGS_GameplayBehaviorConfig_InteractionWithAbility()
|
||||
{
|
||||
BehaviorClass = UGGS_GameplayBehavior_InteractionWithAbility::StaticClass();
|
||||
}
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
EDataValidationResult UGGS_GameplayBehaviorConfig_InteractionWithAbility::IsDataValid(class FDataValidationContext& Context) const
|
||||
{
|
||||
if (BehaviorClass == nullptr || !BehaviorClass->GetClass()->IsChildOf(UGGS_GameplayBehavior_InteractionWithAbility::StaticClass()))
|
||||
{
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
return Super::IsDataValid(Context);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Interaction/Behaviors/GGS_GameplayBehavior_InteractionWithAbility.h"
|
||||
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystemGlobals.h"
|
||||
#include "GGS_LogChannels.h"
|
||||
#include "Abilities/GameplayAbility.h"
|
||||
#include "Interaction/Behaviors/GGS_GameplayBehaviorConfig_InteractionWithAbility.h"
|
||||
#include "Interaction/GGS_InteractionSystemComponent.h"
|
||||
|
||||
bool UGGS_GameplayBehavior_InteractionWithAbility::Trigger(AActor& InAvatar, const UGameplayBehaviorConfig* Config, AActor* SmartObjectOwner)
|
||||
{
|
||||
bTransientIsTriggering = true;
|
||||
bTransientIsActive = false;
|
||||
TransientAvatar = &InAvatar;
|
||||
TransientSmartObjectOwner = SmartObjectOwner;
|
||||
|
||||
UGGS_InteractionSystemComponent* InteractionSystem = InAvatar.FindComponentByClass<UGGS_InteractionSystemComponent>();
|
||||
|
||||
if (!InteractionSystem)
|
||||
{
|
||||
GGS_CLOG(Error, "Missing InteractionSystem Component!")
|
||||
return false;
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* Asc = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(&InAvatar);
|
||||
if (!Asc)
|
||||
{
|
||||
GGS_CLOG(Error, "Missing Ability System Component!")
|
||||
return false;
|
||||
}
|
||||
|
||||
TSubclassOf<UGameplayAbility> AbilityClass{nullptr};
|
||||
int32 AbilityLevel = 0;
|
||||
if (!CheckValidAbilitySetting(Config, AbilityClass, AbilityLevel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FGameplayAbilitySpec* Handle = Asc->FindAbilitySpecFromClass(AbilityClass))
|
||||
{
|
||||
GGS_CLOG(Error, "Try granting repeated interaction ability of class:%s, which is not supported!", *AbilityClass->GetName())
|
||||
return false;
|
||||
}
|
||||
|
||||
GrantedAbilityClass = AbilityClass;
|
||||
|
||||
AbilityEndedDelegateHandle = Asc->OnAbilityEnded.AddUObject(this, &ThisClass::OnAbilityEndedCallback);
|
||||
|
||||
//Ability trigger by event when activation polciy=ServerInitied won't work.
|
||||
AbilitySpecHandle = Asc->K2_GiveAbilityAndActivateOnce(AbilityClass, AbilityLevel);
|
||||
|
||||
if (!AbilitySpecHandle.IsValid())
|
||||
{
|
||||
GGS_CLOG(Error, "Can't active ability of class:%s! Check ability settings!", *AbilityClass->GetName())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Special case: behavior already interrupted
|
||||
if (bBehaviorWasInterrupted && AbilitySpecHandle.IsValid() && !bAbilityEnded)
|
||||
{
|
||||
Asc->ClearAbility(AbilitySpecHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AbilitySpecHandle.IsValid() && bAbilityEnded)
|
||||
{
|
||||
GGS_CLOG(Verbose, "Instantly executed interaction ability:%s,handle%s", *AbilityClass->GetName(), *AbilitySpecHandle.ToString())
|
||||
EndBehavior(InAvatar, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
GGS_CLOG(Verbose, "Granted and activate interaction ability:%s,handle%s", *AbilityClass->GetName(), *AbilitySpecHandle.ToString())
|
||||
|
||||
//TODO what happens when avatar or target get destoryied?
|
||||
// SOOwner销毁的时候, 需要Abort当前行为, 目的是清除赋予的Ability
|
||||
// SmartObjectOwner->OnDestroyed.AddDynamic(this, &ThisClass::OnSmartObjectOwnerDestroyed);
|
||||
GGS_CLOG(Verbose, "Interaction begins with ability:%s", *AbilityClass->GetName())
|
||||
|
||||
bTransientIsTriggering = false;
|
||||
bTransientIsActive = true;
|
||||
return bTransientIsActive;
|
||||
}
|
||||
|
||||
void UGGS_GameplayBehavior_InteractionWithAbility::EndBehavior(AActor& Avatar, const bool bInterrupted)
|
||||
{
|
||||
GGS_CLOG(Verbose, "Interaction behavior ended %s", bInterrupted?TEXT("due to interruption!"):TEXT("normally!"))
|
||||
|
||||
// clear ability stuff.
|
||||
if (UAbilitySystemComponent* Asc = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(&Avatar))
|
||||
{
|
||||
if (AbilityEndedDelegateHandle.IsValid())
|
||||
{
|
||||
Asc->OnAbilityEnded.Remove(AbilityEndedDelegateHandle);
|
||||
AbilityEndedDelegateHandle.Reset();
|
||||
}
|
||||
|
||||
// Special case: behavior interrupting active ability, so cancel ability.
|
||||
if (bInterrupted && bTransientIsActive && !bAbilityEnded && AbilitySpecHandle.IsValid())
|
||||
{
|
||||
if (const FGameplayAbilitySpec* Spec = Asc->FindAbilitySpecFromHandle(AbilitySpecHandle))
|
||||
{
|
||||
GGS_CLOG(Verbose, "Cancel ability(%s) because behavior was interrupted.", *Spec->Ability.GetClass()->GetName())
|
||||
Asc->CancelAbilityHandle(AbilitySpecHandle);
|
||||
}
|
||||
}
|
||||
|
||||
if (bInterrupted && !bTransientIsActive && AbilitySpecHandle.IsValid())
|
||||
{
|
||||
Asc->ClearAbility(AbilitySpecHandle);
|
||||
}
|
||||
}
|
||||
|
||||
Super::EndBehavior(Avatar, bInterrupted);
|
||||
|
||||
bBehaviorWasInterrupted = bInterrupted;
|
||||
}
|
||||
|
||||
bool UGGS_GameplayBehavior_InteractionWithAbility::CheckValidAbilitySetting(const UGameplayBehaviorConfig* Config, TSubclassOf<UGameplayAbility>& OutAbilityClass, int32& OutAbilityLevel)
|
||||
{
|
||||
// Ability class validation.
|
||||
const UGGS_GameplayBehaviorConfig_InteractionWithAbility* InteractionConfig = Cast<const UGGS_GameplayBehaviorConfig_InteractionWithAbility>(Config);
|
||||
if (!InteractionConfig)
|
||||
{
|
||||
GGS_CLOG(Error, "Invalid GameplayBehaviorConfig! expect Config be type of %s.", *UGGS_GameplayBehaviorConfig_InteractionWithAbility::StaticClass()->GetName())
|
||||
return false;
|
||||
}
|
||||
|
||||
const TSubclassOf<UGameplayAbility> AbilityClass = InteractionConfig->AbilityToGrant.LoadSynchronous();
|
||||
if (!AbilityClass)
|
||||
{
|
||||
GGS_CLOG(Error, "Invalid AbilityToGrant Class!")
|
||||
return false;
|
||||
}
|
||||
OutAbilityClass = AbilityClass;
|
||||
OutAbilityLevel = InteractionConfig->AbilityLevel;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UGGS_GameplayBehavior_InteractionWithAbility::OnAbilityEndedCallback(const FAbilityEndedData& EndedData)
|
||||
{
|
||||
if (bAbilityEnded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// check for ability granted by this behavior.
|
||||
if (EndedData.AbilitySpecHandle == AbilitySpecHandle || EndedData.AbilityThatEnded->GetClass() == GrantedAbilityClass)
|
||||
{
|
||||
bAbilityEnded = true;
|
||||
bAbilityWasCancelled = EndedData.bWasCancelled;
|
||||
|
||||
// Special case: behavior already active and abilities ended, ending behavior normally.
|
||||
if (!bTransientIsTriggering && bTransientIsActive)
|
||||
{
|
||||
GGS_CLOG(Verbose, "Interaction ability(%s) %s.", *EndedData.AbilityThatEnded.GetClass()->GetName(),
|
||||
EndedData.bWasCancelled?TEXT("was canceled"):TEXT("ended normally"))
|
||||
EndBehavior(*GetAvatar(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Interaction/GGS_InteractableInterface.h"
|
||||
|
||||
|
||||
// Add default functionality here for any IGGS_InteractableInterface functions that are not pure virtual.
|
||||
|
||||
FText IGGS_InteractableInterface::GetInteractionDisplayNameText_Implementation() const
|
||||
{
|
||||
if (UObject* Object = _getUObject())
|
||||
{
|
||||
return FText::FromString(GetNameSafe(Object));
|
||||
}
|
||||
return FText::GetEmpty();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Interaction/GGS_InteractionDefinition.h"
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Interaction/GGS_InteractionStructLibrary.h"
|
||||
#include "Interaction/GGS_InteractionDefinition.h"
|
||||
|
||||
FString FGGS_InteractionOption::ToString() const
|
||||
{
|
||||
return FString::Format(TEXT("{0} {1} {2}"), {
|
||||
Definition ? Definition->Text.ToString() : TEXT("Null Definition"), SlotState == ESmartObjectSlotState::Free ? TEXT("Valid") : TEXT("Invalid"), SlotIndex
|
||||
});
|
||||
}
|
||||
|
||||
bool operator==(const FGGS_InteractionOption& Lhs, const FGGS_InteractionOption& RHS)
|
||||
{
|
||||
return Lhs.Definition == RHS.Definition
|
||||
&& Lhs.RequestResult == RHS.RequestResult
|
||||
&& Lhs.SlotIndex == RHS.SlotIndex
|
||||
&& Lhs.SlotState == RHS.SlotState;
|
||||
}
|
||||
|
||||
bool operator!=(const FGGS_InteractionOption& Lhs, const FGGS_InteractionOption& RHS)
|
||||
{
|
||||
return !(Lhs == RHS);
|
||||
}
|
||||
|
||||
bool operator<(const FGGS_InteractionOption& Lhs, const FGGS_InteractionOption& RHS)
|
||||
{
|
||||
return Lhs.SlotIndex < RHS.SlotIndex;
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Interaction/GGS_InteractionSystemComponent.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameplayBehaviorSmartObjectBehaviorDefinition.h"
|
||||
#include "GGS_LogChannels.h"
|
||||
#include "SmartObjectComponent.h"
|
||||
#include "SmartObjectSubsystem.h"
|
||||
#include "Interaction/GGS_InteractableInterface.h"
|
||||
#include "Interaction/GGS_SmartObjectFunctionLibrary.h"
|
||||
#include "Interaction/GGS_InteractionStructLibrary.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "Net/Core/PushModel/PushModel.h"
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UGGS_InteractionSystemComponent::UGGS_InteractionSystemComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
SetIsReplicatedByDefault(true);
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
FDoRepLifetimeParams Params;
|
||||
Params.bIsPushBased = true;
|
||||
Params.Condition = COND_OwnerOnly;
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, InteractableActor, Params);
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, NumsOfInteractableActors, Params);
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, InteractingOption, Params);
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, InteractionOptions, Params);
|
||||
}
|
||||
|
||||
UGGS_InteractionSystemComponent* UGGS_InteractionSystemComponent::GetInteractionSystemComponent(const AActor* Actor)
|
||||
{
|
||||
return IsValid(Actor) ? Actor->FindComponentByClass<UGGS_InteractionSystemComponent>() : nullptr;
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::CycleInteractableActors_Implementation(bool bNext)
|
||||
{
|
||||
if (bInteracting || InteractableActors.Num() <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int32 Index = InteractableActor != nullptr ? InteractableActors.IndexOfByKey(InteractableActor) : 0;
|
||||
if (!InteractableActors.IsValidIndex(Index)) //一个都没
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (bNext)
|
||||
{
|
||||
Index = FMath::Clamp(Index + 1, 0, InteractableActors.Num());
|
||||
}
|
||||
else
|
||||
{
|
||||
Index = FMath::Clamp(Index - 1, 0, InteractableActors.Num());
|
||||
}
|
||||
if (InteractableActors.IsValidIndex(Index) && InteractableActors[Index] != nullptr && InteractableActors[Index] !=
|
||||
InteractableActor)
|
||||
{
|
||||
SetInteractableActor(InteractableActors[Index]);
|
||||
}
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::SearchInteractableActors()
|
||||
{
|
||||
OnSearchInteractableActorsEvent.Broadcast();
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::SetInteractableActors(TArray<AActor*> NewActors)
|
||||
{
|
||||
if (!GetOwner()->HasAuthority())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InteractableActors = NewActors;
|
||||
SetInteractableActorsNum(InteractableActors.Num());
|
||||
OnInteractableActorsChanged();
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::SetInteractableActorsNum(int32 NewNum)
|
||||
{
|
||||
if (NewNum != NumsOfInteractableActors)
|
||||
{
|
||||
const int32 PrevNum = NumsOfInteractableActors;
|
||||
NumsOfInteractableActors = NewNum;
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, NumsOfInteractableActors, this)
|
||||
OnInteractableActorsNumChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::SetInteractableActor(AActor* InActor)
|
||||
{
|
||||
if (InActor != InteractableActor)
|
||||
{
|
||||
AActor* OldActor = InteractableActor;
|
||||
InteractableActor = InActor;
|
||||
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, InteractableActor, this)
|
||||
OnInteractableActorChanged(OldActor);
|
||||
}
|
||||
}
|
||||
|
||||
FSmartObjectRequestFilter UGGS_InteractionSystemComponent::GetSmartObjectRequestFilter_Implementation()
|
||||
{
|
||||
return DefaultRequestFilter;
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::StartInteraction(int32 NewIndex)
|
||||
{
|
||||
if (bInteracting)
|
||||
{
|
||||
GGS_CLOG(Warning, "Can't start interaction(%d) while already interacting(%d)", NewIndex, InteractingOption)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InteractionOptions.IsValidIndex(NewIndex))
|
||||
{
|
||||
GGS_CLOG(Warning, "Try start invalid interaction(%d)", NewIndex)
|
||||
return;
|
||||
}
|
||||
|
||||
int32 Prev = InteractingOption;
|
||||
InteractingOption = NewIndex;
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, InteractingOption, this);
|
||||
OnInteractingOptionChanged(Prev);
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::EndInteraction()
|
||||
{
|
||||
if (!bInteracting)
|
||||
{
|
||||
//GGS_CLOG(Warning, TEXT("no need to end interaction when there's no any active interaction."))
|
||||
return;
|
||||
}
|
||||
|
||||
int32 Prev = InteractingOption;
|
||||
InteractingOption = INDEX_NONE;
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, InteractingOption, this);
|
||||
OnInteractingOptionChanged(Prev);
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::InstantInteraction(int32 NewIndex)
|
||||
{
|
||||
if (bInteracting)
|
||||
{
|
||||
GGS_CLOG(Warning, "Can't trigger instant interaction(%d) while already interacting(%d)", NewIndex, InteractingOption)
|
||||
return;
|
||||
}
|
||||
if (!InteractionOptions.IsValidIndex(NewIndex))
|
||||
{
|
||||
GGS_CLOG(Warning, "Try trigger invalid interaction(%d)", NewIndex)
|
||||
return;
|
||||
}
|
||||
|
||||
int32 Prev = InteractingOption;
|
||||
InteractingOption = NewIndex;
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, InteractingOption, this);
|
||||
OnInteractingOptionChanged(Prev);
|
||||
|
||||
int32 Prev2 = InteractingOption;
|
||||
InteractingOption = INDEX_NONE;
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, InteractingOption, this);
|
||||
OnInteractingOptionChanged(Prev2);
|
||||
}
|
||||
|
||||
bool UGGS_InteractionSystemComponent::IsInteracting() const
|
||||
{
|
||||
return bInteracting;
|
||||
}
|
||||
|
||||
int32 UGGS_InteractionSystemComponent::GetInteractingOption() const
|
||||
{
|
||||
return InteractingOption;
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::OnInteractableActorChanged(AActor* OldActor)
|
||||
{
|
||||
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
|
||||
{
|
||||
RefreshOptionsForActor();
|
||||
}
|
||||
|
||||
if (IsValid(OldActor) && OldActor->GetClass()->ImplementsInterface(UGGS_InteractableInterface::StaticClass()))
|
||||
{
|
||||
IGGS_InteractableInterface::Execute_OnInteractionDeselected(OldActor, GetOwner());
|
||||
}
|
||||
|
||||
if (IsValid(InteractableActor) && InteractableActor->GetClass()->ImplementsInterface(UGGS_InteractableInterface::StaticClass()))
|
||||
{
|
||||
IGGS_InteractableInterface::Execute_OnInteractionSelected(InteractableActor, GetOwner());
|
||||
}
|
||||
|
||||
OnInteractableActorChangedEvent.Broadcast(OldActor, InteractableActor);
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::OnInteractableActorsNumChanged()
|
||||
{
|
||||
OnInteractableActorNumChangedEvent.Broadcast(NumsOfInteractableActors);
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::OnInteractableActorsChanged_Implementation()
|
||||
{
|
||||
if (!bInteracting)
|
||||
{
|
||||
// update potential actor.
|
||||
if (!IsValid(InteractableActor) || !InteractableActors.Contains(InteractableActor))
|
||||
{
|
||||
if (InteractableActors.IsValidIndex(0) && IsValid(InteractableActors[0]))
|
||||
{
|
||||
SetInteractableActor(InteractableActors[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetInteractableActor(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (bNewActorHasPriority)
|
||||
{
|
||||
if (IsValid(InteractableActor) && InteractableActors.IsValidIndex(0) && InteractableActors[0] != InteractableActor)
|
||||
{
|
||||
SetInteractableActor(InteractableActors[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::OnSmartObjectEventCallback(const FSmartObjectEventData& EventData)
|
||||
{
|
||||
check(InteractableActor != nullptr);
|
||||
|
||||
for (int32 i = 0; i < InteractionOptions.Num(); i++)
|
||||
{
|
||||
const FGGS_InteractionOption& Option = InteractionOptions[i];
|
||||
if (EventData.SmartObjectHandle == Option.RequestResult.SmartObjectHandle && EventData.SlotHandle == Option.RequestResult.SlotHandle)
|
||||
{
|
||||
if (EventData.Reason == ESmartObjectChangeReason::OnOccupied || EventData.Reason == ESmartObjectChangeReason::OnReleased || EventData.Reason == ESmartObjectChangeReason::OnClaimed)
|
||||
{
|
||||
RefreshOptionsForActor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::OnInteractionOptionsChanged()
|
||||
{
|
||||
for (FGGS_InteractionOption& InteractionOption : InteractionOptions)
|
||||
{
|
||||
GGS_CLOG(Verbose, "Available Options:%s", *InteractionOption.ToString())
|
||||
}
|
||||
OnInteractionOptionsChangedEvent.Broadcast();
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::OnInteractingOptionChanged_Implementation(int32 PrevOptionIndex)
|
||||
{
|
||||
bool bPrevInteracting = bInteracting;
|
||||
bInteracting = InteractingOption != INDEX_NONE;
|
||||
|
||||
if (IsValid(InteractableActor) && InteractableActor->GetClass()->ImplementsInterface(UGGS_InteractableInterface::StaticClass()))
|
||||
{
|
||||
if (!bPrevInteracting && bInteracting)
|
||||
{
|
||||
IGGS_InteractableInterface::Execute_OnInteractionStarted(InteractableActor, GetOwner(), InteractingOption);
|
||||
}
|
||||
if (bPrevInteracting && !bInteracting)
|
||||
{
|
||||
IGGS_InteractableInterface::Execute_OnInteractionEnded(InteractableActor, GetOwner(), PrevOptionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
OnInteractingStateChangedEvent.Broadcast(bInteracting);
|
||||
}
|
||||
|
||||
void UGGS_InteractionSystemComponent::RefreshOptionsForActor()
|
||||
{
|
||||
USmartObjectSubsystem* Subsystem = USmartObjectSubsystem::GetCurrent(GetWorld());
|
||||
|
||||
if (!Subsystem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// getting new options for current interact actor.
|
||||
TArray<FGGS_InteractionOption> NewOptions;
|
||||
{
|
||||
TArray<FSmartObjectRequestResult> Results;
|
||||
if (IsValid(InteractableActor) && UGGS_SmartObjectFunctionLibrary::FindSmartObjectsWithInteractionEntranceInActor(GetSmartObjectRequestFilter(), InteractableActor, Results, GetOwner()))
|
||||
{
|
||||
for (int32 i = 0; i < Results.Num(); i++)
|
||||
{
|
||||
FGGS_InteractionOption Option;
|
||||
UGGS_InteractionDefinition* FoundDefinition;
|
||||
if (UGGS_SmartObjectFunctionLibrary::FindInteractionDefinitionFromSmartObjectSlot(this, Results[i].SlotHandle, FoundDefinition))
|
||||
{
|
||||
Option.Definition = FoundDefinition;
|
||||
Option.SlotState = Subsystem->GetSlotState(Results[i].SlotHandle);
|
||||
Option.RequestResult = Results[i];
|
||||
Option.SlotIndex = i;
|
||||
Option.BehaviorDefinition = Subsystem->GetBehaviorDefinitionByRequestResult(Results[i], USmartObjectBehaviorDefinition::StaticClass());
|
||||
NewOptions.Add(Option);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check any options changed.
|
||||
bool bOptionsChanged = false;
|
||||
{
|
||||
if (NewOptions.Num() == InteractionOptions.Num())
|
||||
{
|
||||
NewOptions.Sort();
|
||||
|
||||
for (int OptionIndex = 0; OptionIndex < NewOptions.Num(); OptionIndex++)
|
||||
{
|
||||
const FGGS_InteractionOption& NewOption = NewOptions[OptionIndex];
|
||||
const FGGS_InteractionOption& CurrentOption = InteractionOptions[OptionIndex];
|
||||
|
||||
if (NewOption != CurrentOption)
|
||||
{
|
||||
bOptionsChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bOptionsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bOptionsChanged)
|
||||
{
|
||||
// unregister event callbacks for existing options.
|
||||
for (int32 i = 0; i < InteractionOptions.Num(); i++)
|
||||
{
|
||||
auto& Handle = InteractionOptions[i].RequestResult.SlotHandle;
|
||||
if (SlotCallbacks.Contains(Handle))
|
||||
{
|
||||
if (FOnSmartObjectEvent* OnEventDelegate = Subsystem->GetSlotEventDelegate(Handle))
|
||||
{
|
||||
OnEventDelegate->Remove(SlotCallbacks[Handle]);
|
||||
SlotCallbacks.Remove(Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (FGGS_InteractionOption& Option : InteractionOptions)
|
||||
{
|
||||
if (SlotCallbacks.Contains(Option.RequestResult.SlotHandle))
|
||||
{
|
||||
if (FOnSmartObjectEvent* OnEventDelegate = Subsystem->GetSlotEventDelegate(Option.RequestResult.SlotHandle))
|
||||
{
|
||||
OnEventDelegate->Remove(SlotCallbacks[Option.RequestResult.SlotHandle]);
|
||||
SlotCallbacks.Remove(Option.RequestResult.SlotHandle);
|
||||
}
|
||||
}
|
||||
// if (Option.DelegateHandle.IsValid())
|
||||
// {
|
||||
// if (FOnSmartObjectEvent* OnEventDelegate = Subsystem->GetSlotEventDelegate(Option.RequestResult.SlotHandle))
|
||||
// {
|
||||
// OnEventDelegate->Remove(Option.DelegateHandle);
|
||||
// Option.DelegateHandle.Reset();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
InteractionOptions = NewOptions;
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, InteractionOptions, this)
|
||||
|
||||
GGS_CLOG(Verbose, "Interaction options changed, nums of options:%d", InteractionOptions.Num())
|
||||
|
||||
// register slot event callbacks.
|
||||
// for (int32 i = 0; i < InteractionOptions.Num(); i++)
|
||||
// {
|
||||
// FGGS_InteractionOption& Option = InteractionOptions[i];
|
||||
// if (FOnSmartObjectEvent* OnEventDelegate = Subsystem->GetSlotEventDelegate(Option.RequestResult.SlotHandle))
|
||||
// {
|
||||
// Option.DelegateHandle = OnEventDelegate->AddUObject(this, &ThisClass::OnSmartObjectEventCallback);
|
||||
// }
|
||||
// }
|
||||
|
||||
for (int32 i = 0; i < InteractionOptions.Num(); i++)
|
||||
{
|
||||
auto& Handle = InteractionOptions[i].RequestResult.SlotHandle;
|
||||
if (FOnSmartObjectEvent* OnEventDelegate = Subsystem->GetSlotEventDelegate(Handle))
|
||||
{
|
||||
FDelegateHandle DelegateHandle = OnEventDelegate->AddUObject(this, &ThisClass::OnSmartObjectEventCallback);
|
||||
SlotCallbacks.Emplace(Handle, DelegateHandle);
|
||||
}
|
||||
}
|
||||
|
||||
OnInteractionOptionsChanged();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Interaction/GGS_SmartObjectFunctionLibrary.h"
|
||||
#include "GameplayBehaviorSmartObjectBehaviorDefinition.h"
|
||||
#include "GameplayBehaviorConfig.h"
|
||||
#include "SmartObjectBlueprintFunctionLibrary.h"
|
||||
#include "SmartObjectDefinition.h"
|
||||
#include "Engine/World.h"
|
||||
#include "SmartObjectSubsystem.h"
|
||||
#include "Interaction/GGS_InteractionDefinition.h"
|
||||
|
||||
UGameplayBehaviorConfig* UGGS_SmartObjectFunctionLibrary::GetGameplayBehaviorConfig(const USmartObjectBehaviorDefinition* BehaviorDefinition)
|
||||
{
|
||||
if (const UGameplayBehaviorSmartObjectBehaviorDefinition* Definition = Cast<UGameplayBehaviorSmartObjectBehaviorDefinition>(BehaviorDefinition))
|
||||
{
|
||||
return Definition->GameplayBehaviorConfig;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool UGGS_SmartObjectFunctionLibrary::FindGameplayBehaviorConfig(const USmartObjectBehaviorDefinition* BehaviorDefinition, TSubclassOf<UGameplayBehaviorConfig> DesiredClass,
|
||||
UGameplayBehaviorConfig*& OutConfig)
|
||||
{
|
||||
if (UClass* RealClass = DesiredClass)
|
||||
{
|
||||
if (UGameplayBehaviorConfig* Config = GetGameplayBehaviorConfig(BehaviorDefinition))
|
||||
{
|
||||
if (Config->GetClass()->IsChildOf(RealClass))
|
||||
{
|
||||
OutConfig = Config;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UGGS_SmartObjectFunctionLibrary::FindSmartObjectsWithInteractionEntranceInActor(const FSmartObjectRequestFilter& Filter, AActor* SearchActor, TArray<FSmartObjectRequestResult>& OutResults,
|
||||
const AActor* UserActor)
|
||||
{
|
||||
if (!IsValid(SearchActor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
TArray<FSmartObjectRequestResult> Results;
|
||||
USmartObjectBlueprintFunctionLibrary::FindSmartObjectsInActor(Filter, SearchActor, Results, UserActor);
|
||||
if (Results.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter results which has definiton entry.
|
||||
for (int32 i = 0; i < Results.Num(); i++)
|
||||
{
|
||||
UGGS_InteractionDefinition* FoundDefinition;
|
||||
if (FindInteractionDefinitionFromSmartObjectSlot(SearchActor, Results[i].SlotHandle, FoundDefinition))
|
||||
{
|
||||
OutResults.Add(Results[i]);
|
||||
}
|
||||
}
|
||||
return !OutResults.IsEmpty();
|
||||
}
|
||||
|
||||
bool UGGS_SmartObjectFunctionLibrary::FindInteractionDefinitionFromSmartObjectSlot(UObject* WorldContext, FSmartObjectSlotHandle SmartObjectSlotHandle, UGGS_InteractionDefinition*& OutDefinition)
|
||||
{
|
||||
if (WorldContext && WorldContext->GetWorld() && SmartObjectSlotHandle.IsValid())
|
||||
{
|
||||
if (USmartObjectSubsystem* Subsystem = WorldContext->GetWorld()->GetSubsystem<USmartObjectSubsystem>())
|
||||
{
|
||||
Subsystem->ReadSlotData(SmartObjectSlotHandle, [ &OutDefinition](FConstSmartObjectSlotView SlotView)
|
||||
{
|
||||
if (const FGGS_SmartObjectInteractionEntranceData* Entry = SlotView.GetDefinitionDataPtr<FGGS_SmartObjectInteractionEntranceData>())
|
||||
{
|
||||
if (!Entry->DefinitionDA.IsNull())
|
||||
{
|
||||
OutDefinition = Entry->DefinitionDA.LoadSynchronous();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return OutDefinition != nullptr;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Interaction/Targeting/GGS_TargetingFilterTask_InteractionSmartObjects.h"
|
||||
#include "Interaction/GGS_InteractionSystemComponent.h"
|
||||
#include "Interaction/GGS_SmartObjectFunctionLibrary.h"
|
||||
|
||||
bool UGGS_TargetingFilterTask_InteractionSmartObjects::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (AActor* Actor = TargetData.HitResult.GetActor())
|
||||
{
|
||||
if (UGGS_InteractionSystemComponent* InteractionSys = UGGS_InteractionSystemComponent::GetInteractionSystemComponent(SourceContext->SourceActor))
|
||||
{
|
||||
TArray<FSmartObjectRequestResult> Results;
|
||||
|
||||
return !UGGS_SmartObjectFunctionLibrary::FindSmartObjectsWithInteractionEntranceInActor(InteractionSys->GetSmartObjectRequestFilter(), Actor, Results, InteractionSys->GetOwner());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Interaction/Tasks/GGS_AbilityTask_UseSmartObjectWithGameplayBehavior.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameplayBehavior.h"
|
||||
#include "GameplayBehaviorConfig.h"
|
||||
#include "GameplayBehaviorSmartObjectBehaviorDefinition.h"
|
||||
#include "GameplayBehaviorSubsystem.h"
|
||||
#include "GGS_LogChannels.h"
|
||||
#include "SmartObjectComponent.h"
|
||||
#include "SmartObjectSubsystem.h"
|
||||
|
||||
UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior::UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer)
|
||||
{
|
||||
bBehaviorFinished = false;
|
||||
}
|
||||
|
||||
UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior* UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior::UseSmartObjectWithGameplayBehavior(UGameplayAbility* OwningAbility,
|
||||
FSmartObjectClaimHandle ClaimHandle, ESmartObjectClaimPriority ClaimPriority)
|
||||
{
|
||||
if (OwningAbility == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior* MyTask = NewAbilityTask<UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior>(OwningAbility);
|
||||
if (MyTask == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
MyTask->SetClaimHandle(ClaimHandle);
|
||||
return MyTask;
|
||||
}
|
||||
|
||||
void UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior::Activate()
|
||||
{
|
||||
Super::Activate();
|
||||
bool bSuccess = false;
|
||||
ON_SCOPE_EXIT
|
||||
{
|
||||
if (!bSuccess)
|
||||
{
|
||||
EndTask();
|
||||
}
|
||||
};
|
||||
|
||||
if (!ensureMsgf(ClaimedHandle.IsValid(), TEXT("SmartObject handle must be valid at this point.")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
APawn* Pawn = Cast<APawn>(GetAvatarActor());
|
||||
if (Pawn == nullptr)
|
||||
{
|
||||
GGS_CLOG(Error, "Pawn required to use GameplayBehavior with claim handle: %s.", *LexToString(ClaimedHandle));
|
||||
return;
|
||||
}
|
||||
USmartObjectSubsystem* SmartObjectSubsystem = USmartObjectSubsystem::GetCurrent(Pawn->GetWorld());
|
||||
if (!ensureMsgf(SmartObjectSubsystem != nullptr, TEXT("SmartObjectSubsystem must be accessible at this point.")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// A valid claimed handle can point to an object that is no longer part of the simulation
|
||||
if (!SmartObjectSubsystem->IsClaimedSmartObjectValid(ClaimedHandle))
|
||||
{
|
||||
GGS_CLOG(Log, "Claim handle: %s refers to an object that is no longer available.", *LexToString(ClaimedHandle));
|
||||
return;
|
||||
}
|
||||
|
||||
// Register a callback to be notified if the claimed slot became unavailable
|
||||
SmartObjectSubsystem->RegisterSlotInvalidationCallback(ClaimedHandle, FOnSlotInvalidated::CreateUObject(this, &UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior::OnSlotInvalidated));
|
||||
|
||||
bSuccess = StartInteraction();
|
||||
}
|
||||
|
||||
bool UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior::StartInteraction()
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
USmartObjectSubsystem* SmartObjectSubsystem = USmartObjectSubsystem::GetCurrent(World);
|
||||
if (!ensure(SmartObjectSubsystem))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const UGameplayBehaviorSmartObjectBehaviorDefinition* SmartObjectGameplayBehaviorDefinition = SmartObjectSubsystem->MarkSlotAsOccupied<
|
||||
UGameplayBehaviorSmartObjectBehaviorDefinition>(ClaimedHandle);
|
||||
const UGameplayBehaviorConfig* GameplayBehaviorConfig = SmartObjectGameplayBehaviorDefinition != nullptr ? SmartObjectGameplayBehaviorDefinition->GameplayBehaviorConfig : nullptr;
|
||||
GameplayBehavior = GameplayBehaviorConfig != nullptr ? GameplayBehaviorConfig->GetBehavior(*World) : nullptr;
|
||||
if (GameplayBehavior == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const USmartObjectComponent* SmartObjectComponent = SmartObjectSubsystem->GetSmartObjectComponent(ClaimedHandle);
|
||||
AActor& InteractorActor = *GetAvatarActor();
|
||||
AActor* InteracteeActor = SmartObjectComponent ? SmartObjectComponent->GetOwner() : nullptr;
|
||||
const bool bBehaviorActive = UGameplayBehaviorSubsystem::TriggerBehavior(*GameplayBehavior, InteractorActor, GameplayBehaviorConfig, InteracteeActor);
|
||||
// Behavior can be successfully triggered AND ended synchronously. We are only interested to register callback when still running
|
||||
if (bBehaviorActive)
|
||||
{
|
||||
OnBehaviorFinishedNotifyHandle = GameplayBehavior->GetOnBehaviorFinishedDelegate().AddUObject(this, &UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior::OnSmartObjectBehaviorFinished);
|
||||
}
|
||||
|
||||
return bBehaviorActive;
|
||||
}
|
||||
|
||||
void UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior::OnSmartObjectBehaviorFinished(UGameplayBehavior& Behavior, AActor& Avatar, const bool bInterrupted)
|
||||
{
|
||||
// Adding an ensure in case the assumptions change in the future.
|
||||
ensure(GetAvatarActor() != nullptr);
|
||||
|
||||
// make sure we handle the right pawn - we can get this notify for a different
|
||||
// Avatar if the behavior sending it out is not instanced (CDO is being used to perform actions)
|
||||
if (GetAvatarActor() == &Avatar)
|
||||
{
|
||||
Behavior.GetOnBehaviorFinishedDelegate().Remove(OnBehaviorFinishedNotifyHandle);
|
||||
bBehaviorFinished = true;
|
||||
EndTask();
|
||||
}
|
||||
}
|
||||
|
||||
void UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior::OnDestroy(bool bInOwnerFinished)
|
||||
{
|
||||
if (ClaimedHandle.IsValid())
|
||||
{
|
||||
USmartObjectSubsystem* SmartObjectSubsystem = USmartObjectSubsystem::GetCurrent(GetWorld());
|
||||
check(SmartObjectSubsystem);
|
||||
SmartObjectSubsystem->MarkSlotAsFree(ClaimedHandle);
|
||||
SmartObjectSubsystem->UnregisterSlotInvalidationCallback(ClaimedHandle);
|
||||
ClaimedHandle.Invalidate();
|
||||
}
|
||||
|
||||
if (TaskState != EGameplayTaskState::Finished)
|
||||
{
|
||||
if (GameplayBehavior != nullptr && bBehaviorFinished)
|
||||
{
|
||||
OnSucceeded.Broadcast();
|
||||
}
|
||||
else
|
||||
{
|
||||
OnFailed.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
Super::OnDestroy(bInOwnerFinished);
|
||||
}
|
||||
|
||||
void UGGS_AbilityTask_UseSmartObjectWithGameplayBehavior::OnSlotInvalidated(const FSmartObjectClaimHandle& ClaimHandle, const ESmartObjectSlotState State)
|
||||
{
|
||||
if (!bBehaviorFinished && GameplayBehavior != nullptr)
|
||||
{
|
||||
check(GetAvatarActor());
|
||||
GameplayBehavior->GetOnBehaviorFinishedDelegate().Remove(OnBehaviorFinishedNotifyHandle);
|
||||
GameplayBehavior->AbortBehavior(*GetAvatarActor());
|
||||
}
|
||||
EndTask();
|
||||
}
|
||||
Reference in New Issue
Block a user