diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs index f57952f95e..fa8958687c 100644 --- a/osu.Game.Rulesets.Catch/CatchInputManager.cs +++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs @@ -23,6 +23,5 @@ namespace osu.Game.Rulesets.Catch MoveRight, [Description("Engage dash")] Dash, - PositionUpdate } } diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index bc53e6e869..f8ca75fae9 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -1,9 +1,12 @@ // 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 osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Replays; using osu.Game.Users; @@ -23,15 +26,78 @@ namespace osu.Game.Rulesets.Catch.Replays public override Replay Generate() { + // todo: add support for HT DT + const double dash_speed = CatcherArea.Catcher.BASE_SPEED; + const double movement_speed = dash_speed / 2; + float lastPosition = 0.5f; + double lastTime = 0; + // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled - Replay.Frames.Add(new CatchReplayFrame(-100000, 0)); + Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition)); + + void moveToNext(CatchHitObject h) + { + float positionChange = Math.Abs(lastPosition - h.X); + double timeAvailable = h.StartTime - lastTime; + + //So we can either make it there without a dash or not. + double speedRequired = positionChange / timeAvailable; + + bool dashRequired = speedRequired > movement_speed && h.StartTime != 0; + + // todo: get correct catcher size, based on difficulty CS. + const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f; + + if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X) + { + //we are already in the correct range. + lastTime = h.StartTime; + Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition)); + return; + } + + if (h is BananaShower.Banana) + { + // auto bananas unrealistically warp to catch 100% combo. + Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + } + else if (h.HyperDash) + { + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition, ReplayButtonState.Right1)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + } + else if (dashRequired) + { + //we do a movement in two parts - the dash part then the normal part... + double timeAtNormalSpeed = positionChange / movement_speed; + double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable; + double timeAtDashSpeed = timeWeNeedToSave / 2; + + float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable); + + //dash movement + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, ReplayButtonState.Left1)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + } + else + { + double timeBefore = positionChange / movement_speed; + + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition, ReplayButtonState.Right1)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + } + + lastTime = h.StartTime; + lastPosition = h.X; + } foreach (var obj in Beatmap.HitObjects) { switch (obj) { case Fruit _: - Replay.Frames.Add(new CatchReplayFrame(obj.StartTime, obj.X)); + moveToNext(obj); break; } @@ -42,7 +108,7 @@ namespace osu.Game.Rulesets.Catch.Replays case BananaShower.Banana _: case TinyDroplet _: case Droplet _: - Replay.Frames.Add(new CatchReplayFrame(nestedObj.StartTime, nestedObj.X)); + moveToNext(nestedObj); break; } } diff --git a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs index 146e31fa69..2f296a2504 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs @@ -14,15 +14,29 @@ namespace osu.Game.Rulesets.Catch.Replays { } - public override List GetPendingStates() => new List + public override List GetPendingStates() { - new CatchReplayState + if (!Position.HasValue) return new List(); + + var action = new List(); + + if (CurrentFrame.ButtonState == ReplayButtonState.Left1) + action.Add(CatchAction.Dash); + + if (Position.Value.X > CurrentFrame.Position.X) + action.Add(CatchAction.MoveRight); + else if (Position.Value.X < CurrentFrame.Position.X) + action.Add(CatchAction.MoveLeft); + + return new List { - PressedActions = new List { CatchAction.PositionUpdate }, - CatcherX = ((CatchReplayFrame)CurrentFrame).MouseX - }, - new CatchReplayState { PressedActions = new List() }, - }; + new CatchReplayState + { + PressedActions = action, + CatcherX = Position.Value.X + }, + }; + } public class CatchReplayState : ReplayState { diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs index c47f60ec3c..0194fc93a4 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs @@ -9,8 +9,8 @@ namespace osu.Game.Rulesets.Catch.Replays { public override bool IsImportant => MouseX > 0; - public CatchReplayFrame(double time, float? x = null) - : base(time, x ?? -1, null, ReplayButtonState.None) + public CatchReplayFrame(double time, float? x = null, ReplayButtonState button = ReplayButtonState.None) + : base(time, x ?? -1, null, button) { } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 17c78f3aa0..5252ba294a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -21,7 +21,7 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.UI { - public class CatcherArea : Container, IKeyBindingHandler + public class CatcherArea : Container { public const float CATCHER_SIZE = 172; @@ -84,16 +84,14 @@ namespace osu.Game.Rulesets.Catch.UI } } - public bool OnPressed(CatchAction action) + protected override void Update() { - if (action != CatchAction.PositionUpdate) return false; + base.Update(); - CatchFramedReplayInputHandler.CatchReplayState state = (CatchFramedReplayInputHandler.CatchReplayState)GetContainingInputManager().CurrentState; + var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState; - if (state.CatcherX.HasValue) + if (state?.CatcherX != null) MovableCatcher.X = state.CatcherX.Value; - - return true; } public bool OnReleased(CatchAction action) => false; diff --git a/osu.Game.Tests/Visual/TestCaseWaveform.cs b/osu.Game.Tests/Visual/TestCaseWaveform.cs index 87492e2332..dd5420400f 100644 --- a/osu.Game.Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game.Tests/Visual/TestCaseWaveform.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 NUnit.Framework; using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; @@ -15,6 +16,7 @@ using osu.Game.Screens.Edit.Screens.Compose.Timeline; namespace osu.Game.Tests.Visual { + [Ignore("CI regularly hangs on this TestCase...")] public class TestCaseWaveform : OsuTestCase { private readonly Bindable beatmapBacking = new Bindable(); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 634b5bcd20..44289e2400 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -287,15 +287,16 @@ namespace osu.Game.Beatmaps Import(archive); downloadNotification.State = ProgressNotificationState.Completed; + currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); - - currentDownloads.Remove(request); }; - request.Failure += data => + request.Failure += error => { + if (error is OperationCanceledException) return; + downloadNotification.State = ProgressNotificationState.Completed; - Logger.Error(data, "Failed to get beatmap download information"); + Logger.Error(error, "Beatmap download failed!"); currentDownloads.Remove(request); }; diff --git a/osu.Game/Graphics/UserInterface/ProgressBar.cs b/osu.Game/Graphics/UserInterface/ProgressBar.cs index 43fab9fead..6021cf08be 100644 --- a/osu.Game/Graphics/UserInterface/ProgressBar.cs +++ b/osu.Game/Graphics/UserInterface/ProgressBar.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface public Color4 FillColour { - set { fill.Colour = value; } + set { fill.FadeColour(value, 150, Easing.OutQuint); } } public Color4 BackgroundColour diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 7da6f1ce2f..e0d806c90f 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -16,7 +16,6 @@ using osu.Game.Graphics.Sprites; using OpenTK.Graphics; using osu.Framework.Input; using osu.Game.Graphics.UserInterface; -using osu.Framework.Logging; using osu.Game.Online.API.Requests; using osu.Framework.Configuration; using osu.Framework.Audio.Track; @@ -65,11 +64,14 @@ namespace osu.Game.Overlays.Direct Colour = Color4.Black.Opacity(0.3f), }; + private OsuColour colours; + [BackgroundDependencyLoader(permitNulls: true)] private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay) { this.beatmaps = beatmaps; this.beatmapSetOverlay = beatmapSetOverlay; + this.colours = colours; AddInternal(content = new Container { @@ -182,7 +184,6 @@ namespace osu.Game.Overlays.Direct { progressBar.Current.Value = 0; progressBar.FadeOut(500); - Logger.Error(e, "Failed to get beatmap download information"); }; request.DownloadProgressed += progress => progressBar.Current.Value = progress; @@ -190,7 +191,7 @@ namespace osu.Game.Overlays.Direct request.Success += data => { progressBar.Current.Value = 1; - progressBar.FadeOut(500); + progressBar.FillColour = colours.Yellow; }; } diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index d5acb9dc86..05b5bba09c 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -298,7 +298,7 @@ namespace osu.Game.Overlays Task.Run(() => { var onlineIds = response.Select(r => r.OnlineBeatmapSetID).ToList(); - var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID)).Select(r => r.OnlineBeatmapSetID).ToList(); + var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID) && !s.DeletePending).Select(r => r.OnlineBeatmapSetID).ToList(); var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList(); // may not need scheduling; loads async internally.