Esempio n. 1
0
    private IEnumerator GenerateRegularStrobe(int type, int ValueA, int ValueB, float endTime, float startTime, bool alternateColors, bool dynamic, int precision, List<MapEvent> containersBetween)
    {
        List<int> alternatingTypes = new List<int>() { ValueA, ValueB };
        int typeIndex = 0;
        if (alternateColors)
        {
            if (ValueA > 4 && ValueA < 8) alternatingTypes.Add(ValueA - 4); //Adds inverse colors (Red -> Blue) for both values if we're alternating
            else if (ValueA > 0) alternatingTypes.Add(ValueA + 4);
            else alternatingTypes.Add(ValueA);
            if (ValueB > 4 && ValueB < 8) alternatingTypes.Add(ValueB - 4);
            else if (ValueB > 0) alternatingTypes.Add(ValueB + 4);
            else alternatingTypes.Add(ValueB);
        }
        alternatingTypes = alternatingTypes.Distinct().ToList();
        float distanceInBeats = endTime - startTime;
        float originalDistance = distanceInBeats;

        while (distanceInBeats >= 0)
        {
            yield return new WaitForEndOfFrame();
            if (typeIndex >= alternatingTypes.Count) typeIndex = 0;
            IEnumerable<MapEvent> any = containersBetween.Where(x => x._time <= endTime - distanceInBeats && x != containersBetween.First() && x != containersBetween.Last());
            if (any.Any() && dynamic)
            {
                alternatingTypes[0] = any.Last()._value;
                if (alternateColors && alternatingTypes.Count <= 4)
                {
                    if (alternatingTypes.Count == 1) alternatingTypes.Add(ValueB);
                    if (alternatingTypes.Count > 2)
                    {
                        if (alternatingTypes[0] > 4 && alternatingTypes[0] < 8) alternatingTypes[3] = alternatingTypes[0] - 4;
                        else if (alternatingTypes[0] > 0) alternatingTypes[3] = alternatingTypes[0] + 4;
                    }
                }
            }
            int value = alternatingTypes[typeIndex];
            typeIndex++;
            MapEvent data = new MapEvent(endTime - distanceInBeats, type, value);
            BeatmapEventContainer eventContainer = eventsContainer.SpawnObject(data, out _, false) as BeatmapEventContainer;
            generatedObjects.Add(eventContainer);
            distanceInBeats -= 1 / (float)precision;
        }
    }
Esempio n. 2
0
    /// <summary>
    /// Pastes any copied objects into the map, selecting them immediately.
    /// </summary>
    public void Paste(bool triggersAction = true)
    {
        DeselectAll();
        CopiedObjects = CopiedObjects.OrderBy((x) => x._time).ToList();
        List <BeatmapObjectContainer> pasted = new List <BeatmapObjectContainer>();

        foreach (BeatmapObject data in CopiedObjects)
        {
            if (data == null)
            {
                continue;
            }
            float newTime = data._time + atsc.CurrentBeat;
            BeatmapObjectContainer pastedContainer = null;
            if (data is BeatmapNote)
            {
                BeatmapObject newData = new BeatmapNote(data.ConvertToJSON());
                newData._time = newTime;
                NotesContainer notes = collections.Where(x => x is NotesContainer).FirstOrDefault() as NotesContainer;
                pastedContainer = notes?.SpawnObject(newData);
            }
            if (data is BeatmapObstacle)
            {
                BeatmapObject newData = new BeatmapObstacle(data.ConvertToJSON());
                newData._time = newTime;
                ObstaclesContainer obstacles = collections.Where(x => x is ObstaclesContainer).FirstOrDefault() as ObstaclesContainer;
                pastedContainer = obstacles?.SpawnObject(newData);
            }
            if (data is MapEvent)
            {
                BeatmapObject newData = new MapEvent(data.ConvertToJSON());
                newData._time = newTime;
                EventsContainer events = collections.Where(x => x is EventsContainer).FirstOrDefault() as EventsContainer;
                pastedContainer = events?.SpawnObject(newData);
            }
            pasted.Add(pastedContainer);
        }
        if (triggersAction)
        {
            BeatmapActionContainer.AddAction(new SelectionPastedAction(pasted, CopiedObjects, atsc.CurrentBeat));
        }
        SelectedObjects.AddRange(pasted);
        RefreshSelectionMaterial(false);
        RefreshMap();
        Debug.Log("Pasted!");
    }
