From 0801a211da226bcb7cc3f802cfcac829f984cdc6 Mon Sep 17 00:00:00 2001 From: ColdVolcano Date: Wed, 17 May 2017 01:14:04 -0500 Subject: [PATCH 01/33] Add a base container with beat syncing logic --- .../Containers/BeatSyncedContainer.cs | 64 +++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 2 files changed, 65 insertions(+) create mode 100644 osu.Game/Graphics/Containers/BeatSyncedContainer.cs diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs new file mode 100644 index 0000000000..1438ccf002 --- /dev/null +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -0,0 +1,64 @@ +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; + +namespace osu.Game.Graphics.Containers +{ + public class BeatSyncedContainer : Container + { + private Bindable beatmap; + private int beat; + private double timingPointStart; + //This is to avoid sending new beats when not at the very start of the beat + private const int seek_tolerance = 20; + private const double min_beat_length = 1E-100; + + public BeatSyncedContainer() + { + } + + protected override void Update() + { + if (beatmap.Value != null) + { + double currentTime = beatmap.Value.Track.CurrentTime; + ControlPoint kiaiControlPoint; + ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(currentTime, out kiaiControlPoint); + + if (controlPoint != null) + { + double oldTimingPointStart = timingPointStart; + double beatLenght = double.MinValue; + int oldBeat = beat; + bool kiai = false; + + beatLenght = controlPoint.BeatLength; + timingPointStart = controlPoint.Time; + kiai = kiaiControlPoint?.KiaiMode ?? false; + + beat = beatLenght > min_beat_length ? (int)((currentTime - timingPointStart) / beatLenght) : 0; + + //should we handle negative beats? (before the start of the controlPoint) + //The beats before the start of the first control point are off by 1, this should do the trick + if (currentTime <= timingPointStart) + beat--; + + if ((timingPointStart != oldTimingPointStart || beat != oldBeat) && (int)((currentTime - timingPointStart) % (beatLenght)) <= seek_tolerance) + OnNewBeat(beat, controlPoint.BeatLength, controlPoint.TimeSignature, kiai); + } + } + } + + protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai) + { + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + beatmap = game.Beatmap; + } + } +} \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 63acdb1914..18c483fe40 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -74,6 +74,7 @@ + From 5f192cae7bb6042e5c1ac8131d4bda557d92673e Mon Sep 17 00:00:00 2001 From: ColdVolcano Date: Wed, 17 May 2017 01:18:56 -0500 Subject: [PATCH 02/33] Add missing licence header --- 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 1438ccf002..9d279ebb44 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -1,4 +1,7 @@ -using osu.Framework.Allocation; +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; From 387d26a5769790142a9ac16f558758d968ad66ed Mon Sep 17 00:00:00 2001 From: ColdVolcano Date: Wed, 17 May 2017 01:26:34 -0500 Subject: [PATCH 03/33] CI Fixes --- .../Graphics/Containers/BeatSyncedContainer.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 9d279ebb44..ba25cc1d1a 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -18,10 +18,6 @@ namespace osu.Game.Graphics.Containers private const int seek_tolerance = 20; private const double min_beat_length = 1E-100; - public BeatSyncedContainer() - { - } - protected override void Update() { if (beatmap.Value != null) @@ -33,23 +29,23 @@ namespace osu.Game.Graphics.Containers if (controlPoint != null) { double oldTimingPointStart = timingPointStart; - double beatLenght = double.MinValue; + double beatLength; int oldBeat = beat; - bool kiai = false; + bool kiai; - beatLenght = controlPoint.BeatLength; + beatLength = controlPoint.BeatLength; timingPointStart = controlPoint.Time; kiai = kiaiControlPoint?.KiaiMode ?? false; - beat = beatLenght > min_beat_length ? (int)((currentTime - timingPointStart) / beatLenght) : 0; + beat = beatLength > min_beat_length ? (int)((currentTime - timingPointStart) / beatLength) : 0; //should we handle negative beats? (before the start of the controlPoint) //The beats before the start of the first control point are off by 1, this should do the trick if (currentTime <= timingPointStart) beat--; - if ((timingPointStart != oldTimingPointStart || beat != oldBeat) && (int)((currentTime - timingPointStart) % (beatLenght)) <= seek_tolerance) - OnNewBeat(beat, controlPoint.BeatLength, controlPoint.TimeSignature, kiai); + if ((timingPointStart != oldTimingPointStart || beat != oldBeat) && (currentTime - timingPointStart) % (beatLength) <= seek_tolerance) + OnNewBeat(beat, beatLength, controlPoint.TimeSignature, kiai); } } } From 198542465b81e9deb6735ff39cf594bd2d36f952 Mon Sep 17 00:00:00 2001 From: ColdVolcano Date: Wed, 17 May 2017 01:37:31 -0500 Subject: [PATCH 04/33] More CI fixes --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index ba25cc1d1a..2fc7731ef5 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -29,13 +29,10 @@ namespace osu.Game.Graphics.Containers if (controlPoint != null) { double oldTimingPointStart = timingPointStart; - double beatLength; + double beatLength = controlPoint.BeatLength; int oldBeat = beat; - bool kiai; - - beatLength = controlPoint.BeatLength; + bool kiai = kiaiControlPoint?.KiaiMode ?? false; timingPointStart = controlPoint.Time; - kiai = kiaiControlPoint?.KiaiMode ?? false; beat = beatLength > min_beat_length ? (int)((currentTime - timingPointStart) / beatLength) : 0; @@ -44,7 +41,7 @@ namespace osu.Game.Graphics.Containers if (currentTime <= timingPointStart) beat--; - if ((timingPointStart != oldTimingPointStart || beat != oldBeat) && (currentTime - timingPointStart) % (beatLength) <= seek_tolerance) + if ((timingPointStart != oldTimingPointStart || beat != oldBeat) && (int)((currentTime - timingPointStart) % beatLength) <= seek_tolerance) OnNewBeat(beat, beatLength, controlPoint.TimeSignature, kiai); } } From f4c7a02a6f06acb13d8ed6c20ef434863fa0c57e Mon Sep 17 00:00:00 2001 From: Javier Flores Date: Wed, 17 May 2017 09:23:04 -0500 Subject: [PATCH 05/33] Fix having negative beat when it should be 0 --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 2fc7731ef5..3b10e96398 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -38,7 +38,7 @@ namespace osu.Game.Graphics.Containers //should we handle negative beats? (before the start of the controlPoint) //The beats before the start of the first control point are off by 1, this should do the trick - if (currentTime <= timingPointStart) + if (currentTime < timingPointStart) beat--; if ((timingPointStart != oldTimingPointStart || beat != oldBeat) && (int)((currentTime - timingPointStart) % beatLength) <= seek_tolerance) @@ -57,4 +57,4 @@ namespace osu.Game.Graphics.Containers beatmap = game.Beatmap; } } -} \ No newline at end of file +} From 793b760ff2244f0cfc6b424bffc84764b1986b1f Mon Sep 17 00:00:00 2001 From: Javier Flores Date: Thu, 18 May 2017 20:19:00 -0500 Subject: [PATCH 06/33] Cleanup logic --- .../Containers/BeatSyncedContainer.cs | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 3b10e96398..d7f5d6c112 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . +// Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; @@ -12,8 +12,8 @@ namespace osu.Game.Graphics.Containers public class BeatSyncedContainer : Container { private Bindable beatmap; - private int beat; - private double timingPointStart; + private int lastBeat; + private double lastTimingPointStart; //This is to avoid sending new beats when not at the very start of the beat private const int seek_tolerance = 20; private const double min_beat_length = 1E-100; @@ -22,27 +22,25 @@ namespace osu.Game.Graphics.Containers { if (beatmap.Value != null) { - double currentTime = beatmap.Value.Track.CurrentTime; + double trackCurrentTime = beatmap.Value.Track.CurrentTime; ControlPoint kiaiControlPoint; - ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(currentTime, out kiaiControlPoint); + ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(trackCurrentTime, out kiaiControlPoint); if (controlPoint != null) { - double oldTimingPointStart = timingPointStart; double beatLength = controlPoint.BeatLength; - int oldBeat = beat; bool kiai = kiaiControlPoint?.KiaiMode ?? false; - timingPointStart = controlPoint.Time; + double timingPointStart = controlPoint.Time; + int beat = beatLength > min_beat_length ? (int)((trackCurrentTime - timingPointStart) / beatLength) : 0; - beat = beatLength > min_beat_length ? (int)((currentTime - timingPointStart) / beatLength) : 0; - - //should we handle negative beats? (before the start of the controlPoint) //The beats before the start of the first control point are off by 1, this should do the trick - if (currentTime < timingPointStart) + if (trackCurrentTime < timingPointStart) beat--; - if ((timingPointStart != oldTimingPointStart || beat != oldBeat) && (int)((currentTime - timingPointStart) % beatLength) <= seek_tolerance) + if ((timingPointStart != lastTimingPointStart || beat != lastBeat) && (int)((trackCurrentTime - timingPointStart) % beatLength) <= seek_tolerance) OnNewBeat(beat, beatLength, controlPoint.TimeSignature, kiai); + lastBeat = beat; + lastTimingPointStart = timingPointStart; } } } From 6ab7a9141553fd7505d5672a0ecabc233e5bfdfc Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 19 May 2017 20:57:20 +0900 Subject: [PATCH 07/33] 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 08/33] 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 09/33] 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 e232968ea72463bb974b4a5d055fa92c837bf0b5 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 10:25:28 +0900 Subject: [PATCH 10/33] 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 11/33] 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 12/33] 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 13/33] 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 f9eb448f162915e1d7293652635a417a2dbed825 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 15:29:17 +0900 Subject: [PATCH 14/33] 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 15/33] 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 16/33] 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 abe9c464a82cc672699075417cfb3419fb901757 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 16:56:40 +0900 Subject: [PATCH 17/33] Invert if statements. --- .../Containers/BeatSyncedContainer.cs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index d7f5d6c112..4a2e8b6262 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -20,29 +20,29 @@ namespace osu.Game.Graphics.Containers protected override void Update() { - if (beatmap.Value != null) - { - double trackCurrentTime = beatmap.Value.Track.CurrentTime; - ControlPoint kiaiControlPoint; - ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(trackCurrentTime, out kiaiControlPoint); + if (beatmap.Value == null) + return; - if (controlPoint != null) - { - double beatLength = controlPoint.BeatLength; - bool kiai = kiaiControlPoint?.KiaiMode ?? false; - double timingPointStart = controlPoint.Time; - int beat = beatLength > min_beat_length ? (int)((trackCurrentTime - timingPointStart) / beatLength) : 0; + double trackCurrentTime = beatmap.Value.Track.CurrentTime; + ControlPoint kiaiControlPoint; + ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(trackCurrentTime, out kiaiControlPoint); - //The beats before the start of the first control point are off by 1, this should do the trick - if (trackCurrentTime < timingPointStart) - beat--; + if (controlPoint == null) + return; - if ((timingPointStart != lastTimingPointStart || beat != lastBeat) && (int)((trackCurrentTime - timingPointStart) % beatLength) <= seek_tolerance) - OnNewBeat(beat, beatLength, controlPoint.TimeSignature, kiai); - lastBeat = beat; - lastTimingPointStart = timingPointStart; - } - } + double beatLength = controlPoint.BeatLength; + bool kiai = kiaiControlPoint?.KiaiMode ?? false; + double timingPointStart = controlPoint.Time; + int beat = beatLength > min_beat_length ? (int)((trackCurrentTime - timingPointStart) / beatLength) : 0; + + //The beats before the start of the first control point are off by 1, this should do the trick + if (trackCurrentTime < timingPointStart) + beat--; + + if ((timingPointStart != lastTimingPointStart || beat != lastBeat) && (int)((trackCurrentTime - timingPointStart) % beatLength) <= seek_tolerance) + OnNewBeat(beat, beatLength, controlPoint.TimeSignature, kiai); + lastBeat = beat; + lastTimingPointStart = timingPointStart; } protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai) From b241140496813ee12431c2a876ca81841c4a2e1b Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 16:59:44 +0900 Subject: [PATCH 18/33] Proper kiai mode detection. --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 4a2e8b6262..5f6f0b76b5 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -30,8 +30,9 @@ namespace osu.Game.Graphics.Containers if (controlPoint == null) return; + bool kiai = (controlPoint ?? controlPoint).KiaiMode; + double beatLength = controlPoint.BeatLength; - bool kiai = kiaiControlPoint?.KiaiMode ?? false; double timingPointStart = controlPoint.Time; int beat = beatLength > min_beat_length ? (int)((trackCurrentTime - timingPointStart) / beatLength) : 0; From 609aac6453a1fd48d8a3a872f3dcefd79f30a110 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 17:00:53 +0900 Subject: [PATCH 19/33] Better kiai mode detection. --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 5f6f0b76b5..521d9650d9 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -24,13 +24,13 @@ namespace osu.Game.Graphics.Containers return; double trackCurrentTime = beatmap.Value.Track.CurrentTime; - ControlPoint kiaiControlPoint; - ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(trackCurrentTime, out kiaiControlPoint); + ControlPoint overridePoint; + ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(trackCurrentTime, out overridePoint); if (controlPoint == null) return; - bool kiai = (controlPoint ?? controlPoint).KiaiMode; + bool kiai = (overridePoint ?? controlPoint).KiaiMode; double beatLength = controlPoint.BeatLength; double timingPointStart = controlPoint.Time; From f81ffa636dc1770284c3d88c9f9514bdc4a30f22 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 17:01:43 +0900 Subject: [PATCH 20/33] Use BindTo instead of taking the game's beatmap bindable. --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 521d9650d9..666d0e3f77 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -11,7 +11,8 @@ namespace osu.Game.Graphics.Containers { public class BeatSyncedContainer : Container { - private Bindable beatmap; + private Bindable beatmap = new Bindable(); + private int lastBeat; private double lastTimingPointStart; //This is to avoid sending new beats when not at the very start of the beat @@ -20,7 +21,7 @@ namespace osu.Game.Graphics.Containers protected override void Update() { - if (beatmap.Value == null) + if (beatmap.Value?.Track == null) return; double trackCurrentTime = beatmap.Value.Track.CurrentTime; @@ -53,7 +54,7 @@ namespace osu.Game.Graphics.Containers [BackgroundDependencyLoader] private void load(OsuGameBase game) { - beatmap = game.Beatmap; + beatmap.BindTo(game.Beatmap); } } } From 70d7f61f7cf38314f2f68390b9924daf526dc9b7 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 17:15:23 +0900 Subject: [PATCH 21/33] More inversion for logic simplification. --- .../Containers/BeatSyncedContainer.cs | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 666d0e3f77..8b38547037 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -11,40 +11,42 @@ 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 Bindable beatmap = new Bindable(); private int lastBeat; - private double lastTimingPointStart; - //This is to avoid sending new beats when not at the very start of the beat - private const int seek_tolerance = 20; - private const double min_beat_length = 1E-100; + private ControlPoint lastControlPoint; protected override void Update() { if (beatmap.Value?.Track == null) return; - double trackCurrentTime = beatmap.Value.Track.CurrentTime; + double currentTrackTime = beatmap.Value.Track.CurrentTime; ControlPoint overridePoint; - ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(trackCurrentTime, out overridePoint); - - if (controlPoint == null) - return; + ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(currentTrackTime, out overridePoint); bool kiai = (overridePoint ?? controlPoint).KiaiMode; + int beat = controlPoint.BeatLength > 0 ? (int)((currentTrackTime - controlPoint.Time) / controlPoint.BeatLength) : 0; - double beatLength = controlPoint.BeatLength; - double timingPointStart = controlPoint.Time; - int beat = beatLength > min_beat_length ? (int)((trackCurrentTime - timingPointStart) / beatLength) : 0; - - //The beats before the start of the first control point are off by 1, this should do the trick - if (trackCurrentTime < timingPointStart) + // 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 ((timingPointStart != lastTimingPointStart || beat != lastBeat) && (int)((trackCurrentTime - timingPointStart) % beatLength) <= seek_tolerance) - OnNewBeat(beat, beatLength, controlPoint.TimeSignature, kiai); + if (controlPoint == lastControlPoint && beat == lastBeat) + return; + + if ((currentTrackTime - controlPoint.Time) % controlPoint.BeatLength > seek_tolerance) + return; + + OnNewBeat(beat, controlPoint.BeatLength, controlPoint.TimeSignature, kiai); + lastBeat = beat; - lastTimingPointStart = timingPointStart; + lastControlPoint = controlPoint; } protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai) From 3235e280a9a64554e56e618ee4ff27322955a4d9 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 17:31:53 +0900 Subject: [PATCH 22/33] Add to csproj. --- osu.Game/osu.Game.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1631311ef6..b94c19e1f8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -292,6 +292,7 @@ + From fc90cfa604d37e1b063f7f5bffbb9f2d5a3cb015 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 22 May 2017 17:50:05 +0900 Subject: [PATCH 23/33] Update BeatSyncedContainer.cs --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 8b38547037..945de822a3 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Graphics.Containers /// private const int seek_tolerance = 20; - private Bindable beatmap = new Bindable(); + private readonly Bindable beatmap = new Bindable(); private int lastBeat; private ControlPoint lastControlPoint; From 95c4704a9e679381274c414d69dc9a5f5a5b908a Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 22 May 2017 20:01:28 +0900 Subject: [PATCH 24/33] 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 25/33] 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 26/33] 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 fe7ac20e292cbd014a1d6d15a41f3756da83c008 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 May 2017 16:26:51 +0900 Subject: [PATCH 27/33] 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 eafe215169b6ba92b813e280d2681e76b7d1b723 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 23 May 2017 11:53:12 +0300 Subject: [PATCH 28/33] 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 29/33] 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 c7a241246ecb912897aded1faf75203589f66614 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 23 May 2017 12:09:32 +0300 Subject: [PATCH 30/33] 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 31/33] 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 32/33] 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 33/33] 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(); } }