#include "ReadLuaValues.h" #include "BlueprintActionDatabaseRegistrar.h" #include "BlueprintNodeSpawner.h" #include "EdGraphSchema_K2.h" #include "K2Node_CallFunction.h" #include "KismetCompiler.h" #include "LuaCall.h" #define LOCTEXT_NAMESPACE "ReadLuaValues" const FName UK2Node_ReadLuaValues::PrototypePinName(TEXT("Prototype")); const FName UK2Node_ReadLuaValues::InputValuesPinName(TEXT("Input Values")); const FName UK2Node_ReadLuaValues::RemainingPinName(TEXT("Remaining")); const FName UK2Node_ReadLuaValues::ErrorPinName(TEXT("Error")); FText UK2Node_ReadLuaValues::GetTooltipText() const { static FText Tooltip = FText::FromString(FString::Printf(TEXT( "Read typed values from a Lua Values array.\n" "\n" "The value prototype must be a hardwired string listing the\n" "types and names of the values to read, for example:\n" "\n" " string x, float y, int z\n" "\n" "If you add '...' at the end, any remaining values will\n" "be available through the Remaining output pin.\n" "\n" "Supported types: %s\n"), *UlxLuaCallLibrary::AllKnownReturnValueTypes())); return Tooltip; } void UK2Node_ReadLuaValues::ReconstructNode() { // Save the value of the Prototype Pin before it gets reconstructed. UEdGraphPin* PrototypePin = FindPin(PrototypePinName); if (PrototypePin != nullptr) { ValuePrototype = PrototypePin->DefaultValue; } Super::ReconstructNode(); } void UK2Node_ReadLuaValues::AllocateDefaultPins() { Pins.Reset(); Super::AllocateDefaultPins(); // Parse the value prototype string. FlxParsedProto ParsedProto = FlxParsedProto::ParseReturnValuesOnly(ValuePrototype); if (!ParsedProto.ErrorMessage.IsEmpty()) { SetErrorMsg(FString::Printf(TEXT("Syntax error in value prototype: %s"), *ParsedProto.ErrorMessage)); } CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute); CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then); CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, ErrorPinName); UEdGraphPin *PrototypePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_String, PrototypePinName); PrototypePin->DefaultValue = ValuePrototype; CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, UlxLuaValues::StaticClass(), InputValuesPinName); // Create output pins for each value. for (const FlxParsedProto::Pin & Pin : ParsedProto.ReturnValues) { FName PrefixedName = AddPrefix(Pin.Name, 'R'); UFunction *Accessor = UlxLuaCallLibrary::GetReturnValueUnpacker(Pin.Type); if (Accessor == nullptr) { SetErrorMsg(FString::Printf(TEXT("Unknown value type: %s"), *Pin.Type)); continue; } CreatePin(EGPD_Output, PropertyToPinType(Accessor->FindPropertyByName(TEXT("Result"))), PrefixedName); } if (ParsedProto.ExtraReturnValues) { CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Object, UlxLuaValues::StaticClass(), RemainingPinName); } } FText UK2Node_ReadLuaValues::GetNodeTitle(ENodeTitleType::Type TitleType) const { return LOCTEXT("ReadLuaValues_Title", "Read Lua Values"); } FText UK2Node_ReadLuaValues::GetPinDisplayName(const UEdGraphPin* Pin) const { // These pins don't need labels. if ((Pin->PinName == UEdGraphSchema_K2::PN_Execute) || (Pin->PinName == UEdGraphSchema_K2::PN_Then) || (Pin->PinName == PrototypePinName)) { return FText::GetEmpty(); } // Return the pin name, removing R: prefix if present. return FText::FromName(RemovePrefix(Pin->PinName)); } void UK2Node_ReadLuaValues::PinDefaultValueChanged(UEdGraphPin* Pin) { if ((Pin->PinName == PrototypePinName) && (Pin->DefaultValue != ValuePrototype)) { ReconstructNode(); } } void UK2Node_ReadLuaValues::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); FlxParsedProto ParsedProto = FlxParsedProto::ParseReturnValuesOnly(ValuePrototype); UEdGraphPin *InputInputValuesCopyPin = FindPinChecked(InputValuesPinName); // Save the cursor so we can restore it on error. // SaveCursor returns the UlxLuaValues*, which we use as the // intermediate pin for all subsequent nodes. UFunction *SaveCursorFunc = UlxLuaValues::StaticClass()->FindFunctionByName(TEXT("SaveCursor")); UK2Node_CallFunction *SaveCursorNode = MakeCallFunctionNode(CompilerContext, SourceGraph, SaveCursorFunc); CompilerContext.MovePinLinksToIntermediate(*InputInputValuesCopyPin, *SaveCursorNode->FindPinChecked(UEdGraphSchema_K2::PN_Self)); CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *SaveCursorNode->GetExecPin()); UEdGraphPin *InputValuesCopyPin = SaveCursorNode->GetReturnValuePin(); UEdGraphPin *ThenPin = SaveCursorNode->GetThenPin(); // The Read functions automatically restore the cursor on failure, // so we just need a pin to wire WrongType outputs to. UEdGraphPin *ErrorExecPin = FindPinChecked(ErrorPinName); // Add Unpacking operations for all output pins. for (const FlxParsedProto::Pin &PinInfo : ParsedProto.ReturnValues) { UEdGraphPin *Pin = FindPinChecked(AddPrefix(PinInfo.Name, 'R')); UFunction *UnpackingFunc = UlxLuaCallLibrary::GetReturnValueUnpacker(PinInfo.Type); if (UnpackingFunc == nullptr) { CompilerContext.MessageLog.Error(TEXT("All value pins must have known types.")); continue; } UK2Node_CallFunction *UnpackNode = MakeCallFunctionNode(CompilerContext, SourceGraph, UnpackingFunc); InputValuesCopyPin->MakeLinkTo(UnpackNode->FindPinChecked(UEdGraphSchema_K2::PN_Self)); CompilerContext.CopyPinLinksToIntermediate(*ErrorExecPin, *UnpackNode->FindPinChecked(TEXT("WrongType"))); CompilerContext.MovePinLinksToIntermediate(*Pin, *UnpackNode->FindPinChecked(TEXT("Result"))); ThenPin = ChainExecPin(ThenPin, UnpackNode, TEXT("Success")); } // If there is a Remaining output pin, pass through the LuaValues object. // The cursor is already past the consumed values. if (ParsedProto.ExtraReturnValues) { UEdGraphPin *RemainingPin = FindPinChecked(RemainingPinName); CompilerContext.MovePinLinksToIntermediate(*RemainingPin, *InputValuesCopyPin); } // Link up the output exec pin. CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *ThenPin); BreakAllNodeLinks(); } UK2Node::ERedirectType UK2Node_ReadLuaValues::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const { if (IsTemplate() || (GetGraph() == nullptr)) return ERedirectType_None; if ((NewPin->PinName == OldPin->PinName) && (NewPin->Direction == OldPin->Direction) && (NewPin->PinType == OldPin->PinType)) { return ERedirectType_Name; } return ERedirectType_None; } bool UK2Node_ReadLuaValues::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const { // The prototype pin cannot be connected. if (MyPin->PinName == PrototypePinName) { OutReason = LOCTEXT("Error_PrototypeMustBeHardwired", "Value prototype must be a hardwired constant.").ToString(); return true; } return Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason); } void UK2Node_ReadLuaValues::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const { UClass* ActionKey = GetClass(); if (ActionRegistrar.IsOpenForRegistration(ActionKey)) { UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); check(NodeSpawner != nullptr); ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner); } } FText UK2Node_ReadLuaValues::GetMenuCategory() const { return FText::FromString(FString(TEXT("Luprex|Lua"))); } #undef LOCTEXT_NAMESPACE