// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. #include "BlueprintGraphPrivatePCH.h" #include "KismetCompiler.h" #include "BlueprintNodeSpawner.h" #include "EditorCategoryUtils.h" #define LOCTEXT_NAMESPACE "K2Node_BreakStruct" ////////////////////////////////////////////////////////////////////////// // FKCHandler_BreakStruct class FKCHandler_BreakStruct : public FNodeHandlingFunctor { public: FKCHandler_BreakStruct(FKismetCompilerContext& InCompilerContext) : FNodeHandlingFunctor(InCompilerContext) { } FBPTerminal* RegisterInputTerm(FKismetFunctionContext& Context, UK2Node_BreakStruct* Node) { check(NULL != Node); if(NULL == Node->StructType) { CompilerContext.MessageLog.Error(*LOCTEXT("BreakStruct_UnknownStructure_Error", "Unknown structure to break for @@").ToString(), Node); return NULL; } //Find input pin UEdGraphPin* InputPin = NULL; for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) { UEdGraphPin* Pin = Node->Pins[PinIndex]; if(Pin && (EGPD_Input == Pin->Direction)) { InputPin = Pin; break; } } check(NULL != InputPin); //Find structure source net UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(InputPin); check(NULL != Net); FBPTerminal** FoundTerm = Context.NetMap.Find(Net); FBPTerminal* Term = FoundTerm ? *FoundTerm : NULL; if(NULL == Term) { // Dont allow literal if ((Net->Direction == EGPD_Input) && (Net->LinkedTo.Num() == 0)) { CompilerContext.MessageLog.Error(*LOCTEXT("InvalidNoInputStructure_Error", "No input structure to break for @@").ToString(), Net); return NULL; } // standard register net else { Term = new (Context.IsEventGraph() ? Context.EventGraphLocals : Context.Locals) FBPTerminal(); Term->CopyFromPin(Net, Context.NetNameMap->MakeValidName(Net)); } Context.NetMap.Add(Net, Term); } UStruct* StructInTerm = Cast(Term->Type.PinSubCategoryObject.Get()); if(NULL == StructInTerm || !StructInTerm->IsChildOf(Node->StructType)) { CompilerContext.MessageLog.Error(*LOCTEXT("BreakStruct_NoMatch_Error", "Structures don't match for @@").ToString(), Node); } return Term; } void RegisterOutputTerm(FKismetFunctionContext& Context, UScriptStruct* StructType, UEdGraphPin* Net, FBPTerminal* ContextTerm) { UProperty* BoundProperty = FindField(StructType, *(Net->PinName)); if (BoundProperty != NULL) { FBPTerminal* Term = new (Context.IsEventGraph() ? Context.EventGraphLocals : Context.Locals) FBPTerminal(); Term->CopyFromPin(Net, Net->PinName); Term->AssociatedVarProperty = BoundProperty; Context.NetMap.Add(Net, Term); Term->Context = ContextTerm; if (BoundProperty->HasAnyPropertyFlags(CPF_BlueprintReadOnly)) { Term->bIsConst = true; } } else { CompilerContext.MessageLog.Error(TEXT("Failed to find a struct member for @@"), Net); } } virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* InNode) override { UK2Node_BreakStruct* Node = Cast(InNode); check(NULL != Node); if(!UK2Node_BreakStruct::CanBeBroken(Node->StructType)) { CompilerContext.MessageLog.Warning(*LOCTEXT("BreakStruct_NoBreak_Error", "The structure cannot be broken using generic 'break' node @@. Try use specialized 'break' function if available.").ToString(), Node); } if(FBPTerminal* StructContextTerm = RegisterInputTerm(Context, Node)) { for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) { UEdGraphPin* Pin = Node->Pins[PinIndex]; if(NULL != Pin && EGPD_Output == Pin->Direction) { RegisterOutputTerm(Context, Node->StructType, Pin, StructContextTerm); } } } } }; UK2Node_BreakStruct::UK2Node_BreakStruct(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP) { } bool UK2Node_BreakStruct::CanCreatePinForProperty(const UProperty* Property) { const UEdGraphSchema_K2* Schema = GetDefault(); FEdGraphPinType DumbGraphPinType; const bool bConvertable = Schema->ConvertPropertyToPinType(Property, /*out*/ DumbGraphPinType); //TODO: remove CPF_Edit in a next release. const bool bVisible = Property ? Property->HasAnyPropertyFlags(CPF_Edit | CPF_BlueprintVisible) : false; return bVisible && bConvertable; } bool UK2Node_BreakStruct::CanBeBroken(const UScriptStruct* Struct) { const UEdGraphSchema_K2* Schema = GetDefault(); if(Struct && Schema && !Struct->HasMetaData(TEXT("HasNativeBreak"))) { for (TFieldIterator It(Struct); It; ++It) { if (CanCreatePinForProperty(*It)) { return true; } } } return false; } void UK2Node_BreakStruct::AllocateDefaultPins() { const UEdGraphSchema_K2* Schema = GetDefault(); if(Schema && StructType) { CreatePin(EGPD_Input, Schema->PC_Struct, TEXT(""), StructType, false, true, StructType->GetName(), true); struct FBreakStructPinManager : public FStructOperationOptionalPinManager { virtual bool CanTreatPropertyAsOptional(UProperty* TestProperty) const override { return UK2Node_BreakStruct::CanCreatePinForProperty(TestProperty); } }; { FBreakStructPinManager OptionalPinManager; OptionalPinManager.RebuildPropertyList(ShowPinForProperties, StructType); OptionalPinManager.CreateVisiblePins(ShowPinForProperties, StructType, EGPD_Output, this); } // When struct has a lot of fields, mark their pins as advanced if(Pins.Num() > 5) { if(ENodeAdvancedPins::NoPins == AdvancedPinDisplay) { AdvancedPinDisplay = ENodeAdvancedPins::Hidden; } for(int32 PinIndex = 3; PinIndex < Pins.Num(); ++PinIndex) { if(UEdGraphPin * EdGraphPin = Pins[PinIndex]) { EdGraphPin->bAdvancedView = true; } } } } } FText UK2Node_BreakStruct::GetNodeTitle(ENodeTitleType::Type TitleType) const { FFormatNamedArguments Args; Args.Add(TEXT("StructName"), FText::FromString(StructType ? StructType->GetName() : FString())); return FText::Format(LOCTEXT("BreakNodeTitle", "Break {StructName}"), Args); } FString UK2Node_BreakStruct::GetTooltip() const { return FString::Printf( *LOCTEXT("MakeStruct_Tooltip", "Adds a node that breaks a '%s' into its member fields").ToString(), *(StructType ? StructType->GetName() : FString()) ); } void UK2Node_BreakStruct::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const { if(!StructType) { MessageLog.Error(*LOCTEXT("NoStruct_Error", "No Struct in @@").ToString(), this); } else { bool bHasAnyBlueprintVisibleProperty = false; for (TFieldIterator It(StructType); It; ++It) { const UProperty* Property = *It; if (CanCreatePinForProperty(Property)) { const bool bIsBlueprintVisible = Property->HasAnyPropertyFlags(CPF_BlueprintVisible) || (Property->GetOwnerStruct() && Property->GetOwnerStruct()->IsA()); bHasAnyBlueprintVisibleProperty |= bIsBlueprintVisible; const UEdGraphPin* Pin = Property ? FindPin(Property->GetName()) : NULL; const bool bIsLinked = Pin && Pin->LinkedTo.Num(); if (!bIsBlueprintVisible && bIsLinked) { MessageLog.Warning(*LOCTEXT("PropertyIsNotBPVisible_Warning", "@@ - the native property is not tagged as BlueprintReadWrite or BlueprintReadOnly, the pin will be removed in a future release.").ToString(), Pin); } if ((Property->ArrayDim > 1) && bIsLinked) { MessageLog.Warning(*LOCTEXT("StaticArray_Warning", "@@ - the native property is a static array, which is not supported by blueprints").ToString(), Pin); } } } if (!bHasAnyBlueprintVisibleProperty) { MessageLog.Warning(*LOCTEXT("StructHasNoBPVisibleProperties_Warning", "@@ has no property tagged as BlueprintReadWrite or BlueprintReadOnly. The node will be removed in a future release.").ToString(), this); } } } FLinearColor UK2Node_BreakStruct::GetNodeTitleColor() const { if(const UEdGraphSchema_K2* K2Schema = GetDefault()) { FEdGraphPinType PinType; PinType.PinCategory = K2Schema->PC_Struct; PinType.PinSubCategoryObject = StructType; return K2Schema->GetPinTypeColor(PinType); } return UK2Node::GetNodeTitleColor(); } UK2Node::ERedirectType UK2Node_BreakStruct::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const { ERedirectType Result = UK2Node::DoPinsMatchForReconstruction(NewPin, NewPinIndex, OldPin, OldPinIndex); if ((ERedirectType_None == Result) && NewPin && OldPin) { if ((EGPD_Input == NewPin->Direction) && (EGPD_Input == OldPin->Direction)) { const UEdGraphSchema_K2* K2Schema = GetDefault(); if (K2Schema->ArePinTypesCompatible( NewPin->PinType, OldPin->PinType)) { Result = ERedirectType_Custom; } } else if ((EGPD_Output == NewPin->Direction) && (EGPD_Output == OldPin->Direction)) { TMap* StructRedirects = UStruct::TaggedPropertyRedirects.Find(StructType->GetFName()); if (StructRedirects) { FName* PropertyRedirect = StructRedirects->Find(FName(*OldPin->PinName)); if (PropertyRedirect) { Result = ((FCString::Stricmp(*PropertyRedirect->ToString(), *NewPin->PinName) != 0) ? ERedirectType_None : ERedirectType_Name); } } } } return Result; } FNodeHandlingFunctor* UK2Node_BreakStruct::CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const { return new FKCHandler_BreakStruct(CompilerContext); } void UK2Node_BreakStruct::GetMenuActions(TArray& ActionListOut) const { for (TObjectIterator StructIt; StructIt; ++StructIt) { UScriptStruct const* Struct = (*StructIt); // we only want to add autonomous structs 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 structs that were changed). bool bIsStandaloneStruct = Struct->GetOuter()->IsA(UPackage::StaticClass()); if (!bIsStandaloneStruct || !UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Struct) || !CanBeBroken(Struct)) { continue; } auto CustomizeBreakNodeLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode, TWeakObjectPtr StructPtr) { UK2Node_BreakStruct* BreakNode = CastChecked(NewNode); if (StructPtr.IsValid()) { BreakNode->StructType = StructPtr.Get(); } }; UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); check(NodeSpawner != nullptr); ActionListOut.Add(NodeSpawner); TWeakObjectPtr StructPtr = Struct; NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeBreakNodeLambda, StructPtr); } } FText UK2Node_BreakStruct::GetMenuCategory() const { return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Struct); } #undef LOCTEXT_NAMESPACE