Esempio n. 3
0
    public IEnumerator LoadMap()
    {
        if (BeatSaberSongContainer.Instance == null)
        {
            yield break;
        }
        PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(true);
        yield return(new WaitUntil(() => atsc.gridStartPosition != -1)); //I need a way to find out when Start has been called.

        song = BeatSaberSongContainer.Instance.song;
        float offset        = 0;
        int   environmentID = 0;
        int   batchSize     = Settings.Instance.InitialLoadBatchSize;
        bool  customPlat    = false;

        environmentID = SongInfoEditUI.GetEnvironmentIDFromString(song.environmentName);
        if (song.customData != null && song.customData["_customEnvironment"] != null && song.customData["_customEnvironment"].Value != "")
        {
            environmentID = SongInfoEditUI.GetCustomPlatformsIndexFromString(song.customData["_customEnvironment"]);
            customPlat    = true;
        }
        GameObject         platform    = (customPlat ? CustomPlatformPrefabs[environmentID] : PlatformPrefabs[environmentID]) ?? PlatformPrefabs[0];
        GameObject         instantiate = Instantiate(platform, new Vector3(0, -0.5f, -1.5f), Quaternion.identity);
        PlatformDescriptor descriptor  = instantiate.GetComponent <PlatformDescriptor>();

        BeatmapEventContainer.ModifyTypeMode = descriptor.SortMode;
        PlatformLoadedEvent.Invoke(descriptor);
        descriptor.RedColor  = BeatSaberSongContainer.Instance.difficultyData.colorLeft;
        descriptor.BlueColor = BeatSaberSongContainer.Instance.difficultyData.colorRight;
        map    = BeatSaberSongContainer.Instance.map;
        offset = (song.beatsPerMinute / 60) * (BeatSaberSongContainer.Instance.song.songTimeOffset / 1000);
        int noteLaneSize  = 2;                                        //Half of it, anyways
        int noteLayerSize = 3;
        Queue <BeatmapObject> queuedData = new Queue <BeatmapObject>( //Take all of our object data and combine them for batch loading.
            map._notes.Concat <BeatmapObject>(map._obstacles).Concat(map._events).Concat(map._BPMChanges));

        totalObjectsToLoad = queuedData.Count;
        if (map != null)
        {
            while (queuedData.Count > 0)
            {
                for (int i = 0; i < batchSize; i++)
                {
                    if (queuedData.Count == 0)
                    {
                        break;
                    }
                    BeatmapObject data = queuedData.Dequeue();
                    if (data is BeatmapNote noteData)
                    {
                        BeatmapNoteContainer beatmapNote = notesContainer.SpawnObject(noteData) as BeatmapNoteContainer;
                        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)
                    {
                        BeatmapObstacleContainer beatmapObstacle = obstaclesContainer.SpawnObject(obstacleData) as BeatmapObstacleContainer;
                        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;
                        }
                    }
                    else if (data is MapEvent eventData)
                    {
                        eventsContainer.SpawnObject(eventData);
                    }
                    else if (data is BeatmapBPMChange bpmData)
                    {
                        bpmContainer.SpawnObject(bpmData);
                    }
                }
                UpdateSlider(batchSize);
                yield return(new WaitForEndOfFrame());
            }
            notesContainer.SortObjects();
            obstaclesContainer.SortObjects();
            eventsContainer.SortObjects();
            bpmContainer.SortObjects();
            noteGrid.localScale = new Vector3((float)(noteLaneSize * 2) / 10 + 0.01f, 1, 1);
        }
        PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(false);
    }
