diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index fd2bb98c..27b071eb 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -1,3 +1,5 @@ +[/Script/Engine.Engine] ++ActiveClassRedirects=(OldClassName="/Script/Integration.lxLookAtWidget",NewClassName="/Script/Integration.lxLuaWidget") [/Script/EngineSettings.GameMapsSettings] diff --git a/Content/LpxLevel.umap b/Content/LpxLevel.umap index 57d259ab..a7a40954 100644 --- a/Content/LpxLevel.umap +++ b/Content/LpxLevel.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b081e31151154830f103c56374bf2d98006a0d0896cff3c0a9a0b8b31f536f28 +oid sha256:a792ca4eef06b29eb1cfb85bdcd6a5505ac380df4b3a9534ebc99858b5c2a633 size 12394 diff --git a/Content/Luprex/lxCrosshairImage.uasset b/Content/Luprex/lxCrosshairImage.uasset index 4113b0b3..fb469856 100644 --- a/Content/Luprex/lxCrosshairImage.uasset +++ b/Content/Luprex/lxCrosshairImage.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:670413c9e1d9992d746405287d04fd27a7f69a6daa30ee3f03091e7499d13969 -size 1425 +oid sha256:4ca857ce5f2eb4674c08f9c1981892b1afeaffde79ad204e389533fe6bb6c893 +size 1458 diff --git a/Content/Luprex/lxGameMode.uasset b/Content/Luprex/lxGameMode.uasset index b50a7925..55ea6c90 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:69964add7c0799d602e46a0e17d624d4bda0dca7314249f78a609b0f382c4966 -size 137786 +oid sha256:bd96174fce58065404c8ce9690f5048d3e50d7fc5051a08c8fb5d5173ffd92aa +size 159188 diff --git a/Content/Luprex/lxMappingContext.uasset b/Content/Luprex/lxMappingContext.uasset index 1b188102..b4e2d669 100644 --- a/Content/Luprex/lxMappingContext.uasset +++ b/Content/Luprex/lxMappingContext.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef23ff90c023a9b6e35e0e1c42a8d5e18793ef24d339730a8fabddaca8a765f6 +oid sha256:2f350ed6b6b5d406bfc7daab68d32a33c2d708992a50788977aed88ed681a9fe size 14501 diff --git a/Content/Luprex/lxUtilityFunctionsLibrary.uasset b/Content/Luprex/lxUtilityFunctionsLibrary.uasset index b4e6eddb..7f7d01f6 100644 --- a/Content/Luprex/lxUtilityFunctionsLibrary.uasset +++ b/Content/Luprex/lxUtilityFunctionsLibrary.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2eb1dedd09ea301c86c3c341a21670d80db46d61908d5912478c598e9c3da307 -size 63985 +oid sha256:4e53473869803421b5cb99e4dafb1edd6afd9ff4b542781bace4b39e3e2c62a9 +size 58604 diff --git a/Content/Luprex/lxUtilityMacroLibrary.uasset b/Content/Luprex/lxUtilityMacroLibrary.uasset index 52205aea..0cb444c9 100644 --- a/Content/Luprex/lxUtilityMacroLibrary.uasset +++ b/Content/Luprex/lxUtilityMacroLibrary.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cb531b158ececdb42ffb71eb803fd0ca428354d09beeb32535fea96336d8ac1 -size 32320 +oid sha256:27482302100b9c4eeba1edc7374980d2ac77b52eb7d7f866eeb734e8255d6b1c +size 28966 diff --git a/Content/Tangibles/TAN_Character.uasset b/Content/Tangibles/TAN_Character.uasset index b9acff4c..a4cefb4a 100644 --- a/Content/Tangibles/TAN_Character.uasset +++ b/Content/Tangibles/TAN_Character.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bdee0a31de1029878f085a66a119a27473dd1edf48cfce01c4b6f95e240e08bc -size 318884 +oid sha256:7e23a73e194bab4dbd8a932d68238059beb5edae6d74e81f06dd310f23660996 +size 313663 diff --git a/Content/Tangibles/TAN_StaticMesh.uasset b/Content/Tangibles/TAN_StaticMesh.uasset index 393582d1..340df287 100644 --- a/Content/Tangibles/TAN_StaticMesh.uasset +++ b/Content/Tangibles/TAN_StaticMesh.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35010ca9031afd57c1bc2cbbd132a96a43d7b6c7bc374d89c4d46e7489c380bb -size 168830 +oid sha256:737637ce0b9987fdecfe20abf3739565de913fdbdb831af14acdffd45e0d04c4 +size 172370 diff --git a/Content/Widgets/TX_Crosshair_Image.uasset b/Content/Widgets/TX_Crosshair_Image.uasset new file mode 100644 index 00000000..4ee61aa7 --- /dev/null +++ b/Content/Widgets/TX_Crosshair_Image.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5b17b2d217436638ae5fdf66a946b95cecc35355605237be358d567d942fb1b +size 8094 diff --git a/Content/Widgets/TX_Hotkey_Label.uasset b/Content/Widgets/TX_Hotkey_Label.uasset new file mode 100644 index 00000000..7e5a0839 --- /dev/null +++ b/Content/Widgets/TX_Hotkey_Label.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e646f3c903c6ee4f5ea579d46bb95eb3fd6822ecf63098d956f8e26e7e1b4cd0 +size 15523 diff --git a/Content/Widgets/WB_Crosshair.uasset b/Content/Widgets/WB_Crosshair.uasset index 912a3dd1..e965787a 100644 --- a/Content/Widgets/WB_Crosshair.uasset +++ b/Content/Widgets/WB_Crosshair.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc85285926aca6cd91e15b94daf309f894f11d0bb53c6003cda9f66a603d9e0f -size 65489 +oid sha256:f13d4b679f3bea54960e95f91bb86de0c36fc3a28d2c5c19bfcc7dcb958e6f36 +size 64665 diff --git a/Content/Widgets/WB_Hotkey_Action.uasset b/Content/Widgets/WB_Hotkey_Action.uasset new file mode 100644 index 00000000..b098b482 --- /dev/null +++ b/Content/Widgets/WB_Hotkey_Action.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9cac355d17042d47ee9938e10ee870e0e98f811b0e9fae71993dd3c8b4abbbfa +size 38863 diff --git a/Content/Widgets/WB_Hotkey_Image.uasset b/Content/Widgets/WB_Hotkey_Image.uasset new file mode 100644 index 00000000..c95f87b7 --- /dev/null +++ b/Content/Widgets/WB_Hotkey_Image.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ade3654dc07f8d8c60e6621cf12bd7df1aee099fe3bbbc43ccbebc4a3d3967d +size 58296 diff --git a/Content/Widgets/WB_Hotkeys.uasset b/Content/Widgets/WB_Hotkeys.uasset index b96f8d7c..9ecee49c 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:4a0b84a261189547f1f141a675d0edf54d5f0903b6b94d0e3ac25c2e8117f4bf -size 51522 +oid sha256:854960103277e7e23ec457ee2be4341ebccab7318a5400845d6857d2f153582d +size 201757 diff --git a/Content/Widgets/WB_Root.uasset b/Content/Widgets/WB_Root.uasset new file mode 100644 index 00000000..14fdcc01 --- /dev/null +++ b/Content/Widgets/WB_Root.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a52aed59201b310f6c3daf4bc87d04414b47954e0ef5b0f64d8420bbea225c9 +size 88813 diff --git a/Content/Widgets/lxCrosshairImage.uasset b/Content/Widgets/lxCrosshairImage.uasset deleted file mode 100644 index dbbce351..00000000 --- a/Content/Widgets/lxCrosshairImage.uasset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dca13b2122893f9b1a14667a5c1a60a362368c88b3715c5288afdd5c038b0acf -size 8084 diff --git a/EnginePatches/uproject b/EnginePatches/uproject index 41bd0c6f..56b727f8 100644 --- a/EnginePatches/uproject +++ b/EnginePatches/uproject @@ -22,6 +22,10 @@ "TargetAllowList": [ "Editor" ] + }, + { + "Name": "CommonUI", + "Enabled": true } ] } diff --git a/Source/Integration/AssetLookup.cpp b/Source/Integration/AssetLookup.cpp index 791d0efa..ab3c5024 100644 --- a/Source/Integration/AssetLookup.cpp +++ b/Source/Integration/AssetLookup.cpp @@ -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); FString Path = mode->GetAssetLookup()->StaticMeshLoadPath(FName(FString("SM_") + Name)); @@ -124,7 +124,7 @@ UStaticMesh *UlxAssetLookup::GetStaticMeshByName(const UObject *Context, const F return Result; } -TSubclassOf UlxAssetLookup::GetTangibleClassByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound) { +TSubclassOf UlxAssetLookup::LoadTangibleBlueprintAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); FString Path = mode->GetAssetLookup()->TangibleLoadPath(FName(FString("TAN_") + Name)); if (Path.IsEmpty()) @@ -152,7 +152,7 @@ TSubclassOf UlxAssetLookup::GetTangibleClassByName(const UObject *Contex return Result; } -TSubclassOf UlxAssetLookup::GetWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound) { +TSubclassOf UlxAssetLookup::LoadUserWidgetAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); FString Path = mode->GetAssetLookup()->WidgetLoadPath(FName(FString("WB_") + Name)); if (Path.IsEmpty()) @@ -168,13 +168,13 @@ TSubclassOf UlxAssetLookup::GetWidgetByName(const UObject *Context, } 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 Result; } -TSubclassOf UlxAssetLookup::GetLookAtWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound) { +TSubclassOf UlxAssetLookup::LoadLuaWidgetAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); FString Path = mode->GetAssetLookup()->WidgetLoadPath(FName(FString("WB_") + Name)); if (Path.IsEmpty()) @@ -188,9 +188,9 @@ TSubclassOf UlxAssetLookup::GetLookAtWidgetByName(const UObject LogMaybeError(ErrorIfNotFound, TEXT("Cannot load widget blueprint"), *Path); 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 Result; diff --git a/Source/Integration/AssetLookup.h b/Source/Integration/AssetLookup.h index c9fac1ae..0b387d90 100644 --- a/Source/Integration/AssetLookup.h +++ b/Source/Integration/AssetLookup.h @@ -49,18 +49,18 @@ public: FString WidgetLoadPath(const FName &AssetName) const; // Get a static mesh by name - UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") - static UStaticMesh *GetStaticMeshByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); + UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Asset Loading") + static UStaticMesh *LoadStaticMeshAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); // Get a tangible class by name - UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") - static TSubclassOf GetTangibleClassByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); + UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Asset Loading") + static TSubclassOf LoadTangibleBlueprintAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); // Get a widget blueprint by name - UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") - static TSubclassOf GetWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); + UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Asset Loading") + static TSubclassOf LoadUserWidgetAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); // Get a look-at widget blueprint by name - UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") - static TSubclassOf GetLookAtWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); + UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Asset Loading") + static TSubclassOf LoadLuaWidgetAsset(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false); }; diff --git a/Source/Integration/Integration.Build.cs b/Source/Integration/Integration.Build.cs index c410d651..a56e6f20 100644 --- a/Source/Integration/Integration.Build.cs +++ b/Source/Integration/Integration.Build.cs @@ -16,6 +16,8 @@ public class Integration : ModuleRules "Sockets", "Networking", "EnhancedInput", + "UMG", + "CommonUI" }); PrivateDependencyModuleNames.AddRange(new string[] { diff --git a/Source/Integration/LuaCall.cpp b/Source/Integration/LuaCall.cpp index 5ca26575..ca2acac8 100644 --- a/Source/Integration/LuaCall.cpp +++ b/Source/Integration/LuaCall.cpp @@ -233,12 +233,12 @@ bool UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place, UlxLuaValu ReturnArray = nullptr; return false; } - ElxSuccessOrError Status; + ElxSuccessOrWrongType Status; FString ErrorMessage; - ReturnArray->ReadString(Status, ErrorMessage); - if (Status != ElxSuccessOrError::Success) + ReturnArray->ReadString(Status, ErrorMessage, false); + 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; return false; } @@ -408,16 +408,19 @@ FString UlxLuaValues::DebugString() const return Output.ToString(); } -ElxSuccessOrError UlxLuaValues::CheckType(ElxLuaValueType Type, ElxLuaValueType Desired) +ElxSuccessOrWrongType UlxLuaValues::CheckType(bool LogErrorOnWrongType, ElxLuaValueType Type, ElxLuaValueType Desired) { if (Type != Desired) { - FString TypeName = StaticEnum()->GetDisplayNameTextByValue(int64(Type)).ToString(); - FString DesiredName = StaticEnum()->GetDisplayNameTextByValue(int64(Desired)).ToString(); - UE_LOG(LogBlueprint, Error, TEXT("Expected a value of type %s, but found %s instead."), *DesiredName, *TypeName); - return ElxSuccessOrError::Error; + if (LogErrorOnWrongType) + { + FString TypeName = StaticEnum()->GetDisplayNameTextByValue(int64(Type)).ToString(); + FString DesiredName = StaticEnum()->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() @@ -437,40 +440,40 @@ ElxLuaValueType UlxLuaValues::NextType() const return Types[Cursor]; } -void UlxLuaValues::ReadString(ElxSuccessOrError &Status, FString &Result) +void UlxLuaValues::ReadString(ElxSuccessOrWrongType &Status, FString &Result, bool LogErrorOnMismatch) { - Status = CheckType(NextType(), ElxLuaValueType::String); - if (Status == ElxSuccessOrError::Error) + Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::String); + if (Status == ElxSuccessOrWrongType::WrongType) { Result.Empty(); return; } 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); - if (Status == ElxSuccessOrError::Error) + Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Name); + if (Status == ElxSuccessOrWrongType::WrongType) { Result = FName(); return; } 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); - if (Status == ElxSuccessOrError::Error) + Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Float); + if (Status == ElxSuccessOrWrongType::WrongType) { Result = 0.0; return; } 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); - if (Status == ElxSuccessOrError::Error) + Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Float); + if (Status == ElxSuccessOrWrongType::WrongType) { Result = 0.0; return; } @@ -478,24 +481,24 @@ void UlxLuaValues::ReadInt(ElxSuccessOrError &Status, int &Result) Result = int(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); - if (Status == ElxSuccessOrError::Error) + Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Vector); + if (Status == ElxSuccessOrWrongType::WrongType) { Result = FVector(); return; } 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); - if (Status == ElxSuccessOrError::Error) + Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Vector); + if (Status == ElxSuccessOrWrongType::WrongType) { Result = FVector2D(); return; } @@ -503,10 +506,10 @@ void UlxLuaValues::ReadVector2D(ElxSuccessOrError &Status, FVector2D &Result) 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); - if (Status == ElxSuccessOrError::Error) + Status = CheckType(LogErrorOnMismatch, NextType(), ElxLuaValueType::Boolean); + if (Status == ElxSuccessOrWrongType::WrongType) { Result = false; return; } diff --git a/Source/Integration/LuaCall.h b/Source/Integration/LuaCall.h index e6d01194..534e6530 100644 --- a/Source/Integration/LuaCall.h +++ b/Source/Integration/LuaCall.h @@ -43,6 +43,12 @@ enum class ElxFoundOrNotFound : uint8 { NotFound, }; +UENUM(BlueprintType) +enum class ElxSuccessOrWrongType : uint8 { + Success, + WrongType, +}; + ///////////////////////////////////////////////////////////////// // // This is a little parser that parses Lua function 'prototypes'. @@ -219,9 +225,9 @@ private: // 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: UlxLuaValues() { Cursor = 0; } @@ -256,23 +262,23 @@ public: ElxLuaValueType SwitchNextType() { return NextType(); } 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") - 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") - 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") - 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") - 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") - 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") - void ReadBoolean(ElxSuccessOrError &Status, bool &Result); + void ReadBoolean(ElxSuccessOrWrongType &Status, bool &Result, bool LogErrorOnWrongType = false); }; \ No newline at end of file diff --git a/Source/Integration/LuaCallNode.cpp b/Source/Integration/LuaCallNode.cpp index 191a33b1..ad647c91 100644 --- a/Source/Integration/LuaCallNode.cpp +++ b/Source/Integration/LuaCallNode.cpp @@ -426,7 +426,7 @@ void UK2Node_LuaInvoke::ExpandNode(class FKismetCompilerContext& CompilerContext } UK2Node_CallFunction *UnpackNode = MakeCallFunctionNode(UnpackingFunc); 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"))); ThenPin->MakeLinkTo(UnpackNode->GetExecPin()); ThenPin = UnpackNode->FindPinChecked(TEXT("Success")); diff --git a/Source/Integration/LuprexGameModeBase.cpp b/Source/Integration/LuprexGameModeBase.cpp index 8fd12497..f94a4004 100644 --- a/Source/Integration/LuprexGameModeBase.cpp +++ b/Source/Integration/LuprexGameModeBase.cpp @@ -349,48 +349,6 @@ ALuprexGameModeBase *ALuprexGameModeBase::FromContext(const UObject *context) { 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(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) { @@ -402,7 +360,6 @@ void ALuprexGameModeBase::SetLookAt(const UObject *Context, const FHitResult &Hi Mode->CurrentLookAt = HitResult; } - FVector2D ALuprexGameModeBase::GetLookAtPixel(const UObject *Context) { ALuprexGameModeBase *Mode = FromContext(Context); diff --git a/Source/Integration/LuprexGameModeBase.h b/Source/Integration/LuprexGameModeBase.h index 50809e84..afcae2bf 100644 --- a/Source/Integration/LuprexGameModeBase.h +++ b/Source/Integration/LuprexGameModeBase.h @@ -13,6 +13,8 @@ #include "TriggeredTask.h" #include "BlueprintErrors.h" #include "Blueprint/UserWidget.h" +#include "Widgets/CommonActivatableWidgetContainer.h" +#include "CommonActivatableWidget.h" #include "LuprexGameModeBase.generated.h" // Messages that come from inside the Luprex Core. @@ -25,12 +27,12 @@ class UlxLuaValues; UCLASS(BlueprintType) -class INTEGRATION_API UlxLookAtWidget : public UUserWidget +class INTEGRATION_API UlxLuaWidget : public UCommonActivatableWidget { GENERATED_BODY() public: - UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Luprex|Look-At Detection") + UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Luprex|Miscellaneous") void ReadLuaConfiguration(UlxLuaValues *Config); }; @@ -79,36 +81,6 @@ public: UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") 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 // @@ -183,9 +155,6 @@ public: bool MustCallLookAtChanged; - UPROPERTY() - UlxLookAtWidget *LookAtWidget; - // The sensitivity level at which a log message triggers a debugger breakpoint. UPROPERTY(EditAnywhere, Category="Debugging Tools") ElxLogVerbosity BreakToDebuggerLogVerbosity; diff --git a/Source/Integration/StoreFNameInputModifier.cpp b/Source/Integration/StoreFNameInputModifier.cpp new file mode 100644 index 00000000..c411241b --- /dev/null +++ b/Source/Integration/StoreFNameInputModifier.cpp @@ -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(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 new file mode 100644 index 00000000..7495aa1a --- /dev/null +++ b/Source/Integration/StoreFNameInputModifier.h @@ -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); + +}; diff --git a/Source/Integration/Tangible.cpp b/Source/Integration/Tangible.cpp index d9bf0a06..4687a66f 100644 --- a/Source/Integration/Tangible.cpp +++ b/Source/Integration/Tangible.cpp @@ -33,10 +33,10 @@ void UlxTangible::SetActorBlueprint(const FString &XName) { } // Get the blueprint. - UClass *blueprint = UlxAssetLookup::GetTangibleClassByName(this, Name); + UClass *blueprint = UlxAssetLookup::LoadTangibleBlueprintAsset(this, Name); if (blueprint == nullptr) { - blueprint = UlxAssetLookup::GetTangibleClassByName(this, DEFAULT_BLUEPRINT); + blueprint = UlxAssetLookup::LoadTangibleBlueprintAsset(this, DEFAULT_BLUEPRINT); check(blueprint != nullptr); } diff --git a/Source/Integration/UtilityLibrary.cpp b/Source/Integration/UtilityLibrary.cpp index 2df84332..b27e737f 100644 --- a/Source/Integration/UtilityLibrary.cpp +++ b/Source/Integration/UtilityLibrary.cpp @@ -8,6 +8,8 @@ #include "Kismet/GameplayStatics.h" #include "Blueprint/UserWidget.h" #include "Components/GridPanel.h" +#include "InputMappingContext.h" + #define LOCTEXT_NAMESPACE "Luprex Utility" @@ -209,3 +211,21 @@ void UlxUtilityLibrary::GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, LowerRightXY.Y = (Row[0] + Row[1]) / TotalY; } } + +void UlxUtilityLibrary::MapAllKeyboardKeysToOneInputAction(UInputMappingContext *IMC, UInputAction *Action) +{ + TArray 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); + } + } +} diff --git a/Source/Integration/UtilityLibrary.h b/Source/Integration/UtilityLibrary.h index 5e9e5293..2a47cb50 100644 --- a/Source/Integration/UtilityLibrary.h +++ b/Source/Integration/UtilityLibrary.h @@ -132,4 +132,13 @@ 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. + // + // 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); + }; diff --git a/luprex/lua/login.lua b/luprex/lua/login.lua index 78846b14..73493b0c 100644 --- a/luprex/lua/login.lua +++ b/luprex/lua/login.lua @@ -21,11 +21,13 @@ end function cube.lookhotkeys(keys) keys:add("X", "Cube Hi", function () dprint("Cube Hi") end) keys:add("A", "Cube Bye", function () dprint("Cube Bye") end) + keys:add("Y", "Cube Yo", function () dprint("Cube Yo") end) end function sphere.lookhotkeys(keys) keys:add("X", "Sphere Hi", function () dprint("Sphere Hi") end) keys:add("A", "Sphere Bye", function () dprint("Sphere Bye") end) + keys:add("Y", "Sphere Yo", function () dprint("Sphere Yo") end) end