diff --git a/osu-framework b/osu-framework index e9b388934e..a7c99e06ff 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit e9b388934ed77cbc1af3cdfd213eb754f71554ae +Subproject commit a7c99e06ff4c3f56fad24bec170eb93f42b1e149 diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs index 1f0bdf40e0..b3cb8c3457 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs @@ -29,7 +29,6 @@ public override void Reset() Add(new CirclePiece { Position = new Vector2(100, 100), - Width = 0, AccentColour = Color4.DarkRed, KiaiMode = kiai, Children = new[] @@ -38,10 +37,9 @@ public override void Reset() } }); - Add(new StrongCirclePiece + Add(new CirclePiece(true) { Position = new Vector2(350, 100), - Width = 0, AccentColour = Color4.DarkRed, KiaiMode = kiai, Children = new[] @@ -53,7 +51,6 @@ public override void Reset() Add(new CirclePiece { Position = new Vector2(100, 300), - Width = 0, AccentColour = Color4.DarkBlue, KiaiMode = kiai, Children = new[] @@ -62,10 +59,9 @@ public override void Reset() } }); - Add(new StrongCirclePiece + Add(new CirclePiece(true) { Position = new Vector2(350, 300), - Width = 0, AccentColour = Color4.DarkBlue, KiaiMode = kiai, Children = new[] @@ -77,7 +73,6 @@ public override void Reset() Add(new CirclePiece { Position = new Vector2(100, 500), - Width = 0, AccentColour = Color4.Orange, KiaiMode = kiai, Children = new[] @@ -86,20 +81,22 @@ public override void Reset() } }); - Add(new CirclePiece + Add(new ElongatedCirclePiece { Position = new Vector2(575, 100), - Width = 0.25f, AccentColour = Color4.Orange, KiaiMode = kiai, + Length = 0.10f, + PlayfieldLengthReference = () => DrawSize.X }); - Add(new StrongCirclePiece + Add(new ElongatedCirclePiece(true) { Position = new Vector2(575, 300), - Width = 0.25f, AccentColour = Color4.Orange, - KiaiMode = kiai + KiaiMode = kiai, + Length = 0.10f, + PlayfieldLengthReference = () => DrawSize.X }); } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs index c7ef33c573..d0a35b7b3e 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; using osu.Framework.Testing; +using osu.Framework.Timing; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Objects; @@ -19,7 +20,11 @@ internal class TestCaseTaikoPlayfield : TestCase private TaikoPlayfield playfield; - protected override double TimePerAction => 500; + protected override double TimePerAction => default_duration * 2; + + private const double default_duration = 300; + + private const float scroll_time = 1000; public override void Reset() { @@ -29,7 +34,7 @@ public override void Reset() AddStep("Miss :(", addMissJudgement); AddStep("DrumRoll", () => addDrumRoll(false)); AddStep("Strong DrumRoll", () => addDrumRoll(true)); - AddStep("Swell", addSwell); + AddStep("Swell", () => addSwell()); AddStep("Centre", () => addCentreHit(false)); AddStep("Strong Centre", () => addCentreHit(true)); AddStep("Rim", () => addRimHit(false)); @@ -37,8 +42,12 @@ public override void Reset() AddStep("Add bar line", () => addBarLine(false)); AddStep("Add major bar line", () => addBarLine(true)); + + var rateAdjustClock = new StopwatchClock(true) { Rate = 1 }; + Add(new Container { + Clock = new FramedClock(rateAdjustClock), RelativeSizeAxes = Axes.X, Y = 200, Children = new[] @@ -77,49 +86,53 @@ private void addMissJudgement() }); } - private void addBarLine(bool major) + private void addBarLine(bool major, double delay = scroll_time) { BarLine bl = new BarLine { - StartTime = Time.Current + 1000, - PreEmpt = 1000 + StartTime = playfield.Time.Current + delay, + ScrollTime = scroll_time }; - playfield.AddBarLine(major ? new DrawableMajorBarLine(bl) : new DrawableBarLine(bl)); + playfield.AddBarLine(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl)); } - private void addSwell() + private void addSwell(double duration = default_duration) { playfield.Add(new DrawableSwell(new Swell { - StartTime = Time.Current + 1000, - EndTime = Time.Current + 1000, - PreEmpt = 1000 + StartTime = playfield.Time.Current + scroll_time, + Duration = duration, + ScrollTime = scroll_time })); } - private void addDrumRoll(bool strong) + private void addDrumRoll(bool strong, double duration = default_duration) { + addBarLine(true); + addBarLine(true, scroll_time + duration); + var d = new DrumRoll { - StartTime = Time.Current + 1000, - Distance = 1000, - PreEmpt = 1000, + StartTime = playfield.Time.Current + scroll_time, + IsStrong = strong, + Duration = duration, + ScrollTime = scroll_time, }; - playfield.Add(strong ? new DrawableStrongDrumRoll(d) : new DrawableDrumRoll(d)); + playfield.Add(new DrawableDrumRoll(d)); } private void addCentreHit(bool strong) { Hit h = new Hit { - StartTime = Time.Current + 1000, - PreEmpt = 1000 + StartTime = playfield.Time.Current + scroll_time, + ScrollTime = scroll_time }; if (strong) - playfield.Add(new DrawableStrongCentreHit(h)); + playfield.Add(new DrawableCentreHitStrong(h)); else playfield.Add(new DrawableCentreHit(h)); } @@ -128,12 +141,12 @@ private void addRimHit(bool strong) { Hit h = new Hit { - StartTime = Time.Current + 1000, - PreEmpt = 1000 + StartTime = playfield.Time.Current + scroll_time, + ScrollTime = scroll_time }; if (strong) - playfield.Add(new DrawableStrongRimHit(h)); + playfield.Add(new DrawableRimHitStrong(h)); else playfield.Add(new DrawableRimHit(h)); } diff --git a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs index 8090263fe1..d89bbfd131 100644 --- a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs @@ -21,6 +21,8 @@ public class OsuPlayfield : Playfield private readonly Container judgementLayer; private readonly ConnectionRenderer connectionLayer; + public override bool ProvidingUserCursor => true; + public override Vector2 Size { get diff --git a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 594ed5f309..4e16623199 100644 --- a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -9,6 +9,8 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Beatmaps.Timing; using osu.Game.Database; namespace osu.Game.Modes.Taiko.Beatmaps @@ -41,6 +43,7 @@ public Beatmap Convert(Beatmap original) { return new Beatmap(original) { + TimingInfo = original is LegacyBeatmap ? new LegacyTimingInfo(original.TimingInfo) : original.TimingInfo, HitObjects = original.HitObjects.SelectMany(h => convertHitObject(h, original)).ToList() }; } @@ -100,7 +103,6 @@ private IEnumerable convertHitObject(HitObject obj, Beatmap beat StartTime = j, Sample = obj.Sample, IsStrong = strong, - VelocityMultiplier = legacy_velocity_multiplier }; } } @@ -111,9 +113,8 @@ private IEnumerable convertHitObject(HitObject obj, Beatmap beat StartTime = obj.StartTime, Sample = obj.Sample, IsStrong = strong, - Distance = distance, + Duration = taikoDuration, TickRate = beatmap.BeatmapInfo.Difficulty.SliderTickRate == 3 ? 3 : 4, - VelocityMultiplier = legacy_velocity_multiplier }; } } @@ -126,9 +127,8 @@ private IEnumerable convertHitObject(HitObject obj, Beatmap beat StartTime = obj.StartTime, Sample = obj.Sample, IsStrong = strong, - EndTime = endTimeData.EndTime, + Duration = endTimeData.Duration, RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier), - VelocityMultiplier = legacy_velocity_multiplier }; } else @@ -142,7 +142,6 @@ private IEnumerable convertHitObject(HitObject obj, Beatmap beat StartTime = obj.StartTime, Sample = obj.Sample, IsStrong = strong, - VelocityMultiplier = legacy_velocity_multiplier }; } else @@ -152,10 +151,25 @@ private IEnumerable convertHitObject(HitObject obj, Beatmap beat StartTime = obj.StartTime, Sample = obj.Sample, IsStrong = strong, - VelocityMultiplier = legacy_velocity_multiplier }; } } } + + private class LegacyTimingInfo : TimingInfo + { + public LegacyTimingInfo(TimingInfo original) + { + if (original is LegacyTimingInfo) + ControlPoints.AddRange(original.ControlPoints); + else + { + ControlPoints.AddRange(original.ControlPoints.Select(c => c.Clone())); + + foreach (var c in ControlPoints) + c.SpeedMultiplier *= legacy_velocity_multiplier; + } + } + } } } diff --git a/osu.Game.Modes.Taiko/Objects/BarLine.cs b/osu.Game.Modes.Taiko/Objects/BarLine.cs index a6eceb2e48..ae3c03de5e 100644 --- a/osu.Game.Modes.Taiko/Objects/BarLine.cs +++ b/osu.Game.Modes.Taiko/Objects/BarLine.cs @@ -1,26 +1,9 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Beatmaps.Timing; -using osu.Game.Database; - namespace osu.Game.Modes.Taiko.Objects { - public class BarLine + public class BarLine : TaikoHitObject { - /// - /// The start time of the control point this bar line represents. - /// - public double StartTime; - - /// - /// The time to scroll in the bar line. - /// - public double PreEmpt; - - public void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) - { - PreEmpt = TaikoHitObject.BASE_SCROLL_TIME / difficulty.SliderMultiplier * timing.BeatLengthAt(StartTime) * timing.SpeedMultiplierAt(StartTime) / 1000; - } } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableBarLine.cs index 6567975796..59f8aca867 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableBarLine.cs @@ -56,20 +56,19 @@ public DrawableBarLine(BarLine barLine) Alpha = 0.75f } }; + + LifetimeStart = BarLine.StartTime - BarLine.ScrollTime * 2; + LifetimeEnd = BarLine.StartTime + BarLine.ScrollTime; } protected override void LoadComplete() { base.LoadComplete(); - - LifetimeStart = BarLine.StartTime - BarLine.PreEmpt * 2; - LifetimeEnd = BarLine.StartTime + BarLine.PreEmpt; - Delay(BarLine.StartTime - Time.Current); - FadeOut(base_fadeout_time * BarLine.PreEmpt / 1000); + FadeOut(base_fadeout_time * BarLine.ScrollTime / 1000); } - private void updateScrollPosition(double time) => MoveToX((float)((BarLine.StartTime - time) / BarLine.PreEmpt)); + private void updateScrollPosition(double time) => MoveToX((float)((BarLine.StartTime - time) / BarLine.ScrollTime)); protected override void Update() { diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableMajorBarLine.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableBarLineMajor.cs similarity index 91% rename from osu.Game.Modes.Taiko/Objects/Drawables/DrawableMajorBarLine.cs rename to osu.Game.Modes.Taiko/Objects/Drawables/DrawableBarLineMajor.cs index 14d3d84f4b..73565e6948 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableMajorBarLine.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableBarLineMajor.cs @@ -8,7 +8,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables { - public class DrawableMajorBarLine : DrawableBarLine + public class DrawableBarLineMajor : DrawableBarLine { /// /// The vertical offset of the triangles from the line tracker. @@ -20,7 +20,7 @@ public class DrawableMajorBarLine : DrawableBarLine /// private const float triangle_size = 20f; - public DrawableMajorBarLine(BarLine barLine) + public DrawableBarLineMajor(BarLine barLine) : base(barLine) { Add(new Container diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableCentreHit.cs index c79351f4d9..ff5ac859b4 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -15,17 +15,13 @@ public class DrawableCentreHit : DrawableHit public DrawableCentreHit(Hit hit) : base(hit) { + MainPiece.Add(new CentreHitSymbolPiece()); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - Circle.AccentColour = colours.PinkDarker; + MainPiece.AccentColour = colours.PinkDarker; } - - protected override CirclePiece CreateCirclePiece() => new CirclePiece - { - Children = new[] { new CentreHitSymbolPiece() } - }; } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongCentreHit.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs similarity index 61% rename from osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongCentreHit.cs rename to osu.Game.Modes.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs index 815c19779a..bc24e2aa65 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongCentreHit.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs @@ -8,24 +8,20 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables { - public class DrawableStrongCentreHit : DrawableStrongHit + public class DrawableCentreHitStrong : DrawableHitStrong { protected override Key[] HitKeys { get; } = { Key.F, Key.J }; - public DrawableStrongCentreHit(Hit hit) + public DrawableCentreHitStrong(Hit hit) : base(hit) { + MainPiece.Add(new CentreHitSymbolPiece()); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - Circle.AccentColour = colours.PinkDarker; + MainPiece.AccentColour = colours.PinkDarker; } - - protected override CirclePiece CreateCirclePiece() => new StrongCirclePiece - { - Children = new[] { new CentreHitSymbolPiece() } - }; } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 4e8d75315d..0a0098dd34 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -3,28 +3,23 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.MathUtils; using osu.Game.Graphics; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; -using osu.Game.Modes.Taiko.Objects.Drawables.Pieces; using OpenTK; using OpenTK.Graphics; +using osu.Game.Modes.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Modes.Taiko.Objects.Drawables { - public class DrawableDrumRoll : DrawableTaikoHitObject + public class DrawableDrumRoll : DrawableTaikoHitObject { /// /// Number of rolling hits required to reach the dark/final accent colour. /// private const int rolling_hits_for_dark_accent = 5; - private readonly DrumRoll drumRoll; - - private readonly CirclePiece circle; - private Color4 accentDarkColour; /// @@ -35,32 +30,32 @@ public class DrawableDrumRoll : DrawableTaikoHitObject public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) { - this.drumRoll = drumRoll; - - RelativeSizeAxes = Axes.X; - Width = (float)(drumRoll.Duration / drumRoll.PreEmpt); - - Add(circle = CreateCirclePiece()); - circle.KiaiMode = HitObject.Kiai; - foreach (var tick in drumRoll.Ticks) { var newTick = new DrawableDrumRollTick(tick) { - X = (float)((tick.StartTime - HitObject.StartTime) / drumRoll.Duration) + X = (float)((tick.StartTime - HitObject.StartTime) / HitObject.Duration) }; newTick.OnJudgement += onTickJudgement; AddNested(newTick); - Add(newTick); + MainPiece.Add(newTick); } } + protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = HitObject.IsStrong }; + + protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(HitObject.IsStrong) + { + Length = (float)(HitObject.Duration / HitObject.ScrollTime), + PlayfieldLengthReference = () => Parent.DrawSize.X + }; + [BackgroundDependencyLoader] private void load(OsuColour colours) { - circle.AccentColour = AccentColour = colours.YellowDark; + MainPiece.AccentColour = AccentColour = colours.YellowDark; accentDarkColour = colours.YellowDarker; } @@ -72,7 +67,7 @@ protected override void LoadComplete() // is further than mid point of the play field, so the time taken to scroll in should always // be greater than the time taken to scroll out to the left of the screen. // Thus, using PreEmpt here is enough for the drum roll to completely scroll out. - LifetimeEnd = drumRoll.EndTime + drumRoll.PreEmpt; + LifetimeEnd = HitObject.EndTime + HitObject.ScrollTime; } private void onTickJudgement(DrawableHitObject obj) @@ -85,7 +80,7 @@ private void onTickJudgement(DrawableHitObject o rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_dark_accent); Color4 newAccent = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_dark_accent, AccentColour, accentDarkColour, 0, 1); - circle.FadeAccent(newAccent, 100); + MainPiece.FadeAccent(newAccent, 100); } protected override void CheckJudgement(bool userTriggered) @@ -98,10 +93,10 @@ protected override void CheckJudgement(bool userTriggered) int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit); - if (countHit > drumRoll.RequiredGoodHits) + if (countHit > HitObject.RequiredGoodHits) { Judgement.Result = HitResult.Hit; - Judgement.TaikoResult = countHit >= drumRoll.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good; + Judgement.TaikoResult = countHit >= HitObject.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good; } else Judgement.Result = HitResult.Miss; @@ -110,7 +105,5 @@ protected override void CheckJudgement(bool userTriggered) protected override void UpdateState(ArmedState state) { } - - protected virtual CirclePiece CreateCirclePiece() => new CirclePiece(); } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 85f9e32c6c..296affedaf 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -3,79 +3,37 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; -using OpenTK; -using OpenTK.Graphics; using OpenTK.Input; +using osu.Game.Modes.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Modes.Taiko.Objects.Drawables { - public class DrawableDrumRollTick : DrawableTaikoHitObject + public class DrawableDrumRollTick : DrawableTaikoHitObject { - /// - /// The size of a tick. - /// - private const float tick_size = TaikoHitObject.CIRCLE_RADIUS / 2; - - /// - /// Any tick that is not the first for a drumroll is not filled, but is instead displayed - /// as a hollow circle. This is what controls the border width of that circle. - /// - private const float tick_border_width = tick_size / 4; - - private readonly DrumRollTick tick; - - private readonly CircularContainer bodyContainer; - public DrawableDrumRollTick(DrumRollTick tick) : base(tick) { - this.tick = tick; - - Anchor = Anchor.CentreLeft; - Origin = Anchor.Centre; - - RelativePositionAxes = Axes.X; - Size = new Vector2(tick_size); - - Children = new[] - { - bodyContainer = new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = tick_border_width, - BorderColour = Color4.White, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = tick.FirstTick ? 1 : 0, - AlwaysPresent = true - } - } - } - }; } - protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = tick.IsStrong }; + protected override TaikoPiece CreateMainPiece() => new TickPiece + { + Filled = HitObject.FirstTick + }; + + protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = HitObject.IsStrong }; protected override void CheckJudgement(bool userTriggered) { if (!userTriggered) { - if (Judgement.TimeOffset > tick.HitWindow) + if (Judgement.TimeOffset > HitObject.HitWindow) Judgement.Result = HitResult.Miss; return; } - if (Math.Abs(Judgement.TimeOffset) < tick.HitWindow) + if (Math.Abs(Judgement.TimeOffset) < HitObject.HitWindow) { Judgement.Result = HitResult.Hit; Judgement.TaikoResult = TaikoHitResult.Great; @@ -87,7 +45,7 @@ protected override void UpdateState(ArmedState state) switch (state) { case ArmedState.Hit: - bodyContainer.ScaleTo(0, 100, EasingTypes.OutQuint); + Content.ScaleTo(0, 100, EasingTypes.OutQuint); break; } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableHit.cs index 25ecd5ac84..f325026be9 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableHit.cs @@ -4,29 +4,19 @@ using System; using System.Linq; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; -using osu.Game.Modes.Taiko.Objects.Drawables.Pieces; using OpenTK.Input; namespace osu.Game.Modes.Taiko.Objects.Drawables { - public abstract class DrawableHit : DrawableTaikoHitObject + public abstract class DrawableHit : DrawableTaikoHitObject { /// /// A list of keys which can result in hits for this HitObject. /// protected abstract Key[] HitKeys { get; } - protected override Container Content => bodyContainer; - - protected readonly CirclePiece Circle; - - private readonly Hit hit; - - private readonly Container bodyContainer; - /// /// Whether the last key pressed is a valid hit key. /// @@ -35,41 +25,28 @@ public abstract class DrawableHit : DrawableTaikoHitObject protected DrawableHit(Hit hit) : base(hit) { - this.hit = hit; - - AddInternal(bodyContainer = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Children = new[] - { - Circle = CreateCirclePiece() - } - }); - - Circle.KiaiMode = HitObject.Kiai; } protected override void CheckJudgement(bool userTriggered) { if (!userTriggered) { - if (Judgement.TimeOffset > hit.HitWindowGood) + if (Judgement.TimeOffset > HitObject.HitWindowGood) Judgement.Result = HitResult.Miss; return; } double hitOffset = Math.Abs(Judgement.TimeOffset); - if (hitOffset > hit.HitWindowMiss) + if (hitOffset > HitObject.HitWindowMiss) return; if (!validKeyPressed) Judgement.Result = HitResult.Miss; - else if (hitOffset < hit.HitWindowGood) + else if (hitOffset < HitObject.HitWindowGood) { Judgement.Result = HitResult.Hit; - Judgement.TaikoResult = hitOffset < hit.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good; + Judgement.TaikoResult = hitOffset < HitObject.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good; } else Judgement.Result = HitResult.Miss; @@ -92,24 +69,26 @@ protected override void UpdateState(ArmedState state) switch (State) { case ArmedState.Idle: - Delay(hit.HitWindowMiss); + Delay(HitObject.HitWindowMiss); break; case ArmedState.Miss: FadeOut(100); break; case ArmedState.Hit: - bodyContainer.ScaleTo(0.8f, 400, EasingTypes.OutQuad); - bodyContainer.MoveToY(-200, 250, EasingTypes.Out); - bodyContainer.Delay(250); - bodyContainer.MoveToY(0, 500, EasingTypes.In); - FadeOut(600); + + const float gravity_time = 300; + const float gravity_travel_height = 200; + + Content.ScaleTo(0.8f, gravity_time * 2, EasingTypes.OutQuad); + + MoveToY(-gravity_travel_height, gravity_time, EasingTypes.Out); + Delay(gravity_time, true); + MoveToY(gravity_travel_height * 2, gravity_time * 2, EasingTypes.In); break; } Expire(); } - - protected abstract CirclePiece CreateCirclePiece(); } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongHit.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableHitStrong.cs similarity index 89% rename from osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongHit.cs rename to osu.Game.Modes.Taiko/Objects/Drawables/DrawableHitStrong.cs index 116ef94ee4..4ab029acb3 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongHit.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -7,10 +7,11 @@ using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; using OpenTK.Input; +using osu.Game.Modes.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Modes.Taiko.Objects.Drawables { - public abstract class DrawableStrongHit : DrawableHit + public abstract class DrawableHitStrong : DrawableHit { /// /// The lenience for the second key press. @@ -22,11 +23,13 @@ public abstract class DrawableStrongHit : DrawableHit private bool firstKeyHeld; private Key firstHitKey; - protected DrawableStrongHit(Hit hit) + protected DrawableHitStrong(Hit hit) : base(hit) { } + protected override TaikoPiece CreateMainPiece() => new CirclePiece(true); + protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement(); protected override void CheckJudgement(bool userTriggered) diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableRimHit.cs index 0fa51b1661..5a311d51ef 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -15,17 +15,13 @@ public class DrawableRimHit : DrawableHit public DrawableRimHit(Hit hit) : base(hit) { + MainPiece.Add(new RimHitSymbolPiece()); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - Circle.AccentColour = colours.BlueDarker; + MainPiece.AccentColour = colours.BlueDarker; } - - protected override CirclePiece CreateCirclePiece() => new CirclePiece - { - Children = new[] { new RimHitSymbolPiece() } - }; } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongRimHit.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableRimHitStrong.cs similarity index 61% rename from osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongRimHit.cs rename to osu.Game.Modes.Taiko/Objects/Drawables/DrawableRimHitStrong.cs index 3af0ff399b..5789dfb140 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongRimHit.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableRimHitStrong.cs @@ -8,24 +8,20 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables { - public class DrawableStrongRimHit : DrawableStrongHit + public class DrawableRimHitStrong : DrawableHitStrong { protected override Key[] HitKeys { get; } = { Key.D, Key.K }; - public DrawableStrongRimHit(Hit hit) + public DrawableRimHitStrong(Hit hit) : base(hit) { + MainPiece.Add(new RimHitSymbolPiece()); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - Circle.AccentColour = colours.BlueDarker; + MainPiece.AccentColour = colours.BlueDarker; } - - protected override CirclePiece CreateCirclePiece() => new StrongCirclePiece - { - Children = new[] { new RimHitSymbolPiece() } - }; } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongDrumRoll.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongDrumRoll.cs deleted file mode 100644 index bb7e197502..0000000000 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableStrongDrumRoll.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Modes.Taiko.Judgements; -using osu.Game.Modes.Taiko.Objects.Drawables.Pieces; - -namespace osu.Game.Modes.Taiko.Objects.Drawables -{ - public class DrawableStrongDrumRoll : DrawableDrumRoll - { - public DrawableStrongDrumRoll(DrumRoll drumRoll) - : base(drumRoll) - { - } - - protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = true }; - - protected override CirclePiece CreateCirclePiece() => new StrongCirclePiece(); - } -} diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableSwell.cs index 717cda9126..e1a590a025 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableSwell.cs @@ -18,7 +18,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables { - public class DrawableSwell : DrawableTaikoHitObject + public class DrawableSwell : DrawableTaikoHitObject { /// /// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime. @@ -31,8 +31,6 @@ public class DrawableSwell : DrawableTaikoHitObject private const float target_ring_scale = 5f; private const float inner_ring_alpha = 0.65f; - private readonly Swell swell; - private readonly Container bodyContainer; private readonly CircularContainer targetRing; private readonly CircularContainer expandingRing; @@ -54,12 +52,11 @@ public class DrawableSwell : DrawableTaikoHitObject public DrawableSwell(Swell swell) : base(swell) { - this.swell = swell; - Children = new Drawable[] { bodyContainer = new Container { + AutoSizeAxes = Axes.Both, Children = new Drawable[] { expandingRing = new CircularContainer @@ -120,6 +117,8 @@ public DrawableSwell(Swell swell) }, circlePiece = new CirclePiece { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Children = new [] { symbol = new SwellSymbolPiece() @@ -146,18 +145,18 @@ protected override void CheckJudgement(bool userTriggered) { userHits++; - var completion = (float)userHits / swell.RequiredHits; + var completion = (float)userHits / HitObject.RequiredHits; expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50); expandingRing.Delay(50); expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint); expandingRing.DelayReset(); - symbol.RotateTo((float)(completion * swell.Duration / 8), 4000, EasingTypes.OutQuint); + symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, EasingTypes.OutQuint); expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint); - if (userHits == swell.RequiredHits) + if (userHits == HitObject.RequiredHits) { Judgement.Result = HitResult.Hit; Judgement.TaikoResult = TaikoHitResult.Great; @@ -169,7 +168,7 @@ protected override void CheckJudgement(bool userTriggered) return; //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP - if (userHits > swell.RequiredHits / 2) + if (userHits > HitObject.RequiredHits / 2) { Judgement.Result = HitResult.Hit; Judgement.TaikoResult = TaikoHitResult.Good; @@ -189,7 +188,7 @@ protected override void UpdateState(ArmedState state) Delay(preempt, true); - Delay(Judgement.TimeOffset + swell.Duration, true); + Delay(Judgement.TimeOffset + HitObject.Duration, true); const float out_transition_time = 300; diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 5f85903827..f15f2bd152 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -3,14 +3,18 @@ using System.Collections.Generic; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Taiko.Objects.Drawables.Pieces; +using OpenTK; using OpenTK.Input; namespace osu.Game.Modes.Taiko.Objects.Drawables { - public abstract class DrawableTaikoHitObject : DrawableHitObject + public abstract class DrawableTaikoHitObject : DrawableHitObject + where TaikoHitType : TaikoHitObject { /// /// A list of keys which this hit object will accept. These are the standard Taiko keys for now. @@ -18,33 +22,52 @@ public abstract class DrawableTaikoHitObject : DrawableHitObject private readonly List validKeys = new List(new[] { Key.D, Key.F, Key.J, Key.K }); - protected DrawableTaikoHitObject(TaikoHitObject hitObject) + public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); + + protected override Container Content => bodyContainer; + + protected readonly TaikoPiece MainPiece; + + private readonly Container bodyContainer; + + public new TaikoHitType HitObject; + + protected DrawableTaikoHitObject(TaikoHitType hitObject) : base(hitObject) { + HitObject = hitObject; + Anchor = Anchor.CentreLeft; - Origin = Anchor.CentreLeft; + Origin = Anchor.Custom; + + AutoSizeAxes = Axes.Both; RelativePositionAxes = Axes.X; - } - protected override void LoadComplete() - { - LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2; + AddInternal(bodyContainer = new Container + { + AutoSizeAxes = Axes.Both, + Children = new[] + { + MainPiece = CreateMainPiece() + } + }); - base.LoadComplete(); + MainPiece.KiaiMode = HitObject.Kiai; + + LifetimeStart = HitObject.StartTime - HitObject.ScrollTime * 2; } protected override TaikoJudgement CreateJudgement() => new TaikoJudgement(); + protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(HitObject.IsStrong); + /// /// Sets the scroll position of the DrawableHitObject relative to the offset between /// a time value and the HitObject's StartTime. /// /// - protected virtual void UpdateScrollPosition(double time) - { - MoveToX((float)((HitObject.StartTime - time) / HitObject.PreEmpt)); - } + protected virtual void UpdateScrollPosition(double time) => X = (float)((HitObject.StartTime - time) / HitObject.ScrollTime); protected override void Update() { diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index 941ef3b37f..6ea1494ea7 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -1,12 +1,10 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using OpenTK.Graphics; @@ -15,26 +13,30 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces /// /// A circle piece which is used uniformly through osu!taiko to visualise hitobjects. /// - /// The body of this piece will overshoot its parent by to form - /// a rounded (_[-Width-]_) figure such that a regular "circle" is the result of a parent with Width = 0. + /// Note that this can actually be non-circle if the width is changed. See + /// for a usage example. /// /// - public class CirclePiece : Container, IHasAccentColour + public class CirclePiece : TaikoPiece { public const float SYMBOL_SIZE = TaikoHitObject.CIRCLE_RADIUS * 2f * 0.45f; public const float SYMBOL_BORDER = 8; public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER; - private Color4 accentColour; + /// + /// The amount to scale up the base circle to show it as a "strong" piece. + /// + private const float strong_scale = 1.5f; + /// /// The colour of the inner circle and outer glows. /// - public Color4 AccentColour + public override Color4 AccentColour { - get { return accentColour; } + get { return base.AccentColour; } set { - accentColour = value; + base.AccentColour = value; background.Colour = AccentColour; @@ -42,107 +44,100 @@ public Color4 AccentColour } } - private bool kiaiMode; /// /// Whether Kiai mode effects are enabled for this circle piece. /// - public bool KiaiMode + public override bool KiaiMode { - get { return kiaiMode; } + get { return base.KiaiMode; } set { - kiaiMode = value; + base.KiaiMode = value; resetEdgeEffects(); } } - public override Anchor Origin - { - get { return Anchor.CentreLeft; } - set { throw new InvalidOperationException($"{nameof(CirclePiece)} must always use CentreLeft origin."); } - } + protected override Container Content => content; - protected override Container Content => SymbolContainer; - protected readonly Container SymbolContainer; + private readonly Container content; private readonly Container background; - private readonly Container innerLayer; - public CirclePiece() + public CirclePiece(bool isStrong = false) { - RelativeSizeAxes = Axes.X; - Height = TaikoHitObject.CIRCLE_RADIUS * 2; - - // The "inner layer" is the body of the CirclePiece that overshoots it by Height/2 px on both sides - AddInternal(innerLayer = new Container + AddInternal(new Drawable[] { - Name = "Inner Layer", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] + background = new CircularContainer { - background = new CircularContainer + Name = "Background", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] { - Name = "Background", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new Drawable[] + new Box { - new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - }, - new Triangles - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - ColourLight = Color4.White, - ColourDark = Color4.White.Darken(0.1f) - } - } - }, - new CircularContainer - { - Name = "Ring", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - BorderThickness = 8, - BorderColour = Color4.White, - Masking = true, - Children = new[] + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + }, + new Triangles { - new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + ColourLight = Color4.White, + ColourDark = Color4.White.Darken(0.1f) } - }, - SymbolContainer = new Container - { - Name = "Symbol", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, } + }, + new CircularContainer + { + Name = "Ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + BorderThickness = 8, + BorderColour = Color4.White, + Masking = true, + Children = new[] + { + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + content = new Container + { + RelativeSizeAxes = Axes.Both, + Name = "Content", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, } }); + + if (isStrong) + { + Size *= strong_scale; + + //default for symbols etc. + Content.Scale *= strong_scale; + } } protected override void Update() { - // Add the overshoot to compensate for corner radius - innerLayer.Width = DrawWidth + DrawHeight; + base.Update(); + + //we want to allow for width of content to remain mapped to the area inside us, regardless of the scale applied above. + Content.Width = 1 / Content.Scale.X; } private void resetEdgeEffects() diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs new file mode 100644 index 0000000000..5431507614 --- /dev/null +++ b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs @@ -0,0 +1,41 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics.Primitives; +using osu.Game.Modes.Taiko.UI; + +namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces +{ + public class ElongatedCirclePiece : CirclePiece + { + /// + /// As we are being used to define the absolute size of hits, we need to be given a relative reference of our containing . + /// + public Func PlayfieldLengthReference; + + /// + /// The length of this piece as a multiple of the value returned by + /// + public float Length; + + public ElongatedCirclePiece(bool isStrong = false) : base(isStrong) + { + } + + protected override void Update() + { + base.Update(); + + var padding = Content.DrawHeight * Content.Width / 2; + + Content.Padding = new MarginPadding + { + Left = padding, + Right = padding, + }; + + Width = (PlayfieldLengthReference?.Invoke() ?? 0) * Length + DrawHeight; + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/StrongCirclePiece.cs b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/StrongCirclePiece.cs deleted file mode 100644 index 9676c4a19f..0000000000 --- a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/StrongCirclePiece.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 OpenTK; - -namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces -{ - /// - /// A type of circle piece which is drawn at a higher scale to represent a "strong" piece. - /// - public class StrongCirclePiece : CirclePiece - { - /// - /// The amount to scale up the base circle to show it as a "strong" piece. - /// - private const float strong_scale = 1.5f; - - public StrongCirclePiece() - { - SymbolContainer.Scale = new Vector2(strong_scale); - } - - public override Vector2 Size => new Vector2(base.Size.X, base.Size.Y * strong_scale); - } -} diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs new file mode 100644 index 0000000000..a0c8865c59 --- /dev/null +++ b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces +{ + public class TaikoPiece : Container, IHasAccentColour + { + private Color4 accentColour; + /// + /// The colour of the inner circle and outer glows. + /// + public virtual Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + } + } + + private bool kiaiMode; + /// + /// Whether Kiai mode effects are enabled for this circle piece. + /// + public virtual bool KiaiMode + { + get { return kiaiMode; } + set + { + kiaiMode = value; + } + } + + public TaikoPiece() + { + //just a default + Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2); + } + } +} diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TickPiece.cs b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TickPiece.cs new file mode 100644 index 0000000000..697102eb22 --- /dev/null +++ b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TickPiece.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces +{ + public class TickPiece : TaikoPiece + { + /// + /// Any tick that is not the first for a drumroll is not filled, but is instead displayed + /// as a hollow circle. This is what controls the border width of that circle. + /// + private const float tick_border_width = TaikoHitObject.CIRCLE_RADIUS / 2 / 4; + + /// + /// The size of a tick. + /// + private const float tick_size = TaikoHitObject.CIRCLE_RADIUS / 2; + + private bool filled; + public bool Filled + { + get { return filled; } + set + { + filled = value; + fillBox.Alpha = filled ? 1 : 0; + } + } + + private readonly Box fillBox; + + public TickPiece() + { + Size = new Vector2(tick_size); + + Add(new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = tick_border_width, + BorderColour = Color4.White, + Children = new[] + { + fillBox = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }); + } + } +} diff --git a/osu.Game.Modes.Taiko/Objects/DrumRoll.cs b/osu.Game.Modes.Taiko/Objects/DrumRoll.cs index ede576835c..3a66c55ea5 100644 --- a/osu.Game.Modes.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Modes.Taiko/Objects/DrumRoll.cs @@ -11,26 +11,16 @@ namespace osu.Game.Modes.Taiko.Objects { - public class DrumRoll : TaikoHitObject, IHasDistance + public class DrumRoll : TaikoHitObject, IHasEndTime { /// /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. /// private const float base_distance = 100; - public double EndTime => StartTime + Distance / Velocity; + public double EndTime => StartTime + Duration; - public double Duration => EndTime - StartTime; - - /// - /// Raw length of the drum roll in positional length units. - /// - public double Distance { get; set; } - - /// - /// Velocity of the drum roll in positional length units per millisecond. - /// - public double Velocity { get; protected set; } = 5; + public double Duration { get; set; } /// /// Numer of ticks per beat length. @@ -69,9 +59,6 @@ public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficul { base.ApplyDefaults(timing, difficulty); - double speedAdjutedBeatLength = timing.SpeedMultiplierAt(StartTime) * timing.BeatLengthAt(StartTime); - - Velocity = base_distance * difficulty.SliderMultiplier / speedAdjutedBeatLength * VelocityMultiplier; tickSpacing = timing.BeatLengthAt(StartTime) / TickRate; RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty); @@ -86,12 +73,12 @@ private List createTicks() return ret; bool first = true; - for (double t = StartTime; t < EndTime + (int)tickSpacing; t += tickSpacing) + for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing) { ret.Add(new DrumRollTick { FirstTick = first, - PreEmpt = PreEmpt, + ScrollTime = ScrollTime, TickSpacing = tickSpacing, StartTime = t, IsStrong = IsStrong, diff --git a/osu.Game.Modes.Taiko/Objects/Swell.cs b/osu.Game.Modes.Taiko/Objects/Swell.cs index f55416509a..97101ea797 100644 --- a/osu.Game.Modes.Taiko/Objects/Swell.cs +++ b/osu.Game.Modes.Taiko/Objects/Swell.cs @@ -7,9 +7,9 @@ namespace osu.Game.Modes.Taiko.Objects { public class Swell : TaikoHitObject, IHasEndTime { - public double EndTime { get; set; } + public double EndTime => StartTime + Duration; - public double Duration => EndTime - StartTime; + public double Duration { get; set; } /// /// The number of hits required to complete the swell successfully. diff --git a/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs index 839471a651..7375ea2b0d 100644 --- a/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs @@ -15,19 +15,14 @@ public abstract class TaikoHitObject : HitObject public const float CIRCLE_RADIUS = 42f; /// - /// Time (in milliseconds) to scroll in the hit object with a speed-adjusted beat length of 1 second. + /// The time taken from the initial (off-screen) spawn position to the centre of the hit target for a of 1000ms. /// - public const double BASE_SCROLL_TIME = 6000; + private const double scroll_time = 6000; /// - /// The velocity multiplier applied to this hit object. + /// Our adjusted taking into consideration local and other speed multipliers. /// - public float VelocityMultiplier = 1; - - /// - /// The time to scroll in the HitObject. - /// - public double PreEmpt; + public double ScrollTime; /// /// Whether this HitObject is a "strong" type. @@ -44,7 +39,7 @@ public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficul { base.ApplyDefaults(timing, difficulty); - PreEmpt = BASE_SCROLL_TIME / difficulty.SliderMultiplier * timing.BeatLengthAt(StartTime) * timing.SpeedMultiplierAt(StartTime) / VelocityMultiplier / 1000; + ScrollTime = scroll_time * (timing.BeatLengthAt(StartTime) / 1000) / (difficulty.SliderMultiplier * timing.SpeedMultiplierAt(StartTime)); ControlPoint overridePoint; Kiai = timing.TimingPointAt(StartTime, out overridePoint).KiaiMode; diff --git a/osu.Game.Modes.Taiko/UI/InputDrum.cs b/osu.Game.Modes.Taiko/UI/InputDrum.cs index e7470ee913..5eea08ad8b 100644 --- a/osu.Game.Modes.Taiko/UI/InputDrum.cs +++ b/osu.Game.Modes.Taiko/UI/InputDrum.cs @@ -128,17 +128,36 @@ protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) return false; Drawable target = null; + Drawable back = null; if (args.Key == CentreKey) + { target = centreHit; + back = centre; + } else if (args.Key == RimKey) + { target = rimHit; + back = rim; + } if (target != null) { - target.FadeTo(Math.Min(target.Alpha + 0.4f, 1), 40, EasingTypes.OutQuint); - target.Delay(40); - target.FadeOut(1000, EasingTypes.OutQuint); + const float scale_amount = 0.05f; + const float alpha_amount = 0.5f; + + const float down_time = 40; + const float up_time = 1000; + + back.ScaleTo(target.Scale.X - scale_amount, down_time, EasingTypes.OutQuint); + back.Delay(down_time); + back.ScaleTo(1, up_time, EasingTypes.OutQuint); + + target.ScaleTo(target.Scale.X - scale_amount, down_time, EasingTypes.OutQuint); + target.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, EasingTypes.OutQuint); + target.Delay(down_time); + target.ScaleTo(1, up_time, EasingTypes.OutQuint); + target.FadeOut(up_time, EasingTypes.OutQuint); } return false; diff --git a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs index c1ee1f6691..29fa693d58 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs @@ -79,7 +79,7 @@ private void loadBarLines() barLine.ApplyDefaults(Beatmap.TimingInfo, Beatmap.BeatmapInfo.Difficulty); - taikoPlayfield.AddBarLine(isMajor ? new DrawableMajorBarLine(barLine) : new DrawableBarLine(barLine)); + taikoPlayfield.AddBarLine(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine)); currentBeat++; } @@ -118,7 +118,7 @@ protected override DrawableHitObject GetVisualRe if (centreHit != null) { if (h.IsStrong) - return new DrawableStrongCentreHit(centreHit); + return new DrawableCentreHitStrong(centreHit); return new DrawableCentreHit(centreHit); } @@ -126,15 +126,13 @@ protected override DrawableHitObject GetVisualRe if (rimHit != null) { if (h.IsStrong) - return new DrawableStrongRimHit(rimHit); + return new DrawableRimHitStrong(rimHit); return new DrawableRimHit(rimHit); } var drumRoll = h as DrumRoll; if (drumRoll != null) { - if (h.IsStrong) - return new DrawableStrongDrumRoll(drumRoll); return new DrawableDrumRoll(drumRoll); } diff --git a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs index 958055d768..9e7eb571a1 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs @@ -85,7 +85,7 @@ public TaikoPlayfield() { new Container { - Padding = new MarginPadding { Left = hit_target_offset }, + X = hit_target_offset, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index 1fbf1b27a1..d0981c2500 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -55,24 +55,25 @@ - + - + - - - + + + - + + diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 748583606b..2bf282346a 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -215,7 +215,7 @@ private void handleTimingPoints(Beatmap beatmap, string val) { Time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo), BeatLength = beatLength > 0 ? beatLength : 0, - VelocityAdjustment = beatLength < 0 ? -beatLength / 100.0 : 1, + SpeedMultiplier = beatLength < 0 ? -beatLength / 100.0 : 1, TimingChange = split.Length <= 6 || split[6][0] == '1', KiaiMode = (effectFlags & 1) > 0, OmitFirstBarLine = (effectFlags & 8) > 0, diff --git a/osu.Game/Beatmaps/Timing/ControlPoint.cs b/osu.Game/Beatmaps/Timing/ControlPoint.cs index 40320a88d7..e306796d24 100644 --- a/osu.Game/Beatmaps/Timing/ControlPoint.cs +++ b/osu.Game/Beatmaps/Timing/ControlPoint.cs @@ -5,18 +5,14 @@ namespace osu.Game.Beatmaps.Timing { public class ControlPoint { - public static ControlPoint Default = new ControlPoint - { - BeatLength = 500, - TimingChange = true, - }; - public TimeSignatures TimeSignature; public double Time; - public double BeatLength; - public double VelocityAdjustment; - public bool TimingChange; + public double BeatLength = 500; + public double SpeedMultiplier = 1; + public bool TimingChange = true; public bool KiaiMode; public bool OmitFirstBarLine; + + public ControlPoint Clone() => (ControlPoint)MemberwiseClone(); } } diff --git a/osu.Game/Beatmaps/Timing/TimingInfo.cs b/osu.Game/Beatmaps/Timing/TimingInfo.cs index 076618beea..19cb0816ba 100644 --- a/osu.Game/Beatmaps/Timing/TimingInfo.cs +++ b/osu.Game/Beatmaps/Timing/TimingInfo.cs @@ -10,8 +10,8 @@ public class TimingInfo { public readonly List ControlPoints = new List(); - public double BPMMaximum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderBy(c => c.BeatLength).FirstOrDefault() ?? ControlPoint.Default).BeatLength; - public double BPMMinimum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? ControlPoint.Default).BeatLength; + public double BPMMaximum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderBy(c => c.BeatLength).FirstOrDefault() ?? new ControlPoint()).BeatLength; + public double BPMMinimum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new ControlPoint()).BeatLength; public double BPMMode => BPMAt(ControlPoints.Where(c => c.BeatLength != 0).GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).First().First().Time); public double BPMAt(double time) @@ -29,7 +29,7 @@ public double SpeedMultiplierAt(double time) ControlPoint overridePoint; ControlPoint timingPoint = TimingPointAt(time, out overridePoint); - return overridePoint?.VelocityAdjustment ?? timingPoint?.VelocityAdjustment ?? 1; + return overridePoint?.SpeedMultiplier ?? timingPoint?.SpeedMultiplier ?? 1; } /// @@ -74,7 +74,7 @@ public ControlPoint TimingPointAt(double time, out ControlPoint overridePoint) else break; } - return timingPoint ?? ControlPoint.Default; + return timingPoint ?? new ControlPoint(); } } } \ No newline at end of file diff --git a/osu.Game/Database/BaseDifficulty.cs b/osu.Game/Database/BaseDifficulty.cs index 3db8b29644..7c9f47e7b6 100644 --- a/osu.Game/Database/BaseDifficulty.cs +++ b/osu.Game/Database/BaseDifficulty.cs @@ -9,12 +9,12 @@ public class BeatmapDifficulty { [PrimaryKey, AutoIncrement] public int ID { get; set; } - public float DrainRate { get; set; } - public float CircleSize { get; set; } - public float OverallDifficulty { get; set; } - public float ApproachRate { get; set; } - public float SliderMultiplier { get; set; } - public float SliderTickRate { get; set; } + public float DrainRate { get; set; } = 5; + public float CircleSize { get; set; } = 5; + public float OverallDifficulty { get; set; } = 5; + public float ApproachRate { get; set; } = 5; + public float SliderMultiplier { get; set; } = 1; + public float SliderTickRate { get; set; } = 1; /// /// Maps a difficulty value [0, 10] to a two-piece linear range of values. diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Database/BeatmapInfo.cs index 890623091d..aabe0755fe 100644 --- a/osu.Game/Database/BeatmapInfo.cs +++ b/osu.Game/Database/BeatmapInfo.cs @@ -15,6 +15,7 @@ public class BeatmapInfo : IEquatable [PrimaryKey, AutoIncrement] public int ID { get; set; } + //TODO: should be in database public int BeatmapVersion; public int? OnlineBeatmapID { get; set; } diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 8dff614f6c..830d0adc97 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -60,8 +60,8 @@ public float TriangleScale protected override void LoadComplete() { base.LoadComplete(); - for (int i = 0; i < aimTriangleCount; i++) - addTriangle(true); + + addTriangles(true); } private int aimTriangleCount => (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); @@ -83,8 +83,8 @@ protected override void Update() t.Expire(); } - while (CreateNewTriangles && Children.Count() < aimTriangleCount) - addTriangle(false); + if (CreateNewTriangles) + addTriangles(false); } protected virtual Triangle CreateTriangle() @@ -113,12 +113,16 @@ protected virtual Triangle CreateTriangle() protected virtual Color4 GetTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1); - private void addTriangle(bool randomY) + private void addTriangles(bool randomY) { - var sprite = CreateTriangle(); - float triangleHeight = sprite.DrawHeight / DrawHeight; - sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1); - Add(sprite); + int addCount = aimTriangleCount - Children.Count(); + for (int i = 0; i < addCount; i++) + { + var sprite = CreateTriangle(); + float triangleHeight = sprite.DrawHeight / DrawHeight; + sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1); + Add(sprite); + } } } } diff --git a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs index ed8269876e..c0b1645b79 100644 --- a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs @@ -56,13 +56,17 @@ protected virtual void PlaySample() Sample?.Play(); } - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load() { - base.LoadComplete(); - //we may be setting a custom judgement in test cases or what not. if (Judgement == null) Judgement = CreateJudgement(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); //force application of the state that was set before we loaded. UpdateState(State); diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index e36d2a101c..a958c61c68 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -42,6 +42,16 @@ public abstract class HitRenderer : Container /// protected readonly KeyConversionInputManager KeyConversionInputManager; + /// + /// Whether we are currently providing the local user a gameplay cursor. + /// + public virtual bool ProvidingUserCursor => false; + + /// + /// Whether we have a replay loaded currently. + /// + public bool HasReplayLoaded => InputManager.ReplayInputHandler != null; + /// /// Whether all the HitObjects have been judged. /// @@ -157,6 +167,8 @@ public abstract class HitRenderer : HitRenderer { public event Action OnJudgement; + public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor; + protected override Container Content => content; protected override bool AllObjectsJudged => Playfield.HitObjects.Children.All(h => h.Judgement.Result != HitResult.None); diff --git a/osu.Game/Modes/UI/Playfield.cs b/osu.Game/Modes/UI/Playfield.cs index eff06ce80f..f31ee0f189 100644 --- a/osu.Game/Modes/UI/Playfield.cs +++ b/osu.Game/Modes/UI/Playfield.cs @@ -22,6 +22,11 @@ public abstract class Playfield : Container internal Container ScaledContent; + /// + /// Whether we are currently providing the local user a gameplay cursor. + /// + public virtual bool ProvidingUserCursor => false; + protected override Container Content => content; private readonly Container content; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d75f8b4d8e..7172aba3be 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -314,7 +314,7 @@ protected override void UpdateAfterChildren() if (intro?.ChildScreen != null) intro.ChildScreen.Padding = new MarginPadding { Top = Toolbar.Position.Y + Toolbar.DrawHeight }; - Cursor.State = currentScreen == null || currentScreen.HasLocalCursorDisplayed ? Visibility.Hidden : Visibility.Visible; + Cursor.State = currentScreen?.HasLocalCursorDisplayed == false ? Visibility.Visible : Visibility.Hidden; } private void screenAdded(Screen newScreen) diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index 77fc010e6d..663c5cf90c 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -155,7 +155,7 @@ public string Text public void MarkAllRead() { - notifications.Children.ForEach(n => n.Read = true); + notifications?.Children.ForEach(n => n.Read = true); } } } \ No newline at end of file diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b263b5507c..f160563c3b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -31,9 +31,7 @@ public class Player : OsuScreen internal override bool ShowOverlays => false; - internal override bool HasLocalCursorDisplayed => !hasReplayLoaded && !IsPaused; - - private bool hasReplayLoaded => HitRenderer.InputManager.ReplayInputHandler != null; + internal override bool HasLocalCursorDisplayed => !IsPaused && HitRenderer.ProvidingUserCursor; public BeatmapInfo BeatmapInfo; @@ -305,7 +303,7 @@ protected override bool OnExiting(Screen next) { if (pauseOverlay == null) return false; - if (hasReplayLoaded) + if (HitRenderer.HasReplayLoaded) return false; if (pauseOverlay.State != Visibility.Visible && !canPause) return true; diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 7443603c8b..f104bf9a37 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -179,11 +179,11 @@ public void SelectRandom() public void Filter(FilterCriteria newCriteria = null, bool debounce = true) { - if (!IsLoaded) return; - if (newCriteria != null) criteria = newCriteria; + if (!IsLoaded) return; + Action perform = delegate { filterTask = null; diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 26daddc3a9..6d92b35993 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -173,9 +173,9 @@ private void load(OsuColour colours, OsuGame osu) { sortTabs.AccentColour = colours.GreenLight; - if (osu != null) - playMode.BindTo(osu.PlayMode); + if (osu != null) playMode.BindTo(osu.PlayMode); playMode.ValueChanged += val => FilterChanged?.Invoke(CreateCriteria()); + playMode.TriggerChange(); } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 44204f5beb..3e8ddc0f64 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -167,13 +167,12 @@ private void load(BeatmapDatabase beatmaps, AudioManager audio, DialogOverlay di BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, promptDelete, Key.Number4, float.MaxValue); } - if (osu != null) - playMode.BindTo(osu.PlayMode); - playMode.ValueChanged += val => Beatmap.PreferredPlayMode = val; - if (database == null) database = beatmaps; + playMode.ValueChanged += val => { if (Beatmap != null) Beatmap.PreferredPlayMode = val; }; + if (osu != null) playMode.BindTo(osu.PlayMode); + database.BeatmapSetAdded += onBeatmapSetAdded; database.BeatmapSetRemoved += onBeatmapSetRemoved;