diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 882628642b..21f00c003b 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -11,13 +11,14 @@ using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using System; +using System.Linq; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Rulesets.Mania.UI { - public class Column : ScrollingPlayfield, IHasAccentColour + public class Column : ScrollingPlayfield, IKeyBindingHandler, IHasAccentColour { private const float key_icon_size = 10; private const float key_icon_corner_radius = 3; @@ -259,5 +260,26 @@ namespace osu.Game.Rulesets.Mania.UI public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false; public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false; } + + public bool OnPressed(ManiaAction action) + { + // Play the sounds of the next hitobject + if (HitObjects.AliveObjects.Any()) + { + // If there are alive hitobjects, we can abuse the fact that AliveObjects are sorted by time (see: Add()) + HitObjects.AliveObjects.First().PlaySamples(); + } + else + { + // If not, we do a slow search - we might want to do a BinarySearch here if this becomes problematic + // We fallback to LastOrDefault() if we're beyond the last note in the map + var hitObject = HitObjects.Objects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ?? HitObjects.Objects.LastOrDefault(); + hitObject?.PlaySamples(); + } + + return false; + } + + public bool OnReleased(ManiaAction action) => false; } } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index cd3f6d066f..dce6c0f55b 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition); Assert.IsTrue(sprite.IsDrawable); Assert.AreEqual(Anchor.Centre, sprite.Origin); - Assert.AreEqual(Path.Combine("SB", "lyric", "ja-21.png"), sprite.Path); + Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path); var animation = background.Elements.ElementAt(12) as StoryboardAnimation; Assert.NotNull(animation); @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(animation.IsDrawable); Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType); Assert.AreEqual(Anchor.Centre, animation.Origin); - Assert.AreEqual(Path.Combine("SB", "red jitter", "red_0000.jpg"), animation.Path); + Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path); Assert.AreEqual(78993, animation.StartTime); } } diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index d60297a26c..a4ff060c83 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -301,6 +301,6 @@ namespace osu.Game.Beatmaps.Formats } } - private string cleanFilename(string path) => FileSafety.PathSanitise(path.Trim('\"')); + private string cleanFilename(string path) => FileSafety.PathStandardise(FileSafety.PathSanitise(path.Trim('\"'))); } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 8680ff4e83..2db02724ed 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -136,7 +136,10 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public event Action ApplyCustomUpdateState; - protected void PlaySamples() => Samples.ForEach(s => s?.Play()); + /// + /// Plays all the hitsounds for this . + /// + public void PlaySamples() => Samples.ForEach(s => s?.Play()); protected override void Update() { diff --git a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs index 735c81aedf..af7c1ef5aa 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs @@ -41,6 +41,12 @@ namespace osu.Game.Screens.Play.BreaksOverlay private readonly InfoContainer info; private readonly ArrowsOverlay arrowsOverlay; + public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor) + : this(letterboxing) + { + bindProcessor(scoreProcessor); + } + public BreakOverlay(bool letterboxing) { this.letterboxing = letterboxing; @@ -148,7 +154,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay arrowsOverlay.Hide(); } - public void BindProcessor(ScoreProcessor processor) + private void bindProcessor(ScoreProcessor processor) { info.AccuracyDisplay.Current.BindTo(processor.Accuracy); info.GradeDisplay.Current.BindTo(processor.Rank); diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 255c071ac1..5fb867e151 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -6,6 +6,8 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; +using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -39,7 +41,7 @@ namespace osu.Game.Screens.Play private static bool hasShownNotificationOnce; - public HUDOverlay() + public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, DecoupleableInterpolatingFramedClock decoupledClock, WorkingBeatmap working, IAdjustableClock adjustableSourceClock) { RelativeSizeAxes = Axes.Both; @@ -59,6 +61,18 @@ namespace osu.Game.Screens.Play ReplaySettingsOverlay = CreateReplaySettingsOverlay(), } }); + + BindProcessor(scoreProcessor); + BindRulesetContainer(rulesetContainer); + + Progress.Objects = rulesetContainer.Objects; + Progress.AudioClock = decoupledClock; + Progress.AllowSeeking = rulesetContainer.HasReplayLoaded; + Progress.OnSeek = pos => decoupledClock.Seek(pos); + + ModDisplay.Current.BindTo(working.Mods); + + ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock; } [BackgroundDependencyLoader(true)] @@ -115,7 +129,7 @@ namespace osu.Game.Screens.Play } } - public virtual void BindRulesetContainer(RulesetContainer rulesetContainer) + protected virtual void BindRulesetContainer(RulesetContainer rulesetContainer) { (rulesetContainer.KeyBindingInputManager as ICanAttachKeyCounter)?.Attach(KeyCounter); @@ -201,7 +215,7 @@ namespace osu.Game.Screens.Play protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay(); - public virtual void BindProcessor(ScoreProcessor processor) + protected virtual void BindProcessor(ScoreProcessor processor) { ScoreCounter?.Current.BindTo(processor.TotalScore); AccuracyCounter?.Current.BindTo(processor.Accuracy); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8d26d63d41..ca4cb98bc8 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1,35 +1,35 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Framework.Logging; -using osu.Framework.Screens; -using osu.Framework.Timing; -using osu.Game.Configuration; -using osu.Game.Rulesets; -using osu.Game.Rulesets.UI; -using osu.Game.Screens.Backgrounds; using System; using System.Linq; using System.Threading.Tasks; -using osu.Framework.Threading; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Ranking; +using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Input; +using osu.Framework.Logging; +using osu.Framework.Screens; +using osu.Framework.Threading; +using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Online.API; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Play.BreaksOverlay; +using osu.Game.Screens.Ranking; using osu.Game.Storyboards.Drawables; +using OpenTK; namespace osu.Game.Screens.Play { @@ -79,7 +79,6 @@ namespace osu.Game.Screens.Play #endregion - private BreakOverlay breakOverlay; private Container storyboardContainer; private DrawableStoryboard storyboard; @@ -155,6 +154,8 @@ namespace osu.Game.Screens.Play userAudioOffset.ValueChanged += v => offsetClock.Offset = v; userAudioOffset.TriggerChange(); + scoreProcessor = RulesetContainer.CreateScoreProcessor(); + Children = new Drawable[] { storyboardContainer = new Container @@ -170,13 +171,12 @@ namespace osu.Game.Screens.Play OnRetry = Restart, OnQuit = Exit, CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded, - OnPause = () => { + OnPause = () => + { pauseContainer.Retries = RestartCount; hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused; }, - OnResume = () => { - hudOverlay.KeyCounter.IsCounting = true; - }, + OnResume = () => hudOverlay.KeyCounter.IsCounting = true, Children = new Drawable[] { new Container @@ -186,12 +186,12 @@ namespace osu.Game.Screens.Play Child = RulesetContainer, }, new SkipButton(firstObjectTime) { AudioClock = decoupledClock }, - hudOverlay = new HUDOverlay + hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, decoupledClock, working, adjustableSourceClock) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, - breakOverlay = new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks) + new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -207,7 +207,8 @@ namespace osu.Game.Screens.Play }, new HotkeyRetryOverlay { - Action = () => { + Action = () => + { //we want to hide the hitrenderer immediately (looks better). //we may be able to remove this once the mouse cursor trail is improved. RulesetContainer?.Hide(); @@ -216,24 +217,9 @@ namespace osu.Game.Screens.Play } }; - scoreProcessor = RulesetContainer.CreateScoreProcessor(); - if (showStoryboard) initializeStoryboard(false); - hudOverlay.BindProcessor(scoreProcessor); - hudOverlay.BindRulesetContainer(RulesetContainer); - - hudOverlay.Progress.Objects = RulesetContainer.Objects; - hudOverlay.Progress.AudioClock = decoupledClock; - hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos); - - hudOverlay.ModDisplay.Current.BindTo(working.Mods); - - breakOverlay.BindProcessor(scoreProcessor); - - hudOverlay.ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock; - // Bind ScoreProcessor to ourselves scoreProcessor.AllJudged += onCompletion; scoreProcessor.Failed += onFail;