From 23194ac0e54cff7f3a52ed075f250979b054da93 Mon Sep 17 00:00:00 2001 From: jyelon Date: Tue, 27 May 2025 16:30:49 -0400 Subject: [PATCH] Finish implementing IsKeyUsedByPlayerController --- Content/Luprex/lxGameMode.uasset | 4 +- Content/Widgets/WB_Hotkeys.uasset | 4 +- Source/Integration/CommonTypes.h | 46 ++++++++++ Source/Integration/Integration.Build.cs | 1 + Source/Integration/LuaCall.h | 34 -------- .../Integration/StoreFNameInputModifier.cpp | 56 ------------- Source/Integration/StoreFNameInputModifier.h | 54 ------------ Source/Integration/UtilityLibrary.cpp | 84 ++++++++++++++++--- Source/Integration/UtilityLibrary.h | 25 ++++-- 9 files changed, 140 insertions(+), 168 deletions(-) delete mode 100644 Source/Integration/StoreFNameInputModifier.cpp delete mode 100644 Source/Integration/StoreFNameInputModifier.h diff --git a/Content/Luprex/lxGameMode.uasset b/Content/Luprex/lxGameMode.uasset index 55ea6c90..2518b745 100644 --- a/Content/Luprex/lxGameMode.uasset +++ b/Content/Luprex/lxGameMode.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd96174fce58065404c8ce9690f5048d3e50d7fc5051a08c8fb5d5173ffd92aa -size 159188 +oid sha256:2293be3fd5733841383cb79fc3e694810cb16d46b86dc548daeae7f0f070393d +size 160427 diff --git a/Content/Widgets/WB_Hotkeys.uasset b/Content/Widgets/WB_Hotkeys.uasset index 9ecee49c..53e60a1a 100644 --- a/Content/Widgets/WB_Hotkeys.uasset +++ b/Content/Widgets/WB_Hotkeys.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:854960103277e7e23ec457ee2be4341ebccab7318a5400845d6857d2f153582d -size 201757 +oid sha256:a85cbbd8752ea2a819a457f632a54efc7534b7503ca813a8333785acb026378d +size 211426 diff --git a/Source/Integration/CommonTypes.h b/Source/Integration/CommonTypes.h index 9db13b00..ac4d733c 100644 --- a/Source/Integration/CommonTypes.h +++ b/Source/Integration/CommonTypes.h @@ -1,5 +1,7 @@ #pragma once +#include "CommonTypes.generated.h" + namespace CommonTypes { // Array of tangible IDs. using IdArray = TArray; @@ -10,3 +12,47 @@ namespace CommonTypes { // Array of std::string_view using StringViewVec = TArray; } + +UENUM(BlueprintType) +enum class ElxLuaValueType : uint8 { + End, + String, + Name, + Float, + Boolean, + Vector +}; + +///////////////////////////////////////////////////////////////// +// +// A variety of boolean-like results that can be conveniently +// be used in conjunction with 'ExpandEnumAsExecs' to create +// branching functions. +// +///////////////////////////////////////////////////////////////// + +UENUM(BlueprintType) +enum class ElxSuccessOrError : uint8 { + Success, + Error, +}; + +UENUM(BlueprintType) +enum class ElxFoundOrNotFound : uint8 { + Found, + NotFound, +}; + +UENUM(BlueprintType) +enum class ElxUsedOrNotUsed : uint8 { + Used, + NotUsed, +}; + +UENUM(BlueprintType) +enum class ElxSuccessOrWrongType : uint8 { + Success, + WrongType, +}; + + diff --git a/Source/Integration/Integration.Build.cs b/Source/Integration/Integration.Build.cs index a56e6f20..41a28f7e 100644 --- a/Source/Integration/Integration.Build.cs +++ b/Source/Integration/Integration.Build.cs @@ -13,6 +13,7 @@ public class Integration : ModuleRules "CoreUObject", "Engine", "InputCore", + "SlateCore", "Sockets", "Networking", "EnhancedInput", diff --git a/Source/Integration/LuaCall.h b/Source/Integration/LuaCall.h index 534e6530..34d43ca4 100644 --- a/Source/Integration/LuaCall.h +++ b/Source/Integration/LuaCall.h @@ -15,40 +15,6 @@ class UlxLuaValues; ///////////////////////////////////////////////////////////////// -UENUM(BlueprintType) -enum class ElxLuaValueType : uint8 { - End, - String, - Name, - Float, - Boolean, - Vector -}; - -///////////////////////////////////////////////////////////////// -// -// A general-purpose 'success or error' type. -// -///////////////////////////////////////////////////////////////// - -UENUM(BlueprintType) -enum class ElxSuccessOrError : uint8 { - Success, - Error, -}; - -UENUM(BlueprintType) -enum class ElxFoundOrNotFound : uint8 { - Found, - NotFound, -}; - -UENUM(BlueprintType) -enum class ElxSuccessOrWrongType : uint8 { - Success, - WrongType, -}; - ///////////////////////////////////////////////////////////////// // // This is a little parser that parses Lua function 'prototypes'. diff --git a/Source/Integration/StoreFNameInputModifier.cpp b/Source/Integration/StoreFNameInputModifier.cpp deleted file mode 100644 index c411241b..00000000 --- a/Source/Integration/StoreFNameInputModifier.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "StoreFNameInputModifier.h" -#include "EnhancedPlayerInput.h" -#include "LuprexGameModeBase.h" - - -static const int ENCODE_LIMIT = 0x00FFFFFF; - -FInputActionValue UPassFNameAsAxis3D::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) -{ - // Internally, an FName is stored as three integers: - // ComparisonIndex, DisplayIndex, and Number. These numbers - // are only valid within a single unreal process, but that's OK. - // We copy these three numbers into an Axis3D. - // - // Yes, that's ugly. It would be nicer if FName was one - // of the types explicitly allowed in an FInputActionValue. - // Maybe some day! - // - // Integers larger than ENCODE_LIMIT cannot be losslessly - // converted to float. Such numbers should never occur in - // practice: the string table should not contain that many - // strings! - // - uint32 cidx = FNameToStore.GetComparisonIndex().ToUnstableInt(); - uint32 didx = FNameToStore.GetDisplayIndex().ToUnstableInt(); - uint32 nidx = FNameToStore.GetNumber(); - - // Make sure the three integers will fit into three floats without rounding or overflow. - if ((cidx > ENCODE_LIMIT) || (didx > ENCODE_LIMIT) || (nidx > ENCODE_LIMIT)) - { - UE_LOG(LogLuprexIntegration, Error, TEXT("Name cannot be converted to FInputActionValue: %s"), *FNameToStore.ToString()); - return FInputActionValue(FVector()); - } - - return FInputActionValue(FVector(cidx, didx, nidx)); -} - -FName UPassFNameAsAxis3D::DecodeFNameFromAxis3D(const FVector &Vec) -{ - uint32 cidx = static_cast(Vec.X); - uint32 didx = static_cast(Vec.Y); - uint32 nidx = static_cast(Vec.Z); - FName Result(FNameEntryId::FromUnstableInt(cidx), FNameEntryId::FromUnstableInt(didx), nidx); - FName Inverse(FNameEntryId::FromUnstableInt(didx), FNameEntryId::FromUnstableInt(cidx), nidx); - - if ((double(cidx) != Vec.X) || (double(didx) != Vec.Y) || (double(nidx) != Vec.Z) || - (cidx > ENCODE_LIMIT) || (didx > ENCODE_LIMIT) || (nidx > ENCODE_LIMIT) || - (!Result.IsValid()) || (!Inverse.IsValid())) - { - UE_LOG(LogLuprexIntegration, Error, TEXT("FVector is not an encoded FName.")); - return NAME_None; - } - return Result; -} - - diff --git a/Source/Integration/StoreFNameInputModifier.h b/Source/Integration/StoreFNameInputModifier.h deleted file mode 100644 index 7495aa1a..00000000 --- a/Source/Integration/StoreFNameInputModifier.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "InputModifiers.h" -#include "StoreFNameInputModifier.generated.h" - -/** - * @brief Allows you to pass an FName into an event handler. - * - * This modifier allows you to type an FName directly into an input mapping in - * the InputMappingContext editor, and have that FName passed through to an - * enhanced input handler. - * - * This modifier has an FName UPROPERTY which is editable in the InputMappingContext - * editor. At runtime, it encodes the FName as an Axis3D. In the event handler, - * the Axis3D can be decoded back into an FName using DecodeFNameFromAxis3D. - * - * One use for this modifier is when you want to know exactly which - * keyboard key triggered an EnhancedInput event. You can put the key's FName - * into the InputMappingContext, and it will get passed through to the event - * handler. - * - */ -UCLASS() -class INTEGRATION_API UPassFNameAsAxis3D : public UInputModifier -{ - GENERATED_BODY() - -public: - /** - * @brief The FName to be encoded into the FInputActionValue. - * - * This property can be configured in the Input Mapping Context editor - * for each specific mapping. - * - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Modifier Settings") - FName FNameToStore; - -protected: - virtual FInputActionValue ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) override; - - /** - * @brief Decodes an FName that was passed by the PassFnameAsAxis3D modifier. - * - * This function is used in an enhanced input event, where the PassFNameAsAxis3D - * modifier was used to pass an FName into the event. The value shows up as - * an Axis3D, which much be decoded back into an FName. - * - */ - UFUNCTION(BlueprintPure, Category = "Input|Enhanced") - static FName DecodeFNameFromAxis3D(const FVector& Encoded); - -}; diff --git a/Source/Integration/UtilityLibrary.cpp b/Source/Integration/UtilityLibrary.cpp index b27e737f..d76d8dd6 100644 --- a/Source/Integration/UtilityLibrary.cpp +++ b/Source/Integration/UtilityLibrary.cpp @@ -9,7 +9,7 @@ #include "Blueprint/UserWidget.h" #include "Components/GridPanel.h" #include "InputMappingContext.h" - +#include "EnhancedInputComponent.h" #define LOCTEXT_NAMESPACE "Luprex Utility" @@ -212,20 +212,80 @@ void UlxUtilityLibrary::GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, } } -void UlxUtilityLibrary::MapAllKeyboardKeysToOneInputAction(UInputMappingContext *IMC, UInputAction *Action) +ElxUsedOrNotUsed UlxUtilityLibrary::IsKeyUsedByMappingContext(const FKeyEvent &KeyEvent, const UInputMappingContext *MappingContext) { - TArray AllKeys; - EKeys::GetAllKeys(AllKeys); - - // Map every keyboard key to the provided LuaAction - for (const FKey& Key : AllKeys) + FKey Key = KeyEvent.GetKey(); + + if (!MappingContext) { - if ((Key.IsValid()) && - (Key.IsBindableInBlueprints()) && - (Key.GetMenuCategory() == EKeys::NAME_KeyboardCategory) && - (!Key.IsModifierKey())) + return ElxUsedOrNotUsed::NotUsed; + } + + for (const FEnhancedActionKeyMapping& Mapping : MappingContext->GetMappings()) + { + if (Mapping.Key == Key) { - IMC->MapKey(Action, Key); + return ElxUsedOrNotUsed::Used; } } + + return ElxUsedOrNotUsed::NotUsed; +} + +class UEnhancedPlayerInputExposed : public UEnhancedPlayerInput +{ +public: + const TArray& GetEnhancedActionMappings() const { return UEnhancedPlayerInput::GetEnhancedActionMappings(); } +}; + +ElxUsedOrNotUsed UlxUtilityLibrary::IsKeyUsedByPlayerController(const FKeyEvent &KeyEvent, bool IncludeEscapeAndFkeys, const APlayerController *PlayerController, const UObject *Context) +{ + if (PlayerController == nullptr) PlayerController = Context->GetWorld()->GetFirstPlayerController(); + + FKey Key = KeyEvent.GetKey(); + + UEnhancedPlayerInput *PlayerInput = Cast(PlayerController->PlayerInput); + if (PlayerInput == nullptr) + { + return ElxUsedOrNotUsed::NotUsed; + } + UEnhancedPlayerInputExposed *Exposed = static_cast(PlayerInput); + + for (const FInputActionKeyMapping& ActionMapping : Exposed->ActionMappings) + { + if (ActionMapping.Key == Key) + { + return ElxUsedOrNotUsed::Used; // Found a match in legacy Action Mappings. + } + } + + for (const FInputAxisKeyMapping& AxisMapping : Exposed->AxisMappings) + { + if (AxisMapping.Key == Key) + { + return ElxUsedOrNotUsed::Used; // Found a match in legacy Axis Mappings. + } + } + + for (const FEnhancedActionKeyMapping& Mapping : Exposed->GetEnhancedActionMappings()) + { + if (Mapping.Key == Key) + { + return ElxUsedOrNotUsed::Used; // Found a match in the active, flattened mappings. + } + } + + if (IncludeEscapeAndFkeys) + { + if (Key == EKeys::Escape || + Key == EKeys::F1 || Key == EKeys::F2 || Key == EKeys::F3 || + Key == EKeys::F4 || Key == EKeys::F5 || Key == EKeys::F6 || + Key == EKeys::F7 || Key == EKeys::F8 || Key == EKeys::F9 || + Key == EKeys::F10 || Key == EKeys::F11 || Key == EKeys::F12) + { + return ElxUsedOrNotUsed::Used; + } + } + + return ElxUsedOrNotUsed::NotUsed; } diff --git a/Source/Integration/UtilityLibrary.h b/Source/Integration/UtilityLibrary.h index 2a47cb50..0d866c95 100644 --- a/Source/Integration/UtilityLibrary.h +++ b/Source/Integration/UtilityLibrary.h @@ -4,7 +4,8 @@ #include "CoreMinimal.h" #include "Kismet/KismetSystemLibrary.h" - +#include "Input/Events.h" +#include "CommonTypes.h" #include "UtilityLibrary.generated.h" class UEnhancedInputLocalPlayerSubsystem; @@ -132,13 +133,21 @@ public: UFUNCTION(BlueprintPure, Category="Widget") static void GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D &UpperLeftXY, FVector2D &LowerRightXY); - // Create a mapping context that maps all keyboard keys to a single input action. + // Check if a given key is used by the mapping context. // - // This mapping context is usually meant to be used in conjunction with a - // hand-crafted mapping context, to act as a catch-all that catches all - // keys that aren't mapped in the hand-crafted mapping. - // - UFUNCTION(BlueprintCallable, Category="Input", meta=(WorldContext="WorldContextObject")) - static void MapAllKeyboardKeysToOneInputAction(UInputMappingContext *IMC, UInputAction *Action); + UFUNCTION(BlueprintCallable, Category = "Input", meta = (ExpandEnumAsExecs="ReturnValue")) + static ElxUsedOrNotUsed IsKeyUsedByMappingContext(const FKeyEvent &KeyEvent, const UInputMappingContext *MappingContext); + // Check if a given key is used by the player controller. + // + // If you pass in a null pointer for the player controller, then this + // function will use the first player controller by default. + // + // If 'Include Escape and FKeys' is checked, then the escape key + // and the function keys are also considered to be 'used' by the + // player controller. It is generally a good idea to treat these + // keys as reserved for play-in-editor. + // + UFUNCTION(BlueprintCallable, Category = "Input", meta = (WorldContext="Context", ExpandEnumAsExecs="ReturnValue", IncludeEscapeAndFkeys="true")) + static ElxUsedOrNotUsed IsKeyUsedByPlayerController(const FKeyEvent &KeyEvent, bool IncludeEscapeAndFkeys, const APlayerController *PlayerController, const UObject *Context); };