第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -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

View File

@@ -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);
}
}
}