246 lines
7.5 KiB
C#
246 lines
7.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Ichni.RhythmGame;
|
|
using Ichni.RhythmGame.Beatmap;
|
|
using UnityEngine;
|
|
|
|
namespace Ichni.Editor.Commands
|
|
{
|
|
internal sealed class ElementTreeSnapshot
|
|
{
|
|
private readonly List<BaseElement_BM> elements = new List<BaseElement_BM>();
|
|
private readonly Guid rootGuid;
|
|
private readonly Guid parentGuid;
|
|
private readonly int parentChildIndex;
|
|
|
|
private ElementTreeSnapshot(GameElement root)
|
|
{
|
|
rootGuid = root.elementGuid;
|
|
parentGuid = root.parentElement?.elementGuid ?? Guid.Empty;
|
|
parentChildIndex = root.parentElement != null
|
|
? root.parentElement.childElementList.IndexOf(root)
|
|
: -1;
|
|
|
|
RegisterExistingAncestorChain(root.parentElement);
|
|
|
|
foreach (GameElement element in root.GetAllGameElementsFromThis())
|
|
{
|
|
if (element == null) continue;
|
|
element.SaveBM();
|
|
if (element.matchedBM != null)
|
|
elements.Add(element.matchedBM);
|
|
|
|
element.submoduleList.RemoveAll(submodule => submodule == null);
|
|
foreach (SubmoduleBase submodule in element.submoduleList)
|
|
{
|
|
submodule.SaveBM();
|
|
if (submodule.matchedBM != null)
|
|
elements.Add(submodule.matchedBM);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static ElementTreeSnapshot Capture(GameElement root)
|
|
{
|
|
return root == null ? null : new ElementTreeSnapshot(root);
|
|
}
|
|
|
|
public GameElement Restore()
|
|
{
|
|
if (elements.Count == 0) return null;
|
|
|
|
GameElement parent = parentGuid == Guid.Empty ? null : FindExistingElement(parentGuid);
|
|
RegisterExistingAncestorChain(parent);
|
|
RepairRootAttachment();
|
|
|
|
foreach (GameElement_BM gameElementBM in elements.OfType<GameElement_BM>())
|
|
{
|
|
GameElement_BM.identifier[gameElementBM.elementGuid] = gameElementBM;
|
|
}
|
|
|
|
foreach (BaseElement_BM elementBM in elements)
|
|
{
|
|
elementBM.ExecuteBM();
|
|
}
|
|
|
|
EditorManager.instance.beatmapContainer.ExecuteLowPriorityActions();
|
|
|
|
GameElement root = GameElement_BM.GetElement(rootGuid);
|
|
if (root == null) return null;
|
|
|
|
RestoreParentOrder(root, parent);
|
|
|
|
foreach (GameElement element in root.GetAllGameElementsFromThis())
|
|
{
|
|
if (element == null) continue;
|
|
element.AfterInitialize();
|
|
element.Refresh();
|
|
}
|
|
|
|
EditorManager.instance.uiManager.hierarchy.FindTab(root);
|
|
return root;
|
|
}
|
|
|
|
private GameElement FindExistingElement(Guid elementGuid)
|
|
{
|
|
GameElement element = GameElement_BM.GetElement(elementGuid);
|
|
if (element != null) return element;
|
|
|
|
return EditorManager.instance.beatmapContainer.gameElementList
|
|
.FirstOrDefault(gameElement => gameElement != null && gameElement.elementGuid == elementGuid);
|
|
}
|
|
|
|
private void RepairRootAttachment()
|
|
{
|
|
if (parentGuid == Guid.Empty) return;
|
|
|
|
GameElement_BM rootBM = elements
|
|
.OfType<GameElement_BM>()
|
|
.FirstOrDefault(gameElementBM => gameElementBM.elementGuid == rootGuid);
|
|
|
|
if (rootBM != null)
|
|
{
|
|
rootBM.attachedElementGuid = parentGuid;
|
|
}
|
|
}
|
|
|
|
private void RegisterExistingAncestorChain(GameElement parent)
|
|
{
|
|
if (parent == null) return;
|
|
|
|
List<GameElement> ancestors = new List<GameElement>();
|
|
for (GameElement current = parent; current != null; current = current.parentElement)
|
|
{
|
|
ancestors.Add(current);
|
|
}
|
|
|
|
ancestors.Reverse();
|
|
foreach (GameElement ancestor in ancestors)
|
|
{
|
|
ancestor.SaveBM();
|
|
if (ancestor.matchedBM is not GameElement_BM ancestorBM) continue;
|
|
|
|
GameElement_BM.identifier[ancestor.elementGuid] = ancestorBM;
|
|
ancestorBM.matchedElement = ancestor;
|
|
}
|
|
}
|
|
|
|
private void RestoreParentOrder(GameElement root, GameElement parent)
|
|
{
|
|
if (parent == null || parentChildIndex < 0) return;
|
|
|
|
parent.childElementList.Remove(root);
|
|
int insertIndex = Mathf.Clamp(parentChildIndex, 0, parent.childElementList.Count);
|
|
parent.childElementList.Insert(insertIndex, root);
|
|
root.transform.SetSiblingIndex(insertIndex);
|
|
parent.Refresh();
|
|
}
|
|
}
|
|
|
|
public sealed class DeleteElementCommand : ICommand
|
|
{
|
|
private readonly ElementTreeSnapshot snapshot;
|
|
private GameElement target;
|
|
|
|
public DeleteElementCommand(GameElement target)
|
|
{
|
|
this.target = target;
|
|
snapshot = ElementTreeSnapshot.Capture(target);
|
|
}
|
|
|
|
public void Execute()
|
|
{
|
|
DeleteCurrentTarget();
|
|
}
|
|
|
|
public void Undo()
|
|
{
|
|
target = snapshot?.Restore();
|
|
}
|
|
|
|
public void Redo()
|
|
{
|
|
DeleteCurrentTarget();
|
|
}
|
|
|
|
public bool TryMerge(ICommand other) => false;
|
|
|
|
private void DeleteCurrentTarget()
|
|
{
|
|
if (target == null) return;
|
|
EditorManager.instance.operationManager.CopyPasteDeleteModule.DeleteElementRaw(target);
|
|
target = null;
|
|
}
|
|
}
|
|
|
|
public sealed class PasteElementCommand : ICommand
|
|
{
|
|
private readonly CopyPasteDeleteModule module;
|
|
private readonly GameElement source;
|
|
private readonly GameElement parent;
|
|
private ElementTreeSnapshot snapshot;
|
|
private GameElement pastedRoot;
|
|
|
|
public PasteElementCommand(CopyPasteDeleteModule module, GameElement source, GameElement parent)
|
|
{
|
|
this.module = module;
|
|
this.source = source;
|
|
this.parent = parent;
|
|
}
|
|
|
|
public void Execute()
|
|
{
|
|
pastedRoot = module.PasteElementRaw(source, parent);
|
|
snapshot ??= ElementTreeSnapshot.Capture(pastedRoot);
|
|
}
|
|
|
|
public void Undo()
|
|
{
|
|
if (pastedRoot == null) return;
|
|
module.DeleteElementRaw(pastedRoot, false);
|
|
pastedRoot = null;
|
|
}
|
|
|
|
public void Redo()
|
|
{
|
|
pastedRoot = snapshot?.Restore();
|
|
}
|
|
|
|
public bool TryMerge(ICommand other) => false;
|
|
}
|
|
|
|
public sealed class CreateElementCommand : ICommand
|
|
{
|
|
private readonly Func<GameElement> createAction;
|
|
private ElementTreeSnapshot snapshot;
|
|
|
|
public GameElement CreatedElement { get; private set; }
|
|
|
|
public CreateElementCommand(Func<GameElement> createAction)
|
|
{
|
|
this.createAction = createAction;
|
|
}
|
|
|
|
public void Execute()
|
|
{
|
|
CreatedElement = createAction?.Invoke();
|
|
snapshot ??= ElementTreeSnapshot.Capture(CreatedElement);
|
|
}
|
|
|
|
public void Undo()
|
|
{
|
|
if (CreatedElement == null) return;
|
|
EditorManager.instance.operationManager.CopyPasteDeleteModule.DeleteElementRaw(CreatedElement, false);
|
|
CreatedElement = null;
|
|
}
|
|
|
|
public void Redo()
|
|
{
|
|
CreatedElement = snapshot?.Restore();
|
|
}
|
|
|
|
public bool TryMerge(ICommand other) => false;
|
|
}
|
|
}
|