From fe7f9cccaa0520791ff08064fd5a851b0eaf423d Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:16:00 +0100 Subject: [PATCH 001/193] BeatmapSetCover can display other types of covers now --- .../Beatmaps/Drawables/BeatmapSetCover.cs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs index 614ebc236b..ba79db3f48 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs @@ -11,21 +11,44 @@ namespace osu.Game.Beatmaps.Drawables public class BeatmapSetCover : Sprite { private readonly BeatmapSetInfo set; - public BeatmapSetCover(BeatmapSetInfo set) + private readonly BeatmapSetCoverType type; + + public BeatmapSetCover(BeatmapSetInfo set, BeatmapSetCoverType type = BeatmapSetCoverType.Cover) { if (set == null) throw new ArgumentNullException(nameof(set)); this.set = set; + this.type = type; } [BackgroundDependencyLoader] private void load(TextureStore textures) { - string resource = set.OnlineInfo.Covers.Cover; + string resource = null; + + switch (type) + { + case BeatmapSetCoverType.Cover: + resource = set.OnlineInfo.Covers.Cover; + break; + case BeatmapSetCoverType.Card: + resource = set.OnlineInfo.Covers.Card; + break; + case BeatmapSetCoverType.List: + resource = set.OnlineInfo.Covers.List; + break; + } if (resource != null) Texture = textures.Get(resource); } } + + public enum BeatmapSetCoverType + { + Cover, + Card, + List, + } } From 1f379cab8fcdaa8b1b5d1497e18b2bae313d2cc1 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:17:14 +0100 Subject: [PATCH 002/193] move BeatmapMetadataContainer to a separate class --- .../Sections/BeatmapMetadataContainer.cs | 60 +++++++++++++++++++ .../Profile/Sections/Ranks/DrawableScore.cs | 44 +------------- osu.Game/osu.Game.csproj | 1 + 3 files changed, 62 insertions(+), 43 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs new file mode 100644 index 0000000000..5104f98f9d --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +namespace osu.Game.Overlays.Profile.Sections +{ + public class BeatmapMetadataContainer : OsuHoverContainer, IHasTooltip + { + private readonly BeatmapInfo beatmap; + + public BeatmapMetadataContainer(BeatmapInfo beatmap) + { + this.beatmap = beatmap; + AutoSizeAxes = Axes.Both; + TooltipText = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}"; + } + + public string TooltipText { get; } + + [BackgroundDependencyLoader(true)] + private void load(LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay) + { + Action = () => + { + if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value); + }; + + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Current = locale.GetUnicodePreference( + $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", + $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] " + ), + TextSize = 15, + Font = "Exo2.0-SemiBoldItalic", + }, + new OsuSpriteText + { + Current = locale.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist), + TextSize = 12, + Padding = new MarginPadding { Top = 3 }, + Font = "Exo2.0-RegularItalic", + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs index 35f4778047..6c475cbb26 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs @@ -14,10 +14,8 @@ 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 { @@ -130,37 +128,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Depth = -1, }); - metadata.Add(new MetadataContainer(Score.Beatmap.Metadata.Title, Score.Beatmap.Metadata.Artist) - { - AutoSizeAxes = Axes.Both, - Action = () => - { - if (Score.Beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(Score.Beatmap.OnlineBeatmapSetID.Value); - }, - Child = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new OsuSpriteText - { - Current = locale.GetUnicodePreference( - $"{Score.Beatmap.Metadata.TitleUnicode ?? Score.Beatmap.Metadata.Title} [{Score.Beatmap.Version}] ", - $"{Score.Beatmap.Metadata.Title ?? Score.Beatmap.Metadata.TitleUnicode} [{Score.Beatmap.Version}] " - ), - TextSize = 15, - Font = "Exo2.0-SemiBoldItalic", - }, - new OsuSpriteText - { - Current = locale.GetUnicodePreference(Score.Beatmap.Metadata.ArtistUnicode, Score.Beatmap.Metadata.Artist), - TextSize = 12, - Padding = new MarginPadding { Top = 3 }, - Font = "Exo2.0-RegularItalic", - }, - }, - }, - }); + metadata.Add(new BeatmapMetadataContainer(Score.Beatmap)); foreach (Mod mod in Score.Mods) modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) }); @@ -181,15 +149,5 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks 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/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7b479bdba2..c5493ea59f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -287,6 +287,7 @@ + From 48b44e8e4eb0f29c2430ca286438dd8b69ee9cc2 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:18:14 +0100 Subject: [PATCH 003/193] add a user most played beatmaps request/response --- .../API/Requests/GetBeatmapSetsResponse.cs | 2 +- .../API/Requests/GetUserBeatmapsRequest.cs | 15 ++++- .../GetUserMostPlayedBeatmapsRequest.cs | 67 +++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 4 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs b/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs index 9e412a9b8b..d187cabf31 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs @@ -65,7 +65,7 @@ namespace osu.Game.Online.API.Requests Ranked = ranked, LastUpdated = lastUpdated, }, - Beatmaps = beatmaps.Select(b => b.ToBeatmap(rulesets)).ToList(), + Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), }; } diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index a66799f404..9c3a32ec9e 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -1,18 +1,19 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using Humanizer; using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public class GetUserBeatmapsRequest : APIRequest> + 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) + protected GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) { this.userId = userId; this.offset = offset; @@ -22,6 +23,16 @@ namespace osu.Game.Online.API.Requests protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}"; } + public class GetUserBeatmapsRequest : GetUserBeatmapsRequest + { + public GetUserBeatmapsRequest(long userID, BeatmapSetType type, int offset = 0) + : base(userID, type, offset) + { + if(type == BeatmapSetType.MostPlayed) + throw new ArgumentException("Please use " + nameof(GetUserMostPlayedBeatmapsRequest) + " instead"); + } + } + public enum BeatmapSetType { MostPlayed, diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs new file mode 100644 index 0000000000..33b755450b --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using Newtonsoft.Json; +using osu.Framework.Extensions; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserMostPlayedBeatmapsRequest : GetUserBeatmapsRequest + { + public GetUserMostPlayedBeatmapsRequest(long userID, BeatmapSetType type, int offset = 0) + : base(userID, type, offset) + { + if (type != BeatmapSetType.MostPlayed) + throw new ArgumentException("Please use " + nameof(GetUserBeatmapsRequest) + " instead"); + } + } + + public class UserMostPlayedBeatmapsResponse + { + [JsonProperty("beatmap_id")] + public int BeatmapID; + + [JsonProperty("count")] + public int PlayCount; + + [JsonProperty] + private BeatmapResponse beatmap; + + [JsonProperty] + private GetBeatmapSetsResponse beatmapSet; + + public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) + { + BeatmapSetInfo setInfo = beatmapSet.ToBeatmapSet(rulesets); + return new BeatmapInfo + { + OnlineBeatmapID = beatmap.Id, + OnlineBeatmapSetID = setInfo.OnlineBeatmapSetID, + Ruleset = rulesets.AvailableRulesets.FirstOrDefault(ruleset => ruleset.Name.Equals(beatmap.Mode)), + StarDifficulty = beatmap.DifficultyRating, + Version = beatmap.Version, + Metadata = setInfo.Metadata, + BeatmapSet = setInfo, + }; + } + + private class BeatmapResponse + { + [JsonProperty] + public int Id; + + [JsonProperty] + public string Mode; + + [JsonProperty("difficulty_rating")] + public double DifficultyRating; + + [JsonProperty] + public string Version; + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c5493ea59f..9fc1674c45 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -282,6 +282,7 @@ + From 4281d76bcf8d8e0c7f56424f8faa863c7aa02dfd Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:19:05 +0100 Subject: [PATCH 004/193] historical section now shows the most played beatmaps --- .../Historical/MostPlayedBeatmapDrawable.cs | 160 ++++++++++++++++++ .../PaginatedMostPlayedBeatmapContainer.cs | 52 ++++++ .../Profile/Sections/HistoricalSection.cs | 8 +- osu.Game/osu.Game.csproj | 2 + 4 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs create mode 100644 osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs diff --git a/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs b/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs new file mode 100644 index 0000000000..0f419ad44e --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs @@ -0,0 +1,160 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +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.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public class MostPlayedBeatmapDrawable : Container + { + private readonly BeatmapInfo beatmap; + private readonly OsuHoverContainer mapperContainer; + + private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0, 1f), + Radius = 2f, + Colour = Color4.Black.Opacity(0.25f), + }; + + private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0, 5f), + Radius = 10f, + Colour = Color4.Black.Opacity(0.25f), + }; + + public MostPlayedBeatmapDrawable(BeatmapInfo beatmap, int playCount) + { + this.beatmap = beatmap; + RelativeSizeAxes = Axes.X; + Height = 50; + Margin = new MarginPadding { Bottom = 10 }; + Masking = true; + EdgeEffect = edgeEffectNormal; + + Children = new Drawable[] + { + new Box //Background for this container, otherwise the shadow would be visible + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f), + }, + new Box //Image Background while loading + { + Size = new Vector2(80, 50), + Colour = Color4.Black, + }, + new DelayedLoadWrapper(new BeatmapSetCover(beatmap.BeatmapSet, BeatmapSetCoverType.List) + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + }), + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(10) { Left = 90 }, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new BeatmapMetadataContainer(beatmap), + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new [] + { + new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Text = playCount.ToString(), + TextSize = 18, + Font = @"Exo2.0-SemiBoldItalic" + }, + new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Text = @"times played ", + TextSize = 12, + Font = @"Exo2.0-RegularItalic" + }, + } + } + }, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"mapped by ", + TextSize = 12, + }, + mapperContainer = new OsuHoverContainer + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = beatmap.Metadata.AuthorString, + TextSize = 12, + Font = @"Exo2.0-MediumItalic" + } + } + }, + } + }, + }, + } + }; + } + + [BackgroundDependencyLoader(true)] + private void load(UserProfileOverlay profileOverlay) + { + if(profileOverlay != null) + mapperContainer.Action = () => profileOverlay.ShowUser(beatmap.BeatmapSet.Metadata.Author); + } + + protected override bool OnHover(InputState state) + { + TweenEdgeEffectTo(edgeEffectHovered, 120, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + TweenEdgeEffectTo(edgeEffectNormal, 120, Easing.OutQuint); + base.OnHoverLost(state); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs new file mode 100644 index 0000000000..d42e00b1a5 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -0,0 +1,52 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests; +using osu.Game.Users; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer + { + public PaginatedMostPlayedBeatmapContainer(Bindable user) + :base(user, "Most Played Beatmaps", "No performance records. :(") + { + ItemsPerPage = 5; + + ItemsContainer.Direction = FillDirection.Vertical; + } + + protected override void ShowMore() + { + base.ShowMore(); + + var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, BeatmapSetType.MostPlayed, VisiblePages++ * ItemsPerPage); + + req.Success += beatmaps => + { + ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); + ShowMoreLoading.Hide(); + + if (!beatmaps.Any() && VisiblePages == 1) + { + MissingText.Show(); + return; + } + + MissingText.Hide(); + + foreach (var beatmap in beatmaps) + { + ItemsContainer.Add(new MostPlayedBeatmapDrawable(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount)); + } + }; + + Api.Queue(req); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index a4d043d20a..ab99abdccd 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -1,7 +1,9 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Graphics; using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Profile.Sections.Historical; using osu.Game.Overlays.Profile.Sections.Ranks; namespace osu.Game.Overlays.Profile.Sections @@ -14,7 +16,11 @@ namespace osu.Game.Overlays.Profile.Sections public HistoricalSection() { - Child = new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)", "No performance records. :("); + Children = new Drawable[] + { + new PaginatedMostPlayedBeatmapContainer(User), + new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)", "No performance records. :("), + }; } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9fc1674c45..fd8e03f623 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -290,6 +290,8 @@ + + From 66c51c7b44a1d3f99187decbb465a884606bc102 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:33:50 +0100 Subject: [PATCH 005/193] cleanup --- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 2 +- .../Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs | 1 - .../Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs | 1 - osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs | 3 +-- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index 9c3a32ec9e..dca0395e3a 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public class GetUserBeatmapsRequest : APIRequest> + public abstract class GetUserBeatmapsRequest : APIRequest> { private readonly long userId; private readonly int offset; diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index 33b755450b..cdc156be05 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using Newtonsoft.Json; -using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Rulesets; diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index d42e00b1a5..916f1f437a 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -5,7 +5,6 @@ using System.Linq; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Online.API.Requests; using osu.Game.Users; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs index 6c475cbb26..4d210f399e 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs @@ -9,7 +9,6 @@ using osu.Game.Graphics; 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; @@ -113,7 +112,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks } [BackgroundDependencyLoader(true)] - private void load(OsuColour colour, LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay) + private void load(OsuColour colour) { coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; From 0df5432f5ebe25ea320f181c3183d3edb483b526 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Wed, 22 Nov 2017 21:45:18 +0100 Subject: [PATCH 006/193] removed line that set metadata per beatmap to null --- osu.Game/Beatmaps/BeatmapManager.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f461317ce1..eb10b23c6f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -515,9 +515,6 @@ namespace osu.Game.Beatmaps if (existing == null) { - // TODO: Diff beatmap metadata with set metadata and leave it here if necessary - beatmap.BeatmapInfo.Metadata = null; - RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); // TODO: this should be done in a better place once we actually need to dynamically update it. From 57f2d8556bd9caacf2bc6616da73a64bf485ff64 Mon Sep 17 00:00:00 2001 From: jorolf Date: Wed, 22 Nov 2017 22:00:17 +0100 Subject: [PATCH 007/193] add a visual test --- .../Visual/TestCaseHistoricalSection.cs | 43 +++++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + 2 files changed, 44 insertions(+) create mode 100644 osu.Game.Tests/Visual/TestCaseHistoricalSection.cs diff --git a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs new file mode 100644 index 0000000000..e67f389969 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Overlays.Profile.Sections; +using osu.Game.Overlays.Profile.Sections.Historical; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + internal class TestCaseHistoricalSection : OsuTestCase + { + public override string Description => "User's History"; + + public override IReadOnlyList RequiredTypes => new [] { typeof(HistoricalSection), typeof(MostPlayedBeatmapDrawable)}; + + + public TestCaseHistoricalSection() + { + HistoricalSection section; + + Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f) + }); + + Add(new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = section = new HistoricalSection(), + }); + + AddStep("Show peppy", () => section.User.Value = new User { Id = 2 }); + AddStep("Show WubWoofWolf", () => section.User.Value = new User { Id = 39828 }); + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 9bba09b1a7..b093989b45 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -110,6 +110,7 @@ + From e3a230320ae1aa3b1f2279b539d0e97b416d48fc Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 23 Nov 2017 19:46:58 +0100 Subject: [PATCH 008/193] compare metdata and remove duplicate from beatmap to prevent redundant storage --- osu.Game/Beatmaps/BeatmapManager.cs | 4 ++++ osu.Game/Beatmaps/BeatmapMetadata.cs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index eb10b23c6f..ca715b8d1e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -515,6 +515,10 @@ namespace osu.Game.Beatmaps if (existing == null) { + // Exclude beatmap-metadata if it's equal to beatmapset-metadata + if (metadata.Equals(beatmap.Metadata)) + beatmap.BeatmapInfo.Metadata = null; + RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); // TODO: this should be done in a better place once we actually need to dynamically update it. diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 89f9ebf47a..2cd8e2669f 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -66,5 +66,23 @@ namespace osu.Game.Beatmaps Source, Tags }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); + + public override bool Equals(object other) + { + var otherMetadata = other as BeatmapMetadata; + if (otherMetadata == null) return false; + + return (onlineBeatmapSetID?.Equals(otherMetadata.onlineBeatmapSetID) ?? false) + && (Title?.Equals(otherMetadata.Title) ?? false) + && (TitleUnicode?.Equals(otherMetadata.TitleUnicode) ?? false) + && (Artist?.Equals(otherMetadata.Artist) ?? false) + && (ArtistUnicode?.Equals(otherMetadata.ArtistUnicode) ?? false) + && (AuthorString?.Equals(otherMetadata.AuthorString) ?? false) + && (Source?.Equals(otherMetadata.Source) ?? false) + && (Tags?.Equals(otherMetadata.Tags) ?? false) + && PreviewTime.Equals(otherMetadata.PreviewTime) + && (AudioFile?.Equals(otherMetadata.AudioFile) ?? false) + && (BackgroundFile?.Equals(otherMetadata.BackgroundFile) ?? false); + } } } From 21d5d107381f28a8efb2642ba0cd88bf05459ce3 Mon Sep 17 00:00:00 2001 From: jorolf Date: Fri, 24 Nov 2017 22:48:56 +0100 Subject: [PATCH 009/193] replace BeatmapResponse with BeatmapInfo --- .../Visual/TestCaseHistoricalSection.cs | 2 +- osu.Game/Beatmaps/BeatmapInfo.cs | 1 + .../GetUserMostPlayedBeatmapsRequest.cs | 32 +++---------------- 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs index e67f389969..d75f9116ce 100644 --- a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs +++ b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual { public override string Description => "User's History"; - public override IReadOnlyList RequiredTypes => new [] { typeof(HistoricalSection), typeof(MostPlayedBeatmapDrawable)}; + public override IReadOnlyList RequiredTypes => new [] { typeof(HistoricalSection), typeof(PaginatedMostPlayedBeatmapContainer), typeof(MostPlayedBeatmapDrawable) }; public TestCaseHistoricalSection() diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 022d64db03..f3a9694982 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -115,6 +115,7 @@ namespace osu.Game.Beatmaps // Metadata public string Version { get; set; } + [JsonProperty("difficulty_rating")] public double StarDifficulty { get; set; } public bool Equals(BeatmapInfo other) diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index cdc156be05..2d08f09d20 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Linq; using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets; @@ -28,7 +27,7 @@ namespace osu.Game.Online.API.Requests public int PlayCount; [JsonProperty] - private BeatmapResponse beatmap; + private BeatmapInfo beatmap; [JsonProperty] private GetBeatmapSetsResponse beatmapSet; @@ -36,31 +35,10 @@ namespace osu.Game.Online.API.Requests public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) { BeatmapSetInfo setInfo = beatmapSet.ToBeatmapSet(rulesets); - return new BeatmapInfo - { - OnlineBeatmapID = beatmap.Id, - OnlineBeatmapSetID = setInfo.OnlineBeatmapSetID, - Ruleset = rulesets.AvailableRulesets.FirstOrDefault(ruleset => ruleset.Name.Equals(beatmap.Mode)), - StarDifficulty = beatmap.DifficultyRating, - Version = beatmap.Version, - Metadata = setInfo.Metadata, - BeatmapSet = setInfo, - }; - } - - private class BeatmapResponse - { - [JsonProperty] - public int Id; - - [JsonProperty] - public string Mode; - - [JsonProperty("difficulty_rating")] - public double DifficultyRating; - - [JsonProperty] - public string Version; + beatmap.BeatmapSet = setInfo; + beatmap.OnlineBeatmapSetID = setInfo.OnlineBeatmapSetID; + beatmap.Metadata = setInfo.Metadata; + return beatmap; } } } From 6b3347d6acfabc2df1dd0444ca97a31637c8d214 Mon Sep 17 00:00:00 2001 From: jorolf Date: Fri, 24 Nov 2017 22:59:21 +0100 Subject: [PATCH 010/193] remove description --- osu.Game.Tests/Visual/TestCaseHistoricalSection.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs index d75f9116ce..37bb935787 100644 --- a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs +++ b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs @@ -15,8 +15,6 @@ namespace osu.Game.Tests.Visual { internal class TestCaseHistoricalSection : OsuTestCase { - public override string Description => "User's History"; - public override IReadOnlyList RequiredTypes => new [] { typeof(HistoricalSection), typeof(PaginatedMostPlayedBeatmapContainer), typeof(MostPlayedBeatmapDrawable) }; From 5da1466e284b584c636a5cf6f2c00c8280d04e3b Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 15:05:59 +0100 Subject: [PATCH 011/193] requested changes use IEquatable instead of overriding Equals and `==` operator for primitive types. --- osu.Game/Beatmaps/BeatmapMetadata.cs | 31 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 2cd8e2669f..22a64820bc 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; @@ -9,7 +10,7 @@ using osu.Game.Users; namespace osu.Game.Beatmaps { - public class BeatmapMetadata + public class BeatmapMetadata : IEquatable { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } @@ -67,22 +68,22 @@ namespace osu.Game.Beatmaps Tags }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); - public override bool Equals(object other) + public bool Equals(BeatmapMetadata other) { - var otherMetadata = other as BeatmapMetadata; - if (otherMetadata == null) return false; + if (other == null) + return false; - return (onlineBeatmapSetID?.Equals(otherMetadata.onlineBeatmapSetID) ?? false) - && (Title?.Equals(otherMetadata.Title) ?? false) - && (TitleUnicode?.Equals(otherMetadata.TitleUnicode) ?? false) - && (Artist?.Equals(otherMetadata.Artist) ?? false) - && (ArtistUnicode?.Equals(otherMetadata.ArtistUnicode) ?? false) - && (AuthorString?.Equals(otherMetadata.AuthorString) ?? false) - && (Source?.Equals(otherMetadata.Source) ?? false) - && (Tags?.Equals(otherMetadata.Tags) ?? false) - && PreviewTime.Equals(otherMetadata.PreviewTime) - && (AudioFile?.Equals(otherMetadata.AudioFile) ?? false) - && (BackgroundFile?.Equals(otherMetadata.BackgroundFile) ?? false); + return onlineBeatmapSetID == other.onlineBeatmapSetID + && (Title?.Equals(other.Title) ?? false) + && (TitleUnicode?.Equals(other.TitleUnicode) ?? false) + && (Artist?.Equals(other.Artist) ?? false) + && (ArtistUnicode?.Equals(other.ArtistUnicode) ?? false) + && (AuthorString?.Equals(other.AuthorString) ?? false) + && (Source?.Equals(other.Source) ?? false) + && (Tags?.Equals(other.Tags) ?? false) + && PreviewTime == other.PreviewTime + && (AudioFile?.Equals(other.AudioFile) ?? false) + && (BackgroundFile?.Equals(other.BackgroundFile) ?? false); } } } From ae55d392de9a05f9d878d79a319e4a39070a1bd3 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 15:28:39 +0100 Subject: [PATCH 012/193] only use `==` for comparion on primitive types --- osu.Game/Beatmaps/BeatmapMetadata.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 22a64820bc..a78ef25166 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -74,16 +74,16 @@ namespace osu.Game.Beatmaps return false; return onlineBeatmapSetID == other.onlineBeatmapSetID - && (Title?.Equals(other.Title) ?? false) - && (TitleUnicode?.Equals(other.TitleUnicode) ?? false) - && (Artist?.Equals(other.Artist) ?? false) - && (ArtistUnicode?.Equals(other.ArtistUnicode) ?? false) - && (AuthorString?.Equals(other.AuthorString) ?? false) - && (Source?.Equals(other.Source) ?? false) - && (Tags?.Equals(other.Tags) ?? false) + && Title == other.Title + && TitleUnicode == other.TitleUnicode + && Artist == other.Artist + && ArtistUnicode == other.ArtistUnicode + && AuthorString == other.AuthorString + && Source == other.Source + && Tags == other.Tags && PreviewTime == other.PreviewTime - && (AudioFile?.Equals(other.AudioFile) ?? false) - && (BackgroundFile?.Equals(other.BackgroundFile) ?? false); + && AudioFile == other.AudioFile + && BackgroundFile == other.BackgroundFile; } } } From 02fa1f9dd6c5418f68bccfd12d4015479855766b Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 26 Nov 2017 21:52:35 +0100 Subject: [PATCH 013/193] move shared stuff between MostPlayedBeatmapDrawable and DrawableScore to DrawableBeatmapRow --- .../Profile/Sections/DrawableBeatmapRow.cs | 118 +++++++++++ .../Historical/MostPlayedBeatmapDrawable.cs | 188 +++++++----------- .../Ranks/DrawablePerformanceScore.cs | 4 +- .../Profile/Sections/Ranks/DrawableScore.cs | 119 ++--------- .../Sections/Ranks/DrawableTotalScore.cs | 2 +- osu.Game/osu.Game.csproj | 1 + 6 files changed, 213 insertions(+), 219 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs diff --git a/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs b/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs new file mode 100644 index 0000000000..f6800a5f32 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs @@ -0,0 +1,118 @@ +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.Framework.Input; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Profile.Sections +{ + public abstract class DrawableBeatmapRow : Container + { + private const int fade_duration = 200; + + private Box underscoreLine; + private readonly Box coloredBackground; + private readonly Container background; + + protected abstract Drawable CreatePicture(); + + protected FillFlowContainer LeftFlowContainer { get; private set; } + protected FillFlowContainer RightFlowContainer { get; private set; } + + protected override Container Content { get; } + + protected DrawableBeatmapRow() + { + RelativeSizeAxes = Axes.X; + Height = 60; + InternalChildren = new Drawable[] + { + background = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 3, + Alpha = 0, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 1f, + Colour = Color4.Black.Opacity(0.2f), + }, + Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both } + }, + Content = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 0.97f, + }, + }; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colour) + { + AddRange(new Drawable[] + { + underscoreLine = new Box + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 1, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + CreatePicture(), + LeftFlowContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 10 }, + Direction = FillDirection.Vertical, + }, + } + }, + RightFlowContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Direction = FillDirection.Vertical, + }, + }); + + coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; + } + + 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); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs b/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs index 0f419ad44e..446af667f2 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs @@ -2,14 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE 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.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using OpenTK; @@ -17,144 +14,99 @@ using OpenTK.Graphics; namespace osu.Game.Overlays.Profile.Sections.Historical { - public class MostPlayedBeatmapDrawable : Container + public class MostPlayedBeatmapDrawable : DrawableBeatmapRow { private readonly BeatmapInfo beatmap; - private readonly OsuHoverContainer mapperContainer; - - private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0, 1f), - Radius = 2f, - Colour = Color4.Black.Opacity(0.25f), - }; - - private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0, 5f), - Radius = 10f, - Colour = Color4.Black.Opacity(0.25f), - }; + private readonly int playCount; + private OsuHoverContainer mapperContainer; public MostPlayedBeatmapDrawable(BeatmapInfo beatmap, int playCount) { this.beatmap = beatmap; - RelativeSizeAxes = Axes.X; - Height = 50; - Margin = new MarginPadding { Bottom = 10 }; - Masking = true; - EdgeEffect = edgeEffectNormal; + this.playCount = playCount; + } + protected override Drawable CreatePicture() => new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, Children = new Drawable[] { - new Box //Background for this container, otherwise the shadow would be visible - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f), - }, new Box //Image Background while loading { - Size = new Vector2(80, 50), - Colour = Color4.Black, + Size = new Vector2(80, 50), + Colour = Color4.Black, }, new DelayedLoadWrapper(new BeatmapSetCover(beatmap.BeatmapSet, BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, }), - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(10) { Left = 90 }, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new BeatmapMetadataContainer(beatmap), - new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new [] - { - new OsuSpriteText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Text = playCount.ToString(), - TextSize = 18, - Font = @"Exo2.0-SemiBoldItalic" - }, - new OsuSpriteText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Text = @"times played ", - TextSize = 12, - Font = @"Exo2.0-RegularItalic" - }, - } - } - }, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = @"mapped by ", - TextSize = 12, - }, - mapperContainer = new OsuHoverContainer - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = beatmap.Metadata.AuthorString, - TextSize = 12, - Font = @"Exo2.0-MediumItalic" - } - } - }, - } - }, - }, - } - }; - } + }, + }; [BackgroundDependencyLoader(true)] private void load(UserProfileOverlay profileOverlay) { + LeftFlowContainer.Add(new BeatmapMetadataContainer(beatmap)); + LeftFlowContainer.Add(new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"mapped by ", + TextSize = 12, + }, + mapperContainer = new OsuHoverContainer + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = beatmap.Metadata.AuthorString, + TextSize = 12, + Font = @"Exo2.0-MediumItalic" + } + } + }, + } + }); + + RightFlowContainer.Add(new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Text = playCount.ToString(), + TextSize = 18, + Font = @"Exo2.0-SemiBoldItalic" + }, + new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Text = @"times played ", + TextSize = 12, + Font = @"Exo2.0-RegularItalic" + }, + } + }); + if(profileOverlay != null) mapperContainer.Action = () => profileOverlay.ShowUser(beatmap.BeatmapSet.Metadata.Author); } - - protected override bool OnHover(InputState state) - { - TweenEdgeEffectTo(edgeEffectHovered, 120, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - TweenEdgeEffectTo(edgeEffectNormal, 120, Easing.OutQuint); - base.OnHoverLost(state); - } } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs index e6ba5b26ac..cd13d14575 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks private void load(OsuColour colour) { double pp = Score.PP ?? 0; - Stats.Add(new OsuSpriteText + RightFlowContainer.Add(new OsuSpriteText { Text = $"{pp:0}pp", Anchor = Anchor.TopRight, @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks if (weight.HasValue) { - Stats.Add(new OsuSpriteText + RightFlowContainer.Add(new OsuSpriteText { Text = $"weighted: {pp * weight:0}pp ({weight:P0})", Anchor = Anchor.TopRight, diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs index 4d210f399e..db7647ce0e 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs @@ -11,24 +11,14 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Overlays.Profile.Sections.Ranks { - public abstract class DrawableScore : Container + public abstract class DrawableScore : DrawableBeatmapRow { - 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) { @@ -38,85 +28,21 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Height = 60; Children = new Drawable[] { - background = new Container + modsContainer = new ScoreModsContainer { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 3, - Alpha = 0, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 1f, - Colour = Color4.Black.Opacity(0.2f), - }, - Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both } - }, - new Container - { - 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 } - } - } - }, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Width = 60, + Margin = new MarginPadding { Right = 160 } + } }; } [BackgroundDependencyLoader(true)] private void load(OsuColour colour) { - coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; - - Stats.Add(new OsuSpriteText + RightFlowContainer.Add(new OsuSpriteText { Text = $"accuracy: {Score.Accuracy:P2}", Anchor = Anchor.TopRight, @@ -127,26 +53,23 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Depth = -1, }); - metadata.Add(new BeatmapMetadataContainer(Score.Beatmap)); + LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap)); + LeftFlowContainer.Add(new OsuSpriteText + { + Text = Score.Date.LocalDateTime.ToShortDateString(), + TextSize = 11, + Colour = OsuColour.Gray(0xAA), + }); foreach (Mod mod in Score.Mods) modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) }); } - protected override bool OnClick(InputState state) => true; - - protected override bool OnHover(InputState state) + protected override Drawable CreatePicture() => new DrawableRank(Score.Rank) { - 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); - } + RelativeSizeAxes = Axes.Y, + Width = 60, + FillMode = FillMode.Fit, + }; } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs index 537b208b39..1539142f1d 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks [BackgroundDependencyLoader] private void load() { - Stats.Add(new OsuSpriteText + RightFlowContainer.Add(new OsuSpriteText { Text = Score.TotalScore.ToString("#,###"), Anchor = Anchor.TopRight, diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4921c4ce05..b774602b76 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -294,6 +294,7 @@ + From 4c68090e59d2139c6bc53033c423b138c7963f3e Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 26 Nov 2017 22:06:03 +0100 Subject: [PATCH 014/193] separate GetUserBeatmapsRequest and GetUserMostPlayedBeatmapsRequest --- .../API/Requests/GetUserBeatmapsRequest.cs | 16 ++-------------- .../Requests/GetUserMostPlayedBeatmapsRequest.cs | 16 ++++++++++------ .../PaginatedMostPlayedBeatmapContainer.cs | 2 +- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index dca0395e3a..691f8496d9 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -1,19 +1,18 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using Humanizer; using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public abstract class GetUserBeatmapsRequest : APIRequest> + public class GetUserBeatmapsRequest : APIRequest> { private readonly long userId; private readonly int offset; private readonly BeatmapSetType type; - protected GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) { this.userId = userId; this.offset = offset; @@ -23,19 +22,8 @@ namespace osu.Game.Online.API.Requests protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}"; } - public class GetUserBeatmapsRequest : GetUserBeatmapsRequest - { - public GetUserBeatmapsRequest(long userID, BeatmapSetType type, int offset = 0) - : base(userID, type, offset) - { - if(type == BeatmapSetType.MostPlayed) - throw new ArgumentException("Please use " + nameof(GetUserMostPlayedBeatmapsRequest) + " instead"); - } - } - public enum BeatmapSetType { - MostPlayed, Favourite, RankedAndApproved, Unranked, diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index 2d08f09d20..c45ef734e6 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -1,21 +1,25 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public class GetUserMostPlayedBeatmapsRequest : GetUserBeatmapsRequest + public class GetUserMostPlayedBeatmapsRequest : APIRequest> { - public GetUserMostPlayedBeatmapsRequest(long userID, BeatmapSetType type, int offset = 0) - : base(userID, type, offset) + private readonly long userId; + private readonly int offset; + + public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0) { - if (type != BeatmapSetType.MostPlayed) - throw new ArgumentException("Please use " + nameof(GetUserBeatmapsRequest) + " instead"); + this.userId = userId; + this.offset = offset; } + + protected override string Target => $@"users/{userId}/beatmapsets/most_played?offset={offset}"; } public class UserMostPlayedBeatmapsResponse diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 916f1f437a..b7df60a7a2 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { base.ShowMore(); - var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, BeatmapSetType.MostPlayed, VisiblePages++ * ItemsPerPage); + var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); req.Success += beatmaps => { From 70b6071898afa38590f00ca43accedb6b808e754 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 26 Nov 2017 22:13:52 +0100 Subject: [PATCH 015/193] add license header --- osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs b/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs index f6800a5f32..4d2affd77c 100644 --- a/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs +++ b/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs @@ -1,4 +1,7 @@ -using osu.Framework.Allocation; +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; From bf8d15108e0085204ff7143cf6c208b04bb35620 Mon Sep 17 00:00:00 2001 From: Santeri Nogelainen Date: Mon, 27 Nov 2017 13:39:01 +0200 Subject: [PATCH 016/193] headerbutton now derives from osubutton --- .../Overlays/BeatmapSet/DownloadButton.cs | 2 +- .../Overlays/BeatmapSet/FavouriteButton.cs | 3 +- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 42 +++++++++---------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs index 18a0cfd968..b256f82a22 100644 --- a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs @@ -14,10 +14,10 @@ namespace osu.Game.Overlays.BeatmapSet public DownloadButton(string title, string subtitle) { Width = 120; - RelativeSizeAxes = Axes.Y; Child = new Container { + Depth = -1, RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = 10 }, Children = new Drawable[] diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs index 9fd4ac177c..af4c56c614 100644 --- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs @@ -17,7 +17,6 @@ namespace osu.Game.Overlays.BeatmapSet public FavouriteButton() { - RelativeSizeAxes = Axes.Y; Container pink; SpriteIcon icon; @@ -25,6 +24,7 @@ namespace osu.Game.Overlays.BeatmapSet { pink = new Container { + Depth = -1, RelativeSizeAxes = Axes.Both, Alpha = 0f, Children = new Drawable[] @@ -45,6 +45,7 @@ namespace osu.Game.Overlays.BeatmapSet }, icon = new SpriteIcon { + Depth = -1, Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.fa_heart_o, diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index 3075020fe6..4616128f1a 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -5,12 +5,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; +using osu.Framework.Audio; +using osu.Framework.Allocation; namespace osu.Game.Overlays.BeatmapSet { - public class HeaderButton : OsuClickableContainer + public class HeaderButton : OsuButton { private readonly Container content; @@ -18,28 +21,25 @@ namespace osu.Game.Overlays.BeatmapSet public HeaderButton() { - CornerRadius = 3; - Masking = true; + Height = 0; + RelativeSizeAxes = Axes.Y; - InternalChildren = new Drawable[] + AddInternal(content = new Container { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"094c5f"), - }, - new Triangles - { - RelativeSizeAxes = Axes.Both, - ColourLight = OsuColour.FromHex(@"0f7c9b"), - ColourDark = OsuColour.FromHex(@"094c5f"), - TriangleScale = 1.5f, - }, - content = new Container - { - RelativeSizeAxes = Axes.Both, - }, - }; + RelativeSizeAxes = Axes.Both + }); } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) + { + Masking = true; + CornerRadius = 3; + BackgroundColour = OsuColour.FromHex(@"094c5f"); + Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); + Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); + Triangles.TriangleScale = 1.5f; + } + } } From 96f782e75a65e61f6f54c8bf225cf3ffe8b5b816 Mon Sep 17 00:00:00 2001 From: Santeri Nogelainen Date: Mon, 27 Nov 2017 13:51:56 +0200 Subject: [PATCH 017/193] fix object reference issue maybe? --- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index 4616128f1a..2296a4e8c0 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -36,9 +36,9 @@ namespace osu.Game.Overlays.BeatmapSet Masking = true; CornerRadius = 3; BackgroundColour = OsuColour.FromHex(@"094c5f"); - Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); - Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); - Triangles.TriangleScale = 1.5f; + this.Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); + this.Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); + this.Triangles.TriangleScale = 1.5f; } } From d87235a2891e64708a7e617af753e420eb4efa3e Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Mon, 27 Nov 2017 20:08:16 +0100 Subject: [PATCH 018/193] prevent inserting duplicate metadata --- osu.Game/Beatmaps/BeatmapStore.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 3875202e32..ef658d2ff6 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; using osu.Game.Database; @@ -32,6 +33,15 @@ namespace osu.Game.Beatmaps { var context = GetContext(); + foreach (var beatmap in beatmapSet.Beatmaps.Where(b => b.Metadata != null)) + { + var contextMetadata = context.Set().Local.SingleOrDefault(e => e.Equals(beatmap.Metadata)); + if (contextMetadata != null) + beatmap.Metadata = contextMetadata; + else + context.BeatmapMetadata.Attach(beatmap.Metadata); + } + context.BeatmapSetInfo.Attach(beatmapSet); context.SaveChanges(); From c058065a3ab78258ec24ef90a33738a4a8b1af69 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Mon, 27 Nov 2017 20:24:01 +0100 Subject: [PATCH 019/193] remove unnecessary using --- osu.Game/Beatmaps/BeatmapStore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index ef658d2ff6..352f793aac 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; using osu.Game.Database; From 7f068c0c683c297d4d1257ebe58eca6db26be55c Mon Sep 17 00:00:00 2001 From: jorolf Date: Mon, 27 Nov 2017 21:13:01 +0100 Subject: [PATCH 020/193] correct string mistake --- .../Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index b7df60a7a2..cec0e9a775 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer { public PaginatedMostPlayedBeatmapContainer(Bindable user) - :base(user, "Most Played Beatmaps", "No performance records. :(") + :base(user, "Most Played Beatmaps", "No records. :(") { ItemsPerPage = 5; From 86de6f8252d52a5f7d327fda28484dbe371813e6 Mon Sep 17 00:00:00 2001 From: Santeri Date: Tue, 28 Nov 2017 00:09:58 +0200 Subject: [PATCH 021/193] derives from trianglebutton rather than osubutton --- osu.Game/Overlays/BeatmapSet/DownloadButton.cs | 4 ++-- osu.Game/Overlays/BeatmapSet/FavouriteButton.cs | 4 ++-- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 16 ++++------------ 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs index b256f82a22..47787d2ced 100644 --- a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.BeatmapSet { Width = 120; - Child = new Container + Add(new Container { Depth = -1, RelativeSizeAxes = Axes.Both, @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Right = 5 }, }, }, - }; + }); } } } diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs index af4c56c614..77be4da5f8 100644 --- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.BeatmapSet Container pink; SpriteIcon icon; - Children = new Drawable[] + AddRange(new Drawable[] { pink = new Container { @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.BeatmapSet Size = new Vector2(18), Shadow = false, }, - }; + }); Favourited.ValueChanged += value => { diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index 2296a4e8c0..6c7edb252e 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -13,21 +13,13 @@ using osu.Framework.Allocation; namespace osu.Game.Overlays.BeatmapSet { - public class HeaderButton : OsuButton + public class HeaderButton : TriangleButton { - private readonly Container content; - - protected override Container Content => content; public HeaderButton() { Height = 0; RelativeSizeAxes = Axes.Y; - - AddInternal(content = new Container - { - RelativeSizeAxes = Axes.Both - }); } [BackgroundDependencyLoader] @@ -36,9 +28,9 @@ namespace osu.Game.Overlays.BeatmapSet Masking = true; CornerRadius = 3; BackgroundColour = OsuColour.FromHex(@"094c5f"); - this.Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); - this.Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); - this.Triangles.TriangleScale = 1.5f; + Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); + Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); + Triangles.TriangleScale = 1.5f; } } From 49949bf6984d712a0e3267f7f23350180983888d Mon Sep 17 00:00:00 2001 From: Santeri Date: Tue, 28 Nov 2017 00:20:44 +0200 Subject: [PATCH 022/193] fix minor param/directive errors --- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index 6c7edb252e..cfcf26d0bd 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -2,12 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Containers; using osu.Framework.Audio; using osu.Framework.Allocation; @@ -23,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet } [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audio) + private void load() { Masking = true; CornerRadius = 3; From e9277d8cb1e1d1d0db6b9cfbc638f34a6c3fe346 Mon Sep 17 00:00:00 2001 From: Santeri Date: Tue, 28 Nov 2017 00:27:58 +0200 Subject: [PATCH 023/193] last directive error... --- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index cfcf26d0bd..371e438a34 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -4,7 +4,6 @@ using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Framework.Audio; using osu.Framework.Allocation; namespace osu.Game.Overlays.BeatmapSet From 898c49c19db9ab54cd5eaf2bd91156aaac5b573d Mon Sep 17 00:00:00 2001 From: Santeri Date: Tue, 28 Nov 2017 16:14:32 +0200 Subject: [PATCH 024/193] remove unnecessary assignments --- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index 371e438a34..cddc05c554 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -20,8 +20,6 @@ namespace osu.Game.Overlays.BeatmapSet [BackgroundDependencyLoader] private void load() { - Masking = true; - CornerRadius = 3; BackgroundColour = OsuColour.FromHex(@"094c5f"); Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); From cd653c1cbc5a5f11652ddfda35e4690502b2f4c0 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Wed, 29 Nov 2017 21:28:02 +0100 Subject: [PATCH 025/193] split storyboard loading into `GetStoryboard()` --- osu.Game/Beatmaps/BeatmapManager.cs | 32 ++++++++++++++++++++++------- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 ++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 0641cabcd8..a5578bcfde 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -25,6 +25,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; +using osu.Game.Storyboards; namespace osu.Game.Beatmaps { @@ -577,13 +578,6 @@ namespace osu.Game.Beatmaps beatmap = decoder.Decode(stream); } - if (beatmap == null || BeatmapSetInfo.StoryboardFile == null) - return beatmap; - - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - decoder.Decode(stream, beatmap); - - return beatmap; } catch @@ -623,6 +617,30 @@ namespace osu.Game.Beatmaps } protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile))); + + protected override Storyboard GetStoryboard() + { + try + { + Beatmap beatmap = Beatmap; + + if (beatmap == null || BeatmapSetInfo.StoryboardFile == null) + return new Storyboard(); + + BeatmapDecoder decoder; + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + decoder = BeatmapDecoder.GetDecoder(stream); + + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) + decoder.Decode(stream, beatmap); + + return beatmap.Storyboard; + } + catch + { + return new Storyboard(); + } + } } /// diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 2a8178882e..456bf29387 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using osu.Game.Storyboards; namespace osu.Game.Beatmaps { @@ -40,6 +41,7 @@ namespace osu.Game.Beatmaps protected abstract Texture GetBackground(); protected abstract Track GetTrack(); protected virtual Waveform GetWaveform() => new Waveform(); + protected virtual Storyboard GetStoryboard() => new Storyboard(); public bool BeatmapLoaded => beatmap.IsValueCreated; public Beatmap Beatmap => beatmap.Value.Result; From 96f5bd33237440dcb6d327f18ec2562945d5e0b4 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Wed, 29 Nov 2017 21:54:04 +0100 Subject: [PATCH 026/193] remove Storyboard from Beatmap, add it to WorkingBeatmap --- osu.Game/Beatmaps/Beatmap.cs | 6 ------ osu.Game/Beatmaps/WorkingBeatmap.cs | 9 +++++++++ osu.Game/Storyboards/Storyboard.cs | 27 ++++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 35b6cc2b02..2c437f8a18 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -41,11 +41,6 @@ namespace osu.Game.Beatmaps /// public double TotalBreakTime => Breaks.Sum(b => b.Duration); - /// - /// The Beatmap's Storyboard. - /// - public Storyboard Storyboard = new Storyboard(); - /// /// Constructs a new beatmap. /// @@ -57,7 +52,6 @@ namespace osu.Game.Beatmaps Breaks = original?.Breaks ?? Breaks; ComboColors = original?.ComboColors ?? ComboColors; HitObjects = original?.HitObjects ?? HitObjects; - Storyboard = original?.Storyboard ?? Storyboard; if (original == null && Metadata == null) { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 456bf29387..cd6e1bb8c2 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -35,6 +35,7 @@ namespace osu.Game.Beatmaps background = new AsyncLazy(populateBackground); track = new AsyncLazy(populateTrack); waveform = new AsyncLazy(populateWaveform); + storyboard = new AsyncLazy(populateStoryboard); } protected abstract Beatmap GetBeatmap(); @@ -86,6 +87,13 @@ namespace osu.Game.Beatmaps private Waveform populateWaveform() => GetWaveform(); + public bool StoryboardLoaded => storyboard.IsValueCreated; + public Storyboard Storyboard => storyboard.Value.Result; + public async Task GetStoryboardAsync() => await storyboard.Value; + private readonly AsyncLazy storyboard; + + private Storyboard populateStoryboard() => GetStoryboard(); + public void TransferTo(WorkingBeatmap other) { if (track.IsValueCreated && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) @@ -99,6 +107,7 @@ namespace osu.Game.Beatmaps { if (BackgroundLoaded) Background?.Dispose(); if (WaveformLoaded) Waveform?.Dispose(); + if (StoryboardLoaded) Storyboard?.Dispose(); } public void DisposeTrack() diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 59cbe74650..4eca910c1e 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -5,10 +5,11 @@ using osu.Game.Beatmaps; using osu.Game.Storyboards.Drawables; using System.Collections.Generic; using System.Linq; +using System; namespace osu.Game.Storyboards { - public class Storyboard + public class Storyboard : IDisposable { private readonly Dictionary layers = new Dictionary(); public IEnumerable Layers => layers.Values; @@ -59,5 +60,29 @@ namespace osu.Game.Storyboards } return drawable; } + + #region Disposal + + ~Storyboard() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private bool isDisposed; + + protected virtual void Dispose(bool isDisposing) + { + if (isDisposed) + return; + isDisposed = true; + } + + #endregion } } From 14fdf98abc0aa6981ddf5d56c263c3b3a0446a7b Mon Sep 17 00:00:00 2001 From: jorolf Date: Wed, 29 Nov 2017 23:08:46 +0100 Subject: [PATCH 027/193] rename GetBeatmapSetsResponse --- .../{GetBeatmapSetsResponse.cs => APIResponseBeatmapSet.cs} | 6 +++--- osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs | 2 +- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 2 +- .../Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs | 2 +- osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game/Online/API/Requests/{GetBeatmapSetsResponse.cs => APIResponseBeatmapSet.cs} (91%) diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs b/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs similarity index 91% rename from osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs rename to osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs index d187cabf31..90d99446c7 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs +++ b/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs @@ -10,7 +10,7 @@ using System; namespace osu.Game.Online.API.Requests { - public class GetBeatmapSetsResponse : BeatmapMetadata // todo: this is a bit wrong... + public class APIResponseBeatmapSet : BeatmapMetadata // todo: this is a bit wrong... { [JsonProperty(@"covers")] private BeatmapSetOnlineCovers covers { get; set; } @@ -45,7 +45,7 @@ namespace osu.Game.Online.API.Requests } [JsonProperty(@"beatmaps")] - private IEnumerable beatmaps { get; set; } + private IEnumerable beatmaps { get; set; } public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) { @@ -69,7 +69,7 @@ namespace osu.Game.Online.API.Requests }; } - private class GetBeatmapSetsBeatmapResponse : BeatmapMetadata + private class APIResponseSetsBeatmap : BeatmapMetadata { [JsonProperty(@"id")] private int onlineBeatmapID { get; set; } diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs index e0fdc9adf2..1e6ceaafc6 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs @@ -3,7 +3,7 @@ namespace osu.Game.Online.API.Requests { - public class GetBeatmapSetRequest : APIRequest + public class GetBeatmapSetRequest : APIRequest { private readonly int beatmapSetId; diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index 691f8496d9..173562e04d 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public class GetUserBeatmapsRequest : APIRequest> + public class GetUserBeatmapsRequest : APIRequest> { private readonly long userId; private readonly int offset; diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index c45ef734e6..80409fc3b9 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -34,7 +34,7 @@ namespace osu.Game.Online.API.Requests private BeatmapInfo beatmap; [JsonProperty] - private GetBeatmapSetsResponse beatmapSet; + private APIResponseBeatmapSet beatmapSet; public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) { diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 56858b3d56..4e6c70124f 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests { - public class SearchBeatmapSetsRequest : APIRequest> + public class SearchBeatmapSetsRequest : APIRequest> { private readonly string query; private readonly RulesetInfo ruleset; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b774602b76..947300cba0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -285,7 +285,7 @@ - + From 36cfa552f43e6ec9f1e3da59022601a524bb0f0d Mon Sep 17 00:00:00 2001 From: Nicolas Brassard Date: Wed, 29 Nov 2017 23:03:26 -0800 Subject: [PATCH 028/193] Fix SensitivitySlider keyboard control --- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 5ebac37cc8..53704ec72d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -80,6 +80,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input set { + KeyboardStep = 0.01f; + BindableDouble doubleValue = (BindableDouble)value; // create a second layer of bindable so we can only handle state changes when not being dragged. @@ -105,8 +107,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input public SensitivitySlider() { - KeyboardStep = 0.01f; - Current.ValueChanged += newValue => { if (!isDragging && Sensitivity != null) @@ -133,4 +133,4 @@ namespace osu.Game.Overlays.Settings.Sections.Input public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : Current.Value.ToString(@"0.##x"); } } -} \ No newline at end of file +} From 016057ab014954b09c5510ed9ae2142cf2f1d6f3 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 30 Nov 2017 16:49:53 +0100 Subject: [PATCH 029/193] readd storyboard to beatmap + minor cleanup --- osu.Game/Beatmaps/Beatmap.cs | 6 ++++++ osu.Game/Beatmaps/BeatmapManager.cs | 17 +++++------------ osu.Game/Screens/Play/Player.cs | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 2c437f8a18..35b6cc2b02 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -41,6 +41,11 @@ namespace osu.Game.Beatmaps /// public double TotalBreakTime => Breaks.Sum(b => b.Duration); + /// + /// The Beatmap's Storyboard. + /// + public Storyboard Storyboard = new Storyboard(); + /// /// Constructs a new beatmap. /// @@ -52,6 +57,7 @@ namespace osu.Game.Beatmaps Breaks = original?.Breaks ?? Breaks; ComboColors = original?.ComboColors ?? ComboColors; HitObjects = original?.HitObjects ?? HitObjects; + Storyboard = original?.Storyboard ?? Storyboard; if (original == null && Metadata == null) { diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index a5578bcfde..4f7c0051c1 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -569,16 +569,11 @@ namespace osu.Game.Beatmaps { try { - Beatmap beatmap; - - BeatmapDecoder decoder; using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) { - decoder = BeatmapDecoder.GetDecoder(stream); - beatmap = decoder.Decode(stream); + BeatmapDecoder decoder = BeatmapDecoder.GetDecoder(stream); + return decoder.Decode(stream); } - - return beatmap; } catch { @@ -622,9 +617,7 @@ namespace osu.Game.Beatmaps { try { - Beatmap beatmap = Beatmap; - - if (beatmap == null || BeatmapSetInfo.StoryboardFile == null) + if (Beatmap == null || BeatmapSetInfo.StoryboardFile == null) return new Storyboard(); BeatmapDecoder decoder; @@ -632,9 +625,9 @@ namespace osu.Game.Beatmaps decoder = BeatmapDecoder.GetDecoder(stream); using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - decoder.Decode(stream, beatmap); + decoder.Decode(stream, Beatmap); - return beatmap.Storyboard; + return Beatmap.Storyboard; } catch { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index dc746b305c..b5b09504da 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -245,7 +245,7 @@ namespace osu.Game.Screens.Play private void initializeStoryboard(bool asyncLoad) { - var beatmap = Beatmap.Value.Beatmap; + var beatmap = Beatmap.Value; storyboard = beatmap.Storyboard.CreateDrawable(Beatmap.Value); storyboard.Masking = true; @@ -388,7 +388,7 @@ namespace osu.Game.Screens.Play initializeStoryboard(true); var beatmap = Beatmap.Value; - var storyboardVisible = showStoryboard && beatmap.Beatmap.Storyboard.HasDrawable; + var storyboardVisible = showStoryboard && beatmap.Storyboard.HasDrawable; storyboardContainer.FadeColour(new Color4(opacity, opacity, opacity, 1), 800); storyboardContainer.FadeTo(storyboardVisible && opacity > 0 ? 1 : 0); From c16925059c992cc428d8cf71862d74ca9a063d1a Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 30 Nov 2017 19:16:13 +0100 Subject: [PATCH 030/193] split parsing a beatmap and parsing a storyboard --- .../Beatmaps/Formats/OsuLegacyDecoderTest.cs | 12 +- .../Beatmaps/IO/OszArchiveReaderTest.cs | 2 +- osu.Game.Tests/Visual/TestCaseStoryboard.cs | 2 +- osu.Game/Beatmaps/Beatmap.cs | 6 - osu.Game/Beatmaps/BeatmapManager.cs | 16 +- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 25 +- osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 400 ++++++++++-------- osu.Game/Tests/Visual/TestCasePlayer.cs | 2 +- 8 files changed, 262 insertions(+), 203 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index 95b691e07f..175e07f99c 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmap = decoder.Decode(new StreamReader(stream)); + var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); var meta = beatmap.BeatmapInfo.Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmapInfo = decoder.Decode(new StreamReader(stream)).BeatmapInfo; + var beatmapInfo = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; Assert.AreEqual(0, beatmapInfo.AudioLeadIn); Assert.AreEqual(false, beatmapInfo.Countdown); Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); @@ -61,7 +61,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmap = decoder.Decode(new StreamReader(stream)).BeatmapInfo; + var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; int[] expectedBookmarks = { 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmap = decoder.Decode(new StreamReader(stream)); + var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); var difficulty = beatmap.BeatmapInfo.BaseDifficulty; Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(4, difficulty.CircleSize); @@ -101,7 +101,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmap = decoder.Decode(new StreamReader(stream)); + var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); Color4[] expected = { new Color4(142, 199, 255, 255), @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmap = decoder.Decode(new StreamReader(stream)); + var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); var curveData = beatmap.HitObjects[0] as IHasCurve; var positionData = beatmap.HitObjects[0] as IHasPosition; diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 12bbde5b57..7b30a4f1fe 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO BeatmapMetadata meta; using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) - meta = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; + meta = BeatmapDecoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); diff --git a/osu.Game.Tests/Visual/TestCaseStoryboard.cs b/osu.Game.Tests/Visual/TestCaseStoryboard.cs index 1dad106cbe..0a158f5662 100644 --- a/osu.Game.Tests/Visual/TestCaseStoryboard.cs +++ b/osu.Game.Tests/Visual/TestCaseStoryboard.cs @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true }; storyboardContainer.Clock = decoupledClock; - storyboard = working.Beatmap.Storyboard.CreateDrawable(beatmapBacking); + storyboard = working.Storyboard.CreateDrawable(beatmapBacking); storyboard.Passing = false; storyboardContainer.Add(storyboard); diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 35b6cc2b02..2c437f8a18 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -41,11 +41,6 @@ namespace osu.Game.Beatmaps /// public double TotalBreakTime => Breaks.Sum(b => b.Duration); - /// - /// The Beatmap's Storyboard. - /// - public Storyboard Storyboard = new Storyboard(); - /// /// Constructs a new beatmap. /// @@ -57,7 +52,6 @@ namespace osu.Game.Beatmaps Breaks = original?.Breaks ?? Breaks; ComboColors = original?.ComboColors ?? ComboColors; HitObjects = original?.HitObjects ?? HitObjects; - Storyboard = original?.Storyboard ?? Storyboard; if (original == null && Metadata == null) { diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 4f7c0051c1..e5f2064ee3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -495,7 +495,7 @@ namespace osu.Game.Beatmaps BeatmapMetadata metadata; using (var stream = new StreamReader(reader.GetStream(mapName))) - metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; + metadata = BeatmapDecoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; // check if a set already exists with the same online id. beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo @@ -519,7 +519,7 @@ namespace osu.Game.Beatmaps ms.Position = 0; var decoder = BeatmapDecoder.GetDecoder(sr); - Beatmap beatmap = decoder.Decode(sr); + Beatmap beatmap = decoder.DecodeBeatmap(sr); beatmap.BeatmapInfo.Path = name; beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); @@ -572,7 +572,7 @@ namespace osu.Game.Beatmaps using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) { BeatmapDecoder decoder = BeatmapDecoder.GetDecoder(stream); - return decoder.Decode(stream); + return decoder.DecodeBeatmap(stream); } } catch @@ -615,19 +615,17 @@ namespace osu.Game.Beatmaps protected override Storyboard GetStoryboard() { + if (BeatmapSetInfo?.StoryboardFile == null) + return new Storyboard(); + try { - if (Beatmap == null || BeatmapSetInfo.StoryboardFile == null) - return new Storyboard(); - BeatmapDecoder decoder; using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) decoder = BeatmapDecoder.GetDecoder(stream); using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - decoder.Decode(stream, Beatmap); - - return Beatmap.Storyboard; + return decoder.DecodeStoryboard(stream); } catch { diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index 7e1a87085c..d785618780 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats { @@ -35,17 +36,12 @@ namespace osu.Game.Beatmaps.Formats decoders[magic] = typeof(T); } - public virtual Beatmap Decode(StreamReader stream) + public virtual Beatmap DecodeBeatmap(StreamReader stream) { - return ParseFile(stream); + return ParseBeatmap(stream); } - public virtual void Decode(StreamReader stream, Beatmap beatmap) - { - ParseFile(stream, beatmap); - } - - protected virtual Beatmap ParseFile(StreamReader stream) + protected virtual Beatmap ParseBeatmap(StreamReader stream) { var beatmap = new Beatmap { @@ -56,10 +52,19 @@ namespace osu.Game.Beatmaps.Formats }, }; - ParseFile(stream, beatmap); + ParseBeatmap(stream, beatmap); return beatmap; } - protected abstract void ParseFile(StreamReader stream, Beatmap beatmap); + protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap); + + public virtual Storyboard DecodeStoryboard(StreamReader stream) + { + var storyboard = new Storyboard(); + ParseStoryboard(stream, storyboard); + return storyboard; + } + + protected abstract void ParseStoryboard(StreamReader stream, Storyboard storyboard); } } diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 11631e9447..e0af018059 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -54,20 +54,135 @@ namespace osu.Game.Beatmaps.Formats beatmapVersion = int.Parse(header.Substring(17)); } - private enum Section + // + + protected override Beatmap ParseBeatmap(StreamReader stream) { - None, - General, - Editor, - Metadata, - Difficulty, - Events, - TimingPoints, - Colours, - HitObjects, - Variables, + return new LegacyBeatmap(base.ParseBeatmap(stream)); } + public override Beatmap DecodeBeatmap(StreamReader stream) + { + return new LegacyBeatmap(base.DecodeBeatmap(stream)); + } + + protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) + { + if (beatmap == null) + throw new ArgumentNullException(nameof(beatmap)); + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion; + + Section section = Section.None; + bool hasCustomColours = false; + + string line; + while ((line = stream.ReadLine()) != null) + { + if (string.IsNullOrWhiteSpace(line)) + continue; + + if (line.StartsWith("//")) + continue; + + if (line.StartsWith(@"osu file format v")) + { + beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); + continue; + } + + if (line.StartsWith(@"[") && line.EndsWith(@"]")) + { + if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) + throw new InvalidDataException($@"Unknown osu section {line}"); + continue; + } + + switch (section) + { + case Section.General: + handleGeneral(beatmap, line); + break; + case Section.Editor: + handleEditor(beatmap, line); + break; + case Section.Metadata: + handleMetadata(beatmap, line); + break; + case Section.Difficulty: + handleDifficulty(beatmap, line); + break; + case Section.Events: + handleEvents(beatmap, line); + break; + case Section.TimingPoints: + handleTimingPoints(beatmap, line); + break; + case Section.Colours: + handleColours(beatmap, line, ref hasCustomColours); + break; + case Section.HitObjects: + + // If the ruleset wasn't specified, assume the osu!standard ruleset. + if (parser == null) + parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); + + var obj = parser.Parse(line); + + if (obj != null) + beatmap.HitObjects.Add(obj); + + break; + case Section.Variables: + handleVariables(line); + break; + } + } + + foreach (var hitObject in beatmap.HitObjects) + hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); + } + + protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) + { + if (storyboard == null) + throw new ArgumentNullException(nameof(storyboard)); + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + Section section = Section.None; + StoryboardSprite storyboardSprite = null; + CommandTimelineGroup timelineGroup = null; + + string line; + while ((line = stream.ReadLine()) != null) + { + if (string.IsNullOrWhiteSpace(line)) + continue; + + if (line.StartsWith("//")) + continue; + + if (line.StartsWith(@"[") && line.EndsWith(@"]")) + { + if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) + throw new InvalidDataException($@"Unknown osu section {line}"); + continue; + } + + switch (section) + { + case Section.Events: + handleEvents(storyboard, line, ref storyboardSprite, ref timelineGroup); + break; + } + } + } + + // + private void handleGeneral(Beatmap beatmap, string line) { if (beatmap == null) @@ -240,38 +355,49 @@ namespace osu.Game.Beatmaps.Formats } } - /// - /// Decodes any beatmap variables present in a line into their real values. - /// - /// The line which may contains variables. - private void decodeVariables(ref string line) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - - while (line.IndexOf('$') >= 0) - { - string origLine = line; - string[] split = line.Split(','); - for (int i = 0; i < split.Length; i++) - { - var item = split[i]; - if (item.StartsWith("$") && variables.ContainsKey(item)) - split[i] = variables[item]; - } - - line = string.Join(",", split); - if (line == origLine) break; - } - } - - private void handleEvents(Beatmap beatmap, string line, ref StoryboardSprite storyboardSprite, ref CommandTimelineGroup timelineGroup) + private void handleEvents(Beatmap beatmap, string line) { if (line == null) throw new ArgumentNullException(nameof(line)); if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); + decodeVariables(ref line); + + string[] split = line.Split(','); + + EventType type; + if (!Enum.TryParse(split[0], out type)) + throw new InvalidDataException($@"Unknown event type {split[0]}"); + + switch (type) + { + case EventType.Background: + string filename = split[2].Trim('"'); + beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + break; + case EventType.Break: + var breakEvent = new BreakPeriod + { + StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), + EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo) + }; + + if (!breakEvent.HasEffect) + return; + + beatmap.Breaks.Add(breakEvent); + break; + } + } + + private void handleEvents(Storyboard storyboard, string line, ref StoryboardSprite storyboardSprite, ref CommandTimelineGroup timelineGroup) + { + if (line == null) + throw new ArgumentNullException(nameof(line)); + if (storyboard == null) + throw new ArgumentNullException(nameof(storyboard)); + var depth = 0; while (line.StartsWith(" ") || line.StartsWith("_")) { @@ -293,26 +419,6 @@ namespace osu.Game.Beatmaps.Formats switch (type) { - case EventType.Video: - case EventType.Background: - string filename = split[2].Trim('"'); - - if (type == EventType.Background) - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; - - break; - case EventType.Break: - var breakEvent = new BreakPeriod - { - StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), - EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo) - }; - - if (!breakEvent.HasEffect) - return; - - beatmap.Breaks.Add(breakEvent); - break; case EventType.Sprite: { var layer = parseLayer(split[1]); @@ -321,7 +427,7 @@ namespace osu.Game.Beatmaps.Formats var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); - beatmap.Storyboard.GetLayer(layer).Add(storyboardSprite); + storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Animation: @@ -335,7 +441,7 @@ namespace osu.Game.Beatmaps.Formats var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); - beatmap.Storyboard.GetLayer(layer).Add(storyboardSprite); + storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Sample: @@ -344,7 +450,7 @@ namespace osu.Game.Beatmaps.Formats var layer = parseLayer(split[2]); var path = cleanFilename(split[3]); var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - beatmap.Storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); } break; } @@ -456,9 +562,15 @@ namespace osu.Game.Beatmaps.Formats var type = split[4]; switch (type) { - case "A": timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); break; - case "H": timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); break; - case "V": timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); break; + case "A": + timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); + break; + case "H": + timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); + break; + case "V": + timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); + break; } } break; @@ -471,30 +583,6 @@ namespace osu.Game.Beatmaps.Formats } } - private static string cleanFilename(string path) - => FileSafety.PathStandardise(path.Trim('\"')); - - private static Anchor parseOrigin(string value) - { - var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); - switch (origin) - { - case LegacyOrigins.TopLeft: return Anchor.TopLeft; - case LegacyOrigins.TopCentre: return Anchor.TopCentre; - case LegacyOrigins.TopRight: return Anchor.TopRight; - case LegacyOrigins.CentreLeft: return Anchor.CentreLeft; - case LegacyOrigins.Centre: return Anchor.Centre; - case LegacyOrigins.CentreRight: return Anchor.CentreRight; - case LegacyOrigins.BottomLeft: return Anchor.BottomLeft; - case LegacyOrigins.BottomCentre: return Anchor.BottomCentre; - case LegacyOrigins.BottomRight: return Anchor.BottomRight; - } - throw new InvalidDataException($@"Unknown origin: {value}"); - } - - private static string parseLayer(string value) - => Enum.Parse(typeof(StoryLayer), value).ToString(); - private void handleTimingPoints(Beatmap beatmap, string line) { if (beatmap == null) @@ -632,97 +720,57 @@ namespace osu.Game.Beatmaps.Formats variables[pair.Key] = pair.Value; } - protected override Beatmap ParseFile(StreamReader stream) + // + + /// + /// Decodes any beatmap variables present in a line into their real values. + /// + /// The line which may contains variables. + private void decodeVariables(ref string line) { - return new LegacyBeatmap(base.ParseFile(stream)); - } + if (line == null) + throw new ArgumentNullException(nameof(line)); - public override Beatmap Decode(StreamReader stream) - { - return new LegacyBeatmap(base.Decode(stream)); - } - - protected override void ParseFile(StreamReader stream, Beatmap beatmap) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion; - - Section section = Section.None; - bool hasCustomColours = false; - StoryboardSprite storyboardSprite = null; - CommandTimelineGroup timelineGroup = null; - - string line; - while ((line = stream.ReadLine()) != null) + while (line.IndexOf('$') >= 0) { - if (string.IsNullOrWhiteSpace(line)) - continue; - - if (line.StartsWith("//")) - continue; - - if (line.StartsWith(@"osu file format v")) + string origLine = line; + string[] split = line.Split(','); + for (int i = 0; i < split.Length; i++) { - beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); - continue; + var item = split[i]; + if (item.StartsWith("$") && variables.ContainsKey(item)) + split[i] = variables[item]; } - if (line.StartsWith(@"[") && line.EndsWith(@"]")) - { - if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) - throw new InvalidDataException($@"Unknown osu section {line}"); - continue; - } - - switch (section) - { - case Section.General: - handleGeneral(beatmap, line); - break; - case Section.Editor: - handleEditor(beatmap, line); - break; - case Section.Metadata: - handleMetadata(beatmap, line); - break; - case Section.Difficulty: - handleDifficulty(beatmap, line); - break; - case Section.Events: - handleEvents(beatmap, line, ref storyboardSprite, ref timelineGroup); - break; - case Section.TimingPoints: - handleTimingPoints(beatmap, line); - break; - case Section.Colours: - handleColours(beatmap, line, ref hasCustomColours); - break; - case Section.HitObjects: - - // If the ruleset wasn't specified, assume the osu!standard ruleset. - if (parser == null) - parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); - - var obj = parser.Parse(line); - - if (obj != null) - beatmap.HitObjects.Add(obj); - - break; - case Section.Variables: - handleVariables(line); - break; - } + line = string.Join(",", split); + if (line == origLine) break; } - - foreach (var hitObject in beatmap.HitObjects) - hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); } + private static string cleanFilename(string path) + => FileSafety.PathStandardise(path.Trim('\"')); + + private static Anchor parseOrigin(string value) + { + var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); + switch (origin) + { + case LegacyOrigins.TopLeft: return Anchor.TopLeft; + case LegacyOrigins.TopCentre: return Anchor.TopCentre; + case LegacyOrigins.TopRight: return Anchor.TopRight; + case LegacyOrigins.CentreLeft: return Anchor.CentreLeft; + case LegacyOrigins.Centre: return Anchor.Centre; + case LegacyOrigins.CentreRight: return Anchor.CentreRight; + case LegacyOrigins.BottomLeft: return Anchor.BottomLeft; + case LegacyOrigins.BottomCentre: return Anchor.BottomCentre; + case LegacyOrigins.BottomRight: return Anchor.BottomRight; + } + throw new InvalidDataException($@"Unknown origin: {value}"); + } + + private static string parseLayer(string value) + => Enum.Parse(typeof(StoryLayer), value).ToString(); + private KeyValuePair splitKeyVal(string line, char separator) { if (line == null) @@ -737,6 +785,20 @@ namespace osu.Game.Beatmaps.Formats ); } + private enum Section + { + None, + General, + Editor, + Metadata, + Difficulty, + Events, + TimingPoints, + Colours, + HitObjects, + Variables, + } + internal enum LegacySampleBank { None = 0, diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index d9951e002b..d17f20ff2f 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) using (var reader = new StreamReader(stream)) - beatmap = BeatmapDecoder.GetDecoder(reader).Decode(reader); + beatmap = BeatmapDecoder.GetDecoder(reader).DecodeBeatmap(reader); return beatmap; } From be018a63c6fa0edfc20d9dc94b505f7b060192f1 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 30 Nov 2017 19:17:11 +0100 Subject: [PATCH 031/193] remove unnecessary lines --- osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index e0af018059..47e35f231f 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -54,8 +54,6 @@ namespace osu.Game.Beatmaps.Formats beatmapVersion = int.Parse(header.Substring(17)); } - // - protected override Beatmap ParseBeatmap(StreamReader stream) { return new LegacyBeatmap(base.ParseBeatmap(stream)); @@ -181,8 +179,6 @@ namespace osu.Game.Beatmaps.Formats } } - // - private void handleGeneral(Beatmap beatmap, string line) { if (beatmap == null) @@ -720,8 +716,6 @@ namespace osu.Game.Beatmaps.Formats variables[pair.Key] = pair.Value; } - // - /// /// Decodes any beatmap variables present in a line into their real values. /// From 7080711cb24184c5561d25a782c113210f797749 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 30 Nov 2017 20:13:10 +0100 Subject: [PATCH 032/193] remove unnecessary `using` --- osu.Game/Beatmaps/Beatmap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 2c437f8a18..c8390310d4 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps.ControlPoints; using osu.Game.IO.Serialization; -using osu.Game.Storyboards; namespace osu.Game.Beatmaps { From f6591851c3a6f7ac3d198e940a813019046fbe55 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 22:01:49 +0900 Subject: [PATCH 033/193] Implement a selection dragger box --- osu.Game/Rulesets/Edit/PlayfieldOverlay.cs | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs index 58b15e3de2..77aeed42e3 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs +++ b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs @@ -1,19 +1,69 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Edit { public class PlayfieldOverlay : CompositeDrawable { + private readonly Drawable dragBox; + public PlayfieldOverlay() { RelativeSizeAxes = Axes.Both; + InternalChildren = new[] + { + dragBox = new Container + { + Masking = true, + BorderColour = Color4.White, + BorderThickness = 2, + MaskingSmoothness = 1, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }; } + private Vector2 dragStartPos; + protected override bool OnDragStart(InputState state) + { + dragStartPos = ToLocalSpace(state.Mouse.NativeState.Position); + return true; + } + + protected override bool OnDrag(InputState state) + { + var dragPos = ToLocalSpace(state.Mouse.NativeState.Position); + var dragRectangle = RectangleF.FromLTRB( + Math.Min(dragStartPos.X, dragPos.X), + Math.Min(dragStartPos.Y, dragPos.Y), + Math.Max(dragStartPos.X, dragPos.X), + Math.Max(dragStartPos.Y, dragPos.Y)); + + dragBox.Position = dragRectangle.Location; + dragBox.Size = dragRectangle.Size; + + return true; + } + + protected override bool OnDragEnd(InputState state) + { + return true; + } } } From cf859a6cf2449896e38e5acd431176e2d20d8192 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 00:26:02 +0900 Subject: [PATCH 034/193] Make the dragger attach to objects it surrounds Plus a lot more implementation. --- osu-framework | 2 +- .../Objects/Drawables/DrawableSlider.cs | 4 ++ .../Objects/Drawables/Pieces/SliderBody.cs | 5 +- .../Visual/TestCaseEditorCompose.cs | 2 + .../Visual/TestCaseEditorPlayfieldOverlay.cs | 54 +++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + osu.Game/Rulesets/Edit/HitObjectComposer.cs | 6 +- osu.Game/Rulesets/Edit/PlayfieldOverlay.cs | 68 +++++++++++++++++-- osu.Game/Rulesets/Edit/SelectionDragger.cs | 12 ++++ .../Objects/Drawables/DrawableHitObject.cs | 12 ++++ osu.Game/Rulesets/UI/RulesetContainer.cs | 17 ++--- osu.Game/osu.Game.csproj | 2 + 12 files changed, 167 insertions(+), 18 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseEditorPlayfieldOverlay.cs create mode 100644 osu.Game/Rulesets/Edit/SelectionDragger.cs diff --git a/osu-framework b/osu-framework index 4fc866eee3..d231ca9f79 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 4fc866eee3803f88b155150e32e021b9c21e647f +Subproject commit d231ca9f79936f3a7f3cff0c7721587755ae168c diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 74454ca555..7e6892e70b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; +using osu.Framework.Graphics.Primitives; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -165,6 +166,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } public Drawable ProxiedLayer => initialCircle.ApproachCircle; + + public override Vector2 SelectionPoint => body.Position; + public override Quad SelectionQuad => body.PathDrawQuad; } internal interface ISliderProgress diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 2082e9a27b..75c2c15084 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -14,6 +14,7 @@ using osu.Game.Configuration; using OpenTK; using OpenTK.Graphics.ES30; using OpenTK.Graphics; +using osu.Framework.Graphics.Primitives; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -49,6 +50,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } + public Quad PathDrawQuad => container.ScreenSpaceDrawQuad; + private int textureWidth => (int)PathWidth * 2; private readonly Slider slider; @@ -182,4 +185,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces SetRange(start, end); } } -} \ No newline at end of file +} diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index d52f27f4ab..226329a852 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -2,8 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Screens.Compose; namespace osu.Game.Tests.Visual diff --git a/osu.Game.Tests/Visual/TestCaseEditorPlayfieldOverlay.cs b/osu.Game.Tests/Visual/TestCaseEditorPlayfieldOverlay.cs new file mode 100644 index 0000000000..f0da23955d --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseEditorPlayfieldOverlay.cs @@ -0,0 +1,54 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseEditorPlayfieldOverlay : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(PlayfieldOverlay) }; + + public TestCaseEditorPlayfieldOverlay() + { + var playfield = new OsuEditPlayfield(); + playfield.Add(new DrawableHitCircle(new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f })); + playfield.Add(new DrawableHitCircle(new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f })); + playfield.Add(new DrawableSlider(new Slider + { + ControlPoints = new List + { + new Vector2(128, 256), + new Vector2(344, 256), + }, + Distance = 400, + Position = new Vector2(128, 256), + Velocity = 1, + TickDistance = 100, + Scale = 0.5f + })); + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Clock = new FramedClock(new StopwatchClock()), + Child = playfield + }, + new PlayfieldOverlay(playfield) + }; + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index ae88fb004f..b8dce4e4f6 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -110,6 +110,7 @@ + diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index f55b7b0531..41958df29b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Edit Alpha = 0, AlwaysPresent = true, }, - CreateUnderlay(rulesetContainer.Playfield), + CreateUnderlay(), rulesetContainer, CreateOverlay(rulesetContainer.Playfield) } @@ -106,9 +106,9 @@ namespace osu.Game.Rulesets.Edit protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); - protected virtual PlayfieldUnderlay CreateUnderlay(Playfield playfield) => new PlayfieldUnderlay(); + protected virtual PlayfieldUnderlay CreateUnderlay() => new PlayfieldUnderlay(); - protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(); + protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(playfield); protected abstract IReadOnlyList CompositionTools { get; } } diff --git a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs index 77aeed42e3..98b3bce265 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs +++ b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs @@ -9,15 +9,27 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using OpenTK; using OpenTK.Graphics; +using System.Collections.Generic; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; +using System.Linq; +using osu.Game.Graphics; namespace osu.Game.Rulesets.Edit { public class PlayfieldOverlay : CompositeDrawable { - private readonly Drawable dragBox; + private readonly static Color4 selection_normal_colour = Color4.White; + private readonly static Color4 selection_attached_colour = OsuColour.FromHex("eeaa00"); - public PlayfieldOverlay() + private readonly Container dragBox; + + private readonly Playfield playfield; + + public PlayfieldOverlay(Playfield playfield) { + this.playfield = playfield; + RelativeSizeAxes = Axes.Both; InternalChildren = new[] @@ -31,25 +43,31 @@ namespace osu.Game.Rulesets.Edit Child = new Box { RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true + Alpha = 0.1f } } }; } private Vector2 dragStartPos; + private RectangleF dragRectangle; + private List capturedHitObjects = new List(); protected override bool OnDragStart(InputState state) { dragStartPos = ToLocalSpace(state.Mouse.NativeState.Position); + dragBox.Position = dragStartPos; + dragBox.Size = Vector2.Zero; + dragBox.FadeTo(1); + dragBox.FadeColour(selection_normal_colour); + dragBox.BorderThickness = 2; return true; } protected override bool OnDrag(InputState state) { var dragPos = ToLocalSpace(state.Mouse.NativeState.Position); - var dragRectangle = RectangleF.FromLTRB( + dragRectangle = RectangleF.FromLTRB( Math.Min(dragStartPos.X, dragPos.X), Math.Min(dragStartPos.Y, dragPos.Y), Math.Max(dragStartPos.X, dragPos.X), @@ -58,11 +76,51 @@ namespace osu.Game.Rulesets.Edit dragBox.Position = dragRectangle.Location; dragBox.Size = dragRectangle.Size; + updateCapturedHitObjects(); + return true; } + private void updateCapturedHitObjects() + { + capturedHitObjects.Clear(); + + foreach (var obj in playfield.HitObjects.Objects) + { + if (!obj.IsAlive || !obj.IsPresent) + continue; + + var objectPosition = obj.Parent.ToScreenSpace(obj.SelectionPoint); + if (dragRectangle.Contains(ToLocalSpace(objectPosition))) + capturedHitObjects.Add(obj); + } + } + protected override bool OnDragEnd(InputState state) { + if (capturedHitObjects.Count == 0) + dragBox.FadeOut(400, Easing.OutQuint); + else + { + // Move the rectangle to cover the hitobjects + var topLeft = new Vector2(float.MaxValue, float.MaxValue); + var bottomRight = new Vector2(float.MinValue, float.MinValue); + + foreach (var obj in capturedHitObjects) + { + topLeft = Vector2.ComponentMin(topLeft, ToLocalSpace(obj.SelectionQuad.TopLeft)); + bottomRight = Vector2.ComponentMax(bottomRight, ToLocalSpace(obj.SelectionQuad.BottomRight)); + } + + topLeft -= new Vector2(5); + bottomRight += new Vector2(5); + + dragBox.MoveTo(topLeft, 200, Easing.OutQuint) + .ResizeTo(bottomRight - topLeft, 200, Easing.OutQuint) + .FadeColour(selection_attached_colour, 200, Easing.OutQuint); + dragBox.BorderThickness = 3; + } + return true; } } diff --git a/osu.Game/Rulesets/Edit/SelectionDragger.cs b/osu.Game/Rulesets/Edit/SelectionDragger.cs new file mode 100644 index 0000000000..35ea3a375e --- /dev/null +++ b/osu.Game/Rulesets/Edit/SelectionDragger.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Rulesets.Edit +{ + public class SelectionDragger : CompositeDrawable + { + + } +} diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 941cedca3f..9eecb9b4f5 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -14,6 +14,8 @@ using osu.Game.Audio; using System.Linq; using osu.Game.Graphics; using osu.Framework.Configuration; +using OpenTK; +using osu.Framework.Graphics.Primitives; namespace osu.Game.Rulesets.Objects.Drawables { @@ -38,6 +40,16 @@ namespace osu.Game.Rulesets.Objects.Drawables { HitObject = hitObject; } + + /// + /// The local point that causes this to be selected in the Editor. + /// + public virtual Vector2 SelectionPoint => DrawPosition; + + /// + /// The local rectangle that outlines this for selections in the Editor. + /// + public virtual Quad SelectionQuad => ScreenSpaceDrawQuad; } public abstract class DrawableHitObject : DrawableHitObject diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 69bf6bba29..5b4565e8a8 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -55,10 +55,11 @@ namespace osu.Game.Rulesets.UI public abstract IEnumerable Objects { get; } + private Playfield playfield; /// /// The playfield. /// - public Playfield Playfield { get; protected set; } + public Playfield Playfield => playfield ?? (playfield = CreatePlayfield()); protected readonly Ruleset Ruleset; @@ -95,6 +96,12 @@ namespace osu.Game.Rulesets.UI Replay = replay; ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; } + + /// + /// Creates a Playfield. + /// + /// The Playfield. + protected abstract Playfield CreatePlayfield(); } /// @@ -198,7 +205,7 @@ namespace osu.Game.Rulesets.UI }); AddInternal(KeyBindingInputManager); - KeyBindingInputManager.Add(Playfield = CreatePlayfield()); + KeyBindingInputManager.Add(Playfield); loadObjects(); } @@ -286,12 +293,6 @@ namespace osu.Game.Rulesets.UI /// The HitObject to make drawable. /// The DrawableHitObject. protected abstract DrawableHitObject GetVisualRepresentation(TObject h); - - /// - /// Creates a Playfield. - /// - /// The Playfield. - protected abstract Playfield CreatePlayfield(); } /// diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ad1370890f..c12ecc8b14 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -304,6 +304,7 @@ + @@ -567,6 +568,7 @@ + From 806c0e3b2629f708c80fe8f5d63e2b5fa0f62156 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 17:43:33 +0100 Subject: [PATCH 035/193] restructured OsuLegacyDecoder into LegacyDecoder Beatmap works, Storyboard not... --- .../Beatmaps/Formats/OsuLegacyDecoderTest.cs | 12 +- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 7 +- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 401 +++++++++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 182 ++++ .../Formats/LegacyStoryboardDecoder.cs | 261 ++++++ osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 837 ------------------ .../Objects/Legacy/ConvertHitObjectParser.cs | 4 +- osu.Game/osu.Game.csproj | 4 +- 8 files changed, 857 insertions(+), 851 deletions(-) create mode 100644 osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs create mode 100644 osu.Game/Beatmaps/Formats/LegacyDecoder.cs create mode 100644 osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs delete mode 100644 osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index 175e07f99c..2ef1b796d1 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeMetadata() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeGeneral() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmapInfo = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeEditor() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeDifficulty() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeColors() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeHitObjects() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index d785618780..1ecc6c0ee0 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps.Formats static BeatmapDecoder() { - OsuLegacyDecoder.Register(); + LegacyDecoder.Register(); } public static BeatmapDecoder GetDecoder(StreamReader stream) @@ -36,10 +36,7 @@ namespace osu.Game.Beatmaps.Formats decoders[magic] = typeof(T); } - public virtual Beatmap DecodeBeatmap(StreamReader stream) - { - return ParseBeatmap(stream); - } + public virtual Beatmap DecodeBeatmap(StreamReader stream) => ParseBeatmap(stream); protected virtual Beatmap ParseBeatmap(StreamReader stream) { diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs new file mode 100644 index 0000000000..c8e6d6cfea --- /dev/null +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -0,0 +1,401 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using OpenTK.Graphics; +using osu.Game.Beatmaps.Timing; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Objects.Legacy; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Storyboards; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.IO.File; + +namespace osu.Game.Beatmaps.Formats +{ + public class LegacyBeatmapDecoder : LegacyDecoder + { + private ConvertHitObjectParser parser; + private bool hasCustomColours = false; + + private LegacySampleBank defaultSampleBank; + private int defaultSampleVolume = 100; + + public LegacyBeatmapDecoder() + { + } + + public LegacyBeatmapDecoder(string header) + { + beatmapVersion = int.Parse(header.Substring(17)); + } + + protected override void processSection(Section section, string line) + { + switch (section) + { + case Section.General: + handleGeneral(line); + break; + case Section.Editor: + handleEditor(line); + break; + case Section.Metadata: + handleMetadata(line); + break; + case Section.Difficulty: + handleDifficulty(line); + break; + case Section.Events: + handleEvents(line); + break; + case Section.TimingPoints: + handleTimingPoints(line); + break; + case Section.Colours: + handleColours(line); + break; + case Section.HitObjects: + handleHitObjects(line); + break; + case Section.Variables: + handleVariables(line); + break; + } + } + + private void handleGeneral(string line) + { + var pair = splitKeyVal(line, ':'); + + var metadata = beatmap.BeatmapInfo.Metadata; + switch (pair.Key) + { + case @"AudioFilename": + metadata.AudioFile = pair.Value; + break; + case @"AudioLeadIn": + beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); + break; + case @"PreviewTime": + metadata.PreviewTime = int.Parse(pair.Value); + break; + case @"Countdown": + beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; + break; + case @"SampleSet": + defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); + break; + case @"SampleVolume": + defaultSampleVolume = int.Parse(pair.Value); + break; + case @"StackLeniency": + beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"Mode": + beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); + + switch (beatmap.BeatmapInfo.RulesetID) + { + case 0: + parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); + break; + case 1: + parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser(); + break; + case 2: + parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser(); + break; + case 3: + parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser(); + break; + } + break; + case @"LetterboxInBreaks": + beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; + break; + case @"SpecialStyle": + beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; + break; + case @"WidescreenStoryboard": + beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; + break; + } + } + + private void handleEditor(string line) + { + var pair = splitKeyVal(line, ':'); + + switch (pair.Key) + { + case @"Bookmarks": + beatmap.BeatmapInfo.StoredBookmarks = pair.Value; + break; + case @"DistanceSpacing": + beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"BeatDivisor": + beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); + break; + case @"GridSize": + beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); + break; + case @"TimelineZoom": + beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + } + } + + private void handleMetadata(string line) + { + var pair = splitKeyVal(line, ':'); + + var metadata = beatmap.BeatmapInfo.Metadata; + switch (pair.Key) + { + case @"Title": + metadata.Title = pair.Value; + break; + case @"TitleUnicode": + metadata.TitleUnicode = pair.Value; + break; + case @"Artist": + metadata.Artist = pair.Value; + break; + case @"ArtistUnicode": + metadata.ArtistUnicode = pair.Value; + break; + case @"Creator": + metadata.AuthorString = pair.Value; + break; + case @"Version": + beatmap.BeatmapInfo.Version = pair.Value; + break; + case @"Source": + beatmap.BeatmapInfo.Metadata.Source = pair.Value; + break; + case @"Tags": + beatmap.BeatmapInfo.Metadata.Tags = pair.Value; + break; + case @"BeatmapID": + beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); + break; + case @"BeatmapSetID": + beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); + metadata.OnlineBeatmapSetID = int.Parse(pair.Value); + break; + } + } + + private void handleDifficulty(string line) + { + var pair = splitKeyVal(line, ':'); + + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; + switch (pair.Key) + { + case @"HPDrainRate": + difficulty.DrainRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"CircleSize": + difficulty.CircleSize = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"OverallDifficulty": + difficulty.OverallDifficulty = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"ApproachRate": + difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"SliderMultiplier": + difficulty.SliderMultiplier = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"SliderTickRate": + difficulty.SliderTickRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + } + } + + private void handleEvents(string line) + { + decodeVariables(ref line); + + string[] split = line.Split(','); + + EventType type; + if (!Enum.TryParse(split[0], out type)) + throw new InvalidDataException($@"Unknown event type {split[0]}"); + + switch (type) + { + case EventType.Background: + string filename = split[2].Trim('"'); + beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + break; + case EventType.Break: + var breakEvent = new BreakPeriod + { + StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), + EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo) + }; + + if (!breakEvent.HasEffect) + return; + + beatmap.Breaks.Add(breakEvent); + break; + } + } + + private void handleTimingPoints(string line) + { + string[] split = line.Split(','); + + double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo); + double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); + double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; + + TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; + if (split.Length >= 3) + timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]); + + LegacySampleBank sampleSet = defaultSampleBank; + if (split.Length >= 4) + sampleSet = (LegacySampleBank)int.Parse(split[3]); + + //SampleBank sampleBank = SampleBank.Default; + //if (split.Length >= 5) + // sampleBank = (SampleBank)int.Parse(split[4]); + + int sampleVolume = defaultSampleVolume; + if (split.Length >= 6) + sampleVolume = int.Parse(split[5]); + + bool timingChange = true; + if (split.Length >= 7) + timingChange = split[6][0] == '1'; + + bool kiaiMode = false; + bool omitFirstBarSignature = false; + if (split.Length >= 8) + { + int effectFlags = int.Parse(split[7]); + kiaiMode = (effectFlags & 1) > 0; + omitFirstBarSignature = (effectFlags & 8) > 0; + } + + string stringSampleSet = sampleSet.ToString().ToLower(); + if (stringSampleSet == @"none") + stringSampleSet = @"normal"; + + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); + SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); + EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); + + if (timingChange) + { + beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint + { + Time = time, + BeatLength = beatLength, + TimeSignature = timeSignature + }); + } + + if (speedMultiplier != difficultyPoint.SpeedMultiplier) + { + beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); + beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint + { + Time = time, + SpeedMultiplier = speedMultiplier + }); + } + + if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) + { + beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint + { + Time = time, + SampleBank = stringSampleSet, + SampleVolume = sampleVolume + }); + } + + if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) + { + beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint + { + Time = time, + KiaiMode = kiaiMode, + OmitFirstBarLine = omitFirstBarSignature + }); + } + } + + private void handleColours(string line) + { + var pair = splitKeyVal(line, ':'); + + string[] split = pair.Value.Split(','); + + if (split.Length != 3) + throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}"); + + byte r, g, b; + if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b)) + throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); + + if (!hasCustomColours) + { + beatmap.ComboColors.Clear(); + hasCustomColours = true; + } + + // Note: the combo index specified in the beatmap is discarded + if (pair.Key.StartsWith(@"Combo")) + { + beatmap.ComboColors.Add(new Color4 + { + R = r / 255f, + G = g / 255f, + B = b / 255f, + A = 1f, + }); + } + } + + private void handleHitObjects(string line) + { + // If the ruleset wasn't specified, assume the osu!standard ruleset. + if (parser == null) + parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); + + var obj = parser.Parse(line); + + if (obj != null) + beatmap.HitObjects.Add(obj); + } + + private void handleVariables(string line) + { + var pair = splitKeyVal(line, '='); + variables[pair.Key] = pair.Value; + } + + private KeyValuePair splitKeyVal(string line, char separator) + { + var split = line.Trim().Split(new[] { separator }, 2); + + return new KeyValuePair + ( + split[0].Trim(), + split.Length > 1 ? split[1].Trim() : string.Empty + ); + } + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs new file mode 100644 index 0000000000..00b06d28d2 --- /dev/null +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -0,0 +1,182 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Storyboards; + +namespace osu.Game.Beatmaps.Formats +{ + public abstract class LegacyDecoder : BeatmapDecoder + { + public static void Register() + { + AddDecoder(@"osu file format v14"); + AddDecoder(@"osu file format v13"); + AddDecoder(@"osu file format v12"); + AddDecoder(@"osu file format v11"); + AddDecoder(@"osu file format v10"); + AddDecoder(@"osu file format v9"); + AddDecoder(@"osu file format v8"); + AddDecoder(@"osu file format v7"); + AddDecoder(@"osu file format v6"); + AddDecoder(@"osu file format v5"); + AddDecoder(@"osu file format v4"); + AddDecoder(@"osu file format v3"); + // TODO: differences between versions + } + + protected Beatmap beatmap; + protected Storyboard storyboard; + + protected int beatmapVersion; + protected readonly Dictionary variables = new Dictionary(); + + public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream)); + + protected override Beatmap ParseBeatmap(StreamReader stream) => new LegacyBeatmap(base.ParseBeatmap(stream)); + + protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + if (beatmap == null) + throw new ArgumentNullException(nameof(beatmap)); + + this.beatmap = beatmap; + this.beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion; + + ParseContent(stream); + + foreach (var hitObject in this.beatmap.HitObjects) + hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); + } + + protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + if (storyboard == null) + throw new ArgumentNullException(nameof(storyboard)); + + this.storyboard = storyboard; + + ParseContent(stream); + } + + protected void ParseContent(StreamReader stream) + { + Section section = Section.None; + + string line; + while ((line = stream.ReadLine()) != null) + { + if (string.IsNullOrWhiteSpace(line)) + continue; + + if (line.StartsWith("//")) + continue; + + if (line.StartsWith(@"osu file format v")) + { + beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); + continue; + } + + if (line.StartsWith(@"[") && line.EndsWith(@"]")) + { + if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) + throw new InvalidDataException($@"Unknown osu section {line}"); + continue; + } + + processSection(section, line); + } + } + + protected abstract void processSection(Section section, string line); + + /// + /// Decodes any beatmap variables present in a line into their real values. + /// + /// The line which may contains variables. + protected void decodeVariables(ref string line) + { + while (line.IndexOf('$') >= 0) + { + string origLine = line; + string[] split = line.Split(','); + for (int i = 0; i < split.Length; i++) + { + var item = split[i]; + if (item.StartsWith("$") && variables.ContainsKey(item)) + split[i] = variables[item]; + } + + line = string.Join(",", split); + if (line == origLine) + break; + } + } + + protected enum Section + { + None, + General, + Editor, + Metadata, + Difficulty, + Events, + TimingPoints, + Colours, + HitObjects, + Variables, + } + + internal enum LegacySampleBank + { + None = 0, + Normal = 1, + Soft = 2, + Drum = 3 + } + + internal enum EventType + { + Background = 0, + Video = 1, + Break = 2, + Colour = 3, + Sprite = 4, + Sample = 5, + Animation = 6 + } + + internal enum LegacyOrigins + { + TopLeft, + Centre, + CentreLeft, + TopRight, + BottomCentre, + TopCentre, + Custom, + CentreRight, + BottomLeft, + BottomRight + }; + + internal enum StoryLayer + { + Background = 0, + Fail = 1, + Pass = 2, + Foreground = 3 + } + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs new file mode 100644 index 0000000000..9362676245 --- /dev/null +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -0,0 +1,261 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.IO.File; +using osu.Game.Storyboards; + +namespace osu.Game.Beatmaps.Formats +{ + public class LegacyStoryboardDecoder : LegacyDecoder + { + private StoryboardSprite storyboardSprite = null; + private CommandTimelineGroup timelineGroup = null; + + public LegacyStoryboardDecoder() + { + } + + public LegacyStoryboardDecoder(string header) + { + beatmapVersion = int.Parse(header.Substring(17)); + } + + protected override void processSection(Section section, string line) + { + switch (section) + { + case Section.Events: + handleEvents(line); + break; + } + } + + private void handleEvents(string line) + { + var depth = 0; + while (line.StartsWith(" ") || line.StartsWith("_")) + { + ++depth; + line = line.Substring(1); + } + + decodeVariables(ref line); + + string[] split = line.Split(','); + + if (depth == 0) + { + storyboardSprite = null; + + EventType type; + if (!Enum.TryParse(split[0], out type)) + throw new InvalidDataException($@"Unknown event type {split[0]}"); + + switch (type) + { + case EventType.Sprite: + { + var layer = parseLayer(split[1]); + var origin = parseOrigin(split[2]); + var path = cleanFilename(split[3]); + var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); + var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); + storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); + storyboard.GetLayer(layer).Add(storyboardSprite); + } + break; + case EventType.Animation: + { + var layer = parseLayer(split[1]); + var origin = parseOrigin(split[2]); + var path = cleanFilename(split[3]); + var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); + var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); + var frameCount = int.Parse(split[6]); + var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); + var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; + storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); + storyboard.GetLayer(layer).Add(storyboardSprite); + } + break; + case EventType.Sample: + { + var time = double.Parse(split[1], CultureInfo.InvariantCulture); + var layer = parseLayer(split[2]); + var path = cleanFilename(split[3]); + var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; + storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + } + break; + } + } + else + { + if (depth < 2) + timelineGroup = storyboardSprite?.TimelineGroup; + + var commandType = split[0]; + switch (commandType) + { + case "T": + { + var triggerName = split[1]; + var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue; + var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue; + var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0; + timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); + } + break; + case "L": + { + var startTime = double.Parse(split[1], CultureInfo.InvariantCulture); + var loopCount = int.Parse(split[2]); + timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); + } + break; + default: + { + if (string.IsNullOrEmpty(split[3])) + split[3] = split[2]; + + var easing = (Easing)int.Parse(split[1]); + var startTime = double.Parse(split[2], CultureInfo.InvariantCulture); + var endTime = double.Parse(split[3], CultureInfo.InvariantCulture); + + switch (commandType) + { + case "F": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "S": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); + } + break; + case "V": + { + var startX = float.Parse(split[4], CultureInfo.InvariantCulture); + var startY = float.Parse(split[5], CultureInfo.InvariantCulture); + var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; + var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; + timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); + } + break; + case "R": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue)); + } + break; + case "M": + { + var startX = float.Parse(split[4], CultureInfo.InvariantCulture); + var startY = float.Parse(split[5], CultureInfo.InvariantCulture); + var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; + var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; + timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); + timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); + } + break; + case "MX": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "MY": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "C": + { + var startRed = float.Parse(split[4], CultureInfo.InvariantCulture); + var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture); + var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture); + var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed; + var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen; + var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue; + timelineGroup?.Colour.Add(easing, startTime, endTime, + new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), + new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); + } + break; + case "P": + { + var type = split[4]; + switch (type) + { + case "A": + timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); + break; + case "H": + timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); + break; + case "V": + timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); + break; + } + } + break; + default: + throw new InvalidDataException($@"Unknown command type: {commandType}"); + } + } + break; + } + } + } + + private static string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString(); + + private static Anchor parseOrigin(string value) + { + var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); + switch (origin) + { + case LegacyOrigins.TopLeft: + return Anchor.TopLeft; + case LegacyOrigins.TopCentre: + return Anchor.TopCentre; + case LegacyOrigins.TopRight: + return Anchor.TopRight; + case LegacyOrigins.CentreLeft: + return Anchor.CentreLeft; + case LegacyOrigins.Centre: + return Anchor.Centre; + case LegacyOrigins.CentreRight: + return Anchor.CentreRight; + case LegacyOrigins.BottomLeft: + return Anchor.BottomLeft; + case LegacyOrigins.BottomCentre: + return Anchor.BottomCentre; + case LegacyOrigins.BottomRight: + return Anchor.BottomRight; + } + throw new InvalidDataException($@"Unknown origin: {value}"); + } + + private static string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('\"')); + } +} diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs deleted file mode 100644 index 47e35f231f..0000000000 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ /dev/null @@ -1,837 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using OpenTK.Graphics; -using osu.Game.Beatmaps.Timing; -using osu.Game.Beatmaps.Legacy; -using osu.Game.Rulesets.Objects.Legacy; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Storyboards; -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.IO.File; - -namespace osu.Game.Beatmaps.Formats -{ - public class OsuLegacyDecoder : BeatmapDecoder - { - public static void Register() - { - AddDecoder(@"osu file format v14"); - AddDecoder(@"osu file format v13"); - AddDecoder(@"osu file format v12"); - AddDecoder(@"osu file format v11"); - AddDecoder(@"osu file format v10"); - AddDecoder(@"osu file format v9"); - AddDecoder(@"osu file format v8"); - AddDecoder(@"osu file format v7"); - AddDecoder(@"osu file format v6"); - AddDecoder(@"osu file format v5"); - AddDecoder(@"osu file format v4"); - AddDecoder(@"osu file format v3"); - // TODO: differences between versions - } - - private ConvertHitObjectParser parser; - - private readonly Dictionary variables = new Dictionary(); - - private LegacySampleBank defaultSampleBank; - private int defaultSampleVolume = 100; - - private readonly int beatmapVersion; - - public OsuLegacyDecoder() - { - } - - public OsuLegacyDecoder(string header) - { - beatmapVersion = int.Parse(header.Substring(17)); - } - - protected override Beatmap ParseBeatmap(StreamReader stream) - { - return new LegacyBeatmap(base.ParseBeatmap(stream)); - } - - public override Beatmap DecodeBeatmap(StreamReader stream) - { - return new LegacyBeatmap(base.DecodeBeatmap(stream)); - } - - protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion; - - Section section = Section.None; - bool hasCustomColours = false; - - string line; - while ((line = stream.ReadLine()) != null) - { - if (string.IsNullOrWhiteSpace(line)) - continue; - - if (line.StartsWith("//")) - continue; - - if (line.StartsWith(@"osu file format v")) - { - beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); - continue; - } - - if (line.StartsWith(@"[") && line.EndsWith(@"]")) - { - if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) - throw new InvalidDataException($@"Unknown osu section {line}"); - continue; - } - - switch (section) - { - case Section.General: - handleGeneral(beatmap, line); - break; - case Section.Editor: - handleEditor(beatmap, line); - break; - case Section.Metadata: - handleMetadata(beatmap, line); - break; - case Section.Difficulty: - handleDifficulty(beatmap, line); - break; - case Section.Events: - handleEvents(beatmap, line); - break; - case Section.TimingPoints: - handleTimingPoints(beatmap, line); - break; - case Section.Colours: - handleColours(beatmap, line, ref hasCustomColours); - break; - case Section.HitObjects: - - // If the ruleset wasn't specified, assume the osu!standard ruleset. - if (parser == null) - parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); - - var obj = parser.Parse(line); - - if (obj != null) - beatmap.HitObjects.Add(obj); - - break; - case Section.Variables: - handleVariables(line); - break; - } - } - - foreach (var hitObject in beatmap.HitObjects) - hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); - } - - protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) - { - if (storyboard == null) - throw new ArgumentNullException(nameof(storyboard)); - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - Section section = Section.None; - StoryboardSprite storyboardSprite = null; - CommandTimelineGroup timelineGroup = null; - - string line; - while ((line = stream.ReadLine()) != null) - { - if (string.IsNullOrWhiteSpace(line)) - continue; - - if (line.StartsWith("//")) - continue; - - if (line.StartsWith(@"[") && line.EndsWith(@"]")) - { - if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) - throw new InvalidDataException($@"Unknown osu section {line}"); - continue; - } - - switch (section) - { - case Section.Events: - handleEvents(storyboard, line, ref storyboardSprite, ref timelineGroup); - break; - } - } - } - - private void handleGeneral(Beatmap beatmap, string line) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, ':'); - - var metadata = beatmap.BeatmapInfo.Metadata; - switch (pair.Key) - { - case @"AudioFilename": - metadata.AudioFile = pair.Value; - break; - case @"AudioLeadIn": - beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); - break; - case @"PreviewTime": - metadata.PreviewTime = int.Parse(pair.Value); - break; - case @"Countdown": - beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; - break; - case @"SampleSet": - defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); - break; - case @"SampleVolume": - defaultSampleVolume = int.Parse(pair.Value); - break; - case @"StackLeniency": - beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"Mode": - beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); - - switch (beatmap.BeatmapInfo.RulesetID) - { - case 0: - parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); - break; - case 1: - parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser(); - break; - case 2: - parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser(); - break; - case 3: - parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser(); - break; - } - break; - case @"LetterboxInBreaks": - beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; - break; - case @"SpecialStyle": - beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; - break; - case @"WidescreenStoryboard": - beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; - break; - } - } - - private void handleEditor(Beatmap beatmap, string line) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, ':'); - - switch (pair.Key) - { - case @"Bookmarks": - beatmap.BeatmapInfo.StoredBookmarks = pair.Value; - break; - case @"DistanceSpacing": - beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"BeatDivisor": - beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); - break; - case @"GridSize": - beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); - break; - case @"TimelineZoom": - beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - } - } - - private void handleMetadata(Beatmap beatmap, string line) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, ':'); - - var metadata = beatmap.BeatmapInfo.Metadata; - switch (pair.Key) - { - case @"Title": - metadata.Title = pair.Value; - break; - case @"TitleUnicode": - metadata.TitleUnicode = pair.Value; - break; - case @"Artist": - metadata.Artist = pair.Value; - break; - case @"ArtistUnicode": - metadata.ArtistUnicode = pair.Value; - break; - case @"Creator": - metadata.AuthorString = pair.Value; - break; - case @"Version": - beatmap.BeatmapInfo.Version = pair.Value; - break; - case @"Source": - beatmap.BeatmapInfo.Metadata.Source = pair.Value; - break; - case @"Tags": - beatmap.BeatmapInfo.Metadata.Tags = pair.Value; - break; - case @"BeatmapID": - beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); - break; - case @"BeatmapSetID": - beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); - metadata.OnlineBeatmapSetID = int.Parse(pair.Value); - break; - } - } - - private void handleDifficulty(Beatmap beatmap, string line) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, ':'); - - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; - switch (pair.Key) - { - case @"HPDrainRate": - difficulty.DrainRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"CircleSize": - difficulty.CircleSize = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"OverallDifficulty": - difficulty.OverallDifficulty = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"ApproachRate": - difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"SliderMultiplier": - difficulty.SliderMultiplier = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"SliderTickRate": - difficulty.SliderTickRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - } - } - - private void handleEvents(Beatmap beatmap, string line) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - - decodeVariables(ref line); - - string[] split = line.Split(','); - - EventType type; - if (!Enum.TryParse(split[0], out type)) - throw new InvalidDataException($@"Unknown event type {split[0]}"); - - switch (type) - { - case EventType.Background: - string filename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; - break; - case EventType.Break: - var breakEvent = new BreakPeriod - { - StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), - EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo) - }; - - if (!breakEvent.HasEffect) - return; - - beatmap.Breaks.Add(breakEvent); - break; - } - } - - private void handleEvents(Storyboard storyboard, string line, ref StoryboardSprite storyboardSprite, ref CommandTimelineGroup timelineGroup) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - if (storyboard == null) - throw new ArgumentNullException(nameof(storyboard)); - - var depth = 0; - while (line.StartsWith(" ") || line.StartsWith("_")) - { - ++depth; - line = line.Substring(1); - } - - decodeVariables(ref line); - - string[] split = line.Split(','); - - if (depth == 0) - { - storyboardSprite = null; - - EventType type; - if (!Enum.TryParse(split[0], out type)) - throw new InvalidDataException($@"Unknown event type {split[0]}"); - - switch (type) - { - case EventType.Sprite: - { - var layer = parseLayer(split[1]); - var origin = parseOrigin(split[2]); - var path = cleanFilename(split[3]); - var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); - var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); - storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); - storyboard.GetLayer(layer).Add(storyboardSprite); - } - break; - case EventType.Animation: - { - var layer = parseLayer(split[1]); - var origin = parseOrigin(split[2]); - var path = cleanFilename(split[3]); - var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); - var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); - var frameCount = int.Parse(split[6]); - var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); - var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; - storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); - storyboard.GetLayer(layer).Add(storyboardSprite); - } - break; - case EventType.Sample: - { - var time = double.Parse(split[1], CultureInfo.InvariantCulture); - var layer = parseLayer(split[2]); - var path = cleanFilename(split[3]); - var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); - } - break; - } - } - else - { - if (depth < 2) - timelineGroup = storyboardSprite?.TimelineGroup; - - var commandType = split[0]; - switch (commandType) - { - case "T": - { - var triggerName = split[1]; - var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue; - var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue; - var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0; - timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); - } - break; - case "L": - { - var startTime = double.Parse(split[1], CultureInfo.InvariantCulture); - var loopCount = int.Parse(split[2]); - timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); - } - break; - default: - { - if (string.IsNullOrEmpty(split[3])) - split[3] = split[2]; - - var easing = (Easing)int.Parse(split[1]); - var startTime = double.Parse(split[2], CultureInfo.InvariantCulture); - var endTime = double.Parse(split[3], CultureInfo.InvariantCulture); - - switch (commandType) - { - case "F": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "S": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); - } - break; - case "V": - { - var startX = float.Parse(split[4], CultureInfo.InvariantCulture); - var startY = float.Parse(split[5], CultureInfo.InvariantCulture); - var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; - var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); - } - break; - case "R": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue)); - } - break; - case "M": - { - var startX = float.Parse(split[4], CultureInfo.InvariantCulture); - var startY = float.Parse(split[5], CultureInfo.InvariantCulture); - var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; - var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; - timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); - timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); - } - break; - case "MX": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "MY": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "C": - { - var startRed = float.Parse(split[4], CultureInfo.InvariantCulture); - var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture); - var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture); - var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed; - var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen; - var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue; - timelineGroup?.Colour.Add(easing, startTime, endTime, - new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), - new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); - } - break; - case "P": - { - var type = split[4]; - switch (type) - { - case "A": - timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); - break; - case "H": - timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); - break; - case "V": - timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); - break; - } - } - break; - default: - throw new InvalidDataException($@"Unknown command type: {commandType}"); - } - } - break; - } - } - } - - private void handleTimingPoints(Beatmap beatmap, string line) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - string[] split = line.Split(','); - - double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo); - double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); - double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; - - TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; - if (split.Length >= 3) - timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]); - - LegacySampleBank sampleSet = defaultSampleBank; - if (split.Length >= 4) - sampleSet = (LegacySampleBank)int.Parse(split[3]); - - //SampleBank sampleBank = SampleBank.Default; - //if (split.Length >= 5) - // sampleBank = (SampleBank)int.Parse(split[4]); - - int sampleVolume = defaultSampleVolume; - if (split.Length >= 6) - sampleVolume = int.Parse(split[5]); - - bool timingChange = true; - if (split.Length >= 7) - timingChange = split[6][0] == '1'; - - bool kiaiMode = false; - bool omitFirstBarSignature = false; - if (split.Length >= 8) - { - int effectFlags = int.Parse(split[7]); - kiaiMode = (effectFlags & 1) > 0; - omitFirstBarSignature = (effectFlags & 8) > 0; - } - - string stringSampleSet = sampleSet.ToString().ToLower(); - if (stringSampleSet == @"none") - stringSampleSet = @"normal"; - - DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); - SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); - EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); - - if (timingChange) - { - beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint - { - Time = time, - BeatLength = beatLength, - TimeSignature = timeSignature - }); - } - - if (speedMultiplier != difficultyPoint.SpeedMultiplier) - { - beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); - beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint - { - Time = time, - SpeedMultiplier = speedMultiplier - }); - } - - if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) - { - beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint - { - Time = time, - SampleBank = stringSampleSet, - SampleVolume = sampleVolume - }); - } - - if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) - { - beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint - { - Time = time, - KiaiMode = kiaiMode, - OmitFirstBarLine = omitFirstBarSignature - }); - } - } - - private void handleColours(Beatmap beatmap, string line, ref bool hasCustomColours) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, ':'); - - string[] split = pair.Value.Split(','); - - if (split.Length != 3) - throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}"); - - byte r, g, b; - if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b)) - throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); - - if (!hasCustomColours) - { - beatmap.ComboColors.Clear(); - hasCustomColours = true; - } - - // Note: the combo index specified in the beatmap is discarded - if (pair.Key.StartsWith(@"Combo")) - { - beatmap.ComboColors.Add(new Color4 - { - R = r / 255f, - G = g / 255f, - B = b / 255f, - A = 1f, - }); - } - } - - private void handleVariables(string line) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, '='); - variables[pair.Key] = pair.Value; - } - - /// - /// Decodes any beatmap variables present in a line into their real values. - /// - /// The line which may contains variables. - private void decodeVariables(ref string line) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - - while (line.IndexOf('$') >= 0) - { - string origLine = line; - string[] split = line.Split(','); - for (int i = 0; i < split.Length; i++) - { - var item = split[i]; - if (item.StartsWith("$") && variables.ContainsKey(item)) - split[i] = variables[item]; - } - - line = string.Join(",", split); - if (line == origLine) break; - } - } - - private static string cleanFilename(string path) - => FileSafety.PathStandardise(path.Trim('\"')); - - private static Anchor parseOrigin(string value) - { - var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); - switch (origin) - { - case LegacyOrigins.TopLeft: return Anchor.TopLeft; - case LegacyOrigins.TopCentre: return Anchor.TopCentre; - case LegacyOrigins.TopRight: return Anchor.TopRight; - case LegacyOrigins.CentreLeft: return Anchor.CentreLeft; - case LegacyOrigins.Centre: return Anchor.Centre; - case LegacyOrigins.CentreRight: return Anchor.CentreRight; - case LegacyOrigins.BottomLeft: return Anchor.BottomLeft; - case LegacyOrigins.BottomCentre: return Anchor.BottomCentre; - case LegacyOrigins.BottomRight: return Anchor.BottomRight; - } - throw new InvalidDataException($@"Unknown origin: {value}"); - } - - private static string parseLayer(string value) - => Enum.Parse(typeof(StoryLayer), value).ToString(); - - private KeyValuePair splitKeyVal(string line, char separator) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var split = line.Trim().Split(new[] { separator }, 2); - - return new KeyValuePair - ( - split[0].Trim(), - split.Length > 1 ? split[1].Trim() : string.Empty - ); - } - - private enum Section - { - None, - General, - Editor, - Metadata, - Difficulty, - Events, - TimingPoints, - Colours, - HitObjects, - Variables, - } - - internal enum LegacySampleBank - { - None = 0, - Normal = 1, - Soft = 2, - Drum = 3 - } - - internal enum EventType - { - Background = 0, - Video = 1, - Break = 2, - Colour = 3, - Sprite = 4, - Sample = 5, - Animation = 6 - } - - internal enum LegacyOrigins - { - TopLeft, - Centre, - CentreLeft, - TopRight, - BottomCentre, - TopCentre, - Custom, - CentreRight, - BottomLeft, - BottomRight - }; - - internal enum StoryLayer - { - Background = 0, - Fail = 1, - Pass = 2, - Foreground = 3 - } - } -} diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index d4f9c7191a..4300f76e9d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -177,8 +177,8 @@ namespace osu.Game.Rulesets.Objects.Legacy string[] split = str.Split(':'); - var bank = (OsuLegacyDecoder.LegacySampleBank)Convert.ToInt32(split[0]); - var addbank = (OsuLegacyDecoder.LegacySampleBank)Convert.ToInt32(split[1]); + var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]); + var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]); // Let's not implement this for now, because this doesn't fit nicely into the bank structure //string sampleFile = split2.Length > 4 ? split2[4] : string.Empty; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ccd1bd03dc..4c33b2d266 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -269,6 +269,8 @@ + + @@ -312,7 +314,7 @@ - + From db50ad794e716f0221eca657c59e9d4616cabd65 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 17:58:11 +0100 Subject: [PATCH 036/193] CI adjustments - removing unnecessary `using`s - name Fields/Methods according to rules - removing unnecessary initializations --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 90 ++++++++----------- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 47 ++++++---- .../Formats/LegacyStoryboardDecoder.cs | 17 ++-- .../Objects/Legacy/ConvertHitObjectParser.cs | 4 +- 4 files changed, 76 insertions(+), 82 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index c8e6d6cfea..61e9bbc11d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -7,34 +7,20 @@ using System.Globalization; using System.IO; using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; -using osu.Game.Beatmaps.Legacy; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Storyboards; -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.IO.File; namespace osu.Game.Beatmaps.Formats { public class LegacyBeatmapDecoder : LegacyDecoder { + private bool hasCustomColours; private ConvertHitObjectParser parser; - private bool hasCustomColours = false; private LegacySampleBank defaultSampleBank; private int defaultSampleVolume = 100; - public LegacyBeatmapDecoder() - { - } - - public LegacyBeatmapDecoder(string header) - { - beatmapVersion = int.Parse(header.Substring(17)); - } - - protected override void processSection(Section section, string line) + protected override void ProcessSection(Section section, string line) { switch (section) { @@ -72,20 +58,20 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var metadata = beatmap.BeatmapInfo.Metadata; + var metadata = Beatmap.BeatmapInfo.Metadata; switch (pair.Key) { case @"AudioFilename": metadata.AudioFile = pair.Value; break; case @"AudioLeadIn": - beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); + Beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); break; case @"PreviewTime": metadata.PreviewTime = int.Parse(pair.Value); break; case @"Countdown": - beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; + Beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; break; case @"SampleSet": defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); @@ -94,12 +80,12 @@ namespace osu.Game.Beatmaps.Formats defaultSampleVolume = int.Parse(pair.Value); break; case @"StackLeniency": - beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + Beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"Mode": - beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); + Beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); - switch (beatmap.BeatmapInfo.RulesetID) + switch (Beatmap.BeatmapInfo.RulesetID) { case 0: parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); @@ -116,13 +102,13 @@ namespace osu.Game.Beatmaps.Formats } break; case @"LetterboxInBreaks": - beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; + Beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; break; case @"SpecialStyle": - beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; + Beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; break; case @"WidescreenStoryboard": - beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; + Beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; break; } } @@ -134,19 +120,19 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"Bookmarks": - beatmap.BeatmapInfo.StoredBookmarks = pair.Value; + Beatmap.BeatmapInfo.StoredBookmarks = pair.Value; break; case @"DistanceSpacing": - beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + Beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"BeatDivisor": - beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); + Beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); break; case @"GridSize": - beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); + Beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); break; case @"TimelineZoom": - beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + Beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; } } @@ -155,7 +141,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var metadata = beatmap.BeatmapInfo.Metadata; + var metadata = Beatmap.BeatmapInfo.Metadata; switch (pair.Key) { case @"Title": @@ -174,19 +160,19 @@ namespace osu.Game.Beatmaps.Formats metadata.AuthorString = pair.Value; break; case @"Version": - beatmap.BeatmapInfo.Version = pair.Value; + Beatmap.BeatmapInfo.Version = pair.Value; break; case @"Source": - beatmap.BeatmapInfo.Metadata.Source = pair.Value; + Beatmap.BeatmapInfo.Metadata.Source = pair.Value; break; case @"Tags": - beatmap.BeatmapInfo.Metadata.Tags = pair.Value; + Beatmap.BeatmapInfo.Metadata.Tags = pair.Value; break; case @"BeatmapID": - beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); + Beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); break; case @"BeatmapSetID": - beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); + Beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); metadata.OnlineBeatmapSetID = int.Parse(pair.Value); break; } @@ -196,7 +182,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; + var difficulty = Beatmap.BeatmapInfo.BaseDifficulty; switch (pair.Key) { case @"HPDrainRate": @@ -222,7 +208,7 @@ namespace osu.Game.Beatmaps.Formats private void handleEvents(string line) { - decodeVariables(ref line); + DecodeVariables(ref line); string[] split = line.Split(','); @@ -234,7 +220,7 @@ namespace osu.Game.Beatmaps.Formats { case EventType.Background: string filename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + Beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; break; case EventType.Break: var breakEvent = new BreakPeriod @@ -246,7 +232,7 @@ namespace osu.Game.Beatmaps.Formats if (!breakEvent.HasEffect) return; - beatmap.Breaks.Add(breakEvent); + Beatmap.Breaks.Add(breakEvent); break; } } @@ -292,13 +278,13 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet == @"none") stringSampleSet = @"normal"; - DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); - SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); - EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); + DifficultyControlPoint difficultyPoint = Beatmap.ControlPointInfo.DifficultyPointAt(time); + SoundControlPoint soundPoint = Beatmap.ControlPointInfo.SoundPointAt(time); + EffectControlPoint effectPoint = Beatmap.ControlPointInfo.EffectPointAt(time); if (timingChange) { - beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint + Beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { Time = time, BeatLength = beatLength, @@ -308,8 +294,8 @@ namespace osu.Game.Beatmaps.Formats if (speedMultiplier != difficultyPoint.SpeedMultiplier) { - beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); - beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint + Beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); + Beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint { Time = time, SpeedMultiplier = speedMultiplier @@ -318,7 +304,7 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) { - beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint + Beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint { Time = time, SampleBank = stringSampleSet, @@ -328,7 +314,7 @@ namespace osu.Game.Beatmaps.Formats if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) { - beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint + Beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = time, KiaiMode = kiaiMode, @@ -352,14 +338,14 @@ namespace osu.Game.Beatmaps.Formats if (!hasCustomColours) { - beatmap.ComboColors.Clear(); + Beatmap.ComboColors.Clear(); hasCustomColours = true; } // Note: the combo index specified in the beatmap is discarded if (pair.Key.StartsWith(@"Combo")) { - beatmap.ComboColors.Add(new Color4 + Beatmap.ComboColors.Add(new Color4 { R = r / 255f, G = g / 255f, @@ -378,13 +364,13 @@ namespace osu.Game.Beatmaps.Formats var obj = parser.Parse(line); if (obj != null) - beatmap.HitObjects.Add(obj); + Beatmap.HitObjects.Add(obj); } private void handleVariables(string line) { var pair = splitKeyVal(line, '='); - variables[pair.Key] = pair.Value; + Variables[pair.Key] = pair.Value; } private KeyValuePair splitKeyVal(string line, char separator) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 00b06d28d2..249b106962 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -4,15 +4,12 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using osu.Game.Beatmaps.Legacy; using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats { - public abstract class LegacyDecoder : BeatmapDecoder + public class LegacyDecoder : BeatmapDecoder { public static void Register() { @@ -31,11 +28,20 @@ namespace osu.Game.Beatmaps.Formats // TODO: differences between versions } - protected Beatmap beatmap; - protected Storyboard storyboard; + public LegacyDecoder() + { + } - protected int beatmapVersion; - protected readonly Dictionary variables = new Dictionary(); + public LegacyDecoder(string header) + { + BeatmapVersion = int.Parse(header.Substring(17)); + } + + protected Beatmap Beatmap; + protected Storyboard Storyboard; + + protected int BeatmapVersion; + protected readonly Dictionary Variables = new Dictionary(); public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream)); @@ -48,13 +54,13 @@ namespace osu.Game.Beatmaps.Formats if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); - this.beatmap = beatmap; - this.beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion; + Beatmap = beatmap; + Beatmap.BeatmapInfo.BeatmapVersion = BeatmapVersion; ParseContent(stream); - foreach (var hitObject in this.beatmap.HitObjects) - hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); + foreach (var hitObject in Beatmap.HitObjects) + hitObject.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); } protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) @@ -64,7 +70,7 @@ namespace osu.Game.Beatmaps.Formats if (storyboard == null) throw new ArgumentNullException(nameof(storyboard)); - this.storyboard = storyboard; + Storyboard = storyboard; ParseContent(stream); } @@ -84,7 +90,7 @@ namespace osu.Game.Beatmaps.Formats if (line.StartsWith(@"osu file format v")) { - beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); + Beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); continue; } @@ -95,17 +101,20 @@ namespace osu.Game.Beatmaps.Formats continue; } - processSection(section, line); + ProcessSection(section, line); } } - protected abstract void processSection(Section section, string line); + protected virtual void ProcessSection(Section section, string line) + { + + } /// /// Decodes any beatmap variables present in a line into their real values. /// /// The line which may contains variables. - protected void decodeVariables(ref string line) + protected void DecodeVariables(ref string line) { while (line.IndexOf('$') >= 0) { @@ -114,8 +123,8 @@ namespace osu.Game.Beatmaps.Formats for (int i = 0; i < split.Length; i++) { var item = split[i]; - if (item.StartsWith("$") && variables.ContainsKey(item)) - split[i] = variables[item]; + if (item.StartsWith("$") && Variables.ContainsKey(item)) + split[i] = Variables[item]; } line = string.Join(",", split); diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 9362676245..576eaead11 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; @@ -18,8 +17,8 @@ namespace osu.Game.Beatmaps.Formats { public class LegacyStoryboardDecoder : LegacyDecoder { - private StoryboardSprite storyboardSprite = null; - private CommandTimelineGroup timelineGroup = null; + private StoryboardSprite storyboardSprite; + private CommandTimelineGroup timelineGroup; public LegacyStoryboardDecoder() { @@ -27,10 +26,10 @@ namespace osu.Game.Beatmaps.Formats public LegacyStoryboardDecoder(string header) { - beatmapVersion = int.Parse(header.Substring(17)); + BeatmapVersion = int.Parse(header.Substring(17)); } - protected override void processSection(Section section, string line) + protected override void ProcessSection(Section section, string line) { switch (section) { @@ -49,7 +48,7 @@ namespace osu.Game.Beatmaps.Formats line = line.Substring(1); } - decodeVariables(ref line); + DecodeVariables(ref line); string[] split = line.Split(','); @@ -71,7 +70,7 @@ namespace osu.Game.Beatmaps.Formats var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); - storyboard.GetLayer(layer).Add(storyboardSprite); + Storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Animation: @@ -85,7 +84,7 @@ namespace osu.Game.Beatmaps.Formats var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); - storyboard.GetLayer(layer).Add(storyboardSprite); + Storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Sample: @@ -94,7 +93,7 @@ namespace osu.Game.Beatmaps.Formats var layer = parseLayer(split[2]); var path = cleanFilename(split[3]); var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + Storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); } break; } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 4300f76e9d..0d7d617405 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -177,8 +177,8 @@ namespace osu.Game.Rulesets.Objects.Legacy string[] split = str.Split(':'); - var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]); - var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]); + var bank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[0]); + var addbank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[1]); // Let's not implement this for now, because this doesn't fit nicely into the bank structure //string sampleFile = split2.Length > 4 ? split2[4] : string.Empty; From a49f3479a2dcc111a821a6891814553443526ec7 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 19:11:52 +0100 Subject: [PATCH 037/193] Split retrieving of beatmap and storyboard decoder Storyboard works again. Not satisfied with the solution though. --- .../Beatmaps/IO/OszArchiveReaderTest.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 10 +- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 67 ---------- osu.Game/Beatmaps/Formats/Decoder.cs | 120 ++++++++++++++++++ .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 9 ++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 40 ++---- .../Formats/LegacyStoryboardDecoder.cs | 3 - osu.Game/Tests/Visual/TestCasePlayer.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 9 files changed, 151 insertions(+), 104 deletions(-) delete mode 100644 osu.Game/Beatmaps/Formats/BeatmapDecoder.cs create mode 100644 osu.Game/Beatmaps/Formats/Decoder.cs diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 7b30a4f1fe..22b1d16f7d 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO BeatmapMetadata meta; using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) - meta = BeatmapDecoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; + meta = Decoder.GetBeatmapDecoder(stream).DecodeBeatmap(stream).Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e5f2064ee3..e0d6ac214e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -495,7 +495,7 @@ namespace osu.Game.Beatmaps BeatmapMetadata metadata; using (var stream = new StreamReader(reader.GetStream(mapName))) - metadata = BeatmapDecoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; + metadata = Decoder.GetBeatmapDecoder(stream).DecodeBeatmap(stream).Metadata; // check if a set already exists with the same online id. beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo @@ -518,7 +518,7 @@ namespace osu.Game.Beatmaps raw.CopyTo(ms); ms.Position = 0; - var decoder = BeatmapDecoder.GetDecoder(sr); + var decoder = Decoder.GetBeatmapDecoder(sr); Beatmap beatmap = decoder.DecodeBeatmap(sr); beatmap.BeatmapInfo.Path = name; @@ -571,7 +571,7 @@ namespace osu.Game.Beatmaps { using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) { - BeatmapDecoder decoder = BeatmapDecoder.GetDecoder(stream); + Decoder decoder = Decoder.GetBeatmapDecoder(stream); return decoder.DecodeBeatmap(stream); } } @@ -620,9 +620,9 @@ namespace osu.Game.Beatmaps try { - BeatmapDecoder decoder; + Decoder decoder; using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) - decoder = BeatmapDecoder.GetDecoder(stream); + decoder = Decoder.GetStoryboardDecoder(stream); using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) return decoder.DecodeStoryboard(stream); diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs deleted file mode 100644 index 1ecc6c0ee0..0000000000 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.IO; -using osu.Game.Storyboards; - -namespace osu.Game.Beatmaps.Formats -{ - public abstract class BeatmapDecoder - { - private static readonly Dictionary decoders = new Dictionary(); - - static BeatmapDecoder() - { - LegacyDecoder.Register(); - } - - public static BeatmapDecoder GetDecoder(StreamReader stream) - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - string line; - do { line = stream.ReadLine()?.Trim(); } - while (line != null && line.Length == 0); - - if (line == null || !decoders.ContainsKey(line)) - throw new IOException(@"Unknown file format"); - return (BeatmapDecoder)Activator.CreateInstance(decoders[line], line); - } - - protected static void AddDecoder(string magic) where T : BeatmapDecoder - { - decoders[magic] = typeof(T); - } - - public virtual Beatmap DecodeBeatmap(StreamReader stream) => ParseBeatmap(stream); - - protected virtual Beatmap ParseBeatmap(StreamReader stream) - { - var beatmap = new Beatmap - { - BeatmapInfo = new BeatmapInfo - { - Metadata = new BeatmapMetadata(), - BaseDifficulty = new BeatmapDifficulty(), - }, - }; - - ParseBeatmap(stream, beatmap); - return beatmap; - } - - protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap); - - public virtual Storyboard DecodeStoryboard(StreamReader stream) - { - var storyboard = new Storyboard(); - ParseStoryboard(stream, storyboard); - return storyboard; - } - - protected abstract void ParseStoryboard(StreamReader stream, Storyboard storyboard); - } -} diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs new file mode 100644 index 0000000000..75e660bc8d --- /dev/null +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -0,0 +1,120 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using osu.Game.Storyboards; + +namespace osu.Game.Beatmaps.Formats +{ + public abstract class Decoder + { + private static readonly Dictionary beatmapDecoders = new Dictionary(); + private static readonly Dictionary storyboardDecoders = new Dictionary(); + + static Decoder() + { + LegacyDecoder.Register(); + } + + /// + /// Retrieves a to parse s. + /// + /// A stream pointing to the to retrieve the version from. + public static Decoder GetBeatmapDecoder(StreamReader stream) + { + string line = readFirstLine(stream); + + if (line == null || !beatmapDecoders.ContainsKey(line)) + throw new IOException(@"Unknown file format"); + return (Decoder)Activator.CreateInstance(beatmapDecoders[line], line); + } + + /// + /// Retrieves a to parse s. + /// + /// A stream pointing to the to retrieve the version from. + public static Decoder GetStoryboardDecoder(StreamReader stream) + { + string line = readFirstLine(stream); + + if (line == null || !storyboardDecoders.ContainsKey(line)) + throw new IOException(@"Unknown file format"); + return (Decoder)Activator.CreateInstance(storyboardDecoders[line], line); + } + + private static string readFirstLine(StreamReader stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + string line; + do + { line = stream.ReadLine()?.Trim(); } + while (line != null && line.Length == 0); + + return line; + } + + /// + /// Adds the to the list of and decoder. + /// + /// Type to decode a with. + /// /// Type to decode a with. + /// A string representation of the version. + protected static void AddDecoder(string version) where A : Decoder where B : Decoder + { + beatmapDecoders[version] = typeof(A); + storyboardDecoders[version] = typeof(B); + } + + /// + /// Adds the to the list of decoder. + /// + /// Type to decode a with. + /// A string representation of the version. + protected static void AddBeatmapDecoder(string version) where T : Decoder + { + beatmapDecoders[version] = typeof(T); + } + + /// + /// Adds the to the list of decoder. + /// + /// Type to decode a with. + /// A string representation of the version. + protected static void AddStoryboardDecoder(string version) where T : Decoder + { + storyboardDecoders[version] = typeof(T); + } + + public virtual Beatmap DecodeBeatmap(StreamReader stream) => ParseBeatmap(stream); + + protected virtual Beatmap ParseBeatmap(StreamReader stream) + { + var beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + Metadata = new BeatmapMetadata(), + BaseDifficulty = new BeatmapDifficulty(), + }, + }; + + ParseBeatmap(stream, beatmap); + return beatmap; + } + + protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap); + + public virtual Storyboard DecodeStoryboard(StreamReader stream) + { + var storyboard = new Storyboard(); + ParseStoryboard(stream, storyboard); + return storyboard; + } + + protected abstract void ParseStoryboard(StreamReader stream, Storyboard storyboard); + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 61e9bbc11d..17215a935c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -20,6 +20,15 @@ namespace osu.Game.Beatmaps.Formats private LegacySampleBank defaultSampleBank; private int defaultSampleVolume = 100; + public LegacyBeatmapDecoder() + { + } + + public LegacyBeatmapDecoder(string header) + { + BeatmapVersion = int.Parse(header.Substring(17)); + } + protected override void ProcessSection(Section section, string line) { switch (section) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 249b106962..b060de5120 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -9,34 +9,25 @@ using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats { - public class LegacyDecoder : BeatmapDecoder + public abstract class LegacyDecoder : Decoder { public static void Register() { - AddDecoder(@"osu file format v14"); - AddDecoder(@"osu file format v13"); - AddDecoder(@"osu file format v12"); - AddDecoder(@"osu file format v11"); - AddDecoder(@"osu file format v10"); - AddDecoder(@"osu file format v9"); - AddDecoder(@"osu file format v8"); - AddDecoder(@"osu file format v7"); - AddDecoder(@"osu file format v6"); - AddDecoder(@"osu file format v5"); - AddDecoder(@"osu file format v4"); - AddDecoder(@"osu file format v3"); + AddDecoder(@"osu file format v14"); + AddDecoder(@"osu file format v13"); + AddDecoder(@"osu file format v12"); + AddDecoder(@"osu file format v11"); + AddDecoder(@"osu file format v10"); + AddDecoder(@"osu file format v9"); + AddDecoder(@"osu file format v8"); + AddDecoder(@"osu file format v7"); + AddDecoder(@"osu file format v6"); + AddDecoder(@"osu file format v5"); + AddDecoder(@"osu file format v4"); + AddDecoder(@"osu file format v3"); // TODO: differences between versions } - public LegacyDecoder() - { - } - - public LegacyDecoder(string header) - { - BeatmapVersion = int.Parse(header.Substring(17)); - } - protected Beatmap Beatmap; protected Storyboard Storyboard; @@ -105,10 +96,7 @@ namespace osu.Game.Beatmaps.Formats } } - protected virtual void ProcessSection(Section section, string line) - { - - } + protected abstract void ProcessSection(Section section, string line); /// /// Decodes any beatmap variables present in a line into their real values. diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 576eaead11..317f99b9d5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -4,9 +4,6 @@ using System; using System.Globalization; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics; diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index d17f20ff2f..bca2cc02d0 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) using (var reader = new StreamReader(stream)) - beatmap = BeatmapDecoder.GetDecoder(reader).DecodeBeatmap(reader); + beatmap = Game.Beatmaps.Formats.Decoder.GetBeatmapDecoder(reader).DecodeBeatmap(reader); return beatmap; } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4c33b2d266..1b1576b4c3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -313,7 +313,7 @@ - + From c466296b14479ac0e8e6d290fd5db77a3a07ba86 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 22:05:01 +0100 Subject: [PATCH 038/193] reverted split at Decoder, moved logic down I'm done experimenting, sorry - `Decoder` only returns a "Beatmap"`Decoder` now - "Storyboard"`Decoder` is retrieved from a "Beatmap"`Decoder` - moved parse methods down in the hierarchy where I forgot to do that - renamed `OsuLegacyDecoderTest` to `LegacyDecoderTest` --- ...acyDecoderTest.cs => LegacyDecoderTest.cs} | 2 +- .../Beatmaps/IO/OszArchiveReaderTest.cs | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 10 +-- osu.Game/Beatmaps/Formats/Decoder.cs | 64 +++----------- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 88 +++++++++++-------- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 64 +++++--------- .../Formats/LegacyStoryboardDecoder.cs | 24 +++-- osu.Game/Tests/Visual/TestCasePlayer.cs | 2 +- 9 files changed, 115 insertions(+), 143 deletions(-) rename osu.Game.Tests/Beatmaps/Formats/{OsuLegacyDecoderTest.cs => LegacyDecoderTest.cs} (97%) diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs similarity index 97% rename from osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs rename to osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs index 2ef1b796d1..4266afa526 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs @@ -14,7 +14,7 @@ using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Tests.Beatmaps.Formats { [TestFixture] - public class OsuLegacyDecoderTest + public class LegacyDecoderTest { [Test] public void TestDecodeMetadata() diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 22b1d16f7d..ffe735c89f 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO BeatmapMetadata meta; using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) - meta = Decoder.GetBeatmapDecoder(stream).DecodeBeatmap(stream).Metadata; + meta = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 312a564f71..7d7adf5983 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -89,7 +89,7 @@ - + diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e0d6ac214e..00182d5d85 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -495,7 +495,7 @@ namespace osu.Game.Beatmaps BeatmapMetadata metadata; using (var stream = new StreamReader(reader.GetStream(mapName))) - metadata = Decoder.GetBeatmapDecoder(stream).DecodeBeatmap(stream).Metadata; + metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; // check if a set already exists with the same online id. beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo @@ -518,7 +518,7 @@ namespace osu.Game.Beatmaps raw.CopyTo(ms); ms.Position = 0; - var decoder = Decoder.GetBeatmapDecoder(sr); + var decoder = Decoder.GetDecoder(sr); Beatmap beatmap = decoder.DecodeBeatmap(sr); beatmap.BeatmapInfo.Path = name; @@ -571,7 +571,7 @@ namespace osu.Game.Beatmaps { using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) { - Decoder decoder = Decoder.GetBeatmapDecoder(stream); + Decoder decoder = Decoder.GetDecoder(stream); return decoder.DecodeBeatmap(stream); } } @@ -622,10 +622,10 @@ namespace osu.Game.Beatmaps { Decoder decoder; using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) - decoder = Decoder.GetStoryboardDecoder(stream); + decoder = Decoder.GetDecoder(stream); using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - return decoder.DecodeStoryboard(stream); + return decoder.GetStoryboardDecoder().DecodeStoryboard(stream); } catch { diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index 75e660bc8d..e157150651 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -10,8 +10,7 @@ namespace osu.Game.Beatmaps.Formats { public abstract class Decoder { - private static readonly Dictionary beatmapDecoders = new Dictionary(); - private static readonly Dictionary storyboardDecoders = new Dictionary(); + private static readonly Dictionary decoders = new Dictionary(); static Decoder() { @@ -19,32 +18,10 @@ namespace osu.Game.Beatmaps.Formats } /// - /// Retrieves a to parse s. + /// Retrieves a to parse a . /// - /// A stream pointing to the to retrieve the version from. - public static Decoder GetBeatmapDecoder(StreamReader stream) - { - string line = readFirstLine(stream); - - if (line == null || !beatmapDecoders.ContainsKey(line)) - throw new IOException(@"Unknown file format"); - return (Decoder)Activator.CreateInstance(beatmapDecoders[line], line); - } - - /// - /// Retrieves a to parse s. - /// - /// A stream pointing to the to retrieve the version from. - public static Decoder GetStoryboardDecoder(StreamReader stream) - { - string line = readFirstLine(stream); - - if (line == null || !storyboardDecoders.ContainsKey(line)) - throw new IOException(@"Unknown file format"); - return (Decoder)Activator.CreateInstance(storyboardDecoders[line], line); - } - - private static string readFirstLine(StreamReader stream) + /// A stream pointing to the . + public static Decoder GetDecoder(StreamReader stream) { if (stream == null) throw new ArgumentNullException(nameof(stream)); @@ -54,44 +31,27 @@ namespace osu.Game.Beatmaps.Formats { line = stream.ReadLine()?.Trim(); } while (line != null && line.Length == 0); - return line; + if (line == null || !decoders.ContainsKey(line)) + throw new IOException(@"Unknown file format"); + return (Decoder)Activator.CreateInstance(decoders[line], line); } /// /// Adds the to the list of and decoder. /// - /// Type to decode a with. - /// /// Type to decode a with. - /// A string representation of the version. - protected static void AddDecoder(string version) where A : Decoder where B : Decoder - { - beatmapDecoders[version] = typeof(A); - storyboardDecoders[version] = typeof(B); - } - - /// - /// Adds the to the list of decoder. - /// /// Type to decode a with. /// A string representation of the version. - protected static void AddBeatmapDecoder(string version) where T : Decoder + protected static void AddDecoder(string version) where T : Decoder { - beatmapDecoders[version] = typeof(T); + decoders[version] = typeof(T); } /// - /// Adds the to the list of decoder. + /// Retrieves a to parse a /// - /// Type to decode a with. - /// A string representation of the version. - protected static void AddStoryboardDecoder(string version) where T : Decoder - { - storyboardDecoders[version] = typeof(T); - } + public abstract Decoder GetStoryboardDecoder(); - public virtual Beatmap DecodeBeatmap(StreamReader stream) => ParseBeatmap(stream); - - protected virtual Beatmap ParseBeatmap(StreamReader stream) + public virtual Beatmap DecodeBeatmap(StreamReader stream) { var beatmap = new Beatmap { diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 17215a935c..29cf7dd913 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -2,18 +2,20 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; +using System.Collections.Generic; namespace osu.Game.Beatmaps.Formats { public class LegacyBeatmapDecoder : LegacyDecoder { + private Beatmap beatmap; + private bool hasCustomColours; private ConvertHitObjectParser parser; @@ -29,6 +31,22 @@ namespace osu.Game.Beatmaps.Formats BeatmapVersion = int.Parse(header.Substring(17)); } + protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + if (beatmap == null) + throw new ArgumentNullException(nameof(beatmap)); + + this.beatmap = beatmap; + this.beatmap.BeatmapInfo.BeatmapVersion = BeatmapVersion; + + ParseContent(stream); + + foreach (var hitObject in this.beatmap.HitObjects) + hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); + } + protected override void ProcessSection(Section section, string line) { switch (section) @@ -67,20 +85,20 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var metadata = Beatmap.BeatmapInfo.Metadata; + var metadata = beatmap.BeatmapInfo.Metadata; switch (pair.Key) { case @"AudioFilename": metadata.AudioFile = pair.Value; break; case @"AudioLeadIn": - Beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); + beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); break; case @"PreviewTime": metadata.PreviewTime = int.Parse(pair.Value); break; case @"Countdown": - Beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; break; case @"SampleSet": defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); @@ -89,12 +107,12 @@ namespace osu.Game.Beatmaps.Formats defaultSampleVolume = int.Parse(pair.Value); break; case @"StackLeniency": - Beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"Mode": - Beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); + beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); - switch (Beatmap.BeatmapInfo.RulesetID) + switch (beatmap.BeatmapInfo.RulesetID) { case 0: parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); @@ -111,13 +129,13 @@ namespace osu.Game.Beatmaps.Formats } break; case @"LetterboxInBreaks": - Beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; break; case @"SpecialStyle": - Beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; break; case @"WidescreenStoryboard": - Beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; break; } } @@ -129,19 +147,19 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"Bookmarks": - Beatmap.BeatmapInfo.StoredBookmarks = pair.Value; + beatmap.BeatmapInfo.StoredBookmarks = pair.Value; break; case @"DistanceSpacing": - Beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"BeatDivisor": - Beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); + beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); break; case @"GridSize": - Beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); + beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); break; case @"TimelineZoom": - Beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; } } @@ -150,7 +168,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var metadata = Beatmap.BeatmapInfo.Metadata; + var metadata = beatmap.BeatmapInfo.Metadata; switch (pair.Key) { case @"Title": @@ -169,19 +187,19 @@ namespace osu.Game.Beatmaps.Formats metadata.AuthorString = pair.Value; break; case @"Version": - Beatmap.BeatmapInfo.Version = pair.Value; + beatmap.BeatmapInfo.Version = pair.Value; break; case @"Source": - Beatmap.BeatmapInfo.Metadata.Source = pair.Value; + beatmap.BeatmapInfo.Metadata.Source = pair.Value; break; case @"Tags": - Beatmap.BeatmapInfo.Metadata.Tags = pair.Value; + beatmap.BeatmapInfo.Metadata.Tags = pair.Value; break; case @"BeatmapID": - Beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); + beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); break; case @"BeatmapSetID": - Beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); + beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); metadata.OnlineBeatmapSetID = int.Parse(pair.Value); break; } @@ -191,7 +209,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var difficulty = Beatmap.BeatmapInfo.BaseDifficulty; + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; switch (pair.Key) { case @"HPDrainRate": @@ -229,7 +247,7 @@ namespace osu.Game.Beatmaps.Formats { case EventType.Background: string filename = split[2].Trim('"'); - Beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; break; case EventType.Break: var breakEvent = new BreakPeriod @@ -241,7 +259,7 @@ namespace osu.Game.Beatmaps.Formats if (!breakEvent.HasEffect) return; - Beatmap.Breaks.Add(breakEvent); + beatmap.Breaks.Add(breakEvent); break; } } @@ -287,13 +305,13 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet == @"none") stringSampleSet = @"normal"; - DifficultyControlPoint difficultyPoint = Beatmap.ControlPointInfo.DifficultyPointAt(time); - SoundControlPoint soundPoint = Beatmap.ControlPointInfo.SoundPointAt(time); - EffectControlPoint effectPoint = Beatmap.ControlPointInfo.EffectPointAt(time); + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); + SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); + EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); if (timingChange) { - Beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint + beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { Time = time, BeatLength = beatLength, @@ -303,8 +321,8 @@ namespace osu.Game.Beatmaps.Formats if (speedMultiplier != difficultyPoint.SpeedMultiplier) { - Beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); - Beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint + beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); + beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint { Time = time, SpeedMultiplier = speedMultiplier @@ -313,7 +331,7 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) { - Beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint + beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint { Time = time, SampleBank = stringSampleSet, @@ -323,7 +341,7 @@ namespace osu.Game.Beatmaps.Formats if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) { - Beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint + beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = time, KiaiMode = kiaiMode, @@ -347,14 +365,14 @@ namespace osu.Game.Beatmaps.Formats if (!hasCustomColours) { - Beatmap.ComboColors.Clear(); + beatmap.ComboColors.Clear(); hasCustomColours = true; } // Note: the combo index specified in the beatmap is discarded if (pair.Key.StartsWith(@"Combo")) { - Beatmap.ComboColors.Add(new Color4 + beatmap.ComboColors.Add(new Color4 { R = r / 255f, G = g / 255f, @@ -373,7 +391,7 @@ namespace osu.Game.Beatmaps.Formats var obj = parser.Parse(line); if (obj != null) - Beatmap.HitObjects.Add(obj); + beatmap.HitObjects.Add(obj); } private void handleVariables(string line) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index b060de5120..85d77d93bc 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -13,57 +13,36 @@ namespace osu.Game.Beatmaps.Formats { public static void Register() { - AddDecoder(@"osu file format v14"); - AddDecoder(@"osu file format v13"); - AddDecoder(@"osu file format v12"); - AddDecoder(@"osu file format v11"); - AddDecoder(@"osu file format v10"); - AddDecoder(@"osu file format v9"); - AddDecoder(@"osu file format v8"); - AddDecoder(@"osu file format v7"); - AddDecoder(@"osu file format v6"); - AddDecoder(@"osu file format v5"); - AddDecoder(@"osu file format v4"); - AddDecoder(@"osu file format v3"); + AddDecoder(@"osu file format v14"); + AddDecoder(@"osu file format v13"); + AddDecoder(@"osu file format v12"); + AddDecoder(@"osu file format v11"); + AddDecoder(@"osu file format v10"); + AddDecoder(@"osu file format v9"); + AddDecoder(@"osu file format v8"); + AddDecoder(@"osu file format v7"); + AddDecoder(@"osu file format v6"); + AddDecoder(@"osu file format v5"); + AddDecoder(@"osu file format v4"); + AddDecoder(@"osu file format v3"); // TODO: differences between versions } - protected Beatmap Beatmap; - protected Storyboard Storyboard; - protected int BeatmapVersion; protected readonly Dictionary Variables = new Dictionary(); - public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream)); + public override Decoder GetStoryboardDecoder() => new LegacyStoryboardDecoder(BeatmapVersion); - protected override Beatmap ParseBeatmap(StreamReader stream) => new LegacyBeatmap(base.ParseBeatmap(stream)); + public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream)); protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - - Beatmap = beatmap; - Beatmap.BeatmapInfo.BeatmapVersion = BeatmapVersion; - - ParseContent(stream); - - foreach (var hitObject in Beatmap.HitObjects) - hitObject.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); + throw new NotImplementedException(); } protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - if (storyboard == null) - throw new ArgumentNullException(nameof(storyboard)); - - Storyboard = storyboard; - - ParseContent(stream); + throw new NotImplementedException(); } protected void ParseContent(StreamReader stream) @@ -79,11 +58,12 @@ namespace osu.Game.Beatmaps.Formats if (line.StartsWith("//")) continue; - if (line.StartsWith(@"osu file format v")) - { - Beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); - continue; - } + // It's already set in ParseBeatmap... why do it again? + //if (line.StartsWith(@"osu file format v")) + //{ + // Beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); + // continue; + //} if (line.StartsWith(@"[") && line.EndsWith(@"]")) { diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 317f99b9d5..aca92f3969 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -14,6 +14,8 @@ namespace osu.Game.Beatmaps.Formats { public class LegacyStoryboardDecoder : LegacyDecoder { + private Storyboard storyboard; + private StoryboardSprite storyboardSprite; private CommandTimelineGroup timelineGroup; @@ -21,9 +23,21 @@ namespace osu.Game.Beatmaps.Formats { } - public LegacyStoryboardDecoder(string header) + public LegacyStoryboardDecoder(int beatmapVersion) { - BeatmapVersion = int.Parse(header.Substring(17)); + BeatmapVersion = beatmapVersion; + } + + protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + if (storyboard == null) + throw new ArgumentNullException(nameof(storyboard)); + + this.storyboard = storyboard; + + ParseContent(stream); } protected override void ProcessSection(Section section, string line) @@ -67,7 +81,7 @@ namespace osu.Game.Beatmaps.Formats var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); - Storyboard.GetLayer(layer).Add(storyboardSprite); + storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Animation: @@ -81,7 +95,7 @@ namespace osu.Game.Beatmaps.Formats var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); - Storyboard.GetLayer(layer).Add(storyboardSprite); + storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Sample: @@ -90,7 +104,7 @@ namespace osu.Game.Beatmaps.Formats var layer = parseLayer(split[2]); var path = cleanFilename(split[3]); var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - Storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); } break; } diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index bca2cc02d0..35d16d14a5 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) using (var reader = new StreamReader(stream)) - beatmap = Game.Beatmaps.Formats.Decoder.GetBeatmapDecoder(reader).DecodeBeatmap(reader); + beatmap = Game.Beatmaps.Formats.Decoder.GetDecoder(reader).DecodeBeatmap(reader); return beatmap; } From e07b85311b29cff6c5b057cb2eb574be5948cb36 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 22:15:10 +0100 Subject: [PATCH 039/193] removed unnecessary using --- osu.Game/Tests/Visual/TestCasePlayer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 35d16d14a5..106f0fa8f3 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -8,7 +8,6 @@ using System.Text; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Formats; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; From 2bbfe0dda1bd886791dc628594822060820ec67b Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sat, 2 Dec 2017 15:28:00 +0100 Subject: [PATCH 040/193] expanded BeatmapDecoder tests, added StoryboardDecoder tests --- .../Formats/LegacyBeatmapDecoderTest.cs | 214 ++ .../Beatmaps/Formats/LegacyDecoderTest.cs | 146 -- .../Formats/LegacyStoryboardDecoderTest.cs | 89 + ...ni Yoroshiku (RLC) [Winber1's Extreme].osu | 1997 +++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 4 +- 5 files changed, 2303 insertions(+), 147 deletions(-) create mode 100644 osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs delete mode 100644 osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs create mode 100644 osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs create mode 100644 osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs new file mode 100644 index 0000000000..86413af4b6 --- /dev/null +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -0,0 +1,214 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using NUnit.Framework; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Tests.Resources; +using System.Linq; +using osu.Game.Audio; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Beatmaps.Formats; +using osu.Game.Beatmaps.Timing; + +namespace osu.Game.Tests.Beatmaps.Formats +{ + [TestFixture] + public class LegacyBeatmapDecoderTest + { + [Test] + public void TestDecodeBeatmapGeneral() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.DecodeBeatmap(stream); + var beatmapInfo = beatmap.BeatmapInfo; + var metadata = beatmap.Metadata; + + Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile); + Assert.AreEqual(0, beatmapInfo.AudioLeadIn); + Assert.AreEqual(164471, metadata.PreviewTime); + Assert.IsFalse(beatmapInfo.Countdown); + Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); + Assert.IsTrue(beatmapInfo.RulesetID == 0); + Assert.IsFalse(beatmapInfo.LetterboxInBreaks); + Assert.IsFalse(beatmapInfo.SpecialStyle); + Assert.IsFalse(beatmapInfo.WidescreenStoryboard); + } + } + + [Test] + public void TestDecodeBeatmapEditor() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmapInfo = decoder.DecodeBeatmap(stream).BeatmapInfo; + + int[] expectedBookmarks = + { + 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, + 95901, 106450, 116999, 119637, 130186, 140735, 151285, + 161834, 164471, 175020, 185570, 196119, 206669, 209306 + }; + Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length); + for (int i = 0; i < expectedBookmarks.Length; i++) + Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]); + Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing); + Assert.AreEqual(4, beatmapInfo.BeatDivisor); + Assert.AreEqual(4, beatmapInfo.GridSize); + Assert.AreEqual(2, beatmapInfo.TimelineZoom); + } + } + + [Test] + public void TestDecodeBeatmapMetadata() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.DecodeBeatmap(stream); + var beatmapInfo = beatmap.BeatmapInfo; + var metadata = beatmap.Metadata; + + Assert.AreEqual("Renatus", metadata.Title); + Assert.AreEqual("Renatus", metadata.TitleUnicode); + Assert.AreEqual("Soleily", metadata.Artist); + Assert.AreEqual("Soleily", metadata.ArtistUnicode); + Assert.AreEqual("Gamu", metadata.AuthorString); + Assert.AreEqual("Insane", beatmapInfo.Version); + Assert.AreEqual(string.Empty, metadata.Source); + Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", metadata.Tags); + Assert.AreEqual(557821, beatmapInfo.OnlineBeatmapID); + Assert.AreEqual(241526, metadata.OnlineBeatmapSetID); + } + } + + [Test] + public void TestDecodeBeatmapDifficulty() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var difficulty = decoder.DecodeBeatmap(stream).BeatmapInfo.BaseDifficulty; + + Assert.AreEqual(6.5f, difficulty.DrainRate); + Assert.AreEqual(4, difficulty.CircleSize); + Assert.AreEqual(8, difficulty.OverallDifficulty); + Assert.AreEqual(9, difficulty.ApproachRate); + Assert.AreEqual(1.8f, difficulty.SliderMultiplier); + Assert.AreEqual(2, difficulty.SliderTickRate); + } + } + + [Test] + public void TestDecodeBeatmapEvents() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.DecodeBeatmap(stream); + var metadata = beatmap.Metadata; + var breakPoint = beatmap.Breaks[0]; + + Assert.AreEqual("machinetop_background.jpg", metadata.BackgroundFile); + Assert.AreEqual(122474, breakPoint.StartTime); + Assert.AreEqual(140135, breakPoint.EndTime); + Assert.IsTrue(breakPoint.HasEffect); + } + } + + [Test] + public void TestDecodeBeatmapTimingPoints() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.DecodeBeatmap(stream); + var controlPoints = beatmap.ControlPointInfo; + + Assert.AreEqual(4, controlPoints.TimingPoints.Count); + var timingPoint = controlPoints.TimingPoints[0]; + Assert.AreEqual(956, timingPoint.Time); + Assert.AreEqual(329.67032967033d, timingPoint.BeatLength); + Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature); + + Assert.AreEqual(5, controlPoints.DifficultyPoints.Count); + var difficultyPoint = controlPoints.DifficultyPoints[0]; + Assert.AreEqual(116999, difficultyPoint.Time); + Assert.AreEqual(0.75000000000000189d, difficultyPoint.SpeedMultiplier); + + Assert.AreEqual(34, controlPoints.SoundPoints.Count); + var soundPoint = controlPoints.SoundPoints[0]; + Assert.AreEqual(956, soundPoint.Time); + Assert.AreEqual("soft", soundPoint.SampleBank); + Assert.AreEqual(60, soundPoint.SampleVolume); + + Assert.AreEqual(8, controlPoints.EffectPoints.Count); + var effectPoint = controlPoints.EffectPoints[0]; + Assert.AreEqual(53703, effectPoint.Time); + Assert.IsTrue(effectPoint.KiaiMode); + Assert.IsFalse(effectPoint.OmitFirstBarLine); + } + } + + [Test] + public void TestDecodeBeatmapColors() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var comboColors = decoder.DecodeBeatmap(stream).ComboColors; + + Color4[] expectedColors = + { + new Color4(142, 199, 255, 255), + new Color4(255, 128, 128, 255), + new Color4(128, 255, 255, 255), + new Color4(128, 255, 128, 255), + new Color4(255, 187, 255, 255), + new Color4(255, 177, 140, 255), + }; + Assert.AreEqual(expectedColors.Length, comboColors.Count); + for (int i = 0; i < expectedColors.Length; i++) + Assert.AreEqual(expectedColors[i], comboColors[i]); + } + } + + [Test] + public void TestDecodeBeatmapHitObjects() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var hitObjects = decoder.DecodeBeatmap(stream).HitObjects; + + var curveData = hitObjects[0] as IHasCurve; + var positionData = hitObjects[0] as IHasPosition; + + Assert.IsNotNull(positionData); + Assert.IsNotNull(curveData); + Assert.AreEqual(new Vector2(192, 168), positionData.Position); + Assert.AreEqual(956, hitObjects[0].StartTime); + Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); + + positionData = hitObjects[1] as IHasPosition; + + Assert.IsNotNull(positionData); + Assert.AreEqual(new Vector2(304, 56), positionData.Position); + Assert.AreEqual(1285, hitObjects[1].StartTime); + Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); + } + } + } +} diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs deleted file mode 100644 index 4266afa526..0000000000 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.IO; -using NUnit.Framework; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Beatmaps.Formats; -using osu.Game.Tests.Resources; -using System.Linq; -using osu.Game.Audio; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Tests.Beatmaps.Formats -{ - [TestFixture] - public class LegacyDecoderTest - { - [Test] - public void TestDecodeMetadata() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); - var meta = beatmap.BeatmapInfo.Metadata; - Assert.AreEqual(241526, meta.OnlineBeatmapSetID); - Assert.AreEqual("Soleily", meta.Artist); - Assert.AreEqual("Soleily", meta.ArtistUnicode); - Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); - Assert.AreEqual("Gamu", meta.AuthorString); - Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile); - Assert.AreEqual(164471, meta.PreviewTime); - Assert.AreEqual(string.Empty, meta.Source); - Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags); - Assert.AreEqual("Renatus", meta.Title); - Assert.AreEqual("Renatus", meta.TitleUnicode); - } - } - - [Test] - public void TestDecodeGeneral() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmapInfo = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; - Assert.AreEqual(0, beatmapInfo.AudioLeadIn); - Assert.AreEqual(false, beatmapInfo.Countdown); - Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); - Assert.AreEqual(false, beatmapInfo.SpecialStyle); - Assert.IsTrue(beatmapInfo.RulesetID == 0); - Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks); - Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard); - } - } - - [Test] - public void TestDecodeEditor() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; - int[] expectedBookmarks = - { - 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, - 95901, 106450, 116999, 119637, 130186, 140735, 151285, - 161834, 164471, 175020, 185570, 196119, 206669, 209306 - }; - Assert.AreEqual(expectedBookmarks.Length, beatmap.Bookmarks.Length); - for (int i = 0; i < expectedBookmarks.Length; i++) - Assert.AreEqual(expectedBookmarks[i], beatmap.Bookmarks[i]); - Assert.AreEqual(1.8, beatmap.DistanceSpacing); - Assert.AreEqual(4, beatmap.BeatDivisor); - Assert.AreEqual(4, beatmap.GridSize); - Assert.AreEqual(2, beatmap.TimelineZoom); - } - } - - [Test] - public void TestDecodeDifficulty() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; - Assert.AreEqual(6.5f, difficulty.DrainRate); - Assert.AreEqual(4, difficulty.CircleSize); - Assert.AreEqual(8, difficulty.OverallDifficulty); - Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8f, difficulty.SliderMultiplier); - Assert.AreEqual(2, difficulty.SliderTickRate); - } - } - - [Test] - public void TestDecodeColors() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); - Color4[] expected = - { - new Color4(142, 199, 255, 255), - new Color4(255, 128, 128, 255), - new Color4(128, 255, 255, 255), - new Color4(128, 255, 128, 255), - new Color4(255, 187, 255, 255), - new Color4(255, 177, 140, 255), - }; - Assert.AreEqual(expected.Length, beatmap.ComboColors.Count); - for (int i = 0; i < expected.Length; i++) - Assert.AreEqual(expected[i], beatmap.ComboColors[i]); - } - } - - [Test] - public void TestDecodeHitObjects() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); - - var curveData = beatmap.HitObjects[0] as IHasCurve; - var positionData = beatmap.HitObjects[0] as IHasPosition; - - Assert.IsNotNull(positionData); - Assert.IsNotNull(curveData); - Assert.AreEqual(new Vector2(192, 168), positionData.Position); - Assert.AreEqual(956, beatmap.HitObjects[0].StartTime); - Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); - - positionData = beatmap.HitObjects[1] as IHasPosition; - - Assert.IsNotNull(positionData); - Assert.AreEqual(new Vector2(304, 56), positionData.Position); - Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime); - Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); - } - } - } -} diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs new file mode 100644 index 0000000000..a42318883c --- /dev/null +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -0,0 +1,89 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NUnit.Framework; +using OpenTK; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.Formats; +using osu.Game.Storyboards; +using osu.Game.Tests.Resources; + +namespace osu.Game.Tests.Beatmaps.Formats +{ + [TestFixture] + public class LegacyStoryboardDecoderTest + { + [Test] + public void TestDecodeStoryboardEvents() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu")) + using (var stream = new StreamReader(resStream)) + { + var storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(stream); + + Assert.IsTrue(storyboard.HasDrawable); + Assert.AreEqual(4, storyboard.Layers.Count()); + + StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3); + Assert.IsNotNull(background); + 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.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.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.IsTrue(foreground.EnabledWhenFailing); + Assert.IsTrue(foreground.EnabledWhenPassing); + Assert.AreEqual("Foreground", foreground.Name); + + int spriteCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardSprite)).Count(); + int animationCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardAnimation)).Count(); + int sampleCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardSample)).Count(); + + Assert.AreEqual(15, spriteCount); + Assert.AreEqual(1, animationCount); + Assert.AreEqual(0, sampleCount); + Assert.AreEqual(background.Elements.Count(), spriteCount + animationCount + sampleCount); + + var sprite = background.Elements.ElementAt(0) as StoryboardSprite; + Assert.IsTrue(sprite.HasCommands); + 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); + + var animation = background.Elements.ElementAt(12) as StoryboardAnimation; + Assert.AreEqual(141175, animation.EndTime); + Assert.AreEqual(10, animation.FrameCount); + Assert.AreEqual(30, animation.FrameDelay); + Assert.IsTrue(animation.HasCommands); + Assert.AreEqual(new Vector2(320, 240), animation.InitialPosition); + Assert.IsTrue(animation.IsDrawable); + Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType); + Assert.AreEqual(Anchor.Centre, animation.Origin); + Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path); + Assert.AreEqual(78993, animation.StartTime); + } + } + } +} diff --git a/osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu b/osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu new file mode 100644 index 0000000000..f7b33fa6a8 --- /dev/null +++ b/osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu @@ -0,0 +1,1997 @@ +osu file format v13 + +[General] +AudioFilename: yotsuya192.mp3 +AudioLeadIn: 0 +PreviewTime: 71220 +Countdown: 0 +SampleSet: Soft +StackLeniency: 0.7 +Mode: 0 +LetterboxInBreaks: 0 +WidescreenStoryboard: 1 + +[Editor] +Bookmarks: 24456,34275,43002,53911,71366,88820,99729,117184,143366,152093,160820,169547 +DistanceSpacing: 1.4 +BeatDivisor: 4 +GridSize: 4 +TimelineZoom: 1 + +[Metadata] +Title:Yotsuya-san ni Yoroshiku +TitleUnicode:四ツ谷さんによろしく +Artist:Himeringo +ArtistUnicode:ひめりんご +Creator:RLC +Version:Winber1's Extreme +Source: +Tags:flask nyquill winber1 skystar amamiya yuko cheesiest onosakihito utaite leave it to eight hatsune miku vocaloid +BeatmapID:378781 +BeatmapSetID:100049 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:8.5 +ApproachRate:9.5 +SliderMultiplier:1.9 +SliderTickRate:1 + +[Events] +//Background and Video events +0,0,"primary.jpg",0,0 +//Break Periods +//Storyboard Layer 0 (Background) +Sprite,Background,Centre,"SB\lyric\ja-21.png",320,240 + S,0,117175,,0.8 + F,0,117175,117584,0,1 + M,0,117175,118675,192,393,-12,393 + F,0,117584,118402,1 + F,0,118402,118675,1,0 +Sprite,Background,Centre,"SB\lyric\ja-22.png",320,240 + S,0,118675,,0.8 + F,0,118675,119084,0,1 + M,0,118675,119629,150,393,23,393 + F,0,119084,119357,1 + F,0,119357,119629,1,0 +Sprite,Background,Centre,"SB\lyric\ja-22-repeat.png",320,240 + S,0,119357,,0.8 + M,0,119629,,196,393 + F,0,119629,119902,0,1 + M,0,119629,121947,196,393,-58,393 + F,0,119902,121675,1 + F,0,121675,121947,1,0 +Sprite,Background,Centre,"SB\lyric\ja-23.png",320,240 + S,0,121947,,0.8 + F,0,121947,122357,0,1 + M,0,121947,123038,158,393,23,393 + F,0,122357,122766,1 + F,0,122766,123038,1,0 +Sprite,Background,Centre,"SB\lyric\ja-24.png",320,240 + S,0,123038,,0.8 + F,0,123038,123447,0,1 + M,0,123038,123720,249,393,187,393 + F,0,123447,123720,1 +Sprite,Background,Centre,"SB\lyric\en-21.png",320,240 + S,0,117175,,0.6 + F,0,117175,117584,0,1 + M,0,117175,117925,641,425,551,425 + F,0,117584,118402,1 + M,0,117925,118675,551,425,461,425 + F,0,118402,118675,1,0 +Sprite,Background,Centre,"SB\lyric\en-22.png",320,240 + S,0,118675,,0.6 + F,0,118675,119084,0,1 + M,0,118675,119152,616,425,554,425 + F,0,119084,119357,1 + M,0,119152,119629,554,425,489,425 + F,0,119357,119629,1,0 +Sprite,Background,Centre,"SB\lyric\en-22-repeat.png",320,240 + S,0,119629,,0.6 + F,0,119629,120038,0,1 + M,0,119629,121947,683,425,417,425 + F,0,120038,121675,1 + F,0,121675,121947,1,0 +Sprite,Background,Centre,"SB\lyric\en-23.png",320,240 + S,0,121947,,0.6 + F,0,121947,122357,0,1 + M,0,121947,123038,617,425,487,425 + F,0,122357,122766,1 + F,0,122766,123038,1,0 +Sprite,Background,Centre,"SB\lyric\en-24.png",320,240 + S,0,123038,,0.6 + F,0,123038,123447,0,1 + M,0,123038,123720,515,425,446,425 +Sprite,Background,Centre,"SB\lower mask.png",320,240 + S,0,117175,,0.625 + R,0,117175,,0.0001 + M,0,117175,123720,320,330 +Sprite,Background,Centre,"SB\black.jpg",320,240 + F,0,-97,,1 + F,0,20084,23357,1,0 + F,0,23357,24447,0 + F,0,24447,25470,0,1 + F,0,25538,26629,0,1 + F,0,27720,28811,0.5,1 + F,0,29902,30993,0,1 + F,0,32084,33175,0.5,1 + F,0,34266,,0 + F,0,70266,,1 + S,0,70266,,1.267815 + R,0,70266,,0.0001 + F,0,71357,,0 + F,0,71357,,0 + F,0,86629,,1 + F,0,87720,88538,1,0 + F,0,88538,,0 + M,0,108877,116493,314,289,312,322 + F,0,116084,,1 + F,0,117175,117720,0,0.5 + F,0,117720,118266,0.5,0 + F,0,118266,118811,0,0.5 + F,0,118811,119357,0.5,0 + F,0,119357,119902,0,0.5 + F,0,119902,120447,0.5,0 + F,0,120447,120993,0,0.5 + F,0,120993,121538,0.5,0 + F,0,121538,122084,0,0.5 + F,0,122084,122629,0.5,0 + F,0,122629,123175,0,0.5 + F,0,123175,123686,0.5,0 + F,0,123720,,1 + F,0,126175,,1,0.07840011 + F,0,141175,,1 + F,0,143357,144447,0,1 + F,0,145538,146629,0.5,1 + F,0,147720,148811,0,1 + F,0,149902,150993,0.5,1 + F,0,150993,,1 + F,0,152084,,0 + F,0,171447,,1 + F,0,171447,177720,1 +Animation,Background,Centre,"SB\red jitter\red_0000.jpg",320,240,10,30,LoopForever + S,0,78993,,1.001 + R,0,78993,,0.0001 + F,0,78993,80084,0,1 + F,0,80084,,1 + F,0,86629,,0 + F,0,133538,134629,0.9180929,1 + F,0,141175,,1 +Sprite,Background,Centre,"SB\brown.jpg",320,240 + M,0,71357,,320,240 + S,0,71357,,1.001 + R,0,71357,,0.0001 + F,0,71357,80084,1 + F,0,80084,81175,1,0 +Sprite,Background,Centre,"SB\blue.jpg",320,240 + S,0,126175,,1.001 + R,0,126175,,0.0001 + F,0,126175,134629,1 + F,0,134629,135720,1,0 +Sprite,Background,Centre,"SB\cloud2.png",320,240 + S,0,126175,,4.659974 + R,0,126175,,0.0001 + M,0,126175,135175,72,29,474,29 + F,0,133538,135175,0.4690581,0 +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +Sprite,Foreground,Centre,"SB\lyric\ja-1.png",320,240 + S,0,53902,,0.8 + F,0,53902,54993,0,1 + M,0,53902,58266,232,393,263,393 + F,0,54993,57175,1 + F,0,57175,58266,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-1.png",320,240 + S,0,53902,,0.6 + F,0,53902,54993,0,1 + M,0,53902,58266,404,425,373,425 + F,0,54993,57175,1 + F,0,57175,58266,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-2.png",320,240 + S,0,58266,,0.8 + F,0,58266,59357,0,1 + M,0,58266,62629,232,393,263,393 + F,0,59357,61538,1 + F,0,61538,,1 + F,0,61538,,1 + F,0,61538,62357,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-3.png",320,240 + S,0,62629,,0.8 + F,0,62629,63720,0,1 + M,0,62629,66447,232,393,263,393 + F,0,63720,65357,1 + F,0,65357,66447,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-4.png",320,240 + S,0,66447,,0.8 + F,0,66447,67538,0,1 + M,0,66447,70266,232,393,263,393 + F,0,67538,69175,1 + F,0,69175,70266,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-5.png",320,240 + S,0,70266,,0.5 + M,0,70266,70402,179,335 +Sprite,Foreground,Centre,"SB\lyric\ja-5.png",320,240 + S,0,70402,,0.8 + M,0,70402,70538,469,297 +Sprite,Foreground,Centre,"SB\lyric\ja-5.png",320,240 + S,0,70538,,1.1 + M,0,70538,70675,255,158 +Sprite,Foreground,Centre,"SB\lyric\ja-5RED.png",320,240 + S,0,70675,,1.3 + F,0,70675,70947,1 + M,0,70675,71220,320,240 + F,0,70947,71357,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-6-1.png",320,240 + S,0,71357,,0.8 + M,0,71357,71902,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-6-2.png",320,240 + S,0,71902,,0.8 + M,0,71902,72447,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-6-3.png",320,240 + S,0,72447,,0.8 + M,0,72447,72857,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-7.png",320,240 + S,0,72857,,0.8 + M,0,72857,73947,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-8.png",320,240 + S,0,73947,,0.8 + M,0,73947,76129,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-9.png",320,240 + S,0,76129,,0.8 + M,0,76129,77220,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-10-2.png",320,240 + S,0,78311,,0.8 + M,0,78311,80084,376,393 +Sprite,Foreground,Centre,"SB\lyric\ja-10-1.png",320,240 + S,0,77220,,0.8 + M,0,77220,80084,264,393 +Animation,Foreground,Centre,"SB\lyric\11-1\ja-11-1_0000.png",320,240,10,30,LoopForever + S,0,80084,,0.8 + M,0,80084,81584,230,326 +Animation,Foreground,Centre,"SB\lyric\11-2\ja-11-2_0000.png",320,240,10,30,LoopForever + S,0,80629,,1.1 + M,0,80629,81584,325,305 +Animation,Foreground,Centre,"SB\lyric\11-3\ja-11-3_0000.png",320,240,10,30,LoopForever + S,0,81175,,1.3 + M,0,81175,81584,425,284 +Animation,Foreground,Centre,"SB\lyric\12\ja-12_0000.png",320,240,10,30,LoopForever + S,0,81584,,1.1 + M,0,81584,82675,306,214 +Animation,Foreground,Centre,"SB\lyric\13\ja-13_0000.png",320,240,10,30,LoopForever + S,0,82675,,0.8 + M,0,82675,84857,315,294 +Animation,Foreground,Centre,"SB\lyric\14\ja-14_0000.png",320,240,10,30,LoopForever + S,0,84857,,1.1 + M,0,84857,85947,320,222 +Animation,Foreground,Centre,"SB\lyric\15-1\ja-15-1_0000.png",320,240,10,30,LoopForever + S,0,85947,,0.8 + M,0,85947,86629,269,393 +Sprite,Foreground,Centre,"SB\lyric\ja-15-1.png",320,240 + S,0,86629,,0.8 + M,0,86629,87720,269,393 + F,0,87720,88538,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-15-2.png",320,240 + S,0,87175,,0.8 + M,0,87175,87720,373,393 + F,0,87720,88538,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-16.png",320,240 + S,0,99720,,0.8 + F,0,99720,100811,0,1 + M,0,99720,104084,232,393,263,393 + F,0,100811,102993,1 + F,0,102993,104084,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-17.png",320,240 + S,0,104084,,0.8 + F,0,104084,105175,0,1 + M,0,104084,108447,232,393,263,393 + F,0,105175,107357,1 + F,0,107357,108175,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-18.png",320,240 + S,0,108447,,0.8 + F,0,108447,109538,0,1 + M,0,108447,112266,232,393,263,393 + F,0,109538,111175,1 + F,0,111175,112266,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-19.png",320,240 + S,0,112266,,0.8 + F,0,112266,113357,0,1 + M,0,112266,116084,232,393,263,393 + F,0,113357,114993,1 + F,0,114993,116084,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-20_1.png",320,240 + S,0,116084,,0.5 + M,0,116084,116220,478,319 +Sprite,Foreground,Centre,"SB\lyric\ja-20_1.png",320,240 + S,0,116220,,0.8 + M,0,116220,116357,160,257 +Sprite,Foreground,Centre,"SB\lyric\ja-20_1.png",320,240 + S,0,116357,,1.1 + M,0,116357,116493,393,161 +Sprite,Foreground,Centre,"SB\lyric\ja-20RED.png",320,240 + M,0,116493,,320,240 + S,0,116493,,1.3 + F,0,116493,116766,1 + F,0,116766,117175,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-24RED.png",320,240 + M,0,123720,,187,393 + S,0,123720,,0.8 + F,0,123720,124811,1 + F,0,124811,125902,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-25-1.png",320,240 + S,0,125766,,0.8 + M,0,125766,126175,156,312 +Sprite,Foreground,Centre,"SB\lyric\ja-25-2.png",320,240 + S,0,126175,,0.8 + M,0,126175,126993,156,312 + F,0,127538,127811,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-25-3.png",320,240 + S,0,126447,,1.1 + R,0,126447,,-0.189046 + M,0,126447,126993,306,288 + F,0,127538,,1 + F,0,127538,127811,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-25-4.png",320,240 + M,0,126993,,388,400 + S,0,126993,,1.3 + R,0,126993,,0.1785437 + F,0,127538,127811,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-26.png",320,240 + S,0,127402,,0.8 + F,0,127402,127538,0,1 + M,0,127402,128084,315,201 + R,0,127402,128084,0.1050253 + F,0,127538,128084,1 + M,0,128084,128493,315,201,315,236 + F,0,128084,128493,1,0 + R,0,128084,128493,0.1050253,0.3360815 +Sprite,Foreground,Centre,"SB\lyric\ja-27.png",320,240 + S,0,128493,,0.9 + F,0,128493,128629,0,1 + M,0,128493,130266,191,426 + R,0,128493,130266,0.06301453 + F,0,128629,130266,1 + M,0,130266,130675,191,426,191,458 + F,0,130266,130675,1,0 + R,0,130266,130675,0.06301453,0.2835687 +Sprite,Foreground,Centre,"SB\lyric\ja-28.png",320,240 + S,0,130675,,0.8 + R,0,130675,,-0.1155283 + F,0,130675,130811,0,1 + M,0,130675,131357,320,240 + F,0,130811,131357,1 + F,0,131357,131766,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-29.png",320,240 + S,0,131766,,0.8 + F,0,131766,131902,0,1 + M,0,131766,133538,230,154 + R,0,131766,133538,0.1260309 + F,0,131902,133538,1 + M,0,133538,134629,230,154,230,206 + F,0,133538,134629,1,0 + R,0,133538,134629,0.1260309,0.2835693 +Animation,Foreground,Centre,"SB\lyric\12\ja-12_0000.png",320,240,10,30,LoopForever + S,0,136129,,1.1 + M,0,136129,137220,306,214 +Animation,Foreground,Centre,"SB\lyric\13\ja-13_0000.png",320,240,10,30,LoopForever + S,0,137220,,0.8 + M,0,137220,139402,315,294 +Animation,Foreground,Centre,"SB\lyric\14\ja-14_0000.png",320,240,10,30,LoopForever + S,0,139402,,1.1 + M,0,139402,140493,320,222 +Animation,Foreground,Centre,"SB\lyric\30-1\ja-30-1_0000.png",320,240,10,30,LoopForever + S,0,134629,,0.8 + M,0,134629,136129,153,292 +Animation,Foreground,Centre,"SB\lyric\30-2\ja-30-2_0000.png",320,240,10,30,LoopForever + S,0,135175,,1.1 + M,0,135175,136129,333,216 +Animation,Foreground,Centre,"SB\lyric\30-3\ja-30-3_0000.png",320,240,10,30,LoopForever + S,0,135720,,1.3 + M,0,135720,136129,485,133 +Animation,Foreground,Centre,"SB\lyric\34-1\ja-34-1_0000.png",320,240,10,30,LoopForever + S,0,140493,,0.8 + M,0,140493,141175,269,393 +Sprite,Foreground,Centre,"SB\lyric\ja-34-1.png",320,240 + S,0,141175,,0.8 + M,0,141175,143357,269,393 + F,0,142266,143357,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-34-2.png",320,240 + S,0,141720,,0.8 + M,0,141720,143357,391,393 + F,0,142266,143357,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-2.png",320,240 + S,0,58266,,0.6 + F,0,58266,59357,0,1 + M,0,58266,62629,404,425,373,425 + F,0,59357,61538,1 + F,0,61538,62357,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-3.png",320,240 + S,0,62629,,0.6 + F,0,62629,63720,0,1 + M,0,62629,66447,404,425,373,425 + F,0,63720,65357,1 + F,0,65357,66447,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-4.png",320,240 + S,0,66447,,0.6 + F,0,66447,67538,0,1 + M,0,66447,70266,404,425,373,425 + F,0,67538,69175,1 + F,0,69175,70266,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-5.png",320,240 + F,0,70266,,1 + S,0,70266,,0.4 + M,0,70266,70402,436,192 +Sprite,Foreground,Centre,"SB\lyric\en-5.png",320,240 + S,0,70402,,0.6 + M,0,70402,70538,181,97 +Sprite,Foreground,Centre,"SB\lyric\en-5.png",320,240 + S,0,70538,,0.8 + M,0,70538,70675,391,392 +Sprite,Foreground,Centre,"SB\lyric\en-5RED.png",320,240 + M,0,70675,,320,294 + F,0,70675,70947,1 + F,0,70947,71357,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-6-1.png",320,240 + S,0,71357,,0.7885935 + M,0,71357,71902,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-6-2.png",320,240 + S,0,71902,,0.7357419 + M,0,71902,72447,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-6-3.png",320,240 + S,0,72447,,0.8084131 + M,0,72447,72857,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-7.png",320,240 + S,0,72857,,0.8018063 + M,0,72857,73947,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-8.png",320,240 + S,0,73947,,0.6961035 + M,0,73947,76129,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-9.png",320,240 + S,0,76129,,0.762168 + M,0,76129,77220,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-10-1.png",320,240 + S,0,77220,,0.6564645 + M,0,77220,80084,262,425 +Sprite,Foreground,Centre,"SB\lyric\en-10-2.png",320,240 + S,0,78311,,0.5837936 + M,0,78311,80084,411,425 +Sprite,Foreground,Centre,"SB\lyric\en-16.png",320,240 + S,0,99720,,0.6 + F,0,99720,100811,0,1 + M,0,99720,104084,404,425,373,425 + F,0,100811,102993,1 + F,0,102993,104084,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-17-1.png",320,240 + S,0,104084,,0.6 + F,0,104084,105175,0,1 + M,0,104084,108447,404,425,373,425 + F,0,105175,107357,1 + F,0,107357,108175,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-17-2.png",320,240 + S,0,104084,,0.6 + F,0,104084,105175,0,1 + M,0,104084,108175,232,457,263,457 + F,0,105175,107357,1 + F,0,107357,108175,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-18.png",320,240 + S,0,108447,,0.6 + F,0,108447,109538,0,1 + M,0,108447,112266,404,425,373,425 + F,0,109538,111175,1 + F,0,111175,112266,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-19.png",320,240 + S,0,112266,,0.6 + F,0,112266,113357,0,1 + M,0,112266,116084,404,425,373,425 + F,0,113357,114993,1 + F,0,114993,116084,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-20-1.png",320,240 + S,0,116084,,0.4 + M,0,116084,116220,205,167 +Sprite,Foreground,Centre,"SB\lyric\en-20-1.png",320,240 + S,0,116220,,0.6 + M,0,116220,116357,389,252 +Sprite,Foreground,Centre,"SB\lyric\en-20-1.png",320,240 + S,0,116357,,0.8 + M,0,116357,116493,220,395 +Sprite,Foreground,Centre,"SB\lyric\en-20RED.png",320,240 + M,0,116493,,320,293 + F,0,116493,116766,1 + F,0,116766,117175,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-24RED.png",320,240 + M,0,123720,,446,425 + S,0,123720,,0.6 + F,0,123720,124811,1 + F,0,124811,125902,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-25-1.png",320,240 + S,0,125766,,0.6 + M,0,125766,126175,408,240 +Sprite,Foreground,Centre,"SB\lyric\en-25-2.png",320,240 + S,0,126175,,0.6 + M,0,126175,126993,408,240 + F,0,127538,127811,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-25-3.png",320,240 + M,0,126447,,525,277 + S,0,126447,,0.8 + R,0,126447,,0.189046 + F,0,127538,127811,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-25-4.png",320,240 + M,0,126993,,219,400 + S,0,126993,,1 + R,0,126993,,-0.1260309 + F,0,126993,127402,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-26.png",320,240 + S,0,127402,,0.7 + F,0,127402,127538,0,1 + M,0,127402,128084,416,325 + R,0,127402,128084,-0.1680408 + F,0,127538,128084,1 + M,0,128084,128493,416,325,416,391 + F,0,128084,128493,1,0 + R,0,128084,128493,-0.1680408,-0.3990967 +Sprite,Foreground,Centre,"SB\lyric\en-27.png",320,240 + S,0,128493,,0.6 + F,0,128493,128629,0,1 + M,0,128493,130266,443,226 + R,0,128493,130266,-0.08402039 + F,0,128629,130266,1 + M,0,130266,130675,443,226,443,252 + F,0,130266,130675,1,0 + R,0,130266,130675,-0.08402039,-0.2310565 +Sprite,Foreground,Centre,"SB\lyric\en-28.png",320,240 + S,0,130675,,0.7 + R,0,130675,,0.273067 + F,0,130675,130811,0,1 + M,0,130675,131357,140,303 + F,0,130811,131357,1 + F,0,131357,131766,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-29.png",320,240 + S,0,131766,,0.7 + F,0,131766,131902,0,1 + M,0,131766,133538,438,257 + R,0,131766,133538,-0.1890472 + F,0,131902,133538,1 + M,0,133538,134629,438,257,438,314 + F,0,133538,134629,1,0 + R,0,133538,134629,-0.1890472,-0.4306067 +Animation,Foreground,Centre,"SB\lyric\11-1\en-11-1_0000.png",320,240,10,30,LoopForever + S,0,80357,,0.6 + M,0,80357,81584,336,384 +Animation,Foreground,Centre,"SB\lyric\11-2\en-11-2_0000.png",320,240,10,30,LoopForever + S,0,80902,,0.8 + M,0,80902,81993,142,245 +Animation,Foreground,Centre,"SB\lyric\11-3\en-11-3_0000.png",320,240,10,30,LoopForever + S,0,81447,,1 + M,0,81447,81993,413,164 +Animation,Foreground,Centre,"SB\lyric\12\en-12_0000.png",320,240,10,30,LoopForever + S,0,81993,,0.8 + M,0,81993,83357,255,360 +Animation,Foreground,Centre,"SB\lyric\13\en-13_0000.png",320,240,10,30,LoopForever + S,0,83357,,0.6 + M,0,83357,85538,222,140 +Animation,Foreground,Centre,"SB\lyric\14\en-14_0000.png",320,240,10,30,LoopForever + S,0,85538,,1 + M,0,85538,86357,437,300 +Animation,Foreground,Centre,"SB\lyric\15-1\en-15-1_0000.png",320,240,10,30,LoopForever + F,0,86357,,1 + S,0,86357,,0.6564642 + M,0,86357,86629,237,430 +Sprite,Foreground,Centre,"SB\lyric\en-15-1.png",320,240 + S,0,86629,,0.6630707 + M,0,86629,87720,234,430 + F,0,86629,87720,1 + F,0,87720,88538,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-15-2.png",320,240 + S,0,87175,,0.7423482 + M,0,87175,87720,417,430 + F,0,87175,87720,1 + F,0,87720,88538,1,0 +Animation,Foreground,Centre,"SB\lyric\30-1\en-30-1_0000.png",320,240,10,30,LoopForever + F,0,134902,,1 + S,0,134902,,0.6 + M,0,134902,136129,419,374 +Animation,Foreground,Centre,"SB\lyric\30-2\en-30-2_0000.png",320,240,10,30,LoopForever + S,0,135447,,0.6 + M,0,135447,136811,156,164 +Animation,Foreground,Centre,"SB\lyric\30-3\en-30-3_0000.png",320,240,10,30,LoopForever + S,0,135993,,0.6 + M,0,135993,136811,128,404 +Animation,Foreground,Centre,"SB\lyric\12\en-12_0000.png",320,240,10,30,LoopForever + S,0,136811,,0.8 + M,0,136811,137902,439,391 +Animation,Foreground,Centre,"SB\lyric\13\en-13_0000.png",320,240,10,30,LoopForever + S,0,137902,,0.6 + M,0,137902,140084,450,142 +Animation,Foreground,Centre,"SB\lyric\14\en-14_0000.png",320,240,10,30,LoopForever + S,0,140084,,1 + M,0,140084,140902,191,294 +Animation,Foreground,Centre,"SB\lyric\34-1\en-34-1_0000.png",320,240,10,30,LoopForever + S,0,140902,,0.6 + M,0,140902,141175,286,425 +Sprite,Foreground,Centre,"SB\lyric\en-34-1-1.png",320,240 + S,0,141175,,0.6 + M,0,141175,142266,286,425 + F,0,142266,143357,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-34-1-2.png",320,240 + S,0,141175,,0.6 + M,0,141175,142266,286,425 + F,0,142266,143357,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-34-2.png",320,240 + S,0,141720,,0.6 + M,0,141720,142266,451,425 + F,0,142266,143357,1,0 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-1.png",320,240 + S,0,25538,,0.6 + M,0,25538,27720,50,240 + M,0,27720,27857,50,240,50,247 + F,0,27720,28538,1,0 + R,0,27720,28538,0,0.3567487 + M,0,27857,27993,50,247,50,263 + M,0,27993,28129,50,263,50,290 + M,0,28129,28266,50,290,50,321 + M,0,28266,28402,50,321,50,360 + M,0,28402,28538,50,360,50,408 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-2.png",320,240 + S,0,25538,,0.6 + M,0,25538,28947,104,240 + M,0,28947,29084,104,240,104,248 + F,0,28947,29766,1,0 + R,0,28947,29766,0,-0.4095996 + M,0,29084,29220,104,248,104,263 + M,0,29220,29357,104,263,104,287 + M,0,29357,29493,104,287,104,320 + M,0,29493,29629,104,320,104,360 + M,0,29629,29766,104,360,104,409 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-3.png",320,240 + S,0,25538,,0.6 + M,0,25538,28129,158,240 + M,0,28129,28266,158,240,158,249 + F,0,28129,28947,1,0 + R,0,28129,28947,0,-0.2114062 + M,0,28266,28402,158,249,158,263 + M,0,28402,28538,158,263,158,288 + M,0,28538,28675,158,288,158,321 + M,0,28675,28811,158,321,158,360 + M,0,28811,28947,158,360,158,409 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-4.png",320,240 + S,0,25538,,0.6 + M,0,25538,28811,212,240 + M,0,28811,28947,212,240,212,248 + F,0,28811,29629,1,0 + R,0,28811,29629,0,0.4624512 + M,0,28947,29084,212,248,212,263 + M,0,29084,29220,212,263,212,288 + M,0,29220,29357,212,288,212,320 + M,0,29357,29493,212,320,212,360 + M,0,29493,29629,212,360,212,407 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-5.png",320,240 + S,0,25538,,0.6 + M,0,25538,28538,266,240 + M,0,28538,28675,266,240,266,248 + F,0,28538,29357,1,0 + R,0,28538,29357,0,0.2906836 + M,0,28675,28811,266,248,266,264 + M,0,28811,28947,266,264,266,288 + M,0,28947,29084,266,288,266,321 + M,0,29084,29220,266,321,266,358 + M,0,29220,29357,266,358,266,407 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-6.png",320,240 + S,0,25538,,0.6 + M,0,25538,27993,320,240 + M,0,27993,28129,320,240,320,247 + F,0,27993,28811,1,0 + R,0,27993,28811,0,0.2906836 + M,0,28129,28266,320,247,320,263 + M,0,28266,28402,320,263,320,288 + M,0,28402,28538,320,288,320,321 + M,0,28538,28675,320,321,320,360 + M,0,28675,28811,320,360,320,408 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-7.png",320,240 + S,0,25538,,0.6 + M,0,25538,28266,374,240 + M,0,28266,28402,374,240,374,248 + F,0,28266,29084,1,0 + R,0,28266,29084,0,0.1717682 + M,0,28402,28538,374,248,374,264 + M,0,28538,28675,374,264,374,288 + M,0,28675,28811,374,288,374,320 + M,0,28811,28947,374,320,374,359 + M,0,28947,29084,374,359,374,409 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-8.png",320,240 + S,0,25538,,0.6 + M,0,25538,27857,428,240 + M,0,27857,27993,428,240,428,249 + F,0,27857,28675,1,0 + R,0,27857,28675,0,-0.237832 + M,0,27993,28129,428,249,428,263 + M,0,28129,28266,428,263,428,288 + M,0,28266,28402,428,288,428,320 + M,0,28402,28538,428,320,428,358 + M,0,28538,28675,428,358,428,408 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-9.png",320,240 + S,0,25538,,0.6 + M,0,25538,28675,482,240 + M,0,28675,28811,482,240,482,247 + F,0,28675,29493,1,0 + R,0,28675,29493,0,-0.5020905 + M,0,28811,28947,482,247,482,263 + M,0,28947,29084,482,263,482,287 + M,0,29084,29220,482,287,482,320 + M,0,29220,29357,482,320,482,360 + M,0,29357,29493,482,360,482,407 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-10-1.png",320,240 + S,0,25538,,0.6 + M,0,25538,28402,536,240 + M,0,28402,28538,536,240,536,247 + F,0,28402,29220,1,0 + R,0,28402,29220,0,-0.3038965 + M,0,28538,28675,536,247,536,263 + M,0,28675,28811,536,263,536,289 + M,0,28811,28947,536,289,536,321 + M,0,28947,29084,536,321,536,360 + M,0,29084,29220,536,360,536,407 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-11.png",320,240 + S,0,25538,,0.6 + M,0,25538,29084,590,240 + F,0,28947,29902,1,0 + R,0,28947,29902,0,0.6474323 + M,0,29084,29220,590,240,590,247 + M,0,29220,29357,590,247,590,263 + M,0,29357,29493,590,263,590,288 + M,0,29493,29629,590,288,590,320 + M,0,29629,29766,590,320,590,361 + M,0,29766,29902,590,361,590,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-1.png",320,240 + S,0,29902,,0.6 + M,0,29902,32084,50,240 + M,0,32084,32221,50,240,50,247 + F,0,32084,32902,1,0 + R,0,32084,32902,0,0.4 + M,0,32221,32357,50,247,50,263 + M,0,32357,32493,50,263,50,290 + M,0,32493,32630,50,290,50,321 + M,0,32630,32766,50,321,50,360 + M,0,32766,32902,50,360,50,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-2.png",320,240 + S,0,29902,,0.6 + M,0,29902,33311,104,240 + M,0,33311,33448,104,240,104,248 + F,0,33311,34130,1,0 + R,0,33311,34130,0,-0.4 + M,0,33448,33584,104,248,104,263 + M,0,33584,33721,104,263,104,287 + M,0,33721,33857,104,287,104,320 + M,0,33857,33993,104,320,104,360 + M,0,33993,34130,104,360,104,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-3.png",320,240 + S,0,29902,,0.6 + M,0,29902,32493,158,240 + M,0,32493,32630,158,240,158,249 + F,0,32493,33311,1,0 + R,0,32493,33311,0,-0.4 + M,0,32630,32766,158,249,158,263 + M,0,32766,32902,158,263,158,288 + M,0,32902,33039,158,288,158,321 + M,0,33039,33175,158,321,158,360 + M,0,33175,33311,158,360,158,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-4.png",320,240 + S,0,29902,,0.6 + M,0,29902,33175,212,240 + M,0,33175,33311,212,240,212,248 + F,0,33175,33993,1,0 + R,0,33175,33993,0,0.4 + M,0,33311,33448,212,248,212,263 + M,0,33448,33584,212,263,212,288 + M,0,33584,33721,212,288,212,320 + M,0,33721,33857,212,320,212,360 + M,0,33857,33993,212,360,212,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-5.png",320,240 + S,0,29902,,0.6 + M,0,29902,32902,266,240 + M,0,32902,33039,266,240,266,248 + F,0,32902,33721,1,0 + R,0,32902,33721,0,0.4 + M,0,33039,33175,266,248,266,264 + M,0,33175,33311,266,264,266,288 + M,0,33311,33448,266,288,266,321 + M,0,33448,33584,266,321,266,358 + M,0,33584,33721,266,358,266,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-6.png",320,240 + S,0,29902,,0.6 + M,0,29902,32357,320,240 + M,0,32357,32493,320,240,320,247 + F,0,32357,33175,1,0 + R,0,32357,33175,0,0.4 + M,0,32493,32630,320,247,320,263 + M,0,32630,32766,320,263,320,288 + M,0,32766,32902,320,288,320,321 + M,0,32902,33039,320,321,320,360 + M,0,33039,33175,320,360,320,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-7.png",320,240 + S,0,29902,,0.6 + M,0,29902,32630,374,240 + M,0,32630,32766,374,240,374,248 + F,0,32630,33448,1,0 + R,0,32630,33448,0,0.4 + M,0,32766,32902,374,248,374,264 + M,0,32902,33039,374,264,374,288 + M,0,33039,33175,374,288,374,320 + M,0,33175,33311,374,320,374,359 + M,0,33311,33448,374,359,374,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-8.png",320,240 + S,0,29902,,0.6 + M,0,29902,32221,428,240 + M,0,32221,32357,428,240,428,249 + F,0,32221,33039,1,0 + R,0,32221,33039,0,-0.4 + M,0,32357,32493,428,249,428,263 + M,0,32493,32630,428,263,428,288 + M,0,32630,32766,428,288,428,320 + M,0,32766,32902,428,320,428,358 + M,0,32902,33039,428,358,428,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-9.png",320,240 + S,0,29902,,0.6 + M,0,29902,33039,482,240 + M,0,33039,33175,482,240,482,247 + F,0,33039,33857,1,0 + R,0,33039,33857,0,-0.4 + M,0,33175,33311,482,247,482,263 + M,0,33311,33448,482,263,482,287 + M,0,33448,33584,482,287,482,320 + M,0,33584,33721,482,320,482,360 + M,0,33721,33857,482,360,482,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-10-1.png",320,240 + S,0,29902,,0.6 + M,0,29902,32766,536,240 + M,0,32766,32902,536,240,536,247 + F,0,32766,33584,1,0 + R,0,32766,33584,0,-0.4 + M,0,32902,33039,536,247,536,263 + M,0,33039,33175,536,263,536,289 + M,0,33175,33311,536,289,536,321 + M,0,33311,33448,536,321,536,360 + M,0,33448,33584,536,360,536,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-11.png",320,240 + S,0,29902,,0.6 + M,0,29902,33448,590,240 + F,0,33311,34266,1,0 + R,0,33311,34266,0,0.4 + M,0,33448,33584,590,240,590,247 + M,0,33584,33721,590,247,590,263 + M,0,33721,33857,590,263,590,288 + M,0,33857,33993,590,288,590,320 + M,0,33993,34130,590,320,590,361 + M,0,34130,34266,590,361,590,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-1.png",320,240 + S,0,143357,,0.6 + M,0,143357,145539,50,240 + M,0,145539,145676,50,240,50,247 + F,0,145539,146357,1,0 + R,0,145539,146357,0,0.4 + M,0,145676,145812,50,247,50,263 + M,0,145812,145948,50,263,50,290 + M,0,145948,146085,50,290,50,321 + M,0,146085,146221,50,321,50,360 + M,0,146221,146357,50,360,50,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-2.png",320,240 + S,0,143357,,0.6 + M,0,143357,146766,104,240 + M,0,146766,146903,104,240,104,248 + F,0,146766,147585,1,0 + R,0,146766,147585,0,-0.4 + M,0,146903,147039,104,248,104,263 + M,0,147039,147176,104,263,104,287 + M,0,147176,147312,104,287,104,320 + M,0,147312,147448,104,320,104,360 + M,0,147448,147585,104,360,104,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-3.png",320,240 + S,0,143357,,0.6 + M,0,143357,145948,158,240 + M,0,145948,146085,158,240,158,249 + F,0,145948,146766,1,0 + R,0,145948,146766,0,-0.4 + M,0,146085,146221,158,249,158,263 + M,0,146221,146357,158,263,158,288 + M,0,146357,146494,158,288,158,321 + M,0,146494,146630,158,321,158,360 + M,0,146630,146766,158,360,158,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-4.png",320,240 + S,0,143357,,0.6 + M,0,143357,146630,212,240 + M,0,146630,146766,212,240,212,248 + F,0,146630,147448,1,0 + R,0,146630,147448,0,0.4 + M,0,146766,146903,212,248,212,263 + M,0,146903,147039,212,263,212,288 + M,0,147039,147176,212,288,212,320 + M,0,147176,147312,212,320,212,360 + M,0,147312,147448,212,360,212,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-5.png",320,240 + S,0,143357,,0.6 + M,0,143357,146357,266,240 + M,0,146357,146494,266,240,266,248 + F,0,146357,147176,1,0 + R,0,146357,147176,0,0.4 + M,0,146494,146630,266,248,266,264 + M,0,146630,146766,266,264,266,288 + M,0,146766,146903,266,288,266,321 + M,0,146903,147039,266,321,266,358 + M,0,147039,147176,266,358,266,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-6.png",320,240 + S,0,143357,,0.6 + M,0,143357,145812,320,240 + M,0,145812,145948,320,240,320,247 + F,0,145812,146630,1,0 + R,0,145812,146630,0,0.4 + M,0,145948,146085,320,247,320,263 + M,0,146085,146221,320,263,320,288 + M,0,146221,146357,320,288,320,321 + M,0,146357,146494,320,321,320,360 + M,0,146494,146630,320,360,320,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-7.png",320,240 + S,0,143357,,0.6 + M,0,143357,146085,374,240 + M,0,146085,146221,374,240,374,248 + F,0,146085,146903,1,0 + R,0,146085,146903,0,0.4 + M,0,146221,146357,374,248,374,264 + M,0,146357,146494,374,264,374,288 + M,0,146494,146630,374,288,374,320 + M,0,146630,146766,374,320,374,359 + M,0,146766,146903,374,359,374,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-8.png",320,240 + S,0,143357,,0.6 + M,0,143357,145676,428,240 + M,0,145676,145812,428,240,428,249 + F,0,145676,146494,1,0 + R,0,145676,146494,0,-0.4 + M,0,145812,145948,428,249,428,263 + M,0,145948,146085,428,263,428,288 + M,0,146085,146221,428,288,428,320 + M,0,146221,146357,428,320,428,358 + M,0,146357,146494,428,358,428,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-9.png",320,240 + S,0,143357,,0.6 + M,0,143357,146494,482,240 + M,0,146494,146630,482,240,482,247 + F,0,146494,147312,1,0 + R,0,146494,147312,0,-0.4 + M,0,146630,146766,482,247,482,263 + M,0,146766,146903,482,263,482,287 + M,0,146903,147039,482,287,482,320 + M,0,147039,147176,482,320,482,360 + M,0,147176,147312,482,360,482,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-10-1.png",320,240 + S,0,143357,,0.6 + M,0,143357,146221,536,240 + M,0,146221,146357,536,240,536,247 + F,0,146221,147039,1,0 + R,0,146221,147039,0,-0.4 + M,0,146357,146494,536,247,536,263 + M,0,146494,146630,536,263,536,289 + M,0,146630,146766,536,289,536,321 + M,0,146766,146903,536,321,536,360 + M,0,146903,147039,536,360,536,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-11.png",320,240 + S,0,143357,,0.6 + M,0,143357,146903,590,240 + F,0,146766,147721,1,0 + R,0,146766,147721,0,0.4 + M,0,146903,147039,590,240,590,247 + M,0,147039,147176,590,247,590,263 + M,0,147176,147312,590,263,590,288 + M,0,147312,147448,590,288,590,320 + M,0,147448,147585,590,320,590,361 + M,0,147585,147721,590,361,590,408 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-1.png",320,240 + S,0,147720,,0.6 + M,0,147720,150993,104,240 + M,0,150993,151130,104,240,104,248 + F,0,150993,151812,1,0 + R,0,150993,151812,0,-0.4 + M,0,151130,151266,104,248,104,263 + M,0,151266,151403,104,263,104,287 + M,0,151403,151539,104,287,104,320 + M,0,151539,151675,104,320,104,360 + M,0,151675,151812,104,360,104,409 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-2.png",320,240 + S,0,147720,,0.6 + M,0,147720,150175,158,240 + M,0,150175,150312,158,240,158,249 + F,0,150175,150993,1,0 + R,0,150175,150993,0,-0.4 + M,0,150312,150448,158,249,158,263 + M,0,150448,150584,158,263,158,288 + M,0,150584,150721,158,288,158,321 + M,0,150721,150857,158,321,158,360 + M,0,150857,150993,158,360,158,409 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-3.png",320,240 + S,0,147720,,0.6 + M,0,147720,150857,212,240 + M,0,150857,150993,212,240,212,248 + F,0,150857,151675,1,0 + R,0,150857,151675,0,0.4 + M,0,150993,151130,212,248,212,263 + M,0,151130,151266,212,263,212,288 + M,0,151266,151403,212,288,212,320 + M,0,151403,151539,212,320,212,360 + M,0,151539,151675,212,360,212,407 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-4.png",320,240 + S,0,147720,,0.6 + M,0,147720,150584,266,240 + M,0,150584,150721,266,240,266,248 + F,0,150584,151403,1,0 + R,0,150584,151403,0,0.4 + M,0,150721,150857,266,248,266,264 + M,0,150857,150993,266,264,266,288 + M,0,150993,151130,266,288,266,321 + M,0,151130,151266,266,321,266,358 + M,0,151266,151403,266,358,266,407 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-5.png",320,240 + S,0,147720,,0.6 + M,0,147720,150039,320,240 + M,0,150039,150175,320,240,320,247 + F,0,150039,150857,1,0 + R,0,150039,150857,0,0.4 + M,0,150175,150312,320,247,320,263 + M,0,150312,150448,320,263,320,288 + M,0,150448,150584,320,288,320,321 + M,0,150584,150721,320,321,320,360 + M,0,150721,150857,320,360,320,408 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-6.png",320,240 + S,0,147720,,0.6 + M,0,147720,150312,374,240 + M,0,150312,150448,374,240,374,248 + F,0,150312,151130,1,0 + R,0,150312,151130,0,0.4 + M,0,150448,150584,374,248,374,264 + M,0,150584,150721,374,264,374,288 + M,0,150721,150857,374,288,374,320 + M,0,150857,150993,374,320,374,359 + M,0,150993,151130,374,359,374,409 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-7.png",320,240 + S,0,147720,,0.6 + M,0,147720,149903,428,240 + M,0,149903,150039,428,240,428,249 + F,0,149903,150721,1,0 + R,0,149903,150721,0,-0.4 + M,0,150039,150175,428,249,428,263 + M,0,150175,150312,428,263,428,288 + M,0,150312,150448,428,288,428,320 + M,0,150448,150584,428,320,428,358 + M,0,150584,150721,428,358,428,408 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-8.png",320,240 + S,0,147720,,0.6 + M,0,147720,150721,482,240 + M,0,150721,150857,482,240,482,247 + F,0,150721,151539,1,0 + R,0,150721,151539,0,-0.4 + M,0,150857,150993,482,247,482,263 + M,0,150993,151130,482,263,482,287 + M,0,151130,151266,482,287,482,320 + M,0,151266,151403,482,320,482,360 + M,0,151403,151539,482,360,482,407 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-9.png",320,240 + S,0,147720,,0.6 + M,0,147720,150448,536,240 + M,0,150448,150584,536,240,536,247 + F,0,150448,151266,1,0 + R,0,150448,151266,0,-0.4 + M,0,150584,150721,536,247,536,263 + M,0,150721,150857,536,263,536,289 + M,0,150857,150993,536,289,536,321 + M,0,150993,151130,536,321,536,360 + M,0,151130,151266,536,360,536,407 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-en1.png",320,240 + S,0,25538,,0.8 + M,0,25538,27720,320,280 + F,0,27720,27993,1,0 +Sprite,Foreground,Centre,"SB\lyric\y2\intro-en2.png",320,240 + M,0,29902,,320,280 + S,0,29902,,0.8 + F,0,32084,32357,1,0 +Sprite,Foreground,Centre,"SB\lyric\z\outro-en1.png",320,240 + M,0,143357,,320,280 + S,0,143357,,0.8 + F,0,145538,145811,1,0 +Sprite,Foreground,Centre,"SB\lyric\z\outro-en2.png",320,240 + M,0,147720,,320,280 + S,0,147720,,0.8 + F,0,149902,150175,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-34-1-1.png",320,240 + S,0,141174,,0.8 + M,0,141174,143356,343,393 + F,0,142265,143356,1,0 +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +2629,272.727272727273,4,2,1,30,1,0 +20083,291.26213592233,4,2,1,30,1,0 +21248,288.461538461538,4,2,1,30,1,0 +22329,-181.818181818182,4,2,1,30,0,0 +22473,-181.818181818182,4,2,1,5,0,0 +23356,272.727272727273,4,2,1,5,1,0 +23492,-100,4,2,1,10,0,0 +23628,-100,4,2,1,15,0,0 +23765,-100,4,2,1,20,0,0 +23901,-100,4,2,1,25,0,0 +24037,-100,4,2,1,30,0,0 +24174,-100,4,2,1,35,0,0 +24310,-100,4,2,1,40,0,0 +24446,-117.647058823529,4,1,1,50,0,0 +25333,-100,4,1,1,5,0,0 +25537,-100,4,1,1,50,0,0 +25810,-117.647058823529,4,2,1,50,0,0 +26492,-117.647058823529,4,1,1,50,0,0 +26765,-117.647058823529,4,2,1,50,0,0 +27719,-117.647058823529,4,1,1,50,0,0 +27992,-117.647058823529,4,2,1,50,0,0 +28674,-117.647058823529,4,1,1,50,0,0 +28878,-117.647058823529,4,2,1,5,0,0 +28946,-117.647058823529,4,2,1,50,0,0 +29901,-100,4,1,1,50,0,0 +30174,-117.647058823529,4,2,1,50,0,0 +30856,-117.647058823529,4,1,1,50,0,0 +31128,-117.647058823529,4,2,1,50,0,0 +32083,-117.647058823529,4,1,1,50,0,0 +32356,-117.647058823529,4,2,1,50,0,0 +33242,-117.647058823529,4,1,1,5,0,0 +33310,-117.647058823529,4,1,1,30,0,0 +34265,-100,4,1,1,60,0,0 +42583,-117.647058823529,4,1,1,40,0,0 +42992,-100,4,1,1,60,0,0 +51719,-133.333333333333,4,2,1,60,0,0 +53492,-117.647058823529,4,2,1,80,0,0 +53628,-200,4,2,1,60,0,0 +56015,-200,4,2,1,5,0,0 +56083,-200,4,2,1,60,0,0 +60992,-200,4,2,1,60,0,0 +61060,-200,4,1,1,20,0,0 +61128,-200,4,1,1,60,0,0 +61196,-200,4,1,1,20,0,0 +61265,-200,4,1,1,60,0,0 +61401,-200,4,1,1,20,0,0 +61537,-200,4,1,1,60,0,0 +61605,-200,4,1,1,20,0,0 +61946,-200,4,1,1,60,0,0 +62015,-200,4,1,1,20,0,0 +62355,-200,4,1,1,60,0,0 +62628,-200,4,2,1,60,0,0 +70265,-100,4,1,1,60,0,0 +71356,-90.9090909090909,4,1,1,60,0,1 +78992,-90.9090909090909,4,1,1,40,0,0 +80083,-90.9090909090909,4,1,1,60,0,1 +86628,-117.647058823529,4,1,1,60,0,0 +87719,-117.647058823529,4,1,1,60,0,0 +88810,-100,4,1,1,60,0,0 +97537,-133.333333333333,4,2,1,60,0,0 +99310,-100,4,2,1,80,0,0 +99446,-200,4,2,1,60,0,0 +101833,-200,4,2,1,5,0,0 +101901,-200,4,2,1,60,0,0 +106810,-200,4,2,1,60,0,0 +106878,-200,4,2,1,20,0,0 +106878,-200,4,2,1,60,0,0 +106946,-200,4,1,1,60,0,0 +107015,-200,4,1,1,20,0,0 +107083,-200,4,1,1,60,0,0 +107219,-200,4,1,1,20,0,0 +107355,-200,4,1,1,60,0,0 +107424,-200,4,1,1,20,0,0 +107765,-200,4,1,1,60,0,0 +107833,-200,4,1,1,20,0,0 +108174,-200,4,1,1,60,0,0 +108446,-200,4,2,1,60,0,0 +116083,-200,4,1,1,60,0,0 +117174,-133.333333333333,4,1,1,60,0,1 +123719,-200,4,1,1,20,0,0 +124674,-200,4,1,1,60,0,0 +126174,-90.9090909090909,4,1,1,60,0,1 +127810,-200,4,1,1,60,0,0 +128356,-100,4,1,1,60,0,1 +129992,-200,4,2,1,60,0,0 +130265,-100,4,1,1,60,0,1 +133537,-100,4,1,1,60,0,0 +134628,-100,4,1,1,60,0,1 +141174,-100,4,1,1,60,0,0 +142265,-117.647058823529,4,1,1,60,0,1 +143287,-117.647058823529,4,1,1,5,0,1 +143355,-117.647058823529,4,1,1,5,0,0 +143356,-133.333333333333,4,2,1,60,0,0 +147651,-133.333333333333,4,2,1,5,0,0 +147719,-133.333333333333,4,2,1,60,0,0 +152083,-100,4,1,1,60,0,0 +160810,-83.3333333333333,4,1,1,60,0,1 +162992,-76.9230769230769,4,1,1,60,0,1 +165174,-71.4285714285714,4,1,1,60,0,1 +167356,-117.647058823529,4,1,1,60,0,0 +168446,-117.647058823529,4,1,1,60,0,1 +169537,-117.647058823529,4,2,1,60,0,0 +171310,-117.647058823529,4,2,1,80,0,0 +171446,-117.647058823529,4,2,1,60,0,0 + + +[Colours] +Combo1 : 255,255,255 +Combo2 : 72,255,72 +Combo3 : 255,128,0 +Combo4 : 255,32,32 +Combo5 : 0,128,255 +Combo6 : 255,85,255 +Combo7 : 0,183,0 +Combo8 : 176,112,77 + +[HitObjects] +154,247,22401,6,0,P|56:170|169:171,1,313.500009567261 +169,170,23356,53,0,1:0:0:0: +192,92,23492,1,0,1:0:0:0: +252,40,23629,1,0,1:0:0:0: +332,28,23765,1,0,1:0:0:0: +404,60,23901,1,0,1:0:0:0: +452,124,24038,1,0,1:0:0:0: +456,204,24174,1,0,1:0:0:0: +424,276,24310,1,0,1:0:0:0: +356,316,24447,54,0,P|316:312|272:312,1,80.750001540184,0|0,0:0|2:0,0:0:0:0: +275,311,24651,1,0,2:0:0:0: +275,311,24719,1,2,2:0:0:0: +168,344,24856,1,4,3:0:0:0: +92,264,24992,1,0,0:0:0:0: +96,249,25060,1,0,2:0:0:0: +99,235,25129,1,0,2:0:0:0: +103,221,25197,1,0,2:0:0:0: +106,207,25265,54,0,P|72:144|92:104,1,121.125002310276 +108,208,25538,54,0,P|153:206|208:204,1,95 +260,88,25810,2,0,L|176:84,1,80.750001540184 +304,220,26083,1,2,0:0:0:0: +400,212,26219,1,0,0:0:0:0: +344,132,26356,1,0,0:0:0:0: +356,300,26492,54,0,P|300:360|212:348,1,161.500003080368,0|0,3:0|0:0,0:0:0:0: +200,216,26901,1,0,0:0:0:0: +200,216,27038,1,0,0:0:0:0: +108,328,27174,1,2,0:0:0:0: +108,328,27310,1,0,0:0:0:0: +28,208,27447,1,2,0:0:0:0: +112,244,27583,1,4,3:0:0:0: +112,244,27651,1,0,0:0:0:0: +112,244,27719,54,0,B|104:176|88:116|88:116|104:124|120:128,1,161.500003080368 +208,164,28129,2,0,P|248:159|284:136,1,80.750001540184,0|2,0:0|0:0,0:0:0:0: +212,72,28401,1,0,0:0:0:0: +280,240,28538,1,0,0:0:0:0: +388,140,28674,54,0,P|392:200|372:264,1,121.125002310276,0|0,3:0|0:0,0:0:0:0: +375,257,28947,2,0,P|336:252|288:252,1,80.750001540184,0|2,0:0|0:0,0:0:0:0: +456,308,29219,1,0,0:0:0:0: +376,372,29356,1,2,0:0:0:0: +376,372,29492,1,0,0:0:0:0: +376,372,29629,1,2,0:0:0:0: +376,372,29765,1,4,3:0:0:0: +376,372,29901,54,0,P|331:371|280:368,1,95 +224,248,30174,2,0,P|264:247|312:244,1,80.750001540184 +164,352,30447,1,2,0:0:0:0: +68,360,30583,1,0,0:0:0:0: +108,272,30719,1,0,0:0:0:0: +40,156,30856,54,0,P|44:80|120:36,1,161.500003080368,0|0,3:0|0:0,0:0:0:0: +136,132,31265,1,0,0:0:0:0: +136,132,31401,1,0,0:0:0:0: +256,48,31538,1,2,0:0:0:0: +256,48,31674,1,0,0:0:0:0: +376,132,31810,1,2,0:0:0:0: +312,224,31947,1,4,3:0:0:0: +312,224,32015,1,0,0:0:0:0: +312,224,32083,54,0,B|272:288|312:352|312:352|280:344,1,161.500003080368 +196,324,32492,1,0,0:0:0:0: +312,352,32629,2,0,P|348:340|396:340,1,80.750001540184,2|0,0:0|0:0,0:0:0:0: +484,368,32901,1,0,0:0:0:0: +448,229,33038,54,0,B|430:168|430:168|448:149|456:113,1,121.125002310276,2|0,0:0|0:0,0:0:0:0: +455,116,33310,2,0,L|375:132,1,80.750001540184 +208,44,33583,2,0,L|290:58,1,80.750001540184,2|0,2:0|0:0,0:0:0:0: +295,65,34265,54,0,L|199:47,1,95,4|0,0:0|0:0,0:0:0:0: +76,88,34538,1,8,0:0:0:0: +116,216,34674,1,2,2:0:0:0: +116,216,34742,1,2,2:0:0:0: +116,216,34810,2,0,P|156:244|208:240,1,95,2|0,2:0|0:0,0:0:0:0: +360,152,35083,2,0,P|311:147|268:177,1,95,8|0,0:0|0:0,0:0:0:0: +336,244,35356,54,0,L|384:60,1,190,2|8,2:0|0:0,0:0:0:0: +436,228,35765,1,2,2:0:0:0: +287,76,35901,1,2,2:0:0:0: +462,128,36038,1,2,2:0:0:0: +260,176,36174,1,8,0:0:0:0: +384,60,36310,1,2,2:0:0:0: +360,152,36447,54,0,B|312:224|348:312|348:312|320:300,1,190,4|8,0:0|0:0,0:0:0:0: +204,252,36856,1,2,2:0:0:0: +204,252,36924,1,2,2:0:0:0: +204,252,36992,2,0,P|160:236|104:244,1,95 +232,356,37265,1,8,0:0:0:0: +176,152,37401,1,0,0:0:0:0: +48,332,37538,54,0,L|144:324,1,95,8|2,0:0|2:0,0:0:0:0: +300,244,37810,2,0,L|204:252,1,95,8|2,0:0|2:0,0:0:0:0: +40,320,38083,2,0,L|136:312,1,95,0|2,0:0|2:0,0:0:0:0: +312,252,38356,2,0,P|340:184|308:124,1,142.5,8|0,0:0|0:0,0:0:0:0: +296,120,38629,54,0,P|340:132|390:129,1,95,4|0,0:0|0:0,0:0:0:0: +264,44,38901,2,0,P|224:72|212:116,1,95,8|0,0:0|0:0,0:0:0:0: +252,192,39174,1,2,2:0:0:0: +96,120,39310,1,0,0:0:0:0: +144,284,39447,1,8,0:0:0:0: +256,304,39583,1,2,2:0:0:0: +256,304,39651,1,2,2:0:0:0: +256,304,39719,54,0,B|352:296|420:292|420:292|404:260,1,190,0|8,0:0|0:0,0:0:0:0: +360,196,40129,1,0,0:0:0:0: +320,376,40265,2,0,L|424:371,1,95,2|2,2:0|2:0,0:0:0:0: +456,192,40538,2,0,L|352:197,1,95,8|0,0:0|0:0,0:0:0:0: +416,272,40810,54,0,P|360:192|400:116,1,190,4|8,0:0|0:0,0:0:0:0: +500,84,41219,1,0,0:0:0:0: +380,28,41356,2,0,P|332:20|284:40,1,95,2|0,2:0|0:0,0:0:0:0: +248,164,41629,1,8,0:0:0:0: +120,184,41765,1,2,2:0:0:0: +164,60,41901,54,0,L|68:56,1,95,8|2,0:0|2:0,0:0:0:0: +25,180,42174,2,0,L|121:184,1,95,8|2,0:0|2:0,0:0:0:0: +164,308,42447,1,8,0:0:0:0: +419,236,42583,1,0,0:0:0:0: +398,233,42651,1,0,0:0:0:0: +377,231,42719,1,8,0:0:0:0: +356,230,42788,1,0,0:0:0:0: +335,231,42856,1,8,0:0:0:0: +314,233,42924,1,8,0:0:0:0: +294,237,42992,54,0,P|257:209|249:161,1,95,4|0,0:0|0:0,0:0:0:0: +296,40,43265,1,8,0:0:0:0: +328,168,43401,1,2,2:0:0:0: +328,168,43469,1,2,2:0:0:0: +328,168,43538,2,0,P|376:176|424:152,1,95,2|0,2:0|0:0,0:0:0:0: +480,64,43810,1,8,0:0:0:0: +492,180,43947,1,0,0:0:0:0: +344,320,44083,54,0,P|256:328|197:233,1,190,2|8,2:0|0:0,0:0:0:0: +294,236,44492,1,0,0:0:0:0: +232,160,44629,53,2,2:0:0:0: +244,28,44765,1,2,2:0:0:0: +112,16,44901,1,8,0:0:0:0: +96,148,45038,1,2,2:0:0:0: +96,148,45106,1,0,0:0:0:0: +96,148,45174,54,0,B|72:216|112:268|112:268|88:264,1,142.5,4|0,0:0|0:0,0:0:0:0: +84,264,45447,2,0,P|36:268|0:304,1,95,8|0,0:0|0:0,0:0:0:0: +72,344,45719,2,0,L|272:360,1,190,2|8,2:0|0:0,0:0:0:0: +261,359,46129,1,0,0:0:0:0: +444,296,46265,54,0,P|472:260|472:200,1,95,8|2,0:0|2:0,0:0:0:0: +408,48,46538,2,0,P|397:92|423:146,1,95,8|2,0:0|2:0,0:0:0:0: +384,224,46810,1,2,2:0:0:0: +384,224,46947,2,0,L|288:216,1,95,8|8,0:0|0:0,0:0:0:0: +320,80,47219,1,2,2:0:0:0: +320,80,47288,1,2,2:0:0:0: +320,80,47356,54,0,B|248:68|184:100|184:100|161:85|120:80,1,190,4|8,0:0|0:0,0:0:0:0: +16,144,47765,1,0,0:0:0:0: +4,284,47901,2,0,P|32:323|84:344,1,95,2|0,2:0|0:0,0:0:0:0: +160,368,48174,1,8,0:0:0:0: +268,348,48310,1,0,0:0:0:0: +160,368,48447,54,0,P|206:365|256:364,1,95,2|0,2:0|0:0,0:0:0:0: +440,308,48719,2,0,P|393:305|344:304,1,95,8|0,0:0|0:0,0:0:0:0: +209,229,48992,1,2,2:0:0:0: +232,224,49060,1,0,2:0:0:0: +253,213,49129,1,2,2:0:0:0: +270,196,49197,1,0,2:0:0:0: +282,175,49265,1,8,0:0:0:0: +287,151,49333,1,0,2:0:0:0: +286,127,49401,1,2,2:0:0:0: +279,104,49469,1,2,2:0:0:0: +266,83,49538,54,0,P|312:97|364:86,1,95,4|0,0:0|0:0,0:0:0:0: +195,142,49810,2,0,P|146:137|99:161,1,95,8|0,0:0|0:0,0:0:0:0: +84,288,50083,2,0,P|188:304|264:256,1,190,2|8,2:0|0:0,0:0:0:0: +160,224,50492,1,0,0:0:0:0: +195,142,50629,53,8,0:0:0:0: +172,60,50765,1,2,2:0:0:0: +272,120,50901,53,8,0:0:0:0: +293,37,51038,1,2,2:0:0:0: +348,132,51174,53,8,0:0:0:0: +407,70,51310,1,2,2:0:0:0: +414,174,51447,53,8,0:0:0:0: +416,179,51515,1,8,0:0:0:0: +419,184,51583,1,8,0:0:0:0: +421,189,51651,1,8,0:0:0:0: +424,195,51719,38,0,P|432:231|432:267,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0: +412,356,51992,2,0,P|378:353|336:352,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0: +180,296,52265,1,0,1:0:0:0: +180,296,52401,1,0,1:0:0:0: +180,296,52538,2,0,P|217:294|252:293,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0: +316,216,52810,101,4,3:0:0:0: +280,124,52947,1,0,0:0:0:0: +280,124,53015,1,0,0:0:0:0: +280,124,53083,1,2,0:0:0:0: +212,196,53219,5,4,3:0:0:0: +176,104,53356,1,0,0:0:0:0: +176,104,53424,1,0,0:0:0:0: +176,104,53492,1,2,0:0:0:0: +116,176,53629,1,0,1:0:0:0: +168,308,53765,5,2,0:0:0:0: +168,308,53901,2,0,P|232:300|312:300,1,142.5,4|0,3:0|0:0,0:0:0:0: +368,348,54447,2,0,P|384:312|372:256,1,95,0|0,1:0|1:0,0:0:0:0: +320,204,54856,1,0,0:0:0:0: +448,240,54992,54,0,L|464:184,1,47.5,4|4,3:0|3:0,3:0:0:0: +392,152,55265,2,0,L|388:100,1,47.5,0|4,1:0|3:0,0:0:0:0: +312,80,55538,2,0,L|284:32,1,47.5,0|4,0:0|3:0,0:0:0:0: +228,116,55810,2,0,P|188:112|136:112,1,71.25,0|0,1:0|0:0,0:0:0:0: +156,111,56083,54,0,P|152:152|100:192,1,95,4|0,3:0|0:0,0:0:0:0: +36,184,56492,1,0,0:0:0:0: +72,252,56629,1,0,1:0:0:0: +80,120,56765,1,4,3:0:0:0: +52,48,56901,53,0,1:0:0:0: +114,188,57038,2,0,P|184:184|256:184,1,142.5,0|2,0:0|0:0,0:0:0:0: +352,284,57583,1,0,0:0:0:0: +352,284,57719,1,2,0:0:0:0: +352,284,57856,1,0,0:0:0:0: +352,284,57992,2,0,L|336:344,1,47.5,2|2,0:0|0:0,0:0:0:0: +352,284,58265,54,0,P|288:277|208:276,1,142.5,4|4,3:0|3:0,0:0:0:0: +209,275,58810,2,0,P|180:240|180:184,1,95,0|0,1:0|1:0,0:0:0:0: +204,120,59219,1,0,1:0:0:0: +252,212,59356,54,0,L|304:196,1,47.5,4|0,3:0|0:0,0:0:0:0: +456,76,59629,2,0,L|404:92,1,47.5,0|4,1:0|3:0,0:0:0:0: +260,220,59901,2,0,L|312:204,1,47.5,0|4,0:0|3:0,0:0:0:0: +464,84,60174,2,0,L|412:100,1,47.5,0|0,1:0|0:0,0:0:0:0: +364,152,60447,54,0,P|320:128|268:136,1,95,4|2,3:0|0:0,0:0:0:0: +304,208,60856,1,0,0:0:0:0: +432,289,60992,1,0,1:0:0:0: +415,287,61060,1,0,0:0:0:0: +398,286,61129,1,4,3:0:0:0: +381,285,61197,1,0,0:0:0:0: +364,286,61265,1,0,1:0:0:0: +263,356,61401,1,0,0:0:0:0: +263,356,61469,1,0,0:0:0:0: +263,356,61538,53,0,0:0:0:0: +246,352,61606,1,0,0:0:0:0: +229,349,61674,1,0,0:0:0:0: +212,348,61742,1,0,0:0:0:0: +195,348,61810,1,0,0:0:0:0: +60,362,61947,1,0,0:0:0:0: +48,343,62015,1,0,0:0:0:0: +39,323,62083,1,0,0:0:0:0: +34,302,62151,1,0,0:0:0:0: +34,280,62219,1,0,0:0:0:0: +38,259,62288,1,0,0:0:0:0: +46,239,62356,1,0,0:0:0:0: +46,239,62629,54,0,B|97:232|157:220|157:220|183:242,1,142.5,4|4,3:0|3:0,0:0:0:0: +92,156,63174,2,0,L|189:138,1,95,0|0,1:0|1:0,0:0:0:0: +256,100,63583,1,0,1:0:0:0: +256,176,63719,53,4,3:0:0:0: +256,176,63856,1,4,3:0:0:0: +256,24,63992,1,0,1:0:0:0: +326,138,64129,2,0,L|420:156,1,95,4|4,3:0|3:0,0:0:0:0: +476,208,64538,2,0,P|492:256|476:304,1,95,0|0,1:0|1:0,0:0:0:0: +480,297,65083,1,0,0:0:0:0: +328,224,65219,1,0,0:0:0:0: +340,304,65356,53,0,1:0:0:0: +208,324,65492,1,4,3:0:0:0: +340,304,65629,2,0,P|288:296|260:264,1,95,0|4,1:0|3:0,1:0:0:0: +260,188,66038,1,0,0:0:0:0: +192,224,66174,2,0,L|136:216,1,47.5,0|4,1:0|3:0,0:0:0:0: +24,196,66447,53,2,0:0:0:0: +24,196,66583,1,4,3:0:0:0: +24,196,66719,1,0,1:0:0:0: +24,196,66856,2,0,P|0:152|8:104,1,95,4|0,3:0|0:0,0:0:0:0: +76,140,67265,2,0,P|122:137|172:132,1,95,2|0,0:0|1:0,0:0:0:0: +332,172,67674,1,4,3:0:0:0: +332,172,67810,2,0,L|284:176|236:180,1,95,0|0,1:0|0:0,0:0:0:0: +128,240,68219,1,0,0:0:0:0: +232,276,68356,1,0,1:0:0:0: +120,332,68492,1,4,3:0:0:0: +224,372,68629,54,0,P|268:376|320:356,1,95,4|0,3:0|1:0,0:0:0:0: +360,300,69038,1,4,3:0:0:0: +432,276,69174,2,0,P|452:232|440:180,1,95,2|0,0:0|1:0,0:0:0:0: +344,44,69583,2,0,P|340:92|372:140,1,95,4|0,3:0|0:0,0:0:0:0: +448,88,69992,1,0,1:0:0:0: +448,88,70265,21,0,0:0:0:0: +419,356,70401,1,0,0:0:0:0: +151,324,70538,1,0,0:0:0:0: +180,56,70674,1,0,0:0:0:0: +180,56,71219,37,0,0:0:0:0: +256,300,71356,2,0,L|224:192,1,104.500003189087,4|0,0:0|0:0,0:0:0:0: +202,130,71629,1,8,0:0:0:0: +252,24,71765,53,0,0:0:0:0: +151,324,71901,2,0,L|192:208,1,104.500003189087 +220,124,72174,1,8,0:0:0:0: +352,56,72310,53,0,0:0:0:0: +20,216,72447,2,0,L|124:172,1,104.500003189087 +236,116,72719,1,8,0:0:0:0: +350,191,72856,53,0,0:0:0:0: +468,120,72992,1,0,0:0:0:0: +472,256,73129,1,0,0:0:0:0: +388,120,73265,1,8,0:0:0:0: +511,187,73401,1,0,0:0:0:0: +392,256,73538,54,0,P|284:228|288:144,1,209.000006378174,4|8,0:0|0:0,0:0:0:0: +168,264,73947,1,0,0:0:0:0: +350,191,74083,2,0,P|297:189|236:188,1,104.500003189087 +468,120,74356,2,0,P|415:118|354:117,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +172,168,74629,54,0,P|184:236|264:260,1,156.750004783631 +172,168,74901,2,0,P|119:164|60:164,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +20,236,75174,53,0,0:0:0:0: +80,56,75310,1,0,0:0:0:0: +248,132,75447,2,0,P|298:125|352:124,1,104.500003189087,8|8,0:0|0:0,0:0:0:0: +351,123,75651,1,8,0:0:0:0: +351,123,75719,54,0,B|348:186|340:240|340:240|352:251|364:276,1,156.750004783631,12|0,0:0|0:0,0:0:0:0: +372,288,75993,2,0,L|384:180,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +280,316,76265,2,0,P|232:340|168:328,1,104.500003189087 +220,260,76538,1,8,0:0:0:0: +344,368,76674,53,0,0:0:0:0: +396,176,76810,2,0,L|384:284,1,104.500003189087 +456,348,77083,1,8,0:0:0:0: +308,200,77219,53,0,0:0:0:0: +472,252,77356,1,0,0:0:0:0: +452,116,77492,1,0,0:0:0:0: +352,24,77629,1,8,0:0:0:0: +216,28,77765,1,0,0:0:0:0: +112,116,77901,54,0,B|76:216|144:288|144:288|148:256,1,209.000006378174,4|8,0:0|0:0,0:0:0:0: +196,80,78310,1,0,0:0:0:0: +372,200,78447,2,0,B|332:164|284:148|284:148|224:156|180:184,1,209.000006378174,0|0,0:0|0:0,0:0:0:0: +36,268,78856,1,0,0:0:0:0: +92,368,78992,53,8,0:0:0:0: +110,362,79060,1,0,0:0:0:0: +128,357,79129,1,0,0:0:0:0: +146,353,79197,1,0,0:0:0:0: +165,350,79265,1,0,0:0:0:0: +183,348,79333,1,0,0:0:0:0: +201,346,79401,1,8,0:0:0:0: +219,345,79469,1,0,0:0:0:0: +237,345,79538,1,0,0:0:0:0: +208,236,79674,1,0,0:0:0:0: +370,255,79810,1,8,0:0:0:0: +386,262,79879,1,0,0:0:0:0: +402,268,79947,1,0,0:0:0:0: +417,278,80015,1,0,0:0:0:0: +431,287,80083,6,0,P|408:243|424:184,1,104.500003189087,4|0,0:0|0:0,0:0:0:0: +486,234,80356,1,8,0:0:0:0: +374,350,80492,1,0,0:0:0:0: +286,215,80629,54,0,P|288:159|324:117,1,104.500003189087 +368,192,80901,1,8,0:0:0:0: +182,243,81038,1,0,0:0:0:0: +152,76,81174,54,0,P|168:28|221:-2,1,104.500003189087 +262,103,81447,1,8,0:0:0:0: +105,201,81583,1,0,0:0:0:0: +56,116,81719,53,0,0:0:0:0: +20,248,81856,1,0,0:0:0:0: +152,280,81992,1,8,0:0:0:0: +192,152,82129,1,0,0:0:0:0: +20,248,82265,54,0,P|8:320|64:380,1,156.750004783631 +60,378,82538,2,0,P|114:374|168:372,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +260,336,82810,2,0,P|280:284|268:232,1,104.500003189087 +260,336,83083,1,8,0:0:0:0: +192,152,83219,1,0,0:0:0:0: +105,201,83356,54,0,P|54:203|-7:209,1,104.500003189087 +56,52,83629,2,0,P|107:54|168:60,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +312,216,83901,2,0,P|368:184|376:96,1,156.750004783631 +360,88,84174,2,0,P|416:92|472:76,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +380,284,84447,54,0,P|312:216|200:216,1,209.000006378174,4|8,0:0|0:0,0:0:0:0: +256,340,84856,1,0,0:0:0:0: +164,364,84992,2,0,P|100:356|64:328,1,104.500003189087 +16,208,85265,1,8,0:0:0:0: +140,260,85401,53,0,0:0:0:0: +92,132,85538,2,0,P|120:88|172:64,1,104.500003189087 +192,168,85810,1,8,0:0:0:0: +192,168,85947,1,0,0:0:0:0: +280,64,86083,53,0,0:0:0:0: +404,120,86219,1,0,0:0:0:0: +392,256,86356,1,8,0:0:0:0: +260,284,86492,1,0,0:0:0:0: +304,176,86629,53,0,0:0:0:0: +288,172,87174,53,0,0:0:0:0: +272,168,87719,69,8,0:0:0:0: +257,174,87788,1,0,0:0:0:0: +241,178,87856,2,0,P|202:170|160:168,1,80.750001540184 +136,284,88129,1,8,0:0:0:0: +151,290,88197,1,0,0:0:0:0: +167,294,88265,2,0,P|206:286|248:284,1,80.750001540184 +57,253,88538,1,8,0:0:0:0: +57,253,88810,6,0,P|80:300|72:348,1,95,4|0,0:0|0:0,0:0:0:0: +0,312,89083,1,8,0:0:0:0: +148,344,89219,1,2,2:0:0:0: +96,176,89356,2,0,P|48:168|0:192,1,95,2|0,2:0|0:0,0:0:0:0: +160,232,89629,1,8,0:0:0:0: +148,344,89765,1,0,0:0:0:0: +176,144,89901,54,0,P|264:136|323:231,1,190,2|8,2:0|0:0,0:0:0:0: +336,292,90310,1,0,0:0:0:0: +252,260,90447,53,2,2:0:0:0: +408,236,90583,1,2,2:0:0:0: +244,272,90719,1,8,0:0:0:0: +420,224,90856,1,2,2:0:0:0: +420,224,90924,1,0,0:0:0:0: +420,224,90992,2,0,B|452:152|400:88,1,142.5,4|0,0:0|0:0,0:0:0:0: +401,90,91265,2,0,P|359:95|296:100,1,95,8|0,0:0|0:0,0:0:0:0: +504,64,91538,2,0,L|304:20,1,190,2|8,2:0|0:0,0:0:0:0: +220,80,91947,1,0,0:0:0:0: +60,48,92083,54,0,P|32:84|32:144,1,95,8|2,0:0|2:0,0:0:0:0: +112,292,92356,2,0,P|123:248|97:194,1,95,8|2,0:0|2:0,0:0:0:0: +32,256,92629,1,8,0:0:0:0: +112,116,92765,1,2,2:0:0:0: +208,236,92901,1,8,0:0:0:0: +192,332,93038,1,2,2:0:0:0: +192,332,93106,1,2,2:0:0:0: +192,332,93174,54,0,B|264:344|328:312|328:312|351:327|392:332,1,190,12|8,0:0|0:0,0:0:0:0: +508,268,93583,1,0,0:0:0:0: +384,184,93719,2,0,P|348:152|336:104,1,95,2|0,2:0|0:0,0:0:0:0: +352,16,93992,1,8,0:0:0:0: +244,36,94129,1,0,0:0:0:0: +352,16,94265,54,0,P|306:19|256:20,1,95,2|0,2:0|0:0,0:0:0:0: +228,184,94538,2,0,P|275:187|324:188,1,95,8|0,0:0|0:0,0:0:0:0: +292,104,94810,53,2,2:0:0:0: +144,116,94947,1,2,2:0:0:0: +217,80,95083,53,8,0:0:0:0: +120,192,95219,1,2,2:0:0:0: +156,264,95356,54,0,P|110:250|58:261,1,95,4|0,0:0|0:0,0:0:0:0: +124,352,95629,2,0,P|173:357|220:333,1,95,8|0,0:0|0:0,0:0:0:0: +412,228,95901,2,0,P|308:212|232:260,1,190,2|8,2:0|0:0,0:0:0:0: +340,308,96310,1,0,0:0:0:0: +389,146,96447,37,8,0:0:0:0: +412,228,96583,1,2,2:0:0:0: +300,124,96719,101,8,0:0:0:0: +323,206,96856,1,2,2:0:0:0: +212,104,96992,37,8,0:0:0:0: +235,186,97129,1,2,2:0:0:0: +148,304,97265,53,8,0:0:0:0: +148,304,97333,1,8,0:0:0:0: +148,304,97401,1,8,0:0:0:0: +148,304,97469,1,8,0:0:0:0: +148,304,97538,86,0,P|112:296|72:288,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0: +28,184,97810,2,0,P|65:177|107:173,1,71.2500027179719,0|0,1:0|0:0,1:0:0:0: +232,152,98083,1,0,1:0:0:0: +232,152,98219,1,0,1:0:0:0: +232,152,98356,2,0,P|222:185|217:223,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0: +248,352,98629,101,4,3:0:0:0: +328,324,98765,1,0,0:0:0:0: +328,324,98833,1,0,0:0:0:0: +328,324,98901,1,2,0:0:0:0: +476,288,99038,5,4,3:0:0:0: +392,268,99174,1,0,0:0:0:0: +392,268,99242,1,0,0:0:0:0: +392,268,99310,1,2,0:0:0:0: +448,212,99447,1,0,1:0:0:0: +264,272,99583,5,2,0:0:0:0: +264,272,99719,2,0,P|256:208|312:160,1,142.5,4|0,3:0|0:0,0:0:0:0: +392,156,100265,2,0,P|381:118|335:82,1,95,0|0,1:0|1:0,0:0:0:0: +264,96,100674,1,0,0:0:0:0: +192,72,100810,54,0,L|136:56,1,47.5,4|0,3:0|0:0,0:0:0:0: +32,96,101083,2,0,L|88:112,1,47.5,0|4,1:0|3:0,0:0:0:0: +168,192,101356,2,0,L|112:176,1,47.5,0|4,0:0|3:0,0:0:0:0: +60,228,101629,2,0,P|56:268|56:320,1,71.25,0|0,1:0|0:0,0:0:0:0: +55,299,101901,54,0,P|100:280|144:288,1,95,4|2,3:0|0:0,0:0:0:0: +108,356,102310,1,0,0:0:0:0: +184,356,102447,1,0,1:0:0:0: +188,224,102583,1,4,3:0:0:0: +224,288,102719,53,0,1:0:0:0: +117,193,102856,2,0,P|173:149|245:173,1,142.5,4|8,3:0|1:0,0:0:0:0: +308,304,103401,1,4,3:0:0:0: +308,304,103538,1,0,1:0:0:0: +308,304,103674,1,4,3:0:0:0: +308,304,103810,2,0,L|324:244,1,47.5,0|0,1:0|0:0,0:0:0:0: +308,304,104083,54,0,P|340:352|424:336,1,142.5,4|4,3:0|3:0,0:0:0:0: +456,280,104629,2,0,P|452:231|448:180,1,95,0|0,1:0|1:0,0:0:0:0: +440,108,105038,1,2,0:0:0:0: +340,44,105174,54,0,L|288:60,1,47.5,4|0,3:0|0:0,0:0:0:0: +172,32,105447,2,0,L|188:84,1,47.5,0|4,1:0|3:0,0:0:0:0: +164,192,105719,2,0,L|216:176,1,47.5,0|4,0:0|3:0,0:0:0:0: +324,208,105992,2,0,L|308:156,1,47.5,0|0,1:0|0:0,0:0:0:0: +252,112,106265,54,0,P|208:88|156:96,1,95,4|2,3:0|0:0,0:0:0:0: +192,164,106674,1,0,0:0:0:0: +48,240,106810,1,0,1:0:0:0: +65,242,106879,1,0,0:0:0:0: +82,243,106947,1,4,3:0:0:0: +99,244,107015,1,0,0:0:0:0: +116,243,107083,1,0,1:0:0:0: +180,324,107219,1,0,0:0:0:0: +180,324,107288,1,0,0:0:0:0: +180,324,107356,53,0,0:0:0:0: +197,320,107424,1,0,0:0:0:0: +214,317,107492,1,0,0:0:0:0: +231,316,107560,1,0,0:0:0:0: +248,316,107629,1,0,0:0:0:0: +364,367,107765,1,0,0:0:0:0: +372,347,107833,1,0,0:0:0:0: +376,326,107901,1,0,0:0:0:0: +376,304,107969,1,0,0:0:0:0: +371,283,108038,1,0,0:0:0:0: +362,263,108106,1,0,0:0:0:0: +350,244,108174,1,0,0:0:0:0: +350,244,108447,54,0,B|361:178|372:120|372:120|344:132,1,142.5,4|4,3:0|3:0,0:0:0:0: +268,256,108992,2,0,L|286:157,1,95,0|0,1:0|1:0,0:0:0:0: +256,92,109401,1,4,3:0:0:0: +208,152,109538,53,4,3:0:0:0: +208,152,109674,1,0,0:0:0:0: +88,76,109810,1,0,1:0:0:0: +84,216,109947,2,0,P|36:212|-4:180,1,95,4|4,3:0|3:0,0:0:0:0: +64,144,110356,1,0,1:0:0:0: +24,320,110629,54,0,P|72:321|124:324,1,95,4|2,3:0|0:0,0:0:0:0: +196,348,111038,1,0,0:0:0:0: +184,272,111174,53,0,1:0:0:0: +316,252,111310,1,4,3:0:0:0: +184,272,111447,2,0,P|236:280|264:312,1,95,0|4,1:0|3:0,0:0:0:0: +292,380,111856,1,0,0:0:0:0: +344,324,111992,2,0,L|400:332,1,47.5,0|4,1:0|3:0,0:0:0:0: +488,248,112265,53,2,0:0:0:0: +488,248,112401,1,4,3:0:0:0: +488,248,112538,1,0,1:0:0:0: +488,248,112674,2,0,P|512:204|504:156,1,95,4|0,3:0|0:0,0:0:0:0: +456,104,113083,2,0,P|410:107|360:112,1,95,2|0,0:0|1:0,0:0:0:0: +308,232,113492,1,4,3:0:0:0: +308,232,113629,2,0,P|268:204|212:212,1,95,0|4,1:0|3:0,0:0:0:0: +248,280,114038,1,0,0:0:0:0: +128,348,114174,1,0,1:0:0:0: +148,220,114310,1,4,3:0:0:0: +76,184,114447,54,0,P|108:150|163:141,1,95,4|0,3:0|1:0,0:0:0:0: +228,164,114856,1,0,0:0:0:0: +288,116,114992,2,0,P|336:104|384:112,1,95,4|0,3:0|1:0,0:0:0:0: +468,244,115401,2,0,P|468:196|440:148,1,95,4|0,3:0|0:0,0:0:0:0: +376,188,115810,1,0,1:0:0:0: +376,188,116083,85,0,0:0:0:0: +248,324,116219,1,0,0:0:0:0: +164,156,116356,1,0,0:0:0:0: +288,16,116492,1,0,0:0:0:0: +288,16,117038,53,0,0:0:0:0: +196,56,117174,2,0,P|124:48|48:48,1,142.500005435944,4|0,0:0|0:0,0:0:0:0: +16,140,117583,1,0,0:0:0:0: +72,232,117719,2,0,P|116:256|180:248,1,106.875004076958,8|0,0:0|0:0,0:0:0:0: +184,248,117992,2,0,P|221:249|256:264,1,71.2500027179719,0|0,0:0|0:0,0:0:0:0: +300,340,118265,6,0,P|372:356|440:320,1,142.500005435944 +448,240,118674,1,0,0:0:0:0: +408,160,118810,101,8,0:0:0:0: +464,92,118947,1,0,0:0:0:0: +324,144,119083,5,0,0:0:0:0: +408,160,119219,1,0,0:0:0:0: +256,192,119356,102,0,P|188:196|112:200,1,142.500005435944 +56,120,119765,1,0,0:0:0:0: +72,288,119901,2,0,P|126:282|184:300,1,106.875004076958,8|0,0:0|0:0,0:0:0:0: +192,304,120174,2,0,P|230:306|264:296,1,71.2500027179719 +352,240,120447,6,0,P|416:208|488:228,1,142.500005435944 +428,308,120856,1,0,0:0:0:0: +460,128,120992,101,8,0:0:0:0: +424,36,121129,1,0,0:0:0:0: +360,112,121265,1,0,0:0:0:0: +296,72,121401,1,0,0:0:0:0: +296,72,121469,1,0,0:0:0:0: +296,72,121538,6,0,P|248:96|180:88,1,106.875004076958,4|0,0:0|0:0,0:0:0:0: +194,19,121810,2,0,P|228:5|268:6,1,71.2500027179719 +136,148,122083,102,0,P|106:192|43:219,1,106.875004076958,8|0,0:0|0:0,0:0:0:0: +23,153,122356,2,0,P|45:123|76:103,1,71.2500027179719 +184,256,122629,6,0,P|236:254|284:278,1,106.875004076958 +253,347,122901,2,0,P|218:343|184:329,1,71.2500027179719 +240,164,123174,101,8,0:0:0:0: +204,88,123310,1,0,0:0:0:0: +324,180,123447,5,0,0:0:0:0: +240,164,123583,1,0,0:0:0:0: +292,100,123719,6,0,P|392:132|328:264,1,285,4|0,3:0|0:0,0:0:0:0: +324,180,124674,101,0,0:0:0:0: +324,180,124810,1,0,0:0:0:0: +248,264,124947,1,0,0:0:0:0: +216,152,125083,1,0,0:0:0:0: +136,232,125219,1,0,0:0:0:0: +104,124,125356,1,0,0:0:0:0: +24,200,125492,1,0,0:0:0:0: +72,352,125629,37,0,0:0:0:0: +72,352,125765,1,0,0:0:0:0: +72,352,125901,2,0,P|108:346|152:344,1,71.25,2|0,2:0|0:0,0:0:0:0: +156,324,126174,6,0,P|108:340|48:332,1,104.500003189087 +248,264,126447,2,0,P|300:258|348:274,1,104.500003189087,0|0,0:0|0:0,0:0:0:0: +432,368,126719,1,8,0:0:0:0: +464,248,126856,53,0,0:0:0:0: +292,340,126992,2,0,L|308:184,1,156.750004783631,0|0,0:0|0:0,0:0:0:0: +312,164,127265,2,0,P|300:108|264:76,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +220,156,127538,1,0,0:0:0:0: +368,76,127674,1,0,0:0:0:0: +200,152,127810,1,8,0:0:0:0: +388,80,127947,53,8,0:0:0:0: +400,96,128015,1,8,0:0:0:0: +412,112,128083,1,8,0:0:0:0: +412,112,128356,54,0,P|348:144|288:96,1,142.5,12|0,0:0|0:0,0:0:0:0: +284,92,128629,2,0,P|238:93|188:88,1,95 +24,176,128901,2,0,P|73:173|120:172,1,95,8|0,0:0|0:0,0:0:0:0: +200,244,129174,53,0,0:0:0:0: +152,384,129310,1,0,0:0:0:0: +304,356,129447,1,8,0:0:0:0: +220,324,129583,1,0,0:0:0:0: +268,188,129719,1,8,0:0:0:0: +288,184,129788,1,0,0:0:0:0: +308,184,129856,1,0,0:0:0:0: +325,188,129924,1,0,0:0:0:0: +342,195,129992,1,2,0:0:0:0: +342,195,130265,54,0,P|320:152|324:100,1,95,4|0,0:0|0:0,0:0:0:0: +428,268,130538,1,8,0:0:0:0: +312,304,130674,1,0,0:0:0:0: +408,92,130810,54,0,P|368:32|284:28,1,142.5,0|0,0:0|0:0,0:0:0:0: +280,32,131083,2,0,P|233:27|180:32,1,95,8|0,0:0|0:0,0:0:0:0: +164,42,131288,1,0,0:0:0:0: +141,54,131356,54,0,B|150:142|150:142|132:163|124:192,1,142.5 +120,204,131629,2,0,P|168:180|208:180,1,95,8|0,0:0|0:0,0:0:0:0: +308,236,131901,53,0,0:0:0:0: +136,320,132038,1,0,0:0:0:0: +228,152,132174,1,8,0:0:0:0: +256,344,132310,1,2,2:0:0:0: +256,344,132379,1,2,2:0:0:0: +256,344,132447,54,0,B|305:335|352:332|352:332|372:280|424:260,1,190,4|8,0:0|0:0,0:0:0:0: +300,256,132856,2,0,P|255:261|204:268,1,95 +120,204,133129,1,0,0:0:0:0: +212,164,133265,53,8,0:0:0:0: +164,20,133401,1,0,0:0:0:0: +312,48,133538,54,0,L|344:88,1,47.5,8|0,0:0|0:0,0:0:0:0: +504,120,133674,2,0,L|453:127,1,47.5 +344,252,133810,2,0,L|362:204,1,47.5 +428,32,133947,2,0,L|409:79,1,47.5,8|0,0:0|0:0,0:0:0:0: +461,234,134083,2,0,L|429:194,1,47.5 +267,161,134219,2,0,L|317:153,1,47.5 +388,140,134356,1,8,0:0:0:0: +394,142,134424,1,0,0:0:0:0: +399,145,134492,1,0,0:0:0:0: +405,148,134560,1,0,0:0:0:0: +411,152,134629,102,0,P|439:208|407:280,1,142.5,4|0,0:0|0:0,0:0:0:0: +372,340,134901,2,0,P|324:341|276:344,1,95,8|0,0:0|0:0,0:0:0:0: +56,308,135174,53,0,0:0:0:0: +200,208,135310,1,8,0:0:0:0: +204,384,135447,1,0,0:0:0:0: +276,244,135583,53,0,0:0:0:0: +128,348,135719,1,0,0:0:0:0: +128,168,135856,2,0,P|168:128|212:124,1,95,0|8,0:0|0:0,0:0:0:0: +20,228,136129,53,0,0:0:0:0: +200,208,136265,1,0,0:0:0:0: +8,128,136401,1,8,0:0:0:0: +186,108,136538,1,0,0:0:0:0: +116,274,136674,1,0,0:0:0:0: +92,64,136810,54,0,B|99:117|112:160|112:160|104:180|96:208,1,142.5,4|0,0:0|0:0,0:0:0:0: +116,274,137083,2,0,P|166:268|212:268,1,95,8|0,0:0|0:0,0:0:0:0: +372,352,137356,1,0,0:0:0:0: +210,267,137492,1,0,0:0:0:0: +372,352,137629,2,0,P|420:340|452:304,1,95,8|0,0:0|0:0,0:0:0:0: +424,216,137901,53,0,0:0:0:0: +448,56,138038,1,0,0:0:0:0: +352,160,138174,5,8,0:0:0:0: +280,16,138310,1,0,0:0:0:0: +264,152,138447,5,0,0:0:0:0: +120,76,138583,1,0,0:0:0:0: +180,188,138719,5,8,0:0:0:0: +24,224,138856,1,0,0:0:0:0: +136,268,138992,70,0,P|184:268|232:272,1,95,4|0,0:0|0:0,0:0:0:0: +332,284,139265,1,8,0:0:0:0: +272,364,139401,53,0,0:0:0:0: +292,188,139538,2,0,P|332:124|408:120,1,142.5 +376,200,139810,2,0,P|425:195|480:188,1,95,8|0,0:0|0:0,0:0:0:0: +368,372,140083,54,0,P|320:368|268:364,1,95 +168,352,140356,1,8,0:0:0:0: +232,276,140492,53,0,0:0:0:0: +220,152,140629,1,0,0:0:0:0: +104,108,140765,1,0,0:0:0:0: +8,188,140901,1,8,0:0:0:0: +4,212,140969,1,0,0:0:0:0: +8,236,141038,1,0,0:0:0:0: +20,256,141106,1,0,0:0:0:0: +38,272,141174,53,0,0:0:0:0: +77,284,141719,53,0,0:0:0:0: +116,292,142265,54,0,B|104:196|8:232|-44:140|4:44|60:20|136:16|172:76|172:76|152:96|152:96|163:120|163:120|144:141|144:141|155:165|155:165|136:188|136:188|156:204|172:236|172:236,1,605.62501155138 +168,224,143356,6,0,P|228:188|304:196,1,142.500005435944,0|2,1:0|0:0,0:0:0:0: +298,193,143697,1,0,0:0:0:0: +298,193,143765,1,0,0:0:0:0: +352,264,143901,1,0,0:0:0:0: +424,212,144038,2,0,P|472:152|464:88,2,142.500005435944,2|2|0,0:0|0:0|0:0,0:0:0:0: +388,132,144719,54,0,L|364:48,1,71.2500027179719,0|0,0:0|0:0,0:0:0:0: +296,112,144992,2,0,P|264:96|224:96,2,71.2500027179719,2|0|0,0:0|0:0|0:0,0:0:0:0: +240,176,145401,53,0,0:0:0:0: +344,308,145538,2,0,P|288:348|216:344,1,142.500005435944,2|0,0:0|0:0,0:0:0:0: +260,260,145947,1,2,0:0:0:0: +172,240,146083,53,0,0:0:0:0: +172,240,146219,1,0,0:0:0:0: +172,240,146356,1,0,0:0:0:0: +172,240,146492,2,0,B|150:244|128:252|128:252|86:238|32:260,1,142.500005435944,2|0,0:0|0:0,0:0:0:0: +124,168,146901,2,0,B|146:164|168:156|168:156|210:170|264:148,1,142.500005435944,2|2,0:0|0:0,0:0:0:0: +276,140,147242,1,0,0:0:0:0: +288,132,147310,1,0,0:0:0:0: +448,180,147447,2,0,B|403:182|360:176|360:176|372:192,1,106.875004076958,2|0,0:0|0:0,0:0:0:0: +380,204,147719,54,0,P|420:264|384:336,1,142.500005435944,4|2,0:0|0:0,0:0:0:0: +397,325,148129,1,0,0:0:0:0: +332,268,148265,1,0,0:0:0:0: +276,332,148401,2,0,P|203:327|128:328,2,142.500005435944,2|2|0,0:0|0:0|0:0,0:0:0:0: +48,264,149083,1,0,0:0:0:0: +48,264,149219,2,0,P|28:196|60:136,2,142.500005435944,2|2|0,0:0|0:0|0:0,0:0:0:0: +112,208,149901,53,2,0:0:0:0: +296,256,150174,1,2,0:0:0:0: +296,256,150242,1,0,0:0:0:0: +296,256,150310,2,0,P|336:252|368:232,1,71.2500027179719,0|0,0:0|0:0,0:0:0:0: +388,72,150583,2,0,P|348:76|316:96,1,71.2500027179719,2|0,0:0|0:0,0:0:0:0: +308,172,150856,2,0,L|460:208,1,142.500005435944,2|0,0:0|0:0,0:0:0:0: +446,204,151265,37,2,0:0:0:0: +446,204,151401,1,0,0:0:0:0: +446,204,151538,5,2,0:0:0:0: +446,204,152083,86,0,P|472:248|464:304,1,95,4|0,0:0|0:0,0:0:0:0: +400,384,152356,1,8,0:0:0:0: +392,264,152492,1,2,2:0:0:0: +392,264,152560,1,2,2:0:0:0: +392,264,152629,2,0,P|348:240|300:240,1,95,2|0,2:0|0:0,0:0:0:0: +328,320,152901,1,8,0:0:0:0: +280,160,153038,1,0,0:0:0:0: +328,320,153174,54,0,L|140:344,1,190,2|8,2:0|0:0,0:0:0:0: +64,296,153583,1,0,0:0:0:0: +176,236,153719,2,2,L|224:244,2,47.5,2|2|2,2:0|2:0|2:0,2:0:0:0: +4,184,153992,1,8,0:0:0:0: +116,124,154129,1,2,2:0:0:0: +116,124,154197,1,2,2:0:0:0: +116,124,154265,54,0,P|136:68|128:32,1,95,4|0,0:0|0:0,0:0:0:0: +16,96,154538,2,0,P|-4:152|4:188,1,95,8|0,0:0|0:0,0:0:0:0: +76,288,154810,2,0,P|124:284|176:284,1,95,2|0,2:0|0:0,0:0:0:0: +292,340,155083,1,8,0:0:0:0: +276,208,155219,1,0,0:0:0:0: +380,312,155356,54,0,P|430:304|488:300,1,95,8|2,0:0|2:0,0:0:0:0: +384,232,155629,2,0,P|434:224|492:220,1,95,8|2,0:0|2:0,0:0:0:0: +312,128,155901,2,0,P|262:120|204:116,1,95,8|2,0:0|2:0,0:0:0:0: +308,48,156174,2,0,P|258:40|200:36,1,95,0|2,0:0|2:0,0:0:0:0: +213,36,156379,1,2,2:0:0:0: +213,36,156447,54,0,P|308:35|408:32,1,190,4|8,0:0|0:0,0:0:0:0: +480,88,156856,1,0,0:0:0:0: +384,232,156992,2,0,P|288:236|236:164,1,190,2|8,2:0|0:0,0:0:0:0: +328,152,157401,1,0,0:0:0:0: +264,324,157538,2,0,P|168:324|64:332,1,190,2|8,2:0|0:0,0:0:0:0: +112,248,157947,1,2,2:0:0:0: +112,248,158015,1,2,2:0:0:0: +112,248,158083,53,2,2:0:0:0: +28,144,158219,1,2,2:0:0:0: +100,32,158356,1,8,0:0:0:0: +228,72,158492,1,2,2:0:0:0: +232,204,158629,54,0,P|264:244|320:256,1,95,4|0,0:0|0:0,0:0:0:0: +484,208,158901,2,0,P|436:189|381:206,1,95,8|0,0:0|0:0,0:0:0:0: +160,328,159174,2,0,P|164:256|216:208,1,142.5,2|2,2:0|2:0,0:0:0:0: +232,204,159447,2,0,P|284:208|320:236,1,95,8|0,0:0|0:0,0:0:0:0: +251,295,159719,54,0,L|215:103,1,190,8|8,0:0|0:0,0:0:0:0: +296,64,160129,1,2,2:0:0:0: +128,152,160265,1,2,2:0:0:0: +105,144,160333,1,2,2:0:0:0: +84,131,160401,1,2,2:0:0:0: +67,114,160469,1,0,2:0:0:0: +54,94,160538,1,8,0:0:0:0: +52,117,160606,1,0,2:0:0:0: +48,140,160674,1,2,2:0:0:0: +37,161,160742,1,2,2:0:0:0: +23,179,160810,6,0,P|5:234|16:292,1,113.999996520996,4|8,0:0|0:0,0:0:0:0: +92,240,161083,2,0,P|103:296|142:341,1,113.999996520996,0|8,0:0|0:0,0:0:0:0: +184,264,161356,2,0,P|222:307|278:326,1,113.999996520996,0|8,0:0|0:0,0:0:0:0: +276,240,161629,2,0,P|331:258|388:247,1,113.999996520996,0|8,0:0|0:0,0:0:0:0: +500,184,161901,54,0,P|518:129|507:71,1,113.999996520996,0|8,0:0|0:0,0:0:0:0: +403,107,162174,2,0,P|391:50|352:5,1,113.999996520996,0|8,0:0|0:0,0:0:0:0: +275,87,162447,2,0,P|236:43|180:24,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: +151,127,162719,2,0,P|96:109|38:120,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: +84,292,162992,53,4,0:0:0:0: +188,228,163129,1,8,0:0:0:0: +200,352,163265,2,0,P|260:344|332:344,1,123.499994346619,0|8,0:0|0:0,0:0:0:0: +512,200,163538,53,0,0:0:0:0: +408,136,163674,1,8,0:0:0:0: +396,260,163810,2,0,P|336:252|264:252,1,123.499994346619,0|8,0:0|0:0,0:0:0:0: +240,80,164083,54,0,P|192:32|128:32,1,123.499994346619,0|8,0:0|0:0,0:0:0:0: +64,176,164356,2,0,P|112:224|176:224,1,123.499994346619,0|8,0:0|0:0,0:0:0:0: +244,72,164629,54,0,P|196:24|132:24,1,123.499994346619,0|8,0:0|0:0,0:0:0:0: +56,184,164901,2,8,P|104:232|168:232,1,123.499994346619,8|8,0:0|0:0,0:0:0:0: +328,128,165174,54,0,P|392:120|448:76,1,132.999995941162,4|8,0:0|0:0,0:0:0:0: +360,212,165447,2,0,B|394:206|416:200|416:200|456:216|500:208,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: +328,300,165719,2,0,P|392:320|452:352,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: +184,300,165992,2,0,P|120:320|60:352,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: +152,212,166265,2,0,B|117:206|96:200|96:200|56:216|12:208,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: +184,128,166538,2,0,P|120:120|64:76,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: +256,80,166810,53,8,0:0:0:0: +256,100,166879,1,0,2:0:0:0: +256,120,166947,1,2,2:0:0:0: +256,140,167015,1,2,2:0:0:0: +256,160,167083,1,2,2:0:0:0: +256,256,167219,1,2,2:0:0:0: +256,256,167288,1,0,2:0:0:0: +256,256,167356,53,4,3:0:0:0: +256,276,167901,53,0,0:0:0:0: +256,296,168447,53,0,0:0:0:0: +256,84,168583,1,8,0:0:0:0: +181,265,168719,1,0,0:0:0:0: +330,115,168856,1,8,0:0:0:0: +150,190,168992,1,0,0:0:0:0: +361,190,169129,1,8,0:0:0:0: +181,115,169265,1,0,0:0:0:0: +330,265,169401,1,8,0:0:0:0: +256,192,169538,38,0,L|344:184,1,80.750001540184,4|0,3:0|0:0,0:0:0:0: +256,192,169810,2,0,L|168:200,1,80.750001540184,4|0,3:0|0:0,0:0:0:0: +256,36,170083,1,4,3:0:0:0: +256,36,170219,1,2,0:0:0:0: +256,36,170356,2,0,L|256:116,1,80.750001540184 +208,260,170629,69,2,0:0:0:0: +191,249,170697,1,0,0:0:0:0: +176,240,170765,1,0,0:0:0:0: +256,192,170901,1,0,0:0:0:0: +304,260,171038,37,2,0:0:0:0: +321,249,171106,1,0,0:0:0:0: +336,240,171174,1,0,0:0:0:0: +256,192,171310,1,0,0:0:0:0: +256,332,171447,101,4,3:0:0:0: diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 7d7adf5983..45de8e2411 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -86,10 +86,11 @@ + - + @@ -147,6 +148,7 @@ + \ No newline at end of file From cc76c58f5f250151bb85ad5efa3f6ce008f0cbb0 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sat, 2 Dec 2017 16:05:39 +0100 Subject: [PATCH 041/193] fall back to .osu file for storyboard if no .osb file is present + CI fixes --- .../Beatmaps/Formats/LegacyStoryboardDecoderTest.cs | 9 +++++---- osu.Game/Beatmaps/BeatmapManager.cs | 6 ++++-- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 7 +++++++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 12 ++++++++---- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 6 +++--- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index a42318883c..839932c640 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.IO; using System.Linq; using NUnit.Framework; @@ -56,9 +55,9 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(foreground.EnabledWhenPassing); Assert.AreEqual("Foreground", foreground.Name); - int spriteCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardSprite)).Count(); - int animationCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardAnimation)).Count(); - int sampleCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardSample)).Count(); + int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite)); + int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation)); + int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSample)); Assert.AreEqual(15, spriteCount); Assert.AreEqual(1, animationCount); @@ -66,6 +65,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(background.Elements.Count(), spriteCount + animationCount + sampleCount); var sprite = background.Elements.ElementAt(0) as StoryboardSprite; + Assert.NotNull(sprite); Assert.IsTrue(sprite.HasCommands); Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition); Assert.IsTrue(sprite.IsDrawable); @@ -73,6 +73,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path); var animation = background.Elements.ElementAt(12) as StoryboardAnimation; + Assert.NotNull(animation); Assert.AreEqual(141175, animation.EndTime); Assert.AreEqual(10, animation.FrameCount); Assert.AreEqual(30, animation.FrameDelay); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 00182d5d85..d97cc6a7bf 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -615,7 +615,7 @@ namespace osu.Game.Beatmaps protected override Storyboard GetStoryboard() { - if (BeatmapSetInfo?.StoryboardFile == null) + if (BeatmapInfo?.Path == null && BeatmapSetInfo?.StoryboardFile == null) return new Storyboard(); try @@ -624,7 +624,9 @@ namespace osu.Game.Beatmaps using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) decoder = Decoder.GetDecoder(stream); - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) + // try for .osb first and fall back to .osu + string storyboardFile = BeatmapSetInfo.StoryboardFile ?? BeatmapInfo.Path; + using (var stream = new StreamReader(store.GetStream(getPathForFile(storyboardFile)))) return decoder.GetStoryboardDecoder().DecodeStoryboard(stream); } catch diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 29cf7dd913..b7004dd3eb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -47,6 +47,13 @@ namespace osu.Game.Beatmaps.Formats hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); } + protected override bool ShouldSkipLine(string line) + { + if (base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_")) + return true; + return false; + } + protected override void ProcessSection(Section section, string line) { switch (section) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 85d77d93bc..96747a870d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -52,10 +52,7 @@ namespace osu.Game.Beatmaps.Formats string line; while ((line = stream.ReadLine()) != null) { - if (string.IsNullOrWhiteSpace(line)) - continue; - - if (line.StartsWith("//")) + if (ShouldSkipLine(line)) continue; // It's already set in ParseBeatmap... why do it again? @@ -76,6 +73,13 @@ namespace osu.Game.Beatmaps.Formats } } + protected virtual bool ShouldSkipLine(string line) + { + if (string.IsNullOrWhiteSpace(line) || line.StartsWith("//")) + return true; + return false; + } + protected abstract void ProcessSection(Section section, string line); /// diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index aca92f3969..8da6a0cefb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -237,9 +237,9 @@ namespace osu.Game.Beatmaps.Formats } } - private static string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString(); + private string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString(); - private static Anchor parseOrigin(string value) + private Anchor parseOrigin(string value) { var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); switch (origin) @@ -266,6 +266,6 @@ namespace osu.Game.Beatmaps.Formats throw new InvalidDataException($@"Unknown origin: {value}"); } - private static string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('\"')); + private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('\"')); } } From ad8cd7eb5de9c97fdafd9e4df3af0a3a83163ada Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sat, 2 Dec 2017 17:04:42 +0100 Subject: [PATCH 042/193] fix possible NullReference? AppVeyor pls? --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index d97cc6a7bf..edbda1a685 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -621,7 +621,7 @@ namespace osu.Game.Beatmaps try { Decoder decoder; - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo?.Path)))) decoder = Decoder.GetDecoder(stream); // try for .osb first and fall back to .osu From 011223048b129b213003083279a9f6793e930fb5 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Mon, 4 Dec 2017 11:47:27 +0100 Subject: [PATCH 043/193] fix crash if any amount of maps were restored from main menu also fixes preview not playing if an entire set is restored --- osu.Game/Screens/Select/BeatmapCarousel.cs | 30 ++++++++++++++-------- osu.Game/Screens/Select/SongSelect.cs | 4 +-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 3f42ae11ac..47bfe6095f 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -34,7 +34,6 @@ namespace osu.Game.Screens.Select public IEnumerable Beatmaps { get { return groups.Select(g => g.BeatmapSet); } - set { scrollableContent.Clear(false); @@ -44,15 +43,14 @@ namespace osu.Game.Screens.Select List newGroups = null; Task.Run(() => - { - newGroups = value.Select(createGroup).Where(g => g != null).ToList(); - criteria.Filter(newGroups); - }).ContinueWith(t => { Schedule(() => { + newGroups = value.Select(createGroup).Where(g => g != null).ToList(); + criteria.Filter(newGroups); + foreach (var g in newGroups) - if (g != null) addGroup(g); + addGroup(g); computeYPositions(); BeatmapsChanged?.Invoke(); @@ -135,7 +133,7 @@ namespace osu.Game.Screens.Select if (i >= 0) groups.Insert(i, newGroup); else - groups.Add(newGroup); + addGroup(newGroup); } bool hadSelection = selectedGroup == group; @@ -149,8 +147,10 @@ namespace osu.Game.Screens.Select if (hadSelection && newGroup != null) { var newSelection = - newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID) ?? - newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, group.BeatmapPanels.IndexOf(selectedPanel))]; + newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID); + + if(newSelection == null && group != null && selectedPanel != null) + newSelection = newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, group.BeatmapPanels.IndexOf(selectedPanel))]; selectGroup(newGroup, newSelection); } @@ -350,6 +350,8 @@ namespace osu.Game.Screens.Select private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet) { + beatmapSet = manager.Refresh(beatmapSet); + if (beatmapSet.Beatmaps.All(b => b.Hidden)) return null; @@ -381,6 +383,10 @@ namespace osu.Game.Screens.Select private void addGroup(BeatmapGroup group) { + // prevent duplicates by concurrent independent actions trying to add a group + if (groups.Any(g => g.BeatmapSet.ID == group.BeatmapSet.ID)) + return; + groups.Add(group); panels.Add(group.Header); panels.AddRange(group.BeatmapPanels); @@ -478,7 +484,8 @@ namespace osu.Game.Screens.Select if (panel == null) panel = group.BeatmapPanels.First(); - if (selectedPanel == panel) return; + if (selectedPanel == panel) + return; Trace.Assert(group.BeatmapPanels.Contains(panel), @"Selected panel must be in provided group"); @@ -490,7 +497,8 @@ namespace osu.Game.Screens.Select panel.State = PanelSelectedState.Selected; - if (selectedPanel == panel) return; + if (selectedPanel == panel) + return; selectedPanel = panel; selectedGroup = group; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 46284226d7..0f72e3fd95 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -255,11 +255,11 @@ namespace osu.Game.Screens.Select UpdateBeatmap(Beatmap.Value); }; - selectionChangedDebounce?.Cancel(); - if (beatmap?.Equals(beatmapNoDebounce) == true) return; + selectionChangedDebounce?.Cancel(); + beatmapNoDebounce = beatmap; if (beatmap == null) From 12665fb8cf83ca2648da32eb72f2f303a3a372d3 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Mon, 4 Dec 2017 12:11:28 +0100 Subject: [PATCH 044/193] remove unnecessary declaration + revert accidental formatting --- osu.Game/Screens/Select/BeatmapCarousel.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 47bfe6095f..fe981f1f2a 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -40,13 +40,11 @@ namespace osu.Game.Screens.Select panels.Clear(); groups.Clear(); - List newGroups = null; - Task.Run(() => { Schedule(() => { - newGroups = value.Select(createGroup).Where(g => g != null).ToList(); + var newGroups = value.Select(createGroup).Where(g => g != null).ToList(); criteria.Filter(newGroups); foreach (var g in newGroups) @@ -484,8 +482,7 @@ namespace osu.Game.Screens.Select if (panel == null) panel = group.BeatmapPanels.First(); - if (selectedPanel == panel) - return; + if (selectedPanel == panel) return; Trace.Assert(group.BeatmapPanels.Contains(panel), @"Selected panel must be in provided group"); @@ -497,8 +494,7 @@ namespace osu.Game.Screens.Select panel.State = PanelSelectedState.Selected; - if (selectedPanel == panel) - return; + if (selectedPanel == panel) return; selectedPanel = panel; selectedGroup = group; From a78441bc5a511d5f780cd949d2d21cc2218c8be9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 23:03:31 +0900 Subject: [PATCH 045/193] Apply changes in line with framework input adjustments --- osu.Game.Rulesets.Osu/OsuInputManager.cs | 3 +++ .../DatabasedKeyBindingInputManager.cs | 4 +-- osu.Game/Input/KeyBindingStore.cs | 2 +- .../KeyBinding/GlobalKeyBindingsSection.cs | 4 +-- osu.Game/Rulesets/UI/RulesetInputManager.cs | 25 +++++++++++++++---- 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index a65d28cec0..56990d1351 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using System.ComponentModel; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.UI; @@ -9,6 +10,8 @@ namespace osu.Game.Rulesets.Osu { public class OsuInputManager : RulesetInputManager { + public IEnumerable PressedActions => KeyBindingContainer.PressedActions; + public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique) { } diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs index bae14fc1dc..784e6462f2 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs @@ -14,7 +14,7 @@ namespace osu.Game.Input.Bindings /// A KeyBindingInputManager with a database backing for custom overrides. /// /// The type of the custom action. - public abstract class DatabasedKeyBindingInputManager : KeyBindingInputManager + public class DatabasedKeyBindingInputManager : KeyBindingContainer where T : struct { private readonly RulesetInfo ruleset; @@ -31,7 +31,7 @@ namespace osu.Game.Input.Bindings /// A reference to identify the current . Used to lookup mappings. Null for global mappings. /// An optional variant for the specified . Used when a ruleset has more than one possible keyboard layouts. /// Specify how to deal with multiple matches of s and s. - protected DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None) + public DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None) : base(simultaneousMode) { this.ruleset = ruleset; diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 95791391f0..9e2988417a 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -27,7 +27,7 @@ namespace osu.Game.Input } } - public void Register(KeyBindingInputManager manager) => insertDefaults(manager.DefaultKeyBindings); + public void Register(KeyBindingContainer manager) => insertDefaults(manager.DefaultKeyBindings); private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) { diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index 8ebd4ac545..4a7e4f4e6e 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.KeyBinding public override FontAwesome Icon => FontAwesome.fa_osu_hot; public override string Header => "Global"; - public GlobalKeyBindingsSection(KeyBindingInputManager manager) + public GlobalKeyBindingsSection(KeyBindingContainer manager) { Add(new DefaultBindingsSubsection(manager)); } @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.KeyBinding { protected override string Header => string.Empty; - public DefaultBindingsSubsection(KeyBindingInputManager manager) + public DefaultBindingsSubsection(KeyBindingContainer manager) : base(null) { Defaults = manager.DefaultKeyBindings; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 8c4d6de1fe..5cd79cff29 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Timing; @@ -16,11 +18,24 @@ using OpenTK.Input; namespace osu.Game.Rulesets.UI { - public abstract class RulesetInputManager : DatabasedKeyBindingInputManager, ICanAttachKeyCounter, IHasReplayHandler + public abstract class RulesetInputManager : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler where T : struct { - protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique) + public class RulesetKeyBindingContainer : DatabasedKeyBindingInputManager { + public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) + : base(ruleset, variant, unique) + { + } + } + + protected readonly KeyBindingContainer KeyBindingContainer; + + protected override Container Content => KeyBindingContainer; + + protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) + { + InternalChild = KeyBindingContainer = new RulesetKeyBindingContainer(ruleset, variant, unique); } #region Action mapping (for replays) @@ -41,10 +56,10 @@ namespace osu.Game.Rulesets.UI List newActions = replayState.PressedActions; foreach (var released in lastPressedActions.Except(newActions)) - PropagateReleased(KeyBindingInputQueue, released); + KeyBindingContainer.TriggerReleased(released); foreach (var pressed in newActions.Except(lastPressedActions)) - PropagatePressed(KeyBindingInputQueue, pressed); + KeyBindingContainer.TriggerPressed(pressed); lastPressedActions = newActions; } @@ -203,7 +218,7 @@ namespace osu.Game.Rulesets.UI Add(receptor); keyCounter.SetReceptor(receptor); - keyCounter.AddRange(DefaultKeyBindings.Select(b => b.GetAction()).Distinct().Select(b => new KeyCounterAction(b))); + keyCounter.AddRange(KeyBindingContainer.DefaultKeyBindings.Select(b => b.GetAction()).Distinct().Select(b => new KeyCounterAction(b))); } public class ActionReceptor : KeyCounterCollection.Receptor, IKeyBindingHandler From 2a1a9b9f1f8454a595daeb89b012410541b9661c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2017 17:26:39 +0900 Subject: [PATCH 046/193] Fix post-merge issue --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index c451460a78..736cc2a0b0 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -87,7 +87,7 @@ namespace osu.Game.Beatmaps private Waveform populateWaveform() => GetWaveform(); - public bool StoryboardLoaded => storyboard.IsValueCreated; + public bool StoryboardLoaded => storyboard.IsResultAvailable; public Storyboard Storyboard => storyboard.Value.Result; public async Task GetStoryboardAsync() => await storyboard.Value; private readonly AsyncLazy storyboard; From 13d0524542bed68cc93a559b64d45103c29bb6ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2017 18:36:49 +0900 Subject: [PATCH 047/193] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 9cd6968a8c..797a351db2 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 9cd6968a8c4d27415808f5e770d24fec5a44485f +Subproject commit 797a351db2e852fef5296453641ffbf6b2f6dc11 From 8c89354b36638a36def5a25ea70e6f96ac738c96 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2017 20:44:38 +0900 Subject: [PATCH 048/193] Remove extra whitespace --- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index cddc05c554..ac5683de00 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -10,7 +10,6 @@ namespace osu.Game.Overlays.BeatmapSet { public class HeaderButton : TriangleButton { - public HeaderButton() { Height = 0; @@ -25,6 +24,5 @@ namespace osu.Game.Overlays.BeatmapSet Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); Triangles.TriangleScale = 1.5f; } - } } From 24b3b10942bca6ef24e531e4e1a12adbc57be3c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2017 20:44:55 +0900 Subject: [PATCH 049/193] Move drawable assignment to load, remove depth overrides --- osu.Game/Overlays/BeatmapSet/FavouriteButton.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs index 77be4da5f8..1b22853656 100644 --- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,16 +16,15 @@ namespace osu.Game.Overlays.BeatmapSet { public readonly Bindable Favourited = new Bindable(); - public FavouriteButton() + [BackgroundDependencyLoader] + private void load() { - Container pink; SpriteIcon icon; AddRange(new Drawable[] { pink = new Container { - Depth = -1, RelativeSizeAxes = Axes.Both, Alpha = 0f, Children = new Drawable[] @@ -45,7 +45,6 @@ namespace osu.Game.Overlays.BeatmapSet }, icon = new SpriteIcon { - Depth = -1, Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.fa_heart_o, From d52b84df46ee3e5662a058cc8de2a8d4414ff554 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 20:53:28 +0900 Subject: [PATCH 050/193] Move KeyboardStep assignment to ctor --- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 53704ec72d..392bc6f1bd 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -80,8 +80,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input set { - KeyboardStep = 0.01f; - BindableDouble doubleValue = (BindableDouble)value; // create a second layer of bindable so we can only handle state changes when not being dragged. @@ -99,6 +97,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input doubleValue.ValueChanged += newValue => base.Bindable.Value = newValue; } } + + public SensitivitySetting() + { + KeyboardStep = 0.01f; + } } private class SensitivitySlider : OsuSliderBar From 76c09ae59ef513cf899f0ca0310252d3d0e2e8b5 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 7 Dec 2017 13:44:47 +0100 Subject: [PATCH 051/193] added comments for local context checking --- osu.Game/Beatmaps/BeatmapStore.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 352f793aac..b23f093786 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -34,11 +34,12 @@ namespace osu.Game.Beatmaps foreach (var beatmap in beatmapSet.Beatmaps.Where(b => b.Metadata != null)) { + // check local context for metadata so we can reuse duplicates from the same set var contextMetadata = context.Set().Local.SingleOrDefault(e => e.Equals(beatmap.Metadata)); if (contextMetadata != null) - beatmap.Metadata = contextMetadata; + beatmap.Metadata = contextMetadata; // reuse existing else - context.BeatmapMetadata.Attach(beatmap.Metadata); + context.BeatmapMetadata.Attach(beatmap.Metadata); // adding new to context } context.BeatmapSetInfo.Attach(beatmapSet); From 1dcbfab18e97a3c39a01ceeeb42bb1096519269e Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 7 Dec 2017 13:56:37 +0100 Subject: [PATCH 052/193] removed redundant comment --- osu.Game/Beatmaps/BeatmapStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index b23f093786..ac40b3378a 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps // check local context for metadata so we can reuse duplicates from the same set var contextMetadata = context.Set().Local.SingleOrDefault(e => e.Equals(beatmap.Metadata)); if (contextMetadata != null) - beatmap.Metadata = contextMetadata; // reuse existing + beatmap.Metadata = contextMetadata; else context.BeatmapMetadata.Attach(beatmap.Metadata); // adding new to context } From 95955d68eff35ed128d90aab1ec1ca532bb4e3a1 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 7 Dec 2017 14:14:50 +0100 Subject: [PATCH 053/193] rephrased description of local context checking --- osu.Game/Beatmaps/BeatmapStore.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index ac40b3378a..fb45c17454 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -34,12 +34,14 @@ namespace osu.Game.Beatmaps foreach (var beatmap in beatmapSet.Beatmaps.Where(b => b.Metadata != null)) { - // check local context for metadata so we can reuse duplicates from the same set + // If we detect a new metadata object it'll be attached to the current context so it can be reused + // to prevent duplicate entries when persisting. To accomplish this we look in the cache (.Local) + // of the corresponding table (.Set()) for matching entries to our criteria. var contextMetadata = context.Set().Local.SingleOrDefault(e => e.Equals(beatmap.Metadata)); if (contextMetadata != null) beatmap.Metadata = contextMetadata; else - context.BeatmapMetadata.Attach(beatmap.Metadata); // adding new to context + context.BeatmapMetadata.Attach(beatmap.Metadata); } context.BeatmapSetInfo.Attach(beatmapSet); From 105d01d85b9616f9d8be3ea9ea69893df8f882a1 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 7 Dec 2017 22:02:53 +0100 Subject: [PATCH 054/193] fix crash when restoring from any non-SongSelect-screen removed unwanted refresh --- osu.Game/Screens/Select/BeatmapCarousel.cs | 29 ++++++++-------------- osu.Game/Screens/Select/SongSelect.cs | 13 ++++++++-- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index fe981f1f2a..cbeefcdda5 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -112,29 +112,26 @@ namespace osu.Game.Screens.Select Schedule(() => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID))); } - public void UpdateBeatmap(BeatmapInfo beatmap) + public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) { - // todo: this method should not run more than once for the same BeatmapSetInfo. - var set = manager.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID); - // todo: this method should be smarter as to not recreate panels that haven't changed, etc. - var group = groups.Find(b => b.BeatmapSet.ID == set.ID); + var oldGroup = groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID); - int i = groups.IndexOf(group); - if (i >= 0) - groups.RemoveAt(i); + var newGroup = createGroup(beatmapSet); - var newGroup = createGroup(set); + int index = groups.IndexOf(oldGroup); + if (index >= 0) + groups.RemoveAt(index); if (newGroup != null) { - if (i >= 0) - groups.Insert(i, newGroup); + if (index >= 0) + groups.Insert(index, newGroup); else addGroup(newGroup); } - bool hadSelection = selectedGroup == group; + bool hadSelection = selectedGroup == oldGroup; if (hadSelection && newGroup == null) selectedGroup = null; @@ -147,8 +144,8 @@ namespace osu.Game.Screens.Select var newSelection = newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID); - if(newSelection == null && group != null && selectedPanel != null) - newSelection = newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, group.BeatmapPanels.IndexOf(selectedPanel))]; + if(newSelection == null && oldGroup != null && selectedPanel != null) + newSelection = newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, oldGroup.BeatmapPanels.IndexOf(selectedPanel))]; selectGroup(newGroup, newSelection); } @@ -306,8 +303,6 @@ namespace osu.Game.Screens.Select if (newCriteria != null) criteria = newCriteria; - if (!IsLoaded) return; - Action perform = delegate { filterTask = null; @@ -348,8 +343,6 @@ namespace osu.Game.Screens.Select private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet) { - beatmapSet = manager.Refresh(beatmapSet); - if (beatmapSet.Beatmaps.All(b => b.Hidden)) return null; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 0f72e3fd95..ee231ff5f4 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -203,8 +203,17 @@ namespace osu.Game.Screens.Select Push(new Editor()); } - private void onBeatmapRestored(BeatmapInfo b) => Schedule(() => carousel.UpdateBeatmap(b)); - private void onBeatmapHidden(BeatmapInfo b) => Schedule(() => carousel.UpdateBeatmap(b)); + private void onBeatmapRestored(BeatmapInfo beatmap) + { + var beatmapSet = beatmaps.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID); + Schedule(() => carousel.UpdateBeatmapSet(beatmapSet)); + } + + private void onBeatmapHidden(BeatmapInfo beatmap) + { + var beatmapSet = beatmaps.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID); + Schedule(() => carousel.UpdateBeatmapSet(beatmapSet)); + } private void carouselBeatmapsLoaded() { From 2e1dfa16a28c3bbcae530d8308aec726374cd052 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Dec 2017 14:59:32 +0900 Subject: [PATCH 055/193] Fix checks disallowing import of older beatmaps with no embedded online IDs --- osu.Game/Beatmaps/BeatmapManager.cs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index edbda1a685..6d6b1cb3f4 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -497,15 +497,21 @@ namespace osu.Game.Beatmaps using (var stream = new StreamReader(reader.GetStream(mapName))) metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; + // check if a set already exists with the same online id. - beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo - { - OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, - Beatmaps = new List(), - Hash = hash, - Files = fileInfos, - Metadata = metadata - }; + if (metadata.OnlineBeatmapSetID != null) + beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID); + + if (beatmapSet == null) + beatmapSet = new BeatmapSetInfo + { + OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, + Beatmaps = new List(), + Hash = hash, + Files = fileInfos, + Metadata = metadata + }; + var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu")); @@ -525,7 +531,7 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); - var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID); + var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || (beatmap.BeatmapInfo.OnlineBeatmapID != null && b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID)); if (existing == null) { From 31884a951aada0914546c5bd4505038da70d640c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Dec 2017 15:53:59 +0900 Subject: [PATCH 056/193] Remove "redundant" parenthesis --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 6d6b1cb3f4..f599e8a108 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -531,7 +531,7 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); - var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || (beatmap.BeatmapInfo.OnlineBeatmapID != null && b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID)); + var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || beatmap.BeatmapInfo.OnlineBeatmapID != null && b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID); if (existing == null) { From faa921ba05790c42ee9d6df15f360bb032023bab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Dec 2017 17:41:10 +0900 Subject: [PATCH 057/193] Fix up post-merge issues --- osu-framework | 2 +- ...layfieldOverlay.cs => TestCaseEditorSelectionLayer.cs} | 8 ++++---- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- .../Edit/{PlayfieldOverlay.cs => SelectionLayer.cs} | 4 ++-- osu.Game/osu.Game.csproj | 1 + 5 files changed, 9 insertions(+), 8 deletions(-) rename osu.Game.Tests/Visual/{TestCaseEditorPlayfieldOverlay.cs => TestCaseEditorSelectionLayer.cs} (87%) rename osu.Game/Rulesets/Edit/{PlayfieldOverlay.cs => SelectionLayer.cs} (94%) diff --git a/osu-framework b/osu-framework index d231ca9f79..797a351db2 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d231ca9f79936f3a7f3cff0c7721587755ae168c +Subproject commit 797a351db2e852fef5296453641ffbf6b2f6dc11 diff --git a/osu.Game.Tests/Visual/TestCaseEditorPlayfieldOverlay.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs similarity index 87% rename from osu.Game.Tests/Visual/TestCaseEditorPlayfieldOverlay.cs rename to osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index f0da23955d..79f3e4f1d3 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorPlayfieldOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -16,11 +16,11 @@ using osu.Game.Rulesets.Osu.UI; namespace osu.Game.Tests.Visual { - public class TestCaseEditorPlayfieldOverlay : OsuTestCase + public class TestCaseEditorSelectionLayer : OsuTestCase { - public override IReadOnlyList RequiredTypes => new[] { typeof(PlayfieldOverlay) }; + public override IReadOnlyList RequiredTypes => new[] { typeof(SelectionLayer) }; - public TestCaseEditorPlayfieldOverlay() + public TestCaseEditorSelectionLayer() { var playfield = new OsuEditPlayfield(); playfield.Add(new DrawableHitCircle(new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f })); @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual Clock = new FramedClock(new StopwatchClock()), Child = playfield }, - new PlayfieldOverlay(playfield) + new SelectionLayer(playfield) }; } } diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index b7536112e3..1596e7e961 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -108,7 +108,7 @@ - + diff --git a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs b/osu.Game/Rulesets/Edit/SelectionLayer.cs similarity index 94% rename from osu.Game/Rulesets/Edit/PlayfieldOverlay.cs rename to osu.Game/Rulesets/Edit/SelectionLayer.cs index 98b3bce265..cfe5f8ae5e 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs +++ b/osu.Game/Rulesets/Edit/SelectionLayer.cs @@ -17,7 +17,7 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Edit { - public class PlayfieldOverlay : CompositeDrawable + public class SelectionLayer : CompositeDrawable { private readonly static Color4 selection_normal_colour = Color4.White; private readonly static Color4 selection_attached_colour = OsuColour.FromHex("eeaa00"); @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Edit private readonly Playfield playfield; - public PlayfieldOverlay(Playfield playfield) + public SelectionLayer(Playfield playfield) { this.playfield = playfield; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 660d7cb5e6..d74774e403 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -565,6 +565,7 @@ + From 5341e791029201976bbf401f8cf4615c1086e4c6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Dec 2017 17:51:15 +0900 Subject: [PATCH 058/193] Remove SelectionDragger for now --- osu.Game/Rulesets/Edit/SelectionDragger.cs | 12 ------------ osu.Game/osu.Game.csproj | 1 - 2 files changed, 13 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/SelectionDragger.cs diff --git a/osu.Game/Rulesets/Edit/SelectionDragger.cs b/osu.Game/Rulesets/Edit/SelectionDragger.cs deleted file mode 100644 index 35ea3a375e..0000000000 --- a/osu.Game/Rulesets/Edit/SelectionDragger.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Rulesets.Edit -{ - public class SelectionDragger : CompositeDrawable - { - - } -} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d74774e403..99156e6658 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -564,7 +564,6 @@ - From 51dc66df12ee244544757498c8a144f4d53352a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Dec 2017 18:45:06 +0900 Subject: [PATCH 059/193] Add support for relevance based search results in osu!direct --- osu.Game/Overlays/Direct/FilterControl.cs | 1 + osu.Game/Overlays/DirectOverlay.cs | 26 +++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 9b52cfd367..46ba000a28 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -105,6 +105,7 @@ namespace osu.Game.Overlays.Direct public enum DirectSortCriteria { + Relevance, Title, Artist, Creator, diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 0b7a30797d..7994483043 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -43,6 +43,7 @@ namespace osu.Game.Overlays protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); private IEnumerable beatmapSets; + public IEnumerable BeatmapSets { get { return beatmapSets; } @@ -69,6 +70,7 @@ namespace osu.Game.Overlays } private ResultCounts resultAmounts; + public ResultCounts ResultAmounts { get { return resultAmounts; } @@ -115,7 +117,23 @@ namespace osu.Game.Overlays }, }; - Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; }; + Filter.Search.Current.ValueChanged += text => + { + if (text != string.Empty) + { + Header.Tabs.Current.Value = DirectTab.Search; + + if (Filter.Tabs.Current.Value == DirectSortCriteria.Ranked) + Filter.Tabs.Current.Value = DirectSortCriteria.Relevance; + } + else + { + Header.Tabs.Current.Value = DirectTab.NewestMaps; + + if (Filter.Tabs.Current.Value == DirectSortCriteria.Relevance) + Filter.Tabs.Current.Value = DirectSortCriteria.Ranked; + } + }; ((FilterControl)Filter).Ruleset.ValueChanged += ruleset => Scheduler.AddOnce(updateSearch); Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels; Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += rankStatus => Scheduler.AddOnce(updateSearch); @@ -271,9 +289,9 @@ namespace osu.Game.Overlays if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return; getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty, - ((FilterControl)Filter).Ruleset.Value, - Filter.DisplayStyleControl.Dropdown.Current.Value, - Filter.Tabs.Current.Value); //todo: sort direction (?) + ((FilterControl)Filter).Ruleset.Value, + Filter.DisplayStyleControl.Dropdown.Current.Value, + Filter.Tabs.Current.Value); //todo: sort direction (?) getSetsRequest.Success += response => { From 679134c0306c5c74d7f1776d335ca075eecd836a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Dec 2017 18:55:25 +0900 Subject: [PATCH 060/193] Add ShortName to rulesets --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 ++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 ++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 ++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 ++ osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 ++ osu.Game/Rulesets/Ruleset.cs | 19 ++++++++++++------- 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 1d5fc0545e..cb46c75583 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -93,6 +93,8 @@ namespace osu.Game.Rulesets.Catch public override string Description => "osu!catch"; + public override string ShortName => "fruits"; + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 4eea884891..070c7b09d1 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -105,6 +105,8 @@ namespace osu.Game.Rulesets.Mania public override string Description => "osu!mania"; + public override string ShortName => "mania"; + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap); diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 0d2343a33e..bb5700976d 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -124,6 +124,8 @@ namespace osu.Game.Rulesets.Osu public override string Description => "osu!"; + public override string ShortName => "osu"; + public override SettingsSubsection CreateSettings() => new OsuSettings(); public override int LegacyID => 0; diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 99ae36967a..4de9ba0ce7 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -95,6 +95,8 @@ namespace osu.Game.Rulesets.Taiko public override string Description => "osu!taiko"; + public override string ShortName => "taiko"; + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap); diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index c187aa592a..1434943da0 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -63,6 +63,8 @@ namespace osu.Game.Beatmaps public override string Description => "dummy"; + public override string ShortName => "dummy"; + public DummyRuleset(RulesetInfo rulesetInfo) : base(rulesetInfo) { diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index df5e12a24f..642647ec69 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -23,13 +23,13 @@ namespace osu.Game.Rulesets public virtual IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { }; public IEnumerable GetAllMods() => Enum.GetValues(typeof(ModType)).Cast() - // Get all mod types as an IEnumerable - .SelectMany(GetModsFor) - // Confine all mods of each mod type into a single IEnumerable - .Where(mod => mod != null) - // Filter out all null mods - .SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod }); - // Resolve MultiMods as their .Mods property + // Get all mod types as an IEnumerable + .SelectMany(GetModsFor) + // Confine all mods of each mod type into a single IEnumerable + .Where(mod => mod != null) + // Filter out all null mods + .SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod }); + // Resolve MultiMods as their .Mods property public abstract IEnumerable GetModsFor(ModType type); @@ -66,6 +66,11 @@ namespace osu.Game.Rulesets /// public virtual int LegacyID => -1; + /// + /// A unique short name to reference this ruleset in online requests. + /// + public abstract string ShortName { get; } + /// /// A list of available variant ids. /// From 4a723f738290cea4cf593cd30762ebeec1dc3976 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Fri, 8 Dec 2017 11:05:00 +0100 Subject: [PATCH 061/193] Added the "Service Include" tag that is used by Visual Studio's test explorer to the .csproj files --- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 3 +++ osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 3 +++ osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 4 +++- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 3 +++ osu.Game/osu.Game.csproj | 3 +++ 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 969ee702e3..b03c8d2eea 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -90,6 +90,9 @@ True + + +