Esempio n. 4
0
    private IEnumerator GenerateOneStrobe(int type, int RepeatingValue, float endTime, float startTime, List <BeatmapObjectContainer> containersBetween)
    {
        bool  alternateValue   = false;
        float distanceInBeats  = endTime - startTime;
        float originalDistance = distanceInBeats;

        int alternateValueType = MapEvent.LIGHT_VALUE_OFF;

        if (RepeatingValue == MapEvent.LIGHT_VALUE_RED_FADE || RepeatingValue == MapEvent.LIGHT_VALUE_RED_FLASH || RepeatingValue == MapEvent.LIGHT_VALUE_RED_ON)
        {
            alternateValueType = MapEvent.LIGHT_VALUE_RED_ON;
        }
        else
        {
            alternateValueType = MapEvent.LIGHT_VALUE_BLUE_ON;
        }

        Dictionary <float, Color> chromaColors = new Dictionary <float, Color>();
        Dictionary <float, int>   eventValues  = new Dictionary <float, int>();

        eventValues.Add(startTime, RepeatingValue);
        foreach (BeatmapObjectContainer container in containersBetween)
        {
            BeatmapEventContainer e = (container as BeatmapEventContainer);
            if (container is null || e is null)
            {
                continue;
            }
            if (e.eventData._value < ColourManager.RGB_INT_OFFSET)
            {
                if (e.eventData._value != eventValues.Last().Value)
                {
                    if (!eventValues.ContainsKey(e.eventData._time))
                    {
                        eventValues.Add(e.eventData._time, e.eventData._value);
                    }
                }
            }
            else
            {
                if (!chromaColors.ContainsKey(e.eventData._time))
                {
                    chromaColors.Add(e.eventData._time, ColourManager.ColourFromInt(e.eventData._value));
                }
            }
        }

        while (distanceInBeats >= (1 / (float)atsc.gridMeasureSnapping))
        {
            yield return(new WaitForEndOfFrame());

            distanceInBeats -= (1 / (float)atsc.gridMeasureSnapping);
            float latestPastValueTime = eventValues.Keys.Aggregate((x, y) => x >= (endTime - distanceInBeats) &&
                                                                   y <= (endTime - distanceInBeats) ? x : startTime);
            int value = alternateValue ? alternateValueType : eventValues[latestPastValueTime];
            if (distanceInBeats == 0)
            {
                value = eventValues.Last().Value;
            }
            MapEvent data = new MapEvent(endTime - distanceInBeats, type, value);
            if (alternateValueType != MapEvent.LIGHT_VALUE_OFF && eventValues[latestPastValueTime] != MapEvent.LIGHT_VALUE_OFF)
            {
                BeatmapEventContainer eventContainer = eventsContainer.SpawnObject(data) as BeatmapEventContainer;
                generatedObjects.Add(eventContainer);
            }
            if (chromaColors.Count >= 2 && distanceInBeats >= (1 / (float)atsc.gridMeasureSnapping))
            {
                float nextChromaTime;
                float latestPastChromaTime = FindLastChromaTime(data._time, chromaColors, out nextChromaTime);
                int   color = -1;
                if (nextChromaTime != -1 && latestPastChromaTime != -1)
                {
                    Color from = chromaColors[latestPastChromaTime];
                    Color to   = chromaColors[nextChromaTime];
                    float distanceBetweenTimes = nextChromaTime - latestPastChromaTime;
                    color = ColourManager.ColourToInt(Color.Lerp(from, to, (data._time - latestPastChromaTime) / (distanceBetweenTimes)));
                }
                else if (latestPastChromaTime != -1 && nextChromaTime == -1)
                {
                    color = ColourManager.ColourToInt(chromaColors[latestPastChromaTime]);
                }
                else
                {
                    continue;
                }
                MapEvent chromaData = new MapEvent(data._time - (1f / 64f), type, color);
                BeatmapEventContainer chromaContainer = eventsContainer.SpawnObject(chromaData) as BeatmapEventContainer;
                generatedObjects.Add(chromaContainer);
            }
            alternateValue = !alternateValue;
        }
    }
