HeavenStudioPlus/Assets/Scripts/LevelEditor/Editor.cs
AstrlJelly 99f525faef Editor Additions (#479)
* favoriting and pick block

favoriting needs to not break after previewing (maybe make it persistent in the settings file?)
pick block needs to pick the icon, too. and preferably scroll to it as well

* final star anim + automatic icon game switching

before i make any more changes im making a checkpoint here cuz i know it works
* i want to add a way to specify which event SwitchGame() will switch to (because that's a cool feature for pick block)
* i'll have to figure out how to auto scroll to the game when the icon is selected
* the star now fully works, even between preview switches 👍

* fix the rest of the stuff

the event name gets colored correctly and hidden games are skipped over, but still loaded.
also i built mm ass buns

* tweaks + zoom and sorting

this stuff will be in the pr desc so it doesn't matter

* oop one more thing

* icons look better now :D

mipmaps to the rescue

* double date fix
2023-06-19 16:40:50 -04:00

480 lines
17 KiB
C#

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using Newtonsoft.Json;
using TMPro;
using Starpelly;
using SFB;
using HeavenStudio.Common;
using HeavenStudio.Editor.Track;
using HeavenStudio.Util;
using HeavenStudio.StudioDance;
using Jukebox;
using System.IO.Compression;
using System.Text;
namespace HeavenStudio.Editor
{
public class Editor : MonoBehaviour
{
private GameInitializer Initializer;
[SerializeField] public Canvas MainCanvas;
[SerializeField] public Camera EditorCamera;
[Header("Rect")]
[SerializeField] private RenderTexture ScreenRenderTexture;
[SerializeField] private RawImage Screen;
[SerializeField] private RectTransform GridGameSelector;
public RectTransform eventSelectorBG;
[Header("Components")]
[SerializeField] private Timeline Timeline;
[SerializeField] private TMP_Text GameEventSelectorTitle;
[SerializeField] private TMP_Text BuildDateDisplay;
[SerializeField] public StudioDanceManager StudioDanceManager;
[Header("Toolbar")]
[SerializeField] private Button NewBTN;
[SerializeField] private Button OpenBTN;
[SerializeField] private Button SaveBTN;
[SerializeField] private Button UndoBTN;
[SerializeField] private Button RedoBTN;
[SerializeField] private Button MusicSelectBTN;
[SerializeField] private Button FullScreenBTN;
[SerializeField] private Button TempoFinderBTN;
[SerializeField] private Button SnapDiagBTN;
[SerializeField] private Button ChartParamBTN;
[SerializeField] private Button SortGamesBTN;
[SerializeField] private Button EditorThemeBTN;
[SerializeField] private Button EditorSettingsBTN;
[Header("Dialogs")]
[SerializeField] private Dialog[] Dialogs;
[Header("Tooltip")]
public TMP_Text tooltipText;
[Header("Properties")]
private bool changedMusic = false;
private bool loadedMusic = false;
private string currentRemixPath = "";
private string remixName = "";
public bool fullscreen;
public bool discordDuringTesting = false;
public bool canSelect = true;
public bool editingInputField = false;
public bool inAuthorativeMenu = false;
public bool isCursorEnabled = true;
public bool isDiscordEnabled = true;
public bool isShortcutsEnabled { get { return (!inAuthorativeMenu) && (!editingInputField); } }
public static Editor instance { get; private set; }
private void Start()
{
instance = this;
Initializer = GetComponent<GameInitializer>();
canSelect = true;
}
public void Init()
{
GameManager.instance.StaticCamera.targetTexture = ScreenRenderTexture;
GameManager.instance.CursorCam.targetTexture = ScreenRenderTexture;
Screen.texture = ScreenRenderTexture;
GameManager.instance.Init();
Timeline.Init();
foreach (var minigame in EventCaller.instance.minigames)
AddIcon(minigame);
Tooltip.AddTooltip(NewBTN.gameObject, "New <color=#adadad>[Ctrl+N]</color>");
Tooltip.AddTooltip(OpenBTN.gameObject, "Open <color=#adadad>[Ctrl+O]</color>");
Tooltip.AddTooltip(SaveBTN.gameObject, "Save Project <color=#adadad>[Ctrl+S]</color>\nSave Project As <color=#adadad>[Ctrl+Alt+S]</color>");
Tooltip.AddTooltip(UndoBTN.gameObject, "Undo <color=#adadad>[Ctrl+Z]</color>");
Tooltip.AddTooltip(RedoBTN.gameObject, "Redo <color=#adadad>[Ctrl+Y or Ctrl+Shift+Z]</color>");
Tooltip.AddTooltip(MusicSelectBTN.gameObject, "Music Select");
Tooltip.AddTooltip(FullScreenBTN.gameObject, "Preview <color=#adadad>[Tab]</color>");
Tooltip.AddTooltip(TempoFinderBTN.gameObject, "Tempo Finder");
Tooltip.AddTooltip(SnapDiagBTN.gameObject, "Snap Settings");
Tooltip.AddTooltip(ChartParamBTN.gameObject, "Remix Properties");
Tooltip.AddTooltip(SortGamesBTN.gameObject, "Sort By Favorite");
Tooltip.AddTooltip(EditorSettingsBTN.gameObject, "Editor Settings <color=#adadad>[Ctrl+Shift+O]</color>");
UpdateEditorStatus(true);
BuildDateDisplay.text = GlobalGameManager.buildTime;
isCursorEnabled = PersistentDataManager.gameSettings.editorCursorEnable;
isDiscordEnabled = PersistentDataManager.gameSettings.discordRPCEnable;
GameManager.instance.CursorCam.enabled = isCursorEnabled;
}
public void AddIcon(Minigames.Minigame minigame)
{
if (minigame.hidden) return;
GameObject GameIcon_ = Instantiate(GridGameSelector.GetChild(0).gameObject, GridGameSelector);
GameIcon_.GetComponent<Image>().sprite = GameIcon(minigame.name);
GameIcon_.GetComponent<GridGameSelectorGame>().MaskTex = GameIconMask(minigame.name);
GameIcon_.GetComponent<GridGameSelectorGame>().UnClickIcon();
GameIcon_.gameObject.SetActive(true);
GameIcon_.name = minigame.name;
}
public void LateUpdate()
{
#region Keyboard Shortcuts
if (isShortcutsEnabled)
{
if (Input.GetKeyDown(KeyCode.Tab))
{
Fullscreen();
}
if (Input.GetKeyDown(KeyCode.Delete) || Input.GetKeyDown(KeyCode.Backspace))
{
List<TimelineEventObj> ev = new List<TimelineEventObj>();
for (int i = 0; i < Selections.instance.eventsSelected.Count; i++) ev.Add(Selections.instance.eventsSelected[i]);
CommandManager.instance.Execute(new Commands.Deletion(ev));
}
if (Input.GetKey(KeyCode.LeftControl))
{
if (Input.GetKeyDown(KeyCode.Z))
{
if (Input.GetKey(KeyCode.LeftShift))
CommandManager.instance.Redo();
else
CommandManager.instance.Undo();
}
else if (Input.GetKeyDown(KeyCode.Y))
{
CommandManager.instance.Redo();
}
if (Input.GetKey(KeyCode.LeftShift))
{
if (Input.GetKeyDown(KeyCode.D))
{
ToggleDebugCam();
}
}
}
if (Input.GetKey(KeyCode.LeftControl))
{
if (Input.GetKeyDown(KeyCode.N))
{
NewBTN.onClick.Invoke();
}
else if (Input.GetKeyDown(KeyCode.O))
{
OpenRemix();
}
else if (Input.GetKey(KeyCode.LeftAlt))
{
if (Input.GetKeyDown(KeyCode.S))
{
SaveRemix(true);
}
}
else if (Input.GetKeyDown(KeyCode.S))
{
SaveRemix(false);
}
}
}
#endregion
if (CommandManager.instance.canUndo())
UndoBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white;
else
UndoBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray;
if (CommandManager.instance.canRedo())
RedoBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white;
else
RedoBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray;
if (Timeline.instance.timelineState.selected && Editor.instance.canSelect)
{
if (Input.GetMouseButtonUp(0))
{
List<TimelineEventObj> selectedEvents = Timeline.instance.eventObjs.FindAll(c => c.selected == true && c.eligibleToMove == true);
if (selectedEvents.Count > 0)
{
List<TimelineEventObj> result = new List<TimelineEventObj>();
for (int i = 0; i < selectedEvents.Count; i++)
{
//TODO: this is in LateUpdate, so this will never run! change this to something that works properly
if (!(selectedEvents[i].isCreating || selectedEvents[i].wasDuplicated))
{
result.Add(selectedEvents[i]);
}
selectedEvents[i].OnUp();
}
CommandManager.instance.Execute(new Commands.Move(result));
}
}
}
}
public static Sprite GameIcon(string name)
{
return Resources.Load<Sprite>($"Sprites/Editor/GameIcons/{name}");
}
public static Texture GameIconMask(string name)
{
return Resources.Load<Texture>($"Sprites/Editor/GameIcons/{name}_mask");
}
#region Dialogs
public void SelectMusic()
{
var extensions = new[]
{
new ExtensionFilter("Music Files", "mp3", "ogg", "wav")
};
#if UNITY_STANDALONE_WINDOWS
StandaloneFileBrowser.OpenFilePanelAsync("Open File", "", extensions, false, async (string[] paths) =>
{
if (paths.Length > 0)
{
try
{
if (paths.Length == 0) return;
RiqFileHandler.WriteSong(paths[0]);
StartCoroutine(LoadMusic());
return;
}
catch (System.Exception e)
{
Debug.Log($"Error selecting music file: {e.Message}");
Debug.LogException(e);
GlobalGameManager.ShowErrorMessage("Error", e.Message);
return;
}
}
await Task.Yield();
}
);
#else
StandaloneFileBrowser.OpenFilePanelAsync("Open File", "", extensions, false, async (string[] paths) =>
{
if (paths.Length > 0)
{
try
{
if (paths.Length == 0) return;
RiqFileHandler.WriteSong(paths[0]);
StartCoroutine(LoadMusic());
return;
}
catch (System.Exception e)
{
Debug.Log($"Error selecting music file: {e.Message}");
Debug.LogException(e);
GlobalGameManager.ShowErrorMessage("Error", e.Message);
return;
}
}
await Task.Yield();
}
);
#endif
}
IEnumerator LoadMusic()
{
yield return GameManager.instance.LoadMusic();
Timeline.FitToSong();
// Timeline.CreateWaveform();
}
public void SaveRemix(bool saveAs = true)
{
if (saveAs == true)
{
SaveRemixFilePanel();
}
else
{
if (currentRemixPath == string.Empty || currentRemixPath == null)
{
SaveRemixFilePanel();
}
else
{
SaveRemixFile(currentRemixPath);
}
}
}
private void SaveRemixFilePanel()
{
var extensions = new[]
{
new ExtensionFilter("Heaven Studio Remix File", "riq")
};
StandaloneFileBrowser.SaveFilePanelAsync("Save Remix As", "", "remix_level", extensions, (string path) =>
{
if (path != String.Empty)
{
SaveRemixFile(path);
}
});
}
private void SaveRemixFile(string path)
{
try
{
RiqFileHandler.WriteRiq(GameManager.instance.Beatmap);
RiqFileHandler.PackRiq(path, true);
Debug.Log("Packed RIQ successfully!");
return;
}
catch (System.Exception e)
{
Debug.Log($"Error packing RIQ: {e.Message}");
return;
}
}
public void NewRemix()
{
if (Timeline.instance != null)
Timeline.instance?.Stop(0);
else
GameManager.instance.Stop(0);
LoadRemix(true);
}
public void LoadRemix(bool create = false)
{
if (create)
{
GameManager.instance.NewRemix();
currentRemixPath = string.Empty;
}
else
{
GameManager.instance.LoadRemix(true);
}
Timeline.instance.LoadRemix();
Timeline.FitToSong();
}
public void OpenRemix()
{
var extensions = new[]
{
new ExtensionFilter("Heaven Studio Remix File ", new string[] { "riq" }),
};
StandaloneFileBrowser.OpenFilePanelAsync("Open Remix", "", extensions, false, (string[] paths) =>
{
var path = Path.Combine(paths);
if (path == string.Empty) return;
try
{
string tmpDir = RiqFileHandler.ExtractRiq(path);
Debug.Log("Imported RIQ successfully!");
LoadRemix();
}
catch (System.Exception e)
{
Debug.Log($"Error importing RIQ: {e.Message}");
Debug.LogException(e);
GlobalGameManager.ShowErrorMessage("Error Loading RIQ", e.Message + "\n\n" + e.StackTrace);
return;
}
StartCoroutine(LoadMusic());
currentRemixPath = path;
remixName = Path.GetFileName(path);
UpdateEditorStatus(false);
CommandManager.instance.Clear();
});
}
#endregion
public void Fullscreen()
{
MainCanvas.gameObject.SetActive(fullscreen);
if (fullscreen == false)
{
MainCanvas.enabled = false;
EditorCamera.enabled = false;
GameManager.instance.StaticCamera.targetTexture = null;
GameManager.instance.CursorCam.enabled = false;
fullscreen = true;
}
else
{
MainCanvas.enabled = true;
EditorCamera.enabled = true;
GameManager.instance.StaticCamera.targetTexture = ScreenRenderTexture;
GameManager.instance.CursorCam.enabled = true && isCursorEnabled;
fullscreen = false;
GameCamera.instance.camera.rect = new Rect(0, 0, 1, 1);
GameManager.instance.CursorCam.rect = new Rect(0, 0, 1, 1);
GameManager.instance.OverlayCamera.rect = new Rect(0, 0, 1, 1);
EditorCamera.rect = new Rect(0, 0, 1, 1);
}
Timeline.AutoBtnUpdate();
}
private void UpdateEditorStatus(bool updateTime)
{
GlobalGameManager.UpdateDiscordStatus($"{remixName}", true, updateTime);
}
public void SetGameEventTitle(string txt)
{
GameEventSelectorTitle.text = txt;
}
public static bool MouseInRectTransform(RectTransform rectTransform)
{
return (rectTransform.gameObject.activeSelf && RectTransformUtility.RectangleContainsScreenPoint(rectTransform, Input.mousePosition, Editor.instance.EditorCamera));
}
public void ToggleDebugCam()
{
var game = GameManager.instance.currentGameO;
if (game != null)
{
foreach(FreeCam c in game.GetComponentsInChildren<FreeCam>(true))
{
c.enabled = !c.enabled;
}
}
}
}
}