Esempio n. 1
0
        /// <summary> Returns an issue if this time is very close behind to a timing line which would modify objects. </summary>
        private IEnumerable <Issue> GetIssue(string aType, double aTime, Beatmap aBeatmap)
        {
            double unsnap = aBeatmap.GetPracticalUnsnap(aTime);

            TimingLine curLine  = aBeatmap.GetTimingLine(aTime);
            TimingLine nextLine = aBeatmap.GetNextTimingLine(aTime);

            if (nextLine != null)
            {
                double curEffectiveBPM  = curLine.svMult * aBeatmap.GetTimingLine <UninheritedLine>(aTime).bpm;
                double nextEffectiveBPM = nextLine.svMult * aBeatmap.GetTimingLine <UninheritedLine>(nextLine.offset).bpm;

                double deltaEffectiveBPM = curEffectiveBPM - nextEffectiveBPM;

                double timeDiff = nextLine.offset - aTime;
                if (timeDiff > 0 && timeDiff <= 5 &&
                    Math.Abs(unsnap) <= 1 &&
                    Math.Abs(deltaEffectiveBPM) > 1)
                {
                    yield return(new Issue(GetTemplate("Behind"), aBeatmap,
                                           Timestamp.Get(aTime), aType, $"{timeDiff:0.##}"));
                }

                if (aBeatmap.generalSettings.mode == Beatmap.Mode.Taiko &&
                    timeDiff < 0 && timeDiff >= -5 &&
                    Math.Abs(unsnap) <= 1 &&
                    Math.Abs(deltaEffectiveBPM) > 1)
                {
                    yield return(new Issue(GetTemplate("After"), aBeatmap,
                                           Timestamp.Get(aTime), aType, $"{timeDiff:0.##}"));
                }
            }
        }
        /// <summary> Returns an issue if this time is very close behind to a timing line which would modify objects. </summary>
        private IEnumerable <Issue> GetIssue(string type, double time, Beatmap beatmap)
        {
            double unsnap = beatmap.GetPracticalUnsnap(time);

            TimingLine curLine  = beatmap.GetTimingLine(time);
            TimingLine nextLine = curLine.Next(skipConcurrent: true);

            if (nextLine != null)
            {
                double curEffectiveBPM  = curLine.svMult * beatmap.GetTimingLine <UninheritedLine>(time).bpm;
                double nextEffectiveBPM = nextLine.svMult * beatmap.GetTimingLine <UninheritedLine>(nextLine.offset).bpm;

                double deltaEffectiveBPM = curEffectiveBPM - nextEffectiveBPM;

                double timeDiff = nextLine.offset - time;
                if (timeDiff > 0 && timeDiff <= 5 &&
                    Math.Abs(unsnap) <= 1 &&
                    Math.Abs(deltaEffectiveBPM) > 1)
                {
                    yield return(new Issue(GetTemplate("Before"), beatmap,
                                           Timestamp.Get(time), type, $"{timeDiff:0.##}"));
                }

                if (beatmap.generalSettings.mode == Beatmap.Mode.Taiko &&
                    timeDiff < 0 && timeDiff >= -5 &&
                    Math.Abs(unsnap) <= 1 &&
                    Math.Abs(deltaEffectiveBPM) > 1)
                {
                    yield return(new Issue(GetTemplate("After"), beatmap,
                                           Timestamp.Get(time), type, $"{timeDiff:0.##}"));
                }
            }
        }
