Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_ForEachElementInEnum.cpp

236 lines
9.5 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "BlueprintGraphPrivatePCH.h"
#include "KismetCompiler.h"
#include "Kismet/KismetNodeHelperLibrary.h"
#include "Kismet/KismetMathLibrary.h"
#include "K2Node_CastByteToEnum.h"
#include "K2Node_ForEachElementInEnum.h"
#include "K2Node_GetNumEnumEntries.h"
#include "BlueprintNodeSpawner.h"
#include "EditorCategoryUtils.h"
#define LOCTEXT_NAMESPACE "K2Node"
struct FForExpandNodeHelper
{
UEdGraphPin* StartLoopExecInPin;
UEdGraphPin* InsideLoopExecOutPin;
UEdGraphPin* LoopCompleteOutExecPin;
UEdGraphPin* IndexOutPin;
// for(Index = 0; Index < IndexLimit; ++Index)
UEdGraphPin* IndexLimitInPin;
FForExpandNodeHelper()
: StartLoopExecInPin(NULL)
, InsideLoopExecOutPin(NULL)
, LoopCompleteOutExecPin(NULL)
, IndexOutPin(NULL)
, IndexLimitInPin(NULL)
{ }
bool BuildLoop(UK2Node* Node, FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
check(Node && SourceGraph && Schema);
bool bResult = true;
// Create int Iterator
UK2Node_TemporaryVariable* IndexNode = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(Node, SourceGraph);
IndexNode->VariableType.PinCategory = Schema->PC_Int;
IndexNode->AllocateDefaultPins();
IndexOutPin = IndexNode->GetVariablePin();
check(IndexOutPin);
// Initialize iterator
UK2Node_AssignmentStatement* IteratorInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(Node, SourceGraph);
IteratorInitialize->AllocateDefaultPins();
IteratorInitialize->GetValuePin()->DefaultValue = TEXT("0");
bResult &= Schema->TryCreateConnection(IndexOutPin, IteratorInitialize->GetVariablePin());
StartLoopExecInPin = IteratorInitialize->GetExecPin();
check(StartLoopExecInPin);
// Do loop branch
UK2Node_IfThenElse* Branch = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(Node, SourceGraph);
Branch->AllocateDefaultPins();
bResult &= Schema->TryCreateConnection(IteratorInitialize->GetThenPin(), Branch->GetExecPin());
LoopCompleteOutExecPin = Branch->GetElsePin();
check(LoopCompleteOutExecPin);
// Do loop condition
UK2Node_CallFunction* Condition = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(Node, SourceGraph);
Condition->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(TEXT("Less_IntInt")));
Condition->AllocateDefaultPins();
bResult &= Schema->TryCreateConnection(Condition->GetReturnValuePin(), Branch->GetConditionPin());
bResult &= Schema->TryCreateConnection(Condition->FindPinChecked(TEXT("A")), IndexOutPin);
IndexLimitInPin = Condition->FindPinChecked(TEXT("B"));
check(IndexLimitInPin);
// body sequence
UK2Node_ExecutionSequence* Sequence = CompilerContext.SpawnIntermediateNode<UK2Node_ExecutionSequence>(Node, SourceGraph);
Sequence->AllocateDefaultPins();
bResult &= Schema->TryCreateConnection(Branch->GetThenPin(), Sequence->GetExecPin());
InsideLoopExecOutPin = Sequence->GetThenPinGivenIndex(0);
check(InsideLoopExecOutPin);
// Iterator increment
UK2Node_CallFunction* Increment = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(Node, SourceGraph);
Increment->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(TEXT("Add_IntInt")));
Increment->AllocateDefaultPins();
bResult &= Schema->TryCreateConnection(Increment->FindPinChecked(TEXT("A")), IndexOutPin);
Increment->FindPinChecked(TEXT("B"))->DefaultValue = TEXT("1");
// Iterator assigned
UK2Node_AssignmentStatement* IteratorAssign = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(Node, SourceGraph);
IteratorAssign->AllocateDefaultPins();
bResult &= Schema->TryCreateConnection(IteratorAssign->GetExecPin(), Sequence->GetThenPinGivenIndex(1));
bResult &= Schema->TryCreateConnection(IteratorAssign->GetVariablePin(), IndexOutPin);
bResult &= Schema->TryCreateConnection(IteratorAssign->GetValuePin(), Increment->GetReturnValuePin());
bResult &= Schema->TryCreateConnection(IteratorAssign->GetThenPin(), Branch->GetExecPin());
return bResult;
}
};
UK2Node_ForEachElementInEnum::UK2Node_ForEachElementInEnum(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
}
const FString UK2Node_ForEachElementInEnum::InsideLoopPinName(TEXT("LoopBody"));
const FString UK2Node_ForEachElementInEnum::EnumOuputPinName(TEXT("EnumValue"));
void UK2Node_ForEachElementInEnum::AllocateDefaultPins()
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
check(K2Schema);
CreatePin(EGPD_Input, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Execute);
if (Enum)
{
CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(""), NULL, false, false, InsideLoopPinName);
CreatePin(EGPD_Output, K2Schema->PC_Byte, TEXT(""), Enum, false, false, EnumOuputPinName);
}
if (auto CompletedPin = CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Then))
{
CompletedPin->PinFriendlyName = LOCTEXT("Completed", "Completed");
}
}
void UK2Node_ForEachElementInEnum::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const
{
if (!Enum)
{
MessageLog.Error(*NSLOCTEXT("K2Node", "ForEachElementInEnum_NoEnumError", "No Enum in @@").ToString(), this);
}
}
FString UK2Node_ForEachElementInEnum::GetTooltip() const
{
return GetNodeTitle(ENodeTitleType::FullTitle).ToString();
}
FText UK2Node_ForEachElementInEnum::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
FFormatNamedArguments Args;
Args.Add(TEXT("EnumName"), Enum ? FText::FromString(Enum->GetName()) : LOCTEXT("Unknown", "UNKNOWN"));
return FText::Format(LOCTEXT("ForEachElementInEnum_Title", "ForEach {EnumName}"), Args);
}
void UK2Node_ForEachElementInEnum::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
if (CompilerContext.bIsFullCompile)
{
if (!Enum)
{
ValidateNodeDuringCompilation(CompilerContext.MessageLog);
return;
}
FForExpandNodeHelper ForLoop;
if (!ForLoop.BuildLoop(this, CompilerContext, SourceGraph))
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "ForEachElementInEnum_ForError", "For Expand error in @@").ToString(), this);
}
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *ForLoop.StartLoopExecInPin);
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(Schema->PN_Then), *ForLoop.LoopCompleteOutExecPin);
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(InsideLoopPinName), *ForLoop.InsideLoopExecOutPin);
UK2Node_GetNumEnumEntries* GetNumEnumEntries = CompilerContext.SpawnIntermediateNode<UK2Node_GetNumEnumEntries>(this, SourceGraph);
GetNumEnumEntries->Enum = Enum;
GetNumEnumEntries->AllocateDefaultPins();
bool bResult = Schema->TryCreateConnection(GetNumEnumEntries->FindPinChecked(Schema->PN_ReturnValue), ForLoop.IndexLimitInPin);
UK2Node_CallFunction* Conv_Func = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
FName Conv_Func_Name = GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, Conv_IntToByte);
Conv_Func->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(Conv_Func_Name));
Conv_Func->AllocateDefaultPins();
bResult &= Schema->TryCreateConnection(Conv_Func->FindPinChecked(TEXT("InInt")), ForLoop.IndexOutPin);
UK2Node_CastByteToEnum* CastByteToEnum = CompilerContext.SpawnIntermediateNode<UK2Node_CastByteToEnum>(this, SourceGraph);
CastByteToEnum->Enum = Enum;
CastByteToEnum->bSafe = true;
CastByteToEnum->AllocateDefaultPins();
bResult &= Schema->TryCreateConnection(Conv_Func->FindPinChecked(Schema->PN_ReturnValue), CastByteToEnum->FindPinChecked(UK2Node_CastByteToEnum::ByteInputPinName));
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(EnumOuputPinName), *CastByteToEnum->FindPinChecked(Schema->PN_ReturnValue));
if (!bResult)
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "ForEachElementInEnum_ExpandError", "Expand error in @@").ToString(), this);
}
BreakAllNodeLinks();
}
}
void UK2Node_ForEachElementInEnum::GetMenuActions(TArray<UBlueprintNodeSpawner*>& ActionListOut) const
{
for (TObjectIterator<UEnum> EnumIt; EnumIt; ++EnumIt)
{
UEnum const* Enum = (*EnumIt);
// we only want to add global "standalone" enums here; those belonging to a
// certain class should instead be associated with that class (so when
// the class is modified we can easily handle any enums that were changed).
//
// @TODO: don't love how this code is essentially duplicated in BlueprintActionDatabase.cpp, for class enums
bool bIsStandaloneEnum = Enum->GetOuter()->IsA(UPackage::StaticClass());
if (!bIsStandaloneEnum || !UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Enum))
{
continue;
}
auto CustomizeEnumNodeLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode, TWeakObjectPtr<UEnum> EnumPtr)
{
UK2Node_ForEachElementInEnum* EnumNode = CastChecked<UK2Node_ForEachElementInEnum>(NewNode);
if (EnumPtr.IsValid())
{
EnumNode->Enum = EnumPtr.Get();
}
};
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
ActionListOut.Add(NodeSpawner);
TWeakObjectPtr<UEnum> EnumPtr = Enum;
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeEnumNodeLambda, EnumPtr);
}
}
FText UK2Node_ForEachElementInEnum::GetMenuCategory() const
{
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Enum);
}
#undef LOCTEXT_NAMESPACE