- Started work on giving UComboActions their own automatically-recognised events in Blueprints, just like UInputAssets have in the EnhancedInput plugin.

- Also gave the data assets a factory class and organised them into the Input category to make them look like a proper Unreal asset.
This commit is contained in:
Jamie Greunbaum 2023-09-12 01:39:23 -04:00
parent 3e71bb31ae
commit 445895b77b
10 changed files with 789 additions and 88 deletions

View File

@ -32,13 +32,7 @@ public class ComboInput : ModuleRules
( (
new string[] new string[]
{ {
"Core",
"CoreUObject",
"Engine",
"UMG",
"EnhancedInput", "EnhancedInput",
// ... add other public dependencies that you statically link with here ...
} }
); );
@ -47,14 +41,15 @@ public class ComboInput : ModuleRules
( (
new string[] new string[]
{ {
"Core",
"CoreUObject",
"Engine", "Engine",
"Slate", "Slate",
"SlateCore", "SlateCore",
"GameplayTags",
"DeveloperSettings", "DeveloperSettings",
"UMG",
"Projects" "BlueprintGraph",
// ... add private dependencies that you statically link with here ... "GraphEditor",
} }
); );

View File

@ -0,0 +1,16 @@
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#include "Events/ComboActionDelegateBinding.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(ComboActionDelegateBinding)
UComboActionDelegateBinding::UComboActionDelegateBinding(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UComboActionDelegateBinding::BindToComboManagerComponent(UComboManagerComponent *ComboManager, UObject *ObjectToBindTo) const
{
}

View File

@ -0,0 +1,423 @@
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#include "Events/K2Node_ComboAction.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintEditorSettings.h"
#include "BlueprintNodeSpawner.h"
#include "ComboInputAssets.h"
#include "EdGraphSchema_K2_Actions.h"
#include "Editor.h"
#include "EditorCategoryUtils.h"
#include "GraphEditorSettings.h"
#include "K2Node_AssignmentStatement.h"
#include "K2Node_ComboActionEvent.h"
#include "K2Node_TemporaryVariable.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "KismetCompiler.h"
#include "Misc/PackageName.h"
#include "Subsystems/AssetEditorSubsystem.h"
#include "Modules/ModuleManager.h"
#include "Styling/AppStyle.h"
#define ACTIVATED_PIN_NAME TEXT("Activated")
#define COMBO_ACTION_PIN_NAME TEXT("Combo Action")
#include UE_INLINE_GENERATED_CPP_BY_NAME(K2Node_ComboAction)
#define LOCTEXT_NAMESPACE "K2Node_ComboAction"
namespace UE::Input
{
static bool bComboActionShouldWarnOnUnsupportedInputPin = false;
static FAutoConsoleVariableRef CVarShouldWarnOnUnsupportedInputPin(
TEXT("ComboAction.bp.bComboActionShouldWarnOnUnsupportedInputPin"),
bComboActionShouldWarnOnUnsupportedInputPin,
TEXT("Should the Combo Action event node throw a warning if an \"Unsupported\" pin has a connection?"),
ECVF_Default);
}
UK2Node_ComboAction::UK2Node_ComboAction(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer)
{
}
void ForEachEventPinName(TFunctionRef<void(ETriggerEvent Event, FName PinName)> PinLambda)
{
UEnum* EventEnum = StaticEnum<ETriggerEvent>();
for (int32 i = 0; i < EventEnum->NumEnums() - 1; ++i)
{
if (!EventEnum->HasMetaData(TEXT("Hidden"), i))
{
PinLambda(ETriggerEvent(EventEnum->GetValueByIndex(i)), *EventEnum->GetNameStringByIndex(i));
}
}
}
void UK2Node_ComboAction::AllocateDefaultPins()
{
this->PreloadObject((UObject*)this->ComboAction);
UEdGraphPin *TriggeredPin = this->CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, TEXT("Triggered"));
TriggeredPin->PinToolTip = TEXT("Executed when this combo action is first activated.");
// Add a special tooltip and display name for pins that are unsupported
if (UE::Input::bComboActionShouldWarnOnUnsupportedInputPin)
{
static const FText UnsuportedTooltip = LOCTEXT("UnsupportedTooltip", "\n\nThis trigger event is not supported by the action! Add a supported trigger to enable this pin.");
TriggeredPin->PinToolTip += UnsuportedTooltip.ToString();
TriggeredPin->PinFriendlyName = FText::Format(LOCTEXT("UnsupportedPinFriendlyName", "(Unsupported) {0}"), FText::FromName(TriggeredPin->GetFName()));
}
HideEventPins(nullptr);
const UEdGraphSchema_K2 *Schema = GetDefault<UEdGraphSchema_K2>();
this->AdvancedPinDisplay = ENodeAdvancedPins::Hidden;
UEdGraphPin *ActivatedValuePin = this->CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Boolean, UEdGraphSchema_K2::PC_Boolean, ACTIVATED_PIN_NAME);
ActivatedValuePin->bAdvancedView = true;
Schema->SetPinAutogeneratedDefaultValue(ActivatedValuePin, TEXT("true"));
if(this->ComboAction)
{
UEdGraphPin *ActionPin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Object, this->ComboAction->GetClass(), COMBO_ACTION_PIN_NAME);
ActionPin->DefaultObject = const_cast<UObject*>(Cast<UObject>(ComboAction));
ActionPin->DefaultValue = ComboAction->GetName();
Schema->ConstructBasicPinTooltip(*ActionPin, LOCTEXT("InputActionPinDescription", "The input action that caused this event to fire"), ActionPin->PinToolTip);
}
Super::AllocateDefaultPins();
}
void UK2Node_ComboAction::HideEventPins(UEdGraphPin *RetainPin)
{
//// Gather pins
//const ETriggerEventsSupported SupportedTriggerEvents = GetDefault<UBlueprintEditorSettings>()->bEnableInputTriggerSupportWarnings && InputAction ? InputAction->GetSupportedTriggerEvents() : ETriggerEventsSupported::All;
//// Hide any event pins that are not supported by this Action's triggers in the advanced view
//ForEachEventPinName([this, SupportedTriggerEvents](ETriggerEvent Event, FName PinName)
//{
// if (UEdGraphPin* Pin = FindPin(PinName))
// {
// const bool bIsSupported = UInputTrigger::IsSupportedTriggerEvent(SupportedTriggerEvents, Event);
//
// Pin->bAdvancedView = !bIsSupported;
// }
//});
}
void UK2Node_ComboAction::PostReconstructNode()
{
Super::PostReconstructNode();
HideEventPins(nullptr);
}
void UK2Node_ComboAction::PinConnectionListChanged(UEdGraphPin* Pin)
{
Super::PinConnectionListChanged(Pin);
HideEventPins(Pin);
}
FLinearColor UK2Node_ComboAction::GetNodeTitleColor() const
{
return GetDefault<UGraphEditorSettings>()->EventNodeTitleColor;
}
FName UK2Node_ComboAction::GetActionName() const
{
return this->ComboAction ? this->ComboAction->GetFName() : FName();
}
FText UK2Node_ComboAction::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
// TODO: Is Using InputAction->GetFName okay here? Full Asset path would be better for disambiguation.
if (TitleType == ENodeTitleType::MenuTitle)
{
return FText::FromName(this->GetActionName());
}
else if (this->CachedNodeTitle.IsOutOfDate(this))
{
FFormatNamedArguments Args;
Args.Add(TEXT("ComboActionName"), FText::FromName(this->GetActionName()));
FText LocFormat = LOCTEXT("ComboAction_Name", "ComboAction {ComboActionName}");
// FText::Format() is slow, so we cache this to save on performance
this->CachedNodeTitle.SetCachedText(FText::Format(LocFormat, Args), this);
}
return this->CachedNodeTitle;
}
FText UK2Node_ComboAction::GetTooltipText() const
{
if (CachedTooltip.IsOutOfDate(this))
{
// FText::Format() is slow, so we cache this to save on performance
FString ActionPath = this->ComboAction ? this->ComboAction->GetFullName() : TEXT("");
CachedTooltip.SetCachedText(
FText::Format(
LOCTEXT("ComboAction_Tooltip", "Event for when '{0}' triggers.\n\nNOTE: This is not guaranteed to fire every frame, only when the Action is triggered and the current Input Mode includes 'Game'."),
FText::FromString(ActionPath)),
this);
}
return CachedTooltip;
}
FSlateIcon UK2Node_ComboAction::GetIconAndTint(FLinearColor& OutColor) const
{
static FSlateIcon Icon(FAppStyle::GetAppStyleSetName(), "GraphEditor.Event_16x");
return Icon;
}
bool UK2Node_ComboAction::IsCompatibleWithGraph(UEdGraph const* Graph) const
{
// This node expands into event nodes and must be placed in a Ubergraph
EGraphType const GraphType = Graph->GetSchema()->GetGraphType(Graph);
bool bIsCompatible = (GraphType == EGraphType::GT_Ubergraph);
if (bIsCompatible)
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph);
UEdGraphSchema_K2 const* K2Schema = Cast<UEdGraphSchema_K2>(Graph->GetSchema());
bool const bIsConstructionScript = (K2Schema != nullptr) ? UEdGraphSchema_K2::IsConstructionScript(Graph) : false;
bIsCompatible = (Blueprint != nullptr) && Blueprint->SupportsInputEvents() && !bIsConstructionScript && Super::IsCompatibleWithGraph(Graph);
}
return bIsCompatible;
}
UObject*UK2Node_ComboAction::GetJumpTargetForDoubleClick() const
{
return const_cast<UObject*>(Cast<UObject>(this->ComboAction));
}
void UK2Node_ComboAction::JumpToDefinition() const
{
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(this->GetJumpTargetForDoubleClick());
}
void UK2Node_ComboAction::ValidateNodeDuringCompilation(class FCompilerResultsLog &MessageLog) const
{
Super::ValidateNodeDuringCompilation(MessageLog);
if (!this->ComboAction)
{
MessageLog.Error(*LOCTEXT("ComboAction_ErrorFmt", "ComboActionEvent references invalid 'null' action for @@").ToString(), this);
return;
}
}
void UK2Node_ComboAction::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
if(!this->ComboAction)
{
static const FText InvalidActionWarning = LOCTEXT("InvalidInputActionDuringExpansion", "@@ does not have a valid Input Action asset!!");
CompilerContext.MessageLog.Warning(*InvalidActionWarning.ToString(), this);
return;
}
//// Establish active pins
//struct ActivePinData
//{
// ActivePinData(UEdGraphPin* InPin, ETriggerEvent InTriggerEvent) : Pin(InPin), TriggerEvent(InTriggerEvent) {}
// UEdGraphPin* Pin;
// ETriggerEvent TriggerEvent;
//};
//const ETriggerEventsSupported SupportedTriggerEvents = GetDefault<UBlueprintEditorSettings>()->bEnableInputTriggerSupportWarnings ? this->ComboAction->GetSupportedTriggerEvents() : ETriggerEventsSupported::All;
//
//TArray<ActivePinData> ActivePins;
//ForEachEventPinName([this, &ActivePins, &SupportedTriggerEvents, &CompilerContext](ETriggerEvent Event, FName PinName)
//{
// UEdGraphPin* ComboActionPin = FindPin(PinName, EEdGraphPinDirection::EGPD_Output);
// if (ComboActionPin && ComboActionPin->LinkedTo.Num() > 0)
// {
// ActivePins.Add(ActivePinData(ComboActionPin, Event));
// // Check if this exec pin is supported!
// if (UE::Input::bShouldWarnOnUnsupportedInputPin && !UInputTrigger::IsSupportedTriggerEvent(SupportedTriggerEvents, Event))
// {
// CompilerContext.MessageLog.Warning(*FText::Format(LOCTEXT("UnsuportedEventTypeOnAction", "'{0}'on @@ may not be executed because it is not a supported trigger on this action!"), ComboActionPin->GetDisplayName()).ToString(), this);
// }
// }
//});
//if (ActivePins.Num() == 0)
//{
// return;
//}
//// Bind all active pins to their action delegate
//const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
//auto CreateComboActionEvent = [this, &CompilerContext, &SourceGraph](UEdGraphPin* Pin, ETriggerEvent TriggerEvent) -> UK2Node_ComboActionEvent*
//{
// if (!this->ComboAction)
// {
// return nullptr;
// }
// UK2Node_ComboActionEvent* InputActionEvent = CompilerContext.SpawnIntermediateEventNode<UK2Node_ComboActionEvent>(this, Pin, SourceGraph);
// InputActionEvent->CustomFunctionName = FName(*FString::Printf(TEXT("InpActEvt_%s_%s"), *GetActionName().ToString(), *InputActionEvent->GetName()));
// InputActionEvent->ComboAction = this->ComboAction;
// InputActionEvent->TriggerEvent = TriggerEvent;
// InputActionEvent->EventReference.SetExternalDelegateMember(FName(TEXT("ComboActionHandlerDynamicSignature__DelegateSignature")));
// InputActionEvent->AllocateDefaultPins();
// return InputActionEvent;
//};
//// Create temporary variables to copy ActionValue and ElapsedSeconds into
//UK2Node_TemporaryVariable* ActionValueVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
//ActionValueVar->VariableType.PinCategory = UK2Node_GetInputActionValue::GetValueCategory(this->ComboAction);
//ActionValueVar->VariableType.PinSubCategory = UK2Node_GetInputActionValue::GetValueSubCategory(this->ComboAction);
//ActionValueVar->VariableType.PinSubCategoryObject = UK2Node_GetInputActionValue::GetValueSubCategoryObject(this->ComboAction);
//ActionValueVar->AllocateDefaultPins();
//UK2Node_TemporaryVariable* ElapsedSecondsVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
//ElapsedSecondsVar->VariableType.PinCategory = UEdGraphSchema_K2::PC_Real;
//ElapsedSecondsVar->VariableType.PinSubCategory = UEdGraphSchema_K2::PC_Double;
//ElapsedSecondsVar->AllocateDefaultPins();
//UK2Node_TemporaryVariable* TriggeredSecondsVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
//TriggeredSecondsVar->VariableType.PinCategory = UEdGraphSchema_K2::PC_Real;
//TriggeredSecondsVar->VariableType.PinSubCategory = UEdGraphSchema_K2::PC_Double;
//TriggeredSecondsVar->AllocateDefaultPins();
//UK2Node_TemporaryVariable* InputActionVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
//InputActionVar->VariableType.PinCategory = UEdGraphSchema_K2::PC_Object;
//InputActionVar->VariableType.PinSubCategoryObject = this->ComboAction->GetClass();
//InputActionVar->AllocateDefaultPins();
//for (ActivePinData& PinData : ActivePins)
//{
// UEdGraphPin* EachPin = PinData.Pin;
// UK2Node_ComboActionEvent *ComboActionEvent = CreateComboActionEvent(EachPin, PinData.TriggerEvent);
// if (!ComboActionEvent)
// {
// continue;
// }
// // Create assignment nodes to assign the action value
// UK2Node_AssignmentStatement* ActionValueInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
// ActionValueInitialize->AllocateDefaultPins();
// Schema->TryCreateConnection(ActionValueVar->GetVariablePin(), ActionValueInitialize->GetVariablePin());
// Schema->TryCreateConnection(ActionValueInitialize->GetValuePin(), ComboActionEvent->FindPinChecked(ActionValuePinName));
// // Connect the events to the assign location nodes
// Schema->TryCreateConnection(Schema->FindExecutionPin(*ComboActionEvent, EGPD_Output), ActionValueInitialize->GetExecPin());
// // Create assignment nodes to assign the elapsed timers and input action
// UK2Node_AssignmentStatement* ElapsedSecondsInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
// ElapsedSecondsInitialize->AllocateDefaultPins();
// Schema->TryCreateConnection(ElapsedSecondsVar->GetVariablePin(), ElapsedSecondsInitialize->GetVariablePin());
// Schema->TryCreateConnection(ElapsedSecondsInitialize->GetValuePin(), ComboActionEvent->FindPinChecked(TEXT("ElapsedTime")));
// UK2Node_AssignmentStatement* TriggeredSecondsInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
// TriggeredSecondsInitialize->AllocateDefaultPins();
// Schema->TryCreateConnection(TriggeredSecondsVar->GetVariablePin(), TriggeredSecondsInitialize->GetVariablePin());
// Schema->TryCreateConnection(TriggeredSecondsInitialize->GetValuePin(), ComboActionEvent->FindPinChecked(TEXT("TriggeredTime")));
// UK2Node_AssignmentStatement* InputActionInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
// InputActionInitialize->AllocateDefaultPins();
// Schema->TryCreateConnection(InputActionVar->GetVariablePin(), InputActionInitialize->GetVariablePin());
// Schema->TryCreateConnection(InputActionInitialize->GetValuePin(), ComboActionEvent->FindPinChecked(TEXT("SourceAction")));
//
// // Connect the assign location to the assign elapsed time nodes
// Schema->TryCreateConnection(ActionValueInitialize->GetThenPin(), ElapsedSecondsInitialize->GetExecPin());
// Schema->TryCreateConnection(ElapsedSecondsInitialize->GetThenPin(), TriggeredSecondsInitialize->GetExecPin());
// Schema->TryCreateConnection(TriggeredSecondsInitialize->GetThenPin(), InputActionInitialize->GetExecPin());
//
// // Move the original event connections to the then pin of the Input Action assign
// CompilerContext.MovePinLinksToIntermediate(*EachPin, *InputActionInitialize->GetThenPin());
// // Move the original event variable connections to the intermediate nodes
// CompilerContext.MovePinLinksToIntermediate(*FindPin(ActionValuePinName), *ActionValueVar->GetVariablePin());
// CompilerContext.MovePinLinksToIntermediate(*FindPin(ElapsedSecondsPinName), *ElapsedSecondsVar->GetVariablePin());
// CompilerContext.MovePinLinksToIntermediate(*FindPin(TriggeredSecondsPinName), *TriggeredSecondsVar->GetVariablePin());
// CompilerContext.MovePinLinksToIntermediate(*FindPin(InputActionPinName), *InputActionVar->GetVariablePin());
//}
}
void UK2Node_ComboAction::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
auto CustomizeComboActionNodeLambda = [](UEdGraphNode *NewNode, bool bIsTemplateNode, TWeakObjectPtr<const UComboAction> Action)
{
UK2Node_ComboAction* ComboActionNode = CastChecked<UK2Node_ComboAction>(NewNode);
ComboActionNode->ComboAction = Action.Get();
};
// Do a first time registration using the node's class to pull in all existing actions
if (ActionRegistrar.IsOpenForRegistration(GetClass()))
{
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
static bool bRegisterOnce = true;
if (bRegisterOnce)
{
bRegisterOnce = false;
if (AssetRegistry.IsLoadingAssets())
{
AssetRegistry.OnFilesLoaded().AddLambda([]() { FBlueprintActionDatabase::Get().RefreshClassActions(StaticClass()); });
}
}
TArray<FAssetData> ActionAssets;
AssetRegistry.GetAssetsByClass(UComboAction::StaticClass()->GetClassPathName(), ActionAssets, true);
for (const FAssetData& ActionAsset : ActionAssets)
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
if (FPackageName::GetPackageMountPoint(ActionAsset.PackageName.ToString()) != NAME_None)
{
if (const UComboAction *Action = Cast<const UComboAction>(ActionAsset.GetAsset()))
{
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeComboActionNodeLambda, TWeakObjectPtr<const UComboAction>(Action));
ActionRegistrar.AddBlueprintAction(Action, NodeSpawner);
}
}
}
}
else if (const UComboAction *Action = Cast<const UComboAction>(ActionRegistrar.GetActionKeyFilter()))
{
// If this is a specific UComboAction asset update it.
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeComboActionNodeLambda, TWeakObjectPtr<const UComboAction>(Action));
ActionRegistrar.AddBlueprintAction(Action, NodeSpawner);
}
}
FText UK2Node_ComboAction::GetMenuCategory() const
{
static FNodeTextCache CachedCategory;
if (CachedCategory.IsOutOfDate(this))
{
// FText::Format() is slow, so we cache this to save on performance
CachedCategory.SetCachedText(FEditorCategoryUtils::BuildCategoryString(FCommonEditorCategory::Input, LOCTEXT("ActionMenuCategory", "Combo Action Events")), this); // TODO: Rename Action Events once old action system is removed
}
return CachedCategory;
}
FBlueprintNodeSignature UK2Node_ComboAction::GetSignature() const
{
FBlueprintNodeSignature NodeSignature = Super::GetSignature();
NodeSignature.AddKeyValue(GetActionName().ToString());
return NodeSignature;
}
TSharedPtr<FEdGraphSchemaAction> UK2Node_ComboAction::GetEventNodeAction(const FText& ActionCategory)
{
// TODO: Custom EdGraphSchemaAction required?
TSharedPtr<FEdGraphSchemaAction_K2InputAction> EventNodeAction = MakeShareable(new FEdGraphSchemaAction_K2InputAction(ActionCategory, GetNodeTitle(ENodeTitleType::EditableTitle), GetTooltipText(), 0));
EventNodeAction->NodeTemplate = this;
return EventNodeAction;
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,33 @@
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#include "Events/K2Node_ComboActionEvent.h"
#include "Events/ComboActionDelegateBinding.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(K2Node_ComboActionEvent)
UK2Node_ComboActionEvent::UK2Node_ComboActionEvent(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer)
{
bInternalEvent = true;
//TriggerEvent = ETriggerEvent::None;
}
UClass *UK2Node_ComboActionEvent::GetDynamicBindingClass() const
{
return UComboActionDelegateBinding::StaticClass();
}
void UK2Node_ComboActionEvent::RegisterDynamicBinding(UDynamicBlueprintBinding *BindingObject) const
{
UComboActionDelegateBinding *ComboActionBindingObject = CastChecked<UComboActionDelegateBinding>(BindingObject);
FBlueprintComboActionBinding Binding;
Binding.InputAction = this->ComboAction;
//Binding.TriggerEvent = this->TriggerEvent;
Binding.FunctionNameToBind = this->CustomFunctionName;
ComboActionBindingObject->ComboActionDelegateBindings.Add(Binding);
}

View File

@ -0,0 +1,33 @@
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "K2Node_Event.h"
#include "K2Node_ComboActionEvent.generated.h"
/**
* Blueprint event node created for each UComboAction data asset, which is called
* when the ComboManagerComponent activates the associated action.
*/
UCLASS()
class COMBOINPUT_API UK2Node_ComboActionEvent : public UK2Node_Event
{
GENERATED_BODY()
public:
UK2Node_ComboActionEvent(const FObjectInitializer &ObjectInitializer);
UPROPERTY()
TObjectPtr<const class UComboAction> ComboAction;
//UPROPERTY()
// ETriggerEvent TriggerEvent;
//~ Begin UK2Node Interface
virtual UClass *GetDynamicBindingClass() const override;
virtual void RegisterDynamicBinding(UDynamicBlueprintBinding *BindingObject) const override;
//~ End UK2Node Interface
};

View File

@ -0,0 +1,32 @@
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#pragma once
#include "Engine/DynamicBlueprintBinding.h"
#include "InputTriggers.h"
#include "ComboActionDelegateBinding.generated.h"
USTRUCT()
struct COMBOINPUT_API FBlueprintComboActionBinding
{
GENERATED_BODY()
public:
UPROPERTY() TObjectPtr<const class UComboAction> InputAction = nullptr;
//UPROPERTY() ETriggerEvent TriggerEvent = ETriggerEvent::None;
UPROPERTY() FName FunctionNameToBind = NAME_None;
};
UCLASS()
class COMBOINPUT_API UComboActionDelegateBinding : public UDynamicBlueprintBinding
{
GENERATED_BODY()
public:
UComboActionDelegateBinding(const FObjectInitializer &ObjectInitializer);
void BindToComboManagerComponent(class UComboManagerComponent *ComboManager, class UObject *ObjectToBindTo) const;
UPROPERTY() TArray<FBlueprintComboActionBinding> ComboActionDelegateBindings;
};

View File

@ -0,0 +1,65 @@
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#pragma once
#include "K2Node.h"
#include "K2Node_EventNodeInterface.h"
#include "EdGraph/EdGraphNodeUtils.h"
#include "K2Node_ComboAction.generated.h"
class UInputAction;
enum class ETriggerEvent : uint8;
namespace ENodeTitleType { enum Type : int; }
struct FBlueprintNodeSignature;
class FBlueprintActionDatabaseRegistrar;
class FKismetCompilerContext;
UCLASS()
class COMBOINPUT_API UK2Node_ComboAction : public UK2Node, public IK2Node_EventNodeInterface
{
GENERATED_BODY()
public:
UK2Node_ComboAction(const FObjectInitializer &ObjectInitializer);
UPROPERTY() TObjectPtr<const class UComboAction> ComboAction;
//~ Begin UEdGraphNode Interface.
virtual void AllocateDefaultPins() override;
virtual FLinearColor GetNodeTitleColor() const override;
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
virtual FText GetTooltipText() const override;
virtual FSlateIcon GetIconAndTint(FLinearColor &OutColor) const override;
virtual bool IsCompatibleWithGraph(UEdGraph const *Graph) const override;
virtual UObject* GetJumpTargetForDoubleClick() const override;
virtual void JumpToDefinition() const override;
//~ End UEdGraphNode Interface.
//~ Begin UK2Node Interface
virtual void ValidateNodeDuringCompilation(class FCompilerResultsLog &MessageLog) const override;
virtual bool ShouldShowNodeProperties() const override { return true; }
virtual void ExpandNode(FKismetCompilerContext &CompilerContext, UEdGraph *SourceGraph) override;
virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar &ActionRegistrar) const override;
virtual FText GetMenuCategory() const override;
virtual bool CanUserEditPinAdvancedViewFlag() const override { return true; }
virtual FBlueprintNodeSignature GetSignature() const override;
virtual void PostReconstructNode();
virtual void PinConnectionListChanged(UEdGraphPin *Pin);
//~ End UK2Node Interface
//~ Begin IK2Node_EventNodeInterface Interface.
virtual TSharedPtr<FEdGraphSchemaAction> GetEventNodeAction(const FText &ActionCategory) override;
//~ End IK2Node_EventNodeInterface Interface.
private:
FName GetActionName() const;
void HideEventPins(UEdGraphPin *RetainPin);
/** Constructing FText strings can be costly, so we cache the node's title/tooltip */
FNodeTextCache CachedTooltip;
FNodeTextCache CachedNodeTitle;
};

