Esempio n. 1
0
 /// <summary>
 ///     Returns true if the two maps are equal by value.
 /// </summary>
 /// <param name="other">the Qua to compare to</param>
 /// <returns></returns>
 public bool EqualByValue(Qua other)
 {
     return(AudioFile == other.AudioFile &&
            SongPreviewTime == other.SongPreviewTime &&
            BackgroundFile == other.BackgroundFile &&
            BannerFile == other.BannerFile &&
            MapId == other.MapId &&
            MapSetId == other.MapSetId &&
            Mode == other.Mode &&
            Title == other.Title &&
            Artist == other.Artist &&
            Source == other.Source &&
            Tags == other.Tags &&
            Creator == other.Creator &&
            DifficultyName == other.DifficultyName &&
            Description == other.Description &&
            Genre == other.Genre &&
            TimingPoints.SequenceEqual(other.TimingPoints, TimingPointInfo.ByValueComparer) &&
            SliderVelocities.SequenceEqual(other.SliderVelocities, SliderVelocityInfo.ByValueComparer) &&
            HitObjects.SequenceEqual(other.HitObjects, HitObjectInfo.ByValueComparer) &&
            CustomAudioSamples.SequenceEqual(other.CustomAudioSamples, CustomAudioSampleInfo.ByValueComparer) &&
            SoundEffects.SequenceEqual(other.SoundEffects, SoundEffectInfo.ByValueComparer) &&
            EditorLayers.SequenceEqual(other.EditorLayers, EditorLayerInfo.ByValueComparer) &&
            RandomizeModifierSeed == other.RandomizeModifierSeed);
 }
Esempio n. 2
0
 /// <summary>
 ///     Returns true if the two maps are equal by value.
 /// </summary>
 /// <param name="other">the Qua to compare to</param>
 /// <returns></returns>
 public bool EqualByValue(Qua other)
 {
     return(AudioFile == other.AudioFile &&
            SongPreviewTime == other.SongPreviewTime &&
            BackgroundFile == other.BackgroundFile &&
            BannerFile == other.BannerFile &&
            MapId == other.MapId &&
            MapSetId == other.MapSetId &&
            Mode == other.Mode &&
            Title == other.Title &&
            Artist == other.Artist &&
            Source == other.Source &&
            Tags == other.Tags &&
            Creator == other.Creator &&
            DifficultyName == other.DifficultyName &&
            Description == other.Description &&
            Genre == other.Genre &&
            TimingPoints.SequenceEqual(other.TimingPoints, TimingPointInfo.ByValueComparer) &&
            SliderVelocities.SequenceEqual(other.SliderVelocities, SliderVelocityInfo.ByValueComparer)
            // ReSharper disable once CompareOfFloatsByEqualityOperator
            && InitialScrollVelocity == other.InitialScrollVelocity &&
            BPMDoesNotAffectScrollVelocity == other.BPMDoesNotAffectScrollVelocity &&
            HasScratchKey == other.HasScratchKey &&
            HitObjects.SequenceEqual(other.HitObjects, HitObjectInfo.ByValueComparer) &&
            CustomAudioSamples.SequenceEqual(other.CustomAudioSamples, CustomAudioSampleInfo.ByValueComparer) &&
            SoundEffects.SequenceEqual(other.SoundEffects, SoundEffectInfo.ByValueComparer) &&
            EditorLayers.SequenceEqual(other.EditorLayers, EditorLayerInfo.ByValueComparer) &&
            RandomizeModifierSeed == other.RandomizeModifierSeed);
 }
Esempio n. 3
0
 /// <summary>
 ///     Does some sorting of the Qua
 /// </summary>
 public void Sort()
 {
     HitObjects       = HitObjects.OrderBy(x => x.StartTime).ToList();
     TimingPoints     = TimingPoints.OrderBy(x => x.StartTime).ToList();
     SliderVelocities = SliderVelocities.OrderBy(x => x.StartTime).ToList();
     SoundEffects     = SoundEffects.OrderBy(x => x.StartTime).ToList();
 }
Esempio n. 4
0
        /// <summary>
        /// Calculates the size of the effective time range of a given timing point.
        /// This range stops at the next timing point, so it just returns the offset of the next timing point.
        /// </summary>
        /// <param name="timingPoint"></param>
        /// <returns>The timing point after specified timing point.</returns>
        public double GetTimingPointEffectiveRange(TimingPoint timingPoint)
        {
            foreach (var tp in TimingPoints.Where(tp => Precision.DefinitelyBigger(tp.Offset, timingPoint.Offset)))
            {
                return(tp.Offset);
            }

            return(double.PositiveInfinity); // Being the last timingpoint, the effective range is infinite (very big)
        }
