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);
	}
}
Esempio n. 6
0
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();
}