Path: blob/master/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs
4571 views
// Copyright (c) ppy Pty Ltd <[email protected]>. 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.Taiko.Difficulty.Preprocessing.Colour.Data; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { /// <summary> /// Utility class to perform various encodings. /// </summary> public static class TaikoColourDifficultyPreprocessor { /// <summary> /// Processes and encodes a list of <see cref="TaikoDifficultyHitObject"/>s into a list of <see cref="TaikoColourData"/>s, /// assigning the appropriate <see cref="TaikoColourData"/>s to each <see cref="TaikoDifficultyHitObject"/>. /// </summary> public static void ProcessAndAssign(List<DifficultyHitObject> hitObjects) { List<RepeatingHitPatterns> hitPatterns = encode(hitObjects); // Assign indexing and encoding data to all relevant objects. foreach (var repeatingHitPattern in hitPatterns) { // The outermost loop is kept a ForEach loop since it doesn't need index information, and we want to // keep i and j for AlternatingMonoPattern's and MonoStreak's index respectively, to keep it in line with // documentation. for (int i = 0; i < repeatingHitPattern.AlternatingMonoPatterns.Count; ++i) { AlternatingMonoPattern monoPattern = repeatingHitPattern.AlternatingMonoPatterns[i]; monoPattern.Parent = repeatingHitPattern; monoPattern.Index = i; for (int j = 0; j < monoPattern.MonoStreaks.Count; ++j) { MonoStreak monoStreak = monoPattern.MonoStreaks[j]; monoStreak.Parent = monoPattern; monoStreak.Index = j; foreach (var hitObject in monoStreak.HitObjects) { hitObject.ColourData.RepeatingHitPattern = repeatingHitPattern; hitObject.ColourData.AlternatingMonoPattern = monoPattern; hitObject.ColourData.MonoStreak = monoStreak; } } } } } /// <summary> /// Encodes a list of <see cref="TaikoDifficultyHitObject"/>s into a list of <see cref="RepeatingHitPatterns"/>s. /// </summary> private static List<RepeatingHitPatterns> encode(List<DifficultyHitObject> data) { List<MonoStreak> monoStreaks = encodeMonoStreak(data); List<AlternatingMonoPattern> alternatingMonoPatterns = encodeAlternatingMonoPattern(monoStreaks); List<RepeatingHitPatterns> repeatingHitPatterns = encodeRepeatingHitPattern(alternatingMonoPatterns); return repeatingHitPatterns; } /// <summary> /// Encodes a list of <see cref="TaikoDifficultyHitObject"/>s into a list of <see cref="MonoStreak"/>s. /// </summary> private static List<MonoStreak> encodeMonoStreak(List<DifficultyHitObject> data) { List<MonoStreak> monoStreaks = new List<MonoStreak>(); MonoStreak? currentMonoStreak = null; for (int i = 0; i < data.Count; i++) { TaikoDifficultyHitObject taikoObject = (TaikoDifficultyHitObject)data[i]; // This ignores all non-note objects, which may or may not be the desired behaviour TaikoDifficultyHitObject? previousObject = taikoObject.PreviousNote(0); // If this is the first object in the list or the colour changed, create a new mono streak if (currentMonoStreak == null || previousObject == null || (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type) { currentMonoStreak = new MonoStreak(); monoStreaks.Add(currentMonoStreak); } // Add the current object to the encoded payload. currentMonoStreak.HitObjects.Add(taikoObject); } return monoStreaks; } /// <summary> /// Encodes a list of <see cref="MonoStreak"/>s into a list of <see cref="AlternatingMonoPattern"/>s. /// </summary> private static List<AlternatingMonoPattern> encodeAlternatingMonoPattern(List<MonoStreak> data) { List<AlternatingMonoPattern> monoPatterns = new List<AlternatingMonoPattern>(); AlternatingMonoPattern? currentMonoPattern = null; for (int i = 0; i < data.Count; i++) { // Start a new AlternatingMonoPattern if the previous MonoStreak has a different mono length, or if this is the first MonoStreak in the list. if (currentMonoPattern == null || data[i].RunLength != data[i - 1].RunLength) { currentMonoPattern = new AlternatingMonoPattern(); monoPatterns.Add(currentMonoPattern); } // Add the current MonoStreak to the encoded payload. currentMonoPattern.MonoStreaks.Add(data[i]); } return monoPatterns; } /// <summary> /// Encodes a list of <see cref="AlternatingMonoPattern"/>s into a list of <see cref="RepeatingHitPatterns"/>s. /// </summary> private static List<RepeatingHitPatterns> encodeRepeatingHitPattern(List<AlternatingMonoPattern> data) { List<RepeatingHitPatterns> hitPatterns = new List<RepeatingHitPatterns>(); RepeatingHitPatterns? currentHitPattern = null; for (int i = 0; i < data.Count; i++) { // Start a new RepeatingHitPattern. AlternatingMonoPatterns that should be grouped together will be handled later within this loop. currentHitPattern = new RepeatingHitPatterns(currentHitPattern); // Determine if future AlternatingMonoPatterns should be grouped. bool isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); if (!isCoupled) { // If not, add the current AlternatingMonoPattern to the encoded payload and continue. currentHitPattern.AlternatingMonoPatterns.Add(data[i]); } else { // If so, add the current AlternatingMonoPattern to the encoded payload and start repeatedly checking if the // subsequent AlternatingMonoPatterns should be grouped by increasing i and doing the appropriate isCoupled check. while (isCoupled) { currentHitPattern.AlternatingMonoPatterns.Add(data[i]); i++; isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); } // Skip over viewed data and add the rest to the payload currentHitPattern.AlternatingMonoPatterns.Add(data[i]); currentHitPattern.AlternatingMonoPatterns.Add(data[i + 1]); i++; } hitPatterns.Add(currentHitPattern); } // Final pass to find repetition intervals for (int i = 0; i < hitPatterns.Count; i++) { hitPatterns[i].FindRepetitionInterval(); } return hitPatterns; } } }