// Copyright Epic Games, Inc. All Rights Reserved. #include "LyraPawnExtensionComponent.h" #include "LyraLogChannels.h" #include "Net/UnrealNetwork.h" #include "GameFramework/Pawn.h" #include "GameFramework/Controller.h" #include "LyraPawnData.h" #include "AbilitySystem/LyraAbilitySystemComponent.h" ULyraPawnExtensionComponent::ULyraPawnExtensionComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { PrimaryComponentTick.bStartWithTickEnabled = false; PrimaryComponentTick.bCanEverTick = false; SetIsReplicatedByDefault(true); PawnData = nullptr; AbilitySystemComponent = nullptr; bPawnReadyToInitialize = false; } void ULyraPawnExtensionComponent::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(ULyraPawnExtensionComponent, PawnData); } void ULyraPawnExtensionComponent::OnRegister() { Super::OnRegister(); const APawn* Pawn = GetPawn(); ensureAlwaysMsgf((Pawn != nullptr), TEXT("LyraPawnExtensionComponent on [%s] can only be added to Pawn actors."), *GetNameSafe(GetOwner())); TArray PawnExtensionComponents; Pawn->GetComponents(ULyraPawnExtensionComponent::StaticClass(), PawnExtensionComponents); ensureAlwaysMsgf((PawnExtensionComponents.Num() == 1), TEXT("Only one LyraPawnExtensionComponent should exist on [%s]."), *GetNameSafe(GetOwner())); } void ULyraPawnExtensionComponent::SetPawnData(const ULyraPawnData* InPawnData) { check(InPawnData); bPawnReadyToInitialize = false; APawn* Pawn = GetPawnChecked(); 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(); CheckPawnReadyToInitialize(); } void ULyraPawnExtensionComponent::OnRep_PawnData() { CheckPawnReadyToInitialize(); } 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(); 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()) { AbilitySystemComponent->CancelAbilities(nullptr, nullptr); 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())) { ensure(AbilitySystemComponent->AbilityActorInfo->OwnerActor == AbilitySystemComponent->GetOwnerActor()); if (AbilitySystemComponent->GetOwnerActor() == nullptr) { UninitializeAbilitySystem(); } else { AbilitySystemComponent->RefreshAbilityActorInfo(); } } CheckPawnReadyToInitialize(); } void ULyraPawnExtensionComponent::HandlePlayerStateReplicated() { CheckPawnReadyToInitialize(); } void ULyraPawnExtensionComponent::SetupPlayerInputComponent() { CheckPawnReadyToInitialize(); } bool ULyraPawnExtensionComponent::CheckPawnReadyToInitialize() { if (bPawnReadyToInitialize) { return true; } // Pawn data is required. if (!PawnData) { return false; } APawn* Pawn = GetPawnChecked(); const bool bHasAuthority = Pawn->HasAuthority(); const bool bIsLocallyControlled = Pawn->IsLocallyControlled(); if (bHasAuthority || bIsLocallyControlled) { // Check for being possessed by a controller. if (!GetController()) { return false; } } // Allow pawn components to have requirements. TArray InteractableComponents = Pawn->GetComponentsByInterface(ULyraReadyInterface::StaticClass()); for (UActorComponent* InteractableComponent : InteractableComponents) { const ILyraReadyInterface* Ready = CastChecked(InteractableComponent); if (!Ready->IsPawnComponentReadyToInitialize()) { return false; } } // Pawn is ready to initialize. bPawnReadyToInitialize = true; OnPawnReadyToInitialize.Broadcast(); BP_OnPawnReadyToInitialize.Broadcast(); return true; } void ULyraPawnExtensionComponent::OnPawnReadyToInitialize_RegisterAndCall(FSimpleMulticastDelegate::FDelegate Delegate) { if (!OnPawnReadyToInitialize.IsBoundToObject(Delegate.GetUObject())) { OnPawnReadyToInitialize.Add(Delegate); } if (bPawnReadyToInitialize) { Delegate.Execute(); } } 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); } }