Esempio n. 5
0
        /// <summary>
        ///     Gets the timing point at a particular time in the map.
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public TimingPointInfo GetTimingPointAt(double time)
        {
            var index = TimingPoints.FindLastIndex(x => x.StartTime <= time);

            // If the point can't be found, we want to return either null if there aren't
            // any points, or the first timing point, since it'll be considered as apart of it anyway.
            if (index == -1)
            {
                return(TimingPoints.Count == 0 ? null : TimingPoints.First());
            }

            return(TimingPoints[index]);
        }
Esempio n. 6
0
 //todo: not optimized
 public void WriteOsuFile(string path, string newDiffName = null)
 {
     File.WriteAllText(path,
                       string.Format("osu file format v{0}\r\n\r\n{1}{2}{3}{4}{5}{6}{7}{8}", Version,
                                     General?.ToSerializedString(),
                                     Editor?.ToSerializedString(),
                                     Metadata?.ToSerializedString(newDiffName),
                                     Difficulty?.ToSerializedString(),
                                     Events?.ToSerializedString(),
                                     TimingPoints?.ToSerializedString(),
                                     Colours?.ToSerializedString(),
                                     HitObjects?.ToSerializedString()));
 }
Esempio n. 7
0
 public static void UpdateActiveTimingPoint(bool runSort)
 {
     if (TimingPoints != null && TimingPoints.Count > 0)
     {
         activeTimingPointIndex =
             Math.Max(0, TimingPoints.FindLastIndex(delegate(TimingPoint t) { return(t.offset <= Time + 50); }));
         if (runSort)
         {
             TimingPoints.Sort();
         }
     }
     else
     {
         activeTimingPointIndex = -1;
     }
 }
Esempio n. 8
0
        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }
            _disposed = true;

            _currentTiming = CurrentFrameTiming.OutOfFrameLoop;

            Layers.AbortAllLayers();

            TimingPoints.AbortAllEvents();
            Lights.Release();
            ContextAssociatedMemorySafety.EnsureCollect(OwnerScreen);   // Must be called before the opengl context is deleted.
            Disposed?.Invoke();
        }
Esempio n. 9
0
        /// <summary>
        ///    Finds the length of a timing point.
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public double GetTimingPointLength(TimingPointInfo point)
        {
            // Find the index of the current timing point.
            var index = TimingPoints.IndexOf(point);

            // ??
            if (index == -1)
            {
                throw new ArgumentException();
            }

            // There is another timing point ahead of this one
            // so we'll need to get the length of the two points.
            if (index + 1 < TimingPoints.Count)
            {
                return(TimingPoints[index + 1].StartTime - TimingPoints[index].StartTime);
            }

            // Only one timing point, so we can assume that it goes to the end of the map.
            return(Length - point.StartTime);
        }