Esempio n. 3
0
        private IEnumerable <Issue> GetInheritedLineIssues(Beatmap aBeatmap)
        {
            List <TimingLine> lines = aBeatmap.timingLines.ToList();

            for (int i = 1; i < lines.Count; ++i)
            {
                if (!(lines[i] is InheritedLine currentLine))
                {
                    continue;
                }

                TimingLine previousLine = lines[i - 1];
                TimingLine nextLine     = aBeatmap.GetNextTimingLine(currentLine.offset);

                double timingSectionEnd = nextLine?.offset ?? aBeatmap.GetPlayTime();

                double prevEndTime     = aBeatmap.GetPrevHitObject(timingSectionEnd)?.GetEndTime() ?? 0;
                double prevSliderStart = aBeatmap.GetPrevHitObject <Slider>(timingSectionEnd)?.time ?? 0;

                bool containsObjects = prevEndTime >= currentLine.offset;
                bool canAffectSV     =
                    prevSliderStart >= currentLine.offset ||
                    aBeatmap.generalSettings.mode == Beatmap.Mode.Mania;

                bool sampleSettingsDiffer =
                    currentLine.sampleset != previousLine.sampleset ||
                    currentLine.customIndex != previousLine.customIndex ||
                    currentLine.volume != previousLine.volume;

                // Conditions for an inherited line being used (simplified, only false positives, e.g. hold notes/spinners)
                // - Changes sampleset, custom index, or volume and there is an edge/body within the time frame
                // - Changes SV and there are sliders starting within the time frame or changes SV and the mode is mania
                // - Changes kiai (causes effects on screen during duration)
                bool used =
                    containsObjects && sampleSettingsDiffer ||
                    canAffectSV && currentLine.svMult != previousLine.svMult ||
                    currentLine.kiai != previousLine.kiai;

                // Since "used" only includes false positives, this only includes false negatives,
                // hence the check will never say that a used line is unused.
                if (!used)
                {
                    // Avoids confusion in case the line actually does change something
                    // from the previous, but just doesn't apply to anything.
                    string changesDesc = "";
                    if (!canAffectSV && currentLine.svMult != previousLine.svMult)
                    {
                        changesDesc += "SV";
                    }
                    if (!containsObjects && sampleSettingsDiffer)
                    {
                        changesDesc += (changesDesc.Length > 0 ? " and " : "") + "sample settings";
                    }
                    changesDesc += changesDesc.Length > 0 ? ", but affects nothing" : "nothing";

                    yield return(new Issue(GetTemplate("Minor Inherited"),
                                           aBeatmap, Timestamp.Get(currentLine.offset), changesDesc));
                }
            }
        }
        private static double GetBeatsPerMinute(this TimingLine timingLine)
        {
            var msPerBeatString = timingLine.code.Split(",")[1];
            var msPerBeat       = double.Parse(msPerBeatString, CultureInfo.InvariantCulture);

            return(60000 / msPerBeat);
        }
        /// <summary> Returns whether this section contains the respective hit object type.
        /// Only counts the start of objects. </summary>
        private bool SectionContainsObject <T>(Beatmap beatmap, TimingLine line) where T : HitObject
        {
            TimingLine nextLine            = line.Next(skipConcurrent: true);
            double     nextSectionEnd      = nextLine?.offset ?? beatmap.GetPlayTime();
            double     objectTimeBeforeEnd = beatmap.GetPrevHitObject <T>(nextSectionEnd)?.time ?? 0;

            return(objectTimeBeforeEnd >= line.offset);
        }
