Files
UnrealEngine/Engine/Source/Editor/AIGraph/Private/AIGraphNode.cpp
2025-05-18 13:04:45 +08:00

413 lines
8.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AIGraphNode.h"
#include "UObject/Class.h"
#include "UObject/UnrealType.h"
#include "Engine/Blueprint.h"
#include "Engine/BlueprintGeneratedClass.h"
#include "AssetRegistry/AssetData.h"
#include "EdGraph/EdGraphSchema.h"
#include "AIGraph.h"
#include "DiffResults.h"
#include "ScopedTransaction.h"
#include "BlueprintNodeHelpers.h"
#define LOCTEXT_NAMESPACE "AIGraph"
UAIGraphNode::UAIGraphNode(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
NodeInstance = nullptr;
CopySubNodeIndex = 0;
bIsReadOnly = false;
bIsSubNode = false;
}
void UAIGraphNode::InitializeInstance()
{
// empty in base class
}
void UAIGraphNode::PostPlacedNewNode()
{
// NodeInstance can be already spawned by paste operation, don't override it
UClass* NodeClass = ClassData.GetClass(true);
if (NodeClass && (NodeInstance == nullptr))
{
UEdGraph* MyGraph = GetGraph();
UObject* GraphOwner = MyGraph ? MyGraph->GetOuter() : nullptr;
if (GraphOwner)
{
NodeInstance = NewObject<UObject>(GraphOwner, NodeClass);
NodeInstance->SetFlags(RF_Transactional);
InitializeInstance();
UpdateErrorMessage();
}
}
}
bool UAIGraphNode::CanDuplicateNode() const
{
return bIsReadOnly ? false : Super::CanDuplicateNode();
}
bool UAIGraphNode::CanUserDeleteNode() const
{
return bIsReadOnly ? false : Super::CanUserDeleteNode();
}
void UAIGraphNode::PrepareForCopying()
{
if (NodeInstance)
{
// Temporarily take ownership of the node instance, so that it is not deleted when cutting
NodeInstance->Rename(nullptr, this, REN_DontCreateRedirectors | REN_DoNotDirty);
}
}
#if WITH_EDITOR
void UAIGraphNode::PostEditImport()
{
ResetNodeOwner();
if (NodeInstance)
{
InitializeInstance();
}
}
void UAIGraphNode::PostEditUndo()
{
UEdGraphNode::PostEditUndo();
ResetNodeOwner();
if (ParentNode)
{
ParentNode->SubNodes.AddUnique(this);
}
}
#endif
void UAIGraphNode::PostCopyNode()
{
ResetNodeOwner();
}
void UAIGraphNode::ResetNodeOwner()
{
if (NodeInstance)
{
UEdGraph* MyGraph = GetGraph();
UObject* GraphOwner = MyGraph ? MyGraph->GetOuter() : nullptr;
NodeInstance->Rename(NULL, GraphOwner, REN_DontCreateRedirectors | REN_DoNotDirty);
NodeInstance->ClearFlags(RF_Transient);
for (auto& SubNode : SubNodes)
{
SubNode->ResetNodeOwner();
}
}
}
FText UAIGraphNode::GetDescription() const
{
FString StoredClassName = ClassData.GetClassName();
StoredClassName.RemoveFromEnd(TEXT("_C"));
return FText::Format(LOCTEXT("NodeClassError", "Class {0} not found, make sure it's saved!"), FText::FromString(StoredClassName));
}
FText UAIGraphNode::GetTooltipText() const
{
FText TooltipDesc;
if (!NodeInstance)
{
FString StoredClassName = ClassData.GetClassName();
StoredClassName.RemoveFromEnd(TEXT("_C"));
TooltipDesc = FText::Format(LOCTEXT("NodeClassError", "Class {0} not found, make sure it's saved!"), FText::FromString(StoredClassName));
}
else
{
if (ErrorMessage.Len() > 0)
{
TooltipDesc = FText::FromString(ErrorMessage);
}
else
{
if (NodeInstance->GetClass()->HasAnyClassFlags(CLASS_CompiledFromBlueprint) && !NodeInstance->GetClass()->GetPackage()->HasAnyPackageFlags(PKG_Cooked))
{
FAssetData AssetData(NodeInstance->GetClass()->ClassGeneratedBy);
FString Description = AssetData.GetTagValueRef<FString>(GET_MEMBER_NAME_CHECKED(UBlueprint, BlueprintDescription));
if (!Description.IsEmpty())
{
Description.ReplaceInline(TEXT("\\n"), TEXT("\n"));
TooltipDesc = FText::FromString(MoveTemp(Description));
}
}
else
{
TooltipDesc = NodeInstance->GetClass()->GetToolTipText();
}
}
}
return TooltipDesc;
}
UEdGraphPin* UAIGraphNode::GetInputPin(int32 InputIndex) const
{
check(InputIndex >= 0);
for (int32 PinIndex = 0, FoundInputs = 0; PinIndex < Pins.Num(); PinIndex++)
{
if (Pins[PinIndex]->Direction == EGPD_Input)
{
if (InputIndex == FoundInputs)
{
return Pins[PinIndex];
}
else
{
FoundInputs++;
}
}
}
return nullptr;
}
UEdGraphPin* UAIGraphNode::GetOutputPin(int32 InputIndex) const
{
check(InputIndex >= 0);
for (int32 PinIndex = 0, FoundInputs = 0; PinIndex < Pins.Num(); PinIndex++)
{
if (Pins[PinIndex]->Direction == EGPD_Output)
{
if (InputIndex == FoundInputs)
{
return Pins[PinIndex];
}
else
{
FoundInputs++;
}
}
}
return nullptr;
}
void UAIGraphNode::AutowireNewNode(UEdGraphPin* FromPin)
{
Super::AutowireNewNode(FromPin);
if (FromPin != nullptr)
{
UEdGraphPin* OutputPin = GetOutputPin();
if (GetSchema()->TryCreateConnection(FromPin, GetInputPin()))
{
FromPin->GetOwningNode()->NodeConnectionListChanged();
}
else if (OutputPin != nullptr && GetSchema()->TryCreateConnection(OutputPin, FromPin))
{
NodeConnectionListChanged();
}
}
}
UAIGraph* UAIGraphNode::GetAIGraph()
{
return CastChecked<UAIGraph>(GetGraph());
}
bool UAIGraphNode::IsSubNode() const
{
return bIsSubNode || (ParentNode != nullptr);
}
void UAIGraphNode::NodeConnectionListChanged()
{
Super::NodeConnectionListChanged();
GetAIGraph()->UpdateAsset();
}
bool UAIGraphNode::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* DesiredSchema) const
{
// override in child class
return false;
}
FString UAIGraphNode::GetPropertyNameAndValueForDiff(const FProperty* Prop, const uint8* PropertyAddr) const
{
return BlueprintNodeHelpers::DescribeProperty(Prop, PropertyAddr);
}
void UAIGraphNode::FindDiffs(UEdGraphNode* OtherNode, FDiffResults& Results)
{
Super::FindDiffs(OtherNode, Results);
if (UAIGraphNode* OtherGraphNode = Cast<UAIGraphNode>(OtherNode))
{
if (NodeInstance && OtherGraphNode->NodeInstance)
{
FDiffSingleResult Diff;
Diff.Diff = EDiffType::NODE_PROPERTY;
Diff.Node1 = this;
Diff.Node2 = OtherNode;
Diff.ToolTip = LOCTEXT("DIF_NodeInstancePropertyToolTip", "A property of the node instance has changed");
Diff.Category = EDiffType::MODIFICATION;
DiffProperties(NodeInstance->GetClass(), OtherGraphNode->NodeInstance->GetClass(), NodeInstance, OtherGraphNode->NodeInstance, Results, Diff);
}
}
}
void UAIGraphNode::AddSubNode(UAIGraphNode* SubNode, class UEdGraph* ParentGraph)
{
const FScopedTransaction Transaction(LOCTEXT("AddNode", "Add Node"));
ParentGraph->Modify();
Modify();
SubNode->SetFlags(RF_Transactional);
// set outer to be the graph so it doesn't go away
SubNode->Rename(nullptr, ParentGraph, REN_NonTransactional);
SubNode->ParentNode = this;
SubNode->CreateNewGuid();
SubNode->PostPlacedNewNode();
SubNode->AllocateDefaultPins();
SubNode->AutowireNewNode(nullptr);
SubNode->NodePosX = 0;
SubNode->NodePosY = 0;
SubNodes.Add(SubNode);
OnSubNodeAdded(SubNode);
ParentGraph->NotifyGraphChanged();
GetAIGraph()->UpdateAsset();
}
void UAIGraphNode::OnSubNodeAdded(UAIGraphNode* SubNode)
{
// empty in base class
}
void UAIGraphNode::RemoveSubNode(UAIGraphNode* SubNode)
{
Modify();
SubNodes.RemoveSingle(SubNode);
OnSubNodeRemoved(SubNode);
}
void UAIGraphNode::RemoveAllSubNodes()
{
SubNodes.Reset();
}
void UAIGraphNode::OnSubNodeRemoved(UAIGraphNode* SubNode)
{
// empty in base class
}
int32 UAIGraphNode::FindSubNodeDropIndex(UAIGraphNode* SubNode) const
{
const int32 InsertIndex = SubNodes.IndexOfByKey(SubNode);
return InsertIndex;
}
void UAIGraphNode::InsertSubNodeAt(UAIGraphNode* SubNode, int32 DropIndex)
{
if (DropIndex > -1)
{
SubNodes.Insert(SubNode, DropIndex);
}
else
{
SubNodes.Add(SubNode);
}
}
void UAIGraphNode::DestroyNode()
{
if (ParentNode)
{
ParentNode->RemoveSubNode(this);
}
UEdGraphNode::DestroyNode();
}
bool UAIGraphNode::UsesBlueprint() const
{
return NodeInstance && NodeInstance->GetClass()->HasAnyClassFlags(CLASS_CompiledFromBlueprint);
}
bool UAIGraphNode::RefreshNodeClass()
{
bool bUpdated = false;
if (NodeInstance == nullptr)
{
if (FGraphNodeClassHelper::IsClassKnown(ClassData))
{
PostPlacedNewNode();
bUpdated = (NodeInstance != nullptr);
}
else
{
FGraphNodeClassHelper::AddUnknownClass(ClassData);
}
}
return bUpdated;
}
void UAIGraphNode::UpdateNodeClassData()
{
if (NodeInstance)
{
UpdateNodeClassDataFrom(NodeInstance->GetClass(), ClassData);
UpdateErrorMessage();
}
}
void UAIGraphNode::UpdateErrorMessage()
{
ErrorMessage = ClassData.GetDeprecatedMessage();
}
void UAIGraphNode::UpdateNodeClassDataFrom(UClass* InstanceClass, FGraphNodeClassData& UpdatedData)
{
if (InstanceClass)
{
if (UBlueprint* BPOwner = Cast<UBlueprint>(InstanceClass->ClassGeneratedBy))
{
UpdatedData = FGraphNodeClassData(BPOwner->GetName(), BPOwner->GetOutermost()->GetName(), InstanceClass->GetName(), InstanceClass);
}
else if (UBlueprintGeneratedClass* BPGC = Cast<UBlueprintGeneratedClass>(InstanceClass))
{
UpdatedData = FGraphNodeClassData(BPGC->GetClassPathName(), BPGC);
}
else
{
UpdatedData = FGraphNodeClassData(InstanceClass, FGraphNodeClassHelper::GetDeprecationMessage(InstanceClass));
}
}
}
bool UAIGraphNode::HasErrors() const
{
return ErrorMessage.Len() > 0 || NodeInstance == nullptr;
}
#undef LOCTEXT_NAMESPACE