Esempio n. 5
0
    public IEnumerator LoadMap()
    {
        if (BeatSaberSongContainer.Instance == null)
        {
            yield break;
        }
        PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(true);
        PersistentUI.Instance.LevelLoadSliderLabel.text = "";
        yield return(new WaitUntil(() => atsc.gridStartPosition != -1)); //I need a way to find out when Start has been called.

        song = BeatSaberSongContainer.Instance.song;                     //Grab songe data
        diff = BeatSaberSongContainer.Instance.difficultyData;

        //Set up some local variables
        int  environmentID = 0;
        int  batchSize     = Settings.Instance.InitialLoadBatchSize;
        bool customPlat    = false;

        environmentID = SongInfoEditUI.GetEnvironmentIDFromString(song.environmentName); //Grab platform by name (Official or Custom)
        if (song.customData != null && song.customData["_customEnvironment"] != null && song.customData["_customEnvironment"].Value != "")
        {
            if (SongInfoEditUI.GetCustomPlatformsIndexFromString(song.customData["_customEnvironment"]) >= 0)
            {
                environmentID = SongInfoEditUI.GetCustomPlatformsIndexFromString(song.customData["_customEnvironment"]);
                customPlat    = true;
            }
        }

        //Instantiate platform, grab descriptor
        GameObject         platform    = (customPlat ? CustomPlatformPrefabs[environmentID] : PlatformPrefabs[environmentID]) ?? PlatformPrefabs[0];
        GameObject         instantiate = Instantiate(platform, new Vector3(0, -0.5f, -1.5f), Quaternion.identity);
        PlatformDescriptor descriptor  = instantiate.GetComponent <PlatformDescriptor>();

        BeatmapEventContainer.ModifyTypeMode = descriptor.SortMode; //Change sort mode

        //Update Colors
        Color leftNote = BeatSaberSong.DEFAULT_LEFTNOTE; //Have default note as base

        if (descriptor.RedColor != BeatSaberSong.DEFAULT_LEFTCOLOR)
        {
            leftNote = descriptor.RedColor;                                                         //Prioritize platforms
        }
        if (diff.colorLeft != BeatSaberSong.DEFAULT_LEFTNOTE)
        {
            leftNote = diff.colorLeft;                                                   //Then prioritize custom colors
        }
        Color rightNote = BeatSaberSong.DEFAULT_RIGHTNOTE;

        if (descriptor.BlueColor != BeatSaberSong.DEFAULT_RIGHTCOLOR)
        {
            rightNote = descriptor.BlueColor;
        }
        if (diff.colorRight != BeatSaberSong.DEFAULT_RIGHTNOTE)
        {
            rightNote = diff.colorRight;
        }

        notesContainer.UpdateColor(leftNote, rightNote);
        obstaclesContainer.UpdateColor(diff.obstacleColor);
        if (diff.colorLeft != BeatSaberSong.DEFAULT_LEFTNOTE)
        {
            descriptor.RedColor = diff.colorLeft;
        }
        if (diff.colorRight != BeatSaberSong.DEFAULT_RIGHTNOTE)
        {
            descriptor.BlueColor = diff.colorRight;
        }
        if (diff.envColorLeft != BeatSaberSong.DEFAULT_LEFTCOLOR)
        {
            descriptor.RedColor = diff.envColorLeft;
        }
        if (diff.envColorRight != BeatSaberSong.DEFAULT_RIGHTCOLOR)
        {
            descriptor.BlueColor = diff.envColorRight;
        }

        PlatformLoadedEvent.Invoke(descriptor);    //Trigger event for classes that use the platform

        map = BeatSaberSongContainer.Instance.map; //Grab map info, do some stuff
        int noteLaneSize  = 2;                     //Half of it, anyways
        int noteLayerSize = 3;

        Queue <BeatmapObject> queuedData = new Queue <BeatmapObject>( //Take all of our object data and combine them for batch loading.
            map._notes.Concat <BeatmapObject>(map._obstacles).Concat(map._events).Concat(map._BPMChanges).Concat(map._customEvents));

        totalObjectsToLoad = queuedData.Count;
        if (map != null)
        {
            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.
                    if (data is BeatmapNote noteData)
                    {
                        BeatmapNoteContainer beatmapNote = notesContainer.SpawnObject(noteData, out _) as BeatmapNoteContainer;
                        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)
                    {
                        BeatmapObstacleContainer beatmapObstacle = obstaclesContainer.SpawnObject(obstacleData, out _) as BeatmapObstacleContainer;
                        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;
                        }
                    }
                    else if (data is MapEvent eventData)
                    {
                        eventsContainer.SpawnObject(eventData, out _);
                    }
                    else if (data is BeatmapBPMChange bpmData)
                    {
                        bpmContainer.SpawnObject(bpmData, out _);
                    }
                    else if (data is BeatmapCustomEvent customData)
                    {
                        customEventsContainer.SpawnObject(customData, out _);
                    }
                }
                UpdateSlider(batchSize);
                yield return(new WaitForEndOfFrame());
            }
            notesContainer.SortObjects(); //Sort these boyes.
            obstaclesContainer.SortObjects();
            eventsContainer.SortObjects();
            bpmContainer.SortObjects();
            customEventsContainer.SortObjects();
            noteGrid.localScale = new Vector3((float)(noteLaneSize * 2) / 10 + 0.01f, 1, 1); //Set note lanes appropriately
        }
        PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(false);                   //Disable progress bar
        LevelLoadedEvent?.Invoke();
    }
