Still working on event handling for hotkey widgets

This commit is contained in:
2025-05-23 15:33:18 -04:00
parent 28f1c4c819
commit 40da211985
32 changed files with 254 additions and 158 deletions

View File

@@ -1,3 +1,5 @@
[/Script/Engine.Engine]
+ActiveClassRedirects=(OldClassName="/Script/Integration.lxLookAtWidget",NewClassName="/Script/Integration.lxLuaWidget")
[/Script/EngineSettings.GameMapsSettings] [/Script/EngineSettings.GameMapsSettings]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Widgets/WB_Root.uasset LFS Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -22,6 +22,10 @@
"TargetAllowList": [ "TargetAllowList": [
"Editor" "Editor"
] ]
},
{
"Name": "CommonUI",
"Enabled": true
} }
] ]
} }

View File

@@ -105,7 +105,7 @@ void UlxAssetLookup::LogMaybeError(bool Error, const TCHAR *Message, const TCHAR
} }
} }
UStaticMesh *UlxAssetLookup::GetStaticMeshByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound) UStaticMesh *UlxAssetLookup::LoadStaticMeshAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound)
{ {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context);
FString Path = mode->GetAssetLookup()->StaticMeshLoadPath(FName(FString("SM_") + Name)); FString Path = mode->GetAssetLookup()->StaticMeshLoadPath(FName(FString("SM_") + Name));
@@ -124,7 +124,7 @@ UStaticMesh *UlxAssetLookup::GetStaticMeshByName(const UObject *Context, const F
return Result; return Result;
} }
TSubclassOf<AActor> UlxAssetLookup::GetTangibleClassByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound) { TSubclassOf<AActor> UlxAssetLookup::LoadTangibleBlueprintAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context);
FString Path = mode->GetAssetLookup()->TangibleLoadPath(FName(FString("TAN_") + Name)); FString Path = mode->GetAssetLookup()->TangibleLoadPath(FName(FString("TAN_") + Name));
if (Path.IsEmpty()) if (Path.IsEmpty())
@@ -152,7 +152,7 @@ TSubclassOf<AActor> UlxAssetLookup::GetTangibleClassByName(const UObject *Contex
return Result; return Result;
} }
TSubclassOf<UUserWidget> UlxAssetLookup::GetWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound) { TSubclassOf<UUserWidget> UlxAssetLookup::LoadUserWidgetAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context);
FString Path = mode->GetAssetLookup()->WidgetLoadPath(FName(FString("WB_") + Name)); FString Path = mode->GetAssetLookup()->WidgetLoadPath(FName(FString("WB_") + Name));
if (Path.IsEmpty()) if (Path.IsEmpty())
@@ -168,13 +168,13 @@ TSubclassOf<UUserWidget> UlxAssetLookup::GetWidgetByName(const UObject *Context,
} }
if (!Result->IsChildOf(UUserWidget::StaticClass())) if (!Result->IsChildOf(UUserWidget::StaticClass()))
{ {
LogMaybeError(ErrorIfNotFound, TEXT("Blueprint is not a Widget Blueprint"), *Path); LogMaybeError(ErrorIfNotFound, TEXT("Blueprint does not derive from UUserWidget"), *Path);
return nullptr; return nullptr;
} }
return Result; return Result;
} }
TSubclassOf<UlxLookAtWidget> UlxAssetLookup::GetLookAtWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound) { TSubclassOf<UlxLuaWidget> UlxAssetLookup::LoadLuaWidgetAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context);
FString Path = mode->GetAssetLookup()->WidgetLoadPath(FName(FString("WB_") + Name)); FString Path = mode->GetAssetLookup()->WidgetLoadPath(FName(FString("WB_") + Name));
if (Path.IsEmpty()) if (Path.IsEmpty())
@@ -188,9 +188,9 @@ TSubclassOf<UlxLookAtWidget> UlxAssetLookup::GetLookAtWidgetByName(const UObject
LogMaybeError(ErrorIfNotFound, TEXT("Cannot load widget blueprint"), *Path); LogMaybeError(ErrorIfNotFound, TEXT("Cannot load widget blueprint"), *Path);
return nullptr; return nullptr;
} }
if (!Result->IsChildOf(UlxLookAtWidget::StaticClass())) if (!Result->IsChildOf(UlxLuaWidget::StaticClass()))
{ {
LogMaybeError(ErrorIfNotFound, TEXT("Blueprint is not a Luprex Look-At Widget"), *Path); LogMaybeError(ErrorIfNotFound, TEXT("Blueprint does not derive from UlxLuaWidget"), *Path);
return nullptr; return nullptr;
} }
return Result; return Result;

