205 lines
7.9 KiB
C++
205 lines
7.9 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "K2Node_GenericCreateObject.h"
|
|
|
|
#include "Components/ActorComponent.h"
|
|
#include "Containers/Array.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "EdGraph/EdGraphNode.h"
|
|
#include "EdGraph/EdGraphPin.h"
|
|
#include "EdGraph/EdGraphSchema.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "Engine/MemberReference.h"
|
|
#include "GameFramework/Actor.h"
|
|
#include "HAL/Platform.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "Internationalization/Text.h"
|
|
#include "K2Node.h"
|
|
#include "K2Node_CallFunction.h"
|
|
#include "K2Node_Self.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
#include "Kismet2/CompilerResultsLog.h"
|
|
#include "KismetCompiler.h"
|
|
#include "KismetCompilerMisc.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Templates/SubclassOf.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/NameTypes.h"
|
|
#include "UObject/Object.h"
|
|
#include "UObject/UObjectBaseUtility.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();
|
|
|
|
// connect GenericCreateObject's self pin to the self node
|
|
bool bSucceeded = ExpandDefaultToSelfPin(CompilerContext, SourceGraph, CallCreateNode);
|
|
|
|
// store off the class to spawn before we mutate pin connections:
|
|
UClass* ClassToSpawn = GetClassToSpawn();
|
|
|
|
//connect exe
|
|
{
|
|
UEdGraphPin* SpawnExecPin = GetExecPin();
|
|
UEdGraphPin* CallExecPin = CallCreateNode->GetExecPin();
|
|
bSucceeded &= SpawnExecPin && CallExecPin && CompilerContext.MovePinLinksToIntermediate(*SpawnExecPin, *CallExecPin).CanSafeConnect();
|
|
}
|
|
|
|
UEdGraphPin* CallClassPin = nullptr;
|
|
//connect class
|
|
{
|
|
UEdGraphPin* SpawnClassPin = GetClassPin();
|
|
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, CallClassPin);
|
|
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);
|
|
}
|
|
}
|
|
|
|
bool UK2Node_GenericCreateObject::IsCompatibleWithGraph(const UEdGraph* TargetGraph) const
|
|
{
|
|
return UK2Node::IsCompatibleWithGraph(TargetGraph);
|
|
}
|
|
|
|
bool UK2Node_GenericCreateObject::ExpandDefaultToSelfPin(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph, UK2Node_CallFunction* CallCreateNode)
|
|
{
|
|
// ensure valid input
|
|
if (!SourceGraph || !CallCreateNode)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Connect a self reference pin if there is a TScriptInterface default to self
|
|
if (const UFunction* TargetFunc = CallCreateNode->GetTargetFunction())
|
|
{
|
|
const FString& MetaData = TargetFunc->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);
|
|
if (!MetaData.IsEmpty())
|
|
{
|
|
// Get the Self Pin from this so we can default it correctly
|
|
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
|
|
if (UEdGraphPin* DefaultToSelfPin = Schema->FindSelfPin(*this, EGPD_Input))
|
|
{
|
|
// If it has no links then spawn a new self node here
|
|
if (DefaultToSelfPin->LinkedTo.IsEmpty())
|
|
{
|
|
UK2Node_Self* SelfNode = CompilerContext.SpawnIntermediateNode<UK2Node_Self>(this, SourceGraph);
|
|
SelfNode->AllocateDefaultPins();
|
|
UEdGraphPin* SelfPin = SelfNode->FindPinChecked(UEdGraphSchema_K2::PSC_Self);
|
|
// Make a connection from this intermediate self pin to here
|
|
return Schema->TryCreateConnection(DefaultToSelfPin, SelfPin);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|