336 lines
10 KiB
C++
336 lines
10 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "LyraEditor.h"
|
|
|
|
#include "AbilitySystemGlobals.h"
|
|
#include "AssetToolsModule.h"
|
|
#include "Containers/Array.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "DataValidationModule.h"
|
|
#include "Delegates/Delegate.h"
|
|
#include "Development/LyraDeveloperSettings.h"
|
|
#include "Editor.h"
|
|
#include "Editor/EditorEngine.h"
|
|
#include "Editor/UnrealEdEngine.h"
|
|
#include "Engine/Engine.h"
|
|
#include "Engine/GameInstance.h"
|
|
#include "Engine/World.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Framework/Commands/UIAction.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Framework/SlateDelegates.h"
|
|
#include "GameEditorStyle.h"
|
|
#include "GameFramework/Actor.h"
|
|
#include "GameModes/LyraExperienceManager.h"
|
|
#include "GameplayAbilitiesEditorModule.h"
|
|
#include "GameplayAbilitiesModule.h"
|
|
#include "GameplayCueInterface.h"
|
|
#include "GameplayCueNotify_Burst.h"
|
|
#include "GameplayCueNotify_BurstLatent.h"
|
|
#include "GameplayCueNotify_Looping.h"
|
|
#include "HAL/Platform.h"
|
|
#include "IAssetTools.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "Internationalization/Text.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Misc/Attribute.h"
|
|
#include "Misc/CoreMisc.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "Private/AssetTypeActions_LyraContextEffectsLibrary.h"
|
|
#include "Subsystems/AssetEditorSubsystem.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Templates/SharedPointer.h"
|
|
#include "Textures/SlateIcon.h"
|
|
#include "ToolMenu.h"
|
|
#include "ToolMenuEntry.h"
|
|
#include "ToolMenuMisc.h"
|
|
#include "ToolMenuSection.h"
|
|
#include "ToolMenus.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/NameTypes.h"
|
|
#include "UObject/ObjectPtr.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "UnrealEdGlobals.h"
|
|
#include "Validation/EditorValidator.h"
|
|
|
|
class SWidget;
|
|
|
|
#define LOCTEXT_NAMESPACE "LyraEditor"
|
|
|
|
DEFINE_LOG_CATEGORY(LogLyraEditor);
|
|
|
|
// This function tells the GameplayCue editor what classes to expose when creating new notifies.
|
|
static void GetGameplayCueDefaultClasses(TArray<UClass*>& Classes)
|
|
{
|
|
Classes.Empty();
|
|
Classes.Add(UGameplayCueNotify_Burst::StaticClass());
|
|
Classes.Add(AGameplayCueNotify_BurstLatent::StaticClass());
|
|
Classes.Add(AGameplayCueNotify_Looping::StaticClass());
|
|
}
|
|
|
|
// This function tells the GameplayCue editor what classes to search for GameplayCue events.
|
|
static void GetGameplayCueInterfaceClasses(TArray<UClass*>& Classes)
|
|
{
|
|
Classes.Empty();
|
|
|
|
for (UClass* Class : TObjectRange<UClass>())
|
|
{
|
|
if (Class->IsChildOf<AActor>() && Class->ImplementsInterface(UGameplayCueInterface::StaticClass()))
|
|
{
|
|
Classes.Add(Class);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This function tells the GameplayCue editor where to create the GameplayCue notifies based on their tag.
|
|
static FString GetGameplayCuePath(FString GameplayCueTag)
|
|
{
|
|
FString Path = FString(TEXT("/Game"));
|
|
|
|
//@TODO: Try plugins (e.g., GameplayCue.ShooterGame.Foo should be in ShooterCore or something)
|
|
|
|
// Default path to the first entry in the UAbilitySystemGlobals::GameplayCueNotifyPaths.
|
|
if (IGameplayAbilitiesModule::IsAvailable())
|
|
{
|
|
IGameplayAbilitiesModule& GameplayAbilitiesModule = IGameplayAbilitiesModule::Get();
|
|
|
|
if (GameplayAbilitiesModule.IsAbilitySystemGlobalsAvailable())
|
|
{
|
|
UAbilitySystemGlobals* AbilitySystemGlobals = GameplayAbilitiesModule.GetAbilitySystemGlobals();
|
|
check(AbilitySystemGlobals);
|
|
|
|
TArray<FString> GetGameplayCueNotifyPaths = AbilitySystemGlobals->GetGameplayCueNotifyPaths();
|
|
|
|
if (GetGameplayCueNotifyPaths.Num() > 0)
|
|
{
|
|
Path = GetGameplayCueNotifyPaths[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
GameplayCueTag.RemoveFromStart(TEXT("GameplayCue."));
|
|
|
|
FString NewDefaultPathName = FString::Printf(TEXT("%s/GCN_%s"), *Path, *GameplayCueTag);
|
|
|
|
return NewDefaultPathName;
|
|
}
|
|
|
|
static bool HasPlayWorld()
|
|
{
|
|
return GEditor->PlayWorld != nullptr;
|
|
}
|
|
|
|
static bool HasNoPlayWorld()
|
|
{
|
|
return !HasPlayWorld();
|
|
}
|
|
|
|
static bool HasPlayWorldAndRunning()
|
|
{
|
|
return HasPlayWorld() && !GUnrealEd->PlayWorld->bDebugPauseExecution;
|
|
}
|
|
|
|
static void OpenCommonMap_Clicked(const FString MapPath)
|
|
{
|
|
if (ensure(MapPath.Len()))
|
|
{
|
|
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(MapPath);
|
|
}
|
|
}
|
|
|
|
static bool CanShowCommonMaps()
|
|
{
|
|
return HasNoPlayWorld() && !GetDefault<ULyraDeveloperSettings>()->CommonEditorMaps.IsEmpty();
|
|
}
|
|
|
|
static TSharedRef<SWidget> GetCommonMapsDropdown()
|
|
{
|
|
FMenuBuilder MenuBuilder(true, nullptr);
|
|
|
|
for (const FSoftObjectPath& Path : GetDefault<ULyraDeveloperSettings>()->CommonEditorMaps)
|
|
{
|
|
if (!Path.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FText DisplayName = FText::FromString(Path.GetAssetName());
|
|
MenuBuilder.AddMenuEntry(
|
|
DisplayName,
|
|
LOCTEXT("CommonPathDescription", "Opens this map in the editor"),
|
|
FSlateIcon(),
|
|
FUIAction(
|
|
FExecuteAction::CreateStatic(&OpenCommonMap_Clicked, Path.ToString()),
|
|
FCanExecuteAction::CreateStatic(&HasNoPlayWorld),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateStatic(&HasNoPlayWorld)
|
|
)
|
|
);
|
|
}
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
static void AddPlayer_Clicked()
|
|
{
|
|
if (ensure(GEditor->PlayWorld))
|
|
{
|
|
if (UGameInstance* GameInstance = GEditor->PlayWorld->GetGameInstance())
|
|
{
|
|
if (GameInstance->GetNumLocalPlayers() == 1)
|
|
{
|
|
GameInstance->DebugCreatePlayer(1);
|
|
}
|
|
else
|
|
{
|
|
GameInstance->DebugRemovePlayer(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static TSharedRef<SWidget> AddLocalPlayer()
|
|
{
|
|
FMenuBuilder MenuBuilder(true, nullptr);
|
|
|
|
MenuBuilder.AddMenuEntry(
|
|
LOCTEXT("SplitscreenButton", "Splitscreen"),
|
|
LOCTEXT("SplitscreenDescription", "Adds/Removes a Splitscreen Player to the current PIE session"),
|
|
FSlateIcon(),
|
|
FUIAction(
|
|
FExecuteAction::CreateStatic(&AddPlayer_Clicked),
|
|
FCanExecuteAction::CreateStatic(&HasPlayWorld),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateStatic(&HasPlayWorld)
|
|
)
|
|
);
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
static void CheckGameContent_Clicked()
|
|
{
|
|
UEditorValidator::ValidateCheckedOutContent(/*bInteractive=*/true, EDataValidationUsecase::Manual);
|
|
}
|
|
|
|
static void RegisterGameEditorMenus()
|
|
{
|
|
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar");
|
|
FToolMenuSection& Section = Menu->AddSection("PlayGameExtensions", TAttribute<FText>(), FToolMenuInsert("Play", EToolMenuInsertType::After));
|
|
|
|
FToolMenuEntry BlueprintEntry = FToolMenuEntry::InitComboButton(
|
|
"OpenGameMenu",
|
|
FUIAction(
|
|
FExecuteAction(),
|
|
FCanExecuteAction::CreateStatic(&HasPlayWorld),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateStatic(&HasPlayWorld)),
|
|
FOnGetContent::CreateStatic(&AddLocalPlayer),
|
|
LOCTEXT("GameOptions_Label", "Game Options"),
|
|
LOCTEXT("GameOptions_ToolTip", "Game Options"),
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.OpenLevelBlueprint")
|
|
);
|
|
BlueprintEntry.StyleNameOverride = "CalloutToolbar";
|
|
Section.AddEntry(BlueprintEntry);
|
|
|
|
FToolMenuEntry CheckContentEntry = FToolMenuEntry::InitToolBarButton(
|
|
"CheckContent",
|
|
FUIAction(
|
|
FExecuteAction::CreateStatic(&CheckGameContent_Clicked),
|
|
FCanExecuteAction::CreateStatic(&HasNoPlayWorld),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateStatic(&HasNoPlayWorld)),
|
|
LOCTEXT( "CheckContentButton", "Check Content" ),
|
|
LOCTEXT( "CheckContentDescription", "Runs the Content Validation job on all checked out assets to look for warnings and errors" ),
|
|
FSlateIcon(FGameEditorStyle::GetStyleSetName(), "GameEditor.CheckContent")
|
|
);
|
|
CheckContentEntry.StyleNameOverride = "CalloutToolbar";
|
|
Section.AddEntry(CheckContentEntry);
|
|
|
|
FToolMenuEntry CommonMapEntry = FToolMenuEntry::InitComboButton(
|
|
"CommonMapOptions",
|
|
FUIAction(
|
|
FExecuteAction(),
|
|
FCanExecuteAction::CreateStatic(&HasNoPlayWorld),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateStatic(&CanShowCommonMaps)),
|
|
FOnGetContent::CreateStatic(&GetCommonMapsDropdown),
|
|
LOCTEXT("CommonMaps_Label", "Common Maps"),
|
|
LOCTEXT("CommonMaps_ToolTip", "Some commonly desired maps while using the editor"),
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Level")
|
|
);
|
|
CommonMapEntry.StyleNameOverride = "CalloutToolbar";
|
|
Section.AddEntry(CommonMapEntry);
|
|
}
|
|
|
|
/**
|
|
* FLyraEditorModule
|
|
*/
|
|
class FLyraEditorModule : public FDefaultGameModuleImpl
|
|
{
|
|
typedef FLyraEditorModule ThisClass;
|
|
|
|
virtual void StartupModule() override
|
|
{
|
|
FGameEditorStyle::Initialize();
|
|
|
|
if (!IsRunningGame())
|
|
{
|
|
FModuleManager::Get().OnModulesChanged().AddRaw(this, &FLyraEditorModule::ModulesChangedCallback);
|
|
|
|
BindGameplayAbilitiesEditorDelegates();
|
|
|
|
if (FSlateApplication::IsInitialized())
|
|
{
|
|
UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateStatic(&RegisterGameEditorMenus));
|
|
}
|
|
|
|
FEditorDelegates::BeginPIE.AddRaw(this, &ThisClass::OnBeginPIE);
|
|
FEditorDelegates::EndPIE.AddRaw(this, &ThisClass::OnEndPIE);
|
|
}
|
|
|
|
// Register the Context Effects Library asset type actions.
|
|
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
|
|
|
AssetTools.RegisterAssetTypeActions(MakeShareable(new FAssetTypeActions_LyraContextEffectsLibrary));
|
|
}
|
|
|
|
void OnBeginPIE(bool bIsSimulating)
|
|
{
|
|
ULyraExperienceManager* ExperienceManager = GEngine->GetEngineSubsystem<ULyraExperienceManager>();
|
|
check(ExperienceManager);
|
|
ExperienceManager->OnPlayInEditorBegun();
|
|
}
|
|
|
|
void OnEndPIE(bool bIsSimulating)
|
|
{
|
|
}
|
|
|
|
virtual void ShutdownModule() override
|
|
{
|
|
FModuleManager::Get().OnModulesChanged().RemoveAll(this);
|
|
}
|
|
|
|
protected:
|
|
|
|
static void BindGameplayAbilitiesEditorDelegates()
|
|
{
|
|
IGameplayAbilitiesEditorModule& GameplayAbilitiesEditorModule = IGameplayAbilitiesEditorModule::Get();
|
|
|
|
GameplayAbilitiesEditorModule.GetGameplayCueNotifyClassesDelegate().BindStatic(&GetGameplayCueDefaultClasses);
|
|
GameplayAbilitiesEditorModule.GetGameplayCueInterfaceClassesDelegate().BindStatic(&GetGameplayCueInterfaceClasses);
|
|
GameplayAbilitiesEditorModule.GetGameplayCueNotifyPathDelegate().BindStatic(&GetGameplayCuePath);
|
|
}
|
|
|
|
void ModulesChangedCallback(FName ModuleThatChanged, EModuleChangeReason ReasonForChange)
|
|
{
|
|
if ((ReasonForChange == EModuleChangeReason::ModuleLoaded) && (ModuleThatChanged.ToString() == TEXT("GameplayAbilitiesEditor")))
|
|
{
|
|
BindGameplayAbilitiesEditorDelegates();
|
|
}
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MODULE(FLyraEditorModule, LyraEditor);
|
|
|
|
#undef LOCTEXT_NAMESPACE |