// 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.Taiko.Difficulty.Evaluators; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { /// /// Utility class to perform various encodings. This is separated out from the encoding classes to prevent circular /// dependencies. /// public class TaikoColourDifficultyPreprocessor { /// /// Process and encode a list of s into a list of s, /// assign the appropriate s to each , /// and preevaluate colour difficulty of each . /// public static List ProcessAndAssign(List hitObjects) { List colours = new List(); List encodings = Encode(hitObjects); // Assign colour to objects encodings.ForEach(coupledEncoding => { coupledEncoding.Payload.ForEach(encoding => { encoding.Payload.ForEach(mono => { mono.EncodedData.ForEach(hitObject => { hitObject.Colour = new TaikoDifficultyHitObjectColour(coupledEncoding); }); }); }); // Preevaluate and assign difficulty values ColourEvaluator.PreEvaluateDifficulties(coupledEncoding); }); return colours; } /// /// Encodes a list of s into a list of s. /// public static List EncodeMono(List data) { List encoded = new List(); MonoEncoding? lastEncoded = 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 the colour changed, or if this is the first object in the run, create a new mono encoding if ( previousObject == null || // First object in the list (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type ) { lastEncoded = new MonoEncoding(); lastEncoded.EncodedData.Add(taikoObject); encoded.Add(lastEncoded); continue; } // If we're here, we're in the same encoding as the previous object, thus lastEncoded is not null. Add // the current object to the encoded payload. lastEncoded!.EncodedData.Add(taikoObject); } return encoded; } /// /// Encodes a list of s into a list of s. /// public static List EncodeColour(List data) { List encoded = new List(); ColourEncoding? lastEncoded = null; for (int i = 0; i < data.Count; i++) { // Starts a new ColourEncoding if the previous MonoEncoding has a different mono length, or if this is // the first MonoEncoding in the list. if (lastEncoded == null || data[i].RunLength != data[i - 1].RunLength) { lastEncoded = new ColourEncoding(); lastEncoded.Payload.Add(data[i]); encoded.Add(lastEncoded); continue; } // If we're here, we're in the same encoding as the previous object. Add the current MonoEncoding to the // encoded payload. lastEncoded.Payload.Add(data[i]); } return encoded; } /// /// Encodes a list of s into a list of s. /// public static List Encode(List data) { List firstPass = EncodeMono(data); List secondPass = EncodeColour(firstPass); List thirdPass = EncodeCoupledColour(secondPass); return thirdPass; } /// /// Encodes a list of s into a list of s. /// public static List EncodeCoupledColour(List data) { List encoded = new List(); CoupledColourEncoding? lastEncoded = null; for (int i = 0; i < data.Count; i++) { // Starts a new CoupledColourEncoding. ColourEncodings that should be grouped together will be handled // later within this loop. lastEncoded = new CoupledColourEncoding { Previous = lastEncoded }; // Determine if future ColourEncodings should be grouped. bool isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); if (!isCoupled) { // If not, add the current ColourEncoding to the encoded payload and continue. lastEncoded.Payload.Add(data[i]); } else { // If so, add the current ColourEncoding to the encoded payload and start repeatedly checking if the // subsequent ColourEncodings should be grouped by increasing i and doing the appropriate isCoupled check. while (isCoupled) { lastEncoded.Payload.Add(data[i]); i++; isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); } // Skip over peeked data and add the rest to the payload lastEncoded.Payload.Add(data[i]); lastEncoded.Payload.Add(data[i + 1]); i++; } encoded.Add(lastEncoded); } // Final pass to find repetition intervals for (int i = 0; i < encoded.Count; i++) { encoded[i].FindRepetitionInterval(); } return encoded; } } }