258 lines
7.1 KiB
C++
258 lines
7.1 KiB
C++
// 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<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()));
|
|
}
|
|
|
|
void ULyraPawnExtensionComponent::SetPawnData(const ULyraPawnData* InPawnData)
|
|
{
|
|
check(InPawnData);
|
|
|
|
bPawnReadyToInitialize = false;
|
|
|
|
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();
|
|
|
|
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<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())
|
|
{
|
|
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<APawn>()))
|
|
{
|
|
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<APawn>();
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Allow pawn components to have requirements.
|
|
TArray<UActorComponent*> InteractableComponents = Pawn->GetComponentsByInterface(ULyraReadyInterface::StaticClass());
|
|
for (UActorComponent* InteractableComponent : InteractableComponents)
|
|
{
|
|
const ILyraReadyInterface* Ready = CastChecked<ILyraReadyInterface>(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);
|
|
}
|
|
}
|