From 72eda3026f5c19ba64ddf84be6c27cec45d59421 Mon Sep 17 00:00:00 2001 From: jyelon Date: Wed, 26 Feb 2025 14:47:53 -0500 Subject: [PATCH] A lot of work on the Lua Call interface and some work on animation queues --- .../Luprex/lxUtilityFunctionsLibrary.uasset | 4 +- Content/Tangibles/TangibleStaticMesh.uasset | 4 +- Content/Tangibles/tangiblecharacter.uasset | 4 +- Source/Integration/FormatError.cpp | 2 +- Source/Integration/LuaCall.cpp | 330 +++++++++++---- Source/Integration/LuaCall.h | 188 +++++++-- Source/Integration/LuaCallNode.cpp | 375 +++++------------- Source/Integration/LuaCallNode.h | 4 - Source/Integration/Tangible.cpp | 18 +- Source/Integration/Tangible.h | 5 +- luprex/lua/login.lua | 5 + 11 files changed, 530 insertions(+), 409 deletions(-) diff --git a/Content/Luprex/lxUtilityFunctionsLibrary.uasset b/Content/Luprex/lxUtilityFunctionsLibrary.uasset index 11cd6249..3fb22fda 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:71511653c686e942baa5b873dc2b29a2c1f997c3b44c9d7352e7737440aeb1b5 -size 83208 +oid sha256:a1afb3a566a2b64730bc10b3de4a8c3177a452f4d75088063de35899d5e2a2a2 +size 50214 diff --git a/Content/Tangibles/TangibleStaticMesh.uasset b/Content/Tangibles/TangibleStaticMesh.uasset index 2dc6b42a..83118783 100644 --- a/Content/Tangibles/TangibleStaticMesh.uasset +++ b/Content/Tangibles/TangibleStaticMesh.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:393dcdb54cb09fa58a75b276ef4dfce6f1065cbf4e83a211753919af74afa88d -size 226548 +oid sha256:1037fccb759217bafeb88ede1dcd0915cc7018163d0e739c40dd1b70547e6f1e +size 219598 diff --git a/Content/Tangibles/tangiblecharacter.uasset b/Content/Tangibles/tangiblecharacter.uasset index 1c5bfc5e..74a55083 100644 --- a/Content/Tangibles/tangiblecharacter.uasset +++ b/Content/Tangibles/tangiblecharacter.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46db59a0feeb2790d2fa724a7616371722e8308334271205c033272e607cb6fb -size 332344 +oid sha256:bfe3336e004d5bb19e9cffb6d3c4cf9caa90dbc597a51ee137ed301cc92f5cce +size 335772 diff --git a/Source/Integration/FormatError.cpp b/Source/Integration/FormatError.cpp index 17b4350b..5bbb1aa9 100644 --- a/Source/Integration/FormatError.cpp +++ b/Source/Integration/FormatError.cpp @@ -114,7 +114,7 @@ void UK2Node_FormatError::CreateCorrectPins() if (FindPin(DisplayDurationPinName, EGPD_Input) == nullptr) { UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum(), DisplayDurationPinName); - P->DefaultValue = TEXT("No_Display"); + P->DefaultValue = TEXT("No Show"); P->AutogeneratedDefaultValue = P->DefaultValue; } diff --git a/Source/Integration/LuaCall.cpp b/Source/Integration/LuaCall.cpp index d0b7cd03..3ca4475c 100644 --- a/Source/Integration/LuaCall.cpp +++ b/Source/Integration/LuaCall.cpp @@ -1,6 +1,7 @@ #include "LuaCall.h" #include "IntegrationGameModeBase.h" +#include "EdGraphSchema_K2.h" static void FatalBlueprintError(const TCHAR *message) { FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::FatalError, FText::FromString(FString(message))); @@ -43,6 +44,164 @@ static constexpr uint64_t ParseNameAsToken(std::string_view str) { } +///////////////////////////////////////////////////////////////// +// +// The lua function prototype parser. +// +///////////////////////////////////////////////////////////////// + + +bool FlxParsedProto::IsLiteral(const TCHAR *text) { + return ((NextToken < Tokens.Num()) && (Tokens[NextToken] == text)); +} + +bool FlxParsedProto::IsIdent() { + return ((NextToken < Tokens.Num()) && (FChar::IsAlpha(Tokens[NextToken][0]))); +} + +void FlxParsedProto::Empty() { + ErrorMessage = TEXT(""); + Tokens.Empty(); + NextToken = 0; + ClassName = TEXT(""); + FunctionName = TEXT(""); + Arguments.Empty(); + ReturnValues.Empty(); + ExtraReturnValues = false; +} + +void FlxParsedProto::Syntax() { + FString Message; + if (Tokens.Num() == 0) { + Message = TEXT("Function prototype cannot be blank"); + } + for (int i = 0; i < Tokens.Num(); i++) { + if (i == NextToken) { + Message.Append(TEXT(" ? ")); + } else { + if ((i > 0) && (FChar::IsAlpha(Tokens[i][0])) && (FChar::IsAlpha(Tokens[i-1][0]))) { + Message.Append(TEXT(" ")); + } + } + Message.Append(Tokens[i]); + } + Empty(); + ErrorMessage = Message; +} + +void FlxParsedProto::Parse(const FString &str) { + Empty(); + + // Step one: tokenize. + int offset = 0; + while (offset < str.Len()) { + TCHAR c = str[offset]; + if (FChar::IsWhitespace(c)) { + offset++; + } else if (FChar::IsAlpha(c)) { + int lo = offset; + while ((offset < str.Len()) && FChar::IsAlnum(str[offset])) offset++; + Tokens.Add(str.Mid(lo, offset-lo)); + } else if (str.Mid(offset, 3) == TEXT("...")) { + Tokens.Add(str.Mid(offset, 3)); + offset += 3; + } else if (FChar::IsPunct(c)) { + Tokens.Add(str.Mid(offset, 1)); + offset += 1; + } else { + Empty(); + ErrorMessage = FString::Printf(TEXT("%s ? %s"), *str.Mid(0, offset), *str.Mid(offset)); + return; + } + } + NextToken = 0; + + // Step two: Parse. + if (!IsLiteral(TEXT("*")) && !IsIdent()) return Syntax(); + ClassName = Tokens[NextToken++]; + if (!IsLiteral(TEXT("."))) return Syntax(); + NextToken++; + if (!IsIdent()) return Syntax(); + FunctionName = Tokens[NextToken++]; + if (!IsLiteral(TEXT("("))) return Syntax(); + NextToken++; + if (IsIdent()) { + while (true) { + if (!IsIdent()) return Syntax(); + FString Type = Tokens[NextToken++]; + if (!IsIdent()) return Syntax(); + FString Name = Tokens[NextToken++]; + Arguments.Add(Pin(Type, Name)); + if (!IsLiteral(TEXT(","))) break; + NextToken++; + } + } + if (!IsLiteral(TEXT(")"))) return Syntax(); + NextToken++; + if (IsLiteral(TEXT(":"))) { + NextToken++; + while (true) { + if (IsLiteral(TEXT("..."))) { + ExtraReturnValues = true; + NextToken++; + break; + } else if (IsIdent()) { + FString Type = Tokens[NextToken++]; + if (!IsIdent()) return Syntax(); + FString Name = Tokens[NextToken++]; + ReturnValues.Add(Pin(Type, Name)); + if (!IsLiteral(TEXT(","))) break; + NextToken++; + } else { + return Syntax(); + } + } + } + if (NextToken != Tokens.Num()) return Syntax(); +} + +///////////////////////////////////////////////////////////////// +// +// General functions of the Lua Call Library. +// +///////////////////////////////////////////////////////////////// + +UFunction *UlxLuaCallLibrary::GetArgumentPacker(const FString &Type) +{ + FString LType = Type.ToLower(); + FName PackerName(FString::Printf(TEXT("LuaCallArgument_%s"), *LType)); + return UlxLuaCallLibrary::StaticClass()->FindFunctionByName(PackerName); +} + +UFunction *UlxLuaCallLibrary::GetReturnValueUnpacker(const FString &Type) +{ + FString LType = Type.ToLower(); + FName PackerName(FString::Printf(TEXT("LuaCallReturnValue_%s"), *LType)); + return UlxLuaCallLibrary::StaticClass()->FindFunctionByName(PackerName); +} + +FString UlxLuaCallLibrary::AllFunctionsWithPrefix(const TCHAR *Prefix) +{ + FString Result; + + for ( TFieldIterator FIT ( UlxLuaCallLibrary::StaticClass(), EFieldIteratorFlags::ExcludeSuper); FIT; ++FIT) + { + UFunction* Function = *FIT; + FString Name = Function->GetName(); + if (Name.RemoveFromStart(Prefix)) + { + if (!Result.IsEmpty()) Result += TEXT(", "); + Result += Name; + } + } + return Result; +} + +///////////////////////////////////////////////////////////////// +// +// General Lua-Callable functions of the Lua Call Library. +// +///////////////////////////////////////////////////////////////// void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, const FString &fname) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); @@ -53,71 +212,6 @@ void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, con } -void UlxLuaCallLibrary::LuaCallAddStringParameter(UObject *context, const FString &pstring) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); - sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING); - sb.write_string(pstring); -} - -void UlxLuaCallLibrary::LuaCallAddNameParameter(UObject *context, const FName &pname) { - FTCHARToUTF8 utf8str(pname.ToString()); - std::string_view namestr(utf8str.Get(), utf8str.Length()); - uint64_t tokvalue = ParseNameAsToken(namestr); - if ((tokvalue == 0) && !namestr.empty()) { - FatalBlueprintError(TEXT("Names passed to lua must be short, and must contain only lowercase and digits")); - } - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); - sb.write_simple_dynamic_tag(SimpleDynamicTag::TOKEN); - sb.write_string(namestr); -} - -void UlxLuaCallLibrary::LuaCallAddFloatParameter(UObject *context, double pfloat) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); - sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); - sb.write_double(pfloat); -} - -void UlxLuaCallLibrary::LuaCallAddIntParameter(UObject *context, int value) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); - sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); - sb.write_double(value); -} - -void UlxLuaCallLibrary::LuaCallAddVectorParameter(UObject *context, const FVector &pvector) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); - sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); - sb.write_fvector(pvector); -} - -void UlxLuaCallLibrary::LuaCallAddVector2DParameter(UObject *context, const FVector2D &pvector) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); - sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); - sb.write_double(pvector.X); - sb.write_double(pvector.Y); - sb.write_double(0.0); -} - -void UlxLuaCallLibrary::LuaCallAddBooleanParameter(UObject *context, bool pbool) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); - sb.write_simple_dynamic_tag(SimpleDynamicTag::BOOLEAN); - sb.write_bool(pbool); -} - - void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); @@ -166,7 +260,85 @@ ELpxSimpleDynamicTag UlxLuaCallLibrary::LuaCallNextResultType(UObject *context) } } -FString UlxLuaCallLibrary::LuaCallGetStringResult(UObject *context) { +///////////////////////////////////////////////////////////////// +// +// Argument Packing functions +// +///////////////////////////////////////////////////////////////// + +void UlxLuaCallLibrary::LuaCallArgument_string(UObject *context, const FString &pstring) { + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); + CheckNotEmpty(sb); + sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING); + sb.write_string(pstring); +} + +void UlxLuaCallLibrary::LuaCallArgument_name(UObject *context, const FName &pname) { + FTCHARToUTF8 utf8str(pname.ToString()); + std::string_view namestr(utf8str.Get(), utf8str.Length()); + uint64_t tokvalue = ParseNameAsToken(namestr); + if ((tokvalue == 0) && !namestr.empty()) { + FatalBlueprintError(TEXT("Names passed to lua must be short, and must contain only lowercase and digits")); + } + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); + CheckNotEmpty(sb); + sb.write_simple_dynamic_tag(SimpleDynamicTag::TOKEN); + sb.write_string(namestr); +} + +void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) { + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); + CheckNotEmpty(sb); + sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); + sb.write_double(pfloat); +} + +void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) { + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); + CheckNotEmpty(sb); + sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); + sb.write_double(value); +} + +void UlxLuaCallLibrary::LuaCallArgument_vector(UObject *context, const FVector &pvector) { + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); + CheckNotEmpty(sb); + sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); + sb.write_fvector(pvector); +} + +void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector2D &pvector) { + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); + CheckNotEmpty(sb); + sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); + sb.write_double(pvector.X); + sb.write_double(pvector.Y); + sb.write_double(0.0); +} + +void UlxLuaCallLibrary::LuaCallArgument_boolean(UObject *context, bool pbool) { + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); + CheckNotEmpty(sb); + sb.write_simple_dynamic_tag(SimpleDynamicTag::BOOLEAN); + sb.write_bool(pbool); +} + + +///////////////////////////////////////////////////////////////// +// +// Return Value Unpacking functions +// +///////////////////////////////////////////////////////////////// + + +FString UlxLuaCallLibrary::LuaCallReturnValue_string(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); @@ -174,7 +346,7 @@ FString UlxLuaCallLibrary::LuaCallGetStringResult(UObject *context) { return sb.read_fstring(); } -FName UlxLuaCallLibrary::LuaCallGetNameResult(UObject *context) { +FName UlxLuaCallLibrary::LuaCallReturnValue_name(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); @@ -182,7 +354,7 @@ FName UlxLuaCallLibrary::LuaCallGetNameResult(UObject *context) { return sb.read_fname(); } -double UlxLuaCallLibrary::LuaCallGetFloatResult(UObject *context) { +double UlxLuaCallLibrary::LuaCallReturnValue_float(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); @@ -190,7 +362,15 @@ double UlxLuaCallLibrary::LuaCallGetFloatResult(UObject *context) { return sb.read_double(); } -FVector UlxLuaCallLibrary::LuaCallGetVectorResult(UObject *context) { +int UlxLuaCallLibrary::LuaCallReturnValue_int(UObject *context) { + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetResult(); + SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); + if (tag != SimpleDynamicTag::NUMBER) FatalBlueprintError(TEXT("expected lua to return a number")); + return int(sb.read_double()); +} + +FVector UlxLuaCallLibrary::LuaCallReturnValue_vector(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); @@ -198,10 +378,20 @@ FVector UlxLuaCallLibrary::LuaCallGetVectorResult(UObject *context) { return sb.read_fvector(); } -bool UlxLuaCallLibrary::LuaCallGetBooleanResult(UObject *context) { +FVector2D UlxLuaCallLibrary::LuaCallReturnValue_vector2d(UObject *context) { + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetResult(); + SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); + if (tag != SimpleDynamicTag::VECTOR) FatalBlueprintError(TEXT("expected lua to return a vector")); + FVector v = sb.read_fvector(); + return FVector2D(v.X, v.Y); +} + +bool UlxLuaCallLibrary::LuaCallReturnValue_boolean(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::BOOLEAN) FatalBlueprintError(TEXT("expected lua to return a boolean")); return sb.read_bool(); } + diff --git a/Source/Integration/LuaCall.h b/Source/Integration/LuaCall.h index 0598371a..da82627a 100644 --- a/Source/Integration/LuaCall.h +++ b/Source/Integration/LuaCall.h @@ -1,8 +1,18 @@ #pragma once #include "CoreMinimal.h" +#include "EdGraph/EdGraphPin.h" #include "LuaCall.generated.h" + +///////////////////////////////////////////////////////////////// +// +// These are the types that can actually be packed into +// an argument or return value buffer. Any other type is +// subject to conversion before packing. +// +///////////////////////////////////////////////////////////////// + UENUM(BlueprintType) enum class ELpxSimpleDynamicTag : uint8 { None, @@ -13,13 +23,88 @@ enum class ELpxSimpleDynamicTag : uint8 { Vector }; -//////////////////////////////////////////////// +///////////////////////////////////////////////////////////////// // -// This UClass is never instantiated. It exists to -// expose certain static functions to the blueprint -// library. +// This is a little parser that parses Lua function 'prototypes'. +// The prototypes look like this: // -//////////////////////////////////////////////// +// class.name(int arg1, int arg2) : int ret1, int ret2 +// +// The return values can be omitted if the function has no return +// values. Optionally, there can be one last return value which +// is just an ellipsis. The class name can be asterisk. +// +// For more information about the meaning of all this, see the docs +// for the 'Call Lua' blueprint node. +// +///////////////////////////////////////////////////////////////// + +class FlxParsedProto +{ +public: + struct Pin + { + FString Type; + FString Name; + Pin(const FString &T, const FString &N) : Type(T), Name(N) {} + }; + + FString ErrorMessage; + TArray Tokens; + int NextToken; + FString ClassName; + FString FunctionName; + TArray Arguments; + TArray ReturnValues; + bool ExtraReturnValues; + +private: + // Check the next token to see if it's exactly equal to text. + // + bool IsLiteral(const TCHAR *text); + + // Check the next token to see if it's an identifier. + // + bool IsIdent(); + + // Make a syntax error message, using the tokens. + // + void Syntax(); + + // Empty out the FlxParsedProto. + // + void Empty(); + + // Parse a function prototype. + // + void Parse(const FString &proto); + +public: + // Construct with a prototype. + // + FlxParsedProto(const FString &str) { Parse(str); } +}; + +///////////////////////////////////////////////////////////////// +// +// To make a Lua Call from inside of a blueprint, use the convenient "Call Lua" +// blueprint node. This node macroexpands into a sequence of low-level +// function calls. This library contains the low-level functions that +// "Call Lua" macroexpands into. +// +// The procedure for making a lua call using the low-level functions is as follows: +// +// * Use LuaCallBegin to put the class name and function name into the argument buffer. +// * Use LuaCallArgumentXXX to put arguments into the argument buffer. +// * Use LuaCallInvoke or LuaCallProbe to actually make the call. +// * Use LuaCallReturnValueXXX to fetch return values from the return buffer. +// +// The two buffers are basically global variables, they are part of the +// IntegrationGameModeBase. This is okay because blueprint is single-threaded. +// +// The following three libraries contain all the low-level functions. +// +///////////////////////////////////////////////////////////////// UCLASS() class INTEGRATION_API UlxLuaCallLibrary : public UObject @@ -27,30 +112,25 @@ class INTEGRATION_API UlxLuaCallLibrary : public UObject GENERATED_BODY() public: + // Get an argument packing function or a return value unpacking function. + // + static UFunction *GetArgumentPacker(const FString &Type); + static UFunction *GetReturnValueUnpacker(const FString &Type); + + // Get the types supported for arguments and return values, as a documentation string. + // + static FString AllFunctionsWithPrefix(const TCHAR *Prefix); + static FString AllKnownArgumentTypes() { return AllFunctionsWithPrefix(TEXT("LuaCallArgument_")); } + static FString AllKnownReturnValueTypes() { return AllFunctionsWithPrefix(TEXT("LuaCallReturnValue_")); } + +public: + // + // Functions that do miscellaneous things. + // + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") static void LuaCallBegin(UObject *context, const FString &ClassName, const FString &FunctionName); - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddStringParameter(UObject *context, const FString &Value); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddNameParameter(UObject *context, const FName &Value); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddFloatParameter(UObject *context, double Value); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddIntParameter(UObject *context, int Value); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddVectorParameter(UObject *context, const FVector &Value); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddVector2DParameter(UObject *context, const FVector2D &Value); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddBooleanParameter(UObject *context, bool Value); - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") static void LuaCallInvoke(UObject *context, AActor *Place); @@ -63,19 +143,55 @@ public: UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") static ELpxSimpleDynamicTag LuaCallNextResultType(UObject *context); - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static FString LuaCallGetStringResult(UObject *context); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static FName LuaCallGetNameResult(UObject *context); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static double LuaCallGetFloatResult(UObject *context); + // + // Functions that pack arguments into the call buffer. + // UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static FVector LuaCallGetVectorResult(UObject *context); + static void LuaCallArgument_string(UObject *context, const FString &Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static bool LuaCallGetBooleanResult(UObject *context); + static void LuaCallArgument_name(UObject *context, const FName &Value); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static void LuaCallArgument_float(UObject *context, double Value); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static void LuaCallArgument_int(UObject *context, int Value); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static void LuaCallArgument_vector(UObject *context, const FVector &Value); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static void LuaCallArgument_vector2d(UObject *context, const FVector2D &Value); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static void LuaCallArgument_boolean(UObject *context, bool Value); + + // + // Functions that extract return values from the return buffer. + // + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static FString LuaCallReturnValue_string(UObject *context); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static FName LuaCallReturnValue_name(UObject *context); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static double LuaCallReturnValue_float(UObject *context); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static int LuaCallReturnValue_int(UObject *context); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static FVector LuaCallReturnValue_vector(UObject *context); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static FVector2D LuaCallReturnValue_vector2d(UObject *context); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static bool LuaCallReturnValue_boolean(UObject *context); }; + diff --git a/Source/Integration/LuaCallNode.cpp b/Source/Integration/LuaCallNode.cpp index 9ccffc08..23f28dfe 100644 --- a/Source/Integration/LuaCallNode.cpp +++ b/Source/Integration/LuaCallNode.cpp @@ -91,160 +91,43 @@ static bool IsPlacePin(const UEdGraphPin *Pin) { return (Pin->PinName == PlacePinName); } -// A parser for lua function prototypes. -// -struct FlxParsedProto { - FString ErrorMessage; - TArray Tokens; - int NextToken; - FString ClassName; - FString FunctionName; - TArray Arguments; - TArray ReturnValues; - bool ExtraReturnValues; +#define LuaCallLibraryFunction(name) (UlxLuaCallLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxLuaCallLibrary, name))) - // Check the next token to see if it's exactly equal to text. - // - bool IsLiteral(const TCHAR *text); - - // Check the next token to see if it's an identifier. - // - bool IsIdent(); - - // Empty out the FlxParsedProto. - // - void Empty(); - - // Make a syntax error message, using the tokens. - // - void Syntax(); - - // Parse a function prototype. - // - void Parse(const FString &proto); - - // Construct with a prototype. - // - FlxParsedProto(const FString &str) { Parse(str); } -}; - -bool FlxParsedProto::IsLiteral(const TCHAR *text) { - return ((NextToken < Tokens.Num()) && (Tokens[NextToken] == text)); +static FEdGraphPinType GetPinType(const FProperty *Property) +{ + FEdGraphPinType PinType; + bool IsWeak; + FName PinCat, PinSubCat; + UObject *PinSubCatObj = nullptr; + UEdGraphSchema_K2::GetPropertyCategoryInfo(Property, PinCat, PinSubCat, PinSubCatObj, IsWeak); + PinType.PinCategory = PinCat; + PinType.PinSubCategory = PinSubCat; + PinType.PinSubCategoryObject = PinSubCatObj; + return PinType; } -bool FlxParsedProto::IsIdent() { - return ((NextToken < Tokens.Num()) && (FChar::IsAlpha(Tokens[NextToken][0]))); -} - -void FlxParsedProto::Empty() { - ErrorMessage = TEXT(""); - Tokens.Empty(); - NextToken = 0; - ClassName = TEXT(""); - FunctionName = TEXT(""); - Arguments.Empty(); - ReturnValues.Empty(); - ExtraReturnValues = false; -} -void FlxParsedProto::Syntax() { - FString Message; - if (Tokens.Num() == 0) { - Message = TEXT("Function prototype cannot be blank"); - } - for (int i = 0; i < Tokens.Num(); i++) { - if (i == NextToken) { - Message.Append(TEXT(" ? ")); - } else { - if ((i > 0) && (FChar::IsAlpha(Tokens[i][0])) && (FChar::IsAlpha(Tokens[i-1][0]))) { - Message.Append(TEXT(" ")); - } - } - Message.Append(Tokens[i]); - } - Empty(); - ErrorMessage = Message; -} - -void FlxParsedProto::Parse(const FString &str) { - Empty(); - - // Step one: tokenize. - int offset = 0; - while (offset < str.Len()) { - TCHAR c = str[offset]; - if (FChar::IsWhitespace(c)) { - offset++; - } else if (FChar::IsAlpha(c)) { - int lo = offset; - while ((offset < str.Len()) && FChar::IsAlnum(str[offset])) offset++; - Tokens.Add(str.Mid(lo, offset-lo)); - } else if (str.Mid(offset, 3) == TEXT("...")) { - Tokens.Add(str.Mid(offset, 3)); - offset += 3; - } else if (FChar::IsPunct(c)) { - Tokens.Add(str.Mid(offset, 1)); - offset += 1; - } else { - Empty(); - ErrorMessage = FString::Printf(TEXT("%s ? %s"), *str.Mid(0, offset), *str.Mid(offset)); - return; - } - } - NextToken = 0; - - // Step two: Parse. - if (!IsLiteral(TEXT("function"))) return Syntax(); - NextToken++; - if (!IsLiteral(TEXT("*")) && !IsIdent()) return Syntax(); - ClassName = Tokens[NextToken++]; - if (!IsLiteral(TEXT("."))) return Syntax(); - NextToken++; - if (!IsIdent()) return Syntax(); - FunctionName = Tokens[NextToken++]; - if (!IsLiteral(TEXT("("))) return Syntax(); - NextToken++; - if (IsIdent()) { - while (true) { - if (!IsIdent()) return Syntax(); - Arguments.Add(Tokens[NextToken++]); - if (!IsLiteral(TEXT(","))) break; - NextToken++; - } - } - if (!IsLiteral(TEXT(")"))) return Syntax(); - NextToken++; - if (IsLiteral(TEXT(":"))) { - NextToken++; - while (true) { - if (IsLiteral(TEXT("..."))) { - ExtraReturnValues = true; - NextToken++; - break; - } else if (IsIdent()) { - ReturnValues.Add(Tokens[NextToken++]); - if (!IsLiteral(TEXT(","))) break; - NextToken++; - } else { - return Syntax(); - } - } - } - if (NextToken != Tokens.Num()) return Syntax(); -} UK2Node_LuaCall::UK2Node_LuaCall(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - NodeTooltip = LOCTEXT("NodeTooltip", - "Probe or Invoke a Lua function.\n" + FString ArgTypes = UlxLuaCallLibrary::AllKnownArgumentTypes(); + FString RetTypes = UlxLuaCallLibrary::AllKnownReturnValueTypes(); + NodeTooltip = FText::FromString(FString::Printf(TEXT( + "Call a Lua function.\n" "\n" "The lua function prototype must be a hardwired string which must look like\n" - "one of the following:" + "one of the following:\n" "\n" - " function cname.fname(arg1, arg2)" - " function cname.fname(arg1, arg2) : ret1, ret2\n" - " function cname.fname(arg1, arg2) : ret1, ret2, ...\n" + " classname.funcname(int arg1, int arg2)\n" + " classname.funcname(int arg1, int arg2) : int ret1, int ret2\n" + " classname.funcname(int arg1, int arg2) : int ret1, int ret2, ...\n" + "\n" + "You must specify types for the argument and return value pins. The:\n" + "types that you may specify are:\n" + "\n" + "Arguments: %s\n" + "Return Values: %s\n" "\n" "The prototype is parsed to determine what lua function to call.\n" "The lua call node will automatically add pins for the arguments and\n" @@ -255,12 +138,7 @@ UK2Node_LuaCall::UK2Node_LuaCall(const FObjectInitializer& ObjectInitializer) "\n" "Optionally, you may use the * wildcard for the classname. In that\n" "case, the class of the 'place' tangible will be used.\n" - "\n" - "Argument and return value pins have wildcard types initially, you can\n" - "hook them to inputs and outputs of the following types:\n" - "\n" - " string, name, float, boolean, vector\n" - "\n"); + "\n"), *ArgTypes, *RetTypes)); } void UK2Node_LuaCall::AllocateDefaultPins() @@ -273,7 +151,7 @@ void UK2Node_LuaCall::CreateCorrectPins() { if (LuaFunctionPrototype.IsEmpty()) { - LuaFunctionPrototype = TEXT("function class.func(arg1, arg2) : ret1, ret2"); + LuaFunctionPrototype = TEXT("class.func(int arg1, int arg2) : int ret1, int ret2"); } if (FindPin(UEdGraphSchema_K2::PN_Execute) == nullptr) { @@ -325,28 +203,44 @@ void UK2Node_LuaCall::CreateCorrectPins() } // Create Argument pins in the correct order, reusing old pins where possible. - for (const FString& Name : ParsedProto.Arguments) + for (const FlxParsedProto::Pin & Pin : ParsedProto.Arguments) { - FName PrefixedName = ArgumentNameAddPrefix(Name); + FName PrefixedName = ArgumentNameAddPrefix(Pin.Name); + UFunction *Accessor = UlxLuaCallLibrary::GetArgumentPacker(Pin.Type); + if (Accessor == nullptr) { + bHasCompilerMessage = true; + ErrorType = EMessageSeverity::Error; + ErrorMsg = FString::Printf(TEXT("Unknown argument type: %s"), *Pin.Type); + continue; + } + FEdGraphPinType PinType = GetPinType(Accessor->FindPropertyByName(TEXT("Value"))); UEdGraphPin **OldPin = OldArgumentPins.Find(PrefixedName); - if (OldPin != nullptr) { + if ((OldPin != nullptr) && ((*OldPin)->PinType == PinType)) { Pins.Emplace(*OldPin); OldArgumentPins.Remove(PrefixedName); } else { - CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, PrefixedName); + CreatePin(EGPD_Input, PinType, PrefixedName); } } // Create ReturnValue pins in the correct order, reusing old pins where possible. - for (const FString& Name : ParsedProto.ReturnValues) + for (const FlxParsedProto::Pin & Pin : ParsedProto.ReturnValues) { - FName PrefixedName = ReturnValueNameAddPrefix(Name); + FName PrefixedName = ReturnValueNameAddPrefix(Pin.Name); + UFunction *Accessor = UlxLuaCallLibrary::GetReturnValueUnpacker(Pin.Type); + if (Accessor == nullptr) { + bHasCompilerMessage = true; + ErrorType = EMessageSeverity::Error; + ErrorMsg = FString::Printf(TEXT("Unknown return value type: %s"), *Pin.Type); + continue; + } + FEdGraphPinType PinType = GetPinType(Accessor->GetReturnProperty()); UEdGraphPin **OldPin = OldReturnValuePins.Find(PrefixedName); - if (OldPin != nullptr) { + if ((OldPin != nullptr) && ((*OldPin)->PinType == PinType)) { Pins.Emplace(*OldPin); OldReturnValuePins.Remove(PrefixedName); } else { - CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Wildcard, PrefixedName); + CreatePin(EGPD_Output, PinType, PrefixedName); } } @@ -366,53 +260,9 @@ void UK2Node_LuaCall::CreateCorrectPins() } -void UK2Node_LuaCall::SynchronizePinType(UEdGraphPin* Pin) -{ - if (IsArgumentPin(Pin) || IsReturnValuePin(Pin)) - { - const UEdGraphSchema_K2* K2Schema = Cast(GetSchema()); - - bool bPinTypeChanged = false; - if (Pin->LinkedTo.Num() == 0) - { - static const FEdGraphPinType WildcardPinType = FEdGraphPinType(UEdGraphSchema_K2::PC_Wildcard, NAME_None, nullptr, EPinContainerType::None, false, FEdGraphTerminalType()); - - // Ensure wildcard - if (Pin->PinType != WildcardPinType) - { - Pin->PinType = WildcardPinType; - bPinTypeChanged = true; - } - } - else - { - UEdGraphPin* OtherPin = Pin->LinkedTo[0]; - - // Take the type of the connected pin - if (Pin->PinType != OtherPin->PinType) - { - Pin->PinType = OtherPin->PinType; - bPinTypeChanged = true; - } - } - - if (bPinTypeChanged) - { - // Let the graph know to refresh - GetGraph()->NotifyNodeChanged(this); - - UBlueprint* Blueprint = GetBlueprint(); - if (!Blueprint->bBeingCompiled) - { - FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); - } - } - } -} - FText UK2Node_LuaCall::GetNodeTitle(ENodeTitleType::Type TitleType) const { - return LOCTEXT("LuaCall_Title", "Probe or Invoke a Lua Function"); + return LOCTEXT("LuaCall_Title", "Call a Lua Function"); } FText UK2Node_LuaCall::GetPinDisplayName(const UEdGraphPin* Pin) const @@ -455,7 +305,6 @@ void UK2Node_LuaCall::PostEditChangeProperty(struct FPropertyChangedEvent& Prope void UK2Node_LuaCall::PinConnectionListChanged(UEdGraphPin* Pin) { Modify(); - SynchronizePinType(Pin); } void UK2Node_LuaCall::PinDefaultValueChanged(UEdGraphPin* Pin) @@ -468,12 +317,6 @@ void UK2Node_LuaCall::PinDefaultValueChanged(UEdGraphPin* Pin) } } -void UK2Node_LuaCall::PinTypeChanged(UEdGraphPin* Pin) -{ - SynchronizePinType(Pin); - Super::PinTypeChanged(Pin); -} - FText UK2Node_LuaCall::GetTooltipText() const { return NodeTooltip; @@ -482,55 +325,11 @@ FText UK2Node_LuaCall::GetTooltipText() const void UK2Node_LuaCall::PostReconstructNode() { Super::PostReconstructNode(); - - UEdGraph* OuterGraph = GetGraph(); - if (!IsTemplate() && OuterGraph && OuterGraph->Schema) { - for (UEdGraphPin* CurrentPin : Pins) - { - SynchronizePinType(CurrentPin); - } - } - CreateCorrectPins(); } #define LuaCallLibraryFunction(name) (UlxLuaCallLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxLuaCallLibrary, name))) -static UFunction *GetArgumentPackingFunction(const FEdGraphPinType &Type) -{ - if (Type.PinCategory == UEdGraphSchema_K2::PC_Real) - { - return LuaCallLibraryFunction(LuaCallAddFloatParameter); - } - if (Type.PinCategory == UEdGraphSchema_K2::PC_Int) - { - return LuaCallLibraryFunction(LuaCallAddIntParameter); - } - else if (Type.PinCategory == UEdGraphSchema_K2::PC_Boolean) - { - return LuaCallLibraryFunction(LuaCallAddBooleanParameter); - } - else if (Type.PinCategory == UEdGraphSchema_K2::PC_Name) - { - return LuaCallLibraryFunction(LuaCallAddNameParameter); - } - else if (Type.PinCategory == UEdGraphSchema_K2::PC_String) - { - return LuaCallLibraryFunction(LuaCallAddStringParameter); - } - else if ((Type.PinCategory == UEdGraphSchema_K2::PC_Struct) && (Type.PinSubCategoryObject == TBaseStructure::Get())) - { - return LuaCallLibraryFunction(LuaCallAddVectorParameter); - } - else if ((Type.PinCategory == UEdGraphSchema_K2::PC_Struct) && (Type.PinSubCategoryObject == TBaseStructure::Get())) - { - return LuaCallLibraryFunction(LuaCallAddVector2DParameter); - } - else - { - return nullptr; - } -} void UK2Node_LuaCall::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { @@ -555,38 +354,56 @@ void UK2Node_LuaCall::ExpandNode(class FKismetCompilerContext& CompilerContext, UK2Node_CallFunction* PrevNode = BeginNode; // Add Packing operations for all argument pins. - for (UEdGraphPin* Pin : Pins) + for (const FlxParsedProto::Pin &PinInfo : ParsedProto.Arguments) { - if (IsArgumentPin(Pin)) + UEdGraphPin *Pin = FindPinChecked(ArgumentNameAddPrefix(PinInfo.Name)); + UFunction *PackingFunc = UlxLuaCallLibrary::GetArgumentPacker(PinInfo.Type); + if (PackingFunc == nullptr) { - UFunction *PackingFunc = GetArgumentPackingFunction(Pin->PinType); - if (PackingFunc != nullptr) - { - UK2Node_CallFunction *PackNode = MakeCallFunctionNode(PackingFunc); - CompilerContext.MovePinLinksToIntermediate(*Pin, *PackNode->FindPinChecked(TEXT("Value"))); - PrevNode->GetThenPin()->MakeLinkTo(PackNode->GetExecPin()); - PrevNode = PackNode; - } - else - { - FText PinName = GetPinDisplayName(Pin); - FText PinType = FText::FromName(Pin->PinType.PinCategory); - FText Error = FText::Format(LOCTEXT("Error_UnexpectedPinType", "Pin '{0}' has an unexpected type: {1}"), PinName, PinType); - CompilerContext.MessageLog.Error(*Error.ToString()); - } + // This codepath should be unreachable, but just in case. + CompilerContext.MessageLog.Error(TEXT("All argument pins must have known types")); + continue; } + UK2Node_CallFunction *PackNode = MakeCallFunctionNode(PackingFunc); + CompilerContext.MovePinLinksToIntermediate(*Pin, *PackNode->FindPinChecked(TEXT("Value"))); + PrevNode->GetThenPin()->MakeLinkTo(PackNode->GetExecPin()); + PrevNode = PackNode; } // Add the invoke or probe node. - bool IsInvoke = (FindPin(InvokeOrProbePinName, EGPD_Input)->DefaultValue == TEXT("Invoke")); + bool IsInvoke = (FindPinChecked(InvokeOrProbePinName, EGPD_Input)->DefaultValue == TEXT("Invoke")); UFunction *Action = IsInvoke ? LuaCallLibraryFunction(LuaCallInvoke) : LuaCallLibraryFunction(LuaCallProbe); UK2Node_CallFunction* ActionNode = MakeCallFunctionNode(Action); CompilerContext.MovePinLinksToIntermediate(*FindPin(PlacePinName, EGPD_Input), *ActionNode->FindPinChecked(TEXT("Place"))); PrevNode->GetThenPin()->MakeLinkTo(ActionNode->GetExecPin()); + PrevNode = ActionNode; + + // Add Unpacking operations for all argument pins. + for (const FlxParsedProto::Pin &PinInfo : ParsedProto.ReturnValues) + { + UEdGraphPin *Pin = FindPinChecked(ReturnValueNameAddPrefix(PinInfo.Name)); + UFunction *UnpackingFunc = UlxLuaCallLibrary::GetReturnValueUnpacker(PinInfo.Type); + if (UnpackingFunc == nullptr) + { + // This codepath should be unreachable, but just in case. + CompilerContext.MessageLog.Error(TEXT("All return value pins must have known types.")); + continue; + } + UK2Node_CallFunction *UnpackNode = MakeCallFunctionNode(UnpackingFunc); + CompilerContext.MovePinLinksToIntermediate(*Pin, *UnpackNode->FindPinChecked(UEdGraphSchema_K2::PN_ReturnValue)); + PrevNode->GetThenPin()->MakeLinkTo(UnpackNode->GetExecPin()); + PrevNode = UnpackNode; + } + + // Make sure we didn't have return values for an invoke. + if (IsInvoke && (ParsedProto.ReturnValues.Num() > 0)) + { + CompilerContext.MessageLog.Error(TEXT("Lua Call in Invoke mode does not support return values")); + } // Link up the Exec pins. CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *BeginNode->GetExecPin()); - CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *ActionNode->GetThenPin()); + CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *PrevNode->GetThenPin()); BreakAllNodeLinks(); } @@ -653,21 +470,9 @@ bool UK2Node_LuaCall::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEd return true; } - // Argument input pins may only be connected to packable types. - if (IsArgumentPin(MyPin)) - { - UFunction *Packer = GetArgumentPackingFunction(OtherPin->PinType); - if (Packer == nullptr) - { - OutReason = LOCTEXT("Error_InvalidArgumentType", "Lua Call Arguments may be float, boolean, string, name, or vector.").ToString(); - return true; - } - } - return Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason); } - void UK2Node_LuaCall::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const { // actions get registered under specific object-keys; the idea is that diff --git a/Source/Integration/LuaCallNode.h b/Source/Integration/LuaCallNode.h index a53b0fa7..70739f8f 100644 --- a/Source/Integration/LuaCallNode.h +++ b/Source/Integration/LuaCallNode.h @@ -52,7 +52,6 @@ class UK2Node_LuaCall : public UK2Node virtual bool ShouldShowNodeProperties() const override { return true; } virtual void PinConnectionListChanged(UEdGraphPin* Pin) override; virtual void PinDefaultValueChanged(UEdGraphPin* Pin) override; - virtual void PinTypeChanged(UEdGraphPin* Pin) override; virtual FText GetTooltipText() const override; virtual FText GetPinDisplayName(const UEdGraphPin* Pin) const override; //~ End UEdGraphNode Interface. @@ -73,9 +72,6 @@ private: /** Create all necessary pins. */ void CreateCorrectPins(); - /** Synchronize the type of the given pin with the type its connected to, or reset it to a wildcard pin if there's no connection */ - void SynchronizePinType(UEdGraphPin* Pin); - private: /** The lua function prototype, which must be saved as a property **/ UPROPERTY() diff --git a/Source/Integration/Tangible.cpp b/Source/Integration/Tangible.cpp index 2b73e44e..eed894b5 100644 --- a/Source/Integration/Tangible.cpp +++ b/Source/Integration/Tangible.cpp @@ -153,12 +153,22 @@ void UlxTangible::GetCurrentAnimation(AActor *target, FlxAnimationStep &step) { step = GetActorTangible(target)->AnimTracker.GetCurrentAnimation(); } -void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step, bool autoxyz, bool autofacing, bool autoplane) { +void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step, bool AutoUpdate) { + if (target == nullptr) + { + UE_LOG(LogBlueprint, Error, TEXT("In FinishedAnimation, tangible cannot be null")); + return; + } UlxTangible *tan = GetActorTangible(target); - if (autoxyz) step.AutoUpdateXYZ(target); - if (autofacing) step.AutoUpdateFacing(target); - if (autoplane) step.AutoUpdatePlane(&(tan->Plane)); + if (AutoUpdate) + { + step.AutoUpdateXYZ(target); + step.AutoUpdateFacing(target); + step.AutoUpdatePlane(&(tan->Plane)); + } tan->AnimTracker.FinishedAnimation(step.Hash); + FString DebugString = UlxAnimationStepLibrary::AnimationStepDebugString(step); + UE_LOG(LogBlueprint, Display, TEXT("Animation Finished: %s"), *DebugString); } FString UlxTangible::GetTangiblePlane(AActor* target) { diff --git a/Source/Integration/Tangible.h b/Source/Integration/Tangible.h index 878bd529..d30b302c 100644 --- a/Source/Integration/Tangible.h +++ b/Source/Integration/Tangible.h @@ -131,9 +131,8 @@ public: UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Animation Queue") static void GetCurrentAnimation(AActor *target, FlxAnimationStep &step); - UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Animation Queue") - static void FinishedAnimation(AActor *target, const FlxAnimationStep &step, - bool AutoUpdateXYZ = true, bool AutoUpdateFacing = true, bool AutoUpdatePlane = true); + UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "Target"), Category = "Luprex|Animation Queue") + static void FinishedAnimation(AActor *Target, const FlxAnimationStep &Step, bool AutoUpdate = true); UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Animation Queue") static void SetAutoFinish(AActor *target, const FString &action, const FVector &xyz); diff --git a/luprex/lua/login.lua b/luprex/lua/login.lua index 8196bdaf..ff961260 100644 --- a/luprex/lua/login.lua +++ b/luprex/lua/login.lua @@ -26,3 +26,8 @@ end function engio.printhi(a1, a2, a3, a4, a5) pprint("Hi there", a1, a2, a3, a4, a5) end + +function engio.retthree(a1, a2) + return { 5, {3, 4, 5}, "Howdy" } +end +