A lot of work on the Lua Call interface and some work on animation queues
This commit is contained in:
@@ -114,7 +114,7 @@ void UK2Node_FormatError::CreateCorrectPins()
|
||||
|
||||
if (FindPin(DisplayDurationPinName, EGPD_Input) == nullptr) {
|
||||
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxErrorDisplayDuration>(), DisplayDurationPinName);
|
||||
P->DefaultValue = TEXT("No_Display");
|
||||
P->DefaultValue = TEXT("No Show");
|
||||
P->AutogeneratedDefaultValue = P->DefaultValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<UFunction> 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<FString> Tokens;
|
||||
int NextToken;
|
||||
FString ClassName;
|
||||
FString FunctionName;
|
||||
TArray<Pin> Arguments;
|
||||
TArray<Pin> 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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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<FString> Tokens;
|
||||
int NextToken;
|
||||
FString ClassName;
|
||||
FString FunctionName;
|
||||
TArray<FString> Arguments;
|
||||
TArray<FString> 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<const UEdGraphSchema_K2>(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<FVector>::Get()))
|
||||
{
|
||||
return LuaCallLibraryFunction(LuaCallAddVectorParameter);
|
||||
}
|
||||
else if ((Type.PinCategory == UEdGraphSchema_K2::PC_Struct) && (Type.PinSubCategoryObject == TBaseStructure<FVector2D>::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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user