Lots of refactoring related to Movement Component State. Still not done yet.
This commit is contained in:
611
Source/Integration/FormatMessage.cpp
Normal file
611
Source/Integration/FormatMessage.cpp
Normal file
@@ -0,0 +1,611 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "FormatMessage.h"
|
||||
|
||||
#include "Internationalization/TextFormatter.h"
|
||||
#include "BlueprintActionDatabaseRegistrar.h"
|
||||
#include "BlueprintNodeSpawner.h"
|
||||
#include "Containers/EnumAsByte.h"
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphSchema.h"
|
||||
#include "EdGraphSchema_K2.h"
|
||||
#include "EdGraphSchema_K2_Actions.h"
|
||||
#include "EditorCategoryUtils.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "HAL/PlatformCrt.h"
|
||||
#include "Internationalization/Internationalization.h"
|
||||
#include "K2Node_CallFunction.h"
|
||||
#include "K2Node_MakeArray.h"
|
||||
#include "K2Node_MakeStruct.h"
|
||||
#include "Kismet/KismetMathLibrary.h"
|
||||
#include "Kismet/KismetTextLibrary.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "Kismet2/CompilerResultsLog.h"
|
||||
#include "KismetCompiler.h"
|
||||
#include "Math/Vector2D.h"
|
||||
#include "Misc/AssertionMacros.h"
|
||||
#include "Misc/CString.h"
|
||||
#include "ScopedTransaction.h"
|
||||
#include "Templates/Casts.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
#include "UObject/Class.h"
|
||||
#include "UObject/ObjectPtr.h"
|
||||
#include "UObject/Package.h"
|
||||
#include "UObject/UnrealNames.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
#include "UObject/WeakObjectPtr.h"
|
||||
#include "UObject/WeakObjectPtrTemplates.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FormatMessage"
|
||||
|
||||
// All argument pins will have Names that start with "A:"
|
||||
|
||||
static bool IsArgumentPin(const UEdGraphPin *Pin) {
|
||||
TCHAR pname[FName::StringBufferSize];
|
||||
Pin->PinName.ToString(pname);
|
||||
return pname[0] == 'A' && pname[1] == ':';
|
||||
}
|
||||
|
||||
static FName ArgumentNameAddPrefix(const FString &name) {
|
||||
FString Prefixed = FString("A:") + name;
|
||||
return FName(*Prefixed);
|
||||
}
|
||||
|
||||
static FString ArgumentNameRemovePrefix(const FName &name) {
|
||||
return name.ToString().Mid(2, FName::StringBufferSize);
|
||||
}
|
||||
|
||||
static const FName VerbosityPinName(TEXT("Verbosity"));
|
||||
static bool IsVerbosityPin(const UEdGraphPin *Pin) {
|
||||
return (Pin->PinName == VerbosityPinName);
|
||||
}
|
||||
|
||||
static const FName FormatPinName(TEXT("Format"));
|
||||
static bool IsFormatPin(const UEdGraphPin *Pin) {
|
||||
return (Pin->PinName == FormatPinName);
|
||||
}
|
||||
|
||||
static const FName ResultPinName(TEXT("Result"));
|
||||
static bool IsResultPin(const UEdGraphPin *Pin) {
|
||||
return (Pin->PinName == ResultPinName);
|
||||
}
|
||||
|
||||
|
||||
void UK2Node_FormatMessage::AllocateDefaultPins()
|
||||
{
|
||||
Super::AllocateDefaultPins();
|
||||
CreateCorrectPins();
|
||||
}
|
||||
|
||||
void UK2Node_FormatMessage::CreateCorrectPins()
|
||||
{
|
||||
if (FindPin(UEdGraphSchema_K2::PN_Execute) == nullptr) {
|
||||
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
|
||||
}
|
||||
|
||||
if (FindPin(UEdGraphSchema_K2::PN_Then) == nullptr) {
|
||||
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
|
||||
}
|
||||
|
||||
if (FindPin(FormatPinName, EGPD_Input) == nullptr) {
|
||||
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_String, FormatPinName);
|
||||
P->DefaultValue = TEXT("Message");
|
||||
}
|
||||
|
||||
// If this is a FormatMessage node, create a pin to output the result as text.
|
||||
//
|
||||
if (!IsFormatErrorMessage())
|
||||
{
|
||||
if (FindPin(ResultPinName, EGPD_Output) == nullptr) {
|
||||
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Text, ResultPinName);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a FormatErrorMessage node, create pins that control the log verbosity
|
||||
//
|
||||
if (IsFormatErrorMessage())
|
||||
{
|
||||
if (FindPin(VerbosityPinName, EGPD_Input) == nullptr) {
|
||||
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxLogVerbosity>(), VerbosityPinName);
|
||||
P->DefaultValue = TEXT("Error");
|
||||
P->AutogeneratedDefaultValue = P->DefaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer all Existing Argument pins to the Old Pins Map.
|
||||
//
|
||||
TMap<FString, UEdGraphPin *> OldPins;
|
||||
for (auto It = Pins.CreateIterator(); It; ++It)
|
||||
{
|
||||
UEdGraphPin* CheckPin = *It;
|
||||
if (IsArgumentPin(CheckPin))
|
||||
{
|
||||
OldPins.Add(ArgumentNameRemovePrefix(CheckPin->PinName), CheckPin);
|
||||
It.RemoveCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
// Create Argument pins in the correct order, reusing old pins where possible.
|
||||
//
|
||||
for (const FString& Name : PinNames)
|
||||
{
|
||||
UEdGraphPin **OldPin = OldPins.Find(Name);
|
||||
if (OldPin != nullptr) {
|
||||
Pins.Emplace(*OldPin);
|
||||
OldPins.Remove(Name);
|
||||
} else {
|
||||
FName PrefixedName = ArgumentNameAddPrefix(Name);
|
||||
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, PrefixedName);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete any unused pins.
|
||||
//
|
||||
for (auto &iter : OldPins)
|
||||
{
|
||||
iter.Value->Modify();
|
||||
iter.Value->MarkAsGarbage();
|
||||
}
|
||||
OldPins.Empty();
|
||||
}
|
||||
|
||||
|
||||
void UK2Node_FormatMessage::SynchronizeArgumentPinType(UEdGraphPin* Pin)
|
||||
{
|
||||
if (IsArgumentPin(Pin))
|
||||
{
|
||||
const UEdGraphSchema_K2* K2Schema = Cast<const UEdGraphSchema_K2>(GetSchema());
|
||||
|
||||
bool bPinTypeChanged = false;
|
||||
if (Pin->LinkedTo.Num() == 0)
|
||||
{
|
||||
static const FEdGraphPinType WildcardPinType = FEdGraphPinType(UEdGraphSchema_K2::PC_Wildcard, NAME_None, nullptr, EPinContainerType::None, false, FEdGraphTerminalType());
|
||||
|
||||
// Ensure wildcard
|
||||
if (Pin->PinType != WildcardPinType)
|
||||
{
|
||||
Pin->PinType = WildcardPinType;
|
||||
bPinTypeChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UEdGraphPin* ArgumentSourcePin = Pin->LinkedTo[0];
|
||||
|
||||
// Take the type of the connected pin
|
||||
if (Pin->PinType != ArgumentSourcePin->PinType)
|
||||
{
|
||||
Pin->PinType = ArgumentSourcePin->PinType;
|
||||
bPinTypeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bPinTypeChanged)
|
||||
{
|
||||
// Let the graph know to refresh
|
||||
GetGraph()->NotifyNodeChanged(this);
|
||||
|
||||
UBlueprint* Blueprint = GetBlueprint();
|
||||
if (!Blueprint->bBeingCompiled)
|
||||
{
|
||||
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FText UK2Node_FormatMessage::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
if (IsFormatErrorMessage())
|
||||
{
|
||||
return LOCTEXT("FormatErrorMessage_Title", "Format Log Message");
|
||||
}
|
||||
else
|
||||
{
|
||||
return LOCTEXT("FormatMessage_Title", "Format Message");
|
||||
}
|
||||
}
|
||||
|
||||
FText UK2Node_FormatMessage::GetPinDisplayName(const UEdGraphPin* Pin) const
|
||||
{
|
||||
// The exec pins don't need labels.
|
||||
if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec)
|
||||
{
|
||||
return FText::GetEmpty();
|
||||
}
|
||||
|
||||
// Many pins can go unlabeled if they have default values.
|
||||
if (IsFormatPin(Pin) || IsVerbosityPin(Pin))
|
||||
{
|
||||
if (Pin->LinkedTo.Num() == 0)
|
||||
{
|
||||
return FText::GetEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
// For argument pins, we must strip off the Argument Pin Prefix.
|
||||
if (IsArgumentPin(Pin)) {
|
||||
return FText::FromString(ArgumentNameRemovePrefix(Pin->PinName));
|
||||
}
|
||||
|
||||
// Otherwise, just return the Pin Name the normal way.
|
||||
return FText::FromName(Pin->PinName);
|
||||
}
|
||||
|
||||
void UK2Node_FormatMessage::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
const FName PropertyName = (PropertyChangedEvent.Property ? PropertyChangedEvent.Property->GetFName() : NAME_None);
|
||||
if (PropertyName == GET_MEMBER_NAME_CHECKED(UK2Node_FormatMessage, PinNames))
|
||||
{
|
||||
ReconstructNode();
|
||||
}
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
GetGraph()->NotifyNodeChanged(this);
|
||||
}
|
||||
|
||||
void UK2Node_FormatMessage::PinConnectionListChanged(UEdGraphPin* Pin)
|
||||
{
|
||||
Modify();
|
||||
SynchronizeArgumentPinType(Pin);
|
||||
}
|
||||
|
||||
void UK2Node_FormatMessage::PinDefaultValueChanged(UEdGraphPin* Pin)
|
||||
{
|
||||
if(IsFormatPin(Pin))
|
||||
{
|
||||
PinNames.Empty();
|
||||
FText::GetFormatPatternParameters(FText::FromString(Pin->DefaultValue), PinNames);
|
||||
CreateCorrectPins();
|
||||
GetGraph()->NotifyNodeChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
void UK2Node_FormatMessage::PinTypeChanged(UEdGraphPin* Pin)
|
||||
{
|
||||
SynchronizeArgumentPinType(Pin);
|
||||
Super::PinTypeChanged(Pin);
|
||||
}
|
||||
|
||||
FText UK2Node_FormatMessage::GetTooltipText() const
|
||||
{
|
||||
return NodeTooltip;
|
||||
}
|
||||
|
||||
|
||||
void UK2Node_FormatMessage::PostReconstructNode()
|
||||
{
|
||||
Super::PostReconstructNode();
|
||||
|
||||
UEdGraph* OuterGraph = GetGraph();
|
||||
if (!IsTemplate() && OuterGraph && OuterGraph->Schema) {
|
||||
for (UEdGraphPin* CurrentPin : Pins)
|
||||
{
|
||||
// Potentially update an argument pin type
|
||||
SynchronizeArgumentPinType(CurrentPin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get a function that can convert the specified type into a FFormatArgumentData.
|
||||
//
|
||||
// For example:
|
||||
// * if you pass in a pin of type 'String', it will return UlxFormatDataLibrary::FormatArgumentDataString
|
||||
// * if you pass in a pin of type 'Vector', it will return UlxFormatDataLibrary::FormatArgumentDataVector
|
||||
// and so forth.
|
||||
//
|
||||
UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphPinType& PinType, bool AllowWild)
|
||||
{
|
||||
// Special case. Wildcard Pins are unconnected pins.
|
||||
//
|
||||
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard && AllowWild)
|
||||
{
|
||||
return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataBlank));
|
||||
}
|
||||
|
||||
// Try to find a match in the UlxFormatDataLibrary.
|
||||
//
|
||||
for (auto It = TFieldIterator<UFunction>(UlxFormatDataLibrary::StaticClass()); It; ++It)
|
||||
{
|
||||
UFunction* Function = *It;
|
||||
FProperty* ValueProperty = Function->FindPropertyByName(TEXT("AutoConvertedValue"));
|
||||
FEdGraphPinType ValuePinType;
|
||||
bool Convertible = Schema->ConvertPropertyToPinType(ValueProperty, ValuePinType);
|
||||
if (!Convertible) continue;
|
||||
if (!Schema->ArePinTypesEquivalent(PinType, ValuePinType)) continue;
|
||||
return Function;
|
||||
}
|
||||
|
||||
// A general handler for Enums. You can override this for specific enums by
|
||||
// putting that particular enum into the UlxFormatDataLibrary.
|
||||
//
|
||||
if ((PinType.PinCategory == UEdGraphSchema_K2::PC_Byte) && (nullptr != Cast<const UEnum>(PinType.PinSubCategoryObject)))
|
||||
{
|
||||
return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataEnum));
|
||||
}
|
||||
|
||||
// A case for subclasses of 'Object' which are not exactly 'Object'
|
||||
//
|
||||
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Object)
|
||||
{
|
||||
return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataObject));
|
||||
}
|
||||
|
||||
// We don't have a match.
|
||||
//
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void UK2Node_FormatMessage::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
|
||||
{
|
||||
Super::ExpandNode(CompilerContext, SourceGraph);
|
||||
|
||||
/**
|
||||
At the end of this, the UK2Node_FormatMessage will not be a part of the Blueprint, it merely handles connecting
|
||||
the other nodes into the Blueprint.
|
||||
*/
|
||||
|
||||
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
|
||||
|
||||
// Create a "Make Array" node to compile the list of arguments into an array for the Format function being called
|
||||
UK2Node_MakeArray* MakeArrayNode = CompilerContext.SpawnIntermediateNode<UK2Node_MakeArray>(this, SourceGraph);
|
||||
MakeArrayNode->AllocateDefaultPins();
|
||||
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(MakeArrayNode, this);
|
||||
|
||||
// Decide which formatting function we're going to call.
|
||||
UFunction *FormatFunction;
|
||||
if (IsFormatErrorMessage())
|
||||
{
|
||||
FormatFunction = UK2Node_FormatMessage::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UK2Node_FormatMessage, FormatLogMessageInternal));
|
||||
}
|
||||
else
|
||||
{
|
||||
FormatFunction = UKismetTextLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, Format));
|
||||
}
|
||||
|
||||
// This is the node that does all the Format work and outputs the message.
|
||||
UK2Node_CallFunction* CallFormatFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
|
||||
CallFormatFunction->SetFromFunction(FormatFunction);
|
||||
CallFormatFunction->AllocateDefaultPins();
|
||||
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallFormatFunction, this);
|
||||
|
||||
// Connect the output of the "Make Array" pin to the function's "InArgs" pin
|
||||
UEdGraphPin* ArrayOut = MakeArrayNode->GetOutputPin();
|
||||
ArrayOut->MakeLinkTo(CallFormatFunction->FindPinChecked(TEXT("InArgs")));
|
||||
|
||||
// This will set the "Make Array" node's type, only works if one pin is connected.
|
||||
MakeArrayNode->PinConnectionListChanged(ArrayOut);
|
||||
|
||||
// For each argument, we will need to add in a "Make Struct" node.
|
||||
for(int32 ArgIdx = 0; ArgIdx < PinNames.Num(); ++ArgIdx)
|
||||
{
|
||||
FString OriginalName = PinNames[ArgIdx];
|
||||
UEdGraphPin* ArgumentPin = FindPin(ArgumentNameAddPrefix(OriginalName), EGPD_Input);
|
||||
|
||||
|
||||
// Find a function that can convert the input into an FFormatArgumentData.
|
||||
UFunction *Converter = ToFormatArgumentData(Schema, ArgumentPin->PinType, true);
|
||||
if (Converter == nullptr)
|
||||
{
|
||||
CompilerContext.MessageLog.Error(TEXT("Cannot convert Pin to a Format Argument"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add a node to call the converter.
|
||||
UK2Node_CallFunction* ConvertNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
|
||||
ConvertNode->SetFromFunction(Converter);
|
||||
ConvertNode->AllocateDefaultPins();
|
||||
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(ConvertNode, this);
|
||||
UEdGraphPin *ValuePin = ConvertNode->FindPin(TEXT("AutoConvertedValue"));
|
||||
UEdGraphPin *NamePin = ConvertNode->FindPinChecked(TEXT("Name"));
|
||||
UEdGraphPin *SubCategoryObjectPin = ConvertNode->FindPin(TEXT("PinSubCategoryObject"));
|
||||
|
||||
// Set a value for the 'Name' pin of the converter.
|
||||
ConvertNode->GetSchema()->TrySetDefaultValue(*NamePin, OriginalName);
|
||||
|
||||
// Connect the Value pin of the converter, if any.
|
||||
if (ValuePin != nullptr)
|
||||
{
|
||||
CompilerContext.MovePinLinksToIntermediate(*ArgumentPin, *ValuePin);
|
||||
}
|
||||
|
||||
// If the converter wants to know the PinSubCategoryObject, pass it in.
|
||||
if (SubCategoryObjectPin != nullptr)
|
||||
{
|
||||
SubCategoryObjectPin->DefaultObject = Cast<UObject>(ArgumentPin->PinType.PinSubCategoryObject);
|
||||
}
|
||||
|
||||
// The "Make Array" node already has one pin available, so don't create one for ArgIdx == 0
|
||||
if(ArgIdx > 0)
|
||||
{
|
||||
MakeArrayNode->AddInputPin();
|
||||
}
|
||||
|
||||
// Find the input pin on the "Make Array" node by index.
|
||||
const FString PinName = FString::Printf(TEXT("[%d]"), ArgIdx);
|
||||
UEdGraphPin* InputPin = MakeArrayNode->FindPinChecked(PinName);
|
||||
|
||||
// Find the output for the pin's "Make Struct" node and link it to the corresponding pin on the "Make Array" node.
|
||||
ConvertNode->GetReturnValuePin()->MakeLinkTo(InputPin);
|
||||
}
|
||||
|
||||
// Connect up other pins to the Formatting node.
|
||||
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(FormatPinName), *CallFormatFunction->FindPinChecked(TEXT("InPattern")));
|
||||
if (IsFormatErrorMessage())
|
||||
{
|
||||
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(VerbosityPinName), *CallFormatFunction->FindPinChecked(TEXT("Verbosity")));
|
||||
}
|
||||
else
|
||||
{
|
||||
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(ResultPinName), *CallFormatFunction->GetReturnValuePin());
|
||||
}
|
||||
|
||||
// Link up the Exec pins.
|
||||
CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *CallFormatFunction->GetExecPin());
|
||||
CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *CallFormatFunction->GetThenPin());
|
||||
|
||||
BreakAllNodeLinks();
|
||||
}
|
||||
|
||||
|
||||
UK2Node::ERedirectType UK2Node_FormatMessage::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const
|
||||
{
|
||||
ERedirectType RedirectType = ERedirectType_None;
|
||||
|
||||
// if the pin names do match
|
||||
if (NewPin->PinName.ToString().Equals(OldPin->PinName.ToString(), ESearchCase::CaseSensitive))
|
||||
{
|
||||
// Make sure we're not dealing with a menu node
|
||||
UEdGraph* OuterGraph = GetGraph();
|
||||
if( OuterGraph && OuterGraph->Schema )
|
||||
{
|
||||
const UEdGraphSchema_K2* K2Schema = Cast<const UEdGraphSchema_K2>(GetSchema());
|
||||
if( !K2Schema || K2Schema->IsSelfPin(*NewPin) || K2Schema->ArePinTypesCompatible(OldPin->PinType, NewPin->PinType) )
|
||||
{
|
||||
RedirectType = ERedirectType_Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
RedirectType = ERedirectType_None;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// try looking for a redirect if it's a K2 node
|
||||
if (UK2Node* Node = Cast<UK2Node>(NewPin->GetOwningNode()))
|
||||
{
|
||||
// if you don't have matching pin, now check if there is any redirect param set
|
||||
TArray<FString> OldPinNames;
|
||||
GetRedirectPinNames(*OldPin, OldPinNames);
|
||||
|
||||
FName NewPinName;
|
||||
RedirectType = ShouldRedirectParam(OldPinNames, /*out*/ NewPinName, Node);
|
||||
|
||||
// make sure they match
|
||||
if ((RedirectType != ERedirectType_None) && (!NewPin->PinName.ToString().Equals(NewPinName.ToString(), ESearchCase::CaseSensitive)))
|
||||
{
|
||||
RedirectType = ERedirectType_None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RedirectType;
|
||||
}
|
||||
|
||||
bool UK2Node_FormatMessage::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
|
||||
{
|
||||
// The following pins cannot be connected. They are meant to be hardwired constants.
|
||||
if (IsFormatPin(MyPin))
|
||||
{
|
||||
OutReason = LOCTEXT("Error_FormatStringMustBeHardwired", "Format string must be a hardwired constant.").ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Argument input pins may only be connected to Byte, Integer, Float, Text, and ETextGender pins...
|
||||
if (IsArgumentPin(MyPin))
|
||||
{
|
||||
const UEdGraphSchema_K2* K2Schema = Cast<const UEdGraphSchema_K2>(GetSchema());
|
||||
const FName& OtherPinCategory = OtherPin->PinType.PinCategory;
|
||||
|
||||
UFunction *Converter = ToFormatArgumentData(K2Schema, OtherPin->PinType, false);
|
||||
if (Converter == nullptr)
|
||||
{
|
||||
OutReason = LOCTEXT("Error_InvalidArgumentType", "Data cannot be converted to text.").ToString();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason);
|
||||
}
|
||||
|
||||
|
||||
void UK2Node_FormatMessage::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
|
||||
{
|
||||
// actions get registered under specific object-keys; the idea is that
|
||||
// actions might have to be updated (or deleted) if their object-key is
|
||||
// mutated (or removed)... here we use the node's class (so if the node
|
||||
// type disappears, then the action should go with it)
|
||||
UClass* ActionKey = GetClass();
|
||||
// to keep from needlessly instantiating a UBlueprintNodeSpawner, first
|
||||
// check to make sure that the registrar is looking for actions of this type
|
||||
// (could be regenerating actions for a specific asset, and therefore the
|
||||
// registrar would only accept actions corresponding to that asset)
|
||||
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
|
||||
{
|
||||
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
|
||||
check(NodeSpawner != nullptr);
|
||||
|
||||
ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
|
||||
}
|
||||
}
|
||||
|
||||
FText UK2Node_FormatMessage::GetMenuCategory() const
|
||||
{
|
||||
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Text);
|
||||
}
|
||||
|
||||
UK2Node_FormatMessage::UK2Node_FormatMessage(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
NodeTooltip = LOCTEXT("NodeTooltip",
|
||||
"Format a message, and output it as Text.\n"
|
||||
"\n"
|
||||
" \u2022 Use {ArgName} to denote format arguments, giving each argument a different ArgName.\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
UK2Node_FormatLogMessage::UK2Node_FormatLogMessage(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
NodeTooltip = LOCTEXT("NodeTooltip",
|
||||
"Output an error, warning, or informational message to the log file.\n"
|
||||
"\n"
|
||||
" \u2022 Use {ArgName} to denote format arguments, giving each argument a different ArgName.\n"
|
||||
"\n"
|
||||
"It is often desirable to use this in conjunction with a separate utility that\n"
|
||||
"pauses the execution of the blueprint whenever an error is logged."
|
||||
);
|
||||
}
|
||||
|
||||
void UK2Node_FormatMessage::FormatLogMessageInternal(UObject *Context, ElxLogVerbosity Verbosity, const FString &InPattern, TArray<FFormatArgumentData> InArgs)
|
||||
{
|
||||
// Generate the formatted string.
|
||||
//
|
||||
FText InPatternText(FText::FromString(InPattern));
|
||||
FText Message = FTextFormatter::Format(MoveTemp(InPatternText), MoveTemp(InArgs), false, false);
|
||||
FString MessageString = Message.ToString();
|
||||
|
||||
// Get the blueprint name.
|
||||
//
|
||||
// Normally, the log function expects you to pass in a filename, and a log
|
||||
// category name. We use the blueprint name for both.
|
||||
//
|
||||
// Using the blueprint name as a log category name is not technically
|
||||
// correct. However, there is no correct way to create log categories
|
||||
// from inside of blueprints. Doing it this way at least produces a reasonable
|
||||
// message inside the log. What doesn't work correctly is the log message
|
||||
// suppression system. Ie, console commands like 'log <category> verbose'
|
||||
// don't have any effect here. The design of the log message suppression
|
||||
// system is such that there just is no reasonable way to hook into it from
|
||||
// inside of blueprints.
|
||||
//
|
||||
FString BlueprintNameString = Context->GetClass()->GetName();
|
||||
auto BlueprintNameAnsi = StringCast<ANSICHAR>(*BlueprintNameString);
|
||||
FLogCategoryName BlueprintNameLogCategory(Context->GetClass()->GetFName());
|
||||
|
||||
// Output to Log
|
||||
//
|
||||
ELogVerbosity::Type VerbosityValue = UlxBlueprintErrorLibrary::ConvertElxLogVerbosity(Verbosity);
|
||||
if (VerbosityValue <= ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY)
|
||||
{
|
||||
FMsg::Logf(BlueprintNameAnsi.Get(), 0, BlueprintNameLogCategory, VerbosityValue, TEXT("%s"), *MessageString);
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
Reference in New Issue
Block a user