View File

@@ -49,18 +49,18 @@ public:
FString WidgetLoadPath(const FName &AssetName) const; FString WidgetLoadPath(const FName &AssetName) const;
// Get a static mesh by name // Get a static mesh by name
UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Asset Loading")
static UStaticMesh *GetStaticMeshByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); static UStaticMesh *LoadStaticMeshAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
// Get a tangible class by name // Get a tangible class by name
UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Asset Loading")
static TSubclassOf<AActor> GetTangibleClassByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); static TSubclassOf<AActor> LoadTangibleBlueprintAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
// Get a widget blueprint by name // Get a widget blueprint by name
UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Asset Loading")
static TSubclassOf<UUserWidget> GetWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); static TSubclassOf<UUserWidget> LoadUserWidgetAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
// Get a look-at widget blueprint by name // Get a look-at widget blueprint by name
UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Asset Loading")
static TSubclassOf<UlxLookAtWidget> GetLookAtWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); static TSubclassOf<UlxLuaWidget> LoadLuaWidgetAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false);
}; };

View File

@@ -16,6 +16,8 @@ public class Integration : ModuleRules
"Sockets", "Sockets",
"Networking", "Networking",
"EnhancedInput", "EnhancedInput",
"UMG",
"CommonUI"
}); });
PrivateDependencyModuleNames.AddRange(new string[] { PrivateDependencyModuleNames.AddRange(new string[] {

View File

@@ -233,12 +233,12 @@ bool UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place, UlxLuaValu
ReturnArray = nullptr; ReturnArray = nullptr;
return false; return false;
} }
ElxSuccessOrError Status; ElxSuccessOrWrongType Status;
FString ErrorMessage; FString ErrorMessage;
ReturnArray->ReadString(Status, ErrorMessage); ReturnArray->ReadString(Status, ErrorMessage, false);
if (Status != ElxSuccessOrError::Success) if (Status != ElxSuccessOrWrongType::Success)
{ {
UE_LOG(LogLuprexIntegration, Error, TEXT("corruption in lua_probe")); UE_LOG(LogLuprexIntegration, Error, TEXT("lua probe should always return an error message (possibly empty) as the first parameter"));
ReturnArray = nullptr; ReturnArray = nullptr;
return false; return false;
} }
@@ -408,16 +408,19 @@ FString UlxLuaValues::DebugString() const
return Output.ToString(); return Output.ToString();
} }
ElxSuccessOrError UlxLuaValues::CheckType(ElxLuaValueType Type, ElxLuaValueType Desired) ElxSuccessOrWrongType UlxLuaValues::CheckType(bool LogErrorOnWrongType, ElxLuaValueType Type, ElxLuaValueType Desired)
{ {
if (Type != Desired) if (Type != Desired)
{ {
FString TypeName = StaticEnum<ElxLuaValueType>()->GetDisplayNameTextByValue(int64(Type)).ToString(); if (LogErrorOnWrongType)
FString DesiredName = StaticEnum<ElxLuaValueType>()->GetDisplayNameTextByValue(int64(Desired)).ToString(); {
UE_LOG(LogBlueprint, Error, TEXT("Expected a value of type %s, but found %s instead."), *DesiredName, *TypeName); FString TypeName = StaticEnum<ElxLuaValueType>()->GetDisplayNameTextByValue(int64(Type)).ToString();
return ElxSuccessOrError::Error; FString DesiredName = StaticEnum<ElxLuaValueType>()->GetDisplayNameTextByValue(int64(Desired)).ToString();
UE_LOG(LogBlueprint, Error, TEXT("Expected a value of type %s, but found %s instead."), *DesiredName, *TypeName);
}
return ElxSuccessOrWrongType::WrongType;
} }
return ElxSuccessOrError::Success; return ElxSuccessOrWrongType::Success;
} }
void UlxLuaValues::DiscardBeforeCursor() void UlxLuaValues::DiscardBeforeCursor()
@@ -437,40 +440,40 @@ ElxLuaValueType UlxLuaValues::NextType() const
return Types[Cursor]; return Types[Cursor];
} }
void UlxLuaValues::ReadString(ElxSuccessOrError &Status, FString &Result) void UlxLuaValues::ReadString(ElxSuccessOrWrongType &Status, FString &Result, bool LogErrorOnMismatch)
{ {
Status = CheckType(NextType(), ElxLuaValueType::String); Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::String);
if (Status == ElxSuccessOrError::Error) if (Status == ElxSuccessOrWrongType::WrongType)
{ {
Result.Empty(); return; Result.Empty(); return;
} }
Result = FlxStreamBuffer(Data[Cursor++]).read_fstring(); Result = FlxStreamBuffer(Data[Cursor++]).read_fstring();
} }
void UlxLuaValues::ReadName(ElxSuccessOrError &Status, FName &Result) void UlxLuaValues::ReadName(ElxSuccessOrWrongType &Status, FName &Result, bool LogErrorOnMismatch)
{ {
Status = CheckType(NextType(), ElxLuaValueType::Name); Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Name);
if (Status == ElxSuccessOrError::Error) if (Status == ElxSuccessOrWrongType::WrongType)
{ {
Result = FName(); return; Result = FName(); return;
} }
Result = FlxStreamBuffer(Data[Cursor++]).read_fname(); Result = FlxStreamBuffer(Data[Cursor++]).read_fname();
} }
void UlxLuaValues::ReadFloat(ElxSuccessOrError &Status, double &Result) void UlxLuaValues::ReadFloat(ElxSuccessOrWrongType &Status, double &Result, bool LogErrorOnMismatch)
{ {
Status = CheckType(NextType(), ElxLuaValueType::Float); Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Float);
if (Status == ElxSuccessOrError::Error) if (Status == ElxSuccessOrWrongType::WrongType)
{ {
Result = 0.0; return; Result = 0.0; return;
} }
Result = FlxStreamBuffer(Data[Cursor++]).read_double(); Result = FlxStreamBuffer(Data[Cursor++]).read_double();
} }
void UlxLuaValues::ReadInt(ElxSuccessOrError &Status, int &Result) void UlxLuaValues::ReadInt(ElxSuccessOrWrongType &Status, int &Result, bool LogErrorOnMismatch)
{ {
Status = CheckType(NextType(), ElxLuaValueType::Float); Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Float);
if (Status == ElxSuccessOrError::Error) if (Status == ElxSuccessOrWrongType::WrongType)
{ {
Result = 0.0; return; Result = 0.0; return;
} }
@@ -478,24 +481,24 @@ void UlxLuaValues::ReadInt(ElxSuccessOrError &Status, int &Result)
Result = int(dvalue); Result = int(dvalue);
if (double(Result) != dvalue) if (double(Result) != dvalue)
{ {
Result = 0; Status = ElxSuccessOrError::Error; return; Result = 0; Status = ElxSuccessOrWrongType::WrongType; return;
} }
} }
void UlxLuaValues::ReadVector(ElxSuccessOrError &Status, FVector &Result) void UlxLuaValues::ReadVector(ElxSuccessOrWrongType &Status, FVector &Result, bool LogErrorOnMismatch)
{ {
Status = CheckType(NextType(), ElxLuaValueType::Vector); Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Vector);
if (Status == ElxSuccessOrError::Error) if (Status == ElxSuccessOrWrongType::WrongType)
{ {
Result = FVector(); return; Result = FVector(); return;
} }
Result = FlxStreamBuffer(Data[Cursor++]).read_fvector(); Result = FlxStreamBuffer(Data[Cursor++]).read_fvector();
} }
void UlxLuaValues::ReadVector2D(ElxSuccessOrError &Status, FVector2D &Result) void UlxLuaValues::ReadVector2D(ElxSuccessOrWrongType &Status, FVector2D &Result, bool LogErrorOnMismatch)
{ {
Status = CheckType(NextType(), ElxLuaValueType::Vector); Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Vector);
if (Status == ElxSuccessOrError::Error) if (Status == ElxSuccessOrWrongType::WrongType)
{ {
Result = FVector2D(); return; Result = FVector2D(); return;
} }
@@ -503,10 +506,10 @@ void UlxLuaValues::ReadVector2D(ElxSuccessOrError &Status, FVector2D &Result)
Result = FVector2D(VValue.X, VValue.Y); Result = FVector2D(VValue.X, VValue.Y);
} }
void UlxLuaValues::ReadBoolean(ElxSuccessOrError &Status, bool &Result) void UlxLuaValues::ReadBoolean(ElxSuccessOrWrongType &Status, bool &Result, bool LogErrorOnMismatch)
{ {
Status = CheckType(NextType(), ElxLuaValueType::Boolean); Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Boolean);
if (Status == ElxSuccessOrError::Error) if (Status == ElxSuccessOrWrongType::WrongType)
{ {
Result = false; return; Result = false; return;
} }

View File

@@ -43,6 +43,12 @@ enum class ElxFoundOrNotFound : uint8 {
NotFound, NotFound,
}; };
UENUM(BlueprintType)
enum class ElxSuccessOrWrongType : uint8 {
Success,
WrongType,
};
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// //
// This is a little parser that parses Lua function 'prototypes'. // This is a little parser that parses Lua function 'prototypes'.
@@ -219,9 +225,9 @@ private:
// //
void Empty(); void Empty();
// Compare two types. If they aren't equal, log an error and return false. // Compare two types. If they aren't equal, possibly log an error, and return a status code.
// //
static ElxSuccessOrError CheckType(ElxLuaValueType Type, ElxLuaValueType Desired); static ElxSuccessOrWrongType CheckType(bool LogErrorOnWrongType, ElxLuaValueType Type, ElxLuaValueType Desired);
public: public:
UlxLuaValues() { Cursor = 0; } UlxLuaValues() { Cursor = 0; }
@@ -256,23 +262,23 @@ public:
ElxLuaValueType SwitchNextType() { return NextType(); } ElxLuaValueType SwitchNextType() { return NextType(); }
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
void ReadString(ElxSuccessOrError &Status, FString &Result); void ReadString(ElxSuccessOrWrongType &Status, FString &Result, bool LogErrorOnWrongType = false);
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
void ReadName(ElxSuccessOrError &Status, FName &Result); void ReadName(ElxSuccessOrWrongType &Status, FName &Result, bool LogErrorOnWrongType = false);
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
void ReadFloat(ElxSuccessOrError &Status, double &Result); void ReadFloat(ElxSuccessOrWrongType &Status, double &Result, bool LogErrorOnWrongType = false);
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
void ReadInt(ElxSuccessOrError &Status, int &Result); void ReadInt(ElxSuccessOrWrongType &Status, int &Result, bool LogErrorOnWrongType = false);
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
void ReadVector(ElxSuccessOrError &Status, FVector &Result); void ReadVector(ElxSuccessOrWrongType &Status, FVector &Result, bool LogErrorOnWrongType = false);
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
void ReadVector2D(ElxSuccessOrError &Status, FVector2D &Result); void ReadVector2D(ElxSuccessOrWrongType &Status, FVector2D &Result, bool LogErrorOnWrongType = false);
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
void ReadBoolean(ElxSuccessOrError &Status, bool &Result); void ReadBoolean(ElxSuccessOrWrongType &Status, bool &Result, bool LogErrorOnWrongType = false);
}; };

