virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override { auto PureAssignmentNode = CastChecked<UK2Node_PureAssignmentStatement>(Node); { auto VariablePin = PureAssignmentNode->GetVariablePin(); if (VariablePin->LinkedTo.Num() == 0) { CompilerContext.MessageLog.Error(*LOCTEXT("NoVariableConnected_Error", "A variable needs to be connected to @@").ToString(), VariablePin); return; } auto OutputPin = PureAssignmentNode->GetOutputPin(); if (OutputPin->LinkedTo.Num() == 0) { CompilerContext.MessageLog.Error(*LOCTEXT("NoOutputConnected_Error", "A output pin needs to be connected to @@").ToString(), OutputPin); return; } // At the moment, a term for variable should be already registered auto VariableTermPtr = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(VariablePin)); if (!VariableTermPtr || !*VariableTermPtr) { CompilerContext.MessageLog.Error(*LOCTEXT("NoVarriableTerm_Error", "ICE: no variable term found in @@").ToString(), Node); return; } FBPTerminal* VariableTerm = *VariableTermPtr; // we must take a copy here because Add takes a reference and the map might be resized Context.NetMap.Add(OutputPin, VariableTerm); } auto ValuePin = PureAssignmentNode->GetValuePin(); ValidateAndRegisterNetIfLiteral(Context, ValuePin); }
void UK2Node_VariableSetRef::CoerceTypeFromPin(const UEdGraphPin* Pin) { const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); UEdGraphPin* TargetPin = GetTargetPin(); UEdGraphPin* ValuePin = GetValuePin(); if( Pin ) { check((Pin != TargetPin) || (Pin->PinType.bIsReference && !Pin->PinType.bIsArray)); TargetPin->PinType = Pin->PinType; TargetPin->PinType.bIsReference = true; ValuePin->PinType = Pin->PinType; ValuePin->PinType.bIsReference = false; } else { // Pin disconnected...revert to wildcard TargetPin->PinType.PinCategory = K2Schema->PC_Wildcard; TargetPin->PinType.PinSubCategory = TEXT(""); TargetPin->PinType.PinSubCategoryObject = NULL; TargetPin->BreakAllPinLinks(); ValuePin->PinType.PinCategory = K2Schema->PC_Wildcard; ValuePin->PinType.PinSubCategory = TEXT(""); ValuePin->PinType.PinSubCategoryObject = NULL; ValuePin->BreakAllPinLinks(); CachedNodeTitle.MarkDirty(); } }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { auto PureAssignmentNode = CastChecked<UK2Node_PureAssignmentStatement>(Node); auto VariablePin = PureAssignmentNode->GetVariablePin(); auto ValuePin = PureAssignmentNode->GetValuePin(); InnerAssignment(Context, Node, VariablePin, ValuePin); }
void UK2Node_VariableGet::GetContextMenuActions(const FGraphNodeContextMenuBuilder& Context) const { Super::GetContextMenuActions(Context); const UEdGraphPin* ValuePin = GetValuePin(); if (IsValidTypeForNonPure(ValuePin->PinType)) { Context.MenuBuilder->BeginSection("K2NodeVariableGet", LOCTEXT("VariableGetHeader", "Variable Get")); { FText MenuEntryTitle; FText MenuEntryTooltip; bool bCanTogglePurity = true; auto CanExecutePurityToggle = [](bool const bInCanTogglePurity)->bool { return bInCanTogglePurity; }; if (bIsPureGet) { MenuEntryTitle = LOCTEXT("ConvertToImpureGetTitle", "Convert to Validated Get"); MenuEntryTooltip = LOCTEXT("ConvertToImpureGetTooltip", "Adds in branching execution pins so that you can separately handle when the returned value is valid/invalid."); const UEdGraphSchema_K2* K2Schema = Cast<UEdGraphSchema_K2>(GetSchema()); check(K2Schema != nullptr); bCanTogglePurity = K2Schema->DoesGraphSupportImpureFunctions(GetGraph()); if (!bCanTogglePurity) { MenuEntryTooltip = LOCTEXT("CannotMakeImpureGetTooltip", "This graph does not support impure calls!"); } } else { MenuEntryTitle = LOCTEXT("ConvertToPureGetTitle", "Convert to pure Get"); MenuEntryTooltip = LOCTEXT("ConvertToPureGetTooltip", "Removes the execution pins to make the node more versatile."); } Context.MenuBuilder->AddMenuEntry( MenuEntryTitle, MenuEntryTooltip, FSlateIcon(), FUIAction( FExecuteAction::CreateUObject(this, &UK2Node_VariableGet::TogglePurity), FCanExecuteAction::CreateStatic(CanExecutePurityToggle, bCanTogglePurity), FIsActionChecked() ) ); } Context.MenuBuilder->EndSection(); } }
void UK2Node_VariableSetRef::NotifyPinConnectionListChanged(UEdGraphPin* Pin) { Super::NotifyPinConnectionListChanged(Pin); UEdGraphPin* TargetPin = GetTargetPin(); UEdGraphPin* ValuePin = GetValuePin(); if( (Pin == TargetPin) || (Pin == ValuePin) ) { UEdGraphPin* ConnectedToPin = (Pin->LinkedTo.Num() > 0) ? Pin->LinkedTo[0] : NULL; CoerceTypeFromPin(ConnectedToPin); } }
bool UK2Node_Variable::RemapRestrictedLinkReference(FName OldVariableName, FName NewVariableName, const UClass* MatchInVariableClass, const UClass* RemapIfLinkedToClass, bool bLogWarning) { bool bRemapped = false; if (VariableReference.GetMemberName() == OldVariableName) { UClass* const VarClass = GetVariableSourceClass(); if (VarClass->IsChildOf(MatchInVariableClass)) { UEdGraphPin* VariablePin = GetValuePin(); if (VariablePin) { for (UEdGraphPin* OtherPin : VariablePin->LinkedTo) { if (OtherPin != nullptr && VariablePin->PinType.PinCategory == OtherPin->PinType.PinCategory) { // If any other pin we are linked to is a more restricted type, we need to do the remap. const UClass* OtherPinClass = Cast<UClass>(OtherPin->PinType.PinSubCategoryObject.Get()); if (OtherPinClass && OtherPinClass->IsChildOf(RemapIfLinkedToClass)) { if (VariableReference.IsSelfContext()) { VariableReference.SetSelfMember(NewVariableName); } else { VariableReference.SetExternalMember(NewVariableName, VarClass); } bRemapped = true; break; } } } } } } if (bRemapped && bLogWarning && GetBlueprint()) { FMessageLog("BlueprintLog").Warning( FText::Format( LOCTEXT("RemapRestrictedLinkReference", "{0}: Variable '{1}' was automatically changed to '{2}'. Verify that logic works as intended. (This warning will disappear once the blueprint has been resaved)"), FText::FromString(GetBlueprint()->GetPathName()), FText::FromString(MatchInVariableClass->GetName() + TEXT(".") + OldVariableName.ToString()), FText::FromString(MatchInVariableClass->GetName() + TEXT(".") + NewVariableName.ToString()) )); } return bRemapped; }
void UK2Node_PureAssignmentStatement::NotifyPinConnectionListChanged(UEdGraphPin* Pin) { Super::NotifyPinConnectionListChanged(Pin); if (Pin->LinkedTo.Num() > 0) { const auto PinType = Pin->LinkedTo[0]->PinType; auto VariablePin = GetVariablePin(); VariablePin->PinType = PinType; UEdGraphSchema_K2::ValidateExistingConnections(VariablePin); auto OutputPin = GetOutputPin(); OutputPin->PinType = PinType; UEdGraphSchema_K2::ValidateExistingConnections(OutputPin); auto ValuePin = GetValuePin(); ValuePin->PinType = PinType; ValuePin->PinType.bIsReference = false; UEdGraphSchema_K2::ValidateExistingConnections(ValuePin); } }
void UK2Node_VariableGet::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); // Do not attempt to expand the node when not a pure get nor when there is no property. Normal compilation error detection will detect the missing property. if (!bIsPureGet && GetPropertyForVariable() != nullptr) { const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); UEdGraphPin* ValuePin = GetValuePin(); // Impure Get nodes convert into three nodes: // 1. A pure Get node // 2. An IsValid node // 3. A Branch node (only impure part // Create the impure Get node UK2Node_VariableGet* VariableGetNode = CompilerContext.SpawnIntermediateNode<UK2Node_VariableGet>(this, SourceGraph); VariableGetNode->VariableReference = VariableReference; VariableGetNode->AllocateDefaultPins(); CompilerContext.MessageLog.NotifyIntermediateObjectCreation(VariableGetNode, this); // Move pin links from Get node we are expanding, to the new pure one we've created CompilerContext.MovePinLinksToIntermediate(*ValuePin, *VariableGetNode->GetValuePin()); if (!VariableReference.IsLocalScope()) { CompilerContext.MovePinLinksToIntermediate(*FindPin(Schema->PN_Self), *VariableGetNode->FindPin(Schema->PN_Self)); } // Create the IsValid node UK2Node_CallFunction* IsValidFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); // Based on if the type is an "Object" or a "Class" changes which function to use if (ValuePin->PinType.PinCategory == UObject::StaticClass()->GetName()) { IsValidFunction->SetFromFunction(UKismetSystemLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetSystemLibrary, IsValid))); } else if (ValuePin->PinType.PinCategory == UClass::StaticClass()->GetName()) { IsValidFunction->SetFromFunction(UKismetSystemLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetSystemLibrary, IsValidClass))); } IsValidFunction->AllocateDefaultPins(); CompilerContext.MessageLog.NotifyIntermediateObjectCreation(IsValidFunction, this); // Connect the value pin from the new Get node to the IsValid UEdGraphPin* ObjectPin = IsValidFunction->Pins[1]; check(ObjectPin->Direction == EGPD_Input); ObjectPin->MakeLinkTo(VariableGetNode->GetValuePin()); // Create the Branch node UK2Node_IfThenElse* BranchNode = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(this, SourceGraph); BranchNode->AllocateDefaultPins(); CompilerContext.MessageLog.NotifyIntermediateObjectCreation(BranchNode, this); // Connect the bool output pin from IsValid node to the Branch node UEdGraphPin* BoolPin = IsValidFunction->Pins[2]; check(BoolPin->Direction == EGPD_Output); BoolPin->MakeLinkTo(BranchNode->GetConditionPin()); // Connect the Branch node to the input of the impure Get node CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *BranchNode->GetExecPin()); // Move the two Branch pins to the Branch node CompilerContext.MovePinLinksToIntermediate(*FindPin(Schema->PN_Then), *BranchNode->FindPin(Schema->PN_Then)); CompilerContext.MovePinLinksToIntermediate(*FindPin(Schema->PN_Else), *BranchNode->FindPin(Schema->PN_Else)); BreakAllNodeLinks(); } }
void UK2Node_LoadAsset::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); check(Schema); bool bIsErrorFree = true; // Create LoadAsset function call auto CallLoadAssetNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); CallLoadAssetNode->FunctionReference.SetExternalMember(NativeFunctionName(), UKismetSystemLibrary::StaticClass()); CallLoadAssetNode->AllocateDefaultPins(); // connect to input exe { auto InputExePin = GetExecPin(); auto CallFunctionInputExePin = CallLoadAssetNode->GetExecPin(); bIsErrorFree &= InputExePin && CallFunctionInputExePin && CompilerContext.MovePinLinksToIntermediate(*InputExePin, *CallFunctionInputExePin).CanSafeConnect(); } // Create Local Variable UK2Node_TemporaryVariable* TempVarOutput = CompilerContext.SpawnInternalVariable(this, GetOutputCategory(), FString(), UObject::StaticClass(), false); // Create assign node auto AssignNode = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph); AssignNode->AllocateDefaultPins(); auto LoadedObjectVariablePin = TempVarOutput->GetVariablePin(); // connect local variable to assign node { auto AssignLHSPPin = AssignNode->GetVariablePin(); bIsErrorFree &= AssignLHSPPin && LoadedObjectVariablePin && Schema->TryCreateConnection(AssignLHSPPin, LoadedObjectVariablePin); } // connect local variable to output { auto OutputObjectPinPin = FindPin(GetOutputPinName()); bIsErrorFree &= LoadedObjectVariablePin && OutputObjectPinPin && CompilerContext.MovePinLinksToIntermediate(*OutputObjectPinPin, *LoadedObjectVariablePin).CanSafeConnect(); } // connect assign exec input to function output { auto CallFunctionOutputExePin = CallLoadAssetNode->FindPin(Schema->PN_Then); auto AssignInputExePin = AssignNode->GetExecPin(); bIsErrorFree &= AssignInputExePin && CallFunctionOutputExePin && Schema->TryCreateConnection(AssignInputExePin, CallFunctionOutputExePin); } // connect assign exec output to output { auto OutputExePin = FindPin(Schema->PN_Then); auto AssignOutputExePin = AssignNode->GetThenPin(); bIsErrorFree &= OutputExePin && AssignOutputExePin && CompilerContext.MovePinLinksToIntermediate(*OutputExePin, *AssignOutputExePin).CanSafeConnect(); } // connect to asset { auto AssetPin = FindPin(GetInputPinName()); auto CallFunctionAssetPin = CallLoadAssetNode->FindPin(GetInputPinName()); ensure(CallFunctionAssetPin); bIsErrorFree &= AssetPin && CallFunctionAssetPin && CompilerContext.MovePinLinksToIntermediate(*AssetPin, *CallFunctionAssetPin).CanSafeConnect(); } // Create OnLoadEvent const FString DelegateOnLoadedParamName(TEXT("OnLoaded")); auto OnLoadEventNode = CompilerContext.SpawnIntermediateNode<UK2Node_CustomEvent>(this, SourceGraph); OnLoadEventNode->CustomFunctionName = *FString::Printf(TEXT("OnLoaded_%s"), *OnLoadEventNode->NodeGuid.ToString()); OnLoadEventNode->AllocateDefaultPins(); { UFunction* LoadAssetFunction = CallLoadAssetNode->GetTargetFunction(); UDelegateProperty* OnLoadDelegateProperty = LoadAssetFunction ? FindField<UDelegateProperty>(LoadAssetFunction, *DelegateOnLoadedParamName) : nullptr; UFunction* OnLoadedSignature = OnLoadDelegateProperty ? OnLoadDelegateProperty->SignatureFunction : nullptr; ensure(OnLoadedSignature); for (TFieldIterator<UProperty> PropIt(OnLoadedSignature); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt) { const UProperty* Param = *PropIt; if (!Param->HasAnyPropertyFlags(CPF_OutParm) || Param->HasAnyPropertyFlags(CPF_ReferenceParm)) { FEdGraphPinType PinType; bIsErrorFree &= Schema->ConvertPropertyToPinType(Param, /*out*/ PinType); bIsErrorFree &= (NULL != OnLoadEventNode->CreateUserDefinedPin(Param->GetName(), PinType, EGPD_Output)); } } } // connect delegate { auto CallFunctionDelegatePin = CallLoadAssetNode->FindPin(DelegateOnLoadedParamName); ensure(CallFunctionDelegatePin); auto EventDelegatePin = OnLoadEventNode->FindPin(UK2Node_CustomEvent::DelegateOutputName); bIsErrorFree &= CallFunctionDelegatePin && EventDelegatePin && Schema->TryCreateConnection(CallFunctionDelegatePin, EventDelegatePin); } // connect loaded object from event to assign { auto LoadedAssetEventPin = OnLoadEventNode->FindPin(TEXT("Loaded")); ensure(LoadedAssetEventPin); auto AssignRHSPPin = AssignNode->GetValuePin(); bIsErrorFree &= AssignRHSPPin && LoadedAssetEventPin && Schema->TryCreateConnection(LoadedAssetEventPin, AssignRHSPPin); } if (!bIsErrorFree) { CompilerContext.MessageLog.Error(*LOCTEXT("InternalConnectionError", "K2Node_LoadAsset: Internal connection error. @@").ToString(), this); } BreakAllNodeLinks(); }