ComboInput/Source/ComboInput/Private/ComboActionGraph.cpp
2023-09-27 00:21:59 -04:00

316 lines
6.9 KiB
C++

// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#include "ComboActionGraph.h"
#include "Nodes/ComboActionGraphEdge.h"
#include "Nodes/ComboActionGraphNode.h"
#include "Nodes/ComboActionGraphNode_StartNode.h"
#define LOCTEXT_NAMESPACE "ComboActionGraph"
DEFINE_LOG_CATEGORY(LogComboActionGraph);
UComboActionGraph::UComboActionGraph()
{
this->NodeType = UComboActionGraph::StaticClass();
this->EdgeType = UComboActionGraph::StaticClass();
this->bEdgeEnabled = false;
this->GraphGUID = FGuid::NewGuid();
#if WITH_EDITORONLY_DATA
this->EdGraph = nullptr;
this->bCanRenameNode = true;
#endif
}
TArray<FComboActionDecorator> UComboActionGraph::GetGraphDecorators() const
{
TArray<FComboActionDecorator> TempReturn;
TArray<FComboActionDecorator> Return;
for (const FComboActionDecorator &Itr : this->GraphDecorators)
{
if (Itr.DecoratorType != nullptr)
{
TempReturn.AddUnique(Itr);
}
}
/* TODO: Cleanup duplicates
for (auto Itr : TempReturn)
{
}
*/
Return = TempReturn;
return Return;
}
TArray<FComboActionDecorator> UComboActionGraph::GetAllDecorators() const
{
TArray<FComboActionDecorator> TempReturn;
TArray<FComboActionDecorator> Return;
for (const UComboActionGraphNode *Itr : this->AllNodes)
{
if (Itr && Itr->GetNodeDecorators().Num() > 0)
{
TempReturn.Append(Itr->NodeDecorators);
}
}
TempReturn.Append(this->GetGraphDecorators());
return TempReturn;
}
bool UComboActionGraph::CanStartDialogueGraph() const
{
bool bSatisfied = true;
if (this->AllNodes.Num() == 0)
{
return false;
}
for (const auto& Itr : this->AllNodes)
{
if (!Itr)
{
return false;
}
if (Itr->ValidateNodeRuntime() == false)
{
return false;
}
}
const TArray<FComboActionDecorator> &Decorators = this->GetAllDecorators();
if (Decorators.Num() == 0)
{
return bSatisfied;
}
TArray<FText> DecoratorValidations;
for (const FComboActionDecorator &Itr : Decorators)
{
if (Itr.ValidateDecorator(DecoratorValidations) == false) bSatisfied = false;
}
if (DecoratorValidations.Num() > 0)
{
for(auto Itr : DecoratorValidations)
{
UE_LOG(LogComboActionGraph, Error, TEXT("%s"), *Itr.ToString());
}
}
return bSatisfied;
}
void UComboActionGraph::CreateGraph()
{
#if WITH_EDITOR
// We already have existing Graph
if (this->EdGraph != nullptr)
{
return;
}
// We already have existing Start Node
if (this->StartNode != nullptr)
{
return;
}
this->StartNode = ConstructDialogueNode<UComboActionGraphNode_StartNode>();
if (this->StartNode != nullptr )
{
this->StartNode->Graph = this;
this->RootNodes.Add(this->StartNode);
this->AllNodes.Add(this->StartNode);
}
#endif
}
void UComboActionGraph::ClearGraph()
{
for (UComboActionGraphNode *Node : this->AllNodes)
{
Node->ParentNodes.Empty();
Node->ChildrenNodes.Empty();
Node->Edges.Empty();
}
this->AllNodes.Empty();
this->RootNodes.Empty();
}
void UComboActionGraph::PostInitProperties()
{
UObject::PostInitProperties();
// Ignore these cases
if (this->HasAnyFlags(EObjectFlags::RF_ClassDefaultObject | EObjectFlags::RF_NeedLoad))
{
return;
}
#if WITH_EDITOR
this->CreateGraph();
#endif
}
#if WITH_EDITOR
bool UComboActionGraph::ValidateGraph(TArray<FText>& ValidationErrors, bool RichTextFormat)
{
bool bReturnValue = true;
// GRAPH DECORATORS VALIDATION
{
TArray<UComboActionDecoratorBase*> UsedNodeDecorators;
for (int i = 0; i < this->GraphDecorators.Num(); i++)
{
const FComboActionDecorator &Decorator = this->GraphDecorators[i];
if (Decorator.DecoratorType)
{
UsedNodeDecorators.Add(Decorator.DecoratorType);
}
else
{
const FString RichTextReturn =
FString("* ")
.Append( TEXT("<RichTextBlock.Bold>Dialogue Graph</>"))
.Append(": has ")
.Append(TEXT("<RichTextBlock.Bold>invalid</> Node Decorator at Index: "))
.Append(FString::FromInt(i))
.Append(".");
const FString TextReturn =
this->GetName()
.Append(": has ")
.Append(TEXT("INVALID Node Decorator at Index: "))
.Append(FString::FromInt(i))
.Append(".");
ValidationErrors.Add(FText::FromString(RichTextFormat ? RichTextReturn : TextReturn));
bReturnValue = false;
}
}
TMap<UClass*, int32> DuplicatedDecoratorsMap;
for (const auto& Itr : UsedNodeDecorators)
{
int32 ClassAppearance = 1;
for (const auto& Itr2 : UsedNodeDecorators)
{
if (Itr != Itr2 && Itr->GetClass() == Itr2->GetClass())
{
auto A = Itr->GetClass()->GetName();
ClassAppearance++;
}
}
if (ClassAppearance > 1 && DuplicatedDecoratorsMap.Contains(Itr->GetClass()) == false)
{
DuplicatedDecoratorsMap.Add(Itr->GetClass(), ClassAppearance);
}
}
if (DuplicatedDecoratorsMap.Num() > 0)
{
for (const TTuple<UClass*, int32> &Itr : DuplicatedDecoratorsMap)
{
bReturnValue = false;
const FString RichTextReturn =
FString("* ")
.Append(TEXT("<RichTextBlock.Bold>Dialogue Graph</>"))
.Append(": has Node Decorator ")
.Append("<RichTextBlock.Bold>")
.Append(Itr.Key->GetName().LeftChop(2))
.Append("</> ")
.Append(FString::FromInt(Itr.Value))
.Append("x times! Please, avoid duplicates!");
const FString TextReturn =
FString(TEXT("Dialogue Graph: has Node Decorator "))
.Append( Itr.Key->GetName().LeftChop(2))
.Append(" ")
.Append(FString::FromInt(Itr.Value))
.Append("x times! Please, avoid duplicates!");
ValidationErrors.Add(FText::FromString(RichTextFormat ? RichTextReturn : TextReturn));
}
}
}
// GRAPH DECORATORS VALIDATION
for (const FComboActionDecorator &Itr : this->GraphDecorators)
{
TArray<FText> DecoratorErrors;
if (!Itr.ValidateDecorator(DecoratorErrors))
{
for (auto Error : DecoratorErrors)
{
const FString ErrorTextRich =
FString("* ")
.Append(TEXT("<RichTextBlock.Bold>Dialogue Graph</>: "))
.Append(FString(Error.ToString()));
const FString ErrorTextSimple =
this->GetName()
.Append(": ")
.Append(FString(Error.ToString()));
ValidationErrors.Add(FText::FromString(RichTextFormat ? ErrorTextRich : ErrorTextSimple));
bReturnValue = false;
}
}
}
if (this->StartNode == nullptr)
{
const FString RichTextReturn =
FString("* ")
.Append(TEXT("<RichTextBlock.Bold>Dialogue Graph</>"))
.Append(": Has no Start Node!");
const FString TextReturn =
this->GetName()
.Append(": Has no Start Node!");
ValidationErrors.Add(FText::FromString(RichTextFormat ? RichTextReturn : TextReturn));
bReturnValue = false;
}
for (UComboActionGraphNode *Itr : this->AllNodes)
{
if (Itr != nullptr && !Itr->ValidateNode(ValidationErrors, RichTextFormat))
{
bReturnValue = false;
}
}
return bReturnValue;
}
EDataValidationResult UComboActionGraph::IsDataValid(TArray<FText>& ValidationErrors)
{
return this->ValidateGraph(ValidationErrors, false)
? EDataValidationResult::Valid
: EDataValidationResult::Invalid;
}
#endif
#undef LOCTEXT_NAMESPACE