View File

@@ -426,7 +426,7 @@ void UK2Node_LuaInvoke::ExpandNode(class FKismetCompilerContext& CompilerContext
} }
UK2Node_CallFunction *UnpackNode = MakeCallFunctionNode(UnpackingFunc); UK2Node_CallFunction *UnpackNode = MakeCallFunctionNode(UnpackingFunc);
ReturnArrayPin->MakeLinkTo(UnpackNode->FindPinChecked(UEdGraphSchema_K2::PN_Self)); ReturnArrayPin->MakeLinkTo(UnpackNode->FindPinChecked(UEdGraphSchema_K2::PN_Self));
CompilerContext.CopyPinLinksToIntermediate(*FindPinChecked(ErrorPinName), *UnpackNode->FindPinChecked(TEXT("Error"))); CompilerContext.CopyPinLinksToIntermediate(*FindPinChecked(ErrorPinName), *UnpackNode->FindPinChecked(TEXT("WrongType")));
CompilerContext.MovePinLinksToIntermediate(*Pin, *UnpackNode->FindPinChecked(TEXT("Result"))); CompilerContext.MovePinLinksToIntermediate(*Pin, *UnpackNode->FindPinChecked(TEXT("Result")));
ThenPin->MakeLinkTo(UnpackNode->GetExecPin()); ThenPin->MakeLinkTo(UnpackNode->GetExecPin());
ThenPin = UnpackNode->FindPinChecked(TEXT("Success")); ThenPin = UnpackNode->FindPinChecked(TEXT("Success"));