View File

@ -15,11 +15,7 @@ public class ComboInputEditor : ModuleRules
PublicDependencyModuleNames.AddRange PublicDependencyModuleNames.AddRange
(new string[] (new string[]
{ {
"Core", "ComboInput",
"CoreUObject",
"Engine",
"UnrealEd",
"AssetTools"
} }
); );
@ -27,27 +23,15 @@ public class ComboInputEditor : ModuleRules
( (
new string[] new string[]
{ {
"ComboInput", "Core",
"CoreUObject",
"AssetTools", "AssetTools",
"Slate",
"SlateCore",
"GraphEditor",
"PropertyEditor",
"EditorStyle",
"Kismet",
"KismetWidgets",
"ApplicationCore",
"ToolMenus",
"DeveloperSettings",
"Projects",
"BlueprintGraph",
"InputCore",
"MainFrame"
// ... add private dependencies that you statically link with here ...
} }
); );
if (Target.bBuildEditor)
{
PrivateDependencyModuleNames.Add("UnrealEd");
}
} }
} }

View File

@ -2,20 +2,130 @@
#include "ComboInputEditor.h" #include "ComboInputEditor.h"
#include "ComboInputAssets.h"
#include "ToolMenuSection.h"
#include "AssetTypeActions/AssetTypeActions_DataAsset.h"
#include "Interfaces/IPluginManager.h" #include "Interfaces/IPluginManager.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(ComboInputEditor)
#define LOCTEXT_NAMESPACE "FComboInputEditorModule" #define LOCTEXT_NAMESPACE "FComboInputEditorModule"
EAssetTypeCategories::Type FComboInputEditorModule::ComboAssetsCategory;
class FAssetTypeActions_ComboAction : public FAssetTypeActions_DataAsset
{
public:
virtual FText GetName() const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_ComboAction", "Combo Action"); }
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_ComboActionDesc", "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 attack animation and the like."); }
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:
virtual FText GetName() const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_ComboInputAsset", "Combo Input Asset"); }
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_ComboInputAssetDesc", "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 to the player controller's ComboManagerComponent, which executes the associated action in the current ComboSequenceNode."); }
virtual UClass *GetSupportedClass() const override { return UComboInputAsset::StaticClass(); }
};
UComboAction_Factory::UComboAction_Factory(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer)
{
SupportedClass = UComboAction::StaticClass();
bEditAfterNew = true;
bCreateNew = true;
}
UObject *UComboAction_Factory::FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn)
{
if (this->ComboActionClass != nullptr)
{
return NewObject<UComboAction>(InParent, this->ComboActionClass, Name, Flags | RF_Transactional, Context);
}
else
{
check(Class->IsChildOf(UComboAction::StaticClass()));
return NewObject<UComboAction>(InParent, Class, Name, Flags | RF_Transactional, Context);
}
}
UComboSequenceNode_Factory::UComboSequenceNode_Factory(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer)
{
SupportedClass = UComboSequenceNode::StaticClass();
bEditAfterNew = true;
bCreateNew = true;
}
UObject *UComboSequenceNode_Factory::FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn)
{
if (this->ComboSequenceNodeClass != nullptr)
{
return NewObject<UComboSequenceNode>(InParent, this->ComboSequenceNodeClass, Name, Flags | RF_Transactional, Context);
}
else
{
check(Class->IsChildOf(UComboSequenceNode::StaticClass()));
return NewObject<UComboSequenceNode>(InParent, Class, Name, Flags | RF_Transactional, Context);
}
}
UComboInputAsset_Factory::UComboInputAsset_Factory(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer)
{
SupportedClass = UComboInputAsset::StaticClass();
bEditAfterNew = true;
bCreateNew = true;
}
UObject *UComboInputAsset_Factory::FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn)
{
if (this->ComboInputAssetClass != nullptr)
{
return NewObject<UComboInputAsset>(InParent, this->ComboInputAssetClass, Name, Flags | RF_Transactional, Context);
}
else
{
check(Class->IsChildOf(UComboInputAsset::StaticClass()));
return NewObject<UComboInputAsset>(InParent, Class, Name, Flags | RF_Transactional, Context);
}
}
void FComboInputEditorModule::StartupModule() void FComboInputEditorModule::StartupModule()
{ {
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module // Register combo action asset
IAssetTools &AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
FComboInputEditorModule::ComboAssetsCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Input")), LOCTEXT("InputAssetsCategory", "Input"));
this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboAction));
this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboSequenceNode));
this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboInputAsset));
} }
void FComboInputEditorModule::ShutdownModule() void FComboInputEditorModule::ShutdownModule()
{ {
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
} }
#undef LOCTEXT_NAMESPACE #undef LOCTEXT_NAMESPACE

