第一次提交
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user