View File

@@ -349,48 +349,6 @@ ALuprexGameModeBase *ALuprexGameModeBase::FromContext(const UObject *context) {
return result; return result;
} }
void ALuprexGameModeBase::ClearLookAtWidget(const UObject *Context)
{
ALuprexGameModeBase *mode = FromContext(Context);
if (mode->LookAtWidget != nullptr)
{
mode->LookAtWidget->RemoveFromParent();
mode->LookAtWidget = nullptr;
}
}
void ALuprexGameModeBase::SetLookAtWidget(const UObject *Context, UlxLookAtWidget *Widget)
{
ALuprexGameModeBase *Mode = FromContext(Context);
if (Mode->LookAtWidget != Widget)
{
ClearLookAtWidget(Context);
}
Mode->LookAtWidget = Widget;
}
UlxLookAtWidget *ALuprexGameModeBase::CreateLookAtWidgetByName(UObject *Context, const FString &BlueprintName,
ElxFoundOrNotFound &Result, bool ErrorIfNotFound, bool AddToViewport, bool SetLookAtWidget)
{
ALuprexGameModeBase *Mode = FromContext(Context);
Result = ElxFoundOrNotFound::NotFound;
auto Blueprint = UlxAssetLookup::GetLookAtWidgetByName(Context, BlueprintName, ErrorIfNotFound);
if (Blueprint == nullptr) return nullptr;
APlayerController *pc = Context->GetWorld()->GetFirstPlayerController();
UlxLookAtWidget *Widget = Cast<UlxLookAtWidget>(UWidgetBlueprintLibrary::Create(Context, Blueprint, pc));
check(Widget != nullptr);
if (AddToViewport)
{
Widget->AddToViewport(100);
}
if (SetLookAtWidget)
{
Mode->SetLookAtWidget(Context, Widget);
}
Result = ElxFoundOrNotFound::Found;
return Widget;
}
void ALuprexGameModeBase::SetLookAt(const UObject *Context, const FHitResult &HitResult) void ALuprexGameModeBase::SetLookAt(const UObject *Context, const FHitResult &HitResult)
{ {
@@ -402,7 +360,6 @@ void ALuprexGameModeBase::SetLookAt(const UObject *Context, const FHitResult &Hi
Mode->CurrentLookAt = HitResult; Mode->CurrentLookAt = HitResult;
} }
FVector2D ALuprexGameModeBase::GetLookAtPixel(const UObject *Context) FVector2D ALuprexGameModeBase::GetLookAtPixel(const UObject *Context)
{ {
ALuprexGameModeBase *Mode = FromContext(Context); ALuprexGameModeBase *Mode = FromContext(Context);

View File

@@ -13,6 +13,8 @@
#include "TriggeredTask.h" #include "TriggeredTask.h"
#include "BlueprintErrors.h" #include "BlueprintErrors.h"
#include "Blueprint/UserWidget.h" #include "Blueprint/UserWidget.h"
#include "Widgets/CommonActivatableWidgetContainer.h"
#include "CommonActivatableWidget.h"
#include "LuprexGameModeBase.generated.h" #include "LuprexGameModeBase.generated.h"
// Messages that come from inside the Luprex Core. // Messages that come from inside the Luprex Core.
@@ -25,12 +27,12 @@ class UlxLuaValues;
UCLASS(BlueprintType) UCLASS(BlueprintType)
class INTEGRATION_API UlxLookAtWidget : public UUserWidget class INTEGRATION_API UlxLuaWidget : public UCommonActivatableWidget
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Luprex|Look-At Detection") UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Luprex|Miscellaneous")
void ReadLuaConfiguration(UlxLuaValues *Config); void ReadLuaConfiguration(UlxLuaValues *Config);
}; };
@@ -79,36 +81,6 @@ public:
UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection")
static FVector2D GetLookAtPixel(const UObject *Context); static FVector2D GetLookAtPixel(const UObject *Context);
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Look-At Detection")
static void SetLookAtWidget(const UObject *Context, UlxLookAtWidget *Widget);
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Look-At Detection")
static UlxLookAtWidget *GetLookAtWidget(const UObject *Context) { return FromContext(Context)->LookAtWidget; }
// Create a new Look-At Widget, given the blueprint's name.
//
// The prefix WB_ is added to the blueprint name, and the blueprint is
// searched for in the folder "Widgets".
//
// * If the specified blueprint is not found, execution continues
// on the "Not Found" pin.
//
// * If the flag "Error if Not Found" is true, and the widget blueprint
// is not found, logs an error.
//
// * If the flag "Add to Viewport" is true, the new widget is added
// to the viewport.
//
// * If the flag "Set Look at Widget" is true, the new widget is stored
// as the current look-at widget.
//
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Result", WorldContext = "Context"), Category = "Luprex|Look-At Detection")
static UlxLookAtWidget *CreateLookAtWidgetByName(UObject *Context, const FString &BlueprintName,
ElxFoundOrNotFound &Result, bool ErrorIfNotFound = true, bool AddToViewport = true, bool SetLookAtWidget = true);
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Look-At Detection")
static void ClearLookAtWidget(const UObject *Context);
// //
// Look-At Related Events // Look-At Related Events
// //
@@ -183,9 +155,6 @@ public:
bool MustCallLookAtChanged; bool MustCallLookAtChanged;
UPROPERTY()
UlxLookAtWidget *LookAtWidget;
// The sensitivity level at which a log message triggers a debugger breakpoint. // The sensitivity level at which a log message triggers a debugger breakpoint.
UPROPERTY(EditAnywhere, Category="Debugging Tools") UPROPERTY(EditAnywhere, Category="Debugging Tools")
ElxLogVerbosity BreakToDebuggerLogVerbosity; ElxLogVerbosity BreakToDebuggerLogVerbosity;