View File

@ -3,68 +3,78 @@
#pragma once #pragma once
#include "Modules/ModuleManager.h" #include "Modules/ModuleManager.h"
#include "EdGraphUtilities.h" #include "EdGraphUtilities.h"
#include "IAssetTools.h" #include "IAssetTools.h"
// #include "Interfaces/IHttpRequest.h" #include "IAssetTypeActions.h"
#include "Factories/Factory.h"
#include "ComboInputEditor.generated.h"
UCLASS()
class COMBOINPUTEDITOR_API UComboAction_Factory : public UFactory
{
GENERATED_BODY()
public:
UComboAction_Factory(const class FObjectInitializer &ObjectInitializer);
UPROPERTY(EditAnywhere, Category="Combo Input")
TSubclassOf<class UComboAction> ComboActionClass;
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<class UComboSequenceNode> 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
{
GENERATED_BODY()
public:
UComboInputAsset_Factory(const class FObjectInitializer &ObjectInitializer);
UPROPERTY(EditAnywhere, Category="Combo Input")
TSubclassOf<class UComboInputAsset> ComboInputAssetClass;
virtual UObject *FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) override;
};
// class FHttpModule;
// class FSlateStyleSet;
class FComboInputEditorModule : public IModuleInterface class FComboInputEditorModule : public IModuleInterface
{ {
public: public:
static FComboInputEditorModule &Get() { return FModuleManager::LoadModuleChecked<FComboInputEditorModule>("ComboInputEditor"); }
static bool IsAvailable() { return FModuleManager::Get().IsModuleLoaded("ComboInputEditor"); }
/**
* Singleton-like access to this module's interface. This is just for convenience!
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
*
* @return Returns singleton instance, loading the module on demand if needed
*/
static FComboInputEditorModule &Get()
{
return FModuleManager::LoadModuleChecked<FComboInputEditorModule>( "ComboInputEditor" );
}
/**
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
*
* @return True if the module is loaded and ready to use
*/
static bool IsAvailable()
{
return FModuleManager::Get().IsModuleLoaded("ComboInputEditor");
}
/* Called when the module is loaded */
virtual void StartupModule() override; virtual void StartupModule() override;
/* Called when the module is unloaded */
virtual void ShutdownModule() override; virtual void ShutdownModule() override;
private: static EAssetTypeCategories::Type GetInputAssetsCategory() { return FComboInputEditorModule::ComboAssetsCategory; }
// void RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef<IAssetTypeActions> Action);
// void OnGetResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
// UFUNCTION() void SendHTTPGet();
// void PluginButtonClicked();
// void RegisterMenus();
private: private:
void RegisterAssetTypeActions(IAssetTools &AssetTools, TSharedRef<IAssetTypeActions> Action)
{
AssetTools.RegisterAssetTypeActions(Action);
CreatedAssetTypeActions.Add(Action);
}
// TSharedPtr<class FUICommandList> PluginCommands; static EAssetTypeCategories::Type ComboAssetsCategory;
// TSharedPtr<FSlateStyleSet> DialogueTreeSet;
// TSharedPtr<class FMounteaDialogueGraphAssetAction> MounteaDialogueGraphAssetActions;
// TSharedPtr<class FMounteaDialogueAdditionalDataAssetAction> MounteaDialogueAdditionalDataAssetActions;
// TSharedPtr<class FMounteaDialogueDecoratorAssetAction> MounteaDialogueDecoratorAssetAction;
// TSharedPtr<struct FGraphPanelNodeFactory> GraphPanelNodeFactory_MounteaDialogueGraph; TArray<TSharedPtr<IAssetTypeActions>> CreatedAssetTypeActions;
// TArray< TSharedPtr<IAssetTypeActions> > CreatedAssetTypeActions;
// EAssetTypeCategories::Type MounteaDialogueGraphAssetCategoryBit;
// FHttpModule* Http;
// TArray<FName> RegisteredCustomClassLayouts;
// TArray<FName> RegisteredCustomPropertyTypeLayout;
}; };