public void ModifyEvent() { var actionContainer = Object.FindObjectOfType <BeatmapActionContainer>(); var collection = BeatmapObjectContainerCollection.GetCollectionForType(BeatmapObject.Type.BPM_CHANGE); if (collection is BPMChangesContainer bpmCollection) { var bpmChange = new BeatmapBPMChange(50, 10); bpmCollection.SpawnObject(bpmChange); if (bpmCollection.LoadedContainers[bpmChange] is BeatmapBPMChangeContainer container) { BeatmapBPMChangeInputController.ChangeBPM(container, "60"); } Assert.AreEqual(1, bpmCollection.LoadedObjects.Count); CheckBPM(bpmCollection, 0, 10, 60); actionContainer.Undo(); Assert.AreEqual(1, bpmCollection.LoadedObjects.Count); CheckBPM(bpmCollection, 0, 10, 50); actionContainer.Redo(); Assert.AreEqual(1, bpmCollection.LoadedObjects.Count); CheckBPM(bpmCollection, 0, 10, 60); } }
public override void SortObjects() { float[] bpmChangeTimes = new float[ShaderArrayMaxSize]; float[] bpmChangeBPMS = new float[ShaderArrayMaxSize]; bpmChangeTimes[0] = 0; bpmChangeBPMS[0] = BeatSaberSongContainer.Instance.song.beatsPerMinute; LoadedContainers = LoadedContainers.OrderBy(x => x.objectData._time).ToList(); for (int i = 0; i < LoadedContainers.Count; i++) { if (i >= ShaderArrayMaxSize - 1) { Debug.LogError($":hyperPepega: :mega: THE CAP FOR BPM CHANGES IS {ShaderArrayMaxSize - 1}, WHY TF DO YOU HAVE THIS MANY BPM CHANGES!?!?"); break; } BeatmapBPMChangeContainer con = LoadedContainers[i] as BeatmapBPMChangeContainer; con.UpdateGridPosition(); BeatmapBPMChange bpmChange = con.objectData as BeatmapBPMChange; bpmChangeTimes[i + 1] = bpmChange._time; bpmChangeBPMS[i + 1] = bpmChange._BPM; } foreach (Renderer renderer in allGridRenderers) { renderer.material.SetFloatArray(Times, bpmChangeTimes); renderer.material.SetFloatArray(BPMs, bpmChangeBPMS); renderer.material.SetInt(BPMCount, LoadedContainers.Count + 1); } measureLinesController.RefreshMeasureLines(); }
/// <summary> /// Copies the current selection for later Pasting. /// </summary> /// <param name="cut">Whether or not to delete the original selection after copying them.</param> public void Copy(bool cut = false) { if (!HasSelectedObjects()) { return; } CopiedObjects.Clear(); float firstTime = SelectedObjects.OrderBy(x => x._time).First()._time; foreach (BeatmapObject data in SelectedObjects) { BeatmapObjectContainerCollection collection = BeatmapObjectContainerCollection.GetCollectionForType(data.beatmapType); if (collection.LoadedContainers.TryGetValue(data, out BeatmapObjectContainer con)) { con.SetOutlineColor(instance.copiedColor); } BeatmapObject copy = BeatmapObject.GenerateCopy(data); copy._time -= firstTime; CopiedObjects.Add(copy); } if (cut) { Delete(); } var bpmChanges = BeatmapObjectContainerCollection.GetCollectionForType <BPMChangesContainer>(BeatmapObject.Type.BPM_CHANGE); BeatmapBPMChange lastBPMChange = bpmChanges.FindLastBPM(atsc.CurrentBeat, true); copiedBPM = lastBPMChange?._BPM ?? atsc.song.beatsPerMinute; }
public static BeatmapBPMChangeContainer SpawnBPMChange(BeatmapBPMChange data, ref GameObject prefab) { BeatmapBPMChangeContainer container = Instantiate(prefab).GetComponent <BeatmapBPMChangeContainer>(); container.bpmData = data; container.GetComponentInChildren <TextMeshProUGUI>().text = data._BPM.ToString(); return(container); }
public void RefreshMeasureLines() { Debug.Log("Refreshing measure lines..."); init = false; Queue <TextMeshProUGUI> existing = new Queue <TextMeshProUGUI>(measureTextsByBeat.Values); measureTextsByBeat.Clear(); previousEnabledByBeat.Clear(); int rawBeatsInSong = Mathf.FloorToInt(atsc.GetBeatFromSeconds(BeatSaberSongContainer.Instance.loadedSong.length)); float jsonBeat = 0; int modifiedBeats = 0; float songBPM = BeatSaberSongContainer.Instance.song.beatsPerMinute; List <BeatmapBPMChange> allBPMChanges = new List <BeatmapBPMChange>() { new BeatmapBPMChange(songBPM, 0) }; allBPMChanges.AddRange(bpmChangesContainer.LoadedObjects.Cast <BeatmapBPMChange>()); while (jsonBeat <= rawBeatsInSong) { TextMeshProUGUI text = existing.Count > 0 ? existing.Dequeue() : Instantiate(measureLinePrefab, parent); text.text = $"{modifiedBeats}"; text.transform.localPosition = new Vector3(0, jsonBeat * EditorScaleController.EditorScale, 0); measureTextsByBeat.Add(jsonBeat, text); previousEnabledByBeat.Add(jsonBeat, true); modifiedBeats++; BeatmapBPMChange last = allBPMChanges.Last(x => x._Beat <= modifiedBeats); jsonBeat = ((modifiedBeats - last._Beat) / last._BPM * songBPM) + last._time; } // Set proper spacing between Notes grid, Measure lines, and Events grid measureLinesGridChild.Size = modifiedBeats > 1000 ? 1 : 0; foreach (TextMeshProUGUI leftovers in existing) { Destroy(leftovers.gameObject); } init = true; RefreshVisibility(); RefreshPositions(); }
public float FindRoundedBPMTime(float beatTimeInSongBPM, float snap = -1) { if (snap == -1) { snap = 1f / AudioTimeSyncController.gridMeasureSnapping; } BeatmapBPMChange lastBPM = FindLastBPM(beatTimeInSongBPM); //Find the last BPM Change before our beat time if (lastBPM is null) { return((float)Math.Round(beatTimeInSongBPM / snap, MidpointRounding.AwayFromZero) * snap); //If its null, return rounded song bpm } float difference = beatTimeInSongBPM - lastBPM._time; float differenceInBPMBeat = difference / BeatSaberSongContainer.Instance.song.beatsPerMinute * lastBPM._BPM; float roundedDifference = (float)Math.Round(differenceInBPMBeat / snap, MidpointRounding.AwayFromZero) * snap; float roundedDifferenceInSongBPM = roundedDifference / lastBPM._BPM * BeatSaberSongContainer.Instance.song.beatsPerMinute; return(roundedDifferenceInSongBPM + lastBPM._time); }
public void RefreshMeasureLines() { init = false; Queue <TextMeshProUGUI> existing = new Queue <TextMeshProUGUI>(measureTextsByBeat.Values); measureTextsByBeat.Clear(); previousEnabledByBeat.Clear(); int rawBeatsInSong = Mathf.FloorToInt(atsc.GetBeatFromSeconds(BeatSaberSongContainer.Instance.loadedSong.length)); float beatsProcessed = 1; float rawBPMtoChangedBPMRatio = 1; int modifiedBeats = 1; BeatmapBPMChange lastBPMChange = null; while (beatsProcessed <= rawBeatsInSong) { TextMeshProUGUI text = existing.Count > 0 ? existing.Dequeue() : Instantiate(measureLinePrefab, parent); text.gameObject.SetActive(true); text.text = $"{modifiedBeats}"; text.transform.localPosition = new Vector3(0, beatsProcessed * EditorScaleController.EditorScale, 0); measureTextsByBeat.Add(beatsProcessed, text); previousEnabledByBeat.Add(beatsProcessed, true); modifiedBeats++; BeatmapBPMChange last = bpmChangesContainer.FindLastBPM(beatsProcessed + rawBPMtoChangedBPMRatio, true); if (last != lastBPMChange && last?._BPM > 0) { lastBPMChange = last; beatsProcessed = last._time; rawBPMtoChangedBPMRatio = BeatSaberSongContainer.Instance.song.beatsPerMinute / last._BPM; } else { beatsProcessed += rawBPMtoChangedBPMRatio; } } foreach (TextMeshProUGUI leftovers in existing) { Destroy(leftovers.gameObject); } init = true; RefreshVisibility(); RefreshPositions(); }
private void LateUpdate() { if (CowBell && !CowBellPlayed) { audioUtil.PlayOneShotSound(moreCowbellSound); CowBellPlayed = true; } else if (!CowBell) { CowBellPlayed = false; } metronomeVolume = Settings.Instance.MetronomeVolume; if (metronomeVolume != 0f && atsc.IsPlaying) { var collection = BeatmapObjectContainerCollection.GetCollectionForType <BPMChangesContainer>(BeatmapObject.Type.BPM_CHANGE); var toCheck = collection.FindLastBPM(atsc.CurrentBeat); if (lastBPMChange != toCheck) { lastBPMChange = toCheck; lastBPM = lastBPMChange?._BPM ?? atsc.song.beatsPerMinute; audioUtil.PlayOneShotSound(CowBell ? cowbellSound : metronomeSound, Settings.Instance.MetronomeVolume); RunAnimation(); beatProgress = 0; } beatProgress += lastBPM / 60f * Time.deltaTime * songSpeed; if (!metronomeUI.activeInHierarchy) { metronomeUI.SetActive(true); } if (beatProgress >= 1) { beatProgress %= 1; audioUtil.PlayOneShotSound(CowBell ? cowbellSound : metronomeSound, Settings.Instance.MetronomeVolume); RunAnimation(); } } else { metronomeUI.SetActive(false); } }
public void RefreshGridShaders() { float[] bpmChangeTimes = new float[ShaderArrayMaxSize]; float[] bpmChangeBPMS = new float[ShaderArrayMaxSize]; bpmChangeTimes[0] = 0; bpmChangeBPMS[0] = BeatSaberSongContainer.Instance.song.beatsPerMinute; for (int i = 0; i < LoadedObjects.Count; i++) { if (i >= ShaderArrayMaxSize - 1) { Debug.LogError($":hyperPepega: :mega: THE CAP FOR BPM CHANGES IS {ShaderArrayMaxSize - 1}, WHY TF DO YOU HAVE THIS MANY BPM CHANGES!?!?"); break; } BeatmapBPMChange bpmChange = LoadedObjects.ElementAt(i) as BeatmapBPMChange; bpmChangeTimes[i + 1] = bpmChange._time; bpmChangeBPMS[i + 1] = bpmChange._BPM; if (i == 0) { bpmChange._Beat = Mathf.CeilToInt(bpmChange._time); } else { float songBPM = BeatSaberSongContainer.Instance.song.beatsPerMinute; BeatmapBPMChange lastChange = LoadedObjects.ElementAt(i - 1) as BeatmapBPMChange; float passedBeats = (bpmChange._time - lastChange._time - 0.01f) / songBPM * lastChange._BPM; bpmChange._Beat = lastChange._Beat + Mathf.CeilToInt(passedBeats); } } foreach (Renderer renderer in allGridRenderers) { renderer.material.SetFloatArray(Times, bpmChangeTimes); renderer.material.SetFloatArray(BPMs, bpmChangeBPMS); renderer.material.SetInt(BPMCount, LoadedObjects.Count + 1); } measureLinesController.RefreshMeasureLines(); }
void OnPlayToggle(bool playing) { if (metronomeVolume == 0) { return; } if (playing) { RunAnimation(); var collection = BeatmapObjectContainerCollection.GetCollectionForType <BPMChangesContainer>(BeatmapObject.Type.BPM_CHANGE); lastBPMChange = collection.FindLastBPM(atsc.CurrentBeat); lastBPM = lastBPMChange?._BPM ?? atsc.song.beatsPerMinute; if (lastBPMChange != null) { float differenceInSongBPM = atsc.CurrentBeat - lastBPMChange._time; float differenceInLastBPM = differenceInSongBPM * lastBPMChange._BPM / atsc.song.beatsPerMinute; beatProgress = differenceInLastBPM % 1; } else { beatProgress = atsc.CurrentBeat % 1; } } }
private void CreateAutogeneratedBPMChange(int res) { if (res == 2) { return; } Settings.Instance.Reminder_UnsupportedEditorOffset = res == 0; float offset = BeatSaberSongContainer.Instance.difficultyData.customData["_editorOffset"]; BeatSaberSongContainer.Instance.difficultyData.customData.Remove("_editorOffset"); BeatSaberSongContainer.Instance.difficultyData.customData.Remove("_editorOldOffset"); BeatmapBPMChange autoGenerated = new BeatmapBPMChange( BeatSaberSongContainer.Instance.song.beatsPerMinute, AudioTimeSyncController.GetBeatFromSeconds(offset / 1000f)); autoGenerated._customData = new SimpleJSON.JSONObject(); autoGenerated._customData.Add("__note", "Autogenerated by ChroMapper"); SpawnObject(autoGenerated, true, false); RefreshGridShaders(); 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!"); }