Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions Source/Flow/Private/AddOns/FlowNodeAddOn_SwitchCase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors

#include "AddOns/FlowNodeAddOn_SwitchCase.h"
#include "AddOns/FlowNodeAddOn_PredicateAND.h"
#include "AddOns/FlowNodeAddOn_PredicateOR.h"
#include "FlowSettings.h"
#include "Nodes/FlowNode.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeAddOn_SwitchCase)

#define LOCTEXT_NAMESPACE "FlowNodeAddOn_SwitchCase"

const FName UFlowNodeAddOn_SwitchCase::DefaultCaseName = "Case";

UFlowNodeAddOn_SwitchCase::UFlowNodeAddOn_SwitchCase()
: Super()
, CaseName(DefaultCaseName)
, OutputPinName(DefaultCaseName)
{
#if WITH_EDITOR
NodeDisplayStyle = FlowNodeStyle::AddOn_SwitchCase;
Category = TEXT("Switch");
#endif
}

#if WITH_EDITOR

void UFlowNodeAddOn_SwitchCase::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);

if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(ThisClass, CaseName))
{
if (CaseName.IsNone())
{
CaseName = DefaultCaseName;
}

RequestReconstructionOnOwningFlowNode();
}
}

TArray<FFlowPin> UFlowNodeAddOn_SwitchCase::GetContextOutputs() const
{
const UFlowNode* FlowNodeOwner = GetFlowNode();
check(IsValid(FlowNodeOwner));

int32 DuplicateCount = 0;
int32 DuplicateCountForThis = 0;
int32 ThisIndex = INDEX_NONE;

const TArray<UFlowNodeAddOn*>& OwnerAddOns = FlowNodeOwner->GetFlowNodeAddOnChildren();
for (int32 Index = 0; Index < OwnerAddOns.Num(); ++Index)
{
const UFlowNodeAddOn_SwitchCase* SwitchCaseAddOn = Cast<UFlowNodeAddOn_SwitchCase>(OwnerAddOns[Index]);
if (!IsValid(SwitchCaseAddOn))
{
continue;
}

const bool bIsThisAddOn = SwitchCaseAddOn == this;

if (CaseName == SwitchCaseAddOn->CaseName)
{
++DuplicateCount;
}

if (bIsThisAddOn)
{
ThisIndex = Index;

DuplicateCountForThis = DuplicateCount;
}
}

check(ThisIndex != INDEX_NONE);

if (DuplicateCount > 1)
{
OutputPinName = FName(FString::Printf(TEXT("%s (%d)"), *CaseName.ToString(), DuplicateCountForThis));
}
else
{
OutputPinName = FName(FString::Printf(TEXT("%s"), *CaseName.ToString()));
}

return { FFlowPin(OutputPinName) };
}
#endif

EFlowAddOnAcceptResult UFlowNodeAddOn_SwitchCase::AcceptFlowNodeAddOnChild_Implementation(
const UFlowNodeAddOn* AddOnTemplate,
const TArray<UFlowNodeAddOn*>& AdditionalAddOnsToAssumeAreChildren) const
{
if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOnTemplate))
{
return EFlowAddOnAcceptResult::TentativeAccept;
}
else
{
// All AddOn children MUST implement IFlowPredicateInterface
// (so do not return Super's implementation which will return Undetermined)
return EFlowAddOnAcceptResult::Reject;
}
}

bool UFlowNodeAddOn_SwitchCase::TryTriggerForCase_Implementation() const
{
bool bResult = false;
FLOW_ASSERT_ENUM_MAX(EFlowPredicateCombinationRule, 2);
if (BranchCombinationRule == EFlowPredicateCombinationRule::AND)
{
bResult = UFlowNodeAddOn_PredicateAND::EvaluatePredicateAND(AddOns);
}
else
{
check(BranchCombinationRule == EFlowPredicateCombinationRule::OR);

bResult = UFlowNodeAddOn_PredicateOR::EvaluatePredicateOR(AddOns);
}

if (bResult)
{
constexpr bool bFinish = true;
GetFlowNode()->TriggerOutput(OutputPinName, bFinish);
}

return bResult;
}

FText UFlowNodeAddOn_SwitchCase::K2_GetNodeTitle_Implementation() const
{
if (GetDefault<UFlowSettings>()->bUseAdaptiveNodeTitles)
{
FLOW_ASSERT_ENUM_MAX(EFlowPredicateCombinationRule, 2);
if (BranchCombinationRule != EFlowPredicateCombinationRule::AND)
{
return FText::Format(LOCTEXT("SwitchCaseTitle", "{0} ({1})"), { FText::FromName(OutputPinName), UEnum::GetDisplayValueAsText(BranchCombinationRule) });
}
else
{
return FText::Format(LOCTEXT("SwitchCaseTitle", "{0}"), { FText::FromName(OutputPinName) });
}
}

return Super::K2_GetNodeTitle_Implementation();
}

