You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Plugins/BlueprintContext - Editor/GlueprintGraph - Editor/GraphEditor - Editor/Kismet - Editor/KismetCompiler - Editor/UnrealEd/Private/Kismet2 note: if you're seeing this CL in the perforce history because you're trying to figure out why there's a null check that doesn't make sense, This is why. The goal of this CL is to preserve the behavior before IsChildOf changed rather than analyze whether that behavior makes sense. Use your best judgement #rb marc.audy #preflight 6299023a6438e3c731307a69 [CL 20474984 by jordan hoffmann in ue5-main branch]
1162 lines
39 KiB
C++
1162 lines
39 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "K2Node_Variable.h"
|
|
#include "K2Node_FunctionEntry.h"
|
|
#include "BlueprintCompilationManager.h"
|
|
#include "UObject/UObjectHash.h"
|
|
#include "Components/PrimitiveComponent.h"
|
|
#include "GameFramework/MovementComponent.h"
|
|
#include "Engine/BlueprintGeneratedClass.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "KismetCompilerMisc.h"
|
|
#include "Kismet2/CompilerResultsLog.h"
|
|
#include "Kismet2/StructureEditorUtils.h"
|
|
#include "Styling/SlateIconFinder.h"
|
|
#include "Logging/MessageLog.h"
|
|
#include "SourceCodeNavigation.h"
|
|
#include "Engine/SimpleConstructionScript.h"
|
|
#include "Editor/UnrealEdEngine.h"
|
|
#include "Preferences/UnrealEdOptions.h"
|
|
#include "UnrealEdGlobals.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "K2Node"
|
|
|
|
UK2Node_Variable::UK2Node_Variable(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
}
|
|
|
|
void UK2Node_Variable::Serialize(FArchive& Ar)
|
|
{
|
|
Super::Serialize(Ar);
|
|
|
|
// Fix old content
|
|
if(Ar.IsLoading())
|
|
{
|
|
if(Ar.UEVer() < VER_UE4_VARK2NODE_USE_MEMBERREFSTRUCT)
|
|
{
|
|
// Copy info into new struct
|
|
VariableReference.SetDirect(VariableName_DEPRECATED, FGuid(), VariableSourceClass_DEPRECATED, bSelfContext_DEPRECATED);
|
|
}
|
|
|
|
if(Ar.UEVer() < VER_UE4_K2NODE_VAR_REFERENCEGUIDS)
|
|
{
|
|
FGuid VarGuid;
|
|
|
|
// Do not let this code run for local variables
|
|
if (!VariableReference.IsLocalScope())
|
|
{
|
|
const bool bSelf = VariableReference.IsSelfContext();
|
|
UClass* MemberParentClass = VariableReference.GetMemberParentClass(nullptr);
|
|
if (UBlueprint::GetGuidFromClassByFieldName<FProperty>(bSelf? *GetBlueprint()->GeneratedClass : MemberParentClass, VariableReference.GetMemberName(), VarGuid))
|
|
{
|
|
VariableReference.SetDirect(VariableReference.GetMemberName(), VarGuid, bSelf ? nullptr : MemberParentClass, bSelf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UK2Node_Variable::SetFromProperty(const FProperty* Property, bool bSelfContext, UClass* OwnerClass)
|
|
{
|
|
SelfContextInfo = bSelfContext ? ESelfContextInfo::Unspecified : ESelfContextInfo::NotSelfContext;
|
|
VariableReference.SetFromField<FProperty>(Property, bSelfContext, OwnerClass);
|
|
}
|
|
|
|
bool UK2Node_Variable::CreatePinForVariable(EEdGraphPinDirection Direction, FName InPinName/* = NAME_None */)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
|
|
FProperty* VariableProperty = GetPropertyForVariable();
|
|
// favor the skeleton property if possible (in case the property type has
|
|
// been changed, and not yet compiled).
|
|
if (!VariableReference.IsSelfContext())
|
|
{
|
|
UClass* VariableClass = VariableReference.GetMemberParentClass(GetBlueprint()->GeneratedClass);
|
|
if (UBlueprintGeneratedClass* BpClassOwner = Cast<UBlueprintGeneratedClass>(VariableClass))
|
|
{
|
|
// this variable could currently only be a part of some skeleton
|
|
// class (the blueprint has not be compiled with it yet), so let's
|
|
// check the skeleton class as well, see if we can pull pin data
|
|
// from there...
|
|
UBlueprint* VariableBlueprint = CastChecked<UBlueprint>(BpClassOwner->ClassGeneratedBy, ECastCheckedType::NullAllowed);
|
|
if (VariableBlueprint)
|
|
{
|
|
if (FProperty* SkelProperty = GetPropertyForVariableFromSkeleton())
|
|
{
|
|
VariableProperty = SkelProperty;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (VariableProperty != nullptr)
|
|
{
|
|
const FName PinName = InPinName.IsNone() ? GetVarName() : InPinName;
|
|
// Create the pin
|
|
UEdGraphPin* VariablePin = CreatePin(Direction, NAME_None, PinName);
|
|
if (VariableProperty->IsNative())
|
|
{
|
|
VariablePin->PinFriendlyName = VariableProperty->GetDisplayNameText();
|
|
}
|
|
|
|
K2Schema->ConvertPropertyToPinType(VariableProperty, /*out*/ VariablePin->PinType);
|
|
K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(VariablePin);
|
|
}
|
|
else
|
|
{
|
|
// Don't handle warning or error logging here, that needs to be done in ValidateNodeDuringCompilation
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void UK2Node_Variable::CreatePinForSelf()
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
// Create the self pin
|
|
if (!K2Schema->FindSelfPin(*this, EGPD_Input))
|
|
{
|
|
// Do not create a self pin for locally scoped variables or sparse class data
|
|
if (!VariableReference.IsLocalScope())
|
|
{
|
|
bool bSelfTarget = VariableReference.IsSelfContext() && (ESelfContextInfo::NotSelfContext != SelfContextInfo);
|
|
UClass* MemberParentClass = VariableReference.GetMemberParentClass(GetBlueprintClassFromNode());
|
|
UClass* TargetClass = MemberParentClass;
|
|
|
|
FProperty* VariableProperty = GetPropertyForVariable();
|
|
|
|
if (VariableProperty)
|
|
{
|
|
UClass* PropertyClass = Cast<UClass>(VariableProperty->GetOwner<UObject>());
|
|
|
|
// Fix up target class if it's not correct, this fixes cases where variables have moved within the hierarchy
|
|
if (PropertyClass && PropertyClass != TargetClass)
|
|
{
|
|
TargetClass = PropertyClass;
|
|
}
|
|
}
|
|
|
|
// Self Target pins should always make the class be the owning class of the property,
|
|
// so if the node is from a Macro Blueprint, it will hook up as self in any placed Blueprint
|
|
if (bSelfTarget)
|
|
{
|
|
if (FProperty* Property = VariableReference.ResolveMember<FProperty>(GetBlueprintClassFromNode()))
|
|
{
|
|
UClass* OwnerClass = Property->GetOwnerClass();
|
|
if (OwnerClass)
|
|
{
|
|
TargetClass = OwnerClass->GetAuthoritativeClass();
|
|
}
|
|
}
|
|
else if (GetBlueprint()->SkeletonGeneratedClass)
|
|
{
|
|
TargetClass = GetBlueprint()->SkeletonGeneratedClass->GetAuthoritativeClass();
|
|
}
|
|
}
|
|
else if (TargetClass && TargetClass->ClassGeneratedBy)
|
|
{
|
|
TargetClass = TargetClass->GetAuthoritativeClass();
|
|
}
|
|
|
|
UEdGraphPin* TargetPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, TargetClass, UEdGraphSchema_K2::PN_Self);
|
|
TargetPin->PinFriendlyName = LOCTEXT("Target", "Target");
|
|
|
|
if (bSelfTarget)
|
|
{
|
|
TargetPin->bHidden = true; // don't show in 'self' context
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//@TODO: Check that the self pin types match!
|
|
}
|
|
}
|
|
|
|
bool UK2Node_Variable::RecreatePinForVariable(EEdGraphPinDirection Direction, TArray<UEdGraphPin*>& OldPins, FName InPinName/* = NAME_None*/)
|
|
{
|
|
// probably the node was pasted to a blueprint without the variable
|
|
// we don't want to beak any connection, so the pin will be recreated from old one, but compiler will throw error
|
|
|
|
// find old variable pin
|
|
const UEdGraphPin* OldVariablePin = nullptr;
|
|
const FName PinName = InPinName.IsNone() ? GetVarName() : InPinName;
|
|
for (const UEdGraphPin* Pin : OldPins)
|
|
{
|
|
if (Pin && PinName == Pin->PinName)
|
|
{
|
|
OldVariablePin = Pin;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nullptr != OldVariablePin)
|
|
{
|
|
// create new pin from old one
|
|
UEdGraphPin* VariablePin = CreatePin(Direction, NAME_None, PinName);
|
|
VariablePin->PinType = OldVariablePin->PinType;
|
|
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(VariablePin);
|
|
|
|
Message_Note(*FString::Printf(TEXT("Pin for variable '%s' recreated, but the variable is missing."), *PinName.ToString()));
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Message_Warn(*FString::Printf(TEXT("RecreatePinForVariable: '%s' pin not found"), *PinName.ToString()));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
FLinearColor UK2Node_Variable::GetNodeTitleColor() const
|
|
{
|
|
FProperty* VariableProperty = GetPropertyForVariable();
|
|
if (VariableProperty)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
|
|
FEdGraphPinType VariablePinType;
|
|
K2Schema->ConvertPropertyToPinType(VariableProperty, VariablePinType);
|
|
|
|
return K2Schema->GetPinTypeColor(VariablePinType);
|
|
}
|
|
|
|
return FLinearColor::White;
|
|
}
|
|
|
|
FString UK2Node_Variable::GetFindReferenceSearchString() const
|
|
{
|
|
FString ResultSearchString;
|
|
if (VariableReference.IsLocalScope())
|
|
{
|
|
ResultSearchString = VariableReference.GetReferenceSearchString(nullptr);
|
|
}
|
|
else
|
|
{
|
|
FProperty* VariableProperty = VariableReference.ResolveMember<FProperty>(GetBlueprintClassFromNode());
|
|
if (VariableProperty)
|
|
{
|
|
ResultSearchString = VariableReference.GetReferenceSearchString(VariableProperty->GetOwnerClass());
|
|
}
|
|
}
|
|
return ResultSearchString;
|
|
}
|
|
|
|
UK2Node::ERedirectType UK2Node_Variable::DoPinsMatchForReconstruction( const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex ) const
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
if( OldPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec )
|
|
{
|
|
return Super::DoPinsMatchForReconstruction(NewPin, NewPinIndex, OldPin, OldPinIndex);
|
|
}
|
|
|
|
const bool bPinNamesMatch = (OldPin->PinName == NewPin->PinName);
|
|
const bool bCanMatchSelfs = bPinNamesMatch || ((OldPin->PinName == UEdGraphSchema_K2::PN_Self) == (NewPin->PinName == UEdGraphSchema_K2::PN_Self));
|
|
const bool bTheSameDirection = (NewPin->Direction == OldPin->Direction);
|
|
|
|
if (bCanMatchSelfs && bTheSameDirection)
|
|
{
|
|
// the order that the PinTypes are passed to ArePinTypesCompatible()
|
|
// matters; object pin types are seen as compatible when the output-
|
|
// pin's type is a subclass of the input-pin's type, so we want to keep
|
|
// that in mind here (should the pins "MatchForReconstruction" if the
|
|
// variable has been changed to a super class of the original? what
|
|
// about a subclass?
|
|
//
|
|
// if these are output nodes, then it is perfectly acceptable that the
|
|
// variable has been altered to be a sub-class ref (meaning we should
|
|
// treat the NewPin as an output)... the opposite applies if the pins
|
|
// are inputs
|
|
const FEdGraphPinType& InputType = (OldPin->Direction == EGPD_Output) ? OldPin->PinType : NewPin->PinType;
|
|
const FEdGraphPinType& OutputType = (OldPin->Direction == EGPD_Output) ? NewPin->PinType : OldPin->PinType;
|
|
|
|
if (K2Schema->ArePinTypesCompatible(OutputType, InputType))
|
|
{
|
|
// If these are split pins, we need to do some name checking logic
|
|
if (NewPin->ParentPin)
|
|
{
|
|
// If the OldPin is not split, then these don't match
|
|
if (OldPin->ParentPin == nullptr)
|
|
{
|
|
return ERedirectType_None;
|
|
}
|
|
|
|
// Go through and find the original variable pin.
|
|
// If the number of steps out to the original variable pin is not the same then these don't match
|
|
const UEdGraphPin* ParentmostNewPin = NewPin;
|
|
const UEdGraphPin* ParentmostOldPin = OldPin;
|
|
|
|
while (ParentmostNewPin->ParentPin)
|
|
{
|
|
if (ParentmostOldPin->ParentPin == nullptr)
|
|
{
|
|
return ERedirectType_None;
|
|
}
|
|
ParentmostNewPin = ParentmostNewPin->ParentPin;
|
|
ParentmostOldPin = ParentmostOldPin->ParentPin;
|
|
}
|
|
|
|
if (ParentmostOldPin->ParentPin)
|
|
{
|
|
return ERedirectType_None;
|
|
}
|
|
|
|
// Compare whether the names, ignoring the original variable's name in the case of renames, match
|
|
FName NewPinPropertyName = FName(*NewPin->PinName.ToString().RightChop(ParentmostNewPin->PinName.ToString().Len() + 1));
|
|
FName OldPinPropertyName = FName(*OldPin->PinName.ToString().RightChop(ParentmostOldPin->PinName.ToString().Len() + 1));
|
|
|
|
if (!DoesRenamedVariableMatch(OldPinPropertyName, NewPinPropertyName, Cast<UStruct>(NewPin->ParentPin->PinType.PinSubCategoryObject.Get())))
|
|
{
|
|
return ERedirectType_None;
|
|
}
|
|
}
|
|
|
|
return ERedirectType_Name;
|
|
}
|
|
else
|
|
{
|
|
const bool bNewPinIsObject = (NewPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object);
|
|
|
|
// Special Case: If we had a pin match, and the class isn't loaded
|
|
// yet because of a cyclic dependency, temporarily
|
|
// cast away the const, and fix up.
|
|
if ( bPinNamesMatch &&
|
|
(bNewPinIsObject || (NewPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Interface)) &&
|
|
(NewPin->PinType.PinSubCategoryObject == NULL) )
|
|
{
|
|
// @TODO: Fix this up to be less hacky
|
|
UBlueprintGeneratedClass* TypeClass = Cast<UBlueprintGeneratedClass>(OldPin->PinType.PinSubCategoryObject.Get());
|
|
if (TypeClass && TypeClass->ClassGeneratedBy && TypeClass->ClassGeneratedBy->HasAnyFlags(RF_BeingRegenerated))
|
|
{
|
|
UEdGraphPin* NonConstNewPin = (UEdGraphPin*)NewPin;
|
|
NonConstNewPin->PinType.PinSubCategoryObject = OldPin->PinType.PinSubCategoryObject.Get();
|
|
return ERedirectType_Name;
|
|
}
|
|
}
|
|
// Special Case: if we have object pins that are "compatible" in the
|
|
// reverse order (meaning one's type is a sub-class of
|
|
// the other's), then they could still be acceptable
|
|
// if all their connections are still valid (for
|
|
// example: if the OldPin was an output only connected
|
|
// to super-class pins)
|
|
else if (bNewPinIsObject && K2Schema->ArePinTypesCompatible(InputType, OutputType))
|
|
{
|
|
bool bLinksCompatible = (OldPin->LinkedTo.Num() > 0) && (OldPin->DefaultObject == nullptr);
|
|
for (UEdGraphPin* OldLink : OldPin->LinkedTo)
|
|
{
|
|
const FEdGraphPinType& LinkInputType = (OldPin->Direction == EGPD_Input) ? NewPin->PinType : OldLink->PinType;
|
|
const FEdGraphPinType& LinkOutputType = (OldPin->Direction == EGPD_Input) ? OldLink->PinType : NewPin->PinType;
|
|
|
|
if (!K2Schema->ArePinTypesCompatible(LinkOutputType, LinkInputType))
|
|
{
|
|
bLinksCompatible = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bLinksCompatible)
|
|
{
|
|
return ERedirectType_Name;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const UClass* PSCOClass = Cast<UClass>(OldPin->PinType.PinSubCategoryObject.Get());
|
|
const bool bOldIsBlueprint = PSCOClass && PSCOClass->IsChildOf(UBlueprint::StaticClass());
|
|
const bool bNewIsClass = (NewPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class);
|
|
// Special Case: If we're migrating from old blueprint references
|
|
// to class references, allow pins to be reconnected if coerced
|
|
if (bNewIsClass && bOldIsBlueprint)
|
|
{
|
|
UEdGraphPin* OldPinNonConst = (UEdGraphPin*)OldPin;
|
|
OldPinNonConst->PinName = NewPin->PinName;
|
|
return ERedirectType_Name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ERedirectType_None;
|
|
}
|
|
|
|
UClass* UK2Node_Variable::GetVariableSourceClass() const
|
|
{
|
|
UClass* Result = VariableReference.GetMemberParentClass(GetBlueprintClassFromNode());
|
|
return Result;
|
|
}
|
|
|
|
FProperty* UK2Node_Variable::GetPropertyForVariable_Internal(UClass* OwningClass) const
|
|
{
|
|
const FName VarName = GetVarName();
|
|
|
|
FProperty* VariableProperty = VariableReference.ResolveMember<FProperty>(OwningClass);
|
|
|
|
// if the variable has been deprecated, don't use it
|
|
if (VariableProperty != nullptr)
|
|
{
|
|
UEdGraphPin* VariablePin = FindPin(VarName);
|
|
|
|
// If the variable has been remapped update the pin
|
|
if (VariablePin && VarName != GetVarName())
|
|
{
|
|
VariablePin->PinName = GetVarName();
|
|
}
|
|
}
|
|
|
|
return VariableProperty;
|
|
}
|
|
|
|
FProperty* UK2Node_Variable::GetPropertyForVariable() const
|
|
{
|
|
if (!FBlueprintCompilationManager::IsGeneratedClassLayoutReady())
|
|
{
|
|
// first look in the skeleton class:
|
|
if (FProperty* SkeletonProperty = GetPropertyForVariableFromSkeleton())
|
|
{
|
|
return SkeletonProperty;
|
|
}
|
|
}
|
|
|
|
return GetPropertyForVariable_Internal(GetBlueprintClassFromNode());
|
|
}
|
|
|
|
FString UK2Node_Variable::GetPinMetaData(FName InPinName, FName InKey)
|
|
{
|
|
FString MetaData;
|
|
|
|
if (GetVarName() == InPinName)
|
|
{
|
|
if (FProperty* VariableProperty = GetPropertyForVariable())
|
|
{
|
|
if (const FString* FoundMetaData = VariableProperty->FindMetaData(FBlueprintMetadata::MD_AllowAbstractClasses))
|
|
{
|
|
MetaData = *FoundMetaData;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (MetaData.IsEmpty())
|
|
{
|
|
MetaData = Super::GetPinMetaData(InPinName, InKey);
|
|
}
|
|
|
|
return MetaData;
|
|
}
|
|
|
|
bool UK2Node_Variable::DoesRenamedVariableMatch(FName OldVariableName, FName NewVariableName, UStruct* StructType)
|
|
{
|
|
if (NewVariableName == OldVariableName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FGuid OldPropertyGuid = FStructureEditorUtils::GetGuidFromPropertyName(OldVariableName);
|
|
FGuid NewPropertyGuid = FStructureEditorUtils::GetGuidFromPropertyName(NewVariableName);
|
|
|
|
// If guids match this was a user struct rename
|
|
if (OldPropertyGuid.IsValid() && (OldPropertyGuid == NewPropertyGuid))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Also check native rename if we can find the struct
|
|
if (StructType)
|
|
{
|
|
FName RedirectedPinName = FProperty::FindRedirectedPropertyName(StructType, OldVariableName);
|
|
|
|
if (NewVariableName == RedirectedPinName)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FProperty* UK2Node_Variable::GetPropertyForVariableFromSkeleton() const
|
|
{
|
|
if (UClass* SkeletonClass = FBlueprintEditorUtils::GetSkeletonClass(VariableReference.GetMemberParentClass(GetBlueprintClassFromNode())))
|
|
{
|
|
return GetPropertyForVariable_Internal(SkeletonClass);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
UEdGraphPin* UK2Node_Variable::GetValuePin() const
|
|
{
|
|
UEdGraphPin* Pin = FindPin(GetVarName());
|
|
check(Pin == nullptr || Pin->Direction == EGPD_Output);
|
|
return Pin;
|
|
}
|
|
|
|
void UK2Node_Variable::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
|
|
{
|
|
Super::ValidateNodeDuringCompilation(MessageLog);
|
|
|
|
FProperty* VariableProperty = GetPropertyForVariable();
|
|
|
|
// Local variables do not exist until much later in the compilation than this function can provide
|
|
if (VariableProperty == NULL && !VariableReference.IsLocalScope())
|
|
{
|
|
if (!VariableReference.IsDeprecated())
|
|
{
|
|
FString OwnerName;
|
|
|
|
UBlueprint* Blueprint = GetBlueprint();
|
|
if (Blueprint != nullptr)
|
|
{
|
|
OwnerName = Blueprint->GetName();
|
|
if (UClass* VarOwnerClass = VariableReference.GetMemberParentClass(Blueprint->GeneratedClass))
|
|
{
|
|
OwnerName = VarOwnerClass->GetName();
|
|
}
|
|
}
|
|
|
|
if (!FKismetCompilerUtilities::IsMissingMemberPotentiallyLoading(Blueprint, VariableReference.GetMemberParentClass()))
|
|
{
|
|
FString const VarName = VariableReference.GetMemberName().ToString();
|
|
|
|
FText const WarningFormat = LOCTEXT("VariableNotFoundFmt", "Could not find a variable named \"{0}\" in '{1}'.\nMake sure '{2}' has been compiled for @@");
|
|
MessageLog.Warning(*FText::Format(WarningFormat, FText::FromString(VarName), FText::FromString(OwnerName), FText::FromString(OwnerName)).ToString(), this);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MessageLog.Warning(*FText::Format(LOCTEXT("VariableDeprecatedFmt", "Variable '{0}' for @@ was deprecated. Please update it."), FText::FromString(VariableReference.GetMemberName().ToString())).ToString(), this);
|
|
}
|
|
}
|
|
|
|
if (VariableProperty && (VariableProperty->ArrayDim > 1))
|
|
{
|
|
MessageLog.Warning(*LOCTEXT("StaticArray_Warning", "@@ - the native property is a static array, which is not supported by blueprints").ToString(), this);
|
|
}
|
|
}
|
|
|
|
FSlateIcon UK2Node_Variable::GetIconAndTint(FLinearColor& ColorOut) const
|
|
{
|
|
const UStruct* VarScope = VariableReference.IsLocalScope() ?
|
|
VariableReference.GetMemberScope(GetBlueprintClassFromNode()) :
|
|
GetVariableSourceClass();
|
|
|
|
return GetVariableIconAndColor(VarScope, GetVarName(), ColorOut);
|
|
}
|
|
|
|
FSlateIcon UK2Node_Variable::GetVarIconFromPinType(const FEdGraphPinType& InPinType, FLinearColor& IconColorOut)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
IconColorOut = K2Schema->GetPinTypeColor(InPinType);
|
|
|
|
if (InPinType.IsArray())
|
|
{
|
|
return FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.AllClasses.ArrayVariableIcon");
|
|
}
|
|
else if (InPinType.IsSet())
|
|
{
|
|
return FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.AllClasses.SetVariableIcon");
|
|
}
|
|
else if (InPinType.IsMap())
|
|
{
|
|
// TODO: Need to properly deal with Key/Value stuff
|
|
return FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.AllClasses.MapVariableKeyIcon");
|
|
}
|
|
else if (InPinType.PinSubCategoryObject.IsValid())
|
|
{
|
|
if(UClass* Class = Cast<UClass>(InPinType.PinSubCategoryObject.Get()))
|
|
{
|
|
return FSlateIconFinder::FindIconForClass( Class );
|
|
}
|
|
}
|
|
|
|
return FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.AllClasses.VariableIcon");
|
|
}
|
|
|
|
FText UK2Node_Variable::GetToolTipHeading() const
|
|
{
|
|
FText Heading = Super::GetToolTipHeading();
|
|
|
|
// attempt to reflect the node's GetCornerIcon() with some tooltip documentation
|
|
FText IconTag;
|
|
if ( FProperty const* VariableProperty = VariableReference.ResolveMember<FProperty>(GetBlueprintClassFromNode()) )
|
|
{
|
|
const UActorComponent* Component = GetActorComponent(VariableProperty);
|
|
const bool IsEditorOnly = VariableProperty->HasAnyPropertyFlags(CPF_EditorOnly) || (Component && Component->bIsEditorOnly);
|
|
const bool IsReplicated = VariableProperty->HasAnyPropertyFlags(CPF_Net) || (Component && Component->GetIsReplicated());
|
|
if (IsEditorOnly && IsReplicated)
|
|
{
|
|
IconTag = LOCTEXT("ReplicatedEditorOnlyVar", "Editor-Only | Replicated");
|
|
}
|
|
else if (IsReplicated)
|
|
{
|
|
IconTag = LOCTEXT("ReplicatedVar", "Replicated");
|
|
}
|
|
else if (IsEditorOnly)
|
|
{
|
|
IconTag = LOCTEXT("EditorOnlyVar", "Editor-Only");
|
|
}
|
|
}
|
|
|
|
if (Heading.IsEmpty())
|
|
{
|
|
return IconTag;
|
|
}
|
|
else if (!IconTag.IsEmpty())
|
|
{
|
|
Heading = FText::Format(FText::FromString("{0}\n{1}"), IconTag, Heading);
|
|
}
|
|
return Heading;
|
|
}
|
|
|
|
void UK2Node_Variable::GetNodeAttributes( TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes ) const
|
|
{
|
|
FProperty* VariableProperty = GetPropertyForVariable();
|
|
const FString VariableName = VariableProperty ? VariableProperty->GetName() : TEXT( "InvalidVariable" );
|
|
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Type" ), TEXT( "Variable" ) ));
|
|
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Class" ), GetClass()->GetName() ));
|
|
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Name" ), VariableName ));
|
|
}
|
|
|
|
void UK2Node_Variable::HandleVariableRenamed(UBlueprint* InBlueprint, UClass* InVariableClass, UEdGraph* InGraph, const FName& InOldVarName, const FName& InNewVarName)
|
|
{
|
|
UClass* const NodeRefClass = VariableReference.GetMemberParentClass(InBlueprint->GeneratedClass);
|
|
if (NodeRefClass && NodeRefClass->IsChildOf(InVariableClass) && InOldVarName == GetVarName())
|
|
{
|
|
Modify();
|
|
|
|
if (VariableReference.IsLocalScope())
|
|
{
|
|
VariableReference.SetLocalMember(InNewVarName, VariableReference.GetMemberScopeName(), VariableReference.GetMemberGuid());
|
|
}
|
|
else if (VariableReference.IsSelfContext())
|
|
{
|
|
VariableReference.SetSelfMember(InNewVarName);
|
|
}
|
|
else
|
|
{
|
|
VariableReference.SetExternalMember(InNewVarName, NodeRefClass);
|
|
}
|
|
|
|
RenameUserDefinedPin(InOldVarName, InNewVarName);
|
|
}
|
|
}
|
|
|
|
void UK2Node_Variable::ReplaceReferences(UBlueprint* InBlueprint, UBlueprint* InReplacementBlueprint, const FMemberReference& InSource, const FMemberReference& InReplacement)
|
|
{
|
|
if (VariableReference.IsLocalScope() || VariableReference.IsSelfContext())
|
|
{
|
|
VariableReference = InReplacement;
|
|
}
|
|
else
|
|
{
|
|
// Make a copy because ResolveMember is non-const
|
|
FMemberReference Replacement = InReplacement;
|
|
const FProperty* ResolvedProperty = Replacement.ResolveMember<FProperty>(InBlueprint);
|
|
VariableReference.SetFromField<FProperty>(ResolvedProperty, InReplacementBlueprint->GeneratedClass);
|
|
}
|
|
}
|
|
|
|
bool UK2Node_Variable::ReferencesVariable(const FName& InVarName, const UStruct* InScope) const
|
|
{
|
|
if (InVarName == GetVarName())
|
|
{
|
|
if (InScope && VariableReference.GetMemberScopeName() != InScope->GetName())
|
|
{
|
|
// Variables are not in the same scope
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FSlateIcon UK2Node_Variable::GetVariableIconAndColor(const UStruct* VarScope, FName VarName, FLinearColor& IconColorOut)
|
|
{
|
|
if(VarScope != NULL)
|
|
{
|
|
FProperty* Property = FindFProperty<FProperty>(VarScope, VarName);
|
|
if(Property != NULL)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
|
|
FEdGraphPinType PinType;
|
|
if(K2Schema->ConvertPropertyToPinType(Property, PinType)) // use schema to get the color
|
|
{
|
|
return GetVarIconFromPinType(PinType, IconColorOut);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.AllClasses.VariableIcon");
|
|
}
|
|
|
|
|
|
void UK2Node_Variable::CheckForErrors(const UEdGraphSchema_K2* Schema, FCompilerResultsLog& MessageLog)
|
|
{
|
|
if(!VariableReference.IsSelfContext() && VariableReference.GetMemberParentClass(GetBlueprintClassFromNode()) != NULL)
|
|
{
|
|
// Check to see if we're not a self context, if we have a valid context. It may have been purged because of a dead execution chain
|
|
UEdGraphPin* ContextPin = Schema->FindSelfPin(*this, EGPD_Input);
|
|
if((ContextPin != NULL) && (ContextPin->LinkedTo.Num() == 0) && (ContextPin->DefaultObject == NULL))
|
|
{
|
|
MessageLog.Error(*LOCTEXT("VarNodeError_InvalidVarTarget", "Variable node @@ uses an invalid target. It may depend on a node that is not connected to the execution chain, and got purged.").ToString(), this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UK2Node_Variable::ReconstructNode()
|
|
{
|
|
// update the variable reference if the property was renamed
|
|
UClass* const VarClass = GetVariableSourceClass();
|
|
if (VarClass)
|
|
{
|
|
bool bRemappedProperty = false;
|
|
UClass* SearchClass = VarClass;
|
|
while (SearchClass != nullptr)
|
|
{
|
|
FName NewPropertyName = FProperty::FindRedirectedPropertyName(SearchClass, VariableReference.GetMemberName());
|
|
|
|
if (NewPropertyName != NAME_None)
|
|
{
|
|
if (VariableReference.IsSelfContext())
|
|
{
|
|
VariableReference.SetSelfMember(NewPropertyName);
|
|
}
|
|
else
|
|
{
|
|
VariableReference.SetExternalMember(NewPropertyName, VarClass);
|
|
}
|
|
|
|
// found, can break
|
|
bRemappedProperty = true;
|
|
break;
|
|
}
|
|
|
|
SearchClass = SearchClass->GetSuperClass();
|
|
}
|
|
|
|
if (!bRemappedProperty)
|
|
{
|
|
static FName OldVariableName(TEXT("UpdatedComponent"));
|
|
static FName NewVariableName(TEXT("UpdatedPrimitive"));
|
|
bRemappedProperty = RemapRestrictedLinkReference(OldVariableName, NewVariableName, UMovementComponent::StaticClass(), UPrimitiveComponent::StaticClass(), true);
|
|
}
|
|
}
|
|
|
|
const FGuid VarGuid = VariableReference.GetMemberGuid();
|
|
if (VarGuid.IsValid())
|
|
{
|
|
const FName VarName = UBlueprint::GetFieldNameFromClassByGuid<FProperty>(VarClass, VarGuid);
|
|
if (VarName != NAME_None && VarName != VariableReference.GetMemberName())
|
|
{
|
|
if (VariableReference.IsSelfContext())
|
|
{
|
|
VariableReference.SetSelfMember( VarName );
|
|
}
|
|
else
|
|
{
|
|
VariableReference.SetExternalMember( VarName, VarClass );
|
|
}
|
|
}
|
|
}
|
|
|
|
Super::ReconstructNode();
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
FName UK2Node_Variable::GetCornerIcon() const
|
|
{
|
|
if (const FProperty* VariableProperty = VariableReference.ResolveMember<FProperty>(GetBlueprintClassFromNode()))
|
|
{
|
|
const UActorComponent* Component = GetActorComponent(VariableProperty);
|
|
if (VariableProperty->HasAllPropertyFlags(CPF_Net) || (Component && Component->GetIsReplicated()))
|
|
{
|
|
return TEXT("Graph.Replication.Replicated");
|
|
}
|
|
else if (VariableProperty->HasAllPropertyFlags(CPF_EditorOnly) || (Component && Component->bIsEditorOnly))
|
|
{
|
|
return TEXT("Graph.Editor.EditorOnlyIcon");
|
|
}
|
|
}
|
|
|
|
return Super::GetCornerIcon();
|
|
}
|
|
|
|
bool UK2Node_Variable::HasExternalDependencies(TArray<class UStruct*>* OptionalOutput) const
|
|
{
|
|
UBlueprint* SourceBlueprint = GetBlueprint();
|
|
FProperty* VariableProperty = GetPropertyForVariable();
|
|
UClass* PropertySourceClass = VariableProperty ? VariableProperty->GetOwnerClass() : nullptr;
|
|
bool bResult = (PropertySourceClass && (PropertySourceClass->ClassGeneratedBy != SourceBlueprint));
|
|
if (bResult && OptionalOutput)
|
|
{
|
|
OptionalOutput->AddUnique(PropertySourceClass);
|
|
}
|
|
|
|
// Also include underlying non-native variable types as external dependencies. Otherwise, contextual
|
|
// type references serialized to bytecode can potentially be invalidated when the type is regenerated.
|
|
if (const UEdGraphPin* VarPin = FindPin(GetVarName()))
|
|
{
|
|
if (UStruct* PinTypeStruct = Cast<UStruct>(VarPin->PinType.PinSubCategoryObject.Get()))
|
|
{
|
|
UClass* PinTypeClass = Cast<UClass>(PinTypeStruct);
|
|
if (!PinTypeStruct->IsNative() && (!PinTypeClass || PinTypeClass->ClassGeneratedBy != SourceBlueprint))
|
|
{
|
|
bResult = true;
|
|
if (OptionalOutput)
|
|
{
|
|
OptionalOutput->AddUnique(PinTypeStruct);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool bSuperResult = Super::HasExternalDependencies(OptionalOutput);
|
|
return bSuperResult || bResult;
|
|
}
|
|
|
|
FString UK2Node_Variable::GetDocumentationLink() const
|
|
{
|
|
if( FProperty* Property = GetPropertyForVariable() )
|
|
{
|
|
// discover if the variable property is a non blueprint user variable
|
|
UClass* SourceClass = Property->GetOwnerClass();
|
|
if( SourceClass && SourceClass->ClassGeneratedBy == NULL )
|
|
{
|
|
UStruct* OwnerStruct = Property->GetOwnerStruct();
|
|
|
|
if( OwnerStruct )
|
|
{
|
|
return FString::Printf( TEXT("Shared/Types/%s%s"), OwnerStruct->GetPrefixCPP(), *OwnerStruct->GetName() );
|
|
}
|
|
}
|
|
}
|
|
return TEXT( "" );
|
|
}
|
|
|
|
FString UK2Node_Variable::GetDocumentationExcerptName() const
|
|
{
|
|
return GetVarNameString();
|
|
}
|
|
|
|
void UK2Node_Variable::AutowireNewNode(UEdGraphPin* FromPin)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = CastChecked<UEdGraphSchema_K2>(GetSchema());
|
|
|
|
// Do some auto-connection
|
|
if (FromPin != NULL)
|
|
{
|
|
bool bConnected = false;
|
|
if(FromPin->Direction == EGPD_Output)
|
|
{
|
|
// If the source pin has a valid PinSubCategoryObject, we might be doing BP Comms, so check if it is a class
|
|
if(FromPin->PinType.PinSubCategoryObject.IsValid() && FromPin->PinType.PinSubCategoryObject->IsA(UClass::StaticClass()))
|
|
{
|
|
FProperty* VariableProperty = VariableReference.ResolveMember<FProperty>(GetBlueprintClassFromNode());
|
|
if(VariableProperty)
|
|
{
|
|
UClass* PropertyOwner = VariableProperty->GetOwnerClass();
|
|
if (PropertyOwner != nullptr)
|
|
{
|
|
PropertyOwner = PropertyOwner->GetAuthoritativeClass();
|
|
}
|
|
|
|
// BP Comms is highly likely at this point, if the source pin's type is a child of the variable's owner class, let's conform the "Target" pin
|
|
if(FromPin->PinType.PinSubCategoryObject == PropertyOwner || dynamic_cast<UClass*>(FromPin->PinType.PinSubCategoryObject.Get())->IsChildOf(PropertyOwner))
|
|
{
|
|
UEdGraphPin* TargetPin = FindPin(UEdGraphSchema_K2::PN_Self);
|
|
if (TargetPin)
|
|
{
|
|
TargetPin->PinType.PinSubCategoryObject = PropertyOwner;
|
|
|
|
if(K2Schema->TryCreateConnection(FromPin, TargetPin))
|
|
{
|
|
bConnected = true;
|
|
|
|
// Setup the VariableReference correctly since it may no longer be a self member
|
|
VariableReference.SetFromField<FProperty>(GetPropertyForVariable(), false);
|
|
TargetPin->bHidden = false;
|
|
FromPin->GetOwningNode()->NodeConnectionListChanged();
|
|
this->NodeConnectionListChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!bConnected)
|
|
{
|
|
Super::AutowireNewNode(FromPin);
|
|
}
|
|
}
|
|
}
|
|
|
|
FBPVariableDescription const* UK2Node_Variable::GetBlueprintVarDescription() const
|
|
{
|
|
FName const& VarName = VariableReference.GetMemberName();
|
|
UStruct const* VariableScope = VariableReference.GetMemberScope(GetBlueprintClassFromNode());
|
|
|
|
bool const bIsLocalVariable = (VariableScope != nullptr);
|
|
if (bIsLocalVariable)
|
|
{
|
|
return FBlueprintEditorUtils::FindLocalVariable(GetBlueprint(), VariableScope, VarName);
|
|
}
|
|
else if (FProperty const* VarProperty = GetPropertyForVariable())
|
|
{
|
|
UClass const* SourceClass = VarProperty->GetOwnerClass();
|
|
UBlueprint const* SourceBlueprint = (SourceClass != nullptr) ? Cast<UBlueprint>(SourceClass->ClassGeneratedBy) : nullptr;
|
|
|
|
if (SourceBlueprint != nullptr)
|
|
{
|
|
int32 const VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(SourceBlueprint, VarName);
|
|
return &SourceBlueprint->NewVariables[VarIndex];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool UK2Node_Variable::CanPasteHere(const UEdGraph* TargetGraph) const
|
|
{
|
|
// Do not allow pasting of variables in BPs that cannot handle them
|
|
if ( FBlueprintEditorUtils::FindBlueprintForGraph(TargetGraph)->BlueprintType == BPTYPE_MacroLibrary && VariableReference.IsSelfContext() )
|
|
{
|
|
// Self variables must be from a parent class to the macro BP
|
|
if(FProperty* Property = VariableReference.ResolveMember<FProperty>(GetBlueprintClassFromNode()))
|
|
{
|
|
const UClass* CurrentClass = GetBlueprint()->SkeletonGeneratedClass->GetAuthoritativeClass();
|
|
const UClass* PropertyClass = Property->GetOwnerClass()->GetAuthoritativeClass();
|
|
const bool bIsChildOf = CurrentClass && CurrentClass->IsChildOf(PropertyClass);
|
|
return bIsChildOf;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void UK2Node_Variable::PostPasteNode()
|
|
{
|
|
Super::PostPasteNode();
|
|
|
|
UBlueprint* Blueprint = GetBlueprint();
|
|
bool bInvalidateVariable = false;
|
|
|
|
if (VariableReference.ResolveMember<FProperty>(Blueprint) == nullptr)
|
|
{
|
|
bInvalidateVariable = true;
|
|
}
|
|
else if (VariableReference.IsLocalScope())
|
|
{
|
|
// Local scoped variables should always validate whether they are being placed in the same graph as their scope
|
|
// ResolveMember will not return nullptr when the graph changes but the Blueprint remains the same.
|
|
const UStruct* MemberScope = VariableReference.GetMemberScope(GetBlueprintClassFromNode());
|
|
UEdGraph* ScopeGraph = FBlueprintEditorUtils::FindScopeGraph(Blueprint, MemberScope);
|
|
const bool bMemberScopeInvalid = (ScopeGraph && MemberScope && ScopeGraph->GetFName() != MemberScope->GetFName());
|
|
if(ScopeGraph != GetGraph() || bMemberScopeInvalid)
|
|
{
|
|
bInvalidateVariable = true;
|
|
}
|
|
}
|
|
|
|
if (bInvalidateVariable)
|
|
{
|
|
// This invalidates the local scope
|
|
VariableReference.InvalidateScope();
|
|
|
|
// If the current graph is a Function graph, look to see if there is a compatible local variable (same name)
|
|
if (GetGraph()->GetSchema()->GetGraphType(GetGraph()) == GT_Function)
|
|
{
|
|
UEdGraph* FunctionGraph = FBlueprintEditorUtils::GetTopLevelGraph(GetGraph());
|
|
FBPVariableDescription* VariableDescription = FBlueprintEditorUtils::FindLocalVariable(Blueprint, FunctionGraph, VariableReference.GetMemberName());
|
|
bool bFoundParam = FunctionParameterExists(FunctionGraph, VariableReference.GetMemberName());
|
|
if(VariableDescription || bFoundParam)
|
|
{
|
|
VariableReference.SetLocalMember(VariableReference.GetMemberName(), FunctionGraph->GetName(), VariableReference.GetMemberGuid());
|
|
}
|
|
}
|
|
// If no variable was found, ResolveMember should automatically find a member variable with the same name in the current Blueprint and hook up to it as expected
|
|
}
|
|
}
|
|
|
|
bool UK2Node_Variable::HasDeprecatedReference() const
|
|
{
|
|
// Check if the referenced variable is deprecated.
|
|
if (VariableReference.IsDeprecated())
|
|
{
|
|
return true;
|
|
}
|
|
else if (FProperty* VariableProperty = VariableReference.ResolveMember<FProperty>(GetBlueprintClassFromNode()))
|
|
{
|
|
// Backcompat: Allow variables tagged only with 'DeprecationMessage' meta to be seen as deprecated if inherited from a native parent class.
|
|
const bool bHasDeprecationMessage = VariableProperty->HasMetaData(FBlueprintMetadata::MD_DeprecationMessage);
|
|
if (bHasDeprecationMessage && VariableProperty->GetOwnerUObject()->IsNative())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FEdGraphNodeDeprecationResponse UK2Node_Variable::GetDeprecationResponse(EEdGraphNodeDeprecationType DeprecationType) const
|
|
{
|
|
FEdGraphNodeDeprecationResponse Response = Super::GetDeprecationResponse(DeprecationType);
|
|
if (DeprecationType == EEdGraphNodeDeprecationType::NodeHasDeprecatedReference)
|
|
{
|
|
if (FProperty* VariableProperty = VariableReference.ResolveMember<FProperty>(GetBlueprintClassFromNode()))
|
|
{
|
|
FText MemberName = FText::FromName(VariableReference.GetMemberName());
|
|
FText DetailedMessage = FText::FromString(VariableProperty->GetMetaData(FBlueprintMetadata::MD_DeprecationMessage));
|
|
Response.MessageText = FBlueprintEditorUtils::GetDeprecatedMemberUsageNodeWarning(MemberName, DetailedMessage);
|
|
}
|
|
}
|
|
|
|
return Response;
|
|
}
|
|
|
|
UObject* UK2Node_Variable::GetJumpTargetForDoubleClick() const
|
|
{
|
|
// Jump to the RepNotify function graph if one exists
|
|
UBlueprint* OwnerBlueprint = GetBlueprint();
|
|
|
|
FName RepNotifyFunc = FBlueprintEditorUtils::GetBlueprintVariableRepNotifyFunc(OwnerBlueprint, GetVarName());
|
|
if (RepNotifyFunc != NAME_None)
|
|
{
|
|
for (UEdGraph* Graph : OwnerBlueprint->FunctionGraphs)
|
|
{
|
|
if (Graph->GetFName() == RepNotifyFunc)
|
|
{
|
|
return Graph;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool UK2Node_Variable::CanJumpToDefinition() const
|
|
{
|
|
const FProperty* VariableProperty = GetPropertyForVariable();
|
|
const bool bNativeVariable = (VariableProperty != nullptr) && (VariableProperty->IsNative());
|
|
const bool bCanJumpToNativeVariable = bNativeVariable && ensure(GUnrealEd) && GUnrealEd->GetUnrealEdOptions()->IsCPPAllowed();
|
|
return bCanJumpToNativeVariable || (GetJumpTargetForDoubleClick() != nullptr);
|
|
}
|
|
|
|
void UK2Node_Variable::JumpToDefinition() const
|
|
{
|
|
if (ensure(GUnrealEd) && GUnrealEd->GetUnrealEdOptions()->IsCPPAllowed())
|
|
{
|
|
// For native variables, try going to the variable definition in C++ if available
|
|
if (FProperty* VariableProperty = GetPropertyForVariable())
|
|
{
|
|
if (VariableProperty->IsNative())
|
|
{
|
|
FSourceCodeNavigation::NavigateToProperty(VariableProperty);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise, fall back to the inherited behavior
|
|
Super::JumpToDefinition();
|
|
}
|
|
|
|
bool UK2Node_Variable::FunctionParameterExists(const UEdGraph* InFunctionGraph, const FName InParameterName)
|
|
{
|
|
TArray<UK2Node_FunctionEntry*> Entry;
|
|
InFunctionGraph->GetNodesOfClass<UK2Node_FunctionEntry>(Entry);
|
|
|
|
if (ensureMsgf(Entry.Num() == 1, TEXT("Couldn't find a Function Entry node in graph %s"), *InFunctionGraph->GetName()))
|
|
{
|
|
check(Entry[0]);
|
|
|
|
return Entry[0]->UserDefinedPinExists(InParameterName);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const UActorComponent* UK2Node_Variable::GetActorComponent(const FProperty* VariableProperty) const
|
|
{
|
|
if (!VariableProperty)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
UBlueprint* OwnerBlueprint = GetBlueprint();
|
|
if(OwnerBlueprint && OwnerBlueprint->SimpleConstructionScript)
|
|
{
|
|
if (const AActor* EditorActorInstance = OwnerBlueprint->SimpleConstructionScript->GetComponentEditorActorInstance())
|
|
{
|
|
for (const UActorComponent* Component : EditorActorInstance->GetComponents())
|
|
{
|
|
if (!Component || Component->GetFName() != VariableProperty->GetFName())
|
|
{
|
|
continue;
|
|
}
|
|
return Component;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|