Esempio n. 6
0
        public override IEnumerable <Issue> GetIssues(Beatmap aBeatmap)
        {
            List <UninheritedLine> lines = aBeatmap.timingLines.OfType <UninheritedLine>().ToList();

            for (int i = 1; i < lines.Count; ++i)
            {
                // Uninherited lines 4 beats apart (varying up to 1 ms for rounding errors),
                // with the same bpm and meter, have the same downbeat structure.
                // At which point the latter could be replaced by an inherited line and
                // function identically (other than the finish in the nightcore mod).
                if (lines[i - 1].bpm == lines[i].bpm &&
                    lines[i - 1].meter == lines[i].meter &&
                    GetBeatOffset(lines[i - 1], lines[i], 4) <= 1)
                {
                    // Check the lines in effect both here and before to see if an inherited
                    // line is placed on top of the red line negating its changes.
                    TimingLine prevLine = aBeatmap.GetTimingLine(lines[i].offset - 1);
                    TimingLine curLine  = aBeatmap.GetTimingLine <UninheritedLine>(lines[i].offset);

                    // If a line omits the first bar line we just treat it as used.
                    if (curLine.omitsBarLine)
                    {
                        continue;
                    }

                    if (prevLine.kiai == curLine.kiai &&
                        prevLine.sampleset == curLine.sampleset &&
                        prevLine.volume == curLine.volume)
                    {
                        // In the nightcore mod, every 4th downbeat is inherently a
                        // finish sound, so that technically changes things
                        if (GetBeatOffset(lines[i - 1], lines[i], 16) <= 1)
                        {
                            yield return(new Issue(GetTemplate("Problem Nothing"),
                                                   aBeatmap, Timestamp.Get(lines[i].offset)));
                        }
                        else
                        {
                            yield return(new Issue(GetTemplate("Warning Nothing"),
                                                   aBeatmap, Timestamp.Get(lines[i].offset)));
                        }
                    }
                    else
                    {
                        if (GetBeatOffset(lines[i - 1], lines[i], 16) <= 1)
                        {
                            yield return(new Issue(GetTemplate("Problem Inherited"),
                                                   aBeatmap, Timestamp.Get(lines[i].offset)));
                        }
                        else
                        {
                            yield return(new Issue(GetTemplate("Warning Inherited"),
                                                   aBeatmap, Timestamp.Get(lines[i].offset)));
                        }
                    }
                }
            }
        }
Esempio n. 7
0
        public override IEnumerable <Issue> GetIssues(Beatmap beatmap)
        {
            foreach (TimingLine line in beatmap.timingLines.Where(line => line.kiai))
            {
                // If we're inside of kiai, a new line with kiai won't cause kiai to start again.
                if (beatmap.GetTimingLine(line.offset - 1).kiai)
                {
                    continue;
                }

                double unsnap = beatmap.GetPracticalUnsnap(line.offset);

                // In taiko the screen changes color more drastically, so timing is more noticable.
                int warningThreshold = beatmap.generalSettings.mode == Beatmap.Mode.Taiko ? 5 : 10;
                if (Math.Abs(unsnap) >= warningThreshold)
                {
                    yield return(new Issue(GetTemplate("Warning"), beatmap,
                                           Timestamp.Get(line.offset), unsnap));
                }

                else if (Math.Abs(unsnap) >= 1)
                {
                    yield return(new Issue(GetTemplate("Minor"), beatmap,
                                           Timestamp.Get(line.offset), unsnap));
                }

                // Prevents duplicate issues occuring from both red and green line on same tick picking next line.
                if (beatmap.timingLines.Any(
                        otherLine => otherLine.offset == line.offset &&
                        !otherLine.uninherited && line.uninherited))
                {
                    continue;
                }

                TimingLine nextLine = line.Next(skipConcurrent: true);
                if (nextLine == null || nextLine.kiai)
                {
                    continue;
                }

                unsnap = beatmap.GetPracticalUnsnap(nextLine.offset);

                if (Math.Abs(unsnap) >= 1)
                {
                    yield return(new Issue(GetTemplate("Minor End"), beatmap,
                                           Timestamp.Get(nextLine.offset), unsnap));
                }
            }
        }
