// Copyright Epic Games, Inc. All Rights Reserved. #include "K2Node_ActorBoundEvent.h" #include "Engine/Blueprint.h" #include "Engine/Level.h" #include "Engine/LevelScriptActor.h" #include "Engine/LevelScriptBlueprint.h" #include "Engine/MemberReference.h" #include "EventEntryHandler.h" #include "GameFramework/Actor.h" #include "HAL/Platform.h" #include "Internationalization/Internationalization.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/CompilerResultsLog.h" #include "Kismet2/KismetEditorUtilities.h" #include "KismetCompiler.h" #include "Serialization/Archive.h" #include "Templates/Casts.h" #include "UObject/Class.h" #include "UObject/Object.h" #include "UObject/ObjectVersion.h" #include "UObject/UnrealType.h" #include "UObject/WeakObjectPtr.h" struct FKismetFunctionContext; #define LOCTEXT_NAMESPACE "K2Node_ActorBoundEvent" ////////////////////////////////////////////////////////////////////////// // FKCHandler_ActorBoundEventEntry class FKCHandler_ActorBoundEventEntry : public FKCHandler_EventEntry { public: FKCHandler_ActorBoundEventEntry(FKismetCompilerContext& InCompilerContext) : FKCHandler_EventEntry(InCompilerContext) { } virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { // Check to make sure that the object the event is bound to is valid const UK2Node_ActorBoundEvent* BoundEventNode = Cast(Node); if (BoundEventNode && BoundEventNode->EventOwner) { FKCHandler_EventEntry::Compile(Context, Node); } else { CompilerContext.MessageLog.Error(*FString(*LOCTEXT("FindNodeBoundEvent_Error", "Couldn't find object for bound event node @@").ToString()), Node); } } }; UK2Node_ActorBoundEvent::UK2Node_ActorBoundEvent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } FNodeHandlingFunctor* UK2Node_ActorBoundEvent::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const { return new FKCHandler_ActorBoundEventEntry(CompilerContext); } void UK2Node_ActorBoundEvent::ReconstructNode() { // We need to fixup our event reference as it may have changed or been redirected FMulticastDelegateProperty* TargetDelegateProp = GetTargetDelegateProperty(); // If we couldn't find the target delegate, then try to find it in the property remap table if (!TargetDelegateProp) { FMulticastDelegateProperty* NewProperty = FMemberReference::FindRemappedField(DelegateOwnerClass, DelegatePropertyName); if (NewProperty) { // Found a remapped property, update the node TargetDelegateProp = NewProperty; DelegatePropertyName = NewProperty->GetFName(); } } if (TargetDelegateProp && TargetDelegateProp->SignatureFunction) { EventReference.SetFromField(TargetDelegateProp->SignatureFunction, false); } CachedNodeTitle.MarkDirty(); Super::ReconstructNode(); } void UK2Node_ActorBoundEvent::DestroyNode() { if (EventOwner) { // If we have an event owner, remove the delegate referencing this event, if any const ULevel* TargetLevel = EventOwner->GetLevel(); if (TargetLevel) { ALevelScriptActor* LSA = TargetLevel->GetLevelScriptActor(); if (LSA) { // Create a delegate of the correct signature to remove FScriptDelegate Delegate; Delegate.BindUFunction(LSA, CustomFunctionName); // Attempt to remove it from the target's MC delegate if (FMulticastDelegateProperty* TargetDelegate = GetTargetDelegateProperty()) { TargetDelegate->RemoveDelegate(Delegate, EventOwner); } } } } Super::DestroyNode(); } bool UK2Node_ActorBoundEvent::CanPasteHere(const UEdGraph* TargetGraph) const { // By default, to be safe, we don't allow events to be pasted, except under special circumstances (see below) bool bDisallowPaste = !Super::CanPasteHere(TargetGraph); if (!bDisallowPaste) { const AActor* ReferencedLevelActor = GetReferencedLevelActor(); ULevel* Level = ReferencedLevelActor ? ReferencedLevelActor->GetLevel() : nullptr; const UBlueprint* LevelBP = Level ? Level->GetLevelScriptBlueprint(true) : nullptr; if (FBlueprintEditorUtils::FindBlueprintForGraph(TargetGraph) == LevelBP) { if (const UK2Node_Event* PreExistingNode = FKismetEditorUtilities::FindBoundEventForActor(GetReferencedLevelActor(), DelegatePropertyName)) { //UE_LOG(LogBlueprint, Log, TEXT("Cannot paste event node (%s) directly because it is flagged as an internal event."), *GetFName().ToString()); bDisallowPaste = true; } } else { ///UE_LOG(LogBlueprint, Log, TEXT("Cannot paste event node (%s) directly because it is flagged as an internal event."), *GetFName().ToString()); } } return !bDisallowPaste; } FText UK2Node_ActorBoundEvent::GetNodeTitle(ENodeTitleType::Type TitleType) const { if (EventOwner == nullptr) { FFormatNamedArguments Args; Args.Add(TEXT("DelegatePropertyName"), GetTargetDelegateDisplayName()); return FText::Format(LOCTEXT("ActorBoundEventTitleNoOwner", "{DelegatePropertyName} (None)"), Args); } else if (CachedNodeTitle.IsOutOfDate(this)) { FFormatNamedArguments Args; Args.Add(TEXT("DelegatePropertyName"), GetTargetDelegateDisplayName()); Args.Add(TEXT("TargetName"), FText::FromString(EventOwner->GetActorLabel())); // FText::Format() is slow, so we cache this to save on performance CachedNodeTitle.SetCachedText(FText::Format(LOCTEXT("ActorBoundEventTitle", "{DelegatePropertyName} ({TargetName})"), Args), this); } return CachedNodeTitle; } FText UK2Node_ActorBoundEvent::GetTooltipText() const { FMulticastDelegateProperty* TargetDelegateProp = GetTargetDelegateProperty(); if (TargetDelegateProp) { return TargetDelegateProp->GetToolTipText(); } else { return FText::FromName(DelegatePropertyName); } } FString UK2Node_ActorBoundEvent::GetDocumentationLink() const { if (UClass* EventSignatureClass = EventReference.GetMemberParentClass(GetBlueprintClassFromNode())) { return FString::Printf(TEXT("Shared/GraphNodes/Blueprint/%s%s"), EventSignatureClass->GetPrefixCPP(), *EventSignatureClass->GetName()); } return FString(); } FString UK2Node_ActorBoundEvent::GetDocumentationExcerptName() const { return DelegatePropertyName.ToString(); } AActor* UK2Node_ActorBoundEvent::GetReferencedLevelActor() const { return EventOwner; } void UK2Node_ActorBoundEvent::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const { Super::ValidateNodeDuringCompilation(MessageLog); // make sure that the actor still exists: AActor* TargetActor = GetReferencedLevelActor(); if(!TargetActor) { MessageLog.Warning( *NSLOCTEXT("KismetCompiler", "MissingActor_ActorBoundEvent", "@@ is referencing an Actor that no longer exists. Attached logic will never execute.").ToString(), this ); } else if(DelegateOwnerClass == nullptr) { MessageLog.Warning( *NSLOCTEXT("KismetCompiler", "MissingClass_ActorBoundEvent", "@@ is trying to find an Event Dispatcher named @@ in a class that no longer exists. Attached logic will never execute.").ToString(), this, *DelegatePropertyName.ToString() ); } else if( GetTargetDelegatePropertyFromSkel() == nullptr ) { MessageLog.Warning( *NSLOCTEXT("KismetCompiler", "MissingDelegate_ActorBoundEvent", "@@ is referencing an Event Dispatcher named @@ that no longer exists in class @@. Attached logic will never execute.").ToString(), this, *DelegatePropertyName.ToString(), DelegateOwnerClass ); } } void UK2Node_ActorBoundEvent::InitializeActorBoundEventParams(AActor* InEventOwner, const FMulticastDelegateProperty* InDelegateProperty) { if (InEventOwner && InDelegateProperty) { EventOwner = InEventOwner; DelegatePropertyName = InDelegateProperty->GetFName(); DelegateOwnerClass = CastChecked(InDelegateProperty->GetOwner())->GetAuthoritativeClass(); EventReference.SetFromField(InDelegateProperty->SignatureFunction, false); CustomFunctionName = FName(*FString::Printf(TEXT("BndEvt__%s_%s_%s_%s"), *GetBlueprint()->GetName(), *InEventOwner->GetName(), *GetName(), *EventReference.GetMemberName().ToString())); bOverrideFunction = false; bInternalEvent = true; CachedNodeTitle.MarkDirty(); } } FMulticastDelegateProperty* UK2Node_ActorBoundEvent::GetTargetDelegateProperty() const { return FindFProperty(DelegateOwnerClass, DelegatePropertyName); } FMulticastDelegateProperty* UK2Node_ActorBoundEvent::GetTargetDelegatePropertyFromSkel() const { return FindFProperty(FBlueprintEditorUtils::GetMostUpToDateClass(DelegateOwnerClass), DelegatePropertyName); } FText UK2Node_ActorBoundEvent::GetTargetDelegateDisplayName() const { FMulticastDelegateProperty* Prop = GetTargetDelegateProperty(); return Prop ? Prop->GetDisplayNameText() : FText::FromName(DelegatePropertyName); } bool UK2Node_ActorBoundEvent::IsUsedByAuthorityOnlyDelegate() const { const FMulticastDelegateProperty* TargetDelegateProp = FindFProperty(DelegateOwnerClass, DelegatePropertyName); return (TargetDelegateProp && TargetDelegateProp->HasAnyPropertyFlags(CPF_BlueprintAuthorityOnly)); } void UK2Node_ActorBoundEvent::Serialize(FArchive& Ar) { Super::Serialize(Ar); // Fix up legacy nodes that may not yet have a delegate pin if (Ar.IsLoading()) { if (Ar.UEVer() < VER_UE4_K2NODE_EVENT_MEMBER_REFERENCE) { DelegateOwnerClass = EventSignatureClass_DEPRECATED; } } } #undef LOCTEXT_NAMESPACE