// 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.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 { /// /// Processes and encodes a list of s into a list of s, /// assigning the appropriate s to each , /// and pre-evaluating colour difficulty of each . /// public static List ProcessAndAssign(List hitObjects) { List colours = new List(); List encodings = Encode(hitObjects); // Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is // assigned with the relevant encodings. encodings.ForEach(coupledEncoding => { coupledEncoding.Payload[0].Payload[0].EncodedData[0].Colour.CoupledColourEncoding = coupledEncoding; // The outermost loop is kept a ForEach loop since it doesn't need index information, and we want to // keep i and j for ColourEncoding's and MonoEncoding's index respectively, to keep it in line with // documentation. // // While parent and index should be part of the encoding process, they are assigned here instead due to // this being a simple one location to assign them. for (int i = 0; i < coupledEncoding.Payload.Count; ++i) { ColourEncoding colourEncoding = coupledEncoding.Payload[i]; colourEncoding.Parent = coupledEncoding; colourEncoding.Index = i; colourEncoding.Payload[0].EncodedData[0].Colour.ColourEncoding = colourEncoding; for (int j = 0; j < colourEncoding.Payload.Count; ++j) { MonoEncoding monoEncoding = colourEncoding.Payload[j]; monoEncoding.Parent = colourEncoding; monoEncoding.Index = j; monoEncoding.EncodedData[0].Colour.MonoEncoding = monoEncoding; } } }); 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 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 viewed 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; } /// /// 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; } } }