OLS/Source/ols/Private/Components/OLSPawnExtensionComponent.cpp

315 lines
9.3 KiB
C++

// © 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 "Components/OLSPawnExtensionComponent.h"
#include "OLSLog.h"
#include "AbilitySystem/OLSAbilitySystemComponent.h"
#include "Components/GameFrameworkComponentManager.h"
#include "DataAssets/OLSPawnDataAsset.h"
#include "Net/UnrealNetwork.h"
DEFINE_LOG_CATEGORY(LogOLSPawnExtensionComponent);
const FName UOLSPawnExtensionComponent::NAME_ActorFeatureName("PawnExtension");
UOLSPawnExtensionComponent::UOLSPawnExtensionComponent(const FObjectInitializer& objectInitializer)
: Super(objectInitializer)
{
PrimaryComponentTick.bStartWithTickEnabled = false;
PrimaryComponentTick.bCanEverTick = false;
SetIsReplicatedByDefault(true);
PawnData = nullptr;
AbilitySystemComponent = nullptr;
}
void UOLSPawnExtensionComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UOLSPawnExtensionComponent, PawnData);
}
FName UOLSPawnExtensionComponent::GetFeatureName() const
{
return NAME_ActorFeatureName;
}
bool UOLSPawnExtensionComponent::CanChangeInitState(
UGameFrameworkComponentManager* manager,
FGameplayTag currentState,
FGameplayTag desiredState) const
{
check(manager);
APawn* pawn = GetPawn<APawn>();
// @TODO: Implement LyraGameplayTags::InitState_Spawned.
if (!currentState.IsValid() /* && desiredState == LyraGameplayTags::InitState_Spawned */)
{
// As long as we are on a valid pawn, we count as spawned
if (pawn)
{
return true;
}
}
// @TODO: Implement LyraGameplayTags::InitState_Spawned,
// @TODO: Implement LyraGameplayTags::InitState_DataAvailable,
// @TODO: Implement LyraGameplayTags::InitState_DataInitialized,
// @TODO: Implement LyraGameplayTags::InitState_GameplayReady
// if (currentState == LyraGameplayTags::InitState_Spawned && desiredState == LyraGameplayTags::InitState_DataAvailable)
// {
// // Pawn data is required.
// if (!PawnData)
// {
// return false;
// }
//
// 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 == LyraGameplayTags::InitState_DataAvailable && desiredState == LyraGameplayTags::InitState_DataInitialized)
// {
// // Transition to initialize if all features have their data available
// return manager->HaveAllFeaturesReachedInitState(pawn, LyraGameplayTags::InitState_DataAvailable);
// }
// else if (currentState == LyraGameplayTags::InitState_DataInitialized && desiredState == LyraGameplayTags::InitState_GameplayReady)
// {
// return true;
// }
return false;
}
void UOLSPawnExtensionComponent::HandleChangeInitState(
UGameFrameworkComponentManager* manager,
FGameplayTag currentState,
FGameplayTag desiredState)
{
// @TODO: Implement LyraGameplayTags::InitState_Spawned.
// if (desiredState == LyraGameplayTags::InitState_DataInitialized)
// {
// // This is currently all handled by other components listening to this state change
// }
}
void UOLSPawnExtensionComponent::OnActorInitStateChanged(const FActorInitStateChangedParams& Params)
{
// If another feature is now in DataAvailable, see if we should transition to DataInitialized
if (Params.FeatureName != NAME_ActorFeatureName)
{
// @TODO: Implement LyraGameplayTags::InitState_DataAvailable.
// if (Params.FeatureState == LyraGameplayTags::InitState_DataAvailable)
// {
// CheckDefaultInitialization();
// }
}
}
void UOLSPawnExtensionComponent::CheckDefaultInitialization()
{
// Before checking our progress, try progressing any other features we might depend on
CheckDefaultInitializationForImplementers();
// @TODO: Implement LyraGameplayTags::InitState_Spawned,
// @TODO: Implement LyraGameplayTags::InitState_DataAvailable,
// @TODO: Implement LyraGameplayTags::InitState_DataInitialized,
// @TODO: Implement LyraGameplayTags::InitState_GameplayReady
// static const TArray<FGameplayTag> StateChain = {
// LyraGameplayTags::InitState_Spawned, LyraGameplayTags::InitState_DataAvailable,
// LyraGameplayTags::InitState_DataInitialized, LyraGameplayTags::InitState_GameplayReady
// };
// 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);
}
UOLSPawnExtensionComponent* UOLSPawnExtensionComponent::FindPawnExtensionComponent(const AActor* actor)
{
return (actor ? actor->FindComponentByClass<UOLSPawnExtensionComponent>() : nullptr);
}
UOLSAbilitySystemComponent* UOLSPawnExtensionComponent::GetOLSAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
void UOLSPawnExtensionComponent::SetPawnData(const UOLSPawnDataAsset* pawnData)
{
check(pawnData);
APawn* pawn = GetPawnChecked<APawn>();
if (pawn->GetLocalRole() != ROLE_Authority)
{
return;
}
if (PawnData)
{
OLS_LOG(LogOLSPawnExtensionComponent, Error,
TEXT("Trying to set PawnData [%s] on pawn [%s] that already has valid PawnData [%s]."),
GET_UOBJECT_NAME(pawnData), GET_UOBJECT_NAME(pawn), GET_UOBJECT_NAME(PawnData));
return;
}
PawnData = pawnData;
pawn->ForceNetUpdate();
CheckDefaultInitialization();
}
void UOLSPawnExtensionComponent::InitializeAbilitySystem(UOLSAbilitySystemComponent* asc, AActor* ownerActor)
{
check(asc);
check(ownerActor);
if (AbilitySystemComponent == asc)
{
// The ability system component hasn't changed.
return;
}
if (AbilitySystemComponent)
{
// Clean up the old ability system component.
UninitializeAbilitySystem();
}
APawn* pawn = GetPawnChecked<APawn>();
AActor* existingAvatar = asc->GetAvatarActor();
OLS_LOG(LogOLSPawnExtensionComponent, Verbose, TEXT("Setting up ASC [%s] on pawn [%s] owner [%s], existing [%s] "),
GET_UOBJECT_NAME(asc), GET_UOBJECT_NAME(pawn), GET_UOBJECT_NAME(ownerActor),
GET_UOBJECT_NAME(existingAvatar));
if ((existingAvatar != nullptr) && (existingAvatar != pawn))
{
OLS_LOG(LogOLSPawnExtensionComponent, 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 (UOLSPawnExtensionComponent* OtherExtensionComponent = FindPawnExtensionComponent(existingAvatar))
{
OtherExtensionComponent->UninitializeAbilitySystem();
}
}
AbilitySystemComponent = asc;
AbilitySystemComponent->InitAbilityActorInfo(ownerActor, pawn);
if (ensure(PawnData))
{
asc->SetTagRelationshipMapping(PawnData->TagRelationshipMapping);
}
OnAbilitySystemInitialized.Broadcast();
}
void UOLSPawnExtensionComponent::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())
{
FGameplayTagContainer abilityTypesToIgnore;
// @TOD:; Implement LyraGameplayTags::Ability_Behavior_SurvivesDeath;
// abilityTypesToIgnore.AddTag(LyraGameplayTags::Ability_Behavior_SurvivesDeath);
AbilitySystemComponent->CancelAbilities(nullptr, &abilityTypesToIgnore);
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 UOLSPawnExtensionComponent::HandleControllerChanged()
{
if (AbilitySystemComponent && (AbilitySystemComponent->GetAvatarActor() == GetPawnChecked<APawn>()))
{
ensure(AbilitySystemComponent->AbilityActorInfo->OwnerActor == AbilitySystemComponent->GetOwnerActor());
if (AbilitySystemComponent->GetOwnerActor() == nullptr)
{
UninitializeAbilitySystem();
}
else
{
AbilitySystemComponent->RefreshAbilityActorInfo();
}
}
CheckDefaultInitialization();
}
void UOLSPawnExtensionComponent::HandlePlayerStateReplicated()
{
CheckDefaultInitialization();
}
void UOLSPawnExtensionComponent::SetupPlayerInputComponent()
{
CheckDefaultInitialization();
}
void UOLSPawnExtensionComponent::OnAbilitySystemInitialized_RegisterAndCall(
FSimpleMulticastDelegate::FDelegate delegate)
{
if (!OnAbilitySystemInitialized.IsBoundToObject(delegate.GetUObject()))
{
OnAbilitySystemInitialized.Add(delegate);
}
if (AbilitySystemComponent)
{
delegate.Execute();
}
}
void UOLSPawnExtensionComponent::OnAbilitySystemUninitialized_Register(FSimpleMulticastDelegate::FDelegate delegate)
{
if (!OnAbilitySystemUninitialized.IsBoundToObject(delegate.GetUObject()))
{
OnAbilitySystemUninitialized.Add(delegate);
}
}
void UOLSPawnExtensionComponent::OnRep_PawnData()
{
CheckDefaultInitialization();
}