View File

@@ -0,0 +1,56 @@
#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<uint32>(Vec.X);
uint32 didx = static_cast<uint32>(Vec.Y);
uint32 nidx = static_cast<uint32>(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;
}

View File

@@ -0,0 +1,54 @@
#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);
};

View File

@@ -33,10 +33,10 @@ void UlxTangible::SetActorBlueprint(const FString &XName) {
} }
// Get the blueprint. // Get the blueprint.
UClass *blueprint = UlxAssetLookup::GetTangibleClassByName(this, Name); UClass *blueprint = UlxAssetLookup::LoadTangibleBlueprintAsset(this, Name);
if (blueprint == nullptr) if (blueprint == nullptr)
{ {
blueprint = UlxAssetLookup::GetTangibleClassByName(this, DEFAULT_BLUEPRINT); blueprint = UlxAssetLookup::LoadTangibleBlueprintAsset(this, DEFAULT_BLUEPRINT);
check(blueprint != nullptr); check(blueprint != nullptr);
} }

View File

@@ -8,6 +8,8 @@
#include "Kismet/GameplayStatics.h" #include "Kismet/GameplayStatics.h"
#include "Blueprint/UserWidget.h" #include "Blueprint/UserWidget.h"
#include "Components/GridPanel.h" #include "Components/GridPanel.h"
#include "InputMappingContext.h"
#define LOCTEXT_NAMESPACE "Luprex Utility" #define LOCTEXT_NAMESPACE "Luprex Utility"
@@ -209,3 +211,21 @@ void UlxUtilityLibrary::GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel,
LowerRightXY.Y = (Row[0] + Row[1]) / TotalY; LowerRightXY.Y = (Row[0] + Row[1]) / TotalY;
} }
} }
void UlxUtilityLibrary::MapAllKeyboardKeysToOneInputAction(UInputMappingContext *IMC, UInputAction *Action)
{
TArray<FKey> AllKeys;
EKeys::GetAllKeys(AllKeys);
// Map every keyboard key to the provided LuaAction
for (const FKey& Key : AllKeys)
{
if ((Key.IsValid()) &&
(Key.IsBindableInBlueprints()) &&
(Key.GetMenuCategory() == EKeys::NAME_KeyboardCategory) &&
(!Key.IsModifierKey()))
{
IMC->MapKey(Action, Key);
}
}
}

