diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs index 1049a8818a..6bd9d35b80 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs @@ -85,25 +85,25 @@ namespace osu.Desktop.VisualTests.Tests Clock = new FramedClock(), Children = new Drawable[] { - new OsuHitRenderer(beatmap) + new OsuHitRenderer(beatmap, false) { Scale = new Vector2(0.5f), Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft }, - new TaikoHitRenderer(beatmap) + new TaikoHitRenderer(beatmap, false) { Scale = new Vector2(0.5f), Anchor = Anchor.TopRight, Origin = Anchor.TopRight }, - new CatchHitRenderer(beatmap) + new CatchHitRenderer(beatmap, false) { Scale = new Vector2(0.5f), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft }, - new ManiaHitRenderer(beatmap) + new ManiaHitRenderer(beatmap, false) { Scale = new Vector2(0.5f), Anchor = Anchor.BottomRight, 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.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index a6faf13d51..53449fd5f5 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch { public class CatchRuleset : Ruleset { - public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new CatchHitRenderer(beatmap); + public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchHitRenderer(beatmap, isForCurrentRuleset); public override IEnumerable GetModsFor(ModType type) { diff --git a/osu.Game.Rulesets.Catch/UI/CatchHitRenderer.cs b/osu.Game.Rulesets.Catch/UI/CatchHitRenderer.cs index f34585be55..179440adb3 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchHitRenderer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchHitRenderer.cs @@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Catch.UI { public class CatchHitRenderer : HitRenderer { - public CatchHitRenderer(WorkingBeatmap beatmap) - : base(beatmap) + public CatchHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(beatmap, isForCurrentRuleset) { } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index e51bbcdc13..933fe0787c 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -1,35 +1,153 @@ // 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 osu.Game.Rulesets.Mania.Beatmaps.Patterns; +using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Database; namespace osu.Game.Rulesets.Mania.Beatmaps { - internal class ManiaBeatmapConverter : BeatmapConverter + public class ManiaBeatmapConverter : BeatmapConverter { protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; + private Pattern lastPattern = new Pattern(); + private FastRandom random; + private Beatmap beatmap; + private bool isForCurrentRuleset; + + protected override Beatmap ConvertBeatmap(Beatmap original, bool isForCurrentRuleset) + { + this.isForCurrentRuleset = isForCurrentRuleset; + + beatmap = original; + + BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty; + + int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate); + random = new FastRandom(seed); + + return base.ConvertBeatmap(original, isForCurrentRuleset); + } + 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 + var maniaOriginal = original as ManiaHitObject; + if (maniaOriginal != null) { - StartTime = original.StartTime, - Column = column, - }; + yield return maniaOriginal; + yield break; + } + + var objects = isForCurrentRuleset ? generateSpecific(original) : generateConverted(original); + + if (objects == null) + yield break; + + foreach (ManiaHitObject obj in objects) + yield return obj; + } + + /// + /// Method that generates hit objects for osu!mania specific beatmaps. + /// + /// The original hit object. + /// The hit objects generated. + private IEnumerable generateSpecific(HitObject original) + { + var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern); + + Pattern newPattern = generator.Generate(); + lastPattern = newPattern; + + return newPattern.HitObjects; + } + + /// + /// Method that generates hit objects for non-osu!mania beatmaps. + /// + /// The original hit object. + /// The hit objects generated. + private IEnumerable generateConverted(HitObject original) + { + var endTimeData = original as IHasEndTime; + var distanceData = original as IHasDistance; + var positionData = original as IHasPosition; + + // Following lines currently commented out to appease resharper + + //Patterns.PatternGenerator conversion = null; + + if (distanceData != null) + { + // Slider + } + else if (endTimeData != null) + { + // Spinner + } + else if (positionData != null) + { + // Circle + } + + //if (conversion == null) + return null; + + //Pattern newPattern = conversion.Generate(); + //lastPattern = newPattern; + + //return newPattern.HitObjects; + } + + /// + /// A pattern generator for osu!mania-specific beatmaps. + /// + private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator + { + public SpecificBeatmapPatternGenerator(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; + + int column = GetColumn(positionData?.X ?? 0); + + var pattern = new Pattern(); + + if (endTimeData != null) + { + pattern.Add(new HoldNote + { + StartTime = HitObject.StartTime, + Samples = HitObject.Samples, + Duration = endTimeData.Duration, + Column = column, + }); + } + else if (positionData != null) + { + pattern.Add(new Note + { + StartTime = HitObject.StartTime, + Samples = HitObject.Samples, + Column = column + }); + } + + return pattern; + } } } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs new file mode 100644 index 0000000000..ad07c03b96 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -0,0 +1,106 @@ +// 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.Beatmaps; +using osu.Game.Database; +using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Rulesets.Objects; +using OpenTK; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy +{ + /// + /// A pattern generator for legacy hit objects. + /// + internal abstract class PatternGenerator : Patterns.PatternGenerator + { + /// + /// The column index at which to start generating random notes. + /// + protected readonly int RandomStart; + + /// + /// The random number generator to use. + /// + protected readonly FastRandom Random; + + protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern) + : base(hitObject, beatmap, previousPattern) + { + Random = random; + + RandomStart = AvailableColumns == 8 ? 1 : 0; + } + + /// + /// 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 = 0, double p5 = 0, double p6 = 0) + { + 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; + } + + 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; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs new file mode 100644 index 0000000000..d4957d41a9 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs @@ -0,0 +1,65 @@ +// 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.Patterns.Legacy +{ + /// + /// The type of pattern to generate. Used for legacy patterns. + /// + [Flags] + internal enum PatternType + { + 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/Patterns/Pattern.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs new file mode 100644 index 0000000000..cbde1f0f53 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs @@ -0,0 +1,61 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Mania.Objects; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns +{ + /// + /// Creates a pattern containing hit objects. + /// + internal class Pattern + { + private readonly List hitObjects = new List(); + + /// + /// All the hit objects contained in this pattern. + /// + public IEnumerable HitObjects => hitObjects; + + /// + /// Whether this pattern already contains a hit object in a code. + /// + /// The column index. + /// 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 pattern. + /// + public int ColumnsFilled => HitObjects.GroupBy(h => h.Column).Count(); + + /// + /// 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 pattern to this one. + /// + /// The other pattern. + public void Add(Pattern other) + { + other.HitObjects.ForEach(Add); + } + + /// + /// Clears this pattern, removing all hit objects. + /// + public void Clear() => hitObjects.Clear(); + + /// + /// 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..dda4d07182 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs @@ -0,0 +1,50 @@ +// 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.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(); + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 26614075b1..30d1846746 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania { public class ManiaRuleset : Ruleset { - public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new ManiaHitRenderer(beatmap); + public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaHitRenderer(beatmap, isForCurrentRuleset); public override IEnumerable GetModsFor(ModType type) { 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; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 767a2b3458..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 } }); } @@ -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.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; } } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index 986aefb2bd..0bf70017e3 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -24,8 +24,8 @@ namespace osu.Game.Rulesets.Mania.UI { public int? Columns; - public ManiaHitRenderer(WorkingBeatmap beatmap) - : base(beatmap) + public ManiaHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(beatmap, isForCurrentRuleset) { } @@ -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 @@ -76,6 +76,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.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 52396debf5..ec426c895f 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -47,7 +47,11 @@ + + + + diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 39e911651a..af4a099e0d 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu { public class OsuRuleset : Ruleset { - public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new OsuHitRenderer(beatmap); + public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuHitRenderer(beatmap, isForCurrentRuleset); public override IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new[] { diff --git a/osu.Game.Rulesets.Osu/UI/OsuHitRenderer.cs b/osu.Game.Rulesets.Osu/UI/OsuHitRenderer.cs index 687518e6d5..e582d2fcd3 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuHitRenderer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuHitRenderer.cs @@ -18,8 +18,8 @@ namespace osu.Game.Rulesets.Osu.UI { public class OsuHitRenderer : HitRenderer { - public OsuHitRenderer(WorkingBeatmap beatmap) - : base(beatmap) + public OsuHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(beatmap, isForCurrentRuleset) { } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 355ce1cf68..a2dea3731e 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -41,13 +41,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(HitObject) }; - protected override Beatmap ConvertBeatmap(Beatmap original) + protected override Beatmap ConvertBeatmap(Beatmap original, bool isForCurrentRuleset) { // Rewrite the beatmap info to add the slider velocity multiplier BeatmapInfo info = original.BeatmapInfo.DeepClone(); info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier; - Beatmap converted = base.ConvertBeatmap(original); + Beatmap converted = base.ConvertBeatmap(original, isForCurrentRuleset); // Post processing step to transform hit objects with the same start time into strong hits converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x => diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 3fb2cf6c28..7c169f820b 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko { public class TaikoRuleset : Ruleset { - public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new TaikoHitRenderer(beatmap); + public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoHitRenderer(beatmap, isForCurrentRuleset); public override IEnumerable GetModsFor(ModType type) { diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs index db15193ce5..73450d576d 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs @@ -22,8 +22,8 @@ namespace osu.Game.Rulesets.Taiko.UI { public class TaikoHitRenderer : HitRenderer { - public TaikoHitRenderer(WorkingBeatmap beatmap) - : base(beatmap) + public TaikoHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(beatmap, isForCurrentRuleset) { } 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/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index 8e9266b644..f483d1e6e3 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps protected DifficultyCalculator(Beatmap beatmap) { - Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects; + Objects = CreateBeatmapConverter().Convert(beatmap, true).HitObjects; foreach (var h in Objects) h.ApplyDefaults(beatmap.TimingInfo, beatmap.BeatmapInfo.Difficulty); 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..3ba3d5ba03 --- /dev/null +++ b/osu.Game/Beatmaps/Events/EventInfo.cs @@ -0,0 +1,33 @@ +// 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); + + /// + /// 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); + } +} 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/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index fb1aed3657..d262826fd2 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -76,6 +76,7 @@ namespace osu.Game.Overlays { TextSize = 24, Font = @"Exo2.0-Light", + Padding = new MarginPadding { Left = 10, Right = 10 }, Anchor = Anchor.Centre, Origin = Anchor.BottomCentre, }, @@ -116,7 +117,7 @@ namespace osu.Game.Overlays private void load(FrameworkConfigManager frameworkConfig) { trackSetting(frameworkConfig.GetBindable(FrameworkSetting.FrameSync), v => display(v, "Frame Limiter", v.GetDescription(), "Ctrl+F7")); - trackSetting(frameworkConfig.GetBindable(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", v, v)); + trackSetting(frameworkConfig.GetBindable(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", string.IsNullOrEmpty(v) ? "Default" : v, v)); trackSetting(frameworkConfig.GetBindable(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10")); Action displayResolution = delegate { display(null, "Screen Resolution", frameworkConfig.Get(FrameworkSetting.Width) + "x" + frameworkConfig.Get(FrameworkSetting.Height)); }; diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs index 9fbb4011b7..c0ee478b37 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Debug protected override string Header => "General"; [BackgroundDependencyLoader] - private void load(FrameworkDebugConfigManager config) + private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig) { Children = new Drawable[] { @@ -20,6 +20,11 @@ namespace osu.Game.Overlays.Settings.Sections.Debug { LabelText = "Bypass caching", Bindable = config.GetBindable(FrameworkDebugConfig.BypassCaching) + }, + new SettingsCheckbox + { + LabelText = "Debug logs", + Bindable = frameworkConfig.GetBindable(FrameworkSetting.ShowLogOverlay) } }; } diff --git a/osu.Game/Rulesets/Beatmaps/BeatmapConverter.cs b/osu.Game/Rulesets/Beatmaps/BeatmapConverter.cs index 07aae6a26e..5342686c3f 100644 --- a/osu.Game/Rulesets/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Rulesets/Beatmaps/BeatmapConverter.cs @@ -26,19 +26,21 @@ namespace osu.Game.Rulesets.Beatmaps /// Converts a Beatmap using this Beatmap Converter. /// /// The un-converted Beatmap. + /// Whether to assume the beatmap is for the current ruleset. /// The converted Beatmap. - public Beatmap Convert(Beatmap original) + public Beatmap Convert(Beatmap original, bool isForCurrentRuleset) { // We always operate on a clone of the original beatmap, to not modify it game-wide - return ConvertBeatmap(new Beatmap(original)); + return ConvertBeatmap(new Beatmap(original), isForCurrentRuleset); } /// /// Performs the conversion of a Beatmap using this Beatmap Converter. /// /// The un-converted Beatmap. + /// Whether to assume the beatmap is for the current ruleset. /// The converted Beatmap. - protected virtual Beatmap ConvertBeatmap(Beatmap original) + protected virtual Beatmap ConvertBeatmap(Beatmap original, bool isForCurrentRuleset) { return new Beatmap { 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..a4c319291c 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,20 @@ 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 - }; + 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); } - 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 +218,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/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index ea35e61b36..286ff331d2 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -18,12 +18,13 @@ namespace osu.Game.Rulesets public abstract IEnumerable GetModsFor(ModType type); /// - /// Attempt to create a HitRenderer for the provided beatmap. + /// Attempt to create a hit renderer for a beatmap /// - /// + /// The beatmap to create the hit renderer for. + /// Whether the hit renderer should assume the beatmap is for the current ruleset. /// Unable to successfully load the beatmap to be usable with this ruleset. /// - public abstract HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap); + public abstract HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset); public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap); diff --git a/osu.Game/Rulesets/UI/HitRenderer.cs b/osu.Game/Rulesets/UI/HitRenderer.cs index 3ec5c353a0..d0cce87e43 100644 --- a/osu.Game/Rulesets/UI/HitRenderer.cs +++ b/osu.Game/Rulesets/UI/HitRenderer.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.UI /// protected abstract bool AllObjectsJudged { get; } - protected HitRenderer() + internal HitRenderer() { KeyConversionInputManager = CreateKeyConversionInputManager(); KeyConversionInputManager.RelativeSizeAxes = Axes.Both; @@ -120,7 +120,12 @@ namespace osu.Game.Rulesets.UI /// public Beatmap Beatmap; - protected HitRenderer(WorkingBeatmap beatmap) + /// + /// Creates a hit renderer for a beatmap. + /// + /// The beatmap to create the hit renderer for. + /// Whether to assume the beatmap is for the current ruleset. + internal HitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) { Debug.Assert(beatmap != null, "HitRenderer initialized with a null beatmap."); @@ -134,7 +139,7 @@ namespace osu.Game.Rulesets.UI throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can't be converted for the current ruleset."); // Convert the beatmap - Beatmap = converter.Convert(beatmap.Beatmap); + Beatmap = converter.Convert(beatmap.Beatmap, isForCurrentRuleset); // Apply defaults foreach (var h in Beatmap.HitObjects) @@ -201,8 +206,13 @@ namespace osu.Game.Rulesets.UI private readonly List> drawableObjects = new List>(); - protected HitRenderer(WorkingBeatmap beatmap) - : base(beatmap) + /// + /// Creates a hit renderer for a beatmap. + /// + /// The beatmap to create the hit renderer for. + /// Whether to assume the beatmap is for the current ruleset. + protected HitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(beatmap, isForCurrentRuleset) { InputManager.Add(content = new Container { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0ed5a5874c..f9307595d2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Play try { - HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap); + HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, ruleset.ID == Beatmap.BeatmapInfo.Ruleset.ID); } catch (BeatmapInvalidForRulesetException) { @@ -95,7 +95,7 @@ namespace osu.Game.Screens.Play // let's try again forcing the beatmap's ruleset. ruleset = Beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); - HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap); + HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, true); } if (!HitRenderer.Objects.Any()) @@ -230,18 +230,20 @@ namespace osu.Game.Screens.Play ValidForResume = false; - Delay(1000); - onCompletionEvent = Schedule(delegate + using (BeginDelayedSequence(1000)) { - var score = new Score + onCompletionEvent = Schedule(delegate { - Beatmap = Beatmap.BeatmapInfo, - Ruleset = ruleset - }; - scoreProcessor.PopulateScore(score); - score.User = HitRenderer.Replay?.User ?? (Game as OsuGame)?.API?.LocalUser?.Value; - Push(new Results(score)); - }); + var score = new Score + { + Beatmap = Beatmap.BeatmapInfo, + Ruleset = ruleset + }; + scoreProcessor.PopulateScore(score); + score.User = HitRenderer.Replay?.User ?? (Game as OsuGame)?.API?.LocalUser?.Value; + Push(new Results(score)); + }); + } } private void onFail() @@ -266,16 +268,18 @@ namespace osu.Game.Screens.Play Content.ScaleTo(0.7f); - Content.Delay(250); - Content.FadeIn(250); + using (Content.BeginDelayedSequence(250)) + Content.FadeIn(250); Content.ScaleTo(1, 750, EasingTypes.OutQuint); - Delay(750); - Schedule(() => - { - decoupledClock.Start(); - }); + using (BeginDelayedSequence(750)) + Schedule(() => + { + if (!pauseContainer.IsPaused) + decoupledClock.Start(); + + }); pauseContainer.Alpha = 0; pauseContainer.FadeIn(750, EasingTypes.OutQuint); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 4982ca096f..c352e6e034 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -198,8 +198,11 @@ namespace osu.Game.Screens.Select var pendingSelection = selectionChangedDebounce; selectionChangedDebounce = null; - pendingSelection?.RunTask(); - pendingSelection?.Cancel(); // cancel the already scheduled task. + if (pendingSelection?.Completed == false) + { + pendingSelection?.RunTask(); + pendingSelection?.Cancel(); // cancel the already scheduled task. + } if (Beatmap == null) return; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6cd32d7fea..ee906caa9b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -74,6 +74,10 @@ + + + + @@ -125,6 +129,7 @@ + @@ -161,7 +166,6 @@ - @@ -341,7 +345,6 @@ -