// Copyright Epic Games, Inc. All Rights Reserved. #include "K2Node_ConvertAsset.h" #include "BlueprintActionDatabaseRegistrar.h" #include "BlueprintActionFilter.h" #include "BlueprintFieldNodeSpawner.h" #include "BlueprintNodeBinder.h" #include "BlueprintNodeSpawner.h" #include "Containers/Array.h" #include "Containers/EnumAsByte.h" #include "Delegates/Delegate.h" #include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphPin.h" #include "EdGraph/EdGraphSchema.h" #include "EdGraphSchema_K2.h" #include "Engine/Blueprint.h" #include "Engine/MemberReference.h" #include "HAL/PlatformMath.h" #include "Internationalization/Internationalization.h" #include "K2Node_CallFunction.h" #include "K2Node_ClassDynamicCast.h" #include "K2Node_DynamicCast.h" #include "Kismet/KismetSystemLibrary.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/CompilerResultsLog.h" #include "KismetCompiler.h" #include "Misc/AssertionMacros.h" #include "Templates/Casts.h" #include "Templates/SubclassOf.h" #include "UObject/Class.h" #include "UObject/NameTypes.h" #include "UObject/Object.h" #include "UObject/UnrealNames.h" #include "UObject/WeakObjectPtr.h" #include "UObject/WeakObjectPtrTemplates.h" class FString; #define LOCTEXT_NAMESPACE "K2Node_ConvertAsset" namespace UK2Node_ConvertAssetImpl { static const FName InputPinName("Input"); static const FName OutputPinName("Output"); } UEdGraphPin* UK2Node_ConvertAsset::GetInputPin() const { return FindPin(UK2Node_ConvertAssetImpl::InputPinName); } UEdGraphPin* UK2Node_ConvertAsset::GetOutputPin() const { return FindPin(UK2Node_ConvertAssetImpl::OutputPinName); } UClass* UK2Node_ConvertAsset::GetTargetClass() const { UEdGraphPin* InputPin = FindPin(UK2Node_ConvertAssetImpl::InputPinName); bool bIsConnected = InputPin && InputPin->LinkedTo.Num() && InputPin->LinkedTo[0]; UEdGraphPin* SourcePin = bIsConnected ? InputPin->LinkedTo[0] : nullptr; return SourcePin ? FBlueprintEditorUtils::GetTypeForPin(*SourcePin) : nullptr; } bool UK2Node_ConvertAsset::IsClassType() const { UEdGraphPin* InputPin = FindPin(UK2Node_ConvertAssetImpl::InputPinName); bool bIsConnected = InputPin && InputPin->LinkedTo.Num() && InputPin->LinkedTo[0]; UEdGraphPin* SourcePin = bIsConnected ? InputPin->LinkedTo[0] : nullptr; return SourcePin ? (SourcePin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftClass || SourcePin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class) : false; } bool UK2Node_ConvertAsset::IsConvertToSoft() const { UEdGraphPin* InputPin = FindPin(UK2Node_ConvertAssetImpl::InputPinName); bool bIsConnected = InputPin && InputPin->LinkedTo.Num() && InputPin->LinkedTo[0]; UEdGraphPin* SourcePin = bIsConnected ? InputPin->LinkedTo[0] : nullptr; return SourcePin ? (SourcePin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class || SourcePin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object) : false; } bool UK2Node_ConvertAsset::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const { UEdGraphPin* InputPin = FindPin(UK2Node_ConvertAssetImpl::InputPinName); if (InputPin && OtherPin && (InputPin == MyPin) && (MyPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard)) { if ((OtherPin->PinType.PinCategory != UEdGraphSchema_K2::PC_SoftObject) && (OtherPin->PinType.PinCategory != UEdGraphSchema_K2::PC_SoftClass) && (OtherPin->PinType.PinCategory != UEdGraphSchema_K2::PC_Object) && (OtherPin->PinType.PinCategory != UEdGraphSchema_K2::PC_Class)) { return true; } } return false; } void UK2Node_ConvertAsset::RefreshPinTypes() { const UEdGraphSchema_K2* K2Schema = CastChecked(GetSchema()); UEdGraphPin* InputPin = FindPin(UK2Node_ConvertAssetImpl::InputPinName); UEdGraphPin* OutputPin = FindPin(UK2Node_ConvertAssetImpl::OutputPinName); ensure(InputPin && OutputPin); if (InputPin && OutputPin) { const bool bIsConnected = InputPin->LinkedTo.Num() > 0; UClass* TargetType = bIsConnected ? GetTargetClass() : nullptr; const bool bIsClassType = bIsConnected ? IsClassType() : false; const bool bIsConvertToSoft = bIsConnected ? IsConvertToSoft() : false; FName InputCategory = UEdGraphSchema_K2::PC_Wildcard; FName OutputCategory = UEdGraphSchema_K2::PC_Wildcard; if (bIsConnected) { if (bIsConvertToSoft) { InputCategory = (bIsClassType ? UEdGraphSchema_K2::PC_Class : UEdGraphSchema_K2::PC_Object); OutputCategory = (bIsClassType ? UEdGraphSchema_K2::PC_SoftClass : UEdGraphSchema_K2::PC_SoftObject); } else { InputCategory = (bIsClassType ? UEdGraphSchema_K2::PC_SoftClass : UEdGraphSchema_K2::PC_SoftObject); OutputCategory = (bIsClassType ? UEdGraphSchema_K2::PC_Class : UEdGraphSchema_K2::PC_Object); } } InputPin->PinType = FEdGraphPinType(InputCategory, NAME_None, TargetType, EPinContainerType::None, false, FEdGraphTerminalType() ); OutputPin->PinType = FEdGraphPinType(OutputCategory, NAME_None, TargetType, EPinContainerType::None, false, FEdGraphTerminalType() ); PinTypeChanged(InputPin); PinTypeChanged(OutputPin); if (OutputPin->LinkedTo.Num()) { TArray PinsToUnlink = OutputPin->LinkedTo; UClass const* CallingContext = nullptr; if (UBlueprint const* Blueprint = GetBlueprint()) { CallingContext = Blueprint->GeneratedClass; if (CallingContext == NULL) { CallingContext = Blueprint->ParentClass; } } for (UEdGraphPin* TargetPin : PinsToUnlink) { if (TargetPin && !K2Schema->ArePinsCompatible(OutputPin, TargetPin, CallingContext)) { OutputPin->BreakLinkTo(TargetPin); } } } } } void UK2Node_ConvertAsset::PostReconstructNode() { RefreshPinTypes(); Super::PostReconstructNode(); } void UK2Node_ConvertAsset::NotifyPinConnectionListChanged(UEdGraphPin* Pin) { Super::NotifyPinConnectionListChanged(Pin); if (Pin && (UK2Node_ConvertAssetImpl::InputPinName == Pin->PinName)) { RefreshPinTypes(); GetGraph()->NotifyGraphChanged(); } } void UK2Node_ConvertAsset::AllocateDefaultPins() { CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, UK2Node_ConvertAssetImpl::InputPinName); CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Wildcard, UK2Node_ConvertAssetImpl::OutputPinName); } UK2Node::ERedirectType UK2Node_ConvertAsset::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const { // Names changed, only thing that matters is the direction if (NewPin->Direction == OldPin->Direction) { return UK2Node::ERedirectType_Name; } return UK2Node::ERedirectType_None; } void UK2Node_ConvertAsset::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const { struct GetMenuActions_Utils { static void OverrideUI(FBlueprintActionContext const& Context, IBlueprintNodeBinder::FBindingSet const& /*Bindings*/, FBlueprintActionUiSpec* UiSpecOut) { bool bShouldPromote = false; bool bIsMakeSoft = false; for (UEdGraphPin* Pin : Context.Pins) { // Auto promote for soft refs if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftClass || Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject) { if (Pin->Direction == EGPD_Output) { // The wildcards don't get set when connecting right side, so don't promote bShouldPromote = true; } } // Change title for hard refs if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class || Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object) { if (Pin->Direction == EGPD_Output) { bIsMakeSoft = true; } } } if (bShouldPromote) { UiSpecOut->Category = NSLOCTEXT("BlueprintFunctionNodeSpawner", "EmptyFunctionCategory", "|"); } if (bIsMakeSoft) { UiSpecOut->MenuName = LOCTEXT("MakeSoftTitle", "Make Soft Reference"); } } }; UClass* ActionKey = GetClass(); if (ActionRegistrar.IsOpenForRegistration(ActionKey)) { UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); check(NodeSpawner != nullptr); NodeSpawner->DynamicUiSignatureGetter = UBlueprintFieldNodeSpawner::FUiSpecOverrideDelegate::CreateStatic(GetMenuActions_Utils::OverrideUI); ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner); } } void UK2Node_ConvertAsset::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); UClass* TargetType = GetTargetClass(); if (TargetType && Schema && (2 == Pins.Num())) { const bool bIsClassType = IsClassType(); UClass *TargetClass = GetTargetClass(); bool bIsErrorFree = true; if (IsConvertToSoft()) { //Create Convert Function UK2Node_CallFunction* ConvertToObjectFunc = CompilerContext.SpawnIntermediateNode(this, SourceGraph); FName ConvertFunctionName = bIsClassType ? GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, Conv_ClassToSoftClassReference) : GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, Conv_ObjectToSoftObjectReference); ConvertToObjectFunc->FunctionReference.SetExternalMember(ConvertFunctionName, UKismetSystemLibrary::StaticClass()); ConvertToObjectFunc->AllocateDefaultPins(); //Connect input to convert UEdGraphPin* InputPin = FindPin(UK2Node_ConvertAssetImpl::InputPinName); const FName ConvertInputName = bIsClassType ? FName(TEXT("Class")) : FName(TEXT("Object")); UEdGraphPin* ConvertInput = ConvertToObjectFunc->FindPin(ConvertInputName); bIsErrorFree = InputPin && ConvertInput && CompilerContext.MovePinLinksToIntermediate(*InputPin, *ConvertInput).CanSafeConnect(); if (UEdGraphPin* ConvertOutput = ConvertToObjectFunc->GetReturnValuePin()) { // Force the convert output pin to the right type. This is only safe because all asset ptrs are type-compatible, the cast is done at resolution time ConvertOutput->PinType.PinSubCategoryObject = TargetClass; UEdGraphPin* OutputPin = FindPin(UK2Node_ConvertAssetImpl::OutputPinName); bIsErrorFree &= OutputPin && CompilerContext.MovePinLinksToIntermediate(*OutputPin, *ConvertOutput).CanSafeConnect(); } else { bIsErrorFree = false; } } else { //Create Convert Function UK2Node_CallFunction* ConvertToObjectFunc = CompilerContext.SpawnIntermediateNode(this, SourceGraph); FName ConvertFunctionName = bIsClassType ? GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, Conv_SoftClassReferenceToClass) : GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, Conv_SoftObjectReferenceToObject); ConvertToObjectFunc->FunctionReference.SetExternalMember(ConvertFunctionName, UKismetSystemLibrary::StaticClass()); ConvertToObjectFunc->AllocateDefaultPins(); //Connect input to convert UEdGraphPin* InputPin = FindPin(UK2Node_ConvertAssetImpl::InputPinName); const FName ConvertInputName = bIsClassType ? FName(TEXT("SoftClass")) : FName(TEXT("SoftObject")); UEdGraphPin* ConvertInput = ConvertToObjectFunc->FindPin(ConvertInputName); bIsErrorFree = InputPin && ConvertInput && CompilerContext.MovePinLinksToIntermediate(*InputPin, *ConvertInput).CanSafeConnect(); UEdGraphPin* ConvertOutput = ConvertToObjectFunc->GetReturnValuePin(); UEdGraphPin* InnerOutput = nullptr; if (UObject::StaticClass() != TargetType) { //Create Cast Node UK2Node_DynamicCast* CastNode = bIsClassType ? CompilerContext.SpawnIntermediateNode(this, SourceGraph) : CompilerContext.SpawnIntermediateNode(this, SourceGraph); CastNode->SetPurity(true); CastNode->TargetType = TargetType; CastNode->AllocateDefaultPins(); // Connect Object/Class to Cast UEdGraphPin* CastInput = CastNode->GetCastSourcePin(); bIsErrorFree &= ConvertOutput && CastInput && Schema->TryCreateConnection(ConvertOutput, CastInput); // Connect output to cast InnerOutput = CastNode->GetCastResultPin(); } else { InnerOutput = ConvertOutput; } UEdGraphPin* OutputPin = FindPin(UK2Node_ConvertAssetImpl::OutputPinName); bIsErrorFree &= OutputPin && InnerOutput && CompilerContext.MovePinLinksToIntermediate(*OutputPin, *InnerOutput).CanSafeConnect(); } if (!bIsErrorFree) { CompilerContext.MessageLog.Error(*LOCTEXT("InternalConnectionError", "K2Node_ConvertAsset: Internal connection error. @@").ToString(), this); } BreakAllNodeLinks(); } } FText UK2Node_ConvertAsset::GetCompactNodeTitle() const { return FText::FromString(TEXT("\x2022")); } FText UK2Node_ConvertAsset::GetMenuCategory() const { return LOCTEXT("UK2Node_LoadAssetGetMenuCategory", "Utilities"); } FText UK2Node_ConvertAsset::GetNodeTitle(ENodeTitleType::Type TitleType) const { const bool bIsConvertToSoft = IsConvertToSoft(); if (bIsConvertToSoft) { return LOCTEXT("MakeSoftTitle", "Make Soft Reference"); } return LOCTEXT("UK2Node_ConvertAssetGetNodeTitle", "Resolve Soft Reference"); } FText UK2Node_ConvertAsset::GetKeywords() const { // Return old name here return LOCTEXT("UK2Node_ConvertAssetGetKeywords", "Resolve Asset ID Convert Soft"); } FText UK2Node_ConvertAsset::GetTooltipText() const { UEdGraphPin* InputPin = FindPin(UK2Node_ConvertAssetImpl::InputPinName); const bool bIsConvertToSoft = IsConvertToSoft(); if (bIsConvertToSoft) { return LOCTEXT("MakeSoftTooltip", "Takes a hard Class or Object reference and makes a Soft Reference."); } else if (InputPin && InputPin->LinkedTo.Num() > 0) { return LOCTEXT("UK2Node_ConvertAssetGetTooltipText", "Resolves a Soft Reference and gets the Class or Object it is pointing to. If the object isn't already loaded in memory this will return none."); } return LOCTEXT("UnknownTypeTooltip", "Resolves or makes a Soft Reference, connect a soft or hard reference to the input pin."); } #undef LOCTEXT_NAMESPACE