2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "LyraPawnExtensionComponent.h"
2022-09-13 07:18:28 +00:00
# include "Abilities/GameplayAbilityTypes.h"
# include "AbilitySystem/LyraAbilitySystemComponent.h"
# include "Components/GameFrameworkComponentManager.h"
# include "Containers/Array.h"
# include "Containers/Set.h"
# include "Containers/UnrealString.h"
# include "Engine/EngineBaseTypes.h"
2022-05-23 18:41:30 +00:00
# include "GameFramework/Controller.h"
2022-09-13 07:18:28 +00:00
# include "GameFramework/Pawn.h"
# include "GameplayTagContainer.h"
# include "HAL/Platform.h"
# include "Logging/LogCategory.h"
# include "Logging/LogMacros.h"
# include "LyraGameplayTags.h"
# include "LyraLogChannels.h"
2022-05-23 18:41:30 +00:00
# include "LyraPawnData.h"
2022-09-13 07:18:28 +00:00
# include "Misc/AssertionMacros.h"
# include "Net/UnrealNetwork.h"
# include "Templates/SharedPointer.h"
# include "Trace/Detail/Channel.h"
# include "UObject/UObjectBaseUtility.h"
# include "UObject/UnrealNames.h"
# include "UObject/WeakObjectPtr.h"
# include "UObject/WeakObjectPtrTemplates.h"
class FLifetimeProperty ;
class UActorComponent ;
const FName ULyraPawnExtensionComponent : : NAME_ActorFeatureName ( " PawnExtension " ) ;
2022-05-23 18:41:30 +00:00
ULyraPawnExtensionComponent : : ULyraPawnExtensionComponent ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
{
PrimaryComponentTick . bStartWithTickEnabled = false ;
PrimaryComponentTick . bCanEverTick = false ;
SetIsReplicatedByDefault ( true ) ;
PawnData = nullptr ;
AbilitySystemComponent = nullptr ;
}
void ULyraPawnExtensionComponent : : GetLifetimeReplicatedProps ( TArray < FLifetimeProperty > & OutLifetimeProps ) const
{
Super : : GetLifetimeReplicatedProps ( OutLifetimeProps ) ;
DOREPLIFETIME ( ULyraPawnExtensionComponent , PawnData ) ;
}
void ULyraPawnExtensionComponent : : OnRegister ( )
{
Super : : OnRegister ( ) ;
const APawn * Pawn = GetPawn < APawn > ( ) ;
ensureAlwaysMsgf ( ( Pawn ! = nullptr ) , TEXT ( " LyraPawnExtensionComponent on [%s] can only be added to Pawn actors. " ) , * GetNameSafe ( GetOwner ( ) ) ) ;
TArray < UActorComponent * > PawnExtensionComponents ;
Pawn - > GetComponents ( ULyraPawnExtensionComponent : : StaticClass ( ) , PawnExtensionComponents ) ;
ensureAlwaysMsgf ( ( PawnExtensionComponents . Num ( ) = = 1 ) , TEXT ( " Only one LyraPawnExtensionComponent should exist on [%s]. " ) , * GetNameSafe ( GetOwner ( ) ) ) ;
2022-09-13 07:18:28 +00:00
// Register with the init state system early, this will only work if this is a game world
RegisterInitStateFeature ( ) ;
}
void ULyraPawnExtensionComponent : : BeginPlay ( )
{
Super : : BeginPlay ( ) ;
// Listen for changes to all features
BindOnActorInitStateChanged ( NAME_None , FGameplayTag ( ) , false ) ;
// Notifies state manager that we have spawned, then try rest of default initialization
ensure ( TryToChangeInitState ( FLyraGameplayTags : : Get ( ) . InitState_Spawned ) ) ;
CheckDefaultInitialization ( ) ;
}
void ULyraPawnExtensionComponent : : EndPlay ( const EEndPlayReason : : Type EndPlayReason )
{
UninitializeAbilitySystem ( ) ;
UnregisterInitStateFeature ( ) ;
Super : : EndPlay ( EndPlayReason ) ;
2022-05-23 18:41:30 +00:00
}
void ULyraPawnExtensionComponent : : SetPawnData ( const ULyraPawnData * InPawnData )
{
check ( InPawnData ) ;
APawn * Pawn = GetPawnChecked < APawn > ( ) ;
if ( Pawn - > GetLocalRole ( ) ! = ROLE_Authority )
{
return ;
}
if ( PawnData )
{
UE_LOG ( LogLyra , Error , TEXT ( " Trying to set PawnData [%s] on pawn [%s] that already has valid PawnData [%s]. " ) , * GetNameSafe ( InPawnData ) , * GetNameSafe ( Pawn ) , * GetNameSafe ( PawnData ) ) ;
return ;
}
PawnData = InPawnData ;
Pawn - > ForceNetUpdate ( ) ;
2022-09-13 07:18:28 +00:00
CheckDefaultInitialization ( ) ;
2022-05-23 18:41:30 +00:00
}
void ULyraPawnExtensionComponent : : OnRep_PawnData ( )
{
2022-09-13 07:18:28 +00:00
CheckDefaultInitialization ( ) ;
2022-05-23 18:41:30 +00:00
}
void ULyraPawnExtensionComponent : : InitializeAbilitySystem ( ULyraAbilitySystemComponent * InASC , AActor * InOwnerActor )
{
check ( InASC ) ;
check ( InOwnerActor ) ;
if ( AbilitySystemComponent = = InASC )
{
// The ability system component hasn't changed.
return ;
}
if ( AbilitySystemComponent )
{
// Clean up the old ability system component.
UninitializeAbilitySystem ( ) ;
}
APawn * Pawn = GetPawnChecked < APawn > ( ) ;
AActor * ExistingAvatar = InASC - > GetAvatarActor ( ) ;
UE_LOG ( LogLyra , Verbose , TEXT ( " Setting up ASC [%s] on pawn [%s] owner [%s], existing [%s] " ) , * GetNameSafe ( InASC ) , * GetNameSafe ( Pawn ) , * GetNameSafe ( InOwnerActor ) , * GetNameSafe ( ExistingAvatar ) ) ;
if ( ( ExistingAvatar ! = nullptr ) & & ( ExistingAvatar ! = Pawn ) )
{
UE_LOG ( LogLyra , Log , TEXT ( " Existing avatar (authority=%d) " ) , ExistingAvatar - > HasAuthority ( ) ? 1 : 0 ) ;
// There is already a pawn acting as the ASC's avatar, so we need to kick it out
// This can happen on clients if they're lagged: their new pawn is spawned + possessed before the dead one is removed
ensure ( ! ExistingAvatar - > HasAuthority ( ) ) ;
if ( ULyraPawnExtensionComponent * OtherExtensionComponent = FindPawnExtensionComponent ( ExistingAvatar ) )
{
OtherExtensionComponent - > UninitializeAbilitySystem ( ) ;
}
}
AbilitySystemComponent = InASC ;
AbilitySystemComponent - > InitAbilityActorInfo ( InOwnerActor , Pawn ) ;
if ( ensure ( PawnData ) )
{
InASC - > SetTagRelationshipMapping ( PawnData - > TagRelationshipMapping ) ;
}
OnAbilitySystemInitialized . Broadcast ( ) ;
}
void ULyraPawnExtensionComponent : : UninitializeAbilitySystem ( )
{
if ( ! AbilitySystemComponent )
{
return ;
}
// Uninitialize the ASC if we're still the avatar actor (otherwise another pawn already did it when they became the avatar actor)
if ( AbilitySystemComponent - > GetAvatarActor ( ) = = GetOwner ( ) )
{
2022-09-13 07:18:28 +00:00
FGameplayTagContainer AbilityTypesToIgnore ;
AbilityTypesToIgnore . AddTag ( FLyraGameplayTags : : Get ( ) . Ability_Behavior_SurvivesDeath ) ;
AbilitySystemComponent - > CancelAbilities ( nullptr , & AbilityTypesToIgnore ) ;
2022-05-23 18:41:30 +00:00
AbilitySystemComponent - > ClearAbilityInput ( ) ;
AbilitySystemComponent - > RemoveAllGameplayCues ( ) ;
if ( AbilitySystemComponent - > GetOwnerActor ( ) ! = nullptr )
{
AbilitySystemComponent - > SetAvatarActor ( nullptr ) ;
}
else
{
// If the ASC doesn't have a valid owner, we need to clear *all* actor info, not just the avatar pairing
AbilitySystemComponent - > ClearActorInfo ( ) ;
}
OnAbilitySystemUninitialized . Broadcast ( ) ;
}
AbilitySystemComponent = nullptr ;
}
void ULyraPawnExtensionComponent : : HandleControllerChanged ( )
{
if ( AbilitySystemComponent & & ( AbilitySystemComponent - > GetAvatarActor ( ) = = GetPawnChecked < APawn > ( ) ) )
{
ensure ( AbilitySystemComponent - > AbilityActorInfo - > OwnerActor = = AbilitySystemComponent - > GetOwnerActor ( ) ) ;
if ( AbilitySystemComponent - > GetOwnerActor ( ) = = nullptr )
{
UninitializeAbilitySystem ( ) ;
}
else
{
AbilitySystemComponent - > RefreshAbilityActorInfo ( ) ;
}
}
2022-09-13 07:18:28 +00:00
CheckDefaultInitialization ( ) ;
2022-05-23 18:41:30 +00:00
}
void ULyraPawnExtensionComponent : : HandlePlayerStateReplicated ( )
{
2022-09-13 07:18:28 +00:00
CheckDefaultInitialization ( ) ;
2022-05-23 18:41:30 +00:00
}
void ULyraPawnExtensionComponent : : SetupPlayerInputComponent ( )
{
2022-09-13 07:18:28 +00:00
CheckDefaultInitialization ( ) ;
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
void ULyraPawnExtensionComponent : : CheckDefaultInitialization ( )
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
// Before checking our progress, try progressing any other features we might depend on
CheckDefaultInitializationForImplementers ( ) ;
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
const FLyraGameplayTags & InitTags = FLyraGameplayTags : : Get ( ) ;
static const TArray < FGameplayTag > StateChain = { InitTags . InitState_Spawned , InitTags . InitState_DataAvailable , InitTags . InitState_DataInitialized , InitTags . InitState_GameplayReady } ;
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
// This will try to progress from spawned (which is only set in BeginPlay) through the data initialization stages until it gets to gameplay ready
ContinueInitStateChain ( StateChain ) ;
}
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
bool ULyraPawnExtensionComponent : : CanChangeInitState ( UGameFrameworkComponentManager * Manager , FGameplayTag CurrentState , FGameplayTag DesiredState ) const
{
check ( Manager ) ;
APawn * Pawn = GetPawn < APawn > ( ) ;
const FLyraGameplayTags & InitTags = FLyraGameplayTags : : Get ( ) ;
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
if ( ! CurrentState . IsValid ( ) & & DesiredState = = InitTags . InitState_Spawned )
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
// As long as we are on a valid pawn, we count as spawned
if ( Pawn )
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
return true ;
2022-05-23 18:41:30 +00:00
}
}
2022-09-13 07:18:28 +00:00
if ( CurrentState = = InitTags . InitState_Spawned & & DesiredState = = InitTags . InitState_DataAvailable )
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
// Pawn data is required.
if ( ! PawnData )
2022-05-23 18:41:30 +00:00
{
return false ;
}
2022-09-13 07:18:28 +00:00
const bool bHasAuthority = Pawn - > HasAuthority ( ) ;
const bool bIsLocallyControlled = Pawn - > IsLocallyControlled ( ) ;
if ( bHasAuthority | | bIsLocallyControlled )
{
// Check for being possessed by a controller.
if ( ! GetController < AController > ( ) )
{
return false ;
}
}
return true ;
}
else if ( CurrentState = = InitTags . InitState_DataAvailable & & DesiredState = = InitTags . InitState_DataInitialized )
{
// Transition to initialize if all features have their data available
return Manager - > HaveAllFeaturesReachedInitState ( Pawn , InitTags . InitState_DataAvailable ) ;
}
else if ( CurrentState = = InitTags . InitState_DataInitialized & & DesiredState = = InitTags . InitState_GameplayReady )
{
return true ;
}
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
return false ;
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
void ULyraPawnExtensionComponent : : HandleChangeInitState ( UGameFrameworkComponentManager * Manager , FGameplayTag CurrentState , FGameplayTag DesiredState )
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
if ( DesiredState = = FLyraGameplayTags : : Get ( ) . InitState_DataInitialized )
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
// This is currently all handled by other components listening to this state change
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
}
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
void ULyraPawnExtensionComponent : : OnActorInitStateChanged ( const FActorInitStateChangedParams & Params )
{
// If another feature is now in DataAvailable, see if we should transition to DataInitialized
if ( Params . FeatureName ! = NAME_ActorFeatureName )
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
const FLyraGameplayTags & InitTags = FLyraGameplayTags : : Get ( ) ;
if ( Params . FeatureState = = InitTags . InitState_DataAvailable )
{
CheckDefaultInitialization ( ) ;
}
2022-05-23 18:41:30 +00:00
}
}
void ULyraPawnExtensionComponent : : OnAbilitySystemInitialized_RegisterAndCall ( FSimpleMulticastDelegate : : FDelegate Delegate )
{
if ( ! OnAbilitySystemInitialized . IsBoundToObject ( Delegate . GetUObject ( ) ) )
{
OnAbilitySystemInitialized . Add ( Delegate ) ;
}
if ( AbilitySystemComponent )
{
Delegate . Execute ( ) ;
}
}
void ULyraPawnExtensionComponent : : OnAbilitySystemUninitialized_Register ( FSimpleMulticastDelegate : : FDelegate Delegate )
{
if ( ! OnAbilitySystemUninitialized . IsBoundToObject ( Delegate . GetUObject ( ) ) )
{
OnAbilitySystemUninitialized . Add ( Delegate ) ;
}
}