Exemple #1
0
        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);
            }
        }
Exemple #2
0
 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();
    }
Exemple #6
0
    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();
    }
Exemple #8
0
    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);
        }
    }
Exemple #9
0
    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();
    }
Exemple #10
0
 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;
         }
     }
 }
Exemple #11
0
    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!");
    }