1428 lines
43 KiB
C++
1428 lines
43 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ConversationDebugger.h"
|
|
|
|
|
|
#define USE_CONVERSATION_DEBUGGER 0
|
|
|
|
// #include "Conversation/BTNode.h"
|
|
// #include "Conversation/BTTaskNode.h"
|
|
// #include "Conversation/BTAuxiliaryNode.h"
|
|
//#include "ConversationGraphNode_CompositeDecorator.h"
|
|
#include "ConversationEditor.h"
|
|
#include "Editor/UnrealEdEngine.h"
|
|
#include "Selection.h"
|
|
#include "UnrealEdGlobals.h"
|
|
//#include "ConversationGraphNode_Requirement.h"
|
|
//#include "ConversationGraphNode_Task.h"
|
|
//#include "Conversation/Conversation.h"
|
|
//#include "ConversationDelegates.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "ConversationDatabase.h"
|
|
|
|
FConversationDebugger::FConversationDebugger()
|
|
{
|
|
TreeAsset = NULL;
|
|
bIsPIEActive = false;
|
|
bIsCurrentSubtree = false;
|
|
StepForwardIntoIdx = INDEX_NONE;
|
|
StepForwardOverIdx = INDEX_NONE;
|
|
StepBackIntoIdx = INDEX_NONE;
|
|
StepBackOverIdx = INDEX_NONE;
|
|
StepOutIdx = INDEX_NONE;
|
|
SavedTimestamp = 0.0f;
|
|
CurrentTimestamp = 0.0f;
|
|
|
|
FEditorDelegates::BeginPIE.AddRaw(this, &FConversationDebugger::OnBeginPIE);
|
|
FEditorDelegates::EndPIE.AddRaw(this, &FConversationDebugger::OnEndPIE);
|
|
FEditorDelegates::PausePIE.AddRaw(this, &FConversationDebugger::OnPausePIE);
|
|
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
UConversationComponent::ActiveDebuggerCounter++;
|
|
#endif
|
|
}
|
|
|
|
FConversationDebugger::~FConversationDebugger()
|
|
{
|
|
FEditorDelegates::BeginPIE.RemoveAll(this);
|
|
FEditorDelegates::EndPIE.RemoveAll(this);
|
|
FEditorDelegates::PausePIE.RemoveAll(this);
|
|
USelection::SelectObjectEvent.RemoveAll(this);
|
|
// FConversationDelegates::OnTreeStarted.RemoveAll(this);
|
|
// FConversationDelegates::OnDebugLocked.RemoveAll(this);
|
|
// FConversationDelegates::OnDebugSelected.RemoveAll(this);
|
|
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
UConversationComponent::ActiveDebuggerCounter--;
|
|
#endif
|
|
}
|
|
|
|
void FConversationDebugger::CacheRootNode()
|
|
{
|
|
// if(RootNode.IsValid() || TreeAsset == nullptr || TreeAsset->BTGraph == nullptr)
|
|
// {
|
|
// return;
|
|
// }
|
|
//
|
|
// for (const auto& Node : TreeAsset->BTGraph->Nodes)
|
|
// {
|
|
// RootNode = Cast<UConversationGraphNode_Root>(Node);
|
|
// if (RootNode.IsValid())
|
|
// {
|
|
// break;
|
|
// }
|
|
// }
|
|
}
|
|
|
|
void FConversationDebugger::Setup(UConversationDatabase* InTreeAsset, TSharedRef<FConversationEditor> InEditorOwner)
|
|
{
|
|
EditorOwner = InEditorOwner;
|
|
TreeAsset = InTreeAsset;
|
|
DebuggerInstanceIndex = INDEX_NONE;
|
|
ActiveStepIndex = 0;
|
|
LastValidStepId = INDEX_NONE;
|
|
ActiveBreakpoints.Reset();
|
|
// KnownInstances.Reset();
|
|
|
|
CacheRootNode();
|
|
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (IsPIESimulating())
|
|
{
|
|
OnBeginPIE(GEditor->bIsSimulatingInEditor);
|
|
|
|
Refresh();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FConversationDebugger::Refresh()
|
|
{
|
|
CacheRootNode();
|
|
|
|
// if (IsPIESimulating() && IsDebuggerReady())
|
|
// {
|
|
// // make sure is grabs data if currently paused
|
|
// if (IsPlaySessionPaused() && TreeInstance.IsValid())
|
|
// {
|
|
// FindLockedDebugActor(GEditor->PlayWorld);
|
|
//
|
|
// UpdateDebuggerInstance();
|
|
// UpdateAvailableActions();
|
|
//
|
|
// if (DebuggerInstanceIndex != INDEX_NONE)
|
|
// {
|
|
// UpdateDebuggerViewOnStepChange();
|
|
// UpdateDebuggerViewOnTick();
|
|
//
|
|
// const FConversationDebuggerInstance& ShowInstance = TreeInstance->DebuggerSteps[ActiveStepIndex].InstanceStack[DebuggerInstanceIndex];
|
|
// OnActiveNodeChanged(ShowInstance.ActivePath, HasContinuousPrevStep() ?
|
|
// TreeInstance->DebuggerSteps[ActiveStepIndex - 1].InstanceStack[DebuggerInstanceIndex].ActivePath :
|
|
// TArray<uint16>());
|
|
//
|
|
// UpdateAssetFlags(ShowInstance, RootNode.Get(), ActiveStepIndex);
|
|
// }
|
|
// }
|
|
// }
|
|
}
|
|
|
|
void FConversationDebugger::Tick(float DeltaTime)
|
|
{
|
|
if (TreeAsset == NULL || IsPlaySessionPaused())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if (!TreeInstance.IsValid())
|
|
// {
|
|
// // clear state when active tree is lost
|
|
// if (DebuggerInstanceIndex != INDEX_NONE)
|
|
// {
|
|
// ClearDebuggerState();
|
|
// }
|
|
//
|
|
// return;
|
|
// }
|
|
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
TArray<uint16> EmptyPath;
|
|
|
|
int32 TestStepIndex = 0;
|
|
for (int32 Idx = TreeInstance->DebuggerSteps.Num() - 1; Idx >= 0; Idx--)
|
|
{
|
|
const FConversationExecutionStep& Step = TreeInstance->DebuggerSteps[Idx];
|
|
if (Step.StepIndex == LastValidStepId)
|
|
{
|
|
TestStepIndex = Idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// find index of previously displayed state and notify about all changes in between to give breakpoints a chance to trigger
|
|
for (int32 i = TestStepIndex; i < TreeInstance->DebuggerSteps.Num(); i++)
|
|
{
|
|
const FConversationExecutionStep& Step = TreeInstance->DebuggerSteps[i];
|
|
if (Step.StepIndex > DisplayedStepIndex)
|
|
{
|
|
ActiveStepIndex = i;
|
|
LastValidStepId = Step.StepIndex;
|
|
|
|
UpdateDebuggerInstance();
|
|
UpdateAvailableActions();
|
|
|
|
if (DebuggerInstanceIndex != INDEX_NONE)
|
|
{
|
|
UpdateDebuggerViewOnStepChange();
|
|
|
|
const FConversationDebuggerInstance& ShowInstance = TreeInstance->DebuggerSteps[ActiveStepIndex].InstanceStack[DebuggerInstanceIndex];
|
|
OnActiveNodeChanged(ShowInstance.ActivePath, HasContinuousPrevStep() ?
|
|
TreeInstance->DebuggerSteps[ActiveStepIndex - 1].InstanceStack[DebuggerInstanceIndex].ActivePath :
|
|
EmptyPath);
|
|
}
|
|
}
|
|
|
|
// skip rest of them if breakpoint hit
|
|
if (IsPlaySessionPaused())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
UpdateDebuggerInstance();
|
|
if (DebuggerInstanceIndex != INDEX_NONE)
|
|
{
|
|
const FConversationDebuggerInstance& ShowInstance = TreeInstance->DebuggerSteps[ActiveStepIndex].InstanceStack[DebuggerInstanceIndex];
|
|
|
|
if (DisplayedStepIndex != TreeInstance->DebuggerSteps[ActiveStepIndex].StepIndex)
|
|
{
|
|
UpdateAssetFlags(ShowInstance, RootNode.Get(), ActiveStepIndex);
|
|
}
|
|
|
|
// collect current runtime descriptions for every node
|
|
TArray<FString> RuntimeDescriptions;
|
|
TreeInstance->StoreDebuggerRuntimeValues(RuntimeDescriptions, ShowInstance.RootNode, DebuggerInstanceIndex);
|
|
|
|
UpdateAssetRuntimeDescription(RuntimeDescriptions, RootNode.Get());
|
|
}
|
|
|
|
UpdateDebuggerViewOnTick();
|
|
#endif
|
|
}
|
|
|
|
bool FConversationDebugger::IsTickable() const
|
|
{
|
|
return IsDebuggerReady();
|
|
}
|
|
|
|
void FConversationDebugger::OnBeginPIE(const bool bIsSimulating)
|
|
{
|
|
bIsPIEActive = true;
|
|
if(EditorOwner.IsValid())
|
|
{
|
|
TSharedPtr<FConversationEditor> EditorOwnerPtr = EditorOwner.Pin();
|
|
EditorOwnerPtr->RegenerateMenusAndToolbars();
|
|
//EditorOwnerPtr->DebuggerUpdateGraph();
|
|
}
|
|
|
|
ActiveBreakpoints.Reset();
|
|
//CollectBreakpointsFromAsset(RootNode.Get());
|
|
|
|
FindMatchingTreeInstance();
|
|
|
|
// remove these delegates first as we can get multiple calls to OnBeginPIE()
|
|
USelection::SelectObjectEvent.RemoveAll(this);
|
|
// FConversationDelegates::OnTreeStarted.RemoveAll(this);
|
|
// FConversationDelegates::OnDebugSelected.RemoveAll(this);
|
|
|
|
USelection::SelectObjectEvent.AddRaw(this, &FConversationDebugger::OnObjectSelected);
|
|
// FConversationDelegates::OnTreeStarted.AddRaw(this, &FConversationDebugger::OnTreeStarted);
|
|
// FConversationDelegates::OnDebugSelected.AddRaw(this, &FConversationDebugger::OnAIDebugSelected);
|
|
}
|
|
|
|
void FConversationDebugger::OnEndPIE(const bool bIsSimulating)
|
|
{
|
|
bIsPIEActive = false;
|
|
if(EditorOwner.IsValid())
|
|
{
|
|
EditorOwner.Pin()->RegenerateMenusAndToolbars();
|
|
}
|
|
|
|
USelection::SelectObjectEvent.RemoveAll(this);
|
|
// FConversationDelegates::OnTreeStarted.RemoveAll(this);
|
|
// FConversationDelegates::OnDebugSelected.RemoveAll(this);
|
|
|
|
ClearDebuggerState();
|
|
ActiveBreakpoints.Reset();
|
|
|
|
// FConversationDebuggerInstance EmptyData;
|
|
// UpdateAssetFlags(EmptyData, RootNode.Get(), INDEX_NONE);
|
|
UpdateDebuggerViewOnInstanceChange();
|
|
}
|
|
|
|
void FConversationDebugger::OnPausePIE(const bool bIsSimulating)
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
// We might have paused while executing a sub-tree, so make sure that the editor is showing the correct tree
|
|
TSharedPtr<FConversationEditor> EditorOwnerPin = EditorOwner.Pin();
|
|
if (EditorOwnerPin.IsValid() && TreeInstance.IsValid() && TreeInstance->DebuggerSteps.IsValidIndex(ActiveStepIndex))
|
|
{
|
|
const FConversationExecutionStep& StepInfo = TreeInstance->DebuggerSteps[ActiveStepIndex];
|
|
const int32 LastInstanceIndex = StepInfo.InstanceStack.Num() - 1;
|
|
if (StepInfo.InstanceStack.IsValidIndex(LastInstanceIndex) && StepInfo.InstanceStack[LastInstanceIndex].TreeAsset != TreeAsset)
|
|
{
|
|
EditorOwnerPin->DebuggerSwitchAsset(StepInfo.InstanceStack[LastInstanceIndex].TreeAsset);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FConversationDebugger::OnObjectSelected(UObject* Object)
|
|
{
|
|
// if (Object && Object->IsSelected())
|
|
// {
|
|
// UConversationComponent* InstanceComp = FindInstanceInActor(Cast<AActor>(Object));
|
|
// if (InstanceComp)
|
|
// {
|
|
// ClearDebuggerState();
|
|
// TreeInstance = InstanceComp;
|
|
//
|
|
// UpdateDebuggerViewOnInstanceChange();
|
|
// }
|
|
// }
|
|
}
|
|
|
|
void FConversationDebugger::OnAIDebugSelected(const APawn* Pawn)
|
|
{
|
|
// UConversationComponent* TestComp = FindInstanceInActor((APawn*)Pawn);
|
|
// if (TestComp)
|
|
// {
|
|
// ClearDebuggerState();
|
|
// TreeInstance = TestComp;
|
|
//
|
|
// UpdateDebuggerViewOnInstanceChange();
|
|
// }
|
|
}
|
|
|
|
//void FConversationDebugger::OnTreeStarted(const UConversationComponent& OwnerComp, const UConversation& InTreeAsset)
|
|
//{
|
|
// // start debugging if tree asset matches, and no other actor was selected
|
|
// if (!TreeInstance.IsValid() && TreeAsset && TreeAsset == &InTreeAsset)
|
|
// {
|
|
// ClearDebuggerState();
|
|
// TreeInstance = MakeWeakObjectPtr(const_cast<UConversationComponent*>(&OwnerComp));
|
|
//
|
|
// UpdateDebuggerViewOnInstanceChange();
|
|
// }
|
|
//
|
|
// // update known instances
|
|
// TWeakObjectPtr<UConversationComponent> KnownComp = const_cast<UConversationComponent*>(&OwnerComp);
|
|
// KnownInstances.AddUnique(KnownComp);
|
|
//}
|
|
|
|
void FConversationDebugger::ClearDebuggerState(bool bKeepSubtree)
|
|
{
|
|
// LastValidStepId = bKeepSubtree ? LastValidStepId : INDEX_NONE;
|
|
//
|
|
// DebuggerInstanceIndex = INDEX_NONE;
|
|
// ActiveStepIndex = 0;
|
|
// DisplayedStepIndex = INDEX_NONE;
|
|
//
|
|
// if (TreeAsset && RootNode.IsValid())
|
|
// {
|
|
// FConversationDebuggerInstance EmptyData;
|
|
// UpdateAssetFlags(EmptyData, RootNode.Get(), INDEX_NONE);
|
|
// }
|
|
}
|
|
|
|
void FConversationDebugger::UpdateDebuggerInstance()
|
|
{
|
|
#if 0
|
|
int32 PrevStackIndex = DebuggerInstanceIndex;
|
|
DebuggerInstanceIndex = INDEX_NONE;
|
|
|
|
if (TreeInstance.IsValid())
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (TreeInstance->DebuggerSteps.IsValidIndex(ActiveStepIndex))
|
|
{
|
|
const FConversationExecutionStep& StepInfo = TreeInstance->DebuggerSteps[ActiveStepIndex];
|
|
for (int32 i = 0; i < StepInfo.InstanceStack.Num(); i++)
|
|
{
|
|
if (StepInfo.InstanceStack[i].TreeAsset == TreeAsset)
|
|
{
|
|
DebuggerInstanceIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
UpdateCurrentSubtree();
|
|
}
|
|
|
|
if (DebuggerInstanceIndex != PrevStackIndex)
|
|
{
|
|
UpdateDebuggerViewOnInstanceChange();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// void FConversationDebugger::SetNodeFlags(const struct FConversationDebuggerInstance& Data, class UConversationGraphNode* Node, class UBTNode* NodeInstance)
|
|
// {
|
|
// const bool bIsNodeActivePath = Data.ActivePath.Contains(NodeInstance->GetExecutionIndex());
|
|
// const bool bIsNodeActiveAdditional = Data.AdditionalActiveNodes.Contains(NodeInstance->GetExecutionIndex());
|
|
// const bool bIsNodeActive = bIsNodeActivePath || bIsNodeActiveAdditional;
|
|
// const bool bIsShowingCurrentState = IsShowingCurrentState();
|
|
//
|
|
// Node->DebuggerUpdateCounter = DisplayedStepIndex;
|
|
// Node->bDebuggerMarkCurrentlyActive = bIsNodeActive && bIsShowingCurrentState;
|
|
// Node->bDebuggerMarkPreviouslyActive = bIsNodeActive && !bIsShowingCurrentState;
|
|
//
|
|
// const bool bIsTaskNode = NodeInstance->IsA(UBTTaskNode::StaticClass());
|
|
// Node->bDebuggerMarkFlashActive = bIsNodeActivePath && bIsTaskNode && IsPlaySessionRunning();
|
|
// Node->bDebuggerMarkSearchTrigger = false;
|
|
// Node->bDebuggerMarkSearchFailedTrigger = false;
|
|
//
|
|
// Node->bDebuggerMarkBreakpointTrigger = NodeInstance->GetExecutionIndex() == StoppedOnBreakpointExecutionIndex;
|
|
// if (Node->bDebuggerMarkBreakpointTrigger)
|
|
// {
|
|
// if(EditorOwner.IsValid())
|
|
// {
|
|
// EditorOwner.Pin()->JumpToNode(Node);
|
|
// }
|
|
// }
|
|
//
|
|
// int32 SearchPathIdx = INDEX_NONE;
|
|
// int32 NumTriggers = 0;
|
|
// bool bTriggerOnly = false;
|
|
//
|
|
// for (int32 i = 0; i < Data.PathFromPrevious.Num(); i++)
|
|
// {
|
|
// const FConversationDebuggerInstance::FNodeFlowData& SearchStep = Data.PathFromPrevious[i];
|
|
// const bool bMatchesNodeIndex = (SearchStep.ExecutionIndex == NodeInstance->GetExecutionIndex());
|
|
// if (SearchStep.bTrigger || SearchStep.bDiscardedTrigger)
|
|
// {
|
|
// NumTriggers++;
|
|
// if (bMatchesNodeIndex)
|
|
// {
|
|
// Node->bDebuggerMarkSearchTrigger = SearchStep.bTrigger;
|
|
// Node->bDebuggerMarkSearchFailedTrigger = SearchStep.bDiscardedTrigger;
|
|
// bTriggerOnly = true;
|
|
// }
|
|
// }
|
|
// else if (bMatchesNodeIndex)
|
|
// {
|
|
// SearchPathIdx = i;
|
|
// bTriggerOnly = false;
|
|
// }
|
|
// }
|
|
//
|
|
// Node->bDebuggerMarkSearchSucceeded = (SearchPathIdx != INDEX_NONE) && Data.PathFromPrevious[SearchPathIdx].bPassed;
|
|
// Node->bDebuggerMarkSearchFailed = (SearchPathIdx != INDEX_NONE) && !Data.PathFromPrevious[SearchPathIdx].bPassed;
|
|
// Node->DebuggerSearchPathIndex = bTriggerOnly ? 0 : FMath::Max(-1, SearchPathIdx - NumTriggers);
|
|
// Node->DebuggerSearchPathSize = Data.PathFromPrevious.Num() - NumTriggers;
|
|
// }
|
|
|
|
void FConversationDebugger::SetCompositeDecoratorFlags(const struct FConversationDebuggerInstance& Data, class UConversationGraphNode_CompositeDecorator* Node)
|
|
{
|
|
// const bool bIsShowingCurrentState = IsShowingCurrentState();
|
|
// bool bIsNodeActive = false;
|
|
// for (int32 i = 0; i < Data.AdditionalActiveNodes.Num(); i++)
|
|
// {
|
|
// if (Node->FirstExecutionIndex <= Data.AdditionalActiveNodes[i] && Node->LastExecutionIndex >= Data.AdditionalActiveNodes[i])
|
|
// {
|
|
// bIsNodeActive = true;
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// Node->DebuggerUpdateCounter = DisplayedStepIndex;
|
|
// Node->bDebuggerMarkCurrentlyActive = bIsNodeActive && bIsShowingCurrentState;
|
|
// Node->bDebuggerMarkPreviouslyActive = bIsNodeActive && !bIsShowingCurrentState;
|
|
//
|
|
// Node->bDebuggerMarkFlashActive = false;
|
|
// Node->bDebuggerMarkSearchTrigger = false;
|
|
// Node->bDebuggerMarkSearchFailedTrigger = false;
|
|
//
|
|
// int32 SearchPathIdx = INDEX_NONE;
|
|
// int32 NumTriggers = 0;
|
|
// bool bTriggerOnly = false;
|
|
// for (int32 i = 0; i < Data.PathFromPrevious.Num(); i++)
|
|
// {
|
|
// const FConversationDebuggerInstance::FNodeFlowData& SearchStep = Data.PathFromPrevious[i];
|
|
// const bool bMatchesNodeIndex = (Node->FirstExecutionIndex <= SearchStep.ExecutionIndex && Node->LastExecutionIndex >= SearchStep.ExecutionIndex);
|
|
// if (SearchStep.bTrigger || SearchStep.bDiscardedTrigger)
|
|
// {
|
|
// NumTriggers++;
|
|
// if (bMatchesNodeIndex)
|
|
// {
|
|
// Node->bDebuggerMarkSearchTrigger = SearchStep.bTrigger;
|
|
// Node->bDebuggerMarkSearchFailedTrigger = SearchStep.bDiscardedTrigger;
|
|
// bTriggerOnly = true;
|
|
// }
|
|
// }
|
|
// else if (bMatchesNodeIndex)
|
|
// {
|
|
// SearchPathIdx = i;
|
|
// bTriggerOnly = false;
|
|
// }
|
|
// }
|
|
//
|
|
// Node->bDebuggerMarkSearchSucceeded = (SearchPathIdx != INDEX_NONE) && Data.PathFromPrevious[SearchPathIdx].bPassed;
|
|
// Node->bDebuggerMarkSearchFailed = (SearchPathIdx != INDEX_NONE) && !Data.PathFromPrevious[SearchPathIdx].bPassed;
|
|
// Node->DebuggerSearchPathIndex = bTriggerOnly ? 0 : FMath::Max(-1, SearchPathIdx - NumTriggers);
|
|
// Node->DebuggerSearchPathSize = Data.PathFromPrevious.Num() - NumTriggers;
|
|
}
|
|
|
|
void FConversationDebugger::UpdateAssetFlags(const struct FConversationDebuggerInstance& Data, class UConversationGraphNode* Node, int32 StepIdx)
|
|
{
|
|
// if (Node == NULL)
|
|
// {
|
|
// return;
|
|
// }
|
|
//
|
|
// // special case for marking root when out of nodes
|
|
// if (Node == RootNode.Get())
|
|
// {
|
|
// const bool bIsNodeActive = (Data.ActivePath.Num() == 0) && (StepIdx >= 0);
|
|
// const bool bIsShowingCurrentState = IsShowingCurrentState();
|
|
//
|
|
// Node->bDebuggerMarkCurrentlyActive = bIsNodeActive && bIsShowingCurrentState;
|
|
// Node->bDebuggerMarkPreviouslyActive = bIsNodeActive && !bIsShowingCurrentState;
|
|
// DisplayedStepIndex = StepIdx;
|
|
// }
|
|
//
|
|
// for (int32 PinIdx = 0; PinIdx < Node->Pins.Num(); PinIdx++)
|
|
// {
|
|
// UEdGraphPin* Pin = Node->Pins[PinIdx];
|
|
// if (Pin->Direction != EGPD_Output)
|
|
// {
|
|
// continue;
|
|
// }
|
|
//
|
|
// for (int32 i = 0; i < Pin->LinkedTo.Num(); i++)
|
|
// {
|
|
// UConversationGraphNode* LinkedNode = Cast<UConversationGraphNode>(Pin->LinkedTo[i]->GetOwningNode());
|
|
// if (LinkedNode)
|
|
// {
|
|
// UBTNode* BTNode = Cast<UBTNode>(LinkedNode->NodeInstance);
|
|
// if (BTNode)
|
|
// {
|
|
// SetNodeFlags(Data, LinkedNode, BTNode);
|
|
// SetNodeRuntimeDescription(Data.RuntimeDesc, LinkedNode, BTNode);
|
|
// }
|
|
//
|
|
// for (int32 iAux = 0; iAux < LinkedNode->Decorators.Num(); iAux++)
|
|
// {
|
|
// UConversationGraphNode_Decorator* DecoratorNode = Cast<UConversationGraphNode_Decorator>(LinkedNode->Decorators[iAux]);
|
|
// UBTAuxiliaryNode* AuxNode = DecoratorNode ? Cast<UBTAuxiliaryNode>(DecoratorNode->NodeInstance) : NULL;
|
|
// if (AuxNode)
|
|
// {
|
|
// SetNodeFlags(Data, DecoratorNode, AuxNode);
|
|
// SetNodeRuntimeDescription(Data.RuntimeDesc, DecoratorNode, AuxNode);
|
|
//
|
|
// // pass restart trigger to parent graph node for drawing
|
|
// LinkedNode->bDebuggerMarkSearchTrigger |= DecoratorNode->bDebuggerMarkSearchTrigger;
|
|
// LinkedNode->bDebuggerMarkSearchFailedTrigger |= DecoratorNode->bDebuggerMarkSearchFailedTrigger;
|
|
// }
|
|
//
|
|
// UConversationGraphNode_CompositeDecorator* CompDecoratorNode = Cast<UConversationGraphNode_CompositeDecorator>(LinkedNode->Decorators[iAux]);
|
|
// if (CompDecoratorNode)
|
|
// {
|
|
// SetCompositeDecoratorFlags(Data, CompDecoratorNode);
|
|
// SetCompositeDecoratorRuntimeDescription(Data.RuntimeDesc, CompDecoratorNode);
|
|
//
|
|
// // pass restart trigger to parent graph node for drawing
|
|
// LinkedNode->bDebuggerMarkSearchTrigger |= CompDecoratorNode->bDebuggerMarkSearchTrigger;
|
|
// LinkedNode->bDebuggerMarkSearchFailedTrigger |= CompDecoratorNode->bDebuggerMarkSearchFailedTrigger;
|
|
// }
|
|
// }
|
|
//
|
|
// for (int32 iAux = 0; iAux < LinkedNode->Services.Num(); iAux++)
|
|
// {
|
|
// UConversationGraphNode_Service* ServiceNode = Cast<UConversationGraphNode_Service>(LinkedNode->Services[iAux]);
|
|
// UBTAuxiliaryNode* AuxNode = ServiceNode ? Cast<UBTAuxiliaryNode>(ServiceNode->NodeInstance) : NULL;
|
|
// if (AuxNode)
|
|
// {
|
|
// SetNodeFlags(Data, ServiceNode, AuxNode);
|
|
// SetNodeRuntimeDescription(Data.RuntimeDesc, ServiceNode, AuxNode);
|
|
// }
|
|
// }
|
|
//
|
|
// UpdateAssetFlags(Data, LinkedNode, StepIdx);
|
|
// }
|
|
// }
|
|
// }
|
|
}
|
|
|
|
void FConversationDebugger::UpdateAssetRuntimeDescription(const TArray<FString>& RuntimeDescriptions, class UConversationGraphNode* Node)
|
|
{
|
|
// if (Node == NULL)
|
|
// {
|
|
// return;
|
|
// }
|
|
//
|
|
// for (int32 PinIdx = 0; PinIdx < Node->Pins.Num(); PinIdx++)
|
|
// {
|
|
// UEdGraphPin* Pin = Node->Pins[PinIdx];
|
|
// if (Pin->Direction != EGPD_Output)
|
|
// {
|
|
// continue;
|
|
// }
|
|
//
|
|
// for (int32 i = 0; i < Pin->LinkedTo.Num(); i++)
|
|
// {
|
|
// UConversationGraphNode* LinkedNode = Cast<UConversationGraphNode>(Pin->LinkedTo[i]->GetOwningNode());
|
|
// if (LinkedNode)
|
|
// {
|
|
// UBTNode* BTNode = Cast<UBTNode>(LinkedNode->NodeInstance);
|
|
// if (BTNode)
|
|
// {
|
|
// SetNodeRuntimeDescription(RuntimeDescriptions, LinkedNode, BTNode);
|
|
// }
|
|
//
|
|
// for (int32 iAux = 0; iAux < LinkedNode->Decorators.Num(); iAux++)
|
|
// {
|
|
// UConversationGraphNode_Decorator* DecoratorNode = Cast<UConversationGraphNode_Decorator>(LinkedNode->Decorators[iAux]);
|
|
// UBTAuxiliaryNode* AuxNode = DecoratorNode ? Cast<UBTAuxiliaryNode>(DecoratorNode->NodeInstance) : NULL;
|
|
// if (AuxNode)
|
|
// {
|
|
// SetNodeRuntimeDescription(RuntimeDescriptions, DecoratorNode, AuxNode);
|
|
// }
|
|
//
|
|
// UConversationGraphNode_CompositeDecorator* CompDecoratorNode = Cast<UConversationGraphNode_CompositeDecorator>(LinkedNode->Decorators[iAux]);
|
|
// if (CompDecoratorNode)
|
|
// {
|
|
// SetCompositeDecoratorRuntimeDescription(RuntimeDescriptions, CompDecoratorNode);
|
|
// }
|
|
// }
|
|
//
|
|
// for (int32 iAux = 0; iAux < LinkedNode->Services.Num(); iAux++)
|
|
// {
|
|
// UConversationGraphNode_Service* ServiceNode = Cast<UConversationGraphNode_Service>(LinkedNode->Services[iAux]);
|
|
// UBTAuxiliaryNode* AuxNode = ServiceNode ? Cast<UBTAuxiliaryNode>(ServiceNode->NodeInstance) : NULL;
|
|
// if (AuxNode)
|
|
// {
|
|
// SetNodeRuntimeDescription(RuntimeDescriptions, ServiceNode, AuxNode);
|
|
// }
|
|
// }
|
|
//
|
|
// UpdateAssetRuntimeDescription(RuntimeDescriptions, LinkedNode);
|
|
// }
|
|
// }
|
|
// }
|
|
}
|
|
|
|
// void FConversationDebugger::SetNodeRuntimeDescription(const TArray<FString>& RuntimeDescriptions, class UConversationGraphNode* Node, class UBTNode* NodeInstance)
|
|
// {
|
|
// Node->DebuggerRuntimeDescription = RuntimeDescriptions.IsValidIndex(NodeInstance->GetExecutionIndex()) ?
|
|
// RuntimeDescriptions[NodeInstance->GetExecutionIndex()] : FString();
|
|
// }
|
|
|
|
// void FConversationDebugger::SetCompositeDecoratorRuntimeDescription(const TArray<FString>& RuntimeDescriptions, class UConversationGraphNode_CompositeDecorator* Node)
|
|
// {
|
|
// Node->DebuggerRuntimeDescription.Empty();
|
|
// for (int32 i = Node->FirstExecutionIndex; i <= Node->LastExecutionIndex; i++)
|
|
// {
|
|
// if (RuntimeDescriptions.IsValidIndex(i) && RuntimeDescriptions[i].Len())
|
|
// {
|
|
// if (Node->DebuggerRuntimeDescription.Len())
|
|
// {
|
|
// Node->DebuggerRuntimeDescription.AppendChar(TEXT('\n'));
|
|
// }
|
|
//
|
|
// Node->DebuggerRuntimeDescription += FString::Printf(TEXT("[%d] %s"), i, *RuntimeDescriptions[i].Replace(TEXT("\n"), TEXT(", ")));
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
void FConversationDebugger::CollectBreakpointsFromAsset(class UConversationGraphNode* Node)
|
|
{
|
|
// if (Node == NULL)
|
|
// {
|
|
// return;
|
|
// }
|
|
//
|
|
// for (int32 PinIdx = 0; PinIdx < Node->Pins.Num(); PinIdx++)
|
|
// {
|
|
// UEdGraphPin* Pin = Node->Pins[PinIdx];
|
|
// if (Pin->Direction != EGPD_Output)
|
|
// {
|
|
// continue;
|
|
// }
|
|
//
|
|
// for (int32 i = 0; i < Pin->LinkedTo.Num(); i++)
|
|
// {
|
|
// UConversationGraphNode* LinkedNode = Cast<UConversationGraphNode>(Pin->LinkedTo[i]->GetOwningNode());
|
|
// if (LinkedNode)
|
|
// {
|
|
// UBTNode* BTNode = Cast<UBTNode>(LinkedNode->NodeInstance);
|
|
// if (BTNode && LinkedNode->bHasBreakpoint && LinkedNode->bIsBreakpointEnabled)
|
|
// {
|
|
// ActiveBreakpoints.Add(BTNode->GetExecutionIndex());
|
|
// }
|
|
//
|
|
// CollectBreakpointsFromAsset(LinkedNode);
|
|
// }
|
|
// }
|
|
// }
|
|
}
|
|
|
|
// int32 FConversationDebugger::FindMatchingDebuggerStack(UConversationComponent& TestInstance) const
|
|
// {
|
|
// #if USE_CONVERSATION_DEBUGGER
|
|
// if (TestInstance.DebuggerSteps.Num())
|
|
// {
|
|
// const FConversationExecutionStep& StepInfo = TestInstance.DebuggerSteps.Last();
|
|
// for (int32 i = 0; i < StepInfo.InstanceStack.Num(); i++)
|
|
// {
|
|
// if (StepInfo.InstanceStack[i].TreeAsset == TreeAsset)
|
|
// {
|
|
// return i;
|
|
// }
|
|
// }
|
|
// }
|
|
// #endif
|
|
//
|
|
// return INDEX_NONE;
|
|
// }
|
|
//
|
|
// UConversationComponent* FConversationDebugger::FindInstanceInActor(AActor* TestActor)
|
|
// {
|
|
// UConversationComponent* FoundInstance = NULL;
|
|
// if (TestActor)
|
|
// {
|
|
// APawn* TestPawn = Cast<APawn>(TestActor);
|
|
// if (TestPawn && TestPawn->GetController())
|
|
// {
|
|
// FoundInstance = TestPawn->GetController()->FindComponentByClass<UConversationComponent>();
|
|
// }
|
|
//
|
|
// if (FoundInstance == NULL)
|
|
// {
|
|
// FoundInstance = TestActor->FindComponentByClass<UConversationComponent>();
|
|
// }
|
|
// }
|
|
//
|
|
// return FoundInstance;
|
|
// }
|
|
|
|
void FConversationDebugger::FindLockedDebugActor(UWorld* World)
|
|
{
|
|
#if 0
|
|
APlayerController* LocalPC = GEngine->GetFirstLocalPlayerController(World);
|
|
if (LocalPC && LocalPC->GetHUD() && LocalPC->GetPawnOrSpectator())
|
|
{
|
|
APawn* SelectedPawn = NULL;
|
|
#if WITH_ENGINE
|
|
const UEditorEngine* EEngine = Cast<UEditorEngine>(GEngine);
|
|
for (FSelectionIterator It = EEngine->GetSelectedActorIterator(); It; ++It)
|
|
{
|
|
SelectedPawn = Cast<APawn>(*It);
|
|
if (SelectedPawn)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
#endif //WITH_ENGINE
|
|
|
|
UConversationComponent* TestInstance = FindInstanceInActor((APawn*)SelectedPawn);
|
|
if (TestInstance)
|
|
{
|
|
TreeInstance = TestInstance;
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
ActiveStepIndex = TestInstance->DebuggerSteps.Num() - 1;
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FConversationDebugger::FindMatchingTreeInstance()
|
|
{
|
|
#if 0
|
|
KnownInstances.Reset();
|
|
|
|
// Find the world for the dedicated server if any, otherwise fallback to the PIE world
|
|
UWorld* PlayWorld = nullptr;
|
|
for (const FWorldContext& PieContext : GEditor->GetWorldContexts())
|
|
{
|
|
if (PieContext.WorldType == EWorldType::PIE && PieContext.World() != nullptr)
|
|
{
|
|
if (PieContext.RunAsDedicated)
|
|
{
|
|
PlayWorld = PieContext.World();
|
|
break;
|
|
}
|
|
else if(!PlayWorld)
|
|
{
|
|
PlayWorld = PieContext.World();
|
|
// Need to continue to see if their is a dedicated server.
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PlayWorld == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UConversationComponent* MatchingComp = NULL;
|
|
for (FActorIterator It(PlayWorld); It; ++It)
|
|
{
|
|
AActor* TestActor = *It;
|
|
UConversationComponent* TestComp = TestActor ? TestActor->FindComponentByClass<UConversationComponent>() : nullptr;
|
|
|
|
if (TestComp)
|
|
{
|
|
KnownInstances.Add(TestComp);
|
|
|
|
const int32 MatchingIdx = FindMatchingDebuggerStack(*TestComp);
|
|
if (MatchingIdx != INDEX_NONE)
|
|
{
|
|
MatchingComp = TestComp;
|
|
|
|
if (TestActor->IsSelected())
|
|
{
|
|
TreeInstance = TestComp;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (MatchingComp != TreeInstance)
|
|
{
|
|
TreeInstance = MatchingComp;
|
|
UpdateDebuggerViewOnInstanceChange();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool FConversationDebugger::IsDebuggerReady() const
|
|
{
|
|
return bIsPIEActive;
|
|
}
|
|
|
|
bool FConversationDebugger::IsDebuggerRunning() const
|
|
{
|
|
// return TreeInstance.IsValid() && (ActiveStepIndex != INDEX_NONE);
|
|
return false;
|
|
}
|
|
|
|
bool FConversationDebugger::IsShowingCurrentState() const
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (TreeInstance.IsValid() && TreeInstance->DebuggerSteps.Num())
|
|
{
|
|
return (TreeInstance->DebuggerSteps.Num() - 1) == ActiveStepIndex;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
int32 FConversationDebugger::GetShownStateIndex() const
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (TreeInstance.IsValid())
|
|
{
|
|
return (TreeInstance->DebuggerSteps.Num() - 1) - ActiveStepIndex;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
void FConversationDebugger::StepForwardInto()
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
UpdateCurrentStep(ActiveStepIndex, StepForwardIntoIdx);
|
|
#endif
|
|
}
|
|
|
|
static void ForEachGameWorld(const TFunction<void(UWorld*)>& Func)
|
|
{
|
|
for (const FWorldContext& PieContext : GUnrealEd->GetWorldContexts())
|
|
{
|
|
UWorld* PlayWorld = PieContext.World();
|
|
if (PlayWorld && PlayWorld->IsGameWorld())
|
|
{
|
|
Func(PlayWorld);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool AreAllGameWorldPaused()
|
|
{
|
|
bool bPaused = true;
|
|
ForEachGameWorld([&](UWorld* World)
|
|
{
|
|
bPaused = bPaused && World->bDebugPauseExecution;
|
|
});
|
|
return bPaused;
|
|
}
|
|
|
|
bool FConversationDebugger::CanStepForwardInto() const
|
|
{
|
|
return AreAllGameWorldPaused() && (StepForwardIntoIdx != INDEX_NONE);
|
|
}
|
|
|
|
void FConversationDebugger::StepForwardOver()
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
UpdateCurrentStep(ActiveStepIndex, StepForwardOverIdx);
|
|
#endif
|
|
}
|
|
|
|
bool FConversationDebugger::CanStepForwardOver() const
|
|
{
|
|
return AreAllGameWorldPaused() && (StepForwardOverIdx != INDEX_NONE);
|
|
}
|
|
|
|
void FConversationDebugger::StepOut()
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
UpdateCurrentStep(ActiveStepIndex, StepOutIdx);
|
|
#endif
|
|
}
|
|
|
|
bool FConversationDebugger::CanStepOut() const
|
|
{
|
|
return AreAllGameWorldPaused() && (StepOutIdx != INDEX_NONE);
|
|
}
|
|
|
|
void FConversationDebugger::StepBackInto()
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
UpdateCurrentStep(ActiveStepIndex, StepBackIntoIdx);
|
|
#endif
|
|
}
|
|
|
|
bool FConversationDebugger::CanStepBackInto() const
|
|
{
|
|
return AreAllGameWorldPaused() && (StepBackIntoIdx != INDEX_NONE);
|
|
}
|
|
|
|
void FConversationDebugger::StepBackOver()
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
UpdateCurrentStep(ActiveStepIndex, StepBackOverIdx);
|
|
#endif
|
|
}
|
|
|
|
bool FConversationDebugger::CanStepBackOver() const
|
|
{
|
|
return AreAllGameWorldPaused() && (StepBackOverIdx != INDEX_NONE);
|
|
}
|
|
|
|
void FConversationDebugger::UpdateCurrentStep(int32 PrevStepIdx, int32 NewStepIdx)
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (TreeInstance.IsValid() && TreeInstance->DebuggerSteps.IsValidIndex(NewStepIdx))
|
|
{
|
|
const int32 CurInstaceIdx = FindActiveInstanceIdx(PrevStepIdx);
|
|
const int32 NewInstanceIdx = FindActiveInstanceIdx(NewStepIdx);
|
|
|
|
const FConversationExecutionStep& CurStepInfo = TreeInstance->DebuggerSteps[PrevStepIdx];
|
|
const FConversationExecutionStep& NewStepInfo = TreeInstance->DebuggerSteps[NewStepIdx];
|
|
|
|
ActiveStepIndex = NewStepIdx;
|
|
|
|
if (NewInstanceIdx != INDEX_NONE && NewStepInfo.InstanceStack[NewInstanceIdx].TreeAsset != TreeAsset)
|
|
{
|
|
if (CurInstaceIdx != NewInstanceIdx ||
|
|
CurStepInfo.InstanceStack[CurInstaceIdx].TreeAsset != NewStepInfo.InstanceStack[NewInstanceIdx].TreeAsset)
|
|
{
|
|
if(EditorOwner.IsValid())
|
|
{
|
|
EditorOwner.Pin()->DebuggerSwitchAsset(NewStepInfo.InstanceStack[NewInstanceIdx].TreeAsset);
|
|
}
|
|
UpdateCurrentSubtree();
|
|
}
|
|
}
|
|
|
|
if (NewStepInfo.InstanceStack.IsValidIndex(DebuggerInstanceIndex))
|
|
{
|
|
const FConversationDebuggerInstance& ShowInstance = NewStepInfo.InstanceStack[DebuggerInstanceIndex];
|
|
UpdateAssetFlags(ShowInstance, RootNode.Get(), ActiveStepIndex);
|
|
}
|
|
else
|
|
{
|
|
ActiveStepIndex = INDEX_NONE;
|
|
|
|
FConversationDebuggerInstance EmptyData;
|
|
UpdateAssetFlags(EmptyData, RootNode.Get(), INDEX_NONE);
|
|
}
|
|
|
|
UpdateDebuggerViewOnStepChange();
|
|
UpdateAvailableActions();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool FConversationDebugger::HasContinuousNextStep() const
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (TreeInstance.IsValid() && TreeInstance->DebuggerSteps.IsValidIndex(ActiveStepIndex + 1))
|
|
{
|
|
const FConversationExecutionStep& NextStepInfo = TreeInstance->DebuggerSteps[ActiveStepIndex + 1];
|
|
const FConversationExecutionStep& CurStepInfo = TreeInstance->DebuggerSteps[ActiveStepIndex];
|
|
|
|
if (CurStepInfo.InstanceStack.IsValidIndex(DebuggerInstanceIndex) &&
|
|
CurStepInfo.InstanceStack.Num() == NextStepInfo.InstanceStack.Num() &&
|
|
CurStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset == NextStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool FConversationDebugger::HasContinuousPrevStep() const
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (TreeInstance.IsValid() && TreeInstance->DebuggerSteps.IsValidIndex(ActiveStepIndex - 1))
|
|
{
|
|
const FConversationExecutionStep& PrevStepInfo = TreeInstance->DebuggerSteps[ActiveStepIndex - 1];
|
|
const FConversationExecutionStep& CurStepInfo = TreeInstance->DebuggerSteps[ActiveStepIndex];
|
|
|
|
if (CurStepInfo.InstanceStack.IsValidIndex(DebuggerInstanceIndex) &&
|
|
CurStepInfo.InstanceStack.Num() == PrevStepInfo.InstanceStack.Num() &&
|
|
CurStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset == PrevStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
void FConversationDebugger::OnActiveNodeChanged(const TArray<uint16>& ActivePath, const TArray<uint16>& PrevStepPath)
|
|
{
|
|
bool bShouldPause = false;
|
|
StoppedOnBreakpointExecutionIndex = MAX_uint16;
|
|
|
|
// breakpoints: check only nodes, that have changed from previous state
|
|
// (e.g. breakpoint on sequence, it would break multiple times for every child
|
|
// but we want only once: when it becomes active)
|
|
|
|
for (int32 i = 0; i < ActivePath.Num(); i++)
|
|
{
|
|
const uint16 TestExecutionIndex = ActivePath[i];
|
|
if (!PrevStepPath.Contains(TestExecutionIndex))
|
|
{
|
|
if (ActiveBreakpoints.Contains(TestExecutionIndex))
|
|
{
|
|
bShouldPause = true;
|
|
StoppedOnBreakpointExecutionIndex = TestExecutionIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bShouldPause)
|
|
{
|
|
if (EditorOwner.IsValid())
|
|
{
|
|
EditorOwner.Pin()->FocusWindow(TreeAsset);
|
|
}
|
|
|
|
PausePlaySession();
|
|
}
|
|
}
|
|
|
|
void FConversationDebugger::StopPlaySession()
|
|
{
|
|
if (GUnrealEd->PlayWorld)
|
|
{
|
|
GEditor->RequestEndPlayMap();
|
|
|
|
// @TODO: we need a unified flow to leave debugging mode from the different debuggers to prevent strong coupling between modules.
|
|
// Each debugger (Blueprint & BehaviorTree for now) could then take the appropriate actions to resume the session.
|
|
if (FSlateApplication::Get().InKismetDebuggingMode())
|
|
{
|
|
FSlateApplication::Get().LeaveDebuggingMode();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FConversationDebugger::PausePlaySession()
|
|
{
|
|
if (GUnrealEd->SetPIEWorldsPaused(true))
|
|
{
|
|
GUnrealEd->PlaySessionPaused();
|
|
}
|
|
}
|
|
|
|
void FConversationDebugger::ResumePlaySession()
|
|
{
|
|
if (GUnrealEd->SetPIEWorldsPaused(false))
|
|
{
|
|
// @TODO: we need a unified flow to leave debugging mode from the different debuggers to prevent strong coupling between modules.
|
|
// Each debugger (Blueprint & BehaviorTree for now) could then take the appropriate actions to resume the session.
|
|
if (FSlateApplication::Get().InKismetDebuggingMode())
|
|
{
|
|
FSlateApplication::Get().LeaveDebuggingMode();
|
|
}
|
|
|
|
GUnrealEd->PlaySessionResumed();
|
|
}
|
|
}
|
|
|
|
bool FConversationDebugger::IsPlaySessionPaused()
|
|
{
|
|
return AreAllGameWorldPaused();
|
|
}
|
|
|
|
bool FConversationDebugger::IsPlaySessionRunning()
|
|
{
|
|
return !AreAllGameWorldPaused();
|
|
}
|
|
|
|
bool FConversationDebugger::IsPIESimulating()
|
|
{
|
|
return GEditor->bIsSimulatingInEditor || GEditor->PlayWorld;
|
|
}
|
|
|
|
bool FConversationDebugger::IsPIENotSimulating()
|
|
{
|
|
return !GEditor->bIsSimulatingInEditor && (GEditor->PlayWorld == NULL);
|
|
}
|
|
|
|
void FConversationDebugger::OnBreakpointAdded(class UConversationGraphNode* Node)
|
|
{
|
|
// if (IsDebuggerReady())
|
|
// {
|
|
// UBTNode* BTNode = Cast<UBTNode>(Node->NodeInstance);
|
|
// if (BTNode)
|
|
// {
|
|
// ActiveBreakpoints.AddUnique(BTNode->GetExecutionIndex());
|
|
// }
|
|
// }
|
|
}
|
|
|
|
void FConversationDebugger::OnBreakpointRemoved(class UConversationGraphNode* Node)
|
|
{
|
|
// if (IsDebuggerReady())
|
|
// {
|
|
// UBTNode* BTNode = Cast<UBTNode>(Node->NodeInstance);
|
|
// if (BTNode)
|
|
// {
|
|
// ActiveBreakpoints.RemoveSingleSwap(BTNode->GetExecutionIndex());
|
|
// }
|
|
// }
|
|
}
|
|
|
|
void FConversationDebugger::UpdateDebuggerViewOnInstanceChange()
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
UBlackboardData* BBAsset = EditorOwner.IsValid() ? EditorOwner.Pin()->GetBlackboardData() : nullptr;
|
|
if (TreeInstance.IsValid() &&
|
|
TreeInstance->DebuggerSteps.IsValidIndex(ActiveStepIndex) &&
|
|
TreeInstance->DebuggerSteps[ActiveStepIndex].InstanceStack.IsValidIndex(DebuggerInstanceIndex))
|
|
{
|
|
const FConversationDebuggerInstance& ShowInstance = TreeInstance->DebuggerSteps[ActiveStepIndex].InstanceStack[DebuggerInstanceIndex];
|
|
if (ShowInstance.TreeAsset)
|
|
{
|
|
BBAsset = ShowInstance.TreeAsset->BlackboardAsset;
|
|
}
|
|
}
|
|
|
|
OnDebuggedBlackboardChangedEvent.Broadcast(BBAsset);
|
|
|
|
if (DebuggerInstanceIndex != INDEX_NONE)
|
|
{
|
|
Refresh();
|
|
}
|
|
else
|
|
{
|
|
ClearDebuggerState(/*bKeepSubtreeData=*/true);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FConversationDebugger::UpdateDebuggerViewOnStepChange()
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (IsDebuggerRunning() &&
|
|
TreeInstance.IsValid() &&
|
|
TreeInstance->DebuggerSteps.IsValidIndex(ActiveStepIndex))
|
|
{
|
|
const FConversationExecutionStep& ShowStep = TreeInstance->DebuggerSteps[ActiveStepIndex];
|
|
|
|
SavedTimestamp = ShowStep.TimeStamp;
|
|
SavedValues = ShowStep.BlackboardValues;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FConversationDebugger::UpdateDebuggerViewOnTick()
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (IsDebuggerRunning() && TreeInstance.IsValid())
|
|
{
|
|
const float GameTime = GEditor && GEditor->PlayWorld ? GEditor->PlayWorld->GetTimeSeconds() : 0.0f;
|
|
CurrentTimestamp = GameTime;
|
|
|
|
TreeInstance->StoreDebuggerBlackboard(CurrentValues);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
FText FConversationDebugger::FindValueForKey(const FName& InKeyName, bool bUseCurrentState) const
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (IsDebuggerRunning() &&
|
|
TreeInstance.IsValid())
|
|
{
|
|
const TMap<FName, FString>* MapToQuery = nullptr;
|
|
if(bUseCurrentState)
|
|
{
|
|
MapToQuery = &CurrentValues;
|
|
}
|
|
else if(TreeInstance->DebuggerSteps.IsValidIndex(ActiveStepIndex))
|
|
{
|
|
MapToQuery = &TreeInstance->DebuggerSteps[ActiveStepIndex].BlackboardValues;
|
|
}
|
|
|
|
if(MapToQuery != nullptr)
|
|
{
|
|
const FString* FindValue = MapToQuery->Find(InKeyName);
|
|
if(FindValue != nullptr)
|
|
{
|
|
return FText::FromString(*FindValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FText();
|
|
#endif
|
|
return FText();
|
|
}
|
|
|
|
float FConversationDebugger::GetTimeStamp(bool bUseCurrentState) const
|
|
{
|
|
return bUseCurrentState ? CurrentTimestamp : SavedTimestamp;
|
|
}
|
|
|
|
FString FConversationDebugger::GetDebuggedInstanceDesc() const
|
|
{
|
|
// UConversationComponent* BTComponent = TreeInstance.Get();
|
|
// return BTComponent ? DescribeInstance(*BTComponent) :
|
|
return NSLOCTEXT("BlueprintEditor", "DebugActorNothingSelected", "No debug object selected").ToString();
|
|
}
|
|
|
|
// FString FConversationDebugger::DescribeInstance(UConversationComponent& InstanceToDescribe) const
|
|
// {
|
|
// FString ActorDesc;
|
|
// if (InstanceToDescribe.GetOwner())
|
|
// {
|
|
// AController* TestController = Cast<AController>(InstanceToDescribe.GetOwner());
|
|
// ActorDesc = TestController ?
|
|
// TestController->GetName() :
|
|
// InstanceToDescribe.GetOwner()->GetActorLabel();
|
|
// }
|
|
//
|
|
// return ActorDesc;
|
|
// }
|
|
|
|
// void FConversationDebugger::OnInstanceSelectedInDropdown(UConversationComponent* SelectedInstance)
|
|
// {
|
|
// if (SelectedInstance)
|
|
// {
|
|
// ClearDebuggerState();
|
|
//
|
|
// AController* OldController = TreeInstance.IsValid() ? Cast<AController>(TreeInstance->GetOwner()) : NULL;
|
|
// APawn* OldPawn = OldController != NULL ? OldController->GetPawn() : NULL;
|
|
// USelection* SelectedActors = GEditor ? GEditor->GetSelectedActors() : NULL;
|
|
// if (SelectedActors)
|
|
// {
|
|
// SelectedActors->DeselectAll();
|
|
// }
|
|
//
|
|
// TreeInstance = SelectedInstance;
|
|
//
|
|
// if (SelectedActors && SelectedInstance && SelectedInstance->GetOwner())
|
|
// {
|
|
// AController* TestController = Cast<AController>(SelectedInstance->GetOwner());
|
|
// APawn* Pawn = TestController != NULL ? TestController->GetPawn() : NULL;
|
|
// if (Pawn)
|
|
// {
|
|
// SelectedActors->Select(Pawn);
|
|
// }
|
|
// }
|
|
//
|
|
// Refresh();
|
|
// }
|
|
// }
|
|
//
|
|
// void FConversationDebugger::GetMatchingInstances(TArray<UConversationComponent*>& MatchingInstances)
|
|
// {
|
|
// for (int32 i = KnownInstances.Num() - 1; i >= 0; i--)
|
|
// {
|
|
// UConversationComponent* TestInstance = KnownInstances[i].Get();
|
|
// if (TestInstance == NULL)
|
|
// {
|
|
// KnownInstances.RemoveAt(i);
|
|
// continue;
|
|
// }
|
|
//
|
|
// const int32 StackIdx = FindMatchingDebuggerStack(*TestInstance);
|
|
// if (StackIdx != INDEX_NONE)
|
|
// {
|
|
// MatchingInstances.Add(TestInstance);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
void FConversationDebugger::InitializeFromParent(class FConversationDebugger* ParentDebugger)
|
|
{
|
|
ClearDebuggerState();
|
|
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
TreeInstance = ParentDebugger->TreeInstance;
|
|
ActiveStepIndex = ParentDebugger->ActiveStepIndex;
|
|
|
|
UpdateDebuggerInstance();
|
|
UpdateAvailableActions();
|
|
|
|
if (TreeInstance.IsValid() &&
|
|
TreeInstance->DebuggerSteps.IsValidIndex(ActiveStepIndex) &&
|
|
TreeInstance->DebuggerSteps[ActiveStepIndex].InstanceStack.IsValidIndex(DebuggerInstanceIndex))
|
|
{
|
|
const FConversationDebuggerInstance& ShowInstance = TreeInstance->DebuggerSteps[ActiveStepIndex].InstanceStack[DebuggerInstanceIndex];
|
|
UpdateAssetFlags(ShowInstance, RootNode.Get(), ActiveStepIndex);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int32 FConversationDebugger::FindActiveInstanceIdx(int32 StepIdx) const
|
|
{
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
const FConversationExecutionStep& StepInfo = TreeInstance->DebuggerSteps[StepIdx];
|
|
for (int32 i = StepInfo.InstanceStack.Num() - 1; i >= 0; i--)
|
|
{
|
|
if (StepInfo.InstanceStack[i].IsValid())
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return INDEX_NONE;
|
|
}
|
|
|
|
void FConversationDebugger::UpdateCurrentSubtree()
|
|
{
|
|
bIsCurrentSubtree = false;
|
|
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
if (TreeInstance->DebuggerSteps.IsValidIndex(ActiveStepIndex))
|
|
{
|
|
const FConversationExecutionStep& StepInfo = TreeInstance->DebuggerSteps[ActiveStepIndex];
|
|
|
|
// assume that top instance is always valid, so it won't take away step buttons when tree is finished as out of nodes
|
|
// current subtree = no child instances, or child instances are not valid
|
|
bIsCurrentSubtree = ((DebuggerInstanceIndex == 0) || (StepInfo.InstanceStack.IsValidIndex(DebuggerInstanceIndex) && StepInfo.InstanceStack[DebuggerInstanceIndex].IsValid())) &&
|
|
(!StepInfo.InstanceStack.IsValidIndex(DebuggerInstanceIndex + 1) || !StepInfo.InstanceStack[DebuggerInstanceIndex + 1].IsValid());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// static int32 GetNumActiveInstances(const FConversationExecutionStep& StepInfo, class UConversationDatabase*& ActiveSubtree)
|
|
// {
|
|
// for (int32 Idx = StepInfo.InstanceStack.Num() - 1; Idx >= 0; Idx--)
|
|
// {
|
|
// //if (StepInfo.InstanceStack[Idx].ActivePath.Num())
|
|
// {
|
|
// ActiveSubtree = StepInfo.InstanceStack[Idx].TreeAsset;
|
|
// return Idx + 1;
|
|
// }
|
|
// }
|
|
//
|
|
// ActiveSubtree = NULL;
|
|
// return 0;
|
|
// }
|
|
|
|
void FConversationDebugger::UpdateAvailableActions()
|
|
{
|
|
StepForwardIntoIdx = INDEX_NONE;
|
|
StepForwardOverIdx = INDEX_NONE;
|
|
StepBackIntoIdx = INDEX_NONE;
|
|
StepBackOverIdx = INDEX_NONE;
|
|
StepOutIdx = INDEX_NONE;
|
|
|
|
#if USE_CONVERSATION_DEBUGGER
|
|
UConversationComponent* TreeInstancePtr = TreeInstance.Get();
|
|
if (TreeInstancePtr && TreeInstancePtr->DebuggerSteps.IsValidIndex(ActiveStepIndex) && DebuggerInstanceIndex >= 0)
|
|
{
|
|
const FConversationExecutionStep& CurStepInfo = TreeInstancePtr->DebuggerSteps[ActiveStepIndex];
|
|
|
|
if (TreeInstancePtr->DebuggerSteps.IsValidIndex(ActiveStepIndex - 1))
|
|
{
|
|
StepBackIntoIdx = ActiveStepIndex - 1;
|
|
}
|
|
|
|
if (TreeInstancePtr->DebuggerSteps.IsValidIndex(ActiveStepIndex + 1))
|
|
{
|
|
StepForwardIntoIdx = ActiveStepIndex + 1;
|
|
}
|
|
|
|
UConversationDatabase* CurTree = CurStepInfo.InstanceStack.IsValidIndex(DebuggerInstanceIndex) ?
|
|
CurStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset : NULL;
|
|
const int32 CurStepInstances = DebuggerInstanceIndex + 1;
|
|
|
|
for (int32 TestStepIndex = ActiveStepIndex - 1; TestStepIndex >= 0; TestStepIndex--)
|
|
{
|
|
const FConversationExecutionStep& TestStepInfo = TreeInstancePtr->DebuggerSteps[TestStepIndex];
|
|
UConversationDatabase* TestTree = NULL;
|
|
const int32 TestStepInstances = GetNumActiveInstances(TestStepInfo, TestTree);
|
|
|
|
StepBackOverIdx = TestStepIndex;
|
|
|
|
// keep going only if the execution is moving to a sub-tree
|
|
if (TestStepInstances <= CurStepInstances ||
|
|
TestStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset != CurStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int32 TestStepIndex = ActiveStepIndex + 1; TestStepIndex < TreeInstancePtr->DebuggerSteps.Num(); TestStepIndex++)
|
|
{
|
|
const FConversationExecutionStep& TestStepInfo = TreeInstancePtr->DebuggerSteps[TestStepIndex];
|
|
UConversationDatabase* TestTree = NULL;
|
|
int32 TestStepInstances = GetNumActiveInstances(TestStepInfo, TestTree);
|
|
|
|
StepForwardOverIdx = TestStepIndex;
|
|
|
|
// keep going only if the execution is moving to a sub-tree
|
|
if (TestStepInstances <= CurStepInstances ||
|
|
TestStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset != CurStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CurStepInfo.InstanceStack.IsValidIndex(DebuggerInstanceIndex) && CurStepInfo.InstanceStack[DebuggerInstanceIndex].ActivePath.Num())
|
|
{
|
|
for (int32 TestStepIndex = ActiveStepIndex + 1; TestStepIndex < TreeInstancePtr->DebuggerSteps.Num(); TestStepIndex++)
|
|
{
|
|
const FConversationExecutionStep& TestStepInfo = TreeInstancePtr->DebuggerSteps[TestStepIndex];
|
|
UConversationDatabase* TestTree = NULL;
|
|
int32 TestStepInstances = GetNumActiveInstances(TestStepInfo, TestTree);
|
|
|
|
if (TestStepInstances < CurStepInstances ||
|
|
TestStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset != CurStepInfo.InstanceStack[DebuggerInstanceIndex].TreeAsset)
|
|
{
|
|
// execution left current subtree
|
|
StepOutIdx = TestStepIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|