From f50e0bbf3ca285fb070fa9279b523135b444e781 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 13 Mar 2017 19:15:25 +0900 Subject: [PATCH] Rework/rewrite beatmap parsing to parse to base hit objects, which mode-specific beatmap converters can then use. --- .../Tests/TestCaseHitObjects.cs | 2 +- .../Beatmaps/CatchBeatmapConverter.cs | 2 +- osu.Game.Modes.Catch/CatchRuleset.cs | 3 - osu.Game.Modes.Catch/Objects/CatchBaseHit.cs | 8 +- .../Objects/CatchConverter.cs | 40 --------- .../Objects/Drawable/DrawableFruit.cs | 10 ++- .../osu.Game.Modes.Catch.csproj | 1 - .../Beatmaps/ManiaBeatmapConverter.cs | 2 +- osu.Game.Modes.Mania/ManiaRuleset.cs | 3 - osu.Game.Modes.Mania/Objects/ManiaBaseHit.cs | 7 +- .../Objects/ManiaConverter.cs | 48 ----------- .../osu.Game.Modes.Mania.csproj | 1 - .../Beatmaps/OsuBeatmapConverter.cs | 82 +++++++++++++++++-- .../Connections/FollowPointRenderer.cs | 3 +- .../Objects/Drawables/DrawableHitCircle.cs | 16 ++-- .../Objects/Drawables/DrawableSlider.cs | 6 +- .../Objects/Drawables/DrawableSliderTick.cs | 2 +- .../Objects/Drawables/DrawableSpinner.cs | 8 +- .../Objects/Drawables/Pieces/SliderBall.cs | 2 +- .../Objects/Drawables/Pieces/SliderBody.cs | 8 +- osu.Game.Modes.Osu/Objects/HitObjectType.cs | 10 +++ osu.Game.Modes.Osu/Objects/OsuHitObject.cs | 23 ++---- osu.Game.Modes.Osu/Objects/Slider.cs | 18 ++-- osu.Game.Modes.Osu/Objects/SliderCurve.cs | 7 +- osu.Game.Modes.Osu/Objects/Spinner.cs | 12 ++- osu.Game.Modes.Osu/OsuAutoReplay.cs | 29 ++++--- osu.Game.Modes.Osu/OsuRuleset.cs | 3 - osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 2 +- .../Objects/Drawable/DrawableTaikoHit.cs | 6 +- .../Objects/TaikoConverter.cs | 39 --------- osu.Game.Modes.Taiko/TaikoRuleset.cs | 3 - .../osu.Game.Modes.Taiko.csproj | 1 - osu.Game/Beatmaps/DifficultyCalculator.cs | 2 +- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 10 ++- osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 2 +- osu.Game/Beatmaps/IBeatmapCoverter.cs | 11 ++- .../Objects/Drawables/DrawableHitObject.cs | 23 +++--- osu.Game/Modes/Objects/Hit.cs | 15 ++++ osu.Game/Modes/Objects/HitObject.cs | 31 +++---- osu.Game/Modes/Objects/HitObjectConverter.cs | 25 ------ osu.Game/Modes/Objects/IHitObjectConverter.cs | 17 ++++ .../Modes/Objects/LegacyHitObjectParser.cs | 42 +++++----- osu.Game/Modes/Objects/NullHitObjectParser.cs | 13 --- osu.Game/Modes/Objects/Slider.cs | 23 ++++++ osu.Game/Modes/Objects/Spinner.cs | 11 +++ osu.Game/Modes/Objects/Types/CurveType.cs | 10 +++ osu.Game/Modes/Objects/Types/IHasCombo.cs | 25 ++++++ osu.Game/Modes/Objects/Types/IHasCurve.cs | 21 +++++ osu.Game/Modes/Objects/Types/IHasDistance.cs | 13 +++ osu.Game/Modes/Objects/Types/IHasEndTime.cs | 18 ++++ osu.Game/Modes/Objects/Types/IHasPosition.cs | 15 ++++ osu.Game/Modes/Objects/Types/IHasRepeats.cs | 13 +++ .../Objects/Types/LegacyHitObjectLive.cs | 16 ++++ osu.Game/Modes/Ruleset.cs | 3 - osu.Game/Modes/UI/HitRenderer.cs | 2 +- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 7 +- osu.Game/osu.Game.csproj | 15 +++- 58 files changed, 470 insertions(+), 322 deletions(-) delete mode 100644 osu.Game.Modes.Catch/Objects/CatchConverter.cs delete mode 100644 osu.Game.Modes.Mania/Objects/ManiaConverter.cs create mode 100644 osu.Game.Modes.Osu/Objects/HitObjectType.cs delete mode 100644 osu.Game.Modes.Taiko/Objects/TaikoConverter.cs create mode 100644 osu.Game/Modes/Objects/Hit.cs delete mode 100644 osu.Game/Modes/Objects/HitObjectConverter.cs create mode 100644 osu.Game/Modes/Objects/IHitObjectConverter.cs rename osu.Game.Modes.Osu/Objects/OsuHitObjectParser.cs => osu.Game/Modes/Objects/LegacyHitObjectParser.cs (74%) delete mode 100644 osu.Game/Modes/Objects/NullHitObjectParser.cs create mode 100644 osu.Game/Modes/Objects/Slider.cs create mode 100644 osu.Game/Modes/Objects/Spinner.cs create mode 100644 osu.Game/Modes/Objects/Types/CurveType.cs create mode 100644 osu.Game/Modes/Objects/Types/IHasCombo.cs create mode 100644 osu.Game/Modes/Objects/Types/IHasCurve.cs create mode 100644 osu.Game/Modes/Objects/Types/IHasDistance.cs create mode 100644 osu.Game/Modes/Objects/Types/IHasEndTime.cs create mode 100644 osu.Game/Modes/Objects/Types/IHasPosition.cs create mode 100644 osu.Game/Modes/Objects/Types/IHasRepeats.cs create mode 100644 osu.Game/Modes/Objects/Types/LegacyHitObjectLive.cs diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs index 436f048e10..9dfd0ed27b 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs @@ -75,7 +75,7 @@ namespace osu.Desktop.VisualTests.Tests add(new DrawableSpinner(new Spinner { StartTime = framedClock.CurrentTime + 600, - Length = 1000, + EndTime = framedClock.CurrentTime + 1600, Position = new Vector2(0, 0), })); break; diff --git a/osu.Game.Modes.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Modes.Catch/Beatmaps/CatchBeatmapConverter.cs index 6460751570..b62292e2bd 100644 --- a/osu.Game.Modes.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Modes.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -10,7 +10,7 @@ namespace osu.Game.Modes.Catch.Beatmaps { internal class CatchBeatmapConverter : IBeatmapConverter { - public Beatmap Convert(Beatmap original) + public Beatmap ConvertBeatmap(Beatmap original) { return new Beatmap(original) { diff --git a/osu.Game.Modes.Catch/CatchRuleset.cs b/osu.Game.Modes.Catch/CatchRuleset.cs index 08edeb7bcb..4ea1d4e6c1 100644 --- a/osu.Game.Modes.Catch/CatchRuleset.cs +++ b/osu.Game.Modes.Catch/CatchRuleset.cs @@ -5,7 +5,6 @@ using OpenTK.Input; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Modes.Catch.UI; -using osu.Game.Modes.Objects; using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System.Collections.Generic; @@ -88,8 +87,6 @@ namespace osu.Game.Modes.Catch public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null; - public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser(); - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap); } } diff --git a/osu.Game.Modes.Catch/Objects/CatchBaseHit.cs b/osu.Game.Modes.Catch/Objects/CatchBaseHit.cs index 79769710a7..92b5a680ff 100644 --- a/osu.Game.Modes.Catch/Objects/CatchBaseHit.cs +++ b/osu.Game.Modes.Catch/Objects/CatchBaseHit.cs @@ -1,12 +1,14 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . +using OpenTK; +// Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Modes.Objects; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Modes.Catch.Objects { - public abstract class CatchBaseHit : HitObject + public abstract class CatchBaseHit : HitObject, IHasPosition { - public float Position; + public Vector2 Position { get; set; } } } diff --git a/osu.Game.Modes.Catch/Objects/CatchConverter.cs b/osu.Game.Modes.Catch/Objects/CatchConverter.cs deleted file mode 100644 index 31ddc73f61..0000000000 --- a/osu.Game.Modes.Catch/Objects/CatchConverter.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.Collections.Generic; -using osu.Game.Modes.Objects; -using osu.Game.Modes.Osu.Objects; -using osu.Game.Beatmaps; - -namespace osu.Game.Modes.Catch.Objects -{ - internal class CatchConverter : HitObjectConverter - { - public override List Convert(Beatmap beatmap) - { - List output = new List(); - - foreach (HitObject i in beatmap.HitObjects) - { - CatchBaseHit h = i as CatchBaseHit; - - if (h == null) - { - OsuHitObject o = i as OsuHitObject; - - if (o == null) throw new HitObjectConvertException(@"Catch", i); - - h = new Fruit - { - StartTime = o.StartTime, - Position = o.Position.X, - }; - } - - output.Add(h); - } - - return output; - } - } -} diff --git a/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs index 542bdeb3c3..216d65f1db 100644 --- a/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Transforms; using OpenTK; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Modes.Catch.Objects.Drawable { @@ -21,7 +22,7 @@ namespace osu.Game.Modes.Catch.Objects.Drawable Origin = Anchor.Centre; Scale = new Vector2(0.1f); RelativePositionAxes = Axes.Y; - Position = new Vector2(h.Position, -0.1f); + Position = new Vector2(h.Position.X, -0.1f); } [BackgroundDependencyLoader] @@ -29,8 +30,11 @@ namespace osu.Game.Modes.Catch.Objects.Drawable { Texture = textures.Get(@"Menu/logo"); - Transforms.Add(new TransformPosition { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = new Vector2(h.Position, -0.1f), EndValue = new Vector2(h.Position, 0.9f) }); - Transforms.Add(new TransformAlpha { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 }); + double endTime = (h as IHasEndTime)?.EndTime ?? h.StartTime; + double duration = endTime - h.StartTime; + + Transforms.Add(new TransformPosition { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = new Vector2(h.Position.X, -0.1f), EndValue = new Vector2(h.Position.X, 0.9f) }); + Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 }); Expire(true); } } diff --git a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj index 10abb312fc..4646d0cd6c 100644 --- a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj +++ b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj @@ -50,7 +50,6 @@ - diff --git a/osu.Game.Modes.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Modes.Mania/Beatmaps/ManiaBeatmapConverter.cs index 0f42da4ac8..9f515ccf04 100644 --- a/osu.Game.Modes.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Modes.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -10,7 +10,7 @@ namespace osu.Game.Modes.Mania.Beatmaps { internal class ManiaBeatmapConverter : IBeatmapConverter { - public Beatmap Convert(Beatmap original) + public Beatmap ConvertBeatmap(Beatmap original) { return new Beatmap(original) { diff --git a/osu.Game.Modes.Mania/ManiaRuleset.cs b/osu.Game.Modes.Mania/ManiaRuleset.cs index e6e9cfd799..6851c883b4 100644 --- a/osu.Game.Modes.Mania/ManiaRuleset.cs +++ b/osu.Game.Modes.Mania/ManiaRuleset.cs @@ -4,7 +4,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Modes.Mania.UI; -using osu.Game.Modes.Objects; using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System.Collections.Generic; @@ -103,8 +102,6 @@ namespace osu.Game.Modes.Mania public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null; - public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser(); - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap); } } diff --git a/osu.Game.Modes.Mania/Objects/ManiaBaseHit.cs b/osu.Game.Modes.Mania/Objects/ManiaBaseHit.cs index 8b3afc82d9..4da9d0c114 100644 --- a/osu.Game.Modes.Mania/Objects/ManiaBaseHit.cs +++ b/osu.Game.Modes.Mania/Objects/ManiaBaseHit.cs @@ -2,11 +2,16 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Modes.Objects; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Modes.Mania.Objects { - public abstract class ManiaBaseHit : HitObject + public abstract class ManiaBaseHit : HitObject, IHasEndTime { public int Column; + + public double EndTime { get; set; } + + public double Duration { get; set; } } } diff --git a/osu.Game.Modes.Mania/Objects/ManiaConverter.cs b/osu.Game.Modes.Mania/Objects/ManiaConverter.cs deleted file mode 100644 index 7e1183c0e3..0000000000 --- a/osu.Game.Modes.Mania/Objects/ManiaConverter.cs +++ /dev/null @@ -1,48 +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.Collections.Generic; -using osu.Game.Modes.Objects; -using osu.Game.Modes.Osu.Objects; -using osu.Game.Beatmaps; - -namespace osu.Game.Modes.Mania.Objects -{ - internal class ManiaConverter : HitObjectConverter - { - private readonly int columns; - - public ManiaConverter(int columns) - { - this.columns = columns; - } - - public override List Convert(Beatmap beatmap) - { - List output = new List(); - - foreach (HitObject i in beatmap.HitObjects) - { - ManiaBaseHit h = i as ManiaBaseHit; - - if (h == null) - { - OsuHitObject o = i as OsuHitObject; - - if (o == null) throw new HitObjectConvertException(@"Mania", i); - - h = new Note - { - StartTime = o.StartTime, - Column = (int)Math.Round(o.Position.X / 512 * columns) - }; - } - - output.Add(h); - } - - return output; - } - } -} diff --git a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj index 66e46f9e48..88bf879bcc 100644 --- a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj +++ b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj @@ -52,7 +52,6 @@ - diff --git a/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs index 33d4562032..c16b6bd289 100644 --- a/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -8,29 +8,90 @@ using osu.Game.Modes.Objects; using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects.Drawables; using System.Collections.Generic; +using osu.Game.Modes.Objects.Types; +using OpenTK.Graphics; namespace osu.Game.Modes.Osu.Beatmaps { internal class OsuBeatmapConverter : IBeatmapConverter { - public Beatmap Convert(Beatmap original) + public Beatmap ConvertBeatmap(Beatmap original) { return new Beatmap(original) { - HitObjects = convertHitObject(original.HitObjects, original.BeatmapInfo?.StackLeniency ?? 0.7f) + HitObjects = convertHitObjects(original.HitObjects, original.BeatmapInfo?.StackLeniency ?? 0.7f) }; } - private List convertHitObject(List hitObjects, float stackLeniency) + private OsuHitObject convertHitObject(HitObject original) + { + IHasCurve ihc = original as IHasCurve; + IHasDistance ihd = original as IHasDistance; + IHasEndTime ihet = original as IHasEndTime; + IHasPosition ihp = original as IHasPosition; + IHasRepeats ihr = original as IHasRepeats; + IHasCombo ihco = original as IHasCombo; + + if (ihc != null) + { + return new Slider + { + StartTime = original.StartTime, + Sample = original.Sample, + + CurveType = ihc.CurveType, + ControlPoints = ihc.ControlPoints, + + Position = ihp?.Position ?? Vector2.Zero, + ComboColour = ihco?.ComboColour ?? Color4.White, + ComboIndex = ihco?.ComboIndex ?? 0, + NewCombo = ihco?.NewCombo ?? false, + Length = ihd?.Distance ?? 0, + RepeatCount = ihr?.RepeatCount ?? 0 + }; + } + + if (ihet != null) + { + return new Spinner + { + StartTime = original.StartTime, + Sample = original.Sample, + Position = new Vector2(512, 384) / 2, + + EndTime = ihet.EndTime, + + ComboColour = ihco?.ComboColour ?? Color4.White, + ComboIndex = ihco?.ComboIndex ?? 0, + NewCombo = ihco?.NewCombo ?? false, + }; + } + + return new HitCircle + { + StartTime = original.StartTime, + Sample = original.Sample, + + Position = ihp?.Position ?? Vector2.Zero, + ComboColour = ihco?.ComboColour ?? Color4.White, + ComboIndex = ihco?.ComboIndex ?? 0, + NewCombo = ihco?.NewCombo ?? false + }; + } + + private List convertHitObjects(List hitObjects, float stackLeniency) { List converted = new List(); int combo = 0; foreach (HitObject h in hitObjects) { - if (h.NewCombo) combo = 0; + OsuHitObject c = convertHitObject(h); - h.ComboIndex = combo++; + if (c.NewCombo) + combo = 0; + + c.ComboIndex = combo++; converted.Add(h as OsuHitObject); } @@ -62,9 +123,12 @@ namespace osu.Game.Modes.Osu.Beatmaps if (stackBaseObject is Spinner) break; OsuHitObject objectN = hitObjects[n]; - if (objectN is Spinner) continue; + if (objectN is Spinner) + continue; - if (objectN.StartTime - stackBaseObject.EndTime > stackThreshold) + double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime; + + if (objectN.StartTime - endTime > stackThreshold) //We are no longer within stacking range of the next object. break; @@ -116,7 +180,9 @@ namespace osu.Game.Modes.Osu.Beatmaps OsuHitObject objectN = hitObjects[n]; if (objectN is Spinner) continue; - if (objectI.StartTime - objectN.EndTime > stackThreshold) + double endTime = (objectN as IHasEndTime)?.EndTime ?? objectN.StartTime; + + if (objectI.StartTime - endTime > stackThreshold) //We are no longer within stacking range of the previous object. break; diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index 6ef9bc100a..f45e4226dd 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using OpenTK; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Modes.Osu.Objects.Drawables.Connections { @@ -63,7 +64,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Connections { Vector2 startPosition = prevHitObject.EndPosition; Vector2 endPosition = currHitObject.Position; - double startTime = prevHitObject.EndTime; + double startTime = (prevHitObject as IHasEndTime)?.EndTime ?? prevHitObject.StartTime; double endTime = currHitObject.StartTime; Vector2 distanceVector = endPosition - startPosition; diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs index e10fa91e6f..4f0d853c8c 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Transforms; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Osu.Objects.Drawables.Pieces; using OpenTK; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Modes.Osu.Objects.Drawables { @@ -35,11 +36,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables { glow = new GlowPiece { - Colour = osuObject.Colour + Colour = osuObject.ComboColour }, circle = new CirclePiece { - Colour = osuObject.Colour, + Colour = osuObject.ComboColour, Hit = () => { if (Judgement.Result.HasValue) return false; @@ -57,11 +58,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables flash = new FlashPiece(), explode = new ExplodePiece { - Colour = osuObject.Colour, + Colour = osuObject.ComboColour, }, ApproachCircle = new ApproachCircle { - Colour = osuObject.Colour, + Colour = osuObject.ComboColour, } }; @@ -118,13 +119,16 @@ namespace osu.Game.Modes.Osu.Objects.Drawables ApproachCircle.FadeOut(); - glow.Delay(osuObject.Duration); + double endTime = (osuObject as IHasEndTime)?.EndTime ?? osuObject.StartTime; + double duration = endTime - osuObject.StartTime; + + glow.Delay(duration); glow.FadeOut(400); switch (state) { case ArmedState.Idle: - Delay(osuObject.Duration + TIME_PREEMPT); + Delay(duration + TIME_PREEMPT); FadeOut(TIME_FADEOUT); break; case ArmedState.Miss: diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs index 94c150bd3e..aa38609c7c 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs @@ -60,7 +60,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables Position = s.StackedPosition, ComboIndex = s.ComboIndex, Scale = s.Scale, - Colour = s.Colour, + ComboColour = s.ComboColour, Sample = s.Sample, }), }; @@ -128,7 +128,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables var j = (OsuJudgementInfo)Judgement; var sc = (OsuJudgementInfo)initialCircle.Judgement; - if (!userTriggered && Time.Current >= HitObject.EndTime) + if (!userTriggered && Time.Current >= slider.EndTime) { var ticksCount = ticks.Children.Count() + 1; var ticksHit = ticks.Children.Count(t => t.Judgement.Result == HitResult.Hit); @@ -165,7 +165,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables ball.FadeIn(); - Delay(HitObject.Duration, true); + Delay(slider.Duration, true); body.FadeOut(160); ball.FadeOut(160); diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs index 7c5fa4a092..1a0c1deb4f 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -47,7 +47,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables new Box { RelativeSizeAxes = Axes.Both, - Colour = sliderTick.Colour, + Colour = sliderTick.ComboColour, Alpha = 0.3f, } }; diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs index cfebd11809..23f248c1d9 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs @@ -46,7 +46,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables Alpha = 0, Anchor = Anchor.Centre, Origin = Anchor.Centre, - DiscColour = s.Colour + DiscColour = s.ComboColour }, circleContainer = new Container { @@ -82,7 +82,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables if (Progress >= 1) disc.Complete = true; - if (!userTriggered && Time.Current >= HitObject.EndTime) + if (!userTriggered && Time.Current >= spinner.EndTime) { if (Progress >= 1) { @@ -102,7 +102,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables else { j.Score = OsuScoreResult.Miss; - if (Time.Current >= HitObject.EndTime) + if (Time.Current >= spinner.EndTime) j.Result = HitResult.Miss; } } @@ -140,7 +140,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables base.UpdateState(state); - Delay(HitObject.Duration, true); + Delay(spinner.Duration, true); FadeOut(160); diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs index 49785efd38..0b1a3aa751 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -51,7 +51,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { new Box { - Colour = slider.Colour, + Colour = slider.ComboColour, Alpha = 0.4f, Width = width, Height = width, diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs index 7f7d558294..b923d05713 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -110,10 +110,10 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { progress -= border_portion; - bytes[i * 4] = (byte)(slider.Colour.R * 255); - bytes[i * 4 + 1] = (byte)(slider.Colour.G * 255); - bytes[i * 4 + 2] = (byte)(slider.Colour.B * 255); - bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (slider.Colour.A * 255)); + bytes[i * 4] = (byte)(slider.ComboColour.R * 255); + bytes[i * 4 + 1] = (byte)(slider.ComboColour.G * 255); + bytes[i * 4 + 2] = (byte)(slider.ComboColour.B * 255); + bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (slider.ComboColour.A * 255)); } } diff --git a/osu.Game.Modes.Osu/Objects/HitObjectType.cs b/osu.Game.Modes.Osu/Objects/HitObjectType.cs new file mode 100644 index 0000000000..bc2a27020d --- /dev/null +++ b/osu.Game.Modes.Osu/Objects/HitObjectType.cs @@ -0,0 +1,10 @@ +namespace osu.Game.Modes.Osu.Objects +{ + public enum HitObjectType + { + Circle, + Slider, + Spinner, + SliderTick + } +} diff --git a/osu.Game.Modes.Osu/Objects/OsuHitObject.cs b/osu.Game.Modes.Osu/Objects/OsuHitObject.cs index ddcdae7be0..28e341270c 100644 --- a/osu.Game.Modes.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Modes.Osu/Objects/OsuHitObject.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.Game.Modes.Objects; using OpenTK; using osu.Game.Beatmaps; using osu.Game.Modes.Osu.Objects.Drawables; +using osu.Game.Modes.Objects.Types; +using OpenTK.Graphics; namespace osu.Game.Modes.Osu.Objects { - public abstract class OsuHitObject : HitObject + public abstract class OsuHitObject : HitObject, IHasCombo { public const double OBJECT_RADIUS = 64; @@ -36,6 +37,12 @@ namespace osu.Game.Modes.Osu.Objects public abstract HitObjectType Type { get; } + public virtual bool NewCombo { get; set; } + + public Color4 ComboColour { get; set; } + + public int ComboIndex { get; set; } + public double HitWindowFor(OsuScoreResult result) { switch (result) @@ -69,16 +76,4 @@ namespace osu.Game.Modes.Osu.Objects Scale = (1.0f - 0.7f * (beatmap.BeatmapInfo.BaseDifficulty.CircleSize - 5) / 5) / 2; } } - - [Flags] - public enum HitObjectType - { - Circle = 1 << 0, - Slider = 1 << 1, - NewCombo = 1 << 2, - Spinner = 1 << 3, - ColourHax = 122, - Hold = 1 << 7, - SliderTick = 1 << 8, - } } diff --git a/osu.Game.Modes.Osu/Objects/Slider.cs b/osu.Game.Modes.Osu/Objects/Slider.cs index 88abb68d0a..fe17d1e50e 100644 --- a/osu.Game.Modes.Osu/Objects/Slider.cs +++ b/osu.Game.Modes.Osu/Objects/Slider.cs @@ -5,14 +5,16 @@ using OpenTK; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Samples; using osu.Game.Beatmaps.Timing; +using osu.Game.Modes.Objects.Types; using System; using System.Collections.Generic; namespace osu.Game.Modes.Osu.Objects { - public class Slider : OsuHitObject + public class Slider : OsuHitObject, IHasEndTime { - public override double EndTime => StartTime + RepeatCount * Curve.Length / Velocity; + public double EndTime => StartTime + RepeatCount * Curve.Length / Velocity; + public double Duration => EndTime - StartTime; public override Vector2 EndPosition => PositionAt(1); @@ -64,7 +66,7 @@ namespace osu.Game.Modes.Osu.Objects set { Curve.Length = value; } } - public CurveTypes CurveType + public CurveType CurveType { get { return Curve.CurveType; } set { Curve.CurveType = value; } @@ -124,7 +126,7 @@ namespace osu.Game.Modes.Osu.Objects Position = Curve.PositionAt(distanceProgress), StackHeight = StackHeight, Scale = Scale, - Colour = Colour, + ComboColour = ComboColour, Sample = new HitSampleInfo { Type = SampleType.None, @@ -138,12 +140,4 @@ namespace osu.Game.Modes.Osu.Objects public override HitObjectType Type => HitObjectType.Slider; } - - public enum CurveTypes - { - Catmull, - Bezier, - Linear, - PerfectCurve - } } diff --git a/osu.Game.Modes.Osu/Objects/SliderCurve.cs b/osu.Game.Modes.Osu/Objects/SliderCurve.cs index 6c65e7fa37..b32ace5c0d 100644 --- a/osu.Game.Modes.Osu/Objects/SliderCurve.cs +++ b/osu.Game.Modes.Osu/Objects/SliderCurve.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using OpenTK; using System.Linq; using osu.Framework.MathUtils; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Modes.Osu.Objects { @@ -14,7 +15,7 @@ namespace osu.Game.Modes.Osu.Objects public List ControlPoints; - public CurveTypes CurveType = CurveTypes.PerfectCurve; + public CurveType CurveType = CurveType.PerfectCurve; public Vector2 Offset; @@ -25,9 +26,9 @@ namespace osu.Game.Modes.Osu.Objects { switch (CurveType) { - case CurveTypes.Linear: + case CurveType.Linear: return subControlPoints; - case CurveTypes.PerfectCurve: + case CurveType.PerfectCurve: //we can only use CircularArc iff we have exactly three control points and no dissection. if (ControlPoints.Count != 3 || subControlPoints.Count != 3) break; diff --git a/osu.Game.Modes.Osu/Objects/Spinner.cs b/osu.Game.Modes.Osu/Objects/Spinner.cs index d55b9eb59c..9bc13c47d8 100644 --- a/osu.Game.Modes.Osu/Objects/Spinner.cs +++ b/osu.Game.Modes.Osu/Objects/Spinner.cs @@ -1,14 +1,18 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Modes.Objects.Types; + namespace osu.Game.Modes.Osu.Objects { - public class Spinner : OsuHitObject + public class Spinner : OsuHitObject, IHasEndTime { - public double Length; - - public override double EndTime => StartTime + Length; + public double EndTime { get; set; } + public double Duration => EndTime - StartTime; public override HitObjectType Type => HitObjectType.Spinner; + + public override bool NewCombo => true; + } } diff --git a/osu.Game.Modes.Osu/OsuAutoReplay.cs b/osu.Game.Modes.Osu/OsuAutoReplay.cs index 953f131797..2b541f49d9 100644 --- a/osu.Game.Modes.Osu/OsuAutoReplay.cs +++ b/osu.Game.Modes.Osu/OsuAutoReplay.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Transforms; using osu.Game.Modes.Osu.Objects.Drawables; using osu.Framework.MathUtils; using System.Diagnostics; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Modes.Osu { @@ -100,20 +101,22 @@ namespace osu.Game.Modes.Osu { OsuHitObject last = (OsuHitObject)beatmap.HitObjects[i - 1]; + double endTime = (last as IHasEndTime)?.EndTime ?? last.StartTime; + //Make the cursor stay at a hitObject as long as possible (mainly for autopilot). - if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) + if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); + if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, LegacyButtonState.None)); } - else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) + else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); + if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, LegacyButtonState.None)); } - else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) + else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) { - if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); + if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, LegacyButtonState.None)); } } @@ -206,8 +209,10 @@ namespace osu.Game.Modes.Osu LegacyButtonState button = buttonIndex % 2 == 0 ? LegacyButtonState.Left1 : LegacyButtonState.Right1; + double hEndTime = (h as IHasEndTime)?.EndTime ?? h.StartTime; + LegacyReplayFrame newFrame = new LegacyReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button); - LegacyReplayFrame endFrame = new LegacyReplayFrame(h.EndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, LegacyButtonState.None); + LegacyReplayFrame endFrame = new LegacyReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, LegacyButtonState.None); // Decrement because we want the previous frame, not the next one int index = findInsertionIndex(newFrame) - 1; @@ -251,6 +256,8 @@ namespace osu.Game.Modes.Osu // We add intermediate frames for spinning / following a slider here. if (h is Spinner) { + Spinner s = h as Spinner; + Vector2 difference = targetPosition - spinner_centre; float radius = difference.Length; @@ -258,7 +265,7 @@ namespace osu.Game.Modes.Osu double t; - for (double j = h.StartTime + frameDelay; j < h.EndTime; j += frameDelay) + for (double j = h.StartTime + frameDelay; j < s.EndTime; j += frameDelay) { t = applyModsToTime(j - h.StartTime) * spinnerDirection; @@ -266,10 +273,10 @@ namespace osu.Game.Modes.Osu addFrameToReplay(new LegacyReplayFrame((int)j, pos.X, pos.Y, button)); } - t = applyModsToTime(h.EndTime - h.StartTime) * spinnerDirection; + t = applyModsToTime(s.EndTime - h.StartTime) * spinnerDirection; Vector2 endPosition = spinner_centre + circlePosition(t / 20 + angle, spin_radius); - addFrameToReplay(new LegacyReplayFrame(h.EndTime, endPosition.X, endPosition.Y, button)); + addFrameToReplay(new LegacyReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button)); endFrame.MouseX = endPosition.X; endFrame.MouseY = endPosition.Y; @@ -284,7 +291,7 @@ namespace osu.Game.Modes.Osu addFrameToReplay(new LegacyReplayFrame(h.StartTime + j, pos.X, pos.Y, button)); } - addFrameToReplay(new LegacyReplayFrame(h.EndTime, s.EndPosition.X, s.EndPosition.Y, button)); + addFrameToReplay(new LegacyReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button)); } // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed! diff --git a/osu.Game.Modes.Osu/OsuRuleset.cs b/osu.Game.Modes.Osu/OsuRuleset.cs index 0c46ece6da..206bd9dddf 100644 --- a/osu.Game.Modes.Osu/OsuRuleset.cs +++ b/osu.Game.Modes.Osu/OsuRuleset.cs @@ -4,7 +4,6 @@ using OpenTK.Input; using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Modes.Objects; using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.UI; using osu.Game.Modes.UI; @@ -94,8 +93,6 @@ namespace osu.Game.Modes.Osu public override FontAwesome Icon => FontAwesome.fa_osu_osu_o; - public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); - public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => new OsuScoreProcessor(hitObjectCount); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap); diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index c214c881d8..31a210fd3b 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -67,8 +67,8 @@ + - diff --git a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 3230010fda..b846351a87 100644 --- a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -9,7 +9,7 @@ namespace osu.Game.Modes.Taiko.Beatmaps { internal class TaikoBeatmapConverter : IBeatmapConverter { - public Beatmap Convert(Beatmap original) + public Beatmap ConvertBeatmap(Beatmap original) { return new Beatmap(original) { diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs index a6af85b6fe..50ec094f3d 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Transforms; using OpenTK; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Modes.Taiko.Objects.Drawable { @@ -29,8 +30,11 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable { Texture = textures.Get(@"Menu/logo"); + double endTime = (h as IHasEndTime)?.EndTime ?? h.StartTime; + double duration = endTime - h.StartTime; + Transforms.Add(new TransformPositionX { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = 1.1f, EndValue = 0.1f }); - Transforms.Add(new TransformAlpha { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 }); + Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 }); Expire(true); } } diff --git a/osu.Game.Modes.Taiko/Objects/TaikoConverter.cs b/osu.Game.Modes.Taiko/Objects/TaikoConverter.cs deleted file mode 100644 index 3a0e07e390..0000000000 --- a/osu.Game.Modes.Taiko/Objects/TaikoConverter.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Modes.Objects; -using osu.Game.Modes.Osu.Objects; -using osu.Game.Beatmaps; - -namespace osu.Game.Modes.Taiko.Objects -{ - internal class TaikoConverter : HitObjectConverter - { - public override List Convert(Beatmap beatmap) - { - List output = new List(); - - foreach (HitObject i in beatmap.HitObjects) - { - TaikoBaseHit h = i as TaikoBaseHit; - - if (h == null) - { - OsuHitObject o = i as OsuHitObject; - - if (o == null) throw new HitObjectConvertException(@"Taiko", i); - - h = new TaikoBaseHit - { - StartTime = o.StartTime, - }; - } - - output.Add(h); - } - - return output; - } - } -} diff --git a/osu.Game.Modes.Taiko/TaikoRuleset.cs b/osu.Game.Modes.Taiko/TaikoRuleset.cs index 418519d90c..43312161b0 100644 --- a/osu.Game.Modes.Taiko/TaikoRuleset.cs +++ b/osu.Game.Modes.Taiko/TaikoRuleset.cs @@ -4,7 +4,6 @@ using OpenTK.Input; using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Modes.Objects; using osu.Game.Modes.Taiko.UI; using osu.Game.Modes.UI; using osu.Game.Screens.Play; @@ -89,8 +88,6 @@ namespace osu.Game.Modes.Taiko public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null; - public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser(); - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap); } } diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index 0089e84532..2cc45682ef 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -51,7 +51,6 @@ - diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index 9c057d5f77..f6613d7c29 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -33,7 +33,7 @@ namespace osu.Game.Beatmaps protected DifficultyCalculator(Beatmap beatmap) { - Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects; + Objects = CreateBeatmapConverter().ConvertBeatmap(beatmap).HitObjects; PreprocessHitObjects(); } diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index d13cb33554..342b548fdc 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -8,6 +8,7 @@ using osu.Game.Modes.Objects; using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Database; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Beatmaps.Formats { @@ -81,8 +82,13 @@ namespace osu.Game.Beatmaps.Formats foreach (HitObject h in b.HitObjects) { - if (h.NewCombo || i == -1) i = (i + 1) % colours.Count; - h.Colour = colours[i]; + IHasCombo ihc = h as IHasCombo; + + if (ihc == null) + continue; + + if (ihc.NewCombo || i == -1) i = (i + 1) % colours.Count; + ihc.ComboColour = colours[i]; } } } diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 9ce5a4cca0..bed64c8bf9 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -265,7 +265,7 @@ namespace osu.Game.Beatmaps.Formats { case Section.General: handleGeneral(beatmap, key, val); - parser = Ruleset.GetRuleset(beatmap.BeatmapInfo.Mode).CreateHitObjectParser(); + parser = new LegacyHitObjectParser(); break; case Section.Editor: handleEditor(beatmap, key, val); diff --git a/osu.Game/Beatmaps/IBeatmapCoverter.cs b/osu.Game/Beatmaps/IBeatmapCoverter.cs index da5892c168..d8a395070b 100644 --- a/osu.Game/Beatmaps/IBeatmapCoverter.cs +++ b/osu.Game/Beatmaps/IBeatmapCoverter.cs @@ -5,8 +5,17 @@ using osu.Game.Modes.Objects; namespace osu.Game.Beatmaps { + /// + /// Converts a Beatmap for another mode. + /// + /// The type of HitObject stored in the Beatmap. public interface IBeatmapConverter where T : HitObject { - Beatmap Convert(Beatmap original); + /// + /// Converts a Beatmap to another mode. + /// + /// The original Beatmap. + /// The converted Beatmap. + Beatmap ConvertBeatmap(Beatmap original); } } diff --git a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs index 33ff8a0810..3b23d55b95 100644 --- a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs @@ -11,6 +11,7 @@ using osu.Framework.Audio.Sample; using osu.Game.Beatmaps.Samples; using OpenTK; using Container = osu.Framework.Graphics.Containers.Container; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Modes.Objects.Drawables { @@ -67,14 +68,14 @@ namespace osu.Game.Modes.Objects.Drawables } } - public abstract class DrawableHitObject : DrawableHitObject - where HitObjectType : HitObject + public abstract class DrawableHitObject : DrawableHitObject + where TObject : HitObject { - public event Action, JudgementInfo> OnJudgement; + public event Action, JudgementInfo> OnJudgement; - public HitObjectType HitObject; + public TObject HitObject; - protected DrawableHitObject(HitObjectType hitObject) + protected DrawableHitObject(TObject hitObject) { HitObject = hitObject; } @@ -88,7 +89,9 @@ namespace osu.Game.Modes.Objects.Drawables if (Judgement.Result != null) return false; - Judgement.TimeOffset = Time.Current - HitObject.EndTime; + double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + + Judgement.TimeOffset = Time.Current - endTime; CheckJudgement(userTriggered); @@ -138,14 +141,14 @@ namespace osu.Game.Modes.Objects.Drawables Sample = audio.Sample.Get($@"Gameplay/{sampleSet.ToString().ToLower()}-hit{type.ToString().ToLower()}"); } - private List> nestedHitObjects; + private List> nestedHitObjects; - protected IEnumerable> NestedHitObjects => nestedHitObjects; + protected IEnumerable> NestedHitObjects => nestedHitObjects; - protected void AddNested(DrawableHitObject h) + protected void AddNested(DrawableHitObject h) { if (nestedHitObjects == null) - nestedHitObjects = new List>(); + nestedHitObjects = new List>(); h.OnJudgement += (d, j) => { OnJudgement?.Invoke(d, j); } ; nestedHitObjects.Add(h); diff --git a/osu.Game/Modes/Objects/Hit.cs b/osu.Game/Modes/Objects/Hit.cs new file mode 100644 index 0000000000..a1cfc275fb --- /dev/null +++ b/osu.Game/Modes/Objects/Hit.cs @@ -0,0 +1,15 @@ +using osu.Game.Modes.Objects.Types; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Modes.Objects +{ + internal class Hit : HitObject, IHasPosition, IHasCombo + { + public Vector2 Position { get; set; } + public Color4 ComboColour { get; set; } + + public bool NewCombo { get; set; } + public int ComboIndex { get; set; } + } +} diff --git a/osu.Game/Modes/Objects/HitObject.cs b/osu.Game/Modes/Objects/HitObject.cs index eda6bc7e2a..20fab2e8d7 100644 --- a/osu.Game/Modes/Objects/HitObject.cs +++ b/osu.Game/Modes/Objects/HitObject.cs @@ -3,28 +3,31 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Samples; -using OpenTK.Graphics; namespace osu.Game.Modes.Objects { /// - /// A hitobject describes a point in a beatmap + /// A HitObject describes an object in a Beatmap. + /// + /// HitObjects may contain more properties for which you should be checking through the IHas* types. + /// /// - public abstract class HitObject + public class HitObject { - public double StartTime; - public virtual double EndTime => StartTime; + /// + /// The time at which the HitObject starts. + /// + public double StartTime { get; set; } - public bool NewCombo { get; set; } - - public Color4 Colour = new Color4(17, 136, 170, 255); - - public double Duration => EndTime - StartTime; - - public HitSampleInfo Sample; - - public int ComboIndex; + /// + /// The sample to be played when this HitObject is hit. + /// + public HitSampleInfo Sample { get; set; } + /// + /// Sets default parameters from a beatmap. + /// + /// The beatmap to set from. public virtual void SetDefaultsFromBeatmap(Beatmap beatmap) { } } } diff --git a/osu.Game/Modes/Objects/HitObjectConverter.cs b/osu.Game/Modes/Objects/HitObjectConverter.cs deleted file mode 100644 index d7c6113af1..0000000000 --- a/osu.Game/Modes/Objects/HitObjectConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -// 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 System; -using System.Collections.Generic; - -namespace osu.Game.Modes.Objects -{ - public abstract class HitObjectConverter - where T : HitObject - { - public abstract List Convert(Beatmap beatmap); - } - - public class HitObjectConvertException : Exception - { - public HitObject Input { get; } - public HitObjectConvertException(string modeName, HitObject input) - : base($@"Can't convert from {input.GetType().Name} to {modeName} HitObject!") - { - Input = input; - } - } -} diff --git a/osu.Game/Modes/Objects/IHitObjectConverter.cs b/osu.Game/Modes/Objects/IHitObjectConverter.cs new file mode 100644 index 0000000000..777a1597a1 --- /dev/null +++ b/osu.Game/Modes/Objects/IHitObjectConverter.cs @@ -0,0 +1,17 @@ +namespace osu.Game.Modes.Objects +{ + /// + /// Converts HitObjects to another mode. + /// + /// The type of HitObject to be converted to. + public interface IHitObjectConverter + where T : HitObject + { + /// + /// Converts a to another mode. + /// + /// The base HitObject to convert. + /// The converted HitObject. + T Convert(HitObject hitObject); + } +} diff --git a/osu.Game.Modes.Osu/Objects/OsuHitObjectParser.cs b/osu.Game/Modes/Objects/LegacyHitObjectParser.cs similarity index 74% rename from osu.Game.Modes.Osu/Objects/OsuHitObjectParser.cs rename to osu.Game/Modes/Objects/LegacyHitObjectParser.cs index 3a8ab00f10..7dcece0477 100644 --- a/osu.Game.Modes.Osu/Objects/OsuHitObjectParser.cs +++ b/osu.Game/Modes/Objects/LegacyHitObjectParser.cs @@ -1,16 +1,13 @@ -// 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.Samples; +using osu.Game.Modes.Objects.Types; using System; using System.Collections.Generic; using System.Globalization; -using osu.Game.Beatmaps.Samples; -using osu.Game.Modes.Objects; -using OpenTK; -namespace osu.Game.Modes.Osu.Objects +namespace osu.Game.Modes.Objects { - public class OsuHitObjectParser : HitObjectParser + internal class LegacyHitObjectParser : HitObjectParser { public override HitObject Parse(string text) { @@ -19,17 +16,19 @@ namespace osu.Game.Modes.Osu.Objects bool combo = type.HasFlag(HitObjectType.NewCombo); type &= (HitObjectType)0xF; type &= ~HitObjectType.NewCombo; - OsuHitObject result; + + HitObject result; switch (type) { case HitObjectType.Circle: - result = new HitCircle + result = new Hit { - Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])) + Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])), + NewCombo = combo }; break; case HitObjectType.Slider: - CurveTypes curveType = CurveTypes.Catmull; + CurveType curveType = CurveType.Catmull; double length = 0; List points = new List { new Vector2(int.Parse(split[0]), int.Parse(split[1])) }; @@ -41,16 +40,16 @@ namespace osu.Game.Modes.Osu.Objects switch (t) { case @"C": - curveType = CurveTypes.Catmull; + curveType = CurveType.Catmull; break; case @"B": - curveType = CurveTypes.Bezier; + curveType = CurveType.Bezier; break; case @"L": - curveType = CurveTypes.Linear; + curveType = CurveType.Linear; break; case @"P": - curveType = CurveTypes.PerfectCurve; + curveType = CurveType.PerfectCurve; break; } continue; @@ -75,17 +74,17 @@ namespace osu.Game.Modes.Osu.Objects result = new Slider { ControlPoints = points, - Length = length, + Distance = length, CurveType = curveType, RepeatCount = repeatCount, - Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])) + Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])), + NewCombo = combo }; break; case HitObjectType.Spinner: result = new Spinner { - Length = Convert.ToDouble(split[5], CultureInfo.InvariantCulture) - Convert.ToDouble(split[2], CultureInfo.InvariantCulture), - Position = new Vector2(512, 384) / 2, + EndTime = Convert.ToDouble(split[5], CultureInfo.InvariantCulture) }; break; default: @@ -97,8 +96,9 @@ namespace osu.Game.Modes.Osu.Objects Type = (SampleType)int.Parse(split[4]), Set = SampleSet.Soft, }; - result.NewCombo = combo; + // TODO: "addition" field + return result; } } diff --git a/osu.Game/Modes/Objects/NullHitObjectParser.cs b/osu.Game/Modes/Objects/NullHitObjectParser.cs deleted file mode 100644 index fdec989632..0000000000 --- a/osu.Game/Modes/Objects/NullHitObjectParser.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Modes.Objects -{ - /// - /// Returns null HitObjects but at least allows us to run. - /// - public class NullHitObjectParser : HitObjectParser - { - public override HitObject Parse(string text) => null; - } -} diff --git a/osu.Game/Modes/Objects/Slider.cs b/osu.Game/Modes/Objects/Slider.cs new file mode 100644 index 0000000000..c567a668a8 --- /dev/null +++ b/osu.Game/Modes/Objects/Slider.cs @@ -0,0 +1,23 @@ +using osu.Game.Modes.Objects.Types; +using System.Collections.Generic; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Modes.Objects +{ + internal class Slider : HitObject, IHasCurve, IHasDistance, IHasPosition, IHasCombo, IHasRepeats + { + public List ControlPoints { get; set; } + public CurveType CurveType { get; set; } + + public double Distance { get; set; } + + public Vector2 Position { get; set; } + + public Color4 ComboColour { get; set; } + public bool NewCombo { get; set; } + public int ComboIndex { get; set; } + + public int RepeatCount { get; set; } + } +} diff --git a/osu.Game/Modes/Objects/Spinner.cs b/osu.Game/Modes/Objects/Spinner.cs new file mode 100644 index 0000000000..68717bec19 --- /dev/null +++ b/osu.Game/Modes/Objects/Spinner.cs @@ -0,0 +1,11 @@ +using osu.Game.Modes.Objects.Types; + +namespace osu.Game.Modes.Objects +{ + internal class Spinner : HitObject, IHasEndTime + { + public double EndTime { get; set; } + + public double Duration => EndTime - StartTime; + } +} diff --git a/osu.Game/Modes/Objects/Types/CurveType.cs b/osu.Game/Modes/Objects/Types/CurveType.cs new file mode 100644 index 0000000000..6f4668428d --- /dev/null +++ b/osu.Game/Modes/Objects/Types/CurveType.cs @@ -0,0 +1,10 @@ +namespace osu.Game.Modes.Objects.Types +{ + public enum CurveType + { + Catmull, + Bezier, + Linear, + PerfectCurve + } +} diff --git a/osu.Game/Modes/Objects/Types/IHasCombo.cs b/osu.Game/Modes/Objects/Types/IHasCombo.cs new file mode 100644 index 0000000000..8d83ccaa05 --- /dev/null +++ b/osu.Game/Modes/Objects/Types/IHasCombo.cs @@ -0,0 +1,25 @@ +using OpenTK.Graphics; + +namespace osu.Game.Modes.Objects.Types +{ + /// + /// A HitObject that is part of a combo. + /// + public interface IHasCombo + { + /// + /// The colour of this HitObject in the combo. + /// + Color4 ComboColour { get; set; } + + /// + /// Whether the HitObject starts a new combo. + /// + bool NewCombo { get; } + + /// + /// The combo index. + /// + int ComboIndex { get; } + } +} diff --git a/osu.Game/Modes/Objects/Types/IHasCurve.cs b/osu.Game/Modes/Objects/Types/IHasCurve.cs new file mode 100644 index 0000000000..ab5b2d55fd --- /dev/null +++ b/osu.Game/Modes/Objects/Types/IHasCurve.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using OpenTK; + +namespace osu.Game.Modes.Objects.Types +{ + /// + /// A HitObject that has a curve. + /// + public interface IHasCurve + { + /// + /// The control points that shape the curve. + /// + List ControlPoints { get; } + + /// + /// The type of curve. + /// + CurveType CurveType { get; } + } +} diff --git a/osu.Game/Modes/Objects/Types/IHasDistance.cs b/osu.Game/Modes/Objects/Types/IHasDistance.cs new file mode 100644 index 0000000000..2489abf591 --- /dev/null +++ b/osu.Game/Modes/Objects/Types/IHasDistance.cs @@ -0,0 +1,13 @@ +namespace osu.Game.Modes.Objects.Types +{ + /// + /// A HitObject that has a distance. + /// + public interface IHasDistance + { + /// + /// The distance of the HitObject. + /// + double Distance { get; } + } +} diff --git a/osu.Game/Modes/Objects/Types/IHasEndTime.cs b/osu.Game/Modes/Objects/Types/IHasEndTime.cs new file mode 100644 index 0000000000..ac0fc562f8 --- /dev/null +++ b/osu.Game/Modes/Objects/Types/IHasEndTime.cs @@ -0,0 +1,18 @@ +namespace osu.Game.Modes.Objects.Types +{ + /// + /// A HitObject that ends at a different time than its start time. + /// + public interface IHasEndTime + { + /// + /// The time at which the HitObject ends. + /// + double EndTime { get; } + + /// + /// The duration of the HitObject. + /// + double Duration { get; } + } +} diff --git a/osu.Game/Modes/Objects/Types/IHasPosition.cs b/osu.Game/Modes/Objects/Types/IHasPosition.cs new file mode 100644 index 0000000000..09e04ef3a0 --- /dev/null +++ b/osu.Game/Modes/Objects/Types/IHasPosition.cs @@ -0,0 +1,15 @@ +using OpenTK; + +namespace osu.Game.Modes.Objects.Types +{ + /// + /// A HitObject that has a starting position. + /// + public interface IHasPosition + { + /// + /// The starting position of the HitObject. + /// + Vector2 Position { get; } + } +} diff --git a/osu.Game/Modes/Objects/Types/IHasRepeats.cs b/osu.Game/Modes/Objects/Types/IHasRepeats.cs new file mode 100644 index 0000000000..4256990049 --- /dev/null +++ b/osu.Game/Modes/Objects/Types/IHasRepeats.cs @@ -0,0 +1,13 @@ +namespace osu.Game.Modes.Objects.Types +{ + /// + /// A HitObject that spans some length. + /// + public interface IHasRepeats + { + /// + /// The amount of times the HitObject repeats. + /// + int RepeatCount { get; } + } +} diff --git a/osu.Game/Modes/Objects/Types/LegacyHitObjectLive.cs b/osu.Game/Modes/Objects/Types/LegacyHitObjectLive.cs new file mode 100644 index 0000000000..63495e2f6a --- /dev/null +++ b/osu.Game/Modes/Objects/Types/LegacyHitObjectLive.cs @@ -0,0 +1,16 @@ +using System; + +namespace osu.Game.Modes.Objects.Types +{ + [Flags] + public enum HitObjectType + { + Circle = 1 << 0, + Slider = 1 << 1, + NewCombo = 1 << 2, + Spinner = 1 << 3, + ColourHax = 122, + Hold = 1 << 7, + SliderTick = 1 << 8, + } +} \ No newline at end of file diff --git a/osu.Game/Modes/Ruleset.cs b/osu.Game/Modes/Ruleset.cs index d8892e64e9..55b670cc56 100644 --- a/osu.Game/Modes/Ruleset.cs +++ b/osu.Game/Modes/Ruleset.cs @@ -3,7 +3,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Modes.Objects; using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System; @@ -33,8 +32,6 @@ namespace osu.Game.Modes public abstract HitRenderer CreateHitRendererWith(Beatmap beatmap); - public abstract HitObjectParser CreateHitObjectParser(); - public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap); public static void Register(Ruleset ruleset) => availableRulesets.TryAdd(ruleset.PlayMode, ruleset.GetType()); diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index e08570522f..db2879608c 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Modes.UI protected HitRenderer(Beatmap beatmap) { - Beatmap = CreateBeatmapConverter().Convert(beatmap); + Beatmap = CreateBeatmapConverter().ConvertBeatmap(beatmap); RelativeSizeAxes = Axes.Both; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index bc3b16079c..03722e4c58 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -21,6 +21,8 @@ using osu.Framework.MathUtils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Modes; +using osu.Game.Modes.Objects; +using osu.Game.Modes.Objects.Types; namespace osu.Game.Screens.Select { @@ -69,11 +71,14 @@ namespace osu.Game.Screens.Select if (beatmap.Beatmap != null) { + HitObject lastObject = beatmap.Beatmap.HitObjects.Last(); + double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime; + labels.Add(new InfoLabel(new BeatmapStatistic { Name = "Length", Icon = FontAwesome.fa_clock_o, - Content = beatmap.Beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(beatmap.Beatmap.HitObjects.Last().EndTime - beatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"), + Content = beatmap.Beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"), })); labels.Add(new InfoLabel(new BeatmapStatistic diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3d4a763cb8..fac1154804 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -91,9 +91,21 @@ + + + + + + - + + + + + + + @@ -114,7 +126,6 @@ -