From 6ab7a9141553fd7505d5672a0ecabc233e5bfdfc Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 19 May 2017 20:57:20 +0900 Subject: [PATCH 01/58] Add generator to generate patterns from HitObjects. --- .../Beatmaps/ManiaBeatmapConverter.cs | 50 ++- .../Legacy/HitObjectPatternGenerator.cs | 425 ++++++++++++++++++ .../osu.Game.Rulesets.Mania.csproj | 1 + 3 files changed, 469 insertions(+), 7 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 933fe0787c..e82b0dd516 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -11,11 +11,19 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Beatmaps.Patterns; using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Database; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; +using OpenTK; namespace osu.Game.Rulesets.Mania.Beatmaps { public class ManiaBeatmapConverter : BeatmapConverter { + /// + /// Maximum number of previous notes to consider for density calculation. + /// + private const int max_notes_for_density = 7; + protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; private Pattern lastPattern = new Pattern(); @@ -55,6 +63,26 @@ namespace osu.Game.Rulesets.Mania.Beatmaps yield return obj; } + private List prevNoteTimes = new List(max_notes_for_density); + private double density = int.MaxValue; + private void computeDensity(double newNoteTime) + { + if (prevNoteTimes.Count == max_notes_for_density) + prevNoteTimes.RemoveAt(0); + prevNoteTimes.Add(newNoteTime); + + density = (prevNoteTimes[prevNoteTimes.Count - 1] - prevNoteTimes[0]) / prevNoteTimes.Count; + } + + private double lastTime; + private Vector2 lastPosition; + private PatternType lastStair; + private void recordNote(double time, Vector2 position) + { + lastTime = time; + lastPosition = position; + } + /// /// Method that generates hit objects for osu!mania specific beatmaps. /// @@ -83,7 +111,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // Following lines currently commented out to appease resharper - //Patterns.PatternGenerator conversion = null; + Patterns.PatternGenerator conversion = null; if (distanceData != null) { @@ -95,16 +123,24 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } else if (positionData != null) { - // Circle + computeDensity(original.StartTime); + + conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair); + + recordNote(original.StartTime, positionData.Position); } - //if (conversion == null) - return null; + if (conversion == null) + return null; - //Pattern newPattern = conversion.Generate(); - //lastPattern = newPattern; + Pattern newPattern = conversion.Generate(); + lastPattern = newPattern; - //return newPattern.HitObjects; + var stairPatternGenerator = conversion as HitObjectPatternGenerator; + if (stairPatternGenerator != null) + lastStair = stairPatternGenerator.StairType; + + return newPattern.HitObjects; } /// diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs new file mode 100644 index 0000000000..1591e0531c --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -0,0 +1,425 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using OpenTK; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy +{ + internal class HitObjectPatternGenerator : PatternGenerator + { + public PatternType StairType { get; private set; } + + private readonly PatternType convertType; + + public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair) + : base(random, hitObject, beatmap, previousPattern) + { + StairType = lastStair; + + ControlPoint overridePoint; + ControlPoint controlPoint = beatmap.TimingInfo.TimingPointAt(hitObject.StartTime, out overridePoint); + + var positionData = hitObject as IHasPosition; + + float positionSeparation = ((positionData?.Position ?? Vector2.Zero) - previousPosition).Length; + double timeSeparation = hitObject.StartTime - previousTime; + + double beatLength = controlPoint.BeatLength; + bool kiai = (overridePoint ?? controlPoint).KiaiMode; + + if (timeSeparation <= 125) + { + // More than 120 BPM + convertType |= PatternType.ForceNotStack; + } + + if (timeSeparation <= 80) + { + // More than 187 BPM + convertType |= PatternType.ForceNotStack | PatternType.KeepSingle; + } + else if (timeSeparation <= 95) + { + // More than 157 BPM + convertType |= PatternType.ForceNotStack | PatternType.KeepSingle | lastStair; + } + else if (timeSeparation <= 105) + { + // More than 140 BPM + convertType |= PatternType.ForceNotStack | PatternType.LowProbability; + } + else if (timeSeparation <= 125) + { + // More than 120 BPM + convertType |= PatternType.ForceNotStack; + } + else if (timeSeparation <= 135 && positionSeparation < 20) + { + // More than 111 BPM stream + convertType |= PatternType.Cycle | PatternType.KeepSingle; + } + else if (timeSeparation <= 150 & positionSeparation < 20) + { + // More than 100 BPM stream + convertType |= PatternType.ForceStack | PatternType.LowProbability; + } + else if (positionSeparation < 20 && density >= beatLength / 2.5) + { + // Low density stream + convertType |= PatternType.Reverse | PatternType.LowProbability; + } + else if (density < beatLength / 2.5 || kiai) + { + // High density + } + else + convertType |= PatternType.LowProbability; + } + + public override Pattern Generate() + { + int lastColumn = PreviousPattern.HitObjects.First().Column; + + if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Count() > 0) + { + // Generate a new pattern by copying the last hit objects in reverse-column order + var pattern = new Pattern(); + + int siblings = PreviousPattern.HitObjects.Count(h => h.Column >= RandomStart); + + for (int i = RandomStart; i < AvailableColumns; i++) + if (PreviousPattern.IsFilled(i)) + addToPattern(pattern, RandomStart + AvailableColumns - i - 1, siblings); + + return pattern; + } + + if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1 + // If we convert to 7K + 1, let's not overload the special key + && (AvailableColumns != 8 || lastColumn != 0) + // Make sure the last column was not the centre column + && (AvailableColumns % 2 == 0 || lastColumn != AvailableColumns / 2)) + { + // Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object) + var pattern = new Pattern(); + + int column = RandomStart + AvailableColumns - lastColumn - 1; + addToPattern(pattern, column); + + return pattern; + } + + if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Count() > 0) + { + // Generate a new pattern by placing on the already filled columns + var pattern = new Pattern(); + + int siblings = PreviousPattern.HitObjects.Count(h => h.Column >= RandomStart); + + for (int i = RandomStart; i < AvailableColumns; i++) + if (PreviousPattern.IsFilled(i)) + addToPattern(pattern, i, siblings); + + return pattern; + } + + if ((convertType & PatternType.Stair) > 0 && PreviousPattern.HitObjects.Count() == 1) + { + // Generate a new pattern by placing on the next column, cycling back to the start if there is no "next" + var pattern = new Pattern(); + + int targetColumn = lastColumn + 1; + if (targetColumn == AvailableColumns) + { + targetColumn = RandomStart; + StairType = PatternType.ReverseStair; + } + + addToPattern(pattern, targetColumn, 1); + return pattern; + } + + if ((convertType & PatternType.ReverseStair) > 0 && PreviousPattern.HitObjects.Count() == 1) + { + // Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous" + var pattern = new Pattern(); + + int targetColumn = lastColumn - 1; + if (targetColumn == RandomStart - 1) + { + targetColumn = AvailableColumns - 1; + StairType = PatternType.Stair; + } + + addToPattern(pattern, targetColumn, 1); + return pattern; + } + + if ((convertType & PatternType.KeepSingle) > 0) + return generateRandomNotes(1); + + if ((convertType & PatternType.Mirror) > 0) + { + if (ConversionDifficulty > 6.5) + return generateRandomPatternWithMirrored(0.12, 0.38, 0.12); + if (ConversionDifficulty > 4) + return generateRandomPatternWithMirrored(0.12, 0.17, 0); + return generateRandomPatternWithMirrored(0.12, 0, 0); + } + + if (ConversionDifficulty > 6.5) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateRandomPattern(0.78, 0.42, 0, 0); + return generateRandomPattern(1, 0.62, 0, 0); + } + + if (ConversionDifficulty > 4) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateRandomPattern(0.35, 0.08, 0, 0); + return generateRandomPattern(0.52, 0.15, 0, 0); + } + + if (ConversionDifficulty > 2) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateRandomPattern(0.18, 0, 0, 0); + return generateRandomPattern(0.45, 0, 0, 0); + } + + return generateRandomPattern(0, 0, 0, 0); + } + + /// + /// Generates random notes. + /// + /// This will generate as many as it can up to , accounting for + /// any stacks if is forcing no stacks. + /// + /// + /// The amount of notes to generate. + /// Custom siblings count if is not the number of siblings in this pattern. + /// The containing the hit objects. + private Pattern generateRandomNotes(int noteCount, int siblingsOverride = -1) + { + var pattern = new Pattern(); + + bool allowStacking = (convertType & PatternType.ForceNotStack) == 0; + + if (!allowStacking) + noteCount = Math.Min(noteCount, AvailableColumns - RandomStart - PreviousPattern.ColumnsFilled); + + int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + for (int i = 0; i < noteCount; i++) + { + while (pattern.IsFilled(nextColumn) || (PreviousPattern.IsFilled(nextColumn) && !allowStacking)) + { + if ((convertType & PatternType.Gathered) > 0) + { + nextColumn++; + if (nextColumn == AvailableColumns) + nextColumn = RandomStart; + } + else + nextColumn = Random.Next(RandomStart, AvailableColumns); + } + + addToPattern(pattern, nextColumn, siblingsOverride != -1 ? siblingsOverride : noteCount); + } + + return pattern; + } + + /// + /// Whether this hit object can generate a note in the special column. + /// + private bool hasSpecialColumn() => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH); + + /// + /// Generates a random pattern. + /// + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// Probability for 4 notes to be generated. + /// Probability for 5 notes to be generated. + /// The containing the hit objects. + private Pattern generateRandomPattern(double p2, double p3, double p4, double p5) + { + var pattern = new Pattern(); + + int noteCount = getRandomNoteCount(p2, p3, p4, p5); + int siblings = noteCount; + + if (RandomStart > 0 && hasSpecialColumn(HitObject.Samples)) + { + siblings++; + addToPattern(pattern, 0, siblings); + } + + pattern.Add(generateRandomNotes(noteCount, siblings)); + + return pattern; + } + + /// + /// Generates a random pattern which has both normal and mirrored notes. + /// + /// The probability for a note to be added to the centre column. + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// The containing the hit objects. + private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3) + { + var pattern = new Pattern(); + + bool addToCentre = false; + int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre); + int siblings = noteCount; + + if (addToCentre) + siblings++; + if (RandomStart > 0 && hasSpecialColumn(HitObject.Samples)) + siblings++; + + int columnLimit = (AvailableColumns % 2 == 0 ? AvailableColumns : AvailableColumns - 1) / 2; + int nextColumn = Random.Next(RandomStart, columnLimit); + for (int i = 0; i < noteCount; i++) + { + while (pattern.IsFilled(nextColumn)) + nextColumn = Random.Next(RandomStart, columnLimit); + // Add normal note + addToPattern(pattern, nextColumn, siblings); + // Add mirrored note + addToPattern(pattern, RandomStart + AvailableColumns - nextColumn - 1); + } + + if (addToCentre) + addToPattern(pattern, AvailableColumns / 2, siblings); + + if (RandomStart > 0 && hasSpecialColumn(HitObject.Samples)) + addToPattern(pattern, 0, siblings); + + return pattern; + } + + /// + /// Generates a count of notes to be generated from a list of probabilities. + /// + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// Probability for 4 notes to be generated. + /// Probability for 5 notes to be generated. + /// The amount of notes to be generated. + private int getRandomNoteCount(double p2, double p3, double p4, double p5) + { + switch (AvailableColumns) + { + case 2: + p2 = 0; + p3 = 0; + p4 = 0; + p5 = 0; + break; + case 3: + p2 = Math.Max(p2, 0.1); + p3 = 0; + p4 = 0; + p5 = 0; + break; + case 4: + p2 = Math.Max(p2, 0.23); + p3 = Math.Max(p3, 0.04); + p4 = 0; + p5 = 0; + break; + case 5: + p3 = Math.Max(p3, 0.15); + p4 = Math.Max(p4, 0.03); + p5 = 0; + break; + } + + if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)) + p2 = 1; + + return GetRandomNoteCount(p2, p3, p4, p5); + } + + /// + /// Generates a count of notes to be generated from a list of probabilities. + /// + /// The probability for a note to be added to the centre column. + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// Whether to add a note to the centre column. + /// The amount of notes to be generated. The note to be added to the centre column will NOT be part of this count. + private int getRandomNoteCountMirrored(double centreProbability, double p2, double p3, out bool addToCentre) + { + addToCentre = false; + + if ((convertType & PatternType.ForceNotStack) > 0) + return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3); + + switch (AvailableColumns) + { + case 2: + centreProbability = 0; + p2 = 0; + p3 = 0; + break; + case 3: + centreProbability = Math.Max(centreProbability, 0.03); + p2 = Math.Max(p2, 0.1); + p3 = 0; + break; + case 4: + centreProbability = 0; + p2 = Math.Max(p2 * 2, 0.2); + p3 = 0; + break; + case 5: + centreProbability = Math.Max(centreProbability, 0.03); + p3 = 0; + break; + case 6: + centreProbability = 0; + p2 = Math.Max(p2 * 2, 0.5); + p3 = Math.Max(p3 * 2, 0.15); + break; + } + + double centreVal = Random.NextDouble(); + int noteCount = GetRandomNoteCount(p2, p3); + + addToCentre = AvailableColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability; + return noteCount; + } + + /// + /// Constructs and adds a note to a pattern. + /// + /// The pattern to add to. + /// The column to add the note to. + /// The number of children alongside this note (these will not be generated, but are used for volume calculations). + private void addToPattern(Pattern pattern, int column, int siblings = 1) + { + pattern.Add(new Note + { + StartTime = HitObject.StartTime, + Samples = HitObject.Samples, + Column = column, + Siblings = siblings + }); + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index ec426c895f..e2909504ec 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -49,6 +49,7 @@ + From f8270f31a9f6e8a1236c40132b810ff46a55239f Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 19 May 2017 21:15:29 +0900 Subject: [PATCH 02/58] Fix build errors. --- .../Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 1591e0531c..5d3b84f57b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -243,7 +243,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// Whether this hit object can generate a note in the special column. /// - private bool hasSpecialColumn() => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH); + private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH); /// /// Generates a random pattern. @@ -260,7 +260,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int noteCount = getRandomNoteCount(p2, p3, p4, p5); int siblings = noteCount; - if (RandomStart > 0 && hasSpecialColumn(HitObject.Samples)) + if (RandomStart > 0 && hasSpecialColumn) { siblings++; addToPattern(pattern, 0, siblings); @@ -288,7 +288,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (addToCentre) siblings++; - if (RandomStart > 0 && hasSpecialColumn(HitObject.Samples)) + if (RandomStart > 0 && hasSpecialColumn) siblings++; int columnLimit = (AvailableColumns % 2 == 0 ? AvailableColumns : AvailableColumns - 1) / 2; @@ -306,7 +306,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (addToCentre) addToPattern(pattern, AvailableColumns / 2, siblings); - if (RandomStart > 0 && hasSpecialColumn(HitObject.Samples)) + if (RandomStart > 0 && hasSpecialColumn) addToPattern(pattern, 0, siblings); return pattern; From 8d94c2ef8be722d339181047cc94400ac8b2a2c5 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 19 May 2017 21:17:14 +0900 Subject: [PATCH 03/58] Fix CI errors (for now). --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 6 ++---- .../Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index e82b0dd516..53e06d451e 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -11,7 +11,6 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Beatmaps.Patterns; using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Database; -using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; using OpenTK; @@ -136,9 +135,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps Pattern newPattern = conversion.Generate(); lastPattern = newPattern; - var stairPatternGenerator = conversion as HitObjectPatternGenerator; - if (stairPatternGenerator != null) - lastStair = stairPatternGenerator.StairType; + var stairPatternGenerator = (HitObjectPatternGenerator)conversion; + lastStair = stairPatternGenerator.StairType; return newPattern.HitObjects; } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 5d3b84f57b..3de3d129bf 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy StairType = PatternType.ReverseStair; } - addToPattern(pattern, targetColumn, 1); + addToPattern(pattern, targetColumn); return pattern; } @@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy StairType = PatternType.Stair; } - addToPattern(pattern, targetColumn, 1); + addToPattern(pattern, targetColumn); return pattern; } @@ -282,7 +282,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { var pattern = new Pattern(); - bool addToCentre = false; + bool addToCentre; int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre); int siblings = noteCount; From 24a813e907c19aae3dddae4c1bd4d4d2e7e4eb5a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 20 May 2017 04:41:06 +0800 Subject: [PATCH 04/58] Basic children management of SectionsContainer. --- .../Graphics/Containers/SectionsContainer.cs | 74 +++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 2 files changed, 75 insertions(+) create mode 100644 osu.Game/Graphics/Containers/SectionsContainer.cs diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs new file mode 100644 index 0000000000..9c2485c49c --- /dev/null +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -0,0 +1,74 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Graphics.Containers +{ + /// + /// A container that can scroll to each section inside it. + /// + public class SectionsContainer : Container + { + private Drawable expandableHeader, fixedHeader; + private ScrollContainer scrollContainer; + + public Drawable ExpandableHeader + { + get { return expandableHeader; } + set + { + if (value == expandableHeader) return; + + scrollContainer.Remove(expandableHeader); + expandableHeader = value; + expandableHeader.Depth = float.MinValue; + scrollContainer.Add(expandableHeader); + } + } + + public Drawable FixedHeader + { + get { return fixedHeader; } + set + { + if (value == fixedHeader) return; + + scrollContainer.Remove(fixedHeader); + fixedHeader = value; + fixedHeader.Depth = float.MinValue / 2; + scrollContainer.Add(fixedHeader); + } + } + + private List sections = new List(); + public IEnumerable Sections + { + get { return sections; } + set + { + if (value == sections) return; + + foreach (var section in sections) + scrollContainer.Remove(section); + + sections = value.ToList(); + scrollContainer.Add(value); + } + } + + public SectionsContainer() + { + Add(scrollContainer = new ScrollContainer + { + RelativeSizeAxes = Axes.Both + }); + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ee906caa9b..1200276027 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -85,6 +85,7 @@ + From 058c5e18a424fcdbc5b29741ac198c1cfc17cfb3 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 20 May 2017 05:48:40 +0800 Subject: [PATCH 05/58] Scrolling support for SectionsContainer. --- .../Graphics/Containers/SectionsContainer.cs | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 9c2485c49c..3557a09622 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -26,10 +24,11 @@ namespace osu.Game.Graphics.Containers { if (value == expandableHeader) return; - scrollContainer.Remove(expandableHeader); + Remove(expandableHeader); expandableHeader = value; expandableHeader.Depth = float.MinValue; - scrollContainer.Add(expandableHeader); + Add(expandableHeader); + updateSectionMargin(); } } @@ -40,10 +39,11 @@ namespace osu.Game.Graphics.Containers { if (value == fixedHeader) return; - scrollContainer.Remove(fixedHeader); + Remove(fixedHeader); fixedHeader = value; fixedHeader.Depth = float.MinValue / 2; - scrollContainer.Add(fixedHeader); + Add(fixedHeader); + updateSectionMargin(); } } @@ -59,10 +59,25 @@ namespace osu.Game.Graphics.Containers scrollContainer.Remove(section); sections = value.ToList(); - scrollContainer.Add(value); + if (sections.Count == 0) return; + + originalSectionMargin = sections[0].Margin; + updateSectionMargin(); + scrollContainer.Add(sections); } } + private MarginPadding originalSectionMargin; + private void updateSectionMargin() + { + if (sections.Count == 0) return; + + var newMargin = originalSectionMargin; + newMargin.Top += ExpandableHeader?.Height ?? 0 + FixedHeader?.Height ?? 0; + + sections[0].Margin = newMargin; + } + public SectionsContainer() { Add(scrollContainer = new ScrollContainer @@ -70,5 +85,17 @@ namespace osu.Game.Graphics.Containers RelativeSizeAxes = Axes.Both }); } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + if (expandableHeader == null) return; + + float position = scrollContainer.Current; + float offset = Math.Max(expandableHeader.Height, position); + + expandableHeader.Y = -offset; + fixedHeader.Y = -offset + expandableHeader.Height; + } } } From e06c917c95fc50e356ee0a9000c6caa6585e9776 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 20 May 2017 06:39:01 +0800 Subject: [PATCH 06/58] Selection support of SectionsContainer. --- .../Graphics/Containers/SectionsContainer.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 3557a09622..536d53554a 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -47,6 +48,9 @@ namespace osu.Game.Graphics.Containers } } + public Bindable SelectedSection { get; } = new Bindable(); + public void ScrollToSection(Drawable section) => scrollContainer.ScrollIntoView(section); + private List sections = new List(); public IEnumerable Sections { @@ -64,6 +68,7 @@ namespace osu.Game.Graphics.Containers originalSectionMargin = sections[0].Margin; updateSectionMargin(); scrollContainer.Add(sections); + SelectedSection.Value = sections[0]; } } @@ -86,6 +91,7 @@ namespace osu.Game.Graphics.Containers }); } + float lastKnownScroll; protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -96,6 +102,28 @@ namespace osu.Game.Graphics.Containers expandableHeader.Y = -offset; fixedHeader.Y = -offset + expandableHeader.Height; + + float currentScroll = scrollContainer.Current; + if (currentScroll != lastKnownScroll) + { + lastKnownScroll = currentScroll; + + Drawable bestMatch = null; + float minDiff = float.MaxValue; + + foreach (var section in sections) + { + float diff = Math.Abs(scrollContainer.GetChildPosInContent(section) - currentScroll); + if (diff < minDiff) + { + minDiff = diff; + bestMatch = section; + } + } + + if (bestMatch != null) + SelectedSection.Value = bestMatch; + } } } } From 473441dfd4e5f3b91f1c34ff4dff3b6d1e3864ee Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 20 May 2017 07:07:27 +0800 Subject: [PATCH 07/58] Fix fields being null. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 536d53554a..65bb318008 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -25,7 +25,8 @@ namespace osu.Game.Graphics.Containers { if (value == expandableHeader) return; - Remove(expandableHeader); + if (expandableHeader != null) + Remove(expandableHeader); expandableHeader = value; expandableHeader.Depth = float.MinValue; Add(expandableHeader); @@ -40,7 +41,8 @@ namespace osu.Game.Graphics.Containers { if (value == fixedHeader) return; - Remove(fixedHeader); + if (fixedHeader != null) + Remove(fixedHeader); fixedHeader = value; fixedHeader.Depth = float.MinValue / 2; Add(fixedHeader); From ecc222c0438b55d062a6853d8efc20a5b91f171e Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 20 May 2017 07:20:46 +0800 Subject: [PATCH 08/58] Allow custom SectionsContainer. --- .../Graphics/Containers/SectionsContainer.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 65bb318008..d46292cd9a 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -16,7 +16,8 @@ namespace osu.Game.Graphics.Containers public class SectionsContainer : Container { private Drawable expandableHeader, fixedHeader; - private ScrollContainer scrollContainer; + private readonly ScrollContainer scrollContainer; + private readonly Container sectionsContainer; public Drawable ExpandableHeader { @@ -53,6 +54,13 @@ namespace osu.Game.Graphics.Containers public Bindable SelectedSection { get; } = new Bindable(); public void ScrollToSection(Drawable section) => scrollContainer.ScrollIntoView(section); + protected virtual Container CreateSectionsContainer() + => new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both + }; + private List sections = new List(); public IEnumerable Sections { @@ -62,14 +70,14 @@ namespace osu.Game.Graphics.Containers if (value == sections) return; foreach (var section in sections) - scrollContainer.Remove(section); + sectionsContainer.Remove(section); sections = value.ToList(); if (sections.Count == 0) return; originalSectionMargin = sections[0].Margin; updateSectionMargin(); - scrollContainer.Add(sections); + sectionsContainer.Add(sections); SelectedSection.Value = sections[0]; } } @@ -87,9 +95,10 @@ namespace osu.Game.Graphics.Containers public SectionsContainer() { - Add(scrollContainer = new ScrollContainer + Add(scrollContainer = new ScrollContainer() { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { sectionsContainer = CreateSectionsContainer() } }); } From da47b0a2603fddc62af2c22b755e258d8d301f61 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 21 May 2017 02:11:55 +0800 Subject: [PATCH 09/58] Fix margin and offset calculating. --- .../Graphics/Containers/SectionsContainer.cs | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index d46292cd9a..5f8ac332e4 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -29,9 +29,10 @@ namespace osu.Game.Graphics.Containers if (expandableHeader != null) Remove(expandableHeader); expandableHeader = value; + if (value == null) return; + expandableHeader.Depth = float.MinValue; Add(expandableHeader); - updateSectionMargin(); } } @@ -45,9 +46,10 @@ namespace osu.Game.Graphics.Containers if (fixedHeader != null) Remove(fixedHeader); fixedHeader = value; + if (value == null) return; + fixedHeader.Depth = float.MinValue / 2; Add(fixedHeader); - updateSectionMargin(); } } @@ -76,19 +78,19 @@ namespace osu.Game.Graphics.Containers if (sections.Count == 0) return; originalSectionMargin = sections[0].Margin; - updateSectionMargin(); sectionsContainer.Add(sections); SelectedSection.Value = sections[0]; } } + float headerHeight; private MarginPadding originalSectionMargin; private void updateSectionMargin() { if (sections.Count == 0) return; var newMargin = originalSectionMargin; - newMargin.Top += ExpandableHeader?.Height ?? 0 + FixedHeader?.Height ?? 0; + newMargin.Top += headerHeight; sections[0].Margin = newMargin; } @@ -102,23 +104,33 @@ namespace osu.Game.Graphics.Containers }); } - float lastKnownScroll; + float lastKnownScroll = float.NaN; protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); + + float height = (ExpandableHeader?.Height ?? 0) + (FixedHeader?.Height ?? 0); + if (height != headerHeight) + { + headerHeight = height; + updateSectionMargin(); + } + if (expandableHeader == null) return; - float position = scrollContainer.Current; - float offset = Math.Max(expandableHeader.Height, position); - - expandableHeader.Y = -offset; - fixedHeader.Y = -offset + expandableHeader.Height; - float currentScroll = scrollContainer.Current; if (currentScroll != lastKnownScroll) { lastKnownScroll = currentScroll; + if (expandableHeader != null) + { + float offset = Math.Min(expandableHeader.Height, currentScroll); + + expandableHeader.Y = -offset; + fixedHeader.Y = -offset + expandableHeader.Height; + } + Drawable bestMatch = null; float minDiff = float.MaxValue; From e040f297c60b4d4df0398b5dd162585eeea4d3cb Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 21 May 2017 02:15:40 +0800 Subject: [PATCH 10/58] Set Masking = false and expose ScrollContainer. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 5f8ac332e4..845970fde7 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Graphics.Containers public class SectionsContainer : Container { private Drawable expandableHeader, fixedHeader; - private readonly ScrollContainer scrollContainer; + public readonly ScrollContainer ScrollContainer; private readonly Container sectionsContainer; public Drawable ExpandableHeader @@ -54,7 +54,6 @@ namespace osu.Game.Graphics.Containers } public Bindable SelectedSection { get; } = new Bindable(); - public void ScrollToSection(Drawable section) => scrollContainer.ScrollIntoView(section); protected virtual Container CreateSectionsContainer() => new FillFlowContainer @@ -97,9 +96,10 @@ namespace osu.Game.Graphics.Containers public SectionsContainer() { - Add(scrollContainer = new ScrollContainer() + Add(ScrollContainer = new ScrollContainer() { RelativeSizeAxes = Axes.Both, + Masking = false, Children = new Drawable[] { sectionsContainer = CreateSectionsContainer() } }); } @@ -118,7 +118,7 @@ namespace osu.Game.Graphics.Containers if (expandableHeader == null) return; - float currentScroll = scrollContainer.Current; + float currentScroll = ScrollContainer.Current; if (currentScroll != lastKnownScroll) { lastKnownScroll = currentScroll; @@ -136,7 +136,7 @@ namespace osu.Game.Graphics.Containers foreach (var section in sections) { - float diff = Math.Abs(scrollContainer.GetChildPosInContent(section) - currentScroll); + float diff = Math.Abs(ScrollContainer.GetChildPosInContent(section) - currentScroll); if (diff < minDiff) { minDiff = diff; From 72621c81dc076908b67f21123b8ff0cca34b4ee0 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 21 May 2017 02:37:34 +0800 Subject: [PATCH 11/58] Handle null and invalidation of headers. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 845970fde7..768141511c 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -33,6 +33,7 @@ namespace osu.Game.Graphics.Containers expandableHeader.Depth = float.MinValue; Add(expandableHeader); + lastKnownScroll = float.NaN; } } @@ -50,6 +51,7 @@ namespace osu.Game.Graphics.Containers fixedHeader.Depth = float.MinValue / 2; Add(fixedHeader); + lastKnownScroll = float.NaN; } } @@ -79,6 +81,7 @@ namespace osu.Game.Graphics.Containers originalSectionMargin = sections[0].Margin; sectionsContainer.Add(sections); SelectedSection.Value = sections[0]; + lastKnownScroll = float.NaN; } } @@ -104,7 +107,7 @@ namespace osu.Game.Graphics.Containers }); } - float lastKnownScroll = float.NaN; + float lastKnownScroll; protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -116,14 +119,12 @@ namespace osu.Game.Graphics.Containers updateSectionMargin(); } - if (expandableHeader == null) return; - float currentScroll = ScrollContainer.Current; if (currentScroll != lastKnownScroll) { lastKnownScroll = currentScroll; - if (expandableHeader != null) + if (expandableHeader != null && fixedHeader != null) { float offset = Math.Min(expandableHeader.Height, currentScroll); From 201b44dbf1f9421885d65f59eec11b3ca40f1182 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 21 May 2017 03:02:51 +0800 Subject: [PATCH 12/58] Do not make the name confusing. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 768141511c..1cf1219941 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Graphics.Containers public Bindable SelectedSection { get; } = new Bindable(); - protected virtual Container CreateSectionsContainer() + protected virtual Container CreateScrollContentContainer() => new FillFlowContainer { Direction = FillDirection.Vertical, @@ -103,7 +103,7 @@ namespace osu.Game.Graphics.Containers { RelativeSizeAxes = Axes.Both, Masking = false, - Children = new Drawable[] { sectionsContainer = CreateSectionsContainer() } + Children = new Drawable[] { sectionsContainer = CreateScrollContentContainer() } }); } From 93668e53a01427a2682d2dd169a2803131557a91 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 21 May 2017 03:44:03 +0800 Subject: [PATCH 13/58] Add footer support. --- .../Graphics/Containers/SectionsContainer.cs | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 1cf1219941..09a85c7dd3 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -15,7 +15,7 @@ namespace osu.Game.Graphics.Containers /// public class SectionsContainer : Container { - private Drawable expandableHeader, fixedHeader; + private Drawable expandableHeader, fixedHeader, footer; public readonly ScrollContainer ScrollContainer; private readonly Container sectionsContainer; @@ -31,7 +31,6 @@ namespace osu.Game.Graphics.Containers expandableHeader = value; if (value == null) return; - expandableHeader.Depth = float.MinValue; Add(expandableHeader); lastKnownScroll = float.NaN; } @@ -49,12 +48,30 @@ namespace osu.Game.Graphics.Containers fixedHeader = value; if (value == null) return; - fixedHeader.Depth = float.MinValue / 2; Add(fixedHeader); lastKnownScroll = float.NaN; } } + public Drawable Footer + { + get { return footer; } + set + { + if (value == footer) return; + + if (footer != null) + ScrollContainer.Remove(footer); + footer = value; + if (value == null) return; + + footer.Anchor |= Anchor.y2; + footer.Origin |= Anchor.y2; + ScrollContainer.Add(footer); + lastKnownScroll = float.NaN; + } + } + public Bindable SelectedSection { get; } = new Bindable(); protected virtual Container CreateScrollContentContainer() @@ -78,23 +95,23 @@ namespace osu.Game.Graphics.Containers sections = value.ToList(); if (sections.Count == 0) return; - originalSectionMargin = sections[0].Margin; sectionsContainer.Add(sections); SelectedSection.Value = sections[0]; lastKnownScroll = float.NaN; } } - float headerHeight; - private MarginPadding originalSectionMargin; - private void updateSectionMargin() + private float headerHeight, footerHeight; + private MarginPadding originalSectionsMargin; + private void updateSectionsMargin() { if (sections.Count == 0) return; - var newMargin = originalSectionMargin; + var newMargin = originalSectionsMargin; newMargin.Top += headerHeight; + newMargin.Bottom += footerHeight; - sections[0].Margin = newMargin; + sectionsContainer.Margin = newMargin; } public SectionsContainer() @@ -105,6 +122,7 @@ namespace osu.Game.Graphics.Containers Masking = false, Children = new Drawable[] { sectionsContainer = CreateScrollContentContainer() } }); + originalSectionsMargin = sectionsContainer.Margin; } float lastKnownScroll; @@ -112,11 +130,13 @@ namespace osu.Game.Graphics.Containers { base.UpdateAfterChildren(); - float height = (ExpandableHeader?.Height ?? 0) + (FixedHeader?.Height ?? 0); - if (height != headerHeight) + float headerHeight = (ExpandableHeader?.Height ?? 0) + (FixedHeader?.Height ?? 0); + float footerHeight = Footer?.Height ?? 0; + if (headerHeight != this.headerHeight || footerHeight != this.footerHeight) { - headerHeight = height; - updateSectionMargin(); + this.headerHeight = headerHeight; + this.footerHeight = footerHeight; + updateSectionsMargin(); } float currentScroll = ScrollContainer.Current; From 35712514a4baf27fb8aa891499eadcdad9e346e8 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 21 May 2017 03:50:04 +0800 Subject: [PATCH 14/58] Update settings UI using SectionsContainer basically. --- osu.Game/Overlays/SettingsOverlay.cs | 99 +++++++++++----------------- 1 file changed, 40 insertions(+), 59 deletions(-) diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 71ad18e081..d348b2c6a1 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -7,10 +7,11 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Overlays.Settings; -using System; -using osu.Game.Overlays.Settings.Sections; using osu.Framework.Input; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; +using osu.Game.Overlays.Settings.Sections; namespace osu.Game.Overlays { @@ -37,6 +38,8 @@ namespace osu.Game.Overlays private SearchContainer searchContainer; + private SettingsSectionsContainer sectionsContainer; + private float lastKnownScroll; public SettingsOverlay() @@ -68,27 +71,27 @@ namespace osu.Game.Overlays Colour = Color4.Black, Alpha = 0.6f, }, - scrollContainer = new ScrollContainer + sectionsContainer = new SettingsSectionsContainer { - ScrollDraggerVisible = false, RelativeSizeAxes = Axes.Y, Width = width, Margin = new MarginPadding { Left = SIDEBAR_WIDTH }, - Children = new Drawable[] + ExpandableHeader = header = new SettingsHeader(() => sectionsContainer.ScrollContainer.Current), + FixedHeader = new SearchTextBox { - searchContainer = new SearchContainer + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Width = 0.95f, + Margin = new MarginPadding { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Children = sections, + Top = 20, + Bottom = 20 }, - footer = new SettingsFooter(), - header = new SettingsHeader(() => scrollContainer.Current) - { - Exit = Hide, - }, - } + Exit = Hide, + }, + Sections = sections, + Footer = footer = new SettingsFooter() }, sidebar = new Sidebar { @@ -104,54 +107,16 @@ namespace osu.Game.Overlays } }; - header.SearchTextBox.Current.ValueChanged += newValue => searchContainer.SearchTerm = newValue; + //header.SearchTextBox.Current.ValueChanged += newValue => searchContainer.SearchTerm = newValue; - scrollContainer.Padding = new MarginPadding { Top = game?.Toolbar.DrawHeight ?? 0 }; - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - //we need to update these manually because we can't put the SettingsHeader inside the SearchContainer (due to its anchoring). - searchContainer.Y = header.DrawHeight; - footer.Y = searchContainer.Y + searchContainer.DrawHeight; - } - - protected override void Update() - { - base.Update(); - - float currentScroll = scrollContainer.Current; - if (currentScroll != lastKnownScroll) - { - lastKnownScroll = currentScroll; - - SettingsSection bestCandidate = null; - float bestDistance = float.MaxValue; - - foreach (SettingsSection section in sections) - { - float distance = Math.Abs(scrollContainer.GetChildPosInContent(section) - currentScroll); - if (distance < bestDistance) - { - bestDistance = distance; - bestCandidate = section; - } - } - - var previous = sidebarButtons.SingleOrDefault(sb => sb.Selected); - var next = sidebarButtons.SingleOrDefault(sb => sb.Section == bestCandidate); - if (previous != null) previous.Selected = false; - if (next != null) next.Selected = true; - } + sectionsContainer.Padding = new MarginPadding { Top = game?.Toolbar.DrawHeight ?? 0 }; } protected override void PopIn() { base.PopIn(); - scrollContainer.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint); + sectionsContainer.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint); sidebar.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint); FadeTo(1, TRANSITION_LENGTH / 2); @@ -162,7 +127,7 @@ namespace osu.Game.Overlays { base.PopOut(); - scrollContainer.MoveToX(-width, TRANSITION_LENGTH, EasingTypes.OutQuint); + sectionsContainer.MoveToX(-width, TRANSITION_LENGTH, EasingTypes.OutQuint); sidebar.MoveToX(-SIDEBAR_WIDTH, TRANSITION_LENGTH, EasingTypes.OutQuint); FadeTo(0, TRANSITION_LENGTH / 2); @@ -175,5 +140,21 @@ namespace osu.Game.Overlays header.SearchTextBox.TriggerFocus(state); return false; } + + private class SettingsSectionsContainer : SectionsContainer + { + public SearchContainer SearchContainer; + protected override Container CreateScrollContentContainer() + => SearchContainer = new SearchContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + }; + public SettingsSectionsContainer() + { + ScrollContainer.ScrollDraggerVisible = false; + } + } } } From a98f109d733836bb8bb6e72b93ad12d0f65bb106 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 21 May 2017 04:01:07 +0800 Subject: [PATCH 15/58] Use LayoutSize for header and footer layout. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 09a85c7dd3..b0de350930 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -130,8 +130,8 @@ namespace osu.Game.Graphics.Containers { base.UpdateAfterChildren(); - float headerHeight = (ExpandableHeader?.Height ?? 0) + (FixedHeader?.Height ?? 0); - float footerHeight = Footer?.Height ?? 0; + float headerHeight = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); + float footerHeight = Footer?.LayoutSize.Y ?? 0; if (headerHeight != this.headerHeight || footerHeight != this.footerHeight) { this.headerHeight = headerHeight; @@ -146,10 +146,10 @@ namespace osu.Game.Graphics.Containers if (expandableHeader != null && fixedHeader != null) { - float offset = Math.Min(expandableHeader.Height, currentScroll); + float offset = Math.Min(expandableHeader.LayoutSize.Y, currentScroll); expandableHeader.Y = -offset; - fixedHeader.Y = -offset + expandableHeader.Height; + fixedHeader.Y = -offset + expandableHeader.LayoutSize.Y; } Drawable bestMatch = null; From aa409ac1a9931e45e6083ed71fe455469541932d Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 21 May 2017 04:12:16 +0800 Subject: [PATCH 16/58] Move SearchTextBox out of SettingsHeader. --- osu.Game/Overlays/Settings/SettingsHeader.cs | 60 ++------------------ osu.Game/Overlays/SettingsOverlay.cs | 46 +++++++++------ 2 files changed, 33 insertions(+), 73 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsHeader.cs b/osu.Game/Overlays/Settings/SettingsHeader.cs index 56018dc7d9..c554b54a87 100644 --- a/osu.Game/Overlays/Settings/SettingsHeader.cs +++ b/osu.Game/Overlays/Settings/SettingsHeader.cs @@ -1,34 +1,16 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using OpenTK.Graphics; namespace osu.Game.Overlays.Settings { public class SettingsHeader : Container { - public SearchTextBox SearchTextBox; - - private Box background; - - private readonly Func currentScrollOffset; - - public Action Exit; - - /// A reference to the current scroll position of the ScrollContainer we are contained within. - public SettingsHeader(Func currentScrollOffset) - { - this.currentScrollOffset = currentScrollOffset; - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -37,11 +19,6 @@ namespace osu.Game.Overlays.Settings Children = new Drawable[] { - background = new Box - { - Colour = Color4.Black, - RelativeSizeAxes = Axes.Both, - }, new FillFlowContainer { AutoSizeAxes = Axes.Y, @@ -53,7 +30,8 @@ namespace osu.Game.Overlays.Settings { Text = "settings", TextSize = 40, - Margin = new MarginPadding { + Margin = new MarginPadding + { Left = SettingsOverlay.CONTENT_MARGINS, Top = Toolbar.Toolbar.TOOLTIP_HEIGHT }, @@ -63,45 +41,15 @@ namespace osu.Game.Overlays.Settings Colour = colours.Pink, Text = "Change the way osu! behaves", TextSize = 18, - Margin = new MarginPadding { + Margin = new MarginPadding + { Left = SettingsOverlay.CONTENT_MARGINS, Bottom = 30 }, }, - SearchTextBox = new SearchTextBox - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Width = 0.95f, - Margin = new MarginPadding { - Top = 20, - Bottom = 20 - }, - Exit = () => Exit(), - }, } } }; } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - // the point at which we will start anchoring to the top. - float anchorOffset = SearchTextBox.Y; - - float scrollPosition = currentScrollOffset(); - - // we want to anchor the search field to the top of the screen when scrolling. - Margin = new MarginPadding { Top = Math.Max(0, scrollPosition - anchorOffset) }; - - // we don't want the header to scroll when scrolling beyond the upper extent. - Y = Math.Min(0, scrollPosition); - - // we get darker as scroll progresses - background.Alpha = Math.Min(1, scrollPosition / anchorOffset) * 0.5f; - } } } \ No newline at end of file diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index d348b2c6a1..05834c9823 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -27,20 +27,13 @@ namespace osu.Game.Overlays private const float sidebar_padding = 10; - private ScrollContainer scrollContainer; private Sidebar sidebar; private SidebarButton[] sidebarButtons; private SettingsSection[] sections; - private SettingsHeader header; - - private SettingsFooter footer; - - private SearchContainer searchContainer; - private SettingsSectionsContainer sectionsContainer; - private float lastKnownScroll; + private SearchTextBox searchTextBox; public SettingsOverlay() { @@ -76,8 +69,8 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Y, Width = width, Margin = new MarginPadding { Left = SIDEBAR_WIDTH }, - ExpandableHeader = header = new SettingsHeader(() => sectionsContainer.ScrollContainer.Current), - FixedHeader = new SearchTextBox + ExpandableHeader = new SettingsHeader(), + FixedHeader = searchTextBox = new SearchTextBox { RelativeSizeAxes = Axes.X, Origin = Anchor.TopCentre, @@ -91,7 +84,7 @@ namespace osu.Game.Overlays Exit = Hide, }, Sections = sections, - Footer = footer = new SettingsFooter() + Footer = new SettingsFooter() }, sidebar = new Sidebar { @@ -101,13 +94,13 @@ namespace osu.Game.Overlays { Selected = sections[0] == section, Section = section, - Action = () => scrollContainer.ScrollIntoView(section), + Action = () => sectionsContainer.ScrollContainer.ScrollIntoView(section), } ).ToArray() } }; - //header.SearchTextBox.Current.ValueChanged += newValue => searchContainer.SearchTerm = newValue; + searchTextBox.Current.ValueChanged += newValue => sectionsContainer.SearchContainer.SearchTerm = newValue; sectionsContainer.Padding = new MarginPadding { Top = game?.Toolbar.DrawHeight ?? 0 }; } @@ -120,7 +113,7 @@ namespace osu.Game.Overlays sidebar.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint); FadeTo(1, TRANSITION_LENGTH / 2); - header.SearchTextBox.HoldFocus = true; + searchTextBox.HoldFocus = true; } protected override void PopOut() @@ -131,19 +124,21 @@ namespace osu.Game.Overlays sidebar.MoveToX(-SIDEBAR_WIDTH, TRANSITION_LENGTH, EasingTypes.OutQuint); FadeTo(0, TRANSITION_LENGTH / 2); - header.SearchTextBox.HoldFocus = false; - header.SearchTextBox.TriggerFocusLost(); + searchTextBox.HoldFocus = false; + searchTextBox.TriggerFocusLost(); } protected override bool OnFocus(InputState state) { - header.SearchTextBox.TriggerFocus(state); + searchTextBox.TriggerFocus(state); return false; } private class SettingsSectionsContainer : SectionsContainer { public SearchContainer SearchContainer; + private readonly Box headerBackground; + protected override Container CreateScrollContentContainer() => SearchContainer = new SearchContainer { @@ -151,9 +146,26 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.X, Direction = FillDirection.Vertical, }; + public SettingsSectionsContainer() { ScrollContainer.ScrollDraggerVisible = false; + Add(headerBackground = new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.X, + Depth = float.MaxValue + }); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + // no null check because the usage of this class is strict + headerBackground.Height = ExpandableHeader.LayoutSize.Y + FixedHeader.LayoutSize.Y; + headerBackground.Y = ExpandableHeader.Y; + headerBackground.Alpha = -ExpandableHeader.Y / ExpandableHeader.LayoutSize.Y * 0.5f; } } } From f06f8b4dcd67d7e77fa3c567961267ed18fab095 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 21 May 2017 04:32:15 +0800 Subject: [PATCH 17/58] Make sidebar buttons working. --- osu.Game/Overlays/Settings/SidebarButton.cs | 5 +++-- osu.Game/Overlays/SettingsOverlay.cs | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index 766c6cf7e2..b6e4ad3f5e 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings private readonly Box backgroundBox; private readonly Box selectionIndicator; private readonly Container text; - public Action Action; + public Action Action; private SettingsSection section; public SettingsSection Section @@ -75,6 +75,7 @@ namespace osu.Game.Overlays.Settings { Width = Sidebar.DEFAULT_WIDTH, RelativeSizeAxes = Axes.Y, + Colour = OsuColour.Gray(0.6f), Children = new[] { headerText = new OsuSpriteText @@ -110,7 +111,7 @@ namespace osu.Game.Overlays.Settings protected override bool OnClick(InputState state) { - Action?.Invoke(); + Action?.Invoke(section); backgroundBox.FlashColour(Color4.White, 400); return true; } diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 05834c9823..af24a057e9 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays private Sidebar sidebar; private SidebarButton[] sidebarButtons; - private SettingsSection[] sections; + private SidebarButton selectedSidebarButton; private SettingsSectionsContainer sectionsContainer; @@ -44,7 +44,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader(permitNulls: true)] private void load(OsuGame game) { - sections = new SettingsSection[] + var sections = new SettingsSection[] { new GeneralSection(), new GraphicsSection(), @@ -92,14 +92,23 @@ namespace osu.Game.Overlays Children = sidebarButtons = sections.Select(section => new SidebarButton { - Selected = sections[0] == section, Section = section, - Action = () => sectionsContainer.ScrollContainer.ScrollIntoView(section), + Action = sectionsContainer.ScrollContainer.ScrollIntoView, } ).ToArray() } }; + selectedSidebarButton = sidebarButtons[0]; + selectedSidebarButton.Selected = true; + + sectionsContainer.SelectedSection.ValueChanged += section => + { + selectedSidebarButton.Selected = false; + selectedSidebarButton = sidebarButtons.Single(b => b.Section == section); + selectedSidebarButton.Selected = true; + }; + searchTextBox.Current.ValueChanged += newValue => sectionsContainer.SearchContainer.SearchTerm = newValue; sectionsContainer.Padding = new MarginPadding { Top = game?.Toolbar.DrawHeight ?? 0 }; From 7357076c4d3bf368f635886ff25bc667995a9f09 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 21 May 2017 04:48:43 +0800 Subject: [PATCH 18/58] CI fixes. --- .../Graphics/Containers/SectionsContainer.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index b0de350930..7b57cb1b56 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -87,8 +87,6 @@ namespace osu.Game.Graphics.Containers get { return sections; } set { - if (value == sections) return; - foreach (var section in sections) sectionsContainer.Remove(section); @@ -102,7 +100,7 @@ namespace osu.Game.Graphics.Containers } private float headerHeight, footerHeight; - private MarginPadding originalSectionsMargin; + private readonly MarginPadding originalSectionsMargin; private void updateSectionsMargin() { if (sections.Count == 0) return; @@ -125,17 +123,17 @@ namespace osu.Game.Graphics.Containers originalSectionsMargin = sectionsContainer.Margin; } - float lastKnownScroll; + private float lastKnownScroll; protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); - float headerHeight = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); - float footerHeight = Footer?.LayoutSize.Y ?? 0; - if (headerHeight != this.headerHeight || footerHeight != this.footerHeight) + float headerH = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); + float footerH = Footer?.LayoutSize.Y ?? 0; + if (headerH != headerHeight || footerH != footerHeight) { - this.headerHeight = headerHeight; - this.footerHeight = footerHeight; + headerHeight = headerH; + footerHeight = footerH; updateSectionsMargin(); } From e232968ea72463bb974b4a5d055fa92c837bf0b5 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 10:25:28 +0900 Subject: [PATCH 19/58] Fix line endings resulting in CI error. --- .../Legacy/HitObjectPatternGenerator.cs | 848 +++++++++--------- 1 file changed, 424 insertions(+), 424 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 3de3d129bf..657ba968a2 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -1,425 +1,425 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using OpenTK; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Mania.MathUtils; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy -{ - internal class HitObjectPatternGenerator : PatternGenerator - { - public PatternType StairType { get; private set; } - - private readonly PatternType convertType; - - public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair) - : base(random, hitObject, beatmap, previousPattern) - { - StairType = lastStair; - - ControlPoint overridePoint; - ControlPoint controlPoint = beatmap.TimingInfo.TimingPointAt(hitObject.StartTime, out overridePoint); - - var positionData = hitObject as IHasPosition; - - float positionSeparation = ((positionData?.Position ?? Vector2.Zero) - previousPosition).Length; - double timeSeparation = hitObject.StartTime - previousTime; - - double beatLength = controlPoint.BeatLength; - bool kiai = (overridePoint ?? controlPoint).KiaiMode; - - if (timeSeparation <= 125) - { - // More than 120 BPM - convertType |= PatternType.ForceNotStack; - } - - if (timeSeparation <= 80) - { - // More than 187 BPM - convertType |= PatternType.ForceNotStack | PatternType.KeepSingle; - } - else if (timeSeparation <= 95) - { - // More than 157 BPM - convertType |= PatternType.ForceNotStack | PatternType.KeepSingle | lastStair; - } - else if (timeSeparation <= 105) - { - // More than 140 BPM - convertType |= PatternType.ForceNotStack | PatternType.LowProbability; - } - else if (timeSeparation <= 125) - { - // More than 120 BPM - convertType |= PatternType.ForceNotStack; - } - else if (timeSeparation <= 135 && positionSeparation < 20) - { - // More than 111 BPM stream - convertType |= PatternType.Cycle | PatternType.KeepSingle; - } - else if (timeSeparation <= 150 & positionSeparation < 20) - { - // More than 100 BPM stream - convertType |= PatternType.ForceStack | PatternType.LowProbability; - } - else if (positionSeparation < 20 && density >= beatLength / 2.5) - { - // Low density stream - convertType |= PatternType.Reverse | PatternType.LowProbability; - } - else if (density < beatLength / 2.5 || kiai) - { - // High density - } - else - convertType |= PatternType.LowProbability; - } - - public override Pattern Generate() - { - int lastColumn = PreviousPattern.HitObjects.First().Column; - - if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Count() > 0) - { - // Generate a new pattern by copying the last hit objects in reverse-column order - var pattern = new Pattern(); - - int siblings = PreviousPattern.HitObjects.Count(h => h.Column >= RandomStart); - - for (int i = RandomStart; i < AvailableColumns; i++) - if (PreviousPattern.IsFilled(i)) - addToPattern(pattern, RandomStart + AvailableColumns - i - 1, siblings); - - return pattern; - } - - if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1 - // If we convert to 7K + 1, let's not overload the special key - && (AvailableColumns != 8 || lastColumn != 0) - // Make sure the last column was not the centre column - && (AvailableColumns % 2 == 0 || lastColumn != AvailableColumns / 2)) - { - // Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object) - var pattern = new Pattern(); - - int column = RandomStart + AvailableColumns - lastColumn - 1; - addToPattern(pattern, column); - - return pattern; - } - - if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Count() > 0) - { - // Generate a new pattern by placing on the already filled columns - var pattern = new Pattern(); - - int siblings = PreviousPattern.HitObjects.Count(h => h.Column >= RandomStart); - - for (int i = RandomStart; i < AvailableColumns; i++) - if (PreviousPattern.IsFilled(i)) - addToPattern(pattern, i, siblings); - - return pattern; - } - - if ((convertType & PatternType.Stair) > 0 && PreviousPattern.HitObjects.Count() == 1) - { - // Generate a new pattern by placing on the next column, cycling back to the start if there is no "next" - var pattern = new Pattern(); - - int targetColumn = lastColumn + 1; - if (targetColumn == AvailableColumns) - { - targetColumn = RandomStart; - StairType = PatternType.ReverseStair; - } - - addToPattern(pattern, targetColumn); - return pattern; - } - - if ((convertType & PatternType.ReverseStair) > 0 && PreviousPattern.HitObjects.Count() == 1) - { - // Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous" - var pattern = new Pattern(); - - int targetColumn = lastColumn - 1; - if (targetColumn == RandomStart - 1) - { - targetColumn = AvailableColumns - 1; - StairType = PatternType.Stair; - } - - addToPattern(pattern, targetColumn); - return pattern; - } - - if ((convertType & PatternType.KeepSingle) > 0) - return generateRandomNotes(1); - - if ((convertType & PatternType.Mirror) > 0) - { - if (ConversionDifficulty > 6.5) - return generateRandomPatternWithMirrored(0.12, 0.38, 0.12); - if (ConversionDifficulty > 4) - return generateRandomPatternWithMirrored(0.12, 0.17, 0); - return generateRandomPatternWithMirrored(0.12, 0, 0); - } - - if (ConversionDifficulty > 6.5) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateRandomPattern(0.78, 0.42, 0, 0); - return generateRandomPattern(1, 0.62, 0, 0); - } - - if (ConversionDifficulty > 4) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateRandomPattern(0.35, 0.08, 0, 0); - return generateRandomPattern(0.52, 0.15, 0, 0); - } - - if (ConversionDifficulty > 2) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateRandomPattern(0.18, 0, 0, 0); - return generateRandomPattern(0.45, 0, 0, 0); - } - - return generateRandomPattern(0, 0, 0, 0); - } - - /// - /// Generates random notes. - /// - /// This will generate as many as it can up to , accounting for - /// any stacks if is forcing no stacks. - /// - /// - /// The amount of notes to generate. - /// Custom siblings count if is not the number of siblings in this pattern. - /// The containing the hit objects. - private Pattern generateRandomNotes(int noteCount, int siblingsOverride = -1) - { - var pattern = new Pattern(); - - bool allowStacking = (convertType & PatternType.ForceNotStack) == 0; - - if (!allowStacking) - noteCount = Math.Min(noteCount, AvailableColumns - RandomStart - PreviousPattern.ColumnsFilled); - - int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - for (int i = 0; i < noteCount; i++) - { - while (pattern.IsFilled(nextColumn) || (PreviousPattern.IsFilled(nextColumn) && !allowStacking)) - { - if ((convertType & PatternType.Gathered) > 0) - { - nextColumn++; - if (nextColumn == AvailableColumns) - nextColumn = RandomStart; - } - else - nextColumn = Random.Next(RandomStart, AvailableColumns); - } - - addToPattern(pattern, nextColumn, siblingsOverride != -1 ? siblingsOverride : noteCount); - } - - return pattern; - } - - /// - /// Whether this hit object can generate a note in the special column. - /// - private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH); - - /// - /// Generates a random pattern. - /// - /// Probability for 2 notes to be generated. - /// Probability for 3 notes to be generated. - /// Probability for 4 notes to be generated. - /// Probability for 5 notes to be generated. - /// The containing the hit objects. - private Pattern generateRandomPattern(double p2, double p3, double p4, double p5) - { - var pattern = new Pattern(); - - int noteCount = getRandomNoteCount(p2, p3, p4, p5); - int siblings = noteCount; - - if (RandomStart > 0 && hasSpecialColumn) - { - siblings++; - addToPattern(pattern, 0, siblings); - } - - pattern.Add(generateRandomNotes(noteCount, siblings)); - - return pattern; - } - - /// - /// Generates a random pattern which has both normal and mirrored notes. - /// - /// The probability for a note to be added to the centre column. - /// Probability for 2 notes to be generated. - /// Probability for 3 notes to be generated. - /// The containing the hit objects. - private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3) - { - var pattern = new Pattern(); - - bool addToCentre; - int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre); - int siblings = noteCount; - - if (addToCentre) - siblings++; - if (RandomStart > 0 && hasSpecialColumn) - siblings++; - - int columnLimit = (AvailableColumns % 2 == 0 ? AvailableColumns : AvailableColumns - 1) / 2; - int nextColumn = Random.Next(RandomStart, columnLimit); - for (int i = 0; i < noteCount; i++) - { - while (pattern.IsFilled(nextColumn)) - nextColumn = Random.Next(RandomStart, columnLimit); - // Add normal note - addToPattern(pattern, nextColumn, siblings); - // Add mirrored note - addToPattern(pattern, RandomStart + AvailableColumns - nextColumn - 1); - } - - if (addToCentre) - addToPattern(pattern, AvailableColumns / 2, siblings); - - if (RandomStart > 0 && hasSpecialColumn) - addToPattern(pattern, 0, siblings); - - return pattern; - } - - /// - /// Generates a count of notes to be generated from a list of probabilities. - /// - /// Probability for 2 notes to be generated. - /// Probability for 3 notes to be generated. - /// Probability for 4 notes to be generated. - /// Probability for 5 notes to be generated. - /// The amount of notes to be generated. - private int getRandomNoteCount(double p2, double p3, double p4, double p5) - { - switch (AvailableColumns) - { - case 2: - p2 = 0; - p3 = 0; - p4 = 0; - p5 = 0; - break; - case 3: - p2 = Math.Max(p2, 0.1); - p3 = 0; - p4 = 0; - p5 = 0; - break; - case 4: - p2 = Math.Max(p2, 0.23); - p3 = Math.Max(p3, 0.04); - p4 = 0; - p5 = 0; - break; - case 5: - p3 = Math.Max(p3, 0.15); - p4 = Math.Max(p4, 0.03); - p5 = 0; - break; - } - - if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)) - p2 = 1; - - return GetRandomNoteCount(p2, p3, p4, p5); - } - - /// - /// Generates a count of notes to be generated from a list of probabilities. - /// - /// The probability for a note to be added to the centre column. - /// Probability for 2 notes to be generated. - /// Probability for 3 notes to be generated. - /// Whether to add a note to the centre column. - /// The amount of notes to be generated. The note to be added to the centre column will NOT be part of this count. - private int getRandomNoteCountMirrored(double centreProbability, double p2, double p3, out bool addToCentre) - { - addToCentre = false; - - if ((convertType & PatternType.ForceNotStack) > 0) - return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3); - - switch (AvailableColumns) - { - case 2: - centreProbability = 0; - p2 = 0; - p3 = 0; - break; - case 3: - centreProbability = Math.Max(centreProbability, 0.03); - p2 = Math.Max(p2, 0.1); - p3 = 0; - break; - case 4: - centreProbability = 0; - p2 = Math.Max(p2 * 2, 0.2); - p3 = 0; - break; - case 5: - centreProbability = Math.Max(centreProbability, 0.03); - p3 = 0; - break; - case 6: - centreProbability = 0; - p2 = Math.Max(p2 * 2, 0.5); - p3 = Math.Max(p3 * 2, 0.15); - break; - } - - double centreVal = Random.NextDouble(); - int noteCount = GetRandomNoteCount(p2, p3); - - addToCentre = AvailableColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability; - return noteCount; - } - - /// - /// Constructs and adds a note to a pattern. - /// - /// The pattern to add to. - /// The column to add the note to. - /// The number of children alongside this note (these will not be generated, but are used for volume calculations). - private void addToPattern(Pattern pattern, int column, int siblings = 1) - { - pattern.Add(new Note - { - StartTime = HitObject.StartTime, - Samples = HitObject.Samples, - Column = column, - Siblings = siblings - }); - } - } +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using OpenTK; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy +{ + internal class HitObjectPatternGenerator : PatternGenerator + { + public PatternType StairType { get; private set; } + + private readonly PatternType convertType; + + public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair) + : base(random, hitObject, beatmap, previousPattern) + { + StairType = lastStair; + + ControlPoint overridePoint; + ControlPoint controlPoint = beatmap.TimingInfo.TimingPointAt(hitObject.StartTime, out overridePoint); + + var positionData = hitObject as IHasPosition; + + float positionSeparation = ((positionData?.Position ?? Vector2.Zero) - previousPosition).Length; + double timeSeparation = hitObject.StartTime - previousTime; + + double beatLength = controlPoint.BeatLength; + bool kiai = (overridePoint ?? controlPoint).KiaiMode; + + if (timeSeparation <= 125) + { + // More than 120 BPM + convertType |= PatternType.ForceNotStack; + } + + if (timeSeparation <= 80) + { + // More than 187 BPM + convertType |= PatternType.ForceNotStack | PatternType.KeepSingle; + } + else if (timeSeparation <= 95) + { + // More than 157 BPM + convertType |= PatternType.ForceNotStack | PatternType.KeepSingle | lastStair; + } + else if (timeSeparation <= 105) + { + // More than 140 BPM + convertType |= PatternType.ForceNotStack | PatternType.LowProbability; + } + else if (timeSeparation <= 125) + { + // More than 120 BPM + convertType |= PatternType.ForceNotStack; + } + else if (timeSeparation <= 135 && positionSeparation < 20) + { + // More than 111 BPM stream + convertType |= PatternType.Cycle | PatternType.KeepSingle; + } + else if (timeSeparation <= 150 & positionSeparation < 20) + { + // More than 100 BPM stream + convertType |= PatternType.ForceStack | PatternType.LowProbability; + } + else if (positionSeparation < 20 && density >= beatLength / 2.5) + { + // Low density stream + convertType |= PatternType.Reverse | PatternType.LowProbability; + } + else if (density < beatLength / 2.5 || kiai) + { + // High density + } + else + convertType |= PatternType.LowProbability; + } + + public override Pattern Generate() + { + int lastColumn = PreviousPattern.HitObjects.First().Column; + + if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Count() > 0) + { + // Generate a new pattern by copying the last hit objects in reverse-column order + var pattern = new Pattern(); + + int siblings = PreviousPattern.HitObjects.Count(h => h.Column >= RandomStart); + + for (int i = RandomStart; i < AvailableColumns; i++) + if (PreviousPattern.IsFilled(i)) + addToPattern(pattern, RandomStart + AvailableColumns - i - 1, siblings); + + return pattern; + } + + if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1 + // If we convert to 7K + 1, let's not overload the special key + && (AvailableColumns != 8 || lastColumn != 0) + // Make sure the last column was not the centre column + && (AvailableColumns % 2 == 0 || lastColumn != AvailableColumns / 2)) + { + // Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object) + var pattern = new Pattern(); + + int column = RandomStart + AvailableColumns - lastColumn - 1; + addToPattern(pattern, column); + + return pattern; + } + + if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Count() > 0) + { + // Generate a new pattern by placing on the already filled columns + var pattern = new Pattern(); + + int siblings = PreviousPattern.HitObjects.Count(h => h.Column >= RandomStart); + + for (int i = RandomStart; i < AvailableColumns; i++) + if (PreviousPattern.IsFilled(i)) + addToPattern(pattern, i, siblings); + + return pattern; + } + + if ((convertType & PatternType.Stair) > 0 && PreviousPattern.HitObjects.Count() == 1) + { + // Generate a new pattern by placing on the next column, cycling back to the start if there is no "next" + var pattern = new Pattern(); + + int targetColumn = lastColumn + 1; + if (targetColumn == AvailableColumns) + { + targetColumn = RandomStart; + StairType = PatternType.ReverseStair; + } + + addToPattern(pattern, targetColumn); + return pattern; + } + + if ((convertType & PatternType.ReverseStair) > 0 && PreviousPattern.HitObjects.Count() == 1) + { + // Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous" + var pattern = new Pattern(); + + int targetColumn = lastColumn - 1; + if (targetColumn == RandomStart - 1) + { + targetColumn = AvailableColumns - 1; + StairType = PatternType.Stair; + } + + addToPattern(pattern, targetColumn); + return pattern; + } + + if ((convertType & PatternType.KeepSingle) > 0) + return generateRandomNotes(1); + + if ((convertType & PatternType.Mirror) > 0) + { + if (ConversionDifficulty > 6.5) + return generateRandomPatternWithMirrored(0.12, 0.38, 0.12); + if (ConversionDifficulty > 4) + return generateRandomPatternWithMirrored(0.12, 0.17, 0); + return generateRandomPatternWithMirrored(0.12, 0, 0); + } + + if (ConversionDifficulty > 6.5) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateRandomPattern(0.78, 0.42, 0, 0); + return generateRandomPattern(1, 0.62, 0, 0); + } + + if (ConversionDifficulty > 4) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateRandomPattern(0.35, 0.08, 0, 0); + return generateRandomPattern(0.52, 0.15, 0, 0); + } + + if (ConversionDifficulty > 2) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateRandomPattern(0.18, 0, 0, 0); + return generateRandomPattern(0.45, 0, 0, 0); + } + + return generateRandomPattern(0, 0, 0, 0); + } + + /// + /// Generates random notes. + /// + /// This will generate as many as it can up to , accounting for + /// any stacks if is forcing no stacks. + /// + /// + /// The amount of notes to generate. + /// Custom siblings count if is not the number of siblings in this pattern. + /// The containing the hit objects. + private Pattern generateRandomNotes(int noteCount, int siblingsOverride = -1) + { + var pattern = new Pattern(); + + bool allowStacking = (convertType & PatternType.ForceNotStack) == 0; + + if (!allowStacking) + noteCount = Math.Min(noteCount, AvailableColumns - RandomStart - PreviousPattern.ColumnsFilled); + + int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + for (int i = 0; i < noteCount; i++) + { + while (pattern.IsFilled(nextColumn) || (PreviousPattern.IsFilled(nextColumn) && !allowStacking)) + { + if ((convertType & PatternType.Gathered) > 0) + { + nextColumn++; + if (nextColumn == AvailableColumns) + nextColumn = RandomStart; + } + else + nextColumn = Random.Next(RandomStart, AvailableColumns); + } + + addToPattern(pattern, nextColumn, siblingsOverride != -1 ? siblingsOverride : noteCount); + } + + return pattern; + } + + /// + /// Whether this hit object can generate a note in the special column. + /// + private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH); + + /// + /// Generates a random pattern. + /// + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// Probability for 4 notes to be generated. + /// Probability for 5 notes to be generated. + /// The containing the hit objects. + private Pattern generateRandomPattern(double p2, double p3, double p4, double p5) + { + var pattern = new Pattern(); + + int noteCount = getRandomNoteCount(p2, p3, p4, p5); + int siblings = noteCount; + + if (RandomStart > 0 && hasSpecialColumn) + { + siblings++; + addToPattern(pattern, 0, siblings); + } + + pattern.Add(generateRandomNotes(noteCount, siblings)); + + return pattern; + } + + /// + /// Generates a random pattern which has both normal and mirrored notes. + /// + /// The probability for a note to be added to the centre column. + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// The containing the hit objects. + private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3) + { + var pattern = new Pattern(); + + bool addToCentre; + int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre); + int siblings = noteCount; + + if (addToCentre) + siblings++; + if (RandomStart > 0 && hasSpecialColumn) + siblings++; + + int columnLimit = (AvailableColumns % 2 == 0 ? AvailableColumns : AvailableColumns - 1) / 2; + int nextColumn = Random.Next(RandomStart, columnLimit); + for (int i = 0; i < noteCount; i++) + { + while (pattern.IsFilled(nextColumn)) + nextColumn = Random.Next(RandomStart, columnLimit); + // Add normal note + addToPattern(pattern, nextColumn, siblings); + // Add mirrored note + addToPattern(pattern, RandomStart + AvailableColumns - nextColumn - 1); + } + + if (addToCentre) + addToPattern(pattern, AvailableColumns / 2, siblings); + + if (RandomStart > 0 && hasSpecialColumn) + addToPattern(pattern, 0, siblings); + + return pattern; + } + + /// + /// Generates a count of notes to be generated from a list of probabilities. + /// + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// Probability for 4 notes to be generated. + /// Probability for 5 notes to be generated. + /// The amount of notes to be generated. + private int getRandomNoteCount(double p2, double p3, double p4, double p5) + { + switch (AvailableColumns) + { + case 2: + p2 = 0; + p3 = 0; + p4 = 0; + p5 = 0; + break; + case 3: + p2 = Math.Max(p2, 0.1); + p3 = 0; + p4 = 0; + p5 = 0; + break; + case 4: + p2 = Math.Max(p2, 0.23); + p3 = Math.Max(p3, 0.04); + p4 = 0; + p5 = 0; + break; + case 5: + p3 = Math.Max(p3, 0.15); + p4 = Math.Max(p4, 0.03); + p5 = 0; + break; + } + + if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)) + p2 = 1; + + return GetRandomNoteCount(p2, p3, p4, p5); + } + + /// + /// Generates a count of notes to be generated from a list of probabilities. + /// + /// The probability for a note to be added to the centre column. + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// Whether to add a note to the centre column. + /// The amount of notes to be generated. The note to be added to the centre column will NOT be part of this count. + private int getRandomNoteCountMirrored(double centreProbability, double p2, double p3, out bool addToCentre) + { + addToCentre = false; + + if ((convertType & PatternType.ForceNotStack) > 0) + return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3); + + switch (AvailableColumns) + { + case 2: + centreProbability = 0; + p2 = 0; + p3 = 0; + break; + case 3: + centreProbability = Math.Max(centreProbability, 0.03); + p2 = Math.Max(p2, 0.1); + p3 = 0; + break; + case 4: + centreProbability = 0; + p2 = Math.Max(p2 * 2, 0.2); + p3 = 0; + break; + case 5: + centreProbability = Math.Max(centreProbability, 0.03); + p3 = 0; + break; + case 6: + centreProbability = 0; + p2 = Math.Max(p2 * 2, 0.5); + p3 = Math.Max(p3 * 2, 0.15); + break; + } + + double centreVal = Random.NextDouble(); + int noteCount = GetRandomNoteCount(p2, p3); + + addToCentre = AvailableColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability; + return noteCount; + } + + /// + /// Constructs and adds a note to a pattern. + /// + /// The pattern to add to. + /// The column to add the note to. + /// The number of children alongside this note (these will not be generated, but are used for volume calculations). + private void addToPattern(Pattern pattern, int column, int siblings = 1) + { + pattern.Add(new Note + { + StartTime = HitObject.StartTime, + Samples = HitObject.Samples, + Column = column, + Siblings = siblings + }); + } + } } \ No newline at end of file From dba9dd35094acf50dc66ee4a783e04e5c13675ff Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 10:30:03 +0900 Subject: [PATCH 20/58] Remove siblings. --- .../Legacy/HitObjectPatternGenerator.cs | 42 ++++++------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 657ba968a2..340b8d95ac 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -94,11 +94,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy // Generate a new pattern by copying the last hit objects in reverse-column order var pattern = new Pattern(); - int siblings = PreviousPattern.HitObjects.Count(h => h.Column >= RandomStart); - for (int i = RandomStart; i < AvailableColumns; i++) if (PreviousPattern.IsFilled(i)) - addToPattern(pattern, RandomStart + AvailableColumns - i - 1, siblings); + addToPattern(pattern, RandomStart + AvailableColumns - i - 1); return pattern; } @@ -123,11 +121,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy // Generate a new pattern by placing on the already filled columns var pattern = new Pattern(); - int siblings = PreviousPattern.HitObjects.Count(h => h.Column >= RandomStart); - for (int i = RandomStart; i < AvailableColumns; i++) if (PreviousPattern.IsFilled(i)) - addToPattern(pattern, i, siblings); + addToPattern(pattern, i); return pattern; } @@ -208,9 +204,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// /// The amount of notes to generate. - /// Custom siblings count if is not the number of siblings in this pattern. /// The containing the hit objects. - private Pattern generateRandomNotes(int noteCount, int siblingsOverride = -1) + private Pattern generateRandomNotes(int noteCount) { var pattern = new Pattern(); @@ -234,7 +229,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy nextColumn = Random.Next(RandomStart, AvailableColumns); } - addToPattern(pattern, nextColumn, siblingsOverride != -1 ? siblingsOverride : noteCount); + addToPattern(pattern, nextColumn); } return pattern; @@ -257,16 +252,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { var pattern = new Pattern(); - int noteCount = getRandomNoteCount(p2, p3, p4, p5); - int siblings = noteCount; + pattern.Add(generateRandomNotes(getRandomNoteCount(p2, p3, p4, p5))); if (RandomStart > 0 && hasSpecialColumn) - { - siblings++; - addToPattern(pattern, 0, siblings); - } - - pattern.Add(generateRandomNotes(noteCount, siblings)); + addToPattern(pattern, 0); return pattern; } @@ -284,12 +273,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy bool addToCentre; int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre); - int siblings = noteCount; - - if (addToCentre) - siblings++; - if (RandomStart > 0 && hasSpecialColumn) - siblings++; int columnLimit = (AvailableColumns % 2 == 0 ? AvailableColumns : AvailableColumns - 1) / 2; int nextColumn = Random.Next(RandomStart, columnLimit); @@ -297,17 +280,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { while (pattern.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, columnLimit); + // Add normal note - addToPattern(pattern, nextColumn, siblings); + addToPattern(pattern, nextColumn); // Add mirrored note addToPattern(pattern, RandomStart + AvailableColumns - nextColumn - 1); } if (addToCentre) - addToPattern(pattern, AvailableColumns / 2, siblings); + addToPattern(pattern, AvailableColumns / 2); if (RandomStart > 0 && hasSpecialColumn) - addToPattern(pattern, 0, siblings); + addToPattern(pattern, 0); return pattern; } @@ -410,15 +394,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// The pattern to add to. /// The column to add the note to. - /// The number of children alongside this note (these will not be generated, but are used for volume calculations). - private void addToPattern(Pattern pattern, int column, int siblings = 1) + private void addToPattern(Pattern pattern, int column) { pattern.Add(new Note { StartTime = HitObject.StartTime, Samples = HitObject.Samples, - Column = column, - Siblings = siblings + Column = column }); } } From 8077ddf9442618093277c80ab6f07689054a8a76 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 10:34:12 +0900 Subject: [PATCH 21/58] Update with pattern changes. --- .../Patterns/Legacy/HitObjectPatternGenerator.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 340b8d95ac..4b65fb7bfd 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy var pattern = new Pattern(); for (int i = RandomStart; i < AvailableColumns; i++) - if (PreviousPattern.IsFilled(i)) + if (PreviousPattern.ColumnHasObject(i)) addToPattern(pattern, RandomStart + AvailableColumns - i - 1); return pattern; @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy var pattern = new Pattern(); for (int i = RandomStart; i < AvailableColumns; i++) - if (PreviousPattern.IsFilled(i)) + if (PreviousPattern.ColumnHasObject(i)) addToPattern(pattern, i); return pattern; @@ -212,12 +212,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy bool allowStacking = (convertType & PatternType.ForceNotStack) == 0; if (!allowStacking) - noteCount = Math.Min(noteCount, AvailableColumns - RandomStart - PreviousPattern.ColumnsFilled); + noteCount = Math.Min(noteCount, AvailableColumns - RandomStart - PreviousPattern.ColumnWithObjects); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); for (int i = 0; i < noteCount; i++) { - while (pattern.IsFilled(nextColumn) || (PreviousPattern.IsFilled(nextColumn) && !allowStacking)) + while (pattern.ColumnHasObject(nextColumn) || (PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking)) { if ((convertType & PatternType.Gathered) > 0) { @@ -278,7 +278,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int nextColumn = Random.Next(RandomStart, columnLimit); for (int i = 0; i < noteCount; i++) { - while (pattern.IsFilled(nextColumn)) + while (pattern.ColumnHasObject(nextColumn)) nextColumn = Random.Next(RandomStart, columnLimit); // Add normal note From f70bfd537834ed58ac6bf3c4e6ccbaea131a419d Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 13:43:53 +0900 Subject: [PATCH 22/58] CI fixes. --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 2 +- .../Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 2e9375960a..125d8cdded 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps yield return obj; } - private List prevNoteTimes = new List(max_notes_for_density); + private readonly List prevNoteTimes = new List(max_notes_for_density); private double density = int.MaxValue; private void computeDensity(double newNoteTime) { diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 4b65fb7bfd..07438d5f2c 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { int lastColumn = PreviousPattern.HitObjects.First().Column; - if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Count() > 0) + if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any()) { // Generate a new pattern by copying the last hit objects in reverse-column order var pattern = new Pattern(); @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy return pattern; } - if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Count() > 0) + if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any()) { // Generate a new pattern by placing on the already filled columns var pattern = new Pattern(); @@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); for (int i = 0; i < noteCount; i++) { - while (pattern.ColumnHasObject(nextColumn) || (PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking)) + while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking) { if ((convertType & PatternType.Gathered) > 0) { From d0ddd62f84ee3138b4724ba1b6b4fc477a35b2f2 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 14:10:37 +0900 Subject: [PATCH 23/58] Add ManiaHitResult to ManiaJudgement. --- .../Judgements/ManiaHitResult.cs | 18 ++++++++++++++++++ .../Judgements/ManiaJudgement.cs | 2 ++ 2 files changed, 20 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs new file mode 100644 index 0000000000..08c12a8151 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; + +namespace osu.Game.Rulesets.Mania.Judgements +{ + public enum ManiaHitResult + { + [Description("PERFECT")] + Perfect, + [Description("GREAT")] + Great, + [Description("GOOD")] + Good, + [Description("OK")] + Ok, + [Description("BAD")] + Bad + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs index 8dafbd01a5..eb544d79fc 100644 --- a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs @@ -10,5 +10,7 @@ namespace osu.Game.Rulesets.Mania.Judgements public override string ResultString => string.Empty; public override string MaxResultString => string.Empty; + + public ManiaHitResult ManiaResult; } } From 409464381c09a2f059e9cb7ef1dceb5900b135c1 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 14:11:14 +0900 Subject: [PATCH 24/58] Expose HitWindows so they're accessible from the drawable hit objects. --- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 2 +- osu.Game.Rulesets.Mania/Objects/Note.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index 701947c381..41bbe08d56 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Objects /// /// The key-release hit windows for this hold note. /// - protected HitWindows ReleaseHitWindows = new HitWindows(); + public HitWindows ReleaseHitWindows { get; protected set; } = new HitWindows(); public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) { diff --git a/osu.Game.Rulesets.Mania/Objects/Note.cs b/osu.Game.Rulesets.Mania/Objects/Note.cs index 1d2e4169b5..e955f6658b 100644 --- a/osu.Game.Rulesets.Mania/Objects/Note.cs +++ b/osu.Game.Rulesets.Mania/Objects/Note.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Objects /// /// The key-press hit window for this note. /// - protected HitWindows HitWindows = new HitWindows(); + public HitWindows HitWindows { get; protected set; } = new HitWindows(); public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) { From c9723352977ecf57fbe8cfb579d8497482dd42a8 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 15:25:37 +0900 Subject: [PATCH 25/58] Add InputTarget to capture input from columns before hit objects. --- osu.Game.Rulesets.Mania/UI/Column.cs | 34 +++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index dea00433e6..e1a925937d 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -95,6 +95,12 @@ namespace osu.Game.Rulesets.Mania.UI Name = "Hit objects", RelativeSizeAxes = Axes.Both, }, + // For column lighting, we need to capture input events before the notes + new InputTarget + { + KeyDown = onKeyDown, + KeyUp = onKeyUp + } } }, new Container @@ -178,12 +184,9 @@ namespace osu.Game.Rulesets.Mania.UI } } - public void Add(DrawableHitObject hitObject) - { - ControlPointContainer.Add(hitObject); - } + public void Add(DrawableHitObject hitObject) => ControlPointContainer.Add(hitObject); - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + private bool onKeyDown(InputState state, KeyDownEventArgs args) { if (args.Repeat) return false; @@ -197,7 +200,7 @@ namespace osu.Game.Rulesets.Mania.UI return false; } - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + private bool onKeyUp(InputState state, KeyUpEventArgs args) { if (args.Key == Key) { @@ -207,5 +210,24 @@ namespace osu.Game.Rulesets.Mania.UI return false; } + + /// + /// This is a simple container which delegates various input events that have to be captured before the notes. + /// + private class InputTarget : Container + { + public Func KeyDown; + public Func KeyUp; + + public InputTarget() + { + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; + Alpha = 0; + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => KeyDown?.Invoke(state, args) ?? false; + protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => KeyUp?.Invoke(state, args) ?? false; + } } } From 3dfe88c0684064a80947de52360b1ebbb512e186 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 15:27:38 +0900 Subject: [PATCH 26/58] Make Key a bindable, pass down to drawable hit objects. --- .../Objects/Drawables/DrawableHoldNote.cs | 6 ++++-- .../Drawables/DrawableManiaHitObject.cs | 12 ++++++++++- .../Objects/Drawables/DrawableNote.cs | 6 ++++-- osu.Game.Rulesets.Mania/UI/Column.cs | 7 ++++++- .../UI/ManiaHitRenderer.cs | 12 +++++++++-- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 21 ++++++++++--------- 6 files changed, 46 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index d9e46f4720..f9d027e7ce 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -5,6 +5,8 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using OpenTK.Graphics; +using osu.Framework.Configuration; +using OpenTK.Input; namespace osu.Game.Rulesets.Mania.Objects.Drawables { @@ -14,8 +16,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables private readonly BodyPiece bodyPiece; private readonly NotePiece tailPiece; - public DrawableHoldNote(HoldNote hitObject) - : base(hitObject) + public DrawableHoldNote(HoldNote hitObject, Bindable key = null) + : base(hitObject, key) { RelativeSizeAxes = Axes.Both; Height = (float)HitObject.Duration; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index d33a8c48ee..4e276fddb7 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -2,6 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK.Graphics; +using OpenTK.Input; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Objects.Drawables; @@ -11,13 +13,21 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public abstract class DrawableManiaHitObject : DrawableHitObject where TObject : ManiaHitObject { + /// + /// The key that will trigger input for this hit object. + /// + protected Bindable Key { get; private set; } = new Bindable(); + public new TObject HitObject; - protected DrawableManiaHitObject(TObject hitObject) + protected DrawableManiaHitObject(TObject hitObject, Bindable key = null) : base(hitObject) { HitObject = hitObject; + if (key != null) + Key.BindTo(key); + RelativePositionAxes = Axes.Y; Y = (float)HitObject.StartTime; } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index b216c362f5..d0519c61a8 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -2,6 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK.Graphics; +using OpenTK.Input; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects.Drawables; @@ -12,8 +14,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { private readonly NotePiece headPiece; - public DrawableNote(Note hitObject) - : base(hitObject) + public DrawableNote(Note hitObject, Bindable key = null) + : base(hitObject, key) { RelativeSizeAxes = Axes.Both; Height = 100; diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index e1a925937d..72c60b28c9 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -18,6 +18,8 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Beatmaps.Timing; +using System; +using osu.Framework.Configuration; namespace osu.Game.Rulesets.Mania.UI { @@ -33,7 +35,10 @@ namespace osu.Game.Rulesets.Mania.UI private const float column_width = 45; private const float special_column_width = 70; - public Key Key; + /// + /// The key that will trigger input actions for this column and hit objects contained inside it. + /// + public Bindable Key = new Bindable(); private readonly Box background; private readonly Container hitTargetBar; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index c67866dc10..4d734d231f 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -4,6 +4,8 @@ using System; using System.Linq; using OpenTK; +using OpenTK.Input; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; @@ -76,13 +78,19 @@ namespace osu.Game.Rulesets.Mania.UI protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h) { + var maniaPlayfield = Playfield as ManiaPlayfield; + if (maniaPlayfield == null) + return null; + + Bindable key = maniaPlayfield.Columns.ElementAt(h.Column).Key; + var holdNote = h as HoldNote; if (holdNote != null) - return new DrawableHoldNote(holdNote); + return new DrawableHoldNote(holdNote, key); var note = h as Note; if (note != null) - return new DrawableNote(note); + return new DrawableNote(note, key); return null; } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 56a86873e9..70bdd3b13c 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -55,7 +55,8 @@ namespace osu.Game.Rulesets.Mania.UI } } - public readonly FlowContainer Columns; + private readonly FlowContainer columns; + public IEnumerable Columns => columns.Children; private readonly ControlPointContainer barlineContainer; @@ -87,7 +88,7 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Both, Colour = Color4.Black }, - Columns = new FillFlowContainer + columns = new FillFlowContainer { Name = "Columns", RelativeSizeAxes = Axes.Y, @@ -114,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.UI }; for (int i = 0; i < columnCount; i++) - Columns.Add(new Column(timingChanges)); + columns.Add(new Column(timingChanges)); TimeSpan = time_span_default; } @@ -133,17 +134,17 @@ namespace osu.Game.Rulesets.Mania.UI // Set the special column + colour + key for (int i = 0; i < columnCount; i++) { - Column column = Columns.Children.ElementAt(i); + Column column = Columns.ElementAt(i); column.IsSpecial = isSpecialColumn(i); if (!column.IsSpecial) continue; - column.Key = Key.Space; + column.Key.Value = Key.Space; column.AccentColour = specialColumnColour; } - var nonSpecialColumns = Columns.Children.Where(c => !c.IsSpecial).ToList(); + var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList(); // We'll set the colours of the non-special columns in a separate loop, because the non-special // column colours are mirrored across their centre and special styles mess with this @@ -162,11 +163,11 @@ namespace osu.Game.Rulesets.Mania.UI int keyOffset = default_keys.Length / 2 - nonSpecialColumns.Count / 2 + i; if (keyOffset >= 0 && keyOffset < default_keys.Length) - column.Key = default_keys[keyOffset]; + column.Key.Value = default_keys[keyOffset]; else // There is no default key defined for this column. Let's set this to Unknown for now // however note that this will be gone after bindings are in place - column.Key = Key.Unknown; + column.Key.Value = Key.Unknown; } } @@ -189,7 +190,7 @@ namespace osu.Game.Rulesets.Mania.UI } } - public override void Add(DrawableHitObject h) => Columns.Children.ElementAt(h.HitObject.Column).Add(h); + public override void Add(DrawableHitObject h) => Columns.ElementAt(h.HitObject.Column).Add(h); protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { @@ -225,7 +226,7 @@ namespace osu.Game.Rulesets.Mania.UI timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max); barlineContainer.TimeSpan = value; - Columns.Children.ForEach(c => c.ControlPointContainer.TimeSpan = value); + Columns.ForEach(c => c.ControlPointContainer.TimeSpan = value); } } From dcf3148d23578776ecb8f484b1abb551935cb037 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 15:28:51 +0900 Subject: [PATCH 27/58] Fix osu!mania failing due to 0 hp. --- osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 96f04f79d4..7a9572a0c7 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -22,5 +22,12 @@ namespace osu.Game.Rulesets.Mania.Scoring protected override void OnNewJudgement(ManiaJudgement judgement) { } + + protected override void Reset() + { + base.Reset(); + + Health.Value = 1; + } } } From 60e036b5f454655b3068897483a9ebd782fc2b1f Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 15:29:02 +0900 Subject: [PATCH 28/58] Fix missing reference. --- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index adcdfd5fae..408f20033e 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -56,6 +56,7 @@ + From 9a578e036e4dac1632886458c65779be52fc1ec9 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 16:09:32 +0900 Subject: [PATCH 29/58] Add xmldoc. --- osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs index eb544d79fc..6e69da3da7 100644 --- a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs @@ -11,6 +11,9 @@ namespace osu.Game.Rulesets.Mania.Judgements public override string MaxResultString => string.Empty; + /// + /// The hit result. + /// public ManiaHitResult ManiaResult; } } From 0f3cf18345c347cab2e3febbfe98214d5123234b Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 16:15:52 +0900 Subject: [PATCH 30/58] Forgot license header. --- osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs index 08c12a8151..207a1fb251 100644 --- a/osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs +++ b/osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using System.ComponentModel; namespace osu.Game.Rulesets.Mania.Judgements From 62c7e97a5f4acf00f5eedc85d884654b18767e2d Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 16:28:44 +0900 Subject: [PATCH 31/58] Add helper method to easily compare time offset to hit windows. --- .../Judgements/HitWindows.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs index 2a0ce88506..307c03c8eb 100644 --- a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs +++ b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Database; +using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Judgements { @@ -140,6 +141,26 @@ namespace osu.Game.Rulesets.Mania.Judgements Miss = BeatmapDifficulty.DifficultyRange(difficulty, miss_max, miss_mid, miss_min); } + /// + /// Retrieves the hit result for a time offset. + /// + /// The time offset. + /// The hit result, or null if the time offset results in a miss. + public ManiaHitResult? ResultFor(double hitOffset) + { + if (hitOffset <= Perfect / 2) + return ManiaHitResult.Perfect; + if (hitOffset <= Great / 2) + return ManiaHitResult.Great; + if (hitOffset <= Good / 2) + return ManiaHitResult.Good; + if (hitOffset <= Ok / 2) + return ManiaHitResult.Ok; + if (hitOffset <= Bad / 2) + return ManiaHitResult.Bad; + return null; + } + /// /// Constructs new hit windows which have been multiplied by a value. /// From 6f00f7f0d084b274d10531f489eda396d6708c41 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 22 May 2017 16:34:38 +0900 Subject: [PATCH 32/58] Update HitWindows.cs --- osu.Game.Rulesets.Mania/Judgements/HitWindows.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs index 307c03c8eb..674d83f6f2 100644 --- a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs +++ b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Database; -using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Judgements { From f9eb448f162915e1d7293652635a417a2dbed825 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 15:29:17 +0900 Subject: [PATCH 33/58] Implement DrawableNote hits. --- .../Objects/Drawables/DrawableNote.cs | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index d0519c61a8..48cfcd66cd 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -1,10 +1,13 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using OpenTK.Graphics; using OpenTK.Input; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects.Drawables; @@ -40,14 +43,50 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables } } - protected override void Update() + protected override void CheckJudgement(bool userTriggered) { - if (Time.Current > HitObject.StartTime) - Colour = Color4.Green; + if (!userTriggered) + { + if (Judgement.TimeOffset > HitObject.HitWindows.Bad / 2) + Judgement.Result = HitResult.Miss; + return; + } + + double offset = Math.Abs(Judgement.TimeOffset); + + if (offset > HitObject.HitWindows.Miss / 2) + return; + + ManiaHitResult? tmpResult = HitObject.HitWindows.ResultFor(offset); + + if (tmpResult.HasValue) + { + Judgement.Result = HitResult.Hit; + Judgement.ManiaResult = tmpResult.Value; + } + else + Judgement.Result = HitResult.Miss; } protected override void UpdateState(ArmedState state) { + switch (State) + { + case ArmedState.Hit: + Colour = Color4.Green; + break; + } + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (Judgement.Result != HitResult.None) + return false; + + if (args.Key != Key) + return false; + + return UpdateJudgement(true); } } } From 7314a9019c96fc7717e676e17a25fdf4c7c12d22 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 16:39:30 +0900 Subject: [PATCH 34/58] Fix nullref. --- .../Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 07438d5f2c..d044ee8893 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy public override Pattern Generate() { - int lastColumn = PreviousPattern.HitObjects.First().Column; + int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0; if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any()) { From 4200e05fe702acb5673cc3c99db7f773f3a94660 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 16:42:14 +0900 Subject: [PATCH 35/58] Don't handle repeat keys. --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 48cfcd66cd..42bb371975 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -86,6 +86,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (args.Key != Key) return false; + if (args.Repeat) + return false; + return UpdateJudgement(true); } } From 85684e5fee4b70f8f1d35d8f1fe1598a6b2add46 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 22 May 2017 15:54:58 +0800 Subject: [PATCH 36/58] Do not scroll headers to when scrolling position is negative. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 7b57cb1b56..5fdb5e869e 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -137,7 +137,7 @@ namespace osu.Game.Graphics.Containers updateSectionsMargin(); } - float currentScroll = ScrollContainer.Current; + float currentScroll = Math.Max(0, ScrollContainer.Current); if (currentScroll != lastKnownScroll) { lastKnownScroll = currentScroll; From 63259a34573957888ea7b94f4faa1aa82cace6e4 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 22 May 2017 15:58:01 +0800 Subject: [PATCH 37/58] Fix depth of header background. Thanks to the order of adding to parent container, the depth value is actually not needed. --- osu.Game/Overlays/SettingsOverlay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index af24a057e9..943545e858 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -162,8 +162,7 @@ namespace osu.Game.Overlays Add(headerBackground = new Box { Colour = Color4.Black, - RelativeSizeAxes = Axes.X, - Depth = float.MaxValue + RelativeSizeAxes = Axes.X }); } From 95c4704a9e679381274c414d69dc9a5f5a5b908a Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 20:01:28 +0900 Subject: [PATCH 38/58] Delay backwards instead of using a millisecond tolerance. --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 945de822a3..d4b042c34c 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -11,11 +11,6 @@ namespace osu.Game.Graphics.Containers { public class BeatSyncedContainer : Container { - /// - /// A new beat will not be sent if the time since the beat is larger than this tolerance. - /// - private const int seek_tolerance = 20; - private readonly Bindable beatmap = new Bindable(); private int lastBeat; @@ -40,10 +35,10 @@ namespace osu.Game.Graphics.Containers if (controlPoint == lastControlPoint && beat == lastBeat) return; - if ((currentTrackTime - controlPoint.Time) % controlPoint.BeatLength > seek_tolerance) - return; + double offsetFromBeat = (controlPoint.Time - currentTrackTime) % controlPoint.BeatLength; - OnNewBeat(beat, controlPoint.BeatLength, controlPoint.TimeSignature, kiai); + using (BeginDelayedSequence(offsetFromBeat, true)) + OnNewBeat(beat, controlPoint.BeatLength, controlPoint.TimeSignature, kiai); lastBeat = beat; lastControlPoint = controlPoint; From de575b3867fb2b91db85494d935db9c99dd934bc Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 20:02:02 +0900 Subject: [PATCH 39/58] Early return if beatLength = 0. --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index d4b042c34c..12ed015c92 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -25,8 +25,11 @@ namespace osu.Game.Graphics.Containers ControlPoint overridePoint; ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(currentTrackTime, out overridePoint); + if (controlPoint.BeatLength == 0) + return; + bool kiai = (overridePoint ?? controlPoint).KiaiMode; - int beat = controlPoint.BeatLength > 0 ? (int)((currentTrackTime - controlPoint.Time) / controlPoint.BeatLength) : 0; + int beat = (int)((currentTrackTime - controlPoint.Time) / controlPoint.BeatLength); // The beats before the start of the first control point are off by 1, this should do the trick if (currentTrackTime < controlPoint.Time) From adf8bb853e16da48f1bc6d9bb06312e963249353 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 20:06:37 +0900 Subject: [PATCH 40/58] Move load() to be directly below ctor. --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 12ed015c92..dfb742f7d1 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -47,14 +47,14 @@ namespace osu.Game.Graphics.Containers lastControlPoint = controlPoint; } - protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai) - { - } - [BackgroundDependencyLoader] private void load(OsuGameBase game) { beatmap.BindTo(game.Beatmap); } + + protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai) + { + } } } From 3cdfd2eef5ca0af577503b8859d1de1792fe1362 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 13:55:18 +0900 Subject: [PATCH 41/58] Split ControlPoint into different types. # Conflicts: # osu.Game.Rulesets.Mania/UI/Column.cs --- osu-framework | 2 +- .../Tests/TestCaseGamefield.cs | 7 +- .../Tests/TestCaseManiaPlayfield.cs | 6 +- .../Legacy/DistanceObjectPatternGenerator.cs | 12 ++- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 5 +- osu.Game.Rulesets.Mania/Objects/Note.cs | 5 +- .../Timing/ControlPointContainer.cs | 9 ++- .../Timing/TimingChange.cs | 23 ++++++ osu.Game.Rulesets.Mania/UI/Column.cs | 3 +- .../UI/ManiaHitRenderer.cs | 31 ++++--- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 3 +- .../osu.Game.Rulesets.Mania.csproj | 1 + osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 5 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 12 ++- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 5 +- .../Beatmaps/TaikoBeatmapConverter.cs | 8 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 9 ++- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 5 +- .../Objects/TaikoHitObject.cs | 15 ++-- .../UI/TaikoHitRenderer.cs | 6 +- osu.Game/Beatmaps/Beatmap.cs | 5 +- .../Beatmaps/ControlPoints/ControlPoint.cs | 10 +++ .../ControlPoints/ControlPointInfo.cs | 63 +++++++++++++++ .../ControlPoints/DifficultyControlPoint.cs | 7 ++ .../ControlPoints/EffectControlPoint.cs | 8 ++ .../ControlPoints/SoundControlPoint.cs | 8 ++ .../ControlPoints/TimingControlPoint.cs | 10 +++ osu.Game/Beatmaps/DifficultyCalculator.cs | 2 +- osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 56 ++++++++++--- osu.Game/Beatmaps/Timing/ControlPoint.cs | 20 ----- osu.Game/Beatmaps/Timing/TimingInfo.cs | 80 ------------------- .../Containers/BeatSyncedContainer.cs | 31 +++---- .../Rulesets/Beatmaps/BeatmapConverter.cs | 2 +- osu.Game/Rulesets/Objects/HitObject.cs | 20 +++-- osu.Game/Rulesets/UI/HitRenderer.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 6 +- osu.Game/osu.Game.csproj | 8 +- 38 files changed, 307 insertions(+), 205 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Timing/TimingChange.cs create mode 100644 osu.Game/Beatmaps/ControlPoints/ControlPoint.cs create mode 100644 osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs create mode 100644 osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs create mode 100644 osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs create mode 100644 osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs create mode 100644 osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs delete mode 100644 osu.Game/Beatmaps/Timing/ControlPoint.cs delete mode 100644 osu.Game/Beatmaps/Timing/TimingInfo.cs diff --git a/osu-framework b/osu-framework index 42e26d49b9..773d60eb6b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 42e26d49b9046fcb96c123b0dfb48e06d741e162 +Subproject commit 773d60eb6b811f395e32a22dc66bb4d2e63a6dbc diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs index 6bd9d35b80..fb5bd8cde4 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs @@ -19,6 +19,7 @@ using System.Collections.Generic; using osu.Desktop.VisualTests.Beatmaps; using osu.Framework.Allocation; using osu.Game.Beatmaps.Timing; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Desktop.VisualTests.Tests { @@ -53,8 +54,8 @@ namespace osu.Desktop.VisualTests.Tests time += RNG.Next(50, 500); } - TimingInfo timing = new TimingInfo(); - timing.ControlPoints.Add(new ControlPoint + var controlPointInfo = new ControlPointInfo(); + controlPointInfo.ControlPoints.Add(new TimingControlPoint { BeatLength = 200 }); @@ -73,7 +74,7 @@ namespace osu.Desktop.VisualTests.Tests Author = @"peppy", }, }, - TimingInfo = timing + ControlPointInfo = controlPointInfo }); Add(new Drawable[] diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs index 04fcd8e94a..88f90fc333 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs @@ -11,6 +11,8 @@ using osu.Game.Beatmaps.Timing; using OpenTK; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mania.Timing; namespace osu.Desktop.VisualTests.Tests { @@ -27,7 +29,7 @@ namespace osu.Desktop.VisualTests.Tests Action createPlayfield = (cols, pos) => { Clear(); - Add(new ManiaPlayfield(cols, new List()) + Add(new ManiaPlayfield(cols, new List()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -41,7 +43,7 @@ namespace osu.Desktop.VisualTests.Tests Clear(); ManiaPlayfield playField; - Add(playField = new ManiaPlayfield(cols, new List { new ControlPoint { BeatLength = 200 } }) + Add(playField = new ManiaPlayfield(cols, new List()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 0cad23304e..1209a5c879 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -32,11 +33,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern) : base(random, hitObject, beatmap, previousPattern) { - ControlPoint overridePoint; - ControlPoint controlPoint = Beatmap.TimingInfo.TimingPointAt(hitObject.StartTime, out overridePoint); - convertType = PatternType.None; - if ((overridePoint ?? controlPoint)?.KiaiMode == false) + if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) convertType = PatternType.LowProbability; var distanceData = hitObject as IHasDistance; @@ -44,13 +42,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy repeatCount = repeatsData?.RepeatCount ?? 1; - double speedAdjustment = beatmap.TimingInfo.SpeedMultiplierAt(hitObject.StartTime); - double speedAdjustedBeatLength = beatmap.TimingInfo.BeatLengthAt(hitObject.StartTime) * speedAdjustment; + TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime); // The true distance, accounting for any repeats double distance = (distanceData?.Distance ?? 0) * repeatCount; // The velocity of the osu! hit object - calculated as the velocity of a slider - double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier / speedAdjustedBeatLength; + double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier / (timingPoint.BeatLength * difficultyPoint.SpeedMultiplier); // The duration of the osu! hit object double osuDuration = distance / osuVelocity; diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index 41bbe08d56..b0a0ed10b8 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Rulesets.Mania.Judgements; @@ -33,9 +34,9 @@ namespace osu.Game.Rulesets.Mania.Objects /// public HitWindows ReleaseHitWindows { get; protected set; } = new HitWindows(); - public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { - base.ApplyDefaults(timing, difficulty); + base.ApplyDefaults(controlPointInfo, difficulty); ReleaseHitWindows = HitWindows * release_window_lenience; } diff --git a/osu.Game.Rulesets.Mania/Objects/Note.cs b/osu.Game.Rulesets.Mania/Objects/Note.cs index e955f6658b..e91c00c145 100644 --- a/osu.Game.Rulesets.Mania/Objects/Note.cs +++ b/osu.Game.Rulesets.Mania/Objects/Note.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Rulesets.Mania.Judgements; @@ -17,9 +18,9 @@ namespace osu.Game.Rulesets.Mania.Objects /// public HitWindows HitWindows { get; protected set; } = new HitWindows(); - public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { - base.ApplyDefaults(timing, difficulty); + base.ApplyDefaults(controlPointInfo, difficulty); HitWindows = new HitWindows(difficulty.OverallDifficulty); } diff --git a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs b/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs index 2ff97047c0..38e975f468 100644 --- a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs +++ b/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using OpenTK; using osu.Game.Beatmaps.Timing; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Mania.Timing { @@ -26,9 +27,9 @@ namespace osu.Game.Rulesets.Mania.Timing /// public double TimeSpan { get; set; } - private readonly List drawableControlPoints; + private readonly List drawableControlPoints = new List(); - public ControlPointContainer(IEnumerable timingChanges) + public ControlPointContainer(IEnumerable timingChanges) { drawableControlPoints = timingChanges.Select(t => new DrawableControlPoint(t)).ToList(); Children = drawableControlPoints; @@ -64,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.Timing /// private class DrawableControlPoint : Container { - private readonly ControlPoint timingChange; + private readonly TimingChange timingChange; protected override Container Content => content; private readonly Container content; @@ -76,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Timing /// the content container will scroll at twice the normal rate. /// /// The control point to create the drawable control point for. - public DrawableControlPoint(ControlPoint timingChange) + public DrawableControlPoint(TimingChange timingChange) { this.timingChange = timingChange; diff --git a/osu.Game.Rulesets.Mania/Timing/TimingChange.cs b/osu.Game.Rulesets.Mania/Timing/TimingChange.cs new file mode 100644 index 0000000000..fb6f1d2db1 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Timing/TimingChange.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Timing +{ + public class TimingChange + { + /// + /// The time at which this timing change happened. + /// + public double Time; + + /// + /// The beat length. + /// + public double BeatLength = 500; + + /// + /// The speed multiplier. + /// + public double SpeedMultiplier = 1; + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 72c60b28c9..12c4cea58c 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Beatmaps.Timing; +using osu.Game.Beatmaps.ControlPoints; using System; using osu.Framework.Configuration; @@ -46,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.UI public readonly ControlPointContainer ControlPointContainer; - public Column(IEnumerable timingChanges) + public Column(IEnumerable timingChanges) { RelativeSizeAxes = Axes.Y; Width = column_width; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index 4d734d231f..8d5e22933c 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -8,6 +8,7 @@ using OpenTK.Input; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; @@ -15,6 +16,7 @@ using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Mania.Timing; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; @@ -33,22 +35,29 @@ namespace osu.Game.Rulesets.Mania.UI protected override Playfield CreatePlayfield() { - ControlPoint firstTimingChange = Beatmap.TimingInfo.ControlPoints.FirstOrDefault(t => t.TimingChange); - - if (firstTimingChange == null) - throw new InvalidOperationException("The Beatmap contains no timing points!"); + double lastSpeedMultiplier = 1; + double lastBeatLength = 500; // Generate the timing points, making non-timing changes use the previous timing change - var timingChanges = Beatmap.TimingInfo.ControlPoints.Select(c => + var timingChanges = Beatmap.ControlPointInfo.ControlPoints.Where(c => c is TimingControlPoint || c is DifficultyControlPoint).Select(c => { - ControlPoint t = c.Clone(); + var change = new TimingChange(); - if (c.TimingChange) - firstTimingChange = c; - else - t.BeatLength = firstTimingChange.BeatLength; + var timingPoint = c as TimingControlPoint; + var difficultyPoint = c as DifficultyControlPoint; - return t; + if (timingPoint != null) + lastBeatLength = timingPoint.BeatLength; + + if (difficultyPoint != null) + lastSpeedMultiplier = difficultyPoint.SpeedMultiplier; + + return new TimingChange + { + Time = c.Time, + BeatLength = lastBeatLength, + SpeedMultiplier = lastSpeedMultiplier + }; }); double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 70bdd3b13c..c0915c5d82 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -22,6 +22,7 @@ using osu.Framework.Input; using osu.Game.Beatmaps.Timing; using osu.Framework.Graphics.Transforms; using osu.Framework.MathUtils; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Mania.UI { @@ -65,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.UI private readonly int columnCount; - public ManiaPlayfield(int columnCount, IEnumerable timingChanges) + public ManiaPlayfield(int columnCount, IEnumerable timingChanges) { this.columnCount = columnCount; diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index a3f30acae0..9442d7cf8f 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -78,6 +78,7 @@ + diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 723a37ed7b..c4b4e3fc4a 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Database; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Osu.Objects { @@ -68,9 +69,9 @@ namespace osu.Game.Rulesets.Osu.Objects return OsuScoreResult.Miss; } - public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { - base.ApplyDefaults(timing, difficulty); + base.ApplyDefaults(controlPointInfo, difficulty); Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 6c0147a3de..1a5065d17d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Database; using System.Linq; using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Osu.Objects { @@ -62,13 +63,16 @@ namespace osu.Game.Rulesets.Osu.Objects public double Velocity; public double TickDistance; - public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { - base.ApplyDefaults(timing, difficulty); + base.ApplyDefaults(controlPointInfo, difficulty); - double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier / timing.SpeedMultiplierAt(StartTime); + TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime); - Velocity = scoringDistance / timing.BeatLengthAt(StartTime); + double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier / difficultyPoint.SpeedMultiplier; + + Velocity = scoringDistance / timingPoint.BeatLength; TickDistance = scoringDistance / difficulty.SliderTickRate; } diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 3761b62b65..8c0a218108 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -4,6 +4,7 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Timing; using osu.Game.Database; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Osu.Objects { @@ -19,9 +20,9 @@ namespace osu.Game.Rulesets.Osu.Objects public override bool NewCombo => true; - public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { - base.ApplyDefaults(timing, difficulty); + base.ApplyDefaults(controlPointInfo, difficulty); SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5)); } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index a2dea3731e..5d44da78f9 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -12,6 +12,7 @@ using osu.Game.Database; using osu.Game.IO.Serialization; using osu.Game.Audio; using osu.Game.Rulesets.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Taiko.Beatmaps { @@ -77,8 +78,11 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { int repeats = repeatsData?.RepeatCount ?? 1; - double speedAdjustment = beatmap.TimingInfo.SpeedMultiplierAt(obj.StartTime); - double speedAdjustedBeatLength = beatmap.TimingInfo.BeatLengthAt(obj.StartTime) * speedAdjustment; + TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime); + + double speedAdjustment = difficultyPoint.SpeedMultiplier; + double speedAdjustedBeatLength = timingPoint.BeatLength * speedAdjustment; // The true distance, accounting for any repeats. This ends up being the drum roll distance later double distance = distanceData.Distance * repeats * legacy_velocity_multiplier; diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index f79c01b643..857dd8d3a5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -8,6 +8,7 @@ using System.Linq; using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Taiko.Objects { @@ -55,11 +56,13 @@ namespace osu.Game.Rulesets.Taiko.Objects /// private double tickSpacing = 100; - public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { - base.ApplyDefaults(timing, difficulty); + base.ApplyDefaults(controlPointInfo, difficulty); - tickSpacing = timing.BeatLengthAt(StartTime) / TickRate; + TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + + tickSpacing = timingPoint.BeatLength / TickRate; RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty); RequiredGreatHits = TotalTicks * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty); diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 136e89124c..cc15b5ab98 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Database; @@ -23,9 +24,9 @@ namespace osu.Game.Rulesets.Taiko.Objects /// public double HitWindowMiss = 95; - public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { - base.ApplyDefaults(timing, difficulty); + base.ApplyDefaults(controlPointInfo, difficulty); HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20); HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 6a6353fde2..a294a13521 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Rulesets.Objects; @@ -51,17 +52,17 @@ namespace osu.Game.Rulesets.Taiko.Objects /// public bool Kiai { get; protected set; } - public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { - base.ApplyDefaults(timing, difficulty); + base.ApplyDefaults(controlPointInfo, difficulty); - ScrollTime = scroll_time * (timing.BeatLengthAt(StartTime) * timing.SpeedMultiplierAt(StartTime) / 1000) / difficulty.SliderMultiplier; + TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime); + EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime); - ControlPoint overridePoint; - Kiai = timing.TimingPointAt(StartTime, out overridePoint).KiaiMode; + ScrollTime = scroll_time * (timingPoint.BeatLength * difficultyPoint.SpeedMultiplier / 1000) / difficulty.SliderMultiplier; - if (overridePoint != null) - Kiai |= overridePoint.KiaiMode; + Kiai |= effectPoint.KiaiMode; } } } \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs index 73450d576d..7b0a0b085d 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs @@ -17,6 +17,8 @@ using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Taiko.Replays; using OpenTK; using osu.Game.Rulesets.Beatmaps; +using System.Linq; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Taiko.UI { @@ -43,7 +45,7 @@ namespace osu.Game.Rulesets.Taiko.UI TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1]; double lastHitTime = 1 + (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime; - var timingPoints = Beatmap.TimingInfo.ControlPoints.FindAll(cp => cp.TimingChange); + var timingPoints = Beatmap.ControlPointInfo.ControlPoints.OfType().ToList(); if (timingPoints.Count == 0) return; @@ -68,7 +70,7 @@ namespace osu.Game.Rulesets.Taiko.UI StartTime = time, }; - barLine.ApplyDefaults(Beatmap.TimingInfo, Beatmap.BeatmapInfo.Difficulty); + barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty); bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0; taikoPlayfield.AddBarLine(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine)); diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 608b2fcd19..0368455b92 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -7,6 +7,7 @@ using osu.Game.Database; using osu.Game.Rulesets.Objects; using System.Collections.Generic; using System.Linq; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Beatmaps { @@ -17,7 +18,7 @@ namespace osu.Game.Beatmaps where T : HitObject { public BeatmapInfo BeatmapInfo; - public TimingInfo TimingInfo = new TimingInfo(); + public ControlPointInfo ControlPointInfo = new ControlPointInfo(); public List Breaks = new List(); public readonly List ComboColors = new List { @@ -46,7 +47,7 @@ namespace osu.Game.Beatmaps public Beatmap(Beatmap original = null) { BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo; - TimingInfo = original?.TimingInfo ?? TimingInfo; + ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; Breaks = original?.Breaks ?? Breaks; ComboColors = original?.ComboColors ?? ComboColors; } diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs new file mode 100644 index 0000000000..5c31259de1 --- /dev/null +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Beatmaps.ControlPoints +{ + public class ControlPoint + { + public double Time; + } +} diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs new file mode 100644 index 0000000000..90c50a3e05 --- /dev/null +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Beatmaps.ControlPoints +{ + public class ControlPointInfo + { + /// + /// All the control points. + /// + public readonly List ControlPoints = new List(); + + /// + /// Finds the difficulty control point that is active at . + /// + /// The time to find the difficulty control point at. + /// The difficulty control point. + public DifficultyControlPoint DifficultyPointAt(double time) => + ControlPoints.OfType().LastOrDefault(t => t.Time <= time) ?? new DifficultyControlPoint(); + + /// + /// Finds the effect control point that is active at . + /// + /// The time to find the effect control point at. + /// The effect control point. + public EffectControlPoint EffectPointAt(double time) => + ControlPoints.OfType().LastOrDefault(t => t.Time <= time) ?? new EffectControlPoint(); + + /// + /// Finds the sound control point that is active at . + /// + /// The time to find the sound control point at. + /// The sound control point. + public SoundControlPoint SoundPointAt(double time) => + ControlPoints.OfType().LastOrDefault(t => t.Time <= time) ?? new SoundControlPoint(); + + /// + /// Finds the timing control point that is active at . + /// + /// The time to find the timing control point at. + /// The timing control point. + public TimingControlPoint TimingPointAt(double time) => + ControlPoints.OfType().LastOrDefault(t => t.Time <= time) ?? new TimingControlPoint(); + + /// + /// Finds the maximum BPM represented by any timing control point. + /// + public double BPMMaximum => + 60000 / (ControlPoints.OfType().OrderBy(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + + /// + /// Finds the minimum BPM represented by any timing control point. + /// + public double BPMMinimum => + 60000 / (ControlPoints.OfType().OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + + /// + /// Finds the mode BPM (most common BPM) represented by the control points. + /// + public double BPMMode => + 60000 / (ControlPoints.OfType().GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).FirstOrDefault()?.FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + } +} \ No newline at end of file diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs new file mode 100644 index 0000000000..abd9d05971 --- /dev/null +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -0,0 +1,7 @@ +namespace osu.Game.Beatmaps.ControlPoints +{ + public class DifficultyControlPoint : ControlPoint + { + public double SpeedMultiplier = 1; + } +} \ No newline at end of file diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs new file mode 100644 index 0000000000..2f6003f57c --- /dev/null +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -0,0 +1,8 @@ +namespace osu.Game.Beatmaps.ControlPoints +{ + public class EffectControlPoint : ControlPoint + { + public bool KiaiMode; + public bool OmitFirstBarLine; + } +} \ No newline at end of file diff --git a/osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs new file mode 100644 index 0000000000..696e684a3d --- /dev/null +++ b/osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs @@ -0,0 +1,8 @@ +namespace osu.Game.Beatmaps.ControlPoints +{ + public class SoundControlPoint : ControlPoint + { + public string SampleBank; + public int SampleVolume; + } +} \ No newline at end of file diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs new file mode 100644 index 0000000000..57c21f110b --- /dev/null +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -0,0 +1,10 @@ +using osu.Game.Beatmaps.Timing; + +namespace osu.Game.Beatmaps.ControlPoints +{ + public class TimingControlPoint : ControlPoint + { + public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple; + public double BeatLength = 500; + } +} \ No newline at end of file diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index f483d1e6e3..474d38aa1b 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps Objects = CreateBeatmapConverter().Convert(beatmap, true).HitObjects; foreach (var h in Objects) - h.ApplyDefaults(beatmap.TimingInfo, beatmap.BeatmapInfo.Difficulty); + h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty); PreprocessHitObjects(); } diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 04208337c7..e140e2282c 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -8,6 +8,8 @@ using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Legacy; using osu.Game.Rulesets.Objects.Legacy; +using System.Linq; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Beatmaps.Formats { @@ -241,6 +243,7 @@ namespace osu.Game.Beatmaps.Formats double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo); double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); + double speedMultiplier = beatLength < 0 ? -beatLength / 100.0 : 1; TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; if (split.Length >= 3) @@ -275,18 +278,49 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet == @"none") stringSampleSet = @"normal"; - beatmap.TimingInfo.ControlPoints.Add(new ControlPoint + TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(time); + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); + SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); + EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); + + if (timingChange && (beatLength != timingPoint.BeatLength || timeSignature != timingPoint.TimeSignature)) { - Time = time, - BeatLength = beatLength, - SpeedMultiplier = beatLength < 0 ? -beatLength / 100.0 : 1, - TimingChange = timingChange, - TimeSignature = timeSignature, - SampleBank = stringSampleSet, - SampleVolume = sampleVolume, - KiaiMode = kiaiMode, - OmitFirstBarLine = omitFirstBarSignature - }); + beatmap.ControlPointInfo.ControlPoints.Add(new TimingControlPoint + { + Time = time, + BeatLength = beatLength, + TimeSignature = timeSignature + }); + } + + if (speedMultiplier != difficultyPoint.SpeedMultiplier) + { + beatmap.ControlPointInfo.ControlPoints.Add(new DifficultyControlPoint + { + Time = time, + SpeedMultiplier = speedMultiplier + }); + } + + if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) + { + beatmap.ControlPointInfo.ControlPoints.Add(new SoundControlPoint + { + Time = time, + SampleBank = stringSampleSet, + SampleVolume = sampleVolume + }); + } + + if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) + { + beatmap.ControlPointInfo.ControlPoints.Add(new EffectControlPoint + { + Time = time, + KiaiMode = kiaiMode, + OmitFirstBarLine = omitFirstBarSignature + }); + } } private void handleColours(Beatmap beatmap, string key, string val, ref bool hasCustomColours) diff --git a/osu.Game/Beatmaps/Timing/ControlPoint.cs b/osu.Game/Beatmaps/Timing/ControlPoint.cs deleted file mode 100644 index fbae7d9614..0000000000 --- a/osu.Game/Beatmaps/Timing/ControlPoint.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Beatmaps.Timing -{ - public class ControlPoint - { - public string SampleBank; - public int SampleVolume; - public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple; - public double Time; - public double BeatLength = 500; - public double SpeedMultiplier = 1; - public bool TimingChange = true; - public bool KiaiMode; - public bool OmitFirstBarLine; - - public ControlPoint Clone() => (ControlPoint)MemberwiseClone(); - } -} diff --git a/osu.Game/Beatmaps/Timing/TimingInfo.cs b/osu.Game/Beatmaps/Timing/TimingInfo.cs deleted file mode 100644 index 19cb0816ba..0000000000 --- a/osu.Game/Beatmaps/Timing/TimingInfo.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Beatmaps.Timing -{ - public class TimingInfo - { - public readonly List ControlPoints = new List(); - - public double BPMMaximum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderBy(c => c.BeatLength).FirstOrDefault() ?? new ControlPoint()).BeatLength; - public double BPMMinimum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new ControlPoint()).BeatLength; - public double BPMMode => BPMAt(ControlPoints.Where(c => c.BeatLength != 0).GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).First().First().Time); - - public double BPMAt(double time) - { - return 60000 / BeatLengthAt(time); - } - - /// - /// Finds the speed multiplier at a time. - /// - /// The time to find the speed multiplier at. - /// The speed multiplier. - public double SpeedMultiplierAt(double time) - { - ControlPoint overridePoint; - ControlPoint timingPoint = TimingPointAt(time, out overridePoint); - - return overridePoint?.SpeedMultiplier ?? timingPoint?.SpeedMultiplier ?? 1; - } - - /// - /// Finds the beat length at a time. This is expressed in milliseconds. - /// - /// The time to find the beat length at. - /// The beat length. - public double BeatLengthAt(double time) - { - ControlPoint overridePoint; - ControlPoint timingPoint = TimingPointAt(time, out overridePoint); - - return timingPoint.BeatLength; - } - - /// - /// Finds the timing point at a time. - /// - /// The time to find the timing point at. - /// The timing point containing the velocity change of the returned timing point. - /// The timing point. - public ControlPoint TimingPointAt(double time, out ControlPoint overridePoint) - { - overridePoint = null; - - ControlPoint timingPoint = null; - foreach (var controlPoint in ControlPoints) - { - // Some beatmaps have the first timingPoint (accidentally) start after the first HitObject(s). - // This null check makes it so that the first ControlPoint that makes a timing change is used as - // the timingPoint for those HitObject(s). - if (controlPoint.Time <= time || timingPoint == null) - { - if (controlPoint.TimingChange) - { - timingPoint = controlPoint; - overridePoint = null; - } - else - overridePoint = controlPoint; - } - else break; - } - - return timingPoint ?? new ControlPoint(); - } - } -} \ No newline at end of file diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index dfb742f7d1..3489a1550b 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -2,9 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; +using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; namespace osu.Game.Graphics.Containers @@ -14,7 +16,8 @@ namespace osu.Game.Graphics.Containers private readonly Bindable beatmap = new Bindable(); private int lastBeat; - private ControlPoint lastControlPoint; + private TimingControlPoint lastTimingPoint; + private EffectControlPoint lastEffectPoint; protected override void Update() { @@ -22,29 +25,29 @@ namespace osu.Game.Graphics.Containers return; double currentTrackTime = beatmap.Value.Track.CurrentTime; - ControlPoint overridePoint; - ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(currentTrackTime, out overridePoint); - if (controlPoint.BeatLength == 0) + TimingControlPoint timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); + EffectControlPoint effectPoint = beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); + + if (timingPoint.BeatLength == 0) return; - bool kiai = (overridePoint ?? controlPoint).KiaiMode; - int beat = (int)((currentTrackTime - controlPoint.Time) / controlPoint.BeatLength); + int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength); // The beats before the start of the first control point are off by 1, this should do the trick - if (currentTrackTime < controlPoint.Time) - beat--; + if (currentTrackTime < timingPoint.Time) + beatIndex--; - if (controlPoint == lastControlPoint && beat == lastBeat) + if (timingPoint == lastTimingPoint && beatIndex == lastBeat) return; - double offsetFromBeat = (controlPoint.Time - currentTrackTime) % controlPoint.BeatLength; + double offsetFromBeat = (timingPoint.Time - currentTrackTime) % timingPoint.BeatLength; using (BeginDelayedSequence(offsetFromBeat, true)) - OnNewBeat(beat, controlPoint.BeatLength, controlPoint.TimeSignature, kiai); + OnNewBeat(beatIndex, timingPoint, effectPoint, beatmap.Value.Track.CurrentAmplitudes); - lastBeat = beat; - lastControlPoint = controlPoint; + lastBeat = beatIndex; + lastTimingPoint = timingPoint; } [BackgroundDependencyLoader] @@ -53,7 +56,7 @@ namespace osu.Game.Graphics.Containers beatmap.BindTo(game.Beatmap); } - protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai) + protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { } } diff --git a/osu.Game/Rulesets/Beatmaps/BeatmapConverter.cs b/osu.Game/Rulesets/Beatmaps/BeatmapConverter.cs index 5342686c3f..aafa576d4b 100644 --- a/osu.Game/Rulesets/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Rulesets/Beatmaps/BeatmapConverter.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Beatmaps return new Beatmap { BeatmapInfo = original.BeatmapInfo, - TimingInfo = original.TimingInfo, + ControlPointInfo = original.ControlPointInfo, HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList() }; } diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 46fb5fcf70..6028464a46 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Rulesets.Objects.Types; @@ -33,31 +34,28 @@ namespace osu.Game.Rulesets.Objects /// /// Applies default values to this HitObject. /// + /// The control points. /// The difficulty settings to use. - /// The timing settings to use. - public virtual void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + public virtual void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { - ControlPoint overridePoint; - ControlPoint timingPoint = timing.TimingPointAt(StartTime, out overridePoint); - - ControlPoint samplePoint = overridePoint ?? timingPoint; + SoundControlPoint soundPoint = controlPointInfo.SoundPointAt(StartTime); // Initialize first sample - Samples.ForEach(s => initializeSampleInfo(s, samplePoint)); + Samples.ForEach(s => initializeSampleInfo(s, soundPoint)); // Initialize any repeat samples var repeatData = this as IHasRepeats; - repeatData?.RepeatSamples?.ForEach(r => r.ForEach(s => initializeSampleInfo(s, samplePoint))); + repeatData?.RepeatSamples?.ForEach(r => r.ForEach(s => initializeSampleInfo(s, soundPoint))); } - private void initializeSampleInfo(SampleInfo sample, ControlPoint controlPoint) + private void initializeSampleInfo(SampleInfo sample, SoundControlPoint soundPoint) { if (sample.Volume == 0) - sample.Volume = controlPoint?.SampleVolume ?? 0; + sample.Volume = soundPoint?.SampleVolume ?? 0; // If the bank is not assigned a name, assign it from the control point if (string.IsNullOrEmpty(sample.Bank)) - sample.Bank = controlPoint?.SampleBank ?? @"normal"; + sample.Bank = soundPoint?.SampleBank ?? @"normal"; } } } diff --git a/osu.Game/Rulesets/UI/HitRenderer.cs b/osu.Game/Rulesets/UI/HitRenderer.cs index d0cce87e43..f0dc143668 100644 --- a/osu.Game/Rulesets/UI/HitRenderer.cs +++ b/osu.Game/Rulesets/UI/HitRenderer.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.UI // Apply defaults foreach (var h in Beatmap.HitObjects) - h.ApplyDefaults(Beatmap.TimingInfo, Beatmap.BeatmapInfo.Difficulty); + h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty); // Post-process the beatmap processor.PostProcess(Beatmap); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 3efc85d743..a39e7dbab2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -123,7 +123,7 @@ namespace osu.Game.Screens.Play decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; var firstObjectTime = HitRenderer.Objects.First().StartTime; - decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Beatmap.TimingInfo.BeatLengthAt(firstObjectTime) * 4, Beatmap.BeatmapInfo.AudioLeadIn))); + decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, Beatmap.BeatmapInfo.AudioLeadIn))); decoupledClock.ProcessFrame(); offsetClock = new FramedOffsetClock(decoupledClock); diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 2f5b35f92a..2d002597ab 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -238,12 +238,12 @@ namespace osu.Game.Screens.Select private string getBPMRange(Beatmap beatmap) { - double bpmMax = beatmap.TimingInfo.BPMMaximum; - double bpmMin = beatmap.TimingInfo.BPMMinimum; + double bpmMax = beatmap.ControlPointInfo.BPMMaximum; + double bpmMin = beatmap.ControlPointInfo.BPMMinimum; if (Precision.AlmostEquals(bpmMin, bpmMax)) return Math.Round(bpmMin) + "bpm"; - return Math.Round(bpmMin) + "-" + Math.Round(bpmMax) + "bpm (mostly " + Math.Round(beatmap.TimingInfo.BPMMode) + "bpm)"; + return Math.Round(bpmMin) + "-" + Math.Round(bpmMax) + "bpm (mostly " + Math.Round(beatmap.ControlPointInfo.BPMMode) + "bpm)"; } public class InfoLabel : Container diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6946462e81..cde7a51ef4 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -87,10 +87,15 @@ + + + + + + - @@ -202,7 +207,6 @@ - From ea4a28532960466c4e8e3c05c80df002275f0ec5 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 14:08:15 +0900 Subject: [PATCH 42/58] Fix compile errors. --- .../Patterns/Legacy/HitObjectPatternGenerator.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index d044ee8893..4547c3d4a3 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -6,6 +6,7 @@ using System.Linq; using OpenTK; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Objects; @@ -25,17 +26,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { StairType = lastStair; - ControlPoint overridePoint; - ControlPoint controlPoint = beatmap.TimingInfo.TimingPointAt(hitObject.StartTime, out overridePoint); + TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); + EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime); var positionData = hitObject as IHasPosition; float positionSeparation = ((positionData?.Position ?? Vector2.Zero) - previousPosition).Length; double timeSeparation = hitObject.StartTime - previousTime; - double beatLength = controlPoint.BeatLength; - bool kiai = (overridePoint ?? controlPoint).KiaiMode; - if (timeSeparation <= 125) { // More than 120 BPM @@ -72,12 +70,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy // More than 100 BPM stream convertType |= PatternType.ForceStack | PatternType.LowProbability; } - else if (positionSeparation < 20 && density >= beatLength / 2.5) + else if (positionSeparation < 20 && density >= timingPoint.BeatLength / 2.5) { // Low density stream convertType |= PatternType.Reverse | PatternType.LowProbability; } - else if (density < beatLength / 2.5 || kiai) + else if (density < timingPoint.BeatLength / 2.5 || effectPoint.KiaiMode) { // High density } From 6bfd7e0fb0b44a0cd9660211b063416a06a38d9c Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 14:11:37 +0900 Subject: [PATCH 43/58] xmldocs. --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 3 +++ osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs | 10 ++++++++++ osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs | 10 ++++++++++ osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs | 10 ++++++++++ 4 files changed, 33 insertions(+) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 5c31259de1..5707caa235 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -5,6 +5,9 @@ namespace osu.Game.Beatmaps.ControlPoints { public class ControlPoint { + /// + /// The time at which the control point takes effect. + /// public double Time; } } diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index 2f6003f57c..7671739cd9 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -1,8 +1,18 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + namespace osu.Game.Beatmaps.ControlPoints { public class EffectControlPoint : ControlPoint { + /// + /// Whether this control point enables Kiai mode. + /// public bool KiaiMode; + + /// + /// Whether the first bar line of this control point is ignored. + /// public bool OmitFirstBarLine; } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs index 696e684a3d..8084229382 100644 --- a/osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs @@ -1,8 +1,18 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + namespace osu.Game.Beatmaps.ControlPoints { public class SoundControlPoint : ControlPoint { + /// + /// The default sample bank at this control point. + /// public string SampleBank; + + /// + /// The default sample volume at this control point. + /// public int SampleVolume; } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 57c21f110b..6f7e38c163 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -1,10 +1,20 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using osu.Game.Beatmaps.Timing; namespace osu.Game.Beatmaps.ControlPoints { public class TimingControlPoint : ControlPoint { + /// + /// The time signature at this control point. + /// public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple; + + /// + /// The beat length at this control point. + /// public double BeatLength = 500; } } \ No newline at end of file From 002a0e99a2ca7452b815df396e6b9705640f7d34 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 15:20:32 +0900 Subject: [PATCH 44/58] Use SortedList + BinarySearch to find control points at time values. --- .../Tests/TestCaseGamefield.cs | 2 +- .../UI/ManiaHitRenderer.cs | 13 +++-- .../UI/TaikoHitRenderer.cs | 2 +- .../Beatmaps/ControlPoints/ControlPoint.cs | 6 ++- .../ControlPoints/ControlPointInfo.cs | 49 +++++++++++++------ osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 8 +-- 6 files changed, 54 insertions(+), 26 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs index fb5bd8cde4..a44780d673 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs @@ -55,7 +55,7 @@ namespace osu.Desktop.VisualTests.Tests } var controlPointInfo = new ControlPointInfo(); - controlPointInfo.ControlPoints.Add(new TimingControlPoint + controlPointInfo.TimingPoints.Add(new TimingControlPoint { BeatLength = 200 }); diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index 8d5e22933c..8b3623cbab 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -2,11 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.Linq; using OpenTK; using OpenTK.Input; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Lists; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; @@ -38,11 +40,14 @@ namespace osu.Game.Rulesets.Mania.UI double lastSpeedMultiplier = 1; double lastBeatLength = 500; - // Generate the timing points, making non-timing changes use the previous timing change - var timingChanges = Beatmap.ControlPointInfo.ControlPoints.Where(c => c is TimingControlPoint || c is DifficultyControlPoint).Select(c => - { - var change = new TimingChange(); + // Merge timing + difficulty points + var allPoints = new SortedList(Comparer.Default); + allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints); + allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints); + // Generate the timing points, making non-timing changes use the previous timing change + var timingChanges = allPoints.Select(c => + { var timingPoint = c as TimingControlPoint; var difficultyPoint = c as DifficultyControlPoint; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs index 7b0a0b085d..f4d411e15f 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Taiko.UI TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1]; double lastHitTime = 1 + (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime; - var timingPoints = Beatmap.ControlPointInfo.ControlPoints.OfType().ToList(); + var timingPoints = Beatmap.ControlPointInfo.TimingPoints.ToList(); if (timingPoints.Count == 0) return; diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 5707caa235..0d1dc21e96 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -1,13 +1,17 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; + namespace osu.Game.Beatmaps.ControlPoints { - public class ControlPoint + public class ControlPoint : IComparable { /// /// The time at which the control point takes effect. /// public double Time; + + public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time); } } diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 90c50a3e05..84b6dcfc20 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -1,63 +1,82 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Lists; namespace osu.Game.Beatmaps.ControlPoints { public class ControlPointInfo { - /// - /// All the control points. - /// - public readonly List ControlPoints = new List(); + public readonly SortedList TimingPoints = new SortedList(Comparer.Default); + public readonly SortedList DifficultyPoints = new SortedList(Comparer.Default); + public readonly SortedList SoundPoints = new SortedList(Comparer.Default); + public readonly SortedList EffectPoints = new SortedList(Comparer.Default); /// /// Finds the difficulty control point that is active at . /// /// The time to find the difficulty control point at. /// The difficulty control point. - public DifficultyControlPoint DifficultyPointAt(double time) => - ControlPoints.OfType().LastOrDefault(t => t.Time <= time) ?? new DifficultyControlPoint(); + public DifficultyControlPoint DifficultyPointAt(double time) => binarySearch(DifficultyPoints, time); /// /// Finds the effect control point that is active at . /// /// The time to find the effect control point at. /// The effect control point. - public EffectControlPoint EffectPointAt(double time) => - ControlPoints.OfType().LastOrDefault(t => t.Time <= time) ?? new EffectControlPoint(); + public EffectControlPoint EffectPointAt(double time) => binarySearch(EffectPoints, time); /// /// Finds the sound control point that is active at . /// /// The time to find the sound control point at. /// The sound control point. - public SoundControlPoint SoundPointAt(double time) => - ControlPoints.OfType().LastOrDefault(t => t.Time <= time) ?? new SoundControlPoint(); + public SoundControlPoint SoundPointAt(double time) => binarySearch(SoundPoints, time); /// /// Finds the timing control point that is active at . /// /// The time to find the timing control point at. /// The timing control point. - public TimingControlPoint TimingPointAt(double time) => - ControlPoints.OfType().LastOrDefault(t => t.Time <= time) ?? new TimingControlPoint(); + public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time); /// /// Finds the maximum BPM represented by any timing control point. /// public double BPMMaximum => - 60000 / (ControlPoints.OfType().OrderBy(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + 60000 / (TimingPoints.OrderBy(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; /// /// Finds the minimum BPM represented by any timing control point. /// public double BPMMinimum => - 60000 / (ControlPoints.OfType().OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + 60000 / (TimingPoints.OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; /// /// Finds the mode BPM (most common BPM) represented by the control points. /// public double BPMMode => - 60000 / (ControlPoints.OfType().GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).FirstOrDefault()?.FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + 60000 / (TimingPoints.GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).FirstOrDefault()?.FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + + private T binarySearch(SortedList list, double time) + where T : ControlPoint, new() + { + if (list.Count == 0) + return new T(); + + if (time < list[0].Time) + return new T(); + + int index = list.BinarySearch(new T() { Time = time }); + + // Check if we've found an exact match (t == time) + if (index >= 0) + return list[index]; + + index = ~index; + + if (index == list.Count) + return list[list.Count - 1]; + return list[index - 1]; + } } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index e140e2282c..875efab5f9 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -285,7 +285,7 @@ namespace osu.Game.Beatmaps.Formats if (timingChange && (beatLength != timingPoint.BeatLength || timeSignature != timingPoint.TimeSignature)) { - beatmap.ControlPointInfo.ControlPoints.Add(new TimingControlPoint + beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { Time = time, BeatLength = beatLength, @@ -295,7 +295,7 @@ namespace osu.Game.Beatmaps.Formats if (speedMultiplier != difficultyPoint.SpeedMultiplier) { - beatmap.ControlPointInfo.ControlPoints.Add(new DifficultyControlPoint + beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint { Time = time, SpeedMultiplier = speedMultiplier @@ -304,7 +304,7 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) { - beatmap.ControlPointInfo.ControlPoints.Add(new SoundControlPoint + beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint { Time = time, SampleBank = stringSampleSet, @@ -314,7 +314,7 @@ namespace osu.Game.Beatmaps.Formats if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) { - beatmap.ControlPointInfo.ControlPoints.Add(new EffectControlPoint + beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = time, KiaiMode = kiaiMode, From 2344f37a3c337f0b6d2f071c7c6eadc38f05e766 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 15:26:07 +0900 Subject: [PATCH 45/58] Always add TimingChange control points. --- osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 875efab5f9..448b6b6b98 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -278,12 +278,11 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet == @"none") stringSampleSet = @"normal"; - TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(time); DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); - if (timingChange && (beatLength != timingPoint.BeatLength || timeSignature != timingPoint.TimeSignature)) + if (timingChange) { beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { From 836bf930a0be6e473578d0ead265b3f4f0cfe0a1 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 15:29:38 +0900 Subject: [PATCH 46/58] More cleanups. --- .../Tests/TestCaseGamefield.cs | 1 - .../Tests/TestCaseManiaPlayfield.cs | 2 -- .../Legacy/DistanceObjectPatternGenerator.cs | 1 - .../Legacy/HitObjectPatternGenerator.cs | 1 - osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 1 - osu.Game.Rulesets.Mania/Objects/Note.cs | 1 - .../Timing/ControlPointContainer.cs | 1 - osu.Game.Rulesets.Mania/UI/Column.cs | 2 -- osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs | 1 - osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 2 -- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 1 - osu.Game.Rulesets.Osu/Objects/Slider.cs | 1 - osu.Game.Rulesets.Osu/Objects/Spinner.cs | 1 - osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 1 - osu.Game.Rulesets.Taiko/Objects/Hit.cs | 1 - .../Objects/TaikoHitObject.cs | 1 - osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs | 1 - .../Beatmaps/ControlPoints/ControlPointInfo.cs | 18 ++++++++++++++++++ .../ControlPoints/DifficultyControlPoint.cs | 6 ++++++ osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 1 - .../Graphics/Containers/BeatSyncedContainer.cs | 1 - osu.Game/Rulesets/Objects/HitObject.cs | 1 - 22 files changed, 24 insertions(+), 23 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs index a44780d673..e2cd2bf67b 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs @@ -18,7 +18,6 @@ using osu.Game.Rulesets.Taiko.UI; using System.Collections.Generic; using osu.Desktop.VisualTests.Beatmaps; using osu.Framework.Allocation; -using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.ControlPoints; namespace osu.Desktop.VisualTests.Tests diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs index 88f90fc333..ec50f19f98 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs @@ -7,11 +7,9 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.UI; using System; using System.Collections.Generic; -using osu.Game.Beatmaps.Timing; using OpenTK; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Mania.Timing; namespace osu.Desktop.VisualTests.Tests diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 1209a5c879..718e0967da 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -5,7 +5,6 @@ using System; using System.Linq; using osu.Game.Audio; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 4547c3d4a3..b1ba99d98b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -7,7 +7,6 @@ using OpenTK; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index b0a0ed10b8..30e71aeb5d 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -3,7 +3,6 @@ using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Objects.Types; diff --git a/osu.Game.Rulesets.Mania/Objects/Note.cs b/osu.Game.Rulesets.Mania/Objects/Note.cs index e91c00c145..6c0cacd277 100644 --- a/osu.Game.Rulesets.Mania/Objects/Note.cs +++ b/osu.Game.Rulesets.Mania/Objects/Note.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Rulesets.Mania.Judgements; diff --git a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs b/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs index 38e975f468..6d390464fe 100644 --- a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs +++ b/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs @@ -7,7 +7,6 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using OpenTK; -using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Mania.Timing diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 12c4cea58c..c8cb5f6387 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -17,8 +17,6 @@ using System.Collections.Generic; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Beatmaps.Timing; -using osu.Game.Beatmaps.ControlPoints; using System; using osu.Framework.Configuration; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index 8b3623cbab..95b7979e43 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics; using osu.Framework.Lists; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Judgements; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index c0915c5d82..ff763f87c4 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -19,10 +19,8 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Mania.Timing; using osu.Framework.Input; -using osu.Game.Beatmaps.Timing; using osu.Framework.Graphics.Transforms; using osu.Framework.MathUtils; -using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Mania.UI { diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index c4b4e3fc4a..e6fd82e6c8 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -6,7 +6,6 @@ using OpenTK; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; -using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Beatmaps.ControlPoints; diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 1a5065d17d..3b44e38d5e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK; -using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Types; using System; using System.Collections.Generic; diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 8c0a218108..eff60ba935 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Rulesets.Objects.Types; -using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Beatmaps.ControlPoints; diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 857dd8d3a5..18e3016fc3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -5,7 +5,6 @@ using osu.Game.Rulesets.Objects.Types; using System; using System.Collections.Generic; using System.Linq; -using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index cc15b5ab98..f31472d0fd 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Timing; using osu.Game.Database; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index a294a13521..4f87467706 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.UI; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs index f4d411e15f..662cace511 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs @@ -18,7 +18,6 @@ using osu.Game.Rulesets.Taiko.Replays; using OpenTK; using osu.Game.Rulesets.Beatmaps; using System.Linq; -using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Taiko.UI { diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 84b6dcfc20..5740c961b1 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using System.Collections.Generic; using System.Linq; using osu.Framework.Lists; @@ -6,9 +9,24 @@ namespace osu.Game.Beatmaps.ControlPoints { public class ControlPointInfo { + /// + /// All timing points. + /// public readonly SortedList TimingPoints = new SortedList(Comparer.Default); + + /// + /// All difficulty points. + /// public readonly SortedList DifficultyPoints = new SortedList(Comparer.Default); + + /// + /// All sound points. + /// public readonly SortedList SoundPoints = new SortedList(Comparer.Default); + + /// + /// All effect points. + /// public readonly SortedList EffectPoints = new SortedList(Comparer.Default); /// diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index abd9d05971..30c7884334 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -1,7 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + namespace osu.Game.Beatmaps.ControlPoints { public class DifficultyControlPoint : ControlPoint { + /// + /// The speed multiplier at this control point. + /// public double SpeedMultiplier = 1; } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 448b6b6b98..cb1d9d2fcd 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -8,7 +8,6 @@ using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Legacy; using osu.Game.Rulesets.Objects.Legacy; -using System.Linq; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Beatmaps.Formats diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 3489a1550b..429a794a03 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -7,7 +7,6 @@ using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Timing; namespace osu.Game.Graphics.Containers { diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 6028464a46..5592681cab 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -3,7 +3,6 @@ using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Rulesets.Objects.Types; From 1867cbb3817439813c8d33c1e5680b8175d86026 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 16:05:58 +0900 Subject: [PATCH 47/58] Revert a bit of BeatSyncedContainer for smaller changeset. --- osu-framework | 2 +- .../Graphics/Containers/BeatSyncedContainer.cs | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/osu-framework b/osu-framework index 773d60eb6b..c6f030d6f1 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 773d60eb6b811f395e32a22dc66bb4d2e63a6dbc +Subproject commit c6f030d6f1ab65a48de9ff1d0c424acb686e0149 diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 429a794a03..5895eabde4 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -2,11 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; namespace osu.Game.Graphics.Containers { @@ -16,7 +16,6 @@ namespace osu.Game.Graphics.Containers private int lastBeat; private TimingControlPoint lastTimingPoint; - private EffectControlPoint lastEffectPoint; protected override void Update() { @@ -31,21 +30,21 @@ namespace osu.Game.Graphics.Containers if (timingPoint.BeatLength == 0) return; - int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength); + int beat = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength); // The beats before the start of the first control point are off by 1, this should do the trick if (currentTrackTime < timingPoint.Time) - beatIndex--; + beat--; - if (timingPoint == lastTimingPoint && beatIndex == lastBeat) + if (timingPoint == lastTimingPoint && beat == lastBeat) return; double offsetFromBeat = (timingPoint.Time - currentTrackTime) % timingPoint.BeatLength; using (BeginDelayedSequence(offsetFromBeat, true)) - OnNewBeat(beatIndex, timingPoint, effectPoint, beatmap.Value.Track.CurrentAmplitudes); + OnNewBeat(beat, timingPoint.BeatLength, timingPoint.TimeSignature, effectPoint.KiaiMode); - lastBeat = beatIndex; + lastBeat = beat; lastTimingPoint = timingPoint; } @@ -55,7 +54,7 @@ namespace osu.Game.Graphics.Containers beatmap.BindTo(game.Beatmap); } - protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai) { } } From aad6f8f5d61f05f752f06932643ce186099db155 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 16:11:46 +0900 Subject: [PATCH 48/58] Refactoring of BeatSyncedContainer. --- osu-framework | 2 +- .../Graphics/Containers/BeatSyncedContainer.cs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu-framework b/osu-framework index c6f030d6f1..773d60eb6b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit c6f030d6f1ab65a48de9ff1d0c424acb686e0149 +Subproject commit 773d60eb6b811f395e32a22dc66bb4d2e63a6dbc diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 5895eabde4..3d08431bea 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -2,11 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; +using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Timing; namespace osu.Game.Graphics.Containers { @@ -30,21 +30,21 @@ namespace osu.Game.Graphics.Containers if (timingPoint.BeatLength == 0) return; - int beat = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength); + int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength); // The beats before the start of the first control point are off by 1, this should do the trick if (currentTrackTime < timingPoint.Time) - beat--; + beatIndex--; - if (timingPoint == lastTimingPoint && beat == lastBeat) + if (timingPoint == lastTimingPoint && beatIndex == lastBeat) return; double offsetFromBeat = (timingPoint.Time - currentTrackTime) % timingPoint.BeatLength; using (BeginDelayedSequence(offsetFromBeat, true)) - OnNewBeat(beat, timingPoint.BeatLength, timingPoint.TimeSignature, effectPoint.KiaiMode); + OnNewBeat(beatIndex, timingPoint, effectPoint, beatmap.Value.Track.CurrentAmplitudes); - lastBeat = beat; + lastBeat = beatIndex; lastTimingPoint = timingPoint; } @@ -54,7 +54,7 @@ namespace osu.Game.Graphics.Containers beatmap.BindTo(game.Beatmap); } - protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai) + protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { } } From 7e5bb61a44daf7ce3e57de1bf5d476830b560d52 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 16:13:51 +0900 Subject: [PATCH 49/58] Fix line endings. --- .../Timing/TimingChange.cs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Timing/TimingChange.cs b/osu.Game.Rulesets.Mania/Timing/TimingChange.cs index fb6f1d2db1..9153ba6991 100644 --- a/osu.Game.Rulesets.Mania/Timing/TimingChange.cs +++ b/osu.Game.Rulesets.Mania/Timing/TimingChange.cs @@ -1,23 +1,23 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Timing -{ - public class TimingChange - { - /// - /// The time at which this timing change happened. - /// - public double Time; - - /// - /// The beat length. - /// - public double BeatLength = 500; - - /// - /// The speed multiplier. - /// - public double SpeedMultiplier = 1; - } +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Timing +{ + public class TimingChange + { + /// + /// The time at which this timing change happened. + /// + public double Time; + + /// + /// The beat length. + /// + public double BeatLength = 500; + + /// + /// The speed multiplier. + /// + public double SpeedMultiplier = 1; + } } \ No newline at end of file From fe7ac20e292cbd014a1d6d15a41f3756da83c008 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 May 2017 16:26:51 +0900 Subject: [PATCH 50/58] Read menu music from osz resource --- osu-resources | 2 +- .../Beatmaps/IO/LegacyFilesystemReader.cs | 2 + osu.Game/Beatmaps/IO/ArchiveReader.cs | 2 + osu.Game/Beatmaps/IO/OszArchiveReader.cs | 2 + osu.Game/Database/BeatmapDatabase.cs | 113 +++++++++--------- osu.Game/OsuGame.cs | 2 +- osu.Game/Screens/Menu/Intro.cs | 49 +++++++- osu.Game/Screens/Menu/MainMenu.cs | 32 +---- 8 files changed, 111 insertions(+), 93 deletions(-) diff --git a/osu-resources b/osu-resources index ffccbeb98d..9f46a456dc 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit ffccbeb98dc9e8f0965520270b5885e63f244c83 +Subproject commit 9f46a456dc3a56dcbff09671a3f588b16a464106 diff --git a/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs b/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs index 8c896646bf..8772fc9f28 100644 --- a/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs +++ b/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs @@ -37,5 +37,7 @@ namespace osu.Desktop.Beatmaps.IO { // no-op } + + public override Stream GetUnderlyingStream() => null; } } diff --git a/osu.Game/Beatmaps/IO/ArchiveReader.cs b/osu.Game/Beatmaps/IO/ArchiveReader.cs index 9d7d67007e..7ff5668b6f 100644 --- a/osu.Game/Beatmaps/IO/ArchiveReader.cs +++ b/osu.Game/Beatmaps/IO/ArchiveReader.cs @@ -63,5 +63,7 @@ namespace osu.Game.Beatmaps.IO return buffer; } } + + public abstract Stream GetUnderlyingStream(); } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/IO/OszArchiveReader.cs b/osu.Game/Beatmaps/IO/OszArchiveReader.cs index 6c550def8d..eb9e740779 100644 --- a/osu.Game/Beatmaps/IO/OszArchiveReader.cs +++ b/osu.Game/Beatmaps/IO/OszArchiveReader.cs @@ -49,5 +49,7 @@ namespace osu.Game.Beatmaps.IO archive.Dispose(); archiveStream.Dispose(); } + + public override Stream GetUnderlyingStream() => archiveStream; } } \ No newline at end of file diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index bd25ebe8a9..efd5631077 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -12,6 +12,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.IO; using osu.Game.IPC; +using osu.Game.Screens.Menu; using SQLite.Net; using SQLiteNetExtensions.Extensions; @@ -38,6 +39,10 @@ namespace osu.Game.Database { foreach (var b in GetAllWithChildren(b => b.DeletePending)) { + if (b.Hash == Intro.MENU_MUSIC_BEATMAP_HASH) + // this is a bit hacky, but will do for now. + continue; + try { Storage.Delete(b.Path); @@ -97,50 +102,49 @@ namespace osu.Game.Database typeof(BeatmapDifficulty), }; + public void Import(string path) + { + try + { + Import(ArchiveReader.GetReader(Storage, path)); + + // We may or may not want to delete the file depending on where it is stored. + // e.g. reconstructing/repairing database with beatmaps from default storage. + // Also, not always a single file, i.e. for LegacyFilesystemReader + // TODO: Add a check to prevent files from storage to be deleted. + try + { + File.Delete(path); + } + catch (Exception e) + { + Logger.Error(e, $@"Could not delete file at {path}"); + } + } + catch (Exception e) + { + e = e.InnerException ?? e; + Logger.Error(e, @"Could not import beatmap set"); + } + } + + public void Import(ArchiveReader archiveReader) + { + BeatmapSetInfo set = getBeatmapSet(archiveReader); + + //If we have an ID then we already exist in the database. + if (set.ID == 0) + Import(new[] { set }); + } + /// /// Import multiple from . /// /// Multiple locations on disk - public void Import(IEnumerable paths) + public void Import(params string[] paths) { foreach (string p in paths) - { - try - { - BeatmapSetInfo set = getBeatmapSet(p); - - //If we have an ID then we already exist in the database. - if (set.ID == 0) - Import(new[] { set }); - - // We may or may not want to delete the file depending on where it is stored. - // e.g. reconstructing/repairing database with beatmaps from default storage. - // Also, not always a single file, i.e. for LegacyFilesystemReader - // TODO: Add a check to prevent files from storage to be deleted. - try - { - File.Delete(p); - } - catch (Exception e) - { - Logger.Error(e, $@"Could not delete file at {p}"); - } - } - catch (Exception e) - { - e = e.InnerException ?? e; - Logger.Error(e, @"Could not import beatmap set"); - } - } - } - - /// - /// Import from . - /// - /// Location on disk - public void Import(string path) - { - Import(new[] { path }); + Import(p); } /// @@ -148,29 +152,26 @@ namespace osu.Game.Database /// /// Content location /// - private BeatmapSetInfo getBeatmapSet(string path) - { - string hash = null; + private BeatmapSetInfo getBeatmapSet(string path) => getBeatmapSet(ArchiveReader.GetReader(Storage, path)); + private BeatmapSetInfo getBeatmapSet(ArchiveReader archiveReader) + { BeatmapMetadata metadata; - using (var reader = ArchiveReader.GetReader(Storage, path)) - { - using (var stream = new StreamReader(reader.GetStream(reader.BeatmapFilenames[0]))) - metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; - } + using (var stream = new StreamReader(archiveReader.GetStream(archiveReader.BeatmapFilenames[0]))) + metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; - if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader + string hash; + string path; + + using (var input = archiveReader.GetUnderlyingStream()) { - using (var input = Storage.GetStream(path)) - { - hash = input.GetMd5Hash(); - input.Seek(0, SeekOrigin.Begin); - path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); - if (!Storage.Exists(path)) - using (var output = Storage.GetStream(path, FileAccess.Write)) - input.CopyTo(output); - } + hash = input.GetMd5Hash(); + input.Seek(0, SeekOrigin.Begin); + path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); + if (!Storage.Exists(path)) + using (var output = Storage.GetStream(path, FileAccess.Write)) + input.CopyTo(output); } var existing = Connection.Table().FirstOrDefault(b => b.Hash == hash); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4cf436a8e4..f13043a745 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -82,7 +82,7 @@ namespace osu.Game if (args?.Length > 0) { var paths = args.Where(a => !a.StartsWith(@"-")); - Task.Run(() => BeatmapDatabase.Import(paths)); + Task.Run(() => BeatmapDatabase.Import(paths.ToArray())); } Dependencies.Cache(this); diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index 01659edd72..5791b7f196 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -8,7 +8,10 @@ using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Screens; using osu.Framework.Graphics; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps.IO; using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Graphics.Containers; using osu.Game.Screens.Backgrounds; using OpenTK.Graphics; @@ -19,6 +22,8 @@ namespace osu.Game.Screens.Menu { private readonly OsuLogo logo; + public const string MENU_MUSIC_BEATMAP_HASH = "21c1271b91234385978b5418881fdd88"; + /// /// Whether we have loaded the menu previously. /// @@ -27,7 +32,6 @@ namespace osu.Game.Screens.Menu private MainMenu mainMenu; private SampleChannel welcome; private SampleChannel seeya; - private Track bgm; internal override bool HasLocalCursorDisplayed => true; @@ -60,15 +64,49 @@ namespace osu.Game.Screens.Menu private Bindable menuVoice; private Bindable menuMusic; + private Track track; [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuConfigManager config) + private void load(AudioManager audio, OsuConfigManager config, BeatmapDatabase beatmaps, Framework.Game game) { menuVoice = config.GetBindable(OsuSetting.MenuVoice); menuMusic = config.GetBindable(OsuSetting.MenuMusic); - bgm = audio.Track.Get(@"circles"); - bgm.Looping = true; + var trackManager = audio.Track; + + BeatmapSetInfo setInfo = null; + + if (!menuMusic) + { + var query = beatmaps.Query().Where(b => !b.DeletePending); + int count = query.Count(); + if (count > 0) + setInfo = query.ElementAt(RNG.Next(0, count - 1)); + } + + if (setInfo == null) + { + var query = beatmaps.Query().Where(b => b.Hash == MENU_MUSIC_BEATMAP_HASH); + + setInfo = query.FirstOrDefault(); + + if (setInfo == null) + { + // we need to import the default menu background beatmap + beatmaps.Import(new OszArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"))); + + setInfo = query.First(); + + setInfo.DeletePending = true; + beatmaps.Update(setInfo, false); + } + } + + beatmaps.GetChildren(setInfo); + Beatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); + + track = Beatmap.Track; + trackManager.SetExclusive(track); welcome = audio.Sample.Get(@"welcome"); seeya = audio.Sample.Get(@"seeya"); @@ -83,8 +121,7 @@ namespace osu.Game.Screens.Menu Scheduler.AddDelayed(delegate { - if (menuMusic) - bgm.Start(); + track.Start(); LoadComponentAsync(mainMenu = new MainMenu()); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 71d020b0f2..b74ee52f9c 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -1,18 +1,12 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Threading.Tasks; using OpenTK; using OpenTK.Input; using osu.Framework.Allocation; -using osu.Framework.Audio.Track; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Input; -using osu.Framework.MathUtils; using osu.Framework.Screens; -using osu.Game.Configuration; -using osu.Game.Database; using osu.Game.Graphics.Containers; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Charts; @@ -60,30 +54,11 @@ namespace osu.Game.Screens.Menu }; } - private Bindable menuMusic; - private TrackManager trackManager; - [BackgroundDependencyLoader] - private void load(OsuGame game, OsuConfigManager config, BeatmapDatabase beatmaps) + private void load(OsuGame game) { - menuMusic = config.GetBindable(OsuSetting.MenuMusic); LoadComponentAsync(background); - if (!menuMusic) - { - trackManager = game.Audio.Track; - - var query = beatmaps.Query().Where(b => !b.DeletePending); - int count = query.Count(); - - if (count > 0) - { - var beatmap = query.ElementAt(RNG.Next(0, count - 1)); - beatmaps.GetChildren(beatmap); - Beatmap = beatmaps.GetWorkingBeatmap(beatmap.Beatmaps[0]); - } - } - buttons.OnSettings = game.ToggleSettings; preloadSongSelect(); @@ -108,14 +83,13 @@ namespace osu.Game.Screens.Menu buttons.FadeInFromZero(500); if (last is Intro && Beatmap != null) { - Task.Run(() => + if (!Beatmap.Track.IsRunning) { - trackManager.SetExclusive(Beatmap.Track); Beatmap.Track.Seek(Beatmap.Metadata.PreviewTime); if (Beatmap.Metadata.PreviewTime == -1) Beatmap.Track.Seek(Beatmap.Track.Length * 0.4f); Beatmap.Track.Start(); - }); + } } } From a4823bca91514e34b39ebd5b62e50fd096ae7980 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 16:47:47 +0900 Subject: [PATCH 51/58] CI fixes. --- osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs b/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs index 6d390464fe..cc8897840e 100644 --- a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs +++ b/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mania.Timing /// public double TimeSpan { get; set; } - private readonly List drawableControlPoints = new List(); + private readonly List drawableControlPoints; public ControlPointContainer(IEnumerable timingChanges) { diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 4f87467706..82ceaeed1a 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -31,12 +31,12 @@ namespace osu.Game.Rulesets.Taiko.Objects public const float DEFAULT_STRONG_CIRCLE_DIAMETER = DEFAULT_CIRCLE_DIAMETER * STRONG_CIRCLE_DIAMETER_SCALE; /// - /// The time taken from the initial (off-screen) spawn position to the centre of the hit target for a of 1000ms. + /// The time taken from the initial (off-screen) spawn position to the centre of the hit target for a of 1000ms. /// private const double scroll_time = 6000; /// - /// Our adjusted taking into consideration local and other speed multipliers. + /// Our adjusted taking into consideration local and other speed multipliers. /// public double ScrollTime; From eafe215169b6ba92b813e280d2681e76b7d1b723 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 23 May 2017 11:53:12 +0300 Subject: [PATCH 52/58] Simplify Hud visibility change --- osu.Game/Screens/Play/HUDOverlay.cs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 1e57c2ba2a..34522d74ef 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -30,7 +30,6 @@ namespace osu.Game.Screens.Play public readonly SongProgress Progress; public readonly ModDisplay ModDisplay; - private Bindable showKeyCounter; private Bindable showHud; private static bool hasShownNotificationOnce; @@ -46,6 +45,7 @@ namespace osu.Game.Screens.Play protected HUDOverlay() { RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; Add(content = new Container { @@ -67,24 +67,8 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader(true)] private void load(OsuConfigManager config, NotificationManager notificationManager) { - showKeyCounter = config.GetBindable(OsuSetting.KeyOverlay); - showKeyCounter.ValueChanged += keyCounterVisibility => - { - if (keyCounterVisibility) - KeyCounter.FadeIn(duration); - else - KeyCounter.FadeOut(duration); - }; - showKeyCounter.TriggerChange(); - showHud = config.GetBindable(OsuSetting.ShowInterface); - showHud.ValueChanged += hudVisibility => - { - if (hudVisibility) - content.FadeIn(duration); - else - content.FadeOut(duration); - }; + showHud.ValueChanged += hudVisibility => FadeTo(hudVisibility ? 1 : 0, duration); showHud.TriggerChange(); if (!showHud && !hasShownNotificationOnce) From 4b23cc47eae4d81a2a2a6d4072d5498b9de836ed Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 23 May 2017 11:53:42 +0300 Subject: [PATCH 53/58] Moved KeyCounter visibility logic to it's own class --- osu.Game/Screens/Play/KeyCounterCollection.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Screens/Play/KeyCounterCollection.cs b/osu.Game/Screens/Play/KeyCounterCollection.cs index 4a561e6036..e53a1dcd99 100644 --- a/osu.Game/Screens/Play/KeyCounterCollection.cs +++ b/osu.Game/Screens/Play/KeyCounterCollection.cs @@ -6,14 +6,22 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using OpenTK.Graphics; using osu.Framework.Input; +using osu.Framework.Configuration; +using osu.Framework.Allocation; +using osu.Game.Configuration; namespace osu.Game.Screens.Play { public class KeyCounterCollection : FillFlowContainer { + private const int duration = 100; + + private Bindable showKeyCounter; + public KeyCounterCollection() { AlwaysReceiveInput = true; + AlwaysPresent = true; Direction = FillDirection.Horizontal; AutoSizeAxes = Axes.Both; @@ -34,6 +42,14 @@ namespace osu.Game.Screens.Play counter.ResetCount(); } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + showKeyCounter = config.GetBindable(OsuSetting.KeyOverlay); + showKeyCounter.ValueChanged += keyCounterVisibility => FadeTo(keyCounterVisibility ? 1 : 0, duration); + showKeyCounter.TriggerChange(); + } + //further: change default values here and in KeyCounter if needed, instead of passing them in every constructor private bool isCounting; public bool IsCounting From 76fe4af9a6c68650ae3ebdbdeb768b08d424638e Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 23 May 2017 18:06:31 +0900 Subject: [PATCH 54/58] Fix visual test failing. --- osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs index ec50f19f98..9dcba02849 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs @@ -41,7 +41,7 @@ namespace osu.Desktop.VisualTests.Tests Clear(); ManiaPlayfield playField; - Add(playField = new ManiaPlayfield(cols, new List()) + Add(playField = new ManiaPlayfield(cols, new List { new TimingChange { BeatLength = 200 } }) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From c7a241246ecb912897aded1faf75203589f66614 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 23 May 2017 12:09:32 +0300 Subject: [PATCH 55/58] Better letterbox settings transition --- .../Sections/Graphics/LayoutSettings.cs | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 9a5fd769cd..8b093c8d79 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; namespace osu.Game.Overlays.Settings.Sections.Graphics { @@ -11,11 +12,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { protected override string Header => "Layout"; - private SettingsSlider letterboxPositionX; - private SettingsSlider letterboxPositionY; + private FillFlowContainer letterboxSettings; private Bindable letterboxing; + private const int letterbox_container_height = 75; + private const int duration = 200; + [BackgroundDependencyLoader] private void load(FrameworkConfigManager config) { @@ -33,34 +36,30 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics LabelText = "Letterboxing", Bindable = letterboxing, }, - letterboxPositionX = new SettingsSlider + letterboxSettings = new FillFlowContainer { - LabelText = "Horizontal position", - Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionX) - }, - letterboxPositionY = new SettingsSlider - { - LabelText = "Vertical position", - Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionY) + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + Masking = true, + + Children = new Drawable[] + { + new SettingsSlider + { + LabelText = "Horizontal position", + Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionX) + }, + new SettingsSlider + { + LabelText = "Vertical position", + Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionY) + }, + } }, }; - letterboxing.ValueChanged += visibilityChanged; + letterboxing.ValueChanged += isVisible => letterboxSettings.ResizeHeightTo(isVisible ? letterbox_container_height : 0, duration, EasingTypes.OutQuint); letterboxing.TriggerChange(); } - - private void visibilityChanged(bool newVisibility) - { - if (newVisibility) - { - letterboxPositionX.Show(); - letterboxPositionY.Show(); - } - else - { - letterboxPositionX.Hide(); - letterboxPositionY.Hide(); - } - } } } From 2e28b10c3678518e496801d799b9b5f6239ad56f Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 23 May 2017 12:24:16 +0300 Subject: [PATCH 56/58] CI fixes and removed useless property --- osu.Game/Screens/Play/HUDOverlay.cs | 3 +-- osu.Game/Screens/Play/KeyCounterCollection.cs | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 34522d74ef..115611c244 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -45,7 +45,6 @@ namespace osu.Game.Screens.Play protected HUDOverlay() { RelativeSizeAxes = Axes.Both; - AlwaysPresent = true; Add(content = new Container { @@ -68,7 +67,7 @@ namespace osu.Game.Screens.Play private void load(OsuConfigManager config, NotificationManager notificationManager) { showHud = config.GetBindable(OsuSetting.ShowInterface); - showHud.ValueChanged += hudVisibility => FadeTo(hudVisibility ? 1 : 0, duration); + showHud.ValueChanged += hudVisibility => content.FadeTo(hudVisibility ? 1 : 0, duration); showHud.TriggerChange(); if (!showHud && !hasShownNotificationOnce) diff --git a/osu.Game/Screens/Play/KeyCounterCollection.cs b/osu.Game/Screens/Play/KeyCounterCollection.cs index e53a1dcd99..6e1c8a34e5 100644 --- a/osu.Game/Screens/Play/KeyCounterCollection.cs +++ b/osu.Game/Screens/Play/KeyCounterCollection.cs @@ -21,7 +21,6 @@ namespace osu.Game.Screens.Play public KeyCounterCollection() { AlwaysReceiveInput = true; - AlwaysPresent = true; Direction = FillDirection.Horizontal; AutoSizeAxes = Axes.Both; From 165d6ef1bb19e6c5079dbd95e2afced50f437adc Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 23 May 2017 12:30:39 +0300 Subject: [PATCH 57/58] Make transition a bit smoother --- osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 8b093c8d79..8ff145823c 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private Bindable letterboxing; private const int letterbox_container_height = 75; - private const int duration = 200; + private const int duration = 300; [BackgroundDependencyLoader] private void load(FrameworkConfigManager config) From 564abc1a5bc171a7f62e5f0568fa17ec87efeffa Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 23 May 2017 14:32:45 +0300 Subject: [PATCH 58/58] using autosize instead of constant height --- .../Settings/Sections/Graphics/LayoutSettings.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 8ff145823c..ea9ccd3492 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -16,8 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private Bindable letterboxing; - private const int letterbox_container_height = 75; - private const int duration = 300; + private const int transition_duration = 400; [BackgroundDependencyLoader] private void load(FrameworkConfigManager config) @@ -40,6 +39,9 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + AutoSizeDuration = transition_duration, + AutoSizeEasing = EasingTypes.OutQuint, Masking = true, Children = new Drawable[] @@ -58,7 +60,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, }; - letterboxing.ValueChanged += isVisible => letterboxSettings.ResizeHeightTo(isVisible ? letterbox_container_height : 0, duration, EasingTypes.OutQuint); + letterboxing.ValueChanged += isVisible => + { + letterboxSettings.ClearTransforms(); + letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None; + + if(!isVisible) + letterboxSettings.ResizeHeightTo(0, transition_duration, EasingTypes.OutQuint); + }; letterboxing.TriggerChange(); } }