View File

@@ -132,4 +132,13 @@ public:
UFUNCTION(BlueprintPure, Category="Widget") UFUNCTION(BlueprintPure, Category="Widget")
static void GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D &UpperLeftXY, FVector2D &LowerRightXY); static void GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D &UpperLeftXY, FVector2D &LowerRightXY);
// Create a mapping context that maps all keyboard keys to a single input action.
//
// 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);
}; };

View File

@@ -21,11 +21,13 @@ end
function cube.lookhotkeys(keys) function cube.lookhotkeys(keys)
keys:add("X", "Cube Hi", function () dprint("Cube Hi") end) keys:add("X", "Cube Hi", function () dprint("Cube Hi") end)
keys:add("A", "Cube Bye", function () dprint("Cube Bye") end) keys:add("A", "Cube Bye", function () dprint("Cube Bye") end)
keys:add("Y", "Cube Yo", function () dprint("Cube Yo") end)
end end
function sphere.lookhotkeys(keys) function sphere.lookhotkeys(keys)
keys:add("X", "Sphere Hi", function () dprint("Sphere Hi") end) keys:add("X", "Sphere Hi", function () dprint("Sphere Hi") end)
keys:add("A", "Sphere Bye", function () dprint("Sphere Bye") end) keys:add("A", "Sphere Bye", function () dprint("Sphere Bye") end)
keys:add("Y", "Sphere Yo", function () dprint("Sphere Yo") end)
end end