Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_GetDataTableRow.cpp
Matthew Griffin bb70b349ce Merging CL 2804086 from //UE4/Release-4.11 to Dev-Main (//UE4/Dev-Main) to isolate copyright update
#lockdown Nick.Penwarden

[CL 2819020 by Matthew Griffin in Main branch]
2016-01-07 08:17:16 -05:00

409 lines
15 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "BlueprintGraphPrivatePCH.h"
#include "KismetCompiler.h"
//#include "Runtime/Engine/Classes/Kismet/GameplayStatics.h"
#include "Kismet/DataTableFunctionLibrary.h"
#include "BlueprintNodeSpawner.h"
#include "EditorCategoryUtils.h"
#include "BlueprintActionDatabaseRegistrar.h"
#define LOCTEXT_NAMESPACE "K2Node_GetDataTableRow"
struct UK2Node_GetDataTableRowHelper
{
static FString DataTablePinName;
static FString RowNamePinName;
static FString RowNotFoundPinName;
};
FString UK2Node_GetDataTableRowHelper::DataTablePinName(LOCTEXT("DataTablePinName","DataTable").ToString());
FString UK2Node_GetDataTableRowHelper::RowNotFoundPinName(LOCTEXT("RowNotFoundPinName","RowNotFound").ToString());
FString UK2Node_GetDataTableRowHelper::RowNamePinName(LOCTEXT("RowNamePinName","RowName").ToString());
UK2Node_GetDataTableRow::UK2Node_GetDataTableRow(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
NodeTooltip = LOCTEXT("NodeTooltip", "Attempts to retrieve a TableRow from a DataTable via it's RowName");
}
void UK2Node_GetDataTableRow::AllocateDefaultPins()
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
// Add execution pins
CreatePin(EGPD_Input, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Execute);
UEdGraphPin* RowFoundPin = CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Then);
RowFoundPin->PinFriendlyName = LOCTEXT("GetDataTableRow Row Found Exec pin", "Row Found");
CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(""), NULL, false, false, UK2Node_GetDataTableRowHelper::RowNotFoundPinName);
// Add DataTable pin
UEdGraphPin* DataTablePin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), UDataTable::StaticClass(), false, false, UK2Node_GetDataTableRowHelper::DataTablePinName);
SetPinToolTip(*DataTablePin, LOCTEXT("DataTablePinDescription", "The DataTable you want to retreive a row from"));
// Row Name pin
UEdGraphPin* RowNamePin = CreatePin(EGPD_Input, K2Schema->PC_Name, TEXT(""), NULL, false, false, UK2Node_GetDataTableRowHelper::RowNamePinName);
SetPinToolTip(*RowNamePin, LOCTEXT("RowNamePinDescription", "The name of the row to retrieve from the DataTable"));
// Result pin
UEdGraphPin* ResultPin = CreatePin(EGPD_Output, K2Schema->PC_Struct, TEXT(""), FTableRowBase::StaticStruct(), false, false, K2Schema->PN_ReturnValue);
ResultPin->PinFriendlyName = LOCTEXT("GetDataTableRow Output Row", "Out Row");
SetPinToolTip(*ResultPin, LOCTEXT("ResultPinDescription", "The returned TableRow, if found"));
Super::AllocateDefaultPins();
}
void UK2Node_GetDataTableRow::SetPinToolTip(UEdGraphPin& MutatablePin, const FText& PinDescription) const
{
MutatablePin.PinToolTip = UEdGraphSchema_K2::TypeToText(MutatablePin.PinType).ToString();
UEdGraphSchema_K2 const* const K2Schema = Cast<const UEdGraphSchema_K2>(GetSchema());
if (K2Schema != nullptr)
{
MutatablePin.PinToolTip += TEXT(" ");
MutatablePin.PinToolTip += K2Schema->GetPinDisplayName(&MutatablePin).ToString();
}
MutatablePin.PinToolTip += FString(TEXT("\n")) + PinDescription.ToString();
}
void UK2Node_GetDataTableRow::SetReturnTypeForStruct(UScriptStruct* RowStruct)
{
if (RowStruct == NULL)
{
RowStruct = FTableRowBase::StaticStruct();
}
UScriptStruct* NewRowStruct = RowStruct;
UScriptStruct* OldRowStruct = GetReturnTypeForStruct();
// If new Data Table uses a different struct type for it's rows
if (NewRowStruct != OldRowStruct)
{
// Doing this just to force the row name drop down to refresh
ReconstructNode();
UEdGraphPin* ResultPin = GetResultPin();
// Because the Return Value struct type has changed, we break the output link
ResultPin->BreakAllPinLinks();
// Change class of output pin
ResultPin->PinType.PinSubCategoryObject = RowStruct;
// When the DataTable pin gets a new value assigned, we need to update the Slate UI so that SGraphNodeCallParameterCollectionFunction will update the ParameterName drop down
UEdGraph* Graph = GetGraph();
Graph->NotifyGraphChanged();
UEdGraphPin* OldRowNamePin = GetRowNamePin();
// Mark dirty
FBlueprintEditorUtils::MarkBlueprintAsModified(GetBlueprint());
}
}
UScriptStruct* UK2Node_GetDataTableRow::GetReturnTypeForStruct()
{
UScriptStruct* ReturnStructType = (UScriptStruct*)(GetResultPin()->PinType.PinSubCategoryObject.Get());
return ReturnStructType;
}
UScriptStruct* UK2Node_GetDataTableRow::GetDataTableRowStructType(const TArray<UEdGraphPin*>* InPinsToSearch /*=NULL*/) const
{
UScriptStruct* RowStructType = NULL;
const TArray<UEdGraphPin*>* PinsToSearch = InPinsToSearch ? InPinsToSearch : &Pins;
UEdGraphPin* DataTablePin = GetDataTablePin(PinsToSearch);
if(DataTablePin && DataTablePin->DefaultObject != NULL && DataTablePin->LinkedTo.Num() == 0)
{
if (DataTablePin->DefaultObject->IsA(UDataTable::StaticClass()))
{
UDataTable* DataTable = (UDataTable*)DataTablePin->DefaultObject;
if (DataTable)
{
RowStructType = DataTable->RowStruct;
}
}
}
return RowStructType;
}
void UK2Node_GetDataTableRow::OnDataTableRowListChanged(const UDataTable* DataTable)
{
UEdGraphPin* DataTablePin = GetDataTablePin();
if (DataTable && DataTablePin && DataTable == DataTablePin->DefaultObject)
{
auto RowNamePin = GetRowNamePin();
const bool TryRefresh = RowNamePin && !RowNamePin->LinkedTo.Num();
const FName CurrentName = RowNamePin ? FName(*RowNamePin->GetDefaultAsString()) : NAME_None;
if (TryRefresh && RowNamePin && !DataTable->GetRowNames().Contains(CurrentName))
{
if (auto BP = GetBlueprint())
{
FBlueprintEditorUtils::MarkBlueprintAsModified(BP);
}
}
}
}
void UK2Node_GetDataTableRow::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{
Super::ReallocatePinsDuringReconstruction(OldPins);
if (UEdGraphPin* DataTablePin = GetDataTablePin(&OldPins))
{
if (UDataTable* DataTable = Cast<UDataTable>(DataTablePin->DefaultObject))
{
// make sure to properly load the data-table object so that we can
// farm the "RowStruct" property from it (below, in GetDataTableRowStructType)
PreloadObject(DataTable);
}
}
UScriptStruct* RowStruct = GetDataTableRowStructType(&OldPins);
if( RowStruct != NULL )
{
SetReturnTypeForStruct(RowStruct);
}
}
void UK2Node_GetDataTableRow::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_GetDataTableRow::GetMenuCategory() const
{
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Utilities);
}
bool UK2Node_GetDataTableRow::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
{
if (MyPin && MyPin->PinName == UK2Node_GetDataTableRowHelper::DataTablePinName)
{
OutReason = LOCTEXT("OnlyLiteralDTSupported", "Only literal data table is supported").ToString();
return true;
}
return false;
}
void UK2Node_GetDataTableRow::PinDefaultValueChanged(UEdGraphPin* ChangedPin)
{
if (ChangedPin && ChangedPin->PinName == UK2Node_GetDataTableRowHelper::DataTablePinName)
{
UScriptStruct* RowStruct = GetDataTableRowStructType();
SetReturnTypeForStruct(RowStruct);
auto RowNamePin = GetRowNamePin();
auto DataTable = Cast<UDataTable>(ChangedPin->DefaultObject);
if (RowNamePin && RowNamePin->DefaultValue.IsEmpty() && DataTable)
{
auto Iterator = DataTable->RowMap.CreateConstIterator();
RowNamePin->DefaultValue = Iterator ? Iterator.Key().ToString() : FString();
}
}
}
FText UK2Node_GetDataTableRow::GetTooltipText() const
{
return NodeTooltip;
}
UEdGraphPin* UK2Node_GetDataTableRow::GetThenPin()const
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
UEdGraphPin* Pin = FindPinChecked(K2Schema->PN_Then);
check(Pin->Direction == EGPD_Output);
return Pin;
}
UEdGraphPin* UK2Node_GetDataTableRow::GetDataTablePin(const TArray<UEdGraphPin*>* InPinsToSearch /*= NULL*/) const
{
const TArray<UEdGraphPin*>* PinsToSearch = InPinsToSearch ? InPinsToSearch : &Pins;
UEdGraphPin* Pin = NULL;
for( auto PinIt = PinsToSearch->CreateConstIterator(); PinIt; ++PinIt )
{
UEdGraphPin* TestPin = *PinIt;
if( TestPin && TestPin->PinName == UK2Node_GetDataTableRowHelper::DataTablePinName)
{
Pin = TestPin;
break;
}
}
check(Pin == NULL || Pin->Direction == EGPD_Input);
return Pin;
}
UEdGraphPin* UK2Node_GetDataTableRow::GetRowNamePin() const
{
UEdGraphPin* Pin = FindPinChecked(UK2Node_GetDataTableRowHelper::RowNamePinName);
check(Pin->Direction == EGPD_Input);
return Pin;
}
UEdGraphPin* UK2Node_GetDataTableRow::GetRowNotFoundPin() const
{
UEdGraphPin* Pin = FindPinChecked(UK2Node_GetDataTableRowHelper::RowNotFoundPinName);
check(Pin->Direction == EGPD_Output);
return Pin;
}
UEdGraphPin* UK2Node_GetDataTableRow::GetResultPin() const
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
UEdGraphPin* Pin = FindPinChecked(K2Schema->PN_ReturnValue);
check(Pin->Direction == EGPD_Output);
return Pin;
}
FText UK2Node_GetDataTableRow::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (TitleType == ENodeTitleType::MenuTitle)
{
return LOCTEXT("ListViewTitle", "Get Data Table Row");
}
else if (UEdGraphPin* DataTablePin = GetDataTablePin())
{
if (DataTablePin->LinkedTo.Num() > 0)
{
return NSLOCTEXT("K2Node", "DataTable_Title_Unknown", "Get Data Table Row");
}
else if (DataTablePin->DefaultObject == nullptr)
{
return NSLOCTEXT("K2Node", "DataTable_Title_None", "Get Data Table Row NONE");
}
else if (CachedNodeTitle.IsOutOfDate(this))
{
FFormatNamedArguments Args;
Args.Add(TEXT("DataTableName"), FText::FromString(DataTablePin->DefaultObject->GetName()));
FText LocFormat = NSLOCTEXT("K2Node", "DataTable", "Get Data Table Row {DataTableName}");
// FText::Format() is slow, so we cache this to save on performance
CachedNodeTitle.SetCachedText(FText::Format(LocFormat, Args), this);
}
}
else
{
return NSLOCTEXT("K2Node", "DataTable_Title_None", "Get Data Table Row NONE");
}
return CachedNodeTitle;
}
void UK2Node_GetDataTableRow::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
UEdGraphPin* OriginalDataTableInPin = GetDataTablePin();
UDataTable* Table = (OriginalDataTableInPin != NULL) ? Cast<UDataTable>(OriginalDataTableInPin->DefaultObject) : NULL;
if((0 == OriginalDataTableInPin->LinkedTo.Num()) && (NULL == Table))
{
CompilerContext.MessageLog.Error(*LOCTEXT("GetDataTableRowNoDataTable_Error", "GetDataTableRow must have a DataTable specified.").ToString(), this);
// we break exec links so this is the only error we get
BreakAllNodeLinks();
return;
}
// FUNCTION NODE
const FName FunctionName = GET_FUNCTION_NAME_CHECKED(UDataTableFunctionLibrary, GetDataTableRowFromName);
UK2Node_CallFunction* GetDataTableRowFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
GetDataTableRowFunction->FunctionReference.SetExternalMember(FunctionName, UDataTableFunctionLibrary::StaticClass());
GetDataTableRowFunction->AllocateDefaultPins();
CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *(GetDataTableRowFunction->GetExecPin()));
// Connect the input of our GetDataTableRow to the Input of our Function pin
UEdGraphPin* DataTableInPin = GetDataTableRowFunction->FindPinChecked(TEXT("Table"));
if(OriginalDataTableInPin->LinkedTo.Num() > 0)
{
// Copy the connection
CompilerContext.MovePinLinksToIntermediate(*OriginalDataTableInPin, *DataTableInPin);
}
else
{
// Copy literal
DataTableInPin->DefaultObject = OriginalDataTableInPin->DefaultObject;
}
UEdGraphPin* RowNameInPin = GetDataTableRowFunction->FindPinChecked(TEXT("RowName"));
CompilerContext.MovePinLinksToIntermediate(*GetRowNamePin(), *RowNameInPin);
// Get some pins to work with
UEdGraphPin* OriginalOutRowPin = FindPinChecked(Schema->PN_ReturnValue);
UEdGraphPin* FunctionOutRowPin = GetDataTableRowFunction->FindPinChecked(TEXT("OutRow"));
UEdGraphPin* FunctionReturnPin = GetDataTableRowFunction->FindPinChecked(Schema->PN_ReturnValue);
UEdGraphPin* FunctionThenPin = GetDataTableRowFunction->GetThenPin();
// Set the type of the OutRow pin on this expanded mode to match original
FunctionOutRowPin->PinType = OriginalOutRowPin->PinType;
FunctionOutRowPin->PinType.PinSubCategoryObject = OriginalOutRowPin->PinType.PinSubCategoryObject;
//BRANCH NODE
UK2Node_IfThenElse* BranchNode = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(this, SourceGraph);
BranchNode->AllocateDefaultPins();
// Hook up inputs to branch
FunctionThenPin->MakeLinkTo(BranchNode->GetExecPin());
FunctionReturnPin->MakeLinkTo(BranchNode->GetConditionPin());
// Hook up outputs
CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *(BranchNode->GetThenPin()));
CompilerContext.MovePinLinksToIntermediate(*GetRowNotFoundPin(), *(BranchNode->GetElsePin()));
CompilerContext.MovePinLinksToIntermediate(*OriginalOutRowPin, *FunctionOutRowPin);
BreakAllNodeLinks();
}
void UK2Node_GetDataTableRow::EarlyValidation(class FCompilerResultsLog& MessageLog) const
{
const auto DataTablePin = GetDataTablePin();
const auto RowNamePin = GetRowNamePin();
if (!DataTablePin || !RowNamePin)
{
MessageLog.Error(*LOCTEXT("MissingPins", "Missing pins in @@").ToString(), this);
return;
}
const auto DataTable = Cast<UDataTable>(DataTablePin->DefaultObject);
if (!DataTable)
{
MessageLog.Error(*LOCTEXT("NoDataTable", "No DataTable in @@").ToString(), this);
return;
}
if (!RowNamePin->LinkedTo.Num())
{
const FName CurrentName = FName(*RowNamePin->GetDefaultAsString());
if (!DataTable->GetRowNames().Contains(CurrentName))
{
const FString Msg = FString::Printf(
*LOCTEXT("WronRowName", "'%s' row name is not stored in '%s'. @@").ToString()
, *CurrentName.ToString()
, *GetFullNameSafe(DataTable));
MessageLog.Error(*Msg, this);
return;
}
}
}
#undef LOCTEXT_NAMESPACE