public override void Undo(BeatmapActionContainer.BeatmapActionParams param) { if (_originalObject != _editedObject || _editedData._time.CompareTo(_originalData._time) != 0) { _collection.DeleteObject(_editedObject, false, false); SelectionController.Deselect(_editedObject, false); _originalObject.Apply(_originalData); _collection.SpawnObject(_originalObject, false, !InCollection); } else { // This is an optimisation only possible if the object has not changed position in the SortedSet _originalObject.Apply(_originalData); if (!InCollection) { RefreshPools(Data); } } SelectionController.Select(_originalObject, _addToSelection, true, !InCollection); }
public void MirrorInTime() { _mirror.MirrorTime(); // Check we can still delete our objects var toDelete = _notesContainer.UnsortedObjects.FirstOrDefault(); _notesContainer.DeleteObject(toDelete); Assert.AreEqual(1, _notesContainer.LoadedObjects.Count); _actionContainer.Undo(); Assert.AreEqual(2, _notesContainer.LoadedObjects.Count); NoteTest.CheckNote(_notesContainer, 0, 2, BeatmapNote.NOTE_TYPE_B, BeatmapNote.LINE_INDEX_FAR_RIGHT, BeatmapNote.LINE_LAYER_TOP, BeatmapNote.NOTE_CUT_DIRECTION_UP_RIGHT); NoteTest.CheckNote(_notesContainer, 1, 3, BeatmapNote.NOTE_TYPE_A, BeatmapNote.LINE_INDEX_FAR_LEFT, BeatmapNote.LINE_LAYER_BOTTOM, BeatmapNote.NOTE_CUT_DIRECTION_LEFT); // Undo mirror _actionContainer.Undo(); NoteTest.CheckNote(_notesContainer, 0, 2, BeatmapNote.NOTE_TYPE_A, BeatmapNote.LINE_INDEX_FAR_LEFT, BeatmapNote.LINE_LAYER_BOTTOM, BeatmapNote.NOTE_CUT_DIRECTION_LEFT); NoteTest.CheckNote(_notesContainer, 1, 3, BeatmapNote.NOTE_TYPE_B, BeatmapNote.LINE_INDEX_FAR_RIGHT, BeatmapNote.LINE_LAYER_TOP, BeatmapNote.NOTE_CUT_DIRECTION_UP_RIGHT); }
public IEnumerator LoadObjects <T>(IEnumerable <T> objects) where T : BeatmapObject { if (!objects.Any()) { yield break; } BeatmapObjectContainerCollection collection = BeatmapObjectContainerCollection.GetCollectionForType((objects.First() as T).beatmapType); if (collection == null) { yield break; } foreach (BeatmapObjectContainer obj in new List <BeatmapObjectContainer>(collection.LoadedContainers)) { collection.DeleteObject(obj); } PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(true); Queue <T> queuedData = new Queue <T>(objects); batchSize = Settings.Instance.InitialLoadBatchSize; totalObjectsToLoad = queuedData.Count; totalObjectsLoaded = 0; while (queuedData.Count > 0) { //Batch loading is loading a certain amount of objects (Batch Size) every frame, so at least ChroMapper remains active. for (int i = 0; i < batchSize; i++) { if (queuedData.Count == 0) { break; } BeatmapObject data = queuedData.Dequeue(); //Dequeue and load them into ChroMapper. BeatmapObjectContainer obj = collection.SpawnObject(data, false, false); if (data is BeatmapNote noteData) { if (noteData._lineIndex >= 1000 || noteData._lineIndex <= -1000 || noteData._lineLayer >= 1000 || noteData._lineLayer <= -1000) { continue; } if (2 - noteData._lineIndex > noteLaneSize) { noteLaneSize = 2 - noteData._lineIndex; } if (noteData._lineIndex - 1 > noteLaneSize) { noteLaneSize = noteData._lineIndex - 1; } if (noteData._lineLayer + 1 > noteLayerSize) { noteLayerSize = noteData._lineLayer + 1; } } else if (data is BeatmapObstacle obstacleData) { if (obstacleData._lineIndex >= 1000 || obstacleData._lineIndex <= -1000) { continue; } if (2 - obstacleData._lineIndex > noteLaneSize) { noteLaneSize = 2 - obstacleData._lineIndex; } if (obstacleData._lineIndex - 1 > noteLaneSize) { noteLaneSize = obstacleData._lineIndex - 1; } } } UpdateSlider <T>(batchSize); yield return(new WaitForEndOfFrame()); } collection.RemoveConflictingObjects(); if (objects.First() is BeatmapNote || objects.First() is BeatmapObstacle) { if (Settings.NonPersistentSettings.ContainsKey("NoteLanes")) { Settings.NonPersistentSettings["NoteLanes"] = (noteLaneSize * 2).ToString(); } else { Settings.NonPersistentSettings.Add("NoteLanes", (noteLaneSize * 2).ToString()); } noteLanesController.UpdateNoteLanes((noteLaneSize * 2).ToString()); } if (objects.First() is MapEvent) { manager.RefreshTracks(); } PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(false); }
public IEnumerator LoadObjects <T>(IEnumerable <T> objects) where T : BeatmapObject { if (!objects.Any()) { yield break; } BeatmapObjectContainerCollection collection = BeatmapObjectContainerCollection.GetCollectionForType(objects.First().beatmapType); if (collection == null) { yield break; } foreach (BeatmapObject obj in collection.LoadedObjects.ToArray()) { collection.DeleteObject(obj, false, false); } PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(true); collection.LoadedObjects = new SortedSet <BeatmapObject>(objects, new BeatmapObjectComparer()); collection.UnsortedObjects = collection.LoadedObjects.ToList(); UpdateSlider <T>(); if (typeof(T) == typeof(BeatmapNote) || typeof(T) == typeof(BeatmapObstacle)) { for (int i = 0; i < objects.Count(); i++) { BeatmapObject data = objects.ElementAt(i); if (data is BeatmapNote noteData) { if (noteData._lineIndex >= 1000 || noteData._lineIndex <= -1000 || noteData._lineLayer >= 1000 || noteData._lineLayer <= -1000) { continue; } if (2 - noteData._lineIndex > noteLaneSize) { noteLaneSize = 2 - noteData._lineIndex; } if (noteData._lineIndex - 1 > noteLaneSize) { noteLaneSize = noteData._lineIndex - 1; } if (noteData._lineLayer + 1 > noteLayerSize) { noteLayerSize = noteData._lineLayer + 1; } } else if (data is BeatmapObstacle obstacleData) { if (obstacleData._lineIndex >= 1000 || obstacleData._lineIndex <= -1000) { continue; } if (2 - obstacleData._lineIndex > noteLaneSize) { noteLaneSize = 2 - obstacleData._lineIndex; } if (obstacleData._lineIndex - 1 > noteLaneSize) { noteLaneSize = obstacleData._lineIndex - 1; } } } if (Settings.NonPersistentSettings.ContainsKey("NoteLanes")) { Settings.NonPersistentSettings["NoteLanes"] = (noteLaneSize * 2).ToString(); } else { Settings.NonPersistentSettings.Add("NoteLanes", (noteLaneSize * 2).ToString()); } noteLanesController.UpdateNoteLanes((noteLaneSize * 2).ToString()); } if (typeof(T) == typeof(MapEvent)) { manager.RefreshTracks(); EventsContainer events = collection as EventsContainer; events.AllRotationEvents = objects.Cast <MapEvent>().Where(x => x.IsRotationEvent).ToList(); events.AllBoostEvents = objects.Cast <MapEvent>().Where(x => x._type == MapEvent.EVENT_TYPE_BOOST_LIGHTS).ToList(); } collection.RefreshPool(true); }
/// <summary> /// Pastes any copied objects into the map, selecting them immediately. /// </summary> public void Paste(bool triggersAction = true, bool overwriteSection = false) { DeselectAll(); // Set up stuff that we need List <BeatmapObject> pasted = new List <BeatmapObject>(); Dictionary <BeatmapObject.Type, BeatmapObjectContainerCollection> collections = new Dictionary <BeatmapObject.Type, BeatmapObjectContainerCollection>(); // Grab the last BPM Change to warp distances between copied objects and maintain BPM. var bpmChanges = BeatmapObjectContainerCollection.GetCollectionForType <BPMChangesContainer>(BeatmapObject.Type.BPM_CHANGE); var lowerValue = new BeatmapBPMChange(420, atsc.CurrentBeat - 0.01f); var upperValue = new BeatmapBPMChange(69, atsc.CurrentBeat); var lastBPMChangeBeforePaste = bpmChanges.FindLastBPM(atsc.CurrentBeat, true); // This first loop creates copy of the data to be pasted. foreach (BeatmapObject data in CopiedObjects) { if (data == null) { continue; } upperValue._time = atsc.CurrentBeat + data._time; var bpmChangeView = bpmChanges.LoadedObjects.GetViewBetween(lowerValue, upperValue); float bpmTime = data._time * (copiedBPM / (lastBPMChangeBeforePaste?._BPM ?? copiedBPM)); if (bpmChangeView.Any()) { var firstBPMChange = bpmChangeView.First() as BeatmapBPMChange; bpmTime = firstBPMChange._time - atsc.CurrentBeat; for (var i = 0; i < bpmChangeView.Count - 1; i++) { var leftBPM = bpmChangeView.ElementAt(i) as BeatmapBPMChange; var rightBPM = bpmChangeView.ElementAt(i + 1) as BeatmapBPMChange; bpmTime += (rightBPM._time - leftBPM._time) * (copiedBPM / leftBPM._BPM); } var lastBPMChange = bpmChangeView.Last() as BeatmapBPMChange; bpmTime += (atsc.CurrentBeat + data._time - lastBPMChange._time) * (copiedBPM / lastBPMChange._BPM); } float newTime = bpmTime + atsc.CurrentBeat; BeatmapObject newData = BeatmapObject.GenerateCopy(data); newData._time = newTime; if (!collections.TryGetValue(newData.beatmapType, out BeatmapObjectContainerCollection collection)) { collection = BeatmapObjectContainerCollection.GetCollectionForType(newData.beatmapType); collections.Add(newData.beatmapType, collection); } pasted.Add(newData); } List <BeatmapObject> totalRemoved = new List <BeatmapObject>(); // We remove conflicting objects with our to-be-pasted objects. foreach (var kvp in collections) { kvp.Value.RemoveConflictingObjects(pasted.Where(x => x.beatmapType == kvp.Key), out var conflicting); totalRemoved.AddRange(conflicting); } // While we're at it, we will also overwrite the entire section if we have to. if (overwriteSection) { float start = pasted.First()._time; float end = pasted.First()._time; foreach (BeatmapObject beatmapObject in pasted) { if (start > beatmapObject._time) { start = beatmapObject._time; } if (end < beatmapObject._time) { end = beatmapObject._time; } } GetObjectTypes(pasted, out bool hasNoteOrObstacle, out bool hasEvent, out bool hasBpmChange); List <(BeatmapObjectContainerCollection, BeatmapObject)> toRemove = new List <(BeatmapObjectContainerCollection, BeatmapObject)>(); ForEachObjectBetweenTimeByGroup(start, end, hasNoteOrObstacle, hasEvent, hasBpmChange, (collection, beatmapObject) => { if (pasted.Contains(beatmapObject)) { return; } toRemove.Add((collection, beatmapObject)); }); foreach ((BeatmapObjectContainerCollection, BeatmapObject)pair in toRemove) { BeatmapObjectContainerCollection collection = pair.Item1; BeatmapObject beatmapObject = pair.Item2; collection.DeleteObject(beatmapObject, false); totalRemoved.Add(beatmapObject); } } // We then spawn our pasted objects into the map and select them. foreach (BeatmapObject data in pasted) { collections[data.beatmapType].SpawnObject(data, false, false); Select(data, true, false, false); } foreach (BeatmapObjectContainerCollection collection in collections.Values) { collection.RefreshPool(); if (collection is BPMChangesContainer con) { con.RefreshGridShaders(); } } if (CopiedObjects.Any(x => (x is MapEvent e) && e.IsRotationEvent)) { tracksManager.RefreshTracks(); } if (triggersAction) { BeatmapActionContainer.AddAction(new SelectionPastedAction(pasted, totalRemoved)); } SelectionPastedEvent?.Invoke(pasted); SelectionChangedEvent?.Invoke(); RefreshSelectionMaterial(false); if (eventPlacement.objectContainerCollection.PropagationEditing != EventsContainer.PropMode.Off) { eventPlacement.objectContainerCollection.PropagationEditing = eventPlacement.objectContainerCollection.PropagationEditing; } Debug.Log("Pasted!"); }