316 lines
6.9 KiB
C++
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
|