Esempio n. 6
0
    public void GenerateStrobe(IEnumerable <StrobeGeneratorPass> passes)
    {
        List <BeatmapObject>   generatedObjects = new List <BeatmapObject>();
        IEnumerable <MapEvent> containers       = SelectionController.SelectedObjects.Where(x => x is MapEvent).Cast <MapEvent>(); //Grab selected objects
        List <BeatmapObject>   oldEvents        = new List <BeatmapObject>();                                                      //For the Action
        //Order by type, then by descending time
        var groupings = containers.GroupBy(x => x._type);

        foreach (var group in groupings)
        {
            int type = group.Key;

            // Try and do this in one pass so we don't tank performance
            var partitioned = group.GroupBy(y => y.IsLightIdEvent ? EventsContainer.PropMode.Light : EventsContainer.PropMode.Off)
                              .ToDictionary(z => z.Key, modeGroup =>
                                            modeGroup.Key == EventsContainer.PropMode.Off ? modeGroup.GroupBy(y => 1) : modeGroup.GroupBy(y => y.LightId[0]));

            foreach (var mode in partitioned)
            {
                var propMode   = mode.Key;
                var propGroups = mode.Value;

                foreach (var propGroup in propGroups)
                {
                    var customData = propGroup.FirstOrDefault()?._customData;
                    var lightIds   = customData == null ? null : customData["_lightID"];
                    if (propGroup.Count() >= 2)
                    {
                        IEnumerable <MapEvent> ordered = propGroup.OrderByDescending(x => x._time);
                        MapEvent end   = ordered.First();
                        MapEvent start = ordered.Last();

                        IEnumerable <MapEvent> containersBetween = eventsContainer.LoadedObjects.GetViewBetween(start, end).Cast <MapEvent>().Where(x =>
                                                                                                                                                    x._type == start._type && //Grab all events between start and end point.
                                                                                                                                                                              // This check isn't perfect but I think it covers anything that could occur without manual mischief
                                                                                                                                                    (propMode != EventsContainer.PropMode.Light || start.IsLightIdEvent == x.IsLightIdEvent && (!start.IsLightIdEvent || x.LightId.Contains(start.LightId[0])))
                                                                                                                                                    );
                        oldEvents.AddRange(containersBetween);

                        foreach (StrobeGeneratorPass pass in passes)
                        {
                            IEnumerable <MapEvent> validEvents = containersBetween.Where(x => pass.IsEventValidForPass(x));
                            if (validEvents.Count() >= 2)
                            {
                                List <MapEvent> strobePassGenerated = pass.StrobePassForLane(validEvents.OrderBy(x => x._time), type, propMode, lightIds).ToList();
                                // REVIEW: Perhaps implement a "smart merge" to conflicting events, rather than outright removing those from previous passes
                                // Now, what would a "smart merge" entail? I have no clue.
                                generatedObjects.RemoveAll(x => strobePassGenerated.Any(y => y.IsConflictingWith(x)));
                                generatedObjects.AddRange(strobePassGenerated);
                            }
                        }
                    }
                }
            }
        }
        generatedObjects.OrderBy(x => x._time);
        if (generatedObjects.Count > 0)
        {
            //Delete conflicting vanilla events
            foreach (MapEvent e in oldEvents)
            {
                eventsContainer.DeleteObject(e, false, false);
            }
            //Spawn objects that were generated
            foreach (MapEvent data in generatedObjects)
            {
                eventsContainer.SpawnObject(data, false, false);
            }
            eventsContainer.RefreshPool(true);
            //yield return PersistentUI.Instance.FadeOutLoadingScreen();
            SelectionController.DeselectAll();
            SelectionController.SelectedObjects = new HashSet <BeatmapObject>(generatedObjects);
            SelectionController.SelectionChangedEvent?.Invoke();
            SelectionController.RefreshSelectionMaterial(false);
            BeatmapActionContainer.AddAction(new StrobeGeneratorGenerationAction(generatedObjects.ToArray(), oldEvents.ToArray()));
        }
    }