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 }