// © 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(); // @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()) // { // 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 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() : nullptr); } UOLSAbilitySystemComponent* UOLSPawnExtensionComponent::GetOLSAbilitySystemComponent() const { return AbilitySystemComponent; } void UOLSPawnExtensionComponent::SetPawnData(const UOLSPawnDataAsset* pawnData) { check(pawnData); APawn* pawn = GetPawnChecked(); 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(); 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())) { 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(); }