RealtimeStyleTransferRuntime/Source/LyraGame/Character/LyraHeroComponent.cpp

499 lines
16 KiB
C++
Raw Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraHeroComponent.h"
#include "LyraLogChannels.h"
#include "GameFramework/Pawn.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "Player/LyraPlayerController.h"
#include "Player/LyraPlayerState.h"
2022-09-13 07:18:28 +00:00
#include "Player/LyraLocalPlayer.h"
2022-05-23 18:41:30 +00:00
#include "Character/LyraPawnExtensionComponent.h"
#include "Character/LyraPawnData.h"
#include "Character/LyraCharacter.h"
#include "AbilitySystem/LyraAbilitySystemComponent.h"
#include "Input/LyraInputConfig.h"
#include "Input/LyraInputComponent.h"
#include "Camera/LyraCameraComponent.h"
#include "LyraGameplayTags.h"
#include "Engine/LocalPlayer.h"
#include "Components/GameFrameworkComponentManager.h"
#include "Settings/LyraSettingsLocal.h"
#include "System/LyraAssetManager.h"
#include "PlayerMappableInputConfig.h"
2022-09-13 07:18:28 +00:00
#include "Camera/LyraCameraMode.h"
2022-05-23 18:41:30 +00:00
#if WITH_EDITOR
#include "Misc/UObjectToken.h"
#endif // WITH_EDITOR
namespace LyraHero
{
static const float LookYawRate = 300.0f;
static const float LookPitchRate = 165.0f;
};
const FName ULyraHeroComponent::NAME_BindInputsNow("BindInputsNow");
2022-09-13 07:18:28 +00:00
const FName ULyraHeroComponent::NAME_ActorFeatureName("Hero");
2022-05-23 18:41:30 +00:00
ULyraHeroComponent::ULyraHeroComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
AbilityCameraMode = nullptr;
bReadyToBindInputs = false;
}
void ULyraHeroComponent::OnRegister()
{
Super::OnRegister();
2022-09-13 07:18:28 +00:00
if (!GetPawn<APawn>())
2022-05-23 18:41:30 +00:00
{
UE_LOG(LogLyra, Error, TEXT("[ULyraHeroComponent::OnRegister] This component has been added to a blueprint whose base class is not a Pawn. To use this component, it MUST be placed on a Pawn Blueprint."));
#if WITH_EDITOR
if (GIsEditor)
{
static const FText Message = NSLOCTEXT("LyraHeroComponent", "NotOnPawnError", "has been added to a blueprint whose base class is not a Pawn. To use this component, it MUST be placed on a Pawn Blueprint. This will cause a crash if you PIE!");
static const FName HeroMessageLogName = TEXT("LyraHeroComponent");
FMessageLog(HeroMessageLogName).Error()
->AddToken(FUObjectToken::Create(this, FText::FromString(GetNameSafe(this))))
->AddToken(FTextToken::Create(Message));
FMessageLog(HeroMessageLogName).Open();
}
#endif
}
2022-09-13 07:18:28 +00:00
else
{
// Register with the init state system early, this will only work if this is a game world
RegisterInitStateFeature();
}
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
bool ULyraHeroComponent::CanChangeInitState(UGameFrameworkComponentManager* Manager, FGameplayTag CurrentState, FGameplayTag DesiredState) const
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
check(Manager);
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
const FLyraGameplayTags& InitTags = FLyraGameplayTags::Get();
APawn* Pawn = GetPawn<APawn>();
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
if (!CurrentState.IsValid() && DesiredState == InitTags.InitState_Spawned)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
// As long as we have a real pawn, let us transition
if (Pawn)
{
return true;
}
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
else if (CurrentState == InitTags.InitState_Spawned && DesiredState == InitTags.InitState_DataAvailable)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
// The player state is required.
if (!GetPlayerState<ALyraPlayerState>())
{
return false;
}
// If we're authority or autonomous, we need to wait for a controller with registered ownership of the player state.
if (Pawn->GetLocalRole() != ROLE_SimulatedProxy)
{
AController* Controller = GetController<AController>();
const bool bHasControllerPairedWithPS = (Controller != nullptr) && \
(Controller->PlayerState != nullptr) && \
(Controller->PlayerState->GetOwner() == Controller);
if (!bHasControllerPairedWithPS)
{
return false;
}
}
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
const bool bIsLocallyControlled = Pawn->IsLocallyControlled();
const bool bIsBot = Pawn->IsBotControlled();
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
if (bIsLocallyControlled && !bIsBot)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
ALyraPlayerController* LyraPC = GetController<ALyraPlayerController>();
// The input component and local player is required when locally controlled.
if (!Pawn->InputComponent || !LyraPC || !LyraPC->GetLocalPlayer())
{
return false;
}
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
return true;
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
else if (CurrentState == InitTags.InitState_DataAvailable && DesiredState == InitTags.InitState_DataInitialized)
{
// Wait for player state and extension component
ALyraPlayerState* LyraPS = GetPlayerState<ALyraPlayerState>();
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
return LyraPS && Manager->HasFeatureReachedInitState(Pawn, ULyraPawnExtensionComponent::NAME_ActorFeatureName, InitTags.InitState_DataInitialized);
}
else if (CurrentState == InitTags.InitState_DataInitialized && DesiredState == InitTags.InitState_GameplayReady)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
// TODO add ability initialization checks?
return true;
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
return false;
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
void ULyraHeroComponent::HandleChangeInitState(UGameFrameworkComponentManager* Manager, FGameplayTag CurrentState, FGameplayTag DesiredState)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
const FLyraGameplayTags& InitTags = FLyraGameplayTags::Get();
if (CurrentState == FLyraGameplayTags::Get().InitState_DataAvailable && DesiredState == FLyraGameplayTags::Get().InitState_DataInitialized)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
APawn* Pawn = GetPawn<APawn>();
ALyraPlayerState* LyraPS = GetPlayerState<ALyraPlayerState>();
if (!ensure(Pawn && LyraPS))
{
return;
}
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
const bool bIsLocallyControlled = Pawn->IsLocallyControlled();
const ULyraPawnData* PawnData = nullptr;
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
if (ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
{
PawnData = PawnExtComp->GetPawnData<ULyraPawnData>();
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
// The player state holds the persistent data for this player (state that persists across deaths and multiple pawns).
// The ability system component and attribute sets live on the player state.
PawnExtComp->InitializeAbilitySystem(LyraPS->GetLyraAbilitySystemComponent(), LyraPS);
}
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
if (ALyraPlayerController* LyraPC = GetController<ALyraPlayerController>())
{
if (Pawn->InputComponent != nullptr)
{
InitializePlayerInput(Pawn->InputComponent);
}
}
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
if (bIsLocallyControlled && PawnData)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
if (ULyraCameraComponent* CameraComponent = ULyraCameraComponent::FindCameraComponent(Pawn))
{
CameraComponent->DetermineCameraModeDelegate.BindUObject(this, &ThisClass::DetermineCameraMode);
}
2022-05-23 18:41:30 +00:00
}
}
2022-09-13 07:18:28 +00:00
}
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
void ULyraHeroComponent::OnActorInitStateChanged(const FActorInitStateChangedParams& Params)
{
if (Params.FeatureName == ULyraPawnExtensionComponent::NAME_ActorFeatureName)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
if (Params.FeatureState == FLyraGameplayTags::Get().InitState_DataInitialized)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
// If the extension component says all all other components are initialized, try to progress to next state
CheckDefaultInitialization();
2022-05-23 18:41:30 +00:00
}
}
2022-09-13 07:18:28 +00:00
}
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
void ULyraHeroComponent::CheckDefaultInitialization()
{
const FLyraGameplayTags& InitTags = FLyraGameplayTags::Get();
static const TArray<FGameplayTag> StateChain = { InitTags.InitState_Spawned, InitTags.InitState_DataAvailable, InitTags.InitState_DataInitialized, InitTags.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);
2022-05-23 18:41:30 +00:00
}
void ULyraHeroComponent::BeginPlay()
{
Super::BeginPlay();
2022-09-13 07:18:28 +00:00
// Listen for when the pawn extension component changes init state
BindOnActorInitStateChanged(ULyraPawnExtensionComponent::NAME_ActorFeatureName, FGameplayTag(), false);
// Notifies that we are done spawning, then try the rest of initialization
ensure(TryToChangeInitState(FLyraGameplayTags::Get().InitState_Spawned));
CheckDefaultInitialization();
2022-05-23 18:41:30 +00:00
}
void ULyraHeroComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
2022-09-13 07:18:28 +00:00
UnregisterInitStateFeature();
2022-05-23 18:41:30 +00:00
Super::EndPlay(EndPlayReason);
}
void ULyraHeroComponent::InitializePlayerInput(UInputComponent* PlayerInputComponent)
{
check(PlayerInputComponent);
const APawn* Pawn = GetPawn<APawn>();
if (!Pawn)
{
return;
}
const APlayerController* PC = GetController<APlayerController>();
check(PC);
2022-09-13 07:18:28 +00:00
const ULyraLocalPlayer* LP = Cast<ULyraLocalPlayer>(PC->GetLocalPlayer());
2022-05-23 18:41:30 +00:00
check(LP);
UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
check(Subsystem);
Subsystem->ClearAllMappings();
if (const ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
{
if (const ULyraPawnData* PawnData = PawnExtComp->GetPawnData<ULyraPawnData>())
{
if (const ULyraInputConfig* InputConfig = PawnData->InputConfig)
{
const FLyraGameplayTags& GameplayTags = FLyraGameplayTags::Get();
// Register any default input configs with the settings so that they will be applied to the player during AddInputMappings
for (const FMappableConfigPair& Pair : DefaultInputConfigs)
{
2022-09-13 07:18:28 +00:00
if (Pair.bShouldActivateAutomatically && Pair.CanBeActivated())
{
FModifyContextOptions Options = {};
Options.bIgnoreAllPressedKeysUntilRelease = false;
// Actually add the config to the local player
Subsystem->AddPlayerMappableConfig(Pair.Config.LoadSynchronous(), Options);
}
2022-05-23 18:41:30 +00:00
}
ULyraInputComponent* LyraIC = CastChecked<ULyraInputComponent>(PlayerInputComponent);
LyraIC->AddInputMappings(InputConfig, Subsystem);
TArray<uint32> BindHandles;
LyraIC->BindAbilityActions(InputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles);
LyraIC->BindNativeAction(InputConfig, GameplayTags.InputTag_Move, ETriggerEvent::Triggered, this, &ThisClass::Input_Move, /*bLogIfNotFound=*/ false);
LyraIC->BindNativeAction(InputConfig, GameplayTags.InputTag_Look_Mouse, ETriggerEvent::Triggered, this, &ThisClass::Input_LookMouse, /*bLogIfNotFound=*/ false);
LyraIC->BindNativeAction(InputConfig, GameplayTags.InputTag_Look_Stick, ETriggerEvent::Triggered, this, &ThisClass::Input_LookStick, /*bLogIfNotFound=*/ false);
LyraIC->BindNativeAction(InputConfig, GameplayTags.InputTag_Crouch, ETriggerEvent::Triggered, this, &ThisClass::Input_Crouch, /*bLogIfNotFound=*/ false);
LyraIC->BindNativeAction(InputConfig, GameplayTags.InputTag_AutoRun, ETriggerEvent::Triggered, this, &ThisClass::Input_AutoRun, /*bLogIfNotFound=*/ false);
}
}
}
if (ensure(!bReadyToBindInputs))
{
bReadyToBindInputs = true;
}
2022-09-13 07:18:28 +00:00
2022-05-23 18:41:30 +00:00
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast<APlayerController*>(PC), NAME_BindInputsNow);
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast<APawn*>(Pawn), NAME_BindInputsNow);
}
void ULyraHeroComponent::AddAdditionalInputConfig(const ULyraInputConfig* InputConfig)
{
TArray<uint32> BindHandles;
const APawn* Pawn = GetPawn<APawn>();
if (!Pawn)
{
return;
}
ULyraInputComponent* LyraIC = Pawn->FindComponentByClass<ULyraInputComponent>();
check(LyraIC);
const APlayerController* PC = GetController<APlayerController>();
check(PC);
const ULocalPlayer* LP = PC->GetLocalPlayer();
check(LP);
UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
check(Subsystem);
if (const ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
{
LyraIC->BindAbilityActions(InputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles);
}
}
void ULyraHeroComponent::RemoveAdditionalInputConfig(const ULyraInputConfig* InputConfig)
{
//@TODO: Implement me!
}
bool ULyraHeroComponent::IsReadyToBindInputs() const
{
return bReadyToBindInputs;
}
void ULyraHeroComponent::Input_AbilityInputTagPressed(FGameplayTag InputTag)
{
if (const APawn* Pawn = GetPawn<APawn>())
{
if (const ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
{
if (ULyraAbilitySystemComponent* LyraASC = PawnExtComp->GetLyraAbilitySystemComponent())
{
LyraASC->AbilityInputTagPressed(InputTag);
}
}
}
}
void ULyraHeroComponent::Input_AbilityInputTagReleased(FGameplayTag InputTag)
{
const APawn* Pawn = GetPawn<APawn>();
if (!Pawn)
{
return;
}
if (const ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
{
if (ULyraAbilitySystemComponent* LyraASC = PawnExtComp->GetLyraAbilitySystemComponent())
{
LyraASC->AbilityInputTagReleased(InputTag);
}
}
}
void ULyraHeroComponent::Input_Move(const FInputActionValue& InputActionValue)
{
APawn* Pawn = GetPawn<APawn>();
AController* Controller = Pawn ? Pawn->GetController() : nullptr;
// If the player has attempted to move again then cancel auto running
if (ALyraPlayerController* LyraController = Cast<ALyraPlayerController>(Controller))
{
LyraController->SetIsAutoRunning(false);
}
if (Controller)
{
const FVector2D Value = InputActionValue.Get<FVector2D>();
const FRotator MovementRotation(0.0f, Controller->GetControlRotation().Yaw, 0.0f);
if (Value.X != 0.0f)
{
const FVector MovementDirection = MovementRotation.RotateVector(FVector::RightVector);
Pawn->AddMovementInput(MovementDirection, Value.X);
}
if (Value.Y != 0.0f)
{
const FVector MovementDirection = MovementRotation.RotateVector(FVector::ForwardVector);
Pawn->AddMovementInput(MovementDirection, Value.Y);
}
}
}
void ULyraHeroComponent::Input_LookMouse(const FInputActionValue& InputActionValue)
{
APawn* Pawn = GetPawn<APawn>();
if (!Pawn)
{
return;
}
const FVector2D Value = InputActionValue.Get<FVector2D>();
if (Value.X != 0.0f)
{
Pawn->AddControllerYawInput(Value.X);
}
if (Value.Y != 0.0f)
{
Pawn->AddControllerPitchInput(Value.Y);
}
}
void ULyraHeroComponent::Input_LookStick(const FInputActionValue& InputActionValue)
{
APawn* Pawn = GetPawn<APawn>();
if (!Pawn)
{
return;
}
const FVector2D Value = InputActionValue.Get<FVector2D>();
const UWorld* World = GetWorld();
check(World);
if (Value.X != 0.0f)
{
Pawn->AddControllerYawInput(Value.X * LyraHero::LookYawRate * World->GetDeltaSeconds());
}
if (Value.Y != 0.0f)
{
Pawn->AddControllerPitchInput(Value.Y * LyraHero::LookPitchRate * World->GetDeltaSeconds());
}
}
void ULyraHeroComponent::Input_Crouch(const FInputActionValue& InputActionValue)
{
if (ALyraCharacter* Character = GetPawn<ALyraCharacter>())
{
Character->ToggleCrouch();
}
}
void ULyraHeroComponent::Input_AutoRun(const FInputActionValue& InputActionValue)
{
if (APawn* Pawn = GetPawn<APawn>())
{
if (ALyraPlayerController* Controller = Cast<ALyraPlayerController>(Pawn->GetController()))
{
// Toggle auto running
Controller->SetIsAutoRunning(!Controller->GetIsAutoRunning());
}
}
}
TSubclassOf<ULyraCameraMode> ULyraHeroComponent::DetermineCameraMode() const
{
if (AbilityCameraMode)
{
return AbilityCameraMode;
}
const APawn* Pawn = GetPawn<APawn>();
if (!Pawn)
{
return nullptr;
}
if (ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
{
if (const ULyraPawnData* PawnData = PawnExtComp->GetPawnData<ULyraPawnData>())
{
return PawnData->DefaultCameraMode;
}
}
return nullptr;
}
void ULyraHeroComponent::SetAbilityCameraMode(TSubclassOf<ULyraCameraMode> CameraMode, const FGameplayAbilitySpecHandle& OwningSpecHandle)
{
if (CameraMode)
{
AbilityCameraMode = CameraMode;
AbilityCameraModeOwningSpecHandle = OwningSpecHandle;
}
}
void ULyraHeroComponent::ClearAbilityCameraMode(const FGameplayAbilitySpecHandle& OwningSpecHandle)
{
if (AbilityCameraModeOwningSpecHandle == OwningSpecHandle)
{
AbilityCameraMode = nullptr;
AbilityCameraModeOwningSpecHandle = FGameplayAbilitySpecHandle();
}
}