diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index cc46ec7be3..edf3f35304 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void Test(double expected, string name) => base.Test(expected, name); - protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuLegacyDifficultyCalculator(new OsuRuleset(), beatmap); + protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap); protected override Ruleset CreateRuleset() => new OsuRuleset(); } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index fd54dc0260..6e991a1d08 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Difficulty { @@ -13,10 +12,5 @@ namespace osu.Game.Rulesets.Osu.Difficulty public double ApproachRate; public double OverallDifficulty; public int MaxCombo; - - public OsuDifficultyAttributes(Mod[] mods, double starRating) - : base(mods, starRating) - { - } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs similarity index 54% rename from osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs rename to osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 137630fb85..e2a1542574 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -1,10 +1,13 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Skills; @@ -13,53 +16,19 @@ using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Difficulty { - public class OsuLegacyDifficultyCalculator : LegacyDifficultyCalculator + public class OsuDifficultyCalculator : DifficultyCalculator { - private const int section_length = 400; private const double difficulty_multiplier = 0.0675; - public OsuLegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) { } - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate) + protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - if (!beatmap.HitObjects.Any()) - return new OsuDifficultyAttributes(mods, 0); - - OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast().ToList(), clockRate); - Skill[] skills = - { - new Aim(), - new Speed() - }; - - double sectionLength = section_length * clockRate; - - // The first object doesn't generate a strain, so we begin with an incremented section end - double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength; - - foreach (OsuDifficultyHitObject h in difficultyBeatmap) - { - while (h.BaseObject.StartTime > currentSectionEnd) - { - foreach (Skill s in skills) - { - s.SaveCurrentPeak(); - s.StartNewSectionFrom(currentSectionEnd); - } - - currentSectionEnd += sectionLength; - } - - foreach (Skill s in skills) - s.Process(h); - } - - // The peak strain will not be saved for the last section in the above loop - foreach (Skill s in skills) - s.SaveCurrentPeak(); + if (beatmap.HitObjects.Count == 0) + return new OsuDifficultyAttributes { Mods = mods }; double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; @@ -73,8 +42,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); - return new OsuDifficultyAttributes(mods, starRating) + return new OsuDifficultyAttributes { + StarRating = starRating, + Mods = mods, AimStrain = aimRating, SpeedStrain = speedRating, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, @@ -83,6 +54,26 @@ namespace osu.Game.Rulesets.Osu.Difficulty }; } + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) + { + // The first jump is formed by the first two hitobjects of the map. + // If the map has less than two OsuHitObjects, the enumerator will not return anything. + for (int i = 1; i < beatmap.HitObjects.Count; i++) + { + var lastLast = i > 1 ? beatmap.HitObjects[i - 2] : null; + var last = beatmap.HitObjects[i - 1]; + var current = beatmap.HitObjects[i]; + + yield return new OsuDifficultyHitObject(current, lastLast, last, clockRate); + } + } + + protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] + { + new Aim(), + new Speed() + }; + protected override Mod[] DifficultyAdjustmentMods => new Mod[] { new OsuModDoubleTime(), diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs deleted file mode 100644 index 068564d50c..0000000000 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Rulesets.Osu.Objects; - -namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing -{ - /// - /// An enumerable container wrapping input as - /// which contains extra data required for difficulty calculation. - /// - public class OsuDifficultyBeatmap : IEnumerable - { - private readonly IEnumerator difficultyObjects; - - /// - /// Creates an enumerator, which preprocesses a list of s recieved as input, wrapping them as - /// which contains extra data required for difficulty calculation. - /// - public OsuDifficultyBeatmap(List objects, double timeRate) - { - // Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases. - // This should probably happen before the objects reach the difficulty calculator. - difficultyObjects = createDifficultyObjectEnumerator(objects.OrderBy(h => h.StartTime).ToList(), timeRate); - } - - /// - /// Returns an enumerator that enumerates all s in the . - /// - public IEnumerator GetEnumerator() => difficultyObjects; - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - private IEnumerator createDifficultyObjectEnumerator(List objects, double timeRate) - { - // The first jump is formed by the first two hitobjects of the map. - // If the map has less than two OsuHitObjects, the enumerator will not return anything. - for (int i = 1; i < objects.Count; i++) - { - var lastLast = i > 1 ? objects[i - 2] : null; - var last = objects[i - 1]; - var current = objects[i]; - - yield return new OsuDifficultyHitObject(lastLast, last, current, timeRate); - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 1ec12adb3b..930c711783 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -1,24 +1,20 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Linq; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osuTK; namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing { - /// - /// A wrapper around extending it with additional data required for difficulty calculation. - /// - public class OsuDifficultyHitObject + public class OsuDifficultyHitObject : DifficultyHitObject { private const int normalized_radius = 52; - /// - /// The this refers to. - /// - public OsuHitObject BaseObject { get; } + protected new OsuHitObject BaseObject => (OsuHitObject)base.BaseObject; /// /// Normalized distance from the end position of the previous to the start position of this . @@ -30,40 +26,30 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing /// public double TravelDistance { get; private set; } - /// - /// Milliseconds elapsed since the StartTime of the previous . - /// - public double DeltaTime { get; private set; } - - /// - /// Milliseconds elapsed since the start time of the previous , with a minimum of 50ms. - /// - public double StrainTime { get; private set; } - /// /// Angle the player has to take to hit this . /// Calculated as the angle between the circles (current-2, current-1, current). /// public double? Angle { get; private set; } + /// + /// Milliseconds elapsed since the start time of the previous , with a minimum of 50ms. + /// + public readonly double StrainTime; + private readonly OsuHitObject lastLastObject; private readonly OsuHitObject lastObject; - private readonly double timeRate; - /// - /// Initializes the object calculating extra data required for difficulty calculation. - /// - public OsuDifficultyHitObject(OsuHitObject lastLastObject, OsuHitObject lastObject, OsuHitObject currentObject, double timeRate) + public OsuDifficultyHitObject(HitObject hitObject, HitObject lastLastObject, HitObject lastObject, double clockRate) + : base(hitObject, lastObject, clockRate) { - this.lastLastObject = lastLastObject; - this.lastObject = lastObject; - this.timeRate = timeRate; - - BaseObject = currentObject; + this.lastLastObject = (OsuHitObject)lastLastObject; + this.lastObject = (OsuHitObject)lastObject; setDistances(); - setTimingValues(); - // Calculate angle here + + // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure + StrainTime = Math.Max(50, DeltaTime); } private void setDistances() @@ -102,14 +88,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing } } - private void setTimingValues() - { - DeltaTime = (BaseObject.StartTime - lastObject.StartTime) / timeRate; - - // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure - StrainTime = Math.Max(50, DeltaTime); - } - private void computeSliderCursorPosition(Slider slider) { if (slider.LazyEndPosition != null) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index b5e57985e9..e74f4933b2 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; +using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -17,33 +20,40 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double SkillMultiplier => 26.25; protected override double StrainDecayBase => 0.15; - protected override double StrainValueOf(OsuDifficultyHitObject current) + protected override double StrainValueOf(DifficultyHitObject current) { + if (current.BaseObject is Spinner) + return 0; + + var osuCurrent = (OsuDifficultyHitObject)current; + double result = 0; - const double scale = 90; - - double applyDiminishingExp(double val) => Math.Pow(val, 0.99); - if (Previous.Count > 0) { - if (current.Angle != null && current.Angle.Value > angle_bonus_begin) + var osuPrevious = (OsuDifficultyHitObject)Previous[0]; + + if (osuCurrent.Angle != null && osuCurrent.Angle.Value > angle_bonus_begin) { + const double scale = 90; + var angleBonus = Math.Sqrt( - Math.Max(Previous[0].JumpDistance - scale, 0) - * Math.Pow(Math.Sin(current.Angle.Value - angle_bonus_begin), 2) - * Math.Max(current.JumpDistance - scale, 0)); - result = 1.5 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, Previous[0].StrainTime); + Math.Max(osuPrevious.JumpDistance - scale, 0) + * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2) + * Math.Max(osuCurrent.JumpDistance - scale, 0)); + result = 1.5 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); } } - double jumpDistanceExp = applyDiminishingExp(current.JumpDistance); - double travelDistanceExp = applyDiminishingExp(current.TravelDistance); + double jumpDistanceExp = applyDiminishingExp(osuCurrent.JumpDistance); + double travelDistanceExp = applyDiminishingExp(osuCurrent.TravelDistance); return Math.Max( - result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(current.StrainTime, timing_threshold), - (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / current.StrainTime + result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.StrainTime, timing_threshold), + (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.StrainTime ); } + + private double applyDiminishingExp(double val) => Math.Pow(val, 0.99); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs deleted file mode 100644 index 2f23552eb9..0000000000 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; -using osu.Game.Rulesets.Osu.Difficulty.Utils; -using osu.Game.Rulesets.Osu.Objects; - -namespace osu.Game.Rulesets.Osu.Difficulty.Skills -{ - /// - /// Used to processes strain values of s, keep track of strain levels caused by the processed objects - /// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects. - /// - public abstract class Skill - { - protected const double SINGLE_SPACING_THRESHOLD = 125; - protected const double STREAM_SPACING_THRESHOLD = 110; - - /// - /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. - /// - protected abstract double SkillMultiplier { get; } - - /// - /// Determines how quickly strain decays for the given skill. - /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second. - /// - protected abstract double StrainDecayBase { get; } - - /// - /// s that were processed previously. They can affect the strain values of the following objects. - /// - protected readonly History Previous = new History(2); // Contained objects not used yet - - private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap. - private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section. - private readonly List strainPeaks = new List(); - - /// - /// Process an and update current strain values accordingly. - /// - public void Process(OsuDifficultyHitObject current) - { - currentStrain *= strainDecay(current.DeltaTime); - if (!(current.BaseObject is Spinner)) - currentStrain += StrainValueOf(current) * SkillMultiplier; - - currentSectionPeak = Math.Max(currentStrain, currentSectionPeak); - - Previous.Push(current); - } - - /// - /// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty. - /// - public void SaveCurrentPeak() - { - if (Previous.Count > 0) - strainPeaks.Add(currentSectionPeak); - } - - /// - /// Sets the initial strain level for a new section. - /// - /// The beginning of the new section in milliseconds - public void StartNewSectionFrom(double offset) - { - // The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries. - // This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level. - if (Previous.Count > 0) - currentSectionPeak = currentStrain * strainDecay(offset - Previous[0].BaseObject.StartTime); - } - - /// - /// Returns the calculated difficulty value representing all processed s. - /// - public double DifficultyValue() - { - strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - double difficulty = 0; - double weight = 1; - - // Difficulty is the weighted sum of the highest strains from every section. - foreach (double strain in strainPeaks) - { - difficulty += strain * weight; - weight *= 0.9; - } - - return difficulty; - } - - /// - /// Calculates the strain value of an . This value is affected by previously processed objects. - /// - protected abstract double StrainValueOf(OsuDifficultyHitObject current); - - private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000); - } -} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index e78691ce53..46a81a9480 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; +using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -11,6 +14,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// public class Speed : Skill { + private const double single_spacing_threshold = 125; + private const double angle_bonus_begin = 5 * Math.PI / 6; private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; @@ -22,9 +27,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double max_speed_bonus = 45; // ~330BPM private const double speed_balancing_factor = 40; - protected override double StrainValueOf(OsuDifficultyHitObject current) + protected override double StrainValueOf(DifficultyHitObject current) { - double distance = Math.Min(SINGLE_SPACING_THRESHOLD, current.TravelDistance + current.JumpDistance); + if (current.BaseObject is Spinner) + return 0; + + var osuCurrent = (OsuDifficultyHitObject)current; + + double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime); double speedBonus = 1.0; @@ -32,20 +42,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); double angleBonus = 1.0; - if (current.Angle != null && current.Angle.Value < angle_bonus_begin) + if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin) { - angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - current.Angle.Value)), 2) / 3.57; - if (current.Angle.Value < pi_over_2) + angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - osuCurrent.Angle.Value)), 2) / 3.57; + if (osuCurrent.Angle.Value < pi_over_2) { angleBonus = 1.28; - if (distance < 90 && current.Angle.Value < pi_over_4) + if (distance < 90 && osuCurrent.Angle.Value < pi_over_4) angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1); else if (distance < 90) - angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - current.Angle.Value) / pi_over_4); + angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - osuCurrent.Angle.Value) / pi_over_4); } } - return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / SINGLE_SPACING_THRESHOLD, 3.5)) / current.StrainTime; + return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Utils/History.cs b/osu.Game.Rulesets.Osu/Difficulty/Utils/History.cs deleted file mode 100644 index e39351087e..0000000000 --- a/osu.Game.Rulesets.Osu/Difficulty/Utils/History.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Osu.Difficulty.Utils -{ - /// - /// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full. - /// Indexing starts at the top of the stack. - /// - public class History : IEnumerable - { - public int Count { get; private set; } - - private readonly T[] array; - private readonly int capacity; - private int marker; // Marks the position of the most recently added item. - - /// - /// Initializes a new instance of the History class that is empty and has the specified capacity. - /// - /// The number of items the History can hold. - public History(int capacity) - { - if (capacity < 0) - throw new ArgumentOutOfRangeException(); - - this.capacity = capacity; - array = new T[capacity]; - marker = capacity; // Set marker to the end of the array, outside of the indexed range by one. - } - - /// - /// The most recently added item is returned at index 0. - /// - public T this[int i] - { - get - { - if (i < 0 || i > Count - 1) - throw new IndexOutOfRangeException(); - - i += marker; - if (i > capacity - 1) - i -= capacity; - - return array[i]; - } - } - - /// - /// Adds the item as the most recent one in the history. - /// The oldest item is disposed if the history is full. - /// - public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition. - { - if (marker == 0) - marker = capacity - 1; - else - --marker; - - array[marker] = item; - - if (Count < capacity) - ++Count; - } - - /// - /// Returns an enumerator which enumerates items in the history starting from the most recently added one. - /// - public IEnumerator GetEnumerator() - { - for (int i = marker; i < capacity; ++i) - yield return array[i]; - - if (Count == capacity) - for (int i = 0; i < marker; ++i) - yield return array[i]; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index a450d52aa9..e7dbe98919 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Osu public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o }; - public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuLegacyDifficultyCalculator(this, beatmap); + public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new OsuPerformanceCalculator(this, beatmap, score);