Esempio n. 10
0
        public TimingPoint GetFirstTimingPointExtended()
        {
            // Add an extra timingpoint that is the same as the first redline but like 10 x meter beats earlier so any objects before the first redline can use that thing

            // When you have a greenline before the first redline, the greenline will act like the first redline and you can snap objects to the greenline's bpm.
            // The value in the greenline will be used as the milliseconds per beat, so for example a 1x SliderVelocity slider will be 600 bpm.
            // The timeline will work like a redline on 0 offset and 1000 milliseconds per beat

            TimingPoint firstTp = TimingPoints.FirstOrDefault();

            if (firstTp != null && firstTp.Uninherited)
            {
                return(new TimingPoint(firstTp.Offset - firstTp.MpB * firstTp.Meter.TempoDenominator * 10, firstTp.MpB,
                                       firstTp.Meter, firstTp.SampleSet, firstTp.SampleIndex, firstTp.Volume, firstTp.Uninherited, false, false));
            }

            if (firstTp != null)
            {
                return(new TimingPoint(0, 1000, firstTp.Meter, firstTp.SampleSet, firstTp.SampleIndex, firstTp.Volume,
                                       firstTp.Uninherited, false, false));
            }

            return(new TimingPoint(0, 0, 0, SampleSet.Auto, 0, 0, true, false, false));
        }
        private Chart GenerateTimingLineChart()
        {
            if (TimingPoints.Count == 0)
            {
                return(null);
            }

            Chart  chart;
            double timingMax    = Math.Ceiling(TimingPoints.Select(x => (x.Value == null ? 0 : (double)x.Value)).Max());
            double bandwidthMax = Math.Ceiling((SpeedBandwidth == null ? 0 : (double)SpeedBandwidth));

            ChartArea   area;
            ChartSeries series;

            area = new ChartArea();

            // style x axis
            area.AxisX.MajorGrid.Enabled = false;
            area.AxisX.IsLabelAutoFit    = false;
            //area.AxisX.LabelAutoFitMinFontSize = 20;
            //area.AxisX.LabelStyle.Format = "MM/dd/yyyy";
            area.AxisX.LabelStyle.Font  = new Font(FontFamily.GenericSansSerif, 15);
            area.AxisX.IntervalAutoMode = IntervalAutoMode.VariableCount;
            area.AxisX.Maximum          = TimingPoints.Count + 1;
            area.AxisX.Minimum          = 0;
            //area.AxisX.Interval = 1;
            //area.AxisX.MajorTickMark.Interval = 1;
            area.AxisX.TextOrientation  = TextOrientation.Rotated90;
            area.AxisX.LabelStyle.Angle = -45;
            //area.AxisX.LabelStyle.IsEndLabelVisible = true;
            //area.AxisX.LabelStyle.IsStaggered = true;

            // style y axis
            //area.AxisY.MajorGrid.LineColor = Color.LightGray;
            area.AxisY.Maximum  = (bandwidthMax > timingMax ? bandwidthMax : timingMax);
            area.AxisY.Minimum  = 0;
            area.AxisY.Interval = 1;
            area.AxisY.LabelAutoFitMinFontSize = 15;
            area.AxisY.LabelStyle.Format       = "0";


            //create chart
            chart        = new Chart();
            chart.Width  = 1200;
            chart.Height = 500;
            chart.Name   = "Waveform Timing";

            Legend legend = new Legend("Legend");

            legend.Font = new Font(FontFamily.GenericSansSerif, 15);
            chart.Legends.Add(legend);
            chart.ChartAreas.Add(area);


            series                     = new ChartSeries("Waveform Timing");
            series.ChartType           = SeriesChartType.Line;
            series.BorderWidth         = 2;
            series.Color               = Color.Black;
            series.IsValueShownAsLabel = true;

            int index = 1;

            foreach (var point in TimingPoints)
            {
                area.AxisX.CustomLabels.Add(index, index + .9, point.Time.ToString("MM/dd/yyyy"));

                if (point.Value == null)
                {
                    //series.Points.AddY(index++);
                }
                else
                {
                    DataPoint dataPoint = new DataPoint(index++, (double)point.Value);

                    series.Points.Add(dataPoint);
                }
            }


            chart.Series.Add(series);

            if (SpeedBandwidth != null)
            {
                chart.Series.Add(MakeLimitSeries((double)SpeedBandwidth, Color.Red, "12% Bandwidth", 0, TimingPoints.Count + 1));
            }
            if (Speed != null)
            {
                chart.Series.Add(MakeLimitSeries((double)Speed, Color.Orange, "MFR Speed", 0, TimingPoints.Count + 1));
            }

            return(chart);
        }
