diff --git a/osu-framework b/osu-framework index 8baad1b948..0f3db5da09 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 8baad1b9484b9f35724e2f965c18cfe710907d80 +Subproject commit 0f3db5da09d0e7c4d2ef3057030e018f34ba536e diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs index 9dcba02849..95287c3199 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs @@ -11,6 +11,9 @@ using OpenTK; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Timing; +using osu.Framework.Configuration; +using OpenTK.Input; +using osu.Framework.Timing; namespace osu.Desktop.VisualTests.Tests { @@ -59,6 +62,51 @@ namespace osu.Desktop.VisualTests.Tests } }; + Action createPlayfieldWithNotesAcceptingInput = () => + { + Clear(); + + var rateAdjustClock = new StopwatchClock(true) { Rate = 0.5 }; + + ManiaPlayfield playField; + Add(playField = new ManiaPlayfield(4, new List { new TimingChange { BeatLength = 200 } }) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1, -1), + Clock = new FramedClock(rateAdjustClock) + }); + + for (int t = 1000; t <= 2000; t += 100) + { + playField.Add(new DrawableNote(new Note + { + StartTime = t, + Column = 0 + }, new Bindable(Key.D))); + + playField.Add(new DrawableNote(new Note + { + StartTime = t, + Column = 3 + }, new Bindable(Key.K))); + } + + playField.Add(new DrawableHoldNote(new HoldNote + { + StartTime = 1000, + Duration = 1000, + Column = 1 + }, new Bindable(Key.F))); + + playField.Add(new DrawableHoldNote(new HoldNote + { + StartTime = 1000, + Duration = 1000, + Column = 2 + }, new Bindable(Key.J))); + }; + AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal)); AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal)); AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left)); @@ -76,11 +124,13 @@ namespace osu.Desktop.VisualTests.Tests AddWaitStep(10); AddStep("Right special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Right)); AddWaitStep(10); + + AddStep("Notes with input", () => createPlayfieldWithNotesAcceptingInput()); } private void triggerKeyDown(Column column) { - column.TriggerKeyDown(new InputState(), new KeyDownEventArgs + column.TriggerOnKeyDown(new InputState(), new KeyDownEventArgs { Key = column.Key, Repeat = false @@ -89,7 +139,7 @@ namespace osu.Desktop.VisualTests.Tests private void triggerKeyUp(Column column) { - column.TriggerKeyUp(new InputState(), new KeyUpEventArgs + column.TriggerOnKeyUp(new InputState(), new KeyUpEventArgs { Key = column.Key }); diff --git a/osu.Desktop.VisualTests/Tests/TestCaseUserPanel.cs b/osu.Desktop.VisualTests/Tests/TestCaseUserPanel.cs index 513bf24e0d..92d58c10c9 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseUserPanel.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseUserPanel.cs @@ -32,14 +32,14 @@ namespace osu.Desktop.VisualTests.Tests Username = @"flyte", Id = 3103765, Country = new Country { FlagName = @"JP" }, - CoverUrl = @"https://assets.ppy.sh/user-profile-covers/3103765/5b012e13611d5761caa7e24fecb3d3a16e1cf48fc2a3032cfd43dd444af83d82.jpeg" + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" }) { Width = 300 }, peppy = new UserPanel(new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" }, - CoverUrl = @"https://assets.ppy.sh/user-profile-covers/2/08cad88747c235a64fca5f1b770e100f120827ded1ffe3b66bfcd19c940afa65.jpeg" + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" }) { Width = 300 }, }, }); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 718e0967da..2d1f75e196 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -471,14 +471,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy } else { - newObject = new HoldNote + var holdNote = new HoldNote { StartTime = startTime, - Samples = sampleInfoListAt(startTime), - EndSamples = sampleInfoListAt(endTime), Column = column, - Duration = endTime - startTime + Duration = endTime - startTime, + Head = { Samples = sampleInfoListAt(startTime) }, + Tail = { Samples = sampleInfoListAt(endTime) } }; + + + newObject = holdNote; } pattern.Add(newObject); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index 8f438f9ff4..6ad7489e0f 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -69,18 +69,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (holdNote) { - newObject = new HoldNote + var hold = new HoldNote { StartTime = HitObject.StartTime, - EndSamples = HitObject.Samples, Column = column, Duration = endTime - HitObject.StartTime }; - newObject.Samples.Add(new SampleInfo + hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL }); + + hold.Tail.Samples = HitObject.Samples; + + newObject = hold; } else { diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs new file mode 100644 index 0000000000..df2f7e9e63 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Judgements +{ + public class HoldNoteTailJudgement : ManiaJudgement + { + /// + /// Whether the hold note has been released too early and shouldn't give full score for the release. + /// + public bool HasBroken; + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs new file mode 100644 index 0000000000..bead455c13 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs @@ -0,0 +1,9 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Judgements +{ + public class HoldNoteTickJudgement : ManiaJudgement + { + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index f9d027e7ce..5d7f3314cd 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -7,14 +7,34 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using OpenTK.Graphics; using osu.Framework.Configuration; using OpenTK.Input; +using osu.Framework.Input; +using OpenTK; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mania.Judgements; +using osu.Framework.Extensions.IEnumerableExtensions; namespace osu.Game.Rulesets.Mania.Objects.Drawables { + /// + /// Visualises a hit object. + /// public class DrawableHoldNote : DrawableManiaHitObject { - private readonly NotePiece headPiece; + private readonly DrawableNote head; + private readonly DrawableNote tail; + private readonly BodyPiece bodyPiece; - private readonly NotePiece tailPiece; + private readonly Container tickContainer; + + /// + /// Time at which the user started holding this hold note. Null if the user is not holding this hold note. + /// + private double? holdStartTime; + + /// + /// Whether the hold note has been released too early and shouldn't give full score for the release. + /// + private bool hasBroken; public DrawableHoldNote(HoldNote hitObject, Bindable key = null) : base(hitObject, key) @@ -32,17 +52,39 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, - headPiece = new NotePiece + tickContainer = new Container + { + RelativeSizeAxes = Axes.Both, + RelativeCoordinateSpace = new Vector2(1, (float)HitObject.Duration) + }, + head = new DrawableHeadNote(this, key) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre }, - tailPiece = new NotePiece + tail = new DrawableTailNote(this, key) { Anchor = Anchor.BottomCentre, Origin = Anchor.TopCentre } }); + + foreach (var tick in HitObject.Ticks) + { + var drawableTick = new DrawableHoldNoteTick(tick) + { + HoldStartTime = () => holdStartTime + }; + + // To make the ticks relative to ourselves we need to offset them backwards + drawableTick.Y -= (float)HitObject.StartTime; + + tickContainer.Add(drawableTick); + AddNested(drawableTick); + } + + AddNested(head); + AddNested(tail); } public override Color4 AccentColour @@ -54,9 +96,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables return; base.AccentColour = value; - headPiece.AccentColour = value; + tickContainer.Children.ForEach(t => t.AccentColour = value); + bodyPiece.AccentColour = value; - tailPiece.AccentColour = value; + head.AccentColour = value; + tail.AccentColour = value; } } @@ -64,14 +108,132 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { } - protected override void Update() + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { - if (Time.Current > HitObject.StartTime) - headPiece.Colour = Color4.Green; - if (Time.Current > HitObject.EndTime) + // Make sure the keypress happened within the body of the hold note + if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime) + return false; + + if (args.Key != Key) + return false; + + if (args.Repeat) + return false; + + // The user has pressed during the body of the hold note, after the head note and its hit windows have passed + // and within the limited range of the above if-statement. This state will be managed by the head note if the + // user has pressed during the hit windows of the head note. + holdStartTime = Time.Current; + + return true; + } + + protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + { + // Make sure that the user started holding the key during the hold note + if (!holdStartTime.HasValue) + return false; + + if (args.Key != Key) + return false; + + holdStartTime = null; + + // If the key has been released too early, the user should not receive full score for the release + if (!tail.Judged) + hasBroken = true; + + return true; + } + + /// + /// The head note of a hold. + /// + private class DrawableHeadNote : DrawableNote + { + private readonly DrawableHoldNote holdNote; + + public DrawableHeadNote(DrawableHoldNote holdNote, Bindable key = null) + : base(holdNote.HitObject.Head, key) { - bodyPiece.Colour = Color4.Green; - tailPiece.Colour = Color4.Green; + this.holdNote = holdNote; + + RelativePositionAxes = Axes.None; + Y = 0; + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (!base.OnKeyDown(state, args)) + return false; + + // We only want to trigger a holding state from the head if the head has received a judgement + if (!Judged) + return false; + + // If the key has been released too early, the user should not receive full score for the release + if (Judgement.Result == HitResult.Miss) + holdNote.hasBroken = true; + + // The head note also handles early hits before the body, but we want accurate early hits to count as the body being held + // The body doesn't handle these early early hits, so we have to explicitly set the holding state here + holdNote.holdStartTime = Time.Current; + + return true; + } + } + + /// + /// The tail note of a hold. + /// + private class DrawableTailNote : DrawableNote + { + private readonly DrawableHoldNote holdNote; + + public DrawableTailNote(DrawableHoldNote holdNote, Bindable key = null) + : base(holdNote.HitObject.Tail, key) + { + this.holdNote = holdNote; + + RelativePositionAxes = Axes.None; + Y = 0; + } + + protected override ManiaJudgement CreateJudgement() => new HoldNoteTailJudgement(); + + protected override void CheckJudgement(bool userTriggered) + { + base.CheckJudgement(userTriggered); + + var tailJudgement = Judgement as HoldNoteTailJudgement; + if (tailJudgement == null) + return; + + tailJudgement.HasBroken = holdNote.hasBroken; + } + + protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + { + // Make sure that the user started holding the key during the hold note + if (!holdNote.holdStartTime.HasValue) + return false; + + if (Judgement.Result != HitResult.None) + return false; + + if (args.Key != Key) + return false; + + UpdateJudgement(true); + + // Handled by the hold note, which will set holding = false + return false; + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + // Tail doesn't handle key down + return false; } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs new file mode 100644 index 0000000000..9ecc77d3fc --- /dev/null +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -0,0 +1,121 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Mania.Judgements; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mania.Objects.Drawables +{ + /// + /// Visualises a hit object. + /// + public class DrawableHoldNoteTick : DrawableManiaHitObject + { + /// + /// References the time at which the user started holding the hold note. + /// + public Func HoldStartTime; + + /// + /// References whether the user is currently holding the hold note. + /// + public Func IsHolding; + + private readonly Container glowContainer; + + public DrawableHoldNoteTick(HoldNoteTick hitObject) + : base(hitObject) + { + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + + RelativeSizeAxes = Axes.X; + Size = new Vector2(1); + + Children = new[] + { + glowContainer = new CircularContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + } + }; + + // Set the default glow + AccentColour = Color4.White; + } + + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + + glowContainer.EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Glow, + Radius = 2f, + Roundness = 15f, + Colour = value.Opacity(0.3f) + }; + } + } + + protected override ManiaJudgement CreateJudgement() => new HoldNoteTickJudgement(); + + protected override void CheckJudgement(bool userTriggered) + { + if (!userTriggered) + return; + + if (Time.Current < HitObject.StartTime) + return; + + if (HoldStartTime?.Invoke() > HitObject.StartTime) + return; + + Judgement.ManiaResult = ManiaHitResult.Perfect; + Judgement.Result = HitResult.Hit; + } + + protected override void UpdateState(ArmedState state) + { + switch (State) + { + case ArmedState.Hit: + AccentColour = Color4.Green; + break; + } + } + + protected override void Update() + { + if (Judgement.Result != HitResult.None) + return; + + if (IsHolding?.Invoke() != true) + return; + + UpdateJudgement(true); + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 42bb371975..658d409bb8 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -13,6 +13,9 @@ using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Objects.Drawables { + /// + /// Visualises a hit object. + /// public class DrawableNote : DrawableManiaHitObject { private readonly NotePiece headPiece; diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index 30e71aeb5d..c241c4cf41 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -1,10 +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.Audio; +using System.Collections.Generic; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Database; -using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Mania.Objects @@ -12,32 +11,96 @@ namespace osu.Game.Rulesets.Mania.Objects /// /// Represents a hit object which requires pressing, holding, and releasing a key. /// - public class HoldNote : Note, IHasEndTime + public class HoldNote : ManiaHitObject, IHasEndTime { - /// - /// Lenience of release hit windows. This is to make cases where the hold note release - /// is timed alongside presses of other hit objects less awkward. - /// - private const double release_window_lenience = 1.5; - - public double Duration { get; set; } public double EndTime => StartTime + Duration; - /// - /// The samples to be played when this hold note is released. - /// - public SampleInfoList EndSamples = new SampleInfoList(); + private double duration; + public double Duration + { + get { return duration; } + set + { + duration = value; + Tail.StartTime = EndTime; + } + } + + public override double StartTime + { + get { return base.StartTime; } + set + { + base.StartTime = value; + Head.StartTime = value; + Tail.StartTime = EndTime; + } + } /// - /// The key-release hit windows for this hold note. + /// The head note of the hold. /// - public HitWindows ReleaseHitWindows { get; protected set; } = new HitWindows(); + public readonly Note Head = new Note(); + + /// + /// The tail note of the hold. + /// + public readonly Note Tail = new TailNote(); + + /// + /// The time between ticks of this hold. + /// + private double tickSpacing = 50; public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { base.ApplyDefaults(controlPointInfo, difficulty); - ReleaseHitWindows = HitWindows * release_window_lenience; + TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate; + } + + /// + /// The scoring scoring ticks of the hold note. + /// + public IEnumerable Ticks => ticks ?? (ticks = createTicks()); + private List ticks; + + private List createTicks() + { + var ret = new List(); + + if (tickSpacing == 0) + return ret; + + for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing) + { + ret.Add(new HoldNoteTick + { + StartTime = t + }); + } + + return ret; + } + + /// + /// The tail of the hold note. + /// + private class TailNote : Note + { + /// + /// Lenience of release hit windows. This is to make cases where the hold note release + /// is timed alongside presses of other hit objects less awkward. + /// + private const double release_window_lenience = 1.5; + + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaults(controlPointInfo, difficulty); + + HitWindows *= release_window_lenience; + } } } } diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs new file mode 100644 index 0000000000..6c4cf127f3 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Objects +{ + /// + /// A scoring tick of a hold note. + /// + public class HoldNoteTick : ManiaHitObject + { + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs b/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs index cc8897840e..0a8bc2d44a 100644 --- a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs +++ b/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using OpenTK; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mania.Timing { @@ -128,6 +129,8 @@ namespace osu.Game.Rulesets.Mania.Timing /// private class AutoTimeRelativeContainer : Container { + protected override IComparer DepthComparer => new HitObjectReverseStartTimeComparer(); + public override void InvalidateFromChild(Invalidation invalidation) { // We only want to re-compute our size when a child's size or position has changed diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index c8cb5f6387..6dfd5000d4 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - using OpenTK; using OpenTK.Graphics; using OpenTK.Input; @@ -188,7 +187,11 @@ namespace osu.Game.Rulesets.Mania.UI } } - public void Add(DrawableHitObject hitObject) => ControlPointContainer.Add(hitObject); + public void Add(DrawableHitObject hitObject) + { + hitObject.AccentColour = AccentColour; + ControlPointContainer.Add(hitObject); + } private bool onKeyDown(InputState state, KeyDownEventArgs args) { diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 9442d7cf8f..7a8ec25fe4 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -57,10 +57,13 @@ + + + @@ -68,6 +71,7 @@ + diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index cb1d9d2fcd..c6aac3bb71 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -428,6 +428,9 @@ namespace osu.Game.Beatmaps.Formats break; } } + + foreach (var hitObject in beatmap.HitObjects) + hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty); } internal enum LegacySampleBank diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 2f53d00c7e..fe1d255bba 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -3,6 +3,7 @@ using OpenTK.Graphics; using OpenTK.Input; +using osu.Framework.Allocation; using osu.Framework.Input; using System; using System.Linq; @@ -23,11 +24,19 @@ namespace osu.Game.Graphics.UserInterface set { focus = value; - if (!focus) - TriggerFocusLost(); + if (!focus && HasFocus) + inputManager.ChangeFocus(null); } } + private InputManager inputManager; + + [BackgroundDependencyLoader] + private void load(UserInputManager inputManager) + { + this.inputManager = inputManager; + } + protected override bool OnFocus(InputState state) { var result = base.OnFocus(state); diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index 237aaa44a6..ebaef661c4 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -5,18 +5,21 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Transforms; using osu.Framework.Input; using OpenTK; using OpenTK.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Containers; +using osu.Game.Beatmaps.ControlPoints; +using osu.Framework.Audio.Track; +using System; namespace osu.Game.Graphics.UserInterface { public class TwoLayerButton : ClickableContainer { - private readonly TextAwesome icon; + private readonly BouncingIcon bouncingIcon; public Box IconLayer; public Box TextLayer; @@ -95,11 +98,10 @@ namespace osu.Game.Graphics.UserInterface }, } }, - icon = new TextAwesome + bouncingIcon = new BouncingIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, - TextSize = 25, }, } }, @@ -146,7 +148,7 @@ namespace osu.Game.Graphics.UserInterface { set { - icon.Icon = value; + bouncingIcon.Icon = value; } } @@ -162,58 +164,20 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(InputState state) { - icon.ClearTransforms(); - ResizeTo(SIZE_EXTENDED, transform_time, EasingTypes.OutElastic); - - int duration = 0; //(int)(Game.Audio.BeatLength / 2); - if (duration == 0) duration = pulse_length; - IconLayer.FadeColour(HoverColour, transform_time, EasingTypes.OutElastic); - const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; - double startTime = Time.Current + offset; - - // basic pulse - icon.Transforms.Add(new TransformScale - { - StartValue = new Vector2(1.1f), - EndValue = Vector2.One, - StartTime = startTime, - EndTime = startTime + duration, - Easing = EasingTypes.Out, - LoopCount = -1, - LoopDelay = duration - }); + bouncingIcon.ScaleTo(1.1f, transform_time, EasingTypes.OutElastic); return true; } protected override void OnHoverLost(InputState state) { - icon.ClearTransforms(); - ResizeTo(SIZE_RETRACTED, transform_time, EasingTypes.OutElastic); - IconLayer.FadeColour(TextLayer.Colour, transform_time, EasingTypes.OutElastic); - int duration = 0; //(int)(Game.Audio.BeatLength); - if (duration == 0) duration = pulse_length * 2; - - const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; - double startTime = Time.Current + offset; - - // slow pulse - icon.Transforms.Add(new TransformScale - { - StartValue = new Vector2(1.1f), - EndValue = Vector2.One, - StartTime = startTime, - EndTime = startTime + duration, - Easing = EasingTypes.Out, - LoopCount = -1, - LoopDelay = duration - }); + bouncingIcon.ScaleTo(1, transform_time, EasingTypes.OutElastic); } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) @@ -239,5 +203,45 @@ namespace osu.Game.Graphics.UserInterface return base.OnClick(state); } + + private class BouncingIcon : BeatSyncedContainer + { + private const double beat_in_time = 60; + + private readonly TextAwesome icon; + + public FontAwesome Icon { set { icon.Icon = value; } } + + public BouncingIcon() + { + EarlyActivationMilliseconds = beat_in_time; + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + icon = new TextAwesome + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + TextSize = 25 + } + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + var beatLength = timingPoint.BeatLength; + + float amplitudeAdjust = Math.Min(1, 0.4f + amplitudes.Maximum); + + if (beatIndex < 0) return; + + icon.ScaleTo(1 - 0.1f * amplitudeAdjust, beat_in_time, EasingTypes.Out); + using (icon.BeginDelayedSequence(beat_in_time)) + icon.ScaleTo(1, beatLength * 2, EasingTypes.OutQuint); + } + } } } \ No newline at end of file diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs index f2ae47354e..a758d5fdef 100644 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs +++ b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs @@ -74,7 +74,7 @@ namespace osu.Game.Graphics.UserInterface.Volume return; } - volumeMeterMaster.TriggerWheel(state); + volumeMeterMaster.TriggerOnWheel(state); } [BackgroundDependencyLoader] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 886ff4f8d1..2c952ee514 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -255,6 +255,9 @@ namespace osu.Game settings.ToggleVisibility(); return true; case Key.D: + if (state.Keyboard.ShiftPressed || state.Keyboard.AltPressed) + return false; + direct.ToggleVisibility(); return true; } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 686a1d513a..dccbc18f30 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -135,17 +135,22 @@ namespace osu.Game.Overlays channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel; } + private double startDragChatHeight; + protected override bool OnDragStart(InputState state) { - if (channelTabs.Hovering) - return true; + if (!channelTabs.Hovering) + return base.OnDragStart(state); - return base.OnDragStart(state); + startDragChatHeight = chatHeight.Value; + return true; } protected override bool OnDrag(InputState state) { - chatHeight.Value = Height - state.Mouse.Delta.Y / Parent.DrawSize.Y; + Trace.Assert(state.Mouse.PositionMouseDown != null); + + chatHeight.Value = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y; return base.OnDrag(state); } @@ -165,7 +170,7 @@ namespace osu.Game.Overlays protected override bool OnFocus(InputState state) { //this is necessary as inputTextBox is masked away and therefore can't get focus :( - inputTextBox.TriggerFocus(); + InputManager.ChangeFocus(inputTextBox); return false; } diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 47674de817..42819f7f87 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -71,7 +71,7 @@ namespace osu.Game.Overlays.Dialog private void pressButtonAtIndex(int index) { if (index < Buttons.Count()) - Buttons.Skip(index).First().TriggerClick(); + Buttons.Skip(index).First().TriggerOnClick(); } protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Dialog if (args.Key == Key.Enter) { - Buttons.OfType().FirstOrDefault()?.TriggerClick(); + Buttons.OfType().FirstOrDefault()?.TriggerOnClick(); return true; } diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 0930c825b6..b1bc7e0c04 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -189,7 +189,7 @@ namespace osu.Game.Overlays protected override bool OnFocus(InputState state) { - filter.Search.TriggerFocus(); + InputManager.ChangeFocus(filter.Search); return false; } diff --git a/osu.Game/Overlays/LoginOverlay.cs b/osu.Game/Overlays/LoginOverlay.cs index e555600028..c3f41270ce 100644 --- a/osu.Game/Overlays/LoginOverlay.cs +++ b/osu.Game/Overlays/LoginOverlay.cs @@ -66,7 +66,7 @@ namespace osu.Game.Overlays settingsSection.Bounding = true; FadeIn(transition_time, EasingTypes.OutQuint); - settingsSection.TriggerFocus(); + InputManager.ChangeFocus(settingsSection); } protected override void PopOut() diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 5e433aa414..82596252b3 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; using osu.Framework.Extensions; +using osu.Framework.Input; namespace osu.Game.Overlays.Music { @@ -35,10 +36,12 @@ namespace osu.Game.Overlays.Music private readonly Bindable beatmapBacking = new Bindable(); public IEnumerable BeatmapSets; + private InputManager inputManager; [BackgroundDependencyLoader] - private void load(OsuGameBase game, BeatmapDatabase beatmaps, OsuColour colours) + private void load(OsuGameBase game, BeatmapDatabase beatmaps, OsuColour colours, UserInputManager inputManager) { + this.inputManager = inputManager; this.beatmaps = beatmaps; trackManager = game.Audio.Track; @@ -100,7 +103,7 @@ namespace osu.Game.Overlays.Music protected override void PopIn() { filter.Search.HoldFocus = true; - Schedule(() => filter.Search.TriggerFocus()); + Schedule(() => inputManager.ChangeFocus(filter.Search)); ResizeTo(new Vector2(1, playlist_height), transition_duration, EasingTypes.OutQuint); FadeIn(transition_duration, EasingTypes.OutQuint); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 4faa339bed..f1be55801f 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -266,24 +266,27 @@ namespace osu.Game.Overlays { progressBar.IsEnabled = beatmap != null; - bool audioEquals = beatmapBacking.Value?.BeatmapInfo?.AudioEquals(current?.BeatmapInfo) ?? false; + TransformDirection direction = TransformDirection.None; - TransformDirection direction; - - if (audioEquals) - direction = TransformDirection.None; - else if (queuedDirection.HasValue) + if (current != null) { - direction = queuedDirection.Value; - queuedDirection = null; - } - else - { - //figure out the best direction based on order in playlist. - var last = current == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count(); - var next = beatmapBacking.Value == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmapBacking.Value.BeatmapSetInfo.ID).Count(); + bool audioEquals = beatmapBacking.Value?.BeatmapInfo?.AudioEquals(current.BeatmapInfo) ?? false; - direction = last > next ? TransformDirection.Prev : TransformDirection.Next; + if (audioEquals) + direction = TransformDirection.None; + else if (queuedDirection.HasValue) + { + direction = queuedDirection.Value; + queuedDirection = null; + } + else + { + //figure out the best direction based on order in playlist. + var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count(); + var next = beatmapBacking.Value == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmapBacking.Value.BeatmapSetInfo.ID).Count(); + + direction = last > next ? TransformDirection.Prev : TransformDirection.Next; + } } current = beatmapBacking.Value; diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 561f81d6c3..00ca50927e 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -51,9 +51,12 @@ namespace osu.Game.Overlays.Settings.Sections.General Spacing = new Vector2(0f, 5f); } + private InputManager inputManager; + [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, APIAccess api) + private void load(OsuColour colours, APIAccess api, UserInputManager inputManager) { + this.inputManager = inputManager; this.colours = colours; api?.Register(this); } @@ -160,12 +163,12 @@ namespace osu.Game.Overlays.Settings.Sections.General break; } - form?.TriggerFocus(); + if (form != null) inputManager.ChangeFocus(form); } protected override bool OnFocus(InputState state) { - form?.TriggerFocus(); + if (form != null) inputManager.ChangeFocus(form); return base.OnFocus(state); } @@ -174,6 +177,7 @@ namespace osu.Game.Overlays.Settings.Sections.General private TextBox username; private TextBox password; private APIAccess api; + private InputManager inputManager; private void performLogin() { @@ -182,8 +186,9 @@ namespace osu.Game.Overlays.Settings.Sections.General } [BackgroundDependencyLoader(permitNulls: true)] - private void load(APIAccess api, OsuConfigManager config) + private void load(APIAccess api, OsuConfigManager config, UserInputManager inputManager) { + this.inputManager = inputManager; this.api = api; Direction = FillDirection.Vertical; Spacing = new Vector2(0, 5); @@ -232,14 +237,7 @@ namespace osu.Game.Overlays.Settings.Sections.General protected override bool OnFocus(InputState state) { - Schedule(() => - { - if (string.IsNullOrEmpty(username.Text)) - username.TriggerFocus(); - else - password.TriggerFocus(); - }); - + Schedule(() => { inputManager.ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); }); return base.OnFocus(state); } } diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 943545e858..474631fd1e 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -134,12 +134,13 @@ namespace osu.Game.Overlays FadeTo(0, TRANSITION_LENGTH / 2); searchTextBox.HoldFocus = false; - searchTextBox.TriggerFocusLost(); + if (searchTextBox.HasFocus) + InputManager.ChangeFocus(null); } protected override bool OnFocus(InputState state) { - searchTextBox.TriggerFocus(state); + InputManager.ChangeFocus(searchTextBox); return false; } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index c5dbc27fd3..fcdcf672d5 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -15,28 +15,51 @@ using System.Linq; namespace osu.Game.Rulesets.Objects.Drawables { - public abstract class DrawableHitObject : Container - where TObject : HitObject - where TJudgement : Judgement + public abstract class DrawableHitObject : Container { - public event Action> OnJudgement; - - public TObject HitObject; + public readonly HitObject HitObject; /// /// The colour used for various elements of this DrawableHitObject. /// public virtual Color4 AccentColour { get; set; } + protected DrawableHitObject(HitObject hitObject) + { + HitObject = hitObject; + } + } + + public abstract class DrawableHitObject : DrawableHitObject + where TObject : HitObject + { + public new readonly TObject HitObject; + + protected DrawableHitObject(TObject hitObject) + : base(hitObject) + { + HitObject = hitObject; + } + } + + public abstract class DrawableHitObject : DrawableHitObject + where TObject : HitObject + where TJudgement : Judgement + { + public event Action> OnJudgement; + public override bool HandleInput => Interactive; public bool Interactive = true; public TJudgement Judgement; - protected abstract TJudgement CreateJudgement(); + protected List Samples = new List(); - protected abstract void UpdateState(ArmedState state); + protected DrawableHitObject(TObject hitObject) + : base(hitObject) + { + } private ArmedState state; public ArmedState State @@ -59,8 +82,6 @@ namespace osu.Game.Rulesets.Objects.Drawables } } - protected List Samples = new List(); - protected void PlaySamples() { Samples.ForEach(s => s?.Play()); @@ -79,11 +100,6 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public bool Judged => (Judgement?.Result ?? HitResult.None) != HitResult.None && (NestedHitObjects?.All(h => h.Judged) ?? true); - protected DrawableHitObject(TObject hitObject) - { - HitObject = hitObject; - } - /// /// Process a hit of this hitobject. Carries out judgement. /// @@ -176,5 +192,8 @@ namespace osu.Game.Rulesets.Objects.Drawables h.OnJudgement += d => OnJudgement?.Invoke(d); nestedHitObjects.Add(h); } + + protected abstract TJudgement CreateJudgement(); + protected abstract void UpdateState(ArmedState state); } } diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 5592681cab..b282965db8 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Objects /// /// The time at which the HitObject starts. /// - public double StartTime; + public virtual double StartTime { get; set; } /// /// The samples to be played when this hit object is hit. diff --git a/osu.Game/Rulesets/Objects/HitObjectStartTimeComparer.cs b/osu.Game/Rulesets/Objects/HitObjectStartTimeComparer.cs new file mode 100644 index 0000000000..b089856dcb --- /dev/null +++ b/osu.Game/Rulesets/Objects/HitObjectStartTimeComparer.cs @@ -0,0 +1,55 @@ +// 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.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Objects +{ + /// + /// Compares two hit objects by their start time, falling back to creation order if their start time is equal. + /// + public class HitObjectStartTimeComparer : Drawable.CreationOrderDepthComparer + { + public override int Compare(Drawable x, Drawable y) + { + var hitObjectX = x as DrawableHitObject; + var hitObjectY = y as DrawableHitObject; + + // If either of the two drawables are not hit objects, fall back to the base comparer + if (hitObjectX?.HitObject == null || hitObjectY?.HitObject == null) + return base.Compare(x, y); + + // Compare by start time + int i = hitObjectX.HitObject.StartTime.CompareTo(hitObjectY.HitObject.StartTime); + if (i != 0) + return i; + + return base.Compare(x, y); + } + } + + /// + /// Compares two hit objects by their start time, falling back to creation order if their start time is equal. + /// This will compare the two hit objects in reverse order. + /// + public class HitObjectReverseStartTimeComparer : Drawable.ReverseCreationOrderDepthComparer + { + public override int Compare(Drawable x, Drawable y) + { + var hitObjectX = x as DrawableHitObject; + var hitObjectY = y as DrawableHitObject; + + // If either of the two drawables are not hit objects, fall back to the base comparer + if (hitObjectX?.HitObject == null || hitObjectY?.HitObject == null) + return base.Compare(x, y); + + // Compare by start time + int i = hitObjectY.HitObject.StartTime.CompareTo(hitObjectX.HitObject.StartTime); + if (i != 0) + return i; + + return base.Compare(x, y); + } + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 7580404e81..8c2aead5ff 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -6,20 +6,30 @@ using System; using System.Collections.Generic; using OpenTK; using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Database; namespace osu.Game.Rulesets.Objects.Legacy { internal abstract class ConvertSlider : HitObject, IHasCurve { + /// + /// Scoring distance with a speed-adjusted beat length of 1 second. + /// + private const float base_scoring_distance = 100; + public List ControlPoints { get; set; } public CurveType CurveType { get; set; } + public double Distance { get; set; } public List RepeatSamples { get; set; } public int RepeatCount { get; set; } = 1; - public double EndTime { get; set; } - public double Duration { get; set; } + public double EndTime => StartTime + RepeatCount * Distance / Velocity; + public double Duration => EndTime - StartTime; + + public double Velocity = 1; public Vector2 PositionAt(double progress) { @@ -35,5 +45,17 @@ namespace osu.Game.Rulesets.Objects.Legacy { throw new NotImplementedException(); } + + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaults(controlPointInfo, difficulty); + + TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime); + + double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier / difficultyPoint.SpeedMultiplier; + + Velocity = scoringDistance / timingPoint.BeatLength; + } } } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 72db445052..52039a8417 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Menu switch (args.Key) { case Key.Space: - osuLogo.TriggerClick(state); + osuLogo.TriggerOnClick(state); return true; case Key.Escape: switch (State) @@ -144,7 +144,7 @@ namespace osu.Game.Screens.Menu State = MenuState.Initial; return true; case MenuState.Play: - backButton.TriggerClick(); + backButton.TriggerOnClick(); return true; } @@ -178,10 +178,10 @@ namespace osu.Game.Screens.Menu State = MenuState.TopLevel; return; case MenuState.TopLevel: - buttonsTopLevel.First().TriggerClick(); + buttonsTopLevel.First().TriggerOnClick(); return; case MenuState.Play: - buttonsPlay.First().TriggerClick(); + buttonsPlay.First().TriggerOnClick(); return; } } diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index faff687ddb..3e31da2348 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Play { if (!args.Repeat && args.Key == Key.Escape) { - Buttons.Children.Last().TriggerClick(); + Buttons.Children.Last().TriggerOnClick(); return true; } diff --git a/osu.Game/Screens/Play/KeyCounterCollection.cs b/osu.Game/Screens/Play/KeyCounterCollection.cs index 6e1c8a34e5..25cbfc14b7 100644 --- a/osu.Game/Screens/Play/KeyCounterCollection.cs +++ b/osu.Game/Screens/Play/KeyCounterCollection.cs @@ -131,13 +131,13 @@ namespace osu.Game.Screens.Play public override bool HandleInput => true; - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => target.Children.Any(c => c.TriggerKeyDown(state, args)); + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => target.Children.Any(c => c.TriggerOnKeyDown(state, args)); - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => target.Children.Any(c => c.TriggerKeyUp(state, args)); + protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => target.Children.Any(c => c.TriggerOnKeyUp(state, args)); - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => target.Children.Any(c => c.TriggerMouseDown(state, args)); + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => target.Children.Any(c => c.TriggerOnMouseDown(state, args)); - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => target.Children.Any(c => c.TriggerMouseUp(state, args)); + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => target.Children.Any(c => c.TriggerOnMouseUp(state, args)); } } } diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs index 1de62048b7..f052bafd63 100644 --- a/osu.Game/Screens/Play/PauseContainer.cs +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -136,7 +136,7 @@ namespace osu.Game.Screens.Play { if (!args.Repeat && args.Key == Key.Escape) { - Buttons.Children.First().TriggerClick(); + Buttons.Children.First().TriggerOnClick(); return true; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a39e7dbab2..707d026e2b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -293,7 +293,7 @@ namespace osu.Game.Screens.Play protected override bool OnExiting(Screen next) { - if (HasFailed || !ValidForResume || pauseContainer.AllowExit || HitRenderer.HasReplayLoaded) + if (HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || HitRenderer?.HasReplayLoaded != false) { fadeOut(); return base.OnExiting(next); @@ -310,7 +310,7 @@ namespace osu.Game.Screens.Play HitRenderer?.FadeOut(fade_out_duration); Content.FadeOut(fade_out_duration); - hudOverlay.ScaleTo(0.7f, fade_out_duration * 3, EasingTypes.In); + hudOverlay?.ScaleTo(0.7f, fade_out_duration * 3, EasingTypes.In); Background?.FadeTo(1f, fade_out_duration); } diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index 86bbb26412..ee11fc0ca6 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -127,7 +127,7 @@ namespace osu.Game.Screens.Play switch (args.Key) { case Key.Space: - button.TriggerClick(); + button.TriggerOnClick(); return true; } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 7c7863acd1..94bc60a6ca 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -154,7 +154,8 @@ namespace osu.Game.Screens.Select public void Deactivate() { searchTextBox.HoldFocus = false; - searchTextBox.TriggerFocusLost(); + if (searchTextBox.HasFocus) + inputManager.ChangeFocus(searchTextBox); } public void Activate() @@ -164,9 +165,13 @@ namespace osu.Game.Screens.Select private readonly Bindable ruleset = new Bindable(); - [BackgroundDependencyLoader(permitNulls:true)] - private void load(OsuColour colours, OsuGame osu) + private InputManager inputManager; + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuColour colours, OsuGame osu, UserInputManager inputManager) { + this.inputManager = inputManager; + sortTabs.AccentColour = colours.GreenLight; if (osu != null) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4d1b1a1b11..0ec0624978 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -176,6 +176,7 @@ +