2025-01-04 16:41:49 +00:00
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
# include "AbilitySystem/OLSAbilitySystemComponent.h"
# include "AbilitySystemGlobals.h"
# include "GameplayCueManager.h"
2025-01-16 19:05:19 +00:00
# include "OLSLog.h"
2025-01-04 16:41:49 +00:00
# include "AbilitySystem/OLSBatchGameplayAbilityInterface.h"
# include "AbilitySystem/OLSGlobaAbilitySubsystem.h"
# include "AnimInstances/OLSBaseLayerAnimInstance.h"
2025-01-16 19:05:19 +00:00
DEFINE_LOG_CATEGORY ( LogOLSAbilitySystemComponent ) ;
2025-01-04 16:41:49 +00:00
// Sets default values for this component's properties
UOLSAbilitySystemComponent : : UOLSAbilitySystemComponent ( )
{
static constexpr bool isReplicated = true ;
SetIsReplicatedByDefault ( isReplicated ) ;
bShouldEnableBatchRPC = true ;
}
void UOLSAbilitySystemComponent : : InitAbilityActorInfo ( AActor * ownerActor , AActor * avatarActor )
{
FGameplayAbilityActorInfo * actorInfo = AbilityActorInfo . Get ( ) ;
check ( actorInfo ) ;
check ( ownerActor ) ;
// Guard condition to ensure we should clear/init for this new Avatar Actor.
const bool hasAvatarChanged = avatarActor & & Cast < APawn > ( avatarActor ) & & ( avatarActor ! = actorInfo - > AvatarActor ) ;
Super : : InitAbilityActorInfo ( ownerActor , avatarActor ) ;
// Apply the new defaults obtained from the owner's interface.
if ( hasAvatarChanged )
{
if ( const TObjectPtr < UOLSGlobaAbilitySubsystem > globalAbilitySystem = UWorld : : GetSubsystem < UOLSGlobaAbilitySubsystem > ( GetWorld ( ) ) )
{
globalAbilitySystem - > RegisterASC ( this ) ;
}
if ( const TObjectPtr < UOLSBaseLayerAnimInstance > animInstance = Cast < UOLSBaseLayerAnimInstance > ( GetAnimInstanceFromActorInfo ( ) ) )
{
animInstance - > InitializeWithAbilitySystem ( this ) ;
}
TryActivateAbilitiesOnSpawn ( ) ;
}
}
bool UOLSAbilitySystemComponent : : ShouldDoServerAbilityRPCBatch ( ) const
{
return bShouldEnableBatchRPC ;
}
float UOLSAbilitySystemComponent : : PlayMontage (
UGameplayAbility * animatingAbility ,
FGameplayAbilityActivationInfo activationInfo ,
UAnimMontage * montage ,
float playRate ,
FName startSectionName ,
float startTimeSeconds )
{
if ( GEnableDefaultPlayMontage )
{
// Always useful to still allow the default flow, if there are some meaningful changes in the core system
// that were not yet reflect in this custom implementation. Can be enabled with CVar "GEnableDefaultPlayMontage".
//
return Super : : PlayMontage ( animatingAbility , activationInfo , montage , playRate , startSectionName , startTimeSeconds ) ;
}
float duration = - 1.f ;
// This method was re-written just to ensure that the Animation Instance is retrieved from the Actor Info
// by default, but also, other scenarios can be supported. Biggest example being an IK Runtime Retarget.
//
// This virtual "GetAnimInstanceFromActorInfo" provides some flexibility on how the Anim Instance is
// retrieved. It can be extended in projects that should support IK Runtime Retargets and also traditional
// Anim Instances set in the Actor Info.
//
const TObjectPtr < UAnimInstance > animInstance = GetAnimInstanceFromActorInfo ( ) ;
if ( animInstance & & montage )
{
duration = animInstance - > Montage_Play (
montage ,
playRate ,
EMontagePlayReturnType : : MontageLength ,
startTimeSeconds ) ;
if ( duration > 0.f )
{
if ( montage - > HasRootMotion ( ) & & animInstance - > GetOwningActor ( ) )
{
UE_LOG ( LogRootMotion , Log , TEXT ( " UAbilitySystemComponent::PlayMontage %s, Role: %s " )
, * GetNameSafe ( montage )
, * UEnum : : GetValueAsString ( TEXT ( " Engine.ENetRole " ) , animInstance - > GetOwningActor ( ) - > GetLocalRole ( ) )
) ;
}
LocalAnimMontageInfo . AnimMontage = montage ;
LocalAnimMontageInfo . AnimatingAbility = animatingAbility ;
LocalAnimMontageInfo . PlayInstanceId = ( LocalAnimMontageInfo . PlayInstanceId < UINT8_MAX ? LocalAnimMontageInfo . PlayInstanceId + 1 : 0 ) ;
if ( animatingAbility )
{
animatingAbility - > SetCurrentMontage ( montage ) ;
}
// Start at a given Section.
if ( startSectionName ! = NAME_None )
{
animInstance - > Montage_JumpToSection ( startSectionName , montage ) ;
}
// Replicate for non-owners and for replay recordings
// The data we set from GetRepAnimMontageInfo_Mutable() is used both by the server to replicate to clients and by clients to record replays.
// We need to set this data for recording clients because there exists network configurations where an abilities montage data will not replicate to some clients (for example: if the client is an autonomous proxy.)
if ( ShouldRecordMontageReplication ( ) )
{
FGameplayAbilityRepAnimMontage & mutableRepAnimMontageInfo = GetRepAnimMontageInfo_Mutable ( ) ;
SetReplicatedMontageInfo ( mutableRepAnimMontageInfo , montage , startSectionName ) ;
// Update parameters that change during Montage lifetime.
AnimMontage_UpdateReplicatedData ( ) ;
}
// Replicate to non-owners
if ( IsOwnerActorAuthoritative ( ) )
{
// Force net update on our avatar actor.
if ( AbilityActorInfo - > AvatarActor ! = nullptr )
{
AbilityActorInfo - > AvatarActor - > ForceNetUpdate ( ) ;
}
}
else
{
// If this prediction key is rejected, we need to end the preview
FPredictionKey predictionKey = GetPredictionKeyForNewAction ( ) ;
if ( predictionKey . IsValidKey ( ) )
{
predictionKey . NewRejectedDelegate ( ) . BindUObject ( this , & ThisClass : : OnPredictiveMontageRejected , montage ) ;
}
}
}
}
return duration ;
}
UAnimInstance * UOLSAbilitySystemComponent : : GetAnimInstanceFromActorInfo ( ) const
{
if ( ! AbilityActorInfo . IsValid ( ) )
{
return nullptr ;
}
const FGameplayAbilityActorInfo * actorInfo = AbilityActorInfo . Get ( ) ;
if ( actorInfo - > AnimInstance . IsValid ( ) & & actorInfo - > AnimInstance - > IsValidLowLevelFast ( ) )
{
// Return the one that was deliberately set in the Actor Info.
return actorInfo - > AnimInstance . Get ( ) ;
}
// Otherwise, let the getter method try to figure out the animation instance.
return actorInfo - > GetAnimInstance ( ) ;
}
FActiveGameplayEffectHandle UOLSAbilitySystemComponent : : ApplyGameplayEffectClassToSelf (
TSubclassOf < UGameplayEffect > effectClass ,
float level )
{
FActiveGameplayEffectHandle handle ;
if ( IsValid ( effectClass ) )
{
FGameplayEffectContextHandle contextHandle = MakeEffectContext ( ) ;
contextHandle . AddSourceObject ( GetOwner ( ) ) ;
const FGameplayEffectSpecHandle specHandle = MakeOutgoingSpec ( effectClass , level , contextHandle ) ;
if ( specHandle . IsValid ( ) )
{
handle = ApplyGameplayEffectSpecToSelf ( * specHandle . Data . Get ( ) ) ;
2025-01-16 19:05:19 +00:00
OLS_LOG ( LogOLSAbilitySystemComponent , Verbose , TEXT ( " [%s] Effect '%s' granted at level %f. " ) ,
GET_UOBJECT_NAME ( GetAvatarActor ( ) ) , GET_UOBJECT_NAME ( effectClass ) , level ) ;
2025-01-04 16:41:49 +00:00
}
}
return handle ;
}
FGameplayAbilitySpecHandle UOLSAbilitySystemComponent : : GiveAbilityFromClass (
const TSubclassOf < UGameplayAbility > abilityClass ,
int32 level ,
int32 input )
{
FGameplayAbilitySpecHandle handle ;
if ( IsValid ( abilityClass ) )
{
const FGameplayAbilitySpec newAbilitySpec ( FGameplayAbilitySpec ( abilityClass , level , input , GetOwner ( ) ) ) ;
handle = GiveAbility ( newAbilitySpec ) ;
2025-01-16 19:05:19 +00:00
OLS_LOG ( LogOLSAbilitySystemComponent , Log , TEXT ( " [%s] Ability '%s' %s at level %d. " ) ,
GET_UOBJECT_NAME ( GetAvatarActor ( ) ) , GET_UOBJECT_NAME ( abilityClass ) ,
2025-01-04 16:41:49 +00:00
handle . IsValid ( ) ? TEXT ( " successfully granted " ) : TEXT ( " failed to be granted " ) , level ) ;
}
return handle ;
}
bool UOLSAbilitySystemComponent : : TryBatchActivateAbility (
FGameplayAbilitySpecHandle abilityHandle ,
bool shouldEndAbilityImmediately )
{
bool isAbilityActivated = false ;
if ( abilityHandle . IsValid ( ) )
{
2025-01-16 19:05:19 +00:00
OLS_LOG ( LogAbilitySystemComponent , Warning , TEXT ( " Ability handle is invalid! " ) ) ;
2025-01-04 16:41:49 +00:00
return isAbilityActivated ;
}
FScopedServerAbilityRPCBatcher batch ( this , abilityHandle ) ;
isAbilityActivated = TryActivateAbility ( abilityHandle , true ) ;
if ( ! shouldEndAbilityImmediately )
{
const FGameplayAbilitySpec * abilitySpec = FindAbilitySpecFromHandle ( abilityHandle ) ;
if ( abilitySpec ! = nullptr )
{
UGameplayAbility * ability = abilitySpec - > GetPrimaryInstance ( ) ;
if ( IsValid ( ability ) & & ability - > Implements < UOLSBatchGameplayAbilityInterface > ( ) )
{
IOLSBatchGameplayAbilityInterface : : Execute_EndAbilityFromBatch ( ability ) ;
}
else
{
2025-01-16 19:05:19 +00:00
OLS_LOG ( LogAbilitySystemComponent , Error ,
TEXT ( " %s does not implement Batch Gameplay Ability Interface " ) , GET_UOBJECT_NAME ( ability ) ) ;
2025-01-04 16:41:49 +00:00
}
}
}
return isAbilityActivated ;
}
void UOLSAbilitySystemComponent : : CancelAbilitiesByTags (
FGameplayTagContainer abilityTags ,
FGameplayTagContainer cancelFilterTags )
{
CancelAbilities ( & abilityTags , & cancelFilterTags ) ;
}
void UOLSAbilitySystemComponent : : ExecuteGameplayCueLocal (
const FGameplayTag gameplayCueTag ,
const FGameplayCueParameters & gameplayCueParameters ) const
{
const TObjectPtr < UGameplayCueManager > cueManager = UAbilitySystemGlobals : : Get ( ) . GetGameplayCueManager ( ) ;
cueManager - > HandleGameplayCue (
GetOwner ( ) ,
gameplayCueTag ,
EGameplayCueEvent : : Type : : Executed ,
gameplayCueParameters ) ;
}
void UOLSAbilitySystemComponent : : AddGameplayCueLocally (
const FGameplayTag gameplayCueTag ,
const FGameplayCueParameters & gameplayCueParameters ) const
{
const TObjectPtr < UGameplayCueManager > cueManager = UAbilitySystemGlobals : : Get ( ) . GetGameplayCueManager ( ) ;
cueManager - > HandleGameplayCue (
GetOwner ( ) ,
gameplayCueTag ,
EGameplayCueEvent : : Type : : OnActive ,
gameplayCueParameters ) ;
cueManager - > HandleGameplayCue (
GetOwner ( ) ,
gameplayCueTag ,
EGameplayCueEvent : : Type : : WhileActive ,
gameplayCueParameters ) ;
}
void UOLSAbilitySystemComponent : : RemoveGameplayCueLocally (
const FGameplayTag gameplayCueTag ,
const FGameplayCueParameters & gameplayCueParameters ) const
{
const TObjectPtr < UGameplayCueManager > cueManager = UAbilitySystemGlobals : : Get ( ) . GetGameplayCueManager ( ) ;
cueManager - > HandleGameplayCue ( GetOwner ( ) , gameplayCueTag , EGameplayCueEvent : : Type : : Removed , gameplayCueParameters ) ;
}
void UOLSAbilitySystemComponent : : TryActivateAbilitiesOnSpawn ( )
{
ABILITYLIST_SCOPE_LOCK ( ) ;
for ( const FGameplayAbilitySpec & abilitySpec : GetActivatableAbilities ( ) )
{
// if (const UOLS)
}
}
void UOLSAbilitySystemComponent : : SetReplicatedMontageInfo (
FGameplayAbilityRepAnimMontage & mutableRepAnimMontageInfo ,
UAnimMontage * newMontageToPlay ,
const FName & startSectionName )
{
const uint8 playInstanceId = mutableRepAnimMontageInfo . PlayInstanceId < UINT8_MAX ? mutableRepAnimMontageInfo . PlayInstanceId + 1 : 0 ;
const uint8 sectionIdToPlay = newMontageToPlay - > GetSectionIndex ( startSectionName ) + 1 ;
TObjectPtr < UAnimSequenceBase > animation = newMontageToPlay ;
if ( newMontageToPlay - > IsDynamicMontage ( ) )
{
animation = newMontageToPlay - > GetFirstAnimReference ( ) ;
check ( ! newMontageToPlay - > SlotAnimTracks . IsEmpty ( ) ) ;
mutableRepAnimMontageInfo . SlotName = newMontageToPlay - > SlotAnimTracks [ 0 ] . SlotName ;
mutableRepAnimMontageInfo . BlendOutTime = newMontageToPlay - > GetDefaultBlendInTime ( ) ;
}
mutableRepAnimMontageInfo . Animation = animation ;
mutableRepAnimMontageInfo . PlayInstanceId = playInstanceId ;
mutableRepAnimMontageInfo . SectionIdToPlay = 0 ;
if ( mutableRepAnimMontageInfo . Animation & & startSectionName ! = NAME_None )
{
mutableRepAnimMontageInfo . SectionIdToPlay = sectionIdToPlay ;
}
}