RealtimeStyleTransferRuntime/Source/LyraGame/GameFeatures/GameFeatureAction_AddInputC...

181 lines
6.4 KiB
C++
Raw Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "GameFeatures/GameFeatureAction_AddInputConfig.h"
2022-09-13 07:18:28 +00:00
#include "Components/GameFrameworkComponentManager.h"
#include "EnhancedInputSubsystems.h"
#include "Character/LyraHeroComponent.h" // for NAME_BindInputsNow
#include "GameFramework/PlayerController.h"
#include "Engine/LocalPlayer.h"
#include "PlayerMappableInputConfig.h"
2022-05-23 18:41:30 +00:00
#define LOCTEXT_NAMESPACE "GameFeatures_AddInputConfig"
void UGameFeatureAction_AddInputConfig::OnGameFeatureRegistering()
{
2022-09-13 07:18:28 +00:00
Super::OnGameFeatureRegistering();
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
// Register the input configs with the local settings, this way the data inside them is available all the time
// and not just when this game feature is active. This is necessary for displaying key binding options
// on the main menu, or other times when the game feature may not be active.
2022-05-23 18:41:30 +00:00
for (const FMappableConfigPair& Pair : InputConfigs)
{
FMappableConfigPair::RegisterPair(Pair);
}
}
2022-09-13 07:18:28 +00:00
void UGameFeatureAction_AddInputConfig::OnGameFeatureUnregistering()
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
Super::OnGameFeatureUnregistering();
2022-05-23 18:41:30 +00:00
for (const FMappableConfigPair& Pair : InputConfigs)
{
2022-09-13 07:18:28 +00:00
FMappableConfigPair::UnregisterPair(Pair);
2022-05-23 18:41:30 +00:00
}
}
2022-09-13 07:18:28 +00:00
void UGameFeatureAction_AddInputConfig::OnGameFeatureActivating(FGameFeatureActivatingContext& Context)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
FPerContextData& ActiveData = ContextData.FindOrAdd(Context);
if (!ensure(ActiveData.ExtensionRequestHandles.IsEmpty()) ||
!ensure(ActiveData.PawnsAddedTo.IsEmpty()))
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
Reset(ActiveData);
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
// Call super after the above logic so that we have our context before being added to the world
Super::OnGameFeatureActivating(Context);
2022-05-23 18:41:30 +00:00
}
2022-09-13 07:18:28 +00:00
void UGameFeatureAction_AddInputConfig::OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context)
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
Super::OnGameFeatureDeactivating(Context);
FPerContextData* ActiveData = ContextData.Find(Context);
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
if (ensure(ActiveData))
2022-05-23 18:41:30 +00:00
{
2022-09-13 07:18:28 +00:00
Reset(*ActiveData);
2022-05-23 18:41:30 +00:00
}
}
#if WITH_EDITOR
EDataValidationResult UGameFeatureAction_AddInputConfig::IsDataValid(TArray<FText>& ValidationErrors)
{
EDataValidationResult Result = CombineDataValidationResults(Super::IsDataValid(ValidationErrors), EDataValidationResult::Valid);
int32 EntryIndex = 0;
for (const FMappableConfigPair& Pair : InputConfigs)
{
if (Pair.Config.IsNull())
{
Result = EDataValidationResult::Invalid;
ValidationErrors.Add(FText::Format(LOCTEXT("NullConfigPointer", "Null Config pointer at index {0} in Pair list"), FText::AsNumber(EntryIndex)));
}
++EntryIndex;
}
return Result;
}
#endif // WITH_EDITOR
2022-09-13 07:18:28 +00:00
void UGameFeatureAction_AddInputConfig::AddToWorld(const FWorldContext& WorldContext, const FGameFeatureStateChangeContext& ChangeContext)
{
UWorld* World = WorldContext.World();
UGameInstance* GameInstance = WorldContext.OwningGameInstance;
FPerContextData& ActiveData = ContextData.FindOrAdd(ChangeContext);
if (GameInstance && World && World->IsGameWorld())
{
if (UGameFrameworkComponentManager* ComponentMan = UGameInstance::GetSubsystem<UGameFrameworkComponentManager>(GameInstance))
{
UGameFrameworkComponentManager::FExtensionHandlerDelegate AddConfigDelegate =
UGameFrameworkComponentManager::FExtensionHandlerDelegate::CreateUObject(this, &ThisClass::HandlePawnExtension, ChangeContext);
TSharedPtr<FComponentRequestHandle> ExtensionRequestHandle = ComponentMan->AddExtensionHandler(APawn::StaticClass(), AddConfigDelegate);
ActiveData.ExtensionRequestHandles.Add(ExtensionRequestHandle);
}
}
}
void UGameFeatureAction_AddInputConfig::Reset(FPerContextData& ActiveData)
{
ActiveData.ExtensionRequestHandles.Empty();
while (!ActiveData.PawnsAddedTo.IsEmpty())
{
TWeakObjectPtr<APawn> PawnPtr = ActiveData.PawnsAddedTo.Top();
if (PawnPtr.IsValid())
{
RemoveInputConfig(PawnPtr.Get(), ActiveData);
}
else
{
ActiveData.PawnsAddedTo.Pop();
}
}
}
void UGameFeatureAction_AddInputConfig::HandlePawnExtension(AActor* Actor, FName EventName, FGameFeatureStateChangeContext ChangeContext)
{
APawn* AsPawn = CastChecked<APawn>(Actor);
FPerContextData& ActiveData = ContextData.FindOrAdd(ChangeContext);
if (EventName == UGameFrameworkComponentManager::NAME_ExtensionAdded || EventName == ULyraHeroComponent::NAME_BindInputsNow)
{
AddInputConfig(AsPawn, ActiveData);
}
else if (EventName == UGameFrameworkComponentManager::NAME_ExtensionRemoved || EventName == UGameFrameworkComponentManager::NAME_ReceiverRemoved)
{
RemoveInputConfig(AsPawn, ActiveData);
}
}
void UGameFeatureAction_AddInputConfig::AddInputConfig(APawn* Pawn, FPerContextData& ActiveData)
{
APlayerController* PlayerController = Cast<APlayerController>(Pawn->GetController());
if (ULocalPlayer* LP = PlayerController ? PlayerController->GetLocalPlayer() : nullptr)
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
// We don't want to ignore keys that were "Down" when we add the mapping context
// This allows you to die holding a movement key, keep holding while waiting for respawn,
// and have it be applied after you respawn immediately. Leaving bIgnoreAllPressedKeysUntilRelease
// to it's default "true" state would require the player to release the movement key,
// and press it again when they respawn
FModifyContextOptions Options = {};
Options.bIgnoreAllPressedKeysUntilRelease = false;
// Add the input mappings
for (const FMappableConfigPair& Pair : InputConfigs)
{
if (Pair.bShouldActivateAutomatically && Pair.CanBeActivated())
{
Subsystem->AddPlayerMappableConfig(Pair.Config.LoadSynchronous(), Options);
}
}
ActiveData.PawnsAddedTo.AddUnique(Pawn);
}
}
}
void UGameFeatureAction_AddInputConfig::RemoveInputConfig(APawn* Pawn, FPerContextData& ActiveData)
{
APlayerController* PlayerController = Cast<APlayerController>(Pawn->GetController());
if (ULocalPlayer* LP = PlayerController ? PlayerController->GetLocalPlayer() : nullptr)
{
// If this is called during the shutdown of the game then there isn't a strict guarantee that the input subsystem is valid
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
// Remove the input mappings
for (const FMappableConfigPair& Pair : InputConfigs)
{
Subsystem->RemovePlayerMappableConfig(Pair.Config.LoadSynchronous());
}
}
}
ActiveData.PawnsAddedTo.Remove(Pawn);
}
2022-05-23 18:41:30 +00:00
#undef LOCTEXT_NAMESPACE