void UK2Node_DynamicCast::NotifyPinConnectionListChanged(UEdGraphPin* Pin) { Super::NotifyPinConnectionListChanged(Pin); if (Pin == GetCastSourcePin()) { Pin->PinFriendlyName = FText::GetEmpty(); FEdGraphPinType& InputPinType = Pin->PinType; if (Pin->LinkedTo.Num() == 0) { InputPinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard; InputPinType.PinSubCategory.Empty(); InputPinType.PinSubCategoryObject = nullptr; } else { const FEdGraphPinType& ConnectedPinType = Pin->LinkedTo[0]->PinType; if (ConnectedPinType.PinCategory == UEdGraphSchema_K2::PC_Interface) { Pin->PinFriendlyName = LOCTEXT("InterfaceInputName", "Interface"); InputPinType.PinCategory = UEdGraphSchema_K2::PC_Interface; InputPinType.PinSubCategoryObject = ConnectedPinType.PinSubCategoryObject; } else if (ConnectedPinType.PinCategory == UEdGraphSchema_K2::PC_Object) { InputPinType.PinCategory = UEdGraphSchema_K2::PC_Object; InputPinType.PinSubCategoryObject = UObject::StaticClass(); } } } }
bool UK2Node_DynamicCast::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const { bool bIsDisallowed = Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason); if (MyPin == GetCastSourcePin()) { const FEdGraphPinType& OtherPinType = OtherPin->PinType; const FText OtherPinName = OtherPin->PinFriendlyName.IsEmpty() ? FText::FromString(OtherPin->PinName) : OtherPin->PinFriendlyName; if (OtherPinType.IsContainer()) { bIsDisallowed = true; OutReason = LOCTEXT("CannotContainerCast", "You cannot cast containers of objects.").ToString(); } else if (TargetType == nullptr) { bIsDisallowed = true; OutReason = LOCTEXT("InvalidTargetType", "This cast has an invalid target type (was the class deleted without a redirect?).").ToString(); } else if ((OtherPinType.PinCategory == UEdGraphSchema_K2::PC_Interface) || TargetType->HasAnyClassFlags(CLASS_Interface)) { // allow all interface casts } else if (OtherPinType.PinCategory == UEdGraphSchema_K2::PC_Object) { // let's handle wasted cast inputs with warnings in ValidateNodeDuringCompilation() instead } else { bIsDisallowed = true; OutReason = LOCTEXT("NonObjectCast", "You can only cast objects/interfaces.").ToString(); } } return bIsDisallowed; }
bool UK2Node_DynamicCast::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const { bool bIsDisallowed = Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason); if (MyPin == GetCastSourcePin()) { const FEdGraphPinType& OtherPinType = OtherPin->PinType; const FText OtherPinName = OtherPin->PinFriendlyName.IsEmpty() ? FText::FromString(OtherPin->PinName) : OtherPin->PinFriendlyName; if (OtherPinType.bIsArray) { bIsDisallowed = true; OutReason = LOCTEXT("CannotArrayCast", "You cannot cast arrays of objects.").ToString(); } else if (TargetType == nullptr) { bIsDisallowed = true; OutReason = LOCTEXT("BadCastNode", "This cast has an invalid target type (was the class deleted without a redirect?).").ToString(); } else if ((OtherPinType.PinCategory == UEdGraphSchema_K2::PC_Interface) || TargetType->HasAnyClassFlags(CLASS_Interface)) { // allow all interface casts } else if (OtherPinType.PinCategory == UEdGraphSchema_K2::PC_Object) { UClass* ObjectClass = Cast<UClass>(OtherPinType.PinSubCategoryObject.Get()); if ((ObjectClass == nullptr) && (OtherPinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self)) { if (UK2Node* K2Node = Cast<UK2Node>(OtherPin->GetOwningNode())) { ObjectClass = K2Node->GetBlueprint()->GeneratedClass; } } // if the ObjectClass is still null, assume it is a UObject, which // will work with everything (so don't disallow it) if (ObjectClass != nullptr) { if (ObjectClass == TargetType) { bIsDisallowed = true; OutReason = FText::Format(LOCTEXT("EqualObjectCast", "'{0}' is already a '{1}', you don't need the cast."), OtherPinName, TargetType->GetDisplayNameText()).ToString(); } else if (ObjectClass->IsChildOf(TargetType)) { bIsDisallowed = true; OutReason = FText::Format(LOCTEXT("UnneededObjectCast", "'{0}' is already a '{1}' (which inherits from '{2}'), so you don't need the cast."), OtherPinName, ObjectClass->GetDisplayNameText(), TargetType->GetDisplayNameText()).ToString(); } else if (!TargetType->IsChildOf(ObjectClass)) { bIsDisallowed = true; OutReason = FText::Format(LOCTEXT("DisallowedObjectCast", "'{0}' does not inherit from '{1}' (the cast would always fail)."), TargetType->GetDisplayNameText(), ObjectClass->GetDisplayNameText()).ToString(); } } } else { bIsDisallowed = true; OutReason = LOCTEXT("NonObjectCast", "You can only cast objects/interfaces.").ToString(); } } return bIsDisallowed; }
void UK2Node_DynamicCast::PostReconstructNode() { Super::PostReconstructNode(); // update the pin name (to "Interface" if an interface is connected) NotifyPinConnectionListChanged(GetCastSourcePin()); }
void UK2Node_DynamicCast::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const { Super::ValidateNodeDuringCompilation(MessageLog); UEdGraphPin* SourcePin = GetCastSourcePin(); if (SourcePin->LinkedTo.Num() > 0) { const UClass* SourceType = *TargetType; if (SourceType == nullptr) { return; } for (UEdGraphPin* CastInput : SourcePin->LinkedTo) { const FEdGraphPinType& SourcePinType = CastInput->PinType; if (SourcePinType.PinCategory != UEdGraphSchema_K2::PC_Object) { // all other types should have been rejected by IsConnectionDisallowed() continue; } UClass* SourceClass = Cast<UClass>(SourcePinType.PinSubCategoryObject.Get()); if ((SourceClass == nullptr) && (SourcePinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self)) { if (UK2Node* K2Node = Cast<UK2Node>(CastInput->GetOwningNode())) { SourceClass = K2Node->GetBlueprint()->GeneratedClass; } } if (SourceClass == nullptr) { const FString SourcePinName = CastInput->PinFriendlyName.IsEmpty() ? CastInput->PinName : CastInput->PinFriendlyName.ToString(); FText const ErrorFormat = LOCTEXT("BadCastInput", "'%s' does not have a clear object type (invalid input into @@)."); MessageLog.Error( *FString::Printf(*ErrorFormat.ToString(), *SourcePinName), this ); continue; } if (SourceClass == SourceType) { const FString SourcePinName = CastInput->PinFriendlyName.IsEmpty() ? CastInput->PinName : CastInput->PinFriendlyName.ToString(); FText const WarningFormat = LOCTEXT("EqualObjectCast", "'%s' is already a '%s', you don't need @@."); MessageLog.Warning( *FString::Printf(*WarningFormat.ToString(), *SourcePinName, *TargetType->GetDisplayNameText().ToString()), this ); } else if (SourceClass->IsChildOf(SourceType)) { const FString SourcePinName = CastInput->PinFriendlyName.IsEmpty() ? CastInput->PinName : CastInput->PinFriendlyName.ToString(); FText const WarningFormat = LOCTEXT("UnneededObjectCast", "'%s' is already a '%s' (which inherits from '%s'), so you don't need @@."); MessageLog.Warning( *FString::Printf(*WarningFormat.ToString(), *SourcePinName, *SourceClass->GetDisplayNameText().ToString(), *TargetType->GetDisplayNameText().ToString()), this ); } else if (!SourceType->IsChildOf(SourceClass) && !FKismetEditorUtilities::IsClassABlueprintInterface(SourceType)) { FText const WarningFormat = LOCTEXT("DisallowedObjectCast", "'%s' does not inherit from '%s' (@@ would always fail)."); MessageLog.Warning( *FString::Printf(*WarningFormat.ToString(), *TargetType->GetDisplayNameText().ToString(), *SourceClass->GetDisplayNameText().ToString()), this ); } } } }