Esempio n. 12
0
        /// <summary>
        ///     Ctor - Automatically parses a Peppy beatmap
        /// </summary>
        public OsuBeatmap(string filePath)
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

            if (!File.Exists(filePath.Trim()))
            {
                IsValid = false;
                return;
            }

            // Create a new beatmap object and default the validity to true.
            IsValid          = true;
            OriginalFileName = filePath;

            // This will hold the section of the beatmap that we are parsing.
            var section = "";

            try
            {
                foreach (var raw_line in File.ReadAllLines(filePath))
                {
                    // Skip empty lines and comments.
                    if (string.IsNullOrWhiteSpace(raw_line) ||
                        raw_line.StartsWith("//", StringComparison.Ordinal) ||
                        raw_line.StartsWith(" ", StringComparison.Ordinal) ||
                        raw_line.StartsWith("_", StringComparison.Ordinal))
                    {
                        continue;
                    }

                    var line = StripComments(raw_line);

                    switch (line.Trim())
                    {
                    case "[General]":
                        section = "[General]";
                        break;

                    case "[Editor]":
                        section = "[Editor]";
                        break;

                    case "[Metadata]":
                        section = "[Metadata]";
                        break;

                    case "[Difficulty]":
                        section = "[Difficulty]";
                        break;

                    case "[Events]":
                        section = "[Events]";
                        break;

                    case "[TimingPoints]":
                        section = "[TimingPoints]";
                        break;

                    case "[HitObjects]":
                        section = "[HitObjects]";
                        break;

                    case "[Colours]":
                        section = "[Colours]";
                        break;

                    default:
                        break;
                    }

                    // Parse Peppy file format
                    if (line.StartsWith("osu file format"))
                    {
                        PeppyFileFormat = line;
                    }

                    // Parse [General] Section
                    if (section.Equals("[General]"))
                    {
                        if (line.Contains(":"))
                        {
                            var key   = line.Substring(0, line.IndexOf(':'));
                            var value = line.Split(':').Last().Trim();

                            switch (key.Trim())
                            {
                            case "AudioFilename":
                                AudioFilename = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(value));
                                break;

                            case "AudioLeadIn":
                                AudioLeadIn = int.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "PreviewTime":
                                PreviewTime = int.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "Countdown":
                                Countdown = int.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "SampleSet":
                                SampleSet = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(value));;
                                break;

                            case "StackLeniency":
                                StackLeniency = float.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "Mode":
                                Mode = int.Parse(value, CultureInfo.InvariantCulture);
                                if (Mode != 3)
                                {
                                    IsValid = false;
                                }
                                break;

                            case "LetterboxInBreaks":
                                LetterboxInBreaks = int.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "SpecialStyle":
                                SpecialStyle = int.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "WidescreenStoryboard":
                                WidescreenStoryboard = int.Parse(value, CultureInfo.InvariantCulture);
                                break;
                            }
                        }
                    }

                    // Parse [Editor] Data
                    if (section.Equals("[Editor]"))
                    {
                        if (line.Contains(":"))
                        {
                            var key   = line.Substring(0, line.IndexOf(':'));
                            var value = line.Split(':').Last();

                            switch (key.Trim())
                            {
                            case "Bookmarks":
                                Bookmarks = value;
                                break;

                            case "DistanceSpacing":
                                DistanceSpacing = float.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "BeatDivisor":
                                BeatDivisor = int.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "GridSize":
                                GridSize = int.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "TimelineZoom":
                                TimelineZoom = float.Parse(value, CultureInfo.InvariantCulture);
                                break;
                            }
                        }
                    }

                    // Parse [Metadata] Data
                    if (section.Equals("[Metadata]"))
                    {
                        if (line.Contains(":"))
                        {
                            var key   = line.Substring(0, line.IndexOf(':'));
                            var value = line.Substring(line.IndexOf(':') + 1);

                            switch (key.Trim())
                            {
                            case "Title":
                                Title = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(value));
                                break;

                            case "TitleUnicode":
                                TitleUnicode = value;
                                break;

                            case "Artist":
                                Artist = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(value));
                                break;

                            case "ArtistUnicode":
                                ArtistUnicode = value;
                                break;

                            case "Creator":
                                Creator = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(value));
                                break;

                            case "Version":
                                Version = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(value));
                                break;

                            case "Source":
                                Source = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(value));
                                break;

                            case "Tags":
                                Tags = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(value));
                                break;

                            case "BeatmapID":
                                BeatmapID = int.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "BeatmapSetID":
                                BeatmapSetID = int.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            default:
                                break;
                            }
                        }
                    }

                    // Parse [Difficulty] Data
                    if (section.Equals("[Difficulty]"))
                    {
                        if (line.Contains(":"))
                        {
                            var key   = line.Substring(0, line.IndexOf(':'));
                            var value = line.Split(':').Last();

                            switch (key.Trim())
                            {
                            case "HPDrainRate":
                                HPDrainRate = float.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "CircleSize":
                                KeyCount = int.Parse(value, CultureInfo.InvariantCulture);

                                if (KeyCount != 4 && KeyCount != 7 && KeyCount != 5 && KeyCount != 8)
                                {
                                    IsValid = false;
                                }
                                break;

                            case "OverallDifficulty":
                                OverallDifficulty = float.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "ApproachRate":
                                ApproachRate = float.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "SliderMultiplier":
                                SliderMultiplier = float.Parse(value, CultureInfo.InvariantCulture);
                                break;

                            case "SliderTickRate":
                                SliderTickRate = float.Parse(value, CultureInfo.InvariantCulture);
                                break;
                            }
                        }
                    }

                    // Parse [Events] Data
                    if (section.Equals("[Events]"))
                    {
                        var values = line.Split(',');

                        // Background
                        if (line.ToLower().Contains("png") || line.ToLower().Contains("jpg") || line.ToLower().Contains("jpeg"))
                        {
                            Background = values[2].Replace("\"", "");
                        }

                        // Sound effects
                        if (values[0] == "Sample" || values[0] == "5")
                        {
                            var path = values[3].Trim('"').Replace(Path.DirectorySeparatorChar, '/');

                            SoundEffects.Add(new OsuSampleInfo()
                            {
                                StartTime = int.Parse(values[1]),
                                Layer     = int.Parse(values[2]),
                                Volume    = Math.Max(0, Math.Min(100, values.Length >= 5 ? int.Parse(values[4], CultureInfo.InvariantCulture) : 100)),
                                Sample    = CustomAudioSampleIndex(path)
                            });
                        }
                    }

                    try
                    {
                        // Parse [TimingPoints] Data
                        if (section.Equals("[TimingPoints]"))
                        {
                            if (line.Contains(","))
                            {
                                var values = line.Split(',');

                                // Parse as double because there are some maps which have this value too large to fit in
                                // a float, for example https://osu.ppy.sh/beatmapsets/681731#mania/1441497. Parsing as
                                // double and then casting to float results in the correct outcome though.
                                var msecPerBeat = double.Parse(values[1], CultureInfo.InvariantCulture);

                                var timingPoint = new OsuTimingPoint
                                {
                                    Offset = float.Parse(values[0], CultureInfo.InvariantCulture),
                                    MillisecondsPerBeat = (float)msecPerBeat,
                                    Signature           = values[2][0] == '0' ? TimeSignature.Quadruple : (TimeSignature)int.Parse(values[2], CultureInfo.InvariantCulture),
                                    SampleType          = int.Parse(values[3], CultureInfo.InvariantCulture),
                                    SampleSet           = int.Parse(values[4], CultureInfo.InvariantCulture),
                                    Volume    = Math.Max(0, int.Parse(values[5], CultureInfo.InvariantCulture)),
                                    Inherited = int.Parse(values[6], CultureInfo.InvariantCulture),
                                    KiaiMode  = int.Parse(values[7], CultureInfo.InvariantCulture)
                                };

                                TimingPoints.Add(timingPoint);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        IsValid = false;
                    }

                    // Parse [HitObjects] Data
                    if (section.Equals("[HitObjects]"))
                    {
                        if (line.Contains(","))
                        {
                            var values = line.Split(',');

                            // We'll need to parse LNs differently than normal HitObjects,
                            // as they have a different syntax. 128 in the object's type
                            // signifies that it is an LN
                            var osuHitObject = new OsuHitObject
                            {
                                X         = int.Parse(values[0], CultureInfo.InvariantCulture),
                                Y         = int.Parse(values[1], CultureInfo.InvariantCulture),
                                StartTime = int.Parse(values[2], CultureInfo.InvariantCulture),
                                Type      = (HitObjectType)int.Parse(values[3], CultureInfo.InvariantCulture),
                                HitSound  = (HitSoundType)int.Parse(values[4], CultureInfo.InvariantCulture),
                                Additions = "0:0:0:0:",
                                KeySound  = -1
                            };

                            // If it's an LN, we'll want to add the object's EndTime as well.
                            if (osuHitObject.Type.HasFlag(HitObjectType.Hold))
                            {
                                var endTime = values[5].Substring(0, values[5].IndexOf(":", StringComparison.Ordinal));
                                osuHitObject.EndTime = int.Parse(endTime, CultureInfo.InvariantCulture);
                            }

                            // Parse the key sound.
                            if (values.Length > 5)
                            {
                                var additions = values[5].Split(':');

                                var volumeField = osuHitObject.Type.HasFlag(HitObjectType.Hold) ? 4 : 3;
                                if (additions.Length > volumeField && additions[volumeField].Length > 0)
                                {
                                    osuHitObject.Volume = Math.Max(0, int.Parse(additions[volumeField], CultureInfo.InvariantCulture));
                                }

                                var keySoundField = volumeField + 1;
                                if (additions.Length > keySoundField && additions[keySoundField].Length > 0)
                                {
                                    osuHitObject.KeySound = CustomAudioSampleIndex(additions[keySoundField]);
                                }
                            }

                            HitObjects.Add(osuHitObject);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                IsValid = false;
            }
        }
Esempio n. 13
0
 /// <summary>
 /// </summary>
 public void SortTimingPoints() => TimingPoints = TimingPoints.OrderBy(x => x.StartTime).ToList();
Esempio n. 14
0
        /// <summary>
        ///     Replaces regular notes with long notes and vice versa.
        ///
        ///     HitObjects and TimingPoints MUST be sorted by StartTime prior to calling this method,
        ///     see <see cref="Sort()"/>.
        /// </summary>
        public void ApplyInverse()
        {
            // Minimal LN and gap lengths in milliseconds.
            //
            // Ideally this should be computed in a smart way using the judgements so that it is always possible to get
            // perfects, but making map mods depend on the judgements (affected by strict/chill/accuracy adjustments) is
            // a really bad idea. I'm setting these to values that will probably work fine for the majority of the
            // cases.
            const int MINIMAL_LN_LENGTH  = 36;
            const int MINIMAL_GAP_LENGTH = 36;

            var newHitObjects = new List <HitObjectInfo>();

            // An array indicating whether the currently processed HitObject is the first in its lane.
            var firstInLane = new bool[GetKeyCount()];

            for (var i = 0; i < firstInLane.Length; i++)
            {
                firstInLane[i] = true;
            }

            for (var i = 0; i < HitObjects.Count; i++)
            {
                var currentObject = HitObjects[i];

                // Find the next and second next hit object in the lane.
                HitObjectInfo nextObjectInLane = null, secondNextObjectInLane = null;
                for (var j = i + 1; j < HitObjects.Count; j++)
                {
                    if (HitObjects[j].Lane == currentObject.Lane)
                    {
                        if (nextObjectInLane == null)
                        {
                            nextObjectInLane = HitObjects[j];
                        }
                        else
                        {
                            secondNextObjectInLane = HitObjects[j];
                            break;
                        }
                    }
                }

                var isFirstInLane = firstInLane[currentObject.Lane - 1];
                firstInLane[currentObject.Lane - 1] = false;

                // If this is the only object in its lane, keep it as is.
                if (nextObjectInLane == null && isFirstInLane)
                {
                    newHitObjects.Add(currentObject);
                    continue;
                }

                // Figure out the time gap between the end of the LN which we'll create and the next object.
                int?timeGap = null;
                if (nextObjectInLane != null)
                {
                    var   timingPoint = GetTimingPointAt(nextObjectInLane.StartTime);
                    float bpm;

                    // If the timing point starts at the next object, we want to use the previous timing point's BPM.
                    // For example, consider a fast section of the map transitioning into a very low BPM ending starting
                    // with the next hit object. Since the LN release and the gap are still in the fast section, they
                    // should use the fast section's BPM.
                    if ((int)Math.Round(timingPoint.StartTime) == nextObjectInLane.StartTime)
                    {
                        var prevTimingPointIndex = TimingPoints.FindLastIndex(x => x.StartTime < timingPoint.StartTime);

                        // No timing points before the object? Just use the first timing point then, it has the correct
                        // BPM.
                        if (prevTimingPointIndex == -1)
                        {
                            prevTimingPointIndex = 0;
                        }

                        bpm = TimingPoints[prevTimingPointIndex].Bpm;
                    }
                    else
                    {
                        bpm = timingPoint.Bpm;
                    }

                    // The time gap is quarter of the milliseconds per beat.
                    timeGap = (int?)Math.Max(Math.Round(15000 / bpm), MINIMAL_GAP_LENGTH);
                }

                // Summary of the changes:
                // Regular 1 -> Regular 2 => LN until 2 - time gap
                // Regular 1 -> LN 2      => LN until 2
                //      LN 1 -> Regular 2 => LN from 1 end until 2 - time gap
                //      LN 1 -> LN 2      => LN from 1 end until 2
                //
                // Exceptions:
                // - last LNs are kept (treated as regular 2)
                // - last regular objects are removed and treated as LN 2

                if (currentObject.IsLongNote)
                {
                    // LNs before regular objects are changed so they start where they ended and end a time gap before
                    // the object.
                    // LNs before LNs do the same but without a time gap.

                    if (nextObjectInLane == null)
                    {
                        // If this is the last object in its lane, though, then it's probably a better idea
                        // to leave it be. For example, finishing long LNs in charts.
                    }
                    else
                    {
                        currentObject.StartTime = currentObject.EndTime; // (this part can mess up the ordering)
                        currentObject.EndTime   = nextObjectInLane.StartTime - timeGap.Value;

                        // Clear the keysounds as we're moving the start, so they won't make sense.
                        currentObject.KeySounds = new List <KeySoundInfo>();

                        // If the next object is not an LN and it's the last object in the lane, or if it's an LN and
                        // not the last object in the lane, create a regular object at the next object's start position.
                        if ((secondNextObjectInLane == null) != nextObjectInLane.IsLongNote)
                        {
                            currentObject.EndTime = nextObjectInLane.StartTime;
                        }

                        // Filter out really short LNs or even negative length resulting from jacks or weird BPM values.
                        if (currentObject.EndTime - currentObject.StartTime < MINIMAL_LN_LENGTH)
                        {
                            // These get skipped entirely.
                            //
                            // Actually, there can be a degenerate pattern of multiple LNs with really short gaps
                            // in between them (less than MINIMAL_LN_LENGTH), which this logic will convert
                            // into nothing. That should be pretty rare though.
                            continue;
                        }
                    }
                }
                else
                {
                    // Regular objects are replaced with LNs starting from their start and ending quarter of a beat
                    // before the next object's start.
                    if (nextObjectInLane == null)
                    {
                        // If this is the last object in lane, though, then it's not included, and instead the previous
                        // LN spans up to this object's StartTime.
                        continue;
                    }

                    currentObject.EndTime = nextObjectInLane.StartTime - timeGap.Value;

                    // If the next object is not an LN and it's the last object in the lane, or if it's an LN and
                    // not the last object in the lane, this LN should span until its start.
                    if ((secondNextObjectInLane == null) == (nextObjectInLane.EndTime == 0))
                    {
                        currentObject.EndTime = nextObjectInLane.StartTime;
                    }

                    // Filter out really short LNs or even negative length resulting from jacks or weird BPM values.
                    if (currentObject.EndTime - currentObject.StartTime < MINIMAL_LN_LENGTH)
                    {
                        // These get converted back into regular objects.
                        currentObject.EndTime = 0;
                    }
                }

                newHitObjects.Add(currentObject);
            }

            // LN conversion can mess up the ordering, so sort it again. See the (this part can mess up the ordering)
            // comment above.
            HitObjects = newHitObjects.OrderBy(x => x.StartTime).ToList();
        }
Esempio n. 15
0
 /// <summary>
 /// Finds the timing control point that is active at <paramref name="time"/>.
 /// </summary>
 /// <param name="time">The time to find the timing control point at.</param>
 /// <returns>The timing control point.</returns>
 public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault());
Esempio n. 16
0
 /// <summary>
 /// Finds all the timing points in a specified time range.
 /// </summary>
 /// <param name="startTime"></param>
 /// <param name="endTime"></param>
 /// <returns></returns>
 public List <TimingPoint> GetTimingPointsInTimeRange(double startTime, double endTime)
 {
     return(TimingPoints.Where(tp => Precision.DefinitelyBigger(tp.Offset, startTime) && Precision.DefinitelyBigger(endTime, tp.Offset)).ToList());
 }
Esempio n. 17
0
    public void ParseLevel(string filename)
    {
        // Read the file and display it line by line.
        string line;
        bool   timingPointsStart = false;
        bool   hitEventsStart    = false;

        string[] tmp;

        System.IO.StreamReader file = new System.IO.StreamReader(filename);
        while ((line = file.ReadLine()) != null)
        {
            System.Console.WriteLine(line);
            line = line.Trim();
            if (line.Length == 0 || line[0] == '/')
            {
                continue;
            }

            //Debug.Log(line);

            if (line == "[TimingPoints]")
            {
                //Debug.Log("TimingPoints start");
                hitEventsStart    = false;
                timingPointsStart = true;
                continue;
            }
            if (line == "[HitEvents]")
            {
                //Debug.Log("hitobject start");
                hitEventsStart    = true;
                timingPointsStart = false;
                continue;
            }

            if (hitEventsStart)
            {
                //Debug.Log(line);
                tmp = line.Split(',');
                HitEvent hitEvents = new HitEvent();
                hitEvents.setKey(tmp[0]);
                hitEvents.setOffset(tmp[1]);
                hitEvents.setIsNote(tmp[2]);
                hitEvents.setIsMine(tmp[2]);
                hitEvents.setColour(tmp[2]);
                hitEvents.setFlashBlack(tmp[2]);
                hitEvents.setIsHold(tmp[2]);
                if (tmp.Length < 5)
                {
                    if (tmp.Length == 4)
                    {
                        hitEvents.setColorArray(tmp[3]);
                    }
                }
                else
                {
                    hitEvents.setEndOffset(tmp[3]);
                    hitEvents.setColorArray(tmp[4]);
                }
                GameManager.Instance.hitEventsList.Add(hitEvents);
            }

            if (timingPointsStart)
            {
                tmp = line.Split(',');
                TimingPoints timingPoints = new TimingPoints(Convert.ToInt32(tmp[0]), Convert.ToSingle(tmp[1]), Convert.ToInt32(tmp[2]), Convert.ToInt32(tmp[3]), Convert.ToInt32(tmp[4]));
                GameManager.Instance.timingPointsList.Add(timingPoints);
            }
        }
        file.Close();
    }
Esempio n. 18
0
 // based off of Quaver.API.Maps.Qua.EqualByValue()
 public bool Equals(Block other)
 {
     return(HitObjects.SequenceEqual(other.HitObjects, HitObjectInfo.ByValueComparer) &&
            TimingPoints.SequenceEqual(other.TimingPoints, TimingPointInfo.ByValueComparer) &&
            ScrollVelocities.SequenceEqual(other.ScrollVelocities, SliderVelocityInfo.ByValueComparer));
 }
Esempio n. 19
0
    void loadLevel()
    {
        Dictionary <string, string> song = SongSelectParser.Instance.selectedSong;
        string    filename  = song["SongMap"];
        AudioClip audioClip = Resources.Load <AudioClip>(song["SongPreview"]);

        songLength = audioClip.length;
        Debug.Log(songLength);
        audioSource.PlayOneShot(audioClip);

        string line;
        bool   timingPointsStart = false;
        bool   hitObjectsStart   = false;

        string[] tmp;


        // Read the file and display it line by line.
        System.IO.StreamReader file =
            new System.IO.StreamReader(filename);
        while ((line = file.ReadLine()) != null)
        {
            System.Console.WriteLine(line);
            line = line.Trim();
            if (line.Length == 0 || line[0] == '/')
            {
                continue;
            }
            //Debug.Log(line);

            if (line == "#TimingPoints")
            {
                //Debug.Log("TimingPoints start");
                hitObjectsStart   = false;
                timingPointsStart = true;
                continue;
            }
            if (line == "#HitObjects")
            {
                //Debug.Log("hitobject start");
                hitObjectsStart   = true;
                timingPointsStart = false;
                continue;
            }

            if (hitObjectsStart)
            {
                //Debug.Log(line);
                tmp = line.Split(',');
                HitObject hitObjects = new HitObject();
                hitObjects.setX(tmp[0]);
                hitObjects.setY(tmp[1]);
                hitObjects.setOffset(tmp[2]);
                hitObjects.setIsNote(tmp[3]);
                hitObjects.setIsMine(tmp[3]);
                hitObjects.setColour(tmp[3]);
                hitObjects.setFlashBlack(tmp[3]);
                hitObjects.setIsHold(tmp[3]);
                hitObjectsList.Add(hitObjects);
            }

            if (timingPointsStart)
            {
                tmp = line.Split(',');
                TimingPoints timingPoints = new TimingPoints(Convert.ToInt32(tmp[0]), Convert.ToDouble(tmp[1]), Convert.ToInt32(tmp[2]), Convert.ToInt32(tmp[3]), Convert.ToInt32(tmp[4]));
                timingPointsList.Add(timingPoints);
            }
        }
        file.Close();
    }
Esempio n. 20
0
 /// <summary>
 /// Sorts all <see cref="TimingPoint"/> in order of time.
 /// </summary>
 public void Sort()
 {
     TimingPoints = TimingPoints.OrderBy(o => o.Offset).ThenByDescending(o => o.Uninherited).ToList();
 }
Esempio n. 21
0
 public List <TimingPoint> GetAllGreenlines()
 {
     return(TimingPoints.Where(tp => !tp.Uninherited).ToList());
 }
Esempio n. 22
0
 /// <summary>
 /// Finds the nearest uninherited timing point which starts after a given time.
 /// </summary>
 /// <param name="time"></param>
 /// <returns></returns>
 public TimingPoint GetRedlineAfterTime(double time)
 {
     return(TimingPoints.FirstOrDefault(tp => Precision.DefinitelyBigger(tp.Offset, time) && tp.Uninherited));
 }