From fe7f9cccaa0520791ff08064fd5a851b0eaf423d Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:16:00 +0100 Subject: [PATCH 001/454] 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/454] 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/454] 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/454] 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/454] 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/454] 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/454] 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 fe559f4b62245d66e2f6b6b0ae9a4ffa2f60fb7b Mon Sep 17 00:00:00 2001 From: naoey Date: Mon, 20 Nov 2017 10:36:26 +0530 Subject: [PATCH 008/454] Add respective query params to GetScoreRequest based on selected tab. --- .../Online/API/Requests/GetScoresRequest.cs | 25 +++++++++++++++-- osu.Game/Screens/Select/BeatmapDetailArea.cs | 1 + .../Select/Leaderboards/Leaderboard.cs | 28 ++++++++++++++++++- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 3777e10a31..a52db4496a 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -10,19 +10,22 @@ using osu.Game.Rulesets; using osu.Game.Users; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Select.Leaderboards; namespace osu.Game.Online.API.Requests { public class GetScoresRequest : APIRequest { private readonly BeatmapInfo beatmap; + private readonly LeaderboardScope scope; - public GetScoresRequest(BeatmapInfo beatmap) + public GetScoresRequest(BeatmapInfo beatmap, LeaderboardScope scope = LeaderboardScope.Global) { if (!beatmap.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); this.beatmap = beatmap; + this.scope = scope; Success += onSuccess; } @@ -33,7 +36,25 @@ namespace osu.Game.Online.API.Requests score.ApplyBeatmap(beatmap); } - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores"; + private string mapScopeToQuery() + { + switch(scope) + { + case LeaderboardScope.Global: + return @"?type=global"; + + case LeaderboardScope.Friends: + return @"?type=friend"; + + case LeaderboardScope.Country: + return @"?type=country"; + + default: + return String.Empty; + } + } + + protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores{mapScopeToQuery()}"; } public class GetScoresResponse diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index a676516300..790a8421a2 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -52,6 +52,7 @@ namespace osu.Game.Screens.Select default: Details.Hide(); + Leaderboard.Scope = (LeaderboardScope) tab - 1; Leaderboard.Show(); break; } diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index d896da5319..20ab09e83e 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -74,6 +74,19 @@ namespace osu.Game.Screens.Select.Leaderboards } } + private LeaderboardScope scope = LeaderboardScope.Global; + public LeaderboardScope Scope + { + get { return scope; } + set + { + if (value == scope) return; + + scope = value; + updateScores(); + } + } + public Leaderboard() { Children = new Drawable[] @@ -120,6 +133,11 @@ namespace osu.Game.Screens.Select.Leaderboards { if (!IsLoaded) return; + if (Scope == LeaderboardScope.Local) + { + // TODO: get local scores from wherever here. + } + Scores = null; getScoresRequest?.Cancel(); @@ -127,7 +145,7 @@ namespace osu.Game.Screens.Select.Leaderboards loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap); + getScoresRequest = new GetScoresRequest(Beatmap, Scope); getScoresRequest.Success += r => { Scores = r.Scores; @@ -165,4 +183,12 @@ namespace osu.Game.Screens.Select.Leaderboards } } } + + public enum LeaderboardScope + { + Local, + Country, + Global, + Friends, + } } From a58bd72c6ede87d84a2c2d2c22d05e69d34e37ad Mon Sep 17 00:00:00 2001 From: naoey Date: Mon, 20 Nov 2017 17:07:41 +0530 Subject: [PATCH 009/454] Add placeholder when there are no scores. --- .../Select/Leaderboards/Leaderboard.cs | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 20ab09e83e..b5cd729739 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -18,13 +18,18 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using System.Linq; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics; namespace osu.Game.Screens.Select.Leaderboards { public class Leaderboard : Container { + private const double fade_duration = 200; + private readonly ScrollContainer scrollContainer; private FillFlowContainer scrollFlow; + private Container placeholderContainer; public Action ScoreSelected; @@ -40,13 +45,19 @@ namespace osu.Game.Screens.Select.Leaderboards scores = value; getScoresRequest?.Cancel(); - scrollFlow?.FadeOut(200); - scrollFlow?.Expire(); + placeholderContainer.FadeOut(fade_duration); + scrollFlow?.FadeOut(fade_duration).Expire(); scrollFlow = null; if (scores == null) return; + if (scores.Count() == 0) + { + placeholderContainer.FadeIn(fade_duration); + return; + } + // schedule because we may not be loaded yet (LoadComponentAsync complains). Schedule(() => { @@ -74,7 +85,7 @@ namespace osu.Game.Screens.Select.Leaderboards } } - private LeaderboardScope scope = LeaderboardScope.Global; + private LeaderboardScope scope; public LeaderboardScope Scope { get { return scope; } @@ -96,7 +107,36 @@ namespace osu.Game.Screens.Select.Leaderboards RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, }, - loading = new LoadingAnimation() + loading = new LoadingAnimation(), + placeholderContainer = new Container + { + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.fa_exclamation_circle, + Size = new Vector2(26), + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Text = @"No records yet!", + TextSize = 22, + }, + } + }, + }, + }, }; } @@ -133,14 +173,16 @@ namespace osu.Game.Screens.Select.Leaderboards { if (!IsLoaded) return; + Scores = null; + getScoresRequest?.Cancel(); + if (Scope == LeaderboardScope.Local) { // TODO: get local scores from wherever here. + Scores = Enumerable.Empty(); + return; } - Scores = null; - getScoresRequest?.Cancel(); - if (api == null || Beatmap?.OnlineBeatmapID == null) return; loading.Show(); From 487483eaddf0833aa39c3f4c305fcb75404a78bb Mon Sep 17 00:00:00 2001 From: naoey Date: Mon, 20 Nov 2017 18:53:50 +0530 Subject: [PATCH 010/454] Move loader hiding to a better place. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index b5cd729739..2567110ef6 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -52,6 +52,8 @@ namespace osu.Game.Screens.Select.Leaderboards if (scores == null) return; + loading.Hide(); + if (scores.Count() == 0) { placeholderContainer.FadeIn(fade_duration); @@ -191,7 +193,6 @@ namespace osu.Game.Screens.Select.Leaderboards getScoresRequest.Success += r => { Scores = r.Scores; - loading.Hide(); }; api.Queue(getScoresRequest); } From 096e98b5d3aaeebb67c528b65c1aad44756d0597 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 21 Nov 2017 19:44:38 +0530 Subject: [PATCH 011/454] Add game mode query to request. - Also update scores when game mode is changed --- .../Online/API/Requests/GetScoresRequest.cs | 44 ++++++++++++++++--- .../Select/Leaderboards/Leaderboard.cs | 14 +++--- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index a52db4496a..37b3cc55f1 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -11,6 +11,7 @@ using osu.Game.Users; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select.Leaderboards; +using System.Collections.Specialized; namespace osu.Game.Online.API.Requests { @@ -18,14 +19,26 @@ namespace osu.Game.Online.API.Requests { private readonly BeatmapInfo beatmap; private readonly LeaderboardScope scope; + private readonly RulesetInfo ruleset; - public GetScoresRequest(BeatmapInfo beatmap, LeaderboardScope scope = LeaderboardScope.Global) + public GetScoresRequest(BeatmapInfo beatmap) + { + if (!beatmap.OnlineBeatmapID.HasValue) + throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); + + this.beatmap = beatmap; + + Success += onSuccess; + } + + public GetScoresRequest(BeatmapInfo beatmap, LeaderboardScope scope, RulesetInfo ruleset) { if (!beatmap.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); this.beatmap = beatmap; this.scope = scope; + this.ruleset = ruleset; Success += onSuccess; } @@ -41,20 +54,41 @@ namespace osu.Game.Online.API.Requests switch(scope) { case LeaderboardScope.Global: - return @"?type=global"; + return @"type=global"; case LeaderboardScope.Friends: - return @"?type=friend"; + return @"type=friend"; case LeaderboardScope.Country: - return @"?type=country"; + return @"type=country"; default: return String.Empty; } } - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores{mapScopeToQuery()}"; + private string mapRulesetToQuery() + { + switch(ruleset.Name) + { + case @"osu!": + return @"mode=osu"; + + case @"osu!taiko": + return @"mode=taiko"; + + case @"osu!catch": + return @"mode=catch"; + + case @"osu!mania": + return @"mode=mania"; + + default: + return String.Empty; + } + } + + protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores?{mapScopeToQuery()}&{mapRulesetToQuery()}"; } public class GetScoresResponse diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 2567110ef6..3bc520e8e6 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -146,6 +146,8 @@ namespace osu.Game.Screens.Select.Leaderboards private BeatmapInfo beatmap; + private OsuGame osuGame; + private ScheduledDelegate pendingBeatmapSwitch; public BeatmapInfo Beatmap @@ -164,9 +166,12 @@ namespace osu.Game.Screens.Select.Leaderboards } [BackgroundDependencyLoader(permitNulls: true)] - private void load(APIAccess api) + private void load(APIAccess api, OsuGame osuGame) { this.api = api; + this.osuGame = osuGame; + + osuGame.Ruleset.ValueChanged += r => updateScores(); } private GetScoresRequest getScoresRequest; @@ -176,7 +181,8 @@ namespace osu.Game.Screens.Select.Leaderboards if (!IsLoaded) return; Scores = null; - getScoresRequest?.Cancel(); + + if (api == null || Beatmap?.OnlineBeatmapID == null) return; if (Scope == LeaderboardScope.Local) { @@ -185,11 +191,9 @@ namespace osu.Game.Screens.Select.Leaderboards return; } - if (api == null || Beatmap?.OnlineBeatmapID == null) return; - loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap, Scope); + getScoresRequest = new GetScoresRequest(Beatmap, Scope, osuGame.Ruleset.Value); getScoresRequest.Success += r => { Scores = r.Scores; From b6de1ce5b649011c6d60d8050215cfcc0a0c7517 Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 22 Nov 2017 09:56:01 +0530 Subject: [PATCH 012/454] Handle query params better. --- .../Online/API/Requests/GetScoresRequest.cs | 58 +++++++++++-------- .../Select/Leaderboards/Leaderboard.cs | 2 +- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 37b3cc55f1..534a209609 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select.Leaderboards; using System.Collections.Specialized; +using osu.Framework.IO.Network; namespace osu.Game.Online.API.Requests { @@ -31,7 +32,7 @@ namespace osu.Game.Online.API.Requests Success += onSuccess; } - public GetScoresRequest(BeatmapInfo beatmap, LeaderboardScope scope, RulesetInfo ruleset) + public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global) { if (!beatmap.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); @@ -49,46 +50,53 @@ namespace osu.Game.Online.API.Requests score.ApplyBeatmap(beatmap); } - private string mapScopeToQuery() + protected override WebRequest CreateWebRequest() { + var req = base.CreateWebRequest(); + switch(scope) { + default: case LeaderboardScope.Global: - return @"type=global"; + req.AddParameter(@"type", @"global"); + break; case LeaderboardScope.Friends: - return @"type=friend"; + req.AddParameter(@"type", @"friend"); + break; case LeaderboardScope.Country: - return @"type=country"; - - default: - return String.Empty; + req.AddParameter(@"type", @"country"); + break; } - } - private string mapRulesetToQuery() - { - switch(ruleset.Name) + if (ruleset != null) { - case @"osu!": - return @"mode=osu"; + switch (ruleset.Name) + { + default: + case @"osu!": + req.AddParameter(@"mode", @"osu"); + break; - case @"osu!taiko": - return @"mode=taiko"; - - case @"osu!catch": - return @"mode=catch"; - - case @"osu!mania": - return @"mode=mania"; + case @"osu!taiko": + req.AddParameter(@"mode", @"taiko"); + break; - default: - return String.Empty; + case @"osu!catch": + req.AddParameter(@"mode", @"catch"); + break; + + case @"osu!mania": + req.AddParameter(@"mode", @"mania"); + break; + } } + + return req; } - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores?{mapScopeToQuery()}&{mapRulesetToQuery()}"; + protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores"; } public class GetScoresResponse diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 3bc520e8e6..3b7e30dafe 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Select.Leaderboards loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap, Scope, osuGame.Ruleset.Value); + getScoresRequest = new GetScoresRequest(Beatmap, osuGame.Ruleset.Value, Scope); getScoresRequest.Success += r => { Scores = r.Scores; From e3a230320ae1aa3b1f2279b539d0e97b416d48fc Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 23 Nov 2017 19:46:58 +0100 Subject: [PATCH 013/454] 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 c5a78e54e97f29dac7f471c5bb87de89782e0629 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 24 Nov 2017 18:40:52 +0530 Subject: [PATCH 014/454] Add a retry button for when scores request fails. --- .../Select/Leaderboards/Leaderboard.cs | 103 +++++++++++++++++- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 3b7e30dafe..b25479704c 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -20,6 +20,8 @@ using osu.Game.Online.API.Requests; using System.Linq; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; +using osu.Framework.Logging; +using System.Net; namespace osu.Game.Screens.Select.Leaderboards { @@ -29,7 +31,8 @@ namespace osu.Game.Screens.Select.Leaderboards private readonly ScrollContainer scrollContainer; private FillFlowContainer scrollFlow; - private Container placeholderContainer; + private Container noResultsPlaceholder; + private Container retryPlaceholder; public Action ScoreSelected; @@ -44,19 +47,20 @@ namespace osu.Game.Screens.Select.Leaderboards { scores = value; getScoresRequest?.Cancel(); + getScoresRequest = null; - placeholderContainer.FadeOut(fade_duration); + noResultsPlaceholder.FadeOut(fade_duration); scrollFlow?.FadeOut(fade_duration).Expire(); scrollFlow = null; + loading.Hide(); + if (scores == null) return; - loading.Hide(); - if (scores.Count() == 0) { - placeholderContainer.FadeIn(fade_duration); + noResultsPlaceholder.FadeIn(fade_duration); return; } @@ -110,7 +114,7 @@ namespace osu.Game.Screens.Select.Leaderboards ScrollbarVisible = false, }, loading = new LoadingAnimation(), - placeholderContainer = new Container + noResultsPlaceholder = new Container { Alpha = 0, RelativeSizeAxes = Axes.Both, @@ -139,6 +143,35 @@ namespace osu.Game.Screens.Select.Leaderboards }, }, }, + retryPlaceholder = new Container + { + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new RetryButton + { + Action = updateScores, + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Anchor = Anchor.TopLeft, + Text = @"An error occurred!", + TextSize = 22, + }, + } + }, + }, + }, }; } @@ -180,6 +213,8 @@ namespace osu.Game.Screens.Select.Leaderboards { if (!IsLoaded) return; + retryPlaceholder.FadeOut(fade_duration); + Scores = null; if (api == null || Beatmap?.OnlineBeatmapID == null) return; @@ -198,6 +233,17 @@ namespace osu.Game.Screens.Select.Leaderboards { Scores = r.Scores; }; + getScoresRequest.Failure += e => + { + // TODO: check why failure is repeatedly invoked even on successful requests + if (e is WebException) + { + Scores = null; + retryPlaceholder.FadeIn(fade_duration); + Logger.Error(e, @"Couldn't fetch beatmap scores!"); + } + }; + api.Queue(getScoresRequest); } @@ -229,6 +275,51 @@ namespace osu.Game.Screens.Select.Leaderboards } } } + + private class RetryButton : ClickableContainer + { + private SpriteIcon icon; + + public RetryButton() + { + Height = 26; + Width = 26; + Children = new Drawable[] + { + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_refresh, + Size = new Vector2(26), + } + }; + } + + protected override bool OnHover(Framework.Input.InputState state) + { + icon.ScaleTo(1.4f, 400, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(Framework.Input.InputState state) + { + icon.ScaleTo(1f, 400, Easing.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnMouseDown(Framework.Input.InputState state, Framework.Input.MouseDownEventArgs args) + { + icon.ScaleTo(0.8f, 200, Easing.InElastic); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(Framework.Input.InputState state, Framework.Input.MouseUpEventArgs args) + { + icon.ScaleTo(1.2f, 400, Easing.OutElastic).Then().ScaleTo(1f, 400, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + } } public enum LeaderboardScope From 21d5d107381f28a8efb2642ba0cd88bf05459ce3 Mon Sep 17 00:00:00 2001 From: jorolf Date: Fri, 24 Nov 2017 22:48:56 +0100 Subject: [PATCH 015/454] 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 016/454] 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 017/454] 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 0b3f75505ef530a9960fec3f816b7f40cbaaf474 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 20:59:03 +0530 Subject: [PATCH 018/454] Don't break VisualTests and add a real beatmap step. --- osu.Game.Tests/Visual/TestCaseLeaderboard.cs | 65 ++++++++++++++++--- .../Select/Leaderboards/Leaderboard.cs | 6 +- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs index 9d6fb3a4ec..ad4aa63aa8 100644 --- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs @@ -6,15 +6,43 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; +using osu.Framework.Allocation; using OpenTK; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; namespace osu.Game.Tests.Visual { [Description("PlaySongSelect leaderboard")] internal class TestCaseLeaderboard : OsuTestCase { + private RulesetStore rulesets; + private readonly Leaderboard leaderboard; + public TestCaseLeaderboard() + { + Add(leaderboard = new Leaderboard + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(550f, 450f), + Scope = LeaderboardScope.Global, + }); + + AddStep(@"New Scores", newScores); + AddStep(@"Empty Scores", () => leaderboard.Scores = Enumerable.Empty()); + AddStep(@"Real beatmap", realBeatmap); + newScores(); + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + this.rulesets = rulesets; + } + private void newScores() { var scores = new[] @@ -204,17 +232,36 @@ namespace osu.Game.Tests.Visual leaderboard.Scores = scores; } - public TestCaseLeaderboard() + private void realBeatmap() { - Add(leaderboard = new Leaderboard + leaderboard.Beatmap = new BeatmapInfo { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(550f, 450f), - }); - - AddStep(@"New Scores", newScores); - newScores(); + StarDifficulty = 1.36, + Version = @"BASIC", + OnlineBeatmapID = 1113057, + Ruleset = rulesets.GetRuleset(0), + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 6.5f, + OverallDifficulty = 6.5f, + ApproachRate = 5, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 115000, + CircleCount = 265, + SliderCount = 71, + PlayCount = 47906, + PassCount = 19899, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }; } } } diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index b25479704c..824a54d372 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -51,6 +51,7 @@ namespace osu.Game.Screens.Select.Leaderboards noResultsPlaceholder.FadeOut(fade_duration); scrollFlow?.FadeOut(fade_duration).Expire(); + scrollContainer.Clear(true); // scores stick around in scrollFlow in VisualTests without this for some reason scrollFlow = null; loading.Hide(); @@ -204,7 +205,8 @@ namespace osu.Game.Screens.Select.Leaderboards this.api = api; this.osuGame = osuGame; - osuGame.Ruleset.ValueChanged += r => updateScores(); + if (osuGame != null) + osuGame.Ruleset.ValueChanged += r => updateScores(); } private GetScoresRequest getScoresRequest; @@ -228,7 +230,7 @@ namespace osu.Game.Screens.Select.Leaderboards loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap, osuGame.Ruleset.Value, Scope); + getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value, Scope); getScoresRequest.Success += r => { Scores = r.Scores; From ae9ce2f122fa29a65dee21db065a532201141f7e Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 21:23:36 +0530 Subject: [PATCH 019/454] Unbind ruleset event from leaderboard. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 824a54d372..3bea1d4bfd 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -22,6 +22,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics; using osu.Framework.Logging; using System.Net; +using osu.Game.Rulesets; namespace osu.Game.Screens.Select.Leaderboards { @@ -206,11 +207,21 @@ namespace osu.Game.Screens.Select.Leaderboards this.osuGame = osuGame; if (osuGame != null) - osuGame.Ruleset.ValueChanged += r => updateScores(); + osuGame.Ruleset.ValueChanged += handleRulesetChange; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (osuGame != null) + osuGame.Ruleset.ValueChanged -= handleRulesetChange; } private GetScoresRequest getScoresRequest; + private void handleRulesetChange(RulesetInfo ruleset) => updateScores(); + private void updateScores() { if (!IsLoaded) return; From f4f732ca4381768d5f54508b90fe06683f2bad0c Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 26 Nov 2017 11:19:42 +0530 Subject: [PATCH 020/454] Remove unnecessary null check and tweak transform a bit. --- .../Online/API/Requests/GetScoresRequest.cs | 31 +++++++++---------- .../Select/Leaderboards/Leaderboard.cs | 8 ++--- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 534a209609..b9db836898 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -70,27 +70,24 @@ namespace osu.Game.Online.API.Requests break; } - if (ruleset != null) + switch (ruleset?.Name) { - switch (ruleset.Name) - { - default: - case @"osu!": - req.AddParameter(@"mode", @"osu"); - break; + default: + case @"osu!": + req.AddParameter(@"mode", @"osu"); + break; - case @"osu!taiko": - req.AddParameter(@"mode", @"taiko"); - break; + case @"osu!taiko": + req.AddParameter(@"mode", @"taiko"); + break; - case @"osu!catch": - req.AddParameter(@"mode", @"catch"); - break; + case @"osu!catch": + req.AddParameter(@"mode", @"catch"); + break; - case @"osu!mania": - req.AddParameter(@"mode", @"mania"); - break; - } + case @"osu!mania": + req.AddParameter(@"mode", @"mania"); + break; } return req; diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 3bea1d4bfd..ef6ba01393 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -232,6 +232,8 @@ namespace osu.Game.Screens.Select.Leaderboards if (api == null || Beatmap?.OnlineBeatmapID == null) return; + loading.Show(); + if (Scope == LeaderboardScope.Local) { // TODO: get local scores from wherever here. @@ -239,8 +241,6 @@ namespace osu.Game.Screens.Select.Leaderboards return; } - loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value, Scope); getScoresRequest.Success += r => { @@ -323,13 +323,13 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool OnMouseDown(Framework.Input.InputState state, Framework.Input.MouseDownEventArgs args) { - icon.ScaleTo(0.8f, 200, Easing.InElastic); + icon.ScaleTo(0.8f, 800, Easing.InElastic); return base.OnMouseDown(state, args); } protected override bool OnMouseUp(Framework.Input.InputState state, Framework.Input.MouseUpEventArgs args) { - icon.ScaleTo(1.2f, 400, Easing.OutElastic).Then().ScaleTo(1f, 400, Easing.OutElastic); + icon.ScaleTo(1.2f, 800, Easing.OutElastic).Then().ScaleTo(1f, 800, Easing.OutElastic); return base.OnMouseUp(state, args); } } From b261d32588887474cd439200a1d371523b680e70 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 26 Nov 2017 12:25:48 +0530 Subject: [PATCH 021/454] Put retry button in a BeatSyncedContainer and change error message. --- .../Select/Leaderboards/Leaderboard.cs | 53 ++++++++++++++----- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index ef6ba01393..8ea577d8c0 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -23,6 +23,9 @@ using osu.Game.Graphics; using osu.Framework.Logging; using System.Net; using osu.Game.Rulesets; +using osu.Framework.Input; +using osu.Game.Beatmaps.ControlPoints; +using osu.Framework.Audio.Track; namespace osu.Game.Screens.Select.Leaderboards { @@ -167,7 +170,7 @@ namespace osu.Game.Screens.Select.Leaderboards new OsuSpriteText { Anchor = Anchor.TopLeft, - Text = @"An error occurred!", + Text = @"Couldn't retrieve scores!", TextSize = 22, }, } @@ -289,47 +292,71 @@ namespace osu.Game.Screens.Select.Leaderboards } } - private class RetryButton : ClickableContainer + private class RetryButton : BeatSyncedContainer { private SpriteIcon icon; + public Action Action; + public RetryButton() { Height = 26; Width = 26; - Children = new Drawable[] + Child = new ClickableContainer { - icon = new SpriteIcon + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = () => Action?.Invoke(), + Child = icon = new SpriteIcon { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, Icon = FontAwesome.fa_refresh, Size = new Vector2(26), - } + }, }; } - protected override bool OnHover(Framework.Input.InputState state) + private bool rightWard; + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + var duration = timingPoint.BeatLength / 2; + + icon.RotateTo(rightWard ? 3 : -3, duration * 2, Easing.OutCubic); + icon.Animate( + i => i.MoveToY(-3, duration, Easing.Out), + i => i.ScaleTo(IsHovered ? 1.3f : 1.1f, duration, Easing.Out) + ).Then( + i => i.MoveToY(0, duration, Easing.In), + i => i.ScaleTo(IsHovered ? 1.4f : 1f, duration, Easing.In) + ); + + rightWard = !rightWard; + } + + protected override bool OnHover(InputState state) { icon.ScaleTo(1.4f, 400, Easing.OutQuint); return base.OnHover(state); } - protected override void OnHoverLost(Framework.Input.InputState state) + protected override void OnHoverLost(InputState state) { + icon.ClearTransforms(); icon.ScaleTo(1f, 400, Easing.OutQuint); base.OnHoverLost(state); } - protected override bool OnMouseDown(Framework.Input.InputState state, Framework.Input.MouseDownEventArgs args) + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - icon.ScaleTo(0.8f, 800, Easing.InElastic); + icon.ClearTransforms(); + icon.ScaleTo(0.8f, 400, Easing.InElastic); return base.OnMouseDown(state, args); } - protected override bool OnMouseUp(Framework.Input.InputState state, Framework.Input.MouseUpEventArgs args) + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - icon.ScaleTo(1.2f, 800, Easing.OutElastic).Then().ScaleTo(1f, 800, Easing.OutElastic); + icon.ScaleTo(1.2f, 400, Easing.OutElastic).Then().ScaleTo(1f, 400, Easing.OutElastic); return base.OnMouseUp(state, args); } } From 421231550496456baebdbc345f0c8fe97a0bf61d Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 26 Nov 2017 12:50:20 +0530 Subject: [PATCH 022/454] Use a single placeholder container for empty and retry. --- .../Select/Leaderboards/Leaderboard.cs | 97 +++++++++---------- 1 file changed, 45 insertions(+), 52 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 8ea577d8c0..31e79022c3 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -35,8 +35,8 @@ namespace osu.Game.Screens.Select.Leaderboards private readonly ScrollContainer scrollContainer; private FillFlowContainer scrollFlow; - private Container noResultsPlaceholder; - private Container retryPlaceholder; + private Container placeholderContainer; + private FillFlowContainer placeholderFlow; public Action ScoreSelected; @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Select.Leaderboards getScoresRequest?.Cancel(); getScoresRequest = null; - noResultsPlaceholder.FadeOut(fade_duration); + placeholderContainer.FadeOut(fade_duration); scrollFlow?.FadeOut(fade_duration).Expire(); scrollContainer.Clear(true); // scores stick around in scrollFlow in VisualTests without this for some reason scrollFlow = null; @@ -65,7 +65,22 @@ namespace osu.Game.Screens.Select.Leaderboards if (scores.Count() == 0) { - noResultsPlaceholder.FadeIn(fade_duration); + placeholderFlow.Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.fa_exclamation_circle, + Size = new Vector2(26), + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Text = @"No records yet!", + TextSize = 22, + }, + }; + + placeholderContainer.FadeIn(fade_duration); return; } @@ -119,61 +134,18 @@ namespace osu.Game.Screens.Select.Leaderboards ScrollbarVisible = false, }, loading = new LoadingAnimation(), - noResultsPlaceholder = new Container + placeholderContainer = new Container { Alpha = 0, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new FillFlowContainer + placeholderFlow = new FillFlowContainer { Direction = FillDirection.Horizontal, Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new SpriteIcon - { - Icon = FontAwesome.fa_exclamation_circle, - Size = new Vector2(26), - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText - { - Text = @"No records yet!", - TextSize = 22, - }, - } - }, - }, - }, - retryPlaceholder = new Container - { - Alpha = 0, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new RetryButton - { - Action = updateScores, - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText - { - Anchor = Anchor.TopLeft, - Text = @"Couldn't retrieve scores!", - TextSize = 22, - }, - } }, }, }, @@ -229,8 +201,6 @@ namespace osu.Game.Screens.Select.Leaderboards { if (!IsLoaded) return; - retryPlaceholder.FadeOut(fade_duration); - Scores = null; if (api == null || Beatmap?.OnlineBeatmapID == null) return; @@ -255,7 +225,21 @@ namespace osu.Game.Screens.Select.Leaderboards if (e is WebException) { Scores = null; - retryPlaceholder.FadeIn(fade_duration); + placeholderFlow.Children = new Drawable[] + { + new RetryButton + { + Action = updateScores, + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Anchor = Anchor.TopLeft, + Text = @"Couldn't retrieve scores!", + TextSize = 22, + }, + }; + placeholderContainer.FadeIn(fade_duration); Logger.Error(e, @"Couldn't fetch beatmap scores!"); } }; @@ -298,6 +282,8 @@ namespace osu.Game.Screens.Select.Leaderboards public Action Action; + private OsuColour colours; + public RetryButton() { Height = 26; @@ -316,6 +302,12 @@ namespace osu.Game.Screens.Select.Leaderboards }; } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + this.colours = colours; + } + private bool rightWard; protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) @@ -350,6 +342,7 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { icon.ClearTransforms(); + icon.FlashColour(colours.Yellow, 400); icon.ScaleTo(0.8f, 400, Easing.InElastic); return base.OnMouseDown(state, args); } From ae201f0ef5dcdf1f4bbae80082ee214739ad7862 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 26 Nov 2017 15:03:49 +0530 Subject: [PATCH 023/454] R# --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 1 - osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index b9db836898..54d656eeca 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -11,7 +11,6 @@ using osu.Game.Users; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select.Leaderboards; -using System.Collections.Specialized; using osu.Framework.IO.Network; namespace osu.Game.Online.API.Requests diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 31e79022c3..62bd292710 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -34,9 +34,10 @@ namespace osu.Game.Screens.Select.Leaderboards private const double fade_duration = 200; private readonly ScrollContainer scrollContainer; + private readonly Container placeholderContainer; + private readonly FillFlowContainer placeholderFlow; + private FillFlowContainer scrollFlow; - private Container placeholderContainer; - private FillFlowContainer placeholderFlow; public Action ScoreSelected; @@ -63,7 +64,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (scores == null) return; - if (scores.Count() == 0) + if (!scores.Any()) { placeholderFlow.Children = new Drawable[] { @@ -278,7 +279,7 @@ namespace osu.Game.Screens.Select.Leaderboards private class RetryButton : BeatSyncedContainer { - private SpriteIcon icon; + private readonly SpriteIcon icon; public Action Action; From ae55d392de9a05f9d878d79a319e4a39070a1bd3 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 15:28:39 +0100 Subject: [PATCH 024/454] 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 025/454] 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 026/454] 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 027/454] 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 028/454] 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 029/454] 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 030/454] 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 031/454] 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 032/454] 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 033/454] 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 034/454] 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 035/454] 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 ac1fb5118c6b075de0d333a6f49665b6396b8d53 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 28 Nov 2017 11:35:39 +0530 Subject: [PATCH 036/454] Fix line endings and derp that was causing request failures. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 62bd292710..5574dd69a1 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -24,7 +24,7 @@ using osu.Framework.Logging; using System.Net; using osu.Game.Rulesets; using osu.Framework.Input; -using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.ControlPoints; using osu.Framework.Audio.Track; namespace osu.Game.Screens.Select.Leaderboards @@ -51,7 +51,6 @@ namespace osu.Game.Screens.Select.Leaderboards set { scores = value; - getScoresRequest?.Cancel(); getScoresRequest = null; placeholderContainer.FadeOut(fade_duration); @@ -202,6 +201,8 @@ namespace osu.Game.Screens.Select.Leaderboards { if (!IsLoaded) return; + getScoresRequest?.Cancel(); + Scores = null; if (api == null || Beatmap?.OnlineBeatmapID == null) return; @@ -318,7 +319,7 @@ namespace osu.Game.Screens.Select.Leaderboards icon.RotateTo(rightWard ? 3 : -3, duration * 2, Easing.OutCubic); icon.Animate( i => i.MoveToY(-3, duration, Easing.Out), - i => i.ScaleTo(IsHovered ? 1.3f : 1.1f, duration, Easing.Out) + i => i.ScaleTo(IsHovered ? 1.3f : 1.1f, duration, Easing.Out) ).Then( i => i.MoveToY(0, duration, Easing.In), i => i.ScaleTo(IsHovered ? 1.4f : 1f, duration, Easing.In) @@ -350,7 +351,7 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - icon.ScaleTo(1.2f, 400, Easing.OutElastic).Then().ScaleTo(1f, 400, Easing.OutElastic); + icon.ScaleTo(1.2f, 400, Easing.OutElastic); return base.OnMouseUp(state, args); } } From e832f163e7174873ae2d68058e6e51ecc1218cf8 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 28 Nov 2017 11:57:29 +0530 Subject: [PATCH 037/454] Add failure test case. - Only show failure if request wasn't cancelled --- osu.Game.Tests/Visual/TestCaseLeaderboard.cs | 23 +++++++- .../Select/Leaderboards/Leaderboard.cs | 59 +++++++++---------- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs index ad4aa63aa8..52daf95810 100644 --- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs @@ -9,6 +9,7 @@ using osu.Game.Users; using osu.Framework.Allocation; using OpenTK; using System.Linq; +using System.Net; using osu.Game.Beatmaps; using osu.Game.Rulesets; @@ -19,11 +20,11 @@ namespace osu.Game.Tests.Visual { private RulesetStore rulesets; - private readonly Leaderboard leaderboard; + private readonly FailableLeaderboard leaderboard; public TestCaseLeaderboard() { - Add(leaderboard = new Leaderboard + Add(leaderboard = new FailableLeaderboard { Origin = Anchor.Centre, Anchor = Anchor.Centre, @@ -33,8 +34,8 @@ namespace osu.Game.Tests.Visual AddStep(@"New Scores", newScores); AddStep(@"Empty Scores", () => leaderboard.Scores = Enumerable.Empty()); + AddStep(@"Network failure", networkFailure); AddStep(@"Real beatmap", realBeatmap); - newScores(); } [BackgroundDependencyLoader] @@ -263,5 +264,21 @@ namespace osu.Game.Tests.Visual }, }; } + + private void networkFailure() + { + leaderboard.Beatmap = new BeatmapInfo(); + } + + private class FailableLeaderboard : Leaderboard + { + protected override void UpdateScores() + { + if (Beatmap?.OnlineBeatmapID == null) + OnUpdateFailed(new WebException()); + else + base.UpdateScores(); + } + } } } diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 5574dd69a1..8fd04e2c25 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -21,7 +21,6 @@ using System.Linq; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; using osu.Framework.Logging; -using System.Net; using osu.Game.Rulesets; using osu.Framework.Input; using osu.Game.Beatmaps.ControlPoints; @@ -51,7 +50,6 @@ namespace osu.Game.Screens.Select.Leaderboards set { scores = value; - getScoresRequest = null; placeholderContainer.FadeOut(fade_duration); scrollFlow?.FadeOut(fade_duration).Expire(); @@ -120,7 +118,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (value == scope) return; scope = value; - updateScores(); + UpdateScores(); } } @@ -171,7 +169,7 @@ namespace osu.Game.Screens.Select.Leaderboards Scores = null; pendingBeatmapSwitch?.Cancel(); - pendingBeatmapSwitch = Schedule(updateScores); + pendingBeatmapSwitch = Schedule(UpdateScores); } } @@ -195,13 +193,14 @@ namespace osu.Game.Screens.Select.Leaderboards private GetScoresRequest getScoresRequest; - private void handleRulesetChange(RulesetInfo ruleset) => updateScores(); + private void handleRulesetChange(RulesetInfo ruleset) => UpdateScores(); - private void updateScores() + protected virtual void UpdateScores() { if (!IsLoaded) return; getScoresRequest?.Cancel(); + getScoresRequest = null; Scores = null; @@ -221,34 +220,34 @@ namespace osu.Game.Screens.Select.Leaderboards { Scores = r.Scores; }; - getScoresRequest.Failure += e => - { - // TODO: check why failure is repeatedly invoked even on successful requests - if (e is WebException) - { - Scores = null; - placeholderFlow.Children = new Drawable[] - { - new RetryButton - { - Action = updateScores, - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText - { - Anchor = Anchor.TopLeft, - Text = @"Couldn't retrieve scores!", - TextSize = 22, - }, - }; - placeholderContainer.FadeIn(fade_duration); - Logger.Error(e, @"Couldn't fetch beatmap scores!"); - } - }; + getScoresRequest.Failure += OnUpdateFailed; api.Queue(getScoresRequest); } + protected void OnUpdateFailed(Exception e) + { + if (e is OperationCanceledException) return; + + Scores = null; + placeholderFlow.Children = new Drawable[] + { + new RetryButton + { + Action = UpdateScores, + Margin = new MarginPadding { Right = 10 }, + }, + new OsuSpriteText + { + Anchor = Anchor.TopLeft, + Text = @"Couldn't retrieve scores!", + TextSize = 22, + }, + }; + placeholderContainer.FadeIn(fade_duration); + Logger.Error(e, @"Couldn't fetch beatmap scores!"); + } + protected override void Update() { base.Update(); From a30cd42ba2cb43b7535a60c4345bd6c77d26b464 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 28 Nov 2017 14:38:35 +0530 Subject: [PATCH 038/454] Make retry button not look drunk. --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 8fd04e2c25..e80f502e73 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -299,6 +299,7 @@ namespace osu.Game.Screens.Select.Leaderboards { Icon = FontAwesome.fa_refresh, Size = new Vector2(26), + Shadow = true, }, }; } @@ -309,13 +310,10 @@ namespace osu.Game.Screens.Select.Leaderboards this.colours = colours; } - private bool rightWard; - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { var duration = timingPoint.BeatLength / 2; - icon.RotateTo(rightWard ? 3 : -3, duration * 2, Easing.OutCubic); icon.Animate( i => i.MoveToY(-3, duration, Easing.Out), i => i.ScaleTo(IsHovered ? 1.3f : 1.1f, duration, Easing.Out) @@ -323,8 +321,6 @@ namespace osu.Game.Screens.Select.Leaderboards i => i.MoveToY(0, duration, Easing.In), i => i.ScaleTo(IsHovered ? 1.4f : 1f, duration, Easing.In) ); - - rightWard = !rightWard; } protected override bool OnHover(InputState state) @@ -342,15 +338,13 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - icon.ClearTransforms(); - icon.FlashColour(colours.Yellow, 400); - icon.ScaleTo(0.8f, 400, Easing.InElastic); + icon.FadeColour(colours.Yellow, 400); return base.OnMouseDown(state, args); } protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - icon.ScaleTo(1.2f, 400, Easing.OutElastic); + icon.FadeColour(Color4.White, 400); return base.OnMouseUp(state, args); } } From 898c49c19db9ab54cd5eaf2bd91156aaac5b573d Mon Sep 17 00:00:00 2001 From: Santeri Date: Tue, 28 Nov 2017 16:14:32 +0200 Subject: [PATCH 039/454] 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 6a4cc933603cf102108f1e354af006b05b871940 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Tue, 28 Nov 2017 21:26:13 +0100 Subject: [PATCH 040/454] fixes crash if all beatmaps of a set are hidden --- osu.Game/Screens/Select/BeatmapCarousel.cs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index b0a636dfb3..e0f3137cec 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Select Task.Run(() => { - newGroups = value.Select(createGroup).ToList(); + newGroups = value.Select(createGroup).Where(g => g != null).ToList(); criteria.Filter(newGroups); }).ContinueWith(t => { @@ -124,16 +124,24 @@ namespace osu.Game.Screens.Select // 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); + BeatmapGroup newGroup; if (group == null) - return; + { + newGroup = createGroup(set); - int i = groups.IndexOf(group); - groups.RemoveAt(i); + if (newGroup != null) + groups.Add(newGroup); + } + else + { + int i = groups.IndexOf(group); + groups.RemoveAt(i); - var newGroup = createGroup(set); + newGroup = createGroup(set); - if (newGroup != null) - groups.Insert(i, newGroup); + if (newGroup != null) + groups.Insert(i, newGroup); + } bool hadSelection = selectedGroup == group; From 3bdf82d8df16ed910920c56938c3a74811a49602 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Tue, 28 Nov 2017 21:38:11 +0100 Subject: [PATCH 041/454] refactor newly added code to be less redundant --- osu.Game/Screens/Select/BeatmapCarousel.cs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index e0f3137cec..3f42ae11ac 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -124,23 +124,18 @@ namespace osu.Game.Screens.Select // 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); - BeatmapGroup newGroup; - if (group == null) - { - newGroup = createGroup(set); - - if (newGroup != null) - groups.Add(newGroup); - } - else - { - int i = groups.IndexOf(group); + int i = groups.IndexOf(group); + if (i >= 0) groups.RemoveAt(i); - newGroup = createGroup(set); + var newGroup = createGroup(set); - if (newGroup != null) + if (newGroup != null) + { + if (i >= 0) groups.Insert(i, newGroup); + else + groups.Add(newGroup); } bool hadSelection = selectedGroup == group; From bc3f11fdb87983e0faeab1acd96aa2e526f89f52 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Wed, 29 Nov 2017 06:24:13 +0100 Subject: [PATCH 042/454] Added PlaysSamples property to prevent certain DrawableHitObjects from playing their samples on hit. Also added this to TaikoObjects so their hitsounds won't be played (will be done by the TaikoRulesetContainer) --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 2 ++ osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 7976cbbbc1..8ebfaaea38 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); + protected override bool PlaysSamples => false; + protected readonly Vector2 BaseSize; protected readonly TaikoPiece MainPiece; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 941cedca3f..12f4fc4f31 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -59,6 +59,9 @@ namespace osu.Game.Rulesets.Objects.Drawables private readonly List judgements = new List(); public IReadOnlyList Judgements => judgements; + // Override in inheriting classes to prevent from playing samples on hit + protected virtual bool PlaysSamples => true; + protected List Samples = new List(); public readonly Bindable State = new Bindable(); @@ -92,7 +95,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { UpdateState(state); - if (State == ArmedState.Hit) + if (State == ArmedState.Hit && PlaysSamples) PlaySamples(); }; From 3e8db8c5e1020031f51a874db7d0c7059a54e3b0 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Wed, 29 Nov 2017 06:27:25 +0100 Subject: [PATCH 043/454] Enabled strong taiko hitobjects playing samples again. Also removes the first hitsound from the strong hitobject so only the "hitfinish" sound gets played. --- .../Objects/Drawables/DrawableHitStrong.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs index c07eaf4d8b..eced24a8da 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// private const double second_hit_window = 30; + protected override bool PlaysSamples => true; + private double firstHitTime; private bool firstKeyHeld; private TaikoAction firstHitAction; @@ -53,6 +55,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return base.OnReleased(action); } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (Samples.Count > 1) + // Removes the "normal" hitsound, leaving only the hitfinish one + Samples.RemoveAt(0); + } + public override bool OnPressed(TaikoAction action) { if (AllJudged) From c00fb47236253aba363c6b2df67a1937a73ec932 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Wed, 29 Nov 2017 06:28:08 +0100 Subject: [PATCH 044/454] Added hitsound handling to the TaikoRulesetContainer so every KeyDown can play a hitsound (instead of the DrawableHitObjects) --- .../UI/TaikoRulesetContainer.cs | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index 48ee0a5b42..e3765c4252 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -2,8 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; +using osu.Game.Input; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Replays; @@ -15,21 +18,45 @@ using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Taiko.Replays; using OpenTK; +using OpenTK.Input; using System.Linq; using osu.Framework.Input; +using System.Collections.Generic; namespace osu.Game.Rulesets.Taiko.UI { public class TaikoRulesetContainer : ScrollingRulesetContainer { + private readonly HashSet centreKeys = new HashSet(); + private readonly HashSet rimKeys = new HashSet(); + private AudioManager audio; + private IEnumerable keyBindings; + public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(ruleset, beatmap, isForCurrentRuleset) { } [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio, KeyBindingStore store) { + keyBindings = store.Query(Ruleset.RulesetInfo.ID, Ruleset.AvailableVariants?.First() ?? 0).Cast(); + if (keyBindings.Count() == 0) + keyBindings = Ruleset.GetDefaultKeyBindings(); + + foreach (var kb in keyBindings) + { + var key = (Key)(kb.KeyCombination.Keys as InputKey[]).First(); + var action = kb.GetAction(); + + if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + centreKeys.Add(key); + + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + rimKeys.Add(key); + } + + this.audio = audio; loadBarLines(); } @@ -77,6 +104,21 @@ namespace osu.Game.Rulesets.Taiko.UI } } + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + var sampleBank = Beatmap.ControlPointInfo.SoundPointAt(WorkingBeatmap.Track.CurrentTime).SampleBank ?? "normal"; + string sampleName = ""; + + if (centreKeys.Contains(args.Key)) + sampleName = "hitnormal"; + + else if (rimKeys.Contains(args.Key)) + sampleName = "hitclap"; + + audio.Sample.Get($"Gameplay/{sampleBank}-{sampleName}")?.Play(); + return base.OnKeyDown(state, args); + } + protected override Vector2 GetPlayfieldAspectAdjust() { const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768; From e3232dd754528a8e2136f6326e20d2671fb60b7d Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Wed, 29 Nov 2017 06:51:00 +0100 Subject: [PATCH 045/454] Added if to ignore more (for our purposes) useless keydowns --- .../UI/TaikoRulesetContainer.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index e3765c4252..f72d253a73 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -106,16 +106,20 @@ namespace osu.Game.Rulesets.Taiko.UI protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { - var sampleBank = Beatmap.ControlPointInfo.SoundPointAt(WorkingBeatmap.Track.CurrentTime).SampleBank ?? "normal"; - string sampleName = ""; + if (!args.Repeat) + { + var sampleBank = Beatmap.ControlPointInfo.SoundPointAt(WorkingBeatmap.Track.CurrentTime).SampleBank ?? "normal"; + string sampleName = ""; - if (centreKeys.Contains(args.Key)) - sampleName = "hitnormal"; + if (centreKeys.Contains(args.Key)) + sampleName = "hitnormal"; - else if (rimKeys.Contains(args.Key)) - sampleName = "hitclap"; + else if (rimKeys.Contains(args.Key)) + sampleName = "hitclap"; + + audio.Sample.Get($"Gameplay/{sampleBank}-{sampleName}")?.Play(); + } - audio.Sample.Get($"Gameplay/{sampleBank}-{sampleName}")?.Play(); return base.OnKeyDown(state, args); } From 8c30fd490c373492e219bead97d5ae9fc7e57bf9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 16:22:11 +0900 Subject: [PATCH 046/454] Add HitObjectComposer class --- osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs | 10 ++++++++++ osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 1 + osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs | 10 ++++++++++ osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 1 + osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs | 10 ++++++++++ osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 1 + osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs | 10 ++++++++++ osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 1 + osu.Game/Rulesets/Edit/HitObjectComposer.cs | 10 ++++++++++ osu.Game/osu.Game.csproj | 1 + 10 files changed, 55 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs create mode 100644 osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs create mode 100644 osu.Game/Rulesets/Edit/HitObjectComposer.cs diff --git a/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs new file mode 100644 index 0000000000..01351fd00c --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Catch.Edit +{ + public class HitObjectComposer : Rulesets.Edit.HitObjectComposer + { + + } +} diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index bf60bc01bb..30c088ac85 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -48,6 +48,7 @@ + diff --git a/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs new file mode 100644 index 0000000000..aa0d43dc75 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Edit +{ + public class HitObjectComposer : Rulesets.Edit.HitObjectComposer + { + + } +} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 6f45a64d92..1470ee57b1 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -53,6 +53,7 @@ + diff --git a/osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs new file mode 100644 index 0000000000..921fb72fdf --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class HitObjectComposer : Rulesets.Edit.HitObjectComposer + { + + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 3c90749777..32a0a34ff2 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -49,6 +49,7 @@ + diff --git a/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs new file mode 100644 index 0000000000..e524b4cdc7 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Taiko.Edit +{ + public class HitObjectComposer : Rulesets.Edit.HitObjectComposer + { + + } +} diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index bf627d205a..41881fc250 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -46,6 +46,7 @@ + diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs new file mode 100644 index 0000000000..2724334ed4 --- /dev/null +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Edit +{ + public class HitObjectComposer + { + + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ce9e8eff9c..b707a3382e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -559,6 +559,7 @@ + From 2ec24f58c8e5c30e9c3cd681f55869f971b4f80e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 16:30:59 +0900 Subject: [PATCH 047/454] Add testcase for editor compose --- .../Visual/TestCaseEditorCompose.cs | 47 +++++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + 2 files changed, 48 insertions(+) create mode 100644 osu.Game.Tests/Visual/TestCaseEditorCompose.cs diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs new file mode 100644 index 0000000000..9f060a9ec4 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -0,0 +1,47 @@ +// 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.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Screens.Edit.Screens.Compose; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseEditorCompose : OsuTestCase + { + private readonly Random random; + private readonly Compose compose; + + public TestCaseEditorCompose() + { + random = new Random(1337); + + Add(compose = new Compose()); + AddStep("Next beatmap", nextBeatmap); + } + + private OsuGameBase osuGame; + private BeatmapManager beatmaps; + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame, BeatmapManager beatmaps) + { + this.osuGame = osuGame; + this.beatmaps = beatmaps; + + compose.Beatmap.BindTo(osuGame.Beatmap); + nextBeatmap(); + } + + private void nextBeatmap() + { + var sets = beatmaps.GetAllUsableBeatmapSets(); + if (sets.Count == 0) + return; + + var b = sets[random.Next(0, sets.Count)].Beatmaps[0]; + osuGame.Beatmap.Value = beatmaps.GetWorkingBeatmap(b); + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 312a564f71..34f79987c5 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -105,6 +105,7 @@ + From f586cbac3285e2b2a8e8586276bd660e9736ddb0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 16:47:36 +0900 Subject: [PATCH 048/454] Restructure Compose to use grids and eventually support HitObjectContainer --- .../Screens/Edit/Screens/Compose/Compose.cs | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 2349c261cf..67e66eecdf 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -12,40 +12,62 @@ namespace osu.Game.Screens.Edit.Screens.Compose { public class Compose : EditorScreen { + private const float vertical_margins = 10; + private const float horizontal_margins = 20; + public Compose() { ScrollableTimeline timeline; - Children = new[] + Children = new Drawable[] { - new Container + new GridContainer { - Name = "Timeline", - RelativeSizeAxes = Axes.X, - Height = 110, - Children = new Drawable[] + RelativeSizeAxes = Axes.Both, + Content = new[] { - new Box + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f) - }, - new Container - { - Name = "Content", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 17, Vertical = 10 }, - Children = new Drawable[] + new Container { - new Container + Name = "Timeline", + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 115 }, - Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f) + }, + new Container + { + Name = "Timeline content", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 115 }, + Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } + } + } + } } } + }, + new Drawable[] + { + new Container + { + Name = "Composer content", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, + } } - } - } + }, + RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, 110) } + }, }; timeline.Beatmap.BindTo(Beatmap); From 309eb4edd752b95d108770f2df55f6efdcd6ac2c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 17:46:12 +0900 Subject: [PATCH 049/454] Integrate HitObjectComposer into Compose Also removes the other rulesets' HitObjectComposers for now. --- .../Edit/HitObjectComposer.cs | 10 -------- .../osu.Game.Rulesets.Catch.csproj | 1 - .../Edit/HitObjectComposer.cs | 10 -------- .../osu.Game.Rulesets.Mania.csproj | 1 - ...ectComposer.cs => OsuHitObjectComposer.cs} | 7 ++++-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 ++++ .../osu.Game.Rulesets.Osu.csproj | 2 +- .../Edit/HitObjectComposer.cs | 10 -------- .../osu.Game.Rulesets.Taiko.csproj | 1 - osu.Game/Rulesets/Edit/HitObjectComposer.cs | 23 +++++++++++++++++- osu.Game/Rulesets/Ruleset.cs | 3 +++ .../Screens/Edit/Screens/Compose/Compose.cs | 24 ++++++++++++++++++- 12 files changed, 58 insertions(+), 38 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs delete mode 100644 osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs rename osu.Game.Rulesets.Osu/Edit/{HitObjectComposer.cs => OsuHitObjectComposer.cs} (52%) delete mode 100644 osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs diff --git a/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs deleted file mode 100644 index 01351fd00c..0000000000 --- a/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Catch.Edit -{ - public class HitObjectComposer : Rulesets.Edit.HitObjectComposer - { - - } -} diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 30c088ac85..bf60bc01bb 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -48,7 +48,6 @@ - diff --git a/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs deleted file mode 100644 index aa0d43dc75..0000000000 --- a/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Edit -{ - public class HitObjectComposer : Rulesets.Edit.HitObjectComposer - { - - } -} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 1470ee57b1..6f45a64d92 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -53,7 +53,6 @@ - diff --git a/osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs similarity index 52% rename from osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs rename to osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 921fb72fdf..18dfbb6711 100644 --- a/osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -3,8 +3,11 @@ namespace osu.Game.Rulesets.Osu.Edit { - public class HitObjectComposer : Rulesets.Edit.HitObjectComposer + public class OsuHitObjectComposer : Rulesets.Edit.HitObjectComposer { - + public OsuHitObjectComposer(Ruleset ruleset) + : base(ruleset) + { + } } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index c87328d87c..64e76d6022 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -14,6 +14,8 @@ using System.Linq; using osu.Framework.Graphics; using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu { @@ -114,6 +116,8 @@ namespace osu.Game.Rulesets.Osu public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods); + public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); + public override string Description => "osu!"; public override SettingsSubsection CreateSettings() => new OsuSettings(); diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 32a0a34ff2..34f9bdf972 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -49,7 +49,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs deleted file mode 100644 index e524b4cdc7..0000000000 --- a/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Taiko.Edit -{ - public class HitObjectComposer : Rulesets.Edit.HitObjectComposer - { - - } -} diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 41881fc250..bf627d205a 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -46,7 +46,6 @@ - diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 2724334ed4..da31f6ba66 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -1,10 +1,31 @@ // 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; + namespace osu.Game.Rulesets.Edit { - public class HitObjectComposer + public abstract class HitObjectComposer : CompositeDrawable { + private readonly Ruleset ruleset; + public HitObjectComposer(Ruleset ruleset) + { + this.ruleset = ruleset; + + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + try + { + InternalChild = ruleset.CreateRulesetContainerWith(osuGame.Beatmap.Value, true); + } + catch { } + } } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index ed2fdf4157..dfa9ea5125 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -9,6 +9,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; @@ -49,6 +50,8 @@ namespace osu.Game.Rulesets public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null); + public virtual HitObjectComposer CreateHitObjectComposer() => null; + public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle }; public abstract string Description { get; } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 67e66eecdf..8cfb04da06 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -1,11 +1,14 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose.Timeline; namespace osu.Game.Screens.Edit.Screens.Compose @@ -15,6 +18,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose private const float vertical_margins = 10; private const float horizontal_margins = 20; + private readonly Container composerContainer; + public Compose() { ScrollableTimeline timeline; @@ -58,7 +63,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose }, new Drawable[] { - new Container + composerContainer = new Container { Name = "Composer content", RelativeSizeAxes = Axes.Both, @@ -71,6 +76,23 @@ namespace osu.Game.Screens.Edit.Screens.Compose }; timeline.Beatmap.BindTo(Beatmap); + + Beatmap.ValueChanged += beatmapChanged; + } + + private void beatmapChanged(WorkingBeatmap newBeatmap) + { + var ruleset = newBeatmap.BeatmapInfo.Ruleset.CreateInstance(); + var composer = ruleset.CreateHitObjectComposer(); + if (composer == null) + { + // Todo: Handle this + //throw new InvalidOperationException($"Ruleset {ruleset.Description} doesn't support hitobject composition."); + return; + } + + composerContainer.Add(composer); + composerContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)newBeatmap.Track ?? new StopwatchClock()); } } } From ee35422efc0396ec7800933ae4e2e51abfb45140 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 18:38:12 +0900 Subject: [PATCH 050/454] Handle rulesets that don't support composition a bit better --- osu.Game/Screens/Edit/Editor.cs | 1 + osu.Game/Screens/Edit/Screens/Compose/Compose.cs | 10 +++++----- osu.Game/Screens/Edit/Screens/EditorScreen.cs | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 607ff792d8..bc86c683c7 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -154,6 +154,7 @@ namespace osu.Game.Screens.Edit } currentScreen.Beatmap.BindTo(Beatmap); + currentScreen.ExitRequested = Exit; screenContainer.Add(currentScreen); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 8cfb04da06..6954995340 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose.Timeline; @@ -76,18 +77,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose }; timeline.Beatmap.BindTo(Beatmap); - Beatmap.ValueChanged += beatmapChanged; } private void beatmapChanged(WorkingBeatmap newBeatmap) { - var ruleset = newBeatmap.BeatmapInfo.Ruleset.CreateInstance(); - var composer = ruleset.CreateHitObjectComposer(); + var ruleset = newBeatmap.BeatmapInfo.Ruleset?.CreateInstance(); + var composer = ruleset?.CreateHitObjectComposer(); if (composer == null) { - // Todo: Handle this - //throw new InvalidOperationException($"Ruleset {ruleset.Description} doesn't support hitobject composition."); + Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); + ExitRequested?.Invoke(); return; } diff --git a/osu.Game/Screens/Edit/Screens/EditorScreen.cs b/osu.Game/Screens/Edit/Screens/EditorScreen.cs index ac248930d8..9a158d20f1 100644 --- a/osu.Game/Screens/Edit/Screens/EditorScreen.cs +++ b/osu.Game/Screens/Edit/Screens/EditorScreen.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 osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -10,6 +11,8 @@ namespace osu.Game.Screens.Edit.Screens { public class EditorScreen : Container { + public Action ExitRequested; + public readonly Bindable Beatmap = new Bindable(); protected override Container Content => content; From d402222f1778fa5e9d43f91354a7d8a6238670fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Nov 2017 05:05:07 +0900 Subject: [PATCH 051/454] Fix DisposeTrack and improve AsyncLazy to support disposal --- osu.Game/Beatmaps/WorkingBeatmap.cs | 63 +++++++++++++++++++++++++---- osu.Game/OsuGameBase.cs | 2 +- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 2a8178882e..93ba51367a 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -31,7 +31,7 @@ namespace osu.Game.Beatmaps Mods.ValueChanged += mods => applyRateAdjustments(); beatmap = new AsyncLazy(populateBeatmap); - background = new AsyncLazy(populateBackground); + background = new AsyncLazy(populateBackground, b => b == null || !b.IsDisposed); track = new AsyncLazy(populateTrack); waveform = new AsyncLazy(populateWaveform); } @@ -99,10 +99,11 @@ namespace osu.Game.Beatmaps if (WaveformLoaded) Waveform?.Dispose(); } - public void DisposeTrack() - { - if (TrackLoaded) Track?.Dispose(); - } + /// + /// Eagerly dispose of the audio track associated with this (if any). + /// Accessing track again will load a fresh instance. + /// + public void RecycleTrack() => track.Recycle(); private void applyRateAdjustments(Track t = null) { @@ -114,11 +115,57 @@ namespace osu.Game.Beatmaps mod.ApplyToClock(t); } - public class AsyncLazy : Lazy> + public class AsyncLazy { - public AsyncLazy(Func valueFactory) - : base(() => Task.Run(valueFactory)) + private Lazy> lazy; + private readonly Func valueFactory; + private readonly Func stillValidFunction; + + public AsyncLazy(Func valueFactory, Func stillValidFunction = null) { + this.valueFactory = valueFactory; + this.stillValidFunction = stillValidFunction; + + init(); + } + + public void Recycle() + { + if (!IsValueCreated) return; + + (lazy.Value.Result as IDisposable)?.Dispose(); + + init(); + } + + public bool IsValueCreated + { + get + { + ensureValid(); + return lazy.IsValueCreated; + } + } + + public Task Value + { + get + { + ensureValid(); + return lazy.Value; + } + } + + private void ensureValid() + { + if (!lazy.IsValueCreated || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; + + init(); + } + + private void init() + { + lazy = new Lazy>(() => Task.Run(valueFactory)); } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8eaa20f781..0ddff5e5aa 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -154,7 +154,7 @@ namespace osu.Game Debug.Assert(lastBeatmap != null); Debug.Assert(lastBeatmap.Track != null); - lastBeatmap.DisposeTrack(); + lastBeatmap.RecycleTrack(); } Audio.Track.AddItem(b.Track); From acb2cafa581da379df2ec85e6a69969f67694d13 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Wed, 29 Nov 2017 21:09:08 +0100 Subject: [PATCH 052/454] fix wedge not appearing --- osu.Game/Screens/Select/SongSelect.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a0b788d777..b07e68f50e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -263,10 +263,7 @@ namespace osu.Game.Screens.Select beatmapNoDebounce = beatmap; if (beatmap == null) - { - if (!Beatmap.IsDefault) - performLoad(); - } + performLoad(); else { if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID) From cd653c1cbc5a5f11652ddfda35e4690502b2f4c0 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Wed, 29 Nov 2017 21:28:02 +0100 Subject: [PATCH 053/454] 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 054/454] 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 055/454] 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 a7a9569aee3c4f8e9adfdfb754d39f2a47aa656b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 13:53:57 +0900 Subject: [PATCH 056/454] Don't exit when we don't have composer for now --- osu.Game/Screens/Edit/Screens/Compose/Compose.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 6954995340..fea439bfb8 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose if (composer == null) { Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); - ExitRequested?.Invoke(); + // ExitRequested?.Invoke(); return; } From abd61256911a9ff7e52cc5390895ec3c9e226fd1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 14:00:17 +0900 Subject: [PATCH 057/454] Make sure that composerContainer is cleared for testing purposes --- osu.Game/Screens/Edit/Screens/Compose/Compose.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index fea439bfb8..7e4ce74c5c 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -82,6 +82,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose private void beatmapChanged(WorkingBeatmap newBeatmap) { + composerContainer.Clear(); + var ruleset = newBeatmap.BeatmapInfo.Ruleset?.CreateInstance(); var composer = ruleset?.CreateHitObjectComposer(); if (composer == null) @@ -91,7 +93,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose return; } - composerContainer.Add(composer); + composerContainer.Child = composer; composerContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)newBeatmap.Track ?? new StopwatchClock()); } } From ead745697875d10bb96fd786c72cbf2e074bac41 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 15:43:19 +0900 Subject: [PATCH 058/454] Add compose radio buttons + testcase --- .../TestCaseEditorComposeRadioButtons.cs | 34 ++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + .../RadioButtons/DrawableRadioButton.cs | 107 ++++++++++++++++++ .../Compose/RadioButtons/RadioButton.cs | 20 ++++ .../RadioButtons/RadioButtonCollection.cs | 48 ++++++++ osu.Game/osu.Game.csproj | 5 +- 6 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs new file mode 100644 index 0000000000..55ceac6fd8 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs @@ -0,0 +1,34 @@ +// 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.UserInterface; +using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseEditorComposeRadioButtons : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableRadioButton) }; + + public TestCaseEditorComposeRadioButtons() + { + Add(new RadioButtonCollection + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 150, + Items = new[] + { + new RadioButton { Text = "Item 1", Action = () => { } }, + new RadioButton { Text = "Item 2", Action = () => { } }, + new RadioButton { Text = "Item 3", Action = () => { } }, + new RadioButton { Text = "Item 4", Action = () => { } }, + new RadioButton { Text = "Item 5", Action = () => { } } + } + }); + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 34f79987c5..ae88fb004f 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -106,6 +106,7 @@ + diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs new file mode 100644 index 0000000000..4ac36cc07e --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -0,0 +1,107 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +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.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons +{ + public class DrawableRadioButton : TriangleButton + { + private static readonly Color4 DefaultBackgroundColour = OsuColour.FromHex("333"); + private static readonly Color4 DefaultBubbleColour = DefaultBackgroundColour.Darken(0.5f); + private static readonly Color4 SelectedBackgroundColour = OsuColour.FromHex("1188aa"); + private static readonly Color4 SelectedBubbleColour = SelectedBackgroundColour.Lighten(0.5f); + + /// + /// Invoked when this has been selected. + /// + public Action Selected; + + private Drawable bubble; + + public DrawableRadioButton(RadioButton button) + { + Text = button.Text; + Action = button.Action; + + RelativeSizeAxes = Axes.X; + } + + [BackgroundDependencyLoader] + private void load() + { + Triangles.Alpha = 0; + BackgroundColour = DefaultBackgroundColour; + + Add(bubble = new CircularContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(0.5f), + X = 10, + Masking = true, + Colour = DefaultBubbleColour, + Blending = BlendingMode.Additive, + Child = new Box { RelativeSizeAxes = Axes.Both } + }); + } + + private bool isSelected; + + public void Deselect() + { + if (!isSelected) + return; + isSelected = false; + + BackgroundColour = DefaultBackgroundColour; + bubble.Colour = DefaultBubbleColour; + } + + public void Select() + { + if (isSelected) + return; + isSelected = true; + Selected?.Invoke(this); + + BackgroundColour = SelectedBackgroundColour; + bubble.Colour = SelectedBubbleColour; + } + + protected override bool OnClick(InputState state) + { + if (isSelected) + return true; + + if (!Enabled) + return true; + + Select(); + + return base.OnClick(state); + } + + protected override SpriteText CreateText() => new OsuSpriteText + { + Depth = -1, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + X = 40f + }; + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs new file mode 100644 index 0000000000..3ef89d2a2b --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons +{ + public class RadioButton + { + /// + /// The text that should be displayed in this button. + /// + public string Text; + + /// + /// The that should be invoked when this button is selected. + /// + public Action Action; + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs new file mode 100644 index 0000000000..557e8e1ee8 --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -0,0 +1,48 @@ +// 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.Linq; +using OpenTK; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons +{ + public class RadioButtonCollection : CompositeDrawable + { + public IReadOnlyList Items + { + set + { + buttonContainer.Clear(); + value.ForEach(addButton); + } + } + + private readonly FlowContainer buttonContainer; + + public RadioButtonCollection() + { + AutoSizeAxes = Axes.Y; + + InternalChild = buttonContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5) + }; + } + + private void addButton(RadioButton button) => buttonContainer.Add(new DrawableRadioButton(button) { Selected = buttonSelected }); + + private DrawableRadioButton currentlySelected; + private void buttonSelected(DrawableRadioButton drawableButton) + { + currentlySelected?.Deselect(); + currentlySelected = drawableButton; + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b4ab8a4cf2..1064665a0e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -306,7 +306,6 @@ - @@ -316,6 +315,10 @@ + + + + From 36cfa552f43e6ec9f1e3da59022601a524bb0f0d Mon Sep 17 00:00:00 2001 From: Nicolas Brassard Date: Wed, 29 Nov 2017 23:03:26 -0800 Subject: [PATCH 059/454] 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 73e41f9dded5128bb4df51efc5ffc74e107828fe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 16:57:30 +0900 Subject: [PATCH 060/454] Add constructors to RadioButton --- .../Visual/TestCaseEditorComposeRadioButtons.cs | 10 +++++----- .../Screens/Compose/RadioButtons/RadioButton.cs | 14 +++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs index 55ceac6fd8..c259ca666e 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs @@ -22,11 +22,11 @@ namespace osu.Game.Tests.Visual Width = 150, Items = new[] { - new RadioButton { Text = "Item 1", Action = () => { } }, - new RadioButton { Text = "Item 2", Action = () => { } }, - new RadioButton { Text = "Item 3", Action = () => { } }, - new RadioButton { Text = "Item 4", Action = () => { } }, - new RadioButton { Text = "Item 5", Action = () => { } } + new RadioButton("Item 1", () => { }), + new RadioButton("Item 2", () => { }), + new RadioButton("Item 3", () => { }), + new RadioButton("Item 4", () => { }), + new RadioButton("Item 5", () => { }) } }); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs index 3ef89d2a2b..bec2d1903d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs @@ -5,7 +5,7 @@ using System; namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons { - public class RadioButton + public struct RadioButton { /// /// The text that should be displayed in this button. @@ -16,5 +16,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons /// The that should be invoked when this button is selected. /// public Action Action; + + public RadioButton(string text) + { + Text = text; + Action = null; + } + + public RadioButton(string text, Action action) + { + Text = text; + Action = action; + } } } From 456bbe25f38e34253cf77b9057e20022add7e9e5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 16:58:14 +0900 Subject: [PATCH 061/454] Implement toolbox into HitObjectComposer --- .../Edit/OsuHitObjectComposer.cs | 11 +++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 67 ++++++++++++++++++- osu.Game/Rulesets/Edit/ToolboxGroup.cs | 19 ++++++ .../Edit/Tools/HitObjectCompositionTool.cs | 10 +++ .../Rulesets/Edit/Tools/ICompositionTool.cs | 10 +++ osu.Game/Rulesets/Edit/Tools/SelectionTool.cs | 7 ++ .../Screens/Edit/Screens/Compose/Compose.cs | 1 - osu.Game/osu.Game.csproj | 4 ++ 8 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/ToolboxGroup.cs create mode 100644 osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs create mode 100644 osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs create mode 100644 osu.Game/Rulesets/Edit/Tools/SelectionTool.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 18dfbb6711..851e572163 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -1,6 +1,10 @@ // 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 osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Osu.Objects; + namespace osu.Game.Rulesets.Osu.Edit { public class OsuHitObjectComposer : Rulesets.Edit.HitObjectComposer @@ -9,5 +13,12 @@ namespace osu.Game.Rulesets.Osu.Edit : base(ruleset) { } + + protected override IReadOnlyList CompositionTools => new ICompositionTool[] + { + new HitObjectCompositionTool(), + new HitObjectCompositionTool(), + new HitObjectCompositionTool() + }; } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index da31f6ba66..8c4969cec5 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -1,9 +1,20 @@ // 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.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; +using osu.Framework.Timing; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; +using osu.Game.Screens.Play.ReplaySettings; namespace osu.Game.Rulesets.Edit { @@ -21,11 +32,63 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { + RulesetContainer rulesetContainer; try { - InternalChild = ruleset.CreateRulesetContainerWith(osuGame.Beatmap.Value, true); + rulesetContainer = ruleset.CreateRulesetContainerWith(osuGame.Beatmap.Value, true); } - catch { } + catch (Exception e) + { + Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error); + return; + } + + RadioButtonCollection toolboxCollection; + InternalChild = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new FillFlowContainer + { + Name = "Sidebar", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 10 }, + Children = new Drawable[] + { + new ToolboxGroup { Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } } + } + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = rulesetContainer + } + }, + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 200), + } + }; + + rulesetContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)osuGame.Beatmap.Value.Track ?? new StopwatchClock()); + + toolboxCollection.Items = + new[] { new RadioButton("Select", () => setCompositionTool(new SelectionTool())) } + .Concat( + CompositionTools.Select(t => new RadioButton(t.Name, () => setCompositionTool(t))) + ) + .ToList(); } + + private void setCompositionTool(ICompositionTool tool) + { + } + + protected abstract IReadOnlyList CompositionTools { get; } } } diff --git a/osu.Game/Rulesets/Edit/ToolboxGroup.cs b/osu.Game/Rulesets/Edit/ToolboxGroup.cs new file mode 100644 index 0000000000..70e4d3a0c5 --- /dev/null +++ b/osu.Game/Rulesets/Edit/ToolboxGroup.cs @@ -0,0 +1,19 @@ +// 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.Screens.Play.ReplaySettings; + +namespace osu.Game.Rulesets.Edit +{ + public class ToolboxGroup : ReplayGroup + { + protected override string Title => "toolbox"; + + public ToolboxGroup() + { + RelativeSizeAxes = Axes.X; + Width = 1; + } + } +} diff --git a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs new file mode 100644 index 0000000000..914cbd11ca --- /dev/null +++ b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs @@ -0,0 +1,10 @@ +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Edit.Tools +{ + public class HitObjectCompositionTool : ICompositionTool + where T : HitObject + { + public string Name => typeof(T).Name; + } +} diff --git a/osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs new file mode 100644 index 0000000000..eba873f0cf --- /dev/null +++ b/osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Edit.Tools +{ + public interface ICompositionTool + { + string Name { get; } + } +} diff --git a/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs b/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs new file mode 100644 index 0000000000..9f3ef78a02 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs @@ -0,0 +1,7 @@ +namespace osu.Game.Rulesets.Edit.Tools +{ + public class SelectionTool : ICompositionTool + { + public string Name => "Select"; + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 7e4ce74c5c..5eeaf3205d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -94,7 +94,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose } composerContainer.Child = composer; - composerContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)newBeatmap.Track ?? new StopwatchClock()); } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1064665a0e..886d3313c6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -562,7 +562,11 @@ + + + + From e5353bb53e2a898a523c542854b6911ed30687ea Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 17:38:55 +0900 Subject: [PATCH 062/454] Add border to playfield, add shadow to toolbox buttons --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 15 ++++++++++++++- .../Compose/RadioButtons/DrawableRadioButton.cs | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 8c4969cec5..ed057dbb03 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -4,10 +4,12 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Rulesets.Edit.Tools; @@ -65,7 +67,18 @@ namespace osu.Game.Rulesets.Edit { RelativeSizeAxes = Axes.Both, Masking = true, - Child = rulesetContainer + BorderColour = Color4.White, + BorderThickness = 2, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + rulesetContainer + } } }, }, diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index 4ac36cc07e..690a50bb11 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -45,6 +45,14 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons Triangles.Alpha = 0; BackgroundColour = DefaultBackgroundColour; + Content.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 2, + Offset = new Vector2(0, 1), + Colour = Color4.Black.Opacity(0.5f) + }; + Add(bubble = new CircularContainer { Anchor = Anchor.CentreLeft, From 33adf569a4d825ac3ffd90d251785ada52ec4b5c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 17:39:06 +0900 Subject: [PATCH 063/454] Don't select a new beatmap in load() --- osu.Game.Tests/Visual/TestCaseEditorCompose.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index 9f060a9ec4..d52f27f4ab 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -31,7 +31,6 @@ namespace osu.Game.Tests.Visual this.beatmaps = beatmaps; compose.Beatmap.BindTo(osuGame.Beatmap); - nextBeatmap(); } private void nextBeatmap() From efa39f38ca1fbd03ad880f010670111f6a7f52f2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 18:48:00 +0900 Subject: [PATCH 064/454] CI fixes --- .../Edit/OsuHitObjectComposer.cs | 3 ++- .../TestCaseEditorComposeRadioButtons.cs | 1 - osu.Game/Rulesets/Edit/HitObjectComposer.cs | 5 +---- .../Screens/Edit/Screens/Compose/Compose.cs | 11 +++++++--- .../RadioButtons/DrawableRadioButton.cs | 20 +++++++++---------- .../RadioButtons/RadioButtonCollection.cs | 1 - 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 851e572163..8a919e0178 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -2,12 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Edit { - public class OsuHitObjectComposer : Rulesets.Edit.HitObjectComposer + public class OsuHitObjectComposer : HitObjectComposer { public OsuHitObjectComposer(Ruleset ruleset) : base(ruleset) diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs index c259ca666e..8c2a07b536 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics; -using osu.Framework.Graphics.UserInterface; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; namespace osu.Game.Tests.Visual diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index ed057dbb03..9297c82ba4 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -6,17 +6,14 @@ using System.Collections.Generic; using System.Linq; using OpenTK.Graphics; using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; -using osu.Game.Screens.Play.ReplaySettings; namespace osu.Game.Rulesets.Edit { @@ -24,7 +21,7 @@ namespace osu.Game.Rulesets.Edit { private readonly Ruleset ruleset; - public HitObjectComposer(Ruleset ruleset) + protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 5eeaf3205d..6bc7356f26 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -1,14 +1,12 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose.Timeline; @@ -85,7 +83,14 @@ namespace osu.Game.Screens.Edit.Screens.Compose composerContainer.Clear(); var ruleset = newBeatmap.BeatmapInfo.Ruleset?.CreateInstance(); - var composer = ruleset?.CreateHitObjectComposer(); + if (ruleset == null) + { + Logger.Log("Beatmap doesn't have a ruleset assigned."); + // ExitRequested?.Invoke(); + return; + } + + var composer = ruleset.CreateHitObjectComposer(); if (composer == null) { Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index 690a50bb11..b3c9983db9 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -19,10 +19,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons { public class DrawableRadioButton : TriangleButton { - private static readonly Color4 DefaultBackgroundColour = OsuColour.FromHex("333"); - private static readonly Color4 DefaultBubbleColour = DefaultBackgroundColour.Darken(0.5f); - private static readonly Color4 SelectedBackgroundColour = OsuColour.FromHex("1188aa"); - private static readonly Color4 SelectedBubbleColour = SelectedBackgroundColour.Lighten(0.5f); + private static readonly Color4 default_background_colour = OsuColour.FromHex("333"); + private static readonly Color4 default_bubble_colour = default_background_colour.Darken(0.5f); + private static readonly Color4 selected_background_colour = OsuColour.FromHex("1188aa"); + private static readonly Color4 selected_bubble_colour = selected_background_colour.Lighten(0.5f); /// /// Invoked when this has been selected. @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons private void load() { Triangles.Alpha = 0; - BackgroundColour = DefaultBackgroundColour; + BackgroundColour = default_background_colour; Content.EdgeEffect = new EdgeEffectParameters { @@ -62,7 +62,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons Scale = new Vector2(0.5f), X = 10, Masking = true, - Colour = DefaultBubbleColour, + Colour = default_bubble_colour, Blending = BlendingMode.Additive, Child = new Box { RelativeSizeAxes = Axes.Both } }); @@ -76,8 +76,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons return; isSelected = false; - BackgroundColour = DefaultBackgroundColour; - bubble.Colour = DefaultBubbleColour; + BackgroundColour = default_background_colour; + bubble.Colour = default_bubble_colour; } public void Select() @@ -87,8 +87,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons isSelected = true; Selected?.Invoke(this); - BackgroundColour = SelectedBackgroundColour; - bubble.Colour = SelectedBubbleColour; + BackgroundColour = selected_background_colour; + bubble.Colour = selected_bubble_colour; } protected override bool OnClick(InputState state) diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs index 557e8e1ee8..8a3194b72e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using System.Linq; using OpenTK; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; From bf386598b6abe4be1758bf2b7045a26cb3208679 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Thu, 30 Nov 2017 10:58:32 +0100 Subject: [PATCH 065/454] Added a new "undelete" button that restores every beatmap with "DeletePending" set to true. --- osu.Game/Beatmaps/BeatmapManager.cs | 22 +++++++++++++++++++ .../Sections/Maintenance/GeneralSettings.cs | 14 ++++++++++++ 2 files changed, 36 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 0641cabcd8..376cbe183a 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -338,6 +338,28 @@ namespace osu.Game.Beatmaps } } + public void Undelete(BeatmapSetInfo beatmapSet) + { + lock (importContext) + { + var context = importContext.Value; + + using (var transaction = context.BeginTransaction()) + { + context.ChangeTracker.AutoDetectChangesEnabled = false; + + var iFiles = new FileStore(() => context, storage); + var iBeatmaps = createBeatmapStore(() => context); + + if (iBeatmaps.Undelete(beatmapSet)) + iFiles.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); + + context.ChangeTracker.AutoDetectChangesEnabled = true; + context.SaveChanges(transaction); + } + } + } + /// /// Delete a beatmap difficulty. /// diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 4f4f381ae1..dcad5ab52c 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -15,6 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private TriangleButton importButton; private TriangleButton deleteButton; private TriangleButton restoreButton; + private TriangleButton undeleteButton; protected override string Header => "General"; @@ -55,6 +56,19 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance }).ContinueWith(t => Schedule(() => restoreButton.Enabled.Value = true)); } }, + undeleteButton = new SettingsButton + { + Text = "Restore all recently deleted beatmaps", + Action = () => + { + undeleteButton.Enabled.Value = false; + Task.Run(() => + { + foreach (var bs in beatmaps.QueryBeatmapSets(bs => bs.DeletePending).ToList()) + beatmaps.Undelete(bs); + }).ContinueWith(t => Schedule(() => undeleteButton.Enabled.Value = true)); + } + }, }; } } From b09ba19d3fa45a3b650014f6f7ec057d788ca5d3 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Thu, 30 Nov 2017 11:02:53 +0100 Subject: [PATCH 066/454] Used the already-existing private method to undelete a mapset --- osu.Game/Beatmaps/BeatmapManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 376cbe183a..cfebaf083e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -351,8 +351,7 @@ namespace osu.Game.Beatmaps var iFiles = new FileStore(() => context, storage); var iBeatmaps = createBeatmapStore(() => context); - if (iBeatmaps.Undelete(beatmapSet)) - iFiles.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); + undelete(iBeatmaps, iFiles, beatmapSet); context.ChangeTracker.AutoDetectChangesEnabled = true; context.SaveChanges(transaction); From 677f3653eb04ed29b7c99f7c21079691ccc6ce67 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 19:19:34 +0900 Subject: [PATCH 067/454] Hide osu! playfield cursor --- .../Edit/OsuEditPlayfield.cs | 13 +++++++++++++ .../Edit/OsuEditRulesetContainer.cs | 19 +++++++++++++++++++ .../Edit/OsuHitObjectComposer.cs | 4 ++++ osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 8 +++++++- .../osu.Game.Rulesets.Osu.csproj | 2 ++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 5 ++++- 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs new file mode 100644 index 0000000000..d5fc1b606b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Cursor; +using osu.Game.Rulesets.Osu.UI; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuEditPlayfield : OsuPlayfield + { + protected override CursorContainer CreateCursor() => null; + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs new file mode 100644 index 0000000000..1e9e4b4686 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuEditRulesetContainer : OsuRulesetContainer + { + public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) + { + } + + protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 8a919e0178..ec3aa4661c 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -2,9 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Edit { @@ -15,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Edit { } + protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new OsuEditRulesetContainer(ruleset, beatmap, true); + protected override IReadOnlyList CompositionTools => new ICompositionTool[] { new HitObjectCompositionTool(), diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 89f6a4e255..387a098a5a 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -13,6 +13,7 @@ using System.Linq; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.UI.Cursor; +using osu.Framework.Graphics.Cursor; namespace osu.Game.Rulesets.Osu.UI { @@ -65,7 +66,10 @@ namespace osu.Game.Rulesets.Osu.UI protected override void LoadComplete() { base.LoadComplete(); - AddInternal(new GameplayCursor()); + + var cursor = CreateCursor(); + if (cursor != null) + AddInternal(cursor); } public override void Add(DrawableHitObject h) @@ -102,5 +106,7 @@ namespace osu.Game.Rulesets.Osu.UI judgementLayer.Add(explosion); } + + protected virtual CursorContainer CreateCursor() => new GameplayCursor(); } } diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index c527265c40..bc343ece05 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -49,6 +49,8 @@ + + diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 9297c82ba4..1e47ea4e63 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; @@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Edit RulesetContainer rulesetContainer; try { - rulesetContainer = ruleset.CreateRulesetContainerWith(osuGame.Beatmap.Value, true); + rulesetContainer = CreateRulesetContainer(ruleset, osuGame.Beatmap.Value); } catch (Exception e) { @@ -99,6 +100,8 @@ namespace osu.Game.Rulesets.Edit { } + protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); + protected abstract IReadOnlyList CompositionTools { get; } } } From e9cbef88f17021032a5cce26d6a616b4648c5a6b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 19:49:55 +0900 Subject: [PATCH 068/454] Improve selection/deselection behaviour of RadioButtonCollections --- .../TestCaseEditorComposeRadioButtons.cs | 10 ++- .../RadioButtons/DrawableRadioButton.cs | 72 ++++++++++--------- .../Compose/RadioButtons/RadioButton.cs | 31 ++++++-- .../RadioButtons/RadioButtonCollection.cs | 26 +++++-- 4 files changed, 93 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs index 8c2a07b536..f8669cde4b 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs @@ -14,7 +14,8 @@ namespace osu.Game.Tests.Visual public TestCaseEditorComposeRadioButtons() { - Add(new RadioButtonCollection + RadioButtonCollection collection; + Add(collection = new RadioButtonCollection { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -28,6 +29,13 @@ namespace osu.Game.Tests.Visual new RadioButton("Item 5", () => { }) } }); + + for (int i = 0; i < collection.Items.Count; i++) + { + int l = i; + AddStep($"Select item {l + 1}", () => collection.Items[l].Select()); + AddStep($"Deselect item {l + 1}", () => collection.Items[l].Deselect()); + } } } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index b3c9983db9..a6c0f48f1f 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -27,16 +27,34 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons /// /// Invoked when this has been selected. /// - public Action Selected; + public Action Selected; private Drawable bubble; + private readonly RadioButton button; + public DrawableRadioButton(RadioButton button) { + this.button = button; + Text = button.Text; Action = button.Action; RelativeSizeAxes = Axes.X; + + bubble = new CircularContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(0.5f), + X = 10, + Masking = true, + Colour = default_bubble_colour, + Blending = BlendingMode.Additive, + Child = new Box { RelativeSizeAxes = Axes.Both } + }; } [BackgroundDependencyLoader] @@ -53,53 +71,41 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons Colour = Color4.Black.Opacity(0.5f) }; - Add(bubble = new CircularContainer + Add(bubble); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + button.Selected.ValueChanged += v => { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Scale = new Vector2(0.5f), - X = 10, - Masking = true, - Colour = default_bubble_colour, - Blending = BlendingMode.Additive, - Child = new Box { RelativeSizeAxes = Axes.Both } - }); + updateSelectionState(); + if (v) + Selected?.Invoke(button); + }; + + updateSelectionState(); } - private bool isSelected; - - public void Deselect() + private void updateSelectionState() { - if (!isSelected) + if (!IsLoaded) return; - isSelected = false; - BackgroundColour = default_background_colour; - bubble.Colour = default_bubble_colour; - } - - public void Select() - { - if (isSelected) - return; - isSelected = true; - Selected?.Invoke(this); - - BackgroundColour = selected_background_colour; - bubble.Colour = selected_bubble_colour; + BackgroundColour = button.Selected ? selected_background_colour : default_background_colour; + bubble.Colour = button.Selected ? selected_bubble_colour : default_bubble_colour; } protected override bool OnClick(InputState state) { - if (isSelected) + if (button.Selected) return true; if (!Enabled) return true; - Select(); + button.Selected.Value = true; return base.OnClick(state); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs index bec2d1903d..055362d9e1 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs @@ -2,11 +2,18 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Configuration; namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons { - public struct RadioButton + public class RadioButton { + /// + /// Whether this is selected. + /// + /// + public readonly BindableBool Selected; + /// /// The text that should be displayed in this button. /// @@ -17,16 +24,28 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons /// public Action Action; + public RadioButton(string text, Action action) + { + Text = text; + Action = action; + Selected = new BindableBool(); + } + public RadioButton(string text) + : this(text, null) { Text = text; Action = null; } - public RadioButton(string text, Action action) - { - Text = text; - Action = action; - } + /// + /// Selects this . + /// + public void Select() => Selected.Value = true; + + /// + /// Deselects this . + /// + public void Deselect() => Selected.Value = false; } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs index 8a3194b72e..ad24a1da52 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -11,10 +11,16 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons { public class RadioButtonCollection : CompositeDrawable { + private IReadOnlyList items; public IReadOnlyList Items { + get { return items; } set { + if (items == value) + return; + items = value; + buttonContainer.Clear(); value.ForEach(addButton); } @@ -35,13 +41,21 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons }; } - private void addButton(RadioButton button) => buttonContainer.Add(new DrawableRadioButton(button) { Selected = buttonSelected }); - - private DrawableRadioButton currentlySelected; - private void buttonSelected(DrawableRadioButton drawableButton) + private RadioButton currentlySelected; + private void addButton(RadioButton button) { - currentlySelected?.Deselect(); - currentlySelected = drawableButton; + button.Selected.ValueChanged += v => + { + if (v) + { + currentlySelected?.Deselect(); + currentlySelected = button; + } + else + currentlySelected = null; + }; + + buttonContainer.Add(new DrawableRadioButton(button)); } } } From 7e34b0f08dcc504b81833487530111117eada6c9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 19:53:52 +0900 Subject: [PATCH 069/454] Remove SelectionTool, make Select the default tool --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 +++- osu.Game/Rulesets/Edit/Tools/SelectionTool.cs | 7 ------- 2 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/Tools/SelectionTool.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1e47ea4e63..b06af84ec8 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -89,11 +89,13 @@ namespace osu.Game.Rulesets.Edit rulesetContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)osuGame.Beatmap.Value.Track ?? new StopwatchClock()); toolboxCollection.Items = - new[] { new RadioButton("Select", () => setCompositionTool(new SelectionTool())) } + new[] { new RadioButton("Select", () => setCompositionTool(null)) } .Concat( CompositionTools.Select(t => new RadioButton(t.Name, () => setCompositionTool(t))) ) .ToList(); + + toolboxCollection.Items[0].Select(); } private void setCompositionTool(ICompositionTool tool) diff --git a/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs b/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs deleted file mode 100644 index 9f3ef78a02..0000000000 --- a/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace osu.Game.Rulesets.Edit.Tools -{ - public class SelectionTool : ICompositionTool - { - public string Name => "Select"; - } -} From 89772f4efd889836f60480ba3de93cd73598caf9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 19:54:58 +0900 Subject: [PATCH 070/454] A few resharper fixes --- .../Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs | 3 +-- .../Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs | 2 +- osu.Game/osu.Game.csproj | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index a6c0f48f1f..48cc7f3379 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -29,8 +29,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons /// public Action Selected; - private Drawable bubble; - + private readonly Drawable bubble; private readonly RadioButton button; public DrawableRadioButton(RadioButton button) diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs index ad24a1da52..08473eaeba 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons get { return items; } set { - if (items == value) + if (ReferenceEquals(items, value)) return; items = value; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 886d3313c6..e1425ff8b8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -564,7 +564,6 @@ - From e8cbde3ae18746e0b96f17e6ddea0114590085b0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 21:56:12 +0900 Subject: [PATCH 071/454] Add overlay/underlay --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 14 ++++++++---- osu.Game/Rulesets/Edit/PlayfieldOverlay.cs | 24 +++++++++++++++++++++ osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs | 21 ++++++++++++++++++ osu.Game/Rulesets/UI/RulesetContainer.cs | 10 ++++----- osu.Game/osu.Game.csproj | 2 ++ 5 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/PlayfieldOverlay.cs create mode 100644 osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index b06af84ec8..3aa633115b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Edit { private readonly Ruleset ruleset; + protected ICompositionTool CurrentTool { get; private set; } + protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; @@ -75,7 +77,9 @@ namespace osu.Game.Rulesets.Edit Alpha = 0, AlwaysPresent = true, }, - rulesetContainer + CreateUnderlay(rulesetContainer.Playfield), + rulesetContainer, + CreateOverlay(rulesetContainer.Playfield) } } }, @@ -98,12 +102,14 @@ namespace osu.Game.Rulesets.Edit toolboxCollection.Items[0].Select(); } - private void setCompositionTool(ICompositionTool tool) - { - } + private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); + protected virtual PlayfieldUnderlay CreateUnderlay(Playfield playfield) => new PlayfieldUnderlay(playfield); + + 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 new file mode 100644 index 0000000000..2138079784 --- /dev/null +++ b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Edit +{ + public class PlayfieldOverlay : CompositeDrawable + { + + private Playfield playfield; + + public PlayfieldOverlay(Playfield playfield) + { + this.playfield = playfield; + + RelativeSizeAxes = Axes.Both; + + } + + } +} diff --git a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs new file mode 100644 index 0000000000..7a18c59613 --- /dev/null +++ b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Edit +{ + public class PlayfieldUnderlay : CompositeDrawable + { + private readonly Playfield playfield; + + public PlayfieldUnderlay(Playfield playfield) + { + this.playfield = playfield; + + RelativeSizeAxes = Axes.Both; + } + } +} diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index ec26f6f310..69bf6bba29 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -55,6 +55,11 @@ namespace osu.Game.Rulesets.UI public abstract IEnumerable Objects { get; } + /// + /// The playfield. + /// + public Playfield Playfield { get; protected set; } + protected readonly Ruleset Ruleset; /// @@ -135,11 +140,6 @@ namespace osu.Game.Rulesets.UI public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this); - /// - /// The playfield. - /// - public Playfield Playfield { get; private set; } - protected override Container Content => content; private Container content; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e1425ff8b8..ad1370890f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -302,6 +302,8 @@ + + From c0c051aa32166fc378e7d13051ecfd932cc04cc7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 21:58:41 +0900 Subject: [PATCH 072/454] Remove unused parameter for now --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 ++-- osu.Game/Rulesets/Edit/PlayfieldOverlay.cs | 9 ++------- osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs | 7 +------ 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 3aa633115b..f55b7b0531 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -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(playfield); + protected virtual PlayfieldUnderlay CreateUnderlay(Playfield playfield) => new PlayfieldUnderlay(); - protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(playfield); + protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(); protected abstract IReadOnlyList CompositionTools { get; } } diff --git a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs index 2138079784..4f5658e39c 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs +++ b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs @@ -3,22 +3,17 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Edit { public class PlayfieldOverlay : CompositeDrawable { - - private Playfield playfield; - - public PlayfieldOverlay(Playfield playfield) + public PlayfieldOverlay() { - this.playfield = playfield; - RelativeSizeAxes = Axes.Both; } + } } diff --git a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs index 7a18c59613..e9198c71c6 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs +++ b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs @@ -3,18 +3,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Edit { public class PlayfieldUnderlay : CompositeDrawable { - private readonly Playfield playfield; - - public PlayfieldUnderlay(Playfield playfield) + public PlayfieldUnderlay() { - this.playfield = playfield; - RelativeSizeAxes = Axes.Both; } } From 52ba68e25dd1fb873608d548507b7f4cd7945445 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 22:21:02 +0900 Subject: [PATCH 073/454] Add/fix up license headers --- osu.Game/Rulesets/Edit/PlayfieldOverlay.cs | 2 +- osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs | 2 +- osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs index 4f5658e39c..58b15e3de2 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs +++ b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs index e9198c71c6..bace5258f8 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs +++ b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs index 914cbd11ca..dd182dcbdb 100644 --- a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs +++ b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Edit.Tools From 016057ab014954b09c5510ed9ae2142cf2f1d6f3 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 30 Nov 2017 16:49:53 +0100 Subject: [PATCH 074/454] 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 075/454] 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 076/454] 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 077/454] 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 76a1c7db3b68318229daca40cd06385088e6ca62 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 21:22:57 +0900 Subject: [PATCH 078/454] Hyperdash preparation --- .../Beatmaps/CatchBeatmapProcessor.cs | 8 +++++ .../Tests/TestCaseFruit.cs | 36 +++++++++++++++++++ .../Tests/TestCaseHyperdash.cs | 26 ++++++++++++++ .../osu.Game.Rulesets.Catch.csproj | 2 ++ 4 files changed, 72 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs create mode 100644 osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index b2f7fdabfc..8a19afcd39 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.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 osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; @@ -18,6 +19,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps CatchHitObject lastObj = null; + convertHyperDash(beatmap.HitObjects); + foreach (var obj in beatmap.HitObjects) { if (obj.NewCombo) @@ -34,5 +37,10 @@ namespace osu.Game.Rulesets.Catch.Beatmaps lastObj = obj; } } + + private void convertHyperDash(List objects) + { + + } } } diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs new file mode 100644 index 0000000000..0ced95edf9 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs @@ -0,0 +1,36 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Tests.Visual; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class TestCaseFruit : OsuTestCase + { + public TestCaseFruit() + { + Add(new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, + new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, + }, + new Drawable[] + { + new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, + new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, + } + } + }); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs new file mode 100644 index 0000000000..822743debc --- /dev/null +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -0,0 +1,26 @@ +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + [Ignore("getting CI working")] + public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer + { + public TestCaseHyperdash() + : base(typeof(CatchRuleset)) + { + } + + protected override Beatmap CreateBeatmap() + { + var beatmap = new Beatmap(); + + for (int i = 0; i < 512; i++) + beatmap.HitObjects.Add(new Fruit { X = 0.5f + (i % 2 == 0 ? -0.4f : 0.4f), StartTime = i * 100, NewCombo = i % 8 == 0 }); + + return beatmap; + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index b7916f674e..4e819fb58b 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -67,6 +67,8 @@ + + From f6591851c3a6f7ac3d198e940a813019046fbe55 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 22:01:49 +0900 Subject: [PATCH 079/454] 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 881745d7561bb25a67c03978a33ee214b09fbc20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 15:08:12 +0900 Subject: [PATCH 080/454] Initial implementation of hyperdash calculation --- .../Beatmaps/CatchBeatmapProcessor.cs | 43 +++++++++++++++ .../Objects/CatchHitObject.cs | 2 + .../Objects/Drawable/DrawableFruit.cs | 15 ++++++ .../Tests/TestCaseHyperdash.cs | 52 +++++++++---------- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- 5 files changed, 87 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 8a19afcd39..d225cdca55 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -1,9 +1,13 @@ // 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.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects.Types; +using OpenTK; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -40,7 +44,46 @@ namespace osu.Game.Rulesets.Catch.Beatmaps private void convertHyperDash(List objects) { + // todo: add difficulty adjust. + const double catcher_width = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH; + const double catcher_width_half = catcher_width / 2; + int lastDirection = 0; + double lastExcess = catcher_width_half; + + int objCount = objects.Count; + + for (int i = 0; i < objCount - 1; i++) + { + CatchHitObject currentObject = objects[i]; + + // not needed? + if (currentObject is TinyDroplet) continue; + + CatchHitObject nextObject = objects[i + 1]; + while (nextObject is TinyDroplet) + { + if (++i == objCount - 1) break; + nextObject = objects[i + 1]; + } + + int thisDirection = nextObject.X > currentObject.X ? 1 : -1; + double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4; + double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : catcher_width_half); + + if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) + { + currentObject.HyperDash = true; + lastExcess = catcher_width_half; + } + else + { + //currentObject.DistanceToHyperDash = timeToNext - distanceToNext; + lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, catcher_width_half); + } + + lastDirection = thisDirection; + } } } } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index cb4e6453ce..bae2edd055 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Catch.Objects public float Scale { get; set; } = 1; + public bool HyperDash { get; set; } + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { base.ApplyDefaults(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index 4c28a9d021..9f46bbd3a4 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawable { @@ -70,6 +71,20 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable } } }; + + if (HitObject.HyperDash) + { + Add(new Pulp + { + RelativePositionAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AccentColour = Color4.Red, + Blending = BlendingMode.Additive, + Alpha = 0.5f, + Scale = new Vector2(2) + }); + } } } } diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs index 822743debc..5b0e1fb93c 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -1,26 +1,26 @@ -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; - -namespace osu.Game.Rulesets.Catch.Tests -{ - [TestFixture] - [Ignore("getting CI working")] - public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer - { - public TestCaseHyperdash() - : base(typeof(CatchRuleset)) - { - } - - protected override Beatmap CreateBeatmap() - { - var beatmap = new Beatmap(); - - for (int i = 0; i < 512; i++) - beatmap.HitObjects.Add(new Fruit { X = 0.5f + (i % 2 == 0 ? -0.4f : 0.4f), StartTime = i * 100, NewCombo = i % 8 == 0 }); - - return beatmap; - } - } -} \ No newline at end of file +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + [Ignore("getting CI working")] + public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer + { + public TestCaseHyperdash() + : base(typeof(CatchRuleset)) + { + } + + protected override Beatmap CreateBeatmap() + { + var beatmap = new Beatmap(); + + for (int i = 0; i < 512; i++) + beatmap.HitObjects.Add(new Fruit { X = 0.5f + (i % 8 < 4 ? -0.4f : 0.4f), StartTime = i * 100, NewCombo = i % 8 == 0 }); + + return beatmap; + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 6fd0793500..3cfdcafc48 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.UI { public class CatchPlayfield : ScrollingPlayfield { - public static readonly float BASE_WIDTH = 512; + public const float BASE_WIDTH = 512; protected override Container Content => content; private readonly Container content; From 51cae24a26933ce468b858ae91dd13382039b837 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 19:24:48 +0900 Subject: [PATCH 081/454] Add basic hyperdash movement Doesn't restrict direction yet. Also improves readability of fruit catch detection. --- .../Beatmaps/CatchBeatmapProcessor.cs | 2 +- .../Objects/CatchHitObject.cs | 10 +- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 104 ++++++++++++++++-- 4 files changed, 108 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index d225cdca55..e12d7f3fd3 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) { - currentObject.HyperDash = true; + currentObject.HyperDashTarget = nextObject; lastExcess = catcher_width_half; } else diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index bae2edd055..38757d4928 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -27,7 +27,15 @@ namespace osu.Game.Rulesets.Catch.Objects public float Scale { get; set; } = 1; - public bool HyperDash { get; set; } + /// + /// Whether this fruit can initiate a hyperdash. + /// + public bool HyperDash => HyperDashTarget != null; + + /// + /// The target fruit if we are to initiate a hyperdash. + /// + public CatchHitObject HyperDashTarget; public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 3cfdcafc48..76dbfa77c6 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.UI }; } - public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.CanCatch(obj); + public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj); public override void Add(DrawableHitObject h) { diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 203db1bb8c..2785647cbd 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.UI { @@ -51,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.UI catcher.Add(fruit); } - public bool CanCatch(CatchHitObject obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X * Math.Abs(catcher.Scale.X) / DrawSize.X / 2; + public bool AttemptCatch(CatchHitObject obj) => catcher.AttemptCatch(obj); public class Catcher : Container, IKeyBindingHandler { @@ -105,14 +106,35 @@ namespace osu.Game.Rulesets.Catch.UI dashing = value; - if (dashing) - Schedule(addAdditiveSprite); + Trail |= dashing; } } - private void addAdditiveSprite() + private bool trail; + + /// + /// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met. + /// + protected bool Trail { - if (!dashing || AdditiveTarget == null) return; + get { return trail; } + set + { + if (value == trail) return; + + trail = value; + + if (Trail) + beginTrail(); + } + } + + private void beginTrail() + { + Trail &= dashing || HyperDashing; + Trail &= AdditiveTarget != null; + + if (!Trail) return; var additive = createCatcherSprite(); @@ -120,6 +142,7 @@ namespace osu.Game.Rulesets.Catch.UI additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly. additive.Position = Position; additive.Scale = Scale; + additive.Colour = HyperDashing ? Color4.Red : Color4.White; additive.RelativePositionAxes = RelativePositionAxes; additive.Blending = BlendingMode.Additive; @@ -127,7 +150,7 @@ namespace osu.Game.Rulesets.Catch.UI additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire(); - Scheduler.AddDelayed(addAdditiveSprite, 50); + Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50); } private Sprite createCatcherSprite() => new Sprite @@ -138,6 +161,10 @@ namespace osu.Game.Rulesets.Catch.UI OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly. }; + /// + /// Add a caught fruit to the catcher's stack. + /// + /// The fruit that was caught. public void Add(DrawableHitObject fruit) { float distance = fruit.DrawSize.X / 2 * fruit.Scale.X; @@ -150,8 +177,35 @@ namespace osu.Game.Rulesets.Catch.UI caughtFruit.Add(fruit); - if (((CatchHitObject)fruit.HitObject).LastInCombo) + var catchObject = (CatchHitObject)fruit.HitObject; + + if (catchObject.LastInCombo) explode(); + + updateHyperDashState(catchObject, true); + } + + /// + /// Let the catcher attempt to catch a fruit. + /// + /// The fruit to catch. + /// Whether the catch is possible. + public bool AttemptCatch(CatchHitObject fruit) + { + const double relative_catcher_width = CATCHER_SIZE / 2; + + // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. + var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH; + var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH; + + var validCatch = + catchObjectPosition >= catcherPosition - relative_catcher_width / 2 && + catchObjectPosition <= catcherPosition + relative_catcher_width / 2; + + // if we are hypderdashing in teh next hit is not, let's change our state here (it's our only opportunity to handle missed fruit currently). + updateHyperDashState(fruit, false); + + return validCatch; } public bool OnPressed(CatchAction action) @@ -203,10 +257,46 @@ namespace osu.Game.Rulesets.Catch.UI double dashModifier = Dashing ? 1 : 0.5; + if (hyperDashModifier != 1) + dashModifier = hyperDashModifier; + Scale = new Vector2(Math.Abs(Scale.X) * Math.Sign(currentDirection), Scale.Y); X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); } + /// + /// Whether we are hypderdashing or not. + /// + protected bool HyperDashing => hyperDashModifier != 1; + + private double hyperDashModifier = 1; + + /// + /// Update whether we are hyper or not. + /// + /// The fruit to use as a condition for deciding our new state. + /// Whether to allow entering hyperdash or not. If false, we will only exit if required, but never enter. + private void updateHyperDashState(CatchHitObject fruit, bool allowBegin) + { + const float transition_length = 180; + + if (!fruit.HyperDash) + { + hyperDashModifier = 1; + this.FadeColour(Color4.White, transition_length, Easing.OutQuint); + this.FadeTo(1, transition_length, Easing.OutQuint); + return; + } + + if (allowBegin) + { + hyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED; + this.FadeColour(Color4.AliceBlue, transition_length, Easing.OutQuint); + this.FadeTo(0.5f, transition_length, Easing.OutQuint); + Trail = true; + } + } + private void explode() { var fruit = caughtFruit.ToArray(); From 07081f400cfcc68846d64f268880d5994b1763cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 19:33:20 +0900 Subject: [PATCH 082/454] Make hyperdash testcase easier to win --- osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs index 5b0e1fb93c..a39c727a61 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Tests var beatmap = new Beatmap(); for (int i = 0; i < 512; i++) - beatmap.HitObjects.Add(new Fruit { X = 0.5f + (i % 8 < 4 ? -0.4f : 0.4f), StartTime = i * 100, NewCombo = i % 8 == 0 }); + beatmap.HitObjects.Add(new Fruit { X = i % 8 < 4 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); return beatmap; } From 445bb70ef5b2d70a9c1552d72d4b5439209a3d4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 19:58:00 +0900 Subject: [PATCH 083/454] Add hyperdash visual testing to TestCaseCatcherArea Also tidies up hyperdash state logic --- .../Tests/TestCaseCatcherArea.cs | 14 +++- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 84 +++++++++---------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs index 538f6930ed..daa3e12800 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs @@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Catch.Tests internal class TestCaseCatcherArea : OsuTestCase { private RulesetInfo catchRuleset; + private TestCatcherArea catcherArea; public override IReadOnlyList RequiredTypes => new[] { @@ -26,6 +27,7 @@ namespace osu.Game.Rulesets.Catch.Tests public TestCaseCatcherArea() { AddSliderStep("CircleSize", 0, 8, 5, createCatcher); + AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t)); } private void createCatcher(float size) @@ -33,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Tests Child = new CatchInputManager(catchRuleset) { RelativeSizeAxes = Axes.Both, - Child = new CatcherArea(new BeatmapDifficulty { CircleSize = size }) + Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size }) { Anchor = Anchor.CentreLeft, Origin = Anchor.BottomLeft @@ -46,5 +48,15 @@ namespace osu.Game.Rulesets.Catch.Tests { catchRuleset = rulesets.GetRuleset(2); } + + private class TestCatcherArea : CatcherArea + { + public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) + : base(beatmapDifficulty) + { + } + + public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1; + } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 2785647cbd..52f06d4396 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -22,18 +22,18 @@ namespace osu.Game.Rulesets.Catch.UI { public const float CATCHER_SIZE = 172; - private readonly Catcher catcher; + protected readonly Catcher MovableCatcher; public Container ExplodingFruitTarget { - set { catcher.ExplodingFruitTarget = value; } + set { MovableCatcher.ExplodingFruitTarget = value; } } public CatcherArea(BeatmapDifficulty difficulty = null) { RelativeSizeAxes = Axes.X; Height = CATCHER_SIZE; - Child = catcher = new Catcher(difficulty) + Child = MovableCatcher = new Catcher(difficulty) { AdditiveTarget = this, }; @@ -42,17 +42,17 @@ namespace osu.Game.Rulesets.Catch.UI public void Add(DrawableHitObject fruit, Vector2 absolutePosition) { fruit.RelativePositionAxes = Axes.None; - fruit.Position = new Vector2(catcher.ToLocalSpace(absolutePosition).X - catcher.DrawSize.X / 2, 0); + fruit.Position = new Vector2(MovableCatcher.ToLocalSpace(absolutePosition).X - MovableCatcher.DrawSize.X / 2, 0); fruit.Anchor = Anchor.TopCentre; fruit.Origin = Anchor.BottomCentre; fruit.Scale *= 0.7f; fruit.LifetimeEnd = double.MaxValue; - catcher.Add(fruit); + MovableCatcher.Add(fruit); } - public bool AttemptCatch(CatchHitObject obj) => catcher.AttemptCatch(obj); + public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); public class Catcher : Container, IKeyBindingHandler { @@ -181,8 +181,6 @@ namespace osu.Game.Rulesets.Catch.UI if (catchObject.LastInCombo) explode(); - - updateHyperDashState(catchObject, true); } /// @@ -202,12 +200,45 @@ namespace osu.Game.Rulesets.Catch.UI catchObjectPosition >= catcherPosition - relative_catcher_width / 2 && catchObjectPosition <= catcherPosition + relative_catcher_width / 2; - // if we are hypderdashing in teh next hit is not, let's change our state here (it's our only opportunity to handle missed fruit currently). - updateHyperDashState(fruit, false); + if (validCatch && fruit.HyperDash) + HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED; + else + HyperDashModifier = 1; return validCatch; } + /// + /// Whether we are hypderdashing or not. + /// + public bool HyperDashing => hyperDashModifier != 1; + + private double hyperDashModifier = 1; + + public double HyperDashModifier + { + get { return hyperDashModifier; } + set + { + if (value == hyperDashModifier) return; + hyperDashModifier = value; + + const float transition_length = 180; + + if (HyperDashing) + { + this.FadeColour(Color4.Yellow, transition_length, Easing.OutQuint); + this.FadeTo(0.2f, transition_length, Easing.OutQuint); + Trail = true; + } + else + { + this.FadeColour(Color4.White, transition_length, Easing.OutQuint); + this.FadeTo(1, transition_length, Easing.OutQuint); + } + } + } + public bool OnPressed(CatchAction action) { switch (action) @@ -264,39 +295,6 @@ namespace osu.Game.Rulesets.Catch.UI X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); } - /// - /// Whether we are hypderdashing or not. - /// - protected bool HyperDashing => hyperDashModifier != 1; - - private double hyperDashModifier = 1; - - /// - /// Update whether we are hyper or not. - /// - /// The fruit to use as a condition for deciding our new state. - /// Whether to allow entering hyperdash or not. If false, we will only exit if required, but never enter. - private void updateHyperDashState(CatchHitObject fruit, bool allowBegin) - { - const float transition_length = 180; - - if (!fruit.HyperDash) - { - hyperDashModifier = 1; - this.FadeColour(Color4.White, transition_length, Easing.OutQuint); - this.FadeTo(1, transition_length, Easing.OutQuint); - return; - } - - if (allowBegin) - { - hyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED; - this.FadeColour(Color4.AliceBlue, transition_length, Easing.OutQuint); - this.FadeTo(0.5f, transition_length, Easing.OutQuint); - Trail = true; - } - } - private void explode() { var fruit = caughtFruit.ToArray(); From 25207c51b5ed04bf50c38f94527d4448d41506aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:07:28 +0900 Subject: [PATCH 084/454] Add directionality --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 52f06d4396..265f19c744 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -201,7 +201,10 @@ namespace osu.Game.Rulesets.Catch.UI catchObjectPosition <= catcherPosition + relative_catcher_width / 2; if (validCatch && fruit.HyperDash) + { HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED; + HyperDashDirection = fruit.HyperDashTarget.X - fruit.X; + } else HyperDashModifier = 1; @@ -215,6 +218,8 @@ namespace osu.Game.Rulesets.Catch.UI private double hyperDashModifier = 1; + public double HyperDashDirection; + public double HyperDashModifier { get { return hyperDashModifier; } @@ -233,6 +238,7 @@ namespace osu.Game.Rulesets.Catch.UI } else { + HyperDashDirection = 0; this.FadeColour(Color4.White, transition_length, Easing.OutQuint); this.FadeTo(1, transition_length, Easing.OutQuint); } @@ -286,13 +292,15 @@ namespace osu.Game.Rulesets.Catch.UI if (currentDirection == 0) return; + var direction = Math.Sign(currentDirection); + double dashModifier = Dashing ? 1 : 0.5; - if (hyperDashModifier != 1) + if (hyperDashModifier != 1 && (HyperDashDirection == 0 || direction == Math.Sign(HyperDashDirection))) dashModifier = hyperDashModifier; - Scale = new Vector2(Math.Abs(Scale.X) * Math.Sign(currentDirection), Scale.Y); - X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); + Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y); + X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); } private void explode() From 273793f1852d5ee0792c39bcbb1414f5806c428e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:08:49 +0900 Subject: [PATCH 085/454] Add comments --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 265f19c744..984aaf98ee 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -218,8 +218,14 @@ namespace osu.Game.Rulesets.Catch.UI private double hyperDashModifier = 1; + /// + /// The direction in which hyperdash is allowed. 0 allows both directions. + /// public double HyperDashDirection; + /// + /// The speed modifier resultant from hyperdash. Will trigger hyperdash when not equal to 1. + /// public double HyperDashModifier { get { return hyperDashModifier; } From bf606522c1c3cd33ec90d70d1290fbfd5114d3aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:13:32 +0900 Subject: [PATCH 086/454] Make hyperdash testcase easier to win again --- osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs index a39c727a61..a0eb5f0054 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -18,7 +18,8 @@ namespace osu.Game.Rulesets.Catch.Tests var beatmap = new Beatmap(); for (int i = 0; i < 512; i++) - beatmap.HitObjects.Add(new Fruit { X = i % 8 < 4 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); + if (i % 5 < 3) + beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); return beatmap; } From e75d73ac1cb8b86ee16d679a59b80e8b98999a2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:13:46 +0900 Subject: [PATCH 087/454] Change hyperdash colour again --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 984aaf98ee..2bb0f3cd18 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Catch.UI if (HyperDashing) { - this.FadeColour(Color4.Yellow, transition_length, Easing.OutQuint); + this.FadeColour(Color4.OrangeRed, transition_length, Easing.OutQuint); this.FadeTo(0.2f, transition_length, Easing.OutQuint); Trail = true; } From 79e1bf3394f696df2c88742bb0943d3b01051125 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:14:42 +0900 Subject: [PATCH 088/454] Remove unused testcase --- .../Tests/TestCaseFruit.cs | 36 ------------------- .../osu.Game.Rulesets.Catch.csproj | 1 - 2 files changed, 37 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs deleted file mode 100644 index 0ced95edf9..0000000000 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Objects.Drawable; -using osu.Game.Tests.Visual; -using OpenTK; - -namespace osu.Game.Rulesets.Catch.Tests -{ - public class TestCaseFruit : OsuTestCase - { - public TestCaseFruit() - { - Add(new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, - new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, - }, - new Drawable[] - { - new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, - new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, - } - } - }); - } - } -} diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 4e819fb58b..264f5c41be 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -67,7 +67,6 @@ - From 997cdfaee42ebb67e15bc0bcbe1162c89b0bbe6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:31:54 +0900 Subject: [PATCH 089/454] Add missing licence header --- osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs index a0eb5f0054..ce3f79bae2 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -1,4 +1,7 @@ -using NUnit.Framework; +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; From 8c3ae9430b53273b95f258279560b6396d9a7b41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:39:58 +0900 Subject: [PATCH 090/454] Add difficulty scaling considerations to hyperdash initialisation --- .../Beatmaps/CatchBeatmapProcessor.cs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index e12d7f3fd3..9901dbde18 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; @@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps CatchHitObject lastObj = null; - convertHyperDash(beatmap.HitObjects); + initialiseHyperDash(beatmap.HitObjects); foreach (var obj in beatmap.HitObjects) { @@ -42,14 +43,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } } - private void convertHyperDash(List objects) + private void initialiseHyperDash(List objects) { // todo: add difficulty adjust. - const double catcher_width = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH; - const double catcher_width_half = catcher_width / 2; + double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2; int lastDirection = 0; - double lastExcess = catcher_width_half; + double lastExcess = halfCatcherWidth; int objCount = objects.Count; @@ -58,28 +58,29 @@ namespace osu.Game.Rulesets.Catch.Beatmaps CatchHitObject currentObject = objects[i]; // not needed? - if (currentObject is TinyDroplet) continue; + // if (currentObject is TinyDroplet) continue; CatchHitObject nextObject = objects[i + 1]; - while (nextObject is TinyDroplet) - { - if (++i == objCount - 1) break; - nextObject = objects[i + 1]; - } + + // while (nextObject is TinyDroplet) + // { + // if (++i == objCount - 1) break; + // nextObject = objects[i + 1]; + // } int thisDirection = nextObject.X > currentObject.X ? 1 : -1; double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4; - double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : catcher_width_half); + double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) { currentObject.HyperDashTarget = nextObject; - lastExcess = catcher_width_half; + lastExcess = halfCatcherWidth; } else { //currentObject.DistanceToHyperDash = timeToNext - distanceToNext; - lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, catcher_width_half); + lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth); } lastDirection = thisDirection; From 75327959356327ec3599a4348c0b3099b9e625ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 22:43:49 +0900 Subject: [PATCH 091/454] Lock during validity checks --- osu.Game/Beatmaps/WorkingBeatmap.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 93ba51367a..8c96074352 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -121,6 +121,8 @@ namespace osu.Game.Beatmaps private readonly Func valueFactory; private readonly Func stillValidFunction; + private readonly object initLock = new object(); + public AsyncLazy(Func valueFactory, Func stillValidFunction = null) { this.valueFactory = valueFactory; @@ -134,7 +136,6 @@ namespace osu.Game.Beatmaps if (!IsValueCreated) return; (lazy.Value.Result as IDisposable)?.Dispose(); - init(); } @@ -158,9 +159,11 @@ namespace osu.Game.Beatmaps private void ensureValid() { - if (!lazy.IsValueCreated || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; - - init(); + lock (initLock) + { + if (!lazy.IsValueCreated || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; + init(); + } } private void init() From ee75f90ab33545e51ab8a919b88a3a000eb60ae5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 22:44:25 +0900 Subject: [PATCH 092/454] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 87d68cda00..cc013fc406 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 87d68cda0015d51dc3da56d2322fa10d399fc4ed +Subproject commit cc013fc4063dda0843f38c1c73568a413abcf229 From cf859a6cf2449896e38e5acd431176e2d20d8192 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 00:26:02 +0900 Subject: [PATCH 093/454] 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 094/454] 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 095/454] 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 de94082b1e142174f8d2275c3369b990df0f3ca3 Mon Sep 17 00:00:00 2001 From: gtensha Date: Fri, 1 Dec 2017 18:10:39 +0100 Subject: [PATCH 096/454] Fix filename casing to compile on Linux --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f814cbb3d3..8b6bdefc6c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -280,7 +280,7 @@ 20171025071459_AddMissingIndexRules.cs - + 20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs From a49f3479a2dcc111a821a6891814553443526ec7 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 19:11:52 +0100 Subject: [PATCH 097/454] 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 098/454] 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 099/454] 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 5a7c4772bd57453466f9cc2f3599f54b7882ce22 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 19:53:11 +0900 Subject: [PATCH 100/454] Remove PlayfieldUnderlay for now --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 3 --- osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs | 16 ---------------- osu.Game/osu.Game.csproj | 1 - 3 files changed, 20 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index f55b7b0531..767e8ade46 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -77,7 +77,6 @@ namespace osu.Game.Rulesets.Edit Alpha = 0, AlwaysPresent = true, }, - CreateUnderlay(rulesetContainer.Playfield), rulesetContainer, CreateOverlay(rulesetContainer.Playfield) } @@ -106,8 +105,6 @@ 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 PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(); protected abstract IReadOnlyList CompositionTools { get; } diff --git a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs deleted file mode 100644 index bace5258f8..0000000000 --- a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs +++ /dev/null @@ -1,16 +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; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Rulesets.Edit -{ - public class PlayfieldUnderlay : CompositeDrawable - { - public PlayfieldUnderlay() - { - RelativeSizeAxes = Axes.Both; - } - } -} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ad1370890f..ee5834890f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -303,7 +303,6 @@ - From a80059032cdc94a37a83b78fd86b4466be095c61 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 20:24:10 +0900 Subject: [PATCH 101/454] Remove PlayfieldOverlay --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 5 +---- osu.Game/Rulesets/Edit/PlayfieldOverlay.cs | 19 ------------------- osu.Game/osu.Game.csproj | 1 - 3 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/PlayfieldOverlay.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 767e8ade46..3184b84e98 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -77,8 +77,7 @@ namespace osu.Game.Rulesets.Edit Alpha = 0, AlwaysPresent = true, }, - rulesetContainer, - CreateOverlay(rulesetContainer.Playfield) + rulesetContainer } } }, @@ -105,8 +104,6 @@ namespace osu.Game.Rulesets.Edit protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); - protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(); - protected abstract IReadOnlyList CompositionTools { get; } } } diff --git a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs deleted file mode 100644 index 58b15e3de2..0000000000 --- a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs +++ /dev/null @@ -1,19 +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; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Rulesets.Edit -{ - public class PlayfieldOverlay : CompositeDrawable - { - public PlayfieldOverlay() - { - RelativeSizeAxes = Axes.Both; - - } - - - } -} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ee5834890f..e1425ff8b8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -302,7 +302,6 @@ - From c14d68e685143e00f8d37a2c1bc3d4f9d883378e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 20:30:18 +0900 Subject: [PATCH 102/454] Use OsuColour for radio buttons --- .../RadioButtons/DrawableRadioButton.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index 48cc7f3379..10b6c07f3d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -19,16 +19,16 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons { public class DrawableRadioButton : TriangleButton { - private static readonly Color4 default_background_colour = OsuColour.FromHex("333"); - private static readonly Color4 default_bubble_colour = default_background_colour.Darken(0.5f); - private static readonly Color4 selected_background_colour = OsuColour.FromHex("1188aa"); - private static readonly Color4 selected_bubble_colour = selected_background_colour.Lighten(0.5f); - /// /// Invoked when this has been selected. /// public Action Selected; + private Color4 defaultBackgroundColour; + private Color4 defaultBubbleColour; + private Color4 selectedBackgroundColour; + private Color4 selectedBubbleColour; + private readonly Drawable bubble; private readonly RadioButton button; @@ -50,17 +50,20 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons Scale = new Vector2(0.5f), X = 10, Masking = true, - Colour = default_bubble_colour, Blending = BlendingMode.Additive, Child = new Box { RelativeSizeAxes = Axes.Both } }; } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { + defaultBackgroundColour = colours.Gray3; + defaultBubbleColour = defaultBackgroundColour.Darken(0.5f); + selectedBackgroundColour = colours.BlueDark; + selectedBubbleColour = selectedBackgroundColour.Lighten(0.5f); + Triangles.Alpha = 0; - BackgroundColour = default_background_colour; Content.EdgeEffect = new EdgeEffectParameters { @@ -92,8 +95,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons if (!IsLoaded) return; - BackgroundColour = button.Selected ? selected_background_colour : default_background_colour; - bubble.Colour = button.Selected ? selected_bubble_colour : default_bubble_colour; + BackgroundColour = button.Selected ? selectedBackgroundColour : defaultBackgroundColour; + bubble.Colour = button.Selected ? selectedBubbleColour : defaultBubbleColour; } protected override bool OnClick(InputState state) From cd20d6df160b1b4617b4f8035dc31ee52063588f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 20:31:10 +0900 Subject: [PATCH 103/454] Apply suggested changes --- osu.Game/Screens/Edit/Editor.cs | 1 - .../Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs | 2 +- osu.Game/Screens/Edit/Screens/EditorScreen.cs | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index bc86c683c7..607ff792d8 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -154,7 +154,6 @@ namespace osu.Game.Screens.Edit } currentScreen.Beatmap.BindTo(Beatmap); - currentScreen.ExitRequested = Exit; screenContainer.Add(currentScreen); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs index 08473eaeba..5f1def4a2e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons items = value; buttonContainer.Clear(); - value.ForEach(addButton); + items.ForEach(addButton); } } diff --git a/osu.Game/Screens/Edit/Screens/EditorScreen.cs b/osu.Game/Screens/Edit/Screens/EditorScreen.cs index 9a158d20f1..773975d493 100644 --- a/osu.Game/Screens/Edit/Screens/EditorScreen.cs +++ b/osu.Game/Screens/Edit/Screens/EditorScreen.cs @@ -11,8 +11,6 @@ namespace osu.Game.Screens.Edit.Screens { public class EditorScreen : Container { - public Action ExitRequested; - public readonly Bindable Beatmap = new Bindable(); protected override Container Content => content; From 2852337b04c0160a6b52e5376acdf73e09af4fd8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 20:35:47 +0900 Subject: [PATCH 104/454] Remove unused using --- osu.Game/Screens/Edit/Screens/EditorScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/EditorScreen.cs b/osu.Game/Screens/Edit/Screens/EditorScreen.cs index 773975d493..ac248930d8 100644 --- a/osu.Game/Screens/Edit/Screens/EditorScreen.cs +++ b/osu.Game/Screens/Edit/Screens/EditorScreen.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; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; From 2bbfe0dda1bd886791dc628594822060820ec67b Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sat, 2 Dec 2017 15:28:00 +0100 Subject: [PATCH 105/454] 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 106/454] 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 107/454] 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 a1dbd7916b8a890e8a241b0d43f55aed39a1cd2a Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 3 Dec 2017 14:03:43 +0100 Subject: [PATCH 108/454] fixes MusicController constantly trying to start a track In Detail: It tried to start a track if the current one ended even if no BeatmapSets were present. Also if only one BeatmapSet is present if will loop by itself now. --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 6 ++++++ osu.Game/Overlays/MusicController.cs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index d05ad85726..eeb1982d91 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -149,6 +149,12 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); + + if (BeatmapSets.Count() == 1) + beatmapBacking.Value.Track.Looping = true; + else + beatmapBacking.Value.Track.Looping = false; + beatmapBacking.Value.Track.Start(); } } diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 4f57ea1bcd..b30ee8f6fc 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -251,7 +251,7 @@ namespace osu.Game.Overlays playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o; - if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled) + if (track.HasCompleted && !beatmapBacking.Disabled && playlist.BeatmapSets.Any()) next(); } else From 0c9ebcd58c2393910b9a07b407a5c45e31ccccda Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 3 Dec 2017 14:15:08 +0100 Subject: [PATCH 109/454] fix possible NullRef --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index eeb1982d91..4bac91edb8 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -150,7 +150,7 @@ namespace osu.Game.Overlays.Music { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - if (BeatmapSets.Count() == 1) + if (BeatmapSets?.Count() == 1) beatmapBacking.Value.Track.Looping = true; else beatmapBacking.Value.Track.Looping = false; From dfa74487162de873d71da25c5698561f1677a120 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 3 Dec 2017 14:25:12 +0100 Subject: [PATCH 110/454] use ?: expression --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 4bac91edb8..1874b7ead8 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -149,12 +149,7 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - - if (BeatmapSets?.Count() == 1) - beatmapBacking.Value.Track.Looping = true; - else - beatmapBacking.Value.Track.Looping = false; - + beatmapBacking.Value.Track.Looping = BeatmapSets?.Count() == 1 ? true : false; beatmapBacking.Value.Track.Start(); } } From 9d13bf36025a8c355a4f5891007fffe62ab8a99a Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 3 Dec 2017 14:35:23 +0100 Subject: [PATCH 111/454] remove redundant expression --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 1874b7ead8..75798bab2d 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -149,7 +149,7 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - beatmapBacking.Value.Track.Looping = BeatmapSets?.Count() == 1 ? true : false; + beatmapBacking.Value.Track.Looping = BeatmapSets?.Count() == 1; beatmapBacking.Value.Track.Start(); } } From 14096c90cc73a03811c58d305ab8db0f6b22c42a Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 3 Dec 2017 16:41:21 +0100 Subject: [PATCH 112/454] removed looping if only one song is in the list (temporarily) It had one problem in relation to SongSelect disabling it when left and in general that topic belongs to another PR. --- osu.Game/Overlays/Music/PlaylistList.cs | 7 ++++++- osu.Game/Overlays/Music/PlaylistOverlay.cs | 16 +++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index af01cdc451..8db4af6fb2 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -35,7 +35,11 @@ namespace osu.Game.Overlays.Music set { base.Padding = value; } } - public IEnumerable BeatmapSets { set { items.Sets = value; } } + public IEnumerable BeatmapSets + { + get { return items.Sets; } + set { items.Sets = value; } + } public BeatmapSetInfo FirstVisibleSet => items.FirstVisibleSet; public BeatmapSetInfo NextSet => items.NextSet; @@ -81,6 +85,7 @@ namespace osu.Game.Overlays.Music public IEnumerable Sets { + get { return items.Select(x => x.BeatmapSetInfo).ToList(); } set { items.Clear(); diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 75798bab2d..54b34aaead 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Music private readonly Bindable beatmapBacking = new Bindable(); - public IEnumerable BeatmapSets; + public IEnumerable BeatmapSets => list.BeatmapSets; [BackgroundDependencyLoader] private void load(OsuGameBase game, BeatmapManager beatmaps, OsuColour colours) @@ -74,11 +74,10 @@ namespace osu.Game.Overlays.Music }, }; - beatmaps.BeatmapSetAdded += s => Schedule(() => list.AddBeatmapSet(s)); - beatmaps.BeatmapSetRemoved += s => Schedule(() => list.RemoveBeatmapSet(s)); - - list.BeatmapSets = BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); + beatmaps.BeatmapSetAdded += delegate (BeatmapSetInfo set) { list.AddBeatmapSet(set); }; + beatmaps.BeatmapSetRemoved += delegate (BeatmapSetInfo set) { list.RemoveBeatmapSet(set); }; + list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); beatmapBacking.BindTo(game.Beatmap); @@ -121,7 +120,7 @@ namespace osu.Game.Overlays.Music return; } - playSpecified(set.Beatmaps[0]); + playSpecified(set.Beatmaps.First()); } public void PlayPrevious() @@ -130,7 +129,7 @@ namespace osu.Game.Overlays.Music if (playable != null) { - playSpecified(playable.Beatmaps[0]); + playSpecified(playable.Beatmaps.First()); list.SelectedSet = playable; } } @@ -141,7 +140,7 @@ namespace osu.Game.Overlays.Music if (playable != null) { - playSpecified(playable.Beatmaps[0]); + playSpecified(playable.Beatmaps.First()); list.SelectedSet = playable; } } @@ -149,7 +148,6 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - beatmapBacking.Value.Track.Looping = BeatmapSets?.Count() == 1; beatmapBacking.Value.Track.Start(); } } From 5f9de399e40d6748a14657fa97da77aa3f916323 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Dec 2017 11:30:25 +0900 Subject: [PATCH 113/454] Add keydown override --- osu.Game/Screens/OsuScreen.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 76ee4a607e..4a27c7f1ea 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -13,6 +13,8 @@ using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Game.Rulesets; using osu.Game.Screens.Menu; +using osu.Framework.Input; +using OpenTK.Input; namespace osu.Game.Screens { @@ -73,6 +75,20 @@ namespace osu.Game.Screens sampleExit = audio.Sample.Get(@"UI/screen-back"); } + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat || !IsCurrentScreen) return false; + + switch (args.Key) + { + case Key.Escape: + Exit(); + return true; + } + + return base.OnKeyDown(state, args); + } + protected override void OnResuming(Screen last) { base.OnResuming(last); From 7cfe694a3fd63ac1dd54caddcb87bce80f09e8cb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Dec 2017 11:37:19 +0900 Subject: [PATCH 114/454] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index cc013fc406..8c16e6244d 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit cc013fc4063dda0843f38c1c73568a413abcf229 +Subproject commit 8c16e6244d8ca884a743a0d5a1f1ee485f18655c From 011223048b129b213003083279a9f6793e930fb5 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Mon, 4 Dec 2017 11:47:27 +0100 Subject: [PATCH 115/454] 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 116/454] 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 a83add8540d59d40ac19003de9ca1b29a2011512 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Dec 2017 20:30:56 +0900 Subject: [PATCH 117/454] Tidy up events --- osu.Game/Overlays/Music/PlaylistList.cs | 10 +++++----- osu.Game/Overlays/Music/PlaylistOverlay.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 8db4af6fb2..245b2d36ce 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Music } public void AddBeatmapSet(BeatmapSetInfo beatmapSet) => items.AddBeatmapSet(beatmapSet); - public bool RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet); + public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet); public void Filter(string searchTerm) => items.SearchTerm = searchTerm; @@ -108,12 +108,11 @@ namespace osu.Game.Overlays.Music }); } - public bool RemoveBeatmapSet(BeatmapSetInfo beatmapSet) + public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) { var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID); - if (itemToRemove == null) - return false; - return items.Remove(itemToRemove); + if (itemToRemove != null) + items.Remove(itemToRemove); } public BeatmapSetInfo SelectedSet @@ -235,6 +234,7 @@ namespace osu.Game.Overlays.Music private class ItemSearchContainer : FillFlowContainer, IHasFilterableChildren { public IEnumerable FilterTerms => new string[] { }; + public bool MatchingFilter { set diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 54b34aaead..2c7c097be2 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -74,8 +74,8 @@ namespace osu.Game.Overlays.Music }, }; - beatmaps.BeatmapSetAdded += delegate (BeatmapSetInfo set) { list.AddBeatmapSet(set); }; - beatmaps.BeatmapSetRemoved += delegate (BeatmapSetInfo set) { list.RemoveBeatmapSet(set); }; + beatmaps.BeatmapSetAdded += list.AddBeatmapSet; + beatmaps.BeatmapSetRemoved += list.RemoveBeatmapSet; list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); From 7649a3115704dfae6c3947917f294ad97177b181 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Dec 2017 20:32:50 +0900 Subject: [PATCH 118/454] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 8c16e6244d..e21fc5e170 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 8c16e6244d8ca884a743a0d5a1f1ee485f18655c +Subproject commit e21fc5e1705593151a323007fd66e90044332398 From caee6c1cf1163407734edf6fd68226849aafba66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Dec 2017 21:40:26 +0900 Subject: [PATCH 119/454] Use Restart instead of Start --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 2c7c097be2..cc55a72f24 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - beatmapBacking.Value.Track.Start(); + beatmapBacking.Value.Track.Restart(); } } From b4641142a8e6205a25b175f3c4bae90b40d17e9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2017 01:00:06 +0900 Subject: [PATCH 120/454] Update framework once more --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index e21fc5e170..d2c6d11f3b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit e21fc5e1705593151a323007fd66e90044332398 +Subproject commit d2c6d11f3bcc54a5aeb5a16d6563036c7e1f4759 From c1b607fed926eeb2051b8d0ebb6e3869efb9dcc9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2017 09:56:24 +0900 Subject: [PATCH 121/454] Wait until track has restarted before continuing operation I removed this from my previous PR thinking it was not required, but it turned out to be required after all. Just isn't so noticeable when it fails. --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index cc55a72f24..23bec53014 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; +using System.Threading; namespace osu.Game.Overlays.Music { @@ -148,7 +149,15 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - beatmapBacking.Value.Track.Restart(); + + var track = beatmapBacking.Value.Track; + + track.Restart(); + + // this is temporary until we have blocking (async.Wait()) audio component methods. + // then we can call RestartAsync().Wait() or the blocking version above. + while (!track.IsRunning) + Thread.Sleep(1); } } From fb6408257a5fa980d0abd728c16cdbc1d42ea22f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2017 15:54:15 +0900 Subject: [PATCH 122/454] Add shader precompile step Resolves https://github.com/ppy/osu-framework/issues/1180 in a way. --- osu.Game/Screens/Loader.cs | 70 +++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index ca541ea552..ec2e8e0cb1 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -1,8 +1,12 @@ // 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.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Shaders; using osu.Game.Screens.Menu; using OpenTK; using osu.Framework.Screens; @@ -33,14 +37,28 @@ namespace osu.Game.Screens logo.FadeInFromZero(5000, Easing.OutQuint); } + private OsuScreen loadScreen; + private ShaderPrecompiler precompiler; + protected override void OnEntering(Screen last) { base.OnEntering(last); - if (showDisclaimer) - LoadComponentAsync(new Disclaimer(), d => Push(d)); - else - LoadComponentAsync(new Intro(), d => Push(d)); + LoadComponentAsync(precompiler = new ShaderPrecompiler(loadIfReady), Add); + LoadComponentAsync(loadScreen = showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro(), s => loadIfReady()); + } + + private void loadIfReady() + { + if (ChildScreen == loadScreen) return; + + if (loadScreen.LoadState != LoadState.Ready) + return; + + if (!precompiler.FinishedCompiling) + return; + + Push(loadScreen); } protected override void LogoSuspending(OsuLogo logo) @@ -54,5 +72,49 @@ namespace osu.Game.Screens { showDisclaimer = game.IsDeployedBuild; } + + /// + /// Compiles a set of shaders before continuing. Attempts to draw some frames between compilation by limiting to one compile per draw frame. + /// + public class ShaderPrecompiler : Drawable + { + private readonly Action onLoaded; + private readonly List loadTargets = new List(); + + public bool FinishedCompiling { get; private set; } + + public ShaderPrecompiler(Action onLoaded) + { + this.onLoaded = onLoaded; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager manager) + { + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE)); + + loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE)); + + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE_ROUNDED)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); + } + + private Shader currentLoadTarget; + + protected override void Update() + { + base.Update(); + + // if our target is null we are done. + if (loadTargets.All(s => s.Loaded)) + { + FinishedCompiling = true; + Expire(); + onLoaded?.Invoke(); + } + } + } } } From b584178e851fa26da60a3027ec3b52708b8bd39a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Dec 2017 00:37:37 +0900 Subject: [PATCH 123/454] Make Beatmap ISerializable and add more JsonIgnores --- osu.Game.Rulesets.Mania/Objects/Note.cs | 2 + .../osu.Game.Rulesets.Mania.csproj | 4 + .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 153 ++++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + osu.Game/Beatmaps/Beatmap.cs | 8 +- osu.Game/Beatmaps/BeatmapDifficulty.cs | 2 + osu.Game/Beatmaps/BeatmapInfo.cs | 7 + osu.Game/Beatmaps/BeatmapMetadata.cs | 5 + .../ControlPoints/ControlPointInfo.cs | 6 +- .../Converters/TypedListConverter.cs | 83 ++++++++++ .../Converters/Vector2Converter.cs | 35 ++++ .../IO/Serialization/IJsonSerializable.cs | 26 +-- osu.Game/Rulesets/RulesetInfo.cs | 3 + osu.Game/osu.Game.csproj | 2 + 14 files changed, 322 insertions(+), 15 deletions(-) create mode 100644 osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs create mode 100644 osu.Game/IO/Serialization/Converters/TypedListConverter.cs create mode 100644 osu.Game/IO/Serialization/Converters/Vector2Converter.cs diff --git a/osu.Game.Rulesets.Mania/Objects/Note.cs b/osu.Game.Rulesets.Mania/Objects/Note.cs index 3c4ff4216f..c4d5a13352 100644 --- a/osu.Game.Rulesets.Mania/Objects/Note.cs +++ b/osu.Game.Rulesets.Mania/Objects/Note.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 Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Mania.Judgements; @@ -15,6 +16,7 @@ namespace osu.Game.Rulesets.Mania.Objects /// /// The key-press hit window for this note. /// + [JsonIgnore] public HitWindows HitWindows { get; protected set; } = new HitWindows(); public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index ec6f59b5be..1e11e2e694 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -40,6 +40,10 @@ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll True + + $(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + True + diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs new file mode 100644 index 0000000000..1531deb265 --- /dev/null +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -0,0 +1,153 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using System.Linq; +using NUnit.Framework; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO.Serialization; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Tests.Resources; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Beatmaps.Formats +{ + [TestFixture] + public class OsuJsonDecoderTest + { + [Test] + public void TestDecodeMetadata() + { + var beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + 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 beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + var beatmapInfo = beatmap.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 beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + var beatmapInfo = beatmap.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 TestDecodeDifficulty() + { + var beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + 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 beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + 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 beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + + 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)); + } + + /// + /// Reads a .osu file first with a , serializes the resulting to JSON + /// and then deserializes the result back into a through an . + /// + /// The .osu file to decode. + /// The after being decoded by an . + private Beatmap decodeAsJson(string filename) + { + using (var stream = Resource.OpenResource(filename)) + using (var sr = new StreamReader(stream)) + { + var legacyDecoded = new OsuLegacyDecoder().Decode(sr); + + using (var ms = new MemoryStream()) + using (var sw = new StreamWriter(ms)) + using (var sr2 = new StreamReader(ms)) + { + sw.Write(legacyDecoded.Serialize()); + sw.Flush(); + + ms.Position = 0; + return new OsuJsonDecoder().Decode(sr2); + } + } + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 092ccfb9b5..60ce67c7f6 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -83,6 +83,7 @@ + diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 35b6cc2b02..c331872dda 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -9,19 +9,21 @@ using System.Linq; using osu.Game.Beatmaps.ControlPoints; using osu.Game.IO.Serialization; using osu.Game.Storyboards; +using Newtonsoft.Json; +using osu.Game.IO.Serialization.Converters; namespace osu.Game.Beatmaps { /// /// A Beatmap containing converted HitObjects. /// - public class Beatmap + public class Beatmap : IJsonSerializable where T : HitObject { public BeatmapInfo BeatmapInfo = new BeatmapInfo(); public ControlPointInfo ControlPointInfo = new ControlPointInfo(); public List Breaks = new List(); - public readonly List ComboColors = new List + public List ComboColors = new List { new Color4(17, 136, 170, 255), new Color4(102, 136, 0, 255), @@ -29,8 +31,10 @@ namespace osu.Game.Beatmaps new Color4(121, 9, 13, 255) }; + [JsonIgnore] public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; + [JsonConverter(typeof(TypedListConverter))] /// /// The HitObjects this Beatmap contains. /// diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 0b0fca8292..03fbf9a0a7 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; namespace osu.Game.Beatmaps { @@ -13,6 +14,7 @@ namespace osu.Game.Beatmaps public const float DEFAULT_DIFFICULTY = 5; [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [JsonIgnore] public int ID { get; set; } public float DrainRate { get; set; } = DEFAULT_DIFFICULTY; diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 022d64db03..6b5f7cb150 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -15,6 +15,7 @@ namespace osu.Game.Beatmaps public class BeatmapInfo : IEquatable, IJsonSerializable, IHasPrimaryKey { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [JsonIgnore] public int ID { get; set; } //TODO: should be in database @@ -38,13 +39,16 @@ namespace osu.Game.Beatmaps set { onlineBeatmapSetID = value > 0 ? value : null; } } + [JsonIgnore] public int BeatmapSetInfoID { get; set; } [Required] + [JsonIgnore] public BeatmapSetInfo BeatmapSet { get; set; } public BeatmapMetadata Metadata { get; set; } + [JsonIgnore] public int BaseDifficultyID { get; set; } public BeatmapDifficulty BaseDifficulty { get; set; } @@ -60,6 +64,7 @@ namespace osu.Game.Beatmaps [JsonProperty("file_sha2")] public string Hash { get; set; } + [JsonIgnore] public bool Hidden { get; set; } /// @@ -74,6 +79,7 @@ namespace osu.Game.Beatmaps public float StackLeniency { get; set; } public bool SpecialStyle { get; set; } + [JsonIgnore] public int RulesetID { get; set; } public RulesetInfo Ruleset { get; set; } @@ -116,6 +122,7 @@ namespace osu.Game.Beatmaps public string Version { get; set; } public double StarDifficulty { get; set; } + public bool ShouldSerializeStarDifficulty() => false; public bool Equals(BeatmapInfo other) { diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 89f9ebf47a..2efbcd4f15 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -12,6 +12,7 @@ namespace osu.Game.Beatmaps public class BeatmapMetadata { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [JsonIgnore] public int ID { get; set; } private int? onlineBeatmapSetID; @@ -29,7 +30,10 @@ namespace osu.Game.Beatmaps public string Artist { get; set; } public string ArtistUnicode { get; set; } + [JsonIgnore] public List Beatmaps { get; set; } + + [JsonIgnore] public List BeatmapSets { get; set; } /// @@ -56,6 +60,7 @@ namespace osu.Game.Beatmaps public string AudioFile { get; set; } public string BackgroundFile { get; set; } + [JsonIgnore] public string[] SearchableTerms => new[] { Author?.Username, diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index e46eb8e20a..f24e25f112 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; using osu.Framework.Lists; namespace osu.Game.Beatmaps.ControlPoints @@ -58,18 +59,21 @@ namespace osu.Game.Beatmaps.ControlPoints /// The timing control point. public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault()); + [JsonIgnore] /// /// Finds the maximum BPM represented by any timing control point. /// public double BPMMaximum => 60000 / (TimingPoints.OrderBy(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + [JsonIgnore] /// /// Finds the minimum BPM represented by any timing control point. /// public double BPMMinimum => 60000 / (TimingPoints.OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + [JsonIgnore] /// /// Finds the mode BPM (most common BPM) represented by the control points. /// @@ -108,4 +112,4 @@ namespace osu.Game.Beatmaps.ControlPoints return list[index - 1]; } } -} \ No newline at end of file +} diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs new file mode 100644 index 0000000000..1d617422e7 --- /dev/null +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace osu.Game.IO.Serialization.Converters +{ + public class TypedListConverter : JsonConverter + { + public override bool CanConvert(Type objectType) => objectType == typeof(List); + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var list = new List(); + + var localSerializer = createLocalSerializer(); + + var obj = JObject.Load(reader); + + var lookupTable = new List(); + localSerializer.Populate(obj["LookupTable"].CreateReader(), lookupTable); + + foreach (var tok in obj["Items"]) + { + var itemReader = tok.CreateReader(); + + var typeName = lookupTable[(int)tok["Type"]]; + var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); + localSerializer.Populate(itemReader, instance); + + list.Add(instance); + } + + return list; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var list = (List)value; + + var localSerializer = createLocalSerializer(); + + var lookupTable = new List(); + var objects = new List(); + foreach (var item in list) + { + var type = item.GetType().AssemblyQualifiedName; + + int typeId = lookupTable.IndexOf(type); + if (typeId == -1) + { + lookupTable.Add(type); + typeId = lookupTable.Count - 1; + } + + var itemObject = JObject.FromObject(item, localSerializer); + itemObject.AddFirst(new JProperty("Type", typeId)); + objects.Add(itemObject); + } + + writer.WriteStartObject(); + + writer.WritePropertyName("LookupTable"); + localSerializer.Serialize(writer, lookupTable); + + writer.WritePropertyName("Items"); + writer.WriteStartArray(); + foreach (var item in objects) + item.WriteTo(writer); + writer.WriteEndArray(); + + writer.WriteEndObject(); + } + + private JsonSerializer createLocalSerializer() + { + var localSettings = JsonSerializableExtensions.CreateGlobalSettings(); + localSettings.Converters = localSettings.Converters.Where(c => !(c is TypedListConverter)).ToArray(); + return JsonSerializer.Create(localSettings); + } + } +} diff --git a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs new file mode 100644 index 0000000000..4ec27311bc --- /dev/null +++ b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs @@ -0,0 +1,35 @@ +// 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 Newtonsoft.Json.Linq; +using OpenTK; + +namespace osu.Game.IO.Serialization.Converters +{ + public class Vector2Converter : JsonConverter + { + public override bool CanConvert(Type objectType) => objectType == typeof(Vector2); + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var obj = JObject.Load(reader); + return new Vector2((float)obj["X"], (float)obj["Y"]); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var vector = (Vector2)value; + + writer.WriteStartObject(); + + writer.WritePropertyName("X"); + writer.WriteValue(vector.X); + writer.WritePropertyName("Y"); + writer.WriteValue(vector.Y); + + writer.WriteEndObject(); + } + } +} diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs index e725742726..8d10f0b291 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/IJsonSerializable.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using Newtonsoft.Json; +using osu.Game.IO.Serialization.Converters; namespace osu.Game.IO.Serialization { @@ -11,20 +12,21 @@ namespace osu.Game.IO.Serialization public static class JsonSerializableExtensions { - public static string Serialize(this IJsonSerializable obj) - { - return JsonConvert.SerializeObject(obj, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); - } + public static string Serialize(this IJsonSerializable obj) => JsonConvert.SerializeObject(obj, CreateGlobalSettings()); - public static T Deserialize(this string objString) - { - return JsonConvert.DeserializeObject(objString); - } + public static T Deserialize(this string objString) => JsonConvert.DeserializeObject(objString, CreateGlobalSettings()); - public static T DeepClone(this T obj) - where T : IJsonSerializable + public static void DeserializeInto(this string objString, T target) => JsonConvert.PopulateObject(objString, target, CreateGlobalSettings()); + + public static T DeepClone(this T obj) where T : IJsonSerializable => Deserialize(Serialize(obj)); + + public static JsonSerializerSettings CreateGlobalSettings() => new JsonSerializerSettings { - return Deserialize(Serialize(obj)); - } + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + Formatting = Formatting.Indented, + ObjectCreationHandling = ObjectCreationHandling.Replace, + DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, + Converters = new JsonConverter[] { new Vector2Converter() } + }; } } diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 17f07158df..e6e0b98293 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -3,18 +3,21 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; namespace osu.Game.Rulesets { public class RulesetInfo : IEquatable { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [JsonIgnore] public int? ID { get; set; } public string Name { get; set; } public string InstantiationInfo { get; set; } + [JsonIgnore] public bool Available { get; set; } public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4ca6123f04..f50e87b074 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -403,6 +403,8 @@ + + From d2dc7c8937dfe1901494c01c81c4459ef40e86ab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Dec 2017 00:38:12 +0900 Subject: [PATCH 124/454] Add OsuJsonDecoder --- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 12 +++++++++- osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs | 25 +++++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index 7e1a87085c..31869e207e 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -14,6 +14,7 @@ namespace osu.Game.Beatmaps.Formats static BeatmapDecoder() { OsuLegacyDecoder.Register(); + OsuJsonDecoder.Register(); } public static BeatmapDecoder GetDecoder(StreamReader stream) @@ -27,7 +28,16 @@ namespace osu.Game.Beatmaps.Formats if (line == null || !decoders.ContainsKey(line)) throw new IOException(@"Unknown file format"); - return (BeatmapDecoder)Activator.CreateInstance(decoders[line], line); + + try + { + return (BeatmapDecoder)Activator.CreateInstance(decoders[line], line); + } + catch + { + // As a default case, try a parameterless constructor + return (BeatmapDecoder)Activator.CreateInstance(decoders[line]); + } } protected static void AddDecoder(string magic) where T : BeatmapDecoder diff --git a/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs b/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs new file mode 100644 index 0000000000..392f1b4890 --- /dev/null +++ b/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using osu.Game.IO.Serialization; + +namespace osu.Game.Beatmaps.Formats +{ + public class OsuJsonDecoder : BeatmapDecoder + { + public static void Register() + { + AddDecoder("{"); + } + + protected override void ParseFile(StreamReader stream, Beatmap beatmap) + { + stream.BaseStream.Position = 0; + stream.DiscardBufferedData(); + + string fullText = stream.ReadToEnd(); + fullText.DeserializeInto(beatmap); + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f50e87b074..9e5d4291ce 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -266,6 +266,7 @@ + From e199ee52252b85ff0b091cc86c6c1c91f1ef2972 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Dec 2017 00:42:17 +0900 Subject: [PATCH 125/454] Add a few xmldocs --- osu.Game/IO/Serialization/Converters/TypedListConverter.cs | 6 ++++++ osu.Game/IO/Serialization/Converters/Vector2Converter.cs | 3 +++ 2 files changed, 9 insertions(+) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 1d617422e7..9c35fae7d4 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -6,6 +6,12 @@ using Newtonsoft.Json.Linq; namespace osu.Game.IO.Serialization.Converters { + /// + /// A type of that serializes a alongside + /// a lookup table for the types contained. The lookup table is used in deserialization to + /// reconstruct the objects with their original types. + /// + /// The type of objects contained in the this attribute is attached to. public class TypedListConverter : JsonConverter { public override bool CanConvert(Type objectType) => objectType == typeof(List); diff --git a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs index 4ec27311bc..5f21018fd5 100644 --- a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs +++ b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs @@ -8,6 +8,9 @@ using OpenTK; namespace osu.Game.IO.Serialization.Converters { + /// + /// A type of that serializes only the X and Y coordinates of a . + /// public class Vector2Converter : JsonConverter { public override bool CanConvert(Type objectType) => objectType == typeof(Vector2); From 15ed3b4aac3c87ae277c1c53696f02b3c1b1097e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 09:49:12 +0900 Subject: [PATCH 126/454] Fix IsValueCreated method not cecking whether the async task was completed Caused potential stutters for components that relied on this check. --- 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 8c96074352..a23d74f288 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -144,7 +144,7 @@ namespace osu.Game.Beatmaps get { ensureValid(); - return lazy.IsValueCreated; + return lazy.Value.IsCompleted; } } From 988cc27f0e7fc461d0d8c66aa3e0375eb24ffa28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 11:59:53 +0900 Subject: [PATCH 127/454] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index d2c6d11f3b..9cd6968a8c 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d2c6d11f3bcc54a5aeb5a16d6563036c7e1f4759 +Subproject commit 9cd6968a8c4d27415808f5e770d24fec5a44485f From 44edb8724fcabc81456616346a9a25dc55dcb54a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Dec 2017 13:46:49 +0900 Subject: [PATCH 128/454] Add JsonIgnores to CommandTimelineGroup --- osu.Game/Storyboards/CommandTimelineGroup.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Storyboards/CommandTimelineGroup.cs b/osu.Game/Storyboards/CommandTimelineGroup.cs index 332a6f79cb..c6d9202121 100644 --- a/osu.Game/Storyboards/CommandTimelineGroup.cs +++ b/osu.Game/Storyboards/CommandTimelineGroup.cs @@ -6,6 +6,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; namespace osu.Game.Storyboards { @@ -23,6 +24,7 @@ namespace osu.Game.Storyboards public CommandTimeline FlipH = new CommandTimeline(); public CommandTimeline FlipV = new CommandTimeline(); + [JsonIgnore] public IEnumerable Timelines { get @@ -39,14 +41,25 @@ namespace osu.Game.Storyboards } } + [JsonIgnore] public double CommandsStartTime => Timelines.Where(t => t.HasCommands).Min(t => t.StartTime); + + [JsonIgnore] public double CommandsEndTime => Timelines.Where(t => t.HasCommands).Max(t => t.EndTime); + + [JsonIgnore] public double CommandsDuration => CommandsEndTime - CommandsStartTime; + [JsonIgnore] public virtual double StartTime => CommandsStartTime; + + [JsonIgnore] public virtual double EndTime => CommandsEndTime; + + [JsonIgnore] public double Duration => EndTime - StartTime; + [JsonIgnore] public bool HasCommands => Timelines.Any(t => t.HasCommands); public virtual IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0) From 887b81148db1741aaee3f85642beeb536ddc0528 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Dec 2017 14:14:07 +0900 Subject: [PATCH 129/454] Don't ignore RulesetId for now --- osu.Game/Beatmaps/BeatmapInfo.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 6b5f7cb150..9450022a01 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -79,7 +79,6 @@ namespace osu.Game.Beatmaps public float StackLeniency { get; set; } public bool SpecialStyle { get; set; } - [JsonIgnore] public int RulesetID { get; set; } public RulesetInfo Ruleset { get; set; } From 04ae64e9fd7bb412cb20cb3424d2325afd94ced0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 16:20:49 +0900 Subject: [PATCH 130/454] Add missing IsCompleted check to ensureValid --- 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 a23d74f288..6378bb7568 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -161,7 +161,7 @@ namespace osu.Game.Beatmaps { lock (initLock) { - if (!lazy.IsValueCreated || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; + if (!lazy.IsValueCreated || !lazy.Value.IsCompleted || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; init(); } } From f9e34dfa3d51415062e8bd7ce677274c7823d274 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Dec 2017 16:23:51 +0900 Subject: [PATCH 131/454] Assume that control points are already sequentially-ordered Fixes up deserializing. --- .../UI/ManiaRulesetContainer.cs | 3 +-- .../Visual/TestCaseBeatSyncedContainer.cs | 4 ++-- .../Beatmaps/ControlPoints/ControlPointInfo.cs | 16 ++++++++++------ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index cbbcb84b31..83306187bd 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Input; -using osu.Framework.Lists; using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.UI // Generate the bar lines double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; - SortedList timingPoints = Beatmap.ControlPointInfo.TimingPoints; + List timingPoints = Beatmap.ControlPointInfo.TimingPoints; var barLines = new List(); for (int i = 0; i < timingPoints.Count; i++) diff --git a/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs index 18555574ba..d99485f3a2 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs @@ -2,12 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Lists; using osu.Framework.Timing; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; @@ -137,7 +137,7 @@ namespace osu.Game.Tests.Visual }; } - private SortedList timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints; + private List timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints; private TimingControlPoint getNextTimingPoint(TimingControlPoint current) { if (timingPoints[timingPoints.Count - 1] == current) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index f24e25f112..652ae42979 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -5,31 +5,35 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using osu.Framework.Lists; namespace osu.Game.Beatmaps.ControlPoints { + [Serializable] public class ControlPointInfo { + [JsonProperty] /// /// All timing points. /// - public readonly SortedList TimingPoints = new SortedList(Comparer.Default); + public List TimingPoints { get; private set; } = new List(); + [JsonProperty] /// /// All difficulty points. /// - public readonly SortedList DifficultyPoints = new SortedList(Comparer.Default); + public List DifficultyPoints { get; private set; } = new List(); + [JsonProperty] /// /// All sound points. /// - public readonly SortedList SoundPoints = new SortedList(Comparer.Default); + public List SoundPoints { get; private set; } = new List(); + [JsonProperty] /// /// All effect points. /// - public readonly SortedList EffectPoints = new SortedList(Comparer.Default); + public List EffectPoints { get; private set; } = new List(); /// /// Finds the difficulty control point that is active at . @@ -87,7 +91,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time to find the control point at. /// The control point to use when is before any control points. If null, a new control point will be constructed. /// The active control point at . - private T binarySearch(SortedList list, double time, T prePoint = null) + private T binarySearch(List list, double time, T prePoint = null) where T : ControlPoint, new() { if (list == null) From ed5f7e5353a77d05203a596cb0c3bee4380fd237 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Dec 2017 16:28:34 +0900 Subject: [PATCH 132/454] Make OsuJsonDecoder apply defaults similar to OsuLegacyDecoder --- osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs b/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs index 392f1b4890..ed4b8f3857 100644 --- a/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs @@ -20,6 +20,9 @@ namespace osu.Game.Beatmaps.Formats string fullText = stream.ReadToEnd(); fullText.DeserializeInto(beatmap); + + foreach (var hitObject in beatmap.HitObjects) + hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); } } } From 34596b3368591d2b9eba717a5139854dd1ce4ba1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 21:47:48 +0900 Subject: [PATCH 133/454] Rename and comment for clarification --- osu.Game/Beatmaps/WorkingBeatmap.cs | 44 ++++++++++++++++------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 6378bb7568..bc826ea140 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -41,7 +41,7 @@ namespace osu.Game.Beatmaps protected abstract Track GetTrack(); protected virtual Waveform GetWaveform() => new Waveform(); - public bool BeatmapLoaded => beatmap.IsValueCreated; + public bool BeatmapLoaded => beatmap.IsResultAvailable; public Beatmap Beatmap => beatmap.Value.Result; public async Task GetBeatmapAsync() => await beatmap.Value; @@ -57,14 +57,14 @@ namespace osu.Game.Beatmaps return b; } - public bool BackgroundLoaded => background.IsValueCreated; + public bool BackgroundLoaded => background.IsResultAvailable; public Texture Background => background.Value.Result; public async Task GetBackgroundAsync() => await background.Value; private AsyncLazy background; private Texture populateBackground() => GetBackground(); - public bool TrackLoaded => track.IsValueCreated; + public bool TrackLoaded => track.IsResultAvailable; public Track Track => track.Value.Result; public async Task GetTrackAsync() => await track.Value; private AsyncLazy track; @@ -77,7 +77,7 @@ namespace osu.Game.Beatmaps return t; } - public bool WaveformLoaded => waveform.IsValueCreated; + public bool WaveformLoaded => waveform.IsResultAvailable; public Waveform Waveform => waveform.Value.Result; public async Task GetWaveformAsync() => await waveform.Value; private readonly AsyncLazy waveform; @@ -86,10 +86,10 @@ namespace osu.Game.Beatmaps public void TransferTo(WorkingBeatmap other) { - if (track.IsValueCreated && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) + if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) other.track = track; - if (background.IsValueCreated && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) + if (background.IsResultAvailable && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) other.background = background; } @@ -107,7 +107,7 @@ namespace osu.Game.Beatmaps private void applyRateAdjustments(Track t = null) { - if (t == null && track.IsValueCreated) t = Track; + if (t == null && track.IsResultAvailable) t = Track; if (t == null) return; t.ResetSpeedAdjustments(); @@ -128,22 +128,22 @@ namespace osu.Game.Beatmaps this.valueFactory = valueFactory; this.stillValidFunction = stillValidFunction; - init(); + recreate(); } public void Recycle() { - if (!IsValueCreated) return; + if (!IsResultAvailable) return; (lazy.Value.Result as IDisposable)?.Dispose(); - init(); + recreate(); } - public bool IsValueCreated + public bool IsResultAvailable { get { - ensureValid(); + recreateIfInvalid(); return lazy.Value.IsCompleted; } } @@ -152,24 +152,28 @@ namespace osu.Game.Beatmaps { get { - ensureValid(); + recreateIfInvalid(); return lazy.Value; } } - private void ensureValid() + private void recreateIfInvalid() { lock (initLock) { - if (!lazy.IsValueCreated || !lazy.Value.IsCompleted || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; - init(); + if (!lazy.IsValueCreated || !lazy.Value.IsCompleted) + // we have not yet been initialised or haven't run the task. + return; + + if (stillValidFunction?.Invoke(lazy.Value.Result) ?? true) + // we are still in a valid state. + return; + + recreate(); } } - private void init() - { - lazy = new Lazy>(() => Task.Run(valueFactory)); - } + private void recreate() => lazy = new Lazy>(() => Task.Run(valueFactory)); } } } From b6b26cfe2565140b2b515ae1c076a0fe72083ab0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 03:04:36 +0900 Subject: [PATCH 134/454] Add basic method to migrate beatmaps to the new JSON format --- osu.Game/Beatmaps/BeatmapManager.cs | 25 ++++++++++++ .../IO/Serialization/IJsonSerializable.cs | 2 +- .../Sections/Maintenance/GeneralSettings.cs | 40 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 0641cabcd8..c96b889213 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -545,6 +545,31 @@ namespace osu.Game.Beatmaps return beatmapSet; } + public void UpdateContent(BeatmapInfo beatmapInfo, Stream newData) + { + // let's only allow one concurrent update at a time for now. + var context = createContext(); + + using (var transaction = context.BeginTransaction()) + { + // create local stores so we can isolate and thread safely, and share a context/transaction. + var setInfo = beatmapInfo.BeatmapSet; + var existingSetFileInfo = setInfo.Files.First(f => f.FileInfo.Hash == beatmapInfo.Hash); + var existingFileInfo = existingSetFileInfo.FileInfo; + + existingSetFileInfo.FileInfo = files.Add(newData); + files.Dereference(existingFileInfo); + + beatmapInfo.Hash = newData.ComputeSHA2Hash(); + beatmapInfo.MD5Hash = newData.ComputeMD5Hash(); + + context.Update(existingSetFileInfo); + context.Update(beatmapInfo); + + context.SaveChanges(transaction); + } + } + /// /// Returns a list of all usable s. /// diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs index 8d10f0b291..e192d702ce 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/IJsonSerializable.cs @@ -16,7 +16,7 @@ namespace osu.Game.IO.Serialization public static T Deserialize(this string objString) => JsonConvert.DeserializeObject(objString, CreateGlobalSettings()); - public static void DeserializeInto(this string objString, T target) => JsonConvert.PopulateObject(objString, target, CreateGlobalSettings()); + public static void DeserializeInto(this string objString, T target) => JsonConvert.DeserializeAnonymousType(objString, target, CreateGlobalSettings()); public static T DeepClone(this T obj) where T : IJsonSerializable => Deserialize(Serialize(obj)); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 4f4f381ae1..a41da6109c 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -1,12 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.IO; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.IO.Serialization; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -15,6 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private TriangleButton importButton; private TriangleButton deleteButton; private TriangleButton restoreButton; + private TriangleButton migrateButton; protected override string Header => "General"; @@ -55,6 +59,42 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance }).ContinueWith(t => Schedule(() => restoreButton.Enabled.Value = true)); } }, + migrateButton = new SettingsButton + { + Text = "Migrate all beatmaps to the new format", + Action = () => + { + migrateButton.Enabled.Value = false; + Task.Run(() => + { + var usableSets = beatmaps.GetAllUsableBeatmapSets(); + foreach (var set in usableSets) + { + foreach (var beatmap in set.Beatmaps) + { + var working = beatmaps.GetWorkingBeatmap(beatmap); + using (var ms = new MemoryStream()) + using (var sw = new StreamWriter(ms)) + { + try + { + sw.Write(working.Beatmap.Serialize()); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + sw.Flush(); + + ms.Position = 0; + beatmaps.UpdateContent(beatmap, ms); + } + } + } + }).ContinueWith(t => Schedule(() => migrateButton.Enabled.Value = true)); + } + } }; } } From 4232a54b32507afeaed5ad317b70ebf9f644160d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 03:04:56 +0900 Subject: [PATCH 135/454] Make TypedListConverter not reconstruct serializers --- .../Converters/TypedListConverter.cs | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 9c35fae7d4..f8897a4e9d 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -20,12 +19,10 @@ namespace osu.Game.IO.Serialization.Converters { var list = new List(); - var localSerializer = createLocalSerializer(); - var obj = JObject.Load(reader); var lookupTable = new List(); - localSerializer.Populate(obj["LookupTable"].CreateReader(), lookupTable); + serializer.Populate(obj["LookupTable"].CreateReader(), lookupTable); foreach (var tok in obj["Items"]) { @@ -33,7 +30,7 @@ namespace osu.Game.IO.Serialization.Converters var typeName = lookupTable[(int)tok["Type"]]; var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); - localSerializer.Populate(itemReader, instance); + serializer.Populate(itemReader, instance); list.Add(instance); } @@ -45,8 +42,6 @@ namespace osu.Game.IO.Serialization.Converters { var list = (List)value; - var localSerializer = createLocalSerializer(); - var lookupTable = new List(); var objects = new List(); foreach (var item in list) @@ -60,7 +55,7 @@ namespace osu.Game.IO.Serialization.Converters typeId = lookupTable.Count - 1; } - var itemObject = JObject.FromObject(item, localSerializer); + var itemObject = JObject.FromObject(item, serializer); itemObject.AddFirst(new JProperty("Type", typeId)); objects.Add(itemObject); } @@ -68,7 +63,7 @@ namespace osu.Game.IO.Serialization.Converters writer.WriteStartObject(); writer.WritePropertyName("LookupTable"); - localSerializer.Serialize(writer, lookupTable); + serializer.Serialize(writer, lookupTable); writer.WritePropertyName("Items"); writer.WriteStartArray(); @@ -78,12 +73,5 @@ namespace osu.Game.IO.Serialization.Converters writer.WriteEndObject(); } - - private JsonSerializer createLocalSerializer() - { - var localSettings = JsonSerializableExtensions.CreateGlobalSettings(); - localSettings.Converters = localSettings.Converters.Where(c => !(c is TypedListConverter)).ToArray(); - return JsonSerializer.Create(localSettings); - } } } From 9787788081d15fa49071f474db45fca99e01f045 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 03:39:43 +0900 Subject: [PATCH 136/454] Revert unintended change --- osu.Game/IO/Serialization/IJsonSerializable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs index e192d702ce..8d10f0b291 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/IJsonSerializable.cs @@ -16,7 +16,7 @@ namespace osu.Game.IO.Serialization public static T Deserialize(this string objString) => JsonConvert.DeserializeObject(objString, CreateGlobalSettings()); - public static void DeserializeInto(this string objString, T target) => JsonConvert.DeserializeAnonymousType(objString, target, CreateGlobalSettings()); + public static void DeserializeInto(this string objString, T target) => JsonConvert.PopulateObject(objString, target, CreateGlobalSettings()); public static T DeepClone(this T obj) where T : IJsonSerializable => Deserialize(Serialize(obj)); From 41b607c1657cfaac915e66dbe0e0a10b7dbdafaa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 03:40:43 +0900 Subject: [PATCH 137/454] Dont serialize hitobject sample properties copied from the control point --- osu.Game/Audio/SampleInfo.cs | 28 +++++++++++++++++++++++--- osu.Game/Rulesets/Objects/HitObject.cs | 14 ++----------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 171a1bdf75..edfda3bdc9 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -1,8 +1,13 @@ // 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.ControlPoints; + namespace osu.Game.Audio { + [Serializable] public class SampleInfo { public const string HIT_WHISTLE = @"hitwhistle"; @@ -10,19 +15,36 @@ namespace osu.Game.Audio public const string HIT_NORMAL = @"hitnormal"; public const string HIT_CLAP = @"hitclap"; + [JsonIgnore] + public SoundControlPoint ControlPoint; + + private string bank; /// /// The bank to load the sample from. /// - public string Bank; + public string Bank + { + get { return string.IsNullOrEmpty(bank) ? (ControlPoint?.SampleBank ?? "normal") : bank; } + set { bank = value; } + } + + public bool ShouldSerializeBank() => Bank == ControlPoint.SampleBank; /// /// The name of the sample to load. /// - public string Name; + public string Name { get; set; } + private int volume; /// /// The sample volume. /// - public int Volume; + public int Volume + { + get { return volume == 0 ? (ControlPoint?.SampleVolume ?? 0) : volume; } + set { volume = value; } + } + + public bool ShouldSerializeVolume() => Volume == ControlPoint.SampleVolume; } } diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index c69979d4cf..92220ff8bd 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -48,21 +48,11 @@ namespace osu.Game.Rulesets.Objects Kiai |= effectPoint.KiaiMode; // Initialize first sample - Samples.ForEach(s => initializeSampleInfo(s, soundPoint)); + Samples.ForEach(s => s.ControlPoint = soundPoint); // Initialize any repeat samples var repeatData = this as IHasRepeats; - repeatData?.RepeatSamples?.ForEach(r => r.ForEach(s => initializeSampleInfo(s, soundPoint))); - } - - private void initializeSampleInfo(SampleInfo sample, SoundControlPoint soundPoint) - { - if (sample.Volume == 0) - sample.Volume = soundPoint?.SampleVolume ?? 0; - - // If the bank is not assigned a name, assign it from the control point - if (string.IsNullOrEmpty(sample.Bank)) - sample.Bank = soundPoint?.SampleBank ?? @"normal"; + repeatData?.RepeatSamples?.ForEach(r => r.ForEach(s => s.ControlPoint = soundPoint)); } } } From a8db3a9484cebcadde7ac61445a39c5ba1ef709f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 04:09:03 +0900 Subject: [PATCH 138/454] Add progress notification to migration --- osu.Game/Beatmaps/BeatmapManager.cs | 48 +++++++++++++++++++ .../Sections/Maintenance/GeneralSettings.cs | 29 +---------- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index c96b889213..6b547afe5d 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -20,6 +20,7 @@ using osu.Game.Beatmaps.IO; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.IO; +using osu.Game.IO.Serialization; using osu.Game.IPC; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -570,6 +571,53 @@ namespace osu.Game.Beatmaps } } + public void MigrateAllToNewFormat() + { + var usableSets = GetAllUsableBeatmapSets(); + + if (usableSets.Count == 0) + return; + + var notification = new ProgressNotification + { + Progress = 0, + State = ProgressNotificationState.Active, + }; + + PostNotification?.Invoke(notification); + + int i = 1; + foreach (var set in usableSets) + { + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; + + notification.Text = $"Migrating ({i} of {usableSets.Count})"; + notification.Progress = (float)i++ / usableSets.Count; + + foreach (var beatmap in set.Beatmaps) + { + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; + + var working = GetWorkingBeatmap(beatmap); + using (var ms = new MemoryStream()) + using (var sw = new StreamWriter(ms)) + { + sw.Write(working.Beatmap.Serialize()); + sw.Flush(); + + ms.Position = 0; + UpdateContent(beatmap, ms); + } + } + } + + notification.State = ProgressNotificationState.Completed; + } + /// /// Returns a list of all usable s. /// diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index a41da6109c..9ae331daf0 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -65,34 +65,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Action = () => { migrateButton.Enabled.Value = false; - Task.Run(() => - { - var usableSets = beatmaps.GetAllUsableBeatmapSets(); - foreach (var set in usableSets) - { - foreach (var beatmap in set.Beatmaps) - { - var working = beatmaps.GetWorkingBeatmap(beatmap); - using (var ms = new MemoryStream()) - using (var sw = new StreamWriter(ms)) - { - try - { - sw.Write(working.Beatmap.Serialize()); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - sw.Flush(); - - ms.Position = 0; - beatmaps.UpdateContent(beatmap, ms); - } - } - } - }).ContinueWith(t => Schedule(() => migrateButton.Enabled.Value = true)); + Task.Factory.StartNew(beatmaps.MigrateAllToNewFormat).ContinueWith(t => Schedule(() => migrateButton.Enabled.Value = true), TaskContinuationOptions.LongRunning); } } }; From 0e3b001b133e310064065be3c98f3f13f320321d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 10:37:51 +0900 Subject: [PATCH 139/454] Make maps with storyboards decode correctly with OsuJsonDecoder --- osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs b/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs index ed4b8f3857..d00cbbb8fb 100644 --- a/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuJsonDecoder.cs @@ -18,8 +18,16 @@ namespace osu.Game.Beatmaps.Formats stream.BaseStream.Position = 0; stream.DiscardBufferedData(); - string fullText = stream.ReadToEnd(); - fullText.DeserializeInto(beatmap); + try + { + string fullText = stream.ReadToEnd(); + fullText.DeserializeInto(beatmap); + } + catch + { + // Temporary because storyboards are deserialized into beatmaps at the moment + // This try-catch shouldn't exist in the future + } foreach (var hitObject in beatmap.HitObjects) hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); From 9597f9d46ba306fa2be72c46f7fccec7428797a2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 11:10:20 +0900 Subject: [PATCH 140/454] Resharper cleanup --- .../Overlays/Settings/Sections/Maintenance/GeneralSettings.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 9ae331daf0..f0f5b434cd 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -1,15 +1,12 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; -using System.IO; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; -using osu.Game.IO.Serialization; namespace osu.Game.Overlays.Settings.Sections.Maintenance { From ea2c67ca5fe03415fe7a79831738131f8ebb1585 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 11:10:34 +0900 Subject: [PATCH 141/454] Fix incorrect serialization condition --- osu.Game/Audio/SampleInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index edfda3bdc9..f8b5bf33d9 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -28,7 +28,7 @@ namespace osu.Game.Audio set { bank = value; } } - public bool ShouldSerializeBank() => Bank == ControlPoint.SampleBank; + public bool ShouldSerializeBank() => Bank != ControlPoint.SampleBank; /// /// The name of the sample to load. @@ -45,6 +45,6 @@ namespace osu.Game.Audio set { volume = value; } } - public bool ShouldSerializeVolume() => Volume == ControlPoint.SampleVolume; + public bool ShouldSerializeVolume() => Volume != ControlPoint.SampleVolume; } } From 499ecb4eddebc72913a4747715fb111bff7be6bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 11:11:38 +0900 Subject: [PATCH 142/454] Add parity checking OsuJsonDecoder test cases --- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 36 +- ...n - The Unforgiving (Armin) [Marathon].osu | 7102 +++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 4 + osu.Game.Tests/packages.config | 1 + 4 files changed, 7135 insertions(+), 8 deletions(-) create mode 100644 osu.Game.Tests/Resources/Within Temptation - The Unforgiving (Armin) [Marathon].osu diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 1531deb265..c3ebcb9e7c 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; +using DeepEqual.Syntax; using NUnit.Framework; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -18,10 +19,13 @@ namespace osu.Game.Tests.Beatmaps.Formats [TestFixture] public class OsuJsonDecoderTest { + private const string beatmap_1 = "Soleily - Renatus (Gamu) [Insane].osu"; + private const string beatmap_2 = "Within Temptation - The Unforgiving (Armin) [Marathon].osu"; + [Test] public void TestDecodeMetadata() { - var beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + var beatmap = decodeAsJson(beatmap_1); var meta = beatmap.BeatmapInfo.Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); @@ -39,7 +43,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeGeneral() { - var beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + var beatmap = decodeAsJson(beatmap_1); var beatmapInfo = beatmap.BeatmapInfo; Assert.AreEqual(0, beatmapInfo.AudioLeadIn); Assert.AreEqual(false, beatmapInfo.Countdown); @@ -53,7 +57,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeEditor() { - var beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + var beatmap = decodeAsJson(beatmap_1); var beatmapInfo = beatmap.BeatmapInfo; int[] expectedBookmarks = @@ -74,7 +78,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeDifficulty() { - var beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + var beatmap = decodeAsJson(beatmap_1); var difficulty = beatmap.BeatmapInfo.BaseDifficulty; Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(4, difficulty.CircleSize); @@ -87,7 +91,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeColors() { - var beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + var beatmap = decodeAsJson(beatmap_1); Color4[] expected = { new Color4(142, 199, 255, 255), @@ -105,7 +109,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeHitObjects() { - var beatmap = decodeAsJson("Soleily - Renatus (Gamu) [Insane].osu"); + var beatmap = decodeAsJson(beatmap_1); var curveData = beatmap.HitObjects[0] as IHasCurve; var positionData = beatmap.HitObjects[0] as IHasPosition; @@ -124,13 +128,29 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); } + [TestCase(beatmap_1)] + [TestCase(beatmap_2)] + public void TestParity(string beatmap) + { + var beatmaps = decode(beatmap); + beatmaps.jsonDecoded.ShouldDeepEqual(beatmaps.legacyDecoded); + } + /// /// Reads a .osu file first with a , serializes the resulting to JSON /// and then deserializes the result back into a through an . /// /// The .osu file to decode. /// The after being decoded by an . - private Beatmap decodeAsJson(string filename) + private Beatmap decodeAsJson(string filename) => decode(filename).jsonDecoded; + + /// + /// Reads a .osu file first with a , serializes the resulting to JSON + /// and then deserializes the result back into a through an . + /// + /// The .osu file to decode. + /// The after being decoded by an . + private (Beatmap legacyDecoded, Beatmap jsonDecoded) decode(string filename) { using (var stream = Resource.OpenResource(filename)) using (var sr = new StreamReader(stream)) @@ -145,7 +165,7 @@ namespace osu.Game.Tests.Beatmaps.Formats sw.Flush(); ms.Position = 0; - return new OsuJsonDecoder().Decode(sr2); + return (legacyDecoded, new OsuJsonDecoder().Decode(sr2)); } } } diff --git a/osu.Game.Tests/Resources/Within Temptation - The Unforgiving (Armin) [Marathon].osu b/osu.Game.Tests/Resources/Within Temptation - The Unforgiving (Armin) [Marathon].osu new file mode 100644 index 0000000000..4f8e8f820f --- /dev/null +++ b/osu.Game.Tests/Resources/Within Temptation - The Unforgiving (Armin) [Marathon].osu @@ -0,0 +1,7102 @@ +osu file format v9 + +[General] +AudioFilename: Within Temptation - The Unforgiving.mp3 +AudioLeadIn: 2000 +PreviewTime: 2513029 +Countdown: 0 +SampleSet: Soft +StackLeniency: 0.7 +Mode: 0 +LetterboxInBreaks: 1 + +[Editor] +Bookmarks: 3177331 +DistanceSpacing: 0.9 +BeatDivisor: 4 +GridSize: 4 + +[Metadata] +Title:The Unforgiving +Artist:Within Temptation +Creator:Armin +Version:Marathon +Source: +Tags:Gonzvlo narakucrimson Roddie Vass_Bass ErufenRito Glass Card N'FoRcE force HakunoKaemi metal Why not me Shot in the Dark In the Middle of the Night Faster Fire and Ice Iron Where is the edge Sinead Lost Murder A Demon's Fate Stairway to the skies TU Marathon symphonic metal rock Sharon den Adel collab + +[Difficulty] +HPDrainRate:3 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8 +SliderMultiplier:2 +SliderTickRate:1 + +[Events] +//Background and Video events +0,0,"Within-Temptation.png" +Video,-300,"Mother Maiden_xvid_003.avi" +Video,910191,"Fire And Ice_x264.avi" +Video,648748,"17 Faster_x264_001.avi" +Video,1730578,"19 Sinead_xvid_001.avi" +Video,34667,"Within Temptation-Shot In The _x264_001.avi" +//Break Periods +2,31107,46554 +2,116273,123530 +2,193254,209002 +2,325697,336343 +2,442893,451543 +2,533893,549943 +2,644043,670986 +2,744936,750986 +2,886436,932774 +2,1017706,1021899 +2,1141547,1152290 +2,1285058,1290472 +2,1372331,1382290 +2,1476876,1505227 +2,1575917,1584318 +2,1666307,1676266 +2,1714619,1747493 +2,1825109,1829544 +2,1903058,1916980 +2,1935879,1949288 +2,1981007,1993647 +2,2205454,2225075 +2,2290311,2308242 +2,2394266,2400928 +2,2473072,2486674 +2,2513371,2525630 +2,2555908,2578758 +2,2686971,2696967 +2,2787469,2800847 +2,2889559,2901019 +2,2925673,2939538 +2,3002710,3007686 +2,3086784,3102501 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +2406,418.848167539267,4,2,0,100,1,0 +15829,418.848167539267,4,2,0,100,1,0 +34572,565.904267861353,4,1,0,100,1,0 +47011,-100,4,2,1,100,0,0 +95696,566.037735849057,4,2,1,100,1,0 +97111,-100,4,1,0,80,0,1 +97247,-100,4,1,0,82,0,1 +97388,-100,4,1,0,84,0,1 +97530,-100,4,1,0,86,0,1 +97671,-100,4,1,0,90,0,1 +97818,-100,4,1,0,93,0,0 +97960,-100,4,1,0,96,0,1 +98096,-100,4,2,1,100,0,1 +106020,-100,4,1,1,80,0,1 +106233,-100,4,1,1,83,0,1 +106374,-100,4,1,1,86,0,1 +106516,-100,4,1,1,89,0,1 +106657,-100,4,1,1,92,0,1 +106799,-100,4,1,1,95,0,1 +106940,-100,4,2,1,98,0,1 +116073,-100,4,2,1,98,0,0 +124134,-100,4,1,0,100,0,0 +124983,-100,4,2,1,100,0,0 +160926,-100,4,1,0,100,0,0 +161209,-100,4,2,1,100,0,0 +165454,-100,4,1,0,100,0,0 +165737,-100,4,2,1,100,0,0 +169983,-100,4,1,0,100,0,0 +170266,-100,4,2,0,87,0,0 +172671,-100,4,1,0,100,0,0 +173101,-100,4,1,0,81,0,1 +173237,-100,4,1,0,84,0,1 +173379,-100,4,1,0,87,0,1 +173520,-100,4,1,0,90,0,1 +174658,-100,4,2,0,100,0,0 +174941,-100,4,2,1,100,0,1 +182436,-100,4,2,0,100,0,1 +183002,-100,4,1,0,100,0,1 +183714,-100,4,2,0,100,0,0 +183997,-100,4,2,1,100,0,1 +191563,-100,4,1,0,80,0,1 +191775,-100,4,1,0,83,0,1 +191917,-100,4,1,0,86,0,1 +192058,-100,4,1,0,89,0,1 +192200,-100,4,1,0,91,0,1 +192341,-100,4,1,0,93,0,1 +192483,-100,4,1,0,95,0,1 +192624,-100,4,1,0,97,0,1 +192766,-100,4,1,0,98,0,1 +192907,-100,4,1,0,99,0,1 +193054,-100,4,2,0,100,0,0 +209888,-100,4,1,0,63,0,0 +211311,-100,4,2,0,5,0,0 +214708,-100,4,2,0,30,0,0 +215549,-100,4,1,0,15,0,0 +215686,566.037735849057,4,1,0,15,1,0 +215761,-100,4,2,0,5,0,0 +217105,-100,4,2,0,31,0,0 +217884,-100,4,1,0,15,0,0 +218450,-100,4,2,0,30,0,0 +218662,-100,4,1,0,15,0,0 +219299,-100,4,2,0,29,0,0 +219511,-100,4,2,0,36,0,0 +220148,-100,4,1,0,16,0,0 +220289,-100,4,2,0,16,0,0 +220997,-100,4,1,0,16,0,0 +221138,-100,4,2,0,16,0,0 +222341,-100,4,1,0,16,0,0 +222907,-100,4,2,0,16,0,0 +223190,-100,4,1,0,16,0,0 +223473,-100,4,2,0,16,0,0 +224605,-100,4,1,0,16,0,0 +225171,-100,4,2,0,16,0,0 +225454,-100,4,1,0,16,0,0 +226091,-100,4,2,0,16,0,0 +226870,-100,4,1,0,16,0,0 +227436,-100,4,2,0,16,0,0 +227719,-100,4,1,0,16,0,0 +228072,-100,4,2,0,16,0,0 +229134,-100,4,1,0,16,0,0 +229770,-100,4,2,0,16,0,0 +229983,-100,4,1,0,16,0,0 +230195,-100,4,2,0,16,0,0 +231469,-100,4,1,0,16,0,0 +231610,-100,4,2,0,16,0,0 +232318,-100,4,1,0,16,0,0 +232601,-100,4,2,0,33,0,0 +233167,-100,4,2,0,21,0,0 +233733,-100,4,1,0,21,0,0 +243355,-100,4,1,0,41,0,0 +246256,-100,4,1,0,26,0,0 +247954,-100,4,1,0,39,0,0 +250510,-100,4,2,0,40,0,0 +250651,-100,4,2,0,50,0,0 +250793,-100,4,2,0,60,0,0 +251912,-100,4,2,1,100,0,1 +267761,-100,4,2,1,100,0,0 +270025,-100,4,2,1,100,0,1 +270171,-76.9230769230769,4,2,1,83,0,1 +285737,-76.9230769230769,4,1,0,36,0,1 +286162,-76.9230769230769,4,1,0,45,0,1 +286445,-76.9230769230769,4,1,0,52,0,1 +286728,-76.9230769230769,4,1,0,60,0,1 +287011,-76.9230769230769,4,1,0,68,0,1 +287294,-76.9230769230769,4,1,0,76,0,1 +287577,-76.9230769230769,4,1,1,83,0,1 +287855,-76.9230769230769,4,2,1,100,0,0 +288138,-76.9230769230769,4,2,1,100,0,1 +296068,-76.9230769230769,4,1,1,54,0,1 +296351,-76.9230769230769,4,1,1,60,0,1 +296634,-76.9230769230769,4,1,1,71,0,1 +296923,-76.9230769230769,4,2,1,93,0,0 +297195,-76.9230769230769,4,2,1,100,0,1 +306252,-76.9230769230769,4,2,1,100,0,0 +324082,-76.9230769230769,4,2,1,100,0,0 +324152,-76.9230769230769,4,2,1,65,0,0 +337093,300,4,2,1,40,1,0 +346663,-100,4,1,0,70,0,0 +346851,-100,4,2,1,70,0,0 +366193,-100,4,2,1,70,0,0 +384870,-100,4,2,0,70,0,0 +384943,-100,4,1,0,70,0,0 +385138,-100,4,2,1,70,0,0 +388663,-100,4,1,0,70,0,0 +389001,-100,4,2,1,70,0,0 +399493,-100,4,1,0,70,0,0 +399651,-100,4,2,1,70,0,0 +408343,-100,4,1,0,70,0,0 +408943,-100,4,1,0,70,0,0 +409093,-100,4,1,0,70,0,0 +409168,-100,4,2,1,70,0,0 +416593,-100,4,1,0,70,0,0 +416670,-100,4,2,0,70,0,0 +416743,-100,4,1,0,50,0,0 +416893,-100,4,1,0,80,0,0 +416968,-100,4,2,0,70,0,0 +417043,-100,4,1,0,50,0,0 +417193,-100,4,1,0,90,0,0 +417270,-100,4,2,0,70,0,0 +417493,-100,4,1,0,100,0,0 +417568,-100,4,2,0,70,0,0 +418693,-100,4,2,1,70,0,1 +428226,-100,4,1,0,70,0,1 +428451,-100,4,2,1,70,0,1 +436634,-100,4,2,0,70,0,1 +436784,-100,4,1,0,70,0,1 +437830,-100,4,1,0,70,0,0 +437893,-100,4,1,0,70,0,1 +442693,-100,4,1,0,70,0,0 +452001,-100,4,2,1,70,0,0 +461293,-100,4,1,0,70,0,0 +461976,-100,4,2,1,70,0,0 +470293,-100,4,1,0,70,0,0 +471576,-100,4,2,0,70,0,0 +471726,-100,4,1,0,70,0,0 +471876,-100,4,2,1,70,0,0 +475693,-100,4,1,0,70,0,0 +475926,-100,4,1,0,70,0,0 +476526,-100,4,1,0,70,0,0 +476676,-100,4,2,1,70,0,0 +479893,-100,4,1,0,70,0,0 +481176,-100,4,2,0,70,0,0 +481326,-100,4,1,0,70,0,0 +481476,-100,4,2,1,70,0,0 +490693,-100,4,1,0,70,0,1 +490851,-100,4,2,1,70,0,1 +499693,-100,4,1,0,60,0,1 +499993,-100,4,1,0,65,0,1 +500143,-100,4,1,0,70,0,0 +500293,-100,4,1,0,70,0,1 +501276,-100,4,2,0,70,0,1 +501426,-100,4,1,0,70,0,1 +501876,-100,4,2,0,70,0,1 +502326,-100,4,1,0,70,0,1 +502776,-100,4,2,0,70,0,1 +502926,-100,4,1,0,70,0,1 +509293,-100,4,1,0,60,0,1 +509593,-100,4,1,0,65,0,1 +514693,-100,4,1,0,70,0,0 +514709,-100,4,1,0,70,0,0 +563151,-83.3333333333333,4,1,0,70,0,0 +574251,-76.9230769230769,4,1,0,70,0,0 +581893,-76.9230769230769,4,1,0,70,0,1 +591193,-76.9230769230769,4,1,0,70,0,0 +591493,-76.9230769230769,4,1,0,70,0,1 +601093,-76.9230769230769,4,1,0,70,0,1 +603726,-83.3333333333333,4,1,0,70,0,1 +605893,-83.3333333333333,4,1,0,70,0,0 +634993,-83.3333333333333,4,1,0,20,0,0 +648736,500,4,2,1,100,1,0 +710611,-100,4,1,0,63,0,0 +710781,-100,4,1,0,65,0,0 +710857,-100,4,1,0,70,0,0 +710982,-100,4,1,0,76,0,0 +711107,-100,4,1,0,79,0,0 +711232,-100,4,1,0,85,0,0 +711357,-100,4,1,0,91,0,0 +711482,-100,4,1,0,94,0,0 +711781,-100,4,1,0,100,0,0 +712736,-76.9230769230769,4,1,0,100,0,1 +712798,-76.9230769230769,4,2,1,100,0,1 +720498,-76.9230769230769,4,1,0,100,0,0 +720736,-76.9230769230769,4,1,0,100,0,1 +721986,-76.9230769230769,4,2,1,100,0,1 +727986,-76.9230769230769,4,1,0,80,0,1 +728111,-76.9230769230769,4,1,0,83,0,1 +728236,-76.9230769230769,4,1,0,86,0,1 +728361,-76.9230769230769,4,1,0,89,0,1 +728486,-76.9230769230769,4,1,0,92,0,1 +728623,-76.9230769230769,4,1,0,95,0,0 +728736,-76.9230769230769,4,1,0,98,0,1 +744736,-100,4,1,0,100,0,0 +751234,-100,4,2,1,100,0,0 +752611,-100,4,1,0,100,0,0 +752861,-100,4,2,1,100,0,0 +759611,-100,4,1,0,100,0,0 +759861,-100,4,2,0,100,0,0 +760173,-100,4,1,0,100,0,0 +760361,-100,4,2,0,100,0,0 +760673,-100,4,1,0,100,0,0 +760861,-100,4,2,1,100,0,0 +784548,-100,4,2,0,100,0,0 +785111,-100,4,1,0,100,0,0 +785361,-83.3333333333333,4,1,0,100,0,0 +785548,-83.3333333333333,4,2,0,100,0,0 +786111,-83.3333333333333,4,1,0,100,0,0 +786361,-71.4285714285714,4,1,0,100,0,0 +786548,-71.4285714285714,4,2,0,100,0,0 +787111,-62.5,4,1,0,100,0,0 +787548,-62.5,4,2,0,100,0,0 +788111,-55.5555555555556,4,1,0,100,0,0 +788548,-55.5555555555556,4,2,0,100,0,0 +789111,-55.5555555555556,4,1,0,100,0,0 +789361,-50,4,1,0,100,0,0 +789548,-50,4,2,0,100,0,0 +790111,-76.9230769230769,4,1,0,100,0,0 +790736,-76.9230769230769,4,1,0,80,0,0 +790986,-76.9230769230769,4,1,0,85,0,0 +791236,-76.9230769230769,4,1,0,90,0,0 +791486,-76.9230769230769,4,1,0,95,0,0 +791736,-76.9230769230769,4,1,0,100,0,0 +792736,-76.9230769230769,4,1,0,100,0,1 +800486,-76.9230769230769,4,1,0,100,0,0 +800736,-76.9230769230769,4,1,0,100,0,1 +808611,-76.9230769230769,4,1,0,100,0,0 +808736,-76.9230769230769,4,1,0,100,0,1 +824611,-76.9230769230769,4,1,0,100,0,0 +824736,-76.9230769230769,4,1,0,100,0,1 +824861,-76.9230769230769,4,1,0,100,0,0 +852361,-76.9230769230769,4,2,0,100,0,0 +858736,-76.9230769230769,4,1,0,100,0,0 +860736,-66.6666666666667,4,1,0,100,0,1 +868486,-66.6666666666667,4,1,0,100,0,0 +868736,-66.6666666666667,4,1,0,100,0,1 +872361,-66.6666666666667,4,1,0,100,0,1 +873861,-66.6666666666667,4,1,0,100,0,1 +884486,-66.6666666666667,4,1,0,100,0,0 +884736,-66.6666666666667,4,1,0,100,0,1 +886236,-66.6666666666667,4,1,0,100,0,0 +912957,856.959223023638,4,2,0,21,1,0 +941887,-117.647058823529,4,2,0,21,0,0 +972298,-100,4,2,0,21,0,0 +974654,-100,4,2,0,21,0,0 +991894,-117.647058823529,4,2,0,21,0,0 +992858,-100,4,2,0,21,0,0 +1015131,-90.9090909090909,4,2,0,21,0,0 +1015452,-90.9090909090909,4,2,0,5,0,0 +1017701,-100,4,2,0,15,0,0 +1022649,428.571428571429,4,2,1,78,1,0 +1030031,-90.9090909090909,4,2,0,79,0,0 +1031103,-100,4,2,1,79,0,0 +1049960,-100,4,1,0,60,0,0 +1050174,-100,4,2,0,100,0,0 +1051674,-100,4,1,0,60,0,0 +1051889,-100,4,2,0,100,0,0 +1052103,-100,4,1,0,60,0,0 +1052317,-100,4,2,0,100,0,0 +1052531,-100,4,1,0,60,0,0 +1052746,-100,4,2,0,100,0,0 +1052960,-100,4,1,0,60,0,0 +1053174,-100,4,2,0,100,0,0 +1053389,-100,4,1,0,80,0,0 +1053603,-100,4,2,0,100,0,0 +1054246,-100,4,1,0,80,0,0 +1054460,-100,4,2,1,81,0,0 +1067103,-100,4,1,0,80,0,0 +1067317,-100,4,2,1,100,0,0 +1069674,-100,4,1,0,85,0,0 +1069996,-100,4,2,1,85,0,0 +1070531,-100,4,1,0,68,0,0 +1070746,-100,4,2,0,100,0,0 +1071389,-100,4,1,0,80,0,0 +1071603,-100,4,2,0,100,0,0 +1072246,-100,4,1,0,60,0,0 +1072460,-100,4,2,0,100,0,0 +1073103,-100,4,1,0,80,0,0 +1073317,-100,4,2,0,100,0,0 +1073960,-100,4,1,0,60,0,0 +1074174,-100,4,2,0,100,0,0 +1074389,-100,4,1,0,60,0,0 +1074603,-100,4,2,0,100,0,0 +1074817,-100,4,1,0,80,0,0 +1075031,-100,4,2,0,100,0,0 +1075674,-100,4,1,0,60,0,0 +1076317,-100,4,2,0,100,0,0 +1076531,-100,4,1,0,80,0,0 +1076746,-100,4,2,0,100,0,0 +1077389,-100,4,2,1,81,0,0 +1081246,-90.9090909090909,4,2,1,82,0,0 +1082317,-100,4,2,1,80,0,0 +1083281,-100,4,2,1,80,0,0 +1093460,-90.9090909090909,4,2,1,80,0,0 +1094317,-100,4,2,1,80,0,0 +1105674,-100,4,2,1,80,0,0 +1118531,-100,4,1,0,80,0,0 +1118746,-100,4,2,1,80,0,0 +1122014,-100,4,1,0,80,0,0 +1122174,-100,4,2,1,80,0,0 +1128817,-133.333333333333,4,2,1,80,0,0 +1135674,-100,4,2,0,22,0,0 +1137505,444.444444444444,4,2,0,22,1,0 +1139282,461.538461538462,4,2,0,16,1,0 +1140205,571.428571428571,4,2,0,19,1,0 +1141347,571.428571428571,4,2,0,19,1,0 +1150313,454.545454545455,4,2,0,100,1,0 +1153040,-100,4,2,1,100,0,0 +1226981,-72.992700729927,4,2,1,100,0,0 +1241222,-68.0272108843537,4,2,1,100,0,1 +1284868,-105.042016806723,4,1,0,100,0,0 +1291224,-80.7754442649435,4,1,0,100,0,0 +1291299,-80.7754442649435,4,2,1,100,0,0 +1291375,-80.7754442649435,4,1,0,100,0,0 +1291451,-80.7754442649435,4,2,1,100,0,0 +1291527,-80.7754442649435,4,1,0,100,0,0 +1291602,-80.7754442649435,4,2,1,100,0,0 +1291678,-74.6268656716418,4,1,0,100,0,0 +1292133,-72.7272727272727,4,2,1,100,0,0 +1328494,-72.992700729927,4,2,1,100,0,1 +1342147,-72.992700729927,4,2,1,100,0,1 +1342737,-72.992700729927,4,2,1,100,0,0 +1343040,-72.992700729927,4,2,1,100,0,1 +1345025,-63.2911392405063,4,2,1,100,0,1 +1345935,-72.992700729927,4,2,1,100,0,1 +1357585,-72.992700729927,4,2,1,100,0,0 +1372139,-72.992700729927,4,2,1,100,0,0 +1408427,-86.2068965517241,4,2,1,100,0,0 +1409571,-87.5656742556918,4,2,1,100,0,0 +1412071,-87.5656742556918,4,1,0,100,0,0 +1412222,-87.5656742556918,4,2,1,100,0,0 +1415767,-72.992700729927,4,2,1,100,0,1 +1430009,-72.992700729927,4,2,1,100,0,0 +1430312,-72.992700729927,4,2,1,100,0,1 +1430632,-63.2911392405063,4,2,1,100,0,1 +1444706,-63.2911392405063,4,2,1,100,0,0 +1444858,-63.2911392405063,4,2,1,100,0,1 +1473949,-63.2911392405063,4,2,1,100,0,0 +1491562,389.61038961039,4,1,0,80,1,0 +1506367,-200,4,1,0,80,0,0 +1507146,-100,4,1,0,80,0,0 +1550782,-100,4,1,0,80,0,1 +1575717,-100,4,1,0,80,0,0 +1584873,-100,4,2,0,70,0,0 +1587511,-100,4,1,0,80,0,0 +1589938,-200,4,1,0,80,0,0 +1590523,-100,4,1,0,80,0,0 +1619354,-100,4,1,0,80,0,1 +1644289,-100,4,1,0,80,0,0 +1662990,-100,4,2,0,40,0,0 +1678575,-100,4,2,0,70,0,0 +1688315,-100,4,1,0,40,0,0 +1688510,-100,4,1,0,50,0,0 +1688704,-100,4,1,0,60,0,0 +1688899,-100,4,1,0,70,0,0 +1689094,-100,4,1,0,80,0,0 +1689289,-100,4,1,0,80,0,0 +1689484,-100,4,1,0,80,0,0 +1691042,-100,4,1,0,80,0,1 +1714419,-100,4,2,0,40,0,0 +1730551,512.820512820513,4,1,0,100,1,0 +1747667,-181.818181818182,4,2,0,35,0,0 +1763137,-120.481927710843,4,2,1,70,0,0 +1795191,-120.481927710843,4,2,1,90,0,0 +1796192,-90.9090909090909,4,2,1,100,0,1 +1824909,-100,4,2,1,100,0,0 +1830166,-119.760479041916,4,2,1,100,0,0 +1874140,-90.9090909090909,4,2,1,100,0,1 +1902858,-100,4,2,1,100,0,0 +1917477,-120.481927710843,4,2,1,100,0,0 +1950551,-120.481927710843,4,2,1,70,0,0 +1951063,-120.481927710843,4,2,1,60,0,0 +1952088,-90.9090909090909,4,2,1,100,0,1 +1967986,-90.9090909090909,4,2,1,100,0,0 +1968499,-90.9090909090909,4,2,1,100,0,1 +1980807,-90.9090909090909,4,2,1,100,0,0 +1994397,428.571428571429,3,2,0,30,1,0 +1995484,-100,3,2,0,48,0,0 +1995913,-100,3,2,0,30,0,0 +2000627,-100,3,2,0,51,0,0 +2001055,-100,3,2,0,30,0,0 +2045825,-100,3,2,0,40,0,0 +2070163,-100,3,1,0,60,0,0 +2070323,-100,3,2,0,60,0,0 +2071539,-100,3,1,0,50,0,0 +2072825,-100,3,1,0,60,0,0 +2073055,-100,3,2,0,60,0,0 +2073913,-100,3,2,1,60,0,0 +2130682,-100,3,2,1,60,0,1 +2141198,-87.719298245614,3,2,1,60,0,1 +2142484,-100,3,2,1,60,0,1 +2153825,-100,3,2,1,60,0,0 +2189734,-87.719298245614,3,2,1,60,0,0 +2197448,-100,3,2,1,60,0,0 +2245012,-100,3,1,0,60,0,0 +2246397,-100,3,1,0,60,0,1 +2246619,-100,3,2,1,60,0,1 +2269111,-100,3,2,1,60,0,0 +2269539,-100,3,2,1,60,0,1 +2290111,-100,3,2,1,60,0,0 +2308992,447.761194029851,4,2,0,29,1,0 +2316044,-100,4,2,1,51,0,0 +2336305,-100,4,1,0,41,0,0 +2337318,-100,4,2,1,51,0,0 +2350969,-100,4,1,0,41,0,0 +2351865,-133.333333333333,4,2,1,60,0,0 +2365521,-133.333333333333,4,1,0,20,0,0 +2365745,-133.333333333333,4,1,0,30,0,0 +2365969,-100,4,1,0,50,0,0 +2366305,-100,4,1,2,60,0,1 +2379408,-100,4,1,2,70,0,1 +2381087,-100,4,1,2,60,0,1 +2394962,-100,4,1,2,60,0,0 +2402350,-100,4,2,1,55,0,0 +2415223,-100,4,1,0,50,0,0 +2416566,-100,4,2,1,50,0,0 +2429775,-100,4,1,0,50,0,0 +2430671,-133.333333333333,4,2,1,50,0,0 +2444327,-133.333333333333,4,2,1,40,0,0 +2444551,-133.333333333333,4,2,1,50,0,0 +2445111,-100,4,1,2,60,0,1 +2470186,-100,4,1,0,65,0,0 +2488096,-100,4,1,0,65,0,1 +2488214,-100,4,1,0,65,0,0 +2502424,-100,4,1,0,65,0,1 +2502542,-100,4,1,0,65,0,0 +2526162,-100,4,1,0,40,0,0 +2526721,-100,4,1,0,50,0,0 +2527169,-100,4,1,0,60,0,0 +2527499,-100,4,1,2,65,0,1 +2541380,-100,4,1,2,65,0,0 +2541827,-100,4,1,2,65,0,1 +2553021,-100,4,1,2,65,0,1 +2555596,-100,4,1,2,5,0,1 +2556156,-100,4,1,2,56,0,0 +2565180,447.761194029851,4,1,0,100,1,0 +2579508,-100,4,2,0,80,0,0 +2586112,-100,4,1,0,69,0,0 +2643985,-100,4,1,0,69,0,1 +2658314,-100,4,1,0,69,0,1 +2671747,-100,4,1,0,69,0,1 +2672642,-100,4,1,0,69,0,0 +2701539,-90.9090909090909,4,1,0,69,0,0 +2715643,-100,4,1,0,69,0,0 +2729956,-100,4,1,0,69,0,1 +2758612,-76.9230769230769,4,1,0,69,0,0 +2761875,-66.6666666666667,4,1,0,69,0,0 +2801605,-133.333333333333,4,1,0,60,0,0 +2815926,-100,4,1,0,60,0,0 +2844582,-100,4,1,0,60,0,1 +2855329,-100,4,1,0,60,0,0 +2858911,-100,4,1,0,60,0,1 +2873239,-100,4,1,0,60,0,0 +2889304,-100,4,2,0,5,0,0 +2895844,370.37037037037,4,2,0,100,1,0 +2901399,-166.666666666667,4,2,0,50,0,0 +2924269,-200,4,2,0,50,0,0 +2940108,-200,4,2,1,50,0,0 +2952145,-100,4,2,1,75,0,0 +2960479,-133.333333333333,4,2,1,75,0,0 +2962331,-100,4,2,1,75,0,0 +2976220,-100,4,2,1,75,0,0 +3016775,-133.333333333333,4,2,1,75,0,0 +3018627,-100,4,2,1,75,0,0 +3057232,-151.515151515152,4,1,0,60,0,0 +3058719,-100,4,2,0,75,0,0 +3059182,-100,4,2,1,75,0,0 +3082562,-100,4,2,0,10,0,0 +3086775,-100,4,2,0,75,0,0 +3103066,-151.515151515152,4,1,0,60,0,0 +3105571,-151.515151515152,4,2,0,75,0,0 +3106126,-151.515151515152,4,1,0,60,0,0 +3108066,-151.515151515152,4,2,0,75,0,0 +3108437,-151.515151515152,4,1,0,60,0,0 +3108626,-151.515151515152,4,2,0,75,0,0 +3109177,-151.515151515152,4,1,0,60,0,0 +3111029,-151.515151515152,4,2,0,75,0,0 +3111400,-151.515151515152,4,1,0,60,0,0 +3111589,-151.515151515152,4,2,0,75,0,0 +3112140,-151.515151515152,4,1,0,60,0,0 +3117978,-100,4,2,1,75,0,0 +3126214,-100,4,2,1,75,0,0 +3165474,-100,4,2,1,75,0,1 +3176960,-100,4,2,1,75,0,0 +3177325,-100,4,2,1,75,0,1 +3177695,-100,4,2,1,75,0,0 + + +[Colours] +Combo1 : 12,50,233 +Combo2 : 253,225,28 +Combo3 : 185,17,17 +Combo4 : 30,136,35 +SliderBorder : 0,0,0 + +[HitObjects] +40,48,2405,1,4 +144,344,2824,1,0 +256,48,3243,1,2 +368,344,3662,1,0 +472,48,4081,1,2 +336,216,4500,2,0,B|308:146|192:136|171:225|171:225,1,200,0|2 +256,256,5128,1,0 +256,336,5337,1,0 +92,88,5756,5,2 +256,144,6175,1,0 +420,88,6594,1,2 +400,252,7013,1,0 +256,336,7432,1,2 +112,252,7851,1,0 +92,88,8269,1,2 +336,172,8688,6,0,B|308:102|192:92|171:181|171:181,1,200,0|2 +348,348,9526,1,0 +164,348,9945,1,2 +80,160,10364,1,0 +256,40,10782,1,2 +432,160,11201,1,0 +256,196,11620,1,2 +256,196,11830,1,0 +256,196,12039,1,0 +176,316,12458,6,0,B|204:386|320:396|341:307|341:307,1,200,2|0 +80,224,13296,1,2 +256,44,13714,1,0 +432,224,14133,1,2 +112,92,14552,1,0 +256,272,14971,1,2 +400,92,15390,1,0 +456,336,15829,5,4 +256,44,16247,1,0 +56,336,16666,1,2 +256,272,17085,1,0 +88,68,17504,1,2 +424,68,17923,1,0 +168,208,18342,1,2 +256,168,18551,1,0 +344,208,18760,1,0 +176,312,19179,6,0,B|204:382|320:392|341:303|341:303,1,200,2|0 +472,192,20017,1,2 +256,40,20436,1,0 +40,192,20855,1,2 +256,348,21274,1,0 +170,80,21692,2,0,B|179:110|211:135|252:141|252:141|280:151|302:184|280:217|256:223|227:217|199:184|225:151|252:141|252:141|296:135|323:114|337:72,1,400,2|2 +432,288,22949,5,0 +256,352,23368,1,2 +80,288,23787,1,0 +164,159,24205,2,0,B|208:128|256:168|256:168|304:128|348:158|348:158,1,200,2|0 +180,36,25043,1,2 +256,60,25253,1,0 +332,36,25462,1,0 +472,120,25881,6,0,B|402:148|392:264|481:285|481:285,1,200,2|0 +336,372,26719,2,0,B|308:302|192:292|171:381|171:381,1,200,2|0 +40,280,27556,2,0,B|110:252|120:136|31:115|31:115,1,200,2|0 +180,68,28394,5,2 +256,20,28603,1,0 +332,68,28813,1,0 +302,153,29022,1,0 +208,152,29232,1,0 +256,192,29336,12,0,30907 +40,116,47304,5,2 +100,44,47587,1,0 +172,104,47870,2,0,B|200:174|316:184|337:95|337:95,1,200,0|2 +412,44,48719,1,0 +472,116,49002,1,0 +428,304,49568,5,2 +354,355,49851,1,0 +342,265,50134,1,0 +168,265,50700,1,2 +156,355,50983,1,0 +82,304,51266,1,0 +212,171,51832,6,0,B|228:191|256:195|256:195|284:191|300:171,1,100,2|0 +333,86,52397,2,0,B|312:44|256:28|256:28|204:40|177:87,1,200,0|0 +256,112,53246,1,2 +255,112,53529,1,0 +340,252,54095,2,0,B|360:348|360:348,1,100,2|0 +340,252,54661,2,0,B|312:322|196:332|175:243|175:243,1,200,0|2 +151,349,55510,2,0,B|152:348|180:252,1,100,0|0 +256,112,56359,5,2 +300,40,56642,1,0 +212,40,56925,1,0 +184,129,57208,2,0,B|190:177|240:200|272:200|324:184|329:130|329:130,1,200,0|2 +348,227,58057,2,0,B|314:260|257:270|201:259|164:228,1,200,0|0 +104,288,58905,5,2 +256,368,59471,1,0 +408,288,60037,1,2 +408,288,60320,1,0 +256,200,60886,5,0 +336,160,61169,2,0,B|308:95|199:74|172:168|172:168,1,200,2|0 +300,28,62301,1,2 +212,32,62584,1,0 +212,32,63150,1,0 +300,32,63433,1,2 +342,197,63999,6,0,B|325:162|279:159|255:183|255:183|230:158|184:162|169:197|169:197,1,200,0|2 +168,280,64847,2,0,B|185:315|232:318|256:294|256:294|280:318|326:315|344:280|344:280,1,200,0|2 +432,272,65696,5,0 +400,356,65979,1,0 +336,188,66545,1,2 +256,144,66828,1,8 +176,188,67111,1,0 +112,28,67677,5,2 +84,112,67960,1,8 +20,44,68243,1,0 +216,88,68809,2,0,B|229:52|285:46|295:91|295:91,1,100,2|8 +256,164,69375,1,0 +432,112,69941,5,2 +492,48,70223,1,8 +400,28,70506,1,0 +332,196,71072,5,2 +256,244,71355,1,8 +180,196,71638,1,0 +336,284,72204,2,0,B|328:340|376:372|376:372,1,100,2|8 +336,284,72770,2,0,B|308:354|192:364|171:275|171:275,1,200,0|2 +137,371,73619,2,0,B|136:372|185:344|175:284,1,100,8|0 +156,108,74468,5,2 +212,36,74751,1,8 +300,36,75034,1,0 +356,108,75317,1,0 +256,124,75883,5,8 +256,124,76165,1,0 +384,332,76731,1,0 +384,332,77014,1,8 +256,220,77580,5,0 +128,332,78146,1,8 +128,332,78429,1,0 +256,308,78995,5,0 +256,308,79278,1,8 +300,232,79561,1,0 +256,220,79702,1,0 +212,232,79844,1,0 +112,92,80410,1,8 +176,32,80693,2,0,B|202:104|314:115|334:26|334:26,1,200,0|0 +400,92,81542,1,8 +332,248,82107,2,0,B|307:287|271:282|271:282|255:302|255:302|239:282|239:282|203:290|178:248,1,200,0|8 +104,300,82956,5,0 +168,364,83239,1,2 +256,380,83522,1,0 +344,364,83805,1,8 +408,300,84088,1,0 +336,240,84371,6,4,B|310:312|198:323|178:234|178:234,1,200,2|8 +156,152,85220,2,0,B|396:152|396:152,1,200,2|0 +336,64,86069,2,0,B|310:-8|198:-19|178:70|178:70,1,200,8|2 +48,184,87201,6,0,B|8:284|8:284,1,100,8|0 +100,264,87767,2,0,B|124:360|124:360,1,100,2|0 +188,300,88332,2,0,B|276:360|276:360,1,100,8|0 +316,280,88898,5,2 +376,344,89464,1,8 +376,344,89747,1,0 +408,260,90313,1,0 +408,260,90596,1,8 +480,316,91162,1,2 +464,200,91728,6,0,B|504:100|504:100,1,100,8|0 +412,120,92294,2,0,B|388:24|388:24,1,100,2|0 +324,84,92860,2,0,B|236:24|236:24,1,100,8|0 +196,104,93426,1,2 +256,192,93567,12,0,95696 +448,352,96262,5,2 +376,296,96545,1,0 +304,352,96828,1,2 +240,288,97111,1,0 +200,272,97252,1,0 +184,232,97394,1,0 +200,192,97535,1,0 +240,176,97677,1,0 +280,192,97818,1,0 +296,232,97960,5,4 +376,200,98243,2,0,B|488:200|488:200,1,100,2|8 +512,120,98809,2,0,B|512:16|512:16,1,100,0|8 +432,64,99375,5,2 +360,8,99658,1,8 +310,88,99941,2,0,B|340:125|338:201|254:252|176:198|152:135|204:82|204:82,1,300,2|8 +152,8,101073,1,2 +80,64,101356,1,8 +16,128,101639,6,0,B|16:240|16:240,1,100,2|8 +96,272,102205,2,0,B|160:220|260:296|192:368|192:368,1,200,2|0 +288,384,103054,2,0,B|320:352|320:352|400:352|400:352,1,100,8|0 +447,299,103620,1,10 +363,264,103903,1,0 +305,199,104186,2,0,B|287:169|287:138|318:107|318:107|349:76|349:30|287:15|271:45|256:61|256:61|241:45|225:15|163:30|163:76|194:107|194:107|225:138|225:169|208:199,1,500,8|2 +112,208,105884,5,0 +40,160,106167,1,0 +24,200,106309,1,0 +32,248,106450,1,0 +56,280,106592,1,0 +96,296,106733,1,0 +144,292,106875,1,0 +184,272,107016,6,0,B|296:272|296:272,1,100,4|0 +328,352,107582,2,0,B|216:352|216:352,1,100,8|0 +376,272,108148,2,0,B|456:352|456:352,1,100,2|0 +488,264,108714,2,0,B|408:184|408:184,1,100,8|0 +328,196,109280,1,2 +256,144,109563,1,0 +184,196,109846,1,8 +160,108,110129,6,0,B|216:12|216:12,1,100,0|2 +301,25,110695,2,0,B|296:16|352:112,1,100,0|8 +256,144,111262,2,0,B|256:352|256:352,1,200,2|0 +344,368,112111,2,0,B|440:296|440:296,1,100,8|0 +168,368,112677,2,0,B|72:296|72:296,1,100,2|0 +177,242,113243,6,0,B|202:312|316:323|340:236|340:236,1,200,8|2 +256,200,114092,1,0 +320,136,114375,6,0,B|336:96|376:72|424:96|424:96,2,100,8|0|2 +256,72,115224,1,0 +192,136,115507,2,0,B|176:96|136:70|88:96|88:96,2,100,8|8|4 +172,64,124280,5,0 +256,36,124563,1,0 +340,64,124846,1,0 +256,224,125412,1,2 +256,136,125695,1,8 +332,184,125979,6,0,B|404:210|415:322|326:342|326:342,1,200,0|2 +256,376,126828,1,8 +180,340,127111,2,0,B|108:314|97:202|186:182|186:182,1,200,0|2 +256,136,127960,1,8 +256,224,128243,1,0 +380,84,128809,6,0,B|472:28,1,100,2|8 +476,120,129375,2,0,B|368:184|368:184|368:260,1,200,0|0 +456,296,130224,1,8 +384,348,130507,1,0 +128,348,131073,5,2 +56,296,131356,1,8 +144,258,131639,2,0,B|144:184|144:184|36:120,1,200,0|0 +46,32,132488,2,0,B|132:84,1,100,8|0 +256,196,133337,5,2 +300,124,133620,1,8 +212,124,133903,1,0 +184,213,134186,2,0,B|190:261|240:284|272:284|324:268|329:214|329:214,1,200,0|8 +256,196,135035,1,0 +412,284,135601,5,0 +412,284,135884,1,8 +256,372,136450,1,0 +100,284,137016,1,8 +100,284,137299,1,0 +28,229,137582,2,0,B|108:40|108:40,1,200,0|8 +484,163,138714,2,0,B|404:352|404:352,1,200,0|8 +316,348,139563,5,0 +196,44,140412,1,8 +256,196,140979,1,0 +352,96,141545,6,0,B|365:60|421:54|431:99|431:99,1,100,8|0 +296,260,142111,2,0,B|283:296|227:302|217:257|217:257,1,100,0|0 +84,96,142677,2,0,B|97:60|153:54|163:99|163:99,1,100,8|0 +336,176,143526,5,0 +256,132,143809,1,8 +176,176,144092,1,0 +80,328,144658,5,0 +148,268,144941,2,0,B|216:360|216:360,1,100,8|0 +432,328,145790,1,0 +364,268,146073,2,0,B|296:360|296:360,1,100,8|0 +260,182,146922,1,0 +256,264,147205,1,8 +260,180,147488,1,0 +80,56,148054,5,0 +148,116,148337,2,0,B|216:24|216:24,1,100,8|0 +432,56,149186,1,0 +364,116,149469,2,0,B|296:24|296:24,1,100,8|0 +444,204,150318,5,0 +420,292,150601,1,8 +344,340,150884,1,0 +168,340,151450,1,0 +92,292,151733,1,8 +68,204,152016,1,0 +256,216,152865,5,8 +256,216,153148,1,0 +360,92,153714,5,0 +300,24,153997,1,8 +212,24,154280,1,0 +152,92,154563,1,0 +211,267,155129,2,0,B|227:291|305:301|313:244|313:244,1,100,8|0 +256,188,155695,1,0 +165,339,156262,2,0,B|213:380|313:386|352:337|352:337,1,200,8|0 +256,188,157394,1,8 +200,128,157677,2,0,B|170:91|172:15|256:-36|334:18|358:81|306:134|306:134,1,300,0|8 +439,58,159092,5,0 +439,58,159658,5,8 +420,149,159941,1,0 +348,198,160224,1,8 +256,192,160507,1,0 +163,185,160790,1,8 +91,234,161073,1,0 +72,325,161356,1,4 +256,348,162205,5,0 +256,192,162771,5,0 +192,132,163054,1,8 +212,92,163195,1,0 +256,76,163337,1,0 +300,92,163479,1,0 +320,132,163620,1,0 +72,58,164186,5,8 +91,149,164469,1,0 +163,198,164752,1,8 +256,192,165035,1,0 +348,185,165318,1,8 +420,234,165601,1,0 +439,325,165884,1,8 +184,304,166733,6,0,B|190:352|240:375|272:375|324:359|329:305|329:305,1,200 +256,256,167582,1,8 +256,168,167865,1,0 +72,120,168714,5,8 +128,48,168997,1,0 +212,16,169280,1,8 +300,16,169563,1,0 +384,48,169846,1,8 +440,120,170129,1,0 +256,192,170412,12,0,172677 +36,64,173101,5,0 +36,64,173243,1,0 +36,64,173384,1,0 +36,64,173526,1,0 +96,132,173809,1,0 +168,80,174092,1,0 +212,64,174233,1,0 +252,80,174375,1,0 +264,124,174516,1,0 +288,160,174658,1,0 +332,168,174799,1,0 +372,148,174941,5,4 +428,76,175224,2,0,B|496:164|496:164,1,100,0|8 +433,225,175790,2,0,B|404:326|404:326,1,100,0|2 +320,292,176356,1,0 +272,368,176639,1,8 +196,320,176922,2,0,B|160:364|88:360|40:304|80:244|80:244,1,200 +112,168,177771,2,0,B|108:104|44:88|44:88,1,100,8|0 +128,32,178337,6,0,B|236:32|236:32,1,100,2|0 +272,112,178903,1,8 +356,80,179186,2,0,B|396:40|472:52|508:132|436:180|436:180,1,200 +384,232,180035,2,0,B|340:276|372:332|372:332,1,100,8|0 +284,360,180601,2,0,B|164:360,1,100,2|0 +124,300,181167,2,0,B|66:343|-4:276|33:216|72:200|72:200|105:183|173:149|86:10|12:108|12:108,1,400,8|8 +4,16,182582,5,0 +92,8,182865,1,2 +168,56,183148,1,0 +208,76,183290,1,0 +232,112,183431,1,0 +256,152,183573,1,0 +280,112,183714,1,0 +304,76,183856,1,0 +344,56,183997,1,4 +420,8,184280,1,0 +508,16,184563,1,8 +484,102,184846,6,0,B|424:92|391:141|391:141,1,100,0|2 +369,222,185412,2,0,B|439:321|439:321,1,100,0|8 +342,362,185979,2,0,B|316:322|256:308|200:320|168:364,1,200,2|0 +85,303,186828,2,0,B|73:321|143:222,1,100,8|0 +117,136,187394,2,0,B|121:141|88:92|28:102,1,100,2|0 +124,32,187960,5,8 +220,104,188243,2,0,B|199:79|201:28|257:-5|309:30|325:72|290:108|290:108,1,200,2|0 +388,32,189092,1,8 +460,100,189375,5,0 +392,172,189658,1,2 +468,236,189941,1,0 +398,303,190224,2,0,B|383:345|327:359|298:317|327:288|312:232|270:246|256:352|256:352|241:246|199:232|185:288|213:317|185:359|114:345|114:303,1,500,8|0 +68,308,191780,5,0 +26,288,191922,1,0 +16,245,192063,1,0 +40,207,192205,1,0 +81,189,192346,1,0 +110,154,192488,1,0 +125,114,192629,1,0 +104,73,192771,1,0 +62,54,192912,1,0 +21,72,193054,1,4 +256,40,209752,5,2 +144,96,210035,1,0 +184,112,210177,1,0 +212,148,210318,1,0 +216,192,210460,1,0 +256,212,210601,1,0 +296,192,210743,1,0 +300,148,210884,1,0 +328,112,211026,1,0 +368,96,211167,1,4 +256,192,211309,12,0,214563 +256,32,215129,5,2 +236,183,215686,2,0,B|172:199|130:270|147:356|224:381|284:386|309:374|391:320|389:233|322:176|264:182|264:182,1,500,0|0 +256,268,217384,1,0 +448,64,217950,5,0 +256,124,218516,1,0 +256,124,218799,1,0 +64,64,219365,1,0 +64,64,219648,1,0 +176,260,220214,2,0,B|204:195|313:174|340:268|340:268,1,200,0|0 +336,337,221063,2,0,B|308:402|199:423|172:329|172:329,1,200,0|0 +256,299,221912,1,0 +256,40,222478,5,0 +172,164,223044,1,0 +256,204,223327,1,0 +340,164,223610,1,0 +460,300,224176,5,0 +256,360,224742,1,0 +52,300,225308,1,0 +52,300,225591,5,0 +256,360,226157,1,0 +256,360,226440,1,0 +460,300,227006,1,0 +340,164,227572,5,0 +256,204,227855,1,0 +172,164,228138,1,0 +172,76,228421,1,0 +256,32,228704,1,0 +340,76,228987,1,0 +256,120,229270,5,0 +376,252,229836,1,0 +293,287,230119,2,0,B|314:312|312:363|256:396|204:361|188:319|223:283|223:283,1,200 +136,252,230969,1,0 +176,72,231535,2,0,B|204:137|313:158|340:64|340:64,1,200,0|0 +256,32,232384,1,0 +256,32,232667,5,2 +480,192,233233,1,2 +256,356,233799,1,0 +32,192,234365,1,0 +336,320,234931,6,0,B|308:255|199:234|172:328|172:328,1,200,0|0 +436,112,236063,2,0,B|371:140|350:249|444:276|444:276,1,200,0|0 +176,72,237195,2,0,B|204:137|313:158|340:64|340:64,1,200,0|0 +76,272,238327,2,0,B|141:244|162:135|68:108|68:108,1,200,0|0 +256,208,239459,5,0 +472,288,240025,1,0 +424,92,240591,1,0 +256,340,241157,1,0 +88,92,241723,1,0 +40,288,242289,1,0 +256,208,242855,1,0 +256,44,243421,5,0 +460,340,244553,1,0 +52,340,245685,1,0 +81,156,246252,6,0,B|81:80|139:80|196:80|196:156|196:156|196:233|254:233|311:233|311:156|311:156|311:80|368:80|437:80|427:164,1,600 +348,328,248516,2,0,B|300:369|200:375|161:326|161:326,1,200 +168,152,249648,2,0,B|256:200|256:200|364:140,1,200 +257,103,250497,2,0,B|256:64,3,33.3333333333333 +257,103,251063,1,0 +452,228,251912,6,0,B|376:320,1,100,4|0 +256,356,252478,2,0,B|256:236,1,100,8|0 +123,305,253044,2,0,B|60:228,1,100,0|0 +120,156,253610,1,8 +184,61,253893,2,0,B|190:109|240:132|272:132|324:116|329:62|329:62,1,200,0|0 +392,156,254742,1,8 +300,232,255025,5,0 +256,244,255167,1,0 +212,232,255308,1,0 +152,324,255591,1,0 +256,368,255874,1,8 +360,324,256157,1,0 +256,136,256723,5,0 +180,56,257006,2,0,B|120:156,1,100,8|0 +332,56,257572,2,0,B|392:156,1,100,0|0 +256,136,258138,1,8 +184,208,258421,2,0,B|204:248|240:250|256:258|256:258|272:268|280:288|268:302|256:302|256:302|240:302|230:290|240:268|256:260|256:260|268:250|304:252|328:208|328:208,1,300,0|8 +384,272,259553,5,0 +352,296,259695,1,0 +336,332,259836,1,0 +312,364,259978,1,0 +276,380,260119,1,0 +236,380,260261,1,0 +200,364,260402,1,8 +176,332,260544,1,0 +160,296,260685,1,0 +128,272,260827,1,0 +116,236,260969,6,0,B|48:156|137:48|228:96|255:150|255:150|282:196|338:291|530:151|381:54|381:54,1,600,4|8 +304,32,262950,6,0,B|276:16|240:16|240:16,3,50 +216,16,263516,2,0,B|192:28|140:20|140:20,3,50,0|0|8|0 +128,24,264082,2,0,B|100:36|72:76|72:76,3,50 +68,88,264648,2,0,B|68:120|44:152|44:152|48:152,3,50,0|0|8|0 +44,172,265214,2,0,B|32:200|40:248|40:248,3,50 +52,260,265780,2,0,B|64:288|64:324|64:324,3,50,0|0|8|0 +92,336,266346,2,0,B|104:356|144:372|144:372,3,50 +172,364,266912,2,0,B|208:348|248:364|248:364,3,50,0|0|8|0 +260,368,267478,2,0,B|288:372|328:356|328:356,2,50 +256,192,267902,12,4,270025 +128,184,270308,5,0 +148,72,270591,1,8 +256,32,270874,1,0 +364,72,271157,1,8 +384,184,271440,1,0 +256,152,271723,1,8 +201,261,272006,2,0,B|177:291|185:358|259:403|325:356|346:301|306:252|306:252,1,259.999995350838 +396,352,272855,5,8 +256,291,273138,1,0 +116,352,273421,1,8 +200,196,273704,2,0,B|228:163|314:147|339:225|339:225,1,129.999997675419,0|8 +376,112,274270,2,0,B|320:40|192:36|144:112|144:112,1,259.999995350838 +100,252,275119,6,0,B|204:352,1,129.999997675419,8|0 +318,342,275686,2,0,B|412:252,1,129.999997675419,8|0 +275,87,276252,2,0,B|305:44|435:126|257:242|257:242|80:146|188:33|251:98,1,519.999990701676,8|8 +412,252,277667,6,0,B|308:352,1,129.999997675419,0|8 +194,342,278233,2,0,B|100:252,1,129.999997675419,0|8 +230,87,278799,2,0,B|164:90|145:147|174:218|231:190|231:190|260:255|260:255|288:190|288:190|340:215|372:147|366:77|281:88|281:88,1,519.999990701676 +416,240,280214,5,8 +256,348,280497,1,0 +96,240,280780,1,8 +204,64,281063,2,0,B|180:94|188:161|262:206|328:159|349:104|309:55|309:55,1,259.999995350838,0|0 +416,240,281912,6,0,B|480:120,1,129.999997675419,8|0 +304,336,282478,2,0,B|300:305|268:290|247:290|214:300|211:335|211:335,1,129.999997675419,8|0 +34,125,283044,2,0,B|96:240,1,129.999997675419,8|0 +256,40,283893,5,0 +432,156,284176,1,8 +360,348,284459,1,0 +152,348,284742,1,8 +80,156,285025,1,0 +256,188,285308,1,8 +314,247,285874,5,0 +314,247,286016,1,0 +314,247,286157,1,0 +314,247,286299,1,0 +314,247,286440,1,0 +314,247,286582,1,0 +314,247,286723,1,0 +314,247,286865,1,0 +314,247,287006,1,0 +314,247,287148,1,0 +314,247,287289,1,0 +314,247,287431,1,0 +314,247,287572,1,0 +314,247,287714,1,0 +314,247,287855,1,0 +314,247,287997,1,0 +314,247,288138,1,4 +320,40,288421,6,0,B|184:40|184:40,1,129.999997675419,0|8 +420,256,288987,2,0,B|420:120|420:120,1,129.999997675419,0|8 +192,344,289553,2,0,B|328:344|328:344,1,129.999997675419,0|8 +92,128,290119,2,0,B|92:264|92:264,1,129.999997675419,0|8 +256,192,290686,1,0 +256,192,290827,1,0 +256,192,290969,1,8 +408,254,291252,6,0,B|312:350,1,129.999997675419,0|8 +316,38,291818,2,0,B|408:130,1,129.999997675419,0|8 +104,130,292384,2,0,B|195:38,1,129.999997675419,0|8 +195,345,292950,2,0,B|104:254,1,129.999997675419,0|8 +256,192,293516,1,0 +408,130,293799,6,0,B|312:34,1,129.999997675419,8|0 +316,345,294365,2,0,B|408:254,1,129.999997675419,8|0 +104,254,294931,2,0,B|195:345,1,129.999997675419,8|0 +195,38,295497,2,0,B|104:130,1,129.999997675419,8|0 +256,192,296063,5,0 +256,192,296204,1,0 +256,192,296346,1,0 +256,192,296487,1,0 +256,192,296629,1,0 +256,192,296770,1,0 +256,192,296912,1,0 +256,192,297053,1,0 +256,192,297195,1,4 +212,324,297478,6,0,B|215:354|247:369|267:369|301:358|304:324|304:324,1,129.999997675419,0|8 +52,248,298044,2,0,B|84:192|84:192|44:120|44:120,1,129.999997675419,0|8 +312,40,298610,2,0,B|256:72|256:72|184:32|184:32,1,129.999997675419,0|8 +460,248,299176,2,0,B|428:192|428:192|468:120|468:120,1,129.999997675419,0|8 +312,348,299742,6,0,B|256:316|256:316|184:356|184:356,1,129.999997675419,0|8 +80,148,300308,2,0,B|50:151|35:183|35:203|46:237|80:240|80:240,1,129.999997675419,0|8 +300,36,300874,2,0,B|297:66|265:81|245:81|211:70|208:36|208:36,1,129.999997675419,0|8 +432,240,301440,2,0,B|462:237|477:205|477:185|466:151|432:148|432:148,1,129.999997675419,0|8 +256,192,302006,5,0 +136,272,302289,2,0,B|110:298|74:278|74:278|94:314|69:338|69:338,1,129.999997675419,8|0 +256,344,302855,1,8 +376,272,303138,2,0,B|401:298|437:278|437:278|417:314|442:338|442:338,1,129.999997675419,0|8 +256,192,303704,5,0 +136,112,303987,2,0,B|110:86|74:106|74:106|94:70|69:46|69:46,1,129.999997675419,8|0 +256,40,304553,1,8 +376,112,304836,2,0,B|401:86|437:106|437:106|417:70|442:46|442:46,1,129.999997675419,0|8 +256,192,305402,6,0,B|296:256|296:256|360:256,1,129.999997675419,0|8 +256,192,305969,2,0,B|216:256|216:256|152:256,1,129.999997675419,0|12 +196,352,306535,2,0,B|232:352|232:352|256:336|256:336|280:352|280:352|328:352,1,129.999997675419,0|8 +416,256,307101,5,0 +444,120,307384,1,8 +348,40,307667,2,0,B|340:101|275:131|234:131|167:110|161:41|161:41,1,259.999995350838,0|0 +68,120,308516,1,8 +96,256,308799,1,0 +212,348,309082,6,0,B|215:318|247:303|267:303|301:314|304:348|304:348,1,129.999997675419,8|0 +320,192,309648,2,0,B|184:192,1,129.999997675419,8|0 +192,48,310214,2,0,B|256:68|256:68|320:48,1,129.999997675419,8|0 +372,176,310780,2,0,B|256:228|256:228|132:172,1,259.999995350838,12|8 +68,292,311629,1,0 +256,368,311912,1,8 +444,292,312195,1,0 +348,45,312761,6,0,B|341:106|277:136|236:136|169:115|163:46|163:46,1,259.999995350838,0|0 +82,161,313610,2,0,B|124:224|188:240|188:240,1,129.999997675419,8|0 +328,238,314176,2,0,B|388:216|430:161,2,129.999997675419,8|0|8 +256,356,315025,1,0 +256,356,315308,5,12 +328,240,315591,2,0,B|452:304|452:304,1,129.999997675419,0|8 +184,240,316157,2,0,B|60:304|60:304,1,129.999997675419,0|8 +151,91,316723,2,0,B|220:112|256:40|256:40|292:112|364:92,1,259.999995350838,0|0 +256,356,317855,5,0 +380,284,318138,1,8 +380,140,318421,1,0 +256,68,318704,1,8 +132,140,318987,1,0 +132,284,319270,1,8 +256,208,319553,1,0 +256,192,319695,12,0,320686 +256,192,320827,12,0,322101 +255,57,322950,5,0 +115,303,323233,1,8 +256,192,323516,1,0 +396,304,323799,1,8 +256,192,324082,6,0,B|256:292|256:292|312:292|380:264|392:164|368:108|332:60|244:56|164:52|120:144|136:200|120:264|244:292|244:292,1,649.999988377094,4|0 +32,64,337093,5,8 +48,64,337243,1,0 +64,64,337393,1,8 +256,92,337693,2,0,B|256:316|256:316,1,200,8|8 +432,352,338293,2,0,B|368:304|368:224|432:176|432:176,1,200,8|8 +82,178,338893,2,0,B|134:224|144:304|80:352,1,200,8|8 +124,339,339343,5,0 +167,327,339493,1,8 +210,313,339643,1,0 +253,301,339793,1,8 +293,102,340093,2,0,B|313:77|311:26|255:-7|203:28|187:70|222:106|222:106,1,200,8|8 +432,192,340693,1,8 +256,296,340993,1,8 +80,192,341293,1,8 +208,360,341593,5,8 +180,272,341743,1,0 +256,216,341893,1,8 +332,272,342043,1,0 +304,360,342193,1,8 +356,44,342493,6,0,B|152:44,1,200,8|8 +356,124,343093,2,0,B|144:124,2,200,8|8|8 +408,272,343993,1,8 +348,344,344143,1,0 +256,372,344293,1,8 +164,344,344443,1,0 +104,272,344593,1,8 +256,192,344893,1,8 +256,192,345043,12,4,346693 +344,36,346993,5,8 +164,36,347293,1,8 +216,171,347593,2,0,B|230:139|284:129|297:175|297:175,1,100,8|8 +329,245,347893,2,0,B|319:311|199:343|183:245|183:245,2,200,8|8|8 +172,356,348793,1,8 +256,384,348943,1,8 +340,356,349093,1,8 +344,56,349393,6,0,B|432:112|432:112|456:232,1,200,8|8 +168,56,349993,1,8 +168,56,350143,1,0 +168,56,350293,2,0,B|80:112|80:112|56:232,1,200,8|8 +348,328,350893,2,0,B|256:368|256:368|148:320,2,200,8|8|4 +256,172,351793,5,8 +388,48,352093,1,8 +256,172,352393,1,8 +256,260,352543,1,8 +256,172,352693,1,8 +124,48,352993,1,8 +124,238,353293,2,0,B|192:272|192:336|192:336|240:336|256:368|256:368|272:336|320:336|320:336|320:272|393:240,1,400,8|4 +256,40,354193,5,8 +76,340,354493,1,8 +436,340,354793,1,8 +376,272,354943,6,0,B|410:236|410:236|476:237,1,100,0|8 +462,146,355243,2,0,B|412:144|412:144|368:94,1,100,0|8 +296,148,355543,2,0,B|282:116|228:106|215:152|215:152,1,100,0|8 +132,106,355843,2,0,B|99:144|99:144|50:146,1,100,0|8 +51,237,356143,2,0,B|101:236|101:236|136:272,1,100,0|4 +216,236,356443,6,0,B|230:268|284:278|297:232|297:232,1,100,0|8 +332,316,356743,2,0,B|352:288|352:288|384:288|384:288|404:260,2,100,0|8|0 +256,360,357193,1,8 +176,316,357343,2,0,B|156:288|156:288|124:288|124:288|104:260,2,100,0|8|0 +212,232,357793,1,8 +300,232,357943,1,0 +340,152,358093,2,0,B|339:105|339:105|380:70,1,100,8|0 +299,18,358393,2,0,B|256:40|256:40|206:15,1,100,8|0 +132,70,358693,2,0,B|173:105|173:105|172:152,1,100,4|0 +96,200,358993,5,8 +20,248,359143,1,0 +68,324,359293,1,8 +144,276,359443,1,0 +212,340,359593,2,0,B|228:340|228:340|248:332|248:332|256:344|256:344|264:332|264:332|280:340|280:340|296:340,1,100,8|0 +368,276,359893,1,8 +444,324,360043,1,0 +492,248,360193,1,8 +416,200,360343,1,0 +344,144,360493,6,0,B|376:130|386:76|340:63|340:63,1,100,8|0 +256,36,360793,1,8 +168,64,360943,2,0,B|136:78|126:132|172:145|172:145,1,100,0|4 +200,228,361243,1,0 +148,300,361393,2,0,B|100:280|100:280|88:232|88:232,2,100,8|0|8 +211,364,361843,2,0,B|256:341|256:341|306:366,1,100,0|8 +364,300,362143,2,0,B|412:280|412:280|424:232|424:232,2,100,0|8|0 +312,228,362593,1,8 +304,140,362743,5,0 +332,56,362893,1,8 +256,4,363043,1,0 +180,56,363193,1,8 +208,140,363343,1,0 +200,228,363493,5,4 +116,192,363643,1,0 +52,252,363793,1,8 +92,336,363943,1,0 +184,320,364093,1,8 +256,372,364243,1,0 +328,320,364393,1,8 +420,336,364543,1,8 +460,252,364693,1,8 +396,192,364843,1,8 +312,228,364993,1,8 +294,139,365143,6,0,B|280:171|226:181|213:135|213:135,2,100,8|8|8 +295,49,365593,2,0,B|281:17|227:7|214:53|214:53,2,100,8|8|4 +416,181,366193,6,0,B|359:309|142:350|89:164|89:164,1,400,0|8 +117,324,367093,2,0,B|176:384|364:408|416:309|426:294|426:294,2,300,0|0|8 +192,209,368293,2,0,B|160:113|256:41|352:113|320:209,2,300,0|0|8 +96,109,369493,6,0,B|148:5|256:-27|364:5|416:109,2,400,0|8|2 +168,224,370993,2,0,B|200:264|256:276|312:264|344:224,2,200,8|0|8 +32,184,371893,6,0,B|92:224|92:224|104:292|172:316,1,200,0|8 +256,204,372493,1,2 +340,315,372793,2,0,B|408:292|420:224|420:224|480:184,1,200,8|0 +360,120,373393,2,0,B|308:128|308:128|200:84|212:20|256:0|300:20|312:84|204:128|204:128|152:120,1,400,8|8 +40,204,374293,6,0,B|108:192|176:232|184:308,1,200,0|8 +184,308,374743,1,4 +328,308,375043,1,2 +328,308,375193,2,0,B|336:232|404:192|472:204,1,200,8|4 +340,156,375793,6,0,B|276:200|260:256|256:276|256:276|252:256|236:200|172:156,1,300,8|2 +172,84,376393,2,0,B|204:36|256:24|308:36|340:84,2,200,8|0|8 +296,152,377293,6,0,B|312:212|256:236|200:260|216:320,1,200,0|8 +216,150,377893,2,0,B|200:212|256:236|312:260|295:320,2,200,0|8|2 +256,16,378793,1,8 +140,96,379093,6,0,B|92:240|232:308|256:352|256:352|280:308|420:240|372:96,1,600,0|8 +304,120,380143,2,0,B|256:144|208:120,1,100,2|0 +68,100,380593,6,0,B|96:304|292:280|324:388,1,400,8|8 +188,385,381493,2,0,B|220:280|416:304|444:100,1,400,2|0 +304,72,382393,1,8 +184,148,382693,2,0,B|212:192|256:216|256:216|300:192|328:148,1,200,0|8 +208,72,383293,1,0 +256,204,383593,1,8 +374,272,383893,6,0,B|342:312|342:312|296:328,1,100,8|8 +216,328,384193,2,0,B|169:312|169:312|137:272,1,100,8|8 +137,195,384493,2,0,B|169:155|169:155|216:140,1,100,8|8 +296,140,384793,2,0,B|342:155|342:155|374:195,1,100,8|0 +436,232,385093,6,0,B|484:112|484:112|448:48,1,200,4|8 +380,8,385543,1,0 +348,80,385693,2,0,B|296:56|296:56|256:72|256:72|216:56|216:56|164:80,1,200,0|8 +132,8,386143,1,0 +62,50,386293,2,0,B|28:112|28:112|76:232,1,200,0|8 +120,300,386743,6,0,B|176:356|256:372|336:356|392:300,1,300,0|8 +424,228,387343,1,2 +344,240,387493,2,0,B|312:280|256:296|200:280|168:240,1,200,0|8 +88,228,387943,1,0 +140,168,388093,1,0 +256,140,388243,6,0,B|256:88,6,50,8|0|8|0|8|0|4 +256,140,389293,1,4 +296,212,389893,6,0,B|284:339|148:363|68:339,2,300,6|2|8 +256,140,390943,1,0 +216,212,391093,2,0,B|227:339|363:363|443:339,2,300,0|2|8 +368,268,392293,6,0,B|412:280|460:252|460:196|460:196|460:128,1,200,0|8 +472,56,392743,2,0,B|428:44|384:68|380:124|380:124|380:192,1,200 +300,192,393193,2,0,B|300:72|300:72|300:16|256:-4|212:16|212:72|212:72|212:192,1,400,8|8 +132,191,393943,2,0,B|132:124|132:124|128:68|84:44|40:56,1,200,2|0 +52,131,394393,2,0,B|52:196|52:196|52:252|100:280|144:268,1,200,8|0 +296,316,394993,6,0,B|440:264|424:68,1,300,8|2 +344,88,395593,2,0,B|364:244|256:288|148:244|168:88,1,400,8|8 +87,71,396343,2,0,B|72:264|216:316,1,300,2|8 +204,108,397093,6,0,B|336:108|336:108|400:140,1,200,0|8 +360,212,397543,1,2 +308,276,397693,2,0,B|176:276|176:276|112:244,1,200,0|8 +152,172,398143,1,0 +128,96,398293,6,0,B|48:32,2,100,8|0|8 +176,32,398743,2,0,B|256:92|256:92|336:32,1,200,0|8 +384,96,399193,2,0,B|464:32,2,100,8|0|4 +404,256,399793,6,0,B|320:280|320:280|348:340|304:388,1,200,8|0 +256,324,400243,1,0 +205,384,400393,2,0,B|164:340|192:280|192:280|108:256,1,200,8|0 +124,180,400843,2,0,B|204:204|204:204|240:216|260:236|264:244|256:256|248:244|252:236|272:216|308:204|308:204|388:180,1,300,2|2 +364,20,401593,6,0,B|292:24|228:68|216:132,1,200,8|0 +296,132,402043,2,0,B|284:68|220:24|148:20,1,200,2|0 +148,100,402493,1,0 +256,216,402793,1,8 +364,100,403093,6,0,B|384:116|400:144|400:204|400:204|400:292,1,200,0|8 +256,356,403693,1,0 +112,290,403993,2,0,B|112:204|112:204|112:144|128:116|148:100,1,200,8|4 +188,256,404593,6,0,B|200:200|256:164|312:200|324:256,2,200,8|0|8 +112,228,405343,1,0 +40,188,405493,2,0,B|60:160|94:147|128:148|156:160|156:160|176:136|216:120,1,200,2|8 +296,120,405943,2,0,B|336:136|356:160|356:160|384:148|417:147|452:160|472:188,1,200 +400,228,406393,1,8 +256,296,406693,6,0,B|336:344|336:344|448:328,2,200,0|8|2 +256,216,407443,1,0 +256,136,407593,2,0,B|176:88|176:88|64:104,2,200,8|0|8 +324,176,408343,5,0 +324,256,408493,1,0 +256,300,408643,1,0 +188,256,408793,1,0 +188,176,408943,1,0 +256,136,409093,1,4 +392,52,409393,6,0,B|396:144|432:192|496:204,1,200,0|8 +496,124,409843,2,0,B|433:135|397:183|393:275,1,200,2|0 +472,272,410293,2,0,B|468:328|432:360|372:360|372:360|316:360,1,200,0|8 +296,284,410743,1,0 +216,284,410893,1,0 +191,360,411043,6,0,B|140:360|140:360|80:360|44:328|40:272,1,200 +118,275,411493,2,0,B|114:183|78:135|16:124,1,200,2|8 +17,203,411943,2,0,B|80:192|116:144|120:52,1,200 +200,72,412393,6,0,B|196:140|256:164|316:140|312:72,1,200,8|0 +376,136,412843,2,0,B|348:208|256:244|164:208|136:136,1,300,2|0 +25,32,413593,5,8 +24,31,413893,2,0,B|61:43|61:43|53:76|53:76|86:88|86:88|73:121|73:121|94:141|94:141|90:178|-3:154|-27:269|49:343|155:302|155:192|155:192|217:192|217:192|217:228|217:228|225:273|267:302|267:302|295:314|308:351|287:384|253:392|221:384|200:351|213:314|241:302|241:302|283:273|291:228|291:228|291:192|291:192|353:192|353:192|353:302|459:343|537:269|513:154|418:178|414:141|414:141|435:121|435:121|422:88|422:88|455:76|455:76|447:43|447:43|484:31,1,1600,0|4 +340,104,416593,5,0 +256,104,416743,1,0 +300,32,416893,1,0 +212,32,417043,1,0 +172,104,417193,1,0 +212,172,417343,1,0 +300,172,417493,1,4 +460,216,417793,5,0 +392,328,418093,1,4 +252,380,418393,1,0 +252,380,418543,1,0 +252,380,418693,1,4 +120,328,418993,1,8 +52,216,419293,1,0 +208,144,419593,5,8 +296,144,419743,1,0 +328,59,419893,2,0,B|318:-7|198:-39|182:59|182:59,1,200,0|8 +128,132,420343,2,0,B|212:176|212:176|212:304,1,200 +300,281,420793,2,0,B|300:176|300:176|384:132,1,200,8|0 +472,156,421243,1,0 +408,220,421393,5,8 +352,384,421693,2,0,B|288:384|288:384|256:368|256:368|224:384|224:384|136:384|136:384,1,200,0|8 +104,220,422293,1,0 +156,48,422593,5,8 +180,140,422743,2,0,B|220:136|248:100|256:81|256:81|264:100|288:132|336:140,1,200 +356,48,423193,1,8 +256,204,423493,5,4 +324,368,423793,2,0,B|396:300,1,100,8|0 +188,368,424243,2,0,B|116:300,1,100,0|8 +216,153,424693,2,0,B|164:204|200:264|235:275|268:272|296:280|352:228|333:181|292:148|292:148,2,300,0|0|8 +348,44,425893,2,0,B|256:8|256:8|160:44,1,200,0|8 +53,188,426493,6,0,B|120:201|143:318|50:338|50:338,1,200,0|8 +216,360,427093,2,0,B|230:328|284:318|297:364|297:364,1,100,8|8 +344,288,427393,2,0,B|368:244|368:244|344:200,1,100,8|8 +296,132,427693,2,0,B|282:164|228:174|215:128|215:128,1,100,8|8 +168,204,427993,2,0,B|144:247|144:247|168:291,1,100,8|8 +256,248,428293,1,4 +456,340,428593,6,0,B|389:327|366:210|459:190|459:190,1,200,8|8 +336,56,429193,1,8 +256,92,429343,1,0 +176,56,429493,1,8 +256,228,429793,1,8 +334,269,429943,2,0,B|315:331|198:354|174:264|174:264,1,200 +120,332,430393,1,8 +105,147,430693,2,0,B|131:68|197:12|304:11|381:62|408:154,1,400,8|8 +392,332,431593,1,8 +184,248,431893,6,0,B|197:291|228:301|256:310|256:310|279:322|279:350|256:361|232:350|232:322|256:310|256:310|279:301|310:291|328:248,2,300,8|8|8 +328,136,433093,2,0,B|314:92|283:82|256:73|256:73|232:61|232:33|256:22|279:33|279:61|256:73|256:73|232:82|201:92|184:136,2,300,8|8|8 +64,60,434293,5,4 +256,248,434743,1,8 +448,60,435193,1,8 +309,184,435493,2,0,B|378:252|330:333|283:348|239:344|201:354|133:282|153:215|208:180|208:180,1,400,4|8 +86,328,436393,5,8 +256,248,436693,1,0 +426,328,436993,1,0 +304,160,437293,1,0 +328,72,437443,1,0 +256,24,437593,1,0 +184,72,437743,1,0 +208,160,437893,1,4 +336,304,438193,2,0,B|317:366|200:389|176:299|176:299,1,200 +49,138,438793,1,0 +149,31,439093,6,0,B|166:122|257:140|257:140|330:158|366:213|366:267|293:340|220:340|147:267|147:213|184:158|257:140|257:140|348:122|366:31,1,800 +453,145,440593,5,0 +370,358,440893,1,0 +142,357,441193,1,0 +59,145,441493,1,0 +256,20,441793,1,0 +179,207,442093,6,0,B|198:145|315:122|339:212|339:212,1,200 +256,256,442543,1,0 +256,352,442693,1,4 +64,332,452293,6,0,B|64:252|64:252|64:204|96:172|136:172,1,200,4|0 +212,84,452893,2,0,B|212:164|212:164|212:212|180:244|140:244,1,200,8|0 +300,300,453493,6,0,B|300:219|300:219|300:171|332:139|371:139,1,200 +448,50,454093,2,0,B|448:130|448:130|448:178|416:210|376:210,1,200,8|0 +200,232,454693,6,0,B|176:184|176:184|132:184|100:172|80:136|84:96,1,200 +136,36,455143,1,2 +160,108,455293,2,0,B|212:108|212:108|256:128|256:128|300:108|300:108|352:108,1,200,8|0 +376,36,455743,1,2 +428,96,455893,2,0,B|432:136|412:172|380:184|336:184|336:184|312:232,1,200 +128,272,456493,2,0,B|152:324|216:332|244:304|256:296|256:296|268:304|296:332|360:324|384:272,1,300,8|0 +428,208,457093,6,0,B|388:180|336:196|312:240|312:240|256:208|256:208|200:240|200:240|176:196|124:180|84:208,1,400,4|8 +28,152,457843,2,0,B|80:116|136:108|176:124|176:124|216:140,1,200 +295,140,458293,2,0,B|336:124|336:124|376:108|432:116|484:152,2,200,2|0|8 +156,232,459193,6,0,B|356:232,1,200 +424,188,459643,1,2 +476,252,459793,1,0 +432,320,459943,2,0,B|404:372|404:372|324:372|324:372|296:320,1,200,0|2 +216,318,460393,2,0,B|188:372|188:372|108:372|108:372|80:320,1,200,0|2 +36,252,460843,1,0 +88,188,460993,1,0 +216,92,461293,6,0,B|176:52,2,50 +296,92,461593,2,0,B|336:52,2,50 +296,172,461893,2,0,B|384:172|384:172|424:172|456:192|472:232,1,200,4|8 +464,312,462343,2,0,B|424:304|400:280|388:252|388:252|320:252|320:252|296:272,1,200,0|2 +216,272,462793,2,0,B|192:252|192:252|124:252|124:252|112:280|88:304|48:312,1,200,8|0 +40,230,463243,2,0,B|56:192|88:172|128:172|128:172|216:172,1,200,0|2 +216,92,463693,2,0,B|216:56|216:56|216:28|240:4|272:4|296:28|296:56|296:56|296:92,1,200,0|8 +424,192,464293,6,0,B|464:264|464:264|384:352,1,200,0|8 +127,351,464893,2,0,B|48:264|48:264|88:192,1,200,0|8 +140,252,465343,1,0 +164,176,465493,2,0,B|204:188|236:244|204:300|204:300|308:300|308:300|276:244|308:188|348:176,1,400,2|0 +324,100,466243,1,0 +256,144,466393,1,8 +188,104,466543,1,0 +256,56,466693,1,0 +408,108,466993,6,0,B|400:152|364:180|320:184|320:184|284:228|256:216|228:228|192:184|192:184|148:180|112:152|104:108,1,400,8|8 +24,244,467893,2,0,B|148:256|148:256|176:284|212:292,1,200,0|8 +299,292,468343,2,0,B|336:284|364:256|364:256|488:244,1,200,2|0 +448,324,468793,6,0,B|404:328|404:328|336:372|256:388|176:372|108:328|108:328|64:324,1,400,8|8 +96,252,469543,2,0,B|200:208|216:104,1,200,0|2 +295,103,469993,2,0,B|312:208|416:252,1,200,8|0 +360,312,470443,5,0 +296,268,470593,1,0 +256,204,470743,1,0 +216,268,470893,1,0 +152,312,471043,1,0 +216,360,471193,1,0 +296,360,471343,1,0 +380,384,471493,6,0,B|436:368|456:304|432:252|376:236,1,200,4|0 +396,160,471943,1,2 +320,184,472093,2,0,B|300:136|300:136|256:116|256:116|212:136|212:136|192:184,1,200,0|8 +116,160,472543,1,2 +134,236,472693,2,0,B|80:252|56:304|76:368|132:384,1,200,0|8 +156,312,473143,2,0,B|356:312,1,200 +336,236,473593,1,8 +256,236,473743,1,0 +176,236,473893,6,0,B|96:240|44:192|32:124,1,200,2|8 +72,56,474343,2,0,B|152:52|204:100|216:168,1,200 +296,166,474793,2,0,B|308:100|360:52|440:56,1,200,8|2 +479,125,475243,2,0,B|468:192|416:240|336:236,1,200 +256,236,475693,1,0 +212,304,475843,1,0 +256,372,475993,1,0 +300,304,476143,1,0 +256,236,476293,6,0,B|256:32,1,200,4|0 +336,36,476743,1,2 +416,36,476893,2,0,B|416:76|384:112|340:116|340:116|340:160|340:200,1,200,0|8 +172,195,477493,2,0,B|172:160|172:116|172:116|128:112|96:76|96:36,1,200,0|8 +176,36,477943,2,0,B|224:44|244:88|244:88|268:88|268:88|288:44|336:36,1,200 +352,116,478393,6,0,B|332:152|316:156|296:164|296:164|296:220|296:220|296:288,2,200,8|2|8 +160,116,479293,2,0,B|180:152|196:156|216:164|216:164|216:220|216:220|216:288,2,200,0|8|0 +120,184,480043,5,0 +140,260,480193,1,0 +216,288,480343,1,0 +256,356,480493,1,0 +296,288,480643,1,0 +372,260,480793,1,0 +392,184,480943,1,0 +377,106,481093,6,0,B|449:94|503:168|451:239,1,200,4|0 +392,184,481543,2,0,B|341:252|395:327|467:315,1,200,2|0 +412,388,481993,2,0,B|312:372|304:316|320:268|280:280|256:260|232:280|192:268|208:316|200:372|100:388,1,400,8|8 +43,315,482743,2,0,B|116:327|170:252|120:184,1,200,2|0 +59,238,483193,2,0,B|8:168|62:94|135:106,1,200,8|0 +296,264,483793,6,0,B|372:264|372:264|400:240|400:240|432:256|432:256|456:244|456:244|480:244,2,200,8|0|8 +256,196,484543,1,2 +216,120,484693,2,0,B|140:120|140:120|112:144|112:144|80:128|80:128|56:140|56:140|32:140,2,200,0|8|0 +351,32,485593,6,0,B|276:104|360:264|480:68|540:348|412:348|352:348|352:348|295:348,1,500,8|0 +216,348,486493,2,0,B|160:348|160:348|100:348|-28:348|32:68|152:264|236:104|160:32,1,500,2|0 +104,88,487393,5,8 +256,204,487693,1,2 +408,88,487993,1,8 +188,164,488293,5,8 +188,84,488443,1,8 +256,40,488593,1,8 +324,84,488743,1,8 +324,164,488893,1,8 +256,204,489043,1,8 +188,248,489193,1,8 +324,248,489343,1,8 +256,292,489493,6,0,B|256:340,8,50,8|0|8|0|8|0|8|0|8 +324,248,490243,1,0 +296,172,490393,1,8 +216,172,490543,1,0 +188,248,490693,1,4 +176,80,490993,6,0,B|195:18|312:-5|336:85|336:85,1,200,8|8 +396,252,491593,2,0,B|392:192|442:160|442:160,1,100,8|0 +488,240,491893,1,8 +340,340,492193,1,8 +295,265,492343,2,0,B|317:234|297:196|271:187|253:182|228:183|180:218|200:250|227:280|227:280,1,200 +172,340,492793,1,8 +24,240,493093,1,8 +73,165,493243,2,0,B|118:192|116:252,1,100,0|8 +169,78,493693,6,0,B|223:78|223:78|255:46|255:46|287:78|287:78|343:78,1,200,8|8 +375,277,494293,6,0,B|346:370|169:405|133:269|133:269,2,300,8|8|8 +315,126,495493,2,0,B|347:77|317:21|282:6|248:-1|209:2|143:56|170:103|211:148|211:148,2,300,8|8|8 +184,248,496693,2,0,B|197:291|228:301|256:310|256:310|279:322|279:350|256:361|232:350|232:322|256:310|256:310|279:301|310:291|328:248,2,300,8|8|8 +101,109,497893,6,0,B|104:69|146:40|188:52|188:52|217:4|256:6|297:6|321:51|321:51|363:38|412:70|414:121,1,400,8|8 +339,285,498793,2,0,B|332:301|311:315|290:309|290:309|275:333|255:336|232:331|221:310|221:310|202:316|175:303|169:277,2,200,8|8|8 +256,86,499693,5,0 +184,138,499843,1,0 +212,222,499993,1,0 +300,222,500143,1,0 +328,138,500293,1,4 +424,276,500593,5,0 +256,372,500893,1,0 +88,276,501193,1,0 +88,276,501343,1,0 +88,276,501493,1,0 +160,92,501793,1,0 +160,92,501943,1,0 +352,92,502243,1,0 +352,92,502393,1,0 +256,248,502693,5,0 +301,170,502843,2,0,B|283:152|255:151|229:152|210:170,1,100 +338,292,503293,2,0,B|316:352|200:377|175:288|175:288,1,200 +91,154,503893,6,0,B|44:150|21:102|10:42|65:7|131:-10|181:42|166:103|166:103,2,300 +421,154,505093,2,0,B|468:150|491:102|502:42|447:7|381:-10|331:42|346:103|346:103,2,300 +213,259,506293,2,0,B|161:310|197:370|232:381|265:378|293:386|349:334|330:287|289:254|289:254,2,300 +170,74,507493,6,0,B|224:74|224:74|256:42|256:42|288:74|288:74|344:74,1,200 +448,224,508093,1,0 +330,360,508393,2,0,B|315:315|255:300|255:300|195:315|182:360,1,200 +64,224,508993,1,0 +256,152,509293,5,0 +328,100,509443,1,0 +300,16,509593,1,0 +212,16,509743,1,0 +184,100,509893,1,4 +256,264,510193,1,0 +376,72,510493,1,0 +136,76,510793,1,0 +380,73,511093,6,0,B|445:147|468:248|406:346|328:392|268:399|149:402|86:328|25:223|84:96|152:64,1,800 +256,196,512593,1,0 +256,196,512893,5,0 +180,240,513043,1,0 +180,328,513193,1,0 +256,372,513343,1,0 +332,328,513493,1,0 +332,240,513643,1,0 +336,152,513793,6,0,B|368:138|378:84|332:71|332:71,1,100,0|0 +256,32,514093,1,0 +176,72,514243,2,0,B|144:86|134:140|180:153|180:153,1,100 +256,196,514543,1,0 +182,251,514693,2,0,B|256:319|256:319|326:249,1,200,4|0 +335,255,515068,1,0 +344,263,515143,1,0 +352,271,515218,1,0 +360,279,515293,2,0,B|415:295|465:274|495:228|488:171,1,200,8|0 +398,188,515743,1,0 +386,180,515818,1,0 +374,172,515893,2,0,B|318:155|268:176|238:222|245:279,2,200,8|0|8 +356,28,516793,1,8 +176,24,517093,5,0 +80,176,517393,1,8 +88,180,517468,1,0 +96,184,517543,1,0 +104,188,517618,1,0 +112,192,517693,2,0,B|168:175|218:196|248:242|241:299,1,200,8|0 +144,288,518143,1,0 +136,292,518218,1,0 +128,296,518293,2,0,B|73:312|23:291|-7:245|0:188,1,200,8|8 +48,100,518743,1,0 +48,100,518818,1,0 +48,100,518893,2,0,B|124:100|124:100|168:100|208:72|216:32,1,200,0|8 +464,284,519493,2,0,B|388:284|388:284|344:284|304:312|296:352,1,200,4|8 +216,352,519943,6,0,B|168:352,2,50,0|0|8 +296,32,520393,2,0,B|344:32,2,50,8|0|0 +204,68,520693,2,0,B|248:120|248:120|380:120,1,200,0|8 +308,316,521293,2,0,B|264:264|264:264|132:264,1,200,0|8 +24,96,521893,6,0,B|56:136|56:136|156:124|156:124|188:160,1,200,0|8 +256,232,522343,2,0,B|256:280,2,50,0|0|8 +324,159,522643,1,0 +324,159,522793,2,0,B|356:124|356:124|456:136|456:136|488:96,1,200,8|8 +428,16,523243,5,4 +24,288,523693,5,8 +84,368,523843,1,4 +256,120,524293,5,4 +452,156,524593,2,0,B|424:100,2,50,8|0|0 +504,240,524893,2,0,B|456:280|360:252|348:176,1,200,8|0 +163,176,525493,2,0,B|152:252|56:280|8:240,1,200,0|8 +60,156,525943,1,0 +60,156,526093,2,0,B|88:100,2,50,8|0|0 +60,156,526393,1,0 +300,336,526693,6,0,B|340:280|340:280|384:292|440:284|460:248,1,200,0|8 +388,180,527143,2,0,B|404:128,2,50,0|0|8 +124,180,527593,2,0,B|108:128,2,50,8|0|0 +52,249,527893,2,0,B|72:284|128:292|172:280|172:280|212:336,1,200,0|8 +256,140,528493,1,0 +256,140,528793,2,0,B|256:40,2,100,8|0|4 +460,192,529393,6,0,B|528:192,2,50,8|0|0 +360,192,529693,1,8 +52,188,529993,2,0,B|-16:188,2,50,8|0|0 +152,192,530293,1,8 +312,308,530593,1,8 +256,108,530893,1,0 +200,308,531193,1,8 +172,112,531493,6,0,B|172:60|212:16|276:12|316:52,1,200,0|8 +340,272,532093,2,0,B|340:324|300:368|236:372|196:332,1,200,0|8 +56,192,532543,5,0 +56,192,532618,1,0 +56,192,532693,1,0 +8,100,532843,1,4 +460,192,533293,5,8 +504,284,533443,1,4 +256,192,550693,12,4,553093 +48,116,553393,6,0,B|114:126|146:246|48:262|48:262,1,200,0|0 +183,36,553993,2,0,B|193:102|313:134|329:36|329:36,1,200,0|0 +464,262,554593,2,0,B|398:252|366:132|464:116|464:116,1,200,0|0 +300,352,555193,1,0 +328,268,555343,1,0 +256,212,555493,1,0 +184,268,555643,1,0 +212,352,555793,1,0 +108,200,556093,5,0 +168,40,556393,1,0 +344,40,556693,1,0 +404,200,556993,1,0 +256,288,557293,1,0 +256,108,557593,5,0 +212,186,557743,1,0 +301,185,557893,1,4 +174,312,558193,2,0,B|183:356|231:356|255:340|255:340|279:356|327:356|335:308,1,200 +470,164,558793,2,0,B|425:158|418:126|411:107|417:88|417:88|398:95|379:89|351:80|344:41,1,200 +166,43,559393,2,0,B|160:80|132:89|113:95|94:88|94:88|100:107|93:126|86:158|42:164,1,200 +152,288,559993,2,0,B|192:384,1,100 +256,320,560293,1,4 +321,380,560443,2,0,B|360:288,1,100 +295,119,560893,2,0,B|317:88|297:50|271:41|253:36|228:37|180:72|200:104|227:134|227:134,1,200 +56,200,561493,5,0 +128,272,561643,1,0 +164,298,561718,1,0 +181,339,561793,1,0 +256,264,561943,1,0 +331,339,562093,1,0 +348,298,562168,1,0 +384,272,562243,1,0 +432,176,562393,1,0 +360,88,562543,1,0 +256,56,562693,1,4 +152,88,562843,1,0 +80,176,562993,1,0 +213,343,563293,6,0,B|199:287|199:287|254:259|254:259|310:287|310:287|296:343,1,240.000005722046 +437,158,563893,2,0,B|471:123|471:123|465:38|381:32|381:32|341:72,1,240.000005722046 +166,67,564493,2,0,B|131:32|131:32|47:38|41:123|41:123|76:158,1,240.000005722046 +200,248,564943,2,0,B|176:304|176:304|200:360,1,120.000002861023,0|4 +312,358,565243,2,0,B|336:304|336:304|312:248,1,120.000002861023 +256,4,565693,5,0 +456,96,565993,1,0 +456,296,566293,1,0 +256,376,566593,1,0 +56,296,566893,1,0 +56,96,567193,1,0 +191,119,567343,2,0,B|231:167|191:231,1,120.000002861023 +320,229,567643,2,0,B|280:165|320:117,1,120.000002861023 +354,331,568093,6,0,B|303:321|271:347|254:380|254:380|238:347|205:321|155:331,1,240.000005722046 +157,90,568693,2,0,B|207:99|239:73|256:40|256:40|272:73|305:99|355:90,1,240.000005722046 +477,296,569293,1,0 +256,204,569593,1,0 +212,348,569743,1,0 +332,264,569893,1,0 +180,264,570043,1,0 +300,348,570193,1,0 +32,296,570493,1,0 +160,96,570793,6,0,B|182:21|324:-6|353:102|353:102,2,240.000005722046 +112,336,571693,2,0,B|161:288|160:224,1,120.000002861023 +256,280,571993,1,0 +352,229,572143,2,0,B|351:288|400:336,1,120.000002861023,0|4 +356,64,572593,6,0,B|306:48|289:14|289:14|256:31|222:14|222:14|205:48|155:64|155:64,1,240.000005722046,0|0 +376,192,573193,2,0,B|136:192,1,240.000005722046 +356,320,573793,2,0,B|306:336|289:370|289:370|256:353|222:370|222:370|205:336|155:320|155:320,1,240.000005722046 +153,67,574393,6,0,B|200:83|208:137|192:165|172:188|144:206|71:203|65:157|70:105|70:105,1,259.999995350838,0|4 +359,67,574993,2,0,B|311:83|303:137|319:165|339:188|367:206|440:203|446:157|441:105|441:105,1,259.999995350838 +367,348,575593,2,0,B|297:348|297:348|256:306|256:306|215:348|215:348|143:348,1,259.999995350838 +64,128,576193,1,0 +364,64,576493,2,0,B|328:10|256:-25|183:10|147:64,1,259.999995350838 +196,179,576943,2,0,B|224:208|286:211|320:176,1,129.999997675419,0|4 +138,298,577393,2,0,B|171:339|257:370|336:339|373:299,1,259.999995350838 +440,144,577993,5,0 +256,32,578293,1,0 +72,144,578593,1,0 +180,308,578893,1,0 +256,360,579043,1,0 +336,296,579193,1,0 +304,216,579343,1,0 +208,216,579493,1,4 +128,152,579643,5,0 +92,56,579793,2,0,B|172:44|212:104|212:104,1,129.999997675419 +303,99,580093,2,0,B|347:46|420:56,1,129.999997675419 +384,152,580393,1,0 +304,216,580543,1,0 +410,291,580693,5,4 +400,311,580768,1,0 +383,327,580843,1,0 +364,339,580918,1,0 +343,345,580993,1,0 +322,347,581068,1,0 +299,342,581143,1,0 +278,333,581218,1,0 +260,320,581293,1,4 +234,333,581368,1,0 +213,342,581443,1,0 +190,347,581518,1,0 +169,345,581593,1,0 +148,339,581668,1,0 +129,327,581743,1,0 +112,311,581818,1,0 +102,291,581893,1,4 +188,252,582043,5,0 +324,252,582193,1,0 +256,188,582343,1,0 +352,152,582493,1,0 +160,152,582643,1,0 +256,92,582793,1,0 +256,92,582943,1,0 +160,52,583093,6,0,B|96:52|32:108|44:192|104:240,1,259.999995350838,0|0 +256,312,583543,1,0 +408,240,583693,2,0,B|468:192|480:108|416:52|352:52,1,259.999995350838 +352,152,584143,1,0 +340,296,584293,2,0,B|316:228|256:200|256:200|196:228|172:296,1,259.999995350838 +256,312,584743,1,0 +172,294,584893,2,0,B|52:244,1,129.999997675419 +36,152,585193,2,0,B|156:204,1,129.999997675419 +236,232,585493,6,0,B|252:180|216:136|216:136|72:72,1,259.999995350838 +292,108,585943,2,0,B|408:44,1,129.999997675419 +320,184,586243,2,0,B|452:184,1,129.999997675419 +312,268,586543,1,0 +312,268,586693,6,0,B|432:316,1,129.999997675419,4|0 +332,376,586993,2,0,B|296:328|292:256|360:184|444:200,1,259.999995350838 +180,376,587593,2,0,B|216:328|220:256|152:184|68:200,1,259.999995350838 +96,124,588043,2,0,B|172:128|216:172,1,129.999997675419 +416,124,588343,2,0,B|340:128|296:172,1,129.999997675419 +140,52,588793,2,0,B|200:64|256:108|256:108|312:64|372:52,1,259.999995350838 +256,12,589243,1,0 +256,12,589393,1,0 +372,224,589693,5,0 +140,224,589993,1,0 +292,360,590293,2,0,B|337:315|305:263|275:254|246:256|222:249|174:294|190:335|226:364|226:364,2,259.999995350838 +364,141,591193,1,0 +256,101,591343,1,0 +148,141,591493,1,4 +363,54,591793,6,0,B|327:0|255:-35|182:0|146:54,2,259.999995350838 +256,103,592543,1,0 +362,150,592693,2,0,B|326:204|254:239|181:204|145:150,1,259.999995350838 +138,299,593143,1,0 +380,297,593443,1,0 +379,296,593593,1,0 +368,184,593893,6,0,B|308:180|244:244|252:324|304:376,1,259.999995350838 +380,288,594343,1,0 +272,320,594493,2,0,B|224:272|152:288,1,129.999997675419 +64,216,594793,2,0,B|20:272|52:340,1,129.999997675419 +144,200,595093,6,0,B|204:204|268:140|260:60|208:8,1,259.999995350838 +132,92,595543,1,0 +240,64,595693,2,0,B|288:112|360:96,1,129.999997675419 +448,168,595993,2,0,B|492:112|460:44,2,129.999997675419,0|0|4 +136,320,596593,1,0 +136,320,596743,1,0 +376,320,597043,1,0 +376,320,597193,1,0 +376,320,597493,6,0,B|424:268|422:191|375:126|311:112,1,259.999995350838 +136,320,597943,2,0,B|87:268|89:191|136:126|200:112,1,259.999995350838 +256,48,598393,1,0 +196,348,598693,2,0,B|256:392|316:348,1,129.999997675419 +256,272,598993,1,0 +256,272,599143,1,0 +308,144,599293,6,0,B|304:76|344:16|432:0|492:60,1,259.999995350838 +412,132,599743,1,0 +336,276,599893,2,0,B|340:204|256:148|172:204|176:276,1,259.999995350838 +100,132,600343,1,0 +25,55,600493,2,0,B|79:0|167:15|207:75|203:143,1,259.999995350838 +256,272,600943,1,0 +256,192,601018,12,0,602293 +256,192,602368,12,0,603493 +180,344,603793,6,0,B|205:286|205:286|257:262|257:262|310:286|310:286|334:344,1,240.000005722046 +488,176,604393,2,0,B|434:168|426:130|417:108|424:85|424:85|402:93|379:86|346:75|337:29,1,240.000005722046 +172,32,604993,2,0,B|164:76|131:86|108:93|86:85|86:85|93:108|85:130|76:168|24:176,1,240.000005722046 +168,320,605593,1,0 +256,360,605743,1,0 +344,320,605893,1,4 +453,145,606193,5,0 +256,20,606493,1,0 +59,145,606793,1,0 +142,357,607093,1,0 +370,358,607393,1,0 +256,188,607693,1,0 +315,98,607843,2,0,B|259:68|186:76|145:139|146:199,1,240.000005722046 +197,285,608293,2,0,B|250:314|325:307|366:246|366:183,1,240.000005722046 +464,288,608893,5,0 +256,156,609193,1,0 +48,288,609493,1,0 +166,112,609793,2,0,B|184:57|256:39|256:39|328:57|344:112,1,240.000005722046 +320,192,610243,2,0,B|320:256|255:272|255:272|182:292|198:370,1,240.000005722046 +314,370,610693,2,0,B|330:292|256:272|256:272|192:256|192:192,1,240.000005722046,4|0 +384,80,611293,5,0 +128,80,611593,1,0 +128,304,611893,1,0 +384,304,612193,1,0 +256,128,612493,1,0 +165,185,612643,2,0,B|217:196|249:228|257:252|257:252|265:228|289:196|352:183,1,240.000005722046 +346,280,613093,6,0,B|327:359|188:394|160:272|160:272,1,240.000005722046 +64,88,613693,1,0 +228,32,613993,2,0,B|256:24|256:24|284:32,4,60.0000014305115 +164,136,614443,2,0,B|200:136|200:136|224:112|224:112|256:152|256:152|288:112|288:112|312:136|312:136|352:136,1,240.000005722046 +420,208,614893,1,0 +368,300,615043,1,0 +256,344,615193,1,0 +144,300,615343,1,0 +92,208,615493,1,4 +420,48,615793,5,0 +92,48,616093,1,0 +92,336,616393,1,0 +420,336,616693,1,0 +256,40,616993,1,0 +88,200,617293,1,0 +160,272,617443,1,0 +256,304,617593,1,0 +352,272,617743,1,0 +424,200,617893,1,0 +164,76,618193,6,0,B|183:155|322:190|350:68|350:68,1,240.000005722046 +345,308,618793,2,0,B|323:230|188:196|161:312|161:312,1,240.000005722046 +388,192,619393,1,0 +124,192,619693,1,0 +200,108,619843,2,0,B|256:128|256:128|312:108,1,120.000002861023 +256,16,620143,1,0 +396,48,620293,6,0,B|451:66|469:138|469:138|451:210|396:226,1,240.000005722046 +116,336,620893,2,0,B|61:318|43:246|43:246|61:174|116:158,1,240.000005722046 +388,280,621493,5,0 +256,48,621793,1,0 +124,280,622093,1,0 +56,196,622243,2,0,B|116:172|116:172|136:116|136:116,1,120.000002861023 +202,192,622543,2,0,B|256:218|256:218|309:192,1,120.000002861023 +377,119,622843,2,0,B|376:116|396:172|396:172|456:196,1,120.000002861023 +400,288,623143,1,0 +304,344,623293,1,0 +256,360,623368,1,0 +208,344,623443,1,0 +128,272,623593,5,0 +160,168,623743,1,0 +256,128,623893,1,0 +352,168,624043,1,0 +384,272,624193,1,0 +256,248,624343,1,0 +352,168,624493,5,0 +368,112,624568,1,0 +352,64,624643,1,0 +312,24,624718,1,0 +256,8,624793,1,0 +200,24,624868,1,0 +160,64,624943,1,0 +144,112,625018,1,0 +160,168,625093,1,4 +84,244,625243,5,0 +152,336,625393,1,0 +256,364,625543,1,0 +360,336,625693,1,0 +428,244,625843,1,0 +428,140,625993,1,0 +360,48,626143,1,0 +256,20,626293,1,0 +152,48,626443,1,0 +84,140,626593,1,0 +206,158,626743,6,0,B|222:129|256:125|289:129|305:158,1,120.000002861023 +376,240,627043,1,0 +304,320,627193,2,0,B|287:349|254:353|220:349|204:320,1,120.000002861023 +136,240,627493,1,0 +256,248,627643,1,0 +312,144,627793,5,0 +312,144,627868,1,0 +312,144,627943,1,0 +344,48,628093,1,0 +256,0,628243,1,0 +168,48,628393,1,0 +200,144,628543,1,0 +96,168,628693,5,0 +88,272,628843,1,0 +192,320,628993,1,0 +256,232,629143,1,0 +312,144,629293,1,0 +416,168,629443,1,0 +424,272,629593,1,0 +320,320,629743,1,0 +256,232,629893,1,0 +200,144,630043,5,0 +208,40,630193,1,0 +256,16,630268,1,0 +304,40,630343,1,0 +312,144,630493,1,0 +416,200,630643,2,0,B|488:304,1,120.000002861023 +408,376,630943,2,0,B|339:277,1,120.000002861023 +256,344,631243,1,0 +172,277,631393,2,0,B|104:376,1,120.000002861023 +27,298,631693,2,0,B|96:200,1,120.000002861023 +93,91,631993,5,0 +206,63,632143,2,0,B|222:33|255:29|289:33|305:63,1,120.000002861023 +419,91,632443,1,0 +352,176,632593,1,0 +360,184,632668,1,0 +368,192,632743,1,0 +400,296,632893,2,0,B|456:278|456:278|476:222,1,120.000002861023 +308,356,633193,2,0,B|289:356|289:356|265:346|265:346|256:360|256:360|246:346|246:346|227:356|227:356|208:356,1,120.000002861023 +35,222,633493,2,0,B|55:278|55:278|112:296,1,120.000002861023 +184,96,633793,5,0 +256,272,633943,1,0 +328,96,634093,1,0 +160,200,634243,1,0 +352,200,634393,1,0 +256,32,634543,1,0 +256,168,634693,5,4 +352,328,634993,5,0 +400,88,635293,1,0 +160,40,635593,1,0 +112,248,635893,1,0 +276,357,636193,5,0 +431,167,636493,1,0 +242,12,636793,1,0 +102,173,637093,1,0 +205,350,637393,5,0 +425,244,637693,1,0 +319,23,637993,1,0 +124,110,638293,1,0 +138,310,638593,5,0 +383,315,638893,1,0 +389,71,639193,1,0 +175,59,639493,1,0 +99,249,639793,5,0 +319,358,640093,1,0 +427,138,640393,1,0 +239,38,640693,1,0 +89,186,640993,5,0 +252,369,641293,1,0 +435,206,641593,1,0 +297,43,641893,1,0 +107,116,642193,5,0 +177,351,642493,1,0 +412,281,642793,1,0 +304,240,642943,5,0 +256,224,643018,1,0 +208,240,643093,1,0 +136,155,643243,2,0,B|187:189|256:155|307:103|307:51|290:17|256:0|221:17|204:51|204:103|256:155|324:189|376:155,1,480.000011444092 +40,44,671736,6,0,B|40:164,1,100,8|8 +120,184,672236,2,0,B|120:76,1,100,8|0 +200,126,672736,2,0,B|217:74|276:53|341:72|356:135,1,200,4|8 +436,72,673486,1,0 +484,148,673736,1,8 +412,204,673986,2,0,B|408:244|424:280|464:296|464:296,1,100,0|8 +404,368,674486,5,0 +325,319,674736,2,0,B|299:294|264:292|232:320|232:320|204:348|157:346|140:313,1,200,8|8 +64,352,675486,2,0,B|16:316|12:208|68:160,1,200 +122,106,676236,2,0,B|174:90|190:38,1,100,8|0 +267,84,676736,2,0,B|243:131|281:187|370:171|373:107|349:70,1,200,8|8 +428,28,677486,5,0 +488,96,677736,1,8 +424,160,677986,1,0 +484,225,678236,2,0,B|470:274|470:274|486:322,1,100,8|0 +412,368,678736,2,0,B|308:368,1,100,8|0 +252,300,679236,1,8 +184,360,679486,5,0 +108,312,679736,2,0,B|48:328|4:284|4:204|68:188|68:188,1,200,8|8 +148,148,680486,2,0,B|194:117|188:60,1,100,0|8 +268,20,680986,1,0 +332,80,681236,2,0,B|448:48,1,100,8|0 +484,124,681736,2,0,B|388:152,1,100,8|0 +376,244,682236,6,0,B|492:212,1,100,8|0 +496,304,682736,1,8 +328,373,683236,2,0,B|313:319|252:296|191:320|176:383,1,200,8|8 +88,328,683986,2,0,B|16:248|48:136|48:136,1,200 +120,192,684736,2,0,B|124:75|216:16,1,200,8|8 +280,80,685486,5,0 +360,32,685736,2,0,B|480:32|480:32,1,100,8|0 +488,120,686236,2,0,B|440:128|440:168|488:176,1,100,8|0 +432,248,686736,2,0,B|480:256|480:296|432:304,1,100,8|0 +360,360,687236,1,8 +288,304,687486,5,0 +216,360,687736,2,0,B|104:360|104:360,1,100,8|0 +72,280,688236,2,0,B|184:280|184:280,1,100,8|0 +136,200,688736,2,0,B|85:203|44:162|36:100|88:60,1,200,12|8 +169,24,689486,1,0 +256,56,689736,2,0,B|360:36,1,100,8|0 +424,88,690236,6,0,B|444:188,1,100,8|0 +368,232,690736,1,8 +216,344,691236,2,0,B|223:318|255:306|287:319|295:351,1,100,8|0 +144,232,692236,1,8 +68,186,692486,2,0,B|88:88,1,100,0|8 +256,200,693236,5,8 +336,72,693736,1,8 +256,24,693986,1,0 +176,72,694236,1,8 +177,236,694736,2,0,B|198:284|254:310|321:285|336:232,1,200,8|8 +408,280,695486,1,0 +408,280,695736,5,8 +256,368,696236,1,8 +104,280,696736,1,4 +104,104,697236,1,8 +256,16,697736,1,8 +408,104,698236,5,8 +408,104,698486,1,0 +408,104,698736,1,8 +338,275,699236,2,0,B|315:320|259:346|192:321|176:269,1,200,8|8 +175,175,699986,2,0,B|198:130|254:104|321:129|337:181,1,200 +257,228,700736,5,8 +256,48,701236,1,8 +176,320,701736,1,8 +256,360,701986,1,0 +336,320,702236,1,8 +352,144,702736,2,0,B|287:143|287:143|255:127|255:127|223:143|223:143|151:143,1,200,8|8 +300,32,703736,5,8 +212,32,703986,1,0 +60,121,704486,6,0,B|94:207|94:207|50:304,1,200,8|0 +49,304,705236,1,8 +124,344,705486,2,0,B|169:247|169:247|257:217,1,200,8|0 +452,120,706236,1,8 +452,120,706486,2,0,B|417:207|417:207|462:304,1,200,8|0 +463,304,707236,1,8 +387,343,707486,2,0,B|342:247|342:247|254:217,1,200,8|0 +158,53,708236,1,8 +158,53,708486,2,0,B|223:52|223:52|255:36|255:36|287:52|287:52|359:52,1,200,8|0 +352,138,709236,1,8 +352,138,709486,2,0,B|287:139|287:139|255:155|255:155|223:139|223:139|151:139,1,200,8|0 +256,285,710236,5,8 +100,340,710486,1,8 +204,340,710736,5,0 +180,304,710861,1,0 +180,260,710986,1,0 +212,220,711111,1,0 +256,208,711236,1,0 +300,220,711361,1,0 +332,260,711486,1,0 +332,304,711611,1,0 +308,340,711736,1,0 +412,340,711986,37,0 +452,164,712486,21,0 +370,97,712736,2,0,B|340:54|262:14|173:45|137:104,1,259.999995350838,4|8 +370,287,713736,2,0,B|340:330|262:370|173:339|137:280,1,259.999995350838,0|8 +194,214,714486,2,0,B|258:263|325:207,1,129.999997675419,0|8 +256,144,714986,1,8 +96,288,715486,6,0,B|32:240|16:192|16:192|32:128|112:80,1,259.999995350838,8|8 +256,192,716236,1,8 +416,288,716486,2,0,B|480:240|496:192|496:192|480:128|400:80,1,259.999995350838,8|0 +200,76,717236,6,0,B|214:55|253:35|298:50|316:80,1,129.999997675419,8|0 +256,336,717736,1,8 +160,180,717986,2,0,B|174:243|252:283|341:252|352:168,2,259.999995350838,8|8|8 +320,344,719236,6,0,B|184:344,1,129.999997675419,8|0 +440,168,719736,2,0,B|360:36,1,129.999997675419,8|0 +72,168,720236,2,0,B|152:36,1,129.999997675419,8|0 +256,216,720736,2,0,B|256:296|256:296|360:292|368:200|340:136|304:100|196:92|156:152|149:244|204:280,1,519.999990701676,4|0 +348,360,722236,5,8 +256,384,722486,1,0 +172,360,722736,1,8 +256,208,723236,1,8 +299,22,723736,1,8 +214,25,723986,2,0,B|173:80|209:160|333:145|330:56|296:19,1,259.999995350838,8|8 +404,172,724986,5,8 +344,240,725236,1,8 +256,260,725486,1,0 +168,236,725736,1,8 +108,172,725986,1,0 +140,340,726486,2,0,B|170:383|248:423|337:392|373:333,1,259.999995350838,8|0 +316,268,727236,2,0,B|252:317|185:261,1,129.999997675419,8|0 +256,176,727736,5,8 +160,128,727986,1,0 +192,96,728111,1,0 +212,48,728236,1,0 +256,20,728361,1,0 +300,48,728486,1,0 +320,96,728611,1,0 +352,128,728736,1,4 +400,224,728986,6,0,B|400:288|400:288|464:352,1,129.999997675419 +352,368,729486,1,0 +279,292,729736,2,0,B|297:263|279:224|217:231|219:275|235:294,1,129.999997675419 +160,368,730236,1,0 +61,333,730486,2,0,B|107:286|107:286|107:222,1,129.999997675419 +195,167,730986,2,0,B|256:143|256:143|316:167,1,129.999997675419 +379,84,731486,2,0,B|256:35|256:35|135:84,1,259.999995350838 +207,173,732236,5,0 +305,174,732486,2,0,B|373:206|373:323|285:353|256:323|256:323|212:279|226:235|285:235|299:279|256:323|256:323|212:353|138:309|138:206|214:170,1,649.999988377094 +162,79,733986,1,0 +350,79,734486,1,0 +365,286,734986,2,0,B|335:243|257:203|168:234|132:293,1,259.999995350838 +195,370,735736,6,0,B|259:419|326:363,2,129.999997675419 +137,282,736486,1,0 +79,182,736736,2,0,B|202:174|221:20|153:-13|84:20|84:89|187:123|256:20|256:20|309:109|412:102|451:11|334:-40|296:20|329:166|352:180|459:186,1,779.999986052513,4|0 +344,248,738486,5,0 +256,192,738736,1,0 +168,248,738986,1,0 +355,355,739486,2,0,B|280:360|256:287|256:287|232:360|155:355,1,259.999995350838 +80,288,740236,1,0 +58,186,740486,6,0,B|28:128|76:66|76:66|158:49|196:100,1,259.999995350838 +256,184,741236,2,0,B|256:216|256:216|240:232|240:232|272:240|272:240|256:256|256:256|256:280,2,129.999997675419 +316,99,741986,2,0,B|354:49|436:66|436:66|484:128|454:186,1,259.999995350838 +315,349,742986,6,0,B|251:398|184:342,1,129.999997675419 +205,246,743486,1,0 +307,246,743736,1,0 +368,160,743986,5,0 +324,156,744111,1,0 +288,132,744236,1,0 +256,100,744361,1,0 +224,132,744486,1,0 +188,156,744611,1,0 +144,160,744736,1,4 +256,12,751736,5,8 +181,62,751986,1,0 +209,150,752236,1,8 +301,150,752486,1,0 +329,62,752736,1,4 +328,243,753236,2,0,B|313:297|252:320|191:296|176:233,1,200,8|8 +344,356,754236,5,8 +256,384,754486,1,0 +168,356,754736,1,8 +126,200,755236,1,8 +183,128,755486,2,0,B|233:132|253:72|253:72|273:132|336:128,1,200 +385,200,756236,1,8 +296,240,756486,2,0,B|288:216|259:203|225:213|217:244,1,100,0|8 +256,372,757236,5,8 +416,228,757736,1,8 +356,56,758236,1,8 +156,56,758736,1,8 +84,228,759236,1,8 +141,298,759486,5,0 +200,225,759736,1,4 +183,187,759861,1,0 +188,145,759986,1,0 +213,111,760111,1,0 +256,94,760236,1,0 +298,111,760361,1,0 +323,145,760486,1,0 +328,187,760611,1,0 +311,225,760736,1,0 +370,298,760986,5,0 +308,368,761236,2,0,B|196:368|196:368,1,100,8|0 +176,280,761736,2,0,B|200:272|213:243|203:209|172:201,1,100,8|0 +256,152,762236,1,8 +336,204,762486,2,0,B|310:211|300:245|313:274|337:282,1,100,0|8 +344,60,763236,2,0,B|256:12|256:12|168:60,1,200,8|8 +169,139,763986,2,0,B|256:186|256:186|344:138,1,200 +256,100,764736,5,8 +256,260,765236,1,8 +120,344,765736,1,8 +256,260,766236,1,8 +392,344,766736,1,8 +446,187,767236,5,8 +385,127,767486,1,0 +368,40,767736,1,8 +296,100,767986,2,0,B|289:126|255:136|226:123|218:99,1,100,0|8 +143,40,768486,1,0 +126,127,768736,1,12 +65,192,768986,1,0 +126,256,769236,5,8 +144,343,769486,1,0 +218,284,769736,2,0,B|226:261|255:248|289:258|296:284,1,100,8|0 +368,343,770236,1,8 +385,256,770486,1,0 +446,192,770736,1,8 +376,38,771236,5,8 +300,84,771486,2,0,B|324:131|286:187|197:171|194:107|218:70,1,200 +135,38,772236,1,8 +100,120,772486,5,0 +40,52,772736,1,8 +57,216,773236,1,8 +181,324,773736,2,0,B|196:375|260:399|323:374|338:309,1,200,8|8 +454,216,774736,1,8 +472,52,775236,5,8 +412,120,775486,1,0 +376,38,775736,1,8 +300,200,776236,2,0,B|324:153|286:97|197:113|194:177|218:214,1,200,8|8 +328,320,777236,2,0,B|278:316|258:376|258:376|238:316|175:320,1,200,8|8 +100,120,778236,5,8 +40,52,778486,1,0 +135,38,778736,1,8 +337,193,779236,2,0,B|314:148|258:122|191:147|175:199,1,200,8|8 +177,284,779986,2,0,B|193:332|254:358|327:334|336:280,1,200 +256,240,780736,5,8 +340,56,781236,1,8 +172,56,781736,1,8 +256,104,781986,1,0 +340,56,782236,1,8 +352,228,782736,2,0,B|287:229|287:229|255:245|255:245|223:229|223:229|151:229,1,200,8|8 +360,352,783736,5,8 +256,384,783986,1,8 +152,352,784236,1,8 +173,256,784486,2,0,B|185:285|256:336|325:292|350:244,1,200,8|0 +256,196,785236,1,0 +309,100,785486,2,0,B|336:52|291:-27|183:-4|172:77|210:125,1,240.000005722046 +112,168,786236,1,0 +128,272,786486,6,0,B|168:232|213:263|213:263|256:289|303:260|303:260|356:231|392:283|392:283,1,279.99999332428 +400,367,787236,1,0 +400,367,787486,2,0,B|354:412|303:377|303:377|254:347|200:380|200:380|140:413|99:354|99:354,1,320 +92,204,788236,5,0 +92,204,788486,2,0,B|135:142|257:127|384:123|441:224|441:224,1,359.999982833863 +442,299,789236,1,0 +442,299,789486,2,0,B|390:365|256:382|119:384|55:280|55:280,1,400 +106,180,790236,5,0 +32,87,790486,1,0 +160,64,790736,1,0 +200,100,790861,1,0 +216,156,790986,1,0 +208,212,791111,1,0 +256,244,791236,1,0 +304,212,791361,1,0 +296,156,791486,1,0 +312,100,791611,1,0 +356,64,791736,1,0 +484,96,791986,5,0 +412,196,792236,1,0 +412,196,792361,1,0 +412,196,792486,1,0 +370,318,792736,2,0,B|340:361|262:401|173:370|137:311,1,259.999995350838,4|0 +316,168,793736,6,0,B|345:220|295:307|176:281|164:192|206:139,1,259.999995350838 +160,68,794486,1,0 +256,8,794736,1,0 +352,68,794986,1,0 +360,240,795486,2,0,B|402:240|402:240|442:240|458:272|458:296|458:296|458:320|442:352|402:352|402:352|354:352|354:352,1,259.999995350838 +256,320,796236,1,0 +152,352,796486,2,0,B|158:352|110:352|110:352|70:352|54:320|54:296|54:296|54:272|70:240|110:240|110:240|152:240,1,259.999995350838 +200,144,797236,6,0,B|256:176|256:176|312:144,1,129.999997675419 +312,48,797736,2,0,B|255:15|255:15|199:47,1,129.999997675419 +368,232,798486,2,0,B|254:297|254:297|142:232,1,259.999995350838 +88,320,799236,6,0,B|200:384,1,129.999997675419 +311,384,799736,2,0,B|424:320,1,129.999997675419 +475,229,800236,1,0 +437,131,800486,1,0 +334,121,800736,2,0,B|354:55|308:3|254:-3|182:5|140:90|200:154|254:187|254:187|312:220|421:280|335:420|211:416|164:335|177:272,1,779.999986052513,4|0 +72,290,802486,5,0 +13,204,802736,1,0 +143,53,803236,2,0,B|173:10|251:-30|340:1|376:60,1,259.999995350838 +370,149,803986,2,0,B|340:192|262:232|173:201|137:142,1,259.999995350838 +192,360,804986,6,0,B|336:360,1,129.999997675419 +324,268,805486,2,0,B|180:268,1,129.999997675419 +128,177,805986,2,0,B|408:177,1,259.999995350838 +206,47,806986,6,0,B|216:16|254:0|298:13|307:47,1,129.999997675419 +304,152,807486,1,0 +256,168,807611,1,0 +208,152,807736,1,0 +128,216,807986,1,0 +160,256,808111,1,0 +208,280,808236,1,0 +256,288,808361,1,0 +304,280,808486,1,0 +352,256,808611,1,0 +384,216,808736,1,4 +464,128,808986,6,0,B|496:256,1,129.999997675419 +320,352,809486,2,0,B|192:384,1,129.999997675419 +16,254,809986,2,0,B|48:128,1,129.999997675419 +197,23,810486,6,0,B|168:76|217:160|334:136|346:48|305:-4,1,259.999995350838 +416,64,811236,1,0 +370,149,811486,2,0,B|340:192|262:232|173:201|137:142,1,259.999995350838 +96,64,812236,1,0 +48,160,812486,2,0,B|96:272|224:320|288:320|416:272|464:160,1,519.999990701676 +512,256,813736,1,0 +512,256,813986,5,0 +256,368,814486,1,0 +0,256,814986,1,0 +0,256,815236,1,0 +72,176,815486,5,0 +104,216,815611,1,0 +110,268,815736,1,0 +208,312,815986,1,0 +256,296,816111,1,0 +304,312,816236,1,0 +402,268,816486,1,0 +408,216,816611,1,0 +440,176,816736,1,4 +360,120,816986,6,0,B|391:110|407:72|394:28|360:19,1,129.999997675419 +256,0,817486,2,0,B|256:128,1,129.999997675419 +152,120,817986,2,0,B|121:110|105:72|118:28|152:19,2,129.999997675419 +215,203,818736,1,0 +300,206,818986,2,0,B|341:261|305:341|181:326|184:237|218:200,1,259.999995350838 +352,352,819986,1,0 +256,384,820236,1,0 +160,352,820486,1,0 +92,159,820986,5,0 +149,69,821236,1,0 +256,36,821486,1,0 +362,69,821736,1,0 +419,159,821986,1,0 +349,362,822486,5,0 +307,353,822611,1,0 +273,328,822736,1,0 +238,302,822861,1,0 +213,260,822986,1,0 +196,226,823111,1,0 +187,183,823236,1,0 +187,140,823361,1,0 +213,106,823486,1,0 +256,89,823611,1,0 +298,106,823736,1,0 +324,140,823861,1,0 +324,183,823986,1,0 +315,226,824111,1,0 +298,260,824236,1,0 +273,302,824361,1,0 +238,328,824486,1,0 +204,353,824611,1,0 +162,362,824736,1,4 +40,240,824986,6,0,B|71:230|87:192|74:148|40:139,1,129.999997675419 +205,34,825486,2,0,B|215:65|253:81|297:68|306:34,1,129.999997675419 +472,144,825986,2,0,B|441:154|425:192|438:236|472:245,1,129.999997675419 +307,350,826486,2,0,B|297:319|259:303|215:316|206:350,1,129.999997675419 +128,56,827236,5,0 +384,56,827736,1,0 +148,348,828236,1,0 +256,296,828486,1,0 +364,348,828736,1,0 +24,104,829236,2,0,B|93:112|135:188|104:277|12:281,1,259.999995350838 +148,36,830236,5,0 +256,88,830486,1,0 +364,36,830736,1,0 +488,280,831236,2,0,B|419:272|377:196|408:107|500:103,1,259.999995350838 +306,327,832236,2,0,B|296:358|258:374|214:361|205:327,1,129.999997675419 +256,192,832736,5,4 +64,48,833236,1,0 +184,280,833736,5,0 +152,152,833986,1,0 +256,80,834236,1,0 +360,152,834486,1,0 +328,280,834736,1,4 +448,48,835236,5,0 +256,192,835736,1,0 +64,336,836236,5,0 +152,240,836486,2,0,B|121:230|105:192|118:148|152:139,1,129.999997675419 +360,240,837236,2,0,B|391:230|407:192|394:148|360:139,1,129.999997675419 +440,336,838236,1,0 +358,236,838486,2,0,B|305:220|265:252|257:292|257:292|249:252|205:218|151:239,1,259.999995350838 +72,336,839236,1,0 +24,216,839486,5,0 +72,96,839736,1,0 +200,32,839986,1,0 +184,80,840111,1,0 +208,128,840236,1,0 +256,144,840361,1,0 +304,128,840486,1,0 +328,80,840611,1,0 +312,32,840736,1,4 +440,96,840986,1,0 +488,216,841236,1,0 +390,300,841486,5,0 +341,342,841611,1,0 +328,280,841736,1,0 +184,280,841986,1,0 +171,342,842111,1,0 +122,300,842236,1,0 +87,178,842486,5,0 +195,106,842736,2,0,B|207:67|256:59|256:59|288:43|272:11|240:11|224:43|256:59|256:59|302:67|321:111,1,259.999995350838 +424,176,843486,1,0 +424,176,843611,1,0 +424,176,843736,1,0 +376,296,843986,1,0 +256,352,844236,1,0 +136,296,844486,1,0 +136,296,844611,1,0 +136,296,844736,1,4 +88,176,844986,1,0 +204,101,845236,2,0,B|214:70|252:54|296:67|305:101,1,129.999997675419 +384,352,845986,5,0 +360,304,846111,1,0 +304,288,846236,1,0 +256,320,846361,1,0 +208,288,846486,1,0 +152,304,846611,1,0 +128,352,846736,1,4 +208,192,846986,5,0 +256,208,847111,1,0 +304,192,847236,1,0 +400,128,847486,1,0 +416,80,847611,1,0 +384,32,847736,1,0 +328,24,847861,1,0 +288,56,847986,1,0 +256,88,848111,1,0 +224,56,848236,1,0 +184,24,848361,1,0 +128,32,848486,1,0 +96,80,848611,1,0 +112,128,848736,1,0 +152,248,848986,5,0 +152,248,849111,1,0 +152,248,849236,1,0 +144,376,849486,1,0 +144,376,849611,1,0 +144,376,849736,1,0 +256,320,849986,1,0 +256,320,850111,1,0 +256,320,850236,1,0 +368,376,850486,1,0 +368,376,850611,1,0 +368,376,850736,1,0 +360,248,850986,1,0 +360,248,851111,1,0 +360,248,851236,1,0 +256,192,851486,5,0 +256,192,851611,1,0 +256,192,851736,1,4 +256,320,851986,1,4 +95,186,852486,6,0,B|159:153|207:57|191:25|127:-7|95:25|63:89|223:153|255:81|255:81|287:25|255:-7|223:25|255:81|255:81|285:136|415:121|383:89|479:-39|301:-25|223:89|431:196|431:196,1,909.999983727932,4|2 +312,280,854486,2,0,B|298:259|259:239|214:254|196:284,1,129.999997675419,0|2 +256,381,854986,1,0 +370,173,855486,2,0,B|340:130|262:90|173:121|137:180,1,259.999995350838,2|0 +208,28,856236,1,2 +304,28,856486,1,0 +256,192,856611,12,0,858236 +256,192,859486,12,4,860736 +191,69,860986,6,0,B|204:43|254:19|302:36|324:74,1,149.999995529652,0|0 +448,172,861486,2,0,B|384:212|384:212|368:304,1,149.999995529652 +256,376,861986,1,0 +140,285,862236,2,0,B|128:212|128:212|64:172,1,149.999995529652 +169,47,862736,2,0,B|216:69|216:133|216:133|256:153|256:153|296:133|296:133|292:69|347:45,1,299.999991059304 +301,358,863736,1,0 +211,358,863986,2,0,B|157:296|201:201|345:218|341:321|296:365,1,299.999991059304 +73,188,864986,5,0 +138,89,865236,1,0 +256,48,865486,1,0 +373,89,865736,1,0 +438,188,865986,1,0 +178,345,866486,2,0,B|212:367|256:331|256:331|284:311|284:279|256:259|228:279|228:311|256:331|256:331|296:367|336:343,1,299.999991059304 +321,225,867236,2,0,B|308:199|258:175|210:192|188:230,1,149.999995529652 +96,148,867736,5,0 +128,108,867861,1,0 +164,80,867986,1,0 +212,64,868111,1,0 +256,56,868236,1,0 +300,64,868361,1,0 +348,80,868486,1,0 +384,108,868611,1,0 +416,148,868736,1,4 +256,224,868986,5,0 +208,364,869236,1,0 +328,276,869486,1,0 +184,276,869736,1,0 +304,364,869986,1,0 +256,224,870236,5,0 +128,112,870486,2,0,B|160:61|249:17|350:49|391:118,1,299.999991059304 +256,200,871236,1,0 +126,291,871486,2,0,B|158:341|247:386|348:354|389:284,1,299.999991059304 +256,200,872236,1,0 +113,156,872486,6,0,B|35:111|52:28|108:-8|156:-7|216:29|209:95|209:95|224:62|256:174|288:62|304:94|304:94|292:32|356:-16|412:-6|465:38|473:120|387:160,1,749.999977648259,0|0 +372,298,873986,2,0,B|311:279|266:316|256:361|256:361|247:316|197:277|136:301,1,299.999991059304 +256,20,874986,5,0 +128,116,875236,1,0 +184,260,875486,1,0 +328,260,875736,1,0 +384,116,875986,1,0 +256,156,876236,5,0 +256,156,876361,1,0 +256,156,876486,1,0 +256,156,876611,1,0 +256,156,876736,1,4 +256,308,876986,5,0 +400,356,877236,1,0 +488,236,877486,1,0 +400,116,877736,1,0 +256,156,877986,1,0 +256,308,878236,1,0 +112,364,878486,1,0 +24,236,878736,1,0 +112,116,878986,1,0 +408,64,879486,6,0,B|459:96|503:185|471:286|402:327,1,299.999991059304 +256,192,880236,1,0 +104,64,880486,2,0,B|53:96|9:185|41:286|110:327,1,299.999991059304 +322,237,881236,2,0,B|309:263|259:287|211:270|189:232,1,149.999995529652 +320,147,881736,2,0,B|307:121|257:97|209:114|187:152,1,149.999995529652 +388,304,882486,6,0,B|356:355|267:399|166:367|125:298,1,299.999991059304 +256,192,883236,1,0 +184,60,883486,1,0 +256,32,883611,1,0 +328,60,883736,1,0 +396,196,883986,1,0 +341,251,884111,1,0 +326,324,884236,1,0 +256,360,884361,1,0 +185,324,884486,1,0 +170,251,884611,1,0 +115,196,884736,1,4 +256,192,884861,12,0,886236 +128,128,933524,5,2 +214,104,933953,1,0 +152,42,934381,1,2 +65,65,934810,1,0 +42,152,935238,1,2 +104,214,935667,1,0 +190,190,936095,1,2 +212,228,936310,1,0 +256,244,936524,1,0 +300,228,936738,1,0 +323,189,936952,5,2 +408,170,937381,1,0 +470,232,937809,1,2 +447,319,938238,1,0 +360,342,938666,1,2 +298,280,939095,1,0 +323,189,939523,1,2 +384,256,939952,1,0 +160,112,940380,5,2 +212,38,940809,1,0 +300,38,941237,1,2 +352,112,941666,1,0 +292,181,942094,2,0,B|272:153|230:153|219:184|219:184,1,84.9999964535238,2|0 +256,260,942951,1,2 +212,288,943165,1,0 +228,340,943380,1,0 +280,340,943594,1,0 +300,292,943808,1,2 +396,256,944236,6,0,B|468:192,1,84.9999964535238,0|2 +416,32,945093,2,0,B|376:108,1,84.9999964535238,0|2 +256,48,945950,1,0 +135,107,946379,2,0,B|96:32,1,84.9999964535238,2|0 +52,199,947236,2,0,B|116:256,1,84.9999964535238,2|0 +292,328,948093,6,0,B|272:356|230:356|219:325|219:325,1,84.9999964535238,2|0 +324,208,948950,2,0,B|352:124,1,84.9999964535238,2|0 +188,208,949807,2,0,B|160:124,1,84.9999964535238,2|0 +256,64,950449,1,0 +256,64,950664,5,2 +400,160,951092,1,0 +340,324,951521,1,2 +172,324,951949,1,0 +112,160,952378,1,2 +256,64,952806,1,0 +256,200,953235,5,2 +188,328,953663,1,0 +256,356,953877,1,0 +324,328,954092,1,2 +428,228,954520,1,0 +392,92,954948,1,2 +256,40,955377,1,0 +120,92,955805,1,2 +84,228,956234,1,0 +220,208,956662,6,0,B|240:236|282:236|293:205|293:205,1,84.9999964535238,2|0 +292,352,957519,2,0,B|272:324|230:324|219:355|219:355,1,84.9999964535238,2|0 +124,244,958376,1,2 +256,140,958805,1,0 +392,244,959233,1,2 +412,80,959662,1,0 +256,264,960090,1,2 +100,80,960519,1,0 +100,80,960733,1,0 +100,80,960947,5,2 +256,348,961376,1,0 +412,80,961804,1,2 +100,304,962233,5,0 +256,36,962661,1,2 +412,304,963090,1,0 +191,224,963518,1,2 +256,276,963732,1,0 +320,224,963947,1,0 +294,150,964161,1,0 +217,150,964375,1,2 +256,360,964804,5,0 +88,256,965232,1,2 +160,64,965660,1,0 +352,64,966089,1,2 +424,256,966517,1,0 +256,192,966946,1,2 +256,192,967160,1,0 +256,192,967374,1,0 +256,192,967589,1,0 +256,192,967803,5,2 +220,352,968231,2,0,B|240:380|282:380|293:349|293:349,1,84.9999964535238,0|2 +364,197,969088,1,0 +396,120,969303,1,0 +148,197,969731,1,0 +116,120,969945,1,0 +256,32,970374,5,2 +256,200,970802,1,0 +256,368,971231,1,2 +196,300,971445,2,0,B|224:300|224:300|256:284|256:284|288:300|288:300|320:300,1,127.499994680286,0|2 +439,128,972516,2,0,B|452:96|439:66|439:66,1,50 +352,57,972945,6,0,B|398:112|337:186|274:153|255:116|255:116|237:84|198:18|65:115|168:182|168:182,1,400,2|2 +120,348,975087,5,0 +178,278,975301,2,0,B|193:324|256:356|318:324|334:278,1,200 +392,348,976372,1,2 +355,173,976801,2,0,B|335:114|257:75|179:114|160:173,1,250 +95,106,978086,5,2 +256,24,978515,1,0 +256,24,978729,1,0 +256,328,979372,1,2 +336,168,979800,2,0,B|312:128|256:112|256:112|200:128|176:168,1,200,0|0 +417,106,981300,5,0 +417,106,981514,1,2 +256,24,981943,1,0 +295,191,982371,2,0,B|282:170|254:162|254:162|226:170|214:191,1,100,2|0 +216,280,983014,2,0,B|232:280|232:280|248:272|248:272|256:280|256:280|256:264|256:264|264:272|264:272|280:280|280:280|296:280,1,100 +344,351,983657,2,0,B|320:383|280:383|256:359|256:359|232:383|192:383|168:351,1,200,2|0 +80,328,984728,5,0 +144,264,984942,1,2 +56,240,985156,1,0 +256,56,985799,1,0 +368,264,986227,1,0 +456,240,986442,1,0 +432,328,986656,1,2 +56,264,987513,6,0,B|102:249|134:186|102:124|56:108,1,200,0|2 +256,92,988798,1,0 +256,92,989013,2,0,B|256:292,1,200 +256,292,990084,1,2 +456,264,990512,2,0,B|410:249|378:186|410:124|456:108,1,200 +256,36,991798,1,2 +196,108,992012,2,0,B|224:108|224:108|256:124|256:124|288:108|288:108|320:108,1,127.499994680286 +346,348,993083,5,2 +256,312,993297,1,0 +168,349,993512,2,0,B|111:265|160:184|191:152|261:139|319:160|356:186|407:266|341:356,1,500 +217,61,996939,6,0,B|224:83|256:100|287:83|295:61,1,100,2|0 +168,192,997796,1,2 +335,302,998225,2,0,B|320:348|257:380|195:348|179:302,1,200,0|2 +344,192,999510,1,0 +188,56,999939,5,2 +256,100,1000153,1,0 +324,56,1000367,1,0 +424,200,1000796,2,0,B|464:228|464:228|480:284,1,100,0|2 +348,360,1001653,1,0 +220,204,1002081,2,0,B|197:237|216:270|229:282|257:288|280:279|295:269|315:237|289:201,1,200,2|0 +164,360,1003367,5,2 +88,200,1003795,2,0,B|48:228|48:228|32:284,1,100 +144,60,1004652,1,2 +194,232,1005081,2,0,B|208:276|208:276|256:300|256:300|304:276|304:276|320:228,1,200 +369,59,1006366,1,2 +448,224,1006795,6,0,B|419:326,1,100,0|2 +256,240,1007651,2,0,B|256:136,1,100,0|2 +64,224,1008508,2,0,B|93:326,1,100,0|2 +256,48,1009794,5,2 +400,152,1010222,1,0 +348,328,1010651,1,2 +164,328,1011079,1,0 +112,152,1011508,1,2 +256,48,1011936,1,0 +256,196,1012793,5,2 +256,348,1013864,5,0 +256,348,1014079,1,2 +256,52,1014507,1,0 +45,192,1015364,2,0,B|61:143|110:127|175:143|142:192|158:256|239:240|256:192|256:192|272:240|353:256|369:192|336:143|401:127|450:143|466:192,1,549.999983608723,2|0 +179,48,1022649,6,0,B|194:94|257:126|319:94|335:48,1,200,8|0 +340,239,1023506,2,0,B|301:254|270:239|255:208|255:208|240:239|209:254|171:239,1,200,8|0 +172,349,1024148,2,0,B|210:333|241:349|256:379|256:379|271:349|302:333|340:349,1,200,0|0 +432,296,1024791,1,0 +456,64,1025220,1,8 +256,152,1025648,5,0 +64,64,1026077,1,8 +112,152,1026291,1,0 +174,64,1026506,2,0,B|201:28|256:10|256:10|310:28|337:64,1,200,0|8 +424,256,1027363,5,0 +360,336,1027577,1,0 +256,368,1027791,1,8 +152,336,1028006,1,0 +88,256,1028220,1,0 +256,40,1028648,1,8 +424,256,1029077,1,0 +408,56,1029506,5,8 +345,355,1029934,1,0 +282,199,1030148,2,0,B|309:172|361:186|361:251|309:263|309:263|270:277|256:315|256:315|243:277|204:263|204:263|152:251|152:186|204:172|230:199,1,439.999986886979 +167,355,1031220,1,8 +73,146,1031648,2,0,B|75:82|134:16|220:36|257:81|257:81|289:32|366:25|442:61|436:157,1,500 +400,256,1032934,1,8 +221,364,1033363,2,0,B|198:331|217:298|230:286|258:280|281:289|296:299|316:331|290:367,1,200,0|8 +112,256,1034220,5,0 +96,144,1034434,1,0 +200,184,1034648,1,8 +312,184,1034863,1,0 +416,144,1035077,1,0 +400,256,1035291,1,0 +333,336,1035506,6,0,B|318:290|255:258|193:290|177:336,1,200,8|0 +96,256,1036148,1,0 +64,152,1036363,2,8,B|160:120|160:120|192:24,1,200,8|0 +320,26,1037220,2,0,B|352:120|352:120|448:152,1,200,8|0 +336,240,1037863,2,0,B|288:240|288:240|256:200|256:200|224:240|224:240|176:240,1,200 +256,320,1038506,1,0 +64,192,1038934,5,8 +256,48,1039363,1,0 +304,184,1039577,1,0 +180,96,1039791,1,8 +332,96,1040006,1,0 +208,184,1040220,1,0 +448,192,1040648,1,8 +216,336,1041077,2,0,B|222:356|256:370|290:356|297:336,1,100 +333,189,1041506,2,0,B|370:117|336:59|311:34|253:28|214:34|176:54|137:117|184:193,2,400,8|8|8 +348,360,1043648,5,0 +256,320,1043863,1,0 +164,360,1044077,2,0,B|118:376|53:357|33:292|50:246,1,200,8|0 +21,137,1044720,1,0 +131,108,1044934,1,8 +160,217,1045148,1,0 +160,216,1045363,2,0,B|256:172|256:260|352:216,1,200,0|8 +460,28,1046220,1,0 +460,28,1046648,6,0,B|480:76|460:124,1,100,8|0 +348,84,1047077,2,0,B|280:176|348:268,1,200,0|8 +356,368,1047720,1,0 +256,336,1047934,1,0 +156,368,1048148,1,0 +163,268,1048363,2,0,B|231:175|163:83,1,200,8|0 +4,192,1049220,5,8 +256,352,1049648,1,0 +256,352,1049863,1,0 +256,352,1050077,6,0,B|352:328,1,100,4|0 +476,252,1050506,2,0,B|380:228,1,100,2|0 +256,148,1050934,2,0,B|352:124,1,100,2|0 +476,48,1051363,2,0,B|380:24,1,100,2|0 +256,48,1051791,6,0,B|160:92|160:4|64:48,1,200,0|0 +100,144,1052434,1,0 +100,144,1052648,2,0,B|120:192|100:240,1,100 +36,320,1053077,2,0,B|129:388|221:320,1,200,0|4 +256,224,1053720,1,0 +291,320,1053934,2,0,B|383:388|476:320,1,200 +512,116,1054791,6,0,B|416:144,1,100 +328,100,1055220,2,0,B|260:192|328:284,1,200,8|0 +312,384,1055863,1,0 +228,332,1056077,2,0,B|200:284|192:240,1,100,8|0 +191,144,1056506,2,0,B|200:100|228:52,1,100 +256,192,1056934,6,0,B|160:236|160:148|64:192,1,200,8|0 +256,192,1057791,2,0,B|352:148|352:236|448:192,1,200,8|0 +492,104,1058434,5,0 +396,112,1058649,2,0,B|354:80|348:28,1,100,8|0 +256,92,1059077,2,0,B|256:192,1,100,0|0 +115,112,1059506,2,0,B|157:80|164:27,1,100,8|0 +20,104,1059934,6,0,B|-48:196|20:288,1,200,0|8 +72,372,1060577,1,0 +160,324,1060791,2,0,B|256:276|352:324,1,200,0|8 +488,192,1061649,6,0,B|508:96,1,100 +372,152,1062077,2,0,B|352:56,1,100,8|0 +256,164,1062506,2,0,B|210:148|145:167|125:232|142:278,1,200,0|8 +370,277,1063363,2,0,B|387:232|367:167|302:148|256:164,1,200,0|8 +136,128,1064006,5,0 +208,76,1064220,2,0,B|256:96|304:76,1,100 +376,128,1064649,2,0,B|476:144,1,100,8|0 +512,272,1065077,6,0,B|413:256,1,100 +348,332,1065506,2,0,B|256:400|164:332,1,200,8|0 +164,52,1066363,2,0,B|256:-16|348:52,1,200,8|0 +356,152,1067006,1,0 +256,192,1067220,1,4 +156,152,1067434,1,0 +160,280,1067649,6,0,B|256:324|256:236|352:280,2,200,0|8|0 +68,320,1068720,1,0 +156,368,1068934,6,0,B|204:388|252:368,1,100,8|0 +356,192,1069363,2,0,B|308:172|260:192,1,100 +258,192,1069791,1,4 +100,276,1070220,1,0 +28,44,1070649,5,4 +120,88,1070863,1,0 +220,84,1071077,2,0,B|320:76,1,100 +412,32,1071506,2,0,B|442:133|378:220,1,200 +424,308,1072149,1,0 +328,340,1072363,1,0 +244,288,1072577,2,0,B|200:316|184:368,1,100 +88,340,1073006,1,0 +120,244,1073220,2,0,B|29:183|23:77,1,200 +112,28,1073863,1,0 +200,80,1074077,6,0,B|202:131|167:170,1,100 +116,260,1074506,2,0,B|216:280,1,100 +297,279,1074934,2,0,B|396:260,1,100 +345,171,1075363,2,0,B|309:131|312:80,1,100 +404,29,1075791,5,0 +481,103,1076006,1,0 +486,209,1076220,1,0 +445,308,1076434,1,0 +348,353,1076649,2,0,B|256:285|164:353,1,200,0|0 +70,309,1077291,1,0 +52,220,1077506,6,0,B|12:191|1:121|51:72|100:71,1,200,8|2 +186,99,1078149,2,0,B|225:127|236:197|186:246|137:247,1,200,0|0 +204,324,1078791,1,2 +304,324,1079006,1,0 +374,247,1079220,2,0,B|325:246|275:197|286:127|326:99,1,200,8|2 +411,71,1079863,2,0,B|460:72|510:121|499:191|460:220,1,200,0|0 +452,320,1080506,1,2 +360,356,1080720,1,0 +300,276,1080934,1,8 +368,204,1081149,1,0 +375,94,1081363,6,0,B|345:34|301:49|256:20|183:94|183:167|256:212|330:167|330:94|256:20|212:49|168:34|138:94,1,439.999986886979,0|2 +144,204,1082434,1,0 +212,276,1082649,1,8 +152,356,1082863,1,0 +60,320,1083077,1,2 +256,368,1083506,5,8 +344,184,1083934,1,0 +256,136,1084149,1,0 +168,184,1084363,1,8 +334,320,1084791,6,0,B|319:274|256:242|194:274|178:320,1,200,0|8 +88,192,1085434,1,0 +178,64,1085649,2,0,B|193:110|256:142|318:110|334:64,1,200,0|8 +424,192,1086291,1,0 +256,328,1086506,5,0 +96,80,1086934,1,8 +416,80,1087363,1,0 +256,194,1087577,1,0 +126,330,1087791,2,0,B|66:314|68:260|85:226|136:209|170:243|170:243|136:174|170:123|187:89|254:60|324:89|341:123|375:174|341:243|341:243|385:204|452:231|448:316|387:332,1,800,8|8 +432,144,1089720,1,8 +256,192,1089934,1,8 +80,144,1090149,1,0 +256,304,1090363,5,8 +200,120,1090577,1,8 +344,232,1090791,1,0 +168,232,1091006,1,8 +312,120,1091220,1,4 +114,294,1091649,6,0,B|68:279|36:216|68:154|114:138,1,200,2|8 +160,48,1092291,1,0 +256,80,1092506,1,2 +352,48,1092720,1,0 +398,138,1092934,2,0,B|444:154|476:216|444:279|398:294,1,200,8|0 +345,359,1093577,2,0,B|276:403|203:333|201:270|252:276|303:269|300:333|221:403|159:358,1,329.999990165234,2|8 +96,288,1094434,1,0 +176,224,1094649,2,0,B|194:180|256:154|319:178|340:230,1,200,2|0 +416,288,1095291,1,0 +337,343,1095506,6,0,B|319:387|257:413|194:389|173:337,1,200,8|2 +256,292,1096149,1,0 +205,208,1096363,1,8 +306,208,1096577,1,0 +376,130,1096791,2,0,B|380:64|353:-4|258:-45|163:0|124:60|137:130,1,400,2|2 +64,200,1097863,5,0 +64,200,1098077,1,8 +216,360,1098506,2,0,B|200:332|211:298|231:286|256:274|280:285|300:295|321:325|297:370,1,200,0|8 +448,200,1099363,1,0 +297,32,1099791,2,0,B|313:60|302:94|282:106|257:118|233:107|213:97|192:67|216:22,2,200,2|0|8 +156,200,1101077,5,2 +179,297,1101291,1,0 +272,322,1101506,1,10 +341,249,1101720,1,0 +317,152,1101934,1,0 +220,128,1102149,1,0 +146,223,1102363,1,8 +193,321,1102577,1,0 +298,327,1102791,1,0 +355,233,1103006,1,0 +308,134,1103220,5,8 +203,128,1103434,1,8 +149,253,1103649,1,8 +227,345,1103863,1,0 +342,321,1104077,1,8 +376,205,1104291,1,0 +297,113,1104506,1,8 +183,137,1104720,1,0 +147,263,1104934,5,12 +327,301,1105363,1,0 +365,121,1105791,1,8 +185,83,1106220,6,0,B|144:55|77:57|40:114|45:163,1,200,0|8 +76,260,1106863,1,0 +160,192,1107077,2,0,B|256:236|256:148|352:192,1,200,0|8 +436,124,1107720,5,0 +466,221,1107934,2,0,B|471:269|434:326|367:328|327:301,1,200,0|8 +256,232,1108577,1,0 +172,176,1108791,6,0,B|152:128|172:80,1,100 +340,208,1109220,2,0,B|360:256|340:304,1,100,8|0 +163,360,1109649,6,0,B|118:376|53:357|33:292|49:246,1,200,0|8 +16,152,1110291,1,0 +108,116,1110506,2,0,B|84:20,1,100 +224,76,1110934,2,0,B|248:173,1,100,8|0 +336,128,1111363,6,0,B|440:173|428:287,1,200,0|8 +84,287,1112220,2,0,B|71:173|176:128,1,200,0|8 +352,348,1113077,6,0,B|256:304|256:392|160:348,1,200,0|8 +112,276,1113720,1,0 +208,260,1113934,2,0,B|188:212|208:164,1,100 +304,164,1114363,2,0,B|324:212|304:260,1,100,8|0 +128,192,1114791,2,0,B|132:124|160:60|256:28|352:60|380:124|384:192,1,400,0|0 +332,328,1116077,1,8 +180,56,1116506,1,0 +372,12,1116934,6,0,B|440:104|372:196,1,200,8|0 +140,372,1117791,2,0,B|72:280|140:188,1,200,8|0 +184,96,1118434,1,0 +300,108,1118649,6,0,B|300:192|212:192|212:280,2,200,4|0|8 +256,20,1119720,1,0 +212,108,1119934,2,0,B|212:192|300:192|300:280,1,200,0|8 +256,364,1120577,1,0 +163,360,1120791,6,0,B|118:376|53:357|33:292|49:246,1,200,0|8 +348,360,1121649,2,0,B|394:376|459:357|479:292|462:246,1,200,0|4 +452,148,1122291,5,0 +356,124,1122506,2,0,B|311:153|308:207,1,100 +352,280,1122934,2,0,B|304:304|256:280|256:280|208:304|160:280,1,200,8|0 +204,207,1123577,2,0,B|200:153|156:124,1,100,0|8 +60,148,1124006,1,8 +60,148,1124220,6,0,B|72:48,1,100 +160,100,1124649,2,0,B|208:124|256:100|256:100|304:76|352:100,1,200,8|0 +440,48,1125291,1,0 +440,48,1125506,6,0,B|452:148,1,100,8|0 +376,268,1125934,2,0,B|364:168,1,100,8|0 +456,304,1126363,6,0,B|436:348|376:380|316:348|296:304,1,200,8|0 +256,192,1127006,1,0 +147,168,1127220,2,0,B|136:268,1,100,8|8 +55,303,1127649,2,0,B|76:348|136:380|196:348|216:303,2,200,8|4|0 +64,220,1128720,1,0 +111,151,1128934,6,0,B|88:80,1,74.9999977648259 +208,32,1129363,2,0,B|224:68|208:104,1,74.9999977648259 +304,156,1129791,2,0,B|288:120|304:84,1,74.9999977648259 +424,80,1130220,2,0,B|400:151,1,74.9999977648259 +482,201,1130649,5,0 +458,275,1130863,1,0 +397,322,1131077,1,0 +320,325,1131291,1,0 +256,284,1131506,1,0 +192,325,1131720,1,0 +115,322,1131934,1,0 +54,275,1132149,1,0 +30,201,1132363,1,0 +256,192,1132470,12,2,1135791 +80,56,1136220,5,0 +212,252,1136649,2,0,B|196:224|207:190|227:178|252:166|276:177|296:187|317:217|293:262,1,200,2|0 +432,56,1137505,1,2 +369,247,1137949,2,0,B|379:196|354:120|287:88|223:88|161:122|127:184|140:247,1,400,0|2 +68,288,1139060,1,0 +48,200,1139282,5,2 +256,224,1139743,1,0 +464,200,1140205,1,2 +346,34,1140776,2,0,B|307:69|273:52|256:35|256:35|238:52|204:69|164:31,1,200,0|2 +256,320,1153040,5,8 +256,252,1153343,1,8 +256,240,1153494,1,8 +256,228,1153646,1,8 +256,216,1153797,1,0 +256,204,1153949,1,4 +380,136,1154252,6,0,B|412:196,1,66.6666666666667,0|8 +456,124,1154555,2,0,B|423:62,1,66.6666666666667,0|0 +324,64,1154858,2,2,B|256:92|188:64,1,133.333333333333,2|0 +88,62,1155312,2,0,B|56:124,1,66.6666666666667,8|0 +99,197,1155616,2,0,B|132:136,1,66.6666666666667,0|0 +256,228,1156070,6,0,B|256:296,2,66.6666666666667,0|8|0 +256,140,1156525,1,0 +196,80,1156676,2,2,B|256:53|256:53|316:80,1,133.333333333333,2|0 +388,140,1157131,2,0,B|456:156,2,66.6666666666667,8|0|0 +316,201,1157585,6,0,B|255:227|255:227|195:201,1,133.333333333333,4|0 +124,140,1158040,2,0,B|56:156,1,66.6666666666667,8|0 +44,336,1158494,2,2,B|184:304,1,133.333333333333,2|0 +256,256,1158949,2,0,B|256:324,1,66.6666666666667,8|0 +468,336,1159403,6,0,B|328:304,1,133.333333333333,4|0 +324,212,1159858,2,0,B|360:152,1,66.6666666666667,8|0 +256,140,1160161,1,0 +188,212,1160312,2,0,B|152:152,1,66.6666666666667,2|0 +204,52,1160616,5,8 +228,40,1160767,1,8 +256,36,1160919,1,8 +288,40,1161070,1,0 +316,52,1161222,1,4 +468,156,1161525,6,0,B|468:224,1,66.6666666666667,0|8 +468,316,1161828,1,0 +380,344,1161979,1,0 +288,352,1162131,2,0,B|288:284|288:284|336:228,1,133.333333333333,2|0 +248,192,1162585,6,0,B|200:248,1,66.6666666666667,8|0 +140,312,1162888,1,0 +80,244,1163040,2,0,B|176:140,1,133.333333333333,0|0 +256,100,1163494,2,0,B|256:30,2,66.6666666666667,8|0|0 +337,141,1163949,2,0,B|432:244,1,133.333333333333,2|0 +339,268,1164403,2,0,B|305:222|256:203|206:222|172:268,1,200,8|4 +172,93,1165161,5,0 +171,92,1165312,2,0,B|205:137|256:156|305:137|339:92,1,200,8|2 +358,254,1166070,2,0,B|332:320,1,66.6666666666667,0|8 +256,360,1166373,1,0 +179,319,1166525,2,0,B|154:254,1,66.6666666666667,0|4 +316,112,1166979,6,0,B|304:44,2,66.6666666666667,8|8|8 +256,180,1167434,1,0 +196,112,1167585,2,8,B|208:44,2,66.6666666666667,8|8|0 +152,224,1168040,6,0,B|216:252,1,66.6666666666667,8|8 +360,224,1168343,2,0,B|296:252,1,66.6666666666667,0|4 +256,32,1168949,5,8 +420,112,1169252,1,0 +494,80,1169403,2,2,B|462:12|375:0|319:68|343:144|343:144|356:188|356:188|379:259|323:287,1,400,2|0 +144,268,1170616,2,0,B|136:232|144:200,1,66.6666666666667,0|8 +172,120,1170919,1,0 +224,56,1171070,2,0,B|256:32|288:56,1,66.6666666666667,0|2 +340,120,1171373,1,0 +367,199,1171525,2,0,B|376:232|368:268,1,66.6666666666667,0|8 +187,287,1172131,6,0,B|132:260|154:188|154:188|168:144|168:144|192:68|136:0|48:12|16:80,1,400,4|2 +24,164,1173191,1,0 +92,112,1173343,1,0 +164,68,1173494,2,0,B|196:48|232:60|259:88|259:88|283:112,1,133.333333333333,8|0 +328,184,1173949,1,2 +256,224,1174100,1,0 +184,184,1174252,1,0 +227,112,1174403,2,0,B|252:88|252:88|280:60|316:48|348:68,1,133.333333333333,8|0 +452,192,1174858,5,4 +256,356,1175161,1,2 +60,192,1175464,1,2 +256,28,1175767,1,4 +256,196,1176070,6,0,B|292:136,1,66.6666666666667,0|8 +256,196,1176373,2,0,B|220:136,1,66.6666666666667 +174,68,1176676,2,0,B|99:68|51:153|99:239|174:239,1,266.666666666667,4|2 +338,67,1177585,2,0,B|412:67|461:152|412:239|338:239,1,266.666666666667,4|2 +192,152,1178494,2,0,B|228:184|256:152|284:120|320:152,2,133.333333333333,4|2|2 +80,281,1179403,6,0,B|136:352|256:396|376:352|441:272,1,400,4|2 +448,188,1180464,1,0 +368,164,1180616,1,0 +356,248,1180767,2,0,B|332:272|292:276,1,66.6666666666667,8|0 +156,248,1181070,6,0,B|180:272|218:275,1,66.6666666666667,8|4 +212,192,1181373,2,0,B|188:168|150:165,1,66.6666666666667,0|0 +68,148,1181676,1,8 +84,64,1181828,1,8 +168,80,1181979,1,8 +256,106,1182131,6,0,B|256:36,1,66.6666666666667 +344,80,1182434,1,0 +428,64,1182585,1,8 +444,148,1182737,1,8 +362,164,1182888,2,0,B|324:168|300:192,1,66.6666666666667,0|4 +68,292,1183494,5,8 +40,212,1183646,2,0,B|84:192|140:220|148:268,2,133.333333333333,0|2|0 +32,128,1184403,1,8 +116,136,1184555,2,0,B|188:152|216:228,1,133.333333333333,0|4 +395,136,1185161,6,0,B|324:152|296:225,1,133.333333333333,0|0 +408,352,1185767,2,0,B|479:336|507:262,1,133.333333333333,2|0 +396,136,1186373,1,0 +104,352,1186676,6,0,B|32:336|4:262,1,133.333333333333,4|0 +80,220,1187131,1,8 +164,204,1187282,2,0,B|148:136,1,66.6666666666667 +192,69,1187585,2,0,B|256:87|256:87|319:69,1,133.333333333333,2|0 +364,135,1188040,2,0,B|348:204,1,66.6666666666667,8|0 +256,344,1188494,5,4 +140,120,1188797,1,2 +332,284,1189100,1,2 +256,44,1189403,1,4 +180,284,1189706,1,2 +372,120,1190009,1,2 +256,344,1190312,1,4 +488,244,1190767,6,0,B|436:40,1,200,8|2 +300,140,1191525,2,0,B|364:164,1,66.6666666666667,0|8 +212,140,1191979,2,0,B|148:164,1,66.6666666666667,8|2 +75,40,1192585,2,0,B|24:244,1,200,8|2 +204,268,1193343,5,0 +228,280,1193494,1,8 +256,284,1193646,1,8 +288,280,1193797,1,0 +316,268,1193949,1,4 +80,203,1194403,6,0,B|141:127|256:85|370:127|431:203,1,400,8|10 +212,312,1195767,2,0,B|180:252,1,66.6666666666667 +300,312,1196222,2,0,B|332:248,1,66.6666666666667,8|0 +212,144,1196676,5,8 +208,108,1196828,1,0 +224,76,1196979,1,0 +256,64,1197131,1,8 +288,76,1197282,1,0 +304,108,1197434,1,0 +300,144,1197585,1,12 +392,264,1197888,5,2 +392,264,1198040,1,2 +256,344,1198343,1,2 +256,344,1198494,1,0 +120,264,1198797,1,2 +176,104,1199100,1,2 +332,104,1199403,1,0 +256,216,1199706,5,0 +256,200,1199858,1,8 +256,184,1200009,1,0 +256,168,1200161,1,0 +256,152,1200312,1,2 +304,284,1200616,1,0 +284,304,1200767,1,8 +256,312,1200919,1,0 +228,304,1201070,1,0 +208,284,1201222,1,2 +84,192,1201525,6,0,B|52:280|52:280,1,66.6666666666667,0|8 +32,108,1201979,2,0,B|64:32|64:32,1,66.6666666666667,0|2 +201,101,1202434,2,0,B|219:71|252:59|293:64|315:108,1,133.333333333333 +313,250,1203040,2,0,B|296:278|260:291|216:285|192:238,1,133.333333333333,2|0 +155,317,1203494,6,0,B|182:349|233:356,1,66.6666666666667,8|0 +298,353,1203797,2,0,B|332:345|357:317,1,66.6666666666667,0|2 +256,188,1204252,1,0 +256,168,1204403,1,8 +256,148,1204555,1,0 +256,128,1204706,1,0 +256,104,1204858,1,4 +428,192,1205161,6,0,B|460:280|460:280,1,66.6666666666667,0|8 +480,108,1205616,2,0,B|448:32|448:32,1,66.6666666666667,0|2 +311,57,1206070,2,0,B|292:30|265:30|254:19|254:19|246:30|220:30|203:55,1,133.333333333333 +121,176,1206676,2,0,B|130:226|215:249|256:199|256:199|288:247|388:235|392:162,1,333.333333333333,2|0 +464,232,1207585,1,2 +337,345,1207888,5,0 +294,354,1208040,1,8 +256,336,1208191,1,0 +217,354,1208343,1,0 +178,345,1208494,1,2 +48,232,1208797,1,0 +48,232,1208949,1,8 +224,165,1209252,6,0,B|256:176|256:176|290:164,1,66.6666666666667,0|2 +197,56,1209706,2,0,B|255:28|255:28|317:55,2,133.333333333333,0|0|2 +116,184,1210616,1,0 +147,197,1210767,1,8 +169,224,1210919,1,0 +181,256,1211070,1,0 +180,292,1211222,1,2 +332,292,1211525,1,8 +331,256,1211676,1,8 +343,224,1211828,1,8 +365,197,1211979,1,0 +396,184,1212131,1,4 +336,44,1212434,6,0,B|336:77|336:77|314:106,1,66.6666666666667,0|8 +176,44,1212888,2,0,B|175:77|175:77|197:106,1,66.6666666666667,0|2 +200,244,1213343,2,0,B|217:272|253:285|297:279|321:232,1,133.333333333333 +99,267,1213949,2,0,B|131:363|263:412|385:368|423:243,1,400,2|2 +180,145,1215312,6,0,B|195:97|255:86|255:86|311:94|333:148,1,200,8|4 +124,44,1216222,5,8 +256,168,1216525,1,0 +256,168,1216676,1,2 +388,44,1216979,1,0 +435,226,1217282,1,2 +353,350,1217585,2,0,B|284:340|117:251|74:123|147:-32|400:-9|466:163|336:289|227:339|160:351,1,800,2|4 +77,224,1219706,1,0 +77,224,1219858,5,8 +120,48,1220161,1,2 +120,48,1220312,1,2 +256,120,1220616,1,0 +392,48,1220919,1,0 +400,200,1221222,2,0,B|382:282|318:334|255:334|166:330|116:273|107:181|107:181,2,400,2|2|4 +256,40,1223494,5,8 +256,184,1223797,1,2 +256,184,1223949,1,2 +319,332,1224252,2,0,B|300:360|264:373|220:367|196:320,1,133.333333333333,0|0 +96,200,1224858,1,2 +184,72,1225161,5,0 +256,40,1225312,1,8 +328,72,1225464,1,0 +416,200,1225767,1,0 +296,356,1226070,1,8 +320,280,1226222,1,8 +256,232,1226373,1,0 +192,280,1226525,1,0 +216,356,1226676,1,4 +336,111,1227131,6,0,B|352:47|288:15|256:15|223:15|159:47|175:111,2,274.000001469851,2|2|8 +256,304,1228343,1,0 +193,277,1228494,2,0,B|181:298|174:343|209:397|268:399|316:376|345:340|324:276|318:268,1,274.000001469851,2|8 +496,128,1229403,1,0 +256,32,1229706,5,0 +216,156,1229858,1,8 +320,80,1230009,1,8 +192,80,1230161,1,8 +296,156,1230312,1,4 +16,128,1230767,5,2 +175,288,1231222,2,0,B|159:352|223:384|256:384|288:384|352:352|336:288,1,274.000001469851,2|8 +438,116,1231979,1,0 +361,51,1232131,2,0,B|334:77|294:90|255:51|255:24|255:24|255:51|215:90|175:77|149:51,1,274.000001469851,2|8 +64,336,1233040,1,2 +256,224,1233343,5,0 +296,356,1233494,1,8 +192,276,1233646,1,8 +320,276,1233797,1,0 +216,356,1233949,1,4 +362,100,1234403,6,0,B|338:31|253:0|175:28|151:108,1,274.000001469851,2|2 +448,336,1235312,1,8 +256,212,1235616,1,0 +203,281,1235767,2,0,B|184:332|184:332|220:332|256:368|256:368|292:332|328:332|328:332|308:279,1,274.000001469851,2|8 +64,337,1236676,1,2 +180,88,1237131,6,0,B|203:42|256:31|310:43|333:90,1,182.666667646567,8|0 +362,187,1237585,2,0,B|324:295|164:293|148:177|148:177,3,274.000001469851,4|8|2|8 +40,360,1239252,1,8 +127,312,1239403,2,0,B|166:350|217:355,1,91.3333338232835,2|0 +305,353,1239706,2,0,B|346:350|385:312,1,91.3333338232835,0|8 +472,360,1240009,1,2 +474,357,1240085,1,0 +476,354,1240161,5,8 +488,248,1240312,1,0 +408,208,1240464,1,0 +320,144,1240616,1,0 +256,64,1240767,1,8 +192,144,1240919,1,0 +112,208,1241070,1,0 +24,248,1241222,1,4 +328,340,1241676,6,0,B|320:312|296:280|256:268|216:280|192:312|184:340,1,196.000001869202,8|0 +120,252,1242131,2,0,B|180:208|256:180|332:208|392:252,1,294.000002803803,2|8 +472,96,1242888,1,0 +392,36,1243040,6,0,B|332:80|256:108|180:80|120:36,1,294.000002803803,4|8 +16,68,1243646,2,0,B|8:168,1,98.0000009346008 +112,140,1243949,2,0,B|196:196|204:308,1,196.000001869202,2|0 +308,305,1244403,2,0,B|316:196|400:140,1,196.000001869202,8|2 +368,36,1244858,6,0,B|304:60|256:128|256:128|208:60|144:36,2,294.000002803803,4|8|2 +503,165,1246070,2,0,B|496:68,1,98.0000009346008,0|8 +424,148,1246373,2,0,B|431:245,1,98.0000009346008,0|0 +340,300,1246676,6,0,B|340:224|292:192|256:192|220:192|172:160|172:84,1,294.000002803803,4|8 +339,85,1247585,2,0,B|340:160|292:192|256:192|220:192|172:224|171:299,1,294.000002803803,2|8 +80,356,1248191,2,0,B|68:304|76:260,1,98.0000009346008,8|0 +136,168,1248494,6,0,B|212:168|228:256|304:256,1,196.000001869202,4|0 +412,248,1248949,2,0,B|480:252|496:180|496:180|512:104,1,196.000001869202,8|0 +424,40,1249403,2,0,B|356:36|340:108|340:108|324:184,1,196.000001869202,2|0 +324,184,1249858,1,8 +100,248,1250161,5,0 +100,248,1250312,2,0,B|32:252|16:180|16:180|0:104,1,196.000001869202,4|0 +88,40,1250767,2,0,B|156:36|172:108|172:108|188:184,1,196.000001869202,8|0 +292,212,1251222,2,0,B|384:220|384:220|396:164|372:120,1,196.000001869202,2|0 +324,60,1251676,1,8 +256,28,1251828,1,0 +188,60,1251979,1,0 +140,120,1252131,6,0,B|116:164|128:220|128:220|220:212,1,196.000001869202,4|0 +328,204,1252585,2,0,B|384:200|384:200|440:196|472:248|452:308|392:312|392:312|340:316,2,294.000002803803,8|2|8 +328,204,1253646,1,0 +174,316,1253949,2,0,B|120:312|120:312|60:308|40:248|72:196|128:200|128:200|184:204,1,294.000002803803,4|8 +192,96,1254555,2,0,B|96:88,2,98.0000009346008,0|8|4 +418,87,1255161,6,0,B|320:96,1,98.0000009346008,0|8 +280,184,1255464,2,0,B|378:175,1,98.0000009346008,8|0 +376,272,1255767,6,0,B|280:280|280:280|224:284|204:336,1,196.000001869202,4|0 +308,336,1256222,2,0,B|288:284|232:280|232:280|136:272,1,196.000001869202,8|0 +40,264,1256676,2,0,B|52:180|140:176|140:176|228:172|240:88,1,294.000002803803,2|8 +144,80,1257282,1,0 +136,272,1257585,5,4 +256,116,1257888,1,0 +256,116,1258040,1,8 +376,272,1258343,1,0 +376,272,1258494,2,0,B|424:252|460:200|440:136|416:108,1,196.000001869202,2|0 +332,60,1258949,2,0,B|280:60|256:8|256:8|232:60|180:60,1,196.000001869202,8|0 +96,108,1259403,2,0,B|71:136|51:200|87:252|135:272,1,196.000001869202,4|0 +180,360,1259858,2,0,B|232:360|256:308|256:308|280:360|332:360,1,196.000001869202,8|0 +424,320,1260312,2,0,B|368:276|336:192|368:108|424:64,2,294.000002803803,2|8|4 +256,176,1261676,5,8 +87,320,1262131,2,0,B|144:276|176:192|144:108|88:64,1,294.000002803803,2|8 +32,144,1262737,1,2 +32,240,1262888,1,0 +132,236,1263040,6,0,B|328:228,1,196.000001869202,4|0 +380,148,1263494,2,0,B|184:156,1,196.000001869202,8|0 +184,156,1263949,1,2 +332,60,1264252,2,0,B|429:55,1,98.0000009346008,0|8 +436,232,1264706,2,0,B|338:236,1,98.0000009346008,0|4 +82,329,1265161,6,0,B|180:324,1,98.0000009346008,0|8 +173,148,1265616,2,0,B|76:152,1,98.0000009346008,0|2 +132,236,1266070,1,0 +132,236,1266222,2,0,B|328:228,1,196.000001869202,8|0 +432,192,1266676,5,4 +256,128,1266979,1,0 +256,128,1267131,1,8 +80,192,1267434,1,0 +80,192,1267585,1,2 +352,240,1267888,1,0 +352,240,1268040,1,8 +160,240,1268343,1,0 +160,240,1268494,1,2 +316,372,1268949,5,8 +256,380,1269100,1,8 +196,372,1269252,1,2 +168,288,1269403,5,8 +256,276,1269555,1,0 +344,284,1269706,1,2 +372,200,1269858,5,8 +256,176,1270009,1,0 +140,200,1270161,1,2 +256,100,1270312,5,4 +384,280,1270767,6,0,B|436:280|488:252|512:192|488:132|436:104|384:104,1,294.000002803803,8|2 +300,126,1271373,2,0,B|300:224,1,98.0000009346008,0|2 +256,296,1271676,1,8 +212,224,1271828,2,0,B|212:126,1,98.0000009346008,0|2 +128,103,1272131,2,0,B|76:104|24:132|0:192|24:252|76:280|128:280,3,294.000002803803,4|2|0|2 +340,76,1273949,6,0,B|384:108|412:184|384:260|332:308|256:328|180:308|128:260|100:184|128:108|172:76,2,588.000005607605,4|2|4 +48,40,1276222,1,8 +36,136,1276373,1,0 +72,228,1276525,1,0 +140,296,1276676,2,0,B|220:304|256:224|256:224|292:304|372:296,1,294.000002803803,2|8 +440,228,1277282,1,0 +476,136,1277434,1,0 +464,40,1277585,1,4 +256,248,1278040,5,8 +256,64,1278494,1,2 +160,164,1278646,5,0 +160,260,1278797,1,0 +256,288,1278949,1,8 +352,260,1279100,1,0 +352,164,1279252,1,2 +256,136,1279403,1,4 +456,56,1279858,6,0,B|456:350,1,294.000002803803,8|2 +56,350,1280767,2,0,B|56:56,1,294.000002803803,8|4 +360,88,1281676,2,0,B|360:212|360:212|360:284|328:324|256:352|184:324|152:284|152:212|152:212|152:88,1,588.000005607605,8|8 +20,284,1283040,5,4 +8,208,1283191,1,0 +16,128,1283343,1,2 +80,80,1283494,1,8 +160,80,1283646,1,0 +228,120,1283797,1,2 +256,192,1283949,1,8 +284,264,1284100,1,0 +352,304,1284252,1,0 +432,304,1284403,1,8 +496,256,1284555,1,8 +504,176,1284706,1,0 +492,100,1284858,1,4 +208,192,1291222,5,0 +148,112,1291373,1,0 +148,112,1291449,1,0 +148,112,1291525,1,0 +52,132,1291676,1,0 +32,228,1291828,1,0 +100,292,1291979,1,0 +184,281,1292131,6,0,B|205:317|263:349|322:311|334:277,1,178.666658215332,4|2 +309,192,1292585,2,0,B|301:140|257:124|257:124|205:104|209:48,1,183.333327869574,8|0 +305,56,1293040,2,0,B|309:104|257:124|257:124|213:140|201:196,2,183.333327869574,2|0|0 +392,40,1293797,1,0 +464,96,1293949,1,2 +392,264,1294252,5,0 +336,328,1294403,1,8 +256,352,1294555,1,0 +176,328,1294706,1,0 +120,264,1294858,1,2 +120,120,1295161,1,0 +176,56,1295312,1,8 +256,32,1295464,1,0 +336,56,1295616,1,0 +392,120,1295767,1,2 +256,200,1296070,5,0 +256,200,1296222,1,8 +256,24,1296525,5,0 +256,24,1296676,1,2 +201,169,1296979,1,0 +331,76,1297282,1,2 +175,76,1297585,1,0 +297,171,1297888,1,0 +300,259,1298040,6,0,B|255:244|255:244|212:259,1,91.6666639347872,8|0 +212,349,1298343,2,0,B|256:363|256:363|299:349,1,91.6666639347872 +392,184,1298797,1,8 +346,101,1298949,1,8 +256,67,1299100,1,0 +165,101,1299252,1,8 +119,184,1299403,1,4 +256,276,1299706,5,0 +182,318,1299858,2,0,B|202:359|257:382|312:360|331:316,1,183.333327869574,2|0 +332,233,1300312,2,0,B|310:192|258:168|200:192|183:234,2,183.333327869574,2|0|0 +422,128,1301222,2,0,B|352:92|336:12|336:12|256:108|256:108|180:8|180:8|156:92|94:128,2,549.999983608723,2|2|4 +188,308,1303494,6,0,B|201:351|256:361|256:361|307:354|327:304,1,183.333327869574,8|0 +256,256,1303949,1,2 +360,80,1304252,1,0 +152,80,1304555,1,2 +68,233,1304858,2,0,B|52:304|140:352|191:289|191:289|209:334|255:347|302:334|320:291|320:291|370:351|463:307|443:225,2,549.999983608723,0|2|4 +256,68,1306979,5,0 +256,68,1307131,1,8 +256,252,1307434,1,0 +182,199,1307585,2,0,B|203:199|203:199|228:176|228:176|256:152|256:152|284:176|284:176|308:199|308:199|328:199,2,183.333327869574,2|0|0 +46,322,1308494,2,0,B|68:284|182:252|208:368|160:384|164:318|240:304|256:251|256:251|272:304|352:320|352:384|304:368|333:281|364:284|455:262|475:337|475:337,2,549.999983608723,2|2|4 +256,384,1310767,5,8 +332,219,1311070,1,0 +332,219,1311222,1,2 +179,217,1311525,1,0 +230,40,1311828,2,0,B|216:49|207:74|216:97|232:112|256:118|280:112|296:97|304:74|296:49|282:40,1,183.333327869574,2|0 +448,74,1312434,6,0,B|489:156,1,91.6666639347872,2|8 +444,238,1312737,2,0,B|416:326,1,91.6666639347872,0|8 +324,366,1313040,2,0,B|310:324|256:313|256:313|206:321|186:370,1,183.333327869574,8|0 +95,325,1313494,2,0,B|68:238,1,91.6666639347872,8|0 +23,156,1313797,2,0,B|64:74,1,91.6666639347872,8|4 +336,47,1314403,6,0,B|296:135|336:224,1,183.333327869574,8|0 +376,304,1314858,2,0,B|296:308|256:361|256:361|211:308|135:304,1,274.999991804362,2|8 +43,288,1315464,2,0,B|51:240|83:208,1,91.6666639347872,2|8 +174,224,1315767,6,0,B|215:135|175:47,1,183.333327869574,4|0 +83,51,1316222,2,0,B|139:107|127:200|43:240|-24:204,1,274.999991804362,8|2 +68,360,1316979,6,0,B|144:308,1,91.6666639347872,0|8 +256,372,1317282,2,0,B|256:280,1,91.6666639347872,8|0 +256,188,1317585,1,4 +444,360,1317888,6,0,B|368:308,1,91.6666639347872,0|8 +209,116,1318343,2,0,B|254:100|297:116,1,91.6666639347872 +344,43,1318646,2,0,B|256:2|167:43,1,183.333327869574,0|8 +44,176,1319252,1,0 +116,120,1319403,2,0,B|180:176|168:288,1,183.333327869574,2|0 +256,316,1319858,2,0,B|256:135,1,183.333327869574,8|0 +396,120,1320312,2,0,B|332:176|344:288,1,183.333327869574,2|0 +428,324,1320767,6,0,B|420:280|424:232,1,91.6666639347872,8|8 +464,84,1321070,2,0,B|472:128|468:176,1,91.6666639347872,0|4 +320,336,1321525,6,0,B|324:292|308:244,1,91.6666639347872,0|8 +256,172,1321828,1,0 +210,102,1321979,2,0,B|255:86|298:102,1,91.6666639347872 +191,339,1322434,6,0,B|187:295|202:249,1,91.6666639347872,0|8 +256,172,1322737,1,0 +312,100,1322888,1,0 +348,14,1323040,2,0,B|401:10|401:10|457:6|485:38|489:83,1,183.333327869574,2|0 +404,123,1323494,2,0,B|320:171|304:268,1,183.333327869574,8|0 +207,268,1323949,2,0,B|191:171|107:123,1,183.333327869574,2|0 +107,123,1324403,1,8 +192,92,1324555,5,0 +256,28,1324706,1,0 +320,92,1324858,1,4 +256,192,1325009,12,4,1326676 +60,192,1327131,5,0 +60,192,1327282,1,0 +160,136,1327434,1,0 +160,124,1327509,1,0 +160,112,1327585,5,2 +256,28,1327737,1,0 +352,112,1327888,1,0 +452,192,1328040,1,8 +352,272,1328191,1,0 +256,356,1328343,1,0 +160,272,1328494,1,4 +40,166,1328797,6,0,B|40:120|64:80,1,91.3333338232835,0|8 +255,146,1329252,2,0,B|268:176|244:208|256:236,1,91.3333338232835,0|2 +471,166,1329706,2,0,B|472:120|448:80,1,91.3333338232835,0|8 +364,312,1330161,5,0 +364,312,1330312,2,0,B|336:380|256:408|175:380|147:312,1,274.000001469851,4|8 +192,232,1330919,1,0 +256,168,1331070,1,0 +320,232,1331222,2,0,B|364:232|408:208|424:163|416:107,1,182.666667646567,2|0 +340,64,1331676,2,0,B|296:96|256:64|256:64|216:32|172:64,1,182.666667646567,8|2 +96,107,1332131,6,0,B|88:164|104:208|148:232|192:232,1,182.666667646567,4|0 +284,232,1332585,2,0,B|332:92|470:44,1,274.000001469851,8|2 +227,151,1333494,2,0,B|178:290|40:338,1,274.000001469851,8|4 +40,156,1334252,6,0,B|84:168|84:168|132:168,1,91.3333338232835,0|8 +179,91,1334555,2,0,B|203:55|256:27|308:55|332:91,1,182.666667646567,0|2 +360,180,1335009,1,0 +336,268,1335161,1,0 +256,308,1335312,1,8 +176,268,1335464,1,8 +152,180,1335616,1,0 +148,88,1335767,6,0,B|208:56|208:56|256:84|316:88,1,182.666667646567,4|0 +408,84,1336222,2,0,B|376:144|376:144|404:192|408:252,1,182.666667646567,8|0 +364,344,1336676,2,0,B|304:312|304:312|256:340|196:344,1,182.666667646567,2|0 +104,300,1337131,2,0,B|136:240|136:240|108:192|104:132,1,182.666667646567,8|0 +27,183,1337585,6,0,B|75:244|175:236|215:147|179:83,1,274.000001469851,4|8 +256,32,1338191,2,0,B|256:216,1,182.666667646567,0|2 +332,84,1338797,1,0 +332,83,1338949,2,0,B|296:148|336:236|436:244|484:183,1,274.000001469851,8|4 +256,192,1339555,12,6,1342131 +112,112,1342282,5,0 +168,40,1342434,1,0 +256,12,1342585,1,8 +344,40,1342737,1,0 +400,112,1342888,1,0 +332,170,1343040,6,0,B|312:129|257:106|202:128|183:172,1,182.666667646567,6|0 +181,260,1343494,2,0,B|201:301|256:324|311:302|330:258,1,182.666667646567,10|0 +256,216,1343949,1,0 +208,384,1344252,2,0,B|304:384|304:384,1,91.3333338232835,0|8 +256,216,1344706,1,0 +124,38,1345161,2,0,B|138:78|197:93|226:63|226:19|190:5|160:49|208:93|256:93|256:93|303:93|351:49|321:5|285:19|285:63|318:93|387:71|395:34,1,421.333338858287,2|0 +440,216,1346070,5,2 +388,304,1346222,1,10 +256,140,1346525,1,0 +204,212,1346676,2,0,B|212:264|256:280|256:280|308:300|304:356,1,182.666667646567 +208,348,1347131,2,0,B|204:300|256:280|256:280|300:264|312:208,1,182.666667646567,8|0 +256,140,1347585,1,0 +72,216,1347888,1,2 +124,304,1348040,1,10 +160,156,1348343,6,0,B|213:103|186:3|123:-2|81:55|123:108|202:81|257:14|257:14|310:81|390:108|432:55|390:-2|326:3|299:103|352:156,1,639.333336762985,0|2 +344,236,1349555,6,0,B|388:316,1,91.3333338232835,0|0 +256,364,1349858,2,0,B|256:252,1,91.3333338232835,8|0 +124,315,1350161,2,0,B|168:236,1,91.3333338232835,0|4 +44,44,1350767,1,8 +335,282,1351222,2,0,B|376:257|405:184|376:114|328:68|258:49|185:68|135:114|111:184|135:257|178:284,1,548.000002939701,2|2 +468,44,1352585,1,8 +357,277,1353040,6,0,B|339:348|254:387|171:346|154:277,1,274.000001469851,4|8 +22,192,1353797,2,0,B|5:-6|238:-24|305:41|372:108|388:158|354:192|256:441|156:192|122:158|138:108|205:41|272:-24|505:-6|483:194,1,1096.0000058794,0|0 +377,321,1355767,2,0,B|319:381|256:336|256:336|187:384|131:316,1,274.000001469851,2|2 +200,268,1356373,1,0 +140,212,1356525,1,0 +132,132,1356676,1,4 +180,64,1356828,1,0 +256,40,1356979,1,0 +332,64,1357131,1,6 +380,132,1357282,1,0 +372,212,1357434,1,0 +312,268,1357585,1,4 +52,80,1358494,6,0,B|52:37|113:29|132:69|132:69|150:110|197:110|214:69|214:69|229:29|279:29|296:69|296:69|312:110|360:110|376:69|376:69|397:29|458:37|458:81,2,548.000002939701,8|0|8 +256,192,1360767,1,0 +52,302,1361222,2,0,B|52:345|113:353|132:313|132:313|150:272|197:272|214:313|214:313|229:353|279:353|296:313|296:313|312:272|360:272|376:313|376:313|397:353|458:345|458:301,2,548.000002939701,0|8|0 +256,44,1363494,5,0 +244,104,1363949,1,8 +264,152,1364403,1,0 +244,208,1364858,1,4 +256,356,1365767,1,8 +256,356,1366373,5,0 +256,356,1366525,1,0 +256,356,1366676,1,0 +308,196,1367131,1,0 +176,288,1367585,1,8 +336,288,1368040,1,0 +204,196,1368494,1,0 +361,51,1369403,6,0,B|334:77|294:90|255:51|255:24|255:24|255:51|215:90|175:77|149:51,1,274.000001469851,8|0 +140,60,1370009,1,0 +127,69,1370161,1,0 +117,79,1370312,1,0 +256,192,1370767,1,0 +344,296,1371222,1,8 +320,324,1371373,1,0 +292,340,1371525,1,0 +260,344,1371676,1,8 +220,340,1371828,1,0 +192,324,1371979,1,0 +168,296,1372131,1,4 +256,192,1383040,12,0,1386676 +256,48,1387131,5,4 +256,216,1387585,1,0 +256,216,1388040,1,8 +176,236,1388191,1,0 +96,256,1388343,1,0 +40,200,1388494,6,0,B|20:156|40:112,1,91.3333338232835 +164,88,1388797,2,0,B|144:128|144:128|108:156,1,91.3333338232835,0|4 +210,327,1389403,6,0,B|256:344|300:328,1,91.3333338232835,0|0 +208,192,1389706,2,0,B|254:175|298:191,1,91.3333338232835,0|4 +472,200,1390161,5,0 +472,200,1390312,2,0,B|492:156|472:112,1,91.3333338232835 +348,88,1390616,2,0,B|368:128|368:128|404:156,1,91.3333338232835,0|4 +256,216,1391222,5,0 +256,216,1391676,1,8 +336,236,1391828,1,0 +416,256,1391979,1,0 +384,332,1392131,6,0,B|296:308,1,91.3333338232835 +128,332,1392434,2,0,B|216:308,1,91.3333338232835,0|4 +384,128,1393040,6,0,B|296:152,1,91.3333338232835 +128,128,1393343,2,0,B|216:152,1,91.3333338232835,0|4 +408,232,1393797,5,0 +408,232,1393949,2,0,B|316:232,1,91.3333338232835 +104,232,1394252,2,0,B|195:232,1,91.3333338232835,0|4 +195,232,1394858,1,0 +302,40,1395312,6,0,B|210:40,1,91.3333338232835,8|0 +296,116,1395616,2,0,B|380:152,1,91.3333338232835 +300,220,1395919,2,0,B|256:228|256:228|212:220,1,91.3333338232835 +216,116,1396222,2,0,B|48:188,1,182.666667646567,4|0 +80,312,1396676,5,8 +164,260,1396828,1,0 +256,300,1396979,1,0 +256,300,1397131,1,4 +395,232,1397282,6,0,B|353:313,1,91.3333338232835,0|0 +256,300,1397585,1,8 +159,313,1397737,2,0,B|117:232,1,91.3333338232835,0|0 +256,116,1398040,1,4 +256,116,1398494,1,0 +356,336,1398949,6,0,B|356:244,1,91.3333338232835,8|0 +256,348,1399252,2,0,B|256:256,1,91.3333338232835 +156,336,1399555,2,0,B|156:244,1,91.3333338232835 +256,116,1399858,1,4 +280,32,1400009,2,0,B|208:32,2,45.6666669116418 +356,84,1400312,1,8 +376,180,1400464,1,0 +348,276,1400616,1,0 +256,316,1400767,1,8 +164,276,1400919,1,0 +136,180,1401070,1,0 +152,88,1401222,6,0,B|112:24,2,45.6666669116418 +208,152,1401525,1,0 +256,72,1401676,2,0,B|256:24,2,45.6666669116418,8|0|0 +304,148,1401979,1,0 +360,88,1402131,2,0,B|400:24,2,45.6666669116418 +304,148,1402434,1,0 +256,216,1402585,2,0,B|256:308,1,91.3333338232835,8|0 +256,384,1402888,1,0 +176,352,1403040,5,2 +156,316,1403116,1,0 +136,280,1403191,1,0 +176,208,1403343,1,0 +184,128,1403494,2,0,B|204:84|256:60|308:84|328:128,1,182.666667646567,8|0 +336,208,1403949,1,8 +376,280,1404100,1,2 +356,316,1404176,1,0 +336,352,1404252,1,0 +256,384,1404403,5,8 +196,268,1404555,1,2 +316,268,1404706,1,0 +416,368,1404858,6,0,B|476:344|492:280|492:280|508:216,1,182.666667646567,4|0 +500,136,1405312,2,0,B|404:172|328:104,1,182.666667646567,8|0 +344,23,1405767,2,0,B|256:-16|167:23,1,182.666667646567,4|2 +11,135,1406373,6,0,B|107:171|180:105,1,182.666667646567,0|2 +330,277,1406979,2,0,B|404:212|500:248,1,182.666667646567,8|0 +180,277,1407585,2,0,B|107:212|11:247,1,182.666667646567,10|2 +120,332,1408040,5,8 +256,248,1408191,1,8 +392,332,1408343,1,0 +384,147,1408494,6,0,B|360:95|304:55|239:63|183:135|195:192|256:232|316:192|328:135|272:63|207:55|151:95|127:147,1,464.000007743836,4|2 +80,208,1409555,2,0,B|104:244|96:292,1,76.1333349488259 +12,180,1409858,2,0,B|12:136|40:100,1,76.1333349488259,8|0 +473,282,1410312,6,0,B|500:248|500:204,1,76.1333349488259,2|0 +415,95,1410616,2,0,B|408:140|432:176,1,76.1333349488259,0|8 +364,268,1410919,2,0,B|280:252,1,76.1333349488259,2|0 +148,116,1411222,2,0,B|232:132,1,76.1333349488259,2|0 +144,304,1411525,6,0,B|116:272|112:228,1,76.1333349488259,0|8 +368,80,1411828,2,0,B|396:112|400:156,1,76.1333349488259,2|8 +256,192,1412131,5,4 +256,192,1412282,12,8,1413494 +84,108,1413949,6,0,B|20:160,1,76.1333349488259,4|0 +84,251,1414252,2,0,B|128:180,1,76.1333349488259,8|2 +216,125,1414555,1,2 +216,125,1414631,1,0 +216,125,1414706,2,0,B|200:209,1,76.1333349488259,0|8 +312,208,1415009,2,0,B|296:125,1,76.1333349488259,0|2 +384,180,1415312,2,0,B|428:252,1,76.1333349488259,2|0 +493,160,1415616,2,0,B|428:108,1,76.1333349488259,0|4 +336,272,1416222,6,0,B|352:336|288:368|256:368|223:368|159:336|175:272,2,274.000001469851,2|2|8 +256,80,1417434,1,0 +193,107,1417585,2,0,B|181:86|174:41|209:-13|268:-15|316:8|345:44|324:108|313:114,1,274.000001469851,2|8 +496,256,1418494,1,2 +256,352,1418797,5,0 +216,228,1418949,1,8 +320,304,1419100,1,8 +192,304,1419252,1,8 +296,228,1419403,1,4 +16,256,1419858,5,2 +175,95,1420312,2,0,B|159:31|223:0|256:0|288:0|352:31|336:95,1,274.000001469851,2|8 +438,268,1421070,1,0 +361,333,1421222,2,0,B|334:307|294:294|255:333|255:360|255:360|255:333|215:294|175:307|149:333,1,274.000001469851,2|8 +80,288,1421828,2,0,B|108:248|160:236,1,91.3333338232835 +112,168,1422131,2,0,B|160:160|188:120,1,91.3333338232835,8|0 +256,76,1422434,1,0 +324,120,1422585,2,0,B|352:160|400:168,1,91.3333338232835,8|0 +357,237,1422888,2,0,B|400:248|432:288,1,91.3333338232835,0|4 +150,100,1423494,6,0,B|174:31|259:0|337:28|361:108,1,274.000001469851,2|2 +64,336,1424403,1,8 +256,212,1424706,1,0 +309,281,1424858,2,0,B|328:332|328:332|292:332|256:368|256:368|220:332|184:332|184:332|204:279,1,274.000001469851,2|8 +448,337,1425767,1,2 +180,88,1426222,6,0,B|203:42|256:31|310:43|333:90,1,182.666667646567,8|0 +362,187,1426676,2,0,B|324:295|164:293|148:177|148:177,3,274.000001469851,4|8|2|8 +40,360,1428494,1,2 +127,312,1428646,2,0,B|166:350|217:355,1,91.3333338232835,2|0 +305,353,1428949,2,0,B|346:350|385:312,1,91.3333338232835,8|8 +476,354,1429252,1,8 +488,248,1429403,5,8 +408,208,1429555,1,0 +320,144,1429706,1,0 +256,64,1429858,1,8 +192,144,1430009,1,0 +112,208,1430161,1,0 +24,248,1430312,1,4 +337,289,1430767,6,0,B|321:338|256:371|190:338|174:289,1,210.666669429143,2|0 +256,248,1431222,1,2 +256,56,1431525,1,0 +175,87,1431676,2,0,B|191:127|256:151|256:151|313:129|336:87,1,210.666669429143,8|0 +416,144,1432131,1,2 +416,336,1432434,2,0,B|368:312|368:312|344:264,1,105.333334714572,0|8 +166,266,1432888,2,0,B|144:312|144:312|96:336,1,105.333334714572,0|2 +96,144,1433343,5,0 +174,91,1433494,2,0,B|229:91|256:35|256:35|282:91|337:91,1,210.666669429143,8|0 +400,184,1433949,2,0,B|430:234|430:234|457:284|446:343|379:351|379:351,1,210.666669429143,2|0 +294,306,1434403,2,0,B|299:294|311:265|305:238|281:222|254:214|225:221|205:241|201:269|207:289|220:306,1,210.666669429143,8|0 +132,351,1434858,2,0,B|65:343|54:284|81:234|81:234|112:184,1,210.666669429143,2|0 +136,96,1435312,5,8 +215,45,1435464,2,0,B|222:20|255:3|288:20|296:45,1,105.333334714572,2|0 +376,96,1435767,1,2 +440,276,1436070,2,0,B|412:308|412:308|376:312|376:312|360:336,1,105.333334714572,0|8 +308,256,1436373,2,0,B|320:220|320:220|352:208|352:208|364:172,1,105.333334714572,2|0 +256,160,1436676,2,0,B|264:136|264:136|240:96|240:96|256:68|256:68,2,105.333334714572,8|0|0 +149,176,1437131,2,0,B|160:208|160:208|192:220|192:220|204:256,1,105.333334714572,8|0 +150,333,1437434,2,0,B|136:312|136:312|100:308|100:308|72:276,1,105.333334714572,0|4 +256,124,1437888,5,0 +176,92,1438040,2,0,B|192:43|257:10|323:43|339:92,1,210.666669429143,8|0 +360,192,1438494,2,0,B|136:192,1,210.666669429143,2|0 +176,292,1438949,2,0,B|192:341|257:374|323:341|339:292,1,210.666669429143,8|0 +416,348,1439403,5,2 +256,252,1439706,1,0 +256,252,1439858,1,8 +96,348,1440161,1,0 +96,348,1440312,1,2 +112,160,1440616,1,0 +164,80,1440767,1,8 +256,52,1440919,1,0 +348,80,1441070,1,0 +392,156,1441222,1,6 +256,192,1441373,12,2,1443040 +256,344,1443191,5,0 +344,312,1443343,1,10 +404,240,1443494,1,0 +404,144,1443646,1,8 +344,72,1443797,1,0 +256,40,1443949,1,8 +168,72,1444100,1,0 +108,144,1444252,1,8 +108,240,1444403,1,8 +168,312,1444555,5,8 +256,344,1444706,1,8 +344,312,1444858,1,4 +412,136,1445161,1,2 +452,52,1445312,1,8 +256,84,1445616,1,2 +256,176,1445767,1,0 +100,136,1446070,1,2 +60,52,1446222,1,8 +134,257,1446525,22,0,B|160:337|259:373|350:340|378:247,1,316.000004143715,2|2 +432,331,1447131,2,0,B|504:216|466:84|466:84,1,210.666669429143,8|0 +402,67,1447585,1,2 +256,188,1447888,1,0 +350,175,1448040,2,0,B|363:214|334:293|255:300|213:278,1,210.666669429143,8|0 +161,208,1448494,2,0,B|148:169|177:90|256:83|298:105,1,210.666669429143,2|0 +256,188,1448949,1,8 +112,68,1449252,1,0 +35,130,1449403,2,0,B|28:248|84:336,1,210.666669429143,2|0 +172,356,1449858,1,8 +300,236,1450161,38,0,B|323:218|338:177|323:136|295:111|254:100|213:111|185:136|170:177|185:218|209:236,1,316.000004143715,2|0 +104,228,1450767,1,8 +108,132,1450919,1,8 +164,56,1451070,1,0 +256,28,1451222,1,8 +348,56,1451373,1,8 +404,132,1451525,1,0 +408,228,1451676,1,8 +344,300,1451828,1,0 +256,332,1451979,1,8 +168,300,1452131,1,4 +44,44,1452585,21,2 +256,232,1453040,1,2 +312,60,1453343,1,0 +168,160,1453646,1,2 +348,160,1453949,1,2 +204,60,1454252,1,2 +256,136,1454403,1,8 +468,44,1454858,1,2 +448,232,1455161,5,0 +448,232,1455312,1,8 +296,352,1455616,1,0 +218,297,1455767,2,0,B|189:364|93:358|82:299|67:262|90:207|157:188|201:214|200:251,1,316.000004143715,2|8 +294,87,1456676,2,0,B|323:20|418:25|429:84|444:121|422:177|354:195|311:170|311:132,1,316.000004143715,2|8 +436,286,1457434,21,2 +350,317,1457585,1,8 +256,302,1457737,1,2 +185,247,1457888,1,2 +161,160,1458040,1,8 +177,66,1458191,1,0 +256,26,1458343,1,2 +334,66,1458494,1,10 +350,160,1458646,1,8 +326,247,1458797,1,0 +256,302,1458949,1,8 +161,317,1459100,1,8 +75,286,1459252,1,0 +40,200,1459403,38,0,B|8:160|16:88|64:48|136:56,1,210.666669429143,4|2 +200,112,1459858,1,8 +256,184,1460009,1,0 +312,112,1460161,1,2 +388,55,1460312,2,0,B|448:48|496:88|504:160|472:200,1,210.666669429143,0|0 +392,248,1460767,1,8 +296,288,1460919,2,0,B|289:263|256:246|223:263|215:288,1,105.333334714572,2|0 +128,240,1461222,1,0 +256,360,1461525,21,0 +256,360,1461676,1,8 +216,176,1461979,2,0,B|223:201|256:218|289:201|297:176,1,105.333334714572,2|0 +120,112,1462434,1,0 +168,32,1462585,1,8 +256,8,1462737,1,0 +344,32,1462888,1,0 +392,112,1463040,1,4 +256,192,1463191,12,2,1464858 +278,319,1465312,6,0,B|239:286|239:286|175:289|175:289,1,105.333334714572,8|0 +360,268,1465616,2,0,B|348:219|348:219|378:162|378:162,1,105.333334714572,2|8 +233,64,1465919,2,0,B|272:97|272:97|336:94|336:94,1,105.333334714572,0|8 +151,115,1466222,2,0,B|163:164|163:164|133:221|133:221,1,105.333334714572,8|0 +94,295,1466525,5,8 +194,352,1466676,2,0,B|203:291|254:272|254:272|315:249|310:184,1,210.666669429143,4|0 +328,96,1467131,1,8 +256,40,1467282,1,0 +184,96,1467434,1,0 +201,192,1467585,2,0,B|196:249|257:272|257:272|308:291|318:352,1,210.666669429143,2|0 +418,295,1468040,5,8 +472,216,1468191,2,0,B|416:192|424:128|424:128,1,105.333334714572 +344,80,1468494,2,0,B|304:96|264:80|256:48|256:48|248:80|208:96|168:80,1,210.666669429143,2|0 +168,176,1468949,2,0,B|216:208|216:208|256:184|256:184|296:208|296:208|344:176,1,210.666669429143,8|0 +256,280,1469403,5,2 +40,216,1469706,2,0,B|97:187|88:128|88:128,1,105.333334714572,0|8 +159,47,1470009,1,0 +257,24,1470161,1,0 +355,49,1470312,2,0,B|483:105|483:330|316:388|254:334|254:334|168:248|196:161|312:161|339:248|254:334|254:334|168:392|24:307|24:105|173:40,1,1264.00001657486,2|8 +264,76,1472282,5,8 +264,76,1472434,1,0 +264,76,1472585,1,8 +264,76,1472737,1,0 +264,76,1472888,1,8 +264,76,1473040,1,4 +400,320,1473494,1,6 +112,320,1473949,1,4 +256,192,1474100,12,0,1476676 +256,44,1505977,5,4 +172,96,1506172,1,4 +256,148,1506367,2,2,B|256:364,1,200,4|6 +100,332,1507536,6,0,B|80:216|132:128,1,200,0|8 +300,36,1508315,2,0,B|372:104|488:120,1,200 +472,220,1508899,1,0 +376,188,1509094,1,0 +216,308,1509484,2,0,B|112:308|16:376,1,200,8|0 +68,172,1510263,5,4 +220,44,1510652,1,0 +228,144,1510847,1,0 +308,204,1511042,1,8 +428,44,1511432,1,0 +488,236,1511821,1,0 +440,324,1512016,1,0 +348,284,1512211,1,0 +160,356,1512600,2,0,B|192:256|164:156,1,200,8|0 +348,76,1513380,5,6 +152,44,1513769,2,0,B|108:60|96:128,2,100,0|0|8 +256,216,1514549,1,0 +256,216,1514938,2,0,B|368:216,2,100 +64,164,1515717,2,0,B|124:72|224:52,1,200,8|0 +420,84,1516497,5,4 +384,280,1516886,1,0 +192,332,1517276,1,8 +68,176,1517665,1,0 +268,200,1518055,2,0,B|320:224|364:192,2,100 +116,104,1518834,5,8 +120,204,1519029,1,0 +220,188,1519224,1,0 +216,88,1519419,2,2,B|316:60|428:92,1,200 +364,176,1520003,1,0 +428,252,1520198,1,2 +260,364,1520587,5,0 +252,264,1520782,1,0 +156,288,1520977,2,2,B|116:188|180:88,1,200 +256,36,1521562,1,0 +396,176,1521951,5,8 +304,132,1522146,1,0 +204,124,1522341,1,0 +120,180,1522536,2,2,B|100:296|212:356,1,200 +256,268,1523120,1,0 +316,348,1523315,2,2,B|415:295|395:179,1,200 +296,168,1523899,1,0 +268,72,1524094,2,2,B|180:120|56:68,1,200 +32,164,1524678,1,0 +172,304,1525068,1,8 +216,216,1525263,5,0 +316,224,1525458,1,0 +332,124,1525652,2,2,B|460:112|492:216,1,200 +420,280,1526237,1,0 +336,336,1526432,2,2,B|240:352|128:336,1,200 +56,280,1527016,1,0 +68,180,1527211,2,2,B|172:116|204:20,1,200 +256,116,1527795,1,0 +432,212,1528185,1,8 +336,244,1528380,5,0 +408,316,1528574,1,0 +316,356,1528769,2,2,B|112:356,1,200 +16,356,1529354,1,0 +32,156,1529743,6,0,B|16:108|32:52,1,100,8|0 +104,128,1530133,2,0,B|120:180|176:208,1,100 +200,108,1530523,2,0,B|222:195|310:225|393:202|416:96,1,300,4|0 +476,184,1531302,1,8 +452,320,1531497,5,0 +360,360,1531691,1,0 +276,308,1531886,2,2,B|180:348|80:256,1,200 +32,184,1532471,1,0 +32,184,1532665,2,2,B|60:52|176:52,1,200 +260,64,1533250,1,0 +260,64,1533445,2,2,B|344:148|260:248,1,200 +196,316,1534029,2,0,B|80:316,2,100,0|0|8 +336,316,1534613,5,0 +432,296,1534808,1,0 +436,196,1535003,2,2,B|428:84|300:32,1,200 +256,116,1535587,1,0 +188,44,1535782,2,2,B|83:85|75:197,1,200 +176,184,1536367,2,0,B|184:240|135:282,1,100,0|2 +200,356,1536756,1,4 +256,164,1537146,1,0 +320,352,1537536,1,8 +448,292,1537730,6,0,B|412:244|444:184,2,100,0|0|2 +484,96,1538510,1,0 +392,64,1538704,1,0 +312,124,1538899,1,2 +216,300,1539289,1,0 +124,340,1539484,1,0 +108,240,1539678,1,2 +188,180,1539873,2,0,B|156:80|44:40,2,200,0|0|8 +280,72,1540847,5,2 +380,84,1541042,1,0 +424,172,1541237,1,2 +440,272,1541432,1,0 +372,344,1541626,2,2,B|264:384|188:292,1,200,2|0 +256,220,1542211,6,0,B|144:220,1,100,8|0 +168,120,1542600,2,0,B|56:120,1,100 +40,216,1542990,2,0,B|104:304|216:320,1,200,4|0 +256,232,1543574,1,0 +256,232,1543769,1,8 +388,272,1543964,5,0 +404,172,1544159,1,0 +364,80,1544354,2,2,B|160:80,1,200 +72,124,1544938,1,0 +160,168,1545133,2,2,B|108:268|192:360,1,200 +260,288,1545717,1,0 +336,352,1545912,1,2 +388,160,1546302,1,0 +484,184,1546497,1,0 +464,88,1546691,2,2,B|364:48|248:88,1,200 +256,180,1547276,1,0 +256,192,1547471,12,6,1549224 +408,32,1550782,5,6 +104,132,1551172,1,0 +408,228,1551562,1,8 +100,312,1551951,1,2 +256,272,1552341,5,0 +256,172,1552536,1,0 +256,272,1552730,1,0 +256,72,1553120,1,8 +56,72,1553510,6,2,B|84:164|20:264,1,200,2|6 +160,120,1554289,2,2,B|256:80|356:124,1,200,2|10 +492,268,1555068,2,2,B|429:169|457:77,1,200 +356,80,1555652,1,0 +260,60,1555847,1,2 +256,220,1556237,1,8 +256,220,1556626,1,0 +224,316,1556821,1,0 +124,308,1557016,5,6 +256,60,1557406,1,0 +380,312,1557795,1,8 +256,60,1558185,2,2,B|256:264,1,200,2|0 +256,260,1558769,1,0 +256,260,1558964,1,0 +256,192,1559354,12,0,1561302 +68,116,1561691,5,0 +168,116,1561886,1,8 +216,136,1561984,1,8 +260,116,1562081,1,8 +360,116,1562276,1,8 +324,208,1562471,1,8 +424,192,1562665,1,0 +360,116,1562860,1,0 +432,280,1563055,6,0,B|384:300|320:292,1,100,0|6 +184,160,1563639,2,0,B|128:160|104:108,2,100,0|0|8 +368,80,1564419,2,2,B|360:196|276:256,1,200 +228,168,1565003,1,0 +280,252,1565198,1,0 +100,336,1565587,6,2,B|32:272|32:136,1,200,10|2 +84,64,1566172,2,0,B|140:64|180:96,1,100,0|4 +256,276,1566756,1,2 +368,108,1567146,1,8 +488,268,1567536,2,2,B|440:348|304:328,1,200 +212,348,1568120,1,0 +200,248,1568315,2,2,B|136:164|12:176,1,200,2|10 +168,40,1569094,1,0 +256,84,1569289,2,0,B|276:28,2,50,0|0|4 +472,192,1569873,5,0 +256,336,1570263,1,8 +40,192,1570652,2,2,B|248:192,1,200,2|0 +240,192,1571237,1,0 +240,192,1571432,1,0 +256,192,1571821,12,0,1573769 +392,292,1574159,5,0 +360,156,1574354,1,8 +236,92,1574549,1,0 +128,180,1574743,1,0 +112,320,1574938,1,8 +240,264,1575133,1,0 +128,180,1575328,1,0 +284,152,1575523,5,0 +300,200,1575620,1,0 +316,248,1575717,1,4 +68,48,1585068,5,2 +132,280,1585652,1,2 +192,48,1586237,1,2 +432,192,1586626,5,2 +192,192,1587211,1,2 +316,88,1587600,5,0 +416,72,1587795,1,0 +316,88,1587990,2,2,B|344:196|288:288,1,200,2|2 +220,352,1588574,1,0 +200,256,1588769,2,2,B|124:192|12:168,1,200 +64,80,1589354,1,0 +64,80,1589549,1,2 +260,44,1589938,2,2,B|412:44,1,150,0|8 +488,104,1590717,5,0 +472,204,1590912,1,0 +376,236,1591107,1,2 +204,340,1591497,1,0 +248,252,1591691,1,0 +148,260,1591886,1,2 +28,100,1592276,1,0 +128,104,1592471,1,0 +208,44,1592665,2,2,B|324:32|380:132,1,200 +428,216,1593250,1,0 +484,24,1593639,1,8 +452,120,1593834,5,0 +428,216,1594029,1,0 +340,164,1594224,1,2 +256,344,1594613,1,0 +256,244,1594808,1,0 +256,344,1595003,1,2 +60,300,1595393,1,0 +156,276,1595587,1,0 +132,180,1595782,2,2,B|108:72|20:8,1,200 +20,316,1596756,1,8 +160,328,1596951,5,0 +260,348,1597146,1,0 +328,276,1597341,1,2 +300,180,1597536,1,0 +204,164,1597730,1,2 +204,164,1598315,1,8 +28,260,1598704,5,0 +80,172,1598899,1,0 +156,240,1599094,1,0 +320,168,1599484,1,0 +280,76,1599678,1,0 +380,84,1599873,1,8 +404,180,1600068,6,0,B|428:284,2,100,0|8|10 +256,44,1600847,1,0 +256,144,1601042,1,0 +256,44,1601237,1,2 +56,68,1601626,2,0,B|88:120|56:168,1,100 +52,264,1602016,2,2,B|96:364|232:352,1,200 +312,328,1602600,1,0 +152,208,1602990,1,8 +208,80,1603185,5,2 +304,100,1603380,1,0 +400,80,1603574,1,2 +464,268,1603964,1,0 +404,348,1604159,1,0 +304,352,1604354,1,2 +128,256,1604743,1,0 +224,280,1604938,1,0 +316,236,1605133,2,2,B|440:236|500:136,2,200,2|0|2 +224,280,1606107,1,8 +256,144,1606302,5,0 +160,120,1606497,1,0 +96,196,1606691,1,2 +292,232,1607081,1,0 +348,316,1607276,1,0 +444,292,1607471,1,2 +248,328,1607860,1,0 +248,328,1608055,2,2,B|148:292|108:176,1,200,2|0 +116,88,1608639,1,2 +200,144,1608834,2,0,B|252:164|312:144,2,100,0|0|8 +252,60,1609419,6,0,B|300:40|356:64,1,100,2|0 +440,100,1609808,2,0,B|420:148|456:216,1,100,2|0 +408,288,1610198,1,2 +124,192,1610782,5,8 +252,344,1611172,1,0 +156,316,1611367,1,0 +60,348,1611562,1,0 +44,148,1611951,2,0,B|112:68|224:64,1,200,0|8 +356,36,1612536,5,0 +328,132,1612730,1,0 +428,140,1612925,2,2,B|448:256|344:324,1,200 +276,256,1613510,1,0 +204,324,1613704,2,2,B|100:324|64:188,1,200 +36,104,1614289,1,0 +116,44,1614484,2,2,B|324:44,1,200 +416,52,1615068,1,0 +480,128,1615263,2,2,B|472:232|352:284,1,200 +356,180,1615847,2,2,B|296:172|272:244,2,100 +132,352,1616626,5,8 +60,232,1616821,1,8 +120,104,1617016,1,8 +240,32,1617211,1,8 +376,72,1617406,1,8 +416,208,1617600,1,0 +236,204,1617795,6,2,B|310:134|424:135,1,200,2|0 +472,328,1618574,1,8 +388,276,1618769,1,0 +476,228,1618964,1,0 +472,328,1619159,1,0 +324,340,1619354,6,0,B|220:364|124:320,1,200,6|0 +44,140,1620133,1,8 +132,188,1620328,1,0 +204,116,1620523,2,2,B|312:116|396:36,1,200 +456,120,1621107,1,0 +364,156,1621302,1,2 +468,328,1621691,1,8 +284,248,1622081,2,2,B|212:188|212:188|192:64,1,200,2|6 +28,192,1622860,5,2 +184,316,1623250,1,8 +248,124,1623639,2,2,B|352:60|458:182|384:292,1,300,2|0 +389,283,1624419,1,2 +200,216,1624808,1,8 +44,340,1625198,1,0 +44,340,1625393,5,0 +44,340,1625490,1,0 +44,340,1625587,2,0,B|88:136,1,200,6|0 +276,80,1626367,2,0,B|372:52|476:88,2,200,8|2|0 +184,124,1627341,1,0 +256,192,1627536,1,0 +256,192,1627925,12,0,1629873 +256,36,1630263,5,0 +292,56,1630458,1,8 +312,88,1630652,1,0 +316,128,1630847,1,0 +308,168,1631042,1,8 +296,208,1631237,1,0 +272,240,1631432,1,0 +244,268,1631626,1,0 +204,276,1631821,6,2,B|91:252|43:156,1,200,6|2 +304,48,1632600,2,0,B|428:72|444:204,2,200,8|2|0 +304,48,1633574,1,0 +304,48,1633769,1,2 +456,284,1634159,2,0,B|372:344|264:304,2,200,8|2|4 +240,104,1635328,6,2,B|140:108|72:204,1,200,2|8 +340,108,1636107,2,2,B|304:212|172:196,1,200 +180,196,1636691,1,0 +180,196,1636886,2,2,B|196:324|328:324,1,200,2|10 +452,84,1637665,1,0 +452,84,1637860,5,8 +452,84,1637958,1,8 +452,84,1638055,2,0,B|348:116|244:68,1,200,6|0 +64,124,1638834,1,8 +64,124,1639224,2,2,B|64:228|152:308,1,200 +244,316,1639808,1,0 +256,216,1640003,1,0 +256,192,1640393,12,8,1641951 +416,192,1642536,5,0 +416,192,1642730,2,8,B|432:116,2,66.6666666666667,0|8|8 +352,272,1643120,2,0,B|368:356,2,66.6666666666667,8|8|0 +292,192,1643510,2,0,B|272:120,2,66.6666666666667,0|8|8 +232,272,1643899,2,0,B|204:348,2,66.6666666666667,8|0|0 +132,296,1644289,1,4 +104,96,1644678,5,0 +196,136,1644873,1,0 +288,96,1645068,1,8 +456,208,1645458,1,0 +356,192,1645652,1,0 +264,228,1645847,2,2,B|152:228|60:324,2,200,10|2|10 +432,116,1647016,1,6 +264,228,1647406,5,8 +344,172,1647600,2,0,B|364:124,2,50 +248,152,1647990,1,0 +176,80,1648185,1,8 +76,96,1648380,1,0 +32,188,1648574,1,0 +84,272,1648769,1,0 +132,260,1648867,1,0 +176,280,1648964,6,2,B|248:344|368:312,1,200,10|2 +248,152,1649743,1,8 +424,56,1650133,2,2,B|320:28|224:56,1,200,2|10 +256,252,1650912,2,2,B|368:252|432:364,1,200,2|10 +220,344,1651691,5,2 +120,328,1651886,1,0 +128,228,1652081,1,8 +36,48,1652471,2,2,B|244:48,1,200,2|10 +436,60,1653250,2,2,B|420:160|468:268,1,200,2|10 +376,304,1653834,5,0 +292,192,1654029,1,0 +268,236,1654126,1,0 +220,248,1654224,1,0 +172,236,1654321,1,0 +148,192,1654419,1,8 +84,68,1654613,1,0 +32,196,1654808,5,0 +32,196,1655003,1,0 +32,196,1655198,1,10 +256,348,1655587,1,2 +476,192,1655977,1,10 +256,40,1656367,2,0,B|256:152,2,100,2|0|6 +160,192,1657146,5,0 +256,220,1657341,1,0 +340,164,1657536,1,8 +156,92,1657925,1,0 +80,156,1658120,1,0 +92,256,1658315,1,10 +276,176,1658704,6,2,B|312:288|196:344,2,200,2|10|2 +212,100,1659678,1,0 +308,68,1659873,1,8 +472,184,1660263,2,0,B|472:244|424:280,2,100,0|0|8 +376,360,1661042,1,0 +324,272,1661237,1,0 +324,272,1661334,1,0 +324,272,1661432,6,0,B|216:248|128:328,1,200,8|0 +52,268,1662016,1,0 +36,168,1662211,2,0,B|84:68|196:64,1,200,2|2 +256,144,1662795,1,0 +256,192,1662990,12,0,1666107 +256,192,1677016,12,4,1678574 +256,180,1678964,5,0 +256,180,1679159,1,0 +256,180,1679354,1,2 +160,308,1679743,1,0 +100,252,1679938,1,0 +56,184,1680133,2,0,B|140:92,1,100,2|0 +196,76,1680523,2,0,B|308:76,3,100,0|0|2|0 +376,76,1681302,2,0,B|412:124|396:176,1,100 +364,244,1681691,5,4 +208,272,1682081,2,0,B|208:392,2,100,0|0|2 +68,192,1682860,1,0 +140,156,1683055,1,0 +152,76,1683250,1,2 +256,200,1683639,1,0 +364,80,1684029,1,2 +428,128,1684224,1,0 +440,208,1684419,1,0 +392,272,1684613,1,0 +392,272,1684808,6,0,B|304:264|180:304,1,200,4|0 +256,152,1685587,1,2 +256,152,1685782,1,0 +256,152,1685977,1,0 +100,188,1686367,1,2 +256,228,1686756,1,0 +412,188,1687146,1,2 +364,124,1687341,1,0 +292,92,1687536,1,0 +220,124,1687730,1,0 +172,188,1687925,1,2 +472,312,1688315,5,0 +400,348,1688510,1,0 +300,348,1688704,1,8 +184,312,1688899,1,8 +136,180,1689094,1,8 +248,68,1689289,1,8 +400,164,1689484,1,6 +56,68,1690263,5,0 +32,132,1690393,1,0 +36,200,1690523,1,8 +68,256,1690652,1,8 +124,296,1690782,1,8 +192,300,1690912,1,0 +252,272,1691042,6,2,B|360:300|428:200,1,200,6|2 +300,48,1691821,1,8 +156,188,1692211,1,2 +284,240,1692406,1,0 +396,172,1692600,2,0,B|452:160|492:200,2,100,0|0|2 +256,316,1693380,1,8 +220,120,1693769,6,2,B|128:48|32:120,1,200,2|6 +56,316,1694549,2,2,B|260:316,1,200,2|10 +452,280,1695328,1,2 +424,184,1695523,1,0 +444,88,1695717,1,0 +348,120,1695912,1,0 +248,128,1696107,1,2 +256,328,1696497,1,8 +92,212,1696886,1,0 +92,212,1697081,5,0 +92,212,1697178,1,0 +92,212,1697276,2,2,B|191:234|304:212,2,200,6|2|10 +200,44,1698445,2,2,B|292:20|416:52,1,200 +444,136,1699029,1,0 +408,232,1699224,1,0 +256,192,1699613,12,0,1701562 +428,192,1701951,5,4 +376,192,1702049,1,0 +328,192,1702146,1,0 +280,192,1702243,1,0 +228,192,1702341,6,0,B|240:260,2,66.6666666666667,0|0|0 +132,228,1702730,2,0,B|100:300,2,66.6666666666667,0|0|0 +100,132,1703120,2,0,B|28:152,2,66.6666666666667 +192,88,1703510,6,2,B|284:44|404:92,1,200,6|2 +420,284,1704289,2,2,B|332:360|228:284,1,200,10|2 +156,352,1704873,1,0 +156,252,1705068,1,0 +60,280,1705263,1,0 +52,180,1705458,2,2,B|148:132|192:32,1,200,2|10 +360,284,1706237,6,2,B|448:204|384:92,2,200,2|6|2 +256,112,1707406,1,8 +184,300,1707795,2,2,B|96:268|56:136,1,200 +32,56,1708380,1,0 +132,56,1708574,2,2,B|352:56,1,200,2|10 +480,188,1709354,5,8 +460,288,1709549,1,8 +412,280,1709646,1,8 +380,240,1709743,1,6 +200,328,1710133,2,0,B|160:236|196:120,2,200,0|8|2 +100,152,1711302,1,0 +28,220,1711497,1,0 +16,120,1711691,1,8 +96,60,1711886,1,0 +196,56,1712081,2,2,B|308:32|400:88,1,200 +356,176,1712665,1,0 +308,264,1712860,1,4 +256,192,1712958,12,0,1714419 +256,308,1748243,6,0,B|256:364,2,54.9999983608723 +256,252,1749012,2,0,B|244:216|268:192|256:144,1,109.999996721745,0|0 +284,36,1750038,1,0 +228,36,1750294,1,0 +347,99,1750807,6,0,B|392:128|424:192|392:260|348:284,1,219.999993443489 +256,192,1752602,1,0 +164,100,1753115,2,0,B|120:124|88:192|120:256|164:284,1,219.999993443489 +332,292,1755166,54,0,B|320:352|256:392|192:352|180:292,1,219.999993443489 +176,236,1756448,1,0 +228,216,1756704,1,0 +284,216,1756961,1,0 +336,236,1757217,1,0 +436,188,1757730,1,0 +348,124,1758243,5,0 +308,84,1758499,2,0,B|256:68|256:68|204:84,1,109.999996721745,0|0 +204,140,1759268,2,0,B|200:184|256:220|312:184|308:140,1,164.999995082617,0|0 +356,332,1760807,6,0,B|324:360|288:364|256:336|256:336|224:364|188:360|156:332,1,219.999993443489,0|0 +204,272,1762345,2,0,B|256:300|308:272,1,109.999996721745,2|0 +336,188,1763371,2,0,B|304:168|256:188|256:188|208:208|176:196,1,165.99999386549,4|8 +100,164,1764140,6,0,B|76:132|74:87,1,82.9999969327451,0|8 +140,32,1764653,2,0,B|220:56,1,82.9999969327451,0|8 +300,76,1765166,1,0 +324,156,1765422,2,0,B|304:188|256:220|208:188|186:154,1,165.99999386549,2|8 +212,76,1766192,1,0 +292,55,1766448,2,0,B|372:32,1,82.9999969327451,2|0 +436,86,1766961,2,0,B|436:132|412:164,1,82.9999969327451,8|0 +336,236,1767474,2,0,B|256:196|176:236,2,165.99999386549,4|8|2 +416,252,1768756,5,0 +432,172,1769012,1,8 +348,156,1769268,2,0,B|260:155|212:76,1,165.99999386549,2|0 +299,76,1770038,2,0,B|251:155|163:155,1,165.99999386549,2|8 +80,144,1770807,1,0 +132,80,1771063,1,2 +52,68,1771320,1,0 +16,196,1771576,2,0,B|36:360,1,165.99999386549,4|8 +116,340,1772345,2,0,B|108:280|108:280|100:220|144:188,1,165.99999386549,2|0 +224,168,1773115,5,8 +288,216,1773371,1,0 +367,196,1773627,2,0,B|412:164|404:104|404:104|396:44,2,165.99999386549,2|8|8 +320,128,1774909,1,0 +256,72,1775166,1,2 +196,128,1775422,1,0 +180,208,1775679,6,0,B|200:284|272:284|272:284|308:284,1,165.99999386549,4|8 +392,288,1776448,2,0,B|372:364|300:364|300:364|264:364,1,165.99999386549,2|0 +180,360,1777217,2,0,B|156:272|64:256,1,165.99999386549,2|8 +40,176,1777986,5,0 +96,116,1778243,1,8 +176,100,1778499,1,0 +256,112,1778756,1,2 +344,192,1779012,1,0 +256,272,1779268,1,8 +168,192,1779525,1,0 +256,112,1779781,1,4 +324,44,1780038,54,0,B|408:32,1,82.9999969327451,0|8 +500,96,1780551,2,0,B|416:108,1,82.9999969327451,0|2 +364,192,1781063,2,0,B|388:272,1,82.9999969327451,0|8 +336,347,1781576,2,0,B|256:314|170:348,1,165.99999386549,4|0 +148,192,1782345,2,0,B|124:272,1,82.9999969327451,2|0 +64,216,1782858,5,8 +88,136,1783115,2,0,B|112:56,1,82.9999969327451,2|0 +196,52,1783627,2,0,B|236:76|256:108|256:108|276:76|316:52,1,165.99999386549,4|0 +364,120,1784397,5,2 +256,176,1784653,1,0 +148,120,1784909,1,2 +336,224,1785166,1,0 +176,224,1785422,1,2 +256,360,1785679,1,0 +256,276,1785935,1,8 +256,192,1786192,1,0 +344,136,1786448,6,0,B|360:52,1,82.9999969327451,8|0 +256,1,1786961,2,0,B|256:84,1,82.9999969327451,2|0 +168,136,1787474,2,0,B|152:52,1,82.9999969327451,8|0 +44,176,1787986,2,0,B|24:340,1,165.99999386549,4|8 +112,296,1788756,2,0,B|116:212,1,82.9999969327451,0|2 +204,81,1789268,2,0,B|204:164,1,82.9999969327451,0|8 +280,244,1789781,5,0 +468,208,1790038,2,0,B|488:44,1,165.99999386549,4|8 +400,88,1790807,2,0,B|396:172,1,82.9999969327451,0|2 +309,302,1791320,2,0,B|308:220,1,82.9999969327451,0|8 +256,136,1791833,1,0 +184,56,1792089,2,0,B|208:24|256:8|300:24|330:57,1,165.99999386549,4|8 +328,156,1792858,5,0 +256,224,1793115,2,0,B|176:276|208:368,1,165.99999386549,2|8 +305,366,1793884,2,0,B|338:275|254:224,1,165.99999386549,2|8 +256,124,1794653,1,8 +256,48,1795166,5,0 +256,244,1795422,1,8 +184,124,1795679,1,8 +328,124,1795935,1,8 +400,244,1796192,1,4 +340,336,1796448,6,2,B|316:280|256:248|196:280|171:338,1,219.999993443489,2|0 +64,352,1797217,2,0,B|40:244,1,109.999996721745,2|0 +92,148,1797730,1,8 +184,84,1797986,2,0,B|268:100|316:156|332:228,1,219.999993443489,4|0 +256,308,1798756,1,8 +180,228,1799012,2,0,B|197:155|245:99|329:83,1,219.999993443489,2|0 +432,120,1799781,1,8 +456,336,1800038,6,0,B|348:288|348:160,1,219.999993443489,4|0 +312,56,1800807,1,8 +200,56,1801063,1,0 +256,216,1801320,1,2 +256,104,1801576,1,0 +164,160,1801833,1,8 +108,68,1802089,1,0 +165,160,1802345,2,0,B|164:288|56:336,1,219.999993443489,4|8 +12,236,1803115,5,0 +64,216,1803243,2,0,B|144:236|200:304,1,164.999995082617,2|0 +308,276,1803884,1,8 +232,196,1804140,1,2 +96,132,1804397,2,0,B|72:75|124:3|204:35|212:103,1,219.999993443489,4|8 +300,36,1805166,1,0 +356,36,1805294,2,0,B|388:32|408:16|408:16|424:40|448:52,1,109.999996721745,2|0 +500,72,1805679,5,2 +492,180,1805935,1,8 +388,148,1806192,1,0 +416,252,1806448,2,0,B|440:309|388:381|308:349|300:281,1,219.999993443489,4|8 +208,220,1807217,2,0,B|199:152|119:120|67:192|91:249,1,219.999993443489,2|0 +176,320,1807986,1,8 +280,352,1808243,1,0 +404,276,1808499,5,4 +404,276,1808756,2,0,B|336:192,1,109.999996721745,2|8 +108,107,1809268,2,0,B|178:193,1,109.999996721745,2|0 +328,100,1809781,2,0,B|360:48|408:28,2,109.999996721745,0|8|2 +185,283,1810551,2,0,B|152:336|104:356,2,109.999996721745,0|2|8 +296,228,1811320,6,0,B|260:212|252:172|216:156,2,109.999996721745,0|2|8 +368,144,1812089,5,8 +308,52,1812345,1,8 +256,36,1812474,1,0 +204,52,1812602,1,4 +144,144,1812858,1,2 +92,240,1813115,1,8 +148,336,1813371,1,0 +256,344,1813627,2,0,B|308:324|324:264,1,109.999996721745,2|0 +268,168,1814140,5,8 +204,256,1814397,2,0,B|292:332|408:316,1,219.999993443489,4|0 +484,236,1815166,1,8 +492,124,1815422,2,0,B|464:52|368:32|312:100,1,219.999993443489,2|0 +216,156,1816192,1,8 +56,48,1816448,6,0,B|8:168|92:252,1,219.999993443489,4|0 +200,256,1817217,2,0,B|312:256,1,109.999996721745,8|0 +420,240,1817730,2,0,B|360:332,1,109.999996721745,2|0 +312,360,1818115,1,2 +200,360,1818371,1,0 +152,332,1818499,2,0,B|92:240,1,109.999996721745,2|4 +256,252,1819012,5,0 +208,100,1819268,1,8 +332,196,1819525,1,0 +180,196,1819781,1,2 +304,100,1820038,5,0 +256,168,1820294,1,8 +400,108,1820551,1,0 +420,160,1820679,1,0 +440,212,1820807,1,4 +356,284,1821063,2,0,B|304:288|256:244|256:244|208:288|156:284,1,219.999993443489,2|0 +52,320,1821833,1,2 +72,212,1822089,5,0 +92,160,1822217,1,0 +112,108,1822345,1,8 +156,8,1822602,2,0,B|207:4|255:48|255:48|303:4|354:7,1,219.999993443489,4|0 +352,120,1823371,1,8 +256,172,1823627,1,2 +160,120,1823884,1,0 +192,224,1824140,5,8 +180,276,1824268,1,0 +204,324,1824397,1,8 +256,344,1824525,1,0 +308,324,1824653,1,2 +332,276,1824781,1,0 +320,224,1824909,1,4 +152,140,1830294,6,0,B|232:111,1,83.5000010451675,0|8 +360,244,1830807,2,0,B|280:272,1,83.5000010451675,0|2 +124,240,1831320,2,0,B|204:212,1,83.5000010451675,0|8 +388,144,1831833,2,0,B|308:172,1,83.5000010451675,0|2 +208,60,1832345,2,0,B|236:140,1,83.5000010451675,0|8 +304,324,1832858,2,0,B|276:244,1,83.5000010451675,0|4 +440,232,1833627,5,8 +428,152,1833884,1,0 +360,200,1834140,1,2 +504,180,1834397,5,0 +492,100,1834653,1,8 +416,72,1834909,1,0 +332,68,1835166,2,0,B|292:68|292:68|256:44|256:44|220:68|220:68|180:68,1,167.000002090335,2|8 +96,72,1835935,1,0 +20,100,1836192,1,2 +8,180,1836448,1,0 +84,148,1836704,1,8 +176,284,1837217,6,0,B|343:284,2,167.000002090335,0|8|0 +100,164,1838499,6,0,B|180:192,1,83.5000010451675,2|8 +412,164,1839012,2,0,B|332:192,1,83.5000010451675,0|2 +76,72,1839525,2,0,B|156:100,1,83.5000010451675,0|8 +436,72,1840038,2,0,B|356:100,1,83.5000010451675,0|2 +256,52,1840551,1,0 +256,136,1840807,1,8 +256,176,1840935,1,0 +256,216,1841063,1,8 +256,300,1841320,1,4 +92,312,1841833,6,0,B|48:304|28:264,1,83.5000010451675,8|0 +104,232,1842345,1,2 +168,180,1842602,1,0 +192,100,1842858,1,8 +196,16,1843115,1,0 +116,36,1843371,2,0,B|120:124|42:176,2,167.000002090335,4|8|2 +176,152,1844653,6,4,B|256:136|256:136|338:152,1,167.000002090335,4|0 +395,36,1845422,2,0,B|392:124|468:178,1,167.000002090335,4|8 +492,96,1846192,2,0,B|368:205,1,167.000002090335,2|2 +292,172,1846961,1,8 +152,104,1847217,6,0,B|116:188|17:185,1,167.000002090335,2|0 +8,268,1847986,2,0,B|85:296,1,83.5000010451675,8|0 +221,264,1848499,2,0,B|143:235,1,83.5000010451675,2|0 +220,344,1849012,2,0,B|299:374,1,83.5000010451675,8|0 +432,356,1849525,5,4 +508,328,1849781,2,0,B|488:288|445:276,1,83.5000010451675,0|8 +364,268,1850294,2,0,B|358:176|289:122,1,167.000002090335 +352,68,1851063,2,0,B|296:8,1,83.5000010451675,8|0 +216,7,1851576,2,0,B|160:68,1,83.5000010451675,2|0 +80,88,1852089,2,0,B|44:168,1,83.5000010451675,8|0 +16,240,1852602,1,2 +32,312,1852858,2,0,B|104:332|124:260|189:280,1,167.000002090335 +323,280,1853627,6,0,B|388:260|408:332|481:311,1,167.000002090335,4|8 +456,240,1854397,2,0,B|388:260|408:332|340:352,1,167.000002090335,0|2 +256,280,1855166,1,8 +148,324,1855422,1,0 +104,220,1855679,1,2 +212,176,1855935,1,0 +256,280,1856192,5,8 +104,220,1856448,1,0 +164,64,1856704,1,2 +316,124,1856961,1,0 +316,124,1857089,1,8 +316,124,1857217,1,8 +420,252,1857474,1,8 +420,252,1857602,1,8 +420,252,1857730,5,4 +476,192,1857986,1,0 +500,272,1858243,2,0,B|496:332|424:344|424:344|372:352,1,167.000002090335,8|2 +300,312,1859012,1,0 +256,240,1859268,1,8 +212,312,1859525,1,0 +137,351,1859781,2,0,B|87:344|87:344|15:332|10:271,1,167.000002090335,4|8 +96,264,1860551,2,0,B|176:244,1,83.5000010451675,0|2 +124,180,1861063,2,0,B|200:148,2,83.5000010451675,0|8|0 +120,96,1861833,6,0,B|176:28|271:47,1,167.000002090335,4|8 +292,160,1862602,1,0 +256,264,1862858,1,2 +220,160,1863115,1,0 +312,224,1863371,1,8 +200,224,1863627,1,0 +243,48,1863884,6,0,B|336:28|393:97,1,167.000002090335,4|8 +460,168,1864653,1,0 +408,252,1864909,1,2 +424,348,1865166,1,0 +324,336,1865422,1,8 +244,280,1865679,1,8 +224,316,1865807,1,8 +188,336,1865935,5,4 +168,256,1866192,2,0,B|212:188|306:172,1,167.000002090335,2|0 +336,96,1866961,2,0,B|376:112|384:160,1,83.5000010451675,2|0 +424,232,1867474,1,8 +344,256,1867730,2,0,B|300:188|206:170,1,167.000002090335,4|0 +128,158,1868499,2,0,B|136:112|176:96,1,83.5000010451675,8|0 +120,12,1869012,2,0,B|112:58|72:74,1,83.5000010451675,2|0 +0,116,1869525,1,8 +40,188,1869781,1,0 +112,148,1870038,1,4 +256,64,1870551,6,0,B|288:132|224:236|256:310,2,250.500003135502,8|2|8 +256,64,1872217,1,0 +380,64,1872474,2,0,B|420:80,1,41.7500005225837 +440,192,1872858,2,0,B|376:164|376:164,1,41.7500005225837 +240,168,1873371,5,8 +204,216,1873499,1,8 +144,224,1873627,1,8 +92,188,1873756,1,0 +88,128,1873884,1,8 +124,76,1874012,1,8 +184,68,1874140,5,4 +292,76,1874397,2,0,B|364:88|360:168|360:168|356:260,1,219.999993443489,2|0 +324,364,1875166,1,2 +256,276,1875422,1,0 +188,364,1875679,1,8 +156,260,1875935,2,0,B|152:168|152:168|148:88|220:76,1,219.999993443489,2|0 +324,104,1876704,1,8 +256,192,1876961,2,0,B|184:104|72:80,1,219.999993443489,2|0 +168,24,1877730,1,8 +256,192,1877986,6,0,B|328:280|440:304,1,219.999993443489,4|0 +352,372,1878756,1,8 +252,328,1879012,2,0,B|216:280|160:276,1,109.999996721745,0|2 +28,300,1879525,2,0,B|64:348|120:352,1,109.999996721745,0|8 +76,224,1880038,1,0 +36,120,1880294,6,0,B|32:76|104:40|120:84|136:128|208:92|196:48,1,219.999993443489,4|8 +204,156,1881063,1,0 +216,208,1881192,2,0,B|228:244|256:244|284:244|296:208,1,109.999996721745,2|0 +308,156,1881576,1,2 +360,24,1881833,1,8 +452,132,1882089,1,2 +476,272,1882345,6,0,B|480:316|408:352|392:308|376:264|304:300|316:344,1,219.999993443489,4|8 +256,252,1883115,1,0 +200,344,1883371,1,2 +108,284,1883627,2,0,B|84:176,1,109.999996721745,0|8 +164,100,1884140,2,0,B|176:208,1,109.999996721745,0|2 +256,132,1884653,2,0,B|256:20,1,109.999996721745,0|8 +348,100,1885166,2,0,B|336:208,1,109.999996721745,0|2 +404,284,1885679,2,0,B|428:176,1,109.999996721745,0|8 +452,68,1886192,1,0 +256,40,1886448,5,4 +152,80,1886704,1,0 +256,196,1886961,1,8 +360,304,1887217,1,2 +256,344,1887474,1,0 +256,196,1887730,1,0 +104,192,1887986,5,8 +144,296,1888243,1,0 +256,196,1888499,1,0 +368,88,1888756,1,2 +408,192,1889012,1,8 +256,196,1889268,1,0 +104,192,1889525,1,2 +144,88,1889781,5,0 +256,40,1890038,1,8 +368,88,1890294,1,8 +396,136,1890422,1,0 +420,184,1890551,1,4 +416,296,1890807,2,0,B|368:312|316:288|316:288|256:256|196:288|196:288|144:312|96:296,1,329.999990165234,2|0 +148,200,1891833,1,0 +256,176,1892089,1,8 +403,79,1892345,6,0,B|456:100|456:100|432:152|456:204|456:204|403:224,1,219.999993443489,4|0 +256,256,1893115,1,8 +109,224,1893371,2,0,B|56:204|56:204|80:152|56:100|56:100|108:80,1,219.999993443489,2|0 +256,48,1894140,1,8 +344,196,1894397,6,0,B|300:264|204:264|160:196,1,219.999993443489,4|0 +152,84,1895166,1,8 +256,48,1895422,1,0 +360,84,1895679,1,2 +440,160,1895935,2,0,B|456:212,1,54.9999983608723,0|2 +412,312,1896320,2,0,B|396:260,1,54.9999983608723,0|2 +256,176,1896704,5,4 +208,328,1896961,1,0 +332,232,1897217,1,8 +180,232,1897474,1,0 +304,328,1897730,1,2 +256,256,1897986,1,0 +116,308,1898243,5,8 +80,264,1898371,1,0 +72,208,1898499,1,2 +84,156,1898627,1,0 +116,112,1898756,1,4 +312,12,1899268,5,8 +412,52,1899525,1,0 +324,120,1899781,1,2 +428,156,1900038,1,0 +428,336,1900294,5,8 +360,248,1900551,2,0,B|292:204|220:316|152:268,1,219.999993443489,4|0 +80,184,1901320,1,8 +188,160,1901576,1,0 +192,104,1901704,1,8 +196,48,1901833,1,8 +316,48,1902089,5,8 +320,104,1902217,1,8 +324,160,1902345,1,0 +340,212,1902474,1,8 +376,256,1902602,1,8 +424,280,1902730,1,0 +480,288,1902858,1,4 +256,132,1917730,5,0 +316,192,1917986,1,0 +256,252,1918243,1,8 +196,192,1918499,1,0 +256,92,1918756,5,2 +360,272,1919012,1,0 +152,272,1919268,1,4 +80,232,1919525,6,0,B|93:151,1,82.9999969327451,0|2 +56,47,1920038,2,0,B|16:120,2,82.9999969327451,0|8|0 +136,32,1920807,1,2 +208,68,1921063,1,0 +352,100,1921320,6,0,B|388:112|432:92|432:92|420:140|436:176,1,165.99999386549,4|2 +424,256,1922089,2,0,B|452:288|496:292,2,82.9999969327451,0|8|0 +360,204,1922858,2,0,B|312:272,1,82.9999969327451,2|0 +208,360,1923371,6,0,B|147:316|111:364|49:325,1,165.99999386549,4|2 +60,240,1924140,2,0,B|108:240|132:208,1,82.9999969327451,0|8 +148,88,1924653,2,0,B|152:132|193:153,1,82.9999969327451,0|2 +212,232,1925166,2,0,B|180:308,1,82.9999969327451 +256,336,1925679,1,0 +332,308,1925935,6,0,B|299:231,1,82.9999969327451,0|0 +312,148,1926448,1,8 +404,60,1926704,1,0 +376,180,1926961,1,2 +328,68,1927217,1,0 +436,132,1927474,1,4 +136,180,1927986,5,2 +108,60,1928243,1,0 +200,140,1928499,1,8 +76,132,1928756,1,0 +184,68,1929012,1,2 +136,180,1929268,1,0 +176,296,1929525,6,0,B|340:296,1,165.99999386549,4|2 +400,236,1930294,2,0,B|330:191,1,82.9999969327451,0|8 +256,152,1930807,1,0 +181,191,1931063,2,0,B|112:236,1,82.9999969327451,2|0 +80,128,1931576,6,0,B|112:48|112:48|188:24,1,165.99999386549,4|2 +256,100,1932345,1,0 +324,24,1932602,2,0,B|400:48|400:48|432:128,1,165.99999386549,8|2 +408,204,1933371,1,0 +388,240,1933499,1,0 +356,268,1933627,1,0 +308,200,1933884,1,0 +256,264,1934140,1,0 +204,200,1934397,1,0 +256,136,1934653,1,8 +104,204,1934909,5,0 +124,240,1935038,1,0 +156,268,1935166,1,0 +256,264,1935422,1,0 +256,280,1935679,1,0 +136,128,1950038,6,0,B|134:99|134:99,5,27.6666656442484 +208,128,1950551,2,0,B|208:95|208:95,5,27.6666656442484 +280,128,1951063,2,0,B|280:97|280:97,5,27.6666656442484 +352,128,1951576,2,0,B|352:99|352:99,5,27.6666656442484 +404,184,1952089,6,0,B|384:244|304:256|304:256|216:268,1,219.999993443489,4|8 +104,280,1952858,1,0 +128,328,1952986,2,0,B|188:328|228:364,1,109.999996721745,2|0 +287,368,1953371,2,0,B|328:332|388:332,1,109.999996721745,2|8 +356,228,1953884,6,0,B|384:184|356:124|356:124|384:68|356:16,1,219.999993443489,4|0 +256,60,1954653,1,8 +155,16,1954909,2,0,B|128:68|156:124|156:124|128:184|156:228,1,219.999993443489,2|0 +256,184,1955679,1,8 +448,128,1955935,6,0,B|468:208|416:288|348:296,1,219.999993443489,4|0 +176,200,1956704,1,8 +256,52,1956961,1,0 +336,200,1957217,1,2 +176,104,1957474,1,0 +336,104,1957730,1,8 +256,248,1957986,1,0 +64,127,1958243,6,0,B|44:208|96:288|164:296,1,219.999993443489,4|8 +208,196,1959012,1,2 +256,168,1959140,1,0 +304,196,1959268,1,2 +388,80,1959525,1,0 +256,20,1959781,1,8 +124,80,1960038,1,2 +76,204,1960294,5,4 +84,336,1960551,2,0,B|112:288,1,54.9999983608723,2|0 +160,244,1960807,1,10 +256,152,1961063,2,0,B|256:208,1,54.9999983608723,2|0 +256,272,1961320,1,2 +428,336,1961576,2,0,B|400:288,1,54.9999983608723,2|0 +352,244,1961833,1,10 +256,332,1962089,1,0 +160,244,1962345,5,2 +216,244,1962474,1,0 +272,232,1962602,1,0 +320,204,1962730,1,0 +344,156,1962858,1,8 +348,100,1962986,1,0 +328,48,1963115,1,0 +284,16,1963243,1,0 +228,16,1963371,5,2 +184,48,1963499,1,0 +164,100,1963627,1,0 +168,156,1963756,1,0 +192,204,1963884,1,8 +240,232,1964012,1,0 +296,244,1964140,1,0 +352,244,1964268,1,0 +408,236,1964397,5,4 +408,124,1964653,1,0 +256,148,1964909,1,8 +316,44,1965166,1,2 +196,44,1965422,1,0 +108,208,1965679,5,2 +256,312,1965935,1,8 +404,208,1966192,1,4 +256,192,1966448,12,4,1968499 +76,64,1969012,5,8 +180,104,1969268,1,0 +140,208,1969525,2,0,B|112:260|56:272,2,109.999996721745,2|0|8 +40,160,1970294,6,0,B|72:276|180:320,1,219.999993443489,4|0 +256,192,1971063,1,8 +332,64,1971320,2,0,B|440:108|472:224,1,219.999993443489,2|0 +256,288,1972089,1,8 +40,223,1972345,6,0,B|72:108|180:64,1,219.999993443489,4|0 +200,172,1973115,2,0,B|312:172,2,109.999996721745,8|0|2 +148,268,1973884,1,0 +204,272,1974012,2,0,B|256:288|256:288|308:272,1,109.999996721745,2|0 +364,268,1974397,1,2 +190,203,1974653,6,0,B|116:168|116:64|192:28,1,219.999993443489,4|8 +256,116,1975422,1,0 +320,28,1975679,2,0,B|396:64|396:168|320:204,1,219.999993443489,4|8 +256,292,1976448,1,0 +168,360,1976704,6,0,B|204:284|308:284|344:360,2,219.999993443489,4|8|2 +144,252,1977986,1,0 +40,216,1978243,1,8 +56,324,1978499,1,4 +256,44,1979012,1,0 +456,324,1979525,1,2 +192,244,1980038,5,8 +180,192,1980166,1,0 +204,144,1980294,1,8 +256,124,1980422,1,0 +308,144,1980551,1,2 +332,192,1980679,1,0 +320,244,1980807,1,4 +256,64,1994397,5,2 +336,320,1994825,1,0 +128,160,1995254,1,0 +384,160,1995682,1,0 +176,320,1996111,1,0 +256,192,1996539,1,0 +336,320,1996968,1,2 +336,144,1997397,6,0,B|320:101|256:69|192:101|176:149,1,200 +384,32,1998254,1,2 +256,192,1998682,1,0 +128,32,1999111,1,0 +176,240,1999539,2,0,B|192:283|256:315|320:283|336:235,1,200,2|0 +128,352,2000397,5,0 +256,192,2000825,1,0 +384,352,2001254,1,0 +472,200,2001682,2,0,B|456:96|456:96|384:32,1,200,0|2 +213,100,2002539,2,0,B|192:128|208:176|255:196|304:176|320:128|296:95,1,200 +126,33,2003397,2,0,B|56:96|56:96|40:200,1,200,2|0 +176,352,2004254,6,0,B|200:312|256:296|256:296|312:312|336:352,1,200,0|2 +301,182,2005111,1,0 +213,182,2005325,1,0 +304,24,2005754,2,0,B|349:50|349:50|368:96,1,100,0|2 +208,24,2006397,2,0,B|163:50|163:50|144:96,1,100 +339,233,2007254,6,0,B|380:288|348:384|255:424|157:384|125:288|173:223,1,400,2|0 +86,73,2008539,2,0,B|108:33|161:17|214:22|256:77|256:77|299:21|352:13|416:31|429:79|429:79,1,400,2|0 +336,248,2009825,2,0,B|320:202|256:170|192:202|176:245,1,200,2|0 +176,336,2010468,2,0,B|191:381|255:413|319:381|335:338,1,200 +256,296,2011111,5,2 +336,136,2011539,1,0 +176,136,2011968,1,0 +256,176,2012182,1,0 +336,136,2012397,2,0,B|368:80|336:32|304:16|304:16|256:52|256:52|208:16|208:16|176:32|136:80|180:144,1,400,2|0 +94,276,2013682,2,0,B|134:359|222:383|262:386|300:380|377:360|420:275,1,400,2|0 +256,192,2014968,5,2 +104,112,2015397,1,0 +184,64,2015611,2,0,B|199:18|263:-13|327:18|343:61,1,200 +408,112,2016254,1,2 +256,192,2016682,5,0 +168,360,2017111,1,0 +256,320,2017325,1,0 +340,360,2017539,2,0,B|395:298|408:225|358:155|311:129|254:113|210:125|154:141|108:209|110:255|111:328|188:373,1,600,2|2 +32,240,2019254,2,0,B|32:140,1,100 +104,84,2019682,1,0 +176,248,2020111,2,0,B|200:288|256:304|256:304|312:288|336:248,1,200,2|0 +336,156,2020754,2,0,B|312:116|256:100|256:100|200:116|176:156,1,200 +256,200,2021397,5,2 +356,348,2021825,1,0 +480,240,2022254,2,0,B|480:140,1,100,2|0 +408,84,2022682,1,0 +300,228,2023111,6,0,B|321:256|305:304|258:324|209:304|193:256|217:223,1,200,0|2 +256,148,2023754,1,0 +256,60,2023968,1,0 +100,84,2024397,1,2 +100,84,2024611,1,0 +100,84,2024825,1,0 +175,247,2025254,2,0,B|190:293|254:324|318:293|334:250,2,200,2|0|0 +342,361,2026539,5,2 +256,200,2026968,1,0 +256,200,2027182,1,0 +169,361,2027611,1,0 +169,361,2027825,1,2 +64,203,2028254,6,0,B|92:107|92:107|168:43,1,200,0|2 +343,42,2029111,2,0,B|420:107|420:107|448:203,1,200 +336,339,2029968,1,2 +256,312,2030182,1,0 +176,340,2030397,2,0,B|160:282|194:232|256:224|256:224|296:208|328:160|296:112|256:104|216:112|176:160|216:208|256:224|256:224|320:232|355:286|334:348,1,600,0|2 +440,192,2032111,5,0 +344,40,2032539,1,0 +256,16,2032754,1,0 +168,40,2032968,1,2 +72,192,2033397,1,0 +211,312,2033825,2,0,B|190:340|206:388|253:408|302:388|318:340|294:307,1,200,0|2 +208,156,2034682,5,0 +256,24,2034897,1,0 +304,156,2035111,1,2 +184,72,2035325,1,0 +324,76,2035539,1,0 +408,236,2035968,5,2 +256,340,2036397,1,0 +104,236,2036825,1,2 +172,68,2037254,1,0 +172,68,2037468,1,0 +340,68,2037897,1,0 +340,68,2038111,1,0 +256,200,2038539,5,2 +336,360,2038968,2,0,B|312:320|256:304|256:304|200:320|176:360,1,200,0|2 +104,196,2039825,1,0 +256,100,2040254,1,2 +408,200,2040682,1,0 +256,192,2040897,12,0,2043254 +256,24,2044325,5,2 +301,102,2044539,2,0,B|322:130|306:178|259:198|210:178|194:130|218:97,1,200 +340,232,2045397,1,2 +256,260,2045611,1,0 +172,232,2045825,1,4 +76,360,2046254,1,0 +436,360,2047111,1,2 +334,144,2047539,6,0,B|320:183|265:230|188:199|179:142|179:142,1,200,0|2 +212,52,2048182,2,0,B|224:38|241:23|296:26|305:57|305:57,1,100 +64,336,2049682,5,2 +173,204,2050111,2,0,B|187:236|247:285|320:257|341:200|341:200,1,200,0|2 +374,109,2050754,2,0,B|347:44|270:-14|156:19|137:117|137:117,1,300 +448,336,2052254,1,2 +256,192,2052682,5,0 +192,250,2052897,2,0,B|157:294|184:334|233:346|256:312|256:312|282:348|328:336|364:292|309:241,2,300,0|2|0 +56,324,2054611,1,0 +56,324,2054825,5,2 +176,96,2055254,2,0,B|200:56|256:40|256:40|312:56|336:96,1,200,0|2 +256,144,2055897,1,0 +336,188,2056111,2,0,B|312:228|256:244|256:244|200:228|176:188,1,200 +344,356,2057397,2,0,B|312:320|276:336|256:356|256:356|236:332|192:320|160:368,1,200,2|0 +256,264,2058039,1,0 +359,34,2058682,6,0,B|413:71|379:165|309:209|236:120|188:73|200:24|245:-2|264:-12|283:-2|302:16|321:73|283:120|231:211|109:185|106:73|123:44|171:20,1,600,2|2 +256,208,2060397,1,0 +212,372,2060825,1,2 +300,372,2061039,1,0 +136,168,2061682,6,0,B|163:233|240:291|354:258|373:160|373:160,1,300,0|0 +337,73,2062539,2,0,B|323:41|263:-8|190:20|169:77|169:77,1,200,2|0 +254,129,2063182,1,0 +176,352,2063825,1,2 +256,316,2064039,1,0 +336,352,2064254,1,0 +256,192,2064468,12,0,2065968 +46,270,2066397,6,0,B|106:194|106:194|46:110,1,200,2|0 +299,34,2067254,2,0,B|320:62|304:110|257:130|208:110|192:62|216:29,1,200,2|0 +454,270,2068111,2,0,B|394:194|394:194|454:110,1,200,2|0 +298,350,2068968,2,0,B|319:322|303:274|256:254|207:274|191:322|215:355,1,200,4|0 +480,60,2070254,6,0,B|480:107|412:116|391:71|391:71|372:26|320:26|301:71|301:71|284:116|230:116|211:71|211:71|194:26|140:26|123:71|123:71|99:116|33:107|33:58,2,600 +256,200,2073254,5,0 +168,360,2073682,1,0 +340,360,2074111,2,0,B|395:298|408:225|358:155|311:129|254:113|210:125|154:141|108:209|110:255|111:328|188:373,2,600,8|8|8 +472,200,2077325,6,0,B|472:100,1,100,8|8 +392,56,2077754,1,0 +336,128,2077968,2,0,B|312:168|256:184|256:184|200:168|176:128,1,200,8|0 +40,200,2078825,2,0,B|40:100,1,100,8|0 +116,52,2079254,1,4 +177,271,2079682,6,0,B|201:231|257:215|257:215|313:231|337:271,1,200 +299,355,2080325,2,0,B|283:372|256:377|256:377|227:372|213:354,1,100,0|8 +144,90,2080968,6,0,B|153:55|199:44|221:77|199:100|209:146|242:133|255:48|255:48|265:133|298:146|310:100|288:77|310:44|364:50|366:88,1,400,0|8 +340,268,2082254,1,0 +256,304,2082468,1,8 +172,268,2082682,1,0 +328,360,2083111,5,8 +256,196,2083539,1,0 +184,360,2083968,1,0 +112,194,2084397,2,0,B|172:135|127:69|127:69|216:94|257:0|257:0|292:94|382:69|382:69|339:136|399:194,2,600,8|8|8 +206,348,2087397,6,0,B|310:348,1,100,8|8 +355,271,2087825,2,0,B|154:271,1,200,8|8 +208,40,2088682,6,0,B|312:40,1,100,8|8 +357,117,2089111,2,0,B|156:117,1,200,8|4 +52,260,2089968,1,0 +212,344,2090397,2,0,B|224:358|241:373|296:370|305:339|305:339,1,100,0|2 +256,268,2090825,1,8 +460,260,2091254,1,0 +416,84,2091682,5,2 +352,148,2091897,1,0 +328,60,2092111,1,8 +160,148,2092539,1,0 +96,84,2092754,1,8 +184,60,2092968,1,0 +129,290,2093397,6,0,B|157:341|243:400|353:360|384:290|384:290,1,300,8|0 +300,228,2094254,2,0,B|288:242|271:257|216:254|207:223|207:223,1,100,2|0 +256,144,2094682,1,8 +148,44,2095111,6,0,B|72:112|72:112|56:208,1,200 +80,288,2095754,1,0 +168,324,2095968,2,0,B|192:360|232:360|256:344|256:344|276:360|320:360|344:324,1,200,8|0 +456,208,2096825,2,0,B|440:112|440:112|364:44,1,200,2|8 +256,192,2097682,1,0 +301,268,2097897,2,0,B|256:288|256:288|210:268,1,100,8|0 +48,344,2098539,5,8 +192,48,2099182,2,0,B|160:89|184:161|254:190|327:161|351:89|315:40,1,300,2|8 +464,344,2100468,1,0 +256,272,2100897,5,2 +256,360,2101111,1,8 +172,208,2101539,2,0,B|186:176|246:127|319:155|340:212|340:212,1,200,0|0 +123,97,2102397,2,0,B|158:52|257:5|353:49|389:100,1,300,8|8 +388,99,2103254,1,0 +332,296,2103682,1,8 +256,340,2103897,1,0 +180,296,2104111,1,0 +176,208,2104325,1,0 +256,160,2104539,1,0 +336,208,2104754,1,0 +256,248,2104968,5,8 +256,36,2105611,1,0 +256,248,2106039,5,2 +176,304,2106254,2,0,B|200:344|256:360|256:360|312:344|336:304,1,200,8|0 +411,139,2107111,2,0,B|370:42,1,100,2|0 +299,99,2107539,2,0,B|320:127|304:175|257:195|208:175|192:127|216:94,1,200,8|0 +139,46,2108182,2,0,B|101:139,1,100,8|0 +128,288,2108825,2,0,B|256:368|256:368|384:288,1,300,8|8 +300,232,2109682,2,0,B|256:256|256:256|212:232,1,100,8|0 +256,152,2110111,1,4 +108,52,2110539,5,2 +204,204,2110968,2,0,B|210:255|210:255|254:287|302:255|302:255|306:203,1,200,0|8 +412,60,2111825,1,2 +412,60,2112039,1,0 +440,224,2112468,1,0 +440,224,2112682,2,0,B|416:324|416:324|328:368|328:368,1,200,8|2 +184,368,2113539,2,0,B|97:324|97:324|73:224,1,200,0|8 +176,76,2114397,2,0,B|200:116|256:132|256:132|312:116|336:76,1,200,2|8 +256,32,2115039,1,0 +256,32,2115254,5,4 +432,192,2115682,2,0,B|404:171|356:187|336:234|356:283|404:299|437:275,1,200,2|0 +256,360,2116539,1,8 +80,276,2116968,2,0,B|108:297|156:281|176:234|156:185|108:169|75:193,1,200,2|0 +176,40,2117825,5,8 +336,40,2118254,1,0 +256,96,2118468,1,8 +176,40,2118682,1,0 +256,200,2119111,1,8 +208,348,2119539,5,8 +176,260,2119754,1,8 +256,200,2119968,1,8 +336,260,2120182,1,0 +304,348,2120397,1,12 +348,72,2120825,6,0,B|288:72|288:72|256:48|256:48|224:72|224:72|164:72,1,200,2|0 +160,352,2121682,6,0,B|128:338|79:278|107:205|164:184|164:184,1,200,8|2 +256,172,2122325,2,0,B|256:372,1,200,0|2 +352,352,2122968,2,0,B|384:338|433:278|405:205|348:184|348:184,1,200,8|0 +256,32,2123825,5,2 +96,152,2124254,1,8 +164,336,2124682,1,2 +348,336,2125111,1,0 +416,152,2125539,1,0 +256,192,2125647,12,0,2127682 +348,80,2127897,6,0,B|288:80|288:80|256:56|256:56|224:80|224:80|164:80,1,200,2|0 +172,288,2128754,2,0,B|186:256|246:207|319:235|340:292|340:292,1,200,0|0 +256,344,2129397,1,8 +192,152,2129825,2,0,B|156:56,1,100,8|0 +256,68,2130254,1,8 +320,152,2130468,2,0,B|356:56,1,100,0|4 +426,311,2131111,6,0,B|404:351|351:367|298:362|256:307|256:307|213:363|160:371|96:353|83:305|83:305,1,400,0|8 +256,192,2132397,5,0 +340,32,2132825,1,2 +256,64,2133039,1,0 +172,32,2133254,1,8 +256,284,2133897,1,8 +182,323,2134111,2,0,B|256:387|256:387|336:323,1,200,0|8 +320,152,2134968,2,0,B|292:208|292:208|256:188|220:208|220:208|196:152,1,200 +144,76,2135611,2,0,B|196:64|232:20|232:20|256:56|256:56|276:20|276:20|320:64|368:76,1,300 +404,252,2136682,5,0 +256,356,2137111,1,8 +108,252,2137539,1,0 +164,172,2137754,2,0,B|224:140|256:172|304:220|288:268|224:268|208:220|256:172|288:140|348:172,2,300,0|2|0 +256,28,2139468,5,0 +256,28,2139682,1,8 +396,140,2140111,1,8 +344,216,2140325,1,8 +256,240,2140539,1,0 +168,216,2140754,1,8 +116,140,2140968,1,12 +113,306,2141397,2,0,B|128:352|192:368|224:336|224:288|184:272|152:320|204:368|256:368|256:368|308:368|360:320|328:272|288:288|288:336|324:368|400:344|403:296,1,456.000004348755,0|8 +256,144,2142682,1,0 +256,56,2142897,1,0 +344,360,2143539,6,0,B|294:310|319:215|378:210|418:265|378:315|304:290|254:225|254:225|204:290|129:315|89:265|129:210|189:215|214:310|164:360,1,600,0|8 +172,68,2145254,2,0,B|186:36|246:-13|319:15|340:72|340:72,1,200,0|2 +373,163,2145897,2,0,B|346:228|269:286|155:253|136:155|136:155,2,300,0|0|2 +440,224,2147397,5,8 +213,335,2147825,2,0,B|236:352|256:360|256:360|277:351|304:331,1,100,0|2 +76,228,2148682,1,8 +256,192,2148789,12,2,2151254 +60,85,2151682,6,0,B|32:134|31:182,1,100,2|8 +120,156,2152111,2,0,B|98:269|152:358,1,200,0|8 +451,298,2152968,6,0,B|479:249|480:201,1,100,8|8 +391,227,2153397,2,0,B|413:114|359:25,1,200,8|4 +256,192,2154254,5,0 +33,334,2155111,6,0,B|33:287|101:278|122:323|122:323|141:368|193:368|212:323|212:323|229:278|283:278|302:323|302:323|319:368|373:368|390:323|390:323|414:278|480:287|480:336,1,600,8|8 +256,152,2157039,1,8 +176,108,2157254,2,0,B|192:65|256:33|320:65|336:113,2,200,8|8|8 +256,264,2158539,1,8 +80,336,2158968,6,0,B|132:368|160:336|176:320|176:320|192:336|208:352|232:344|232:344|256:368|280:344|280:344|304:352|320:336|336:320|336:320|352:336|380:368|432:336,1,400,8|0 +340,172,2160254,2,0,B|381:116|349:21|256:-18|159:21|127:116|175:181,1,400,8|0 +157,353,2161539,2,0,B|358:353,1,200,8|0 +306,276,2162182,2,0,B|202:276,1,100,8|0 +56,176,2162825,5,8 +157,31,2163254,2,0,B|358:31,1,200,0|8 +306,108,2163897,2,0,B|202:108,1,100,0|4 +256,280,2164539,1,0 +456,176,2164968,5,0 +380,228,2165182,1,0 +464,268,2165397,1,8 +300,364,2165825,2,0,B|321:336|305:288|258:268|209:288|193:336|217:369,1,200 +256,192,2166682,1,8 +56,176,2167111,1,0 +132,228,2167325,1,8 +48,264,2167539,1,0 +256,192,2167968,5,8 +300,20,2168397,2,0,B|321:48|305:96|258:116|209:96|193:48|217:15,1,200 +97,157,2169254,2,0,B|128:223|176:255|256:301|335:255|383:223|415:160,1,400,8|0 +437,320,2170539,2,0,B|368:384|256:416|139:386|72:316,1,400,8|0 +165,176,2171825,2,0,B|195:207|256:237|316:207|346:176,1,200,8|0 +352,88,2172468,2,0,B|296:0,1,100,8|0 +213,5,2172897,2,0,B|159:89,1,100,8|8 +48,232,2173539,5,8 +24,320,2173754,1,0 +104,360,2173968,1,8 +176,304,2174182,1,0 +136,224,2174397,1,4 +76,56,2174825,2,0,B|128:20|112:100|207:51|191:51|191:51|223:83|255:50|255:50|287:83|330:51|314:51|314:51|384:100|384:20|442:59,1,400,0|8 +464,232,2176111,5,8 +488,320,2176325,1,0 +408,360,2176539,1,8 +336,304,2176754,1,0 +376,224,2176968,1,8 +312,56,2177397,2,0,B|416:104|416:104,1,100 +200,56,2178039,2,0,B|96:104|96:104,1,100,0|8 +176,264,2178682,2,0,B|232:272|256:216|256:216|280:272|336:264,1,200 +344,360,2179325,1,0 +344,360,2179539,2,0,B|312:396|276:380|256:360|256:360|236:384|192:396|160:348,2,200,4|0|0 +367,36,2180825,6,0,B|398:21|475:23|512:92|491:149|491:149,1,200,8|2 +456,240,2181468,2,0,B|392:271|296:264|232:164|282:78|282:78,1,300,0|2 +256,336,2182754,1,0 +256,336,2182968,1,0 +256,152,2183397,1,8 +145,36,2183825,2,0,B|114:21|37:23|0:92|21:149|21:149,1,200,8|2 +56,240,2184468,2,0,B|120:271|216:264|280:164|230:78|230:78,1,300,0|2 +388,164,2185539,5,0 +256,360,2185968,1,8 +124,164,2186397,1,2 +180,308,2186825,1,0 +256,272,2187039,1,0 +332,308,2187254,1,8 +256,24,2187897,5,8 +256,216,2188325,1,0 +336,172,2188539,2,0,B|312:132|256:116|256:116|200:132|176:172,2,200,8|0|0 +428,337,2189825,2,0,B|397:384|334:368|272:337|319:306|319:259|303:243|256:228|209:243|194:259|194:306|241:337|178:368|116:384|85:337,1,456.000004348755,4|0 +16,176,2191111,2,0,B|59:186|103:161|113:118|113:118|114:83|115:49|81:30|46:46|28:80|44:116|78:117|113:118|113:118|157:111|184:69|177:25,1,456.000004348755,8|0 +334,24,2192397,2,0,B|327:69|354:111|398:118|398:118|433:117|467:116|483:80|465:46|430:30|396:49|397:83|398:118|398:118|408:161|452:186|496:176,1,456.000004348755,8|0 +464,254,2193468,1,0 +404,328,2193682,5,8 +256,192,2194111,1,0 +176,124,2194325,1,8 +208,36,2194539,1,8 +304,36,2194754,1,0 +336,124,2194968,1,12 +96,226,2195397,2,0,B|112:308|179:376|252:395|355:368|404:296|422:226,1,456.000004348755,0|8 +256,104,2196682,5,0 +44,48,2197111,1,0 +104,116,2197325,1,0 +176,56,2197539,2,0,B|192:13|256:-19|320:13|336:61,1,200,8|0 +336,148,2198182,2,0,B|320:191|256:223|192:191|176:143,1,200,8|0 +256,104,2198825,5,8 +164,300,2199254,1,0 +348,300,2199682,1,0 +256,192,2200111,12,8,2201397 +256,192,2201504,12,8,2202682 +256,192,2202789,12,4,2205254 +384,80,2225825,5,4 +128,80,2226468,1,2 +132,308,2227111,1,8 +384,308,2227754,1,2 +256,192,2228182,5,0 +336,148,2228397,1,8 +336,60,2228611,1,0 +256,24,2228825,1,2 +176,60,2229039,1,0 +176,148,2229254,1,0 +256,192,2229468,1,0 +320,256,2229682,2,0,B|351:297|327:368|257:398|185:368|161:297|196:248,1,300,8|2 +108,228,2230539,5,0 +72,208,2230682,1,0 +52,168,2230825,1,0 +56,128,2230968,2,0,B|64:76|112:28|176:36|212:76|212:76,1,200,8|0 +288,120,2231611,2,0,B|348:140|388:104|388:104,1,100,2|0 +468,96,2232039,1,0 +440,180,2232254,2,0,B|395:224|395:268|395:313|440:357,1,200,8|2 +256,364,2233111,2,0,B|256:156,1,200,0|8 +72,180,2233968,2,0,B|116:224|116:268|116:313|72:357,2,200,2|0|8 +48,136,2234968,1,0 +56,88,2235111,1,0 +88,60,2235254,2,0,B|112:32|148:32|172:68|172:68|196:100|236:104|260:72,1,200,2|0 +344,48,2235897,5,0 +428,80,2236111,2,0,B|496:88|508:160|492:220|440:240|372:224|372:224,1,200,4|0 +452,308,2236754,2,0,B|404:328|344:296|328:244|328:204|356:176|356:176,1,200,2|0 +336,100,2237397,2,0,B|312:60|256:44|256:44|200:60|176:100,1,200,8|0 +164,188,2238039,2,0,B|156:176|184:204|184:244|168:296|108:328|60:308,1,200,2|0 +76,220,2238682,1,8 +88,132,2238897,1,0 +172,100,2239111,5,2 +212,109,2239218,1,0 +245,142,2239325,1,0 +304,208,2239539,1,0 +388,172,2239754,1,0 +468,212,2239968,2,0,B|500:248|500:304|468:364|388:344|388:344,1,200,8|2 +368,328,2240539,1,0 +345,310,2240682,1,0 +314,311,2240825,1,0 +285,326,2240968,1,0 +252,331,2241111,1,0 +230,317,2241254,2,0,B|193:288|193:230|252:200|252:200|310:171|310:113|252:80,1,300,4|2 +176,48,2242111,6,0,B|136:92|80:76|80:76,1,100,0|0 +80,156,2242539,1,8 +168,324,2242968,5,2 +344,324,2243397,1,0 +432,156,2243825,1,4 +256,32,2244254,1,2 +176,76,2244468,2,0,B|192:119|256:151|320:119|336:71,1,200,8|0 +407,131,2245111,5,0 +388,154,2245218,1,0 +366,175,2245325,1,0 +341,192,2245432,1,0 +313,202,2245539,1,0 +283,210,2245647,1,0 +256,212,2245754,1,0 +229,210,2245861,1,0 +199,202,2245968,1,0 +171,192,2246075,1,0 +146,175,2246182,1,0 +124,154,2246289,1,0 +105,131,2246397,1,4 +52,240,2246825,5,2 +140,320,2247254,1,0 +256,348,2247682,1,8 +372,320,2248111,1,2 +460,240,2248539,1,0 +488,156,2248754,1,0 +404,172,2248968,6,0,B|320:128|320:128|304:20,1,200,8|2 +207,23,2249611,1,0 +207,23,2249825,2,0,B|192:128|192:128|108:172,1,200,0|8 +336,292,2250682,6,0,B|322:324|262:373|189:345|168:288|168:288,1,200,2|0 +135,197,2251325,2,0,B|162:132|239:74|353:107|372:205|372:205,1,300,2|0 +152,36,2252397,1,2 +360,36,2252825,1,8 +256,192,2253254,5,0 +192,250,2253468,2,0,B|157:294|184:334|233:346|256:312|256:312|282:348|328:336|364:292|309:241,2,300,2|8|0 +56,324,2255182,5,2 +56,324,2255397,1,8 +176,96,2255825,2,0,B|200:56|256:40|256:40|312:56|336:96,1,200,2|0 +256,144,2256468,1,0 +336,188,2256682,2,0,B|312:228|256:244|256:244|200:228|176:188,1,200,4|0 +456,324,2257539,1,0 +256,364,2257968,1,8 +56,324,2258397,1,2 +92,232,2258611,6,0,B|140:232|152:194|150:166|150:166|182:167|220:143|206:87,1,200,0|0 +303,101,2259254,2,0,B|298:142|329:167|361:166|361:166|359:194|371:232|420:232,1,200,2|0 +376,312,2259897,2,0,B|324:324|324:324|300:372,1,100,8|0 +208,365,2260325,2,0,B|188:324|188:324|136:312,1,100,0|8 +56,128,2260968,5,2 +256,104,2261397,1,0 +304,20,2261611,1,0 +208,20,2261825,1,8 +372,132,2262254,2,0,B|345:197|268:255|154:222|135:124|135:124,1,300,2|0 +56,188,2263111,2,0,B|76:240|144:300|220:308,1,200,8|0 +304,306,2263754,2,0,B|372:294|436:240|456:188,1,200,2|0 +456,187,2264397,1,2 +256,192,2264504,12,8,2265682 +256,192,2265789,12,10,2266968 +184,124,2267397,5,8 +256,76,2267611,1,8 +328,124,2267825,1,8 +304,212,2268039,1,0 +212,212,2268254,1,8 +256,352,2268682,5,8 +80,68,2269111,1,8 +432,64,2269539,1,12 +466,303,2269968,2,0,B|466:182|332:145|256:220|256:220|142:315|199:391|256:411|313:391|370:315|256:220|256:220|180:145|9:172|46:321|46:321,1,1000,0|8 +256,32,2272754,1,8 +176,76,2272968,2,0,B|192:119|256:151|320:119|336:71,1,200,8|8 +256,200,2273825,5,0 +168,360,2274254,1,0 +340,360,2274682,2,0,B|395:298|408:225|358:155|311:129|254:113|210:125|154:141|108:209|110:255|111:328|188:373,2,600,8|8|8 +472,200,2277897,6,0,B|472:100,1,100,8|8 +392,56,2278325,1,8 +336,128,2278539,2,0,B|312:168|256:184|256:184|200:168|176:128,1,200,4|0 +40,200,2279397,2,0,B|40:100,1,100,2|0 +116,52,2279825,1,4 +177,271,2280254,6,0,B|201:231|257:215|257:215|313:231|337:271,1,200 +299,355,2280897,2,0,B|283:372|256:377|256:377|227:372|213:354,1,100,0|8 +144,90,2281539,6,0,B|153:55|199:44|221:77|199:100|209:146|242:133|255:48|255:48|265:133|298:146|310:100|288:77|310:44|364:50|366:88,1,400,0|8 +340,268,2282825,5,0 +256,304,2283039,1,8 +172,268,2283254,1,0 +328,360,2283682,5,8 +256,196,2284111,1,0 +184,360,2284539,1,0 +256,196,2284968,5,4 +256,192,2285075,12,0,2286254 +256,192,2286361,12,0,2287539 +256,192,2287647,12,0,2290111 +192,144,2308992,6,0,B|160:96|96:80,2,100,2|0|0 +256,112,2309663,2,0,B|256:0,2,100,2|0|0 +320,144,2310335,2,0,B|352:96|416:80,2,100,2|0|0 +256,192,2311006,1,0 +176,304,2311678,2,0,B|208:352|256:368|256:368|304:352|336:304,1,200,4|4 +416,128,2312574,6,0,B|416:64|352:48,2,100,2|0|0 +256,144,2313245,2,0,B|256:32,2,100,2|0|0 +96,128,2313917,2,0,B|96:64|160:48,2,100,2|0|0 +256,144,2314589,1,0 +164,336,2315260,2,0,B|208:305|256:345|256:345|304:305|348:335|348:335,1,200,4|4 +256,256,2316156,5,0 +208,176,2316380,1,0 +112,192,2316603,1,2 +64,112,2316827,2,0,B|112:96|144:48,2,100,0|8|0 +192,112,2317499,2,0,B|256:32|384:48,1,200,2|0 +352,128,2318171,1,0 +336,208,2318394,2,0,B|336:272|400:304,1,100,2|0 +464,304,2318842,2,0,B|464:240|400:208,1,100,8|0 +352,128,2319290,5,2 +208,80,2319514,1,0 +112,192,2319738,2,0,B|64:224|80:288,1,100 +256,256,2320186,2,0,B|208:240|144:256,1,100,2|0 +112,192,2320633,1,8 +80,112,2320857,1,0 +160,80,2321081,2,0,B|256:128|352:80,1,200,2|0 +400,160,2321753,5,0 +352,240,2321977,2,0,B|351:240|307:270|259:230|259:230|211:270|167:239,1,200,2|8 +96,176,2322648,1,0 +64,256,2322872,2,0,B|64:304|80:368|176:368|208:352,1,200,2|4 +192,272,2323544,5,0 +256,208,2323768,1,2 +320,272,2323991,1,0 +311,354,2324215,2,0,B|336:368|432:368|448:304|448:256,1,200,8|0 +416,176,2324887,2,0,B|384:112|416:48,2,100,0|0|0 +320,192,2325559,1,2 +224,192,2325783,2,0,B|224:256|176:288,1,100,0|8 +160,128,2326230,2,0,B|160:64|112:32,1,100,0|2 +224,192,2326678,1,0 +160,128,2326902,1,4 +64,128,2327126,5,0 +192,48,2327350,1,2 +224,192,2327574,1,0 +304,112,2327797,2,0,B|400:48|512:80,1,200,8|2 +464,160,2328469,2,0,B|432:208|432:272,2,100,0|0|0 +464,336,2329141,1,2 +384,304,2329365,1,0 +304,256,2329589,2,0,B|256:288|208:256,1,100,8|0 +124,304,2330036,2,0,B|188:96,1,200,2|4 +156,16,2330708,6,0,B|188:128,1,100,0|2 +272,128,2331156,1,0 +352,176,2331380,2,0,B|304:288|368:384,1,200,8|0 +153,368,2332051,2,0,B|208:288|160:176,1,200 +64,176,2332723,2,0,B|96:224|96:272,1,100,2|0 +192,84,2333171,2,0,B|160:132|160:180,1,100,8|0 +256,192,2333618,1,2 +160,336,2334066,6,0,B|256:384|352:336,2,200,4|0|8 +160,256,2335186,1,2 +160,256,2335409,2,0,B|256:320|352:256,1,200,4|0 +344,168,2336081,6,0,B|360:116|332:72,1,100,0|4 +256,144,2336529,1,0 +256,144,2336641,1,0 +256,144,2336753,1,12 +192,80,2336977,1,0 +128,144,2337200,2,0,B|160:192|128:240,1,100,8|0 +240,336,2337648,6,0,B|192:240|256:144,1,200,4|2 +320,96,2338320,2,0,B|368:64|416:64,2,100,0|8|0 +380,164,2338991,1,0 +380,164,2339215,2,0,B|300:248|180:204,1,200,2|2 +284,108,2339887,1,8 +284,108,2340111,2,0,B|304:220|244:304,1,200,8|2 +160,236,2340783,1,2 +64,288,2341006,6,0,B|48:240|64:176,1,100,0|4 +48,16,2341454,2,0,B|64:64|48:128,1,100,0|2 +128,144,2341902,2,0,B|240:112,1,100,0|8 +304,128,2342350,1,0 +256,304,2342797,6,0,B|336:384|464:352,2,200,0|0|0 +381,253,2343917,2,0,B|381:189|333:157,1,100,8|0 +253,205,2344365,2,0,B|269:253|253:317,1,100,2|0 +173,253,2344812,5,4 +32,128,2345036,2,0,B|32:64|80:32,1,100,0|2 +160,80,2345484,2,0,B|144:128|160:192,1,100,0|8 +240,192,2345932,1,0 +288,112,2346156,1,2 +368,272,2346380,6,0,B|384:208|336:176,1,100,0|0 +288,256,2346827,2,0,B|272:320|320:352,1,100,0|2 +288,256,2347275,1,0 +352,176,2347499,1,8 +392,80,2347723,2,0,B|320:80|256:32|256:32|192:80|104:80,1,300,0|4 +80,160,2348618,5,0 +64,256,2348842,2,0,B|192:240,1,100,2|0 +224,320,2349290,2,0,B|96:336,1,100,8|0 +346,300,2349738,2,0,B|273:227|161:243,1,200,2|0 +176,144,2350409,1,0 +224,64,2350633,5,2 +400,48,2350857,1,0 +336,192,2351081,1,0 +256,80,2351305,5,0 +352,64,2351529,1,0 +320,160,2351753,1,0 +240,256,2351977,6,0,B|144:176|208:48,1,224.999993294478,4|2 +140,112,2352872,2,0,B|120:204|27:226,1,149.999995529652,8|0 +48,220,2353544,1,0 +88,300,2353768,1,2 +168,344,2353991,1,8 +256,364,2354215,2,0,B|336:348,1,74.9999977648259,0|0 +420,344,2354663,6,0,B|356:280|404:200,1,149.999995529652,8|2 +464,144,2355335,1,0 +384,108,2355559,2,0,B|316:68|240:120,1,150.000005722046,0|2 +260,208,2356230,1,0 +260,208,2356454,2,0,B|276:112|212:48|132:32|68:80,1,299.999991059304,10|0 +132,144,2357574,6,0,B|168:140|184:188,2,75.0000028610231,8|2|8 +140,328,2358245,2,0,B|140:232|252:200,1,149.999995529652,8|2 +216,212,2358917,1,8 +372,328,2359141,2,0,B|372:232|260:200,1,149.999995529652,4|2 +256,132,2359812,1,0 +184,80,2360036,2,0,B|256:48|256:48|328:80,2,149.999995529652,8|2|0 +108,128,2361156,1,8 +96,216,2361380,6,0,B|224:312,1,149.999995529652,2|8 +256,224,2362051,1,2 +416,216,2362275,2,0,B|288:312,2,149.999995529652,0|4|2 +420,128,2363394,1,8 +336,92,2363618,2,0,B|288:44|224:28|144:44|96:92|80:156,2,299.999991059304,8|0|8 +288,176,2365633,5,0 +160,256,2365857,1,4 +304,320,2366081,1,0 +288,176,2366305,5,6 +224,80,2366529,2,0,B|160:112|96:96,1,100,2|0 +48,48,2366977,2,2,B|16:96|48:160,1,100,2|0 +128,176,2367424,2,2,B|144:240|112:272,1,100,0|2 +160,368,2367872,2,2,B|240:288|224:176,1,200 +128,176,2368544,5,0 +227,187,2368768,2,2,B|259:139|307:123,1,100 +464,48,2369215,2,0,B|432:80|368:96,1,100,2|0 +368,192,2369663,2,2,B|480:224|480:352,1,200,6|2 +384,320,2370335,5,0 +288,336,2370559,1,2 +192,352,2370783,1,0 +96,352,2371006,2,0,B|128:304|96:256,1,100,2|0 +97,258,2371454,2,2,B|0:192|48:64,1,200 +144,80,2372126,1,0 +240,112,2372350,5,0 +336,80,2372574,1,2 +416,128,2372797,1,0 +416,32,2373021,1,2 +416,128,2373245,2,2,B|320:192|304:288,1,200,4|0 +384,320,2373917,2,0,B|288:256|160:304,1,200,2|0 +112,336,2374589,1,0 +80,256,2374812,1,0 +80,256,2375036,2,2,B|48:128|144:80,1,200,2|2 +208,144,2375708,6,2,B|256:160|320:144,1,100,0|2 +432,80,2376156,2,2,B|381:66|333:82,1,100 +240,64,2376603,1,0 +144,64,2376827,2,2,B|64:128|80:272,1,200,6|0 +32,176,2377499,1,0 +32,176,2377723,2,2,B|112:256|32:384,1,200,6|0 +120,296,2378394,2,2,B|196:196|325:267,1,200,0|4 +480,296,2379066,2,0,B|428:304|380:276,1,100 +432,96,2379514,5,0 +336,192,2379738,1,4 +472,220,2379962,1,0 +428,112,2380186,1,4 +352,184,2380409,1,0 +456,208,2380633,1,4 +256,192,2381081,6,2,B|160:112,1,100 +96,112,2381529,1,2 +96,112,2381753,1,0 +48,192,2381977,1,2 +48,192,2382200,2,2,B|204:316,1,200 +288,320,2382872,2,0,B|352:304|352:240,2,100,0|0|2 +288,320,2383544,1,0 +256,144,2383768,6,0,B|256:192|288:256,2,100,2|0|4 +192,64,2384439,2,2,B|184:121|126:149,1,100,2|2 +32,144,2384887,2,0,B|80:192|64:272,1,100,2|0 +128,320,2385335,1,2 +32,320,2385559,1,0 +128,320,2385783,6,2,B|176:224|304:256,1,200,6|0 +416,240,2386454,2,0,B|360:136|240:176,1,200,0|0 +188,92,2387126,1,4 +288,48,2387350,1,0 +368,112,2387574,1,0 +264,168,2387797,5,6 +368,112,2388021,2,2,B|432:96|480:32,2,100,0|2|2 +331,208,2388693,2,2,B|224:272|203:384,1,200,0|2 +208,358,2389365,1,0 +304,360,2389589,2,2,B|277:248|186:198,1,200,6|2 +128,128,2390260,1,0 +164,48,2390484,2,0,B|208:17|256:57|256:57|304:17|348:47|348:47,1,200,2|0 +384,128,2391156,1,2 +128,128,2391603,1,4 +256,192,2391827,12,0,2394066 +256,48,2401678,5,0 +256,48,2401790,1,0 +256,48,2401902,1,0 +256,48,2402014,1,0 +256,48,2402126,1,4 +208,112,2402350,6,0,B|256:144|304:112,1,100,0|2 +344,176,2402797,1,0 +344,176,2403021,2,0,B|312:208|256:224|192:208|152:168,1,200,8|0 +64,160,2403693,1,2 +32,240,2403917,1,0 +160,176,2404141,2,0,B|224:176|256:128,1,100,0|2 +160,96,2404589,2,0,B|128:48|160:-16,1,100,0|8 +240,32,2405036,2,0,B|309:113|453:97,1,200 +432,192,2405708,1,4 +384,272,2405932,5,0 +336,352,2406156,1,2 +256,304,2406380,2,0,B|256:192,1,100,0|8 +176,176,2406827,1,0 +176,176,2407051,2,0,B|192:48|64:32,2,200,2|0|2 +256,144,2408171,1,2 +256,144,2408394,2,0,B|272:32|400:32,1,200,8|2 +448,208,2409066,1,0 +448,208,2409290,6,0,B|432:144|448:96,1,100,4|0 +352,144,2409738,2,0,B|368:192|352:256,1,100,2|0 +272,288,2410186,2,0,B|256:224|272:176,1,100,8|0 +176,224,2410633,2,0,B|192:272|176:336,1,100,2|0 +96,320,2411081,5,0 +64,224,2411305,1,0 +80,128,2411529,2,0,B|176:64,1,100,2|0 +256,96,2411977,1,8 +353,76,2412200,2,0,B|449:12,1,100,2|0 +416,144,2412648,1,2 +416,144,2412872,6,0,B|464:240|416:352,2,200,8|0|8 +336,176,2413991,1,0 +240,176,2414215,1,2 +288,256,2414439,1,0 +240,176,2414663,1,0 +144,176,2414887,1,0 +192,256,2415111,1,10 +240,176,2415335,5,0 +240,176,2415447,1,0 +240,176,2415559,1,12 +144,48,2415783,1,0 +336,48,2416006,1,8 +240,176,2416230,1,0 +240,176,2416454,1,4 +304,240,2416678,5,0 +352,320,2416902,2,0,B|384:272|448:256,1,100,2|0 +432,176,2417350,2,0,B|320:144|320:16,1,200,8|2 +240,48,2418021,2,0,B|208:160|64:160,1,200,0|2 +48,256,2418693,1,2 +96,352,2418917,1,0 +96,352,2419141,2,0,B|144:240|256:240,2,200,8|0|4 +208,352,2420260,5,0 +304,352,2420484,1,2 +400,352,2420708,1,0 +400,352,2420932,2,0,B|352:240|240:240,1,200,8|0 +176,192,2421603,5,0 +144,112,2421827,1,2 +240,128,2422051,1,0 +240,128,2422275,2,0,B|352:128|416:16,1,200,0|8 +464,144,2422947,1,0 +416,272,2423171,1,2 +256,352,2423618,5,4 +160,336,2423842,2,0,B|112:240|176:144,1,200 +112,96,2424514,2,0,B|160:64|208:64,2,100,8|0|2 +288,80,2425186,1,0 +368,128,2425409,1,0 +352,224,2425633,1,0 +432,352,2425857,6,0,B|416:304|448:256,1,100,2|0 +256,208,2426305,2,0,B|256:320,1,100,8|0 +80,352,2426753,2,0,B|96:304|64:256,1,100,2|0 +64,160,2427200,1,4 +144,112,2427424,2,0,B|272:80,2,100,0|2|0 +192,192,2428096,2,0,B|208:320,1,100,8|0 +307,291,2428544,2,0,B|320:192,1,100,2|0 +256,96,2428991,5,0 +208,176,2429215,1,8 +304,176,2429439,1,0 +256,64,2429663,1,0 +176,176,2429887,1,4 +336,176,2430111,1,0 +388,272,2430335,1,0 +256,304,2430559,1,0 +128,272,2430783,6,0,B|128:144|48:64,1,224.999993294478,4|0 +168,88,2431678,2,0,B|252:100|312:40,1,150.000005722046,8|0 +396,40,2432350,1,8 +452,108,2432574,1,0 +416,192,2432797,1,0 +328,176,2433021,1,8 +320,88,2433245,1,0 +416,192,2433469,6,0,B|467:195|491:221,1,74.9999977648259,8|0 +420,280,2433917,2,0,B|384:364|288:344,1,150.000005722046,0|4 +324,260,2434589,1,0 +324,260,2434812,2,0,B|332:180|402:139,2,149.999995529652,0|8|0 +300,348,2435932,1,8 +220,200,2436156,1,0 +400,140,2436380,6,0,B|352:140|320:108,2,74.9999977648259,8|8|0 +348,32,2437051,2,0,B|348:120|260:144,1,150.000005722046,8|0 +260,232,2437723,1,0 +260,60,2437947,2,0,B|163:69|157:170,1,149.999995529652,4|0 +156,244,2438618,1,0 +256,336,2438842,2,0,B|352:336|360:248,2,150.000005722046,8|0|0 +256,336,2439962,1,8 +268,248,2440186,6,0,B|168:124,1,150.000005722046,0|8 +120,204,2440857,1,0 +80,124,2441081,2,0,B|168:112|196:40,2,150.000005722046,0|4|0 +120,204,2442200,1,8 +168,280,2442424,2,0,B|216:328|280:344|360:328|408:280|424:216,2,299.999991059304,8|0|0 +224,208,2444439,5,0 +352,128,2444663,1,0 +224,64,2444887,1,0 +128,80,2445111,6,2,B|80:112|96:192,1,100,6|2 +48,256,2445559,1,0 +96,336,2445783,2,0,B|160:320|224:368,1,100,0|2 +288,352,2446230,1,0 +368,320,2446454,1,2 +288,352,2446678,2,2,B|240:256|288:160,1,200 +288,64,2447350,2,0,B|176:48,1,100 +352,128,2447797,2,2,B|464:144,1,100,2|0 +400,224,2448245,5,2 +400,320,2448469,2,2,B|304:272|192:320,1,200,6|0 +128,352,2449141,2,2,B|144:288|128:240,1,100,2|0 +80,64,2449589,2,2,B|64:128|80:176,1,100,2|2 +176,192,2450036,1,0 +176,192,2450260,2,2,B|64:224|48:352,1,200 +128,256,2450932,6,0,B|240:304,1,100 +288,368,2451380,1,2 +384,320,2451603,2,0,B|475:280,1,100 +464,176,2452051,6,2,B|352:176|288:48,1,200,6|0 +302,73,2452723,1,2 +180,56,2452947,1,0 +152,180,2453171,1,2 +276,204,2453394,1,0 +152,180,2453618,1,2 +32,256,2453842,2,2,B|80:368|224:352,1,200,0|2 +256,288,2454514,1,0 +320,352,2454738,6,2,B|384:336|400:256,2,100,0|2|2 +256,288,2455409,1,0 +320,224,2455633,2,2,B|384:112|512:144,1,200,4|0 +400,128,2456305,1,0 +400,128,2456529,2,2,B|272:112|256:0,1,200,2|0 +176,32,2457200,2,2,B|192:160|64:208,1,200,0|2 +64,288,2457872,1,0 +64,288,2458096,6,0,B|128:288|176:336,1,100 +352,348,2458544,1,4 +240,352,2458768,1,0 +288,272,2458991,1,0 +304,256,2459103,1,0 +320,240,2459215,1,0 +336,224,2459327,1,0 +352,208,2459439,1,4 +256,48,2459887,6,0,B|176:128,2,100,0|2|2 +320,112,2460559,2,2,B|240:192,1,100 +384,176,2461006,2,2,B|240:320,1,200 +160,336,2461678,1,0 +80,288,2461902,1,0 +64,192,2462126,6,0,B|48:144|64:80,1,100,2|0 +128,144,2462574,2,2,B|144:192|128:256,1,100,2|0 +208,208,2463021,2,0,B|224:80|352:64,1,200,2|0 +416,96,2463693,1,0 +384,176,2463917,2,0,B|432:384,2,200,2|2|0 +304,224,2465036,1,0 +304,224,2465260,6,0,B|336:336,1,100 +192,240,2465708,2,0,B|160:128,1,100 +32,112,2466156,1,0 +144,32,2466380,1,0 +144,32,2466603,6,2,B|192:64|240:48,2,100,6|0|0 +144,128,2467275,2,2,B|224:192|208:320,1,200 +304,304,2467947,1,0 +304,304,2468171,2,2,B|289:188|369:124,1,200 +416,48,2468842,5,4 +256,32,2469066,1,0 +96,48,2469290,1,4 +192,128,2469514,1,0 +320,128,2469738,1,0 +256,240,2469962,1,0 +256,240,2470409,1,0 +256,192,2470633,12,0,2472872 +256,64,2487424,5,0 +256,64,2487648,1,0 +432,192,2488096,5,4 +368,256,2488320,2,0,B|368:368,2,100,0|0|8 +336,176,2488991,2,0,B|240:112,2,100,0|0|8 +256,224,2489663,2,0,B|160:160,2,100,0|0|8 +176,272,2490335,2,0,B|112:272|80:336,2,100,0|0|8 +336,272,2491006,2,0,B|400:272|432:336,2,100,0|0|8 +384,192,2491678,5,4 +416,112,2491902,1,0 +384,32,2492126,1,0 +256,32,2492350,5,8 +256,128,2492574,1,0 +256,224,2492797,1,0 +128,192,2493021,5,8 +96,112,2493245,1,0 +128,32,2493469,1,0 +256,32,2493693,6,0,B|256:160,3,100,8|0|0|8 +352,128,2494589,1,0 +432,176,2494812,2,0,B|384:208|400:288,1,100,8|0 +448,336,2495260,6,0,B|456:296|440:264|380:264|380:264|296:264,1,200,4|8 +124,256,2495932,2,0,B|116:216|132:184|192:184|192:184|276:184,1,200,0|8 +416,176,2496603,2,0,B|424:136|408:104|348:104|348:104|264:104,1,200,0|8 +164,104,2497275,2,0,B|100:24,2,100,0|0|8 +100,184,2497947,2,0,B|4:104,2,100,0|0|8 +256,192,2498618,1,0 +256,192,2498842,12,4,2500633 +384,48,2500857,6,0,B|368:112|384:160,1,100 +336,272,2501305,1,0 +336,272,2501529,2,0,B|272:64,1,200,4|0 +176,64,2502200,2,0,B|64:80,1,100,0|4 +64,176,2502648,6,0,B|32:304,1,100,8|0 +144,192,2503096,2,0,B|112:304,1,100,0|8 +224,208,2503544,2,0,B|192:320,1,100,0|8 +384,240,2503991,6,0,B|416:112,1,100 +304,224,2504439,2,0,B|336:112,1,100,8|0 +224,208,2504887,2,0,B|256:95,1,100,0|8 +176,48,2505335,5,0 +80,80,2505559,1,8 +48,176,2505783,1,0 +64,272,2506006,5,4 +256,352,2506454,1,0 +448,272,2506902,1,4 +352,96,2507350,1,0 +160,96,2507797,1,4 +256,272,2508245,2,0,B|292:185|228:158|260:74,2,200,0|4|0 +256,192,2509589,12,4,2513171 +336,48,2526380,5,0 +304,80,2526603,1,0 +272,112,2526827,1,0 +240,144,2527051,1,0 +208,176,2527275,1,0 +128,240,2527499,5,6 +48,304,2527723,2,2,B|144:336,1,100,2|0 +304,320,2528171,2,2,B|208:288,1,100,2|0 +304,224,2528618,1,2 +336,144,2528842,1,0 +304,224,2529066,2,2,B|496:288,1,200 +464,192,2529738,6,0,B|432:144|464:80,1,100 +256,64,2530186,2,2,B|368:80,1,100 +176,32,2530633,1,0 +176,32,2530857,2,2,B|176:160|16:192,1,200,6|0 +32,352,2531529,6,2,B|80:240,1,100,2|0 +208,304,2531977,1,2 +160,208,2532200,1,0 +71,261,2532424,1,0 +208,304,2532648,2,2,B|272:304|272:304|320:296|320:296|408:296,1,200,2|2 +448,204,2533320,6,0,B|320:204,1,100 +256,56,2533768,2,0,B|300:160,1,100,2|0 +220,216,2534215,1,0 +220,216,2534439,2,2,B|132:216|132:216|84:224|84:224|20:224,1,200,6|0 +80,308,2535111,1,2 +80,308,2535335,1,0 +80,308,2535559,2,2,B|132:276|148:216|120:164|72:152,1,200,2|0 +71,152,2536230,2,2,B|84:104|140:76|204:92|228:144,1,200,2|0 +380,136,2536902,5,0 +228,144,2537126,1,0 +308,208,2537350,1,2 +404,304,2537574,2,0,B|280:332,1,100,0|2 +116,256,2538021,2,2,B|192:232|192:232|248:232|248:232|324:204,1,200,6|0 +224,156,2538693,1,0 +224,156,2538917,2,2,B|300:132|300:132|356:132|356:132|432:104,1,200,6|0 +352,36,2539589,6,0,B|256:28|256:28|152:40,2,200 +312,164,2540708,1,0 +176,128,2540932,1,4 +56,156,2541156,5,0 +88,296,2541380,1,4 +220,204,2541603,1,0 +220,204,2541827,1,4 +420,224,2542275,5,0 +420,224,2542499,2,2,B|408:84,1,100,2|0 +328,68,2542947,1,2 +228,72,2543171,1,0 +328,68,2543394,2,2,B|344:272,1,200 +264,332,2544066,2,0,B|208:340|160:308,1,100 +72,336,2544514,6,2,B|192:316|180:184,1,200,2|0 +80,208,2545186,2,2,B|68:86|188:66,1,200,6|0 +284,48,2545857,2,0,B|384:40,2,100,0|2|0 +304,148,2546529,1,2 +208,116,2546753,1,0 +176,212,2546977,6,2,B|-8:316,1,200,2|0 +100,336,2547648,2,2,B|284:232,1,200,2|0 +376,248,2548320,2,0,B|436:248|460:200,2,100 +384,148,2548991,5,6 +364,48,2549215,1,0 +264,56,2549439,1,2 +192,124,2549663,2,2,B|144:228|208:320,1,200,0|2 +315,314,2550335,1,0 +315,314,2550559,2,2,B|376:228|328:124,1,200 +396,52,2551230,6,0,B|508:108,1,100 +328,124,2551678,2,0,B|240:80,1,100 +96,132,2552126,2,0,B|208:188,1,100 +256,292,2552574,1,4 +256,292,2552797,1,0 +256,192,2553021,12,4,2555708 +357,168,2579508,5,4 +232,96,2579732,1,2 +152,234,2579956,1,0 +280,300,2580179,1,0 +280,300,2580851,5,4 +152,234,2581075,1,2 +232,96,2581299,1,0 +357,168,2581523,1,0 +256,192,2582194,5,2 +256,192,2582418,1,0 +256,192,2582642,1,2 +208,364,2583090,6,0,B|224:378|256:394|288:378|304:362,1,100,4|0 +256,296,2583538,1,2 +200,232,2583762,2,0,B|163:179|192:112|255:92|304:104|346:153|336:200|312:232|312:232,1,300,0|4 +368,301,2584657,2,0,B|411:263|413:197,1,100,0|2 +405,123,2585105,1,0 +144,301,2585777,2,0,B|101:263|99:197,1,100,4|0 +107,123,2586224,1,4 +256,192,2586672,5,0 +256,192,2586784,1,0 +256,192,2586896,1,0 +256,192,2587120,1,0 +256,280,2587344,1,0 +451,140,2588015,6,0,B|465:124|481:92|465:60|449:44,1,100 +362,48,2588463,2,0,B|349:59|333:91|349:123|363:139,1,100 +336,328,2589359,1,0 +256,368,2589582,1,0 +176,328,2589806,1,0 +148,140,2590254,2,0,B|162:124|178:92|162:60|146:44,1,100 +59,48,2590702,2,0,B|46:59|30:91|46:123|60:139,1,100 +209,344,2591597,6,0,B|255:362|255:362|302:344,1,100 +301,243,2592045,2,0,B|254:225|254:225|207:244,1,100 +332,76,2592941,5,0 +256,20,2593165,1,0 +180,76,2593388,1,0 +212,156,2593612,1,0 +300,156,2593836,1,0 +300,156,2593948,1,0 +300,156,2594060,1,0 +376,204,2594284,5,0 +468,196,2594508,1,0 +504,276,2594732,1,0 +440,344,2594956,1,0 +360,296,2595179,1,0 +304,373,2595403,2,0,B|288:387|256:403|224:387|208:371,1,100,0|0 +152,296,2595851,1,0 +72,344,2596075,1,0 +8,276,2596299,1,0 +44,196,2596523,1,0 +136,204,2596747,1,0 +176,124,2596971,5,0 +208,92,2597082,1,0 +252,76,2597194,1,0 +296,92,2597306,1,0 +324,124,2597418,1,0 +256,184,2597642,1,0 +340,240,2597866,2,0,B|368:280|368:280|420:296|420:296,2,100 +256,284,2598538,2,0,B|256:388|256:388,2,100 +172,240,2599209,2,0,B|144:280|144:280|92:296|92:296,2,100 +256,180,2599881,1,0 +360,256,2600105,5,0 +360,256,2600217,1,0 +360,256,2600329,1,0 +360,256,2600441,1,0 +360,256,2600553,1,0 +360,256,2600665,1,0 +360,256,2600777,1,0 +360,256,2600888,1,0 +360,257,2601000,6,0,B|347:343|241:378,2,150,4|0|0 +400,240,2601784,1,0 +448,226,2601896,1,0 +277,64,2602344,1,0 +229,78,2602456,1,0 +181,92,2602568,1,0 +157,188,2602791,6,0,B|71:175|36:69,2,150 +145,237,2603575,1,0 +127,283,2603687,1,0 +284,159,2604135,1,0 +302,113,2604247,1,0 +314,65,2604359,1,0 +397,119,2604582,6,0,B|420:34|505:13,2,150 +444,136,2605366,1,0 +491,152,2605478,1,0 +346,290,2605926,1,0 +299,272,2606038,1,0 +252,252,2606150,1,0 +221,156,2606374,6,0,B|246:79|180:5,2,150 +179,163,2607157,1,0 +129,170,2607269,1,0 +17,336,2607717,1,0 +66,331,2607829,1,0 +115,323,2607941,1,0 +196,265,2608165,6,0,B|289:271|311:373,2,150 +205,215,2608948,1,0 +214,165,2609060,1,0 +401,233,2609508,1,0 +413,184,2609620,1,0 +426,135,2609732,1,0 +415,36,2609956,6,0,B|290:22|248:130,2,200 +216,8,2611299,1,0 +35,94,2611747,5,0 +58,137,2611859,1,0 +78,182,2611971,2,0,B|141:186|158:255,1,100 +99,323,2612418,1,0 +183,376,2612642,1,0 +183,376,2612866,1,0 +249,301,2613090,6,0,B|388:315,1,100 +429,251,2613538,1,0 +429,251,2613762,1,0 +375,167,2613985,1,0 +388,67,2614209,1,0 +196,65,2614433,6,0,B|171:20,7,50,0|0|0|0|0|0|0|0 +218,111,2615329,2,0,B|163:231|296:295,1,200,4|0 +347,216,2616000,1,0 +438,257,2616224,1,0 +414,354,2616448,5,0 +414,354,2616560,1,0 +414,354,2616672,2,0,B|175:368,1,200 +214,365,2617232,1,0 +214,365,2617344,1,0 +118,336,2617568,1,0 +161,246,2617791,1,0 +80,186,2618015,2,0,B|68:67|191:31,1,200 +180,34,2618575,1,0 +180,34,2618687,1,0 +230,121,2618911,5,0 +230,121,2619023,1,0 +230,121,2619135,2,0,B|363:104,1,100 +321,208,2619582,1,0 +321,208,2619694,1,0 +321,208,2619806,2,0,B|454:191,1,100 +482,274,2620254,6,0,B|281:299,1,200 +283,299,2620814,1,0 +283,299,2620926,1,0 +208,365,2621150,1,0 +122,312,2621374,1,0 +195,244,2621597,1,0 +195,244,2621709,1,0 +195,244,2621821,2,0,B|209:139,1,100 +148,63,2622269,1,0 +75,131,2622493,5,0 +75,131,2622605,1,0 +75,131,2622717,2,0,B|61:236,1,100 +87,327,2623165,1,0 +175,372,2623388,2,0,B|301:374|320:255,1,200 +409,218,2624060,1,0 +409,218,2624284,5,0 +103,218,2624732,1,0 +409,218,2625179,1,0 +103,218,2625627,1,0 +256,48,2626075,5,0 +256,48,2626187,1,0 +256,48,2626299,2,0,B|281:167,1,100 +186,191,2626747,1,0 +234,278,2626971,2,0,B|218:342|129:338,1,100 +88,268,2627418,1,0 +88,268,2627642,1,0 +43,178,2627866,5,0 +43,178,2627978,1,0 +43,178,2628090,2,0,B|66:72,1,100 +179,45,2628538,2,0,B|156:151,1,100 +240,255,2628985,2,0,B|263:149,1,100 +420,100,2629433,1,0 +420,100,2629545,1,0 +420,100,2629657,1,4 +340,310,2630105,6,0,B|316:352|256:374|204:357|173:311,1,200,0|0 +172,74,2631000,2,0,B|196:32|256:10|308:27|339:73,1,200,0|0 +339,73,2631560,1,0 +339,73,2631672,1,0 +43,206,2632120,6,0,B|66:312,1,100 +179,339,2632568,2,0,B|156:233,1,100 +240,129,2633015,2,0,B|263:235,1,100,0|4 +398,343,2633687,2,0,B|433:355|475:325|475:280|454:238|395:234|371:270,1,200 +393,60,2634582,2,0,B|357:47|315:77|315:122|336:164|395:168|419:132,1,200 +414,138,2635142,1,0 +414,138,2635254,1,0 +242,86,2635702,6,0,B|220:38|220:38|166:28|166:28,1,100 +119,101,2636150,1,0 +36,64,2636374,2,0,B|-24:160|36:252,1,200,0|4 +336,356,2637269,2,0,B|312:312|256:300|256:300|200:312|176:356,1,200 +476,251,2638165,2,0,B|536:160|476:64,1,200 +476,64,2638724,1,0 +476,64,2638836,1,0 +256,116,2639284,5,0 +328,172,2639508,1,0 +300,256,2639732,1,0 +212,256,2639956,1,0 +184,172,2640179,1,0 +108,128,2640403,6,0,B|80:64|80:64,2,50,0|0|0 +176,296,2640851,2,0,B|148:360|148:360,2,50 +256,96,2641299,2,0,B|256:48|256:48,2,50 +336,296,2641747,2,0,B|364:360|364:360,2,50 +404,128,2642194,2,0,B|432:64|432:64,2,50 +296,208,2642642,22,0,B|288:232|256:240|224:232|216:208,1,100 +256,120,2643090,1,0 +140,268,2643314,1,0 +172,296,2643426,1,0 +212,316,2643538,1,0 +256,324,2643650,1,0 +300,316,2643762,1,0 +340,296,2643874,1,0 +372,268,2643985,1,4 +152,152,2644433,6,0,B|130:104|130:104|76:94|76:94,1,100 +256,100,2644881,2,0,B|256:-8,1,100 +360,152,2645329,2,0,B|382:104|382:104|436:94|436:94,1,100 +209,344,2646224,6,0,B|255:362|255:362|302:344,1,100 +303,243,2646672,2,0,B|256:225|256:225|209:244,1,100 +40,50,2647344,22,0,B|61:104|116:123|179:104|196:45,1,200 +296,160,2648015,2,0,B|288:136|256:128|224:136|216:160,1,100 +317,50,2648463,2,0,B|333:104|396:123|451:104|472:50,1,200,0|0 +296,160,2649135,2,0,B|328:216|328:216|280:232|257:262|257:262|232:232|185:214|185:214|222:152,1,300 +304,348,2650030,6,0,B|256:376|204:348,1,100 +424,192,2650478,1,0 +257,53,2650702,1,0 +88,192,2650926,1,0 +88,192,2651038,1,0 +88,192,2651150,1,4 +297,350,2651597,6,0,B|297:302|297:302|337:262,1,100 +217,350,2652045,2,0,B|217:302|217:302|177:262,1,100 +417,115,2652717,22,0,B|367:16|260:-22|138:16|95:118,1,400,0|0 +172,238,2653836,2,0,B|198:283|257:300|314:282|339:237,1,200,0|0 +256,152,2654508,1,0 +256,192,2654732,12,0,2656299 +114,110,2656971,6,0,B|231:63,1,100 +398,274,2657418,2,0,B|281:321,1,100 +257,327,2657754,1,0 +207,329,2657866,1,0 +160,311,2657978,1,0 +136,266,2658090,1,0 +143,216,2658202,1,0 +179,181,2658314,2,0,B|280:89|156:-1,1,200,4|0 +376,255,2658985,6,0,B|367:325|432:349,1,100 +450,247,2659433,2,0,B|459:177|394:153,1,100 +312,172,2659881,2,0,B|259:272|112:233,1,200 +243,9,2660553,6,0,B|234:79|299:103,1,100 +367,44,2661000,1,0 +451,98,2661224,2,0,B|460:168|395:192,1,100 +454,275,2661672,2,0,B|225:269,1,200 +122,144,2662344,6,0,B|222:146,1,100 +220,311,2662791,2,0,B|420:317,1,200 +291,72,2663463,2,0,B|92:67,1,200 +96,233,2664135,5,0 +69,321,2664359,1,0 +155,346,2664582,1,0 +182,261,2664806,1,0 +96,234,2665030,1,0 +182,262,2665254,2,0,B|398:231,1,200 +506,369,2665926,6,0,B|497:299|449:257,1,100 +500,182,2666374,1,0 +427,113,2666597,2,0,B|436:43|492:10,1,100 +372,3,2667045,2,0,B|267:26|234:157,1,200 +385,212,2667717,6,0,B|381:273|324:303,1,100 +399,33,2668165,2,0,B|294:56|261:187,1,200 +79,197,2668836,2,0,B|184:174|217:43,1,200 +302,18,2669508,5,0 +376,85,2669732,2,0,B|427:96|465:165,2,100 +305,156,2670403,2,0,B|347:255|469:286,1,200 +231,107,2671075,6,0,B|145:108|111:163,2,100 +248,8,2671747,2,0,B|132:2|44:73,1,200 +22,95,2672306,1,0 +11,143,2672418,1,0 +35,186,2672530,1,0 +84,195,2672642,6,0,B|148:201|156:271,1,100,4|0 +252,240,2673090,1,0 +235,338,2673314,2,0,B|358:357|418:242,1,200 +175,277,2673985,6,0,B|110:267|73:328,1,100 +0,256,2674433,1,0 +82,199,2674657,2,0,B|47:88|92:-9,1,200 +216,125,2675329,6,0,B|294:191,1,100 +338,100,2675777,2,0,B|494:232,1,200 +463,325,2676448,1,0 +463,325,2676672,2,0,B|422:365|353:347,1,100 +275,25,2677568,5,0 +275,25,2677791,1,0 +275,25,2678015,2,0,B|303:70|297:135,1,100 +44,299,2678911,6,0,B|108:297|155:246,1,100 +47,322,2679359,2,0,B|111:320|158:269,1,100 +237,272,2679806,5,0 +237,272,2679918,1,0 +237,272,2680030,2,0,B|352:306,1,100 +370,207,2680478,1,0 +444,273,2680702,2,0,B|493:237|487:181,2,100 +425,371,2681374,6,0,B|214:354,1,200 +133,262,2682045,2,0,B|332:278,1,200 +358,196,2682717,2,0,B|252:187,1,100 +208,183,2683053,1,0 +158,178,2683165,1,0 +108,173,2683277,1,0 +58,166,2683388,2,0,B|20:124|41:55,1,100 +108,2,2683836,2,0,B|146:44|125:113,1,100 +222,57,2684284,6,0,B|433:40,1,200,0|0 +492,111,2684956,2,0,B|293:127,1,200 +235,209,2685627,2,0,B|341:200,1,100 +263,129,2686075,1,0 +213,134,2686187,1,0 +163,141,2686299,2,0,B|125:183|146:252,1,100 +256,30,2697717,5,0 +110,125,2697941,1,0 +133,290,2698165,1,0 +299,333,2698388,1,0 +421,211,2698612,1,0 +342,54,2698836,5,0 +169,54,2699060,1,0 +90,211,2699284,1,0 +212,333,2699508,1,0 +378,290,2699732,1,0 +401,125,2699956,1,0 +256,30,2700179,1,0 +256,192,2700403,5,0 +256,192,2700515,1,0 +256,192,2700627,1,0 +256,192,2700739,1,0 +256,192,2700851,1,0 +256,192,2700963,1,0 +256,192,2701075,1,0 +256,192,2701187,1,0 +256,192,2701299,1,4 +326,324,2701635,6,0,B|306:358|256:377|213:363|187:324,1,164.999995082617,0|0 +60,280,2702194,1,0 +76,148,2702418,2,0,B|88:92|88:92|132:52,1,109.999996721745 +209,147,2702866,2,0,B|219:123|254:115|289:122|302:146,1,109.999996721745 +256,188,2703202,1,0 +256,240,2703314,1,0 +384,56,2703538,2,0,B|424:92|424:92|436:148,1,109.999996721745 +326,311,2703985,2,0,B|306:345|256:364|213:350|187:311,2,164.999995082617 +256,192,2704881,5,0 +256,144,2704993,1,0 +256,96,2705105,1,0 +130,80,2705329,2,0,B|119:56|84:48|50:55|37:79,1,109.999996721745 +209,259,2705777,2,0,B|219:282|254:290|289:284|302:260,1,109.999996721745 +475,79,2706224,2,0,B|462:55|427:48|392:56|382:80,1,109.999996721745 +408,296,2706672,6,0,B|464:264,2,54.9999983608723 +300,336,2707120,2,0,B|286:360|256:366|256:366|225:360|212:336,1,109.999996721745 +192,228,2707568,2,0,B|211:192|257:182|257:182|303:192|322:228,2,164.999995082617 +104,296,2708463,2,0,B|48:264,2,54.9999983608723 +136,108,2708911,5,0 +256,32,2709135,1,0 +376,108,2709359,1,0 +336,260,2709582,1,0 +176,260,2709806,1,0 +256,152,2710030,1,0 +256,152,2710254,5,0 +404,348,2710702,1,0 +108,348,2711150,1,0 +175,131,2711597,6,0,B|192:71|257:48|320:72|338:134,1,219.999993443489,0|0 +256,156,2712157,2,0,B|256:232,1,54.9999983608723 +356,280,2712493,2,0,B|372:332|372:332|436:360|436:360,2,109.999996721745 +256,364,2713165,1,0 +156,280,2713388,2,0,B|140:332|140:332|76:360|76:360,2,109.999996721745 +156,280,2713948,1,0 +156,280,2714060,1,0 +209,160,2714284,2,0,B|219:136|254:128|289:135|302:159,1,109.999996721745 +95,106,2714732,5,0 +124,65,2714844,1,0 +161,33,2714956,1,0 +206,15,2715068,1,0 +256,8,2715179,1,0 +306,15,2715291,1,0 +351,33,2715403,1,0 +388,65,2715515,1,0 +417,106,2715627,1,4 +340,310,2716075,6,0,B|316:352|256:374|204:357|173:311,1,200,0|0 +340,129,2716971,2,0,B|316:87|256:65|204:82|173:128,1,200,0|0 +256,176,2717530,2,0,B|256:232|256:232,1,50 +400,336,2718090,6,0,B|440:224,1,100 +256,176,2718538,2,0,B|256:276,1,100 +112,336,2718985,2,0,B|72:224,1,100,0|4 +176,88,2719657,6,0,B|200:44|256:32|256:32|312:44|336:88,1,200 +336,296,2720553,2,0,B|312:340|256:352|256:352|200:340|176:296,1,200 +256,252,2721112,2,0,B|256:184|256:184,1,50 +160,48,2721672,5,0 +216,120,2721896,2,0,B|224:96|256:88|288:96|296:120,1,100 +352,48,2722344,1,0 +256,204,2722791,5,4 +160,356,2723239,1,0 +352,356,2723687,1,0 +340,157,2724135,2,0,B|316:115|256:93|204:110|173:156,1,200,0|0 +256,204,2724694,2,0,B|256:260|256:260,1,50 +416,92,2725254,5,0 +348,28,2725478,1,0 +256,8,2725702,1,0 +164,28,2725926,1,0 +96,92,2726150,1,0 +160,164,2726374,5,0 +144,212,2726485,1,0 +104,244,2726597,1,0 +208,316,2726821,2,0,B|190:336|190:336|180:360,2,50 +304,316,2727269,2,0,B|322:336|322:336|332:360,2,50 +408,244,2727717,1,0 +368,212,2727829,1,0 +352,164,2727941,1,0 +184,80,2728165,5,0 +80,104,2728388,1,0 +328,80,2728612,5,0 +432,104,2728836,1,0 +256,32,2729060,5,0 +256,104,2729284,1,0 +270,190,2729508,1,0 +270,190,2729620,1,0 +270,190,2729732,1,0 +270,190,2729844,1,0 +270,190,2729956,6,0,B|271:312|126:334,1,200,4|0 +344,248,2730627,2,0,B|402:270|419:337,2,100 +409,169,2731299,1,0 +310,151,2731523,2,0,B|325:32|457:27,1,200 +297,226,2732194,6,0,B|286:279|226:297,2,100 +204,186,2732866,1,0 +131,117,2733090,1,0 +208,53,2733314,2,0,B|432:48,1,200 +226,288,2733985,6,0,B|160:280|141:204,1,100 +276,358,2734433,2,0,B|393:349|414:209,1,200 +236,26,2735105,2,0,B|119:35|98:175,1,200 +166,229,2735777,5,0 +84,285,2736000,2,0,B|65:344|-6:353,2,100 +48,190,2736672,1,0 +118,118,2736896,1,0 +118,118,2737008,1,0 +118,118,2737120,2,0,B|326:103,1,200,4|0 +455,238,2737791,6,0,B|393:233|379:150,2,100 +476,139,2738463,1,0 +458,301,2738687,2,0,B|338:294|324:163,1,200 +476,139,2739359,1,0 +476,139,2739582,1,0 +416,356,2739806,6,0,B|295:340|272:206,1,200 +96,28,2740478,2,0,B|216:43|240:178,1,200 +145,200,2741150,1,0 +218,268,2741374,2,0,B|330:261,2,100 +169,355,2742045,6,0,B|389:363,1,200 +419,366,2742605,1,0 +468,367,2742717,1,0 +500,272,2742941,1,0 +448,186,2743165,1,0 +397,184,2743277,1,0 +347,181,2743388,1,0 +369,83,2743612,5,0 +318,82,2743724,1,0 +268,78,2743836,1,0 +217,77,2743948,1,0 +166,75,2744060,1,0 +116,71,2744172,1,0 +66,68,2744284,6,0,B|22:166|85:277,1,200,4|0 +174,237,2744956,1,0 +152,334,2745179,1,0 +152,334,2745403,2,0,B|259:358,1,100 +316,280,2745851,2,0,B|435:312|488:212,1,200 +194,111,2746523,5,0 +274,50,2746747,1,0 +194,111,2746971,1,0 +194,111,2747194,1,0 +261,185,2747418,1,0 +206,269,2747642,2,0,B|236:385|393:359,1,200 +438,298,2748314,6,0,B|440:198,1,100 +330,214,2748762,2,0,B|336:14,1,200 +209,285,2749433,2,0,B|214:86,1,200 +445,24,2750105,5,0 +345,30,2750329,1,0 +245,36,2750553,1,0 +145,41,2750777,1,0 +150,140,2751000,1,0 +50,153,2751224,2,0,B|21:275|129:337,1,200 +444,267,2751896,5,0 +345,254,2752120,1,0 +345,254,2752344,1,0 +444,267,2752568,1,0 +345,254,2752791,1,0 +285,335,2753015,2,0,B|161:325|165:199,1,200 +80,150,2753687,6,0,B|40:183|33:247,1,100 +139,102,2754135,2,0,B|234:49|359:83,1,200 +373,282,2754806,2,0,B|278:335|153:301,1,200 +147,211,2755478,5,0 +78,284,2755702,2,0,B|4:294|-11:364,2,100 +20,201,2756374,2,0,B|62:102|17:-2,1,200 +207,276,2757045,6,0,B|272:283|323:217,2,100 +156,362,2757717,2,0,B|249:393|381:350,1,200 +401,346,2758277,1,0 +443,319,2758388,1,0 +467,275,2758500,1,4 +480,228,2758612,5,4 +304,57,2759060,2,0,B|335:101|310:159|256:176|213:166|177:124|184:84|207:56|207:56,1,259.999995350838,0|0 +32,228,2759956,1,0 +298,356,2760403,2,0,B|312:296|312:296|256:256|256:256|199:296|199:296|215:357|215:357,1,259.999995350838 +368,112,2761299,5,0 +256,48,2761523,1,0 +144,112,2761747,1,0 +223,233,2761971,2,0,B|232:217|256:211|282:220|290:232,1,74.9999977648259 +256,304,2762194,1,0 +474,73,2762642,6,0,B|430:90|431:171|489:190|489:190|430:215|427:293|489:319,1,299.999991059304 +37,311,2763538,6,0,B|80:288|82:215|23:190|23:190|81:171|82:90|38:73,1,299.999991059304 +256,296,2764433,5,0 +256,24,2764881,1,0 +308,169,2765105,1,0 +184,88,2765329,1,0 +336,88,2765553,1,0 +336,88,2765665,1,0 +336,88,2765777,1,0 +212,168,2766000,1,0 +80,224,2766224,5,0 +144,328,2766448,1,0 +256,368,2766672,1,0 +368,328,2766896,1,0 +432,224,2767120,1,0 +298,192,2767344,2,0,B|302:215|288:248|259:253|233:252|210:230|206:209|215:190|215:190,1,149.999995529652,0|0 +192,82,2767791,2,0,B|256:42|256:42|320:82,1,149.999995529652 +397,162,2768239,6,0,B|465:191|465:191|484:274,1,149.999995529652 +440,362,2768687,1,0 +344,312,2768911,1,0 +308,348,2769023,1,0 +256,360,2769135,1,0 +204,348,2769247,1,0 +168,312,2769359,1,0 +72,364,2769582,1,0 +30,265,2769807,2,0,B|47:191|47:191|115:162,1,149.999995529652 +96,60,2770254,5,0 +196,96,2770478,2,0,B|205:120|244:118|256:89|256:89|265:118|306:118|315:97,1,149.999995529652 +420,60,2770926,1,0 +404,164,2771150,1,0 +318,240,2771374,2,0,B|299:208|254:191|215:204|191:239,1,149.999995529652,0|0 +208,340,2771821,1,0 +256,316,2771933,1,0 +304,340,2772045,1,0 +388,168,2772269,5,0 +356,128,2772381,1,0 +308,100,2772493,1,0 +256,88,2772605,1,0 +204,100,2772717,1,0 +156,128,2772829,1,0 +124,168,2772941,1,4 +256,296,2773165,5,0 +256,296,2773277,1,0 +256,296,2773388,1,0 +120,360,2773612,1,0 +28,240,2773836,1,0 +112,116,2774060,1,0 +256,152,2774284,1,0 +256,296,2774508,5,0 +256,296,2774620,1,0 +256,296,2774731,1,0 +392,360,2774955,1,0 +484,240,2775179,1,0 +400,116,2775403,1,0 +320,16,2775627,6,0,B|301:48|256:65|217:52|193:17,1,149.999995529652,0|0 +112,120,2776075,1,0 +204,240,2776299,1,0 +256,220,2776411,1,0 +304,240,2776523,1,4 +196,344,2776747,2,0,B|213:376|255:385|255:385|297:376|315:344,1,149.999995529652 +436,252,2777194,5,0 +396,108,2777418,1,0 +256,56,2777642,1,0 +116,108,2777866,1,0 +76,252,2778090,1,0 +76,252,2778202,1,0 +76,252,2778314,1,0 +208,348,2778538,5,0 +256,208,2778762,1,0 +304,348,2778985,1,0 +180,264,2779209,1,0 +332,264,2779433,1,0 +348,124,2779657,1,0 +312,80,2779769,1,0 +256,60,2779881,1,0 +200,80,2779993,1,0 +164,124,2780105,1,4 +76,240,2780329,5,0 +256,160,2780553,1,0 +436,244,2780777,1,0 +192,368,2781000,2,0,B|211:336|256:319|295:332|319:367,2,149.999995529652 +192,192,2781672,1,0 +256,192,2781784,1,0 +320,192,2781896,1,0 +320,16,2782120,6,0,B|301:48|256:65|217:52|193:17,2,149.999995529652 +424,120,2782791,1,0 +256,192,2783015,1,0 +96,120,2783239,1,0 +256,352,2783463,1,0 +256,192,2783687,6,0,B|252:203|238:234|285:241|301:236|315:230|336:212|323:182|328:171|332:161|326:145|310:125|295:132|283:107|277:112|244:105|219:108|204:131|189:147|180:169|180:169|169:197|172:220|178:254|191:280|206:310|255:317|286:330|322:338|357:307|381:282|403:250|408:216|414:197|422:176|425:150|406:108|386:57|353:37|305:22|251:26|214:22|174:41|139:77|121:113|107:146|107:146|97:176|87:233|95:284|124:325|166:371|200:383|250:410|330:406|384:383|417:348|445:311|483:242|479:162,1,1799.99994635582 +478,147,2786485,1,0 +472,126,2786597,1,0 +466,104,2786709,1,0 +457,83,2786821,1,0 +444,64,2786933,1,0 +432,44,2787045,1,0 +415,28,2787157,1,0 +397,14,2787269,1,4 +256,80,2801597,5,4 +256,192,2801709,12,0,2804284 +366,72,2804508,5,0 +422,120,2804732,1,0 +422,192,2804956,1,0 +366,239,2805179,2,0,B|253:192|145:240,1,224.999993294478,4|0 +113,306,2806075,2,0,B|258:390|408:299,1,300 +494,192,2807418,1,0 +398,79,2807866,2,0,B|258:-6|114:78,1,299.999991059304,4|4 +256,192,2808874,12,0,2811896 +64,152,2812120,5,0 +64,232,2812344,1,4 +256,192,2812456,12,0,2814135 +424,116,2814359,5,0 +426,153,2814471,1,0 +411,187,2814582,1,0 +380,208,2814694,1,0 +342,205,2814806,1,0 +309,186,2814918,1,0 +272,177,2815030,1,0 +234,178,2815142,1,0 +199,192,2815254,1,0 +177,222,2815366,1,0 +167,258,2815478,1,0 +171,293,2815590,1,0 +195,321,2815702,1,0 +227,339,2815814,1,0 +264,339,2815926,6,0,B|306:298|375:323,1,100 +441,263,2816374,1,0 +362,201,2816597,2,0,B|347:124|383:92,2,100 +272,248,2817269,1,0 +201,177,2817493,5,0 +201,177,2817605,1,0 +201,177,2817717,2,0,B|93:194,2,100 +289,129,2818388,1,0 +312,32,2818612,5,0 +410,30,2818836,1,0 +204,32,2819060,1,0 +106,30,2819284,1,0 +29,94,2819508,6,0,B|14:141|65:193,1,100 +57,184,2819844,1,0 +57,184,2819956,1,0 +52,284,2820179,5,0 +52,284,2820291,1,0 +52,284,2820403,2,0,B|170:273,1,100 +151,274,2820739,1,0 +151,274,2820851,1,0 +232,333,2821075,5,0 +232,333,2821187,1,0 +232,333,2821299,2,0,B|298:328|314:249,1,100 +306,275,2821635,1,0 +306,275,2821747,1,0 +401,305,2821971,5,0 +401,305,2822082,1,0 +401,305,2822194,2,0,B|382:81,1,200 +349,69,2822754,1,0 +303,47,2822866,1,0 +254,60,2822978,1,0 +232,105,2823090,6,0,B|234:166|174:188,1,100 +147,220,2823426,1,0 +111,255,2823538,1,0 +204,291,2823762,5,0 +239,326,2823874,1,0 +274,361,2823985,2,0,B|398:345,1,100 +421,363,2824321,1,0 +468,379,2824433,1,0 +462,279,2824657,5,0 +437,235,2824769,1,0 +392,211,2824881,2,0,B|379:150|437:120,1,100 +471,99,2825217,1,0 +480,49,2825329,1,0 +387,10,2825553,5,0 +337,16,2825665,1,0 +288,27,2825777,2,0,B|282:137|177:182,1,200 +235,263,2826448,1,0 +134,263,2826672,2,0,B|109:321|132:372,1,100,4|0 +127,360,2827008,1,0 +127,360,2827120,1,0 +225,343,2827344,5,0 +321,367,2827568,1,0 +338,268,2827791,1,0 +338,268,2827903,1,0 +338,268,2828015,2,0,B|479:251,1,100 +373,124,2828463,6,0,B|273:135,1,100 +189,80,2828911,1,0 +149,111,2829023,1,0 +139,160,2829135,1,0 +157,206,2829247,1,0 +196,236,2829359,1,0 +245,243,2829471,1,0 +290,262,2829582,1,0 +316,304,2829694,1,0 +307,353,2829806,1,0 +263,377,2829918,1,0 +213,381,2830030,1,0 +166,361,2830142,1,0 +116,366,2830254,1,4 +48,296,2830478,5,0 +17,256,2830590,1,0 +19,206,2830702,1,0 +54,168,2830814,1,0 +100,160,2830926,1,0 +145,180,2831038,1,0 +194,188,2831150,1,0 +243,181,2831262,1,0 +285,154,2831374,1,0 +302,107,2831485,1,0 +291,58,2831597,1,0 +255,22,2831709,1,0 +205,14,2831821,1,0 +160,36,2831933,1,0 +136,80,2832045,1,0 +194,188,2832269,5,0 +226,222,2832381,1,0 +232,273,2832493,1,0 +204,314,2832605,1,0 +155,328,2832717,1,0 +101,243,2832941,1,0 +53,227,2833053,1,0 +28,183,2833165,1,0 +112,132,2833388,1,0 +130,87,2833500,1,0 +176,62,2833612,1,0 +224,71,2833724,1,0 +255,111,2833836,1,0 +340,144,2834060,5,0 +390,145,2834172,1,0 +434,121,2834284,1,0 +492,202,2834508,2,0,B|456:227|438:286|453:314,1,100 +360,348,2834956,1,0 +360,348,2835068,1,0 +360,348,2835180,1,0 +296,288,2835403,2,0,B|288:312|256:320|224:312|216:288,1,100 +152,348,2835851,1,0 +152,348,2835963,1,0 +152,348,2836075,1,0 +256,208,2836299,5,0 +170,101,2836523,2,0,B|203:58|256:44|312:56|344:104,3,200,0|0|4|0 +404,192,2838090,5,0 +384,200,2838202,1,0 +368,212,2838314,1,0 +356,228,2838426,1,0 +348,248,2838538,1,0 +344,268,2838650,1,0 +348,288,2838762,1,0 +356,308,2838874,1,0 +368,324,2838985,1,0 +384,336,2839097,1,0 +404,344,2839209,1,0 +256,196,2839657,5,0 +108,192,2839881,5,0 +128,200,2839993,1,0 +144,212,2840105,1,0 +156,228,2840217,1,0 +164,248,2840329,1,0 +168,268,2840441,1,0 +164,288,2840553,1,0 +156,308,2840665,1,0 +144,324,2840776,1,0 +128,336,2840888,1,0 +108,344,2841000,1,0 +216,124,2841448,6,0,B|224:148|256:156|288:148|296:124,1,100 +296,123,2841784,1,0 +296,123,2841896,1,0 +256,36,2842120,1,0 +216,260,2842344,6,0,B|224:236|256:228|288:236|296:260,1,100 +256,348,2842791,1,0 +256,192,2842847,12,4,2843687 +256,192,2843799,12,0,2844582 +176,104,2845030,5,0 +256,48,2845254,1,0 +336,104,2845478,1,0 +308,200,2845702,1,0 +204,200,2845926,1,0 +183,297,2846150,2,0,B|198:352|258:373|316:351|333:294,1,200,0|0 +424,344,2846821,6,0,B|448:296|448:296|496:280,1,100 +424,208,2847269,1,0 +456,112,2847493,1,0 +372,48,2847717,1,0 +293,106,2847941,2,0,B|316:139|297:184|256:197|223:189|195:157|201:126|218:105|218:105,1,200,0|0 +140,48,2848612,1,0 +56,112,2848836,1,0 +88,208,2849060,1,0 +256,352,2849508,1,0 +338,300,2849732,2,0,B|310:254|257:240|202:251|172:305,1,200 +120,216,2850403,5,0 +208,168,2850627,1,0 +256,156,2850739,1,0 +304,168,2850851,1,0 +392,216,2851075,1,0 +396,116,2851299,1,0 +304,56,2851523,1,0 +256,44,2851635,1,0 +208,56,2851747,1,4 +116,116,2851971,1,0 +120,216,2852194,5,0 +208,268,2852418,2,0,B|184:276|176:308|184:340|208:348,1,100 +303,348,2852866,2,0,B|328:340|336:308|328:276|304:268,1,100 +392,216,2853314,6,0,B|336:188|332:132|332:132|384:121|408:60,1,200 +308,32,2853985,1,0 +204,32,2854209,1,0 +111,75,2854433,2,0,B|134:120|180:132|180:132|176:188|120:216,1,200 +216,280,2855105,2,0,B|227:258|255:252|255:252|283:258|295:280,1,100 +256,192,2855385,12,0,2856224 +256,192,2856280,12,0,2857120 +333,71,2857568,6,0,B|328:134|394:164,1,100 +179,313,2858015,2,0,B|184:250|118:220,1,100 +96,197,2858351,1,0 +74,151,2858463,1,0 +87,102,2858575,1,0 +129,75,2858687,1,0 +178,79,2858799,1,0 +218,108,2858911,2,0,B|298:184|412:151,1,200,4|0 +163,154,2859582,6,0,B|192:206|164:266,2,100 +93,81,2860254,1,0 +23,153,2860478,2,0,B|-22:249|34:367,1,200 +308,324,2861150,6,0,B|210:318,2,100 +353,234,2861821,2,0,B|362:164|297:140,1,100 +247,222,2862269,2,0,B|41:209,1,200 +126,25,2862941,6,0,B|102:71|96:129,1,100 +278,101,2863388,2,0,B|293:199|248:304,1,200 +428,259,2864060,2,0,B|404:145|420:45,1,200 +243,29,2864732,6,0,B|369:47,2,100 +143,15,2865403,1,0 +107,50,2865515,1,0 +105,100,2865627,1,0 +138,137,2865739,1,0 +184,155,2865851,1,0 +216,193,2865963,1,0 +227,241,2866075,2,0,B|261:337|415:331,1,200,4|0 +359,235,2866747,2,0,B|395:120,1,100 +474,190,2867194,2,0,B|510:75,1,100 +269,128,2867642,2,0,B|202:341,1,200 +460,196,2868314,6,0,B|496:81,1,100 +255,134,2868762,2,0,B|188:347,1,200 +195,324,2869433,1,0 +256,192,2869545,12,0,2873239 +256,96,2873463,5,0 +256,96,2873575,1,0 +256,96,2873687,1,0 +256,96,2873911,1,0 +256,96,2874135,1,0 +256,288,2874359,5,0 +256,288,2874471,1,0 +256,288,2874582,1,0 +256,288,2874806,1,0 +256,288,2875030,1,0 +352,192,2875254,5,0 +352,192,2875366,1,0 +352,192,2875478,1,0 +352,192,2875702,1,0 +352,192,2875926,1,0 +160,192,2876150,5,0 +160,192,2876262,1,0 +160,192,2876374,1,0 +160,192,2876597,1,0 +160,192,2876821,1,0 +296,56,2877045,5,0 +304,56,2877157,1,0 +312,56,2877269,1,0 +216,56,2877493,1,0 +200,56,2877717,1,0 +216,328,2877941,5,0 +208,328,2878053,1,0 +200,328,2878165,1,0 +296,328,2878389,1,0 +312,328,2878613,1,0 +256,192,2878724,12,0,2880403 +392,128,2880627,5,0 +392,128,2880739,1,0 +392,128,2880851,1,0 +392,248,2881075,1,0 +392,128,2881299,1,0 +120,256,2881523,5,0 +120,256,2881635,1,0 +120,256,2881747,1,0 +120,136,2881971,1,0 +120,256,2882194,1,0 +320,328,2882418,5,0 +320,328,2882530,1,0 +320,328,2882642,1,0 +200,328,2882866,1,0 +320,328,2883090,1,0 +192,56,2883314,5,0 +192,56,2883426,1,0 +192,56,2883538,1,0 +312,56,2883762,1,0 +192,56,2883985,1,0 +256,192,2884209,5,0 +256,192,2884321,1,0 +256,192,2884433,1,0 +256,192,2884657,1,0 +256,192,2884881,1,0 +256,192,2885105,5,0 +256,192,2885217,1,0 +256,192,2885329,1,0 +256,192,2885553,1,0 +256,192,2885777,1,0 +64,344,2886224,5,0 +96,344,2886336,1,0 +128,344,2886448,1,0 +160,344,2886560,1,0 +192,344,2886672,1,0 +224,344,2886784,1,0 +256,344,2886896,1,0 +288,344,2887008,1,0 +320,344,2887120,1,0 +352,344,2887232,1,0 +384,344,2887344,1,0 +416,344,2887456,1,0 +448,344,2887568,1,0 +256,192,2887679,12,0,2889359 +176,192,2901769,6,0,B|196:288|272:340|364:360,1,240.000005722046 +332,84,2903251,1,0 +428,136,2903621,1,2 +336,192,2903991,1,0 +428,252,2904362,1,2 +336,192,2904732,6,0,B|316:288|240:340|148:360,1,240.000005722046 +180,84,2906214,1,0 +84,136,2906584,1,2 +176,192,2906954,1,0 +84,252,2907325,1,2 +176,192,2907695,6,0,B|196:96|272:44|364:24,1,240.000005722046 +332,300,2909176,1,0 +428,248,2909547,1,2 +336,192,2909917,1,0 +428,132,2910288,1,2 +336,192,2910658,6,0,B|316:96|240:44|148:24,1,240.000005722046 +180,324,2912139,1,0 +332,324,2912510,1,2 +176,192,2912880,1,0 +204,236,2913065,1,0 +256,248,2913251,1,2 +308,236,2913436,1,0 +336,192,2913621,5,0 +76,324,2914362,1,0 +236,96,2915102,1,0 +340,128,2915473,2,0,B|308:164|304:200|352:248,1,120.000002861023,2|0 +240,276,2916214,1,2 +176,192,2916584,5,0 +436,324,2917325,1,0 +276,96,2918065,1,0 +172,128,2918436,2,0,B|204:164|208:200|160:248,1,120.000002861023,2|0 +272,276,2919176,1,2 +336,192,2919547,5,0 +76,60,2920288,1,0 +236,288,2921028,1,0 +340,256,2921399,2,0,B|308:220|304:184|352:136,1,120.000002861023,2|0 +240,108,2922139,1,2 +176,192,2922510,5,0 +436,60,2923251,1,0 +316,312,2923991,1,0 +228,296,2924362,2,0,B|228:280|228:280|228:208|280:208|280:120|280:120|280:104,1,200 +196,88,2925473,1,0 +174,95,2940288,6,0,B|194:52|255:27|316:48|338:97,2,200,2|8|2 +256,144,2942139,1,0 +208,224,2942510,1,8 +304,224,2942880,1,0 +347,302,2943251,2,0,B|308:333|270:317|255:302|255:302|240:317|199:333|164:302,1,200,2|8 +88,155,2944732,5,2 +68,67,2945102,1,0 +149,28,2945473,1,8 +210,93,2945843,1,0 +181,184,2946214,2,0,B|191:220|255:244|255:244|319:220|333:183,1,200,2|8 +424,229,2947695,5,2 +444,317,2948065,1,0 +363,356,2948436,1,8 +302,291,2948806,1,0 +331,200,2949176,2,0,B|321:164|257:140|257:140|194:160|181:202,1,200,2|8 +352,88,2950658,1,2 +256,48,2951028,1,0 +152,88,2951399,1,8 +44,56,2951769,21,8 +44,56,2951954,1,0 +44,56,2952139,1,4 +160,348,2952880,1,8 +36,172,2953436,1,0 +36,172,2953621,1,0 +192,140,2953991,1,0 +320,244,2954362,1,8 +476,212,2954732,1,0 +476,212,2954917,2,0,B|424:8|112:76|168:296,1,500,0|8 +320,244,2956214,1,0 +476,212,2956584,5,0 +320,244,2956954,1,0 +192,140,2957325,1,8 +36,172,2957695,1,0 +36,172,2957880,2,0,B|88:376|400:308|344:88,1,500,0|8 +192,140,2959176,1,0 +112,276,2959547,5,0 +256,344,2959917,1,0 +400,276,2960288,1,8 +340,136,2960658,2,0,B|364:66|291:34|256:69|256:69|221:34|148:66|172:136,2,299.999991059304 +460,270,2962510,1,0 +421,325,2962695,1,0 +360,300,2962880,1,0 +256,256,2963251,1,8 +152,300,2963621,1,0 +91,325,2963806,1,0 +52,270,2963991,5,0 +352,56,2964732,1,8 +116,92,2965288,1,0 +116,92,2965473,1,0 +176,240,2965843,1,0 +336,240,2966214,1,8 +396,92,2966584,1,0 +396,92,2966769,2,0,B|492:128|532:196|532:304|448:392|348:384|260:320,1,500,0|8 +396,92,2968065,5,0 +396,92,2968251,1,0 +396,92,2968436,1,0 +336,240,2968806,1,0 +176,240,2969176,1,8 +116,92,2969547,1,0 +116,92,2969732,2,0,B|20:128|-20:196|-20:304|64:392|164:384|252:320,1,500,0|8 +256,36,2971399,5,0 +176,172,2971769,1,0 +336,172,2972139,1,8 +432,300,2972510,2,0,B|356:399|257:327|257:327|158:399|83:300,1,400 +256,236,2973621,1,8 +432,172,2973991,2,0,B|356:73|257:144|257:144|158:73|83:172,1,400 +256,236,2975102,1,8 +256,60,2975473,1,8 +256,60,2975658,1,0 +256,60,2975843,5,4 +378,372,2976584,2,0,B|348:306|260:264|172:297|136:371,1,300,8|0 +156,204,2977325,2,0,B|372:204,1,200,8|8 +296,32,2978065,2,0,B|314:70|296:101|268:115|240:115|212:101|194:70|212:32,1,200,8|0 +126,127,2978621,2,0,B|174:192|256:213|337:192|385:127,1,300 +432,304,2979547,5,8 +256,352,2979917,1,0 +80,304,2980288,1,8 +181,159,2980658,2,0,B|191:195|255:219|255:219|319:195|333:158,1,200,0|8 +127,79,2981399,2,0,B|143:14|207:-1|272:14|304:46|288:95|256:111|223:95|207:46|239:14|304:-1|368:14|384:79,1,400 +480,224,2982510,5,8 +360,322,2982880,2,0,B|325:339|290:339|256:322|221:304|204:252|221:201|256:183|290:201|308:252|290:304|256:322|221:339|187:339|152:322,1,400 +32,224,2983991,1,8 +173,146,2984362,2,0,B|181:115|213:90|254:84|254:84|281:74|303:41|281:9|257:3|229:9|201:41|227:74|254:84|254:84|297:90|324:111|338:152,1,400 +448,288,2985473,1,8 +256,368,2985843,5,8 +184,312,2986028,1,8 +208,224,2986214,1,0 +304,224,2986399,1,8 +328,312,2986584,1,8 +334,99,2986954,2,0,B|317:52|255:28|199:50|176:101,1,200,4|0 +256,136,2987510,1,0 +256,136,2987695,5,4 +256,352,2988436,1,8 +412,108,2988991,1,0 +412,194,2989176,2,0,B|362:199|313:154|308:94|344:51,1,200,8|0 +167,51,2989917,2,0,B|203:94|198:154|149:199|100:194,1,200,8|0 +100,108,2990473,1,0 +256,204,2990843,1,0 +330,254,2991028,2,0,B|320:290|256:314|256:314|192:290|178:253,1,200,0|8 +364,344,2991769,5,0 +420,140,2992139,1,0 +256,48,2992510,1,0 +92,140,2992880,1,8 +148,344,2993251,1,0 +256,48,2993991,5,0 +432,336,2994732,1,0 +80,332,2995473,1,0 +256,304,2995843,1,8 +172,152,2996214,2,0,B|136:76|172:14|227:-12|283:-12|338:14|374:76|338:152,1,400 +472,276,2997325,1,8 +311,381,2997695,2,0,B|311:325|255:307|255:307|200:288|200:233,1,200,0|8 +256,168,2998251,1,8 +311,232,2998436,2,0,B|311:288|256:307|256:307|201:325|201:381,1,200,8|8 +39,276,2999176,1,8 +39,276,2999362,1,8 +39,276,2999547,1,4 +256,192,2999639,12,8,3001769 +256,192,3002510,5,4 +468,56,3008436,5,4 +352,348,3009176,1,8 +476,172,3009732,1,0 +476,172,3009917,1,0 +320,140,3010288,1,0 +192,244,3010658,1,8 +36,212,3011028,1,0 +36,212,3011214,2,0,B|88:8|400:76|344:296,1,500,0|8 +36,212,3012880,5,0 +192,244,3013251,1,0 +320,140,3013621,1,8 +476,172,3013991,1,0 +476,172,3014176,2,0,B|424:376|112:308|168:88,1,500,0|8 +320,140,3015473,1,0 +400,276,3015843,5,0 +256,344,3016214,1,0 +112,276,3016584,1,8 +172,136,3016954,2,0,B|148:66|221:34|256:69|256:69|291:34|364:66|340:136,2,299.999991059304 +256,272,3018806,1,0 +412,300,3019176,1,0 +256,272,3019547,1,8 +100,300,3019917,1,0 +100,300,3020102,1,0 +100,300,3020288,5,0 +376,99,3021028,2,0,B|349:30|255:-8|170:25|135:103,1,300,8|0 +176,180,3021769,2,0,B|193:227|255:251|311:229|334:178,1,200,0|0 +488,264,3022510,1,8 +336,348,3022880,1,0 +256,372,3023065,1,0 +176,348,3023251,1,0 +256,52,3023991,5,8 +24,264,3024547,1,0 +24,264,3024732,1,0 +120,112,3025102,1,0 +256,228,3025473,1,8 +392,112,3025843,1,0 +336,180,3026028,2,0,B|308:136|239:99|166:123|80:208|151:317|208:352|279:356|344:292|344:245,1,500,0|8 +448,352,3027325,5,0 +255,229,3027695,1,0 +64,352,3028065,1,0 +32,128,3028436,5,8 +173,13,3028806,2,0,B|137:89|173:151|228:177|284:177|339:151|375:89|339:13,1,400 +480,128,3029917,1,8 +352,288,3030288,5,0 +160,288,3030658,1,0 +336,184,3031028,1,8 +256,216,3031214,1,0 +176,184,3031399,1,8 +304,96,3031769,21,8 +208,96,3031954,1,0 +256,32,3032139,1,4 +256,32,3032880,37,8 +48,280,3033436,1,0 +48,280,3033621,1,0 +192,212,3033991,1,0 +328,292,3034362,1,8 +464,216,3034732,1,0 +464,216,3034917,2,0,B|397:316|291:313|235:285|185:249|132:157|191:49,1,500,0|8 +320,48,3036214,1,0 +256,196,3036584,5,0 +320,336,3036954,1,0 +416,208,3037325,1,8 +256,195,3037695,2,0,B|122:154|122:154|189:218|155:337|25:311,2,400 +256,196,3039917,1,0 +256,36,3040288,1,8 +256,195,3040658,2,0,B|389:154|389:154|322:218|356:337|486:311,1,400 +476,152,3041769,1,8 +352,52,3042139,5,0 +316,108,3042325,1,0 +256,136,3042510,1,0 +196,108,3042695,1,8 +160,52,3042880,1,0 +256,232,3043251,1,8 +256,40,3043621,1,0 +256,40,3043806,1,0 +256,40,3043991,5,4 +256,40,3044732,1,8 +464,280,3045288,1,0 +464,280,3045473,1,0 +320,212,3045843,1,0 +184,292,3046214,1,8 +48,216,3046584,1,0 +48,216,3046769,2,0,B|114:316|220:313|276:285|326:249|379:157|320:49,1,500,0|8 +192,48,3048065,1,0 +256,196,3048436,5,0 +192,336,3048806,1,0 +96,208,3049176,1,8 +256,196,3049547,2,0,B|389:236|389:236|322:173|356:54|486:80,2,400 +256,196,3051769,1,0 +256,355,3052139,1,8 +256,196,3052510,2,0,B|122:236|122:236|189:173|155:54|25:80,1,400 +36,239,3053621,1,8 +160,339,3053991,5,0 +196,283,3054176,1,0 +256,255,3054362,1,0 +316,283,3054547,1,8 +352,339,3054732,1,0 +256,159,3055102,1,8 +256,351,3055473,1,0 +256,351,3055658,1,0 +256,351,3055843,5,4 +256,36,3056584,1,8 +256,260,3057139,1,0 +256,260,3057325,2,0,B|256:112,1,132.00000125885 +312,96,3057880,1,0 +316,160,3058065,1,0 +256,188,3058251,1,0 +196,160,3058436,1,0 +200,96,3058621,1,0 +256,64,3058806,5,4 +332,348,3059547,2,0,B|289:366|256:333|256:308|256:308|256:333|222:366|181:348,1,200,8|0 +80,192,3060288,1,8 +256,88,3060658,1,0 +432,192,3061028,1,8 +340,39,3061399,2,0,B|376:115|340:177|285:203|229:203|174:177|138:115|174:39,2,400 +432,192,3063251,5,8 +256,296,3063621,1,0 +80,192,3063991,1,8 +172,345,3064362,2,0,B|136:269|172:207|227:181|283:181|338:207|374:269|338:345,2,400 +356,48,3066584,6,0,B|156:48,1,200,0|8 +52,196,3067325,1,0 +174,338,3067695,2,0,B|321:183|321:183|256:126|256:126|190:183|190:183|337:338,1,600,8|0 +464,192,3069176,1,8 +464,192,3069362,1,8 +464,192,3069547,1,0 +341,61,3069917,6,0,B|309:102|257:126|200:102|170:59,1,200,8|0 +19,154,3070658,2,0,B|49:231|123:301|196:315|255:330|314:315|392:300|461:231|492:152,1,600,4|0 +480,336,3072139,5,8 +384,80,3072510,1,0 +256,288,3072880,1,8 +128,80,3073251,1,0 +32,336,3073621,1,8 +97,274,3073806,1,0 +171,219,3073991,2,0,B|202:175|257:161|311:175|342:219,1,200,0|8 +344,39,3074732,6,0,B|311:82|258:96|203:82|171:39,1,200,0|8 +160,224,3075473,1,0 +352,224,3075843,1,8 +256,352,3076214,1,0 +256,352,3076954,5,0 +256,192,3077325,1,8 +120,76,3077695,2,0,B|147:143|226:120|256:102|256:37|256:37|256:102|292:120|368:144|400:65,1,400 +376,248,3078806,5,8 +256,344,3079176,1,0 +256,140,3079547,1,0 +136,248,3079917,1,0 +256,44,3080288,1,8 +376,248,3080658,1,0 +340,172,3080843,1,8 +256,140,3081028,1,8 +172,172,3081214,1,8 +136,248,3081399,1,8 +256,344,3081769,21,8 +488,104,3082510,2,0,B|500:97|521:64|516:9|478:-2|456:-23|369:-26|316:12|357:88|357:122|390:190|423:190|491:190|495:255|495:288|495:288|496:323|465:380|423:389|358:386|328:339|323:322|323:289|325:257|345:218|345:218|56:190|256:-177|457:190|157:221|157:221|189:250|198:289|190:322|190:338|156:389|90:389|51:373|21:337|19:289|19:289|20:240|33:208|90:190|123:190|156:122|156:88|169:17|140:-9|52:-29|18:-4|-9:55|4:88|27:110,1,2200,4|0 +72,192,3103251,5,0 +168,332,3103621,1,0 +344,332,3103991,1,0 +440,192,3104362,1,0 +256,148,3104732,1,0 +256,148,3105102,1,0 +168,296,3105473,5,0 +200,348,3105658,1,0 +256,364,3105843,1,0 +312,348,3106028,1,0 +344,296,3106214,2,0,B|272:208|348:88|472:128,1,264.0000025177 +500,268,3107510,1,0 +500,268,3107695,1,0 +400,236,3108065,1,0 +300,204,3108436,1,0 +300,100,3108806,1,0 +168,88,3109176,6,0,B|240:176|164:296|40:256,1,264.0000025177 +12,116,3110473,1,0 +12,116,3110658,1,0 +112,148,3111028,1,0 +212,180,3111399,1,0 +212,284,3111769,1,0 +344,288,3112139,6,0,B|410:277|421:196|403:192|403:192|421:189|410:108|344:97,2,264.0000025177 +256,192,3113991,1,0 +168,96,3114362,2,0,B|101:106|90:187|108:191|108:191|90:194|101:275|168:286,1,264.0000025177 +344,284,3115473,1,0 +256,192,3115843,1,0 +168,100,3116214,1,0 +196,44,3116399,1,0 +256,20,3116584,1,0 +316,44,3116769,1,0 +344,100,3116954,1,0 +256,192,3117325,1,0 +168,300,3117695,1,0 +168,300,3117880,1,0 +168,300,3118065,6,0,B|208:344|312:364|448:344|444:164|432:108|372:56|288:-28|144:4|64:152|140:248,2,800,4|8|8 +256,192,3121121,12,8,3122510 +256,192,3122602,12,8,3123991 +256,192,3124084,12,8,3126214 +108,240,3126584,5,0 +168,72,3126954,1,0 +348,72,3127325,1,0 +404,240,3127695,1,8 +256,360,3128065,1,8 +192,312,3128251,1,8 +212,232,3128436,1,8 +300,232,3128621,1,0 +320,312,3128806,1,8 +344,56,3129176,6,0,B|340:104|340:104|288:136,1,100,8|0 +216,131,3129547,2,0,B|171:103|171:103|167:55,1,100,8|0 +256,44,3129917,5,4 +256,44,3130658,1,8 +464,280,3131214,1,0 +464,280,3131399,1,0 +320,212,3131769,1,0 +184,292,3132139,1,8 +48,216,3132510,1,0 +48,216,3132695,2,0,B|115:316|221:313|277:285|327:249|380:157|321:49,1,500,0|8 +192,48,3133991,1,0 +256,196,3134362,5,0 +192,336,3134732,1,0 +96,208,3135102,1,8 +256,195,3135473,2,0,B|390:154|390:154|323:218|357:337|487:311,2,400 +256,196,3137695,5,0 +256,36,3138065,1,8 +256,195,3138436,2,0,B|123:154|123:154|190:218|156:337|26:311,1,400 +36,152,3139547,1,8 +160,52,3139917,1,0 +196,108,3140102,1,0 +256,136,3140288,1,0 +316,108,3140473,1,8 +352,52,3140658,1,0 +256,232,3141028,5,8 +256,40,3141399,1,0 +256,40,3141584,1,0 +256,40,3141769,1,4 +378,372,3142510,6,0,B|348:306|260:264|172:297|136:371,1,300,8|0 +156,204,3143251,2,0,B|372:204,1,200,8|8 +296,32,3143991,2,0,B|314:70|296:101|268:115|240:115|212:101|194:70|212:32,1,200,8|0 +126,127,3144547,2,0,B|174:192|256:213|337:192|385:127,1,300 +428,304,3145473,5,8 +256,364,3145843,1,0 +84,304,3146214,1,0 +332,212,3146584,6,0,B|322:176|258:152|258:152|192:172|180:213,1,200,0|8 +104,72,3147325,2,0,B|189:109|256:42|256:-7|256:-7|256:42|322:109|406:72,1,400 +480,224,3148436,1,8 +360,322,3148806,2,0,B|325:339|290:339|256:322|221:304|204:252|221:201|256:183|290:201|308:252|290:304|256:322|221:339|187:339|152:322,1,400 +32,224,3149917,5,8 +173,146,3150288,2,0,B|181:115|213:90|254:84|254:84|281:74|303:41|281:9|257:3|229:9|201:41|227:74|254:84|254:84|297:90|324:111|338:152,1,400 +492,232,3151399,1,8 +352,344,3151769,2,0,B|312:312|312:312|304:264,1,100,8|0 +392,256,3152139,2,0,B|384:208|384:208|344:176,1,100,8|0 +328,88,3152510,1,8 +256,144,3152695,1,0 +184,88,3152880,1,8 +168,176,3153065,2,0,B|128:208|128:208|120:256,1,100,0|8 +208,264,3153436,2,0,B|200:312|200:312|160:344,1,100,0|4 +160,56,3154362,5,8 +396,92,3154917,1,0 +396,92,3155102,1,0 +336,240,3155473,1,0 +176,240,3155843,1,8 +116,91,3156214,2,0,B|51:116|28:160|20:208|72:284|148:287|204:212,1,300 +176,240,3156954,1,0 +288,116,3157325,1,8 +116,92,3157695,5,0 +116,92,3157880,1,0 +116,92,3158065,1,0 +176,240,3158436,1,0 +336,240,3158806,1,8 +396,91,3159176,2,0,B|460:116|484:160|492:208|440:284|363:287|308:212,1,300 +336,240,3159917,2,0,B|372:288|288:332|256:256|256:256|224:332|140:288|176:240,1,300 +175,240,3160658,1,0 +256,80,3161028,5,0 +388,320,3161399,1,0 +124,320,3161769,1,8 +96,104,3162139,2,0,B|132:10|259:-40|374:4|421:109,1,400 +256,192,3163251,1,8 +96,280,3163621,2,0,B|132:373|259:424|374:379|421:274,1,400 +366,112,3164732,6,0,B|320:92|320:92|300:48,1,100,8|0 +212,46,3165102,2,0,B|192:92|192:92|146:112,1,100,8|0 +207,184,3165473,2,0,B|146:207|146:207|67:176|67:176|99:348|99:348|177:317|177:317|224:348|224:348|256:333|256:333|287:348|287:348|334:317|334:317|412:348|412:348|444:176|444:176|365:207|365:207|303:184,1,1000,4|0 +256,24,3167695,5,8 +396,91,3168065,2,0,B|461:116|484:160|492:208|440:284|364:287|308:212,1,300 +336,240,3168806,1,0 +224,116,3169176,1,8 +396,92,3169547,5,0 +396,92,3169732,1,0 +396,92,3169917,1,0 +336,240,3170288,1,0 +176,240,3170658,1,8 +116,91,3171028,2,0,B|52:116|28:160|20:208|72:284|149:287|204:212,1,300 +176,240,3171769,2,0,B|140:288|224:332|256:256|256:256|288:332|372:288|336:240,1,300 +337,240,3172510,1,0 +256,80,3172880,5,0 +124,320,3173251,1,0 +388,320,3173621,1,8 +416,104,3173991,2,0,B|380:10|253:-40|138:4|91:109,1,400 +256,192,3175102,1,8 +104,304,3175473,2,0,B|189:267|256:334|256:383|256:383|256:334|322:267|406:304,1,400,8|0 +386,195,3176460,5,0 +309,126,3176583,2,0,B|341:62,2,66.6666666666667,8|0|8 +203,126,3176955,2,0,B|171:62,2,66.6666666666667,8|0|8 +126,195,3177325,1,4 diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 60ce67c7f6..3a15b5e6b1 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -30,6 +30,9 @@ false + + $(SolutionDir)\packages\DeepEqual.1.6.0.0\lib\net40\DeepEqual.dll + $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True @@ -147,6 +150,7 @@ + \ No newline at end of file diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config index ecc44f0c70..e09f2a07ba 100644 --- a/osu.Game.Tests/packages.config +++ b/osu.Game.Tests/packages.config @@ -4,6 +4,7 @@ Copyright (c) 2007-2017 ppy Pty Ltd . Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE --> + From 851c20aff0be3c55fc84c47d8b137de3cbc59222 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 11:17:32 +0900 Subject: [PATCH 143/454] Add a few comments --- osu.Game/Audio/SampleInfo.cs | 4 ++++ osu.Game/IO/Serialization/IJsonSerializable.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index f8b5bf33d9..3c3a64dbb2 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -15,6 +15,10 @@ namespace osu.Game.Audio public const string HIT_NORMAL = @"hitnormal"; public const string HIT_CLAP = @"hitclap"; + /// + /// The that is used for and + /// if the values have not already been provided by the hitobject. + /// [JsonIgnore] public SoundControlPoint ControlPoint; diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs index 8d10f0b291..38e7f47656 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/IJsonSerializable.cs @@ -20,6 +20,10 @@ namespace osu.Game.IO.Serialization public static T DeepClone(this T obj) where T : IJsonSerializable => Deserialize(Serialize(obj)); + /// + /// Creates the default that should be used for all s. + /// + /// public static JsonSerializerSettings CreateGlobalSettings() => new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, From b0684cb194c11a24e6293e38bee98d3f4fc94191 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 12:02:34 +0900 Subject: [PATCH 144/454] Add storyboard test case but disable for now --- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 23 +- ...ow - Rengetsu Ouka (_Kiva) [Yuki YukI].osu | 1231 +++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + 3 files changed, 1245 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Tests/Resources/Kozato snow - Rengetsu Ouka (_Kiva) [Yuki YukI].osu diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index c3ebcb9e7c..5a66a7047d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -19,13 +19,14 @@ namespace osu.Game.Tests.Beatmaps.Formats [TestFixture] public class OsuJsonDecoderTest { - private const string beatmap_1 = "Soleily - Renatus (Gamu) [Insane].osu"; - private const string beatmap_2 = "Within Temptation - The Unforgiving (Armin) [Marathon].osu"; + private const string normal = "Soleily - Renatus (Gamu) [Insane].osu"; + private const string marathon = "Within Temptation - The Unforgiving (Armin) [Marathon].osu"; + private const string with_sb = "Kozato snow - Rengetsu Ouka (_Kiva) [Yuki YukI].osu"; [Test] public void TestDecodeMetadata() { - var beatmap = decodeAsJson(beatmap_1); + var beatmap = decodeAsJson(normal); var meta = beatmap.BeatmapInfo.Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); @@ -43,7 +44,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeGeneral() { - var beatmap = decodeAsJson(beatmap_1); + var beatmap = decodeAsJson(normal); var beatmapInfo = beatmap.BeatmapInfo; Assert.AreEqual(0, beatmapInfo.AudioLeadIn); Assert.AreEqual(false, beatmapInfo.Countdown); @@ -57,7 +58,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeEditor() { - var beatmap = decodeAsJson(beatmap_1); + var beatmap = decodeAsJson(normal); var beatmapInfo = beatmap.BeatmapInfo; int[] expectedBookmarks = @@ -78,7 +79,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeDifficulty() { - var beatmap = decodeAsJson(beatmap_1); + var beatmap = decodeAsJson(normal); var difficulty = beatmap.BeatmapInfo.BaseDifficulty; Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(4, difficulty.CircleSize); @@ -91,7 +92,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeColors() { - var beatmap = decodeAsJson(beatmap_1); + var beatmap = decodeAsJson(normal); Color4[] expected = { new Color4(142, 199, 255, 255), @@ -109,7 +110,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeHitObjects() { - var beatmap = decodeAsJson(beatmap_1); + var beatmap = decodeAsJson(normal); var curveData = beatmap.HitObjects[0] as IHasCurve; var positionData = beatmap.HitObjects[0] as IHasPosition; @@ -128,8 +129,10 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); } - [TestCase(beatmap_1)] - [TestCase(beatmap_2)] + [TestCase(normal)] + [TestCase(marathon)] + // Currently fails: + // [TestCase(with_sb)] public void TestParity(string beatmap) { var beatmaps = decode(beatmap); diff --git a/osu.Game.Tests/Resources/Kozato snow - Rengetsu Ouka (_Kiva) [Yuki YukI].osu b/osu.Game.Tests/Resources/Kozato snow - Rengetsu Ouka (_Kiva) [Yuki YukI].osu new file mode 100644 index 0000000000..38db42fddc --- /dev/null +++ b/osu.Game.Tests/Resources/Kozato snow - Rengetsu Ouka (_Kiva) [Yuki YukI].osu @@ -0,0 +1,1231 @@ +osu file format v9 + +[General] +AudioFilename: Kozato snow.mp3 +AudioLeadIn: 1000 +PreviewTime: 173263 +Countdown: 0 +SampleSet: Soft +StackLeniency: 0.5 +Mode: 0 +LetterboxInBreaks: 0 + +[Editor] +Bookmarks: 14893,27933,40766,54427,67053,80300,94167,107414,120247,133919,147166,160424,173673,187336,200172 +DistanceSpacing: 0.5 +BeatDivisor: 4 +GridSize: 8 + +[Metadata] +Title:Rengetsu Ouka +Artist:Kozato snow +Creator:_Kiva +Version:Yuki YukI +Source: +Tags:Kiva Snowy Dream Yumeko Yuki HakuNoKaemi wmfchris aabc271 + +[Difficulty] +HPDrainRate:8 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8 +SliderMultiplier:1.8 +SliderTickRate:0.5 + +[Events] +//Background and Video events +0,0,"BG_example.JPG" +//Break Periods +2,54644,66322 +2,67893,79572 +2,107641,119526 +2,160627,172513 +//Storyboard Layer 0 (Background) +Sprite,Background,Centre,"SB\bg2.png",320,240 + S,0,26288,26495,1,1.04096 + F,0,26288,27530,1,0 + S,0,26495,27530,1.04096,1.26624 +Sprite,Background,Centre,"SB\C32.png",320,240 + M,0,41193,54436,259,283,320,240 + F,0,41193,54436,1 + S,0,41193,54436,1.2048,1.13312 + R,0,41193,54436,0,0.1023999 + F,0,54436,55264,1,0 + S,0,54436,55264,1.13312,1.29696 +Sprite,Background,Centre,"SB\bg1.png",320,240 + F,0,27944,,1 + S,0,27944,41193,1,1.327681 + S,0,41193,,1.327681 +Sprite,Background,Centre,"SB\bg3.png",320,240 + F,0,160427,162083,0,1 + S,0,160427,162083,1 + S,0,162083,166829,1,1.035869 + F,0,162083,173677,1,0.9987923 + S,0,166829,173677,1.035869,1.11264 + F,0,173677,173780,0.9987923,0.8605523 +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +Sprite,Foreground,Centre,"SB\bg2.png",320,240 + F,0,1445,2687,0.8,0 + S,0,1445,2687,1,1.16 +Sprite,Foreground,Centre,"SB\Mini Light.png",320,240 + M,0,24631,,140,147 + S,0,24631,24838,0.19104,0.488 + F,0,24631,25045,1,0 + S,0,24838,25045,0.488,0.5903998 +Sprite,Foreground,Centre,"SB\MINI SNOW.png",320,240 + M,0,25045,,472,224 + S,0,25045,25252,0.32416,0.5392001 + F,0,25045,25459,1,0 + S,0,25252,25459,0.5392001,0.6518402 +Sprite,Foreground,Centre,"SB\black.png",320,240 + F,0,26702,27944,0,1 + F,0,27944,28358,1,0.55296 + F,0,28358,28772,0.55296,0.005119934 +Sprite,Foreground,Centre,"SB\OP effect.png",320,240 + M,0,25459,,193,293 + F,0,25459,25666,1,0 + S,0,25459,25666,0.61088,1.24576 +Sprite,Foreground,Centre,"SB\OP effect.png",320,240 + M,0,25666,,387,373 + P,0,25666,,H + V,0,25666,,1.06144,1 + V,0,25666,,1.06144,1 + F,0,25666,25873,1,0 + S,0,25666,25873,0.6313599,1.4096 +Sprite,Foreground,Centre,"SB\OP effect.png",320,240 + M,0,25873,,417,134 + M,0,25873,,417,134 + P,0,25873,,V + F,0,25873,26080,1,0 + S,0,25873,26080,1,1.52224 +Sprite,Foreground,Centre,"SB\OP effect.png",320,240 + F,0,26080,26288,1,0 + S,0,26080,26288,1,1.54272 +Sprite,Foreground,Centre,"SB\sakura_b.png",320,240 + S,0,26288,26391,0.3,0.7 + F,0,26288,27116,1 + S,0,26391,27530,0.7,0.9 + F,0,27116,27530,1,0 +Sprite,Foreground,Centre,"SB\sakura_b.png",320,240 + M,0,26288,,529,377 + P,0,26288,,H + S,0,26288,26391,0.4,0.55 + F,0,26288,26702,1,0 + S,0,26391,26702,0.55,0.9 +Sprite,Foreground,Centre,"SB\sakura_b.png",320,240 + M,0,26288,,202,210 + P,0,26288,,H + P,0,26288,,V + S,0,26288,26391,0.4,0.64 + F,0,26288,26702,1,0 + S,0,26391,26702,0.64,0.7 +Sprite,Foreground,Centre,"SB\azure.png",320,240 + F,0,40779,41193,0,1 + F,0,41193,41814,1,0 +Sprite,Foreground,Centre,"SB\sakura_b",320,240 + F,0,54436,55264,0,1 + R,0,54436,61060,0.2252804,0 + S,0,54443,,1.5625 + M,0,54443,61060,378,177,304,342 + F,0,55264,59404,1 + F,0,59404,61060,1,0 +Sprite,Foreground,Centre,"SB\bg2.png",320,240 + F,0,61060,61267,0,0.7122133 + S,0,61060,62302,1,1.1024 + F,0,61267,62302,0.7122133,0 +Sprite,Foreground,Centre,"SB\bg2.png",320,240 + F,0,67685,67892,0,0.8333333 + S,0,67685,68927,1,1.13312 + F,0,67892,68927,0.8333333,0 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,80935,,305,116 + F,0,80935,81038,1,0.9206253 + S,0,80935,81038,0.3548797,0.7181275 + S,0,81038,,0.7181275 + F,0,81038,81349,0.9206253,0.6809599 + S,0,81038,81556,0.7181275,0.8000475 + F,0,81349,81556,0.6809599,0 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + F,0,14694,14901,0,0.8333333 + S,0,14694,14901,0.7337599,1.41984 + S,0,14901,15108,1.41984,1.6144 + F,0,14901,15936,0.8333333,0 + S,0,15108,15522,1.6144,1.80896 + S,0,15522,15936,1.80896,1.94208 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + S,0,67685,,1 + R,0,67685,,0 + F,0,67685,68513,0,0.7696 + M,0,67685,74310,102,25,93,434 + F,0,68513,69341,0.7696,1 + F,0,69341,70169,1,0.7696 + F,0,70169,70998,0.7696,1 + F,0,70998,71826,1,0.8771199 + F,0,71826,72654,0.8771199,1 + F,0,72654,73482,1,0.73888 + F,0,73482,74310,0.73888,0 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + S,0,67685,,1 + R,0,67685,,0 + F,0,67685,68513,0,0.7696 + M,0,67685,74310,411,130,387,308 + F,0,68513,69341,0.7696,1 + F,0,69341,70169,1,0.7696 + F,0,70169,70998,0.7696,1 + F,0,70998,71826,1,0.8771199 + F,0,71826,72654,0.8771199,1 + F,0,72654,73482,1,0.73888 + F,0,73482,74310,0.73888,0 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + S,0,67685,,1 + R,0,67685,,0 + F,0,67685,68513,0,0.7696 + M,0,67685,74310,102,25,93,434 + F,0,68513,69341,0.7696,1 + F,0,69341,70169,1,0.7696 + F,0,70169,70998,0.7696,1 + F,0,70998,71826,1,0.8771199 + F,0,71826,72654,0.8771199,1 + F,0,72654,73482,1,0.73888 + F,0,73482,74310,0.73888,0 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + S,0,67685,,1 + R,0,67685,,0 + F,0,67685,68513,0,0.7696 + M,0,67685,74310,179,179,203,467 + F,0,68513,69341,0.7696,1 + F,0,69341,70169,1,0.7696 + F,0,70169,70998,0.7696,1 + F,0,70998,71826,1,0.8771199 + F,0,71826,72654,0.8771199,1 + F,0,72654,73482,1,0.73888 + F,0,73482,74310,0.73888,0 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + S,0,67685,,1 + R,0,67685,,0 + F,0,67685,68513,0,0.7696 + M,0,67685,74310,237,361,274,534 + F,0,68513,69341,0.7696,1 + F,0,69341,70169,1,0.7696 + F,0,70169,70998,0.7696,1 + F,0,70998,71826,1,0.8771199 + F,0,71826,72654,0.8771199,1 + F,0,72654,73482,1,0.73888 + F,0,73482,74310,0.73888,0 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + S,0,67685,,0.63136 + R,0,67685,,0 + F,0,67685,68513,0,0.7696 + M,0,67685,74310,227,175,267,525 + F,0,68513,69341,0.7696,1 + F,0,69341,70169,1,0.7696 + F,0,70169,70998,0.7696,1 + F,0,70998,71826,1,0.8771199 + F,0,71826,72654,0.8771199,1 + F,0,72654,73482,1,0.73888 + F,0,73482,74310,0.73888,0 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + S,0,67685,,0.41632 + R,0,67685,,0 + F,0,67685,68513,0,0.7696 + M,0,67685,74310,564,-67,619,531 + F,0,68513,69341,0.7696,1 + F,0,69341,70169,1,0.7696 + F,0,70169,70998,0.7696,1 + F,0,70998,71826,1,0.8771199 + F,0,71826,72654,0.8771199,1 + F,0,72654,73482,1,0.73888 + F,0,73482,74310,0.73888,0 +Sprite,Foreground,Centre,"SB\SNOW.png",320,240 + S,0,67685,,2.35168 + F,0,67685,70998,0,1 + M,0,67685,80314,320,-211,317,693 + F,0,68513,80314,1,0 + F,0,70998,79279,1 + F,0,79279,80314,1,0 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + F,0,70998,71826,0,1 + M,0,70998,74299,257,66,263,208 + F,0,71826,72654,1,0.7132799 + F,0,72654,73689,0.7132799,1 + F,0,73689,74310,1,0 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + S,0,70998,,0.7030401 + F,0,70998,71826,0,1 + M,0,70998,74310,431,109,422,223 + F,0,71826,72654,1,0.7132799 + F,0,72654,73689,0.7132799,1 + F,0,73689,74310,1,0 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + F,0,70998,71826,0,1 + M,0,70998,74299,73,-34,66,270 + F,0,71826,72654,1,0.7132799 + F,0,72654,73689,0.7132799,1 + F,0,73689,74310,1,0 +Animation,Foreground,Centre,"SB\am2\am2.png",320,240,5,500,LoopForever + S,0,70998,,0.6825599 + F,0,70998,71826,0,1 + M,0,70998,74299,390,75,370,115 + F,0,71826,72654,1,0.7132799 + F,0,72654,73689,0.7132799,1 + F,0,73689,74310,1,0 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,81763,,249,251 + S,0,81763,81866,0.39584,0.6620799 + F,0,81763,82384,1,0 + S,0,81866,82384,0.6620799,0.8975999 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,82591,,152,394 + S,0,82591,82694,0.3651201,0.7952 + F,0,82591,83212,1,0 + S,0,82694,83212,0.7952,1.01024 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,83419,,456,324 + S,0,83419,83523,0.3856,0.744 + F,0,83419,84040,1,0 + S,0,83523,84040,0.744,1.02048 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,85075,,321,197 + S,0,85075,85179,0.37536,0.80544 + F,0,85075,85696,0.9948799,0 + S,0,85179,85696,0.80544,1.12288 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,85903,,248,284 + S,0,85903,86007,0.3344,0.8464 + F,0,85903,86524,1,0 + S,0,86007,86524,0.8464,1.2048 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,86731,,207,101 + S,0,86731,86835,0.37536,0.8 + F,0,86731,87352,1,0 + S,0,86835,87352,0.8463999,1.22528 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,87560,,472,229 + S,0,87560,87663,0.3,0.8 + F,0,87560,88180,1,0 + S,0,87663,88180,0.8,1.12 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,88388,,408,373 + S,0,88388,88491,0.3,0.8 + F,0,88388,88905,1,0 + S,0,88491,88905,0.8,1.12 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,89216,,321,136 + S,0,89216,89319,0.3,0.8 + F,0,89216,89733,1,0 + S,0,89319,89733,0.8,1.12 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,90044,,257,252 + S,0,90044,90147,0.3,0.8 + F,0,90044,90561,1,0 + S,0,90147,90561,0.8,1.12 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,90872,,265,292 + S,0,90872,90975,0.3,0.8 + F,0,90872,91389,1,0 + S,0,90975,91389,0.8,1.12 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,91700,,265,132 + S,0,91700,91804,0.3,0.8 + F,0,91700,92217,1,0 + S,0,91804,92217,0.8,1.12 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,92528,,521,252 + S,0,92528,92632,0.3,0.8 + F,0,92528,93045,1,0 + S,0,92632,93045,0.8,1.12 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,93356,,304,309 + S,0,93356,93460,0.3,0.8 + F,0,93356,93873,1,0 + S,0,93460,93873,0.8,1.12 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,84247,,495,122 + S,0,84247,84351,0.3855998,0.7849598 + F,0,84247,84868,1,0 + S,0,84351,84868,0.7849598,1.13312 +Sprite,Foreground,Centre,"SB\bg2.png",320,240 + M,0,94184,,320,240 + S,0,94184,94391,1,1.05632 + F,0,94184,95012,1,0 + S,0,94391,95012,1.05632,1.19456 +Sprite,Foreground,Centre,"SB\azure.png",320,240 + F,0,100809,102051,0.38048,0 +Sprite,Foreground,Centre,"SB\azure.png",320,240 + R,0,120276,,0 + F,0,120276,120690,0,1 + F,0,120690,121518,1,0 +Sprite,Foreground,Centre,"SB\bg4.png",320,240 + F,0,107441,133112,1 + S,0,107441,133112,1.3,1 + R,0,107441,133112,0.2,0 +Sprite,Foreground,Centre,"SB\azure.png",320,240 + F,0,107020,107434,0,1 + F,0,107434,108269,1,0 +Sprite,Foreground,Centre,"SB\C32.png",320,240 + F,0,173682,190322,1 + M,0,173682,200181,228,268,320,240 + S,0,173682,200181,1.70656,1 + R,0,173682,200181,0.3072,0 + F,0,190322,192412,1 + F,0,192412,200181,1 + F,0,200181,201009,1,0 +Sprite,Foreground,Centre,"SB\sakura_b.png",320,240 + F,0,173677,173682,0.69792,0.3702399 + S,0,173682,180306,1,1.43008 + R,0,173682,180306,0.04096008,2.7648 + F,0,173682,186103,0.3702399 + S,0,180306,186931,1.43008,1.64512 + R,0,180306,186931,2.7648,4.68992 + F,0,186103,186931,0.3702399,0 +Sprite,Foreground,Centre,"SB\MINI SNOW.png",320,240 + F,0,186931,187759,0,1 + S,0,186931,200181,1,1.53248 + R,0,186931,200181,1.92512,0 + F,0,187759,199353,1 + F,0,199353,200181,1,0 +Sprite,Foreground,Centre,"SB\sakura_b.png",320,240 + F,0,186931,188173,0,0.3503307 + S,0,186931,199353,1,1.28672 + R,0,186931,199353,-3.23584,-1.76128 + F,0,188173,198110,0.3503307,0.19104 + F,0,198110,199353,0.19104,0.005120086 + F,0,199353,200724,0.005120086,0 +Animation,Foreground,Centre,"SB\am1\am1.jpg",320,240,5,500,LoopForever + M,0,74310,,313,234 + F,0,74310,74724,0.05792008,1 + F,0,74724,75345,1,0.8813094 + F,0,75345,75966,0.8813094,0.9981386 + F,0,75966,76794,0.9981386,0.8279376 + F,0,76794,77622,0.8279376,0.9956568 + F,0,77622,78450,0.9956568,0.7588959 + F,0,78450,79279,0.7588959,0.9931734 + F,0,79279,80107,0.9931734,0.01013343 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + M,0,74310,,313,232 + S,0,74310,74413,0.3139198,0.6415999 + F,0,74310,75138,1,0 + S,0,74413,75138,0.6415999,0.7961942 +Sprite,Foreground,Centre,"SB\bg2.png",320,240 + F,0,133112,133940,0,1 + F,0,133940,147178,1 + S,0,133940,147178,1,1.16384 + F,0,147178,147592,1,0 + S,0,147178,147592,1.16384,1.33792 +Sprite,Foreground,Centre,"SB\sakura_b.png",320,240 + M,0,120690,,320,240 + S,0,120690,120794,1,1.29696 + F,0,120690,121104,1 + S,0,120794,121932,1.29696,1.44032 + F,0,121104,121932,1,0.002415478 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + S,0,160427,160531,0.7337601,1.4608 + F,0,160427,162083,1,0 + S,0,160531,161255,1.4608,1.77824 + S,0,161255,162083,1.77824,2.03615 +Sprite,Foreground,Centre,"SB\azure.png",320,240 + R,0,132284,,0 + F,0,132284,133112,0,1 + F,0,133112,133940,1,0 +Sprite,Foreground,Centre,"SB\wakka.png",320,240 + S,0,133940,134043,1,1.38912 + F,0,133940,134457,1,0.80544 + S,0,134043,134975,1.38912,1.89088 + F,0,134457,134975,0.80544,0 +Sprite,Foreground,Centre,"SB\SNOW.png",320,240 + S,0,108269,,2.37216 + F,0,108269,109097,0,1 + M,0,108269,120173,314,-250,315,229 + F,0,109097,112409,1 + F,0,112409,112823,1,0.80032 + F,0,112823,113237,0.80032,1 + F,0,113237,119344,1 + F,0,119344,120173,1,0 +Sprite,Foreground,Centre,"SB\black.png",320,240 + S,0,173263,,1 + F,0,173263,173682,0,1 + F,0,173682,174510,1,0 + F,0,173682,174510,1,0.004830897 +Sprite,Foreground,Centre,"SB\White Effect.png",320,240 + M,0,173677,,217,128 + F,0,173677,174510,1,0 + S,0,173677,174510,0.2115199,1.09216 +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +1445,414.050100062108,4,2,1,70,1,0 +13659,-100,4,2,1,40,0,0 +13762,-100,4,2,1,45,0,0 +13866,-100,4,2,1,50,0,0 +13970,-100,4,2,1,55,0,0 +14073,-100,4,2,1,50,0,0 +14177,-100,4,2,1,55,0,0 +14280,-100,4,2,1,60,0,0 +14384,-100,4,2,1,75,0,0 +14487,-100,4,2,1,80,0,0 +14591,-100,4,2,1,85,0,0 +14694,-100,4,2,1,60,0,0 +14901,-100,4,2,0,45,0,0 +15005,-100,4,2,1,60,0,0 +15315,-100,4,2,0,45,0,0 +15419,-100,4,2,1,60,0,0 +15626,-100,4,2,0,45,0,0 +15833,-100,4,2,1,60,0,0 +16040,-100,4,2,0,45,0,0 +16247,-100,4,2,1,60,0,0 +16454,-100,4,2,0,45,0,0 +16764,-100,4,2,1,60,0,0 +16868,-100,4,2,0,45,0,0 +17178,-100,4,2,1,60,0,0 +17282,-100,4,2,0,45,0,0 +17489,-100,4,2,1,60,0,0 +17696,-100,4,2,0,45,0,0 +17903,-100,4,2,1,60,0,0 +18110,-100,4,2,0,45,0,0 +18317,-100,4,2,1,60,0,0 +18628,-100,4,2,0,45,0,0 +18731,-100,4,2,1,60,0,0 +18938,-100,4,2,0,45,0,0 +19145,-100,4,2,1,60,0,0 +19352,-100,4,2,0,45,0,0 +19559,-100,4,2,1,60,0,0 +19766,-100,4,2,0,45,0,0 +19973,-100,4,2,1,60,0,0 +20180,-100,4,2,0,45,0,0 +20387,-100,4,2,1,60,0,0 +20594,-100,4,2,0,45,0,0 +20801,-100,4,2,1,60,0,0 +21112,-100,4,2,0,45,0,0 +21215,-100,4,2,1,60,0,0 +21422,-100,4,2,0,45,0,0 +21629,-100,4,2,1,60,0,0 +21836,-100,4,2,0,45,0,0 +22043,-100,4,2,1,60,0,0 +22354,-100,4,2,0,45,0,0 +22458,-100,4,2,1,60,0,0 +22768,-100,4,2,0,45,0,0 +22872,-100,4,2,1,60,0,0 +23079,-100,4,2,0,45,0,0 +23286,-100,4,2,1,60,0,0 +23596,-100,4,2,0,45,0,0 +23700,-100,4,2,1,60,0,0 +23907,-100,4,2,0,45,0,0 +24114,-100,4,2,1,60,0,0 +24321,-100,4,2,0,45,0,0 +24528,-100,4,1,1,60,0,0 +27323,-100,4,2,1,60,0,0 +27530,-100,4,2,1,60,0,0 +27944,-100,4,2,1,60,0,0 +29082,-100,4,2,1,65,0,0 +39951,-100,4,2,1,80,0,0 +40158,-100,4,2,1,85,0,0 +40766,-100,4,2,1,30,0,0 +41193,-100,4,2,1,85,0,0 +41814,-100,4,2,1,85,0,0 +43471,-100,4,2,1,75,0,0 +44092,-100,4,2,1,65,0,0 +44299,-100,4,2,1,55,0,0 +44506,-100,4,2,1,45,0,0 +44920,-100,4,2,1,50,0,0 +45437,-100,4,2,1,55,0,0 +46162,-100,4,2,1,60,0,0 +47094,-100,4,2,1,65,0,0 +47818,-100,4,2,1,55,0,0 +48750,-100,4,2,1,60,0,0 +50095,-100,4,2,1,65,0,0 +54444,414.050100062108,4,2,1,70,1,0 +67383,-100,4,2,1,50,0,0 +67590,-100,4,2,1,70,0,0 +102680,-200,4,2,1,70,0,0 +105993,-100,4,2,1,70,0,0 +107441,414.050100062108,4,2,1,70,1,0 +107648,-100,4,2,1,70,0,0 +120380,-100,4,2,1,75,0,0 +132905,-100,4,2,1,35,0,0 +133008,-100,4,2,1,40,0,0 +133112,-100,4,2,1,45,0,0 +133215,-100,4,2,1,45,0,0 +133319,-100,4,2,1,50,0,0 +133422,-100,4,2,1,55,0,0 +133526,-100,4,2,1,60,0,0 +133629,-100,4,2,1,65,0,0 +133733,-100,4,2,1,70,0,0 +133836,-100,4,2,1,75,0,0 +133940,-100,4,2,1,80,0,1 +136217,-100,4,2,1,60,0,1 +137045,-100,4,2,1,80,0,1 +147174,-100,4,2,1,80,0,0 +147178,414.050100062108,4,2,1,70,1,0 +173263,-100,4,2,1,60,0,0 +173682,414.050100062108,4,2,1,70,1,0 +174303,-100,4,2,1,40,0,0 +174406,-100,4,2,1,45,0,0 +174510,-100,4,2,1,50,0,0 +174613,-100,4,2,1,55,0,0 +174717,-100,4,2,1,60,0,0 +174820,-100,4,2,1,65,0,0 +174924,-100,4,2,1,70,0,0 +175027,-100,4,2,1,75,0,0 +175131,-100,4,2,1,80,0,0 +186517,-100,4,2,1,30,0,0 +186931,-100,4,2,1,80,0,1 +187035,-100,4,2,1,80,0,1 +196247,-100,4,2,1,70,0,1 +196454,-100,4,2,1,60,0,1 +196661,-100,4,2,1,50,0,1 +196868,-100,4,2,1,80,0,1 +200181,-100,4,2,1,80,0,0 + +[Colours] +Combo1 : 152,48,207 +Combo2 : 254,75,169 +Combo3 : 226,5,221 +SliderBorder : 224,224,224 + +[HitObjects] +112,96,1030,1,0 +144,108,1134,1,0 +176,92,1237,1,0 +208,108,1341,1,0 +240,96,1445,1,0 +408,272,2894,5,0 +408,272,2997,1,0 +408,272,3101,2,0,B|360:280|312:272,1,90 +104,276,3515,2,0,B|152:268|200:276,1,90 +216,184,3929,1,0 +168,88,4136,1,0 +256,144,4343,1,0 +208,48,4550,1,0 +312,64,4757,5,0 +416,64,4964,1,0 +416,64,5067,2,0,B|448:104|464:144|464:200,1,135 +463,187,5481,1,0 +463,187,5585,1,0 +437,289,5792,1,0 +337,262,5999,1,0 +364,162,6206,1,0 +464,188,6413,1,0 +464,88,6620,6,0,B|424:64|368:56,1,90 +280,56,7034,2,0,B|184:56,1,90 +190,56,7448,1,0 +128,136,7655,1,0 +120,168,7759,1,0 +128,200,7862,1,0 +136,232,7966,1,0 +128,264,8069,1,0 +192,336,8276,5,0 +192,336,8380,2,0,B|240:336|288:328|336:312,1,135 +324,315,8794,1,0 +324,315,8897,1,0 +336,216,9104,1,0 +440,200,9311,1,0 +480,104,9518,1,0 +392,40,9726,6,0,B|288:40,1,90 +224,88,10140,1,0 +216,104,10243,1,0 +208,120,10347,1,0 +200,136,10450,1,0 +192,152,10554,1,0 +256,296,10761,1,0 +256,152,10968,1,0 +256,296,11175,1,0 +168,344,11382,5,0 +168,248,11589,1,0 +168,248,11692,1,0 +128,112,12003,1,0 +128,112,12106,1,0 +128,112,12210,2,0,B|144:160|128:208,1,90 +48,248,12624,2,0,B|32:200|48:152,1,90 +48,64,13038,5,0 +48,64,13245,2,0,B|96:64,2,45 +208,64,13659,1,0 +240,64,13762,1,0 +272,72,13866,1,0 +304,88,13970,1,0 +328,112,14073,1,8 +344,144,14177,1,8 +352,176,14280,1,8 +352,208,14384,5,0 +344,240,14487,1,0 +328,272,14591,1,0 +304,296,14694,2,0,B|264:320|216:328,1,90,0|2 +104,280,15108,2,0,B|144:256|192:248,1,90,0|2 +280,224,15522,1,0 +296,120,15729,1,2 +192,96,15936,1,0 +168,192,16143,5,2 +168,192,16247,1,0 +168,192,16350,1,0 +144,288,16557,2,0,B|208:288|208:288|232:264,1,90,2|0 +168,192,16971,2,0,B|104:192|104:192|80:216,1,90,2|0 +144,288,17385,1,2 +256,352,17592,1,0 +368,288,17799,1,2 +256,104,18007,5,0 +256,104,18214,1,2 +256,104,18317,2,0,B|256:240,1,135,0|2 +256,239,18731,1,0 +256,239,18835,1,0 +168,296,19042,1,2 +256,352,19249,1,0 +168,296,19456,1,2 +256,239,19663,1,0 +344,296,19870,1,2 +256,239,20077,1,0 +136,152,20284,5,2 +256,96,20491,1,0 +376,152,20698,1,2 +424,264,20905,1,0 +416,264,21008,1,0 +408,264,21112,1,2 +400,264,21215,1,0 +392,264,21319,1,0 +288,264,21526,1,2 +288,264,21629,1,0 +136,208,21940,5,2 +136,208,22043,1,0 +136,208,22147,2,0,B|136:144|136:144|160:128,1,90,0|2 +248,104,22561,2,0,B|248:168|248:168|224:184,1,90,0|2 +136,104,22975,1,0 +248,56,23182,1,2 +360,104,23389,1,0 +376,120,23493,1,0 +392,136,23596,1,2 +408,152,23700,1,0 +424,168,23803,1,0 +432,280,24010,5,2 +256,328,24217,1,0 +80,280,24424,1,2 +256,192,24631,12,0,27116 +256,192,27323,5,0 +256,192,27426,1,0 +256,192,27530,1,4 +72,136,27944,5,0 +161,84,28151,1,0 +158,182,28358,1,0 +96,80,28565,1,0 +192,32,28772,5,0 +224,32,28875,1,0 +256,32,28979,1,0 +288,32,29082,1,0 +320,32,29186,2,0,B|368:32,4,45 +272,112,29807,6,0,B|248:160|272:200,1,90 +360,160,30221,1,0 +368,168,30324,1,0 +376,176,30428,1,0 +384,184,30532,1,0 +392,192,30635,2,0,B|448:192,4,45 +328,280,31256,5,0 +224,296,31463,1,0 +128,248,31670,2,0,B|96:216|104:160,1,90 +176,88,32084,2,0,B|208:120|200:176,1,90 +128,248,32498,2,0,B|96:280,2,45 +216,192,32912,2,0,B|256:168|304:192,1,90 +264,288,33326,2,0,B|304:312|352:288,1,90 +408,208,33740,1,0 +424,104,33947,1,0 +336,40,34154,6,0,B|288:40,4,45,0|0|0|0|8 +336,136,34776,1,0 +176,136,34983,1,0 +256,72,35190,1,0 +256,112,35293,1,0 +256,160,35397,1,0 +88,264,35604,2,0,B|128:240|176:240,1,90 +424,264,36018,2,0,B|384:240|336:240,1,90 +208,320,36432,6,0,B|208:232,1,90 +304,320,36846,2,0,B|304:232,1,90 +256,144,37260,1,0 +256,144,37467,1,0 +256,144,37570,1,0 +256,144,37674,1,0 +336,80,37881,1,0 +424,136,38088,1,0 +424,136,38191,1,0 +424,136,38295,2,0,B|424:232,1,90 +344,272,38709,5,0 +344,176,38916,1,0 +256,328,39123,1,0 +256,120,39330,1,0 +176,224,39537,5,0 +176,224,39744,1,0 +176,224,39848,1,0 +176,224,39951,1,0 +120,128,40158,1,0 +120,128,40262,1,0 +120,128,40365,1,0 +224,128,40572,1,0 +224,128,40676,1,0 +224,128,40779,6,0,B|224:96,7,22.5 +224,105,41193,6,0,B|312:104,1,90,4|0 +400,152,41607,1,0 +400,256,41814,2,0,B|432:288,4,45 +304,312,42435,1,0 +208,256,42642,1,0 +208,256,42746,1,0 +208,256,42850,2,0,B|208:168,1,90 +168,80,43264,6,0,B|208:56|256:80,1,90 +272,112,43574,1,0 +288,144,43678,2,0,B|312:184|368:192,1,90 +368,96,44092,1,0 +368,288,44299,1,0 +280,336,44506,6,0,B|240:368,4,45 +232,248,45127,1,0 +232,248,45230,1,0 +128,248,45437,1,0 +120,248,45541,1,0 +112,248,45644,1,0 +104,248,45748,1,0 +48,168,45955,1,0 +104,80,46162,5,0 +120,80,46265,1,0 +136,80,46369,1,0 +152,80,46472,1,0 +168,80,46576,1,0 +272,80,46783,1,0 +272,80,46886,1,0 +376,80,47094,1,0 +400,80,47197,1,0 +424,80,47301,1,0 +448,80,47404,1,0 +456,176,47611,5,0 +400,256,47818,1,8 +368,256,47922,1,0 +336,256,48025,1,0 +304,256,48129,1,0 +272,256,48232,1,0 +176,256,48439,1,0 +176,256,48543,1,0 +80,256,48750,5,0 +80,256,48853,1,0 +80,256,48957,1,0 +80,256,49060,1,0 +128,168,49267,1,0 +32,168,49474,1,0 +32,168,49578,1,0 +32,168,49681,1,0 +32,168,49785,1,0 +32,168,49888,1,0 +80,80,50095,5,0 +96,72,50199,1,0 +200,40,50406,1,0 +224,40,50509,1,0 +248,40,50613,1,0 +272,40,50716,1,0 +360,96,50923,1,0 +415,175,51131,5,0 +336,230,51338,1,0 +281,151,51545,1,0 +360,96,51752,1,0 +360,96,51855,1,0 +360,96,51959,1,0 +442,214,52166,1,0 +323,297,52373,1,0 +241,178,52580,1,0 +241,178,52683,1,0 +241,178,52787,1,0 +256,192,52890,12,4,54444 +168,112,67072,5,0 +168,112,67176,1,0 +168,112,67279,1,0 +168,112,67486,1,8 +168,112,67693,1,4 +144,96,80322,5,0 +144,96,80425,1,0 +144,96,80529,1,0 +144,96,80736,1,0 +240,56,80943,1,12 +280,152,81150,1,0 +280,152,81253,1,0 +280,152,81357,1,0 +184,192,81564,1,0 +184,192,81771,1,4 +88,232,81978,5,0 +88,232,82185,1,0 +88,232,82392,1,0 +88,336,82599,2,8,B|184:336,1,90,8|0 +208,336,82909,1,0 +240,336,83013,2,0,B|328:336,1,90 +392,264,83427,1,4 +464,168,83634,1,0 +392,264,83841,1,0 +464,168,84048,1,0 +432,64,84255,6,8,B|336:64,1,90,8|0 +80,64,84669,2,8,B|176:64,1,90,0|0 +256,136,85083,1,4 +256,304,85290,1,0 +256,136,85497,1,0 +256,304,85704,1,0 +184,224,85911,5,8 +88,224,86118,1,0 +80,216,86222,1,0 +72,208,86325,1,0 +72,112,86532,1,0 +144,40,86739,2,0,B|240:40,1,90,4|0 +224,128,87153,2,0,B|312:128,1,90 +408,168,87568,6,0,B|432:208|416:256,1,90,8|0 +416,254,87878,1,0 +416,254,87982,1,0 +344,312,88189,1,0 +344,312,88396,1,4 +168,256,88603,1,0 +344,200,88810,1,0 +168,144,89017,1,0 +256,76,89224,5,8 +256,160,89431,1,0 +256,192,89534,1,0 +256,224,89638,1,0 +256,307,89845,1,0 +192,192,90052,1,4 +256,76,90259,1,0 +320,192,90466,1,0 +256,307,90673,1,0 +200,232,90880,5,8 +312,232,91087,1,0 +152,152,91294,1,0 +360,152,91501,1,0 +200,72,91708,5,4 +312,72,91915,1,0 +152,152,92122,1,0 +360,152,92329,1,0 +456,192,92536,5,8 +416,288,92743,1,0 +384,288,92847,1,0 +352,288,92950,1,0 +264,336,93157,2,0,B|240:296|240:240,1,90,0|4 +240,160,93571,1,0 +208,64,93778,1,0 +176,72,93882,1,0 +152,96,93985,1,0 +136,128,94089,1,0 +136,160,94192,5,8 +72,240,94399,2,0,B|16:256,2,45 +136,312,94813,1,0 +192,232,95020,2,0,B|240:152,1,90 +376,112,95434,2,0,B|328:192,1,90 +464,112,96056,5,0 +496,192,96263,1,0 +464,272,96470,1,0 +48,272,96884,1,0 +16,192,97091,1,0 +48,112,97298,1,0 +152,64,97505,5,0 +224,136,97712,1,0 +152,64,97919,1,0 +248,40,98126,1,0 +344,72,98333,2,0,B|392:88|416:144,1,90 +392,224,98747,2,0,B|344:208|320:152,1,90 +224,136,99161,5,0 +152,208,99368,1,0 +144,240,99471,1,0 +152,272,99575,1,0 +248,222,99782,1,0 +248,312,99989,2,0,B|248:216,1,90 +328,112,100403,2,0,B|328:216,1,90 +360,288,100817,5,2 +432,216,101024,1,0 +432,184,101128,1,0 +432,152,101231,1,0 +400,64,101438,1,0 +304,40,101645,2,0,B|256:40|216:72,1,90 +200,168,102059,2,0,B|248:168|288:136,1,90 +176,280,102680,6,0,B|176:336,2,45 +336,104,103508,2,0,B|336:48,2,45 +336,104,104130,5,0 +416,192,104337,1,0 +336,280,104544,1,0 +256,192,104751,1,0 +176,280,104958,1,0 +96,192,105165,1,0 +176,104,105372,1,0 +256,192,105579,1,0 +256,192,105786,5,0 +256,192,105993,2,0,B|256:136,2,45 +256,192,106407,1,0 +256,288,106614,1,0 +256,288,106821,2,0,B|160:288,1,90 +144,288,107131,1,0 +128,288,107235,1,0 +112,288,107338,1,0 +96,288,107441,1,4 +256,56,120276,5,0 +248,88,120380,1,0 +256,120,120483,1,0 +264,152,120587,1,0 +256,184,120690,5,4 +160,208,120897,2,0,B|176:256|224:280,1,90 +296,208,121311,1,0 +312,192,121415,1,0 +336,184,121518,5,0 +360,184,121622,1,0 +376,192,121725,1,0 +392,208,121829,1,0 +400,232,121932,1,0 +376,336,122139,1,0 +376,336,122243,1,0 +376,336,122346,6,0,B|336:360|288:360,1,90 +192,336,122760,2,0,B|168:296|168:248,1,90 +272,208,123174,1,0 +216,104,123381,1,0 +336,88,123588,2,0,B|304:120|304:176,2,90 +416,168,124210,1,0 +416,168,124313,2,0,B|472:192|472:256|424:280,1,135 +432,275,124727,1,0 +432,275,124831,1,0 +344,224,125038,1,0 +240,208,125245,2,0,B|248:160|304:136,1,90 +360,80,125659,6,0,B|328:40|272:56,1,90 +216,144,126073,1,0 +104,136,126280,1,0 +104,136,126383,1,0 +104,136,126487,2,0,B|64:168|64:224,1,90 +136,296,126901,5,0 +160,320,127004,1,0 +192,328,127108,1,0 +224,320,127211,1,0 +248,296,127315,2,0,B|280:264|296:216,1,90 +294,219,127625,1,0 +408,80,127936,5,0 +368,80,128039,1,0 +328,80,128143,1,0 +224,80,128350,1,0 +72,40,128557,1,0 +104,192,128764,1,0 +272,144,128971,5,0 +120,104,129178,1,0 +152,256,129385,1,0 +152,256,129489,1,0 +152,256,129592,1,0 +152,256,129696,1,0 +152,256,129799,2,0,B|200:280|240:256,1,90 +328,128,130213,2,0,B|280:104|240:128,1,90 +240,224,130627,5,0 +336,256,130834,1,0 +320,288,130938,1,0 +296,312,131041,2,0,B|256:344|208:352,1,90 +184,352,131352,1,0 +152,352,131455,1,0 +64,264,131662,1,0 +160,264,131869,1,0 +32,248,132076,1,0 +192,248,132284,1,0 +96,160,132491,1,0 +120,136,132594,1,0 +152,128,132698,1,0 +248,128,132905,5,0 +280,120,133008,1,0 +312,120,133112,1,0 +344,128,133215,1,0 +372,144,133319,1,0 +392,176,133422,1,0 +400,208,133526,1,0 +400,240,133629,1,0 +392,272,133733,1,0 +376,304,133836,1,0 +344,320,133940,6,0,B|304:344|256:344,1,90,4|0 +192,280,134354,1,0 +92,264,134561,1,0 +80,232,134664,1,0 +76,196,134768,1,0 +80,160,134871,1,0 +88,128,134975,2,0,B|104:88|144:56,1,90 +232,104,135389,6,0,B|256:152,2,45,0|0|8 +232,104,135803,1,0 +380,49,136010,1,0 +289,185,136217,1,0 +283,14,136424,1,0 +389,149,136631,1,0 +232,104,136838,1,0 +380,49,137045,1,0 +289,185,137252,5,4 +289,185,137459,1,0 +289,185,137563,1,0 +112,256,137873,1,0 +112,256,137977,1,0 +112,256,138080,2,0,B|160:272|208:256,1,90 +400,256,138494,2,0,B|352:240|304:256,1,90 +256,184,138908,5,8 +288,72,139115,1,0 +232,152,139322,1,0 +264,40,139529,1,0 +168,72,139736,1,0 +168,72,139943,1,0 +100,160,140150,1,0 +88,192,140254,1,0 +84,224,140357,1,0 +88,256,140461,1,0 +96,288,140565,6,0,B|128:328|184:336,1,90,4|0 +208,336,140875,1,0 +248,336,140979,2,0,B|336:336,1,90 +376,336,141289,1,0 +408,336,141393,1,0 +455,255,141600,1,0 +460,82,141807,1,0 +351,217,142014,1,0 +512,172,142221,5,8 +361,117,142428,1,0 +256,116,142635,2,0,B|208:116,3,45 +184,132,143049,2,0,B|144:160|144:216,1,90 +256,280,143463,2,0,B|296:252|296:196,1,90 +256,192,143877,12,4,147178 +152,120,148627,6,0,B|184:88,2,45 +152,232,149041,1,0 +264,232,149248,2,0,B|264:280|224:320,1,90 +152,232,149662,2,0,B|152:184|192:144,1,90 +288,128,150076,5,0 +416,64,150283,1,0 +288,128,150490,1,8 +392,184,150697,1,0 +392,184,150800,2,0,B|408:232|408:280|352:320,1,135 +367,307,151214,1,0 +367,307,151318,5,0 +256,344,151525,1,0 +98,316,151732,1,0 +126,158,151939,1,0 +283,186,152146,1,0 +194,290,152353,1,0 +36,262,152560,1,0 +64,104,152767,1,0 +221,132,152974,5,0 +194,290,153181,1,0 +128,192,153388,1,0 +144,192,153492,1,0 +160,192,153595,1,0 +176,192,153699,1,0 +192,192,153802,2,0,B|320:192,1,90,4|0 +280,192,154113,1,0 +472,152,154423,5,0 +480,192,154527,1,0 +472,232,154630,1,0 +336,192,154837,1,0 +456,112,155044,1,0 +456,272,155251,1,0 +360,328,155459,5,0 +152,328,155666,1,0 +256,272,155873,1,0 +256,240,155976,1,0 +256,208,156080,1,0 +256,176,156183,1,0 +256,144,156287,1,0 +320,64,156494,2,0,B|376:64|392:112,2,90,0|0|0 +216,64,157115,5,8 +216,64,157322,1,0 +216,64,157425,1,0 +88,104,157736,1,0 +88,104,157839,1,0 +88,104,157943,2,0,B|88:200,1,90 +184,256,158357,2,0,B|184:160,1,90 +88,228,158771,2,0,B|88:324,1,90 +88,318,159081,1,0 +88,318,159185,1,0 +192,320,159392,5,0 +208,320,159495,1,0 +224,320,159599,1,0 +240,320,159703,1,0 +256,320,159806,1,0 +272,320,159910,1,0 +288,320,160013,1,0 +304,320,160117,1,0 +320,320,160220,1,0 +336,320,160324,1,0 +352,320,160427,1,4 +152,72,173263,6,0,B|152:128,4,45,0|0|0|0|8 +248,48,173889,2,0,B|336:48,1,90,0|0 +408,120,174303,2,0,B|456:120,2,45,0|0|0 +376,152,174613,2,0,B|328:152,2,45 +408,184,174924,2,0,B|464:184,2,45 +376,216,175234,1,0 +344,240,175338,6,0,B|240:240,1,90,0|0 +184,184,175752,1,0 +144,296,175959,1,0 +152,160,176166,1,0 +112,272,176373,1,0 +32,216,176580,1,0 +32,112,176787,2,0,B|48:72|96:48,1,90,0|0 +200,48,177201,5,0 +200,48,177304,2,0,B|344:48,1,135 +368,48,177718,1,0 +400,48,177822,1,0 +448,144,178029,1,0 +424,256,178236,2,0,B|376:240|352:192,1,90,0|0 +344,160,178547,1,0 +336,120,178650,2,0,B|288:128|248:168,1,90,0|0 +160,224,179064,6,0,B|256:248,1,90 +352,160,179478,2,0,B|256:136,1,90 +176,88,179892,1,0 +144,72,179996,1,0 +112,88,180099,1,0 +80,72,180203,1,0 +56,88,180306,5,0 +56,176,180513,1,0 +56,176,180617,2,0,B|56:312,1,135 +56,311,181031,1,0 +56,311,181134,1,0 +168,312,181341,1,0 +256,256,181548,1,0 +344,312,181755,1,0 +440,280,181963,2,0,B|472:240|464:192,1,90,0|0 +440,96,182377,5,0 +432,80,182480,1,0 +424,64,182584,1,0 +416,48,182687,1,0 +408,32,182791,2,0,B|344:32|344:32|328:56,1,90 +288,136,183205,2,0,B|200:136,1,90 +128,64,183619,2,0,B|120:32|120:32|56:32,1,90 +32,32,183929,2,0,B|32:168,1,135 +32,200,184343,1,0 +32,232,184447,5,0 +128,272,184654,2,0,B|216:320,1,90 +328,272,185068,2,0,B|240:224,1,90 +168,160,185482,1,0 +176,120,185585,1,0 +208,96,185689,1,0 +320,80,185896,5,0 +352,88,185999,1,0 +376,112,186103,1,0 +384,152,186207,1,0 +376,184,186310,1,0 +344,208,186414,1,0 +312,208,186517,2,0,B|280:192,8,22.5,0|0|0|0|0|0|0|0|4 +256,192,187035,12,0,188173 +256,96,188380,5,0 +256,96,188484,1,0 +256,96,188587,1,8 +376,96,188794,1,0 +448,184,189001,2,0,B|360:216,1,90 +424,304,189415,2,0,B|336:336,1,90 +216,336,189829,1,0 +144,232,190036,1,0 +216,336,190244,1,4 +280,232,190451,5,0 +280,232,190554,2,0,B|280:96,1,135 +280,64,190968,1,0 +280,32,191072,1,0 +176,64,191279,1,0 +121,179,191486,1,0 +237,233,191693,1,0 +291,117,191900,2,0,B|360:184,1,90,8|0 +400,264,192314,6,0,B|352:256|304:264,1,90 +112,264,192728,2,0,B|160:256|208:264,1,90 +256,168,193142,2,0,B|280:128,4,45,0|0|0|0|4 +160,112,193763,1,0 +160,112,193866,2,0,B|104:112|64:144|64:208,1,135 +64,224,194280,1,0 +64,256,194384,1,0 +128,352,194591,5,0 +192,256,194798,1,0 +256,352,195005,1,0 +368,352,195212,2,0,B|416:340|420:280,1,90,8|0 +416,184,195626,1,0 +408,168,195730,1,0 +400,152,195833,1,0 +392,136,195937,1,0 +384,120,196040,5,0 +184,64,196247,1,0 +128,264,196454,1,0 +328,320,196661,1,0 +256,192,196868,12,8,199767 +256,192,200181,5,4 diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 3a15b5e6b1..df22aede95 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -151,6 +151,7 @@ + \ No newline at end of file From fcfdbc8e07cf19ee234e2bff03eb0105272c9410 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 12:02:48 +0900 Subject: [PATCH 145/454] Don't show the migrate button on deployed builds for now --- .../Sections/Maintenance/GeneralSettings.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index f0f5b434cd..2b40ade895 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override string Header => "General"; [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps) + private void load(OsuGameBase osuGame, BeatmapManager beatmaps) { Children = new Drawable[] { @@ -55,8 +55,12 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance beatmaps.Restore(b); }).ContinueWith(t => Schedule(() => restoreButton.Enabled.Value = true)); } - }, - migrateButton = new SettingsButton + } + }; + + if (!osuGame.IsDeployedBuild) + { + Add(migrateButton = new SettingsButton { Text = "Migrate all beatmaps to the new format", Action = () => @@ -64,8 +68,8 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance migrateButton.Enabled.Value = false; Task.Factory.StartNew(beatmaps.MigrateAllToNewFormat).ContinueWith(t => Schedule(() => migrateButton.Enabled.Value = true), TaskContinuationOptions.LongRunning); } - } - }; + }); + } } } } From feef4b1890daa1bb609c995d3bd6a90d8c84b580 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 12:18:00 +0900 Subject: [PATCH 146/454] Add license header --- osu.Game/IO/Serialization/Converters/TypedListConverter.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index f8897a4e9d..36b53e1137 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -1,3 +1,6 @@ +// 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 Newtonsoft.Json; From f1dbcc4f1aab9d0b5540a9dfabeabea29858bc00 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 12:23:53 +0900 Subject: [PATCH 147/454] Remove misleading comments --- osu.Game/Beatmaps/BeatmapManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 6b547afe5d..8d20a3dcfb 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -548,12 +548,10 @@ namespace osu.Game.Beatmaps public void UpdateContent(BeatmapInfo beatmapInfo, Stream newData) { - // let's only allow one concurrent update at a time for now. var context = createContext(); using (var transaction = context.BeginTransaction()) { - // create local stores so we can isolate and thread safely, and share a context/transaction. var setInfo = beatmapInfo.BeatmapSet; var existingSetFileInfo = setInfo.Files.First(f => f.FileInfo.Hash == beatmapInfo.Hash); var existingFileInfo = existingSetFileInfo.FileInfo; From 9e51480aa392935b9d06b29869cd9b24439d9d6a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 12:27:30 +0900 Subject: [PATCH 148/454] Cleanup TypedListConverter a bit --- osu.Game/IO/Serialization/Converters/TypedListConverter.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 36b53e1137..c3d5869035 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -69,10 +69,7 @@ namespace osu.Game.IO.Serialization.Converters serializer.Serialize(writer, lookupTable); writer.WritePropertyName("Items"); - writer.WriteStartArray(); - foreach (var item in objects) - item.WriteTo(writer); - writer.WriteEndArray(); + serializer.Serialize(writer, objects); writer.WriteEndObject(); } From d026587a915ad17d1ab9b3871fa0144ef1b6e095 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 12:49:16 +0900 Subject: [PATCH 149/454] Add flag to explicitly serialize the type version --- .../Converters/TypedListConverter.cs | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index c3d5869035..32b7772092 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -16,6 +16,26 @@ namespace osu.Game.IO.Serialization.Converters /// The type of objects contained in the this attribute is attached to. public class TypedListConverter : JsonConverter { + private readonly bool requiresTypeVersion; + + /// + /// Constructs a new . + /// + // ReSharper disable once UnusedMember.Global + public TypedListConverter() + { + } + + /// + /// Constructs a new . + /// + /// Whether the version of the type should be serialized. + // ReSharper disable once UnusedMember.Global (Used in Beatmap) + public TypedListConverter(bool requiresTypeVersion) + { + this.requiresTypeVersion = requiresTypeVersion; + } + public override bool CanConvert(Type objectType) => objectType == typeof(List); public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) @@ -49,12 +69,17 @@ namespace osu.Game.IO.Serialization.Converters var objects = new List(); foreach (var item in list) { - var type = item.GetType().AssemblyQualifiedName; + var type = item.GetType(); + var assemblyName = type.Assembly.GetName(); - int typeId = lookupTable.IndexOf(type); + var typeString = $"{type.FullName}, {assemblyName.Name}"; + if (requiresTypeVersion) + typeString += $", {assemblyName.Version}"; + + int typeId = lookupTable.IndexOf(typeString); if (typeId == -1) { - lookupTable.Add(type); + lookupTable.Add(typeString); typeId = lookupTable.Count - 1; } From a78441bc5a511d5f780cd949d2d21cc2218c8be9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 23:03:31 +0900 Subject: [PATCH 150/454] 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 245b5f759ff4a2cc00e4ca89a5b553bccbca0ade Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 14:36:32 +0900 Subject: [PATCH 151/454] Underscore + lowercase all keys --- .../Converters/TypedListConverter.cs | 12 ++++++------ .../Serialization/Converters/Vector2Converter.cs | 6 +++--- osu.Game/IO/Serialization/IJsonSerializable.cs | 3 ++- osu.Game/IO/Serialization/KeyContractResolver.cs | 16 ++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 5 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 osu.Game/IO/Serialization/KeyContractResolver.cs diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 32b7772092..7147f9110e 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -45,13 +45,13 @@ namespace osu.Game.IO.Serialization.Converters var obj = JObject.Load(reader); var lookupTable = new List(); - serializer.Populate(obj["LookupTable"].CreateReader(), lookupTable); + serializer.Populate(obj["lookup_table"].CreateReader(), lookupTable); - foreach (var tok in obj["Items"]) + foreach (var tok in obj["items"]) { var itemReader = tok.CreateReader(); - var typeName = lookupTable[(int)tok["Type"]]; + var typeName = lookupTable[(int)tok["type"]]; var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); serializer.Populate(itemReader, instance); @@ -84,16 +84,16 @@ namespace osu.Game.IO.Serialization.Converters } var itemObject = JObject.FromObject(item, serializer); - itemObject.AddFirst(new JProperty("Type", typeId)); + itemObject.AddFirst(new JProperty("type", typeId)); objects.Add(itemObject); } writer.WriteStartObject(); - writer.WritePropertyName("LookupTable"); + writer.WritePropertyName("lookup_table"); serializer.Serialize(writer, lookupTable); - writer.WritePropertyName("Items"); + writer.WritePropertyName("items"); serializer.Serialize(writer, objects); writer.WriteEndObject(); diff --git a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs index 5f21018fd5..87fbd31d21 100644 --- a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs +++ b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs @@ -18,7 +18,7 @@ namespace osu.Game.IO.Serialization.Converters public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var obj = JObject.Load(reader); - return new Vector2((float)obj["X"], (float)obj["Y"]); + return new Vector2((float)obj["x"], (float)obj["y"]); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) @@ -27,9 +27,9 @@ namespace osu.Game.IO.Serialization.Converters writer.WriteStartObject(); - writer.WritePropertyName("X"); + writer.WritePropertyName("x"); writer.WriteValue(vector.X); - writer.WritePropertyName("Y"); + writer.WritePropertyName("y"); writer.WriteValue(vector.Y); writer.WriteEndObject(); diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs index 38e7f47656..892c266fe3 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/IJsonSerializable.cs @@ -30,7 +30,8 @@ namespace osu.Game.IO.Serialization Formatting = Formatting.Indented, ObjectCreationHandling = ObjectCreationHandling.Replace, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, - Converters = new JsonConverter[] { new Vector2Converter() } + Converters = new JsonConverter[] { new Vector2Converter() }, + ContractResolver = new KeyContractResolver() }; } } diff --git a/osu.Game/IO/Serialization/KeyContractResolver.cs b/osu.Game/IO/Serialization/KeyContractResolver.cs new file mode 100644 index 0000000000..10375646ce --- /dev/null +++ b/osu.Game/IO/Serialization/KeyContractResolver.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Humanizer; +using Newtonsoft.Json.Serialization; + +namespace osu.Game.IO.Serialization +{ + public class KeyContractResolver : DefaultContractResolver + { + protected override string ResolvePropertyName(string propertyName) + { + return propertyName.Underscore(); + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9e5d4291ce..a2d4a00110 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -272,6 +272,7 @@ + 20171019041408_InitialCreate.cs From f5f7658e906125fca54bc57532971d8d3cb74b36 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 14:40:28 +0900 Subject: [PATCH 152/454] Don't serialize TotalBreakTime --- osu.Game/Beatmaps/Beatmap.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index c331872dda..e9342584cc 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -34,15 +34,16 @@ namespace osu.Game.Beatmaps [JsonIgnore] public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; - [JsonConverter(typeof(TypedListConverter))] /// /// The HitObjects this Beatmap contains. /// + [JsonConverter(typeof(TypedListConverter))] public List HitObjects = new List(); /// /// Total amount of break time in the beatmap. /// + [JsonIgnore] public double TotalBreakTime => Breaks.Sum(b => b.Duration); /// From e573db04d43361caf21e14b5c7a601ec0df4ebd5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 14:42:36 +0900 Subject: [PATCH 153/454] Don't serialize HitObject.Kiai --- osu.Game/Rulesets/Objects/HitObject.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 92220ff8bd..0772e7707e 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.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 Newtonsoft.Json; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -33,6 +34,7 @@ namespace osu.Game.Rulesets.Objects /// /// Whether this is in Kiai time. /// + [JsonIgnore] public bool Kiai { get; private set; } /// @@ -45,7 +47,7 @@ namespace osu.Game.Rulesets.Objects SoundControlPoint soundPoint = controlPointInfo.SoundPointAt(StartTime); EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime); - Kiai |= effectPoint.KiaiMode; + Kiai = effectPoint.KiaiMode; // Initialize first sample Samples.ForEach(s => s.ControlPoint = soundPoint); From 0ba8988580a266343c1ab9ad42f0f5a85a7662d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 15:32:39 +0900 Subject: [PATCH 154/454] Don't serialize Author + add SerializableAttributes --- osu.Game/Beatmaps/BeatmapInfo.cs | 1 + osu.Game/Beatmaps/BeatmapMetadata.cs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 9450022a01..dd66812ea5 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets; namespace osu.Game.Beatmaps { + [Serializable] public class BeatmapInfo : IEquatable, IJsonSerializable, IHasPrimaryKey { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 2efbcd4f15..67d6de49cd 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,6 +10,7 @@ using osu.Game.Users; namespace osu.Game.Beatmaps { + [Serializable] public class BeatmapMetadata { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -51,6 +53,7 @@ namespace osu.Game.Beatmaps /// The author of the beatmaps in this set. /// public User Author; + public bool ShouldSerializeAuthor() => false; public string Source { get; set; } From 09f54b06ace0c82a3e500d18acd81e3737b475ff Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 15:55:22 +0900 Subject: [PATCH 155/454] Just don't serialize Author altogether for now --- osu.Game/Beatmaps/BeatmapMetadata.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 67d6de49cd..a9488ce0e2 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -52,8 +52,8 @@ namespace osu.Game.Beatmaps /// /// The author of the beatmaps in this set. /// + [JsonIgnore] public User Author; - public bool ShouldSerializeAuthor() => false; public string Source { get; set; } From bdf283a4e1b3d3a913beb6dfeb54e78d050d13de Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 15:55:38 +0900 Subject: [PATCH 156/454] A bit more cleanup --- osu.Game/IO/Serialization/Converters/TypedListConverter.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 7147f9110e..b218f76b53 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -43,9 +43,7 @@ namespace osu.Game.IO.Serialization.Converters var list = new List(); var obj = JObject.Load(reader); - - var lookupTable = new List(); - serializer.Populate(obj["lookup_table"].CreateReader(), lookupTable); + var lookupTable = serializer.Deserialize>(obj["lookup_table"].CreateReader()); foreach (var tok in obj["items"]) { From 2a1a9b9f1f8454a595daeb89b012410541b9661c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2017 17:26:39 +0900 Subject: [PATCH 157/454] 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 158/454] 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 159/454] 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 160/454] 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 161/454] 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 162/454] 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 163/454] 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 164/454] 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 43c270ea49559e2a8515d91dfbccddcf5d3f6a18 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Thu, 7 Dec 2017 21:18:51 +0100 Subject: [PATCH 165/454] Rolled back the bad earlier implementation --- .../Objects/Drawables/DrawableHitStrong.cs | 11 ----- .../Drawables/DrawableTaikoHitObject.cs | 2 - .../UI/TaikoRulesetContainer.cs | 48 +------------------ .../Objects/Drawables/DrawableHitObject.cs | 4 +- 4 files changed, 2 insertions(+), 63 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs index eced24a8da..c07eaf4d8b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -16,8 +16,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// private const double second_hit_window = 30; - protected override bool PlaysSamples => true; - private double firstHitTime; private bool firstKeyHeld; private TaikoAction firstHitAction; @@ -55,15 +53,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return base.OnReleased(action); } - protected override void LoadComplete() - { - base.LoadComplete(); - - if (Samples.Count > 1) - // Removes the "normal" hitsound, leaving only the hitfinish one - Samples.RemoveAt(0); - } - public override bool OnPressed(TaikoAction action) { if (AllJudged) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 8ebfaaea38..7976cbbbc1 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -14,8 +14,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); - protected override bool PlaysSamples => false; - protected readonly Vector2 BaseSize; protected readonly TaikoPiece MainPiece; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index f72d253a73..48ee0a5b42 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -2,11 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.Graphics; -using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; -using osu.Game.Input; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Replays; @@ -18,45 +15,21 @@ using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Taiko.Replays; using OpenTK; -using OpenTK.Input; using System.Linq; using osu.Framework.Input; -using System.Collections.Generic; namespace osu.Game.Rulesets.Taiko.UI { public class TaikoRulesetContainer : ScrollingRulesetContainer { - private readonly HashSet centreKeys = new HashSet(); - private readonly HashSet rimKeys = new HashSet(); - private AudioManager audio; - private IEnumerable keyBindings; - public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(ruleset, beatmap, isForCurrentRuleset) { } [BackgroundDependencyLoader] - private void load(AudioManager audio, KeyBindingStore store) + private void load() { - keyBindings = store.Query(Ruleset.RulesetInfo.ID, Ruleset.AvailableVariants?.First() ?? 0).Cast(); - if (keyBindings.Count() == 0) - keyBindings = Ruleset.GetDefaultKeyBindings(); - - foreach (var kb in keyBindings) - { - var key = (Key)(kb.KeyCombination.Keys as InputKey[]).First(); - var action = kb.GetAction(); - - if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - centreKeys.Add(key); - - if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - rimKeys.Add(key); - } - - this.audio = audio; loadBarLines(); } @@ -104,25 +77,6 @@ namespace osu.Game.Rulesets.Taiko.UI } } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (!args.Repeat) - { - var sampleBank = Beatmap.ControlPointInfo.SoundPointAt(WorkingBeatmap.Track.CurrentTime).SampleBank ?? "normal"; - string sampleName = ""; - - if (centreKeys.Contains(args.Key)) - sampleName = "hitnormal"; - - else if (rimKeys.Contains(args.Key)) - sampleName = "hitclap"; - - audio.Sample.Get($"Gameplay/{sampleBank}-{sampleName}")?.Play(); - } - - return base.OnKeyDown(state, args); - } - protected override Vector2 GetPlayfieldAspectAdjust() { const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 12f4fc4f31..0a9de15586 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -59,8 +59,6 @@ namespace osu.Game.Rulesets.Objects.Drawables private readonly List judgements = new List(); public IReadOnlyList Judgements => judgements; - // Override in inheriting classes to prevent from playing samples on hit - protected virtual bool PlaysSamples => true; protected List Samples = new List(); @@ -95,7 +93,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { UpdateState(state); - if (State == ArmedState.Hit && PlaysSamples) + if (State == ArmedState.Hit) PlaySamples(); }; From 105d01d85b9616f9d8be3ea9ea69893df8f882a1 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 7 Dec 2017 22:02:53 +0100 Subject: [PATCH 166/454] 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 d2f3d5a80769eeae5932b2c4e42039430253e468 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Thu, 7 Dec 2017 23:09:51 +0100 Subject: [PATCH 167/454] Added basic SampleInfo.FromSoundPoint and GetChannel methods --- osu.Game/Audio/SampleInfo.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 171a1bdf75..4a5d836c53 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -1,6 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Configuration; +using osu.Game.Beatmaps.ControlPoints; + namespace osu.Game.Audio { public class SampleInfo @@ -10,6 +15,24 @@ namespace osu.Game.Audio public const string HIT_NORMAL = @"hitnormal"; public const string HIT_CLAP = @"hitclap"; + public static SampleInfo FromSoundPoint(SoundControlPoint soundPoint, string sampleName = SampleInfo.HIT_NORMAL) + { + return new SampleInfo() + { + Bank = soundPoint.SampleBank, + Name = sampleName, + Volume = soundPoint.SampleVolume, + }; + } + + public SampleChannel GetChannel(SampleManager manager) + { + var channel = manager.Get($"{Bank}-{Name}"); + + channel.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(Volume / 100.0)); + return channel; + } + /// /// The bank to load the sample from. /// From 229c9b5b4ea4a2524b1fc4285bf86ce5d78ef2c5 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Thu, 7 Dec 2017 23:11:34 +0100 Subject: [PATCH 168/454] First basic implementation of IKeyBindingHandler for the playfield so it can handle taiko hitsounds --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 33 +++++++++++++++++-- .../UI/TaikoRulesetContainer.cs | 2 +- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index ac3796f5b8..c4210dd261 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -16,10 +16,16 @@ using osu.Framework.Extensions.Color4Extensions; using System.Linq; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Framework.Input.Bindings; +using osu.Game.Beatmaps.ControlPoints; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using System.Collections.Generic; +using osu.Game.Audio; namespace osu.Game.Rulesets.Taiko.UI { - public class TaikoPlayfield : ScrollingPlayfield + public class TaikoPlayfield : ScrollingPlayfield, IKeyBindingHandler { /// /// Default height of a when inside a . @@ -54,9 +60,14 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Box overlayBackground; private readonly Box background; - public TaikoPlayfield() + private ControlPointInfo controlPointInfo; + private IEnumerable allSamples; + private AudioManager audio; + + public TaikoPlayfield(ControlPointInfo controlPointInfo) : base(Axes.X) { + this.controlPointInfo = controlPointInfo; AddRangeInternal(new Drawable[] { backgroundContainer = new Container @@ -194,8 +205,15 @@ namespace osu.Game.Rulesets.Taiko.UI } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { + this.audio = audio; + + foreach (var soundPoint in controlPointInfo.SoundPoints) + { + SampleInfo.FromSoundPoint(soundPoint).GetChannel(audio.Sample); + } + overlayBackgroundContainer.BorderColour = colours.Gray0; overlayBackground.Colour = colours.Gray1; @@ -258,5 +276,14 @@ namespace osu.Game.Rulesets.Taiko.UI kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim)); } } + + public bool OnPressed(TaikoAction action) + { + var soundPoint = controlPointInfo.SoundPointAt(Time.Current); + + return true; + } + + public bool OnReleased(TaikoAction action) => false; } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index 48ee0a5b42..614b446181 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Taiko.UI public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); - protected override Playfield CreatePlayfield() => new TaikoPlayfield + protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft From 2e1dfa16a28c3bbcae530d8308aec726374cd052 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Dec 2017 14:59:32 +0900 Subject: [PATCH 169/454] 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 170/454] 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 40e750f309eb8d9f61d4ee16d44c8d5fa5131931 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Fri, 8 Dec 2017 08:32:16 +0100 Subject: [PATCH 171/454] Added sample preloading and playing --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index c4210dd261..eda5c75906 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -22,6 +22,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using System.Collections.Generic; using osu.Game.Audio; +using System; namespace osu.Game.Rulesets.Taiko.UI { @@ -61,7 +62,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Box background; private ControlPointInfo controlPointInfo; - private IEnumerable allSamples; + private SortedDictionary> allSamples; private AudioManager audio; public TaikoPlayfield(ControlPointInfo controlPointInfo) @@ -211,7 +212,9 @@ namespace osu.Game.Rulesets.Taiko.UI foreach (var soundPoint in controlPointInfo.SoundPoints) { - SampleInfo.FromSoundPoint(soundPoint).GetChannel(audio.Sample); + var normalSample = SampleInfo.FromSoundPoint(soundPoint).GetChannel(audio.Sample); + var clapSample = SampleInfo.FromSoundPoint(soundPoint, SampleInfo.HIT_CLAP).GetChannel(audio.Sample); + allSamples.Add(soundPoint.Time, new Tuple(normalSample, clapSample)); } overlayBackgroundContainer.BorderColour = colours.Gray0; @@ -279,7 +282,13 @@ namespace osu.Game.Rulesets.Taiko.UI public bool OnPressed(TaikoAction action) { - var soundPoint = controlPointInfo.SoundPointAt(Time.Current); + if (!allSamples.TryGetValue(controlPointInfo.SoundPointAt(Clock.CurrentTime).Time, out Tuple samples)) + throw new InvalidOperationException("Current sample set not found."); + + if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + samples.Item1.Play(); + else + samples.Item2.Play(); return true; } From faa921ba05790c42ee9d6df15f360bb032023bab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Dec 2017 17:41:10 +0900 Subject: [PATCH 172/454] 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 61a6a2919ef47d50ee0087d92618cd9da27c3a35 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Fri, 8 Dec 2017 09:41:13 +0100 Subject: [PATCH 173/454] Fixed multiple critical bugs and changed allSamples to a normal Dictionary for faster access --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 8 ++++++-- osu.Game/Audio/SampleInfo.cs | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index eda5c75906..394b683531 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Box background; private ControlPointInfo controlPointInfo; - private SortedDictionary> allSamples; + private Dictionary> allSamples; private AudioManager audio; public TaikoPlayfield(ControlPointInfo controlPointInfo) @@ -210,6 +210,7 @@ namespace osu.Game.Rulesets.Taiko.UI { this.audio = audio; + allSamples = new Dictionary>(); foreach (var soundPoint in controlPointInfo.SoundPoints) { var normalSample = SampleInfo.FromSoundPoint(soundPoint).GetChannel(audio.Sample); @@ -282,7 +283,10 @@ namespace osu.Game.Rulesets.Taiko.UI public bool OnPressed(TaikoAction action) { - if (!allSamples.TryGetValue(controlPointInfo.SoundPointAt(Clock.CurrentTime).Time, out Tuple samples)) + var currentTime = Clock.CurrentTime; + var soundPoint = currentTime < controlPointInfo.SoundPoints[0].Time ? controlPointInfo.SoundPoints[0] : controlPointInfo.SoundPointAt(currentTime); + + if (!allSamples.TryGetValue(soundPoint.Time, out Tuple samples)) throw new InvalidOperationException("Current sample set not found."); if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 4a5d836c53..6ee8a7f296 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -27,7 +27,7 @@ namespace osu.Game.Audio public SampleChannel GetChannel(SampleManager manager) { - var channel = manager.Get($"{Bank}-{Name}"); + var channel = manager.Get($"Gameplay/{Bank}-{Name}"); channel.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(Volume / 100.0)); return channel; From 9d3d9bcdc8efab16d62946dbb658416b555f3b02 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Fri, 8 Dec 2017 09:42:10 +0100 Subject: [PATCH 174/454] Corrected the return values for taiko DrawableHit and DrawableHitStrong (OnPressed == true if new Judgement occurs and is a hit) --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 3 ++- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 489eacf386..9d797b77b5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -67,7 +67,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { validKeyPressed = HitActions.Contains(action); - return UpdateJudgement(true); + // Only count this as handled if the new judgement is a hit + return UpdateJudgement(true) && Judgements.Last().IsHit; } protected override void Update() diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs index c07eaf4d8b..a6575df6d7 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return false; // Assume the intention was to hit the strong hit with both keys only if the first key is still being held down - return firstKeyHeld && UpdateJudgement(true); + return firstKeyHeld && Judgements.Last().IsHit; } } } From 5341e791029201976bbf401f8cf4615c1086e4c6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Dec 2017 17:51:15 +0900 Subject: [PATCH 175/454] 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 4b3cf0773d0bffcc503755bccbca1a652491c8e1 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Fri, 8 Dec 2017 09:52:58 +0100 Subject: [PATCH 176/454] Various CI adjustments --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 5 +---- osu.Game/Audio/SampleInfo.cs | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 394b683531..549c59cba1 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -61,9 +61,8 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Box overlayBackground; private readonly Box background; - private ControlPointInfo controlPointInfo; + private readonly ControlPointInfo controlPointInfo; private Dictionary> allSamples; - private AudioManager audio; public TaikoPlayfield(ControlPointInfo controlPointInfo) : base(Axes.X) @@ -208,8 +207,6 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load(OsuColour colours, AudioManager audio) { - this.audio = audio; - allSamples = new Dictionary>(); foreach (var soundPoint in controlPointInfo.SoundPoints) { diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 6ee8a7f296..d3fc1d4932 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -15,9 +15,9 @@ namespace osu.Game.Audio public const string HIT_NORMAL = @"hitnormal"; public const string HIT_CLAP = @"hitclap"; - public static SampleInfo FromSoundPoint(SoundControlPoint soundPoint, string sampleName = SampleInfo.HIT_NORMAL) + public static SampleInfo FromSoundPoint(SoundControlPoint soundPoint, string sampleName = HIT_NORMAL) { - return new SampleInfo() + return new SampleInfo { Bank = soundPoint.SampleBank, Name = sampleName, From 51dc66df12ee244544757498c8a144f4d53352a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Dec 2017 18:45:06 +0900 Subject: [PATCH 177/454] 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 178/454] 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 179/454] 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 + + +