305 lines
9.0 KiB
C++
305 lines
9.0 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"
|
|
|
|
DEFINE_LOG_CATEGORY(LogOLSPawnExtensionComponent);
|
|
|
|
UOLSPawnExtensionComponent::UOLSPawnExtensionComponent(const FObjectInitializer& objectInitializer)
|
|
: Super(objectInitializer)
|
|
{
|
|
PrimaryComponentTick.bStartWithTickEnabled = false;
|
|
PrimaryComponentTick.bCanEverTick = false;
|
|
|
|
SetIsReplicatedByDefault(true);
|
|
|
|
PawnData = nullptr;
|
|
AbilitySystemComponent = nullptr;
|
|
}
|
|
|
|
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();
|
|
}
|