void UK2Node_EaseFunction::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); /** At the end of this, the UK2Node_EaseFunction will not be a part of the Blueprint, it merely handles connecting the other nodes into the Blueprint. */ UFunction* Function = UKismetMathLibrary::StaticClass()->FindFunctionByName(*EaseFunctionName); if (Function == NULL) { CompilerContext.MessageLog.Error(*LOCTEXT("InvalidFunctionName", "BaseAsyncTask: Type not supported or not initialized. @@").ToString(), this); return; } const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); // The call function does all the real work, each child class implementing easing for a given type provides // the name of the desired function UK2Node_CallFunction* CallFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); CallFunction->SetFromFunction(Function); CallFunction->AllocateDefaultPins(); CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallFunction, this); // Move the ease function and the alpha connections from us to the call function CompilerContext.MovePinLinksToIntermediate(*FindPin(FEaseFunctionNodeHelper::GetEaseFuncPinName()), *CallFunction->FindPin(TEXT("EasingFunc"))); CompilerContext.MovePinLinksToIntermediate(*FindPin(FEaseFunctionNodeHelper::GetAlphaPinName()), *CallFunction->FindPin(TEXT("Alpha"))); // Move base connections to the call function's connections CompilerContext.MovePinLinksToIntermediate(*FindPin(FEaseFunctionNodeHelper::GetAPinName()), *CallFunction->FindPin(TEXT("A"))); CompilerContext.MovePinLinksToIntermediate(*FindPin(FEaseFunctionNodeHelper::GetBPinName()), *CallFunction->FindPin(TEXT("B"))); CompilerContext.MovePinLinksToIntermediate(*FindPin(FEaseFunctionNodeHelper::GetResultPinName()), *CallFunction->GetReturnValuePin()); // Now move the custom pins to their new locations UEdGraphPin* ShortestPathPin = FindPinChecked(FEaseFunctionNodeHelper::GetShortestPathPinName()); if (!ShortestPathPin->bHidden) { CompilerContext.MovePinLinksToIntermediate(*ShortestPathPin, *CallFunction->FindPinChecked(TEXT("bShortestPath"))); } UEdGraphPin* BlendExpPin = FindPinChecked(FEaseFunctionNodeHelper::GetBlendExpPinName()); if (!BlendExpPin->bHidden) { CompilerContext.MovePinLinksToIntermediate(*BlendExpPin, *CallFunction->FindPinChecked(FEaseFunctionNodeHelper::GetBlendExpPinName())); } UEdGraphPin* StepsPin = FindPinChecked(FEaseFunctionNodeHelper::GetStepsPinName()); if (!StepsPin->bHidden) { CompilerContext.MovePinLinksToIntermediate(*StepsPin, *CallFunction->FindPinChecked(FEaseFunctionNodeHelper::GetStepsPinName())); } // Cleanup links to ourself and we are done! BreakAllNodeLinks(); }
void UK2Node_ForEachElementInEnum::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { if (CompilerContext.bIsFullCompile) { if (!Enum) { ValidateNodeDuringCompilation(CompilerContext.MessageLog); return; } FForExpandNodeHelper ForLoop; if (!ForLoop.BuildLoop(this, CompilerContext, SourceGraph)) { CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "ForEachElementInEnum_ForError", "For Expand error in @@").ToString(), this); } const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); CompilerContext.CheckConnectionResponse(Schema->MovePinLinks(*GetExecPin(), *ForLoop.StartLoopExecInPin), this); CompilerContext.CheckConnectionResponse(Schema->MovePinLinks(*FindPinChecked(Schema->PN_Then), *ForLoop.LoopCompleteOutExecPin), this); CompilerContext.CheckConnectionResponse(Schema->MovePinLinks(*FindPinChecked(InsideLoopPinName), *ForLoop.InsideLoopExecOutPin), this); UK2Node_GetNumEnumEntries* GetNumEnumEntries = CompilerContext.SpawnIntermediateNode<UK2Node_GetNumEnumEntries>(this, SourceGraph); GetNumEnumEntries->Enum = Enum; GetNumEnumEntries->AllocateDefaultPins(); bool bResult = Schema->TryCreateConnection(GetNumEnumEntries->FindPinChecked(Schema->PN_ReturnValue), ForLoop.IndexLimitInPin); UK2Node_CallFunction* Conv_Func = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); FName Conv_Func_Name = GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, Conv_IntToByte); Conv_Func->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(Conv_Func_Name)); Conv_Func->AllocateDefaultPins(); bResult &= Schema->TryCreateConnection(Conv_Func->FindPinChecked(TEXT("InInt")), ForLoop.IndexOutPin); UK2Node_CastByteToEnum* CastByteToEnum = CompilerContext.SpawnIntermediateNode<UK2Node_CastByteToEnum>(this, SourceGraph); CastByteToEnum->Enum = Enum; CastByteToEnum->bSafe = true; CastByteToEnum->AllocateDefaultPins(); bResult &= Schema->TryCreateConnection(Conv_Func->FindPinChecked(Schema->PN_ReturnValue), CastByteToEnum->FindPinChecked(UK2Node_CastByteToEnum::ByteInputPinName)); CompilerContext.CheckConnectionResponse(Schema->MovePinLinks(*FindPinChecked(EnumOuputPinName), *CastByteToEnum->FindPinChecked(Schema->PN_ReturnValue)), this); if (!bResult) { CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "ForEachElementInEnum_ExpandError", "Expand error in @@").ToString(), this); } BreakAllNodeLinks(); } }
void UK2Node_CastByteToEnum::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); if (bSafe && Enum) { const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); // FUNCTION NODE const FName FunctionName = GetFunctionName(); const UFunction* Function = UKismetNodeHelperLibrary::StaticClass()->FindFunctionByName(FunctionName); check(NULL != Function); UK2Node_CallFunction* CallValidation = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); CallValidation->SetFromFunction(Function); CallValidation->AllocateDefaultPins(); check(CallValidation->IsNodePure()); // FUNCTION ENUM PIN UEdGraphPin* FunctionEnumPin = CallValidation->FindPinChecked(TEXT("Enum")); Schema->TrySetDefaultObject(*FunctionEnumPin, Enum); check(FunctionEnumPin->DefaultObject == Enum); // FUNCTION INPUT BYTE PIN UEdGraphPin* OrgInputPin = FindPinChecked(ByteInputPinName); UEdGraphPin* FunctionIndexPin = CallValidation->FindPinChecked(TEXT("EnumeratorIndex")); check(EGPD_Input == FunctionIndexPin->Direction && Schema->PC_Byte == FunctionIndexPin->PinType.PinCategory); CompilerContext.MovePinLinksToIntermediate(*OrgInputPin, *FunctionIndexPin); // UNSAFE CAST NODE UK2Node_CastByteToEnum* UsafeCast = CompilerContext.SpawnIntermediateNode<UK2Node_CastByteToEnum>(this, SourceGraph); UsafeCast->Enum = Enum; UsafeCast->bSafe = false; UsafeCast->AllocateDefaultPins(); // UNSAFE CAST INPUT UEdGraphPin* CastInputPin = UsafeCast->FindPinChecked(ByteInputPinName); UEdGraphPin* FunctionReturnPin = CallValidation->GetReturnValuePin(); check(NULL != FunctionReturnPin); Schema->TryCreateConnection(CastInputPin, FunctionReturnPin); // OPUTPUT PIN UEdGraphPin* OrgReturnPin = FindPinChecked(Schema->PN_ReturnValue); UEdGraphPin* NewReturnPin = UsafeCast->FindPinChecked(Schema->PN_ReturnValue); CompilerContext.MovePinLinksToIntermediate(*OrgReturnPin, *NewReturnPin); BreakAllNodeLinks(); } }
void UK2Node_GetInputAxisValue::AllocateDefaultPins() { Super::AllocateDefaultPins(); UEdGraphPin* InputAxisNamePin = FindPinChecked(TEXT("InputAxisName")); InputAxisNamePin->DefaultValue = InputAxisName.ToString(); }
void UK2Node_Event::UpdateDelegatePin() { UEdGraphPin* Pin = FindPinChecked(DelegateOutputName); checkSlow(EGPD_Output == Pin->Direction); const UObject* OldSignature = Pin->PinType.PinSubCategoryObject.Get(); if(bOverrideFunction) { Pin->PinType.PinSubCategoryObject = EventSignatureClass->FindFunctionByName(EventSignatureName); } else if(UBlueprint* Blueprint = GetBlueprint()) { Pin->PinType.PinSubCategoryObject = Blueprint->SkeletonGeneratedClass ? Blueprint->SkeletonGeneratedClass->FindFunctionByName(CustomFunctionName) : NULL; } else { Pin->PinType.PinSubCategoryObject = NULL; } if(OldSignature != Pin->PinType.PinSubCategoryObject.Get()) { PinTypeChanged(Pin); } }
UEdGraphPin* UK2Node_FormatText::GetFormatPin() const { if (!CachedFormatPin) { const_cast<UK2Node_FormatText*>(this)->CachedFormatPin = FindPinChecked(FFormatTextNodeHelper::GetFormatPinName()); } return CachedFormatPin; }
UEdGraphPin* UK2Node_SpawnActor::GetThenPin()const { const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); UEdGraphPin* Pin = FindPinChecked(K2Schema->PN_Then); check(Pin->Direction == EGPD_Output); return Pin; }
UEdGraphPin* UK2Node_LatentAbilityCall::GetResultPin() const { const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); UEdGraphPin* Pin = FindPinChecked(K2Schema->PN_ReturnValue); check(Pin->Direction == EGPD_Output); return Pin; }
UEdGraphPin* UK2Node_EaseFunction::GetEaseFuncPin() const { if (!CachedEaseFuncPin) { const_cast<UK2Node_EaseFunction*>(this)->CachedEaseFuncPin = FindPinChecked(FEaseFunctionNodeHelper::GetEaseFuncPinName()); } return CachedEaseFuncPin; }
UEdGraphPin* UK2Node_SpawnActorFromClass::GetResultPin() const { const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); UEdGraphPin* Pin = FindPinChecked(K2Schema->PN_ReturnValue); check(Pin->Direction == EGPD_Output); return Pin; }
void UK2Node_GetEnumeratorName::NotifyPinConnectionListChanged(UEdGraphPin* Pin) { Super::NotifyPinConnectionListChanged(Pin); if (Pin == FindPinChecked(EnumeratorPinName)) { UpdatePinType(); } }
void UK2Node_EaseFunction::GenerateExtraPins() { const UEdGraphSchema_K2* K2Schema = Cast<const UEdGraphSchema_K2>(GetSchema()); // Add pins based on the pin type const UEdGraphPin* APin = FindPinChecked(FEaseFunctionNodeHelper::GetAPinName()); const bool bIsRotator = APin->PinType.PinCategory == K2Schema->PC_Struct && APin->PinType.PinSubCategoryObject.Get()->GetName() == TEXT("Rotator"); UEdGraphPin* ShortestPathPin = FindPinChecked(FEaseFunctionNodeHelper::GetShortestPathPinName()); if (bIsRotator) { ShortestPathPin->bHidden = false; } else { ShortestPathPin->BreakAllPinLinks(); ShortestPathPin->bHidden = true; } }
void UK2Node_GetEnumeratorName::UpdatePinType() { UEdGraphPin* EnumPin = FindPinChecked(EnumeratorPinName); UEnum* Enum = GetEnum(); if (EnumPin->PinType.PinSubCategoryObject != Enum) { EnumPin->PinType.PinSubCategoryObject = Enum; PinTypeChanged(EnumPin); } }
void UK2Node_GetEnumeratorName::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const { const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>(); const UEdGraphPin* OutputPin = FindPinChecked(Schema->PN_ReturnValue); /*Don't validate isolated nodes */ if (0 != OutputPin->LinkedTo.Num()) { EarlyValidation(MessageLog); } }
void UK2Node_EaseFunction::RefreshPinVisibility() { const auto EaseFuncPin = GetEaseFuncPin(); UEnum * Enum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EEasingFunc"), true); check(Enum != NULL); const int32 NewEasingFunc = CanCustomizeCurve() ? Enum->GetValueByName(*EaseFuncPin->DefaultValue) : -1; // Early exit in case no changes are required const UEdGraphSchema_K2* K2Schema = Cast<const UEdGraphSchema_K2>(GetSchema()); UEdGraphPin* BlendExpPin = FindPinChecked(FEaseFunctionNodeHelper::GetBlendExpPinName()); if (NewEasingFunc == -1 || NewEasingFunc == EEasingFunc::EaseIn || NewEasingFunc == EEasingFunc::EaseOut || NewEasingFunc == EEasingFunc::EaseInOut) { // Show the BlendExpPin BlendExpPin->bHidden = false; } else { // Hide the BlendExpPin: BlendExpPin->BreakAllPinLinks(); BlendExpPin->bHidden = true; } UEdGraphPin* StepsPin = FindPinChecked(FEaseFunctionNodeHelper::GetStepsPinName()); if (NewEasingFunc == -1 || NewEasingFunc == EEasingFunc::Step) { // Show the Steps pin: StepsPin->bHidden = false; } else { // Hide the Steps pin: StepsPin->BreakAllPinLinks(); StepsPin->bHidden = true; } }
void UK2Node_SetFieldsInStruct::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const { Super::ValidateNodeDuringCompilation(MessageLog); if (UEdGraphPin* FoundPin = FindPinChecked(SetFieldsInStructHelper::StructRefPinName())) { if (FoundPin->LinkedTo.Num() <= 0) { FText ErrorMessage = LOCTEXT("SetStructFields_NoStructRefError", "The @@ pin must be connected to the struct that you wish to set."); MessageLog.Error(*ErrorMessage.ToString(), FoundPin); } } }
bool UK2Node_GetEnumeratorName::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const { const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>(); const UEdGraphPin* InputPin = FindPinChecked(EnumeratorPinName); if((InputPin == MyPin) && OtherPin && (OtherPin->PinType.PinCategory == Schema->PC_Byte)) { if(NULL == Cast<UEnum>(OtherPin->PinType.PinSubCategoryObject.Get())) { OutReason = NSLOCTEXT("K2Node", "GetNumEnumEntries_NotEnum_Msg", "Input is not an Enum.").ToString(); return true; } } return false; }
void UK2Node_GetNumEnumEntries::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); if(NULL == Enum) { CompilerContext.MessageLog.Error(*FString::Printf(*NSLOCTEXT("K2Node", "GetNumEnumEntries_Error", "@@ must have a valid enum defined").ToString()), this); return; } // Force the enum to load its values if it hasn't already if (Enum->HasAnyFlags(RF_NeedLoad)) { Enum->GetLinker()->Preload(Enum); } const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); check(NULL != Schema); //MAKE LITERAL const FName FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, MakeLiteralInt); UK2Node_CallFunction* MakeLiteralInt = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); MakeLiteralInt->SetFromFunction(UKismetSystemLibrary::StaticClass()->FindFunctionByName(FunctionName)); MakeLiteralInt->AllocateDefaultPins(); //OPUTPUT PIN UEdGraphPin* OrgReturnPin = FindPinChecked(Schema->PN_ReturnValue); UEdGraphPin* NewReturnPin = MakeLiteralInt->GetReturnValuePin(); check(NULL != NewReturnPin); CompilerContext.MovePinLinksToIntermediate(*OrgReturnPin, *NewReturnPin); //INPUT PIN UEdGraphPin* InputPin = MakeLiteralInt->FindPinChecked(TEXT("Value")); check(EGPD_Input == InputPin->Direction); const FString DefaultValue = FString::FromInt(Enum->NumEnums() - 1); InputPin->DefaultValue = DefaultValue; BreakAllNodeLinks(); }
/** * This is essentially a mix of K2Node_BaseAsyncTask::ExpandNode and K2Node_SpawnActorFromClass::ExpandNode. * Several things are going on here: * -Factory call to create proxy object (K2Node_BaseAsyncTask) * -Task return delegates are created and hooked up (K2Node_BaseAsyncTask) * -A BeginSpawn function is called on proxyu object (similiar to K2Node_SpawnActorFromClass) * -BeginSpawn can choose to spawn or not spawn an actor (and return it) * -If spawned: * -SetVars are run on the newly spawned object (set expose on spawn variables - K2Node_SpawnActorFromClass) * -FinishSpawn is called on the proxy object * * * Also, a K2Node_SpawnActorFromClass could not be used directly here, since we want the proxy object to implement its own * BeginSpawn/FinishSpawn function (custom game logic will often be performed in the native implementation). K2Node_SpawnActorFromClass also * requires a SpawnTransform be wired into it, and in most ability task cases, the spawn transform is implied or not necessary. * * */ void UK2Node_LatentAbilityCall::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { bool validatedActorSpawn = ValidateActorSpawning(CompilerContext, false); bool validatedActorArraySpawn = ValidateActorArraySpawning(CompilerContext, false); UEdGraphPin* ClassPin = GetClassPin(); if (ClassPin == nullptr) { // Nothing special about this task, just call super Super::ExpandNode(CompilerContext, SourceGraph); return; } UK2Node::ExpandNode(CompilerContext, SourceGraph); if (!validatedActorSpawn && !validatedActorArraySpawn) { ValidateActorSpawning(CompilerContext, true); ValidateActorArraySpawning(CompilerContext, true); } const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); check(SourceGraph && Schema); bool bIsErrorFree = true; // ------------------------------------------------------------------------------------------ // CREATE A CALL TO FACTORY THE PROXY OBJECT // ------------------------------------------------------------------------------------------ UK2Node_CallFunction* const CallCreateProxyObjectNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); CallCreateProxyObjectNode->FunctionReference.SetExternalMember(ProxyFactoryFunctionName, ProxyFactoryClass); CallCreateProxyObjectNode->AllocateDefaultPins(); bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(Schema->PN_Execute), *CallCreateProxyObjectNode->FindPinChecked(Schema->PN_Execute)).CanSafeConnect(); for (auto CurrentPin : Pins) { if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Input, Schema)) { UEdGraphPin* DestPin = CallCreateProxyObjectNode->FindPin(CurrentPin->PinName); // match function inputs, to pass data to function from CallFunction node // NEW: if no DestPin, assume it is a Class Spawn PRoperty - not an error if (DestPin) { bIsErrorFree &= CompilerContext.CopyPinLinksToIntermediate(*CurrentPin, *DestPin).CanSafeConnect(); } } } // ------------------------------------------------------------------------------------------ // GATHER OUTPUT PARAMETERS AND PAIR THEM WITH LOCAL VARIABLES // ------------------------------------------------------------------------------------------ TArray<FBaseAsyncTaskHelper::FOutputPinAndLocalVariable> VariableOutputs; for (auto CurrentPin : Pins) { if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Output, Schema)) { const FEdGraphPinType& PinType = CurrentPin->PinType; UK2Node_TemporaryVariable* TempVarOutput = CompilerContext.SpawnInternalVariable( this, PinType.PinCategory, PinType.PinSubCategory, PinType.PinSubCategoryObject.Get(), PinType.bIsArray); bIsErrorFree &= TempVarOutput->GetVariablePin() && CompilerContext.MovePinLinksToIntermediate(*CurrentPin, *TempVarOutput->GetVariablePin()).CanSafeConnect(); VariableOutputs.Add(FBaseAsyncTaskHelper::FOutputPinAndLocalVariable(CurrentPin, TempVarOutput)); } } // ------------------------------------------------------------------------------------------ // FOR EACH DELEGATE DEFINE EVENT, CONNECT IT TO DELEGATE AND IMPLEMENT A CHAIN OF ASSIGMENTS // ------------------------------------------------------------------------------------------ UEdGraphPin* LastThenPin = CallCreateProxyObjectNode->FindPinChecked(Schema->PN_Then); UEdGraphPin* const ProxyObjectPin = CallCreateProxyObjectNode->GetReturnValuePin(); for (TFieldIterator<UMulticastDelegateProperty> PropertyIt(ProxyClass, EFieldIteratorFlags::ExcludeSuper); PropertyIt && bIsErrorFree; ++PropertyIt) { bIsErrorFree &= FBaseAsyncTaskHelper::HandleDelegateImplementation(*PropertyIt, VariableOutputs, ProxyObjectPin, LastThenPin, this, SourceGraph, CompilerContext); } if (CallCreateProxyObjectNode->FindPinChecked(Schema->PN_Then) == LastThenPin) { CompilerContext.MessageLog.Error(*LOCTEXT("MissingDelegateProperties", "BaseAsyncTask: Proxy has no delegates defined. @@").ToString(), this); return; } // ------------------------------------------------------------------------------------------ // NEW: CREATE A CALL TO THE PRESPAWN FUNCTION, IF IT RETURNS TRUE, THEN WE WILL SPAWN THE NEW ACTOR // ------------------------------------------------------------------------------------------ FName ProxyPrespawnFunctionName = validatedActorArraySpawn ? *FK2Node_LatentAbilityCallHelper::BeginSpawnArrayFuncName : *FK2Node_LatentAbilityCallHelper::BeginSpawnFuncName; UFunction* PreSpawnFunction = ProxyFactoryClass->FindFunctionByName(ProxyPrespawnFunctionName); FName ProxyPostpawnFunctionName = validatedActorArraySpawn ? *FK2Node_LatentAbilityCallHelper::FinishSpawnArrayFuncName : *FK2Node_LatentAbilityCallHelper::FinishSpawnFuncName; UFunction* PostSpawnFunction = ProxyFactoryClass->FindFunctionByName(ProxyPostpawnFunctionName); if (PreSpawnFunction == nullptr) { if (validatedActorArraySpawn) { CompilerContext.MessageLog.Error(*LOCTEXT("MissingBeginSpawningActorArrayFunction", "AbilityTask: Proxy is missing BeginSpawningActorArray native function. @@").ToString(), this); } else { CompilerContext.MessageLog.Error(*LOCTEXT("MissingBeginSpawningActorFunction", "AbilityTask: Proxy is missing BeginSpawningActor native function. @@").ToString(), this); } return; } if (PostSpawnFunction == nullptr) { if (validatedActorArraySpawn) { CompilerContext.MessageLog.Error(*LOCTEXT("MissingFinishSpawningActorArrayFunction", "AbilityTask: Proxy is missing FinishSpawningActorArray native function. @@").ToString(), this); } else { CompilerContext.MessageLog.Error(*LOCTEXT("MissingFinishSpawningActorFunction", "AbilityTask: Proxy is missing FinishSpawningActor native function. @@").ToString(), this); } return; } UK2Node_CallFunction* const CallPrespawnProxyObjectNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); CallPrespawnProxyObjectNode->FunctionReference.SetExternalMember(ProxyPrespawnFunctionName, ProxyClass); CallPrespawnProxyObjectNode->AllocateDefaultPins(); // Hook up the self connection UEdGraphPin* PrespawnCallSelfPin = Schema->FindSelfPin(*CallPrespawnProxyObjectNode, EGPD_Input); check(PrespawnCallSelfPin); bIsErrorFree &= Schema->TryCreateConnection(ProxyObjectPin, PrespawnCallSelfPin); // Hook up input parameters to PreSpawn for (auto CurrentPin : Pins) { if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Input, Schema)) { UEdGraphPin* DestPin = CallPrespawnProxyObjectNode->FindPin(CurrentPin->PinName); if (DestPin) { bIsErrorFree &= CompilerContext.CopyPinLinksToIntermediate(*CurrentPin, *DestPin).CanSafeConnect(); } } } // Hook the activate node up in the exec chain UEdGraphPin* PrespawnExecPin = CallPrespawnProxyObjectNode->FindPinChecked(Schema->PN_Execute); UEdGraphPin* PrespawnThenPin = CallPrespawnProxyObjectNode->FindPinChecked(Schema->PN_Then); UEdGraphPin* PrespawnReturnPin = CallPrespawnProxyObjectNode->FindPinChecked(Schema->PN_ReturnValue); UEdGraphPin* SpawnedActorReturnPin = CallPrespawnProxyObjectNode->FindPinChecked(FK2Node_LatentAbilityCallHelper::SpawnedActorPinName); bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, PrespawnExecPin); LastThenPin = PrespawnThenPin; // ------------------------------------------- // Branch based on return value of Prespawn // ------------------------------------------- UK2Node_IfThenElse* BranchNode = SourceGraph->CreateBlankNode<UK2Node_IfThenElse>(); BranchNode->AllocateDefaultPins(); CompilerContext.MessageLog.NotifyIntermediateObjectCreation(BranchNode, this); // Link return value of prespawn with the branch condtional bIsErrorFree &= Schema->TryCreateConnection(PrespawnReturnPin, BranchNode->GetConditionPin()); // Link our Prespawn call to the branch node bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, BranchNode->GetExecPin()); UEdGraphPin* BranchElsePin = BranchNode->GetElsePin(); LastThenPin = BranchNode->GetThenPin(); UClass* ClassToSpawn = GetClassToSpawn(); if (validatedActorArraySpawn && ClassToSpawn) { //Branch for main loop control UK2Node_IfThenElse* Branch = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(this, SourceGraph); Branch->AllocateDefaultPins(); //Create int Iterator UK2Node_TemporaryVariable* IteratorVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph); IteratorVar->VariableType.PinCategory = Schema->PC_Int; IteratorVar->AllocateDefaultPins(); //Iterator assignment (initialization to zero) UK2Node_AssignmentStatement* IteratorInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph); IteratorInitialize->AllocateDefaultPins(); IteratorInitialize->GetValuePin()->DefaultValue = TEXT("0"); //Iterator assignment (incrementing) UK2Node_AssignmentStatement* IteratorAssign = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph); IteratorAssign->AllocateDefaultPins(); //Increment iterator command UK2Node_CallFunction* Increment = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); Increment->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(TEXT("Add_IntInt"))); Increment->AllocateDefaultPins(); Increment->FindPinChecked(TEXT("B"))->DefaultValue = TEXT("1"); //Array length UK2Node_CallArrayFunction* ArrayLength = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(this, SourceGraph); ArrayLength->SetFromFunction(UKismetArrayLibrary::StaticClass()->FindFunctionByName(TEXT("Array_Length"))); ArrayLength->AllocateDefaultPins(); //Array element retrieval UK2Node_CallArrayFunction* GetElement = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(this, SourceGraph); GetElement->SetFromFunction(UKismetArrayLibrary::StaticClass()->FindFunctionByName(TEXT("Array_Get"))); GetElement->AllocateDefaultPins(); //Check node for iterator versus array length UK2Node_CallFunction* Condition = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); Condition->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(TEXT("Less_IntInt"))); Condition->AllocateDefaultPins(); //Connections to set up the loop UEdGraphSchema_K2 const* const K2Schema = Cast<const UEdGraphSchema_K2>(Schema); bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, IteratorInitialize->GetExecPin()); bIsErrorFree &= Schema->TryCreateConnection(IteratorVar->GetVariablePin(), IteratorInitialize->GetVariablePin()); bIsErrorFree &= Schema->TryCreateConnection(IteratorInitialize->GetThenPin(), Branch->GetExecPin()); bIsErrorFree &= Schema->TryCreateConnection(SpawnedActorReturnPin, ArrayLength->GetTargetArrayPin()); bIsErrorFree &= Schema->TryCreateConnection(Condition->GetReturnValuePin(), Branch->GetConditionPin()); bIsErrorFree &= Schema->TryCreateConnection(IteratorVar->GetVariablePin(), Condition->FindPinChecked(TEXT("A"))); bIsErrorFree &= Schema->TryCreateConnection(ArrayLength->FindPin(K2Schema->PN_ReturnValue), Condition->FindPinChecked(TEXT("B"))); //Connections to establish loop iteration bIsErrorFree &= Schema->TryCreateConnection(IteratorVar->GetVariablePin(), Increment->FindPinChecked(TEXT("A"))); bIsErrorFree &= Schema->TryCreateConnection(IteratorVar->GetVariablePin(), IteratorAssign->GetVariablePin()); bIsErrorFree &= Schema->TryCreateConnection(Increment->GetReturnValuePin(), IteratorAssign->GetValuePin()); bIsErrorFree &= Schema->TryCreateConnection(IteratorAssign->GetThenPin(), Branch->GetExecPin()); //This is the inner loop LastThenPin = Branch->GetThenPin(); //Connect the loop branch to the spawn-assignment code block bIsErrorFree &= Schema->TryCreateConnection(SpawnedActorReturnPin, GetElement->GetTargetArrayPin()); bIsErrorFree &= Schema->TryCreateConnection(IteratorVar->GetVariablePin(), GetElement->FindPinChecked(K2Schema->PN_Index)); bIsErrorFree &= ConnectSpawnProperties(ClassToSpawn, Schema, CompilerContext, SourceGraph, LastThenPin, GetElement->FindPinChecked(K2Schema->PN_Item)); //Last argument is the array element bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, IteratorAssign->GetExecPin()); //Connect the spawn-assignment code block to the iterator increment //Finish by providing the proper path out LastThenPin = Branch->GetElsePin(); } // ------------------------------------------- // Set spawn variables // Borrowed heavily from FKismetCompilerUtilities::GenerateAssignmentNodes // ------------------------------------------- if (validatedActorSpawn && ClassToSpawn) { bIsErrorFree &= ConnectSpawnProperties(ClassToSpawn, Schema, CompilerContext, SourceGraph, LastThenPin, SpawnedActorReturnPin); } // ------------------------------------------- // Call FinishSpawning // ------------------------------------------- UK2Node_CallFunction* const CallPostSpawnnProxyObjectNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); CallPostSpawnnProxyObjectNode->FunctionReference.SetExternalMember(ProxyPostpawnFunctionName, ProxyClass); CallPostSpawnnProxyObjectNode->AllocateDefaultPins(); // Hook up the self connection UEdGraphPin* PostspawnCallSelfPin = Schema->FindSelfPin(*CallPostSpawnnProxyObjectNode, EGPD_Input); check(PostspawnCallSelfPin); bIsErrorFree &= Schema->TryCreateConnection(ProxyObjectPin, PostspawnCallSelfPin); // Link our Postspawn call in bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, CallPostSpawnnProxyObjectNode->FindPinChecked(Schema->PN_Execute)); // Hook up any other input parameters to PostSpawn for (auto CurrentPin : Pins) { if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Input, Schema)) { UEdGraphPin* DestPin = CallPostSpawnnProxyObjectNode->FindPin(CurrentPin->PinName); if (DestPin) { bIsErrorFree &= CompilerContext.CopyPinLinksToIntermediate(*CurrentPin, *DestPin).CanSafeConnect(); } } } UEdGraphPin* InSpawnedActorPin = CallPostSpawnnProxyObjectNode->FindPin(TEXT("SpawnedActor")); if (InSpawnedActorPin == nullptr) { CompilerContext.MessageLog.Error(*LOCTEXT("MissingSpawnedActorInputPin", "AbilityTask: Proxy is missing SpawnedActor input pin in FinishSpawningActor. @@").ToString(), this); return; } bIsErrorFree &= Schema->TryCreateConnection(SpawnedActorReturnPin, InSpawnedActorPin); LastThenPin = CallPostSpawnnProxyObjectNode->FindPinChecked(Schema->PN_Then); // Move the connections from the original node then pin to the last internal then pin bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(Schema->PN_Then), *LastThenPin).CanSafeConnect(); bIsErrorFree &= CompilerContext.CopyPinLinksToIntermediate(*LastThenPin, *BranchElsePin).CanSafeConnect(); if (!bIsErrorFree) { CompilerContext.MessageLog.Error(*LOCTEXT("InternalConnectionError", "BaseAsyncTask: Internal connection error. @@").ToString(), this); } // Make sure we caught everything BreakAllNodeLinks(); }
void UGameplayTagsK2Node_LiteralGameplayTag::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); // Get The input and output pins to our node UEdGraphPin* TagInPin = FindPin(TEXT("TagIn")); UEdGraphPin* TagOutPin = FindPinChecked(Schema->PN_ReturnValue); // Create a Make Struct UK2Node_MakeStruct* MakeStructNode = SourceGraph->CreateBlankNode<UK2Node_MakeStruct>(); MakeStructNode->StructType = FGameplayTagContainer::StaticStruct(); MakeStructNode->AllocateDefaultPins(); // Create a Make Array UK2Node_MakeArray* MakeArrayNode = SourceGraph->CreateBlankNode<UK2Node_MakeArray>(); MakeArrayNode->AllocateDefaultPins(); // Connect the output of our MakeArray to the Input of our MakeStruct so it sets the Array Type UEdGraphPin* InPin = MakeStructNode->FindPin( TEXT("GameplayTags") ); if( InPin ) { InPin->MakeLinkTo( MakeArrayNode->GetOutputPin() ); } // Add the FName Values to the MakeArray input pins UEdGraphPin* ArrayInputPin = NULL; FString TagString = TagInPin->GetDefaultAsString(); if( TagString.StartsWith( TEXT("(") ) && TagString.EndsWith( TEXT(")") ) ) { TagString = TagString.LeftChop(1); TagString = TagString.RightChop(1); TagString.Split("=", NULL, &TagString); TagString = TagString.LeftChop(1); TagString = TagString.RightChop(1); FString ReadTag; FString Remainder; int32 MakeIndex = 0; while( TagString.Split( TEXT(","), &ReadTag, &Remainder ) ) { TagString = Remainder; ArrayInputPin = MakeArrayNode->FindPin( FString::Printf( TEXT("[%d]"), MakeIndex ) ); ArrayInputPin->PinType.PinCategory = TEXT("struct"); ArrayInputPin->PinType.PinSubCategoryObject = FGameplayTag::StaticStruct(); ArrayInputPin->DefaultValue = ReadTag; MakeIndex++; MakeArrayNode->AddInputPin(); } if( Remainder.IsEmpty() ) { Remainder = TagString; } if( !Remainder.IsEmpty() ) { ArrayInputPin = MakeArrayNode->FindPin( FString::Printf( TEXT("[%d]"), MakeIndex ) ); ArrayInputPin->PinType.PinCategory = TEXT("struct"); ArrayInputPin->PinType.PinSubCategoryObject = FGameplayTag::StaticStruct(); ArrayInputPin->DefaultValue = Remainder; MakeArrayNode->PostReconstructNode(); } else { MakeArrayNode->RemoveInputPin(MakeArrayNode->FindPin(TEXT("[0]"))); MakeArrayNode->PostReconstructNode(); } } else { MakeArrayNode->RemoveInputPin(MakeArrayNode->FindPin(TEXT("[0]"))); MakeArrayNode->PostReconstructNode(); } // Move the Output of the MakeArray to the Output of our node UEdGraphPin* OutPin = MakeStructNode->FindPin( MakeStructNode->StructType->GetName() ); if( OutPin && TagOutPin ) { OutPin->PinType = TagOutPin->PinType; // Copy type so it uses the right actor subclass CompilerContext.MovePinLinksToIntermediate(*TagOutPin, *OutPin); } // Break any links to the expanded node BreakAllNodeLinks(); }
UEdGraphPin* UK2Node_SpawnActor::GetNoCollisionFailPin()const { UEdGraphPin* Pin = FindPinChecked(NoCollisionFailPinName); check(Pin->Direction == EGPD_Input); return Pin; }
UEdGraphPin* UK2Node_SpawnActor::GetSpawnTransformPin()const { UEdGraphPin* Pin = FindPinChecked(SpawnTransformPinName); check(Pin->Direction == EGPD_Input); return Pin; }
UEdGraphPin* UK2Node_ClassDynamicCast::GetCastSourcePin() const { UEdGraphPin* Pin = FindPinChecked(FClassDynamicCastHelper::GetClassToCastName()); check(Pin->Direction == EGPD_Input); return Pin; }
UEdGraphPin* UK2Node_PureAssignmentStatement::GetValuePin() const { return FindPinChecked(ValuePinName); }
UEdGraphPin *UK2Node_LiveEditObject::GetClampMaxPin() const { UEdGraphPin *Pin = FindPinChecked( UK2Node_LiveEditObjectStatics::ClampMaxPinName ); check(Pin->Direction == EGPD_Input); return Pin; }
UEdGraphPin *UK2Node_LiveEditObject::GetOnMidiInputPin() const { UEdGraphPin *Pin = FindPinChecked( UK2Node_LiveEditObjectStatics::EventPinName ); check(Pin->Direction == EGPD_Output); return Pin; }
UEdGraphPin *UK2Node_LiveEditObject::GetPermittedBindingsPin() const { UEdGraphPin *Pin = FindPinChecked( UK2Node_LiveEditObjectStatics::PermittedBindingsPinName ); check(Pin->Direction == EGPD_Input); return Pin; }
UEdGraphPin* UK2Node_TransitionRuleGetter::GetOutputPin() const { return FindPinChecked(TEXT("Output")); }
void UK2Node_PlayMovieScene::PinConnectionListChanged( UEdGraphPin* InPin ) { // Call parent implementation Super::PostReconstructNode(); // @todo sequencer: What about the "default" value for the pin? Right now we don't do anything with that. Might feel buggy. // --> Actually, we can't use that yet because Actor references won't be fixed up for PIE except for literals CreateBindingsIfNeeded(); // Update the MovieScene bindings for any changes that were made to the node // @todo sequencer: Only a single incoming Pin is changing, but we're refreshing all pin states here. Could be simplified bool bAnyBindingsChanged = false; { auto& BoundObjects = MovieSceneBindings->GetBoundObjects(); for( auto BoundObjectIter( BoundObjects.CreateIterator() ); BoundObjectIter; ++BoundObjectIter ) { auto& BoundObject = *BoundObjectIter; TArray< UObject* > OtherObjects; // Find the pin that goes with this object auto* Pin = FindPinChecked( BoundObject.GetPossessableGuid().ToString( EGuidFormats::DigitsWithHyphens ) ); // Is the pin even linked to anything? if( Pin->LinkedTo.Num() > 0 ) { // @todo sequencer major: Add support for multiple connections to a single input pin to be treated as an "unordered array" for( auto OtherPinIter( Pin->LinkedTo.CreateIterator() ); OtherPinIter; ++OtherPinIter ) { auto* OtherPin = *OtherPinIter; if( OtherPin != NULL ) { // Look for an object bound to a literal. We can use these for scrub preview in the editor! UK2Node_Literal* OtherLiteralPin = Cast< UK2Node_Literal >( OtherPin->GetOwningNode() ); if( OtherLiteralPin != NULL ) { // @todo sequencer: Because we only recognize object literals, any dynamically bound actor won't be scrubable // in the editor. Maybe we should support a "preview actor" that can be hooked up just for scrubbing and puppeting? // ==> Maybe use the pin's default value as the preview actor, even when overridden with a dynamically spawned actor? UObject* OtherObject = OtherLiteralPin->GetObjectRef(); if ( OtherObject != NULL ) { OtherObjects.Add( OtherObject ); } } } } } // Update our bindings to match the state of the node if( BoundObject.GetObjects() != OtherObjects ) // @todo sequencer: Kind of weird to compare entire array (order change shouldn't cause us to invalidate). Change to a set compare? { // @todo sequencer: No type checking is happening here. Should we? (interactive during pin drag?) Modify(); BoundObject.SetObjects( OtherObjects ); bAnyBindingsChanged = true; } } } if( bAnyBindingsChanged ) { OnBindingsChangedEvent.Broadcast(); } }
UEdGraphPin* UK2Node_PlayMovieScene::GetPlayPin() const { UEdGraphPin* Pin = FindPinChecked( PlayMovieScenePinNames::Play ); check( Pin->Direction == EGPD_Input ); return Pin; }