#undef LOCTEXT_NAMESPACE
1 change: 1 addition & 0 deletions Source/Flow/Private/FlowTags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::AddOn, "Flow.NodeStyle.AddOn");
UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::AddOn_PerSpawnedActor, "Flow.NodeStyle.AddOn.PerSpawnedActor");
UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::AddOn_Predicate, "Flow.NodeStyle.AddOn.Predicate");
UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::AddOn_Predicate_Composite, "Flow.NodeStyle.AddOn.Predicate.Composite");
UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::AddOn_SwitchCase, "Flow.NodeStyle.AddOn.SwitchCase");
20 changes: 20 additions & 0 deletions Source/Flow/Private/Interfaces/FlowSwitchCaseInterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors

#include "Interfaces/FlowSwitchCaseInterface.h"
#include "AddOns/FlowNodeAddOn.h"

bool IFlowSwitchCaseInterface::ImplementsInterfaceSafe(const UFlowNodeAddOn* AddOnTemplate)
{
if (!IsValid(AddOnTemplate))
{
return false;
}

UClass* AddOnClass = AddOnTemplate->GetClass();
if (AddOnClass->ImplementsInterface(UFlowSwitchCaseInterface::StaticClass()))
{
return true;
}

return false;
}
17 changes: 17 additions & 0 deletions Source/Flow/Private/Nodes/FlowNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ void UFlowNode::ValidateFlowPinArrayIsUnique(const TArray<FFlowPin>& FlowPins, T
}
}
}

void UFlowNode::EnsureSetFlowNodeForEditorForAllAddOns() const
{
UFlowNode* MutableThis = const_cast<UFlowNode*>(this);

MutableThis->ForEachAddOn(
[MutableThis](UFlowNodeAddOn& AddOn) -> EFlowForEachAddOnFunctionReturnValue
{
AddOn.SetFlowNodeForEditor(MutableThis);
return EFlowForEachAddOnFunctionReturnValue::Continue;
});
}

#endif

void UFlowNode::PostLoad()
Expand Down Expand Up @@ -315,6 +328,8 @@ bool UFlowNode::SupportsContextPins() const

