503 lines
15 KiB
C++
503 lines
15 KiB
C++
// 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"
|
|
#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"
|
|
|
|
#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");
|
|
|
|
ULyraHeroComponent::ULyraHeroComponent(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
AbilityCameraMode = nullptr;
|
|
bPawnHasInitialized = false;
|
|
bReadyToBindInputs = false;
|
|
}
|
|
|
|
void ULyraHeroComponent::OnRegister()
|
|
{
|
|
Super::OnRegister();
|
|
|
|
if (const APawn* Pawn = GetPawn<APawn>())
|
|
{
|
|
if (ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
|
|
{
|
|
PawnExtComp->OnPawnReadyToInitialize_RegisterAndCall(FSimpleMulticastDelegate::FDelegate::CreateUObject(this, &ThisClass::OnPawnReadyToInitialize));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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
|
|
}
|
|
}
|
|
|
|
bool ULyraHeroComponent::IsPawnComponentReadyToInitialize() const
|
|
{
|
|
// The player state is required.
|
|
if (!GetPlayerState<ALyraPlayerState>())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const APawn* Pawn = GetPawn<APawn>();
|
|
|
|
// A pawn is required.
|
|
if (!Pawn)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
const bool bIsLocallyControlled = Pawn->IsLocallyControlled();
|
|
const bool bIsBot = Pawn->IsBotControlled();
|
|
|
|
if (bIsLocallyControlled && !bIsBot)
|
|
{
|
|
// The input component is required when locally controlled.
|
|
if (!Pawn->InputComponent)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ULyraHeroComponent::OnPawnReadyToInitialize()
|
|
{
|
|
if (!ensure(!bPawnHasInitialized))
|
|
{
|
|
// Don't initialize twice
|
|
return;
|
|
}
|
|
|
|
APawn* Pawn = GetPawn<APawn>();
|
|
if (!Pawn)
|
|
{
|
|
return;
|
|
}
|
|
const bool bIsLocallyControlled = Pawn->IsLocallyControlled();
|
|
|
|
ALyraPlayerState* LyraPS = GetPlayerState<ALyraPlayerState>();
|
|
check(LyraPS);
|
|
|
|
const ULyraPawnData* PawnData = nullptr;
|
|
|
|
if (ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
|
|
{
|
|
PawnData = PawnExtComp->GetPawnData<ULyraPawnData>();
|
|
|
|
// 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);
|
|
}
|
|
|
|
if (ALyraPlayerController* LyraPC = GetController<ALyraPlayerController>())
|
|
{
|
|
if (Pawn->InputComponent != nullptr)
|
|
{
|
|
InitializePlayerInput(Pawn->InputComponent);
|
|
}
|
|
}
|
|
|
|
if (bIsLocallyControlled && PawnData)
|
|
{
|
|
if (ULyraCameraComponent* CameraComponent = ULyraCameraComponent::FindCameraComponent(Pawn))
|
|
{
|
|
CameraComponent->DetermineCameraModeDelegate.BindUObject(this, &ThisClass::DetermineCameraMode);
|
|
}
|
|
}
|
|
|
|
bPawnHasInitialized = true;
|
|
}
|
|
|
|
void ULyraHeroComponent::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
}
|
|
|
|
void ULyraHeroComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
if (const APawn* Pawn = GetPawn<APawn>())
|
|
{
|
|
if (ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
|
|
{
|
|
PawnExtComp->UninitializeAbilitySystem();
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
const ULocalPlayer* LP = PC->GetLocalPlayer();
|
|
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)
|
|
{
|
|
FMappableConfigPair::ActivatePair(Pair);
|
|
}
|
|
|
|
ULyraInputComponent* LyraIC = CastChecked<ULyraInputComponent>(PlayerInputComponent);
|
|
LyraIC->AddInputMappings(InputConfig, Subsystem);
|
|
if (ULyraSettingsLocal* LocalSettings = ULyraSettingsLocal::Get())
|
|
{
|
|
LocalSettings->OnInputConfigActivated.AddUObject(this, &ULyraHeroComponent::OnInputConfigActivated);
|
|
LocalSettings->OnInputConfigDeactivated.AddUObject(this, &ULyraHeroComponent::OnInputConfigDeactivated);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast<APlayerController*>(PC), NAME_BindInputsNow);
|
|
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast<APawn*>(Pawn), NAME_BindInputsNow);
|
|
}
|
|
|
|
void ULyraHeroComponent::OnInputConfigActivated(const FLoadedMappableConfigPair& ConfigPair)
|
|
{
|
|
if (ALyraPlayerController* LyraPC = GetController<ALyraPlayerController>())
|
|
{
|
|
if (APawn* Pawn = GetPawn<APawn>())
|
|
{
|
|
if (ULyraInputComponent* LyraIC = Cast<ULyraInputComponent>(Pawn->InputComponent))
|
|
{
|
|
if (const ULocalPlayer* LP = LyraPC->GetLocalPlayer())
|
|
{
|
|
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
|
|
{
|
|
LyraIC->AddInputConfig(ConfigPair, Subsystem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ULyraHeroComponent::OnInputConfigDeactivated(const FLoadedMappableConfigPair& ConfigPair)
|
|
{
|
|
if (ALyraPlayerController* LyraPC = GetController<ALyraPlayerController>())
|
|
{
|
|
if (APawn* Pawn = GetPawn<APawn>())
|
|
{
|
|
if (ULyraInputComponent* LyraIC = Cast<ULyraInputComponent>(Pawn->InputComponent))
|
|
{
|
|
if (const ULocalPlayer* LP = LyraPC->GetLocalPlayer())
|
|
{
|
|
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
|
|
{
|
|
LyraIC->RemoveInputConfig(ConfigPair, Subsystem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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::HasPawnInitialized() const
|
|
{
|
|
return bPawnHasInitialized;
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|