// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. #include "BlueprintGraphPrivatePCH.h" #include "KismetCompiler.h" #include "Runtime/Engine/Classes/Kismet/GameplayStatics.h" #include "BlueprintNodeSpawner.h" #include "EditorCategoryUtils.h" struct FK2Node_SpawnActorFromClassHelper { static FString WorldContextPinName; static FString ClassPinName; static FString SpawnTransformPinName; static FString NoCollisionFailPinName; }; FString FK2Node_SpawnActorFromClassHelper::WorldContextPinName(TEXT("WorldContextObject")); FString FK2Node_SpawnActorFromClassHelper::ClassPinName(TEXT("Class")); FString FK2Node_SpawnActorFromClassHelper::SpawnTransformPinName(TEXT("SpawnTransform")); FString FK2Node_SpawnActorFromClassHelper::NoCollisionFailPinName(TEXT("SpawnEvenIfColliding")); #define LOCTEXT_NAMESPACE "K2Node_SpawnActorFromClass" UK2Node_SpawnActorFromClass::UK2Node_SpawnActorFromClass(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP) { NodeTooltip = LOCTEXT("NodeTooltip", "Attempts to spawn a new Actor with the specified transform").ToString(); } void UK2Node_SpawnActorFromClass::AllocateDefaultPins() { const UEdGraphSchema_K2* K2Schema = GetDefault(); // Add execution pins CreatePin(EGPD_Input, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Execute); CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Then); // If required add the world context pin if (GetBlueprint()->ParentClass->HasMetaData(FBlueprintMetadata::MD_ShowHiddenSelfPins)) { CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), UObject::StaticClass(), false, false, FK2Node_SpawnActorFromClassHelper::WorldContextPinName); } // Add blueprint pin UEdGraphPin* ClassPin = CreatePin(EGPD_Input, K2Schema->PC_Class, TEXT(""), AActor::StaticClass(), false, false, FK2Node_SpawnActorFromClassHelper::ClassPinName); SetPinToolTip(*ClassPin, LOCTEXT("ClassPinDescription", "The Actor class you want to spawn")); // Transform pin UScriptStruct* TransformStruct = FindObjectChecked(UObject::StaticClass(), TEXT("Transform")); UEdGraphPin* TransformPin = CreatePin(EGPD_Input, K2Schema->PC_Struct, TEXT(""), TransformStruct, false, false, FK2Node_SpawnActorFromClassHelper::SpawnTransformPinName); SetPinToolTip(*TransformPin, LOCTEXT("TransformPinDescription", "The transform to spawn the Actor with")); // bNoCollisionFail pin UEdGraphPin* NoCollisionFailPin = CreatePin(EGPD_Input, K2Schema->PC_Boolean, TEXT(""), NULL, false, false, FK2Node_SpawnActorFromClassHelper::NoCollisionFailPinName); SetPinToolTip(*NoCollisionFailPin, LOCTEXT("NoCollisionFailPinDescription", "Determines if the Actor should be spawned when the location is blocked by a collision")); // Result pin UEdGraphPin* ResultPin = CreatePin(EGPD_Output, K2Schema->PC_Object, TEXT(""), AActor::StaticClass(), false, false, K2Schema->PN_ReturnValue); SetPinToolTip(*ResultPin, LOCTEXT("ResultPinDescription", "The spawned Actor")); Super::AllocateDefaultPins(); } void UK2Node_SpawnActorFromClass::SetPinToolTip(UEdGraphPin& MutatablePin, const FText& PinDescription) const { MutatablePin.PinToolTip = UEdGraphSchema_K2::TypeToString(MutatablePin.PinType); UEdGraphSchema_K2 const* const K2Schema = Cast(GetSchema()); if (K2Schema != nullptr) { MutatablePin.PinToolTip += TEXT(" "); MutatablePin.PinToolTip += K2Schema->GetPinDisplayName(&MutatablePin); } MutatablePin.PinToolTip += FString(TEXT("\n")) + PinDescription.ToString(); } void UK2Node_SpawnActorFromClass::CreatePinsForClass(UClass* InClass) { check(InClass != NULL); const UEdGraphSchema_K2* K2Schema = GetDefault(); const UObject* const ClassDefaultObject = InClass->GetDefaultObject(false); for (TFieldIterator PropertyIt(InClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; UClass* PropertyClass = CastChecked(Property->GetOuter()); const bool bIsDelegate = Property->IsA(UMulticastDelegateProperty::StaticClass()); const bool bIsExposedToSpawn = UEdGraphSchema_K2::IsPropertyExposedOnSpawn(Property); const bool bIsSettableExternally = !Property->HasAnyPropertyFlags(CPF_DisableEditOnInstance); if( bIsExposedToSpawn && !Property->HasAnyPropertyFlags(CPF_Parm) && bIsSettableExternally && Property->HasAllPropertyFlags(CPF_BlueprintVisible) && !bIsDelegate && (NULL == FindPin(Property->GetName()) ) ) { UEdGraphPin* Pin = CreatePin(EGPD_Input, TEXT(""), TEXT(""), NULL, false, false, Property->GetName()); const bool bPinGood = (Pin != NULL) && K2Schema->ConvertPropertyToPinType(Property, /*out*/ Pin->PinType); if( ClassDefaultObject && K2Schema->PinDefaultValueIsEditable(*Pin)) { FString DefaultValueAsString; const bool bDefaultValueSet = FBlueprintEditorUtils::PropertyValueToString(Property, reinterpret_cast(ClassDefaultObject), DefaultValueAsString); check( bDefaultValueSet ); K2Schema->TrySetDefaultValue(*Pin, DefaultValueAsString); } } } // Change class of output pin UEdGraphPin* ResultPin = GetResultPin(); ResultPin->PinType.PinSubCategoryObject = InClass; } UClass* UK2Node_SpawnActorFromClass::GetClassToSpawn(const TArray* InPinsToSearch /*=NULL*/) const { UClass* UseSpawnClass = NULL; const TArray* PinsToSearch = InPinsToSearch ? InPinsToSearch : &Pins; UEdGraphPin* ClassPin = GetClassPin(PinsToSearch); if(ClassPin && ClassPin->DefaultObject != NULL && ClassPin->LinkedTo.Num() == 0) { UseSpawnClass = CastChecked(ClassPin->DefaultObject); } return UseSpawnClass; } void UK2Node_SpawnActorFromClass::ReallocatePinsDuringReconstruction(TArray& OldPins) { AllocateDefaultPins(); UClass* UseSpawnClass = GetClassToSpawn(&OldPins); if( UseSpawnClass != NULL ) { CreatePinsForClass(UseSpawnClass); } } bool UK2Node_SpawnActorFromClass::IsSpawnVarPin(UEdGraphPin* Pin) { const UEdGraphSchema_K2* K2Schema = GetDefault(); UEdGraphPin* ParentPin = Pin->ParentPin; while (ParentPin) { if (ParentPin->PinName == FK2Node_SpawnActorFromClassHelper::SpawnTransformPinName) { return false; } ParentPin = ParentPin->ParentPin; } return( Pin->PinName != K2Schema->PN_Execute && Pin->PinName != K2Schema->PN_Then && Pin->PinName != K2Schema->PN_ReturnValue && Pin->PinName != FK2Node_SpawnActorFromClassHelper::ClassPinName && Pin->PinName != FK2Node_SpawnActorFromClassHelper::WorldContextPinName && Pin->PinName != FK2Node_SpawnActorFromClassHelper::NoCollisionFailPinName && Pin->PinName != FK2Node_SpawnActorFromClassHelper::SpawnTransformPinName ); } void UK2Node_SpawnActorFromClass::PinDefaultValueChanged(UEdGraphPin* ChangedPin) { if (ChangedPin->PinName == FK2Node_SpawnActorFromClassHelper::ClassPinName) { const UEdGraphSchema_K2* K2Schema = GetDefault(); // Because the archetype has changed, we break the output link as the output pin type will change UEdGraphPin* ResultPin = GetResultPin(); ResultPin->BreakAllPinLinks(); // Remove all pins related to archetype variables TArray OldPins = Pins; for (int32 i = 0; i < OldPins.Num(); i++) { UEdGraphPin* OldPin = OldPins[i]; if (IsSpawnVarPin(OldPin)) { OldPin->BreakAllPinLinks(); Pins.Remove(OldPin); } } UClass* UseSpawnClass = GetClassToSpawn(); if (UseSpawnClass != NULL) { CreatePinsForClass(UseSpawnClass); } // Refresh the UI for the graph so the pin changes show up UEdGraph* Graph = GetGraph(); Graph->NotifyGraphChanged(); // Mark dirty FBlueprintEditorUtils::MarkBlueprintAsModified(GetBlueprint()); } } FString UK2Node_SpawnActorFromClass::GetTooltip() const { return NodeTooltip; } UEdGraphPin* UK2Node_SpawnActorFromClass::GetThenPin()const { const UEdGraphSchema_K2* K2Schema = GetDefault(); UEdGraphPin* Pin = FindPinChecked(K2Schema->PN_Then); check(Pin->Direction == EGPD_Output); return Pin; } UEdGraphPin* UK2Node_SpawnActorFromClass::GetClassPin(const TArray* InPinsToSearch /*= NULL*/) const { const TArray* PinsToSearch = InPinsToSearch ? InPinsToSearch : &Pins; UEdGraphPin* Pin = NULL; for( auto PinIt = PinsToSearch->CreateConstIterator(); PinIt; ++PinIt ) { UEdGraphPin* TestPin = *PinIt; if( TestPin && TestPin->PinName == FK2Node_SpawnActorFromClassHelper::ClassPinName ) { Pin = TestPin; break; } } check(Pin == NULL || Pin->Direction == EGPD_Input); return Pin; } UEdGraphPin* UK2Node_SpawnActorFromClass::GetSpawnTransformPin() const { UEdGraphPin* Pin = FindPinChecked(FK2Node_SpawnActorFromClassHelper::SpawnTransformPinName); check(Pin->Direction == EGPD_Input); return Pin; } UEdGraphPin* UK2Node_SpawnActorFromClass::GetNoCollisionFailPin() const { UEdGraphPin* Pin = FindPinChecked(FK2Node_SpawnActorFromClassHelper::NoCollisionFailPinName); check(Pin->Direction == EGPD_Input); return Pin; } UEdGraphPin* UK2Node_SpawnActorFromClass::GetWorldContextPin() const { UEdGraphPin* Pin = FindPin(FK2Node_SpawnActorFromClassHelper::WorldContextPinName); check(Pin == NULL || Pin->Direction == EGPD_Input); return Pin; } UEdGraphPin* UK2Node_SpawnActorFromClass::GetResultPin() const { const UEdGraphSchema_K2* K2Schema = GetDefault(); UEdGraphPin* Pin = FindPinChecked(K2Schema->PN_ReturnValue); check(Pin->Direction == EGPD_Output); return Pin; } FLinearColor UK2Node_SpawnActorFromClass::GetNodeTitleColor() const { return Super::GetNodeTitleColor(); } FText UK2Node_SpawnActorFromClass::GetNodeTitle(ENodeTitleType::Type TitleType) const { FText NodeTitle = NSLOCTEXT("K2Node", "SpawnActor_BaseTitle", "Spawn Actor from Class"); if (TitleType != ENodeTitleType::ListView) { FText SpawnString = NSLOCTEXT("K2Node", "None", "NONE"); if (UEdGraphPin* ClassPin = GetClassPin()) { if(ClassPin->LinkedTo.Num() > 0) { // Blueprint will be determined dynamically, so we don't have the name in this case SpawnString = FText::GetEmpty(); } else if(ClassPin->DefaultObject != NULL) { SpawnString = FText::FromString(ClassPin->DefaultObject->GetName()); } } FFormatNamedArguments Args; Args.Add(TEXT("ClassName"), SpawnString); NodeTitle = FText::Format(NSLOCTEXT("K2Node", "SpawnActor", "SpawnActor {ClassName}"), Args); } return NodeTitle; } bool UK2Node_SpawnActorFromClass::IsCompatibleWithGraph(const UEdGraph* TargetGraph) const { UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TargetGraph); return Super::IsCompatibleWithGraph(TargetGraph) && (!Blueprint || FBlueprintEditorUtils::FindUserConstructionScript(Blueprint) != TargetGraph); } void UK2Node_SpawnActorFromClass::GetNodeAttributes( TArray>& OutNodeAttributes ) const { UClass* ClassToSpawn = GetClassToSpawn(); const FString ClassToSpawnStr = ClassToSpawn ? ClassToSpawn->GetName() : TEXT( "InvalidClass" ); OutNodeAttributes.Add( TKeyValuePair( TEXT( "Type" ), TEXT( "SpawnActorFromClass" ) )); OutNodeAttributes.Add( TKeyValuePair( TEXT( "Class" ), GetClass()->GetName() )); OutNodeAttributes.Add( TKeyValuePair( TEXT( "Name" ), GetName() )); OutNodeAttributes.Add( TKeyValuePair( TEXT( "ActorClass" ), ClassToSpawnStr )); } void UK2Node_SpawnActorFromClass::GetMenuActions(TArray& ActionListOut) const { UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); check(NodeSpawner != nullptr); ActionListOut.Add(NodeSpawner); } FText UK2Node_SpawnActorFromClass::GetMenuCategory() const { return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Gameplay); } FNodeHandlingFunctor* UK2Node_SpawnActorFromClass::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const { return new FNodeHandlingFunctor(CompilerContext); } void UK2Node_SpawnActorFromClass::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); if (CompilerContext.bIsFullCompile) { static FName BeginSpawningBlueprintFuncName = GET_FUNCTION_NAME_CHECKED(UGameplayStatics, BeginSpawningActorFromClass); static FString ActorClassParamName = FString(TEXT("ActorClass")); static FString WorldContextParamName = FString(TEXT("WorldContextObject")); static FName FinishSpawningFuncName = GET_FUNCTION_NAME_CHECKED(UGameplayStatics, FinishSpawningActor); static FString ActorParamName = FString(TEXT("Actor")); static FString TransformParamName = FString(TEXT("SpawnTransform")); static FString NoCollisionFailParamName = FString(TEXT("bNoCollisionFail")); static FString ObjectParamName = FString(TEXT("Object")); static FString ValueParamName = FString(TEXT("Value")); static FString PropertyNameParamName = FString(TEXT("PropertyName")); UK2Node_SpawnActorFromClass* SpawnNode = this; UEdGraphPin* SpawnNodeExec = SpawnNode->GetExecPin(); UEdGraphPin* SpawnNodeTransform = SpawnNode->GetSpawnTransformPin(); UEdGraphPin* SpawnNodeNoCollisionFail = GetNoCollisionFailPin(); UEdGraphPin* SpawnWorldContextPin = SpawnNode->GetWorldContextPin(); UEdGraphPin* SpawnClassPin = SpawnNode->GetClassPin(); UEdGraphPin* SpawnNodeThen = SpawnNode->GetThenPin(); UEdGraphPin* SpawnNodeResult = SpawnNode->GetResultPin(); UClass* SpawnClass = (SpawnClassPin != NULL) ? Cast(SpawnClassPin->DefaultObject) : NULL; if((0 == SpawnClassPin->LinkedTo.Num()) && (NULL == SpawnClass)) { CompilerContext.MessageLog.Error(*LOCTEXT("SpawnActorNodeMissingClass_Error", "Spawn node @@ must have a class specified.").ToString(), SpawnNode); // we break exec links so this is the only error we get, don't want the SpawnActor node being considered and giving 'unexpected node' type warnings SpawnNode->BreakAllNodeLinks(); return; } ////////////////////////////////////////////////////////////////////////// // create 'begin spawn' call node UK2Node_CallFunction* CallBeginSpawnNode = CompilerContext.SpawnIntermediateNode(SpawnNode, SourceGraph); CallBeginSpawnNode->FunctionReference.SetExternalMember(BeginSpawningBlueprintFuncName, UGameplayStatics::StaticClass()); CallBeginSpawnNode->AllocateDefaultPins(); UEdGraphPin* CallBeginExec = CallBeginSpawnNode->GetExecPin(); UEdGraphPin* CallBeginWorldContextPin = CallBeginSpawnNode->FindPinChecked(WorldContextParamName); UEdGraphPin* CallBeginActorClassPin = CallBeginSpawnNode->FindPinChecked(ActorClassParamName); UEdGraphPin* CallBeginTransform = CallBeginSpawnNode->FindPinChecked(TransformParamName); UEdGraphPin* CallBeginNoCollisionFail = CallBeginSpawnNode->FindPinChecked(NoCollisionFailParamName); UEdGraphPin* CallBeginResult = CallBeginSpawnNode->GetReturnValuePin(); // Move 'exec' connection from spawn node to 'begin spawn' CompilerContext.MovePinLinksToIntermediate(*SpawnNodeExec, *CallBeginExec); if(SpawnClassPin->LinkedTo.Num() > 0) { // Copy the 'blueprint' connection from the spawn node to 'begin spawn' CompilerContext.MovePinLinksToIntermediate(*SpawnClassPin, *CallBeginActorClassPin); } else { // Copy blueprint literal onto begin spawn call CallBeginActorClassPin->DefaultObject = SpawnClass; } // Copy the world context connection from the spawn node to 'begin spawn' if necessary if (SpawnWorldContextPin) { CompilerContext.MovePinLinksToIntermediate(*SpawnWorldContextPin, *CallBeginWorldContextPin); } // Copy the 'transform' connection from the spawn node to 'begin spawn' CompilerContext.MovePinLinksToIntermediate(*SpawnNodeTransform, *CallBeginTransform); // Copy the 'bNoCollisionFail' connection from the spawn node to 'begin spawn' CompilerContext.MovePinLinksToIntermediate(*SpawnNodeNoCollisionFail, *CallBeginNoCollisionFail); ////////////////////////////////////////////////////////////////////////// // create 'finish spawn' call node UK2Node_CallFunction* CallFinishSpawnNode = CompilerContext.SpawnIntermediateNode(SpawnNode, SourceGraph); CallFinishSpawnNode->FunctionReference.SetExternalMember(FinishSpawningFuncName, UGameplayStatics::StaticClass()); CallFinishSpawnNode->AllocateDefaultPins(); UEdGraphPin* CallFinishExec = CallFinishSpawnNode->GetExecPin(); UEdGraphPin* CallFinishThen = CallFinishSpawnNode->GetThenPin(); UEdGraphPin* CallFinishActor = CallFinishSpawnNode->FindPinChecked(ActorParamName); UEdGraphPin* CallFinishTransform = CallFinishSpawnNode->FindPinChecked(TransformParamName); UEdGraphPin* CallFinishResult = CallFinishSpawnNode->GetReturnValuePin(); // Move 'then' connection from spawn node to 'finish spawn' CompilerContext.MovePinLinksToIntermediate(*SpawnNodeThen, *CallFinishThen); // Copy transform connection CompilerContext.CopyPinLinksToIntermediate(*CallBeginTransform, *CallFinishTransform); // Connect output actor from 'begin' to 'finish' CallBeginResult->MakeLinkTo(CallFinishActor); // Move result connection from spawn node to 'finish spawn' CallFinishResult->PinType = SpawnNodeResult->PinType; // Copy type so it uses the right actor subclass CompilerContext.MovePinLinksToIntermediate(*SpawnNodeResult, *CallFinishResult); ////////////////////////////////////////////////////////////////////////// // create 'set var' nodes // Get 'result' pin from 'begin spawn', this is the actual actor we want to set properties on UEdGraphPin* LastThen = FKismetCompilerUtilities::GenerateAssignmentNodes(CompilerContext, SourceGraph, CallBeginSpawnNode, SpawnNode, CallBeginResult, GetClassToSpawn() ); // Make exec connection between 'then' on last node and 'finish' LastThen->MakeLinkTo(CallFinishExec); // Break any links to the expanded node SpawnNode->BreakAllNodeLinks(); } } bool UK2Node_SpawnActorFromClass::HasExternalBlueprintDependencies(TArray* OptionalOutput) const { UClass* SourceClass = GetClassToSpawn(); const UBlueprint* SourceBlueprint = GetBlueprint(); const bool bResult = (SourceClass != NULL) && (SourceClass->ClassGeneratedBy != NULL) && (SourceClass->ClassGeneratedBy != SourceBlueprint); if (bResult && OptionalOutput) { OptionalOutput->Add(SourceClass); } return bResult || Super::HasExternalBlueprintDependencies(OptionalOutput); } #undef LOCTEXT_NAMESPACE