diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs index 57c71d0411..42646851d7 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable { private readonly Container bananaContainer; - public DrawableBananaShower(BananaShower s, Func> getVisualRepresentation = null) + public DrawableBananaShower(BananaShower s, Func> createDrawableRepresentation = null) : base(s) { RelativeSizeAxes = Axes.X; @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable AddInternal(bananaContainer = new Container { RelativeSizeAxes = Axes.Both }); foreach (var b in s.NestedHitObjects.Cast()) - AddNested(getVisualRepresentation?.Invoke(b)); + AddNested(createDrawableRepresentation?.Invoke(b)); } protected override void AddNested(DrawableHitObject h) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs index ae2232f8f1..9e5e9f6a04 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable { private readonly Container dropletContainer; - public DrawableJuiceStream(JuiceStream s, Func> getVisualRepresentation = null) + public DrawableJuiceStream(JuiceStream s, Func> createDrawableRepresentation = null) : base(s) { RelativeSizeAxes = Axes.Both; @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable AddInternal(dropletContainer = new Container { RelativeSizeAxes = Axes.Both, }); foreach (var o in s.NestedHitObjects.Cast()) - AddNested(getVisualRepresentation?.Invoke(o)); + AddNested(createDrawableRepresentation?.Invoke(o)); } protected override void AddNested(DrawableHitObject h) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 43d0dc026d..b6d8cf9cbe 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.UI internal readonly CatcherArea CatcherArea; - public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation) + public CatchPlayfield(BeatmapDifficulty difficulty, Func> createDrawableRepresentation) { Container explodingFruitContainer; @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.UI }, CatcherArea = new CatcherArea(difficulty) { - GetVisualRepresentation = getVisualRepresentation, + CreateDrawableRepresentation = createDrawableRepresentation, ExplodingFruitTarget = explodingFruitContainer, Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft, diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index c6dd0a86a0..83f791690a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.UI protected internal readonly Catcher MovableCatcher; - public Func> GetVisualRepresentation; + public Func> CreateDrawableRepresentation; public Container ExplodingFruitTarget { @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.UI if (result.IsHit && fruit.CanBePlated) { - var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject); + var caughtFruit = (DrawableCatchHitObject)CreateDrawableRepresentation?.Invoke(fruit.HitObject); if (caughtFruit == null) return; diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index 6981c98ec7..a8ae5c7337 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -34,13 +34,13 @@ namespace osu.Game.Rulesets.Catch.UI protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); - protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation); + protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation); protected override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchPlayfieldAdjustmentContainer(); protected override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); - public override DrawableHitObject GetVisualRepresentation(CatchHitObject h) + public override DrawableHitObject CreateDrawableRepresentation(CatchHitObject h) { switch (h) { @@ -49,9 +49,9 @@ namespace osu.Game.Rulesets.Catch.UI case Fruit fruit: return new DrawableFruit(fruit); case JuiceStream stream: - return new DrawableJuiceStream(stream, GetVisualRepresentation); + return new DrawableJuiceStream(stream, CreateDrawableRepresentation); case BananaShower shower: - return new DrawableBananaShower(shower, GetVisualRepresentation); + return new DrawableBananaShower(shower, CreateDrawableRepresentation); case TinyDroplet tiny: return new DrawableTinyDroplet(tiny); case Droplet droplet: diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 0dc081f3da..e9aa69e4f3 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.UI protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant); - public override DrawableHitObject GetVisualRepresentation(ManiaHitObject h) + public override DrawableHitObject CreateDrawableRepresentation(ManiaHitObject h) { switch (h) { diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index ad8341af09..162e6e596d 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Linq; @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer(); - public override DrawableHitObject GetVisualRepresentation(OsuHitObject h) + public override DrawableHitObject CreateDrawableRepresentation(OsuHitObject h) { switch (h) { diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index f595432082..c71141b4c7 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo); - public override DrawableHitObject GetVisualRepresentation(TaikoHitObject h) + public override DrawableHitObject CreateDrawableRepresentation(TaikoHitObject h) { switch (h) { diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 136d1de930..2288d04493 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -29,28 +29,28 @@ namespace osu.Game.Tests.Beatmaps.Formats StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3); Assert.IsNotNull(background); - Assert.AreEqual(16, background.Elements.Count()); + Assert.AreEqual(16, background.Elements.Count); Assert.IsTrue(background.EnabledWhenFailing); Assert.IsTrue(background.EnabledWhenPassing); Assert.AreEqual("Background", background.Name); StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2); Assert.IsNotNull(fail); - Assert.AreEqual(0, fail.Elements.Count()); + Assert.AreEqual(0, fail.Elements.Count); Assert.IsTrue(fail.EnabledWhenFailing); Assert.IsFalse(fail.EnabledWhenPassing); Assert.AreEqual("Fail", fail.Name); StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1); Assert.IsNotNull(pass); - Assert.AreEqual(0, pass.Elements.Count()); + Assert.AreEqual(0, pass.Elements.Count); Assert.IsFalse(pass.EnabledWhenFailing); Assert.IsTrue(pass.EnabledWhenPassing); Assert.AreEqual("Pass", pass.Name); StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0); Assert.IsNotNull(foreground); - Assert.AreEqual(151, foreground.Elements.Count()); + Assert.AreEqual(151, foreground.Elements.Count); Assert.IsTrue(foreground.EnabledWhenFailing); Assert.IsTrue(foreground.EnabledWhenPassing); Assert.AreEqual("Foreground", foreground.Name); @@ -62,7 +62,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(15, spriteCount); Assert.AreEqual(1, animationCount); Assert.AreEqual(0, sampleCount); - Assert.AreEqual(background.Elements.Count(), spriteCount + animationCount + sampleCount); + Assert.AreEqual(background.Elements.Count, spriteCount + animationCount + sampleCount); var sprite = background.Elements.ElementAt(0) as StoryboardSprite; Assert.NotNull(sprite); @@ -70,9 +70,9 @@ 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("SB/lyric/ja-21.png", sprite.Path); + Assert.AreEqual("SB/black.jpg", sprite.Path); - var animation = background.Elements.ElementAt(12) as StoryboardAnimation; + var animation = background.Elements.OfType().First(); Assert.NotNull(animation); Assert.AreEqual(141175, animation.EndTime); Assert.AreEqual(10, animation.FrameCount); diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 9584b10ef5..0f83edf034 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics; @@ -38,6 +39,10 @@ namespace osu.Game.Beatmaps.Formats { this.storyboard = storyboard; base.ParseStreamInto(stream, storyboard); + + // OrderBy is used to guarantee that the parsing order of elements with equal start times is maintained (stably-sorted) + foreach (StoryboardLayer layer in storyboard.Layers) + layer.Elements = layer.Elements.OrderBy(h => h.StartTime).ToList(); } protected override void ParseLine(Storyboard storyboard, Section section, string line) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d0c6537605..d33762374f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -15,7 +15,6 @@ using osu.Framework.Allocation; using osu.Game.Overlays.Toolbar; using osu.Game.Screens; using osu.Game.Screens.Menu; -using osuTK; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -442,7 +441,7 @@ namespace osu.Game loadComponentSingleFile(musicController = new MusicController { - Position = new Vector2(0, Toolbar.HEIGHT), + GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, floatingOverlayContent.Add); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index de5204ad43..ce2137346f 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -56,6 +56,11 @@ namespace osu.Game.Overlays private readonly Bindable beatmap = new Bindable(); + /// + /// Provide a source for the toolbar height. + /// + public Func GetToolbarHeight; + public MusicController() { Width = 400; @@ -244,6 +249,8 @@ namespace osu.Game.Overlays { base.UpdateAfterChildren(); Height = dragContainer.Height; + + dragContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; } protected override void Update() diff --git a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs index 76a2e7af12..68d57c559e 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Edit processor?.PostProcess(); // Add visual representation - var drawableObject = drawableRuleset.GetVisualRepresentation(tObject); + var drawableObject = drawableRuleset.CreateDrawableRepresentation(tObject); drawableRuleset.Playfield.Add(drawableObject); drawableRuleset.Playfield.PostProcess(); diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 6e4a9cfc0a..8345b0c5cf 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.UI private void loadObjects() { foreach (TObject h in Beatmap.HitObjects) - addRepresentation(h); + addHitObject(h); Playfield.PostProcess(); @@ -175,9 +175,9 @@ namespace osu.Game.Rulesets.UI /// Creates and adds the visual representation of a to this . /// /// The to add the visual representation for. - private void addRepresentation(TObject hitObject) + private void addHitObject(TObject hitObject) { - var drawableObject = GetVisualRepresentation(hitObject); + var drawableObject = CreateDrawableRepresentation(hitObject); if (drawableObject == null) return; @@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.UI /// /// The HitObject to make drawable. /// The DrawableHitObject. - public abstract DrawableHitObject GetVisualRepresentation(TObject h); + public abstract DrawableHitObject CreateDrawableRepresentation(TObject h); public void Attach(KeyCounterDisplay keyCounter) => (KeyBindingInputManager as ICanAttachKeyCounter)?.Attach(keyCounter); diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index 9fa481b8b6..ffd238d4e1 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -25,7 +25,7 @@ namespace osu.Game.Storyboards.Drawables public DrawableStoryboardSample(StoryboardSample sample) { this.sample = sample; - LifetimeStart = sample.Time; + LifetimeStart = sample.StartTime; } [BackgroundDependencyLoader] @@ -43,27 +43,27 @@ namespace osu.Game.Storyboards.Drawables base.Update(); // TODO: this logic will need to be consolidated with other game samples like hit sounds. - if (Time.Current < sample.Time) + if (Time.Current < sample.StartTime) { // We've rewound before the start time of the sample channel?.Stop(); // In the case that the user fast-forwards to a point far beyond the start time of the sample, // we want to be able to fall into the if-conditional below (therefore we must not have a life time end) - LifetimeStart = sample.Time; + LifetimeStart = sample.StartTime; LifetimeEnd = double.MaxValue; } - else if (Time.Current - Time.Elapsed < sample.Time) + else if (Time.Current - Time.Elapsed < sample.StartTime) { // We've passed the start time of the sample. We only play the sample if we're within an allowable range // from the sample's start, to reduce layering if we've been fast-forwarded far into the future - if (Time.Current - sample.Time < allowable_late_start) + if (Time.Current - sample.StartTime < allowable_late_start) channel?.Play(); // In the case that the user rewinds to a point far behind the start time of the sample, // we want to be able to fall into the if-conditional above (therefore we must not have a life time start) LifetimeStart = double.MinValue; - LifetimeEnd = sample.Time; + LifetimeEnd = sample.StartTime; } } } diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index 454db2afc2..c4c150a8a4 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -10,6 +10,8 @@ namespace osu.Game.Storyboards string Path { get; } bool IsDrawable { get; } + double StartTime { get; } + Drawable CreateDrawable(); } } diff --git a/osu.Game/Storyboards/StoryboardLayer.cs b/osu.Game/Storyboards/StoryboardLayer.cs index daf03b00b4..d15f771534 100644 --- a/osu.Game/Storyboards/StoryboardLayer.cs +++ b/osu.Game/Storyboards/StoryboardLayer.cs @@ -13,8 +13,7 @@ namespace osu.Game.Storyboards public bool EnabledWhenPassing = true; public bool EnabledWhenFailing = true; - private readonly List elements = new List(); - public IEnumerable Elements => elements; + public List Elements = new List(); public StoryboardLayer(string name, int depth) { @@ -24,7 +23,7 @@ namespace osu.Game.Storyboards public void Add(IStoryboardElement element) { - elements.Add(element); + Elements.Add(element); } public DrawableStoryboardLayer CreateDrawable() diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs index 1bdf774e74..24231cdca6 100644 --- a/osu.Game/Storyboards/StoryboardSample.cs +++ b/osu.Game/Storyboards/StoryboardSample.cs @@ -11,13 +11,14 @@ namespace osu.Game.Storyboards public string Path { get; set; } public bool IsDrawable => true; - public double Time; + public double StartTime { get; } + public float Volume; public StoryboardSample(string path, double time, float volume) { Path = path; - Time = time; + StartTime = time; Volume = volume; }