From 231f7939a684377bdaf8aeba6099e65ae29a5cf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2017 12:41:37 +0900 Subject: [PATCH 01/77] Fix channels being unnecessarily recycled on disconnect Resolves #768 --- osu.Game/Overlays/ChatOverlay.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index a85af251c5..cbcfbbfd9e 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -206,18 +206,12 @@ namespace osu.Game.Overlays private long? lastMessageId; - private List careChannels; + private List careChannels = new List(); private readonly List loadedChannels = new List(); private void initializeChannels() { - currentChannelContainer.Clear(); - - loadedChannels.Clear(); - - careChannels = new List(); - SpriteText loading; Add(loading = new OsuSpriteText { @@ -232,8 +226,6 @@ namespace osu.Game.Overlays ListChannelsRequest req = new ListChannelsRequest(); req.Success += delegate (List channels) { - Debug.Assert(careChannels.Count == 0); - Scheduler.Add(delegate { loading.FadeOut(100); From 93e2b21084cce70a18abecec6ca3aea15c7b2a9d Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 17 May 2017 12:52:07 +0900 Subject: [PATCH 02/77] Add FastRandom class for use in beatmap conversion. --- .../MathUtils/FastRandom.cs | 92 +++++++++++++++++++ .../osu.Game.Rulesets.Mania.csproj | 1 + 2 files changed, 93 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs new file mode 100644 index 0000000000..d090fbb0a0 --- /dev/null +++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs @@ -0,0 +1,92 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Rulesets.Mania.MathUtils +{ + /// + /// A PRNG specified in http://heliosphan.org/fastrandom.html. + /// + internal class FastRandom + { + private const double uint_to_real = 1.0 / (int.MaxValue + 1.0); + private const uint int_mask = 0x7FFFFFFF; + private const uint y = 842502087; + private const uint z = 3579807591; + private const uint w = 273326509; + private uint _x, _y = y, _z = z, _w = w; + + public FastRandom(int seed) + { + _x = (uint)seed; + } + + public FastRandom() + : this(Environment.TickCount) + { + } + + /// + /// Generates a random unsigned integer within the range [, ). + /// + /// The random value. + public uint NextUInt() + { + uint t = (_x ^ (_x << 11)); + _x = _y; + _y = _z; + _z = _w; + return (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))); + } + + /// + /// Generates a random integer value within the range [0, ). + /// + /// The random value. + public int Next() => (int)(int_mask & NextUInt()); + + /// + /// Generates a random integer value within the range [0, ). + /// + /// The upper bound. + /// The random value. + public int Next(int upperBound) => (int)(NextDouble() * upperBound); + + /// + /// Generates a random integer value within the range [, ). + /// + /// The lower bound of the range. + /// The upper bound of the range. + /// The random value. + public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); + + /// + /// Generates a random double value within the range [0, 1). + /// + /// The random value. + public double NextDouble() => uint_to_real * NextUInt(); + + private uint bitBuffer; + private int bitIndex = 32; + + /// + /// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls. + /// + /// The random value. + public bool NextBool() + { + if (bitIndex == 32) + { + bitBuffer = NextUInt(); + bitIndex = 1; + + return (bitBuffer & 1) == 1; + } + + bitIndex++; + return ((bitBuffer >>= 1) & 1) == 1; + } + + } +} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 00deaba85d..52396debf5 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -48,6 +48,7 @@ + From 51f7904c137aaf8bec05cd468306d53f4729aac1 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 17 May 2017 13:04:34 +0900 Subject: [PATCH 03/77] Remove redundant parentheses. --- osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs | 4 ++-- osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs index d090fbb0a0..381e24c84c 100644 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs @@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Mania.MathUtils /// The random value. public uint NextUInt() { - uint t = (_x ^ (_x << 11)); + uint t = _x ^ _x << 11; _x = _y; _y = _z; _z = _w; - return (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))); + return _w = _w ^ _w >> 19 ^ t ^ t >> 8; } /// diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index 986aefb2bd..da4a693dd8 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.Mania.MathUtils; namespace osu.Game.Rulesets.Mania.UI { @@ -27,6 +28,11 @@ namespace osu.Game.Rulesets.Mania.UI public ManiaHitRenderer(WorkingBeatmap beatmap) : base(beatmap) { + FastRandom fr = new FastRandom(1337); + while (true) + { + int value = fr.Next(); + } } protected override Playfield CreatePlayfield() From 1ded04377815f3b0ef926982f2f2c93afb7a1d2f Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 17 May 2017 13:07:56 +0900 Subject: [PATCH 04/77] Add basic mania-specific beatmap conversion. # Conflicts: # osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs --- .../Beatmaps/LegacyConverter.cs | 72 +++++++++++++++++++ .../Beatmaps/ManiaBeatmapConverter.cs | 41 ++++++----- .../osu.Game.Rulesets.Mania.csproj | 1 + 3 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs new file mode 100644 index 0000000000..20a3dc612b --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs @@ -0,0 +1,72 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using SharpNeat.Utility; +using System; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Mania.Beatmaps +{ + /// + /// Special converter used for converting from osu!stable beatmaps. + /// + internal class LegacyConverter + { + private readonly FastRandom random; + + private readonly int availableColumns; + private readonly float localXDivisor; + + private readonly Beatmap beatmap; + + public LegacyConverter(Beatmap beatmap) + { + this.beatmap = beatmap; + + int seed = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.DrainRate + beatmap.BeatmapInfo.Difficulty.CircleSize) + * 20 + (int)(beatmap.BeatmapInfo.Difficulty.OverallDifficulty * 41.2) + (int)Math.Round(beatmap.BeatmapInfo.Difficulty.ApproachRate); + + availableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); + localXDivisor = 512.0f / availableColumns; + } + + public IEnumerable Convert(HitObject original) + { + if (beatmap.BeatmapInfo.RulesetID == 3) + yield return generateSpecific(original); + } + + private ManiaHitObject generateSpecific(HitObject original) + { + var endTimeData = original as IHasEndTime; + var positionData = original as IHasPosition; + + int column = getColumn(positionData?.X ?? 0); + + if (endTimeData != null) + { + return new HoldNote + { + StartTime = original.StartTime, + Samples = original.Samples, + Duration = endTimeData.Duration, + Column = column, + }; + } + + return new Note + { + StartTime = original.StartTime, + Samples = original.Samples, + Column = column + }; + } + + private int getColumn(float position) => MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, availableColumns - 1); + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index e51bbcdc13..e9dae95782 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -1,35 +1,38 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; -using System.Collections.Generic; -using System; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using System; +using System.Collections.Generic; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; -using OpenTK; +using osu.Game.Rulesets.Objects.Types; +using System.Linq; namespace osu.Game.Rulesets.Mania.Beatmaps { - internal class ManiaBeatmapConverter : BeatmapConverter + public class ManiaBeatmapConverter : BeatmapConverter { protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; + protected override Beatmap ConvertBeatmap(Beatmap original) + { + // Todo: This should be cased when we get better conversion methods + var converter = new LegacyConverter(original); + + return new Beatmap + { + BeatmapInfo = original.BeatmapInfo, + TimingInfo = original.TimingInfo, + HitObjects = original.HitObjects.SelectMany(converter.Convert).ToList() + }; + } + protected override IEnumerable ConvertHitObject(HitObject original, Beatmap beatmap) { - int availableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); - - var positionData = original as IHasXPosition; - - float localWDivisor = 512.0f / availableColumns; - int column = MathHelper.Clamp((int)Math.Floor((positionData?.X ?? 1) / localWDivisor), 0, availableColumns - 1); - - yield return new Note - { - StartTime = original.StartTime, - Column = column, - }; + // Handled by the LegacyConvereter + yield return null; } } } diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 52396debf5..781dc3e228 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -47,6 +47,7 @@ + From fdd98e23bdf315e1f7071fb21dd1cdc6b5dc99ed Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 12 May 2017 16:12:18 +0900 Subject: [PATCH 05/77] Fix generation. --- osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs index 20a3dc612b..3d3b052ce6 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private ManiaHitObject generateSpecific(HitObject original) { var endTimeData = original as IHasEndTime; - var positionData = original as IHasPosition; + var positionData = original as IHasXPosition; int column = getColumn(positionData?.X ?? 0); From 17466a662cb98fbbd10caef177619e34be3a6c10 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 12 May 2017 16:35:57 +0900 Subject: [PATCH 06/77] Add hold note conversion. --- .../UI/ManiaHitRenderer.cs | 4 +++ .../Legacy/Catch/ConvertHitObjectParser.cs | 5 ++++ .../Objects/Legacy/ConvertHitObjectParser.cs | 29 +++++++++++++------ .../Rulesets/Objects/Legacy/ConvertHold.cs | 22 -------------- .../Legacy/Mania/ConvertHitObjectParser.cs | 9 ++++++ .../Objects/Legacy/Mania/ConvertHold.cs | 16 ++++++++++ .../Legacy/Osu/ConvertHitObjectParser.cs | 5 ++++ .../Legacy/Taiko/ConvertHitObjectParser.cs | 5 ++++ osu.Game/Rulesets/Objects/Types/IHasHold.cs | 4 +++ osu.Game/osu.Game.csproj | 2 +- 10 files changed, 69 insertions(+), 32 deletions(-) delete mode 100644 osu.Game/Rulesets/Objects/Legacy/ConvertHold.cs create mode 100644 osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index da4a693dd8..9387b8e9cb 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -82,6 +82,10 @@ namespace osu.Game.Rulesets.Mania.UI protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h) { + var holdNote = h as HoldNote; + if (holdNote != null) + return new DrawableHoldNote(holdNote); + var note = h as Note; if (note != null) return new DrawableNote(note); diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 5c534456ef..c7f7802191 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -43,5 +43,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch EndTime = endTime }; } + + protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) + { + return null; + } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index c5551082ec..3b2d7211bb 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Globalization; using osu.Game.Beatmaps.Formats; using osu.Game.Audio; +using System.Linq; namespace osu.Game.Rulesets.Objects.Legacy { @@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy var soundType = (LegacySoundType)int.Parse(split[4]); var bankInfo = new SampleBankInfo(); - HitObject result; + HitObject result = null; if ((type & ConvertHitObjectType.Circle) > 0) { @@ -140,17 +141,19 @@ namespace osu.Game.Rulesets.Objects.Legacy { // Note: Hold is generated by BMS converts - // Todo: Apparently end time is determined by samples?? - // Shouldn't need implementation until mania + double endTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture); - result = new ConvertHold + if (split.Length > 5 && !string.IsNullOrEmpty(split[5])) { - Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])), - NewCombo = combo - }; + endTime = Convert.ToDouble(split[0], CultureInfo.InvariantCulture); + readCustomSampleBanks(string.Join(":", split.Skip(1)), bankInfo); + } + + result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime); } - else - throw new InvalidOperationException($@"Unknown hit object type {type}"); + + if (result == null) + throw new InvalidOperationException($@"Unknown hit object type {type}."); result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture); result.Samples = convertSoundType(soundType, bankInfo); @@ -214,6 +217,14 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The hit object. protected abstract HitObject CreateSpinner(Vector2 position, double endTime); + /// + /// Creates a legacy Hold-type hit object. + /// + /// The position of the hit object. + /// Whether the hit object creates a new combo. + /// The hold end time. + protected abstract HitObject CreateHold(Vector2 position, bool newCombo, double endTime); + private SampleInfoList convertSoundType(LegacySoundType type, SampleBankInfo bankInfo) { var soundTypes = new SampleInfoList diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHold.cs deleted file mode 100644 index d79f6e324e..0000000000 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHold.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy -{ - /// - /// Legacy Hold-type, used for parsing "specials" in beatmaps. - /// - internal sealed class ConvertHold : HitObject, IHasPosition, IHasCombo, IHasHold - { - public Vector2 Position { get; set; } - - public float X => Position.X; - - public float Y => Position.Y; - - public bool NewCombo { get; set; } - } -} diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index 224f068323..7141876b8b 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -44,5 +44,14 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania EndTime = endTime }; } + + protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) + { + return new ConvertHold + { + X = position.X, + EndTime = endTime + }; + } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs new file mode 100644 index 0000000000..1dda577320 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Mania +{ + internal sealed class ConvertHold : HitObject, IHasXPosition, IHasEndTime + { + public float X { get; set; } + + public double EndTime { get; set; } + + public double Duration => EndTime - StartTime; + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 41bf142831..00fe171f0f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -44,5 +44,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu EndTime = endTime }; } + + protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) + { + return null; + } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index 0d755d7527..5929c5a907 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -41,5 +41,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko EndTime = endTime }; } + + protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) + { + return null; + } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasHold.cs b/osu.Game/Rulesets/Objects/Types/IHasHold.cs index 82ec790524..4054fc4fd1 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasHold.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasHold.cs @@ -8,5 +8,9 @@ namespace osu.Game.Rulesets.Objects.Types /// public interface IHasHold { + /// + /// The time at which the hold ends. + /// + double EndTime { get; } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 63acdb1914..9352cfba6e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -125,6 +125,7 @@ + @@ -161,7 +162,6 @@ - From 9565bb9572a80aeb4cf8ef32c23b92d4b583e3f7 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 12 May 2017 19:10:26 +0900 Subject: [PATCH 07/77] Fix hold notes - remove glow for now. # Conflicts: # osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs # osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs --- .../Tests/TestCaseManiaHitObjects.cs | 14 +++------- .../Objects/Drawables/DrawableHoldNote.cs | 11 ++++++++ .../Drawables/DrawableManiaHitObject.cs | 26 ------------------- .../Objects/Drawables/Pieces/BodyPiece.cs | 1 - .../Objects/Legacy/ConvertHitObjectParser.cs | 5 ++-- 5 files changed, 17 insertions(+), 40 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs index 3ad83beb73..3113b63db1 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs @@ -43,13 +43,8 @@ namespace osu.Desktop.VisualTests.Tests RelativeCoordinateSpace = new Vector2(1, 10000), Children = new[] { - new DrawableNote(new Note - { - StartTime = 5000 - }) - { - AccentColour = Color4.Red - } + new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red }, + new DrawableNote(new Note { StartTime = 6000 }) { AccentColour = Color4.Red } } } } @@ -74,10 +69,7 @@ namespace osu.Desktop.VisualTests.Tests { StartTime = 5000, Duration = 1000 - }) - { - AccentColour = Color4.Red - } + }) { AccentColour = Color4.Red } } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 767a2b3458..a1327a3105 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -61,5 +61,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected override void UpdateState(ArmedState state) { } + + protected override void Update() + { + if (Time.Current > HitObject.StartTime) + headPiece.Colour = Color4.Green; + if (Time.Current > HitObject.EndTime) + { + bodyPiece.Colour = Color4.Green; + tailPiece.Colour = Color4.Green; + } + } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 0307e9162a..d33a8c48ee 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -3,8 +3,6 @@ using OpenTK.Graphics; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Objects.Drawables; @@ -15,8 +13,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { public new TObject HitObject; - private readonly Container glowContainer; - protected DrawableManiaHitObject(TObject hitObject) : base(hitObject) { @@ -24,21 +20,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables RelativePositionAxes = Axes.Y; Y = (float)HitObject.StartTime; - - Add(glowContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - }); } public override Color4 AccentColour @@ -49,13 +30,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (base.AccentColour == value) return; base.AccentColour = value; - - glowContainer.EdgeEffect = new EdgeEffect - { - Type = EdgeEffectType.Glow, - Radius = 5, - Colour = value - }; } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs index ce61a7a86f..c10aa9994b 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs @@ -19,7 +19,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces public BodyPiece() { RelativeSizeAxes = Axes.Both; - Masking = true; Children = new[] { diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 3b2d7211bb..a4c319291c 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -145,8 +145,9 @@ namespace osu.Game.Rulesets.Objects.Legacy if (split.Length > 5 && !string.IsNullOrEmpty(split[5])) { - endTime = Convert.ToDouble(split[0], CultureInfo.InvariantCulture); - readCustomSampleBanks(string.Join(":", split.Skip(1)), bankInfo); + string[] ss = split[5].Split(':'); + endTime = Convert.ToDouble(ss[0], CultureInfo.InvariantCulture); + readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); } result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime); From c5d823d7d985948aeddf7181277268bf36176b80 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 17 May 2017 13:04:34 +0900 Subject: [PATCH 08/77] Remove redundant parentheses. --- osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs index d090fbb0a0..381e24c84c 100644 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs @@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Mania.MathUtils /// The random value. public uint NextUInt() { - uint t = (_x ^ (_x << 11)); + uint t = _x ^ _x << 11; _x = _y; _y = _z; _z = _w; - return (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))); + return _w = _w ^ _w >> 19 ^ t ^ t >> 8; } /// From 4d9aea6aecefb0287b78ffeaaaed798e25401228 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 17 May 2017 13:14:42 +0900 Subject: [PATCH 09/77] Remove while statement (came from fastrandom...). --- osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index 9387b8e9cb..3c31c6b854 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -17,7 +17,6 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.Mania.MathUtils; namespace osu.Game.Rulesets.Mania.UI { @@ -28,11 +27,6 @@ namespace osu.Game.Rulesets.Mania.UI public ManiaHitRenderer(WorkingBeatmap beatmap) : base(beatmap) { - FastRandom fr = new FastRandom(1337); - while (true) - { - int value = fr.Next(); - } } protected override Playfield CreatePlayfield() From 2f8556a44a36a88041f5efc7fc380e6a98c9b99f Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 17 May 2017 13:20:33 +0900 Subject: [PATCH 10/77] Fix hold notes + make it compile again. --- osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs | 2 +- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs index 3d3b052ce6..544f396f4a 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs @@ -6,9 +6,9 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; -using SharpNeat.Utility; using System; using System.Collections.Generic; +using osu.Game.Rulesets.Mania.MathUtils; namespace osu.Game.Rulesets.Mania.Beatmaps { diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index a1327a3105..d9e46f4720 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables tailPiece = new NotePiece { Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre + Origin = Anchor.TopCentre } }); } From 78c1d4581f84978a2db96e78807edaac2b417456 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2017 13:44:43 +0900 Subject: [PATCH 11/77] Revert "Use generic IComparable for message." This reverts commit aaaee5ed102e54a2fe40209e0ebe967015b29b83. --- osu.Game/Online/Chat/Channel.cs | 2 +- osu.Game/Online/Chat/Message.cs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 93fd0a8956..2925c3ccb4 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -23,7 +23,7 @@ namespace osu.Game.Online.Chat [JsonProperty(@"channel_id")] public int Id; - public readonly SortedList Messages = new SortedList(Comparer.Default); + public readonly SortedList Messages = new SortedList((m1, m2) => m1.Id.CompareTo(m2.Id)); //internal bool Joined; diff --git a/osu.Game/Online/Chat/Message.cs b/osu.Game/Online/Chat/Message.cs index c1887e7824..bf53a68910 100644 --- a/osu.Game/Online/Chat/Message.cs +++ b/osu.Game/Online/Chat/Message.cs @@ -8,7 +8,7 @@ using osu.Game.Users; namespace osu.Game.Online.Chat { - public class Message : IComparable + public class Message { [JsonProperty(@"message_id")] public readonly long Id; @@ -42,7 +42,17 @@ namespace osu.Game.Online.Chat Id = id; } - public int CompareTo(Message other) => Id.CompareTo(other.Id); + public override bool Equals(object obj) + { + var objMessage = obj as Message; + + return Id == objMessage?.Id; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } } public enum TargetType From cab12ee55a6ce09ae2f01eed9fb8bf06a40e330a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2017 16:45:17 +0900 Subject: [PATCH 12/77] Reduce the possibility of recomputing drawables when underlying beatmap hasn't changed --- osu.Game/Screens/Select/BeatmapDetails.cs | 2 ++ .../Screens/Select/Leaderboards/Leaderboard.cs | 2 ++ osu.Game/Screens/Select/SongSelect.cs | 16 ++++++++-------- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index b750fc9bbe..abe54375cc 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -49,6 +49,8 @@ namespace osu.Game.Screens.Select get { return beatmap; } set { + if (beatmap == value) return; + beatmap = value; pendingBeatmapSwitch?.Cancel(); diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index a136e298b5..a7aa752d65 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -100,6 +100,8 @@ namespace osu.Game.Screens.Select.Leaderboards get { return beatmap; } set { + if (beatmap == value) return; + beatmap = value; Scores = null; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 4438b656b0..4982ca096f 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -313,15 +313,15 @@ namespace osu.Game.Screens.Select { bool beatmapSetChange = false; - if (!beatmap.Equals(Beatmap?.BeatmapInfo)) + if (beatmap.Equals(Beatmap?.BeatmapInfo)) + return; + + if (beatmap.BeatmapSetInfoID == selectionChangeNoBounce?.BeatmapSetInfoID) + sampleChangeDifficulty.Play(); + else { - if (beatmap.BeatmapSetInfoID == selectionChangeNoBounce?.BeatmapSetInfoID) - sampleChangeDifficulty.Play(); - else - { - sampleChangeBeatmap.Play(); - beatmapSetChange = true; - } + sampleChangeBeatmap.Play(); + beatmapSetChange = true; } selectionChangeNoBounce = beatmap; From 7bb38e927b5a0487994d3616f33b8525405d10e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2017 17:15:25 +0900 Subject: [PATCH 13/77] Fix song select reverting to first difficulty of current group on entering --- osu.Game/Screens/Select/BeatmapCarousel.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index fbdaa948cc..4df24c1314 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -107,6 +107,8 @@ namespace osu.Game.Screens.Select return; } + if (beatmap == SelectedBeatmap) return; + foreach (BeatmapGroup group in groups) { var panel = group.BeatmapPanels.FirstOrDefault(p => p.Beatmap.Equals(beatmap)); @@ -204,7 +206,7 @@ namespace osu.Game.Screens.Select if (selectedGroup == null || selectedGroup.State == BeatmapGroupState.Hidden) SelectNext(); else - selectGroup(selectedGroup); + selectGroup(selectedGroup, selectedPanel); }; filterTask?.Cancel(); @@ -339,6 +341,8 @@ namespace osu.Game.Screens.Select selectedGroup.State = BeatmapGroupState.Collapsed; group.State = BeatmapGroupState.Expanded; + group.SelectedPanel = panel; + panel.State = PanelSelectedState.Selected; if (selectedPanel == panel) return; From cba77967777da8f22469ae31b2f0f31bc34dbbf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2017 18:49:30 +0900 Subject: [PATCH 14/77] Fix ratio container being added to a level too high That's a bad ratio container! How did we not notice this until now? --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 7e16030ec3..2c39a82245 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -146,7 +146,7 @@ namespace osu.Game { base.LoadComplete(); - AddInternal(ratioContainer = new RatioAdjust + base.Content.Add(ratioContainer = new RatioAdjust { Children = new Drawable[] { From 88a70e407ca2ce9805177f88f18ae073acb77265 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2017 20:11:41 +0900 Subject: [PATCH 15/77] Fix and simplifty pause logic Resolves #770 --- osu.Game/Screens/Play/Player.cs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8ee0de12b9..68fbbaddcc 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play public Action RestartRequested; - public bool IsPaused => !decoupledClock.IsRunning; + public bool IsPaused { get; private set; } internal override bool AllowRulesetChange => false; @@ -264,19 +264,18 @@ namespace osu.Game.Screens.Play { if (!canPause && !force) return; - // the actual pausing is potentially happening on a different thread. - // we want to wait for the source clock to stop so we can be sure all components are in a stable state. - if (!IsPaused) - { - decoupledClock.Stop(); + if (IsPaused) return; - Schedule(() => Pause(force)); - return; - } + // stop the decoupled clock (stops the audio eventually) + decoupledClock.Stop(); + + // stop processing updatess on the offset clock (instantly freezes time for all our components) + offsetClock.ProcessSourceClockFrames = false; + + IsPaused = true; // we need to do a final check after all of our children have processed up to the paused clock time. - // this is to cover cases where, for instance, the player fails in the last processed frame (which would change canPause). - // as the scheduler runs before children updates, let's schedule for the next frame. + // this is to cover cases where, for instance, the player fails in the current processing frame. Schedule(() => { if (!canPause) return; @@ -291,6 +290,11 @@ namespace osu.Game.Screens.Play public void Resume() { + if (!IsPaused) return; + + IsPaused = false; + offsetClock.ProcessSourceClockFrames = true; + lastPauseActionTime = Time.Current; hudOverlay.KeyCounter.IsCounting = true; hudOverlay.Progress.Hide(); From 74c23ff6d8a4f110993141266070a8ed843b85ee Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 17 May 2017 18:42:48 +0900 Subject: [PATCH 16/77] Add base for event parsing code. --- osu.Game/Beatmaps/Beatmap.cs | 3 + .../{EventType.cs => BackgroundEvent.cs} | 15 ++--- osu.Game/Beatmaps/Events/BreakEvent.cs | 28 +++++++++ osu.Game/Beatmaps/Events/Event.cs | 13 ++++ osu.Game/Beatmaps/Events/EventInfo.cs | 26 ++++++++ osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 59 +++++++++++++++---- osu.Game/osu.Game.csproj | 5 +- 7 files changed, 126 insertions(+), 23 deletions(-) rename osu.Game/Beatmaps/Events/{EventType.cs => BackgroundEvent.cs} (52%) create mode 100644 osu.Game/Beatmaps/Events/BreakEvent.cs create mode 100644 osu.Game/Beatmaps/Events/Event.cs create mode 100644 osu.Game/Beatmaps/Events/EventInfo.cs diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 3964fd25a7..a64002e0b0 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK.Graphics; +using osu.Game.Beatmaps.Events; using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Rulesets.Objects; @@ -17,6 +18,7 @@ namespace osu.Game.Beatmaps { public BeatmapInfo BeatmapInfo; public TimingInfo TimingInfo = new TimingInfo(); + public EventInfo EventInfo = new EventInfo(); public readonly List ComboColors = new List { new Color4(17, 136, 170, 255), @@ -40,6 +42,7 @@ namespace osu.Game.Beatmaps { BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo; TimingInfo = original?.TimingInfo ?? TimingInfo; + EventInfo = original?.EventInfo ?? EventInfo; ComboColors = original?.ComboColors ?? ComboColors; } } diff --git a/osu.Game/Beatmaps/Events/EventType.cs b/osu.Game/Beatmaps/Events/BackgroundEvent.cs similarity index 52% rename from osu.Game/Beatmaps/Events/EventType.cs rename to osu.Game/Beatmaps/Events/BackgroundEvent.cs index 0d4be693f0..215373bd3b 100644 --- a/osu.Game/Beatmaps/Events/EventType.cs +++ b/osu.Game/Beatmaps/Events/BackgroundEvent.cs @@ -3,14 +3,11 @@ namespace osu.Game.Beatmaps.Events { - public enum EventType + public class BackgroundEvent : Event { - Background = 0, - Video = 1, - Break = 2, - Colour = 3, - Sprite = 4, - Sample = 5, - Animation = 6 + /// + /// The file name. + /// + public string Filename; } -} \ No newline at end of file +} diff --git a/osu.Game/Beatmaps/Events/BreakEvent.cs b/osu.Game/Beatmaps/Events/BreakEvent.cs new file mode 100644 index 0000000000..78e33f2fbb --- /dev/null +++ b/osu.Game/Beatmaps/Events/BreakEvent.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Beatmaps.Events +{ + public class BreakEvent : Event + { + /// + /// The minimum duration required for a break to have any effect. + /// + private const double min_break_duration = 650; + + /// + /// The break end time. + /// + public double EndTime; + + /// + /// The duration of the break. + /// + public double Duration => EndTime - StartTime; + + /// + /// Whether the break has any effect. Breaks that are too short are culled before they reach the EventInfo. + /// + public bool HasEffect => Duration >= min_break_duration; + } +} diff --git a/osu.Game/Beatmaps/Events/Event.cs b/osu.Game/Beatmaps/Events/Event.cs new file mode 100644 index 0000000000..3af3909462 --- /dev/null +++ b/osu.Game/Beatmaps/Events/Event.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Beatmaps.Events +{ + public abstract class Event + { + /// + /// The event start time. + /// + public double StartTime; + } +} diff --git a/osu.Game/Beatmaps/Events/EventInfo.cs b/osu.Game/Beatmaps/Events/EventInfo.cs new file mode 100644 index 0000000000..646dea898e --- /dev/null +++ b/osu.Game/Beatmaps/Events/EventInfo.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Beatmaps.Events +{ + public class EventInfo + { + /// + /// All the background events. + /// + public readonly List Backgrounds = new List(); + + /// + /// All the break events. + /// + public readonly List Breaks = new List(); + + /// + /// Total duration of all breaks. + /// + public double TotalBreakTime => Breaks.Sum(b => b.Duration); + } +} diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 95213417ed..772c0e9c07 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -204,23 +204,42 @@ namespace osu.Game.Beatmaps.Formats private void handleEvents(Beatmap beatmap, string val) { - if (val.StartsWith(@"//")) - return; - if (val.StartsWith(@" ")) - return; // TODO string[] split = val.Split(','); + EventType type; - int intType; - if (!int.TryParse(split[0], out intType)) + if (!Enum.TryParse(split[0], out type)) + throw new InvalidDataException($@"Unknown event type {split[0]}"); + + // Todo: Implement the rest + switch (type) { - if (!Enum.TryParse(split[0], out type)) - throw new InvalidDataException($@"Unknown event type {split[0]}"); + case EventType.Video: + case EventType.Background: + string filename = split[2].Trim('"'); + + beatmap.EventInfo.Backgrounds.Add(new BackgroundEvent + { + StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), + Filename = filename + }); + + if (type == EventType.Background) + beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + + break; + case EventType.Break: + var breakEvent = new BreakEvent + { + StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), + EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo) + }; + + if (!breakEvent.HasEffect) + return; + + beatmap.EventInfo.Breaks.Add(breakEvent); + break; } - else - type = (EventType)intType; - // TODO: Parse and store the rest of the event - if (type == EventType.Background) - beatmap.BeatmapInfo.Metadata.BackgroundFile = split[2].Trim('"'); } private void handleTimingPoints(Beatmap beatmap, string val) @@ -330,6 +349,9 @@ namespace osu.Game.Beatmaps.Formats if (string.IsNullOrEmpty(line)) continue; + if (line.StartsWith(" ") || line.StartsWith("_") || line.StartsWith("//")) + continue; + if (line.StartsWith(@"osu file format v")) { beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); @@ -390,5 +412,16 @@ namespace osu.Game.Beatmaps.Formats Soft = 2, Drum = 3 } + + internal enum EventType + { + Background = 0, + Video = 1, + Break = 2, + Colour = 3, + Sprite = 4, + Sample = 5, + Animation = 6 + } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9352cfba6e..ed2532d238 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -74,6 +74,10 @@ + + + + @@ -342,7 +346,6 @@ - From b52ec11260f4d6a207d63c7232941cc974fddcc7 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 17 May 2017 20:47:02 +0900 Subject: [PATCH 17/77] Add (almost finished) basis of slider hit object conversion... --- .../Beatmaps/DistanceObjectConversion.cs | 409 ++++++++++++++++++ .../Beatmaps/LegacyConvertType.cs | 62 +++ .../Beatmaps/LegacyConverter.cs | 67 ++- .../Beatmaps/ObjectConversion.cs | 77 ++++ osu.Game.Rulesets.Mania/Beatmaps/ObjectRow.cs | 44 ++ .../osu.Game.Rulesets.Mania.csproj | 4 + 6 files changed, 657 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs create mode 100644 osu.Game.Rulesets.Mania/Beatmaps/LegacyConvertType.cs create mode 100644 osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs create mode 100644 osu.Game.Rulesets.Mania/Beatmaps/ObjectRow.cs diff --git a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs new file mode 100644 index 0000000000..c775c189e1 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs @@ -0,0 +1,409 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using System; +using osu.Game.Rulesets.Mania.MathUtils; +using System.Linq; +using OpenTK; +using osu.Game.Database; +using osu.Game.Audio; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Mania.Beatmaps +{ + internal class DistanceObjectConversion : ObjectConversion + { + private readonly HitObject originalObject; + + private readonly double endTime; + private readonly int repeatCount; + + private LegacyConvertType convertType; + + public DistanceObjectConversion(HitObject originalObject, ObjectRow previousRow, FastRandom random, Beatmap beatmap) + : base(previousRow, random, beatmap) + { + this.originalObject = originalObject; + + ControlPoint overridePoint; + ControlPoint controlPoint = Beatmap.TimingInfo.TimingPointAt(originalObject.StartTime, out overridePoint); + + convertType = LegacyConvertType.None; + if ((overridePoint ?? controlPoint)?.KiaiMode == false) + convertType = LegacyConvertType.LowProbability; + + var distanceData = originalObject as IHasDistance; + var repeatsData = originalObject as IHasRepeats; + + endTime = distanceData?.EndTime ?? 0; + repeatCount = repeatsData?.RepeatCount ?? 1; + } + + public override ObjectRow GenerateConversion() + { + double segmentDuration = endTime / repeatCount; + + if (repeatCount > 1) + { + if (segmentDuration <= 90) + return generateRandomHoldNotes(originalObject.StartTime, endTime, 1); + + if (segmentDuration <= 120) + { + convertType |= LegacyConvertType.ForceNotStack; + return addRandomNotes(originalObject.StartTime, segmentDuration, repeatCount); + } + + if (segmentDuration <= 160) + return addStair(originalObject.StartTime, segmentDuration, repeatCount); + + if (segmentDuration <= 200 && conversionDifficulty > 3) + return addMultipleNotes(originalObject.StartTime, segmentDuration, repeatCount); + + double duration = endTime - originalObject.StartTime; + if (duration >= 4000) + return addNRandomNotes(originalObject.StartTime, endTime, 0.23, 0, 0); + + if (segmentDuration > 400 && duration < 4000 && repeatCount < AvailableColumns - 1 - RandomStart) + return generateTiledHoldNotes(originalObject.StartTime, segmentDuration, repeatCount); + + return generateLongAndNormalNotes(originalObject.StartTime, segmentDuration); + } + + if (segmentDuration <= 110) + { + if (PreviousRow.Columns < AvailableColumns) + convertType |= LegacyConvertType.ForceNotStack; + else + convertType &= ~LegacyConvertType.ForceNotStack; + return addRandomNotes(originalObject.StartTime, segmentDuration, segmentDuration < 80 ? 0 : 1); + } + + if (conversionDifficulty > 6.5) + { + if ((convertType & LegacyConvertType.LowProbability) > 0) + return addNRandomNotes(originalObject.StartTime, endTime, 0.78, 0.3, 0); + return addNRandomNotes(originalObject.StartTime, endTime, 0.85, 0.36, 0.03); + } + + if (conversionDifficulty > 4) + { + if ((convertType & LegacyConvertType.LowProbability) > 0) + return addNRandomNotes(originalObject.StartTime, endTime, 0.43, 0.08, 0); + return addNRandomNotes(originalObject.StartTime, endTime, 0.56, 0.18, 0); + } + + if (conversionDifficulty > 2.5) + { + if ((convertType & LegacyConvertType.LowProbability) > 0) + return addNRandomNotes(originalObject.StartTime, endTime, 0.3, 0, 0); + return addNRandomNotes(originalObject.StartTime, endTime, 0.37, 0.08, 0); + } + + if ((convertType & LegacyConvertType.LowProbability) > 0) + return addNRandomNotes(originalObject.StartTime, endTime, 0.17, 0, 0); + return addNRandomNotes(originalObject.StartTime, endTime, 0.27, 0, 0); + } + + /// + /// Adds random hold notes. + /// + /// Number of hold notes. + /// Start time of each hold note. + /// End time of the hold notes. + /// The new row. + private ObjectRow generateRandomHoldNotes(double startTime, double endTime, int count) + { + var newRow = new ObjectRow(); + + int usableColumns = AvailableColumns - RandomStart - PreviousRow.Columns; + int nextColumn = Random.Next(RandomStart, AvailableColumns); + for (int i = 0; i < Math.Min(usableColumns, count); i++) + { + while (newRow.IsTaken(nextColumn) || PreviousRow.IsTaken(nextColumn)) //find available column + nextColumn = Random.Next(RandomStart, AvailableColumns); + add(newRow, nextColumn, startTime, endTime, count); + } + + // This is can't be combined with the above loop due to RNG + for (int i = 0; i < count - usableColumns; i++) + { + while (newRow.IsTaken(nextColumn)) + nextColumn = Random.Next(RandomStart, AvailableColumns); + add(newRow, nextColumn, startTime, endTime, count); + } + + return newRow; + } + + /// + /// Adds random notes, with one note per row. No stacking. + /// + /// The start time. + /// The separation of notes between rows. + /// The number of rows. + /// The new row. + private ObjectRow addRandomNotes(double startTime, double separationTime, int repeatCount) + { + var newRow = new ObjectRow(); + + int nextColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); + if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousRow.Columns < AvailableColumns) + { + while (PreviousRow.IsTaken(nextColumn)) + nextColumn = Random.Next(RandomStart, AvailableColumns); + } + + int lastColumn = nextColumn; + for (int i = 0; i <= repeatCount; i++) + { + add(newRow, nextColumn, startTime, startTime); + while (nextColumn == lastColumn) + nextColumn = Random.Next(RandomStart, AvailableColumns); + + lastColumn = nextColumn; + startTime += separationTime; + } + + return newRow; + } + + /// + /// Creates a stair of notes, with one note per row. + /// + /// The start time. + /// The separation of notes between rows. + /// The number of rows/notes. + /// The new row. + private ObjectRow addStair(double startTime, double separationTime, int repeatCount) + { + var newRow = new ObjectRow(); + + int column = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); + bool increasing = Random.NextDouble() > 0.5; + + for (int i = 0; i <= repeatCount; i++) + { + add(newRow, column, startTime, startTime); + startTime += separationTime; + + // Check if we're at the borders of the stage, and invert the pattern if so + if (increasing) + { + if (column >= AvailableColumns - 1) + { + increasing = false; + column--; + } + else + column++; + } + else + { + if (column <= RandomStart) + { + increasing = true; + column++; + } + else + column--; + } + } + + return newRow; + } + + /// + /// Adds random notes, with 1-2 notes per row. No stacking. + /// + /// The start time. + /// The separation of notes between rows. + /// The number of rows. + /// The new row. + private ObjectRow addMultipleNotes(double startTime, double separationTime, int repeatCount) + { + var newRow = new ObjectRow(); + + bool legacy = AvailableColumns >= 4 && AvailableColumns <= 8; + int interval = Random.Next(1, AvailableColumns - (legacy ? 1 : 0)); + + int nextColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); + for (int i = 0; i <= repeatCount; i++) + { + add(newRow, nextColumn, startTime, startTime, 2); + + nextColumn += interval; + if (nextColumn >= AvailableColumns - RandomStart) + nextColumn = nextColumn - AvailableColumns - RandomStart + (legacy ? 1 : 0); + nextColumn += RandomStart; + + // If we're in 2K, let's not add many consecutive doubles + if (AvailableColumns > 2) + add(newRow, nextColumn, startTime, startTime, 2); + + nextColumn = Random.Next(RandomStart, AvailableColumns); + startTime += separationTime; + } + + return newRow; + } + + /// + /// Generates random hold notes. The amount of hold notes generated is determined by probabilities. + /// + /// The hold note start time. + /// The hold note end time. + /// The probability required for 2 hold notes to be generated. + /// The probability required for 3 hold notes to be generated. + /// The probability required for 4 hold notes to be generated. + /// The new row. + private ObjectRow addNRandomNotes(double startTime, double endTime, double p2, double p3, double p4) + { + switch (AvailableColumns) + { + case 2: + p2 = 0; + p3 = 0; + p4 = 0; + break; + case 3: + p2 = Math.Max(p2, 0.1); + p3 = 0; + p4 = 0; + break; + case 4: + p2 = Math.Max(p2, 0.3); + p3 = Math.Max(p3, 0.04); + p4 = 0; + break; + case 5: + p2 = Math.Max(p2, 0.34); + p3 = Math.Max(p3, 0.1); + p4 = Math.Max(p4, 0.03); + break; + } + + Func isDoubleSample = sample => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH; + + bool canGenerateTwoNotes = (convertType & LegacyConvertType.LowProbability) == 0; + canGenerateTwoNotes &= originalObject.Samples.Any(isDoubleSample) || sampleInfoListAt(originalObject.StartTime, originalObject.StartTime - endTime).Any(isDoubleSample); + + if (canGenerateTwoNotes) + p2 = 0; + + return generateRandomHoldNotes(startTime, endTime, GetRandomNoteCount(p2, p3, p4)); + } + + private ObjectRow generateTiledHoldNotes(double startTime, double separationTime, int noteCount) + { + var newRow = new ObjectRow(); + + int columnRepeat = Math.Min(noteCount, AvailableColumns); + + int nextColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); + if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousRow.Columns < AvailableColumns) + { + while (PreviousRow.IsTaken(nextColumn)) + nextColumn = Random.Next(RandomStart, AvailableColumns); + } + + for (int i = 0; i < columnRepeat; i++) + { + while (newRow.IsTaken(nextColumn)) + nextColumn = Random.Next(RandomStart, AvailableColumns); + + add(newRow, nextColumn, startTime, endTime, noteCount); + startTime += separationTime; + } + + return newRow; + } + + private ObjectRow generateLongAndNormalNotes(double startTime, double separationTime) + { + var newRow = new ObjectRow(); + + int holdColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); + if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousRow.Columns < AvailableColumns) + { + while (PreviousRow.IsTaken(holdColumn)) + holdColumn = Random.Next(RandomStart, AvailableColumns); + } + + // Create the hold note + add(newRow, holdColumn, startTime, separationTime * repeatCount); + + // Todo: Complete + } + + private void add(ObjectRow row, int column, double startTime, double endTime, int siblings = 1) + { + ManiaHitObject newObject; + + if (startTime == endTime) + { + newObject = new Note + { + StartTime = startTime, + Samples = originalObject.Samples, + Column = column + }; + } + else + { + newObject = new HoldNote + { + StartTime = startTime, + Samples = originalObject.Samples, + Column = column, + Duration = endTime - startTime + }; + } + + // Todo: Consider siblings and write sample volumes (probably at ManiaHitObject level) + + row.Add(newObject); + } + + private SampleInfoList sampleInfoListAt(double time, double separationTime) + { + var curveData = originalObject as IHasCurve; + + if (curveData == null) + return originalObject.Samples; + + int index = (int)(separationTime == 0 ? 0 : (time - originalObject.StartTime) / separationTime); + return curveData.RepeatSamples[index]; + } + + private double? _conversionDifficulty; + private double conversionDifficulty + { + get + { + if (_conversionDifficulty != null) + return _conversionDifficulty.Value; + + HitObject lastObject = Beatmap.HitObjects.LastOrDefault(); + HitObject firstObject = Beatmap.HitObjects.FirstOrDefault(); + + double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0); + drainTime -= Beatmap.EventInfo.TotalBreakTime; + + if (drainTime == 0) + drainTime = 10000; + + BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.Difficulty; + _conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; + _conversionDifficulty = Math.Min(_conversionDifficulty.Value, 12); + + return _conversionDifficulty.Value; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConvertType.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConvertType.cs new file mode 100644 index 0000000000..086bc89292 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConvertType.cs @@ -0,0 +1,62 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Rulesets.Mania.Beatmaps +{ + [Flags] + internal enum LegacyConvertType + { + None = 0, + /// + /// Keep the same as last row. + /// + ForceStack = 1, + /// + /// Keep different from last row. + /// + ForceNotStack = 2, + /// + /// Keep as single note at its original position. + /// + KeepSingle = 4, + /// + /// Use a lower random value. + /// + LowProbability = 8, + /// + /// Reserved. + /// + Alternate = 16, + /// + /// Ignore the repeat count. + /// + ForceSigSlider = 32, + /// + /// Convert slider to circle. + /// + ForceNotSlider = 64, + /// + /// Notes gathered together. + /// + Gathered = 128, + Mirror = 256, + /// + /// Change 0 -> 6. + /// + Reverse = 512, + /// + /// 1 -> 5 -> 1 -> 5 like reverse. + /// + Cycle = 1024, + /// + /// Next note will be at column + 1. + /// + Stair = 2048, + /// + /// Next note will be at column - 1. + /// + ReverseStair = 4096 + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs index 544f396f4a..ebc6ed2dcf 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Objects.Types; using System; using System.Collections.Generic; using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Beatmaps.Timing; namespace osu.Game.Rulesets.Mania.Beatmaps { @@ -17,28 +18,51 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// internal class LegacyConverter { + private const int max_previous_note_times = 7; + private readonly FastRandom random; + private readonly List previousNoteTimes; + private readonly bool[] previousNotes; + private readonly double lastNoteTime; + private readonly float lastNotePosition; + private readonly int availableColumns; private readonly float localXDivisor; private readonly Beatmap beatmap; - public LegacyConverter(Beatmap beatmap) + public LegacyConverter(ObjectRow previousrow, Beatmap beatmap) { this.beatmap = beatmap; int seed = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.DrainRate + beatmap.BeatmapInfo.Difficulty.CircleSize) * 20 + (int)(beatmap.BeatmapInfo.Difficulty.OverallDifficulty * 41.2) + (int)Math.Round(beatmap.BeatmapInfo.Difficulty.ApproachRate); + random = new FastRandom(seed); availableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); localXDivisor = 512.0f / availableColumns; + + previousNoteTimes = new List(max_previous_note_times); + previousNotes = new bool[availableColumns]; } public IEnumerable Convert(HitObject original) { + var maniaOriginal = original as ManiaHitObject; + if (maniaOriginal != null) + { + yield return maniaOriginal; + yield break; + } + if (beatmap.BeatmapInfo.RulesetID == 3) yield return generateSpecific(original); + else + { + foreach (ManiaHitObject c in generateConverted(original)) + yield return c; + } } private ManiaHitObject generateSpecific(HitObject original) @@ -59,12 +83,43 @@ namespace osu.Game.Rulesets.Mania.Beatmaps }; } - return new Note + if (positionData != null) { - StartTime = original.StartTime, - Samples = original.Samples, - Column = column - }; + return new Note + { + StartTime = original.StartTime, + Samples = original.Samples, + Column = column + }; + } + + return null; + } + + private IEnumerable generateConverted(HitObject original) + { + var endTimeData = original as IHasEndTime; + var distanceData = original as IHasDistance; + var positionData = original as IHasPosition; + + ObjectConversion conversion = null; + + if (distanceData != null) + conversion = new DistanceObjectConversion(distanceData, beatmap); + else if (endTimeData != null) + { + // Spinner + } + else if (positionData != null) + { + // Circle + } + + if (conversion == null) + yield break; + + foreach (ManiaHitObject obj in conversion.GenerateConversion()) + yield return obj; } private int getColumn(float position) => MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, availableColumns - 1); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs new file mode 100644 index 0000000000..9dee1116e0 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs @@ -0,0 +1,77 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.MathUtils; +using System; + +namespace osu.Game.Rulesets.Mania.Beatmaps +{ + internal abstract class ObjectConversion + { + protected readonly int AvailableColumns; + protected readonly int RandomStart; + + protected ObjectRow PreviousRow; + protected readonly FastRandom Random; + protected readonly Beatmap Beatmap; + + protected ObjectConversion(ObjectRow previousRow, FastRandom random, Beatmap beatmap) + { + PreviousRow = previousRow; + Random = random; + Beatmap = beatmap; + + AvailableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); + RandomStart = AvailableColumns == 8 ? 1 : 0; + } + + /// + /// Generates a new row filled with converted hit objects. + /// + /// The new row. + public abstract ObjectRow GenerateConversion(); + + /// + /// Converts an x-position into a column. + /// + /// The x-position. + /// Whether to treat as 7K + 1. + /// The column. + protected int GetColumn(float position, bool allowSpecial = false) + { + if (allowSpecial && AvailableColumns == 8) + { + const float local_x_divisor = 512f / 7; + return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1; + } + + float localXDivisor = 512f / AvailableColumns; + return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, AvailableColumns - 1); + } + + /// + /// Generates a count of notes to be generated from 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. + /// Probability for 6 notes to be generated. + /// The amount of notes to be generated. + protected int GetRandomNoteCount(double p2, double p3, double p4 = 1, double p5 = 1, double p6 = 1) + { + double val = Random.NextDouble(); + if (val >= 1 - p6) + return 6; + if (val >= 1 - p5) + return 5; + if (val >= 1 - p4) + return 4; + if (val >= 1 - p3) + return 3; + return val >= 1 - p2 ? 2 : 1; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ObjectRow.cs b/osu.Game.Rulesets.Mania/Beatmaps/ObjectRow.cs new file mode 100644 index 0000000000..fe51c16bed --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/ObjectRow.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mania.Objects; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Rulesets.Mania.Beatmaps +{ + internal class ObjectRow + { + private readonly List hitObjects = new List(); + public IEnumerable HitObjects => hitObjects; + + /// + /// Whether a column of this row has been taken. + /// + /// The column index. + /// Whether the column already contains a hit object. + public bool IsTaken(int column) => hitObjects.Exists(h => h.Column == column); + + /// + /// Amount of columns taken up by hit objects in this row. + /// + public int Columns => HitObjects.GroupBy(h => h.Column).Count(); + + /// + /// Adds a hit object to this row. + /// + /// The hit object to add. + public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject); + + /// + /// Clears this row. + /// + public void Clear() => hitObjects.Clear(); + + /// + /// Removes a hit object from this row. + /// + /// The hit object to remove. + public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject); + } +} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 781dc3e228..36c17ccf04 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -47,8 +47,12 @@ + + + + From 41c252760d560bb1a567e0a0ff7814154ebd6c9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2017 21:57:01 +0900 Subject: [PATCH 18/77] Move all pause logic into PauseContainer --- .../Tests/TestCaseMenuOverlays.cs | 4 +- osu.Game/Screens/Play/KeyCounterCollection.cs | 11 +- osu.Game/Screens/Play/MenuOverlay.cs | 20 +- osu.Game/Screens/Play/Pause/PauseButton.cs | 19 -- .../Screens/Play/Pause/PauseProgressBar.cs | 149 ------------- .../Screens/Play/Pause/PauseProgressGraph.cs | 12 -- osu.Game/Screens/Play/PauseContainer.cs | 155 ++++++++++++++ osu.Game/Screens/Play/PauseOverlay.cs | 40 ---- osu.Game/Screens/Play/Player.cs | 195 ++++++------------ osu.Game/Screens/Play/SongProgress.cs | 2 + osu.Game/osu.Game.csproj | 5 +- 11 files changed, 242 insertions(+), 370 deletions(-) delete mode 100644 osu.Game/Screens/Play/Pause/PauseButton.cs delete mode 100644 osu.Game/Screens/Play/Pause/PauseProgressBar.cs delete mode 100644 osu.Game/Screens/Play/Pause/PauseProgressGraph.cs create mode 100644 osu.Game/Screens/Play/PauseContainer.cs delete mode 100644 osu.Game/Screens/Play/PauseOverlay.cs diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs b/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs index acf98ea86b..23fe8f16db 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs @@ -12,7 +12,7 @@ namespace osu.Desktop.VisualTests.Tests { public override string Description => @"Tests pause and fail overlays"; - private PauseOverlay pauseOverlay; + private PauseContainer.PauseOverlay pauseOverlay; private FailOverlay failOverlay; private int retryCount; @@ -22,7 +22,7 @@ namespace osu.Desktop.VisualTests.Tests retryCount = 0; - Add(pauseOverlay = new PauseOverlay + Add(pauseOverlay = new PauseContainer.PauseOverlay { OnResume = () => Logger.Log(@"Resume"), OnRetry = () => Logger.Log(@"Retry"), diff --git a/osu.Game/Screens/Play/KeyCounterCollection.cs b/osu.Game/Screens/Play/KeyCounterCollection.cs index ef6e635c49..4a561e6036 100644 --- a/osu.Game/Screens/Play/KeyCounterCollection.cs +++ b/osu.Game/Screens/Play/KeyCounterCollection.cs @@ -41,12 +41,11 @@ namespace osu.Game.Screens.Play get { return isCounting; } set { - if (value != isCounting) - { - isCounting = value; - foreach (var child in Children) - child.IsCounting = value; - } + if (value == isCounting) return; + + isCounting = value; + foreach (var child in Children) + child.IsCounting = value; } } diff --git a/osu.Game/Screens/Play/MenuOverlay.cs b/osu.Game/Screens/Play/MenuOverlay.cs index fa522956f7..424b26350f 100644 --- a/osu.Game/Screens/Play/MenuOverlay.cs +++ b/osu.Game/Screens/Play/MenuOverlay.cs @@ -8,11 +8,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Graphics.Sprites; -using osu.Game.Screens.Play.Pause; using OpenTK; using OpenTK.Graphics; using osu.Game.Graphics; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Play @@ -89,7 +89,7 @@ namespace osu.Game.Screens.Play protected void AddButton(string text, Color4 colour, Action action) { - Buttons.Add(new PauseButton + Buttons.Add(new Button { Text = text, ButtonColour = colour, @@ -179,12 +179,6 @@ namespace osu.Game.Screens.Play } } }, - new PauseProgressBar - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Width = 1f - } }; Retries = 0; @@ -195,5 +189,15 @@ namespace osu.Game.Screens.Play AlwaysReceiveInput = true; RelativeSizeAxes = Axes.Both; } + + public class Button : DialogButton + { + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + SampleHover = audio.Sample.Get(@"Menu/menuclick"); + SampleClick = audio.Sample.Get(@"Menu/menuback"); + } + } } } diff --git a/osu.Game/Screens/Play/Pause/PauseButton.cs b/osu.Game/Screens/Play/Pause/PauseButton.cs deleted file mode 100644 index 7698913ea5..0000000000 --- a/osu.Game/Screens/Play/Pause/PauseButton.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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.Audio; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Screens.Play.Pause -{ - public class PauseButton : DialogButton - { - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - SampleHover = audio.Sample.Get(@"Menu/menuclick"); - SampleClick = audio.Sample.Get(@"Menu/menuback"); - } - } -} diff --git a/osu.Game/Screens/Play/Pause/PauseProgressBar.cs b/osu.Game/Screens/Play/Pause/PauseProgressBar.cs deleted file mode 100644 index bfde7dd6d7..0000000000 --- a/osu.Game/Screens/Play/Pause/PauseProgressBar.cs +++ /dev/null @@ -1,149 +0,0 @@ -// 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.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Beatmaps; -using OpenTK.Graphics; - -namespace osu.Game.Screens.Play.Pause -{ - public class PauseProgressBar : Container - { - private readonly Color4 fillColour = new Color4(221, 255, 255, 255); - private readonly Color4 glowColour = new Color4(221, 255, 255, 150); - - private readonly Container fill; - private WorkingBeatmap current; - - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) - { - current = osuGame.Beatmap.Value; - } - - protected override void Update() - { - base.Update(); - - if (current?.TrackLoaded ?? false) - { - fill.Width = (float)(current.Track.CurrentTime / current.Track.Length); - } - } - - public PauseProgressBar() - { - RelativeSizeAxes = Axes.X; - Height = 60; - - Children = new Drawable[] - { - new PauseProgressGraph - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Height = 35, - Margin = new MarginPadding - { - Bottom = 5 - } - }, - new Container - { - Origin = Anchor.BottomRight, - Anchor = Anchor.BottomRight, - RelativeSizeAxes = Axes.X, - Height = 5, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.5f - } - } - }, - fill = new Container - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Width = 0, - Height = 60, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Masking = true, - Children = new Drawable[] - { - new Container - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = 5, - Masking = true, - EdgeEffect = new EdgeEffect - { - Type = EdgeEffectType.Glow, - Colour = glowColour, - Radius = 5 - }, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = fillColour - } - } - } - } - }, - new Container - { - Origin = Anchor.BottomRight, - Anchor = Anchor.BottomRight, - Width = 2, - Height = 35, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White - }, - new Container - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.TopCentre, - Width = 14, - Height = 25, - CornerRadius = 5, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White - } - } - } - } - } - } - } - }; - } - } -} diff --git a/osu.Game/Screens/Play/Pause/PauseProgressGraph.cs b/osu.Game/Screens/Play/Pause/PauseProgressGraph.cs deleted file mode 100644 index 89b9b42b86..0000000000 --- a/osu.Game/Screens/Play/Pause/PauseProgressGraph.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Screens.Play.Pause -{ - public class PauseProgressGraph : Container - { - // TODO: Implement the pause progress graph - } -} diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs new file mode 100644 index 0000000000..1de62048b7 --- /dev/null +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -0,0 +1,155 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Timing; +using osu.Game.Graphics; +using OpenTK.Graphics; +using OpenTK.Input; + +namespace osu.Game.Screens.Play +{ + /// + /// A container which handles pausing children, displaying a pause overlay with choices etc. + /// This alleviates a lot of the intricate pause logic from being in + /// + public class PauseContainer : Container + { + public bool IsPaused { get; private set; } + + public bool AllowExit => IsPaused && pauseOverlay.Alpha == 1; + + public Func CheckCanPause; + + private const double pause_cooldown = 1000; + private double lastPauseActionTime; + + private readonly PauseOverlay pauseOverlay; + + private readonly Container content; + + protected override Container Content => content; + + public int Retries { set { pauseOverlay.Retries = value; } } + + public bool CanPause => (CheckCanPause?.Invoke() ?? true) && Time.Current >= lastPauseActionTime + pause_cooldown; + + public Action OnRetry; + public Action OnQuit; + + public Action OnResume; + public Action OnPause; + + public IAdjustableClock AudioClock; + public FramedClock FramedClock; + + public PauseContainer() + { + RelativeSizeAxes = Axes.Both; + + AddInternal(content = new Container { RelativeSizeAxes = Axes.Both }); + + AddInternal(pauseOverlay = new PauseOverlay + { + OnResume = delegate + { + Delay(400); + Schedule(Resume); + }, + OnRetry = () => OnRetry(), + OnQuit = () => OnQuit(), + }); + } + + public void Pause(bool force = false) + { + if (!CanPause && !force) return; + + if (IsPaused) return; + + // stop the decoupled clock (stops the audio eventually) + AudioClock.Stop(); + + // stop processing updatess on the offset clock (instantly freezes time for all our components) + FramedClock.ProcessSourceClockFrames = false; + + IsPaused = true; + + // we need to do a final check after all of our children have processed up to the paused clock time. + // this is to cover cases where, for instance, the player fails in the current processing frame. + Schedule(() => + { + if (!CanPause) return; + + lastPauseActionTime = Time.Current; + + OnPause?.Invoke(); + pauseOverlay.Show(); + }); + } + + public void Resume() + { + if (!IsPaused) return; + + IsPaused = false; + FramedClock.ProcessSourceClockFrames = true; + + lastPauseActionTime = Time.Current; + + OnResume?.Invoke(); + + pauseOverlay.Hide(); + AudioClock.Start(); + } + + private OsuGameBase game; + + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + this.game = game; + } + + protected override void Update() + { + // eagerly pause when we lose window focus (if we are locally playing). + if (!game.IsActive && CanPause) + Pause(); + + base.Update(); + } + + public class PauseOverlay : MenuOverlay + { + public Action OnResume; + + public override string Header => "paused"; + public override string Description => "you're not going to do what i think you're going to do, are ya?"; + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (!args.Repeat && args.Key == Key.Escape) + { + Buttons.Children.First().TriggerClick(); + return true; + } + + return base.OnKeyDown(state, args); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AddButton("Continue", colours.Green, OnResume); + AddButton("Retry", colours.YellowDark, OnRetry); + AddButton("Quit", new Color4(170, 27, 39, 255), OnQuit); + } + } + } +} diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs deleted file mode 100644 index 9561979751..0000000000 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ /dev/null @@ -1,40 +0,0 @@ -// 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 osu.Framework.Input; -using osu.Game.Graphics; -using OpenTK.Input; -using OpenTK.Graphics; -using osu.Framework.Allocation; - -namespace osu.Game.Screens.Play -{ - public class PauseOverlay : MenuOverlay - { - public Action OnResume; - - public override string Header => "paused"; - public override string Description => "you're not going to do what i think you're going to do, are ya?"; - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (!args.Repeat && args.Key == Key.Escape) - { - Buttons.Children.First().TriggerClick(); - return true; - } - - return base.OnKeyDown(state, args); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AddButton("Continue", colours.Green, OnResume); - AddButton("Retry", colours.YellowDark, OnRetry); - AddButton("Quit", new Color4(170, 27, 39, 255), OnQuit); - } - } -} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 68fbbaddcc..d9585b8c04 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -32,29 +32,24 @@ namespace osu.Game.Screens.Play internal override bool ShowOverlays => false; - internal override bool HasLocalCursorDisplayed => !IsPaused && !HasFailed && HitRenderer.ProvidingUserCursor; + internal override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && HitRenderer.ProvidingUserCursor; public BeatmapInfo BeatmapInfo; public Action RestartRequested; - public bool IsPaused { get; private set; } - internal override bool AllowRulesetChange => false; public bool HasFailed { get; private set; } public int RestartCount; - private const double pause_cooldown = 1000; - private double lastPauseActionTime; - - private bool canPause => ValidForResume && !HasFailed && Time.Current >= lastPauseActionTime + pause_cooldown; - private IAdjustableClock adjustableSourceClock; private FramedOffsetClock offsetClock; private DecoupleableInterpolatingFramedClock decoupledClock; + private PauseContainer pauseContainer; + private RulesetInfo ruleset; private ScoreProcessor scoreProcessor; @@ -70,10 +65,7 @@ namespace osu.Game.Screens.Play private SkipButton skipButton; - private Container hitRendererContainer; - private HUDOverlay hudOverlay; - private PauseOverlay pauseOverlay; private FailOverlay failOverlay; [BackgroundDependencyLoader(permitNulls: true)] @@ -152,14 +144,63 @@ namespace osu.Game.Screens.Play decoupledClock.ChangeSource(adjustableSourceClock); }); - scoreProcessor = HitRenderer.CreateScoreProcessor(); - - hudOverlay = new StandardHUDOverlay() + Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre + pauseContainer = new PauseContainer + { + AudioClock = decoupledClock, + FramedClock = offsetClock, + OnRetry = Restart, + OnQuit = Exit, + CheckCanPause = () => ValidForResume && !HasFailed, + Retries = RestartCount, + OnPause = () => { + hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused; + }, + OnResume = () => { + hudOverlay.KeyCounter.IsCounting = true; + }, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Clock = offsetClock, + Children = new Drawable[] + { + HitRenderer, + skipButton = new SkipButton + { + Alpha = 0, + Margin = new MarginPadding { Bottom = 140 } // this is temporary + }, + } + }, + hudOverlay = new StandardHUDOverlay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }, + } + }, + failOverlay = new FailOverlay + { + OnRetry = Restart, + OnQuit = Exit, + }, + new HotkeyRetryOverlay + { + Action = () => { + //we want to hide the hitrenderer immediately (looks better). + //we may be able to remove this once the mouse cursor trail is improved. + HitRenderer?.Hide(); + Restart(); + }, + } }; + scoreProcessor = HitRenderer.CreateScoreProcessor(); + hudOverlay.KeyCounter.Add(rulesetInstance.CreateGameplayKeys()); hudOverlay.BindProcessor(scoreProcessor); hudOverlay.BindHitRenderer(HitRenderer); @@ -176,61 +217,6 @@ namespace osu.Game.Screens.Play //bind ScoreProcessor to ourselves (for a fail situation) scoreProcessor.Failed += onFail; - - Children = new Drawable[] - { - hitRendererContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Clock = offsetClock, - Children = new Drawable[] - { - HitRenderer, - skipButton = new SkipButton { Alpha = 0 }, - } - }, - } - }, - hudOverlay, - pauseOverlay = new PauseOverlay - { - OnResume = delegate - { - Delay(400); - Schedule(Resume); - }, - OnRetry = Restart, - OnQuit = Exit, - }, - failOverlay = new FailOverlay - { - OnRetry = Restart, - OnQuit = Exit, - }, - new HotkeyRetryOverlay - { - Action = () => { - //we want to hide the hitrenderer immediately (looks better). - //we may be able to remove this once the mouse cursor trail is improved. - HitRenderer?.Hide(); - Restart(); - }, - } - }; - } - - protected override void Update() - { - // eagerly pause when we lose window focus (if we are locally playing). - if (!Game.IsActive && !HitRenderer.HasReplayLoaded) - Pause(); - - base.Update(); } private void initializeSkipButton() @@ -260,48 +246,6 @@ namespace osu.Game.Screens.Play skipButton.Expire(); } - public void Pause(bool force = false) - { - if (!canPause && !force) return; - - if (IsPaused) return; - - // stop the decoupled clock (stops the audio eventually) - decoupledClock.Stop(); - - // stop processing updatess on the offset clock (instantly freezes time for all our components) - offsetClock.ProcessSourceClockFrames = false; - - IsPaused = true; - - // we need to do a final check after all of our children have processed up to the paused clock time. - // this is to cover cases where, for instance, the player fails in the current processing frame. - Schedule(() => - { - if (!canPause) return; - - lastPauseActionTime = Time.Current; - hudOverlay.KeyCounter.IsCounting = false; - hudOverlay.Progress.Show(); - pauseOverlay.Retries = RestartCount; - pauseOverlay.Show(); - }); - } - - public void Resume() - { - if (!IsPaused) return; - - IsPaused = false; - offsetClock.ProcessSourceClockFrames = true; - - lastPauseActionTime = Time.Current; - hudOverlay.KeyCounter.IsCounting = true; - hudOverlay.Progress.Hide(); - pauseOverlay.Hide(); - decoupledClock.Start(); - } - public void Restart() { ValidForResume = false; @@ -367,8 +311,8 @@ namespace osu.Game.Screens.Play initializeSkipButton(); }); - hitRendererContainer.Alpha = 0; - hitRendererContainer.FadeIn(750, EasingTypes.OutQuint); + pauseContainer.Alpha = 0; + pauseContainer.FadeIn(750, EasingTypes.OutQuint); } protected override void OnSuspending(Screen next) @@ -379,23 +323,14 @@ namespace osu.Game.Screens.Play protected override bool OnExiting(Screen next) { - if (!HasFailed && ValidForResume) + if (HasFailed || !ValidForResume || pauseContainer.AllowExit || HitRenderer.HasReplayLoaded) { - if (pauseOverlay != null && !HitRenderer.HasReplayLoaded) - { - //pause screen override logic. - if (pauseOverlay?.State == Visibility.Hidden && !canPause) return true; - - if (!IsPaused) // For if the user presses escape quickly when entering the map - { - Pause(); - return true; - } - } + fadeOut(); + return base.OnExiting(next); } - fadeOut(); - return base.OnExiting(next); + pauseContainer.Pause(); + return true; } private void fadeOut() @@ -410,6 +345,6 @@ namespace osu.Game.Screens.Play Background?.FadeTo(1f, fade_out_duration); } - protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value && !IsPaused; + protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value && !pauseContainer.IsPaused; } } diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 887442b8c0..6b3f81628c 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -31,6 +31,8 @@ namespace osu.Game.Screens.Play public Action OnSeek; + public override bool HandleInput => AllowSeeking; + private IClock audioClock; public IClock AudioClock { set { audioClock = info.AudioClock = value; } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 63acdb1914..6cd32d7fea 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -231,6 +231,7 @@ + @@ -254,8 +255,6 @@ - - @@ -395,8 +394,6 @@ - - From d606b5b3a73a70c51d8f89c06bebe2d753f85a6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2017 22:13:56 +0900 Subject: [PATCH 19/77] Fix CI warning --- osu.Game/Overlays/ChatOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index cbcfbbfd9e..2836be22ae 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -206,7 +206,7 @@ namespace osu.Game.Overlays private long? lastMessageId; - private List careChannels = new List(); + private readonly List careChannels = new List(); private readonly List loadedChannels = new List(); From d1f85c3f8e6dc4982a731b51073b2b948fdafa35 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2017 23:21:23 +0900 Subject: [PATCH 20/77] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 67f3958036..f8e5b10f68 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 67f39580365f7d0a42f8788eae2b60881dde1c67 +Subproject commit f8e5b10f6883af83ffbc431b03fe4ee3e89797a6 From 817bae3219871b7541569984522c11cb8425ed20 Mon Sep 17 00:00:00 2001 From: MrTheMake Date: Thu, 18 May 2017 01:54:17 +0200 Subject: [PATCH 21/77] Fixes the auto mod not hitting stacks of notes on certain maps. --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index f8365bf9ab..db99ddf0d4 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -92,24 +92,24 @@ namespace osu.Game.Rulesets.Osu.Replays if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); - if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, ReplayButtonState.None)); + if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); - if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, ReplayButtonState.None)); + if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); - if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, ReplayButtonState.None)); + if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } } private void addHitObjectReplay(OsuHitObject h) { // Default values for circles/sliders - Vector2 startPosition = h.Position; + Vector2 startPosition = h.StackedPosition; EasingTypes easing = preferredEasing; float spinnerDirection = -1; @@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Replays // TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime. double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY; int endDelay = h is Spinner ? 1 : 0; - ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, ReplayButtonState.None); + ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.StackedEndPosition.X, h.StackedEndPosition.Y, ReplayButtonState.None); // Decrement because we want the previous frame, not the next one int index = FindInsertionIndex(startFrame) - 1; From 91859042e8f5f167291b1ade81f3a0a07128d97f Mon Sep 17 00:00:00 2001 From: MrTheMake Date: Thu, 18 May 2017 02:15:16 +0200 Subject: [PATCH 22/77] Fix not all cases of the auto replay generator fix being covered. --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index db99ddf0d4..22246af5fd 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -91,17 +91,17 @@ namespace osu.Game.Rulesets.Osu.Replays // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } } From 58ebb24032350bf2ef79aef08ae9a5dd44833efc Mon Sep 17 00:00:00 2001 From: MrTheMake Date: Thu, 18 May 2017 02:17:49 +0200 Subject: [PATCH 23/77] Use the end position of a previous note stack rather than the start position. --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 22246af5fd..5ede3f56f5 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -91,17 +91,17 @@ namespace osu.Game.Rulesets.Osu.Replays // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } } From 22128f158cfb774aa284616ed2849f111ccae4c1 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 12:10:01 +0900 Subject: [PATCH 24/77] Fix a few errors. --- osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs | 2 ++ osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs index c775c189e1..5df4279cca 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs @@ -339,6 +339,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps add(newRow, holdColumn, startTime, separationTime * repeatCount); // Todo: Complete + + return newRow; } private void add(ObjectRow row, int column, double startTime, double endTime, int siblings = 1) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs index ebc6ed2dcf..7c8e58862b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private readonly Beatmap beatmap; - public LegacyConverter(ObjectRow previousrow, Beatmap beatmap) + public LegacyConverter(Beatmap beatmap) { this.beatmap = beatmap; @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps if (conversion == null) yield break; - foreach (ManiaHitObject obj in conversion.GenerateConversion()) + foreach (ManiaHitObject obj in conversion.GenerateConversion().HitObjects) yield return obj; } From e9275138e1b0312cdc9e5a306ec52eb86807cad3 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 12:39:37 +0900 Subject: [PATCH 25/77] Finish up DistanceObjectConversion. --- .../Beatmaps/DistanceObjectConversion.cs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs index 5df4279cca..6bc7c8c45c 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs @@ -338,7 +338,37 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // Create the hold note add(newRow, holdColumn, startTime, separationTime * repeatCount); - // Todo: Complete + int noteCount = 0; + if (conversionDifficulty > 6.5) + noteCount = GetRandomNoteCount(0.63, 0); + else if (conversionDifficulty > 4) + noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0.12 : 0.45, 0); + else if (conversionDifficulty > 2.5) + noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0 : 0.24, 0); + noteCount = Math.Min(AvailableColumns - 1, noteCount); + + bool ignoreHead = !sampleInfoListAt(startTime, separationTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); + int nextColumn = Random.Next(RandomStart, AvailableColumns); + + var tempRow = new ObjectRow(); + for (int i = 0; i <= repeatCount; i++) + { + if (!(ignoreHead && startTime == originalObject.StartTime)) + { + for (int j = 0; j < noteCount; j++) + { + while (tempRow.IsTaken(nextColumn) || nextColumn == holdColumn) + nextColumn = Random.Next(RandomStart, AvailableColumns); + add(tempRow, nextColumn, startTime, startTime, noteCount + 1); + } + } + + foreach (ManiaHitObject obj in tempRow.HitObjects) + newRow.Add(obj); + + tempRow.Clear(); + startTime += separationTime; + } return newRow; } From f6dc85c2f8334d8ab1ab368d4a3d6dcdb6f9c038 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 12:39:51 +0900 Subject: [PATCH 26/77] Fix incorrect note count probabilities. --- osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs index 9dee1116e0..bee7477261 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// Probability for 5 notes to be generated. /// Probability for 6 notes to be generated. /// The amount of notes to be generated. - protected int GetRandomNoteCount(double p2, double p3, double p4 = 1, double p5 = 1, double p6 = 1) + protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0) { double val = Random.NextDouble(); if (val >= 1 - p6) From 16eda2467fc87a563b06f2e6552569968f878063 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 12:50:12 +0900 Subject: [PATCH 27/77] Make compile. --- osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs index 7c8e58862b..18dea8ebc3 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs @@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private readonly double lastNoteTime; private readonly float lastNotePosition; + private ObjectRow lastRow = new ObjectRow(); + private readonly int availableColumns; private readonly float localXDivisor; @@ -105,7 +107,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps ObjectConversion conversion = null; if (distanceData != null) - conversion = new DistanceObjectConversion(distanceData, beatmap); + conversion = new DistanceObjectConversion(original, lastRow, random, beatmap); else if (endTimeData != null) { // Spinner @@ -118,8 +120,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps if (conversion == null) yield break; - foreach (ManiaHitObject obj in conversion.GenerateConversion().HitObjects) + ObjectRow newRow = conversion.GenerateConversion(); + + foreach (ManiaHitObject obj in newRow.HitObjects) yield return obj; + + lastRow = newRow; } private int getColumn(float position) => MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, availableColumns - 1); From c1a44ea6b4f1d2452ad293d908cf97698b55a2b3 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 13:21:54 +0900 Subject: [PATCH 28/77] Renaming + commenting. --- .../Beatmaps/DistanceObjectConversion.cs | 238 ++++++++++++------ .../Beatmaps/LegacyConverter.cs | 4 +- .../Beatmaps/ObjectConversion.cs | 12 +- .../Beatmaps/{ObjectRow.cs => ObjectList.cs} | 30 ++- .../osu.Game.Rulesets.Mania.csproj | 2 +- 5 files changed, 189 insertions(+), 97 deletions(-) rename osu.Game.Rulesets.Mania/Beatmaps/{ObjectRow.cs => ObjectList.cs} (56%) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs index 6bc7c8c45c..52c5cf16da 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs @@ -12,7 +12,6 @@ using System.Linq; using OpenTK; using osu.Game.Database; using osu.Game.Audio; -using System.Collections.Generic; namespace osu.Game.Rulesets.Mania.Beatmaps { @@ -25,8 +24,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private LegacyConvertType convertType; - public DistanceObjectConversion(HitObject originalObject, ObjectRow previousRow, FastRandom random, Beatmap beatmap) - : base(previousRow, random, beatmap) + public DistanceObjectConversion(HitObject originalObject, ObjectList previousObjects, FastRandom random, Beatmap beatmap) + : base(previousObjects, random, beatmap) { this.originalObject = originalObject; @@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps repeatCount = repeatsData?.RepeatCount ?? 1; } - public override ObjectRow GenerateConversion() + public override ObjectList Generate() { double segmentDuration = endTime / repeatCount; @@ -56,113 +55,125 @@ namespace osu.Game.Rulesets.Mania.Beatmaps if (segmentDuration <= 120) { convertType |= LegacyConvertType.ForceNotStack; - return addRandomNotes(originalObject.StartTime, segmentDuration, repeatCount); + return generateRandomNotes(originalObject.StartTime, segmentDuration, repeatCount); } if (segmentDuration <= 160) - return addStair(originalObject.StartTime, segmentDuration, repeatCount); + return generateStair(originalObject.StartTime, segmentDuration, repeatCount); if (segmentDuration <= 200 && conversionDifficulty > 3) - return addMultipleNotes(originalObject.StartTime, segmentDuration, repeatCount); + return generateRandomMultipleNotes(originalObject.StartTime, segmentDuration, repeatCount); double duration = endTime - originalObject.StartTime; if (duration >= 4000) - return addNRandomNotes(originalObject.StartTime, endTime, 0.23, 0, 0); + return generateNRandomNotes(originalObject.StartTime, endTime, 0.23, 0, 0); if (segmentDuration > 400 && duration < 4000 && repeatCount < AvailableColumns - 1 - RandomStart) return generateTiledHoldNotes(originalObject.StartTime, segmentDuration, repeatCount); - return generateLongAndNormalNotes(originalObject.StartTime, segmentDuration); + return generateHoldAndNormalNotes(originalObject.StartTime, segmentDuration); } if (segmentDuration <= 110) { - if (PreviousRow.Columns < AvailableColumns) + if (PreviousObjects.ColumnsFilled < AvailableColumns) convertType |= LegacyConvertType.ForceNotStack; else convertType &= ~LegacyConvertType.ForceNotStack; - return addRandomNotes(originalObject.StartTime, segmentDuration, segmentDuration < 80 ? 0 : 1); + return generateRandomNotes(originalObject.StartTime, segmentDuration, segmentDuration < 80 ? 0 : 1); } if (conversionDifficulty > 6.5) { if ((convertType & LegacyConvertType.LowProbability) > 0) - return addNRandomNotes(originalObject.StartTime, endTime, 0.78, 0.3, 0); - return addNRandomNotes(originalObject.StartTime, endTime, 0.85, 0.36, 0.03); + return generateNRandomNotes(originalObject.StartTime, endTime, 0.78, 0.3, 0); + return generateNRandomNotes(originalObject.StartTime, endTime, 0.85, 0.36, 0.03); } if (conversionDifficulty > 4) { if ((convertType & LegacyConvertType.LowProbability) > 0) - return addNRandomNotes(originalObject.StartTime, endTime, 0.43, 0.08, 0); - return addNRandomNotes(originalObject.StartTime, endTime, 0.56, 0.18, 0); + return generateNRandomNotes(originalObject.StartTime, endTime, 0.43, 0.08, 0); + return generateNRandomNotes(originalObject.StartTime, endTime, 0.56, 0.18, 0); } if (conversionDifficulty > 2.5) { if ((convertType & LegacyConvertType.LowProbability) > 0) - return addNRandomNotes(originalObject.StartTime, endTime, 0.3, 0, 0); - return addNRandomNotes(originalObject.StartTime, endTime, 0.37, 0.08, 0); + return generateNRandomNotes(originalObject.StartTime, endTime, 0.3, 0, 0); + return generateNRandomNotes(originalObject.StartTime, endTime, 0.37, 0.08, 0); } if ((convertType & LegacyConvertType.LowProbability) > 0) - return addNRandomNotes(originalObject.StartTime, endTime, 0.17, 0, 0); - return addNRandomNotes(originalObject.StartTime, endTime, 0.27, 0, 0); + return generateNRandomNotes(originalObject.StartTime, endTime, 0.17, 0, 0); + return generateNRandomNotes(originalObject.StartTime, endTime, 0.27, 0, 0); } /// - /// Adds random hold notes. + /// Generates random hold notes that start at an span the same amount of rows. /// - /// Number of hold notes. /// Start time of each hold note. /// End time of the hold notes. - /// The new row. - private ObjectRow generateRandomHoldNotes(double startTime, double endTime, int count) + /// Number of hold notes. + /// The containing the hit objects. + private ObjectList generateRandomHoldNotes(double startTime, double endTime, int noteCount) { - var newRow = new ObjectRow(); + // - - - - + // ■ - ■ ■ + // □ - □ □ + // ■ - ■ ■ + + var newObjects = new ObjectList(); + + int usableColumns = AvailableColumns - RandomStart - PreviousObjects.ColumnsFilled; - int usableColumns = AvailableColumns - RandomStart - PreviousRow.Columns; int nextColumn = Random.Next(RandomStart, AvailableColumns); - for (int i = 0; i < Math.Min(usableColumns, count); i++) + for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) { - while (newRow.IsTaken(nextColumn) || PreviousRow.IsTaken(nextColumn)) //find available column + while (newObjects.IsFilled(nextColumn) || PreviousObjects.IsFilled(nextColumn)) //find available column nextColumn = Random.Next(RandomStart, AvailableColumns); - add(newRow, nextColumn, startTime, endTime, count); + add(newObjects, nextColumn, startTime, endTime, noteCount); } // This is can't be combined with the above loop due to RNG - for (int i = 0; i < count - usableColumns; i++) + for (int i = 0; i < noteCount - usableColumns; i++) { - while (newRow.IsTaken(nextColumn)) + while (newObjects.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, AvailableColumns); - add(newRow, nextColumn, startTime, endTime, count); + add(newObjects, nextColumn, startTime, endTime, noteCount); } - return newRow; + return newObjects; } /// - /// Adds random notes, with one note per row. No stacking. + /// Generates random notes, with one note per row and no stacking. /// /// The start time. /// The separation of notes between rows. /// The number of rows. - /// The new row. - private ObjectRow addRandomNotes(double startTime, double separationTime, int repeatCount) + /// The containing the hit objects. + private ObjectList generateRandomNotes(double startTime, double separationTime, int repeatCount) { - var newRow = new ObjectRow(); + // - - - - + // x - - - + // - - x - + // - - - x + // x - - - + + var newObjects = new ObjectList(); int nextColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousRow.Columns < AvailableColumns) + if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousObjects.ColumnsFilled < AvailableColumns) { - while (PreviousRow.IsTaken(nextColumn)) + while (PreviousObjects.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, AvailableColumns); } int lastColumn = nextColumn; for (int i = 0; i <= repeatCount; i++) { - add(newRow, nextColumn, startTime, startTime); + add(newObjects, nextColumn, startTime, startTime); while (nextColumn == lastColumn) nextColumn = Random.Next(RandomStart, AvailableColumns); @@ -170,26 +181,35 @@ namespace osu.Game.Rulesets.Mania.Beatmaps startTime += separationTime; } - return newRow; + return newObjects; } /// - /// Creates a stair of notes, with one note per row. + /// Generates a stair of notes, with one note per row. /// /// The start time. /// The separation of notes between rows. /// The number of rows/notes. - /// The new row. - private ObjectRow addStair(double startTime, double separationTime, int repeatCount) + /// The containing the hit objects. + private ObjectList generateStair(double startTime, double separationTime, int repeatCount) { - var newRow = new ObjectRow(); + // - - - - + // x - - - + // - x - - + // - - x - + // - - - x + // - - x - + // - x - - + // x - - - + + var newObjects = new ObjectList(); int column = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); bool increasing = Random.NextDouble() > 0.5; for (int i = 0; i <= repeatCount; i++) { - add(newRow, column, startTime, startTime); + add(newObjects, column, startTime, startTime); startTime += separationTime; // Check if we're at the borders of the stage, and invert the pattern if so @@ -215,19 +235,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } } - return newRow; + return newObjects; } /// - /// Adds random notes, with 1-2 notes per row. No stacking. + /// Generates random notes with 1-2 notes per row and no stacking. /// /// The start time. /// The separation of notes between rows. /// The number of rows. - /// The new row. - private ObjectRow addMultipleNotes(double startTime, double separationTime, int repeatCount) + /// The containing the hit objects. + private ObjectList generateRandomMultipleNotes(double startTime, double separationTime, int repeatCount) { - var newRow = new ObjectRow(); + // - - - - + // x - - + // - x x - + // - - - x + // x - x - + + var newObjects = new ObjectList(); bool legacy = AvailableColumns >= 4 && AvailableColumns <= 8; int interval = Random.Next(1, AvailableColumns - (legacy ? 1 : 0)); @@ -235,7 +261,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps int nextColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); for (int i = 0; i <= repeatCount; i++) { - add(newRow, nextColumn, startTime, startTime, 2); + add(newObjects, nextColumn, startTime, startTime, 2); nextColumn += interval; if (nextColumn >= AvailableColumns - RandomStart) @@ -244,13 +270,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // If we're in 2K, let's not add many consecutive doubles if (AvailableColumns > 2) - add(newRow, nextColumn, startTime, startTime, 2); + add(newObjects, nextColumn, startTime, startTime, 2); nextColumn = Random.Next(RandomStart, AvailableColumns); startTime += separationTime; } - return newRow; + return newObjects; } /// @@ -261,9 +287,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// The probability required for 2 hold notes to be generated. /// The probability required for 3 hold notes to be generated. /// The probability required for 4 hold notes to be generated. - /// The new row. - private ObjectRow addNRandomNotes(double startTime, double endTime, double p2, double p3, double p4) + /// The containing the hit objects. + private ObjectList generateNRandomNotes(double startTime, double endTime, double p2, double p3, double p4) { + // - - - - + // ■ - ■ ■ + // □ - □ □ + // ■ - ■ ■ + switch (AvailableColumns) { case 2: @@ -291,7 +322,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps Func isDoubleSample = sample => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH; bool canGenerateTwoNotes = (convertType & LegacyConvertType.LowProbability) == 0; - canGenerateTwoNotes &= originalObject.Samples.Any(isDoubleSample) || sampleInfoListAt(originalObject.StartTime, originalObject.StartTime - endTime).Any(isDoubleSample); + canGenerateTwoNotes &= originalObject.Samples.Any(isDoubleSample) || sampleInfoListAt(originalObject.StartTime).Any(isDoubleSample); if (canGenerateTwoNotes) p2 = 0; @@ -299,44 +330,72 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return generateRandomHoldNotes(startTime, endTime, GetRandomNoteCount(p2, p3, p4)); } - private ObjectRow generateTiledHoldNotes(double startTime, double separationTime, int noteCount) + /// + /// Generates tiled hold notes. You can think of this as a stair of hold notes. + /// + /// The first hold note start time. + /// The separation time between hold notes. + /// The amount of hold notes. + /// The containing the hit objects. + private ObjectList generateTiledHoldNotes(double startTime, double separationTime, int noteCount) { - var newRow = new ObjectRow(); + // - - - - + // ■ ■ ■ ■ + // □ □ □ □ + // □ □ □ □ + // □ □ □ ■ + // □ □ ■ - + // □ ■ - - + // ■ - - - + + var newObjects = new ObjectList(); int columnRepeat = Math.Min(noteCount, AvailableColumns); int nextColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousRow.Columns < AvailableColumns) + if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousObjects.ColumnsFilled < AvailableColumns) { - while (PreviousRow.IsTaken(nextColumn)) + while (PreviousObjects.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, AvailableColumns); } for (int i = 0; i < columnRepeat; i++) { - while (newRow.IsTaken(nextColumn)) + while (newObjects.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, AvailableColumns); - add(newRow, nextColumn, startTime, endTime, noteCount); + add(newObjects, nextColumn, startTime, endTime, noteCount); startTime += separationTime; } - return newRow; + return newObjects; } - private ObjectRow generateLongAndNormalNotes(double startTime, double separationTime) + /// + /// Generates a hold note alongside normal notes. + /// + /// The start time of notes. + /// The separation time between notes. + /// The containing the hit objects. + private ObjectList generateHoldAndNormalNotes(double startTime, double separationTime) { - var newRow = new ObjectRow(); + // - - - - + // ■ x x - + // ■ - x x + // ■ x - x + // ■ - x x + + var newObjects = new ObjectList(); int holdColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousRow.Columns < AvailableColumns) + if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousObjects.ColumnsFilled < AvailableColumns) { - while (PreviousRow.IsTaken(holdColumn)) + while (PreviousObjects.IsFilled(holdColumn)) holdColumn = Random.Next(RandomStart, AvailableColumns); } // Create the hold note - add(newRow, holdColumn, startTime, separationTime * repeatCount); + add(newObjects, holdColumn, startTime, separationTime * repeatCount); int noteCount = 0; if (conversionDifficulty > 6.5) @@ -347,33 +406,41 @@ namespace osu.Game.Rulesets.Mania.Beatmaps noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0 : 0.24, 0); noteCount = Math.Min(AvailableColumns - 1, noteCount); - bool ignoreHead = !sampleInfoListAt(startTime, separationTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); + bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); int nextColumn = Random.Next(RandomStart, AvailableColumns); - var tempRow = new ObjectRow(); + var tempObjects = new ObjectList(); for (int i = 0; i <= repeatCount; i++) { if (!(ignoreHead && startTime == originalObject.StartTime)) { for (int j = 0; j < noteCount; j++) { - while (tempRow.IsTaken(nextColumn) || nextColumn == holdColumn) + while (tempObjects.IsFilled(nextColumn) || nextColumn == holdColumn) nextColumn = Random.Next(RandomStart, AvailableColumns); - add(tempRow, nextColumn, startTime, startTime, noteCount + 1); + add(tempObjects, nextColumn, startTime, startTime, noteCount + 1); } } - foreach (ManiaHitObject obj in tempRow.HitObjects) - newRow.Add(obj); + foreach (ManiaHitObject obj in tempObjects.HitObjects) + newObjects.Add(obj); - tempRow.Clear(); + tempObjects.Clear(); startTime += separationTime; } - return newRow; + return newObjects; } - private void add(ObjectRow row, int column, double startTime, double endTime, int siblings = 1) + /// + /// Constructs and adds a note to an object list. + /// + /// The list to add to. + /// The column to add the note to. + /// The start time of the note. + /// The end time of the note (set to for a non-hold note). + /// The number of children alongside this note (these will not be generated, but are used for volume calculations). + private void add(ObjectList objectList, int column, double startTime, double endTime, int siblings = 1) { ManiaHitObject newObject; @@ -399,21 +466,32 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // Todo: Consider siblings and write sample volumes (probably at ManiaHitObject level) - row.Add(newObject); + objectList.Add(newObject); } - private SampleInfoList sampleInfoListAt(double time, double separationTime) + /// + /// Retrieves the sample info list at a point in time. + /// + /// The time to retrieve the sample info list from. + /// + /// + private SampleInfoList sampleInfoListAt(double time) { var curveData = originalObject as IHasCurve; if (curveData == null) return originalObject.Samples; - int index = (int)(separationTime == 0 ? 0 : (time - originalObject.StartTime) / separationTime); + double segmentTime = (curveData.EndTime - originalObject.StartTime) / repeatCount; + + int index = (int)(segmentTime == 0 ? 0 : (time - originalObject.StartTime) / segmentTime); return curveData.RepeatSamples[index]; } private double? _conversionDifficulty; + /// + /// A difficulty factor used for various conversion methods from osu!stable. + /// private double conversionDifficulty { get diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs index 18dea8ebc3..1acf2fbec2 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private readonly double lastNoteTime; private readonly float lastNotePosition; - private ObjectRow lastRow = new ObjectRow(); + private ObjectList lastRow = new ObjectList(); private readonly int availableColumns; private readonly float localXDivisor; @@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps if (conversion == null) yield break; - ObjectRow newRow = conversion.GenerateConversion(); + ObjectList newRow = conversion.Generate(); foreach (ManiaHitObject obj in newRow.HitObjects) yield return obj; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs index bee7477261..42510e8e7b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs @@ -13,13 +13,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps protected readonly int AvailableColumns; protected readonly int RandomStart; - protected ObjectRow PreviousRow; + protected ObjectList PreviousObjects; protected readonly FastRandom Random; protected readonly Beatmap Beatmap; - protected ObjectConversion(ObjectRow previousRow, FastRandom random, Beatmap beatmap) + protected ObjectConversion(ObjectList previousObjects, FastRandom random, Beatmap beatmap) { - PreviousRow = previousRow; + PreviousObjects = previousObjects; Random = random; Beatmap = beatmap; @@ -28,10 +28,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } /// - /// Generates a new row filled with converted hit objects. + /// Generates a new object list filled with converted hit objects. /// - /// The new row. - public abstract ObjectRow GenerateConversion(); + /// The containing the hit objects. + public abstract ObjectList Generate(); /// /// Converts an x-position into a column. diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ObjectRow.cs b/osu.Game.Rulesets.Mania/Beatmaps/ObjectList.cs similarity index 56% rename from osu.Game.Rulesets.Mania/Beatmaps/ObjectRow.cs rename to osu.Game.Rulesets.Mania/Beatmaps/ObjectList.cs index fe51c16bed..13bb32a113 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ObjectRow.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ObjectList.cs @@ -1,42 +1,56 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mania.Objects; using System.Collections.Generic; using System.Linq; namespace osu.Game.Rulesets.Mania.Beatmaps { - internal class ObjectRow + internal class ObjectList { private readonly List hitObjects = new List(); + + /// + /// All the hit objects contained in this list. + /// public IEnumerable HitObjects => hitObjects; /// - /// Whether a column of this row has been taken. + /// Whether a column of this list has been taken. /// /// The column index. /// Whether the column already contains a hit object. - public bool IsTaken(int column) => hitObjects.Exists(h => h.Column == column); + public bool IsFilled(int column) => hitObjects.Exists(h => h.Column == column); /// - /// Amount of columns taken up by hit objects in this row. + /// Amount of columns taken up by hit objects in this list. /// - public int Columns => HitObjects.GroupBy(h => h.Column).Count(); + public int ColumnsFilled => HitObjects.GroupBy(h => h.Column).Count(); /// - /// Adds a hit object to this row. + /// Adds a hit object to this list. /// /// The hit object to add. public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject); /// - /// Clears this row. + /// Copies hit object from another list to this one. + /// + /// The other list. + public void Add(ObjectList other) + { + other.HitObjects.ForEach(Add); + } + + /// + /// Clears this list. /// public void Clear() => hitObjects.Clear(); /// - /// Removes a hit object from this row. + /// Removes a hit object from this list. /// /// The hit object to remove. public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject); diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 36c17ccf04..3d93dd3068 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -52,7 +52,7 @@ - + From 7c48fb2b9259c8cc252a4c860aafc9550172d23f Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 13:37:22 +0900 Subject: [PATCH 29/77] General fixes. --- .../Beatmaps/DistanceObjectConversion.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs index 52c5cf16da..20ee944dbf 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs @@ -126,7 +126,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps var newObjects = new ObjectList(); int usableColumns = AvailableColumns - RandomStart - PreviousObjects.ColumnsFilled; - int nextColumn = Random.Next(RandomStart, AvailableColumns); for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) { @@ -325,7 +324,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps canGenerateTwoNotes &= originalObject.Samples.Any(isDoubleSample) || sampleInfoListAt(originalObject.StartTime).Any(isDoubleSample); if (canGenerateTwoNotes) - p2 = 0; + p2 = 1; return generateRandomHoldNotes(startTime, endTime, GetRandomNoteCount(p2, p3, p4)); } @@ -397,7 +396,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // Create the hold note add(newObjects, holdColumn, startTime, separationTime * repeatCount); - int noteCount = 0; + int noteCount = 1; if (conversionDifficulty > 6.5) noteCount = GetRandomNoteCount(0.63, 0); else if (conversionDifficulty > 4) @@ -422,10 +421,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } } - foreach (ManiaHitObject obj in tempObjects.HitObjects) - newObjects.Add(obj); - + newObjects.Add(tempObjects); tempObjects.Clear(); + startTime += separationTime; } From 6010243736f6bd1cc7fc798bd5940c356bcdc36a Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 14:07:57 +0900 Subject: [PATCH 30/77] Fix incorrect uint_to_real factor. --- osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs index 381e24c84c..ff3fd8e4b7 100644 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils /// internal class FastRandom { - private const double uint_to_real = 1.0 / (int.MaxValue + 1.0); + private const double uint_to_real = 1.0 / (uint.MaxValue + 1.0); private const uint int_mask = 0x7FFFFFFF; private const uint y = 842502087; private const uint z = 3579807591; From 6dae5cbacf53281e47506e548442007586e9771f Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 14:08:32 +0900 Subject: [PATCH 31/77] Fix distance object conversion not calculating end time. --- .../Beatmaps/DistanceObjectConversion.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs index 20ee944dbf..0dbc38ded6 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs @@ -17,6 +17,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { internal class DistanceObjectConversion : ObjectConversion { + /// + /// Base osu! slider scoring distance. + /// + private const float osu_base_scoring_distance = 100; + private readonly HitObject originalObject; private readonly double endTime; @@ -39,8 +44,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps var distanceData = originalObject as IHasDistance; var repeatsData = originalObject as IHasRepeats; - endTime = distanceData?.EndTime ?? 0; repeatCount = repeatsData?.RepeatCount ?? 1; + + double speedAdjustment = beatmap.TimingInfo.SpeedMultiplierAt(originalObject.StartTime); + double speedAdjustedBeatLength = beatmap.TimingInfo.BeatLengthAt(originalObject.StartTime) * speedAdjustment; + + // The true distance, accounting for any repeats. This ends up being the drum roll distance later + double distance = distanceData.Distance * repeatCount; + + // The velocity of the osu! hit object - calculated as the velocity of a slider + double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier / speedAdjustedBeatLength; + // The duration of the osu! hit object + double osuDuration = distance / osuVelocity; + + endTime = originalObject.StartTime + osuDuration; } public override ObjectList Generate() From de8271ad6bff2193377e12fafbccf0953d99402d Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 14:08:48 +0900 Subject: [PATCH 32/77] Fix out of range exceptions due to out-of-order hitobjects. --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index e9dae95782..37150adb66 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -25,7 +25,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { BeatmapInfo = original.BeatmapInfo, TimingInfo = original.TimingInfo, - HitObjects = original.HitObjects.SelectMany(converter.Convert).ToList() + // We need to sort here, because the converter generates patterns + HitObjects = original.HitObjects.SelectMany(converter.Convert).OrderBy(h => h.StartTime).ToList() }; } From 618c162535162b321f4db36b995f2da873fc8641 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 14:17:23 +0900 Subject: [PATCH 33/77] Move more functions to ObjectConversion + resharper cleanup. --- .../Beatmaps/DistanceObjectConversion.cs | 101 +++--------------- .../Beatmaps/LegacyConverter.cs | 11 -- .../Beatmaps/ObjectConversion.cs | 70 ++++++++++++ 3 files changed, 86 insertions(+), 96 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs index 0dbc38ded6..59d4a7a80c 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Mania.Objects; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects; @@ -9,8 +8,6 @@ using osu.Game.Rulesets.Objects.Types; using System; using osu.Game.Rulesets.Mania.MathUtils; using System.Linq; -using OpenTK; -using osu.Game.Database; using osu.Game.Audio; namespace osu.Game.Rulesets.Mania.Beatmaps @@ -78,7 +75,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps if (segmentDuration <= 160) return generateStair(originalObject.StartTime, segmentDuration, repeatCount); - if (segmentDuration <= 200 && conversionDifficulty > 3) + if (segmentDuration <= 200 && ConversionDifficulty > 3) return generateRandomMultipleNotes(originalObject.StartTime, segmentDuration, repeatCount); double duration = endTime - originalObject.StartTime; @@ -100,21 +97,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return generateRandomNotes(originalObject.StartTime, segmentDuration, segmentDuration < 80 ? 0 : 1); } - if (conversionDifficulty > 6.5) + if (ConversionDifficulty > 6.5) { if ((convertType & LegacyConvertType.LowProbability) > 0) return generateNRandomNotes(originalObject.StartTime, endTime, 0.78, 0.3, 0); return generateNRandomNotes(originalObject.StartTime, endTime, 0.85, 0.36, 0.03); } - if (conversionDifficulty > 4) + if (ConversionDifficulty > 4) { if ((convertType & LegacyConvertType.LowProbability) > 0) return generateNRandomNotes(originalObject.StartTime, endTime, 0.43, 0.08, 0); return generateNRandomNotes(originalObject.StartTime, endTime, 0.56, 0.18, 0); } - if (conversionDifficulty > 2.5) + if (ConversionDifficulty > 2.5) { if ((convertType & LegacyConvertType.LowProbability) > 0) return generateNRandomNotes(originalObject.StartTime, endTime, 0.3, 0, 0); @@ -148,7 +145,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { while (newObjects.IsFilled(nextColumn) || PreviousObjects.IsFilled(nextColumn)) //find available column nextColumn = Random.Next(RandomStart, AvailableColumns); - add(newObjects, nextColumn, startTime, endTime, noteCount); + Add(newObjects, originalObject, nextColumn, startTime, endTime, noteCount); } // This is can't be combined with the above loop due to RNG @@ -156,7 +153,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { while (newObjects.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, AvailableColumns); - add(newObjects, nextColumn, startTime, endTime, noteCount); + Add(newObjects, originalObject, nextColumn, startTime, endTime, noteCount); } return newObjects; @@ -189,7 +186,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps int lastColumn = nextColumn; for (int i = 0; i <= repeatCount; i++) { - add(newObjects, nextColumn, startTime, startTime); + Add(newObjects, originalObject, nextColumn, startTime, startTime); while (nextColumn == lastColumn) nextColumn = Random.Next(RandomStart, AvailableColumns); @@ -225,7 +222,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps for (int i = 0; i <= repeatCount; i++) { - add(newObjects, column, startTime, startTime); + Add(newObjects, originalObject, column, startTime, startTime); startTime += separationTime; // Check if we're at the borders of the stage, and invert the pattern if so @@ -277,7 +274,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps int nextColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); for (int i = 0; i <= repeatCount; i++) { - add(newObjects, nextColumn, startTime, startTime, 2); + Add(newObjects, originalObject, nextColumn, startTime, startTime, 2); nextColumn += interval; if (nextColumn >= AvailableColumns - RandomStart) @@ -286,7 +283,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // If we're in 2K, let's not add many consecutive doubles if (AvailableColumns > 2) - add(newObjects, nextColumn, startTime, startTime, 2); + Add(newObjects, originalObject, nextColumn, startTime, startTime, 2); nextColumn = Random.Next(RandomStart, AvailableColumns); startTime += separationTime; @@ -380,7 +377,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps while (newObjects.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, AvailableColumns); - add(newObjects, nextColumn, startTime, endTime, noteCount); + Add(newObjects, originalObject, nextColumn, startTime, endTime, noteCount); startTime += separationTime; } @@ -411,14 +408,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } // Create the hold note - add(newObjects, holdColumn, startTime, separationTime * repeatCount); + Add(newObjects, originalObject, holdColumn, startTime, separationTime * repeatCount); int noteCount = 1; - if (conversionDifficulty > 6.5) + if (ConversionDifficulty > 6.5) noteCount = GetRandomNoteCount(0.63, 0); - else if (conversionDifficulty > 4) + else if (ConversionDifficulty > 4) noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0.12 : 0.45, 0); - else if (conversionDifficulty > 2.5) + else if (ConversionDifficulty > 2.5) noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0 : 0.24, 0); noteCount = Math.Min(AvailableColumns - 1, noteCount); @@ -434,7 +431,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { while (tempObjects.IsFilled(nextColumn) || nextColumn == holdColumn) nextColumn = Random.Next(RandomStart, AvailableColumns); - add(tempObjects, nextColumn, startTime, startTime, noteCount + 1); + Add(tempObjects, originalObject, nextColumn, startTime, startTime, noteCount + 1); } } @@ -447,48 +444,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return newObjects; } - /// - /// Constructs and adds a note to an object list. - /// - /// The list to add to. - /// The column to add the note to. - /// The start time of the note. - /// The end time of the note (set to for a non-hold note). - /// The number of children alongside this note (these will not be generated, but are used for volume calculations). - private void add(ObjectList objectList, int column, double startTime, double endTime, int siblings = 1) - { - ManiaHitObject newObject; - - if (startTime == endTime) - { - newObject = new Note - { - StartTime = startTime, - Samples = originalObject.Samples, - Column = column - }; - } - else - { - newObject = new HoldNote - { - StartTime = startTime, - Samples = originalObject.Samples, - Column = column, - Duration = endTime - startTime - }; - } - - // Todo: Consider siblings and write sample volumes (probably at ManiaHitObject level) - - objectList.Add(newObject); - } - /// /// Retrieves the sample info list at a point in time. /// /// The time to retrieve the sample info list from. - /// /// private SampleInfoList sampleInfoListAt(double time) { @@ -502,33 +461,5 @@ namespace osu.Game.Rulesets.Mania.Beatmaps int index = (int)(segmentTime == 0 ? 0 : (time - originalObject.StartTime) / segmentTime); return curveData.RepeatSamples[index]; } - - private double? _conversionDifficulty; - /// - /// A difficulty factor used for various conversion methods from osu!stable. - /// - private double conversionDifficulty - { - get - { - if (_conversionDifficulty != null) - return _conversionDifficulty.Value; - - HitObject lastObject = Beatmap.HitObjects.LastOrDefault(); - HitObject firstObject = Beatmap.HitObjects.FirstOrDefault(); - - double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0); - drainTime -= Beatmap.EventInfo.TotalBreakTime; - - if (drainTime == 0) - drainTime = 10000; - - BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.Difficulty; - _conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; - _conversionDifficulty = Math.Min(_conversionDifficulty.Value, 12); - - return _conversionDifficulty.Value; - } - } } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs index 1acf2fbec2..0a85b8c8af 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs @@ -9,7 +9,6 @@ using osu.Game.Rulesets.Objects.Types; using System; using System.Collections.Generic; using osu.Game.Rulesets.Mania.MathUtils; -using osu.Game.Beatmaps.Timing; namespace osu.Game.Rulesets.Mania.Beatmaps { @@ -18,15 +17,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// internal class LegacyConverter { - private const int max_previous_note_times = 7; - private readonly FastRandom random; - private readonly List previousNoteTimes; - private readonly bool[] previousNotes; - private readonly double lastNoteTime; - private readonly float lastNotePosition; - private ObjectList lastRow = new ObjectList(); private readonly int availableColumns; @@ -44,9 +36,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps availableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); localXDivisor = 512.0f / availableColumns; - - previousNoteTimes = new List(max_previous_note_times); - previousNotes = new bool[availableColumns]; } public IEnumerable Convert(HitObject original) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs index 42510e8e7b..83c0af9d46 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs @@ -5,6 +5,10 @@ using OpenTK; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.MathUtils; using System; +using System.Linq; +using osu.Game.Database; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mania.Beatmaps { @@ -73,5 +77,71 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return 3; return val >= 1 - p2 ? 2 : 1; } + + /// + /// Constructs and adds a note to an object list. + /// + /// The list to add to. + /// The original hit object (used for samples). + /// The column to add the note to. + /// The start time of the note. + /// The end time of the note (set to for a non-hold note). + /// The number of children alongside this note (these will not be generated, but are used for volume calculations). + protected void Add(ObjectList objectList, HitObject originalObject, int column, double startTime, double endTime, int siblings = 1) + { + ManiaHitObject newObject; + + if (startTime == endTime) + { + newObject = new Note + { + StartTime = startTime, + Samples = originalObject.Samples, + Column = column + }; + } + else + { + newObject = new HoldNote + { + StartTime = startTime, + Samples = originalObject.Samples, + Column = column, + Duration = endTime - startTime + }; + } + + // Todo: Consider siblings and write sample volumes (probably at ManiaHitObject level) + + objectList.Add(newObject); + } + + private double? conversionDifficulty; + /// + /// A difficulty factor used for various conversion methods from osu!stable. + /// + protected double ConversionDifficulty + { + get + { + if (conversionDifficulty != null) + return conversionDifficulty.Value; + + HitObject lastObject = Beatmap.HitObjects.LastOrDefault(); + HitObject firstObject = Beatmap.HitObjects.FirstOrDefault(); + + double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0); + drainTime -= Beatmap.EventInfo.TotalBreakTime; + + if (drainTime == 0) + drainTime = 10000; + + BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.Difficulty; + conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; + conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); + + return conversionDifficulty.Value; + } + } } } From 1fa1897d71122d08ad93e8a82be35af6a87d0485 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 15:05:45 +0900 Subject: [PATCH 34/77] Define the concept of patterns + additional comments. --- ...Converter.cs => LegacyBeatmapConverter.cs} | 25 +- .../Beatmaps/ManiaBeatmapConverter.cs | 2 +- .../Legacy/DistanceObjectPatternGenerator.cs} | 218 +++++++++--------- .../Legacy/PatternGenerator.cs} | 73 ++---- .../Legacy/PatternType.cs} | 7 +- .../{ObjectList.cs => Patterns/Pattern.cs} | 31 +-- .../Beatmaps/Patterns/PatternGenerator.cs | 89 +++++++ .../osu.Game.Rulesets.Mania.csproj | 11 +- 8 files changed, 255 insertions(+), 201 deletions(-) rename osu.Game.Rulesets.Mania/Beatmaps/{LegacyConverter.cs => LegacyBeatmapConverter.cs} (83%) rename osu.Game.Rulesets.Mania/Beatmaps/{DistanceObjectConversion.cs => Patterns/Legacy/DistanceObjectPatternGenerator.cs} (58%) rename osu.Game.Rulesets.Mania/Beatmaps/{ObjectConversion.cs => Patterns/Legacy/PatternGenerator.cs} (59%) rename osu.Game.Rulesets.Mania/Beatmaps/{LegacyConvertType.cs => Patterns/Legacy/PatternType.cs} (85%) rename osu.Game.Rulesets.Mania/Beatmaps/{ObjectList.cs => Patterns/Pattern.cs} (62%) create mode 100644 osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs similarity index 83% rename from osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs rename to osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs index 0a85b8c8af..4108ba7809 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs @@ -1,32 +1,33 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; +using System; +using System.Collections.Generic; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; +using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; -using System; -using System.Collections.Generic; -using osu.Game.Rulesets.Mania.MathUtils; +using OpenTK; namespace osu.Game.Rulesets.Mania.Beatmaps { /// /// Special converter used for converting from osu!stable beatmaps. /// - internal class LegacyConverter + internal class LegacyBeatmapConverter { private readonly FastRandom random; - private ObjectList lastRow = new ObjectList(); + private Patterns.Pattern lastPattern = new Patterns.Pattern(); private readonly int availableColumns; private readonly float localXDivisor; private readonly Beatmap beatmap; - public LegacyConverter(Beatmap beatmap) + public LegacyBeatmapConverter(Beatmap beatmap) { this.beatmap = beatmap; @@ -93,10 +94,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps var distanceData = original as IHasDistance; var positionData = original as IHasPosition; - ObjectConversion conversion = null; + Patterns.PatternGenerator conversion = null; if (distanceData != null) - conversion = new DistanceObjectConversion(original, lastRow, random, beatmap); + conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern); else if (endTimeData != null) { // Spinner @@ -109,12 +110,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps if (conversion == null) yield break; - ObjectList newRow = conversion.Generate(); + Patterns.Pattern newPattern = conversion.Generate(); - foreach (ManiaHitObject obj in newRow.HitObjects) + foreach (ManiaHitObject obj in newPattern.HitObjects) yield return obj; - lastRow = newRow; + lastPattern = newPattern; } private int getColumn(float position) => MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, availableColumns - 1); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 37150adb66..aea77cd2b5 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps protected override Beatmap ConvertBeatmap(Beatmap original) { // Todo: This should be cased when we get better conversion methods - var converter = new LegacyConverter(original); + var converter = new LegacyBeatmapConverter(original); return new Beatmap { diff --git a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs similarity index 58% rename from osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs rename to osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 59d4a7a80c..03240a2be4 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/DistanceObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -1,50 +1,49 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using System; -using osu.Game.Rulesets.Mania.MathUtils; using System.Linq; using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; -namespace osu.Game.Rulesets.Mania.Beatmaps +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { - internal class DistanceObjectConversion : ObjectConversion + /// + /// A pattern generator for IHasDistance hit objects. + /// + internal class DistanceObjectPatternGenerator : PatternGenerator { /// /// Base osu! slider scoring distance. /// private const float osu_base_scoring_distance = 100; - private readonly HitObject originalObject; - private readonly double endTime; private readonly int repeatCount; - private LegacyConvertType convertType; + private PatternType convertType; - public DistanceObjectConversion(HitObject originalObject, ObjectList previousObjects, FastRandom random, Beatmap beatmap) - : base(previousObjects, random, beatmap) + public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern) + : base(random, hitObject, beatmap, previousPattern) { - this.originalObject = originalObject; - ControlPoint overridePoint; - ControlPoint controlPoint = Beatmap.TimingInfo.TimingPointAt(originalObject.StartTime, out overridePoint); + ControlPoint controlPoint = Beatmap.TimingInfo.TimingPointAt(hitObject.StartTime, out overridePoint); - convertType = LegacyConvertType.None; + convertType = PatternType.None; if ((overridePoint ?? controlPoint)?.KiaiMode == false) - convertType = LegacyConvertType.LowProbability; + convertType = PatternType.LowProbability; - var distanceData = originalObject as IHasDistance; - var repeatsData = originalObject as IHasRepeats; + var distanceData = hitObject as IHasDistance; + var repeatsData = hitObject as IHasRepeats; repeatCount = repeatsData?.RepeatCount ?? 1; - double speedAdjustment = beatmap.TimingInfo.SpeedMultiplierAt(originalObject.StartTime); - double speedAdjustedBeatLength = beatmap.TimingInfo.BeatLengthAt(originalObject.StartTime) * speedAdjustment; + double speedAdjustment = beatmap.TimingInfo.SpeedMultiplierAt(hitObject.StartTime); + double speedAdjustedBeatLength = beatmap.TimingInfo.BeatLengthAt(hitObject.StartTime) * speedAdjustment; // The true distance, accounting for any repeats. This ends up being the drum roll distance later double distance = distanceData.Distance * repeatCount; @@ -54,73 +53,73 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // The duration of the osu! hit object double osuDuration = distance / osuVelocity; - endTime = originalObject.StartTime + osuDuration; + endTime = hitObject.StartTime + osuDuration; } - public override ObjectList Generate() + public override Pattern Generate() { double segmentDuration = endTime / repeatCount; if (repeatCount > 1) { if (segmentDuration <= 90) - return generateRandomHoldNotes(originalObject.StartTime, endTime, 1); + return generateRandomHoldNotes(HitObject.StartTime, endTime, 1); if (segmentDuration <= 120) { - convertType |= LegacyConvertType.ForceNotStack; - return generateRandomNotes(originalObject.StartTime, segmentDuration, repeatCount); + convertType |= PatternType.ForceNotStack; + return generateRandomNotes(HitObject.StartTime, segmentDuration, repeatCount); } if (segmentDuration <= 160) - return generateStair(originalObject.StartTime, segmentDuration, repeatCount); + return generateStair(HitObject.StartTime, segmentDuration); if (segmentDuration <= 200 && ConversionDifficulty > 3) - return generateRandomMultipleNotes(originalObject.StartTime, segmentDuration, repeatCount); + return generateRandomMultipleNotes(HitObject.StartTime, segmentDuration, repeatCount); - double duration = endTime - originalObject.StartTime; + double duration = endTime - HitObject.StartTime; if (duration >= 4000) - return generateNRandomNotes(originalObject.StartTime, endTime, 0.23, 0, 0); + return generateNRandomNotes(HitObject.StartTime, endTime, 0.23, 0, 0); if (segmentDuration > 400 && duration < 4000 && repeatCount < AvailableColumns - 1 - RandomStart) - return generateTiledHoldNotes(originalObject.StartTime, segmentDuration, repeatCount); + return generateTiledHoldNotes(HitObject.StartTime, segmentDuration, repeatCount); - return generateHoldAndNormalNotes(originalObject.StartTime, segmentDuration); + return generateHoldAndNormalNotes(HitObject.StartTime, segmentDuration); } if (segmentDuration <= 110) { - if (PreviousObjects.ColumnsFilled < AvailableColumns) - convertType |= LegacyConvertType.ForceNotStack; + if (PreviousPattern.ColumnsFilled < AvailableColumns) + convertType |= PatternType.ForceNotStack; else - convertType &= ~LegacyConvertType.ForceNotStack; - return generateRandomNotes(originalObject.StartTime, segmentDuration, segmentDuration < 80 ? 0 : 1); + convertType &= ~PatternType.ForceNotStack; + return generateRandomNotes(HitObject.StartTime, segmentDuration, segmentDuration < 80 ? 0 : 1); } if (ConversionDifficulty > 6.5) { - if ((convertType & LegacyConvertType.LowProbability) > 0) - return generateNRandomNotes(originalObject.StartTime, endTime, 0.78, 0.3, 0); - return generateNRandomNotes(originalObject.StartTime, endTime, 0.85, 0.36, 0.03); + if ((convertType & PatternType.LowProbability) > 0) + return generateNRandomNotes(HitObject.StartTime, endTime, 0.78, 0.3, 0); + return generateNRandomNotes(HitObject.StartTime, endTime, 0.85, 0.36, 0.03); } if (ConversionDifficulty > 4) { - if ((convertType & LegacyConvertType.LowProbability) > 0) - return generateNRandomNotes(originalObject.StartTime, endTime, 0.43, 0.08, 0); - return generateNRandomNotes(originalObject.StartTime, endTime, 0.56, 0.18, 0); + if ((convertType & PatternType.LowProbability) > 0) + return generateNRandomNotes(HitObject.StartTime, endTime, 0.43, 0.08, 0); + return generateNRandomNotes(HitObject.StartTime, endTime, 0.56, 0.18, 0); } if (ConversionDifficulty > 2.5) { - if ((convertType & LegacyConvertType.LowProbability) > 0) - return generateNRandomNotes(originalObject.StartTime, endTime, 0.3, 0, 0); - return generateNRandomNotes(originalObject.StartTime, endTime, 0.37, 0.08, 0); + if ((convertType & PatternType.LowProbability) > 0) + return generateNRandomNotes(HitObject.StartTime, endTime, 0.3, 0, 0); + return generateNRandomNotes(HitObject.StartTime, endTime, 0.37, 0.08, 0); } - if ((convertType & LegacyConvertType.LowProbability) > 0) - return generateNRandomNotes(originalObject.StartTime, endTime, 0.17, 0, 0); - return generateNRandomNotes(originalObject.StartTime, endTime, 0.27, 0, 0); + if ((convertType & PatternType.LowProbability) > 0) + return generateNRandomNotes(HitObject.StartTime, endTime, 0.17, 0, 0); + return generateNRandomNotes(HitObject.StartTime, endTime, 0.27, 0, 0); } /// @@ -129,34 +128,34 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// Start time of each hold note. /// End time of the hold notes. /// Number of hold notes. - /// The containing the hit objects. - private ObjectList generateRandomHoldNotes(double startTime, double endTime, int noteCount) + /// The containing the hit objects. + private Pattern generateRandomHoldNotes(double startTime, double endTime, int noteCount) { // - - - - // ■ - ■ ■ // □ - □ □ // ■ - ■ ■ - var newObjects = new ObjectList(); + var pattern = new Pattern(); - int usableColumns = AvailableColumns - RandomStart - PreviousObjects.ColumnsFilled; + int usableColumns = AvailableColumns - RandomStart - PreviousPattern.ColumnsFilled; int nextColumn = Random.Next(RandomStart, AvailableColumns); for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) { - while (newObjects.IsFilled(nextColumn) || PreviousObjects.IsFilled(nextColumn)) //find available column + while (pattern.IsFilled(nextColumn) || PreviousPattern.IsFilled(nextColumn)) //find available column nextColumn = Random.Next(RandomStart, AvailableColumns); - Add(newObjects, originalObject, nextColumn, startTime, endTime, noteCount); + AddToPattern(pattern, HitObject, nextColumn, startTime, endTime, noteCount); } // This is can't be combined with the above loop due to RNG for (int i = 0; i < noteCount - usableColumns; i++) { - while (newObjects.IsFilled(nextColumn)) + while (pattern.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, AvailableColumns); - Add(newObjects, originalObject, nextColumn, startTime, endTime, noteCount); + AddToPattern(pattern, HitObject, nextColumn, startTime, endTime, noteCount); } - return newObjects; + return pattern; } /// @@ -165,8 +164,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// The start time. /// The separation of notes between rows. /// The number of rows. - /// The containing the hit objects. - private ObjectList generateRandomNotes(double startTime, double separationTime, int repeatCount) + /// The containing the hit objects. + private Pattern generateRandomNotes(double startTime, double separationTime, int repeatCount) { // - - - - // x - - - @@ -174,19 +173,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // - - - x // x - - - - var newObjects = new ObjectList(); + var pattern = new Pattern(); - int nextColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousObjects.ColumnsFilled < AvailableColumns) + int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnsFilled < AvailableColumns) { - while (PreviousObjects.IsFilled(nextColumn)) + while (PreviousPattern.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, AvailableColumns); } int lastColumn = nextColumn; for (int i = 0; i <= repeatCount; i++) { - Add(newObjects, originalObject, nextColumn, startTime, startTime); + AddToPattern(pattern, HitObject, nextColumn, startTime, startTime); while (nextColumn == lastColumn) nextColumn = Random.Next(RandomStart, AvailableColumns); @@ -194,7 +193,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps startTime += separationTime; } - return newObjects; + return pattern; } /// @@ -202,9 +201,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// /// The start time. /// The separation of notes between rows. - /// The number of rows/notes. - /// The containing the hit objects. - private ObjectList generateStair(double startTime, double separationTime, int repeatCount) + /// The containing the hit objects. + private Pattern generateStair(double startTime, double separationTime) { // - - - - // x - - - @@ -215,14 +213,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // - x - - // x - - - - var newObjects = new ObjectList(); + var pattern = new Pattern(); - int column = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); + int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); bool increasing = Random.NextDouble() > 0.5; for (int i = 0; i <= repeatCount; i++) { - Add(newObjects, originalObject, column, startTime, startTime); + AddToPattern(pattern, HitObject, column, startTime, startTime); startTime += separationTime; // Check if we're at the borders of the stage, and invert the pattern if so @@ -248,7 +246,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } } - return newObjects; + return pattern; } /// @@ -257,8 +255,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// The start time. /// The separation of notes between rows. /// The number of rows. - /// The containing the hit objects. - private ObjectList generateRandomMultipleNotes(double startTime, double separationTime, int repeatCount) + /// The containing the hit objects. + private Pattern generateRandomMultipleNotes(double startTime, double separationTime, int repeatCount) { // - - - - // x - - @@ -266,15 +264,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // - - - x // x - x - - var newObjects = new ObjectList(); + var pattern = new Pattern(); bool legacy = AvailableColumns >= 4 && AvailableColumns <= 8; int interval = Random.Next(1, AvailableColumns - (legacy ? 1 : 0)); - int nextColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); + int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); for (int i = 0; i <= repeatCount; i++) { - Add(newObjects, originalObject, nextColumn, startTime, startTime, 2); + AddToPattern(pattern, HitObject, nextColumn, startTime, startTime, 2); nextColumn += interval; if (nextColumn >= AvailableColumns - RandomStart) @@ -283,13 +281,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // If we're in 2K, let's not add many consecutive doubles if (AvailableColumns > 2) - Add(newObjects, originalObject, nextColumn, startTime, startTime, 2); + AddToPattern(pattern, HitObject, nextColumn, startTime, startTime, 2); nextColumn = Random.Next(RandomStart, AvailableColumns); startTime += separationTime; } - return newObjects; + return pattern; } /// @@ -300,8 +298,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// The probability required for 2 hold notes to be generated. /// The probability required for 3 hold notes to be generated. /// The probability required for 4 hold notes to be generated. - /// The containing the hit objects. - private ObjectList generateNRandomNotes(double startTime, double endTime, double p2, double p3, double p4) + /// The containing the hit objects. + private Pattern generateNRandomNotes(double startTime, double endTime, double p2, double p3, double p4) { // - - - - // ■ - ■ ■ @@ -334,8 +332,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps Func isDoubleSample = sample => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH; - bool canGenerateTwoNotes = (convertType & LegacyConvertType.LowProbability) == 0; - canGenerateTwoNotes &= originalObject.Samples.Any(isDoubleSample) || sampleInfoListAt(originalObject.StartTime).Any(isDoubleSample); + bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0; + canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample); if (canGenerateTwoNotes) p2 = 1; @@ -349,8 +347,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// The first hold note start time. /// The separation time between hold notes. /// The amount of hold notes. - /// The containing the hit objects. - private ObjectList generateTiledHoldNotes(double startTime, double separationTime, int noteCount) + /// The containing the hit objects. + private Pattern generateTiledHoldNotes(double startTime, double separationTime, int noteCount) { // - - - - // ■ ■ ■ ■ @@ -361,27 +359,27 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // □ ■ - - // ■ - - - - var newObjects = new ObjectList(); + var pattern = new Pattern(); int columnRepeat = Math.Min(noteCount, AvailableColumns); - int nextColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousObjects.ColumnsFilled < AvailableColumns) + int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnsFilled < AvailableColumns) { - while (PreviousObjects.IsFilled(nextColumn)) + while (PreviousPattern.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, AvailableColumns); } for (int i = 0; i < columnRepeat; i++) { - while (newObjects.IsFilled(nextColumn)) + while (pattern.IsFilled(nextColumn)) nextColumn = Random.Next(RandomStart, AvailableColumns); - Add(newObjects, originalObject, nextColumn, startTime, endTime, noteCount); + AddToPattern(pattern, HitObject, nextColumn, startTime, endTime, noteCount); startTime += separationTime; } - return newObjects; + return pattern; } /// @@ -389,8 +387,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// /// The start time of notes. /// The separation time between notes. - /// The containing the hit objects. - private ObjectList generateHoldAndNormalNotes(double startTime, double separationTime) + /// The containing the hit objects. + private Pattern generateHoldAndNormalNotes(double startTime, double separationTime) { // - - - - // ■ x x - @@ -398,17 +396,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // ■ x - x // ■ - x x - var newObjects = new ObjectList(); + var pattern = new Pattern(); - int holdColumn = GetColumn((originalObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & LegacyConvertType.ForceNotStack) > 0 && PreviousObjects.ColumnsFilled < AvailableColumns) + int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnsFilled < AvailableColumns) { - while (PreviousObjects.IsFilled(holdColumn)) + while (PreviousPattern.IsFilled(holdColumn)) holdColumn = Random.Next(RandomStart, AvailableColumns); } // Create the hold note - Add(newObjects, originalObject, holdColumn, startTime, separationTime * repeatCount); + AddToPattern(pattern, HitObject, holdColumn, startTime, separationTime * repeatCount); int noteCount = 1; if (ConversionDifficulty > 6.5) @@ -422,26 +420,26 @@ namespace osu.Game.Rulesets.Mania.Beatmaps bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); int nextColumn = Random.Next(RandomStart, AvailableColumns); - var tempObjects = new ObjectList(); + var rowPattern = new Pattern(); for (int i = 0; i <= repeatCount; i++) { - if (!(ignoreHead && startTime == originalObject.StartTime)) + if (!(ignoreHead && startTime == HitObject.StartTime)) { for (int j = 0; j < noteCount; j++) { - while (tempObjects.IsFilled(nextColumn) || nextColumn == holdColumn) + while (rowPattern.IsFilled(nextColumn) || nextColumn == holdColumn) nextColumn = Random.Next(RandomStart, AvailableColumns); - Add(tempObjects, originalObject, nextColumn, startTime, startTime, noteCount + 1); + AddToPattern(rowPattern, HitObject, nextColumn, startTime, startTime, noteCount + 1); } } - newObjects.Add(tempObjects); - tempObjects.Clear(); + pattern.Add(rowPattern); + rowPattern.Clear(); startTime += separationTime; } - return newObjects; + return pattern; } /// @@ -451,14 +449,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// private SampleInfoList sampleInfoListAt(double time) { - var curveData = originalObject as IHasCurve; + var curveData = HitObject as IHasCurve; if (curveData == null) - return originalObject.Samples; + return HitObject.Samples; - double segmentTime = (curveData.EndTime - originalObject.StartTime) / repeatCount; + double segmentTime = (curveData.EndTime - HitObject.StartTime) / repeatCount; - int index = (int)(segmentTime == 0 ? 0 : (time - originalObject.StartTime) / segmentTime); + int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); return curveData.RepeatSamples[index]; } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs similarity index 59% rename from osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs rename to osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index 83c0af9d46..ad07c03b96 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ObjectConversion.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -1,42 +1,39 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; using System; using System.Linq; +using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; +using OpenTK; -namespace osu.Game.Rulesets.Mania.Beatmaps +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { - internal abstract class ObjectConversion + /// + /// A pattern generator for legacy hit objects. + /// + internal abstract class PatternGenerator : Patterns.PatternGenerator { - protected readonly int AvailableColumns; + /// + /// The column index at which to start generating random notes. + /// protected readonly int RandomStart; - protected ObjectList PreviousObjects; + /// + /// The random number generator to use. + /// protected readonly FastRandom Random; - protected readonly Beatmap Beatmap; - protected ObjectConversion(ObjectList previousObjects, FastRandom random, Beatmap beatmap) + protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern) + : base(hitObject, beatmap, previousPattern) { - PreviousObjects = previousObjects; Random = random; - Beatmap = beatmap; - AvailableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); RandomStart = AvailableColumns == 8 ? 1 : 0; } - /// - /// Generates a new object list filled with converted hit objects. - /// - /// The containing the hit objects. - public abstract ObjectList Generate(); - /// /// Converts an x-position into a column. /// @@ -78,44 +75,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return val >= 1 - p2 ? 2 : 1; } - /// - /// Constructs and adds a note to an object list. - /// - /// The list to add to. - /// The original hit object (used for samples). - /// The column to add the note to. - /// The start time of the note. - /// The end time of the note (set to for a non-hold note). - /// The number of children alongside this note (these will not be generated, but are used for volume calculations). - protected void Add(ObjectList objectList, HitObject originalObject, int column, double startTime, double endTime, int siblings = 1) - { - ManiaHitObject newObject; - - if (startTime == endTime) - { - newObject = new Note - { - StartTime = startTime, - Samples = originalObject.Samples, - Column = column - }; - } - else - { - newObject = new HoldNote - { - StartTime = startTime, - Samples = originalObject.Samples, - Column = column, - Duration = endTime - startTime - }; - } - - // Todo: Consider siblings and write sample volumes (probably at ManiaHitObject level) - - objectList.Add(newObject); - } - private double? conversionDifficulty; /// /// A difficulty factor used for various conversion methods from osu!stable. diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConvertType.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs similarity index 85% rename from osu.Game.Rulesets.Mania/Beatmaps/LegacyConvertType.cs rename to osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs index 086bc89292..d4957d41a9 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyConvertType.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs @@ -3,10 +3,13 @@ using System; -namespace osu.Game.Rulesets.Mania.Beatmaps +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { + /// + /// The type of pattern to generate. Used for legacy patterns. + /// [Flags] - internal enum LegacyConvertType + internal enum PatternType { None = 0, /// diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ObjectList.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs similarity index 62% rename from osu.Game.Rulesets.Mania/Beatmaps/ObjectList.cs rename to osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs index 13bb32a113..cbde1f0f53 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ObjectList.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs @@ -1,56 +1,59 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Game.Rulesets.Mania.Objects; using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Mania.Objects; -namespace osu.Game.Rulesets.Mania.Beatmaps +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns { - internal class ObjectList + /// + /// Creates a pattern containing hit objects. + /// + internal class Pattern { private readonly List hitObjects = new List(); /// - /// All the hit objects contained in this list. + /// All the hit objects contained in this pattern. /// public IEnumerable HitObjects => hitObjects; /// - /// Whether a column of this list has been taken. + /// Whether this pattern already contains a hit object in a code. /// /// The column index. - /// Whether the column already contains a hit object. + /// Whether this pattern already contains a hit object in public bool IsFilled(int column) => hitObjects.Exists(h => h.Column == column); /// - /// Amount of columns taken up by hit objects in this list. + /// Amount of columns taken up by hit objects in this pattern. /// public int ColumnsFilled => HitObjects.GroupBy(h => h.Column).Count(); /// - /// Adds a hit object to this list. + /// Adds a hit object to this pattern. /// /// The hit object to add. public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject); /// - /// Copies hit object from another list to this one. + /// Copies hit object from another pattern to this one. /// - /// The other list. - public void Add(ObjectList other) + /// The other pattern. + public void Add(Pattern other) { other.HitObjects.ForEach(Add); } /// - /// Clears this list. + /// Clears this pattern, removing all hit objects. /// public void Clear() => hitObjects.Clear(); /// - /// Removes a hit object from this list. + /// Removes a hit object from this pattern. /// /// The hit object to remove. public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs new file mode 100644 index 0000000000..81aeb48c01 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs @@ -0,0 +1,89 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns +{ + /// + /// Generator to create a pattern from a hit object. + /// + internal abstract class PatternGenerator + { + /// + /// The number of columns available to create the pattern. + /// + protected readonly int AvailableColumns; + + /// + /// The last pattern. + /// + protected readonly Pattern PreviousPattern; + + /// + /// The hit object to create the pattern for. + /// + protected readonly HitObject HitObject; + + /// + /// The beatmap which is a part of. + /// + protected readonly Beatmap Beatmap; + + protected PatternGenerator(HitObject hitObject, Beatmap beatmap, Pattern previousPattern) + { + PreviousPattern = previousPattern; + HitObject = hitObject; + Beatmap = beatmap; + + AvailableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); + } + + /// + /// Generates the pattern for , filled with hit objects. + /// + /// The containing the hit objects. + public abstract Pattern Generate(); + + /// + /// Constructs and adds a note to a pattern. + /// + /// The pattern to add to. + /// The original hit object (used for samples). + /// The column to add the note to. + /// The start time of the note. + /// The end time of the note (set to for a non-hold note). + /// The number of children alongside this note (these will not be generated, but are used for volume calculations). + protected void AddToPattern(Pattern pattern, HitObject originalObject, int column, double startTime, double endTime, int siblings = 1) + { + ManiaHitObject newObject; + + if (startTime == endTime) + { + newObject = new Note + { + StartTime = startTime, + Samples = originalObject.Samples, + Column = column + }; + } + else + { + newObject = new HoldNote + { + StartTime = startTime, + Samples = originalObject.Samples, + Column = column, + Duration = endTime - startTime + }; + } + + // Todo: Consider siblings and write sample volumes (probably at ManiaHitObject level) + + pattern.Add(newObject); + } + } +} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 3d93dd3068..9de9cd703f 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -47,12 +47,13 @@ - - - - + + + + + - + From 164a1bb743f9c8bbf7ec2998199a0b431259fa9b Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 15:35:16 +0900 Subject: [PATCH 35/77] Remove distance object pattern generator for now (next PR). --- .../Beatmaps/LegacyBeatmapConverter.cs | 16 +- .../Legacy/DistanceObjectPatternGenerator.cs | 463 ------------------ .../osu.Game.Rulesets.Mania.csproj | 1 - 3 files changed, 3 insertions(+), 477 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs index 4108ba7809..4a98751b9d 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -18,10 +16,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// internal class LegacyBeatmapConverter { - private readonly FastRandom random; - - private Patterns.Pattern lastPattern = new Patterns.Pattern(); - private readonly int availableColumns; private readonly float localXDivisor; @@ -31,10 +25,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { this.beatmap = beatmap; - int seed = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.DrainRate + beatmap.BeatmapInfo.Difficulty.CircleSize) - * 20 + (int)(beatmap.BeatmapInfo.Difficulty.OverallDifficulty * 41.2) + (int)Math.Round(beatmap.BeatmapInfo.Difficulty.ApproachRate); - random = new FastRandom(seed); - availableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); localXDivisor = 512.0f / availableColumns; } @@ -97,7 +87,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps Patterns.PatternGenerator conversion = null; if (distanceData != null) - conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern); + { + // Slider + } else if (endTimeData != null) { // Spinner @@ -114,8 +106,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps foreach (ManiaHitObject obj in newPattern.HitObjects) yield return obj; - - lastPattern = newPattern; } private int getColumn(float position) => MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, availableColumns - 1); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs deleted file mode 100644 index 03240a2be4..0000000000 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ /dev/null @@ -1,463 +0,0 @@ -// 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 osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Mania.MathUtils; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy -{ - /// - /// A pattern generator for IHasDistance hit objects. - /// - internal class DistanceObjectPatternGenerator : PatternGenerator - { - /// - /// Base osu! slider scoring distance. - /// - private const float osu_base_scoring_distance = 100; - - private readonly double endTime; - private readonly int repeatCount; - - private PatternType convertType; - - public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern) - : base(random, hitObject, beatmap, previousPattern) - { - ControlPoint overridePoint; - ControlPoint controlPoint = Beatmap.TimingInfo.TimingPointAt(hitObject.StartTime, out overridePoint); - - convertType = PatternType.None; - if ((overridePoint ?? controlPoint)?.KiaiMode == false) - convertType = PatternType.LowProbability; - - var distanceData = hitObject as IHasDistance; - var repeatsData = hitObject as IHasRepeats; - - repeatCount = repeatsData?.RepeatCount ?? 1; - - double speedAdjustment = beatmap.TimingInfo.SpeedMultiplierAt(hitObject.StartTime); - double speedAdjustedBeatLength = beatmap.TimingInfo.BeatLengthAt(hitObject.StartTime) * speedAdjustment; - - // The true distance, accounting for any repeats. This ends up being the drum roll distance later - double distance = distanceData.Distance * repeatCount; - - // The velocity of the osu! hit object - calculated as the velocity of a slider - double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier / speedAdjustedBeatLength; - // The duration of the osu! hit object - double osuDuration = distance / osuVelocity; - - endTime = hitObject.StartTime + osuDuration; - } - - public override Pattern Generate() - { - double segmentDuration = endTime / repeatCount; - - if (repeatCount > 1) - { - if (segmentDuration <= 90) - return generateRandomHoldNotes(HitObject.StartTime, endTime, 1); - - if (segmentDuration <= 120) - { - convertType |= PatternType.ForceNotStack; - return generateRandomNotes(HitObject.StartTime, segmentDuration, repeatCount); - } - - if (segmentDuration <= 160) - return generateStair(HitObject.StartTime, segmentDuration); - - if (segmentDuration <= 200 && ConversionDifficulty > 3) - return generateRandomMultipleNotes(HitObject.StartTime, segmentDuration, repeatCount); - - double duration = endTime - HitObject.StartTime; - if (duration >= 4000) - return generateNRandomNotes(HitObject.StartTime, endTime, 0.23, 0, 0); - - if (segmentDuration > 400 && duration < 4000 && repeatCount < AvailableColumns - 1 - RandomStart) - return generateTiledHoldNotes(HitObject.StartTime, segmentDuration, repeatCount); - - return generateHoldAndNormalNotes(HitObject.StartTime, segmentDuration); - } - - if (segmentDuration <= 110) - { - if (PreviousPattern.ColumnsFilled < AvailableColumns) - convertType |= PatternType.ForceNotStack; - else - convertType &= ~PatternType.ForceNotStack; - return generateRandomNotes(HitObject.StartTime, segmentDuration, segmentDuration < 80 ? 0 : 1); - } - - if (ConversionDifficulty > 6.5) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateNRandomNotes(HitObject.StartTime, endTime, 0.78, 0.3, 0); - return generateNRandomNotes(HitObject.StartTime, endTime, 0.85, 0.36, 0.03); - } - - if (ConversionDifficulty > 4) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateNRandomNotes(HitObject.StartTime, endTime, 0.43, 0.08, 0); - return generateNRandomNotes(HitObject.StartTime, endTime, 0.56, 0.18, 0); - } - - if (ConversionDifficulty > 2.5) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateNRandomNotes(HitObject.StartTime, endTime, 0.3, 0, 0); - return generateNRandomNotes(HitObject.StartTime, endTime, 0.37, 0.08, 0); - } - - if ((convertType & PatternType.LowProbability) > 0) - return generateNRandomNotes(HitObject.StartTime, endTime, 0.17, 0, 0); - return generateNRandomNotes(HitObject.StartTime, endTime, 0.27, 0, 0); - } - - /// - /// Generates random hold notes that start at an span the same amount of rows. - /// - /// Start time of each hold note. - /// End time of the hold notes. - /// Number of hold notes. - /// The containing the hit objects. - private Pattern generateRandomHoldNotes(double startTime, double endTime, int noteCount) - { - // - - - - - // ■ - ■ ■ - // □ - □ □ - // ■ - ■ ■ - - var pattern = new Pattern(); - - int usableColumns = AvailableColumns - RandomStart - PreviousPattern.ColumnsFilled; - int nextColumn = Random.Next(RandomStart, AvailableColumns); - for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) - { - while (pattern.IsFilled(nextColumn) || PreviousPattern.IsFilled(nextColumn)) //find available column - nextColumn = Random.Next(RandomStart, AvailableColumns); - AddToPattern(pattern, HitObject, nextColumn, startTime, endTime, noteCount); - } - - // This is can't be combined with the above loop due to RNG - for (int i = 0; i < noteCount - usableColumns; i++) - { - while (pattern.IsFilled(nextColumn)) - nextColumn = Random.Next(RandomStart, AvailableColumns); - AddToPattern(pattern, HitObject, nextColumn, startTime, endTime, noteCount); - } - - return pattern; - } - - /// - /// Generates random notes, with one note per row and no stacking. - /// - /// The start time. - /// The separation of notes between rows. - /// The number of rows. - /// The containing the hit objects. - private Pattern generateRandomNotes(double startTime, double separationTime, int repeatCount) - { - // - - - - - // x - - - - // - - x - - // - - - x - // x - - - - - var pattern = new Pattern(); - - int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnsFilled < AvailableColumns) - { - while (PreviousPattern.IsFilled(nextColumn)) - nextColumn = Random.Next(RandomStart, AvailableColumns); - } - - int lastColumn = nextColumn; - for (int i = 0; i <= repeatCount; i++) - { - AddToPattern(pattern, HitObject, nextColumn, startTime, startTime); - while (nextColumn == lastColumn) - nextColumn = Random.Next(RandomStart, AvailableColumns); - - lastColumn = nextColumn; - startTime += separationTime; - } - - return pattern; - } - - /// - /// Generates a stair of notes, with one note per row. - /// - /// The start time. - /// The separation of notes between rows. - /// The containing the hit objects. - private Pattern generateStair(double startTime, double separationTime) - { - // - - - - - // x - - - - // - x - - - // - - x - - // - - - x - // - - x - - // - x - - - // x - - - - - var pattern = new Pattern(); - - int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - bool increasing = Random.NextDouble() > 0.5; - - for (int i = 0; i <= repeatCount; i++) - { - AddToPattern(pattern, HitObject, column, startTime, startTime); - startTime += separationTime; - - // Check if we're at the borders of the stage, and invert the pattern if so - if (increasing) - { - if (column >= AvailableColumns - 1) - { - increasing = false; - column--; - } - else - column++; - } - else - { - if (column <= RandomStart) - { - increasing = true; - column++; - } - else - column--; - } - } - - return pattern; - } - - /// - /// Generates random notes with 1-2 notes per row and no stacking. - /// - /// The start time. - /// The separation of notes between rows. - /// The number of rows. - /// The containing the hit objects. - private Pattern generateRandomMultipleNotes(double startTime, double separationTime, int repeatCount) - { - // - - - - - // x - - - // - x x - - // - - - x - // x - x - - - var pattern = new Pattern(); - - bool legacy = AvailableColumns >= 4 && AvailableColumns <= 8; - int interval = Random.Next(1, AvailableColumns - (legacy ? 1 : 0)); - - int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - for (int i = 0; i <= repeatCount; i++) - { - AddToPattern(pattern, HitObject, nextColumn, startTime, startTime, 2); - - nextColumn += interval; - if (nextColumn >= AvailableColumns - RandomStart) - nextColumn = nextColumn - AvailableColumns - RandomStart + (legacy ? 1 : 0); - nextColumn += RandomStart; - - // If we're in 2K, let's not add many consecutive doubles - if (AvailableColumns > 2) - AddToPattern(pattern, HitObject, nextColumn, startTime, startTime, 2); - - nextColumn = Random.Next(RandomStart, AvailableColumns); - startTime += separationTime; - } - - return pattern; - } - - /// - /// Generates random hold notes. The amount of hold notes generated is determined by probabilities. - /// - /// The hold note start time. - /// The hold note end time. - /// The probability required for 2 hold notes to be generated. - /// The probability required for 3 hold notes to be generated. - /// The probability required for 4 hold notes to be generated. - /// The containing the hit objects. - private Pattern generateNRandomNotes(double startTime, double endTime, double p2, double p3, double p4) - { - // - - - - - // ■ - ■ ■ - // □ - □ □ - // ■ - ■ ■ - - switch (AvailableColumns) - { - case 2: - p2 = 0; - p3 = 0; - p4 = 0; - break; - case 3: - p2 = Math.Max(p2, 0.1); - p3 = 0; - p4 = 0; - break; - case 4: - p2 = Math.Max(p2, 0.3); - p3 = Math.Max(p3, 0.04); - p4 = 0; - break; - case 5: - p2 = Math.Max(p2, 0.34); - p3 = Math.Max(p3, 0.1); - p4 = Math.Max(p4, 0.03); - break; - } - - Func isDoubleSample = sample => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH; - - bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0; - canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample); - - if (canGenerateTwoNotes) - p2 = 1; - - return generateRandomHoldNotes(startTime, endTime, GetRandomNoteCount(p2, p3, p4)); - } - - /// - /// Generates tiled hold notes. You can think of this as a stair of hold notes. - /// - /// The first hold note start time. - /// The separation time between hold notes. - /// The amount of hold notes. - /// The containing the hit objects. - private Pattern generateTiledHoldNotes(double startTime, double separationTime, int noteCount) - { - // - - - - - // ■ ■ ■ ■ - // □ □ □ □ - // □ □ □ □ - // □ □ □ ■ - // □ □ ■ - - // □ ■ - - - // ■ - - - - - var pattern = new Pattern(); - - int columnRepeat = Math.Min(noteCount, AvailableColumns); - - int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnsFilled < AvailableColumns) - { - while (PreviousPattern.IsFilled(nextColumn)) - nextColumn = Random.Next(RandomStart, AvailableColumns); - } - - for (int i = 0; i < columnRepeat; i++) - { - while (pattern.IsFilled(nextColumn)) - nextColumn = Random.Next(RandomStart, AvailableColumns); - - AddToPattern(pattern, HitObject, nextColumn, startTime, endTime, noteCount); - startTime += separationTime; - } - - return pattern; - } - - /// - /// Generates a hold note alongside normal notes. - /// - /// The start time of notes. - /// The separation time between notes. - /// The containing the hit objects. - private Pattern generateHoldAndNormalNotes(double startTime, double separationTime) - { - // - - - - - // ■ x x - - // ■ - x x - // ■ x - x - // ■ - x x - - var pattern = new Pattern(); - - int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnsFilled < AvailableColumns) - { - while (PreviousPattern.IsFilled(holdColumn)) - holdColumn = Random.Next(RandomStart, AvailableColumns); - } - - // Create the hold note - AddToPattern(pattern, HitObject, holdColumn, startTime, separationTime * repeatCount); - - int noteCount = 1; - if (ConversionDifficulty > 6.5) - noteCount = GetRandomNoteCount(0.63, 0); - else if (ConversionDifficulty > 4) - noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0.12 : 0.45, 0); - else if (ConversionDifficulty > 2.5) - noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0 : 0.24, 0); - noteCount = Math.Min(AvailableColumns - 1, noteCount); - - bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); - int nextColumn = Random.Next(RandomStart, AvailableColumns); - - var rowPattern = new Pattern(); - for (int i = 0; i <= repeatCount; i++) - { - if (!(ignoreHead && startTime == HitObject.StartTime)) - { - for (int j = 0; j < noteCount; j++) - { - while (rowPattern.IsFilled(nextColumn) || nextColumn == holdColumn) - nextColumn = Random.Next(RandomStart, AvailableColumns); - AddToPattern(rowPattern, HitObject, nextColumn, startTime, startTime, noteCount + 1); - } - } - - pattern.Add(rowPattern); - rowPattern.Clear(); - - startTime += separationTime; - } - - return pattern; - } - - /// - /// Retrieves the sample info list at a point in time. - /// - /// The time to retrieve the sample info list from. - /// - private SampleInfoList sampleInfoListAt(double time) - { - var curveData = HitObject as IHasCurve; - - if (curveData == null) - return HitObject.Samples; - - double segmentTime = (curveData.EndTime - HitObject.StartTime) / repeatCount; - - int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); - return curveData.RepeatSamples[index]; - } - } -} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 9de9cd703f..ea65588a81 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -47,7 +47,6 @@ - From af34777f44b93c4c23e2214dd352f2b6647ed8d6 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 17:00:11 +0900 Subject: [PATCH 36/77] Trim whitespace. --- osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs index 4a98751b9d..ceb83be7ca 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps internal class LegacyBeatmapConverter { private readonly int availableColumns; - private readonly float localXDivisor; + private readonly float localXDivisor; private readonly Beatmap beatmap; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs index 81aeb48c01..6ccd39c6ac 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns /// The hit object to create the pattern for. /// protected readonly HitObject HitObject; - + /// /// The beatmap which is a part of. /// From 873ff344874d16bcf7494450ba0a7b13e5b9022c Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 17:43:59 +0900 Subject: [PATCH 37/77] This function can't be used for all pattern generations. So let's not use it. --- .../Beatmaps/Patterns/PatternGenerator.cs | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs index 6ccd39c6ac..558d64080b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs @@ -47,43 +47,5 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns /// /// The containing the hit objects. public abstract Pattern Generate(); - - /// - /// Constructs and adds a note to a pattern. - /// - /// The pattern to add to. - /// The original hit object (used for samples). - /// The column to add the note to. - /// The start time of the note. - /// The end time of the note (set to for a non-hold note). - /// The number of children alongside this note (these will not be generated, but are used for volume calculations). - protected void AddToPattern(Pattern pattern, HitObject originalObject, int column, double startTime, double endTime, int siblings = 1) - { - ManiaHitObject newObject; - - if (startTime == endTime) - { - newObject = new Note - { - StartTime = startTime, - Samples = originalObject.Samples, - Column = column - }; - } - else - { - newObject = new HoldNote - { - StartTime = startTime, - Samples = originalObject.Samples, - Column = column, - Duration = endTime - startTime - }; - } - - // Todo: Consider siblings and write sample volumes (probably at ManiaHitObject level) - - pattern.Add(newObject); - } } } From afd3eb4b8d53e020d1b971c4cd76d234fd0c683a Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 18:04:32 +0900 Subject: [PATCH 38/77] Implement things a bit more to remove warnings. --- .../Beatmaps/LegacyBeatmapConverter.cs | 113 +++++++++++------- .../Beatmaps/Patterns/PatternGenerator.cs | 1 - osu.Game/Beatmaps/Events/EventInfo.cs | 7 ++ 3 files changed, 78 insertions(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs index ceb83be7ca..3aa88af62d 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs @@ -7,7 +7,8 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; -using OpenTK; +using osu.Game.Rulesets.Mania.Beatmaps.Patterns; +using osu.Game.Rulesets.Mania.MathUtils; namespace osu.Game.Rulesets.Mania.Beatmaps { @@ -16,17 +17,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// internal class LegacyBeatmapConverter { - private readonly int availableColumns; - private readonly float localXDivisor; + private Pattern lastPattern = new Pattern(); + private readonly FastRandom random; private readonly Beatmap beatmap; public LegacyBeatmapConverter(Beatmap beatmap) { this.beatmap = beatmap; - availableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); - localXDivisor = 512.0f / availableColumns; + int seed = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.DrainRate + beatmap.BeatmapInfo.Difficulty.CircleSize) + * 20 + (int)(beatmap.BeatmapInfo.Difficulty.OverallDifficulty * 41.2) + (int)Math.Round(beatmap.BeatmapInfo.Difficulty.ApproachRate); + random = new FastRandom(seed); } public IEnumerable Convert(HitObject original) @@ -38,44 +40,32 @@ namespace osu.Game.Rulesets.Mania.Beatmaps yield break; } - if (beatmap.BeatmapInfo.RulesetID == 3) - yield return generateSpecific(original); - else + IEnumerable objects = null; + switch (beatmap.BeatmapInfo.RulesetID) { - foreach (ManiaHitObject c in generateConverted(original)) - yield return c; + default: + objects = generateConverted(original); + break; + case 3: + objects = generateSpecific(original); + break; } + + if (objects == null) + yield break; + + foreach (ManiaHitObject obj in objects) + yield return obj; } - private ManiaHitObject generateSpecific(HitObject original) + private IEnumerable generateSpecific(HitObject original) { - var endTimeData = original as IHasEndTime; - var positionData = original as IHasXPosition; + var generator = new SpecificPatternGenerator(random, original, beatmap, lastPattern); - int column = getColumn(positionData?.X ?? 0); + Pattern newPattern = generator.Generate(); + lastPattern = newPattern; - if (endTimeData != null) - { - return new HoldNote - { - StartTime = original.StartTime, - Samples = original.Samples, - Duration = endTimeData.Duration, - Column = column, - }; - } - - if (positionData != null) - { - return new Note - { - StartTime = original.StartTime, - Samples = original.Samples, - Column = column - }; - } - - return null; + return newPattern.HitObjects; } private IEnumerable generateConverted(HitObject original) @@ -84,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps var distanceData = original as IHasDistance; var positionData = original as IHasPosition; - Patterns.PatternGenerator conversion = null; + PatternGenerator conversion = null; if (distanceData != null) { @@ -100,14 +90,53 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } if (conversion == null) - yield break; + return new ManiaHitObject[0]; - Patterns.Pattern newPattern = conversion.Generate(); + Pattern newPattern = conversion.Generate(); + lastPattern = newPattern; - foreach (ManiaHitObject obj in newPattern.HitObjects) - yield return obj; + return newPattern.HitObjects; } - private int getColumn(float position) => MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, availableColumns - 1); + /// + /// A pattern generator for mania-specific beatmaps. + /// + private class SpecificPatternGenerator : Patterns.Legacy.PatternGenerator + { + public SpecificPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern) + : base(random, hitObject, beatmap, previousPattern) + { + } + + public override Pattern Generate() + { + var endTimeData = HitObject as IHasEndTime; + var positionData = HitObject as IHasXPosition; + + var pattern = new Pattern(); + + if (endTimeData != null) + { + pattern.Add(new HoldNote + { + StartTime = HitObject.StartTime, + Samples = HitObject.Samples, + Duration = endTimeData.Duration, + Column = GetColumn(positionData?.X ?? 0), + }); + } + else if (positionData != null) + { + pattern.Add(new Note + { + StartTime = HitObject.StartTime, + Samples = HitObject.Samples, + Column = GetColumn(positionData?.X ?? 0) + }); + } + + return pattern; + } + } } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs index 558d64080b..dda4d07182 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs @@ -3,7 +3,6 @@ using System; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns diff --git a/osu.Game/Beatmaps/Events/EventInfo.cs b/osu.Game/Beatmaps/Events/EventInfo.cs index 646dea898e..3ba3d5ba03 100644 --- a/osu.Game/Beatmaps/Events/EventInfo.cs +++ b/osu.Game/Beatmaps/Events/EventInfo.cs @@ -22,5 +22,12 @@ namespace osu.Game.Beatmaps.Events /// Total duration of all breaks. /// public double TotalBreakTime => Breaks.Sum(b => b.Duration); + + /// + /// Retrieves the active background at a time. + /// + /// The time to retrieve the background at. + /// The background. + public BackgroundEvent BackgroundAt(double time) => Backgrounds.FirstOrDefault(b => b.StartTime <= time); } } From af129b3eaba1233654e87b6de4185876933ba033 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 18:13:08 +0900 Subject: [PATCH 39/77] Add siblings, will be used in generator branches. --- osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs index 93aaa94f45..f6eb4aea2c 100644 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs @@ -9,5 +9,11 @@ namespace osu.Game.Rulesets.Mania.Objects public abstract class ManiaHitObject : HitObject, IHasColumn { public int Column { get; set; } + + /// + /// The number of other that start at + /// the same time as this hit object. + /// + public int Siblings { get; set; } } } From c80e2e8f0a16840448874d861790db4ba59c765c Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 18:46:50 +0900 Subject: [PATCH 40/77] Attempt to solve warnings... --- .../Beatmaps/LegacyBeatmapConverter.cs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs index 3aa88af62d..e3045df185 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/LegacyBeatmapConverter.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps yield break; } - IEnumerable objects = null; + IEnumerable objects; switch (beatmap.BeatmapInfo.RulesetID) { default: @@ -74,7 +74,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps var distanceData = original as IHasDistance; var positionData = original as IHasPosition; - PatternGenerator conversion = null; + // Following lines currently commented out to appease resharper + + //PatternGenerator conversion = null; if (distanceData != null) { @@ -89,13 +91,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps // Circle } - if (conversion == null) - return new ManiaHitObject[0]; + //if (conversion == null) + return null; - Pattern newPattern = conversion.Generate(); - lastPattern = newPattern; + //Pattern newPattern = conversion.Generate(); + //lastPattern = newPattern; - return newPattern.HitObjects; + //return newPattern.HitObjects; } /// @@ -113,6 +115,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps var endTimeData = HitObject as IHasEndTime; var positionData = HitObject as IHasXPosition; + int column = GetColumn(positionData?.X ?? 0); + var pattern = new Pattern(); if (endTimeData != null) @@ -122,7 +126,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps StartTime = HitObject.StartTime, Samples = HitObject.Samples, Duration = endTimeData.Duration, - Column = GetColumn(positionData?.X ?? 0), + Column = column, }); } else if (positionData != null) @@ -131,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { StartTime = HitObject.StartTime, Samples = HitObject.Samples, - Column = GetColumn(positionData?.X ?? 0) + Column = column }); } From 4e8ead9cf450e47713e39b16cf2050e59254cbe4 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 18 May 2017 19:05:03 +0900 Subject: [PATCH 41/77] Fix possible nullref. --- osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index 3c31c6b854..b699758c3e 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.UI return t; }); - double lastObjectTime = (Objects.Last() as IHasEndTime)?.EndTime ?? Objects.Last().StartTime; + double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; // Perform some post processing of the timing changes timingChanges = timingChanges From f112546085499eccd445624ad45f8ab8a064135b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 May 2017 19:39:40 +0900 Subject: [PATCH 42/77] Don't load a mode when opening TestCaseHitObjects --- osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs index dceb7a9cff..8c913ae95e 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs @@ -129,8 +129,6 @@ namespace osu.Desktop.VisualTests.Tests }; Add(clockAdjustContainer); - - load(mode); } private int depth; From 9cd41c0e87894a2c70db18ef539211eb5cfbe60d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 May 2017 19:40:20 +0900 Subject: [PATCH 43/77] Spinner design refresh --- .../Objects/Drawables/DrawableSpinner.cs | 138 ++++++++++++------ .../Objects/Drawables/Pieces/CirclePiece.cs | 1 - .../Drawables/Pieces/SpinnerBackground.cs | 46 +++++- .../Objects/Drawables/Pieces/SpinnerDisc.cs | 138 +++++------------- .../Objects/Drawables/Pieces/SpinnerTicks.cs | 71 +++++++++ .../osu.Game.Rulesets.Osu.csproj | 5 +- 6 files changed, 249 insertions(+), 150 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index a8ff231cc7..642f088445 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -1,15 +1,16 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using OpenTK; using OpenTK.Graphics; -using osu.Game.Rulesets.Osu.UI; +using osu.Game.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Allocation; +using osu.Game.Screens.Ranking; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -18,9 +19,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly Spinner spinner; private readonly SpinnerDisc disc; + private readonly SpinnerTicks ticks; + + private readonly Container mainContainer; + private readonly SpinnerBackground background; private readonly Container circleContainer; - private readonly DrawableHitCircle circle; + private readonly CirclePiece circle; + + private readonly TextAwesome symbol; + + private Color4 normalColour; + private Color4 completeColour; public DrawableSpinner(Spinner s) : base(s) { @@ -29,57 +39,84 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Origin = Anchor.Centre; Position = s.Position; - //take up full playfield. - Size = new Vector2(OsuPlayfield.BASE_SIZE.X); + RelativeSizeAxes = Axes.Both; + + // we are slightly bigger than our parent, to clip the top and bottom of the circle + Height = 1.2f; spinner = s; Children = new Drawable[] { - background = new SpinnerBackground - { - Alpha = 0, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - DiscColour = Color4.Black - }, - disc = new SpinnerDisc - { - Alpha = 0, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - DiscColour = AccentColour - }, circleContainer = new Container { AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Children = new [] + Children = new Drawable[] { - circle = new DrawableHitCircle(s) + circle = new CirclePiece { - Interactive = false, Position = Vector2.Zero, Anchor = Anchor.Centre, + }, + new RingPiece(), + symbol = new TextAwesome + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + UseFullGlyphHeight = true, + TextSize = 48, + Icon = FontAwesome.fa_asterisk, + Shadow = false, } } - } + }, + mainContainer = new AspectContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + background = new SpinnerBackground + { + Alpha = 0.2f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + disc = new SpinnerDisc(spinner) + { + Scale = Vector2.Zero, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + circleContainer.CreateProxy(), + ticks = new SpinnerTicks + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } + }, }; - - background.Scale = scaleToCircle; - disc.Scale = scaleToCircle; } + public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1); + protected override void CheckJudgement(bool userTriggered) { if (Time.Current < HitObject.StartTime) return; - disc.ScaleTo(Interpolation.ValueAt(Math.Sqrt(Progress), scaleToCircle, Vector2.One, 0, 1), 100); - - if (Progress >= 1) + if (Progress >= 1 && !disc.Complete) + { disc.Complete = true; + disc.FadeAccent(completeColour, 500); + background.FadeAccent(completeColour, 500); + circle.FadeColour(completeColour, 500); + } + if (!userTriggered && Time.Current >= spinner.EndTime) { if (Progress >= 1) @@ -106,26 +143,45 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - private Vector2 scaleToCircle => circle.Scale * circle.DrawWidth / DrawWidth * 0.95f; + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + normalColour = colours.BlueDark; + completeColour = colours.YellowLight.Opacity(0.8f); - public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1); + disc.AccentColour = normalColour; + background.AccentColour = normalColour; + circle.Colour = normalColour; + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + circle.Rotation = disc.Rotation; + ticks.Rotation = disc.Rotation; + + float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; + disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, EasingTypes.OutQuint); + + symbol.RotateTo(disc.Rotation / 2, 500, EasingTypes.OutQuint); + } protected override void UpdatePreemptState() { base.UpdatePreemptState(); - circleContainer.ScaleTo(1, 400, EasingTypes.OutElastic); + circleContainer.ScaleTo(spinner.Scale * 0.3f); + circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, EasingTypes.OutQuint); - background.Delay(TIME_PREEMPT - 500); + disc.RotateTo(-720); + symbol.RotateTo(-720); - background.ScaleTo(scaleToCircle * 1.2f, 400, EasingTypes.OutQuint); - background.FadeIn(200); + mainContainer.ScaleTo(0); + mainContainer.ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, EasingTypes.OutQuint); - background.Delay(400); - background.ScaleTo(1, 250, EasingTypes.OutQuint); - - disc.Delay(TIME_PREEMPT - 50); - disc.FadeIn(200); + mainContainer.Delay(TIME_PREEMPT - 150); + mainContainer.ScaleTo(1, 500, EasingTypes.OutQuint); } protected override void UpdateCurrentState(ArmedState state) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index 9a90c07517..3004dafda7 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { private readonly Sprite disc; - public Func Hit; public CirclePiece() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs index 72024bbe99..b3ce075575 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs @@ -1,10 +1,54 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class SpinnerBackground : SpinnerDisc + public class SpinnerBackground : CircularContainer, IHasAccentColour { public override bool HandleInput => false; + + protected Sprite Disc; + + public Color4 AccentColour + { + get { return Disc.Colour; } + set { Disc.Colour = value; } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Glow, + Radius = 14, + Colour = colours.BlueLight.Opacity(0.3f), + }; + } + + public SpinnerBackground() + { + RelativeSizeAxes = Axes.Both; + Masking = true; + + Children = new Drawable[] + { + Disc = new Box + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Alpha = 1, + }, + }; + } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs index 71adba74c7..2d50754b4f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs @@ -2,13 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Transforms; using osu.Framework.Input; using osu.Game.Graphics; @@ -17,104 +12,31 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class SpinnerDisc : CircularContainer + public class SpinnerDisc : CircularContainer, IHasAccentColour { - protected Sprite Disc; + private readonly Spinner spinner; - public SRGBColour DiscColour + public Color4 AccentColour { - get { return Disc.Colour; } - set { Disc.Colour = value; } + get { return background.AccentColour; } + set { background.AccentColour = value; } } - private Color4 completeColour; + private readonly SpinnerBackground background; - [BackgroundDependencyLoader] - private void load(OsuColour colours) + private const float idle_alpha = 0.1f; + private const float tracking_alpha = 0.3f; + + public SpinnerDisc(Spinner s) { - completeColour = colours.YellowLight.Opacity(0.8f); - Masking = true; - } + spinner = s; - private class SpinnerBorder : Container - { - public SpinnerBorder() - { - Origin = Anchor.Centre; - Anchor = Anchor.Centre; - RelativeSizeAxes = Axes.Both; - - layout(); - } - - private int lastLayoutDotCount; - private void layout() - { - int count = (int)(MathHelper.Pi * ScreenSpaceDrawQuad.Width / 9); - - if (count == lastLayoutDotCount) return; - - lastLayoutDotCount = count; - - while (Children.Count() < count) - { - Add(new CircularContainer - { - Colour = Color4.White, - RelativePositionAxes = Axes.Both, - Masking = true, - Origin = Anchor.Centre, - Size = new Vector2(1 / ScreenSpaceDrawQuad.Width * 2000), - Children = new[] - { - new Box - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - } - } - }); - } - - var size = new Vector2(1 / ScreenSpaceDrawQuad.Width * 2000); - - int i = 0; - foreach (var d in Children) - { - d.Size = size; - d.Position = new Vector2( - 0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2, - 0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2 - ); - - i++; - } - } - - protected override void Update() - { - base.Update(); - layout(); - } - } - - public SpinnerDisc() - { AlwaysReceiveInput = true; - RelativeSizeAxes = Axes.Both; Children = new Drawable[] { - Disc = new Box - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Alpha = 0.2f, - }, - new SpinnerBorder() + background = new SpinnerBackground { Alpha = idle_alpha }, }; } @@ -125,10 +47,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces set { if (value == tracking) return; - tracking = value; - Disc.FadeTo(tracking ? 0.5f : 0.2f, 100); + background.FadeTo(tracking ? tracking_alpha : idle_alpha, 100); } } @@ -139,31 +60,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces set { if (value == complete) return; - complete = value; - Disc.FadeColour(completeColour, 200); - updateCompleteTick(); } } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - Tracking = true; + Tracking |= state.Mouse.HasMainButtonPressed; return base.OnMouseDown(state, args); } protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - Tracking = false; + Tracking &= state.Mouse.HasMainButtonPressed; return base.OnMouseUp(state, args); } protected override bool OnMouseMove(InputState state) { Tracking |= state.Mouse.HasMainButtonPressed; - mousePosition = state.Mouse.Position; + mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position); return base.OnMouseMove(state); } @@ -177,13 +95,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360)); + private bool rotationTransferred; + protected override void Update() { base.Update(); var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2)); - if (tracking) + + bool validAndTracking = tracking && spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current; + + if (validAndTracking) { + if (!rotationTransferred) + { + currentRotation = Rotation * 2; + rotationTransferred = true; + } + if (thisAngle - lastAngle > 180) lastAngle += 360; else if (lastAngle - thisAngle > 180) @@ -192,17 +121,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces currentRotation += thisAngle - lastAngle; RotationAbsolute += Math.Abs(thisAngle - lastAngle); } + lastAngle = thisAngle; if (Complete && updateCompleteTick()) { - Disc.Flush(flushType: typeof(TransformAlpha)); - Disc.FadeTo(0.75f, 30, EasingTypes.OutExpo); - Disc.Delay(30); - Disc.FadeTo(0.5f, 250, EasingTypes.OutQuint); + background.Flush(flushType: typeof(TransformAlpha)); + background.FadeTo(0.75f, 60, EasingTypes.OutExpo); + background.Delay(60); + background.FadeTo(0.5f, 250, EasingTypes.OutQuint); } - RotateTo(currentRotation, 100, EasingTypes.OutExpo); + RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, EasingTypes.OutExpo); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs new file mode 100644 index 0000000000..d08051eed6 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs @@ -0,0 +1,71 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class SpinnerTicks : Container + { + private Color4 glowColour; + + public SpinnerTicks() + { + Origin = Anchor.Centre; + Anchor = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + glowColour = colours.BlueDarker.Opacity(0.4f); + layout(); + } + + private void layout() + { + const int count = 18; + + for (int i = 0; i < count; i++) + { + Add(new Container + { + Colour = Color4.Black, + Alpha = 0.4f, + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Glow, + Radius = 20, + Colour = glowColour, + }, + RelativePositionAxes = Axes.Both, + Masking = true, + CornerRadius = 5, + Size = new Vector2(60, 10), + Origin = Anchor.Centre, + Position = new Vector2( + 0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2 * 0.9f, + 0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2 * 0.9f + ), + Rotation = -(float)i / count * 360 + 90, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + } + } + }); + } + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 8974b1bcbd..b91bdc6a78 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -64,6 +64,7 @@ + @@ -103,9 +104,7 @@ - - - +