Esempio n. 8
0
        public override IEnumerable <Issue> GetIssues(Beatmap aBeatmap)
        {
            foreach (TimingLine line in aBeatmap.timingLines.Where(aLine => aLine.kiai))
            {
                // If we're inside of kiai, a new line with kiai won't cause kiai to start again.
                if (aBeatmap.GetTimingLine(line.offset - 1).kiai)
                {
                    continue;
                }

                double unsnap = aBeatmap.GetPracticalUnsnap(line.offset);

                if (Math.Abs(unsnap) >= 10)
                {
                    yield return(new Issue(GetTemplate("Warning"), aBeatmap,
                                           Timestamp.Get(line.offset), unsnap));
                }

                else if (Math.Abs(unsnap) >= 1)
                {
                    yield return(new Issue(GetTemplate("Minor"), aBeatmap,
                                           Timestamp.Get(line.offset), unsnap));
                }

                // Prevents duplicate issues occuring from both red and green line on same tick picking next line.
                if (aBeatmap.timingLines.Any(aLine => aLine.offset == line.offset && !aLine.uninherited && line.uninherited))
                {
                    continue;
                }

                TimingLine nextLine = aBeatmap.GetNextTimingLine(line.offset);
                if (nextLine != null && !nextLine.kiai)
                {
                    unsnap = aBeatmap.GetPracticalUnsnap(nextLine.offset);

                    if (Math.Abs(unsnap) >= 1)
                    {
                        yield return(new Issue(GetTemplate("Minor End"), aBeatmap,
                                               Timestamp.Get(nextLine.offset), unsnap));
                    }
                }
            }
        }
Esempio n. 9
0
        public override IEnumerable <Issue> GetIssues(Beatmap aBeatmap)
        {
            if (aBeatmap.timingLines.Count > 0)
            {
                TimingLine line = aBeatmap.timingLines[0];

                if (!line.uninherited)
                {
                    yield return(new Issue(GetTemplate("Inherited"), aBeatmap, Timestamp.Get(line.offset)));
                }
                else if (line.kiai)
                {
                    yield return(new Issue(GetTemplate("Toggles Kiai"), aBeatmap, Timestamp.Get(line.offset)));
                }
            }
            else
            {
                yield return(new Issue(GetTemplate("No Lines"), aBeatmap));
            }
        }
        private IEnumerable <Issue> GetInheritedLineIssues(Beatmap beatmap)
        {
            List <TimingLine> lines = beatmap.timingLines.ToList();

            for (int i = 1; i < lines.Count; ++i)
            {
                if (!(lines[i] is InheritedLine currentLine))
                {
                    continue;
                }

                TimingLine previousLine = lines[i - 1];

                // Since "used" only includes false positives, this will only result in false negatives,
                // hence the check will never say that a used line is unused.
                if (IsLineUsed(beatmap, currentLine, previousLine))
                {
                    continue;
                }

                // Avoids confusion in case the line actually does change something from the
                // previous, but just doesn't apply to anything.
                string changesDesc = "";
                if (!UsesSV(beatmap, currentLine, previousLine) && currentLine.svMult != previousLine.svMult)
                {
                    changesDesc += "SV";
                }
                if (!UsesSamples(beatmap, currentLine, previousLine) && SamplesDiffer(currentLine, previousLine))
                {
                    changesDesc += (changesDesc.Length > 0 ? " and " : "") + "sample settings";
                }
                changesDesc += changesDesc.Length > 0 ? ", but affects nothing" : "nothing";

                yield return(new Issue(GetTemplate("Minor Inherited"),
                                       beatmap, Timestamp.Get(currentLine.offset), changesDesc));
            }
        }
