diff --git a/osu-framework b/osu-framework index 49b563e2cf..8f36ddab94 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 49b563e2cf170eb19006b98dd5b69c2398362d9e +Subproject commit 8f36ddab946ff538620081ede7719461d4732b79 diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 8e6391168b..f37282366a 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -45,7 +45,7 @@ namespace osu.Desktop { protected override string LocateBasePath() { - Func checkExists = p => Directory.Exists(Path.Combine(p, "Songs")); + bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); string stableInstallPath; diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 923356c610..090173f38d 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -31,7 +31,8 @@ namespace osu.Desktop.Overlays private OsuConfigManager config; private OsuGameBase game; - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; [BackgroundDependencyLoader] private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 8ea30a2899..7f56c3bbb1 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -58,6 +58,7 @@ namespace osu.Game.Rulesets.Catch.UI public override void Add(DrawableHitObject h) { h.Depth = (float)h.HitObject.StartTime; + h.OnJudgement += onJudgement; base.Add(h); @@ -65,6 +66,6 @@ namespace osu.Game.Rulesets.Catch.UI fruit.CheckPosition = CheckIfWeCanCatch; } - public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement); + private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement); } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index d15303af39..1d739c114e 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -321,7 +321,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy break; } - Func isDoubleSample = sample => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH; + bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH; bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0; canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 58f024870d..57a4888b2b 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables holdStartTime = null; // If the key has been released too early, the user should not receive full score for the release - if (!tail.AllJudged) + if (!tail.IsHit) hasBroken = true; return true; diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index eef57897de..d79a4d62c4 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -204,12 +204,13 @@ namespace osu.Game.Rulesets.Mania.UI public override void Add(DrawableHitObject hitObject) { hitObject.Depth = (float)hitObject.HitObject.StartTime; - hitObject.AccentColour = AccentColour; + hitObject.OnJudgement += onJudgement; + HitObjects.Add(hitObject); } - public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) { if (!judgement.IsHit) return; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 919518dbe8..7d3df6cda7 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -192,11 +192,8 @@ namespace osu.Game.Rulesets.Mania.UI } } - public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { - var maniaObject = (ManiaHitObject)judgedObject.HitObject; - columns[maniaObject.Column].OnJudgement(judgedObject, judgement); - judgements.Clear(); judgements.Add(new DrawableManiaJudgement(judgement) { @@ -224,7 +221,11 @@ namespace osu.Game.Rulesets.Mania.UI } } - public override void Add(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column).Add(h); + public override void Add(DrawableHitObject h) + { + h.OnJudgement += OnJudgement; + Columns.ElementAt(((ManiaHitObject)h.HitObject).Column).Add(h); + } public void Add(DrawableBarLine barline) => HitObjects.Add(barline); diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs index a3f0b79475..5f232b1889 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs @@ -1,15 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Graphics.Cursor; using osu.Game.Rulesets.Osu.UI; namespace osu.Game.Rulesets.Osu.Edit { public class OsuEditPlayfield : OsuPlayfield { - protected override CursorContainer CreateCursor() => null; - protected override bool ProxyApproachCircles => false; } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs index 96b9acbf78..56efc25fa5 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Graphics.Cursor; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; @@ -15,5 +16,7 @@ namespace osu.Game.Rulesets.Osu.Edit } protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); + + protected override CursorContainer CreateCursor() => null; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index f18d982bc0..28ff4b4cdf 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -11,7 +11,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public class DrawableRepeatPoint : DrawableOsuHitObject + public class DrawableRepeatPoint : DrawableOsuHitObject, ITrackSnaking { private readonly RepeatPoint repeatPoint; private readonly DrawableSlider drawableSlider; @@ -71,5 +71,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables break; } } + + public void UpdateSnakingPosition(Vector2 start, Vector2 end) => Position = repeatPoint.RepeatIndex % 2 == 1 ? end : start; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 6aa3268e5e..eb499b5da6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public readonly DrawableHitCircle InitialCircle; - private readonly List components = new List(); + private readonly List components = new List(); private readonly Container ticks; private readonly Container repeatPoints; @@ -101,6 +101,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }; repeatPoints.Add(drawableRepeatPoint); + components.Add(drawableRepeatPoint); AddNested(drawableRepeatPoint); } } @@ -126,7 +127,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!InitialCircle.Judgements.Any(j => j.IsHit)) InitialCircle.Position = slider.Curve.PositionAt(progress); - foreach (var c in components) c.UpdateProgress(progress, repeat); + foreach (var c in components.OfType()) c.UpdateProgress(progress, repeat); + foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); foreach (var t in ticks.Children) t.Tracking = Ball.Tracking; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs new file mode 100644 index 0000000000..b5fd87f60b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + /// + /// A component which tracks the current end snaking position of a slider. + /// + public interface ITrackSnaking + { + void UpdateSnakingPosition(Vector2 start, Vector2 end); + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs index 7c96a2a8cf..c44d7594ad 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs @@ -11,7 +11,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class SpinnerBackground : CircularContainer, IHasAccentColour { - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; protected Box Disc; diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 40ec57d434..2da285a434 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -61,6 +61,7 @@ namespace osu.Game.Rulesets.Osu.Objects public int RepeatCount { get; set; } = 1; private int stackHeight; + public override int StackHeight { get { return stackHeight; } @@ -130,6 +131,17 @@ namespace osu.Game.Rulesets.Osu.Objects var distanceProgress = d / length; var timeProgress = reversed ? 1 - distanceProgress : distanceProgress; + var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL) ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) + var sampleList = new List(); + + if (firstSample != null) + sampleList.Add(new SampleInfo + { + Bank = firstSample.Bank, + Volume = firstSample.Volume, + Name = @"slidertick", + }); + AddNested(new SliderTick { RepeatIndex = repeat, @@ -138,12 +150,7 @@ namespace osu.Game.Rulesets.Osu.Objects StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, - Samples = new List(Samples.Select(s => new SampleInfo - { - Bank = s.Bank, - Name = @"slidertick", - Volume = s.Volume - })) + Samples = sampleList }); } } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index 325f22f425..0aeb14514d 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -159,5 +159,17 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor return false; } + + protected override void PopIn() + { + ActiveCursor.FadeTo(1, 250, Easing.OutQuint); + ActiveCursor.ScaleTo(1, 400, Easing.OutQuint); + } + + protected override void PopOut() + { + ActiveCursor.FadeTo(0, 250, Easing.OutQuint); + ActiveCursor.ScaleTo(0.6f, 250, Easing.In); + } } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index e7d47da391..17521f8992 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -12,8 +12,6 @@ using osu.Game.Rulesets.UI; using System.Linq; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Osu.UI.Cursor; -using osu.Framework.Graphics.Cursor; namespace osu.Game.Rulesets.Osu.UI { @@ -23,8 +21,6 @@ namespace osu.Game.Rulesets.Osu.UI private readonly Container judgementLayer; private readonly ConnectionRenderer connectionLayer; - public override bool ProvidingUserCursor => true; - // Todo: This should not be a thing, but is currently required for the editor // https://github.com/ppy/osu-framework/issues/1283 protected virtual bool ProxyApproachCircles => true; @@ -70,19 +66,12 @@ namespace osu.Game.Rulesets.Osu.UI }); } - protected override void LoadComplete() - { - base.LoadComplete(); - - var cursor = CreateCursor(); - if (cursor != null) - AddInternal(cursor); - } - public override void Add(DrawableHitObject h) { h.Depth = (float)h.HitObject.StartTime; + h.OnJudgement += onJudgement; + var c = h as IDrawableHitObjectWithProxiedApproach; if (c != null && ProxyApproachCircles) approachCircles.Add(c.ProxiedLayer.CreateProxy()); @@ -97,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.UI .OrderBy(h => h.StartTime).OfType(); } - public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) { var osuJudgement = (OsuJudgement)judgement; var osuObject = (OsuHitObject)judgedObject.HitObject; @@ -113,7 +102,5 @@ namespace osu.Game.Rulesets.Osu.UI judgementLayer.Add(explosion); } - - protected virtual CursorContainer CreateCursor() => new GameplayCursor(); } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index 5c7413a71e..526348062f 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using OpenTK; using osu.Game.Beatmaps; @@ -10,6 +11,7 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Replays; @@ -49,5 +51,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay); protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f); + + protected override CursorContainer CreateCursor() => new GameplayCursor(); } } diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 7d6001359a..a59d4607df 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -71,6 +71,7 @@ + diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 2fa6c8ed95..29d464f614 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (timeOffset < 0) return; - int countHit = NestedHitObjects.Count(o => o.AllJudged); + int countHit = NestedHitObjects.Count(o => o.IsHit); if (countHit > HitObject.RequiredGoodHits) { diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs index 9500a1a747..fd396c201d 100644 --- a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs @@ -143,18 +143,18 @@ namespace osu.Game.Rulesets.Taiko.Tests var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; - rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult }); + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult }); if (RNG.Next(10) == 0) { - rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult }); - rulesetContainer.Playfield.OnJudgement(h, new TaikoStrongHitJudgement()); + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult }); + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoStrongHitJudgement()); } } private void addMissJudgement() { - rulesetContainer.Playfield.OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss }); + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss }); } private void addBarLine(bool major, double delay = scroll_time) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 3c5093d82f..49c87f7480 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -209,6 +209,8 @@ namespace osu.Game.Rulesets.Taiko.UI { h.Depth = (float)h.HitObject.StartTime; + h.OnJudgement += OnJudgement; + base.Add(h); var barline = h as DrawableBarLine; @@ -221,7 +223,7 @@ namespace osu.Game.Rulesets.Taiko.UI swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy()); } - public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null) { diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 322f0ec608..ece1f626ec 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -117,8 +117,8 @@ namespace osu.Game.Tests.Beatmaps.IO //ensure we were stored to beatmap database backing... Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1)."); - Func> queryBeatmaps = () => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0); - Func> queryBeatmapSets = () => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526); + IEnumerable queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0); + IEnumerable queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526); //if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. waitForOrAssert(() => queryBeatmaps().Count() == 12, diff --git a/osu.Game.Tests/Visual/TestCaseAutoplay.cs b/osu.Game.Tests/Visual/TestCaseAutoplay.cs new file mode 100644 index 0000000000..d954d0543c --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseAutoplay.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + [Description("Player instantiated with an autoplay mod.")] + public class TestCaseAutoplay : TestCasePlayer + { + protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) + { + beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + return base.CreatePlayer(beatmap, ruleset); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseCursors.cs b/osu.Game.Tests/Visual/TestCaseCursors.cs new file mode 100644 index 0000000000..363f6b53f0 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseCursors.cs @@ -0,0 +1,274 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Framework.Testing.Input; +using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseCursors : OsuTestCase + { + private readonly ManualInputManager inputManager; + private readonly CursorOverrideContainer cursorOverrideContainer; + private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; + + public TestCaseCursors() + { + Child = inputManager = new ManualInputManager + { + Child = cursorOverrideContainer = new CursorOverrideContainer + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + // Middle user + cursorBoxes[0] = new CustomCursorBox(Color4.Green) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), + }, + // Top-left user + cursorBoxes[1] = new CustomCursorBox(Color4.Blue) + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Bottom-right user + cursorBoxes[2] = new CustomCursorBox(Color4.Red) + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Bottom-left local + cursorBoxes[3] = new CustomCursorBox(Color4.Magenta, false) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Top-right local + cursorBoxes[4] = new CustomCursorBox(Color4.Cyan, false) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Left-local + cursorBoxes[5] = new CustomCursorBox(Color4.Yellow, false) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.2f, 1), + }, + } + } + }; + + returnUserInput(); + + AddToggleStep("Smooth transitions", b => cursorBoxes.ForEach(box => box.SmoothTransition = b)); + + testUserCursor(); + testLocalCursor(); + testUserCursorOverride(); + testMultipleLocalCursors(); + returnUserInput(); + } + + /// + /// Returns input back to the user. + /// + private void returnUserInput() + { + AddStep("Return user input", () => inputManager.UseParentState = true); + } + + /// + /// -- Green Box -- + /// Tests whether hovering in and out of a drawable that provides the user cursor (green) + /// results in the correct visibility state for that cursor. + /// + private void testUserCursor() + { + AddStep("Move to green area", () => inputManager.MoveMouseTo(cursorBoxes[0])); + AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); + } + + /// + /// -- Purple Box -- + /// Tests whether hovering in and out of a drawable that provides a local cursor (purple) + /// results in the correct visibility and state for that cursor. + /// + private void testLocalCursor() + { + AddStep("Move to purple area", () => inputManager.MoveMouseTo(cursorBoxes[3])); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); + AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); + AddAssert("Check global cursor at mouse", () => checkAtMouse(cursorOverrideContainer.Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); + } + + /// + /// -- Blue-Green Box Boundary -- + /// Tests whether overriding a user cursor (green) with another user cursor (blue) + /// results in the correct visibility and states for the cursors. + /// + private void testUserCursorOverride() + { + AddStep("Move to blue-green boundary", () => inputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); + AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check blue cursor not visible", () => !checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check green cursor not visible", () => !checkVisible(cursorBoxes[0].Cursor)); + } + + /// + /// -- Yellow-Purple Box Boundary -- + /// Tests whether multiple local cursors (purple + yellow) may be visible and at the mouse position at the same time. + /// + private void testMultipleLocalCursors() + { + AddStep("Move to yellow-purple boundary", () => inputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + } + + /// + /// -- Yellow-Blue Box Boundary -- + /// Tests whether a local cursor (yellow) may be displayed along with a user cursor override (blue). + /// + private void testUserOverrideWithLocal() + { + AddStep("Move to yellow-blue boundary", () => inputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); + AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check blue cursor invisible", () => !checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + } + + /// + /// Moves the cursor to a point not covered by any cursor containers. + /// + private void moveOut() + => inputManager.MoveMouseTo(new Vector2(inputManager.ScreenSpaceDrawQuad.Centre.X, inputManager.ScreenSpaceDrawQuad.TopLeft.Y)); + + /// + /// Checks if a cursor is visible. + /// + /// The cursor to check. + private bool checkVisible(CursorContainer cursorContainer) => cursorContainer.State == Visibility.Visible; + + /// + /// Checks if a cursor is at the current inputmanager screen position. + /// + /// The cursor to check. + private bool checkAtMouse(CursorContainer cursorContainer) + => Precision.AlmostEquals(inputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); + + private class CustomCursorBox : Container, IProvideCursor + { + public bool SmoothTransition; + + public CursorContainer Cursor { get; } + public bool ProvidingUserCursor { get; } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => base.ReceiveMouseInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor; + + private readonly Box background; + + public CustomCursorBox(Color4 cursorColour, bool providesUserCursor = true) + { + ProvidingUserCursor = providesUserCursor; + + Colour = cursorColour; + Masking = true; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.1f + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = providesUserCursor ? "User cursor" : "Local cursor" + }, + Cursor = new TestCursorContainer + { + State = providesUserCursor ? Visibility.Hidden : Visibility.Visible, + } + }; + } + + protected override bool OnHover(InputState state) + { + background.FadeTo(0.4f, 250, Easing.OutQuint); + return false; + } + + protected override void OnHoverLost(InputState state) + { + background.FadeTo(0.1f, 250); + base.OnHoverLost(state); + } + } + + private class TestCursorContainer : CursorContainer + { + protected override Drawable CreateCursor() => new TestCursor(); + + private class TestCursor : CircularContainer + { + public TestCursor() + { + Origin = Anchor.Centre; + + Size = new Vector2(50); + Masking = true; + + Blending = BlendingMode.Additive; + Alpha = 0.5f; + + Child = new Box { RelativeSizeAxes = Axes.Both }; + } + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index f16cf73039..809de2b8db 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual // this is by no means clean. should be replacing inside of OsuGameBase somehow. var context = new OsuDbContext(); - Func contextFactory = () => context; + OsuDbContext contextFactory() => context; dependencies.Cache(rulesets = new RulesetStore(contextFactory)); dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null) diff --git a/osu.Game.Tests/Visual/TestCaseReplay.cs b/osu.Game.Tests/Visual/TestCaseReplay.cs index 451c4e013f..237687458d 100644 --- a/osu.Game.Tests/Visual/TestCaseReplay.cs +++ b/osu.Game.Tests/Visual/TestCaseReplay.cs @@ -1,19 +1,35 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; namespace osu.Game.Tests.Visual { + [Description("Player instantiated with a replay.")] public class TestCaseReplay : TestCasePlayer { protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) { + // We create a dummy RulesetContainer just to get the replay - we don't want to use mods here + // to simulate setting a replay rather than having the replay already set for us beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); - return base.CreatePlayer(beatmap, ruleset); + var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(beatmap, false); + + // We have the replay + var replay = dummyRulesetContainer.Replay; + + // Reset the mods + beatmap.Mods.Value = beatmap.Mods.Value.Where(m => !(m is ModAutoplay)); + + return new ReplayPlayer(replay) + { + InitialBeatmap = beatmap + }; } } } diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 2eb79f6b35..059adc6d55 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -104,6 +104,7 @@ + @@ -136,6 +137,7 @@ + diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 02564489ad..df71c5c0d0 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -156,6 +156,7 @@ namespace osu.Game.Beatmaps public IQueryable Beatmaps => GetContext().BeatmapInfo .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) + .Include(b => b.BeatmapSet).ThenInclude(s => s.Files).ThenInclude(f => f.FileInfo) .Include(b => b.Metadata) .Include(b => b.Ruleset) .Include(b => b.BaseDifficulty); diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 5e23389b89..6f9d83473f 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -30,7 +30,9 @@ namespace osu.Game.Graphics.Backgrounds /// private const float edge_smoothness = 1; - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; + public Color4 ColourLight = Color4.White; public Color4 ColourDark = Color4.Black; diff --git a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs new file mode 100644 index 0000000000..0fae4579fa --- /dev/null +++ b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Input; + +namespace osu.Game.Graphics.Cursor +{ + /// + /// A container which provides a which can be overridden by hovered s. + /// + public class CursorOverrideContainer : Container, IProvideCursor + { + protected override Container Content => content; + private readonly Container content; + + /// + /// Whether any cursors can be displayed. + /// + public bool CanShowCursor = true; + + public CursorContainer Cursor { get; } + public bool ProvidingUserCursor => true; + + public CursorOverrideContainer() + { + AddRangeInternal(new Drawable[] + { + Cursor = new MenuCursor { State = Visibility.Hidden }, + content = new Container { RelativeSizeAxes = Axes.Both } + }); + } + + private InputManager inputManager; + + protected override void LoadComplete() + { + base.LoadComplete(); + inputManager = GetContainingInputManager(); + } + + private IProvideCursor currentTarget; + protected override void Update() + { + base.Update(); + + if (!CanShowCursor) + { + currentTarget?.Cursor?.Hide(); + return; + } + + var newTarget = inputManager.HoveredDrawables.OfType().FirstOrDefault(t => t.ProvidingUserCursor) ?? this; + + if (currentTarget == newTarget) + return; + + currentTarget?.Cursor?.Hide(); + newTarget.Cursor?.Show(); + + currentTarget = newTarget; + } + } +} diff --git a/osu.Game/Graphics/Cursor/IProvideCursor.cs b/osu.Game/Graphics/Cursor/IProvideCursor.cs new file mode 100644 index 0000000000..91b44234fb --- /dev/null +++ b/osu.Game/Graphics/Cursor/IProvideCursor.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; + +namespace osu.Game.Graphics.Cursor +{ + /// + /// Interface for s that display cursors which can replace the user's cursor. + /// + public interface IProvideCursor : IDrawable + { + /// + /// The cursor provided by this . + /// May be null if no cursor should be visible. + /// + CursorContainer Cursor { get; } + + /// + /// Whether should be displayed as the singular user cursor. This will temporarily hide any other user cursor. + /// This value is checked every frame and may be used to control whether multiple cursors are displayed (e.g. watching replays). + /// + bool ProvidingUserCursor { get; } + } +} diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 39af99f02e..0de6279b2e 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -99,8 +99,8 @@ namespace osu.Game.Graphics.Cursor protected override void PopOut() { - ActiveCursor.FadeTo(0, 900, Easing.OutQuint); - ActiveCursor.ScaleTo(0, 500, Easing.In); + ActiveCursor.FadeTo(0, 250, Easing.OutQuint); + ActiveCursor.ScaleTo(0.6f, 250, Easing.In); } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs index 99afc02d5d..5ee0aba9cf 100644 --- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs @@ -42,7 +42,9 @@ namespace osu.Game.Graphics.UserInterface //don't allow clicking between transitions and don't make the chevron clickable public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceiveMouseInputAt(screenSpacePos); - public override bool HandleInput => State == Visibility.Visible; + + public override bool HandleKeyboardInput => State == Visibility.Visible; + public override bool HandleMouseInput => State == Visibility.Visible; private Visibility state; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5604ef8f3c..b48e25f1fe 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -157,6 +157,11 @@ namespace osu.Game { base.LoadComplete(); + // The next time this is updated is in UpdateAfterChildren, which occurs too late and results + // in the cursor being shown for a few frames during the intro. + // This prevents the cursor from showing until we have a screen with CursorVisible = true + CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; + // hook up notifications to components. BeatmapManager.PostNotification = n => notifications?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; @@ -297,8 +302,6 @@ namespace osu.Game else Toolbar.State = Visibility.Visible; }; - - Cursor.State = Visibility.Hidden; } private void forwardLoggedErrorsToNotifications() @@ -447,7 +450,7 @@ namespace osu.Game mainContent.Padding = new MarginPadding { Top = ToolbarOffset }; - Cursor.State = currentScreen?.HasLocalCursorDisplayed == false ? Visibility.Visible : Visibility.Hidden; + CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; } private void screenAdded(Screen newScreen) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 1982fa0db5..ef02f1a7ec 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -44,6 +44,8 @@ namespace osu.Game protected KeyBindingStore KeyBindingStore; + protected CursorOverrideContainer CursorOverrideContainer; + protected override string MainResourceFile => @"osu.Game.Resources.dll"; public APIAccess API; @@ -52,8 +54,6 @@ namespace osu.Game protected override Container Content => content; - protected MenuCursor Cursor; - public Bindable Beatmap { get; private set; } private Bindable fpsDisplayVisible; @@ -211,21 +211,14 @@ namespace osu.Game GlobalKeyBindingInputManager globalBinding; - base.Content.Add(new DrawSizePreservingFillContainer + CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; + CursorOverrideContainer.Child = globalBinding = new GlobalKeyBindingInputManager(this) { - Children = new Drawable[] - { - Cursor = new MenuCursor(), - globalBinding = new GlobalKeyBindingInputManager(this) - { - RelativeSizeAxes = Axes.Both, - Child = content = new OsuTooltipContainer(Cursor) - { - RelativeSizeAxes = Axes.Both, - } - } - } - }); + RelativeSizeAxes = Axes.Both, + Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both } + }; + + base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer }); KeyBindingStore.Register(globalBinding); dependencies.Cache(globalBinding); diff --git a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/PreviewButton.cs index 11d1769f1e..4f5a6ba718 100644 --- a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/PreviewButton.cs @@ -82,7 +82,8 @@ namespace osu.Game.Overlays.BeatmapSet if (Playing.Value && preview != null) { - progress.Width = (float)(preview.CurrentTime / preview.Length); + // prevent negative (potential infinite) width if a track without length was loaded + progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f; } } diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 9ecddb01ba..c00fb9b122 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -9,6 +9,7 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; +using osu.Framework.IO.Stores; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -39,6 +40,8 @@ namespace osu.Game.Overlays.Direct private readonly SpriteIcon icon; private readonly LoadingAnimation loadingAnimation; + private readonly BindableDouble muteBindable = new BindableDouble(); + private const float transition_duration = 500; private bool loading @@ -83,9 +86,10 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader] - private void load(OsuColour colour) + private void load(OsuColour colour, AudioManager audio) { hoverColour = colour.Yellow; + this.audio = audio; } protected override bool OnClick(InputState state) @@ -128,21 +132,30 @@ namespace osu.Game.Overlays.Direct return; } - Preview.Seek(0); - Preview.Start(); + Preview.Restart(); + + audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); } else { + audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); + Preview?.Stop(); loading = false; } } private TrackLoader trackLoader; + private AudioManager audio; private void beginAudioLoad() { - if (trackLoader != null) return; + if (trackLoader != null) + { + Preview = trackLoader.Preview; + Playing.TriggerChange(); + return; + } loading = true; @@ -164,6 +177,7 @@ namespace osu.Game.Overlays.Direct private readonly string preview; public Track Preview; + private TrackManager trackManager; public TrackLoader(string preview) { @@ -171,10 +185,22 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load(AudioManager audio, FrameworkConfigManager config) { + // create a local trackManager to bypass the mute we are applying above. + audio.AddItem(trackManager = new TrackManager(new OnlineStore())); + + // add back the user's music volume setting (since we are no longer in the global TrackManager's hierarchy). + config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); + if (!string.IsNullOrEmpty(preview)) - Preview = audio.Track.Get(preview); + Preview = trackManager.Get(preview); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + trackManager?.Dispose(); } } } diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 979a357d1e..d5acb9dc86 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -313,6 +313,14 @@ namespace osu.Game.Overlays api.Queue(getSetsRequest); } + protected override void PopOut() + { + base.PopOut(); + + if (playing != null) + playing.PreviewPlaying.Value = false; + } + private int distinctCount(List list) => list.Distinct().ToArray().Length; public class ResultCounts diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index 6a1bd8e182..65803b477b 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -22,7 +22,8 @@ namespace osu.Game.Overlays { private readonly Container box; - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; private readonly SpriteText textLine1; private readonly SpriteText textLine2; @@ -121,7 +122,7 @@ namespace osu.Game.Overlays trackSetting(frameworkConfig.GetBindable(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", string.IsNullOrEmpty(v) ? "Default" : v, v)); trackSetting(frameworkConfig.GetBindable(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10")); - Action displayResolution = delegate { display(null, "Screen Resolution", frameworkConfig.Get(FrameworkSetting.Width) + "x" + frameworkConfig.Get(FrameworkSetting.Height)); }; + void displayResolution() => display(null, "Screen Resolution", frameworkConfig.Get(FrameworkSetting.Width) + "x" + frameworkConfig.Get(FrameworkSetting.Height)); trackSetting(frameworkConfig.GetBindable(FrameworkSetting.Width), v => displayResolution()); trackSetting(frameworkConfig.GetBindable(FrameworkSetting.Height), v => displayResolution()); diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 6a9f4b84b0..924b5d6c9d 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -319,11 +319,11 @@ namespace osu.Game.Overlays.Profile colourBar.Show(); } - Action boldItalic = t => + void boldItalic(SpriteText t) { t.Font = @"Exo2.0-BoldItalic"; t.Alpha = 1; - }; + } if (user.Age != null) { @@ -474,7 +474,8 @@ namespace osu.Game.Overlays.Profile private class LinkFlowContainer : OsuTextFlowContainer { - public override bool HandleInput => true; + public override bool HandleKeyboardInput => true; + public override bool HandleMouseInput => true; public LinkFlowContainer(Action defaultCreationParameters = null) : base(defaultCreationParameters) { @@ -488,7 +489,8 @@ namespace osu.Game.Overlays.Profile { private readonly OsuHoverContainer content; - public override bool HandleInput => content.Action != null; + public override bool HandleKeyboardInput => content.Action != null; + public override bool HandleMouseInput => content.Action != null; protected override Container Content => content ?? (Container)this; diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index 0fa6fa23a2..6d58c78c37 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -87,7 +87,8 @@ namespace osu.Game.Overlays.Toolbar ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(); } - public override bool HandleInput => !ruleset.Disabled; + public override bool HandleKeyboardInput => !ruleset.Disabled; + public override bool HandleMouseInput => !ruleset.Disabled; private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs index c840cb0c38..fcb2f8f57f 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs @@ -165,7 +165,8 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection } private bool isActive = true; - public override bool HandleInput => isActive; + public override bool HandleKeyboardInput => isActive; + public override bool HandleMouseInput => isActive; public override void Hide() { diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 0f6642ae0e..c1bf55b214 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -40,7 +40,8 @@ namespace osu.Game.Rulesets.Judgements Anchor = Anchor.Centre, Text = judgement.Result.GetDescription().ToUpper(), Font = @"Venera", - TextSize = 16 + Scale = new Vector2(0.85f, 1), + TextSize = 12 } }; } diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index 97b2fe7d1e..c4c0f38faf 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods { const float ratio = 1.4f; difficulty.CircleSize *= 1.3f; // CS uses a custom 1.3 ratio. - difficulty.ApproachRate *= ratio; + difficulty.ApproachRate = Math.Min(difficulty.ApproachRate * ratio, 10.0f); difficulty.DrainRate *= ratio; difficulty.OverallDifficulty *= ratio; } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index af14c43a3f..8680ff4e83 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -68,7 +68,8 @@ namespace osu.Game.Rulesets.Objects.Drawables private bool judgementFinalized => judgements.LastOrDefault()?.Final == true; public bool Interactive = true; - public override bool HandleInput => Interactive; + public override bool HandleKeyboardInput => Interactive; + public override bool HandleMouseInput => Interactive; public override bool RemoveWhenNotAlive => false; public override bool RemoveCompletedTransforms => false; @@ -167,6 +168,11 @@ namespace osu.Game.Rulesets.Objects.Drawables { if (nestedHitObjects == null) nestedHitObjects = new List(); + + h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j); + h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j); + h.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j); + nestedHitObjects.Add(h); } diff --git a/osu.Game/Rulesets/Scoring/ScoreStore.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs index fe366f52a5..d21ca79736 100644 --- a/osu.Game/Rulesets/Scoring/ScoreStore.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -10,6 +10,7 @@ using osu.Game.Database; using osu.Game.IO.Legacy; using osu.Game.IPC; using osu.Game.Rulesets.Replays; +using osu.Game.Users; using SharpCompress.Compressors.LZMA; namespace osu.Game.Rulesets.Scoring @@ -54,7 +55,7 @@ namespace osu.Game.Rulesets.Scoring var beatmapHash = sr.ReadString(); score.Beatmap = beatmaps.QueryBeatmap(b => b.MD5Hash == beatmapHash); /* score.PlayerName = */ - sr.ReadString(); + score.User = new User { Username = sr.ReadString() }; /* var localScoreChecksum = */ sr.ReadString(); /* score.Count300 = */ @@ -107,7 +108,10 @@ namespace osu.Game.Rulesets.Scoring using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize)) using (var reader = new StreamReader(lzma)) + { score.Replay = createLegacyReplay(reader); + score.Replay.User = score.User; + } } } @@ -129,9 +133,22 @@ namespace osu.Game.Rulesets.Scoring { var split = l.Split('|'); - if (split.Length < 4 || float.Parse(split[0]) < 0) continue; + if (split.Length < 4) + continue; - lastTime += float.Parse(split[0]); + if (split[0] == "-12345") + { + // Todo: The seed is provided in split[3], which we'll need to use at some point + continue; + } + + var diff = float.Parse(split[0]); + lastTime += diff; + + // Todo: At some point we probably want to rewind and play back the negative-time frames + // but for now we'll achieve equal playback to stable by skipping negative frames + if (diff < 0) + continue; frames.Add(new ReplayFrame( lastTime, diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 25a7adb5a7..b17ea07355 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -2,11 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; -using osu.Game.Rulesets.Judgements; using osu.Framework.Allocation; namespace osu.Game.Rulesets.UI @@ -20,14 +20,16 @@ namespace osu.Game.Rulesets.UI public 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; + private List nestedPlayfields; + + /// + /// All the s nested inside this playfield. + /// + public IReadOnlyList NestedPlayfields => nestedPlayfields; + /// /// A container for keeping track of DrawableHitObjects. /// @@ -69,7 +71,7 @@ namespace osu.Game.Rulesets.UI /// /// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield. /// - public virtual void PostProcess() { } + public virtual void PostProcess() => nestedPlayfields?.ForEach(p => p.PostProcess()); /// /// Adds a DrawableHitObject to this Playfield. @@ -84,11 +86,17 @@ namespace osu.Game.Rulesets.UI public virtual void Remove(DrawableHitObject h) => HitObjects.Remove(h); /// - /// Triggered when a new occurs on a . + /// Registers a as a nested . + /// This does not add the to the draw hierarchy. /// - /// The object that occured for. - /// The that occurred. - public virtual void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { } + /// The to add. + protected void AddNested(Playfield otherPlayfield) + { + if (nestedPlayfields == null) + nestedPlayfields = new List(); + + nestedPlayfields.Add(otherPlayfield); + } /// /// Creates the container that will be used to contain the s. diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 4d559549b7..bb4466208b 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -13,6 +13,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; @@ -44,14 +46,9 @@ namespace osu.Game.Rulesets.UI public PassThroughInputManager KeyBindingInputManager; /// - /// Whether we are currently providing the local user a gameplay cursor. + /// Whether a replay is currently loaded. /// - public virtual bool ProvidingUserCursor => false; - - /// - /// Whether we have a replay loaded currently. - /// - public bool HasReplayLoaded => ReplayInputManager?.ReplayInputHandler != null; + public readonly BindableBool HasReplayLoaded = new BindableBool(); public abstract IEnumerable Objects { get; } @@ -61,6 +58,11 @@ namespace osu.Game.Rulesets.UI /// public Playfield Playfield => playfield.Value; + /// + /// The cursor provided by this . May be null if no cursor is provided. + /// + public readonly CursorContainer Cursor; + protected readonly Ruleset Ruleset; /// @@ -71,6 +73,8 @@ namespace osu.Game.Rulesets.UI { Ruleset = ruleset; playfield = new Lazy(CreatePlayfield); + + Cursor = CreateCursor(); } public abstract ScoreProcessor CreateScoreProcessor(); @@ -96,8 +100,16 @@ namespace osu.Game.Rulesets.UI Replay = replay; ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; + + HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null; } + + /// + /// Creates the cursor. May be null if the doesn't provide a custom cursor. + /// + protected virtual CursorContainer CreateCursor() => null; + /// /// Creates a Playfield. /// @@ -144,8 +156,6 @@ namespace osu.Game.Rulesets.UI /// protected readonly bool IsForCurrentRuleset; - public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor; - public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this); protected override Container Content => content; @@ -212,6 +222,9 @@ namespace osu.Game.Rulesets.UI AddInternal(KeyBindingInputManager); KeyBindingInputManager.Add(Playfield); + if (Cursor != null) + KeyBindingInputManager.Add(Cursor); + loadObjects(); } @@ -252,12 +265,7 @@ namespace osu.Game.Rulesets.UI if (drawableObject == null) continue; - drawableObject.OnJudgement += (d, j) => - { - Playfield.OnJudgement(d, j); - OnJudgement?.Invoke(j); - }; - + drawableObject.OnJudgement += (d, j) => OnJudgement?.Invoke(j); drawableObject.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(j); Playfield.Add(drawableObject); diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 11185015b8..287e917c7b 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -76,25 +75,6 @@ namespace osu.Game.Rulesets.UI.Scrolling HitObjects.TimeRange.BindTo(VisibleTimeRange); } - private List nestedPlayfields; - /// - /// All the s nested inside this playfield. - /// - public IEnumerable NestedPlayfields => nestedPlayfields; - - /// - /// Adds a to this playfield. The nested - /// will be given all of the same speed adjustments as this playfield. - /// - /// The to add. - protected void AddNested(ScrollingPlayfield otherPlayfield) - { - if (nestedPlayfields == null) - nestedPlayfields = new List(); - - nestedPlayfields.Add(otherPlayfield); - } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { if (!UserScrollSpeedAdjustment) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs index 286545270f..5f6b6801ce 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.UI.Scrolling private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield) { playfield.HitObjects.AddControlPoint(controlPoint); - playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p)); + playfield.NestedPlayfields?.OfType().ForEach(p => applySpeedAdjustment(controlPoint, p)); } /// diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index f5dd5cb500..268be51347 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -222,7 +222,8 @@ namespace osu.Game.Screens.Menu boxHoverLayer.FadeOut(800, Easing.OutExpo); } - public override bool HandleInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; + public override bool HandleKeyboardInput => state != ButtonState.Exploded; + public override bool HandleMouseInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; protected override void Update() { diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 529565312e..72c26af2a6 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -199,7 +199,8 @@ namespace osu.Game.Screens.Menu private MenuState state; - public override bool HandleInput => state != MenuState.Exit; + public override bool HandleKeyboardInput => state != MenuState.Exit; + public override bool HandleMouseInput => state != MenuState.Exit; public MenuState State { diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 977c8828d2..8285416ecb 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -19,8 +19,7 @@ namespace osu.Game.Screens.Menu private Color4 iconColour; public override bool ShowOverlaysOnEnter => false; - - public override bool HasLocalCursorDisplayed => true; + public override bool CursorVisible => false; public Disclaimer() { diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index fcee071f04..10b08d704d 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -31,9 +31,8 @@ namespace osu.Game.Screens.Menu private SampleChannel welcome; private SampleChannel seeya; - public override bool HasLocalCursorDisplayed => true; - public override bool ShowOverlaysOnEnter => false; + public override bool CursorVisible => false; protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty(); diff --git a/osu.Game/Screens/Menu/IntroSequence.cs b/osu.Game/Screens/Menu/IntroSequence.cs index 577eb33d18..ff3b4eba56 100644 --- a/osu.Game/Screens/Menu/IntroSequence.cs +++ b/osu.Game/Screens/Menu/IntroSequence.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Linq; using OpenTK; using OpenTK.Graphics; @@ -188,7 +187,7 @@ namespace osu.Game.Screens.Menu mediumRing.ResizeTo(130, 340, Easing.OutQuad); mediumRing.Foreground.ResizeTo(1, 880, Easing.Out); - Func remainingTime = () => length - TransformDelay; + double remainingTime() => length - TransformDelay; using (BeginDelayedSequence(250, true)) { diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 3fb5cc6d0d..3a3f3d4650 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -64,7 +64,8 @@ namespace osu.Game.Screens.Menu private readonly float[] frequencyAmplitudes = new float[256]; - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; private Shader shader; private readonly Texture texture; diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index 3cf2ce8e89..f94e2bddc1 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -19,7 +19,8 @@ namespace osu.Game.Screens.Menu { public class MenuSideFlashes : BeatSyncedContainer { - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; private readonly Bindable beatmap = new Bindable(); diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 4a13ae421e..b91ff0d74b 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -231,7 +231,7 @@ namespace osu.Game.Screens.Menu /// If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared. public void AppendAnimatingAction(Action action, bool waitForPrevious) { - Action runnableAction = () => + void runnableAction() { if (waitForPrevious) this.DelayUntilTransformsFinished().Schedule(action); @@ -240,12 +240,12 @@ namespace osu.Game.Screens.Menu ClearTransforms(); action(); } - }; + } if (IsLoaded) runnableAction(); else - Schedule(() => runnableAction()); + Schedule(runnableAction); } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 0111dceb40..a2d41dc206 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -35,9 +35,12 @@ namespace osu.Game.Screens /// public virtual bool ShowOverlaysOnEnter => true; - protected new OsuGameBase Game => base.Game as OsuGameBase; + /// + /// Whether this allows the cursor to be displayed. + /// + public virtual bool CursorVisible => true; - public virtual bool HasLocalCursorDisplayed => false; + protected new OsuGameBase Game => base.Game as OsuGameBase; private OsuLogo logo; diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index af222b82e2..615c124ea7 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -142,8 +142,6 @@ namespace osu.Game.Screens.Play } } - public override bool HandleInput => State == Visibility.Visible; - protected override void PopIn() => this.FadeIn(transition_duration, Easing.In); protected override void PopOut() => this.FadeOut(transition_duration, Easing.In); diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index bf3cf6b066..557c4585ca 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play public readonly PlayerSettingsOverlay PlayerSettingsOverlay; private Bindable showHud; - private bool replayLoaded; + private readonly BindableBool replayLoaded = new BindableBool(); private static bool hasShownNotificationOnce; @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Play HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), - PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), + PlayerSettingsOverlay = CreatePlayerSettingsOverlay() } }); } @@ -91,16 +91,24 @@ namespace osu.Game.Screens.Play } } - public virtual void BindRulesetContainer(RulesetContainer rulesetContainer) + protected override void LoadComplete() { - (rulesetContainer.KeyBindingInputManager as ICanAttachKeyCounter)?.Attach(KeyCounter); + base.LoadComplete(); - replayLoaded = rulesetContainer.HasReplayLoaded; + replayLoaded.ValueChanged += replayLoadedValueChanged; + replayLoaded.TriggerChange(); + } - PlayerSettingsOverlay.ReplayLoaded = replayLoaded; + private void replayLoadedValueChanged(bool loaded) + { + PlayerSettingsOverlay.ReplayLoaded = loaded; - // in the case a replay isn't loaded, we want some elements to only appear briefly. - if (!replayLoaded) + if (loaded) + { + PlayerSettingsOverlay.PlaybackSettings.Show(); + ModDisplay.FadeIn(200); + } + else { PlayerSettingsOverlay.PlaybackSettings.Hide(); PlayerSettingsOverlay.VisualSettings.Autohide = true; @@ -108,6 +116,15 @@ namespace osu.Game.Screens.Play } } + public virtual void BindRulesetContainer(RulesetContainer rulesetContainer) + { + (rulesetContainer.KeyBindingInputManager as ICanAttachKeyCounter)?.Attach(KeyCounter); + + replayLoaded.BindTo(rulesetContainer.HasReplayLoaded); + + Progress.BindRulestContainer(rulesetContainer); + } + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { if (args.Repeat) return false; diff --git a/osu.Game/Screens/Play/KeyCounterCollection.cs b/osu.Game/Screens/Play/KeyCounterCollection.cs index 554fbbaf61..e40776ae4a 100644 --- a/osu.Game/Screens/Play/KeyCounterCollection.cs +++ b/osu.Game/Screens/Play/KeyCounterCollection.cs @@ -111,7 +111,8 @@ namespace osu.Game.Screens.Play } } - public override bool HandleInput => receptor == null; + public override bool HandleKeyboardInput => receptor == null; + public override bool HandleMouseInput => receptor == null; private Receptor receptor; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e35a3c4413..2eb8b0a7db 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -23,22 +23,22 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; using osu.Framework.Audio.Sample; +using osu.Framework.Graphics.Cursor; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Graphics.Cursor; using osu.Game.Online.API; using osu.Game.Screens.Play.BreaksOverlay; using osu.Game.Storyboards.Drawables; namespace osu.Game.Screens.Play { - public class Player : OsuScreen + public class Player : OsuScreen, IProvideCursor { protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); public override bool ShowOverlaysOnEnter => false; - public override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor; - public Action RestartRequested; public override bool AllowBeatmapRulesetChange => false; @@ -51,6 +51,9 @@ namespace osu.Game.Screens.Play public int RestartCount; + public CursorContainer Cursor => RulesetContainer.Cursor; + public bool ProvidingUserCursor => RulesetContainer?.Cursor != null && !RulesetContainer.HasReplayLoaded.Value; + private IAdjustableClock adjustableSourceClock; private FramedOffsetClock offsetClock; private DecoupleableInterpolatingFramedClock decoupledClock; @@ -176,13 +179,13 @@ namespace osu.Game.Screens.Play }, Children = new Drawable[] { - new SkipButton(firstObjectTime) { AudioClock = decoupledClock }, new Container { RelativeSizeAxes = Axes.Both, Clock = offsetClock, Child = RulesetContainer, }, + new SkipButton(firstObjectTime) { AudioClock = decoupledClock }, hudOverlay = new HUDOverlay { Anchor = Anchor.Centre, @@ -194,7 +197,7 @@ namespace osu.Game.Screens.Play Origin = Anchor.Centre, Clock = decoupledClock, Breaks = beatmap.Breaks - }, + } } }, failOverlay = new FailOverlay @@ -223,7 +226,6 @@ namespace osu.Game.Screens.Play hudOverlay.Progress.Objects = RulesetContainer.Objects; hudOverlay.Progress.AudioClock = decoupledClock; - hudOverlay.Progress.AllowSeeking = RulesetContainer.HasReplayLoaded; hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos); hudOverlay.ModDisplay.Current.BindTo(working.Mods); diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index f1468aba14..fe8518c6a2 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -29,7 +29,10 @@ namespace osu.Game.Screens.Play.PlayerSettings if (autohide && hideStopWatch == null) hideStopWatch = Stopwatch.StartNew(); else if (!autohide) + { + this.FadeIn(50); hideStopWatch = null; + } } } diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index 55b2e21c53..827d77a73a 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -21,7 +21,7 @@ using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play { - public class SkipButton : Container, IKeyBindingHandler + public class SkipButton : OverlayContainer, IKeyBindingHandler { private readonly double startTime; public IAdjustableClock AudioClock; @@ -36,6 +36,8 @@ namespace osu.Game.Screens.Play { this.startTime = startTime; + State = Visibility.Visible; + RelativePositionAxes = Axes.Both; RelativeSizeAxes = Axes.Both; @@ -112,6 +114,16 @@ namespace osu.Game.Screens.Play Expire(); } + protected override void PopIn() + { + this.FadeIn(); + } + + protected override void PopOut() + { + this.FadeOut(); + } + protected override void Update() { base.Update(); diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 897fe4bba7..12f501a632 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -9,9 +9,12 @@ using System.Collections.Generic; using osu.Game.Graphics; using osu.Framework.Allocation; using System.Linq; +using osu.Framework.Configuration; using osu.Framework.Timing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.UI; + namespace osu.Game.Screens.Play { public class SongProgress : OverlayContainer @@ -28,7 +31,8 @@ namespace osu.Game.Screens.Play public Action OnSeek; - public override bool HandleInput => AllowSeeking; + public override bool HandleKeyboardInput => AllowSeeking; + public override bool HandleMouseInput => AllowSeeking; private IClock audioClock; public IClock AudioClock { set { audioClock = info.AudioClock = value; } } @@ -53,6 +57,8 @@ namespace osu.Game.Screens.Play } } + private readonly BindableBool replayLoaded = new BindableBool(); + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -97,6 +103,14 @@ namespace osu.Game.Screens.Play protected override void LoadComplete() { State = Visibility.Visible; + + replayLoaded.ValueChanged += v => AllowSeeking = v; + replayLoaded.TriggerChange(); + } + + public void BindRulestContainer(RulesetContainer rulesetContainer) + { + replayLoaded.BindTo(rulesetContainer.HasReplayLoaded); } private bool allowSeeking; diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 7bccad0c34..48013f7943 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -21,7 +21,8 @@ namespace osu.Game.Screens.Play public int ColumnCount => columns.Length; - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; private int progress; public int Progress diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index c0767cc07a..6a6042d7d4 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -51,7 +51,8 @@ namespace osu.Game.Screens.Select /// public Action SelectionChanged; - public override bool HandleInput => AllowSelection; + public override bool HandleKeyboardInput => AllowSelection; + public override bool HandleMouseInput => AllowSelection; /// /// Used to avoid firing null selections before the initial beatmaps have been loaded via . diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 7431a6e0e6..357931b878 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -266,7 +266,7 @@ namespace osu.Game.Screens.Select /// private void carouselSelectionChanged(BeatmapInfo beatmap) { - Action performLoad = delegate + void performLoad() { // We may be arriving here due to another component changing the bindable Beatmap. // In these cases, the other component has already loaded the beatmap, so we don't need to do so again. @@ -279,7 +279,7 @@ namespace osu.Game.Screens.Select } UpdateBeatmap(Beatmap.Value); - }; + } if (beatmap?.Equals(beatmapNoDebounce) == true) return; diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index 17a678c191..498dd7de6f 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -265,7 +265,7 @@ namespace osu.Game.Screens.Tournament private void writeResults(string text) { - Action writeAction = () => + void writeAction() { try { @@ -280,9 +280,9 @@ namespace osu.Game.Screens.Tournament { Logger.Error(ex, "Failed to write results."); } - }; + } - writeOp = writeOp?.ContinueWith(t => { writeAction(); }) ?? Task.Run(writeAction); + writeOp = writeOp?.ContinueWith(t => { writeAction(); }) ?? Task.Run((Action)writeAction); } private void reloadTeams() diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 7e8a80d86a..2489369493 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -26,7 +26,8 @@ namespace osu.Game.Storyboards.Drawables protected override Container Content => content; protected override Vector2 DrawScale => new Vector2(Parent.DrawHeight / 480); - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; private bool passing = true; public bool Passing diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1e52fcb4d6..31aac56b83 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -389,8 +389,10 @@ + +