2021-09-28 13:33:17 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "StateTreeState.h"
|
|
|
|
|
#include "StateTree.h"
|
2022-04-28 03:54:07 -04:00
|
|
|
#include "StateTreeEditorData.h"
|
2021-09-28 13:33:17 -04:00
|
|
|
#include "StateTreeConditionBase.h"
|
2022-02-03 09:13:49 -05:00
|
|
|
#include "StateTreeTaskBase.h"
|
2021-09-28 13:33:17 -04:00
|
|
|
#include "StateTreeDelegates.h"
|
|
|
|
|
|
2022-09-28 01:06:15 -04:00
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(StateTreeState)
|
|
|
|
|
|
2021-10-21 04:08:20 -04:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// FStateTreeTransition
|
|
|
|
|
|
2022-09-01 09:06:53 -04:00
|
|
|
FStateTreeTransition::FStateTreeTransition(const EStateTreeTransitionTrigger InTrigger, const EStateTreeTransitionType InType, const UStateTreeState* InState)
|
|
|
|
|
: Trigger(InTrigger)
|
|
|
|
|
{
|
2023-01-23 12:48:04 -05:00
|
|
|
State = InState ? InState->GetLinkToState() : FStateTreeStateLink(InType);
|
2022-09-01 09:06:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FStateTreeTransition::FStateTreeTransition(const EStateTreeTransitionTrigger InTrigger, const FGameplayTag InEventTag, const EStateTreeTransitionType InType, const UStateTreeState* InState)
|
|
|
|
|
: Trigger(InTrigger)
|
|
|
|
|
, EventTag(InEventTag)
|
2021-09-28 13:33:17 -04:00
|
|
|
{
|
2023-01-23 12:48:04 -05:00
|
|
|
State = InState ? InState->GetLinkToState() : FStateTreeStateLink(InType);
|
2021-09-28 13:33:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-10-21 04:08:20 -04:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// UStateTreeState
|
|
|
|
|
|
2021-09-28 13:33:17 -04:00
|
|
|
UStateTreeState::UStateTreeState(const FObjectInitializer& ObjectInitializer)
|
|
|
|
|
: Super(ObjectInitializer)
|
|
|
|
|
, ID(FGuid::NewGuid())
|
|
|
|
|
{
|
2022-04-28 09:47:16 -04:00
|
|
|
Parameters.ID = FGuid::NewGuid();
|
2021-09-28 13:33:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
|
void UStateTreeState::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent)
|
|
|
|
|
{
|
|
|
|
|
Super::PostEditChangeChainProperty(PropertyChangedEvent);
|
|
|
|
|
|
|
|
|
|
FProperty* Property = PropertyChangedEvent.Property;
|
|
|
|
|
FProperty* MemberProperty = nullptr;
|
|
|
|
|
if (PropertyChangedEvent.PropertyChain.GetActiveMemberNode())
|
|
|
|
|
{
|
|
|
|
|
MemberProperty = PropertyChangedEvent.PropertyChain.GetActiveMemberNode()->GetValue();
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-30 12:21:34 -04:00
|
|
|
auto TryGetConditionIndex = [&PropertyChangedEvent]() -> int32
|
|
|
|
|
{
|
|
|
|
|
check (PropertyChangedEvent.PropertyChain.GetActiveMemberNode());
|
|
|
|
|
FEditPropertyChain::TDoubleLinkedListNode* ConditionPropertyNode = PropertyChangedEvent.PropertyChain.GetActiveMemberNode()->GetNextNode();
|
|
|
|
|
FProperty* ConditionsProperty = ConditionPropertyNode ? ConditionPropertyNode->GetValue() : nullptr;
|
|
|
|
|
if (ConditionsProperty && ConditionsProperty->GetFName() == GET_MEMBER_NAME_CHECKED(FStateTreeTransition, Conditions))
|
|
|
|
|
{
|
|
|
|
|
return PropertyChangedEvent.GetArrayIndex(ConditionsProperty->GetFName().ToString());
|
|
|
|
|
}
|
|
|
|
|
return INDEX_NONE;
|
|
|
|
|
};
|
2023-01-10 15:44:28 -05:00
|
|
|
|
|
|
|
|
auto CopyBindings = [this](const FGuid FromStructID, const FGuid ToStructID)
|
|
|
|
|
{
|
|
|
|
|
if (UStateTreeEditorData* EditorData = GetTypedOuter<UStateTreeEditorData>())
|
|
|
|
|
{
|
|
|
|
|
if (FromStructID.IsValid())
|
|
|
|
|
{
|
|
|
|
|
if (FStateTreeEditorPropertyBindings* Bindings = EditorData->GetPropertyEditorBindings())
|
|
|
|
|
{
|
|
|
|
|
Bindings->CopyBindings(FromStructID, ToStructID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-09-30 12:21:34 -04:00
|
|
|
|
2021-09-28 13:33:17 -04:00
|
|
|
if (Property)
|
|
|
|
|
{
|
2022-04-05 03:20:57 -04:00
|
|
|
if (Property->GetOwnerClass() == UStateTreeState::StaticClass()
|
|
|
|
|
&& Property->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, Name))
|
2021-09-28 13:33:17 -04:00
|
|
|
{
|
|
|
|
|
UStateTree* StateTree = GetTypedOuter<UStateTree>();
|
|
|
|
|
if (ensure(StateTree))
|
|
|
|
|
{
|
|
|
|
|
UE::StateTree::Delegates::OnIdentifierChanged.Broadcast(*StateTree);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-05 03:20:57 -04:00
|
|
|
if (Property->GetOwnerClass() == UStateTreeState::StaticClass()
|
|
|
|
|
&& Property->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, Type))
|
|
|
|
|
{
|
2022-04-28 03:54:07 -04:00
|
|
|
// Remove any tasks and evaluators when they are not used.
|
2022-04-05 03:20:57 -04:00
|
|
|
if (Type == EStateTreeStateType::Group || Type == EStateTreeStateType::Linked)
|
|
|
|
|
{
|
|
|
|
|
Tasks.Reset();
|
|
|
|
|
}
|
2022-04-28 03:54:07 -04:00
|
|
|
|
|
|
|
|
// If transitioning from linked state, reset the linked state.
|
2022-04-05 03:20:57 -04:00
|
|
|
if (Type != EStateTreeStateType::Linked)
|
|
|
|
|
{
|
2022-09-30 11:31:57 -04:00
|
|
|
LinkedSubtree = FStateTreeStateLink();
|
2022-04-05 03:20:57 -04:00
|
|
|
}
|
2022-04-28 03:54:07 -04:00
|
|
|
|
|
|
|
|
if (Type == EStateTreeStateType::Linked)
|
|
|
|
|
{
|
|
|
|
|
// Linked parameter layout is fixed, and copied from the linked target state.
|
|
|
|
|
Parameters.bFixedLayout = true;
|
2022-09-30 11:31:57 -04:00
|
|
|
UpdateParametersFromLinkedSubtree();
|
2022-04-28 03:54:07 -04:00
|
|
|
}
|
|
|
|
|
else if (Type == EStateTreeStateType::Subtree)
|
|
|
|
|
{
|
|
|
|
|
// Subtree parameter layout can be edited
|
|
|
|
|
Parameters.bFixedLayout = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Parameters.Reset();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Property->GetOwnerClass() == UStateTreeState::StaticClass()
|
2022-09-30 11:31:57 -04:00
|
|
|
&& Property->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, LinkedSubtree))
|
2022-04-28 03:54:07 -04:00
|
|
|
{
|
|
|
|
|
// When switching to new state, update the parameters.
|
|
|
|
|
if (Type == EStateTreeStateType::Linked)
|
|
|
|
|
{
|
2022-09-30 11:31:57 -04:00
|
|
|
UpdateParametersFromLinkedSubtree();
|
2022-04-28 03:54:07 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Property->GetOwnerClass() == UStateTreeState::StaticClass()
|
|
|
|
|
&& Property->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, Parameters))
|
|
|
|
|
{
|
|
|
|
|
if (Type == EStateTreeStateType::Subtree)
|
|
|
|
|
{
|
|
|
|
|
// Broadcast subtree parameter edits so that the linked states can adapt.
|
|
|
|
|
const UStateTree* StateTree = GetTypedOuter<UStateTree>();
|
|
|
|
|
if (ensure(StateTree))
|
|
|
|
|
{
|
|
|
|
|
UE::StateTree::Delegates::OnStateParametersChanged.Broadcast(*StateTree, ID);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-05 03:20:57 -04:00
|
|
|
}
|
|
|
|
|
|
2021-10-21 04:08:20 -04:00
|
|
|
if (MemberProperty)
|
2021-09-28 13:33:17 -04:00
|
|
|
{
|
|
|
|
|
// Ensure unique ID on duplicated items.
|
2021-10-21 04:08:20 -04:00
|
|
|
if (PropertyChangedEvent.ChangeType == EPropertyChangeType::Duplicate)
|
2021-09-28 13:33:17 -04:00
|
|
|
{
|
2021-10-21 04:08:20 -04:00
|
|
|
if (MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, Tasks))
|
|
|
|
|
{
|
|
|
|
|
const int32 ArrayIndex = PropertyChangedEvent.GetArrayIndex(MemberProperty->GetFName().ToString());
|
|
|
|
|
if (Tasks.IsValidIndex(ArrayIndex))
|
|
|
|
|
{
|
2022-02-03 09:13:49 -05:00
|
|
|
if (FStateTreeTaskBase* Task = Tasks[ArrayIndex].Node.GetMutablePtr<FStateTreeTaskBase>())
|
2021-10-21 04:08:20 -04:00
|
|
|
{
|
|
|
|
|
Task->Name = FName(Task->Name.ToString() + TEXT(" Duplicate"));
|
|
|
|
|
}
|
2023-01-10 15:44:28 -05:00
|
|
|
const FGuid OldStructID = Tasks[ArrayIndex].ID;
|
2021-11-12 05:48:11 -05:00
|
|
|
Tasks[ArrayIndex].ID = FGuid::NewGuid();
|
2023-01-10 15:44:28 -05:00
|
|
|
CopyBindings(OldStructID, Tasks[ArrayIndex].ID);
|
2021-10-21 04:08:20 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, EnterConditions))
|
|
|
|
|
{
|
|
|
|
|
const int32 ArrayIndex = PropertyChangedEvent.GetArrayIndex(MemberProperty->GetFName().ToString());
|
|
|
|
|
if (EnterConditions.IsValidIndex(ArrayIndex))
|
|
|
|
|
{
|
2022-09-30 12:21:34 -04:00
|
|
|
if (FStateTreeConditionBase* Condition = EnterConditions[ArrayIndex].Node.GetMutablePtr<FStateTreeConditionBase>())
|
|
|
|
|
{
|
|
|
|
|
Condition->Name = FName(Condition->Name.ToString() + TEXT(" Duplicate"));
|
|
|
|
|
}
|
2023-01-10 15:44:28 -05:00
|
|
|
const FGuid OldStructID = EnterConditions[ArrayIndex].ID;
|
2021-11-12 05:48:11 -05:00
|
|
|
EnterConditions[ArrayIndex].ID = FGuid::NewGuid();
|
2023-01-10 15:44:28 -05:00
|
|
|
CopyBindings(OldStructID, EnterConditions[ArrayIndex].ID);
|
2021-10-21 04:08:20 -04:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-30 12:21:34 -04:00
|
|
|
if (MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, Transitions))
|
|
|
|
|
{
|
|
|
|
|
const int32 TransitionsIndex = PropertyChangedEvent.GetArrayIndex(MemberProperty->GetFName().ToString());
|
|
|
|
|
const int32 ConditionsIndex = TryGetConditionIndex();
|
|
|
|
|
|
|
|
|
|
if (Transitions.IsValidIndex(TransitionsIndex))
|
|
|
|
|
{
|
|
|
|
|
FStateTreeTransition& Transition = Transitions[TransitionsIndex];
|
|
|
|
|
if (Transition.Conditions.IsValidIndex(ConditionsIndex))
|
|
|
|
|
{
|
|
|
|
|
if (FStateTreeConditionBase* Condition = Transition.Conditions[ConditionsIndex].Node.GetMutablePtr<FStateTreeConditionBase>())
|
|
|
|
|
{
|
|
|
|
|
Condition->Name = FName(Condition->Name.ToString() + TEXT(" Duplicate"));
|
|
|
|
|
}
|
2023-01-10 15:44:28 -05:00
|
|
|
const FGuid OldStructID = Transition.Conditions[ConditionsIndex].ID;
|
2022-09-30 12:21:34 -04:00
|
|
|
Transition.Conditions[ConditionsIndex].ID = FGuid::NewGuid();
|
2023-01-10 15:44:28 -05:00
|
|
|
CopyBindings(OldStructID, Transition.Conditions[ConditionsIndex].ID);
|
2022-09-30 12:21:34 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-28 13:33:17 -04:00
|
|
|
}
|
2022-12-02 07:57:31 -05:00
|
|
|
else if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayAdd)
|
|
|
|
|
{
|
|
|
|
|
if (MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, Transitions))
|
|
|
|
|
{
|
|
|
|
|
const int32 TransitionsIndex = PropertyChangedEvent.GetArrayIndex(MemberProperty->GetFName().ToString());
|
|
|
|
|
if (Transitions.IsValidIndex(TransitionsIndex))
|
|
|
|
|
{
|
|
|
|
|
// Set default transition on newly created states.
|
|
|
|
|
FStateTreeTransition& Transition = Transitions[TransitionsIndex];
|
|
|
|
|
Transition.Trigger = EStateTreeTransitionTrigger::OnStateCompleted;
|
|
|
|
|
const UStateTreeState* RootState = GetRootState();
|
2023-01-23 12:48:04 -05:00
|
|
|
Transition.State = RootState->GetLinkToState();
|
2022-12-02 07:57:31 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-10 15:44:28 -05:00
|
|
|
else if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayRemove)
|
|
|
|
|
{
|
|
|
|
|
if (MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, Tasks)
|
|
|
|
|
|| MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, EnterConditions)
|
|
|
|
|
|| MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, Transitions))
|
|
|
|
|
{
|
|
|
|
|
if (UStateTreeEditorData* TreeData = GetTypedOuter<UStateTreeEditorData>())
|
|
|
|
|
{
|
|
|
|
|
TreeData->Modify();
|
|
|
|
|
FStateTreeEditorPropertyBindings* Bindings = TreeData->GetPropertyEditorBindings();
|
|
|
|
|
check(Bindings);
|
|
|
|
|
TMap<FGuid, const UStruct*> AllStructIDs;
|
|
|
|
|
TreeData->GetAllStructIDs(AllStructIDs);
|
|
|
|
|
Bindings->RemoveUnusedBindings(AllStructIDs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-02 07:57:31 -05:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UStateTreeState, Transitions))
|
|
|
|
|
{
|
|
|
|
|
const int32 TransitionsIndex = PropertyChangedEvent.GetArrayIndex(MemberProperty->GetFName().ToString());
|
|
|
|
|
if (Transitions.IsValidIndex(TransitionsIndex))
|
|
|
|
|
{
|
|
|
|
|
FStateTreeTransition& Transition = Transitions[TransitionsIndex];
|
|
|
|
|
|
|
|
|
|
if (EnumHasAnyFlags(Transition.Trigger, EStateTreeTransitionTrigger::OnStateCompleted))
|
|
|
|
|
{
|
|
|
|
|
// Reset delay on completion transitions
|
|
|
|
|
Transition.bDelayTransition = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-28 13:33:17 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-21 08:02:21 -04:00
|
|
|
void UStateTreeState::PostLoad()
|
|
|
|
|
{
|
|
|
|
|
Super::PostLoad();
|
|
|
|
|
|
|
|
|
|
// Make sure state has transactional flags to make it work with undo (to fix a bug where root states were created without this flag).
|
|
|
|
|
if (!HasAnyFlags(RF_Transactional))
|
|
|
|
|
{
|
|
|
|
|
SetFlags(RF_Transactional);
|
|
|
|
|
}
|
2022-05-16 05:13:27 -04:00
|
|
|
|
2022-09-01 09:06:53 -04:00
|
|
|
#if WITH_EDITORONLY_DATA
|
2022-05-16 05:13:27 -04:00
|
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
2022-09-01 09:06:53 -04:00
|
|
|
// Move deprecated evaluators to editor data.
|
2022-05-16 05:13:27 -04:00
|
|
|
if (Evaluators_DEPRECATED.Num() > 0)
|
|
|
|
|
{
|
|
|
|
|
if (UStateTreeEditorData* TreeData = GetTypedOuter<UStateTreeEditorData>())
|
|
|
|
|
{
|
|
|
|
|
TreeData->Evaluators.Append(Evaluators_DEPRECATED);
|
|
|
|
|
Evaluators_DEPRECATED.Reset();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
2022-09-01 09:06:53 -04:00
|
|
|
#endif
|
2022-04-21 08:02:21 -04:00
|
|
|
}
|
|
|
|
|
|
2022-09-30 11:31:57 -04:00
|
|
|
void UStateTreeState::UpdateParametersFromLinkedSubtree()
|
2022-04-28 03:54:07 -04:00
|
|
|
{
|
|
|
|
|
if (const UStateTreeEditorData* TreeData = GetTypedOuter<UStateTreeEditorData>())
|
|
|
|
|
{
|
2022-09-30 11:31:57 -04:00
|
|
|
if (const UStateTreeState* LinkTargetState = TreeData->GetStateByID(LinkedSubtree.ID))
|
2022-04-28 03:54:07 -04:00
|
|
|
{
|
|
|
|
|
Parameters.Parameters.MigrateToNewBagInstance(LinkTargetState->Parameters.Parameters);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-21 08:02:21 -04:00
|
|
|
|
2021-09-28 13:33:17 -04:00
|
|
|
#endif
|
|
|
|
|
|
2022-12-02 07:57:31 -05:00
|
|
|
const UStateTreeState* UStateTreeState::GetRootState() const
|
|
|
|
|
{
|
|
|
|
|
const UStateTreeState* RootState = this;
|
|
|
|
|
while (RootState->Parent != nullptr)
|
|
|
|
|
{
|
|
|
|
|
RootState = RootState->Parent;
|
|
|
|
|
}
|
|
|
|
|
return RootState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UStateTreeState* UStateTreeState::GetNextSiblingState() const
|
2021-09-28 13:33:17 -04:00
|
|
|
{
|
|
|
|
|
if (!Parent)
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
for (int32 ChildIdx = 0; ChildIdx < Parent->Children.Num(); ChildIdx++)
|
|
|
|
|
{
|
|
|
|
|
if (Parent->Children[ChildIdx] == this)
|
|
|
|
|
{
|
|
|
|
|
const int NextIdx = ChildIdx + 1;
|
|
|
|
|
if (NextIdx < Parent->Children.Num())
|
|
|
|
|
{
|
|
|
|
|
return Parent->Children[NextIdx];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2022-09-28 01:06:15 -04:00
|
|
|
|
2023-01-23 12:48:04 -05:00
|
|
|
FStateTreeStateLink UStateTreeState::GetLinkToState() const
|
|
|
|
|
{
|
|
|
|
|
FStateTreeStateLink Link(EStateTreeTransitionType::GotoState);
|
|
|
|
|
Link.Name = Name;
|
|
|
|
|
Link.ID = ID;
|
|
|
|
|
return Link;
|
|
|
|
|
}
|