// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. #include "ComboInputEditor.h" #include "ComboActionGraph.h" #include "ComboInputAssets.h" #include "ToolMenuSection.h" #include "AssetTypeActions/AssetTypeActions_DataAsset.h" #include "Ed/AssetEditor_ComboActionGraph.h" #include "Ed/EdComboActionGraphEdge.h" #include "Ed/EdComboActionGraphNode.h" #include "Ed/FComboActionGraphEditorCommands.h" #include "Ed/SEdComboActionGraphEdge.h" #include "Ed/SEdComboActionGraphNode.h" #include "Interfaces/IPluginManager.h" #include "Styling/SlateStyle.h" #include "Styling/SlateStyleMacros.h" #include "Styling/SlateStyleRegistry.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(ComboInputEditor) #define LOCTEXT_NAMESPACE "FComboInputEditorModule" EAssetTypeCategories::Type FComboInputEditorModule::ComboAssetsCategory; EAssetTypeCategories::Type FComboInputEditorModule::ComboActionGraphCategory; 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_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 ComboActionGraph."); } virtual UClass *GetSupportedClass() const override { return UComboInputAsset::StaticClass(); } }; class FAssetTypeActions_ComboActionGraph : public FAssetTypeActions_Base { public: virtual FText GetName() const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_ComboActionGraph", "Combo Action Graph"); } 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_ComboActionGraphDesc", "A node graph containing a sequence of combo actions. Each node can respond to any number of combo inputs, and when activated will execute the associated event."); } virtual UClass *GetSupportedClass() const override { return UComboActionGraph::StaticClass(); } virtual void OpenAssetEditor(const TArray &InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override { const EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone; for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ObjIt++) { if (UComboActionGraph *Graph = Cast(*ObjIt)) { TSharedRef NewGraphEditor(new FAssetEditor_ComboActionGraph()); NewGraphEditor->InitComboActionGraphAssetEditor(Mode, EditWithinLevelEditor, Graph); } } } }; class FComboActionGraphPanelNodeFactory : public FGraphPanelNodeFactory { public: virtual TSharedPtr CreateNode(UEdGraphNode *Node) const override { if (UEdComboActionGraphNode *EdGraphNode = Cast(Node)) { return SNew(SEdComboActionGraphNode, EdGraphNode); } else if (UEdComboActionGraphEdge *EdGraphEdge = Cast(Node)) { return SNew(SEdComboActionGraphEdge, EdGraphEdge); } return nullptr; } }; UComboAction_Factory::UComboAction_Factory(const FObjectInitializer &ObjectInitializer) : Super(ObjectInitializer) { this->SupportedClass = UComboAction::StaticClass(); this->bEditAfterNew = true; this->bCreateNew = true; } UObject *UComboAction_Factory::FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) { if (this->ComboActionClass != nullptr) { return NewObject(InParent, this->ComboActionClass, Name, Flags | EObjectFlags::RF_Transactional, Context); } else { check(Class->IsChildOf(UComboAction::StaticClass())); return NewObject(InParent, Class, Name, Flags | EObjectFlags::RF_Transactional, Context); } } UComboInputAsset_Factory::UComboInputAsset_Factory(const FObjectInitializer &ObjectInitializer) : Super(ObjectInitializer) { this->SupportedClass = UComboInputAsset::StaticClass(); this->bEditAfterNew = true; this->bCreateNew = true; } UObject *UComboInputAsset_Factory::FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) { if (this->ComboInputAssetClass != nullptr) { return NewObject(InParent, this->ComboInputAssetClass, Name, Flags | EObjectFlags::RF_Transactional, Context); } else { check(Class->IsChildOf(UComboInputAsset::StaticClass())); return NewObject(InParent, Class, Name, Flags | EObjectFlags::RF_Transactional, Context); } } UComboActionGraph_Factory::UComboActionGraph_Factory(const FObjectInitializer &ObjectInitializer) : Super(ObjectInitializer) { this->SupportedClass = UComboActionGraph::StaticClass(); this->bEditAfterNew = true; this->bCreateNew = true; } UObject *UComboActionGraph_Factory::FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) { if (this->ComboActionGraphClass != nullptr) { return NewObject(InParent, this->ComboActionGraphClass, Name, Flags | EObjectFlags::RF_Transactional, Context); } else { check(Class->IsChildOf(UComboActionGraph::StaticClass())); return NewObject(InParent, Class, Name, Flags | EObjectFlags::RF_Transactional, Context); } } FComboInputSlateStyle::FComboInputSlateStyle() : FSlateStyleSet("ComboInputEditor") { this->SetParentStyleName(FAppStyle::GetAppStyleSetName()); const FString &PluginSlateDir = IPluginManager::Get().FindPlugin("ComboInput")->GetBaseDir(); this->SetContentRoot(PluginSlateDir / TEXT("Resources/Slate")); this->SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate")); // Combo Input Editor icons static const FVector2D Icon12(12.0f, 12.0f); static const FVector2D Icon16(16.0f, 16.0f); static const FVector2D Icon40(40.0f, 40.0f); static const FVector2D Icon64(64.0f, 64.0f); this->Set("ComboInputIcon_Small", new IMAGE_BRUSH_SVG("Icons/ComboInput_16", Icon16)); this->Set("ComboInputIcon_Large", new IMAGE_BRUSH_SVG("Icons/ComboInput_64", Icon64)); this->Set("ClassIcon.ComboAction", new IMAGE_BRUSH_SVG("Icons/ComboAction_16", Icon16)); this->Set("ClassThumbnail.ComboAction", new IMAGE_BRUSH_SVG("Icons/ComboAction_64", Icon64)); this->Set("ClassIcon.ComboSequenceNode", new IMAGE_BRUSH_SVG("Icons/ComboSequenceNode_16", Icon16)); this->Set("ClassThumbnail.ComboSequenceNode", new IMAGE_BRUSH_SVG("Icons/ComboSequenceNode_64", Icon64)); this->Set("ClassIcon.ComboInputAsset", new IMAGE_BRUSH_SVG("Icons/ComboInputAsset_16", Icon16)); this->Set("ClassThumbnail.ComboInputAsset", new IMAGE_BRUSH_SVG("Icons/ComboInputAsset_64", Icon64)); this->Set("ClassIcon.ComboActionGraph", new IMAGE_BRUSH_SVG("Icons/ComboSequenceNode_16", Icon16)); this->Set("ClassThumbnail.ComboActionGraph", new IMAGE_BRUSH_SVG("Icons/ComboSequenceNode_64", Icon64)); // Combo Action Graph editor brushes { this->SetContentRoot(PluginSlateDir / TEXT("Resources/Slate/Editor")); this->SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate/Editor")); this->Set("MDSStyleSet.AutoArrange.small", new IMAGE_BRUSH(TEXT("AutoArrangeIcon"), Icon16)); this->Set("MDSStyleSet.AutoArrange", new IMAGE_BRUSH(TEXT("AutoArrangeIcon"), Icon40)); this->Set("MDSStyleSet.AutoArrange.large", new IMAGE_BRUSH(TEXT("AutoArrangeIcon"), Icon64)); this->Set("MDSStyleSet.GraphSettings.small", new IMAGE_BRUSH(TEXT("GraphSettings"), Icon16)); this->Set("MDSStyleSet.GraphSettings", new IMAGE_BRUSH(TEXT("GraphSettings"), Icon40)); this->Set("MDSStyleSet.GraphSettings.large", new IMAGE_BRUSH(TEXT("GraphSettings"), Icon64)); this->Set("MDSStyleSet.ValidateGraph.small", new IMAGE_BRUSH(TEXT("ValidateGraph"), Icon16)); this->Set("MDSStyleSet.ValidateGraph", new IMAGE_BRUSH(TEXT("ValidateGraph"), Icon40)); this->Set("MDSStyleSet.ValidateGraph.large", new IMAGE_BRUSH(TEXT("ValidateGraph"), Icon64)); this->Set("MDSStyleSet.Graph.NodeOverlay", new BOX_BRUSH(TEXT("NodeOverlay"), FMargin(8.0f / 64.0f, 3.0f / 32.0f, 0, 0))); this->Set("MDSStyleSet.Graph.PinDocksOverlay", new BOX_BRUSH(TEXT("PinDocksOverlay"), FMargin(8.0f / 64.0f, 3.0f / 32.0f, 0, 0))); this->Set("MDSStyleSet.Graph.SimpleArrow", new IMAGE_BRUSH(TEXT("SimpleArrow"), Icon16)); this->Set("MDSStyleSet.Graph.HollowArrow", new IMAGE_BRUSH(TEXT("HollowArrow"), Icon16)); this->Set("MDSStyleSet.Graph.FancyArrow", new IMAGE_BRUSH(TEXT("FancyArrow"), Icon16)); this->Set("MDSStyleSet.Graph.Bubble", new IMAGE_BRUSH(TEXT("Bubble"), Icon16)); this->Set("MDSStyleSet.Node.SoftEdges", new BOX_BRUSH(TEXT("NodeSoftCorners"), FMargin(16.f / 64.f, 25.f / 64.f, 16.f / 64.f, 16.f / 64.f))); this->Set("MDSStyleSet.Node.HardEdges", new BOX_BRUSH(TEXT("NodeHardCorners"), FMargin(16.f / 64.f, 25.f / 64.f, 16.f / 64.f, 16.f / 64.f))); this->Set("MDSStyleSet.Node.TextSoftEdges", new BOX_BRUSH(TEXT("TextSoftCorners"), FMargin(16.f / 64.f, 25.f / 64.f, 16.f / 64.f, 16.f / 64.f))); this->Set("MDSStyleSet.Node.TextHardEdges", new BOX_BRUSH(TEXT("TextHardCorners"), FMargin(16.f / 64.f, 25.f / 64.f, 16.f / 64.f, 16.f / 64.f))); this->Set("MDSStyleSet.Node.IndexCircle", new IMAGE_BRUSH(TEXT("IndexIcon"), Icon16)); this->Set("MDSStyleSet.Icon.OK", new IMAGE_BRUSH(TEXT("OKIcon"), Icon16)); this->Set("MDSStyleSet.Icon.Error", new IMAGE_BRUSH(TEXT("ErroIcon"), Icon16)); this->Set("MDSStyleSet.Icon.BulletPoint", new IMAGE_BRUSH(TEXT("CircleBox"), Icon16)); this->Set("MDSStyleSet.Graph.CornerImage", new IMAGE_BRUSH(TEXT("Icon128"), FVector2D(128.0f, 128.0f))); this->Set("MDSStyleSet.Icon.Browse", new IMAGE_BRUSH(TEXT("BrowseIcon"), Icon12)); this->Set("MDSStyleSet.Icon.Edit", new IMAGE_BRUSH(TEXT("EditIcon"), Icon12)); this->Set("MDSStyleSet.Buttons.Documentation", new IMAGE_BRUSH(TEXT("Documentation"), FVector2D(200.0f, 70.0f))); this->Set("MDSStyleSet.Buttons.Documentation.small", new IMAGE_BRUSH(TEXT("DocumentationIcon"), Icon12)); this->Set("MDSStyleSet.Node.Icon.large", new IMAGE_BRUSH(TEXT("DialogueNodeIcon"), Icon64)); this->Set("MDSStyleSet.Node.Icon", new IMAGE_BRUSH(TEXT("DialogueNodeIcon"), Icon16)); this->Set("MDSStyleSet.Node.Icon.small", new IMAGE_BRUSH(TEXT("DialogueNodeIcon"), Icon12)); this->Set("MDSStyleSet.Icon.Close", new IMAGE_BRUSH(TEXT("CloseIcon"), Icon12)); this->Set("MDSStyleSet.Icon.SupportDiscord", new IMAGE_BRUSH(TEXT("DiscordIcon"), Icon12)); this->Set("MDSStyleSet.Icon.HeartIcon", new IMAGE_BRUSH(TEXT("HeartIcon"), Icon12)); this->Set("MDSStyleSet.Icon.UBIcon", new IMAGE_BRUSH(TEXT("UnrealBucketIcon"), Icon12)); this->Set("MDSStyleSet.Icon.MoneyIcon", new IMAGE_BRUSH(TEXT("MoneyIcon"), Icon12)); this->Set("MDSStyleSet.Buttons.Style", FButtonStyle() .SetNormal(BOX_BRUSH("RoundedSelection_16x", 4.0f / 16.0f, FLinearColor(1, 1, 1, 0.1f))) .SetHovered(BOX_BRUSH("RoundedSelection_16x", 4.0f / 16.0f, FLinearColor(1, .55f, 0, 0.2f))) .SetPressed(BOX_BRUSH("RoundedSelection_16x", 4.0f / 16.0f, FLinearColor(1, .55f, 0, 0.4f)))); const FScrollBarStyle ScrollBar = GetWidgetStyle("ScrollBar"); FTextBlockStyle NormalText = FTextBlockStyle() .SetFont(DEFAULT_FONT("Regular", FCoreStyle::RegularTextSize)) .SetColorAndOpacity(FSlateColor::UseForeground()) .SetShadowOffset(FVector2D::ZeroVector) .SetShadowColorAndOpacity(FLinearColor::Black) .SetHighlightColor(FLinearColor(0.02f, 0.3f, 0.0f)) .SetHighlightShape(BOX_BRUSH("TextBlockHighlightShape", FMargin(3.f / 8.f))); FTextBlockStyle NodeTitle = FTextBlockStyle(NormalText) .SetFont(DEFAULT_FONT("Bold", 14)) .SetColorAndOpacity(FLinearColor(230.0f / 255.0f, 230.0f / 255.0f, 230.0f / 255.0f)) .SetShadowOffset(FVector2D(2, 2)) .SetShadowColorAndOpacity(FLinearColor(0.f, 0.f, 0.f, 0.7f)); this->Set("MDSStyleSet.NodeTitle", NodeTitle); FEditableTextBoxStyle NodeTitleEditableText = FEditableTextBoxStyle() .SetFont(NormalText.Font) .SetBackgroundImageNormal(BOX_BRUSH("TextBox", FMargin(4.0f / 16.0f))) .SetBackgroundImageHovered(BOX_BRUSH("TextBox_Hovered", FMargin(4.0f / 16.0f))) .SetBackgroundImageFocused(BOX_BRUSH("TextBox_Hovered", FMargin(4.0f / 16.0f))) .SetBackgroundImageReadOnly(BOX_BRUSH("TextBox_ReadOnly", FMargin(4.0f / 16.0f))) .SetScrollBarStyle(ScrollBar); this->Set("MDSStyleSet.NodeTitleEditableText", NodeTitleEditableText); this->Set("MDSStyleSet.NodeTitleInlineEditableText", FInlineEditableTextBlockStyle() .SetTextStyle(NodeTitle) .SetEditableTextBoxStyle(NodeTitleEditableText) ); } } void FComboInputEditorModule::StartupModule() { // Register combo action asset 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_ComboInputAsset)); // Make a new style set for Combo Input, which will register any custom icons for the types in this plugin this->ComboInputEditorStyleSet = MakeShared(); FSlateStyleRegistry::RegisterSlateStyle(*this->ComboInputEditorStyleSet.Get()); // Combo Action Graph { this->ComboActionGraphPanelNodeFactory = MakeShareable(new FComboActionGraphPanelNodeFactory()); FEdGraphUtilities::RegisterVisualNodeFactory(this->ComboActionGraphPanelNodeFactory); // Register asset actions this->ComboActionGraphAssetActions = MakeShared(); this->RegisterAssetTypeActions(AssetTools, this->ComboActionGraphAssetActions.ToSharedRef()); } FComboActionGraphEditorCommands::Register(); } void FComboInputEditorModule::ShutdownModule() { FComboActionGraphEditorCommands::Unregister(); } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FComboInputEditorModule, ComboInputEditorModule)