diff --git a/osu-framework b/osu-framework index 36fad894f0..c95b9350ed 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 36fad894f0657d0fdc998ffd3f2f3fa87e45d67d +Subproject commit c95b9350edb6305cfefdf08f902f6f73d336736b diff --git a/osu.Game/Graphics/SpriteIcon.cs b/osu.Game/Graphics/SpriteIcon.cs index d4f9127d54..ca108bfa7a 100644 --- a/osu.Game/Graphics/SpriteIcon.cs +++ b/osu.Game/Graphics/SpriteIcon.cs @@ -57,11 +57,6 @@ namespace osu.Game.Graphics private void load(FontStore store) { this.store = store; - } - - protected override void LoadComplete() - { - base.LoadComplete(); updateTexture(); } diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs index 6dedf7385b..bae14fc1dc 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs @@ -51,7 +51,9 @@ namespace osu.Game.Input.Bindings protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - store.KeyBindingChanged -= ReloadMappings; + + if (store != null) + store.KeyBindingChanged -= ReloadMappings; } protected override void ReloadMappings() => KeyBindings = store.Query(ruleset?.ID, variant).ToList(); diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs new file mode 100644 index 0000000000..66b2cae892 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Humanizer; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserBeatmapsRequest : APIRequest> + { + private readonly long userId; + private readonly int offset; + private readonly BeatmapSetType type; + + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) + { + this.userId = userId; + this.offset = offset; + this.type = type; + } + + protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}"; + } + + public enum BeatmapSetType + { + MostPlayed, + Favourite, + RankedAndApproved + } +} diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index 9402ed82f4..6df31b9f85 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -29,7 +29,10 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmap) return; beatmap = value; - var rate = (float)beatmap.OnlineInfo.PassCount / beatmap.OnlineInfo.PlayCount; + int passCount = beatmap.OnlineInfo.PassCount; + int playCount = beatmap.OnlineInfo.PlayCount; + + var rate = playCount != 0 ? (float)passCount / playCount : 0; successPercent.Text = rate.ToString("P0"); successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs new file mode 100644 index 0000000000..2607585573 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -0,0 +1,79 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Direct; +using osu.Game.Users; +using System.Linq; + +namespace osu.Game.Overlays.Profile.Sections.Beatmaps +{ + public class PaginatedBeatmapContainer : PaginatedContainer + { + private const float panel_padding = 10f; + + private readonly BeatmapSetType type; + + private DirectPanel playing; + + public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing) + : base(user, header, missing) + { + this.type = type; + + ItemsPerPage = 6; + + ItemsContainer.Spacing = new Vector2(panel_padding); + ItemsContainer.Margin = new MarginPadding { Bottom = panel_padding }; + } + + protected override void ShowMore() + { + base.ShowMore(); + + var req = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); + + req.Success += sets => + { + ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); + ShowMoreLoading.Hide(); + + if (!sets.Any()) + { + MissingText.Show(); + return; + } + + foreach (var s in sets) + { + if (!s.OnlineBeatmapSetID.HasValue) + continue; + + var subReq = new GetBeatmapSetRequest(s.OnlineBeatmapSetID.Value); + subReq.Success += b => + { + var panel = new DirectGridPanel(b.ToBeatmapSet(Rulesets)) { Width = 400 }; + ItemsContainer.Add(panel); + + panel.PreviewPlaying.ValueChanged += newValue => + { + if (newValue) + { + if (playing != null && playing != panel) + playing.PreviewPlaying.Value = false; + playing = panel; + } + }; + }; + + Api.Queue(subReq); + } + }; + + Api.Queue(req); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 1c39223e6f..f55de9b83f 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -1,6 +1,9 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Profile.Sections.Beatmaps; + namespace osu.Game.Overlays.Profile.Sections { public class BeatmapsSection : ProfileSection @@ -8,5 +11,14 @@ namespace osu.Game.Overlays.Profile.Sections public override string Title => "Beatmaps"; public override string Identifier => "beatmaps"; + + public BeatmapsSection() + { + Children = new[] + { + new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, "Favourite Beatmaps", "None... yet."), + new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, "Ranked & Approved Beatmaps", "None... yet."), + }; + } } } diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index d25407f4a3..a4d043d20a 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Sections public HistoricalSection() { - Child = new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)"); + Child = new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)", "No performance records. :("); } } } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs new file mode 100644 index 0000000000..b75d1bc4d6 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -0,0 +1,109 @@ +// Copyright (c) 2007-2017 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.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Rulesets; +using osu.Game.Users; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class PaginatedContainer : FillFlowContainer + { + protected readonly FillFlowContainer ItemsContainer; + protected readonly OsuHoverContainer ShowMoreButton; + protected readonly LoadingAnimation ShowMoreLoading; + protected readonly OsuSpriteText MissingText; + + protected int VisiblePages; + protected int ItemsPerPage; + + protected readonly Bindable User = new Bindable(); + + protected APIAccess Api; + protected RulesetStore Rulesets; + + public PaginatedContainer(Bindable user, string header, string missing) + { + User.BindTo(user); + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + + Children = new Drawable[] + { + new OsuSpriteText + { + TextSize = 15, + Text = header, + Font = "Exo2.0-RegularItalic", + Margin = new MarginPadding { Top = 10, Bottom = 10 }, + }, + ItemsContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }, + ShowMoreButton = new OsuHoverContainer + { + Alpha = 0, + Action = ShowMore, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Child = new OsuSpriteText + { + TextSize = 14, + Text = "show more", + } + }, + ShowMoreLoading = new LoadingAnimation + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(14), + }, + MissingText = new OsuSpriteText + { + TextSize = 14, + Text = missing, + Alpha = 0, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(APIAccess api, RulesetStore rulesets) + { + Api = api; + Rulesets = rulesets; + + User.ValueChanged += onUserChanged; + User.TriggerChange(); + } + + private void onUserChanged(User newUser) + { + VisiblePages = 0; + ItemsContainer.Clear(); + ShowMoreButton.Hide(); + + if (newUser != null) + ShowMore(); + } + + protected virtual void ShowMore() + { + ShowMoreLoading.Show(); + ShowMoreButton.Hide(); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs index 0380b6c4b7..e6ba5b26ac 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks } [BackgroundDependencyLoader] - private new void load(OsuColour colour) + private void load(OsuColour colour) { double pp = Score.PP ?? 0; Stats.Add(new OsuSpriteText diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs index 6ce438baed..5088fe5f67 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs @@ -6,73 +6,119 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select.Leaderboards; using osu.Framework.Localisation; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Input; +using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Profile.Sections.Ranks { public abstract class DrawableScore : Container { + private const int fade_duration = 200; + protected readonly FillFlowContainer Stats; private readonly FillFlowContainer metadata; private readonly ScoreModsContainer modsContainer; protected readonly Score Score; + private readonly Box underscoreLine; + private readonly Box coloredBackground; + private readonly Container background; protected DrawableScore(Score score) { Score = score; + RelativeSizeAxes = Axes.X; + Height = 60; Children = new Drawable[] { - new DrawableRank(score.Rank) + background = new Container { - RelativeSizeAxes = Axes.Y, - Width = 60, - FillMode = FillMode.Fit, - }, - Stats = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Direction = FillDirection.Vertical, - }, - metadata = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Left = 70 }, - Direction = FillDirection.Vertical, - Child = new OsuSpriteText + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 3, + Alpha = 0, + EdgeEffect = new EdgeEffectParameters { - Text = score.Date.LocalDateTime.ToShortDateString(), - TextSize = 11, - Colour = OsuColour.Gray(0xAA), - Depth = -1, + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 1f, + Colour = Color4.Black.Opacity(0.2f), }, + Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both } }, - modsContainer = new ScoreModsContainer + new Container { - AutoSizeAxes = Axes.Y, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Width = 60, - Margin = new MarginPadding { Right = 150 } - } + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 0.97f, + Children = new Drawable[] + { + underscoreLine = new Box + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 1, + }, + new DrawableRank(score.Rank) + { + RelativeSizeAxes = Axes.Y, + Width = 60, + FillMode = FillMode.Fit, + }, + Stats = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Direction = FillDirection.Vertical, + }, + metadata = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 70 }, + Direction = FillDirection.Vertical, + Child = new OsuSpriteText + { + Text = score.Date.LocalDateTime.ToShortDateString(), + TextSize = 11, + Colour = OsuColour.Gray(0xAA), + Depth = -1, + }, + }, + modsContainer = new ScoreModsContainer + { + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Width = 60, + Margin = new MarginPadding { Right = 160 } + } + } + }, }; } [BackgroundDependencyLoader(true)] private void load(OsuColour colour, LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay) { + coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; + Stats.Add(new OsuSpriteText { Text = $"accuracy: {Score.Accuracy:P2}", @@ -84,7 +130,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Depth = -1, }); - metadata.Add(new OsuHoverContainer + metadata.Add(new MetadataContainer(Score.Beatmap.Metadata.Title, Score.Beatmap.Metadata.Artist) { AutoSizeAxes = Axes.Both, Action = () => @@ -123,5 +169,31 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Scale = new Vector2(0.5f), }); } + + protected override bool OnClick(InputState state) => true; + + protected override bool OnHover(InputState state) + { + background.FadeIn(fade_duration, Easing.OutQuint); + underscoreLine.FadeOut(fade_duration, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(InputState state) + { + background.FadeOut(fade_duration, Easing.OutQuint); + underscoreLine.FadeIn(fade_duration, Easing.OutQuint); + base.OnHoverLost(state); + } + + private class MetadataContainer : OsuHoverContainer, IHasTooltip + { + public string TooltipText { get; set; } + + public MetadataContainer(string title, string artist) + { + TooltipText = $"{artist} - {title}"; + } + } } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs index 7aa9d75f02..537b208b39 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks } [BackgroundDependencyLoader] - private new void load() + private void load() { Stats.Add(new OsuSpriteText { diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 060bb03014..bb383cac0d 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -1,130 +1,53 @@ // Copyright (c) 2007-2017 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.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Rulesets; using osu.Game.Users; using System; using System.Linq; namespace osu.Game.Overlays.Profile.Sections.Ranks { - public class PaginatedScoreContainer : FillFlowContainer + public class PaginatedScoreContainer : PaginatedContainer { - private readonly FillFlowContainer scoreContainer; - private readonly OsuSpriteText missing; - private readonly OsuHoverContainer showMoreButton; - private readonly LoadingAnimation showMoreLoading; - private readonly bool includeWeight; private readonly ScoreType type; - private int visiblePages; - private readonly Bindable user = new Bindable(); - - private RulesetStore rulesets; - private APIAccess api; - - public PaginatedScoreContainer(ScoreType type, Bindable user, string header, bool includeWeight = false) + public PaginatedScoreContainer(ScoreType type, Bindable user, string header, string missing, bool includeWeight = false) + : base(user, header, missing) { this.type = type; this.includeWeight = includeWeight; - this.user.BindTo(user); - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; + ItemsPerPage = 5; - Children = new Drawable[] - { - new OsuSpriteText - { - TextSize = 15, - Text = header, - Font = "Exo2.0-RegularItalic", - Margin = new MarginPadding { Top = 10, Bottom = 10 }, - }, - scoreContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - }, - showMoreButton = new OsuHoverContainer - { - Alpha = 0, - Action = showMore, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Child = new OsuSpriteText - { - TextSize = 14, - Text = "show more", - } - }, - showMoreLoading = new LoadingAnimation - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Size = new Vector2(14), - }, - missing = new OsuSpriteText - { - TextSize = 14, - Text = type == ScoreType.Recent ? "No performance records. :(" : "No awesome performance records yet. :(", - }, - }; + ItemsContainer.Direction = FillDirection.Vertical; } - [BackgroundDependencyLoader] - private void load(APIAccess api, RulesetStore rulesets) + protected override void ShowMore() { - this.api = api; - this.rulesets = rulesets; + base.ShowMore(); - user.ValueChanged += user_ValueChanged; - user.TriggerChange(); - } - - private void user_ValueChanged(User newUser) - { - visiblePages = 0; - scoreContainer.Clear(); - showMoreButton.Hide(); - missing.Show(); - - if (newUser != null) - showMore(); - } - - private void showMore() - { - var req = new GetUserScoresRequest(user.Value.Id, type, visiblePages++ * 5); - - showMoreLoading.Show(); - showMoreButton.Hide(); + var req = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); req.Success += scores => { foreach (var s in scores) - s.ApplyRuleset(rulesets.GetRuleset(s.OnlineRulesetID)); + s.ApplyRuleset(Rulesets.GetRuleset(s.OnlineRulesetID)); - showMoreButton.FadeTo(scores.Count == 5 ? 1 : 0); - showMoreLoading.Hide(); + ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0); + ShowMoreLoading.Hide(); - if (!scores.Any()) return; + if (!scores.Any()) + { + MissingText.Show(); + return; + } - missing.Hide(); + MissingText.Hide(); foreach (OnlineScore score in scores) { @@ -133,21 +56,18 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks switch (type) { default: - drawableScore = new DrawablePerformanceScore(score, includeWeight ? Math.Pow(0.95, scoreContainer.Count) : (double?)null); + drawableScore = new DrawablePerformanceScore(score, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); break; case ScoreType.Recent: drawableScore = new DrawableTotalScore(score); break; } - drawableScore.RelativeSizeAxes = Axes.X; - drawableScore.Height = 60; - - scoreContainer.Add(drawableScore); + ItemsContainer.Add(drawableScore); } }; - api.Queue(req); + Api.Queue(req); } } } diff --git a/osu.Game/Overlays/Profile/Sections/RanksSection.cs b/osu.Game/Overlays/Profile/Sections/RanksSection.cs index 553691ef77..7691100d7a 100644 --- a/osu.Game/Overlays/Profile/Sections/RanksSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RanksSection.cs @@ -16,8 +16,8 @@ namespace osu.Game.Overlays.Profile.Sections { Children = new[] { - new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", true), - new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks"), + new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", "No performance records. :(", true), + new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks", "No awesome performance records yet. :("), }; } } diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 5032f2d55b..ce35f7e547 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -91,12 +91,12 @@ namespace osu.Game.Overlays sections = new ProfileSection[] { - new AboutSection(), + //new AboutSection(), //new RecentSection(), new RanksSection(), //new MedalsSection(), new HistoricalSection(), - //new BeatmapsSection(), + new BeatmapsSection(), //new KudosuSection() }; tabs = new ProfileTabControl diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 091af04106..941cedca3f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -212,6 +212,7 @@ namespace osu.Game.Rulesets.Objects.Drawables nestedHitObjects = new List>(); h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j); + h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j); nestedHitObjects.Add(h); } diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 36dce7218d..278814ea7e 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -242,7 +242,7 @@ namespace osu.Game.Rulesets.UI OnJudgement?.Invoke(j); }; - drawableObject.OnJudgementRemoved += (d, j) => { OnJudgementRemoved?.Invoke(j); }; + drawableObject.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(j); Playfield.Add(drawableObject); } diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 0419070b42..8c4d6de1fe 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -76,6 +76,8 @@ namespace osu.Game.Rulesets.UI #region Clock control + protected override bool ShouldProcessClock => false; // We handle processing the clock ourselves + private ManualClock clock; private IFrameBasedClock parentClock; @@ -151,6 +153,12 @@ namespace osu.Game.Rulesets.UI } requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime; + + // The manual clock time has changed in the above code. The framed clock now needs to be updated + // to ensure that the its time is valid for our children before input is processed + Clock.ProcessFrame(); + + // Process input base.Update(); } diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index 3afaa02824..ca541ea552 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -24,7 +24,6 @@ namespace osu.Game.Screens { base.LogoArriving(logo, resuming); - logo.RelativePositionAxes = Axes.None; logo.Triangles = false; logo.Origin = Anchor.BottomRight; logo.Anchor = Anchor.BottomRight; @@ -47,11 +46,7 @@ namespace osu.Game.Screens protected override void LogoSuspending(OsuLogo logo) { base.LogoSuspending(logo); - logo.FadeOut(100).OnComplete(l => - { - l.Anchor = Anchor.TopLeft; - l.Origin = Anchor.Centre; - }); + logo.FadeOut(100); } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index 0445733b23..0cb343c1ac 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -127,8 +127,6 @@ namespace osu.Game.Screens.Menu if (!resuming) { - logo.Triangles = true; - logo.ScaleTo(1); logo.FadeIn(); logo.PlayIntro(); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index b0170edfe1..90f68ba9f1 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -112,9 +112,6 @@ namespace osu.Game.Screens.Menu buttons.SetOsuLogo(logo); - logo.Triangles = true; - logo.Ripple = false; - logo.FadeColour(Color4.White, 100, Easing.OutQuint); logo.FadeIn(100, Easing.OutQuint); diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index dccb910d86..fb8e755b61 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -221,6 +221,30 @@ namespace osu.Game.Screens.Menu }; } + /// + /// Schedule a new extenral animation. Handled queueing and finishing previous animations in a sane way. + /// + /// The animation to be performed + /// If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared. + internal void AppendAnimatingAction(Action action, bool waitForPrevious) + { + Action runnableAction = () => + { + if (waitForPrevious) + this.DelayUntilTransformsFinished().Schedule(action); + else + { + ClearTransforms(); + action(); + } + }; + + if (IsLoaded) + runnableAction(); + else + Schedule(() => runnableAction()); + } + [BackgroundDependencyLoader] private void load(TextureStore textures, AudioManager audio) { diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 3dd175ca88..f5ff9ea036 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -76,7 +76,7 @@ namespace osu.Game.Screens protected override void OnResuming(Screen last) { base.OnResuming(last); - logo.DelayUntilTransformsFinished().Schedule(() => LogoArriving(logo, true)); + logo.AppendAnimatingAction(() => LogoArriving(logo, true), true); sampleExit?.Play(); } @@ -118,11 +118,11 @@ namespace osu.Game.Screens } if ((logo = lastOsu?.logo) == null) - AddInternal(logo = new OsuLogo()); + LoadComponentAsync(logo = new OsuLogo { Alpha = 0 }, AddInternal); + + logo.AppendAnimatingAction(() => LogoArriving(logo, false), true); base.OnEntering(last); - - logo.DelayUntilTransformsFinished().Schedule(() => LogoArriving(logo, false)); } protected override bool OnExiting(Screen next) @@ -155,12 +155,16 @@ namespace osu.Game.Screens { logo.Action = null; logo.FadeOut(300, Easing.OutQuint); + logo.Anchor = Anchor.TopLeft; + logo.Origin = Anchor.Centre; + logo.RelativePositionAxes = Axes.None; + logo.Triangles = true; + logo.Ripple = true; } private void onExitingLogo() { - logo.ClearTransforms(); - LogoExiting(logo); + logo.AppendAnimatingAction(() => { LogoExiting(logo); }, false); } /// @@ -172,8 +176,7 @@ namespace osu.Game.Screens private void onSuspendingLogo() { - logo.ClearTransforms(); - LogoSuspending(logo); + logo.AppendAnimatingAction(() => { LogoSuspending(logo); }, false); } /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 59d56a5a44..675d20fe63 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -144,22 +144,6 @@ namespace osu.Game.Screens.Play userAudioOffset.ValueChanged += v => offsetClock.Offset = v; userAudioOffset.TriggerChange(); - Task.Run(() => - { - adjustableSourceClock.Reset(); - - // this is temporary until we have blocking (async.Wait()) audio component methods. - // then we can call ResetAsync().Wait() or the blocking version above. - while (adjustableSourceClock.IsRunning) - Thread.Sleep(1); - - Schedule(() => - { - decoupledClock.ChangeSource(adjustableSourceClock); - applyRateFromMods(); - }); - }); - Children = new Drawable[] { storyboardContainer = new Container @@ -329,10 +313,26 @@ namespace osu.Game.Screens.Play .Delay(250) .FadeIn(250); - this.Delay(750).Schedule(() => + Task.Run(() => { - if (!pauseContainer.IsPaused) - decoupledClock.Start(); + adjustableSourceClock.Reset(); + + // this is temporary until we have blocking (async.Wait()) audio component methods. + // then we can call ResetAsync().Wait() or the blocking version above. + while (adjustableSourceClock.IsRunning) + Thread.Sleep(1); + + Schedule(() => + { + decoupledClock.ChangeSource(adjustableSourceClock); + applyRateFromMods(); + + this.Delay(750).Schedule(() => + { + if (!pauseContainer.IsPaused) + decoupledClock.Start(); + }); + }); }); pauseContainer.Alpha = 0; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 53a2dcc41f..de67bef004 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -99,7 +99,6 @@ namespace osu.Game.Screens.Play { base.LogoArriving(logo, resuming); - logo.ClearTransforms(targetMember: nameof(Position)); logo.RelativePositionAxes = Axes.Both; logo.ScaleTo(new Vector2(0.15f), 300, Easing.In); diff --git a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs index bf2909b8ab..3109552532 100644 --- a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs @@ -3,11 +3,16 @@ using osu.Framework.Timing; using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; namespace osu.Game.Screens.Play.ReplaySettings { public class PlaybackSettings : ReplayGroup { + private const int padding = 10; + protected override string Title => @"playback"; public IAdjustableClock AdjustableClock { set; get; } @@ -16,17 +21,45 @@ namespace osu.Game.Screens.Play.ReplaySettings public PlaybackSettings() { - Child = sliderbar = new ReplaySliderBar + OsuSpriteText multiplierText; + + Children = new Drawable[] { - LabelText = "Playback speed", - Bindable = new BindableDouble(1) + new Container { - Default = 1, - MinValue = 0.5, - MaxValue = 2, - Precision = 0.01, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = padding }, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "Playback speed", + }, + multiplierText = new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Text = "1x", + Font = @"Exo2.0-Bold", + } + }, }, + sliderbar = new ReplaySliderBar + { + Bindable = new BindableDouble(1) + { + Default = 1, + MinValue = 0.5, + MaxValue = 2, + Precision = 0.01, + }, + } }; + + sliderbar.Bindable.ValueChanged += rateMultiplier => multiplierText.Text = $"{rateMultiplier}x"; } protected override void LoadComplete() diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 121a53f699..5500d06136 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -315,9 +315,7 @@ namespace osu.Game.Screens.Select { base.LogoArriving(logo, resuming); - logo.ClearTransforms(); logo.RelativePositionAxes = Axes.Both; - Vector2 position = new Vector2(0.95f, 0.96f); if (logo.Alpha > 0.8f) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index beea9c59aa..ac1498c9d9 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -88,6 +88,9 @@ $(SolutionDir)\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll True + + $(SolutionDir)\packages\Humanizer.Core.2.2.0\lib\netstandard1.0\Humanizer.dll + $(SolutionDir)\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll @@ -283,6 +286,9 @@ + + + diff --git a/osu.Game/packages.config b/osu.Game/packages.config index ae7b74ef16..02ace918de 100644 --- a/osu.Game/packages.config +++ b/osu.Game/packages.config @@ -5,6 +5,48 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +