diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs index 025ab0037f..7d00c35862 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs @@ -9,14 +9,14 @@ namespace osu.Game.Beatmaps.Drawables { - public class DifficultyColouredContainer : Container, IHasAccentColour + public abstract class DifficultyColouredContainer : Container, IHasAccentColour { public Color4 AccentColour { get; set; } private readonly BeatmapInfo beatmap; private OsuColour palette; - public DifficultyColouredContainer(BeatmapInfo beatmap) + protected DifficultyColouredContainer(BeatmapInfo beatmap) { this.beatmap = beatmap; } diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 42c98aef24..24604711d4 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -3,10 +3,14 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { @@ -14,7 +18,8 @@ public class DifficultyIcon : DifficultyColouredContainer { private readonly BeatmapInfo beatmap; - public DifficultyIcon(BeatmapInfo beatmap) : base(beatmap) + public DifficultyIcon(BeatmapInfo beatmap) + : base(beatmap) { if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); @@ -28,16 +33,29 @@ private void load() { Children = new Drawable[] { - new SpriteIcon + new CircularContainer { + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.84f), Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Colour = AccentColour, - Icon = FontAwesome.fa_circle + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.08f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = AccentColour, + }, }, new ConstrainedIconContainer { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) Icon = beatmap.Ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.fa_question_circle_o } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs new file mode 100644 index 0000000000..cdec8c042f --- /dev/null +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -0,0 +1,69 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; + +namespace osu.Game.Storyboards.Drawables +{ + public class DrawableStoryboardSample : Component + { + /// + /// The amount of time allowable beyond the start time of the sample, for the sample to start. + /// + private const double allowable_late_start = 100; + + private readonly StoryboardSample sample; + private SampleChannel channel; + + public override bool RemoveWhenNotAlive => false; + + public DrawableStoryboardSample(StoryboardSample sample) + { + this.sample = sample; + LifetimeStart = sample.Time; + } + + [BackgroundDependencyLoader] + private void load(IBindableBeatmap beatmap) + { + // Try first with the full name, then attempt with no path + channel = beatmap.Value.Skin.GetSample(sample.Path) ?? beatmap.Value.Skin.GetSample(Path.ChangeExtension(sample.Path, null)); + + if (channel != null) + channel.Volume.Value = sample.Volume / 100; + } + + protected override void Update() + { + base.Update(); + + // TODO: this logic will need to be consolidated with other game samples like hitsounds. + if (Time.Current < sample.Time) + { + // 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; + LifetimeEnd = double.MaxValue; + } + else if (Time.Current - Time.Elapsed < sample.Time) + { + // 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) + 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; + } + } + } +} diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs index d0555493a6..c34a39a7bf 100644 --- a/osu.Game/Storyboards/StoryboardSample.cs +++ b/osu.Game/Storyboards/StoryboardSample.cs @@ -2,14 +2,14 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using System; +using osu.Game.Storyboards.Drawables; namespace osu.Game.Storyboards { public class StoryboardSample : IStoryboardElement { public string Path { get; set; } - public bool IsDrawable => false; + public bool IsDrawable => true; public double Time; public float Volume; @@ -21,9 +21,6 @@ public StoryboardSample(string path, double time, float volume) Volume = volume; } - public Drawable CreateDrawable() - { - throw new InvalidOperationException(); - } + public Drawable CreateDrawable() => new DrawableStoryboardSample(this); } }