You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Plugins/BlueprintContext - Editor/GlueprintGraph - Editor/GraphEditor - Editor/Kismet - Editor/KismetCompiler - Editor/UnrealEd/Private/Kismet2 note: if you're seeing this CL in the perforce history because you're trying to figure out why there's a null check that doesn't make sense, This is why. The goal of this CL is to preserve the behavior before IsChildOf changed rather than analyze whether that behavior makes sense. Use your best judgement #rb marc.audy #preflight 6299023a6438e3c731307a69 [CL 20474984 by jordan hoffmann in ue5-main branch]
369 lines
12 KiB
C++
369 lines
12 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "K2Node_Switch.h"
|
|
#include "UObject/UnrealType.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "EdGraph/EdGraphNodeUtils.h"
|
|
#include "EdGraphUtilities.h"
|
|
#include "BPTerminal.h"
|
|
#include "KismetCompilerMisc.h"
|
|
#include "KismetCompiler.h"
|
|
#include "EditorCategoryUtils.h"
|
|
#include "Styling/AppStyle.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "K2Node_Switch"
|
|
|
|
namespace
|
|
{
|
|
static FName DefaultPinName(TEXT("Default"));
|
|
static FName SelectionPinName(TEXT("Selection"));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FKCHandler_Switch
|
|
|
|
class FKCHandler_Switch : public FNodeHandlingFunctor
|
|
{
|
|
protected:
|
|
TMap<UEdGraphNode*, FBPTerminal*> BoolTermMap;
|
|
|
|
public:
|
|
FKCHandler_Switch(FKismetCompilerContext& InCompilerContext)
|
|
: FNodeHandlingFunctor(InCompilerContext)
|
|
{
|
|
}
|
|
|
|
virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override
|
|
{
|
|
UK2Node_Switch* SwitchNode = Cast<UK2Node_Switch>(Node);
|
|
|
|
FNodeHandlingFunctor::RegisterNets(Context, Node);
|
|
|
|
// Create a term to determine if the compare was successful or not
|
|
//@TODO: Ideally we just create one ever, not one per switch
|
|
FBPTerminal* BoolTerm = Context.CreateLocalTerminal();
|
|
BoolTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Boolean;
|
|
BoolTerm->Source = Node;
|
|
BoolTerm->Name = Context.NetNameMap->MakeValidName(Node, TEXT("CmpSuccess"));
|
|
BoolTermMap.Add(Node, BoolTerm);
|
|
}
|
|
|
|
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
|
|
{
|
|
UK2Node_Switch* SwitchNode = CastChecked<UK2Node_Switch>(Node);
|
|
|
|
FEdGraphPinType ExpectedExecPinType;
|
|
ExpectedExecPinType.PinCategory = UEdGraphSchema_K2::PC_Exec;
|
|
|
|
// Make sure that the input pin is connected and valid for this block
|
|
UEdGraphPin* ExecTriggeringPin = Context.FindRequiredPinByName(SwitchNode, UEdGraphSchema_K2::PN_Execute, EGPD_Input);
|
|
if ((ExecTriggeringPin == NULL) || !Context.ValidatePinType(ExecTriggeringPin, ExpectedExecPinType))
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("NoValidExecutionPinForSwitch_Error", "@@ must have a valid execution pin @@").ToString(), SwitchNode, ExecTriggeringPin);
|
|
return;
|
|
}
|
|
|
|
// Make sure that the selection pin is connected and valid for this block
|
|
UEdGraphPin* SelectionPin = SwitchNode->GetSelectionPin();
|
|
if ((SelectionPin == NULL) || !Context.ValidatePinType(SelectionPin, SwitchNode->GetPinType()))
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("NoValidSelectionPinForSwitch_Error", "@@ must have a valid execution pin @@").ToString(), SwitchNode, SelectionPin);
|
|
return;
|
|
}
|
|
|
|
// Find the boolean intermediate result term, so we can track whether the compare was successful
|
|
FBPTerminal* BoolTerm = BoolTermMap.FindRef(SwitchNode);
|
|
|
|
// Generate the output impulse from this node
|
|
UEdGraphPin* SwitchSelectionNet = FEdGraphUtilities::GetNetFromPin(SelectionPin);
|
|
FBPTerminal* SwitchSelectionTerm = Context.NetMap.FindRef(SwitchSelectionNet);
|
|
|
|
if ((BoolTerm != NULL) && (SwitchSelectionTerm != NULL))
|
|
{
|
|
UEdGraphNode* TargetNode = NULL;
|
|
UEdGraphPin* FuncPin = SwitchNode->GetFunctionPin();
|
|
FBPTerminal* FuncContext = Context.NetMap.FindRef(FuncPin);
|
|
UEdGraphPin* DefaultPin = SwitchNode->GetDefaultPin();
|
|
|
|
// We don't need to generate if checks if there are no connections to it if there is no default pin or if the default pin is not linked
|
|
// If there is a default pin that is linked then it would fall through to that default if we do not generate the cases
|
|
const bool bCanSkipUnlinkedCase = (DefaultPin == nullptr || DefaultPin->LinkedTo.Num() == 0);
|
|
|
|
// Pull out function to use
|
|
UClass* FuncClass = Cast<UClass>(FuncPin->PinType.PinSubCategoryObject.Get());
|
|
UFunction* FunctionPtr = FindUField<UFunction>(FuncClass, FuncPin->PinName);
|
|
check(FunctionPtr);
|
|
|
|
// Run thru all the output pins except for the default label
|
|
for (auto PinIt = SwitchNode->Pins.CreateIterator(); PinIt; ++PinIt)
|
|
{
|
|
UEdGraphPin* Pin = *PinIt;
|
|
|
|
if ((Pin->Direction == EGPD_Output) && (Pin != DefaultPin) && (!bCanSkipUnlinkedCase || Pin->LinkedTo.Num() > 0))
|
|
{
|
|
// Create a term for the switch case value
|
|
FBPTerminal* CaseValueTerm = new FBPTerminal();
|
|
Context.Literals.Add(CaseValueTerm);
|
|
CaseValueTerm->Name = SwitchNode->GetExportTextForPin(Pin);
|
|
CaseValueTerm->Type = SwitchNode->GetInnerCaseType();
|
|
CaseValueTerm->SourcePin = Pin;
|
|
CaseValueTerm->bIsLiteral = true;
|
|
|
|
// Call the comparison function associated with this switch node
|
|
FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(SwitchNode);
|
|
Statement.Type = KCST_CallFunction;
|
|
Statement.FunctionToCall = FunctionPtr;
|
|
Statement.FunctionContext = FuncContext;
|
|
Statement.bIsParentContext = false;
|
|
|
|
Statement.LHS = BoolTerm;
|
|
Statement.RHS.Add(SwitchSelectionTerm);
|
|
Statement.RHS.Add(CaseValueTerm);
|
|
|
|
// Jump to output if strings are actually equal
|
|
FBlueprintCompiledStatement& IfFailTest_SucceedAtBeingEqualGoto = Context.AppendStatementForNode(SwitchNode);
|
|
IfFailTest_SucceedAtBeingEqualGoto.Type = KCST_GotoIfNot;
|
|
IfFailTest_SucceedAtBeingEqualGoto.LHS = BoolTerm;
|
|
|
|
Context.GotoFixupRequestMap.Add(&IfFailTest_SucceedAtBeingEqualGoto, Pin);
|
|
}
|
|
}
|
|
|
|
// Finally output default pin
|
|
GenerateSimpleThenGoto(Context, *SwitchNode, DefaultPin);
|
|
}
|
|
else
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("ResolveTermPassed_Error", "Failed to resolve term passed into @@").ToString(), SelectionPin);
|
|
}
|
|
}
|
|
|
|
private:
|
|
FEdGraphPinType ExpectedSelectionPinType;
|
|
};
|
|
|
|
UK2Node_Switch::UK2Node_Switch(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
|
|
bHasDefaultPin = true;
|
|
bHasDefaultPinValueChanged = false;
|
|
}
|
|
|
|
void UK2Node_Switch::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
|
|
if (PropertyName == TEXT("bHasDefaultPin"))
|
|
{
|
|
// Signal to the reconstruction logic that the default pin value has changed
|
|
bHasDefaultPinValueChanged = true;
|
|
|
|
if (!bHasDefaultPin)
|
|
{
|
|
UEdGraphPin* DefaultPin = GetDefaultPin();
|
|
if (DefaultPin)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
K2Schema->BreakPinLinks(*DefaultPin, true);
|
|
}
|
|
}
|
|
|
|
ReconstructNode();
|
|
|
|
// Clear the default pin value change flag
|
|
bHasDefaultPinValueChanged = false;
|
|
|
|
}
|
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
|
}
|
|
|
|
FName UK2Node_Switch::GetSelectionPinName()
|
|
{
|
|
return SelectionPinName;
|
|
}
|
|
|
|
void UK2Node_Switch::AllocateDefaultPins()
|
|
{
|
|
// Add default pin
|
|
if (bHasDefaultPin)
|
|
{
|
|
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, DefaultPinName);
|
|
}
|
|
|
|
// Add exec input pin
|
|
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
|
|
|
|
// Create selection pin based on type
|
|
CreateSelectionPin();
|
|
|
|
// Create a new function pin
|
|
CreateFunctionPin();
|
|
|
|
// Create any case pins if required
|
|
CreateCasePins();
|
|
}
|
|
|
|
UK2Node::ERedirectType UK2Node_Switch::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const
|
|
{
|
|
// If the default pin setting has changed, return a match for the "execute" input pin (which will have swapped slots), so that we don't have to break any links to it
|
|
if(bHasDefaultPinValueChanged && ((OldPinIndex == 0) || (NewPinIndex == 0)))
|
|
{
|
|
if((bHasDefaultPin && OldPinIndex == 0 && NewPinIndex == 1)
|
|
|| (!bHasDefaultPin && OldPinIndex == 1 && NewPinIndex == 0))
|
|
{
|
|
return ERedirectType_Name;
|
|
}
|
|
}
|
|
else if (NewPin->PinName == OldPin->PinName)
|
|
{
|
|
// Compare the names, case-sensitively
|
|
return ERedirectType_Name;
|
|
}
|
|
return ERedirectType_None;
|
|
}
|
|
|
|
FLinearColor UK2Node_Switch::GetNodeTitleColor() const
|
|
{
|
|
// Use yellow for now
|
|
return FLinearColor(255.0f, 255.0f, 0.0f);
|
|
}
|
|
|
|
FSlateIcon UK2Node_Switch::GetIconAndTint(FLinearColor& OutColor) const
|
|
{
|
|
static FSlateIcon Icon(FAppStyle::GetAppStyleSetName(), "GraphEditor.Switch_16x");
|
|
return Icon;
|
|
}
|
|
|
|
void UK2Node_Switch::AddPinToSwitchNode()
|
|
{
|
|
const FName NewPinName = GetUniquePinName();
|
|
if (!NewPinName.IsNone())
|
|
{
|
|
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, NewPinName);
|
|
}
|
|
}
|
|
|
|
void UK2Node_Switch::RemovePinFromSwitchNode(UEdGraphPin* TargetPin)
|
|
{
|
|
// If removing the default pin, we'll need to reconstruct the node, so send a property changed event to handle that
|
|
if(bHasDefaultPin && TargetPin == GetDefaultPin())
|
|
{
|
|
FProperty* HasDefaultPinProperty = FindFProperty<FProperty>(GetClass(), "bHasDefaultPin");
|
|
if(HasDefaultPinProperty)
|
|
{
|
|
PreEditChange(HasDefaultPinProperty);
|
|
|
|
bHasDefaultPin = false;
|
|
|
|
FPropertyChangedEvent HasDefaultPinPropertyChangedEvent(HasDefaultPinProperty);
|
|
PostEditChangeProperty(HasDefaultPinPropertyChangedEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RemovePin(TargetPin);
|
|
|
|
TargetPin->MarkAsGarbage();
|
|
Pins.Remove(TargetPin);
|
|
}
|
|
}
|
|
|
|
bool UK2Node_Switch::CanRemoveExecutionPin(UEdGraphPin* TargetPin) const
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
// Don't allow removing last pin
|
|
int32 NumExecPins = 0;
|
|
for (int32 i = 0; i < Pins.Num(); ++i)
|
|
{
|
|
UEdGraphPin* PotentialPin = Pins[i];
|
|
if (K2Schema->IsExecPin(*PotentialPin) && (PotentialPin->Direction == EGPD_Output))
|
|
{
|
|
NumExecPins++;
|
|
}
|
|
}
|
|
|
|
return NumExecPins > 1;
|
|
}
|
|
|
|
// Returns the exec output pin name for a given 0-based index
|
|
FName UK2Node_Switch::GetPinNameGivenIndex(int32 Index) const
|
|
{
|
|
return *FString::Printf(TEXT("%d"), Index);
|
|
}
|
|
|
|
void UK2Node_Switch::CreateFunctionPin()
|
|
{
|
|
// Set properties on the function pin
|
|
UEdGraphPin* FunctionPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, FunctionClass, FunctionName);
|
|
FunctionPin->bDefaultValueIsReadOnly = true;
|
|
FunctionPin->bNotConnectable = true;
|
|
FunctionPin->bHidden = true;
|
|
|
|
UFunction* Function = FindUField<UFunction>(FunctionClass, FunctionName);
|
|
const bool bIsStaticFunc = Function ? Function->HasAllFunctionFlags(FUNC_Static) : false;
|
|
if (bIsStaticFunc)
|
|
{
|
|
// Wire up the self to the CDO of the class if it's not us
|
|
if (UBlueprint* BP = GetBlueprint())
|
|
{
|
|
UClass* FunctionOwnerClass = Function->GetOuterUClass();
|
|
if (!BP->SkeletonGeneratedClass || !BP->SkeletonGeneratedClass->IsChildOf(FunctionOwnerClass))
|
|
{
|
|
FunctionPin->DefaultObject = FunctionOwnerClass->GetDefaultObject();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UEdGraphPin* UK2Node_Switch::GetFunctionPin() const
|
|
{
|
|
//@TODO: Should probably use a specific index, though FindPin starts at 0, so this won't *currently* conflict with user created pins
|
|
return FindPin(FunctionName);
|
|
}
|
|
|
|
UEdGraphPin* UK2Node_Switch::GetSelectionPin() const
|
|
{
|
|
//@TODO: Should probably use a specific index, though FindPin starts at 0, so this won't *currently* conflict with user created pins
|
|
return FindPin(SelectionPinName);
|
|
}
|
|
|
|
UEdGraphPin* UK2Node_Switch::GetDefaultPin() const
|
|
{
|
|
return (bHasDefaultPin)
|
|
? Pins[0]
|
|
: NULL;
|
|
}
|
|
|
|
FNodeHandlingFunctor* UK2Node_Switch::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
|
|
{
|
|
return new FKCHandler_Switch(CompilerContext);
|
|
}
|
|
|
|
FText UK2Node_Switch::GetMenuCategory() const
|
|
{
|
|
static FNodeTextCache CachedCategory;
|
|
if (CachedCategory.IsOutOfDate(this))
|
|
{
|
|
// FText::Format() is slow, so we cache this to save on performance
|
|
CachedCategory.SetCachedText(FEditorCategoryUtils::BuildCategoryString(FCommonEditorCategory::FlowControl, LOCTEXT("ActionMenuCategory", "Switch")), this);
|
|
}
|
|
return CachedCategory;
|
|
}
|
|
|
|
FString UK2Node_Switch::GetExportTextForPin(const UEdGraphPin* Pin) const
|
|
{
|
|
return Pin->PinName.ToString();
|
|
}
|
|
|
|
FEdGraphPinType UK2Node_Switch::GetInnerCaseType() const
|
|
{
|
|
UEdGraphPin* SelectionPin = GetSelectionPin();
|
|
if (ensure(SelectionPin))
|
|
{
|
|
return SelectionPin->PinType;
|
|
}
|
|
return FEdGraphPinType();
|
|
}
|
|
#undef LOCTEXT_NAMESPACE
|