Esempio n. 11
0
        /// <summary> Returns an issue if this time is very close behind to a timing line which would modify objects. </summary>
        private IEnumerable <Issue> GetIssue(string type, double time, Beatmap beatmap)
        {
            double unsnap = beatmap.GetPracticalUnsnap(time);

            TimingLine curLine  = beatmap.GetTimingLine(time);
            TimingLine nextLine = curLine.Next(skipConcurrent: true);

            if (nextLine == null)
            {
                yield break;
            }

            double curEffectiveBPM  = curLine.svMult * beatmap.GetTimingLine <UninheritedLine>(time).bpm;
            double nextEffectiveBPM = nextLine.svMult * beatmap.GetTimingLine <UninheritedLine>(nextLine.offset).bpm;

            double deltaEffectiveBPM = curEffectiveBPM - nextEffectiveBPM;

            double timeDiff = nextLine.offset - time;

            if (timeDiff > 0 && timeDiff <= 5 &&
                Math.Abs(unsnap) <= 1 &&
                Math.Abs(deltaEffectiveBPM) > 1)
            {
                yield return(new Issue(GetTemplate("Before"), beatmap,
                                       Timestamp.Get(time), type, $"{timeDiff:0.##}"));
            }

            // Modes where SV affects AR would be impacted even if the object was right after the line.
            if (IsSVAffectingAR(beatmap) &&
                timeDiff < 0 && timeDiff >= -5 &&
                Math.Abs(unsnap) <= 1 &&
                Math.Abs(deltaEffectiveBPM) > 1)
            {
                yield return(new Issue(GetTemplate("After"), beatmap,
                                       Timestamp.Get(time), type, $"{timeDiff:0.##}"));
            }
        }
