Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_GenericCreateObject.cpp
Lina Halper b0bdc590d5 Merging //UE4/Dev-Main to Dev-Anim (//UE4/Dev-Anim)
#lockdown: thomas.sarkanen
#fyi: Laurent.Delayen
#rb: none

[CL 6861656 by Lina Halper in Dev-Anim branch]
2019-06-06 12:33:54 -04:00

153 lines
6.0 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "K2Node_GenericCreateObject.h"
#include "Kismet/GameplayStatics.h"
#include "K2Node_CallFunction.h"
#include "KismetCompilerMisc.h"
#include "KismetCompiler.h"
#define LOCTEXT_NAMESPACE "K2Node_GenericCreateObject"
struct FK2Node_GenericCreateObject_Utils
{
static bool CanSpawnObjectOfClass(TSubclassOf<UObject> ObjectClass, bool bAllowAbstract)
{
// Initially include types that meet the basic requirements.
// Note: CLASS_Deprecated is an inherited class flag, so any subclass of an explicitly-deprecated class also cannot be spawned.
bool bCanSpawnObject = (nullptr != *ObjectClass)
&& (bAllowAbstract || !ObjectClass->HasAnyClassFlags(CLASS_Abstract))
&& !ObjectClass->HasAnyClassFlags(CLASS_Deprecated | CLASS_NewerVersionExists);
// UObject is a special case where if we are allowing abstract we are going to allow it through even though it doesn't have BlueprintType on it
if (bCanSpawnObject && (!bAllowAbstract || (*ObjectClass != UObject::StaticClass())))
{
static const FName BlueprintTypeName(TEXT("BlueprintType"));
static const FName NotBlueprintTypeName(TEXT("NotBlueprintType"));
static const FName DontUseGenericSpawnObjectName(TEXT("DontUseGenericSpawnObject"));
auto IsClassAllowedLambda = [](const UClass* InClass)
{
return InClass != AActor::StaticClass()
&& InClass != UActorComponent::StaticClass();
};
// Exclude all types in the initial set by default.
bCanSpawnObject = false;
const UClass* CurrentClass = ObjectClass;
// Climb up the class hierarchy and look for "BlueprintType." If "NotBlueprintType" is seen first, or if the class is not allowed, then stop searching.
while (!bCanSpawnObject && CurrentClass != nullptr && !CurrentClass->GetBoolMetaData(NotBlueprintTypeName) && IsClassAllowedLambda(CurrentClass))
{
// Include any type that either includes or inherits 'BlueprintType'
bCanSpawnObject = CurrentClass->GetBoolMetaData(BlueprintTypeName);
// Stop searching if we encounter 'BlueprintType' with 'DontUseGenericSpawnObject'
if (bCanSpawnObject && CurrentClass->GetBoolMetaData(DontUseGenericSpawnObjectName))
{
bCanSpawnObject = false;
break;
}
CurrentClass = CurrentClass->GetSuperClass();
}
// If we validated the given class, continue walking up the hierarchy to make sure we exclude it if it's an Actor or ActorComponent derivative.
while (bCanSpawnObject && CurrentClass != nullptr)
{
bCanSpawnObject &= IsClassAllowedLambda(CurrentClass);
CurrentClass = CurrentClass->GetSuperClass();
}
}
return bCanSpawnObject;
}
};
void UK2Node_GenericCreateObject::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
UK2Node_CallFunction* CallCreateNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
CallCreateNode->FunctionReference.SetExternalMember(GET_FUNCTION_NAME_CHECKED(UGameplayStatics, SpawnObject), UGameplayStatics::StaticClass());
CallCreateNode->AllocateDefaultPins();
// store off the class to spawn before we mutate pin connections:
UClass* ClassToSpawn = GetClassToSpawn();
bool bSucceeded = true;
//connect exe
{
UEdGraphPin* SpawnExecPin = GetExecPin();
UEdGraphPin* CallExecPin = CallCreateNode->GetExecPin();
bSucceeded &= SpawnExecPin && CallExecPin && CompilerContext.MovePinLinksToIntermediate(*SpawnExecPin, *CallExecPin).CanSafeConnect();
}
//connect class
{
UEdGraphPin* SpawnClassPin = GetClassPin();
UEdGraphPin* CallClassPin = CallCreateNode->FindPin(TEXT("ObjectClass"));
bSucceeded &= SpawnClassPin && CallClassPin && CompilerContext.MovePinLinksToIntermediate(*SpawnClassPin, *CallClassPin).CanSafeConnect();
}
//connect outer
{
UEdGraphPin* SpawnOuterPin = GetOuterPin();
UEdGraphPin* CallOuterPin = CallCreateNode->FindPin(TEXT("Outer"));
bSucceeded &= SpawnOuterPin && CallOuterPin && CompilerContext.MovePinLinksToIntermediate(*SpawnOuterPin, *CallOuterPin).CanSafeConnect();
}
UEdGraphPin* CallResultPin = nullptr;
//connect result
{
UEdGraphPin* SpawnResultPin = GetResultPin();
CallResultPin = CallCreateNode->GetReturnValuePin();
// cast HACK. It should be safe. The only problem is native code generation.
if (SpawnResultPin && CallResultPin)
{
CallResultPin->PinType = SpawnResultPin->PinType;
}
bSucceeded &= SpawnResultPin && CallResultPin && CompilerContext.MovePinLinksToIntermediate(*SpawnResultPin, *CallResultPin).CanSafeConnect();
}
//assign exposed values and connect then
{
UEdGraphPin* LastThen = FKismetCompilerUtilities::GenerateAssignmentNodes(CompilerContext, SourceGraph, CallCreateNode, this, CallResultPin, ClassToSpawn);
UEdGraphPin* SpawnNodeThen = GetThenPin();
bSucceeded &= SpawnNodeThen && LastThen && CompilerContext.MovePinLinksToIntermediate(*SpawnNodeThen, *LastThen).CanSafeConnect();
}
BreakAllNodeLinks();
if (!bSucceeded)
{
CompilerContext.MessageLog.Error(*LOCTEXT("GenericCreateObject_Error", "ICE: GenericCreateObject error @@").ToString(), this);
}
}
void UK2Node_GenericCreateObject::EarlyValidation(class FCompilerResultsLog& MessageLog) const
{
Super::EarlyValidation(MessageLog);
UEdGraphPin* ClassPin = GetClassPin(&Pins);
const bool bAllowAbstract = ClassPin && ClassPin->LinkedTo.Num();
UClass* ClassToSpawn = GetClassToSpawn();
if (!FK2Node_GenericCreateObject_Utils::CanSpawnObjectOfClass(ClassToSpawn, bAllowAbstract))
{
MessageLog.Error(*FText::Format(LOCTEXT("GenericCreateObject_WrongClassFmt", "Cannot construct objects of type '{0}' in @@"), FText::FromString(GetPathNameSafe(ClassToSpawn))).ToString(), this);
}
UEdGraphPin* OuterPin = GetOuterPin();
if (!OuterPin || (!OuterPin->DefaultObject && !OuterPin->LinkedTo.Num()))
{
MessageLog.Error(*LOCTEXT("GenericCreateObject_NoOuter", "Outer object is required in @@").ToString(), this);
}
}
bool UK2Node_GenericCreateObject::IsCompatibleWithGraph(const UEdGraph* TargetGraph) const
{
return UK2Node::IsCompatibleWithGraph(TargetGraph);
}
#undef LOCTEXT_NAMESPACE