diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 0054047573..3826e4cec4 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -49,6 +49,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty { CatchHitObject lastObject = null; + List objects = new List(); + int index = 0; + // In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream. foreach (var hitObject in beatmap.HitObjects .SelectMany(obj => obj is JuiceStream stream ? stream.NestedHitObjects.AsEnumerable() : new[] { obj }) @@ -60,10 +63,15 @@ namespace osu.Game.Rulesets.Catch.Difficulty continue; if (lastObject != null) - yield return new CatchDifficultyHitObject(hitObject, lastObject, clockRate, halfCatcherWidth); + { + objects.Add(new CatchDifficultyHitObject(hitObject, lastObject, clockRate, halfCatcherWidth, objects, index)); + index++; + } lastObject = hitObject; } + + return objects; } protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index b22ec93b43..8c229cf6c5 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; @@ -24,8 +25,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// public readonly double StrainTime; - public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) - : base(hitObject, lastObject, clockRate) + public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth, List objects, int position) + : base(hitObject, lastObject, clockRate, objects, position) { // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. float scalingFactor = normalized_hitobject_radius / halfCatcherWidth; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index b17aa7fc4d..7ce73c98d1 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -62,8 +62,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty LegacySortHelper.Sort(sortedObjects, Comparer.Create((a, b) => (int)Math.Round(a.StartTime) - (int)Math.Round(b.StartTime))); + List objects = new List(); + for (int i = 1; i < sortedObjects.Length; i++) - yield return new ManiaDifficultyHitObject(sortedObjects[i], sortedObjects[i - 1], clockRate); + objects.Add(new ManiaDifficultyHitObject(sortedObjects[i], sortedObjects[i - 1], clockRate, objects, i - 1)); + + return objects; } // Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required. diff --git a/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs b/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs index 29ba934e9f..5c9b3b9b54 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs @@ -1,6 +1,7 @@ // 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.Generic; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; @@ -11,8 +12,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Preprocessing { public new ManiaHitObject BaseObject => (ManiaHitObject)base.BaseObject; - public ManiaDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate) - : base(hitObject, lastObject, clockRate) + public ManiaDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, List objects, int position) + : base(hitObject, lastObject, clockRate, objects, position) { } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 31550a8105..626f1bebbd 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -84,9 +84,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills return individualStrain + overallStrain - CurrentStrain; } - protected override double CalculateInitialStrain(double offset) - => applyDecay(individualStrain, offset - Previous[0].StartTime, individual_decay_base) - + applyDecay(overallStrain, offset - Previous[0].StartTime, overall_decay_base); + protected override double CalculateInitialStrain(double offset, DifficultyHitObject current) + => applyDecay(individualStrain, offset - current.Previous(0).StartTime, individual_decay_base) + + applyDecay(overallStrain, offset - current.Previous(0).StartTime, overall_decay_base); private double applyDecay(double value, double deltaTime, double decayBase) => value * Math.Pow(decayBase, deltaTime / 1000); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 4eb5c79808..3b21ab49c2 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -87,16 +87,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { + List objects = new List(); + // 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]; + objects.Add(new OsuDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate, objects, i - 1)); - yield return new OsuDifficultyHitObject(current, lastLast, last, clockRate); - } + return objects; } protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index cf4802d282..0598cbe4c9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Mods; @@ -12,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing { public class OsuDifficultyHitObject : DifficultyHitObject { - private const int normalised_radius = 50; // Change radius to 50 to make 100 the diameter. Easier for mental maths. + private const int normalised_radius = 50; private const int min_delta_time = 25; private const float maximum_slider_radius = normalised_radius * 2.4f; private const float assumed_slider_radius = normalised_radius * 1.8f; @@ -74,10 +75,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing private readonly OsuHitObject lastLastObject; private readonly OsuHitObject lastObject; - public OsuDifficultyHitObject(HitObject hitObject, HitObject lastLastObject, HitObject lastObject, double clockRate) - : base(hitObject, lastObject, clockRate) + public OsuDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, List objects, int position) + : base(hitObject, lastObject, clockRate, objects, position) { - this.lastLastObject = (OsuHitObject)lastLastObject; + lastLastObject = position > 1 ? (OsuHitObject)Previous(1).BaseObject : null; this.lastObject = (OsuHitObject)lastObject; // Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects. diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index a6301aed6d..e3ec997740 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -22,8 +22,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private readonly bool withSliders; - protected override int HistoryLength => 2; - private const double wide_angle_multiplier = 1.5; private const double acute_angle_multiplier = 2.0; private const double slider_multiplier = 1.5; @@ -36,12 +34,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double strainValueOf(DifficultyHitObject current) { - if (current.BaseObject is Spinner || Previous.Count <= 1 || Previous[0].BaseObject is Spinner) + if (current.BaseObject is Spinner || current.Position <= 1 || current.Previous(0).BaseObject is Spinner) return 0; var osuCurrObj = (OsuDifficultyHitObject)current; - var osuLastObj = (OsuDifficultyHitObject)Previous[0]; - var osuLastLastObj = (OsuDifficultyHitObject)Previous[1]; + var osuLastObj = (OsuDifficultyHitObject)current.Previous(0); + var osuLastLastObj = (OsuDifficultyHitObject)current.Previous(1); // Calculate the velocity to the current hitobject, which starts with a base distance / time assuming the last object is a hitcircle. double currVelocity = osuCurrObj.LazyJumpDistance / osuCurrObj.StrainTime; @@ -152,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); - protected override double CalculateInitialStrain(double time) => currentStrain * strainDecay(time - Previous[0].StartTime); + protected override double CalculateInitialStrain(double time, DifficultyHitObject current) => currentStrain * strainDecay(time - current.Previous(0).StartTime); protected override double StrainValueAt(DifficultyHitObject current) { diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index d93007fae5..75796a5993 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -25,7 +25,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double skillMultiplier => 0.05; private double strainDecayBase => 0.15; protected override double DecayWeight => 1.0; - protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. private readonly bool hidden; @@ -51,9 +50,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills OsuDifficultyHitObject lastObj = osuCurrent; // This is iterating backwards in time from the current object. - for (int i = 0; i < Previous.Count; i++) + for (int i = 0; i < Math.Min(current.Position, 10); i++) { - var currentObj = (OsuDifficultyHitObject)Previous[i]; + var currentObj = (OsuDifficultyHitObject)current.Previous(i); var currentHitObject = (OsuHitObject)(currentObj.BaseObject); if (!(currentObj.BaseObject is Spinner)) @@ -89,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); - protected override double CalculateInitialStrain(double time) => currentStrain * strainDecay(time - Previous[0].StartTime); + protected override double CalculateInitialStrain(double time, DifficultyHitObject current) => currentStrain * strainDecay(time - current.Previous(0).StartTime); protected override double StrainValueAt(DifficultyHitObject current) { diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 06d1ef7346..f8905664f4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -29,8 +29,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override int ReducedSectionCount => 5; protected override double DifficultyMultiplier => 1.04; - protected override int HistoryLength => 32; - private readonly double greatWindow; public Speed(Mod[] mods, double hitWindowGreat) @@ -55,20 +53,22 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills bool firstDeltaSwitch = false; + int historicalNoteCount = Math.Min(current.Position, 32); + int rhythmStart = 0; - while (rhythmStart < Previous.Count - 2 && current.StartTime - Previous[rhythmStart].StartTime < history_time_max) + while (rhythmStart < historicalNoteCount - 2 && current.StartTime - current.Previous(rhythmStart).StartTime < history_time_max) rhythmStart++; for (int i = rhythmStart; i > 0; i--) { - OsuDifficultyHitObject currObj = (OsuDifficultyHitObject)Previous[i - 1]; - OsuDifficultyHitObject prevObj = (OsuDifficultyHitObject)Previous[i]; - OsuDifficultyHitObject lastObj = (OsuDifficultyHitObject)Previous[i + 1]; + OsuDifficultyHitObject currObj = (OsuDifficultyHitObject)current.Previous(i - 1); + OsuDifficultyHitObject prevObj = (OsuDifficultyHitObject)current.Previous(i); + OsuDifficultyHitObject lastObj = (OsuDifficultyHitObject)current.Previous(i + 1); double currHistoricalDecay = (history_time_max - (current.StartTime - currObj.StartTime)) / history_time_max; // scales note 0 to 1 from history to now - currHistoricalDecay = Math.Min((double)(Previous.Count - i) / Previous.Count, currHistoricalDecay); // either we're limited by time or limited by object count. + currHistoricalDecay = Math.Min((double)(historicalNoteCount - i) / historicalNoteCount, currHistoricalDecay); // either we're limited by time or limited by object count. double currDelta = currObj.StrainTime; double prevDelta = prevObj.StrainTime; @@ -90,10 +90,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } else { - if (Previous[i - 1].BaseObject is Slider) // bpm change is into slider, this is easy acc window + if (current.Previous(i - 1).BaseObject is Slider) // bpm change is into slider, this is easy acc window effectiveRatio *= 0.125; - if (Previous[i].BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle + if (current.Previous(i).BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle effectiveRatio *= 0.25; if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // derive strainTime for calculation var osuCurrObj = (OsuDifficultyHitObject)current; - var osuPrevObj = Previous.Count > 0 ? (OsuDifficultyHitObject)Previous[0] : null; + var osuPrevObj = current.Position > 0 ? (OsuDifficultyHitObject)current.Previous(0) : null; double strainTime = osuCurrObj.StrainTime; double greatWindowFull = greatWindow * 2; @@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); - protected override double CalculateInitialStrain(double time) => (currentStrain * currentRhythm) * strainDecay(time - Previous[0].StartTime); + protected override double CalculateInitialStrain(double time, DifficultyHitObject current) => (currentStrain * currentRhythm) * strainDecay(time - current.Previous(0).StartTime); protected override double StrainValueAt(DifficultyHitObject current) { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs index 3b1a9ad777..586a7fb6a7 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Utils; using osu.Game.Rulesets.Taiko.Objects; @@ -34,9 +35,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// /// The list of all s in the map. /// - private readonly List hitObjects; + private readonly List hitObjects; - public StaminaCheeseDetector(List hitObjects) + public StaminaCheeseDetector(List hitObjects) { this.hitObjects = hitObjects; } @@ -62,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// The length of a single repeating pattern to consider (triplets/quadruplets). private void findRolls(int patternLength) { - var history = new LimitedCapacityQueue(2 * patternLength); + var history = new LimitedCapacityQueue(2 * patternLength); // for convenience, we're tracking the index of the item *before* our suspected repeat's start, // as that index can be simply subtracted from the current index to get the number of elements in between @@ -97,11 +98,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// /// Determines whether the objects stored in contain a repetition of a pattern of length . /// - private static bool containsPatternRepeat(LimitedCapacityQueue history, int patternLength) + private static bool containsPatternRepeat(LimitedCapacityQueue history, int patternLength) { for (int j = 0; j < patternLength; j++) { - if (history[j].HitType != history[j + patternLength].HitType) + if (((TaikoDifficultyHitObject)history[j]).HitType != ((TaikoDifficultyHitObject)history[j + patternLength]).HitType) return false; } @@ -120,7 +121,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing for (int i = parity; i < hitObjects.Count; i += 2) { - if (hitObjects[i].HitType == type) + if (((TaikoDifficultyHitObject)hitObjects[i]).HitType == type) tlLength += 2; else tlLength = -2; @@ -139,7 +140,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing private void markObjectsAsCheese(int start, int end) { for (int i = start; i <= end; i++) - hitObjects[i].StaminaCheese = true; + ((TaikoDifficultyHitObject)hitObjects[i]).StaminaCheese = true; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index ae33c184d0..79e760d149 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; @@ -24,11 +25,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public readonly HitType? HitType; - /// - /// The index of the object in the beatmap. - /// - public readonly int ObjectIndex; - /// /// Whether the object should carry a penalty due to being hittable using special techniques /// making it easier to do so. @@ -42,16 +38,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// The gameplay preceding . /// The gameplay preceding . /// The rate of the gameplay clock. Modified by speed-changing mods. - /// The index of the object in the beatmap. - public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, int objectIndex) - : base(hitObject, lastObject, clockRate) + /// The list of s in the current beatmap. + /// The index of the object in the beatmap. + public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, List objects, int position) + : base(hitObject, lastObject, clockRate, objects, position) { var currentHit = hitObject as Hit; Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate); HitType = currentHit?.Type; - - ObjectIndex = objectIndex; } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs index 973e55f4b4..9150b6980b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills if (!samePattern(start, mostRecentPatternsToCompare)) continue; - int notesSince = hitObject.ObjectIndex - rhythmHistory[start].ObjectIndex; + int notesSince = hitObject.Position - rhythmHistory[start].Position; penalty *= repetitionPenalty(notesSince); break; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 54cf233d69..6922f57060 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -66,11 +66,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current; - if (hitObject.ObjectIndex % 2 == hand) + if (hitObject.Position % 2 == hand) { double objectStrain = 1; - if (hitObject.ObjectIndex == 1) + if (hitObject.Position == 1) return 1; notePairDurationHistory.Enqueue(hitObject.DeltaTime + offhandObjectDuration); diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index a9d512f076..f14765a925 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -47,13 +47,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { - List taikoDifficultyHitObjects = new List(); + List taikoDifficultyHitObjects = new List(); for (int i = 2; i < beatmap.HitObjects.Count; i++) { taikoDifficultyHitObjects.Add( new TaikoDifficultyHitObject( - beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, i + beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, taikoDifficultyHitObjects, i - 2 ) ); } diff --git a/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs b/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs index 5edfb2207b..4f001a520c 100644 --- a/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs +++ b/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs @@ -1,6 +1,7 @@ // 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.Generic; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Difficulty.Preprocessing @@ -10,6 +11,13 @@ namespace osu.Game.Rulesets.Difficulty.Preprocessing /// public class DifficultyHitObject { + private readonly List difficultyHitObjects; + + /// + /// The position of this in the list. + /// + public int Position; + /// /// The this wraps. /// @@ -41,13 +49,21 @@ namespace osu.Game.Rulesets.Difficulty.Preprocessing /// The which this wraps. /// The last which occurs before in the beatmap. /// The rate at which the gameplay clock is run at. - public DifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate) + /// The list of s in the current beatmap. + /// The position of this in the list. + public DifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, List objects, int position) { + difficultyHitObjects = objects; + Position = position; BaseObject = hitObject; LastObject = lastObject; DeltaTime = (hitObject.StartTime - lastObject.StartTime) / clockRate; StartTime = hitObject.StartTime / clockRate; EndTime = hitObject.GetEndTime() / clockRate; } + + public DifficultyHitObject Previous(int backwardsIndex) => difficultyHitObjects[Position - (backwardsIndex + 1)]; + + public DifficultyHitObject Next(int forwardsIndex) => difficultyHitObjects[Position + (forwardsIndex + 1)]; } } diff --git a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs index b5c94ad806..f23f3b5f2a 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Difficulty.Utils; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Difficulty.Skills @@ -12,21 +11,10 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// A bare minimal abstract skill for fully custom skill implementations. /// /// - /// This class should be considered a "processing" class and not persisted, as it keeps references to - /// gameplay objects after processing is run (see ). + /// This class should be considered a "processing" class and not persisted. /// public abstract class Skill { - /// - /// s that were processed previously. They can affect the strain values of the following objects. - /// - protected readonly ReverseQueue Previous; - - /// - /// Number of previous s to keep inside the queue. - /// - protected virtual int HistoryLength => 1; - /// /// Mods for use in skill calculations. /// @@ -37,17 +25,11 @@ namespace osu.Game.Rulesets.Difficulty.Skills protected Skill(Mod[] mods) { this.mods = mods; - Previous = new ReverseQueue(HistoryLength + 1); } internal void ProcessInternal(DifficultyHitObject current) { - while (Previous.Count > HistoryLength) - Previous.Dequeue(); - Process(current); - - Previous.Enqueue(current); } /// diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs index d8babf2f32..3f3d57938b 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills { } - protected override double CalculateInitialStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].StartTime); + protected override double CalculateInitialStrain(double time, DifficultyHitObject current) => CurrentStrain * strainDecay(time - current.Previous(0).StartTime); protected override double StrainValueAt(DifficultyHitObject current) { diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index 97266562e4..eb3203e9ec 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -47,13 +47,13 @@ namespace osu.Game.Rulesets.Difficulty.Skills protected sealed override void Process(DifficultyHitObject current) { // The first object doesn't generate a strain, so we begin with an incremented section end - if (Previous.Count == 0) + if (current.Position == 0) currentSectionEnd = Math.Ceiling(current.StartTime / SectionLength) * SectionLength; while (current.StartTime > currentSectionEnd) { saveCurrentPeak(); - startNewSectionFrom(currentSectionEnd); + startNewSectionFrom(currentSectionEnd, current); currentSectionEnd += SectionLength; } @@ -72,19 +72,21 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// Sets the initial strain level for a new section. /// /// The beginning of the new section in milliseconds. - private void startNewSectionFrom(double time) + /// The current hit object. + private void startNewSectionFrom(double time, DifficultyHitObject current) { // The maximum strain of the new section is not zero by default // This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level. - currentSectionPeak = CalculateInitialStrain(time); + currentSectionPeak = CalculateInitialStrain(time, current); } /// /// Retrieves the peak strain at a point in time. /// /// The time to retrieve the peak strain at. + /// The current hit object. /// The peak strain. - protected abstract double CalculateInitialStrain(double time); + protected abstract double CalculateInitialStrain(double time, DifficultyHitObject current); /// /// Returns a live enumerable of the peak strains for each section of the beatmap,