Esempio n. 12
0
        private IEnumerable <Issue> GetUninheritedLineIssues(Beatmap aBeatmap)
        {
            // If the previous line omits the first barline in taiko and is less than a beat apart from the new one,
            // then the new one does change things even if it's just a ms ahead (prevents the barline from being
            // thicker than normal).
            bool canOmitBarLine =
                aBeatmap.generalSettings.mode == Beatmap.Mode.Taiko ||
                aBeatmap.generalSettings.mode == Beatmap.Mode.Mania;

            List <UninheritedLine> lines = aBeatmap.timingLines.OfType <UninheritedLine>().ToList();

            for (int i = 1; i < lines.Count; ++i)
            {
                bool negligibleDownbeatOffset        = GetBeatOffset(lines[i - 1], lines[i], lines[i - 1].meter) <= 1;
                bool negligibleNightcoreCymbalOffset = GetBeatOffset(lines[i - 1], lines[i], 4 * lines[i - 1].meter) <= 1;

                if (canOmitBarLine && lines[i - 1].omitsBarLine)
                {
                    negligibleDownbeatOffset = GetBeatOffset(lines[i - 1], lines[i], lines[i - 1].meter) == 0;
                }

                // Uninherited lines 4 (or whatever the meter is) beats apart (varying up to 1 ms for rounding errors),
                // with the same bpm and meter, have the same downbeat structure. At which point the latter could be
                // replaced by an inherited line and function identically (other than the finish in the nightcore mod).
                if (lines[i - 1].bpm != lines[i].bpm ||
                    lines[i - 1].meter != lines[i].meter ||
                    !negligibleDownbeatOffset)
                {
                    continue;
                }

                // Check the lines in effect both here and before to see if an inherited
                // line is placed on top of the red line negating its changes.
                TimingLine previousLine = aBeatmap.GetTimingLine(lines[i].offset - 1);
                TimingLine currentLine  = aBeatmap.GetTimingLine <UninheritedLine>(lines[i].offset);

                // If a line omits the first bar line we just treat it as used.
                if (canOmitBarLine && currentLine.omitsBarLine)
                {
                    continue;
                }

                if (previousLine.kiai == currentLine.kiai &&
                    previousLine.sampleset == currentLine.sampleset &&
                    previousLine.customIndex == currentLine.customIndex &&
                    previousLine.volume == currentLine.volume)
                {
                    // In the nightcore mod, every 4th (or whatever the meter is) downbeat
                    // has an added cymbal, so that technically changes things.
                    if (negligibleNightcoreCymbalOffset)
                    {
                        yield return(new Issue(GetTemplate("Problem Nothing"),
                                               aBeatmap, Timestamp.Get(lines[i].offset)));
                    }
                    else
                    {
                        yield return(new Issue(GetTemplate("Warning Nothing"),
                                               aBeatmap, Timestamp.Get(lines[i].offset)));
                    }
                }
                else
                {
                    if (negligibleNightcoreCymbalOffset)
                    {
                        yield return(new Issue(GetTemplate("Problem Inherited"),
                                               aBeatmap, Timestamp.Get(lines[i].offset)));
                    }
                    else
                    {
                        yield return(new Issue(GetTemplate("Warning Inherited"),
                                               aBeatmap, Timestamp.Get(lines[i].offset)));
                    }
                }
            }
        }
 /// <summary> Returns whether changes to SV for the line will be used. </summary>
 private bool CanUseSV(Beatmap beatmap, TimingLine line) =>
 SectionContainsObject <Slider>(beatmap, line) ||
 // Taiko and mania affect approach rate through SV.
 beatmap.generalSettings.mode == Beatmap.Mode.Taiko ||
 beatmap.generalSettings.mode == Beatmap.Mode.Mania;
 /// <summary> Returns whether this section is affected by SV changes. </summary>
 private bool UsesSV(Beatmap beatmap, TimingLine currentLine, TimingLine previousLine) =>
 CanUseSV(beatmap, currentLine) && currentLine.svMult != previousLine.svMult;
 /// <summary> Returns whether this section changes sample settings (i.e. volume, sampleset, or custom index). </summary>
 private bool SamplesDiffer(TimingLine currentLine, TimingLine previousLine) =>
 currentLine.sampleset != previousLine.sampleset ||
 currentLine.customIndex != previousLine.customIndex ||
 currentLine.volume != previousLine.volume;
 /// <summary> Returns whether this section makes use of sample changes (i.e. volume, sampleset, or custom index). </summary>
 private bool UsesSamples(Beatmap beatmap, TimingLine currentLine, TimingLine previousLine) =>
 SectionContainsObject <HitObject>(beatmap, currentLine) && SamplesDiffer(currentLine, previousLine);
 /// <summary> Returns whether a line is considered used. Only partially covers uninherited lines.
 /// <br></br><br></br>
 /// Conditions for an inherited line being used (simplified, only false positives, e.g. hold notes/spinners) <br></br>
 /// - Changes sampleset, custom index, or volume and there is an edge/body within the time frame <br></br>
 /// - Changes SV and there are sliders starting within the time frame or changes SV and the mode is mania <br></br>
 /// - Changes kiai (causes effects on screen during duration) </summary>
 private bool IsLineUsed(Beatmap beatmap, TimingLine currentLine, TimingLine previousLine) =>
 UsesSamples(beatmap, currentLine, previousLine) ||
 UsesSV(beatmap, currentLine, previousLine) ||
 currentLine.kiai != previousLine.kiai;
        private IEnumerable <Issue> GetUninheritedLineIssues(Beatmap beatmap)
        {
            List <TimingLine> lines = beatmap.timingLines.ToList();

            for (int i = 1; i < lines.Count; ++i)
            {
                if (!(lines[i] is UninheritedLine currentLine))
                {
                    continue;
                }

                // Can't do lines[i - 1] since that could give a green line on the same offset, which we don't want.
                TimingLine      previousLine            = beatmap.GetTimingLine(currentLine.offset - 1);
                UninheritedLine previousUninheritedLine = beatmap.GetTimingLine <UninheritedLine>(currentLine.offset - 1);

                if (!DownbeatsAlign(beatmap, currentLine, previousUninheritedLine))
                {
                    continue;
                }

                bool changesNCCymbals = false;
                if (!NightcoreCymbalsAlign(beatmap, currentLine, previousUninheritedLine))
                {
                    changesNCCymbals = true;
                }

                bool omittingBarline   = false;
                bool correctingBarline = false;
                if (CanOmitBarLine(beatmap))
                {
                    // e.g. red line used mid-measure to account for bpm change shouldn't create a barline, so it's omitted, but the
                    // end of the measure won't have a barline unless another red line is placed there to correct it, hence both used.
                    omittingBarline   = currentLine.omitsBarLine;
                    correctingBarline = previousUninheritedLine.omitsBarLine && !BarLinesAlign(beatmap, currentLine, previousUninheritedLine);
                    // Omitting bar lines isn't commonly seen in standard, so it's likely that people will
                    // miss incorrect usages of it, hence warn if it's the only thing keeping it used.
                    if ((omittingBarline || correctingBarline) && beatmap.generalSettings.mode != Beatmap.Mode.Standard)
                    {
                        continue;
                    }
                }

                List <string> notImmediatelyObvious = new List <string>();
                if (omittingBarline)
                {
                    notImmediatelyObvious.Add("omitting first barline");
                }
                if (correctingBarline)
                {
                    notImmediatelyObvious.Add($"correcting the omitted barline at {Timestamp.Get(previousUninheritedLine.offset)}");
                }
                if (changesNCCymbals)
                {
                    notImmediatelyObvious.Add("nightcore mod cymbals");
                }
                string notImmediatelyObviousStr = string.Join(" and ", notImmediatelyObvious);

                if (!IsLineUsed(beatmap, currentLine, previousLine))
                {
                    if (notImmediatelyObvious.Count == 0)
                    {
                        yield return(new Issue(GetTemplate("Problem"),
                                               beatmap, Timestamp.Get(currentLine.offset)));
                    }
                    else
                    {
                        yield return(new Issue(GetTemplate("Warning"),
                                               beatmap, Timestamp.Get(currentLine.offset), notImmediatelyObviousStr));
                    }
                }
                else
                {
                    if (notImmediatelyObvious.Count == 0)
                    {
                        yield return(new Issue(GetTemplate("Problem Inherited"),
                                               beatmap, Timestamp.Get(currentLine.offset)));
                    }
                    else
                    {
                        yield return(new Issue(GetTemplate("Warning Inherited"),
                                               beatmap, Timestamp.Get(currentLine.offset), notImmediatelyObviousStr));
                    }
                }
            }
        }
