227 lines
7.3 KiB
C++
227 lines
7.3 KiB
C++
|
|
|
||
|
|
|
||
|
|
#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<ParsingStep>& Steps)
|
||
|
|
{
|
||
|
|
TArray<FString> 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<FName> 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<ParsingStep> 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<ParsingStep> 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
|