724 lines
29 KiB
C++
724 lines
29 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "K2Node_MultiGate.h"
|
|
|
|
#include "BPTerminal.h"
|
|
#include "BlueprintActionDatabaseRegistrar.h"
|
|
#include "BlueprintCompiledStatement.h"
|
|
#include "BlueprintNodeSpawner.h"
|
|
#include "Containers/EnumAsByte.h"
|
|
#include "Containers/Map.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "EdGraph/EdGraphPin.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "EdGraphUtilities.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "K2Node_AssignmentStatement.h"
|
|
#include "K2Node_TemporaryVariable.h"
|
|
#include "Kismet/KismetMathLibrary.h"
|
|
#include "Kismet/KismetNodeHelperLibrary.h"
|
|
#include "Kismet/KismetSystemLibrary.h"
|
|
#include "Kismet2/CompilerResultsLog.h"
|
|
#include "KismetCompiledFunctionContext.h"
|
|
#include "KismetCompiler.h"
|
|
#include "KismetCompilerMisc.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Templates/Casts.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/UnrealType.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "K2Node_MultiGate"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FKCHandler_MultiGate
|
|
|
|
class FKCHandler_MultiGate : public FNodeHandlingFunctor
|
|
{
|
|
protected:
|
|
// Map to a bool that determines if we're in the first execution of the node or not
|
|
TMap<UEdGraphNode*, FBPTerminal*> FirstRunTermMap;
|
|
// Map to an int used to keep track of which outputs have been used
|
|
TMap<UEdGraphNode*, FBPTerminal*> DataTermMap;
|
|
|
|
/**
|
|
* These are locals that don't need to be independent for each node, instead
|
|
* they can be shared between each multigate node in the graph.
|
|
*/
|
|
struct FFunctionScopedTerms
|
|
{
|
|
FFunctionScopedTerms() : GenericBoolTerm(nullptr), IndexTerm(nullptr) {}
|
|
|
|
// Generic bool term used for run-time conditions
|
|
FBPTerminal* GenericBoolTerm;
|
|
// Index term used for run-time index determination
|
|
FBPTerminal* IndexTerm;
|
|
};
|
|
TMap<UFunction*, FFunctionScopedTerms> FunctionTermMap;
|
|
|
|
public:
|
|
FKCHandler_MultiGate(FKismetCompilerContext& InCompilerContext)
|
|
: FNodeHandlingFunctor(InCompilerContext)
|
|
{
|
|
}
|
|
|
|
virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node)
|
|
{
|
|
FNodeHandlingFunctor::RegisterNets(Context, Node);
|
|
|
|
const FString BaseNetName = Context.NetNameMap->MakeValidName(Node);
|
|
|
|
// Create a term to store a bool that determines if we're in the first execution of the node or not
|
|
FBPTerminal* FirstRunTerm = Context.CreateLocalTerminal();
|
|
FirstRunTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Boolean;
|
|
FirstRunTerm->Source = Node;
|
|
FirstRunTerm->Name = BaseNetName + TEXT("_FirstRun");
|
|
FirstRunTermMap.Add(Node, FirstRunTerm);
|
|
|
|
UK2Node_MultiGate* GateNode = Cast<UK2Node_MultiGate>(Node);
|
|
// If there is already a data node from expansion phase
|
|
if (!GateNode || !GateNode->DataNode)
|
|
{
|
|
FBPTerminal* DataTerm = Context.CreateLocalTerminal();
|
|
DataTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Int;
|
|
DataTerm->Source = Node;
|
|
DataTerm->Name = BaseNetName + TEXT("_Data");
|
|
DataTermMap.Add(Node, DataTerm);
|
|
}
|
|
|
|
FFunctionScopedTerms& FuncLocals = FunctionTermMap.FindOrAdd(Context.Function);
|
|
|
|
// Create a local scratch bool for run-time if there isn't already one
|
|
if (!FuncLocals.GenericBoolTerm)
|
|
{
|
|
FuncLocals.GenericBoolTerm = Context.CreateLocalTerminal();
|
|
FuncLocals.GenericBoolTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Boolean;
|
|
FuncLocals.GenericBoolTerm->Source = Node;
|
|
FuncLocals.GenericBoolTerm->Name = BaseNetName + TEXT("_ScratchBool");
|
|
}
|
|
|
|
// Create a local scratch int for run-time index tracking if there isn't already one
|
|
if (!FuncLocals.IndexTerm)
|
|
{
|
|
FuncLocals.IndexTerm = Context.CreateLocalTerminal();
|
|
FuncLocals.IndexTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Int;
|
|
FuncLocals.IndexTerm->Source = Node;
|
|
FuncLocals.IndexTerm->Name = BaseNetName + TEXT("_ScratchIndex");
|
|
}
|
|
}
|
|
|
|
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node)
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// Get the node, retrieve the helper functions, and create a local "Index" variable
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Get the multi gate node and the helper functions
|
|
UK2Node_MultiGate* GateNode = Cast<UK2Node_MultiGate>(Node);
|
|
|
|
// Get function names and class pointers to helper nodes
|
|
FName MarkBitFunctionName = "";
|
|
UClass* MarkBitFunctionClass = NULL;
|
|
GateNode->GetMarkBitFunction(MarkBitFunctionName, &MarkBitFunctionClass);
|
|
UFunction* MarkBitFunction = FindUField<UFunction>(MarkBitFunctionClass, MarkBitFunctionName);
|
|
|
|
FName HasUnmarkedBitFunctionName = "";
|
|
UClass* HasUnmarkedBitFunctionClass = NULL;
|
|
GateNode->GetHasUnmarkedBitFunction(HasUnmarkedBitFunctionName, &HasUnmarkedBitFunctionClass);
|
|
UFunction* HasUnmarkedBitFunction = FindUField<UFunction>(HasUnmarkedBitFunctionClass, HasUnmarkedBitFunctionName);
|
|
|
|
FName GetUnmarkedBitFunctionName = "";
|
|
UClass* GetUnmarkedBitFunctionClass = NULL;
|
|
GateNode->GetUnmarkedBitFunction(GetUnmarkedBitFunctionName, &GetUnmarkedBitFunctionClass);
|
|
UFunction* GetUnmarkedBitFunction = FindUField<UFunction>(GetUnmarkedBitFunctionClass, GetUnmarkedBitFunctionName);
|
|
|
|
FName ConditionalFunctionName = "";
|
|
UClass* ConditionalFunctionClass = NULL;
|
|
GateNode->GetConditionalFunction(ConditionalFunctionName, &ConditionalFunctionClass);
|
|
UFunction* ConditionFunction = FindUField<UFunction>(ConditionalFunctionClass, ConditionalFunctionName);
|
|
|
|
FName EqualityFunctionName = "";
|
|
UClass* EqualityFunctionClass = NULL;
|
|
GateNode->GetEqualityFunction(EqualityFunctionName, &EqualityFunctionClass);
|
|
UFunction* EqualityFunction = FindUField<UFunction>(EqualityFunctionClass, EqualityFunctionName);
|
|
|
|
FName BoolNotEqualFunctionName = "";
|
|
UClass* BoolNotEqualFunctionClass = NULL;
|
|
GateNode->GetBoolNotEqualFunction(BoolNotEqualFunctionName, &BoolNotEqualFunctionClass);
|
|
UFunction* BoolNotEqualFunction = FindUField<UFunction>(BoolNotEqualFunctionClass, BoolNotEqualFunctionName);
|
|
|
|
FName PrintStringFunctionName = "";
|
|
UClass* PrintStringFunctionClass = NULL;
|
|
GateNode->GetPrintStringFunction(PrintStringFunctionName, &PrintStringFunctionClass);
|
|
UFunction* PrintFunction = FindUField<UFunction>(PrintStringFunctionClass, PrintStringFunctionName);
|
|
|
|
FName ClearBitsFunctionName = "";
|
|
UClass* ClearBitsFunctionClass = NULL;
|
|
GateNode->GetClearAllBitsFunction(ClearBitsFunctionName, &ClearBitsFunctionClass);
|
|
UFunction* ClearBitsFunction = FindUField<UFunction>(ClearBitsFunctionClass, ClearBitsFunctionName);
|
|
|
|
// Find the data terms if there is already a data node from expansion phase
|
|
FBPTerminal* DataTerm = NULL;
|
|
if (GateNode->DataNode)
|
|
{
|
|
UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->DataNode->GetVariablePin());
|
|
FBPTerminal** DataTermPtr = Context.NetMap.Find(PinToTry);
|
|
DataTerm = (DataTermPtr != NULL) ? *DataTermPtr : NULL;
|
|
}
|
|
// Else we built it in the net registration, so find it
|
|
else
|
|
{
|
|
DataTerm = DataTermMap.FindRef(GateNode);
|
|
}
|
|
check(DataTerm);
|
|
|
|
// Used for getting all the nets from pins
|
|
UEdGraphPin* PinToTry = NULL;
|
|
|
|
// The StartIndex passed into the multi gate node
|
|
PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->GetStartIndexPin());
|
|
FBPTerminal** StartIndexPinTerm = Context.NetMap.Find(PinToTry);
|
|
|
|
// Get the bRandom pin as a kismet term from the multi gate node
|
|
PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->GetIsRandomPin());
|
|
FBPTerminal** RandomTerm = Context.NetMap.Find(PinToTry);
|
|
|
|
// Get the Loop pin as a kismet term from the multi gate node
|
|
PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->GetLoopPin());
|
|
FBPTerminal** LoopTerm = Context.NetMap.Find(PinToTry);
|
|
|
|
// Find the local boolean for use in determining if this is the first run of the node or not
|
|
FBPTerminal* FirstRunBoolTerm = FirstRunTermMap.FindRef(GateNode);
|
|
|
|
// Create a literal pin that represents a -1 value
|
|
FBPTerminal* InvalidIndexTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
|
|
InvalidIndexTerm->bIsLiteral = true;
|
|
InvalidIndexTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Int;
|
|
InvalidIndexTerm->Name = TEXT("-1");
|
|
|
|
// Create a literal pin that represents a true value
|
|
FBPTerminal* TrueBoolTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
|
|
TrueBoolTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Boolean;
|
|
TrueBoolTerm->bIsLiteral = true;
|
|
TrueBoolTerm->Name = TEXT("true");
|
|
|
|
// Get the out pins and create a literal describing how many logical outs there are
|
|
TArray<UEdGraphPin*> OutPins;
|
|
GateNode->GetOutPins(OutPins);
|
|
FBPTerminal* NumOutsTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
|
|
NumOutsTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Int;
|
|
NumOutsTerm->bIsLiteral = true;
|
|
NumOutsTerm->Name = FString::Printf(TEXT("%d"), OutPins.Num());
|
|
|
|
///////////////////////////////////////////////////
|
|
// See if this is the first time in
|
|
///////////////////////////////////////////////////
|
|
|
|
FFunctionScopedTerms& FuncLocals = FunctionTermMap.FindChecked(Context.Function);
|
|
check(FuncLocals.GenericBoolTerm != nullptr);
|
|
|
|
// (bIsNotFirstTime != true)
|
|
FBlueprintCompiledStatement& BoolNotEqualStatement = Context.AppendStatementForNode(Node);
|
|
BoolNotEqualStatement.Type = KCST_CallFunction;
|
|
BoolNotEqualStatement.FunctionToCall = BoolNotEqualFunction;
|
|
BoolNotEqualStatement.FunctionContext = NULL;
|
|
BoolNotEqualStatement.bIsParentContext = false;
|
|
// Set the params
|
|
BoolNotEqualStatement.LHS = FuncLocals.GenericBoolTerm;
|
|
BoolNotEqualStatement.RHS.Add(FirstRunBoolTerm);
|
|
BoolNotEqualStatement.RHS.Add(TrueBoolTerm);
|
|
|
|
// if (bIsNotFirstTime == false)
|
|
// {
|
|
FBlueprintCompiledStatement& IfFirstTimeStatement = Context.AppendStatementForNode(Node);
|
|
IfFirstTimeStatement.Type = KCST_GotoIfNot;
|
|
IfFirstTimeStatement.LHS = FuncLocals.GenericBoolTerm;
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// This is the first time in... set the bool and the start index
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
// bIsNotFirstTime = true;
|
|
FBlueprintCompiledStatement& AssignBoolStatement = Context.AppendStatementForNode(Node);
|
|
AssignBoolStatement.Type = KCST_Assignment;
|
|
AssignBoolStatement.LHS = FirstRunBoolTerm;
|
|
AssignBoolStatement.RHS.Add(TrueBoolTerm);
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// See if the StartIndex is greater than -1 (they supplied an index)
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// (StartIndex > -1)
|
|
FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node);
|
|
Statement.Type = KCST_CallFunction;
|
|
Statement.FunctionToCall = ConditionFunction;
|
|
Statement.FunctionContext = NULL;
|
|
Statement.bIsParentContext = false;
|
|
Statement.LHS = FuncLocals.GenericBoolTerm;
|
|
Statement.RHS.Add(*StartIndexPinTerm);
|
|
Statement.RHS.Add(InvalidIndexTerm);
|
|
|
|
// if (StartIndex > -1)
|
|
// {
|
|
FBlueprintCompiledStatement& IfHasIndexStatement = Context.AppendStatementForNode(Node);
|
|
IfHasIndexStatement.Type = KCST_GotoIfNot;
|
|
IfHasIndexStatement.LHS = FuncLocals.GenericBoolTerm;
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// They supplied a start index so set the index to it
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
// Index = StartIndex; // (StartIndex is from multi gate pin for it)
|
|
FBlueprintCompiledStatement& AssignSuppliedIndexStatement = Context.AppendStatementForNode(Node);
|
|
AssignSuppliedIndexStatement.Type = KCST_Assignment;
|
|
AssignSuppliedIndexStatement.LHS = FuncLocals.IndexTerm;
|
|
AssignSuppliedIndexStatement.RHS.Add(*StartIndexPinTerm);
|
|
|
|
// Jump to index usage
|
|
FBlueprintCompiledStatement& ElseGotoIndexUsageStatement = Context.AppendStatementForNode(Node);
|
|
ElseGotoIndexUsageStatement.Type = KCST_UnconditionalGoto;
|
|
// }
|
|
// else
|
|
// {
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// They did NOT supply a start index so figure one out
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
check(FuncLocals.IndexTerm != nullptr);
|
|
|
|
// Index = GetUnmarkedBit(Data, -1, bRandom);
|
|
FBlueprintCompiledStatement& GetStartIndexStatement = Context.AppendStatementForNode(Node);
|
|
GetStartIndexStatement.Type = KCST_CallFunction;
|
|
GetStartIndexStatement.FunctionToCall = GetUnmarkedBitFunction;
|
|
GetStartIndexStatement.bIsParentContext = false;
|
|
GetStartIndexStatement.LHS = FuncLocals.IndexTerm;
|
|
GetStartIndexStatement.RHS.Add(DataTerm);
|
|
GetStartIndexStatement.RHS.Add(*StartIndexPinTerm);
|
|
GetStartIndexStatement.RHS.Add(NumOutsTerm);
|
|
GetStartIndexStatement.RHS.Add(*RandomTerm);
|
|
// Hook the IfHasIndexStatement jump to this node
|
|
GetStartIndexStatement.bIsJumpTarget = true;
|
|
IfHasIndexStatement.TargetLabel = &GetStartIndexStatement;
|
|
|
|
// Jump to index usage
|
|
FBlueprintCompiledStatement& StartIndexGotoIndexUsageStatement = Context.AppendStatementForNode(Node);
|
|
StartIndexGotoIndexUsageStatement.Type = KCST_UnconditionalGoto;
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Else this is NOT the first time in, see if there is an available index
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// (HasUnmarkedBit())
|
|
FBlueprintCompiledStatement& IsAvailableStatement = Context.AppendStatementForNode(Node);
|
|
IsAvailableStatement.Type = KCST_CallFunction;
|
|
IsAvailableStatement.FunctionToCall = HasUnmarkedBitFunction;
|
|
IsAvailableStatement.FunctionContext = NULL;
|
|
IsAvailableStatement.bIsParentContext = false;
|
|
IsAvailableStatement.LHS = FuncLocals.GenericBoolTerm;
|
|
IsAvailableStatement.RHS.Add(DataTerm);
|
|
IsAvailableStatement.RHS.Add(NumOutsTerm);
|
|
// Hook the IfFirstTimeStatement jump to this node
|
|
IsAvailableStatement.bIsJumpTarget = true;
|
|
IfFirstTimeStatement.TargetLabel = &IsAvailableStatement;
|
|
|
|
// if (HasUnmarkedBit())
|
|
// {
|
|
FBlueprintCompiledStatement& IfIsAvailableStatement = Context.AppendStatementForNode(Node);
|
|
IfIsAvailableStatement.Type = KCST_GotoIfNot;
|
|
IfIsAvailableStatement.LHS = FuncLocals.GenericBoolTerm;
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Has available index so figure it out and jump to its' usage
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Index = GetUnmarkedBit(Data, -1, bRandom)
|
|
FBlueprintCompiledStatement& GetNextIndexStatement = Context.AppendStatementForNode(Node);
|
|
GetNextIndexStatement.Type = KCST_CallFunction;
|
|
GetNextIndexStatement.FunctionToCall = GetUnmarkedBitFunction;
|
|
GetNextIndexStatement.bIsParentContext = false;
|
|
GetNextIndexStatement.LHS = FuncLocals.IndexTerm;
|
|
GetNextIndexStatement.RHS.Add(DataTerm);
|
|
GetNextIndexStatement.RHS.Add(*StartIndexPinTerm);
|
|
GetNextIndexStatement.RHS.Add(NumOutsTerm);
|
|
GetNextIndexStatement.RHS.Add(*RandomTerm);
|
|
|
|
// Goto Index usage
|
|
FBlueprintCompiledStatement& GotoIndexUsageStatement = Context.AppendStatementForNode(Node);
|
|
GotoIndexUsageStatement.Type = KCST_UnconditionalGoto;
|
|
// }
|
|
// else
|
|
// {
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// No available index, see if we can loop
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// if (bLoop)
|
|
FBlueprintCompiledStatement& IfLoopingStatement = Context.AppendStatementForNode(Node);
|
|
IfLoopingStatement.Type = KCST_GotoIfNot;
|
|
IfLoopingStatement.LHS = *LoopTerm;
|
|
IfLoopingStatement.bIsJumpTarget = true;
|
|
IfIsAvailableStatement.TargetLabel = &IfLoopingStatement;
|
|
// {
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Reset the data and jump back up to "if (HasUnmarkedBit())"
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Clear the data
|
|
// Data = 0;
|
|
FBlueprintCompiledStatement& ClearDataStatement = Context.AppendStatementForNode(Node);
|
|
ClearDataStatement.Type = KCST_CallFunction;
|
|
ClearDataStatement.FunctionToCall = ClearBitsFunction;
|
|
ClearDataStatement.bIsParentContext = false;
|
|
ClearDataStatement.RHS.Add(DataTerm);
|
|
|
|
// Goto back up to attempt an index again
|
|
FBlueprintCompiledStatement& RetryStatement = Context.AppendStatementForNode(Node);
|
|
RetryStatement.Type = KCST_UnconditionalGoto;
|
|
IsAvailableStatement.bIsJumpTarget = true;
|
|
RetryStatement.TargetLabel = &IsAvailableStatement;
|
|
// }
|
|
// else
|
|
// {
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Dead... Jump to end of thread
|
|
////////////////////////////////////////////////////////////////////////////
|
|
FBlueprintCompiledStatement& NoLoopStatement = Context.AppendStatementForNode(Node);
|
|
NoLoopStatement.Type = KCST_EndOfThread;
|
|
NoLoopStatement.bIsJumpTarget = true;
|
|
IfLoopingStatement.TargetLabel = &NoLoopStatement;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
//////////////////////////////////////
|
|
// We have a valid index so mark it
|
|
//////////////////////////////////////
|
|
|
|
// MarkBit(Data, Index);
|
|
FBlueprintCompiledStatement& MarkIndexStatement = Context.AppendStatementForNode(Node);
|
|
MarkIndexStatement.Type = KCST_CallFunction;
|
|
MarkIndexStatement.FunctionToCall = MarkBitFunction;
|
|
MarkIndexStatement.bIsParentContext = false;
|
|
MarkIndexStatement.LHS = FuncLocals.IndexTerm;
|
|
MarkIndexStatement.RHS.Add(DataTerm);
|
|
MarkIndexStatement.RHS.Add(FuncLocals.IndexTerm);
|
|
|
|
// Setup jump label
|
|
MarkIndexStatement.bIsJumpTarget = true;
|
|
GotoIndexUsageStatement.TargetLabel = &MarkIndexStatement;
|
|
ElseGotoIndexUsageStatement.TargetLabel = &MarkIndexStatement;
|
|
StartIndexGotoIndexUsageStatement.TargetLabel = &MarkIndexStatement;
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// We have a valid index so mark it, then find the correct exec out pin
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// Call the correct exec pin out of the multi gate node
|
|
FBlueprintCompiledStatement* PrevIndexEqualityStatement = NULL;
|
|
FBlueprintCompiledStatement* PrevIfIndexMatchesStatement = NULL;
|
|
for (int32 OutIdx = 0; OutIdx < OutPins.Num(); OutIdx++)
|
|
{
|
|
// (Index == OutIdx)
|
|
FBlueprintCompiledStatement& IndexEqualityStatement = Context.AppendStatementForNode(Node);
|
|
IndexEqualityStatement.Type = KCST_CallFunction;
|
|
IndexEqualityStatement.FunctionToCall = EqualityFunction;
|
|
IndexEqualityStatement.FunctionContext = NULL;
|
|
IndexEqualityStatement.bIsParentContext = false;
|
|
// LiteralIndexTerm will be the right side of the == statemnt
|
|
FBPTerminal* LiteralIndexTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
|
|
LiteralIndexTerm->bIsLiteral = true;
|
|
LiteralIndexTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Int;
|
|
LiteralIndexTerm->Name = FString::Printf(TEXT("%d"), OutIdx);
|
|
// Set the params
|
|
IndexEqualityStatement.LHS = FuncLocals.GenericBoolTerm;
|
|
IndexEqualityStatement.RHS.Add(FuncLocals.IndexTerm);
|
|
IndexEqualityStatement.RHS.Add(LiteralIndexTerm);
|
|
|
|
// if (Index == OutIdx)
|
|
FBlueprintCompiledStatement& IfIndexMatchesStatement = Context.AppendStatementForNode(Node);
|
|
IfIndexMatchesStatement.Type = KCST_GotoIfNot;
|
|
IfIndexMatchesStatement.LHS = FuncLocals.GenericBoolTerm;
|
|
// {
|
|
//////////////////////////////////////
|
|
// Found a match - Jump there
|
|
//////////////////////////////////////
|
|
|
|
GenerateSimpleThenGoto(Context, *GateNode, OutPins[OutIdx]);
|
|
// }
|
|
// else
|
|
// {
|
|
////////////////////////////////////////////////////
|
|
// Not a match so loop will attempt the next index
|
|
////////////////////////////////////////////////////
|
|
|
|
if (PrevIndexEqualityStatement && PrevIfIndexMatchesStatement)
|
|
{
|
|
// Attempt next index
|
|
IndexEqualityStatement.bIsJumpTarget = true;
|
|
PrevIfIndexMatchesStatement->TargetLabel = &IndexEqualityStatement;
|
|
}
|
|
// }
|
|
|
|
PrevIndexEqualityStatement = &IndexEqualityStatement;
|
|
PrevIfIndexMatchesStatement = &IfIndexMatchesStatement;
|
|
}
|
|
|
|
check(PrevIfIndexMatchesStatement);
|
|
|
|
// Should have jumped to proper index, print error (should never happen)
|
|
// Create a CallFunction statement for doing a print string of our error message
|
|
FBlueprintCompiledStatement& PrintStatement = Context.AppendStatementForNode(Node);
|
|
PrintStatement.Type = KCST_CallFunction;
|
|
PrintStatement.bIsJumpTarget = true;
|
|
PrintStatement.FunctionToCall = PrintFunction;
|
|
PrintStatement.FunctionContext = NULL;
|
|
PrintStatement.bIsParentContext = false;
|
|
// Create a local int for use in the equality call function below (LiteralTerm = the right hand side of the EqualEqual_IntInt or NotEqual_BoolBool statement)
|
|
FBPTerminal* LiteralStringTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
|
|
LiteralStringTerm->bIsLiteral = true;
|
|
LiteralStringTerm->Type.PinCategory = UEdGraphSchema_K2::PC_String;
|
|
LiteralStringTerm->Name = FText::Format(LOCTEXT("MultiGateNode IndexWarningFmt", "MultiGate Node failed! Out of bounds indexing of the out pins. There are only {0} outs available."), OutPins.Num()).ToString();
|
|
PrintStatement.RHS.Add(LiteralStringTerm);
|
|
// Hook the IfNot statement's jump target to this statement
|
|
PrevIfIndexMatchesStatement->TargetLabel = &PrintStatement;
|
|
}
|
|
};
|
|
|
|
UK2Node_MultiGate::UK2Node_MultiGate(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
}
|
|
|
|
FText UK2Node_MultiGate::GetTooltipText() const
|
|
{
|
|
return NSLOCTEXT("K2Node", "MultiGate_Tooltip", "Executes a series of pins in order");
|
|
}
|
|
|
|
FLinearColor UK2Node_MultiGate::GetNodeTitleColor() const
|
|
{
|
|
return FLinearColor::White;
|
|
}
|
|
|
|
FText UK2Node_MultiGate::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
|
{
|
|
return NSLOCTEXT("K2Node", "MultiGate", "MultiGate");
|
|
}
|
|
|
|
bool UK2Node_MultiGate::CanAddPin() const
|
|
{
|
|
// Since an int32 is used to track which gates have been used, we can only have 32 outputs
|
|
return GetNumOutPins() < 32;
|
|
}
|
|
|
|
void UK2Node_MultiGate::AllocateDefaultPins()
|
|
{
|
|
Super::AllocateDefaultPins();
|
|
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, TEXT("Reset"));
|
|
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Boolean, TEXT("IsRandom"));
|
|
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Boolean, TEXT("Loop"));
|
|
UEdGraphPin* IndexPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Int, TEXT("StartIndex"));
|
|
K2Schema->SetPinAutogeneratedDefaultValue(IndexPin, TEXT("-1"));
|
|
}
|
|
|
|
void UK2Node_MultiGate::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
|
|
{
|
|
Super::ReallocatePinsDuringReconstruction(OldPins);
|
|
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, TEXT("Reset"));
|
|
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Boolean, TEXT("IsRandom"));
|
|
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Boolean, TEXT("Loop"));
|
|
UEdGraphPin* IndexPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Int, TEXT("StartIndex"));
|
|
K2Schema->SetPinAutogeneratedDefaultValue(IndexPin, TEXT("-1"));
|
|
}
|
|
|
|
UEdGraphPin* UK2Node_MultiGate::GetResetPin() const
|
|
{
|
|
UEdGraphPin* Pin = FindPin(TEXT("Reset"));
|
|
check(Pin != NULL);
|
|
return Pin;
|
|
}
|
|
|
|
UEdGraphPin* UK2Node_MultiGate::GetIsRandomPin() const
|
|
{
|
|
UEdGraphPin* Pin = FindPin(TEXT("IsRandom"));
|
|
check(Pin != NULL);
|
|
return Pin;
|
|
}
|
|
|
|
UEdGraphPin* UK2Node_MultiGate::GetLoopPin() const
|
|
{
|
|
UEdGraphPin* Pin = FindPin(TEXT("Loop"));
|
|
check(Pin != NULL);
|
|
return Pin;
|
|
}
|
|
|
|
UEdGraphPin* UK2Node_MultiGate::GetStartIndexPin() const
|
|
{
|
|
UEdGraphPin* Pin = FindPin(TEXT("StartIndex"));
|
|
check(Pin != NULL);
|
|
return Pin;
|
|
}
|
|
|
|
void UK2Node_MultiGate::GetOutPins(TArray<UEdGraphPin*>& OutPins) const
|
|
{
|
|
OutPins.Reset(Pins.Num());
|
|
|
|
for (UEdGraphPin* Pin : Pins)
|
|
{
|
|
if (Pin->Direction == EGPD_Output)
|
|
{
|
|
OutPins.Add(Pin);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 UK2Node_MultiGate::GetNumOutPins() const
|
|
{
|
|
int32 NumOutPins = 0;
|
|
|
|
for (UEdGraphPin* Pin : Pins)
|
|
{
|
|
if (Pin->Direction == EGPD_Output)
|
|
{
|
|
++NumOutPins;
|
|
}
|
|
}
|
|
|
|
return NumOutPins;
|
|
}
|
|
|
|
void UK2Node_MultiGate::GetMarkBitFunction(FName& FunctionName, UClass** FunctionClass)
|
|
{
|
|
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetNodeHelperLibrary, MarkBit);
|
|
*FunctionClass = UKismetNodeHelperLibrary::StaticClass();
|
|
}
|
|
|
|
void UK2Node_MultiGate::GetHasUnmarkedBitFunction(FName& FunctionName, UClass** FunctionClass)
|
|
{
|
|
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetNodeHelperLibrary, HasUnmarkedBit);
|
|
*FunctionClass = UKismetNodeHelperLibrary::StaticClass();
|
|
}
|
|
|
|
void UK2Node_MultiGate::GetUnmarkedBitFunction(FName& FunctionName, UClass** FunctionClass)
|
|
{
|
|
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetNodeHelperLibrary, GetUnmarkedBit);
|
|
*FunctionClass = UKismetNodeHelperLibrary::StaticClass();
|
|
}
|
|
|
|
/** Gets the name and class of the Greater_IntInt function from the KismetMathLibrary */
|
|
void UK2Node_MultiGate::GetConditionalFunction(FName& FunctionName, UClass** FunctionClass)
|
|
{
|
|
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, Greater_IntInt);
|
|
*FunctionClass = UKismetMathLibrary::StaticClass();
|
|
}
|
|
|
|
void UK2Node_MultiGate::GetEqualityFunction(FName& FunctionName, UClass** FunctionClass)
|
|
{
|
|
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, EqualEqual_IntInt);
|
|
*FunctionClass = UKismetMathLibrary::StaticClass();
|
|
}
|
|
|
|
/** Gets the name and class of the NotEqual_BoolBool function from the KismetMathLibrary */
|
|
void UK2Node_MultiGate::GetBoolNotEqualFunction(FName& FunctionName, UClass** FunctionClass)
|
|
{
|
|
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, NotEqual_BoolBool);
|
|
*FunctionClass = UKismetMathLibrary::StaticClass();
|
|
}
|
|
|
|
/** Gets the name and class of the PrintString function */
|
|
void UK2Node_MultiGate::GetPrintStringFunction(FName& FunctionName, UClass** FunctionClass)
|
|
{
|
|
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, PrintWarning);
|
|
*FunctionClass = UKismetSystemLibrary::StaticClass();
|
|
}
|
|
|
|
void UK2Node_MultiGate::GetClearAllBitsFunction(FName& FunctionName, UClass** FunctionClass)
|
|
{
|
|
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetNodeHelperLibrary, ClearAllBits);
|
|
*FunctionClass = UKismetNodeHelperLibrary::StaticClass();
|
|
}
|
|
|
|
FName UK2Node_MultiGate::GetPinNameGivenIndex(int32 Index) const
|
|
{
|
|
return *FString::Printf(TEXT("Out %d"), Index);
|
|
}
|
|
|
|
FNodeHandlingFunctor* UK2Node_MultiGate::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
|
|
{
|
|
return new FKCHandler_MultiGate(CompilerContext);
|
|
}
|
|
|
|
void UK2Node_MultiGate::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
|
|
{
|
|
Super::ExpandNode(CompilerContext, SourceGraph);
|
|
|
|
/////////////////////////////
|
|
// Handle the "Reset"
|
|
/////////////////////////////
|
|
|
|
// Redirect the reset pin if linked to
|
|
UEdGraphPin* ResetPin = GetResetPin();
|
|
if (ResetPin->LinkedTo.Num() > 0)
|
|
{
|
|
/////////////////////////////
|
|
// Temporary Variable node
|
|
/////////////////////////////
|
|
|
|
// Create the node
|
|
UK2Node_TemporaryVariable* TempVarNode = SourceGraph->CreateIntermediateNode<UK2Node_TemporaryVariable>();
|
|
TempVarNode->VariableType.PinCategory = UEdGraphSchema_K2::PC_Int;
|
|
TempVarNode->AllocateDefaultPins();
|
|
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(TempVarNode, this);
|
|
// Give a reference of the variable node to the multi gate node
|
|
DataNode = TempVarNode;
|
|
|
|
/////////////////////////////
|
|
// Assignment node
|
|
/////////////////////////////
|
|
|
|
// Create the node
|
|
UK2Node_AssignmentStatement* AssignmentNode = SourceGraph->CreateIntermediateNode<UK2Node_AssignmentStatement>();
|
|
AssignmentNode->AllocateDefaultPins();
|
|
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(AssignmentNode, this);
|
|
|
|
// Coerce the wildcards pin types (set the default of the value to 0)
|
|
AssignmentNode->GetVariablePin()->PinType = TempVarNode->GetVariablePin()->PinType;
|
|
AssignmentNode->GetVariablePin()->MakeLinkTo(TempVarNode->GetVariablePin());
|
|
AssignmentNode->GetValuePin()->PinType = TempVarNode->GetVariablePin()->PinType;
|
|
AssignmentNode->GetValuePin()->DefaultValue = TEXT("0");
|
|
|
|
// Move the "Reset" link to the Assignment node
|
|
CompilerContext.MovePinLinksToIntermediate(*ResetPin, *AssignmentNode->GetExecPin());
|
|
}
|
|
}
|
|
|
|
void UK2Node_MultiGate::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
|
|
{
|
|
// actions get registered under specific object-keys; the idea is that
|
|
// actions might have to be updated (or deleted) if their object-key is
|
|
// mutated (or removed)... here we use the node's class (so if the node
|
|
// type disappears, then the action should go with it)
|
|
UClass* ActionKey = GetClass();
|
|
// to keep from needlessly instantiating a UBlueprintNodeSpawner, first
|
|
// check to make sure that the registrar is looking for actions of this type
|
|
// (could be regenerating actions for a specific asset, and therefore the
|
|
// registrar would only accept actions corresponding to that asset)
|
|
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
|
|
{
|
|
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
|
|
check(NodeSpawner != nullptr);
|
|
|
|
ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|