Esempio n. 19
0
        public override IEnumerable <DiffInstance> Translate(IEnumerable <DiffInstance> aDiffs)
        {
            List <Tuple <DiffInstance, TimingLine> > addedTimingLines   = new List <Tuple <DiffInstance, TimingLine> >();
            List <Tuple <DiffInstance, TimingLine> > removedTimingLines = new List <Tuple <DiffInstance, TimingLine> >();

            foreach (DiffInstance diff in aDiffs)
            {
                TimingLine timingLine = null;
                try
                {
                    timingLine = new TimingLine(diff.difference.Split(','), beatmap: null);
                }
                catch
                {
                    // Failing to parse a changed line shouldn't stop it from showing.
                }

                if (timingLine != null)
                {
                    if (diff.diffType == DiffType.Added)
                    {
                        addedTimingLines.Add(new Tuple <DiffInstance, TimingLine>(diff, timingLine));
                    }
                    else
                    {
                        removedTimingLines.Add(new Tuple <DiffInstance, TimingLine>(diff, timingLine));
                    }
                }
                else
                {
                    // Shows the raw .osu line change.
                    yield return(diff);
                }
            }

            foreach (Tuple <DiffInstance, TimingLine> addedTuple in addedTimingLines)
            {
                DiffInstance addedDiff = addedTuple.Item1;
                TimingLine   addedLine = addedTuple.Item2;

                string stamp = Timestamp.Get(addedLine.offset);
                string type  = addedLine.uninherited ? "Uninherited line" : "Inherited line";

                bool found = false;
                foreach (TimingLine removedLine in removedTimingLines.Select(aTuple => aTuple.Item2).ToList())
                {
                    if (!addedLine.offset.AlmostEqual(removedLine.offset))
                    {
                        continue;
                    }

                    string removedType = removedLine.uninherited ? "Uninherited line" : "Inherited line";
                    if (type != removedType)
                    {
                        continue;
                    }

                    List <string> changes = new List <string>();

                    if (addedLine.kiai != removedLine.kiai)
                    {
                        changes.Add("Kiai changed from " + (removedLine.kiai ? "enabled" : "disabled") +
                                    " to " + (addedLine.kiai ? "enabled" : "disabled") + ".");
                    }

                    if (addedLine.meter != removedLine.meter)
                    {
                        changes.Add("Timing signature changed from " + removedLine.meter + "/4" +
                                    " to " + addedLine.meter + "/4.");
                    }

                    if (addedLine.sampleset != removedLine.sampleset)
                    {
                        changes.Add("Sampleset changed from " +
                                    removedLine.sampleset.ToString().ToLower() + " to " +
                                    addedLine.sampleset.ToString().ToLower() + ".");
                    }

                    if (addedLine.customIndex != removedLine.customIndex)
                    {
                        changes.Add("Custom sampleset index changed from " +
                                    removedLine.customIndex.ToString().ToLower() + " to " +
                                    addedLine.customIndex.ToString().ToLower() + ".");
                    }

                    if (!addedLine.volume.AlmostEqual(removedLine.volume))
                    {
                        changes.Add("Volume changed from " + removedLine.volume +
                                    " to " + addedLine.volume + ".");
                    }

                    if (type == "Uninherited line")
                    {
                        UninheritedLine addedUninherited   = new UninheritedLine(addedLine.code.Split(','), beatmap: null);
                        UninheritedLine removedUninherited = new UninheritedLine(removedLine.code.Split(','), beatmap: null);

                        if (!addedUninherited.bpm.AlmostEqual(removedUninherited.bpm))
                        {
                            changes.Add("BPM changed from " + removedUninherited.bpm +
                                        " to " + addedUninherited.bpm + ".");
                        }
                    }
                    else if (!addedLine.svMult.AlmostEqual(removedLine.svMult))
                    {
                        changes.Add("Slider velocity multiplier changed from " + removedLine.svMult +
                                    " to " + addedLine.svMult + ".");
                    }

                    if (changes.Count == 1)
                    {
                        yield return(new DiffInstance(stamp + changes[0],
                                                      Section, DiffType.Changed, new List <string>(), addedDiff.snapshotCreationDate));
                    }
                    else if (changes.Count > 1)
                    {
                        yield return(new DiffInstance(stamp + type + " changed.",
                                                      Section, DiffType.Changed, changes, addedDiff.snapshotCreationDate));
                    }

                    found = true;
                    removedTimingLines.RemoveAll(aTuple => aTuple.Item2.code == removedLine.code);
                }

                if (!found)
                {
                    yield return(new DiffInstance(stamp + type + " added.",
                                                  Section, DiffType.Added, new List <string>(), addedDiff.snapshotCreationDate));
                }
            }

            foreach (Tuple <DiffInstance, TimingLine> removedTuple in removedTimingLines)
            {
                DiffInstance removedDiff = removedTuple.Item1;
                TimingLine   removedLine = removedTuple.Item2;

                string stamp = Timestamp.Get(removedLine.offset);
                string type  = removedLine.uninherited ? "Uninherited line" : "Inherited line";

                yield return(new DiffInstance(stamp + type + " removed.",
                                              Section, DiffType.Removed, new List <string>(), removedDiff.snapshotCreationDate));
            }
        }
Esempio n. 20
0
 /// <summary> Returns whether this section changes sample settings (i.e. volume, sampleset, or custom index). </summary>
 private static bool SamplesDiffer(TimingLine currentLine, TimingLine previousLine) =>
 currentLine.sampleset != previousLine.sampleset ||
 currentLine.customIndex != previousLine.customIndex ||
 !currentLine.volume.AlmostEqual(previousLine.volume);
Esempio n. 21
0
 /// <summary> Returns whether this section is affected by SV changes. </summary>
 private static bool UsesSV(Beatmap beatmap, TimingLine currentLine, TimingLine previousLine) =>
 CanUseSV(beatmap, currentLine) && !currentLine.svMult.AlmostEqual(previousLine.svMult);