TArray<FFlowPin> UFlowNode::GetContextInputs() const
{
EnsureSetFlowNodeForEditorForAllAddOns();

TArray<FFlowPin> ContextOutputs = Super::GetContextInputs();

// Add the Auto-Generated DataPins as GetContextInputs
Expand All @@ -328,6 +343,8 @@ TArray<FFlowPin> UFlowNode::GetContextInputs() const

TArray<FFlowPin> UFlowNode::GetContextOutputs() const
{
EnsureSetFlowNodeForEditorForAllAddOns();

TArray<FFlowPin> ContextOutputs = Super::GetContextOutputs();

// Add the Auto-Generated DataPins as ContextOutputs
Expand Down
45 changes: 43 additions & 2 deletions Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

#include "Nodes/Route/FlowNode_Branch.h"
#include "AddOns/FlowNodeAddOn_PredicateAND.h"
#include "AddOns/FlowNodeAddOn_PredicateOR.h"
#include "FlowSettings.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Branch)

#define LOCTEXT_NAMESPACE "FlowNode_Branch"

const FName UFlowNode_Branch::INPIN_Evaluate = TEXT("Evaluate");
const FName UFlowNode_Branch::OUTPIN_True = TEXT("True");
const FName UFlowNode_Branch::OUTPIN_False = TEXT("False");
Expand Down Expand Up @@ -38,6 +42,43 @@ EFlowAddOnAcceptResult UFlowNode_Branch::AcceptFlowNodeAddOnChild_Implementation

void UFlowNode_Branch::ExecuteInput(const FName& PinName)
{
const bool bResult = UFlowNodeAddOn_PredicateAND::EvaluatePredicateAND(AddOns);
TriggerOutput(bResult ? OUTPIN_True : OUTPIN_False, true);
bool bPassedRootPredicates = false;
FName ResultPinName = OUTPIN_False;

// Test the root-level IFlowPredicateInterface addons
FLOW_ASSERT_ENUM_MAX(EFlowPredicateCombinationRule, 2);
if (BranchCombinationRule == EFlowPredicateCombinationRule::AND)
{
bPassedRootPredicates = UFlowNodeAddOn_PredicateAND::EvaluatePredicateAND(AddOns);
}
else
{
check(BranchCombinationRule == EFlowPredicateCombinationRule::OR);

bPassedRootPredicates = UFlowNodeAddOn_PredicateOR::EvaluatePredicateOR(AddOns);
}

constexpr bool bFinish = true;
if (bPassedRootPredicates)
{
TriggerOutput(OUTPIN_True, bFinish);
}
else
{
TriggerOutput(OUTPIN_False, bFinish);
}
}

FText UFlowNode_Branch::K2_GetNodeTitle_Implementation() const
{
FLOW_ASSERT_ENUM_MAX(EFlowPredicateCombinationRule, 2);
if (BranchCombinationRule != EFlowPredicateCombinationRule::AND &&
GetDefault<UFlowSettings>()->bUseAdaptiveNodeTitles)
{
return FText::Format(LOCTEXT("BranchTitle", "{0} ({1})"), { Super::K2_GetNodeTitle_Implementation(), UEnum::GetDisplayValueAsText(BranchCombinationRule) });
}

return Super::K2_GetNodeTitle_Implementation();
}

#undef LOCTEXT_NAMESPACE
84 changes: 84 additions & 0 deletions Source/Flow/Private/Nodes/Route/FlowNode_Switch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors

#include "Nodes/Route/FlowNode_Switch.h"
#include "AddOns/FlowNodeAddOn.h"
#include "FlowSettings.h"
#include "Interfaces/FlowSwitchCaseInterface.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Switch)

#define LOCTEXT_NAMESPACE "FlowNode_Switch"

const FName UFlowNode_Switch::INPIN_Evaluate = TEXT("Evaluate");
const FName UFlowNode_Switch::OUTPIN_DefaultCase = TEXT("None Passed");

UFlowNode_Switch::UFlowNode_Switch(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
#if WITH_EDITOR
Category = TEXT("Route|Logic");
NodeDisplayStyle = FlowNodeStyle::Logic;
#endif

InputPins.Reset();
InputPins.Add(FFlowPin(INPIN_Evaluate));

OutputPins.Reset();
OutputPins.Add(FFlowPin(OUTPIN_DefaultCase.ToString(), FString(TEXT("Triggered when no cases pass (during a Switch Evaluate)"))));

AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled};
}

EFlowAddOnAcceptResult UFlowNode_Switch::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate, const TArray<UFlowNodeAddOn*>& AdditionalAddOnsToAssumeAreChildren) const
{
if (IFlowSwitchCaseInterface::ImplementsInterfaceSafe(AddOnTemplate))
{
return EFlowAddOnAcceptResult::TentativeAccept;
}

return Super::AcceptFlowNodeAddOnChild_Implementation(AddOnTemplate, AdditionalAddOnsToAssumeAreChildren);
}

void UFlowNode_Switch::ExecuteInput(const FName& PinName)
{
int32 TriggeringCaseCount = 0;

// Trigger the IFlowSwitchCaseInterface addons that pass
const EFlowForEachAddOnFunctionReturnValue SwitchCaseResult =
ForEachAddOnForClassConst<UFlowSwitchCaseInterface>(
[&TriggeringCaseCount, this](const UFlowNodeAddOn& SwitchCaseAddOn)
{
const IFlowSwitchCaseInterface* SwitchCaseInterface = CastChecked<IFlowSwitchCaseInterface>(&SwitchCaseAddOn);

if (IFlowSwitchCaseInterface::Execute_TryTriggerForCase(&SwitchCaseAddOn))
{
++TriggeringCaseCount;

if (bOnlyTriggerFirstPassingCase)
{
return EFlowForEachAddOnFunctionReturnValue::BreakWithSuccess;
}
}

return EFlowForEachAddOnFunctionReturnValue::Continue;
});

if (TriggeringCaseCount == 0)
{
// Trigger the default case if none of the cases passed
constexpr bool bFinish = true;
TriggerOutput(OUTPIN_DefaultCase, bFinish);
}
}

FText UFlowNode_Switch::K2_GetNodeTitle_Implementation() const
{
if (!bOnlyTriggerFirstPassingCase && GetDefault<UFlowSettings>()->bUseAdaptiveNodeTitles)
{
return FText::Format(LOCTEXT("SwitchTitle", "{0} (All Passing)"), { Super::K2_GetNodeTitle_Implementation() });
}

return Super::K2_GetNodeTitle_Implementation();
}

#undef LOCTEXT_NAMESPACE
4 changes: 4 additions & 0 deletions Source/Flow/Public/AddOns/FlowNodeAddOn.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ class UFlowNodeAddOn : public UFlowNodeBase
// --

FLOW_API void RequestReconstructionOnOwningFlowNode() const;

// Editor-only method to set the FlowNode for any follow-up operations
// that the addon will need a reliable FlowNode pointer at editor-time
void SetFlowNodeForEditor(UFlowNode* FlowNodeOwner) { FlowNode = FlowNodeOwner; }
#endif // WITH_EDITOR

protected:
Expand Down
Loading