@@ -1,5 +1,6 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.RhythmGame;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.Editor.Commands
|
||||
@@ -14,6 +15,8 @@ namespace Ichni.Editor.Commands
|
||||
/// </summary>
|
||||
public static void ExecuteCommand(ICommand command)
|
||||
{
|
||||
if (command == null) return;
|
||||
|
||||
if (undoStack.Count > 0)
|
||||
{
|
||||
var topCommand = undoStack.Peek();
|
||||
@@ -28,7 +31,14 @@ namespace Ichni.Editor.Commands
|
||||
// 新操作彻底切断原本可能存在的重做未来
|
||||
command.Execute();
|
||||
undoStack.Push(command);
|
||||
redoStack.Clear();
|
||||
redoStack.Clear();
|
||||
}
|
||||
|
||||
public static GameElement ExecuteCreate(Func<GameElement> createAction)
|
||||
{
|
||||
var command = new CreateElementCommand(createAction);
|
||||
ExecuteCommand(command);
|
||||
return command.CreatedElement;
|
||||
}
|
||||
|
||||
public static void Undo()
|
||||
|
||||
245
Assets/Scripts/CommandSystem/GameElementStructuralCommands.cs
Normal file
245
Assets/Scripts/CommandSystem/GameElementStructuralCommands.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96273157236b1a54fbf492d88d4c540f
|
||||
Reference in New Issue
Block a user