/ Source / ReasonablePlanningAINodes / Private / K2Node_RpaiReadMemory.cpp
K2Node_RpaiReadMemory.cpp
  1  // Copyright (C) 2025 Radaway Software LLC. All Rights Reserved.
  2  
  3  
  4  #include "K2Node_RpaiReadMemory.h"
  5  
  6  #include "Core/RpaiTypes.h"
  7  #include "RpaiBPLibrary.h"
  8  #include "BlueprintActionFilter.h"
  9  #include "BlueprintFieldNodeSpawner.h"
 10  #include "BlueprintNodeBinder.h"
 11  #include "BlueprintNodeSpawner.h"
 12  #include "KismetCompiler.h"
 13  #include "EdGraphUtilities.h"
 14  #include "K2Node_CallFunction.h"
 15  #include "K2Node_IfThenElse.h"
 16  
 17  FName UK2Node_RpaiReadMemory::MemoryStructInputPin = FName(TEXT("MemoryStruct"));
 18  
 19  void UK2Node_RpaiReadMemory::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
 20  {
 21     bool bIsDirty = false;
 22     FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
 23     if (PropertyName == GET_MEMBER_NAME_CHECKED(UK2Node_RpaiReadMemory, StructType))
 24     {
 25        bIsDirty = true;
 26     }
 27  
 28     if (bIsDirty)
 29     {
 30        ReconstructNode();
 31        GetGraph()->NotifyGraphChanged();
 32     }
 33  
 34     Super::PostEditChangeProperty(PropertyChangedEvent);
 35  }
 36  
 37  void UK2Node_RpaiReadMemory::PreloadRequiredAssets()
 38  {
 39     Super::PreloadRequiredAssets();
 40     PreloadObject(StructType);
 41  }
 42  
 43  UEdGraphPin* UK2Node_RpaiReadMemory::GetMemoryStructInputPin() const
 44  {
 45     return FindPin(MemoryStructInputPin, EGPD_Input);
 46  }
 47  
 48  UEdGraphPin* UK2Node_RpaiReadMemory::GetThenOutputPin() const
 49  {
 50     return FindPin(UEdGraphSchema_K2::PN_Then, EGPD_Output);
 51  }
 52  
 53  UEdGraphPin* UK2Node_RpaiReadMemory::GetInvalidOutputPin() const
 54  {
 55     return FindPin(UEdGraphSchema_K2::PN_Else, EGPD_Output);
 56  }
 57  
 58  FText UK2Node_RpaiReadMemory::GetNodeTitle(ENodeTitleType::Type TitleType) const
 59  {
 60     if (StructType == nullptr)
 61     {
 62        return NSLOCTEXT("Rpai", "ReadMemoryNullStructTitle", "Read Memory as <unknown struct>");
 63     }
 64     else if (CachedTitle.IsOutOfDate(this))
 65     {
 66        FFormatNamedArguments Args;
 67        Args.Add(TEXT("StructName"), FText::FromName(StructType->GetFName()));
 68        CachedTitle.SetCachedText(FText::Format(NSLOCTEXT("Rpai", "ReadMemoryAsStruct", "Read Memory As {StructName}"), Args), this);
 69     }
 70     return CachedTitle;
 71  }
 72  
 73  void UK2Node_RpaiReadMemory::AllocateDefaultPins()
 74  {
 75     CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
 76     CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Struct, FRpaiMemoryStruct::StaticStruct(), MemoryStructInputPin);
 77     if (StructType != nullptr)
 78     {
 79        auto Then = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
 80        Then->PinFriendlyName = NSLOCTEXT("Rpai", "ThenPin", "Valid");
 81        auto Else = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Else);
 82        Else->PinFriendlyName = NSLOCTEXT("Rpai", "InvalidPin", "Invalid");
 83        CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Struct, StructType, UEdGraphSchema_K2::PN_ReturnValue);
 84     }
 85  }
 86  
 87  void UK2Node_RpaiReadMemory::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
 88  {
 89     Super::ExpandNode(CompilerContext, SourceGraph);
 90     auto InputPin = GetMemoryStructInputPin();
 91     if(StructType && InputPin != nullptr)
 92     {
 93        const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
 94  
 95        const FName ReadMemoryFunctionName = GET_FUNCTION_NAME_CHECKED(URpaiBPLibrary, ReadMemory);
 96        const UFunction* ReadMemoryFunction = URpaiBPLibrary::StaticClass()->FindFunctionByName(ReadMemoryFunctionName);
 97        check(NULL != ReadMemoryFunction);
 98  
 99        UK2Node_CallFunction* ReadMemory = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
100        ReadMemory->SetFromFunction(ReadMemoryFunction);
101        ReadMemory->AllocateDefaultPins();
102  
103        UEdGraphPin* OriginalOutStructPin = FindPinChecked(UEdGraphSchema_K2::PN_ReturnValue, EGPD_Output);
104        UEdGraphPin* ReadMemoryOutParamStruct = ReadMemory->FindPinChecked(TEXT("OutStruct"));
105        ReadMemoryOutParamStruct->PinType = OriginalOutStructPin->PinType;
106        ReadMemoryOutParamStruct->PinType.PinSubCategoryObject = OriginalOutStructPin->PinType.PinSubCategoryObject;
107        CompilerContext.MovePinLinksToIntermediate(*OriginalOutStructPin, *ReadMemoryOutParamStruct);
108  
109        UEdGraphPin* ReadMemoryMemoryInputPin = ReadMemory->FindPinChecked(TEXT("Memory"));
110        CompilerContext.MovePinLinksToIntermediate(*InputPin, *ReadMemoryMemoryInputPin);
111  
112        CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *ReadMemory->GetExecPin());
113  
114        UK2Node_IfThenElse* IfThenElse = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(this, SourceGraph);
115        IfThenElse->AllocateDefaultPins();
116  
117        Schema->TryCreateConnection(ReadMemory->GetReturnValuePin(), IfThenElse->GetConditionPin());
118        Schema->TryCreateConnection(ReadMemory->GetThenPin(), IfThenElse->GetExecPin());
119  
120        CompilerContext.MovePinLinksToIntermediate(*GetThenOutputPin(), *IfThenElse->GetThenPin());
121        CompilerContext.MovePinLinksToIntermediate(*GetInvalidOutputPin(), *IfThenElse->GetElsePin());
122  
123        BreakAllNodeLinks();
124     }
125  }
126  
127  void UK2Node_RpaiReadMemory::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
128  {
129     struct GetMenuActions_Utils
130     {
131        static void SetNodeStructType(UEdGraphNode* NewNode, FFieldVariant /*StructField*/, TWeakObjectPtr<UScriptStruct> NonConstStructPtr)
132        {
133           UK2Node_RpaiReadMemory* StructNode = CastChecked<UK2Node_RpaiReadMemory>(NewNode);
134           StructNode->StructType = NonConstStructPtr.Get();
135        }
136  
137        static void OverrideCategory(FBlueprintActionContext const& Context, IBlueprintNodeBinder::FBindingSet const& /*Bindings*/, FBlueprintActionUiSpec* UiSpecOut, TWeakObjectPtr<UScriptStruct> StructPtr)
138        {
139           for (UEdGraphPin* Pin : Context.Pins)
140           {
141              UScriptStruct* PinStruct = Cast<UScriptStruct>(Pin->PinType.PinSubCategoryObject.Get());
142              if ((PinStruct != nullptr) && (StructPtr.Get() == PinStruct) && (Pin->Direction == EGPD_Output))
143              {
144                 UiSpecOut->Category = NSLOCTEXT("Rpai", "EmptyFunctionCategory", "|");
145                 break;
146              }
147           }
148        }
149     };
150  
151     UClass* NodeClass = GetClass();
152     ActionRegistrar.RegisterStructActions(FBlueprintActionDatabaseRegistrar::FMakeStructSpawnerDelegate::CreateLambda([NodeClass](const UScriptStruct* Struct) -> UBlueprintNodeSpawner*
153        {
154           UBlueprintFieldNodeSpawner* NodeSpawner = nullptr;
155           if (Struct && UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Struct, false))
156           {
157              NodeSpawner = UBlueprintFieldNodeSpawner::Create(NodeClass, const_cast<UScriptStruct*>(Struct));
158              check(NodeSpawner != nullptr);
159              TWeakObjectPtr<UScriptStruct> NonConstStructPtr = MakeWeakObjectPtr(const_cast<UScriptStruct*>(Struct));
160              NodeSpawner->SetNodeFieldDelegate = UBlueprintFieldNodeSpawner::FSetNodeFieldDelegate::CreateStatic(GetMenuActions_Utils::SetNodeStructType, NonConstStructPtr);
161              NodeSpawner->DynamicUiSignatureGetter = UBlueprintFieldNodeSpawner::FUiSpecOverrideDelegate::CreateStatic(GetMenuActions_Utils::OverrideCategory, NonConstStructPtr);
162           }
163           return NodeSpawner;
164        }));
165  }