diff --git a/Source/ComboInput/Private/Components/ComboManagerComponent.cpp b/Source/ComboInput/Private/Components/ComboManagerComponent.cpp index 2a3afd4..6dcd2b6 100644 --- a/Source/ComboInput/Private/Components/ComboManagerComponent.cpp +++ b/Source/ComboInput/Private/Components/ComboManagerComponent.cpp @@ -2,21 +2,31 @@ #include "Components/ComboManagerComponent.h" +#include "ComboActionGraph.h" #include "ComboInputAssets.h" #include "Components/InputBufferComponent.h" #include "Engine/LocalPlayer.h" #include "GameFramework/Character.h" #include "Kismet/GameplayStatics.h" +#include "Nodes/ComboActionGraphNode_ActionNode.h" DEFINE_LOG_CATEGORY(LogComboManagerComponent); UComboManagerComponent::UComboManagerComponent() { - PrimaryComponentTick.bStartWithTickEnabled = false; - PrimaryComponentTick.bTickEvenWhenPaused = false; - PrimaryComponentTick.bCanEverTick = false; + this->PrimaryComponentTick.bStartWithTickEnabled = false; + this->PrimaryComponentTick.bTickEvenWhenPaused = false; + this->PrimaryComponentTick.bCanEverTick = false; +} + +void UComboManagerComponent::BeginPlay() +{ + Super::BeginPlay(); + + checkf(this->ComboGraph, TEXT("No combo graph is set for %s in actor %s"), *UComboManagerComponent::StaticClass()->GetName(), *this->GetOwner()->GetName()); + this->ActiveNode = this->ComboGraph->StartNode; } @@ -59,23 +69,29 @@ void UComboManagerComponent::ActivateComboAction(const UComboInputAsset *Input) return; } - const TObjectPtr CurrentNode = (this->ActiveNode ? this->ActiveNode : this->DefaultStartNode); - checkf(CurrentNode, TEXT("No combo sequence nodes available.")); - - const FComboSequenceAction *ActionData = CurrentNode->ComboBranch.Find(Input); - // If this node has an action we can activate, then activate it. - if (ActionData && ActionData->ComboAction) + // Find the next node in the graph. + const UComboAction *ComboAction = nullptr; + const UComboActionGraphNode *NextNode = this->FindActiveNodeData(this->ActiveNode, Input, EComboActionTriggerEvent::Activated, ComboAction); + if (!NextNode) { - this->BeginNodeTransition(ActionData->NextNode); - this->BroadcastDelegates(ActionData->ComboAction, EComboActionTriggerEvent::Activated); - UE_LOG(LogComboManagerComponent, Verbose, TEXT("%s activated"), *ActionData->ComboAction->ActionName.ToString()); + // If there is no next active node, then return to the start node. + NextNode = this->FindActiveNodeData(this->ComboGraph->StartNode, Input, EComboActionTriggerEvent::Activated, ComboAction); + } + checkf(NextNode, TEXT("No combo sequence nodes available.")); + + // If this node has an action we can activate, then activate it. + if (ComboAction) + { + this->BeginNodeTransition(NextNode); + this->BroadcastDelegates(ComboAction, EComboActionTriggerEvent::Activated); + UE_LOG(LogComboManagerComponent, Verbose, TEXT("%s activated"), *ComboAction->ActionName.ToString()); } // Otherwise, see if we have a fallback we can use. - else if (const TObjectPtr *ComboAction = this->FallbackActions.Find(Input)) + else if (const TObjectPtr *FallbackAction = this->FallbackActions.Find(Input)) { this->ResetCombo(); - this->BroadcastDelegates(*ComboAction, EComboActionTriggerEvent::Activated); - UE_LOG(LogComboManagerComponent, Verbose, TEXT("Fallback action %s activated"), *(*ComboAction)->ActionName.ToString()); + this->BroadcastDelegates(*FallbackAction, EComboActionTriggerEvent::Activated); + UE_LOG(LogComboManagerComponent, Verbose, TEXT("Fallback action %s activated"), *(*FallbackAction)->ActionName.ToString()); } // If we haven't found an action to perform, end here. else @@ -102,7 +118,7 @@ void UComboManagerComponent::ReleaseComboAction(const class UComboInputAsset *In } -void UComboManagerComponent::BeginNodeTransition(const UComboSequenceNode *NextNode) +void UComboManagerComponent::BeginNodeTransition(const UComboActionGraphNode *NextNode) { this->PreviousNode = this->ActiveNode; this->ActiveNode = NextNode; @@ -119,7 +135,28 @@ void UComboManagerComponent::FinishTransition() void UComboManagerComponent::ResetCombo() { this->GetWorld()->GetTimerManager().ClearTimer(this->DEBUG__ResetComboTimer); - this->ActiveNode = this->PreviousNode = nullptr; + this->PreviousNode = this->ActiveNode; + this->ActiveNode = this->ComboGraph->StartNode; +} + + +const UComboActionGraphNode *UComboManagerComponent::FindActiveNodeData(const UComboActionGraphNode *CurrentNode, const UComboInputAsset *Input, const EComboActionTriggerEvent TriggerEvent, const UComboAction *&ComboAction) +{ + // Find a node that matches both the combo input and the trigger action. + const UComboActionGraphNode *NextNode = nullptr; + for (const UComboActionGraphNode *GraphNode : CurrentNode->ChildrenNodes) + { + if (const UComboActionGraphNode_ActionNode *ActionNode = Cast(GraphNode)) + { + if (ActionNode->GetComboInput() == Input && ActionNode->GetTriggerEvent() == EComboActionTriggerEvent::Activated) + { + ComboAction = ActionNode->GetComboAction(); + NextNode = ActionNode; + break; + } + } + } + return NextNode; } diff --git a/Source/ComboInput/Public/ComboInputAssets.h b/Source/ComboInput/Public/ComboInputAssets.h index 8dbe19f..bf779a4 100644 --- a/Source/ComboInput/Public/ComboInputAssets.h +++ b/Source/ComboInput/Public/ComboInputAssets.h @@ -9,24 +9,6 @@ #include "ComboInputAssets.generated.h" -/** - * Struct that is used as the value for a combo branch. ComboAction is the action to be - * executed, and NextNode is the node that will be activated next in the sequence. - */ -USTRUCT(BlueprintType) -struct COMBOINPUT_API FComboSequenceAction -{ - GENERATED_BODY() -public: - // Action to perform when the associated combo sequence node is activated. - UPROPERTY(BlueprintReadOnly, EditAnywhere) - TObjectPtr ComboAction; - - // Sequence node to switch to once this action is complete. - UPROPERTY(BlueprintReadOnly, EditAnywhere) - TObjectPtr NextNode; -}; - /** * An action that can be executed as part of a combo sequence. This is essentially a * representation of an attack, and can be sent to the Animation Graph to play an @@ -43,21 +25,6 @@ public: FName ActionName; }; -/** - * This represents a node in the combo graph, with each key in the ComboBranch being - * an input this node can react to, and each value containing the action to be executed - * next, and the node to activate after the action is complete. - */ -UCLASS(BlueprintType) -class COMBOINPUT_API UComboSequenceNode : public UDataAsset -{ - GENERATED_BODY() - -public: - UPROPERTY(BlueprintReadOnly, EditAnywhere) - TMap ComboBranch; -}; - /** * This maps a sequence of button inputs from EnhancedInput to a combo action that can * be used to execute a sequence of moves. This gets sent from the input buffer subsystem diff --git a/Source/ComboInput/Public/Components/ComboManagerComponent.h b/Source/ComboInput/Public/Components/ComboManagerComponent.h index cc5cf30..b939698 100644 --- a/Source/ComboInput/Public/Components/ComboManagerComponent.h +++ b/Source/ComboInput/Public/Components/ComboManagerComponent.h @@ -43,6 +43,8 @@ class COMBOINPUT_API UComboManagerComponent : public UActorComponent public: UComboManagerComponent(); + virtual void BeginPlay() override; + UFUNCTION(BlueprintCallable) void HandleComboInput(const class UComboInputAsset *Input, const EComboActionTriggerEvent &TriggerEvent); @@ -56,7 +58,7 @@ public: protected: UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) - TObjectPtr DefaultStartNode; + TObjectPtr ComboGraph; // This input will be recognised as an offset input, which cancels a node transition // if activated early enough in the transition. This locks the current place in the @@ -80,16 +82,18 @@ private: void ActivateComboAction(const class UComboInputAsset *Input); void ReleaseComboAction(const class UComboInputAsset *Input); - void BeginNodeTransition(const class UComboSequenceNode *NextNode); + void BeginNodeTransition(const class UComboActionGraphNode *NextNode); void FinishTransition(); void ResetCombo(); + const UComboActionGraphNode *FindActiveNodeData(const UComboActionGraphNode *CurrentNode, const UComboInputAsset *Input, const EComboActionTriggerEvent TriggerEvent, const UComboAction *&ComboAction); + void BroadcastDelegates(const class UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent); void DEBUG__UnlockAction(TObjectPtr Unlock); - TObjectPtr ActiveNode = nullptr; - TObjectPtr PreviousNode = nullptr; + TObjectPtr ActiveNode = nullptr; + TObjectPtr PreviousNode = nullptr; TObjectPtr LastComboAction = nullptr; diff --git a/Source/ComboInputEditor/Private/ComboInputEditor.cpp b/Source/ComboInputEditor/Private/ComboInputEditor.cpp index 5191445..8e00f42 100644 --- a/Source/ComboInputEditor/Private/ComboInputEditor.cpp +++ b/Source/ComboInputEditor/Private/ComboInputEditor.cpp @@ -36,16 +36,6 @@ public: virtual UClass *GetSupportedClass() const override { return UComboAction::StaticClass(); } }; -class FAssetTypeActions_ComboSequenceNode : public FAssetTypeActions_DataAsset -{ -public: - virtual FText GetName() const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_ComboSequenceNode", "Combo Sequence Node"); } - virtual uint32 GetCategories() override { return FComboInputEditorModule::GetInputAssetsCategory(); } - virtual FColor GetTypeColor() const override { return FColor(255, 127, 255); } - virtual FText GetAssetDescription(const FAssetData &AssetData) const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_ComboSequenceNodeDesc", "This represents a node in the combo graph, with each key in the ComboBranch being an input this node can react to, and each value containing the action to be executed next, and the node to activate after the action is complete."); } - virtual UClass *GetSupportedClass() const override { return UComboSequenceNode::StaticClass(); } -}; - class FAssetTypeActions_ComboInputAsset : public FAssetTypeActions_DataAsset { public: @@ -120,28 +110,6 @@ UObject *UComboAction_Factory::FactoryCreateNew(UClass *Class, UObject *InParent } -UComboSequenceNode_Factory::UComboSequenceNode_Factory(const FObjectInitializer &ObjectInitializer) - : Super(ObjectInitializer) -{ - this->SupportedClass = UComboSequenceNode::StaticClass(); - this->bEditAfterNew = true; - this->bCreateNew = true; -} - -UObject *UComboSequenceNode_Factory::FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) -{ - if (this->ComboSequenceNodeClass != nullptr) - { - return NewObject(InParent, this->ComboSequenceNodeClass, Name, Flags | EObjectFlags::RF_Transactional, Context); - } - else - { - check(Class->IsChildOf(UComboSequenceNode::StaticClass())); - return NewObject(InParent, Class, Name, Flags | EObjectFlags::RF_Transactional, Context); - } -} - - UComboInputAsset_Factory::UComboInputAsset_Factory(const FObjectInitializer &ObjectInitializer) : Super(ObjectInitializer) { @@ -315,7 +283,6 @@ void FComboInputEditorModule::StartupModule() IAssetTools &AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); FComboInputEditorModule::ComboAssetsCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Combo Input")), LOCTEXT("ComboInputAssetsCategory", "Combo Input")); this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboAction)); - this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboSequenceNode)); this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboInputAsset)); // Make a new style set for Combo Input, which will register any custom icons for the types in this plugin diff --git a/Source/ComboInputEditor/Public/ComboInputEditor.h b/Source/ComboInputEditor/Public/ComboInputEditor.h index 80d0f41..3876702 100644 --- a/Source/ComboInputEditor/Public/ComboInputEditor.h +++ b/Source/ComboInputEditor/Public/ComboInputEditor.h @@ -27,20 +27,6 @@ public: virtual UObject *FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) override; }; -UCLASS() -class COMBOINPUTEDITOR_API UComboSequenceNode_Factory : public UFactory -{ - GENERATED_BODY() - -public: - UComboSequenceNode_Factory(const class FObjectInitializer &ObjectInitializer); - - UPROPERTY(EditAnywhere, Category="Combo Input") - TSubclassOf ComboSequenceNodeClass; - - virtual UObject *FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) override; -}; - UCLASS() class COMBOINPUTEDITOR_API UComboInputAsset_Factory : public UFactory {