#include "ReadSlashCommand.h" #include "BlueprintActionDatabaseRegistrar.h" #include "BlueprintNodeSpawner.h" #include "EdGraphSchema_K2.h" #include "K2Node_CallFunction.h" #include "KismetCompiler.h" #include "LuaCall.h" #include "SlashCommand.h" #define LOCTEXT_NAMESPACE "ReadSlashCommand" const FName UK2Node_ReadSlashCommand::PrototypePinName(TEXT("Prototype")); const FName UK2Node_ReadSlashCommand::InputValuesPinName(TEXT("Input Values")); const FName UK2Node_ReadSlashCommand::ErrorPinName(TEXT("Error")); bool UK2Node_ReadSlashCommand::ParsePrototype(const FString &Prototype, TArray& Steps) { TArray Words; Prototype.ParseIntoArrayWS(Words); // The command name must be a slash followed by alphanumerics. if (Words.Num() == 0 || !UlxSlashCommand::IsSlashCommand(Words[0])) { SetErrorMsg(TEXT("The prototype must start with a command name, e.g. \"/foo\".")); Steps.Empty(); return false; } Steps.SetNum(Words.Num()); Steps[0].Word = Words[0]; TSet UsedNames; for (int32 i = 1; i < Words.Num(); i++) { const FString& Word = Words[i]; FString FuncName = FString(TEXT("Read")) + Word; UFunction* ReadFunc = UlxSlashCommand::StaticClass()->FindFunctionByName(FName(*FuncName)); if (ReadFunc == nullptr) { SetErrorMsg(FString::Printf(TEXT("Unknown value type: %s"), *Word)); Steps.Empty(); return false; } FName PinName = AddPrefix(Word, 'R'); for (int Suffix = 2; UsedNames.Contains(PinName); Suffix++) { PinName = AddPrefix(FString::Printf(TEXT("%s%d"), *Word, Suffix), 'R'); } Steps[i].Word = Word; Steps[i].PinName = PinName; Steps[i].ReadFunction = ReadFunc; UsedNames.Add(PinName); } return true; } FText UK2Node_ReadSlashCommand::GetTooltipText() const { static FText Tooltip = FText::FromString(TEXT( "Parse a slash command.\n" "\n" "The prototype must be a hardwired string. The first word\n" "is the command name; each remaining word names a value to\n" "read and becomes an output pin.\n" "\n" "For example:\n" "\n" " /command Integer Float\n" "\n" "Supported types: Token, Integer, Float, Rest\n")); return Tooltip; } void UK2Node_ReadSlashCommand::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_ReadSlashCommand::AllocateDefaultPins() { Pins.Reset(); Super::AllocateDefaultPins(); TArray Steps; ParsePrototype(ValuePrototype, Steps); 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, UlxSlashCommand::StaticClass(), InputValuesPinName); // Create an output pin for each value word. for (int32 i = 1; i < Steps.Num(); i++) { // The value comes back through the function's "Result" out-parameter. CreatePin(EGPD_Output, PropertyToPinType(Steps[i].ReadFunction->FindPropertyByName(TEXT("Result"))), Steps[i].PinName); } } FText UK2Node_ReadSlashCommand::GetNodeTitle(ENodeTitleType::Type TitleType) const { return LOCTEXT("ReadSlashCommand_Title", "Read Slash Command"); } FText UK2Node_ReadSlashCommand::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_ReadSlashCommand::PinDefaultValueChanged(UEdGraphPin* Pin) { if ((Pin->PinName == PrototypePinName) && (Pin->DefaultValue != ValuePrototype)) { ReconstructNode(); } } void UK2Node_ReadSlashCommand::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); TArray Steps; if (!ParsePrototype(ValuePrototype, Steps)) { CompilerContext.MessageLog.Error(*ErrorMsg); BreakAllNodeLinks(); return; } UEdGraphPin *InputSlashCommandPin = FindPinChecked(InputValuesPinName); UEdGraphPin *ErrorExecPin = FindPinChecked(ErrorPinName); UFunction *CheckCommandFunc = UlxSlashCommand::StaticClass()->FindFunctionByName(TEXT("CheckCommand")); UK2Node_CallFunction *CheckCommandNode = MakeCallFunctionNode(CompilerContext, SourceGraph, CheckCommandFunc); CompilerContext.CopyPinLinksToIntermediate(*InputSlashCommandPin, *CheckCommandNode->FindPinChecked(UEdGraphSchema_K2::PN_Self)); CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *CheckCommandNode->GetExecPin()); CheckCommandNode->FindPinChecked(TEXT("Literal"))->DefaultValue = Steps[0].Word; CheckCommandNode->FindPinChecked(TEXT("Prototype"))->DefaultValue = ValuePrototype; CompilerContext.CopyPinLinksToIntermediate(*ErrorExecPin, *CheckCommandNode->FindPinChecked(TEXT("Error"))); UEdGraphPin *ThenPin = CheckCommandNode->FindPinChecked(TEXT("Success")); for (int32 i = 1; i < Steps.Num(); i++) { UK2Node_CallFunction *ReadNode = MakeCallFunctionNode(CompilerContext, SourceGraph, Steps[i].ReadFunction); CompilerContext.CopyPinLinksToIntermediate(*InputSlashCommandPin, *ReadNode->FindPinChecked(UEdGraphSchema_K2::PN_Self)); CompilerContext.CopyPinLinksToIntermediate(*ErrorExecPin, *ReadNode->FindPinChecked(TEXT("Error"))); UEdGraphPin *OutputPin = FindPinChecked(Steps[i].PinName); CompilerContext.MovePinLinksToIntermediate(*OutputPin, *ReadNode->FindPinChecked(TEXT("Result"))); ThenPin = ChainExecPin(ThenPin, ReadNode, TEXT("Success")); } CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *ThenPin); BreakAllNodeLinks(); } UK2Node::ERedirectType UK2Node_ReadSlashCommand::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_ReadSlashCommand::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_ReadSlashCommand::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_ReadSlashCommand::GetMenuCategory() const { return FText::FromString(FString(TEXT("Luprex|Slash Commands"))); } #undef LOCTEXT_NAMESPACE