diff --git a/osu-framework b/osu-framework index 71900dc350..6372fb22c1 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 71900dc350bcebbb60d912d4023a1d2a6bbbc3c1 +Subproject commit 6372fb22c1c85f600921a139849b8dedf71026d5 diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index d49e9c7c26..4dbe65b3ce 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Catch public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap); - public override int LegacyID => 2; + public override int? LegacyID => 2; public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame(); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 3ad498e6ea..16b6888f2b 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Mania public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap); - public override int LegacyID => 3; + public override int? LegacyID => 3; public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index baa9eac1a3..058e3606f4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -50,10 +50,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdatePreemptState() { - this.Animate( - d => d.FadeIn(ANIM_DURATION), - d => d.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf) - ); + this.FadeOut().FadeIn(ANIM_DURATION); + this.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf); } protected override void UpdateCurrentState(ArmedState state) @@ -64,12 +62,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.Delay(HitObject.TimePreempt).FadeOut(); break; case ArmedState.Miss: - this.FadeOut(ANIM_DURATION) - .FadeColour(Color4.Red, ANIM_DURATION / 2); + this.FadeOut(ANIM_DURATION); + this.FadeColour(Color4.Red, ANIM_DURATION / 2); break; case ArmedState.Hit: - this.FadeOut(ANIM_DURATION, Easing.OutQuint) - .ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out); + this.FadeOut(ANIM_DURATION, Easing.OutQuint); + this.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out); break; } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 3f0aea5cb2..d407835a96 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Osu public override SettingsSubsection CreateSettings() => new OsuSettings(); - public override int LegacyID => 0; + public override int? LegacyID => 0; public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index e5fe288f20..9d6b5b5ce4 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -101,16 +101,16 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps // The duration of the taiko hit object double taikoDuration = distance / taikoVelocity; - // For some reason, old osu! always uses speedAdjustment to determine the taiko velocity, but - // only uses it to determine osu! velocity if beatmap version < 8. Let's account for that here. - if (beatmap.BeatmapInfo.BeatmapVersion >= 8) - speedAdjustedBeatLength *= speedAdjustment; - // The velocity of the osu! hit object - calculated as the velocity of a slider double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; // The duration of the osu! hit object double osuDuration = distance / osuVelocity; + // osu-stable always uses the speed-adjusted beatlength to determine the velocities, but + // only uses it for tick rate if beatmap version < 8 + if (beatmap.BeatmapInfo.BeatmapVersion >= 8) + speedAdjustedBeatLength *= speedAdjustment; + // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans); diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-expected-conversion.json b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-expected-conversion.json new file mode 100644 index 0000000000..fc7d466c1b --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-expected-conversion.json @@ -0,0 +1,87 @@ +{ + "Mappings": [{ + "StartTime": 6590, + "Objects": [{ + "StartTime": 6590, + "EndTime": 8320, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + }, + { + "StartTime": 8436, + "Objects": [{ + "StartTime": 8436, + "EndTime": 10166, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + }, + { + "StartTime": 10282, + "Objects": [{ + "StartTime": 10282, + "EndTime": 12012, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + }, + { + "StartTime": 12128, + "Objects": [{ + "StartTime": 12128, + "EndTime": 13858, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + }, + { + "StartTime": 41666, + "Objects": [{ + "StartTime": 41666, + "EndTime": 42589, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + }, + { + "StartTime": 62666, + "Objects": [{ + "StartTime": 62666, + "EndTime": 63127, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + }, + { + "StartTime": 208743, + "Objects": [{ + "StartTime": 208743, + "EndTime": 209204, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll.osu b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll.osu new file mode 100644 index 0000000000..4c493b47d4 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll.osu @@ -0,0 +1,25 @@ +osu file format v14 + +[Difficulty] +HPDrainRate:6 +CircleSize:4.2 +OverallDifficulty:9 +ApproachRate:9.8 +SliderMultiplier:1.87 +SliderTickRate:1 + +[TimingPoints] +6590,461.538461538462,4,2,2,15,1,0 +6590,-200,4,2,2,15,0,0 +49051,230.769230769231,4,2,1,15,1,0 +62666,-200,4,2,1,60,0,0 +197666,-100,4,2,1,85,0,1 + +[HitObjects] +88,104,6590,6,0,B|176:156|256:108|256:108|336:60|423:112,1,350.625,6|0,0:0|0:0,0:0:0:0: +396,213,8436,2,0,P|277:247|376:172,1,350.625,6|0,0:0|0:0,0:0:0:0: +472,220,10282,2,0,P|456:288|220:300,1,350.625,6|0,0:0|0:0,0:0:0:0: +277,200,12128,2,0,P|398:225|276:244,1,350.625,6|0,0:0|0:0,0:0:0:0: +268,229,41666,2,0,L|473:210,1,187,2|2,0:0|0:0,0:0:0:0: +133,342,62666,2,0,B|132:316|132:316|128:316|128:316|130:295|130:295|126:296|126:296|129:275|129:275|125:275|125:275|127:254|127:254|123:255|123:255|125:234|125:234|121:234|121:234|123:213|123:213|119:214|119:214|121:193|121:193|118:193|118:193|118:172,1,187,8|8,0:0|0:0,0:0:0:0: +481,338,208743,6,0,P|492:262|383:195,2,187,2|8|2,0:0|0:0|0:0,0:0:0:0: diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 713506e831..0a9719f27b 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap); - public override int LegacyID => 1; + public override int? LegacyID => 1; public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); diff --git a/osu.Game.Rulesets.Taiko/Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko/Tests/TaikoBeatmapConversionTest.cs index 64f728a018..385e041ace 100644 --- a/osu.Game.Rulesets.Taiko/Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko/Tests/TaikoBeatmapConversionTest.cs @@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [NonParallelizable] [TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2152")] + [TestCase("slider-generating-drumroll", false)] public void Test(string name, bool isForCurrentRuleset) { this.isForCurrentRuleset = isForCurrentRuleset; diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 07d27455b8..8f0aa88e62 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -149,6 +149,8 @@ + + diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 2e774e0924..ab10da2cd1 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -102,7 +102,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(8, difficulty.OverallDifficulty); Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8f, difficulty.SliderMultiplier); + Assert.AreEqual(1.8, difficulty.SliderMultiplier); Assert.AreEqual(2, difficulty.SliderTickRate); } } diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 8168de091e..89d96c774e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(8, difficulty.OverallDifficulty); Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8f, difficulty.SliderMultiplier); + Assert.AreEqual(1.8, difficulty.SliderMultiplier); Assert.AreEqual(2, difficulty.SliderTickRate); } diff --git a/osu.Game.Tests/Visual/TestCaseUserProfileRecentSection.cs b/osu.Game.Tests/Visual/TestCaseUserProfileRecentSection.cs new file mode 100644 index 0000000000..1f7a7e7165 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseUserProfileRecentSection.cs @@ -0,0 +1,161 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Profile.Sections; +using osu.Game.Overlays.Profile.Sections.Recent; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseUserProfileRecentSection : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(RecentSection), + typeof(DrawableRecentActivity), + typeof(PaginatedRecentActivityContainer), + typeof(MedalIcon) + }; + + public TestCaseUserProfileRecentSection() + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f) + }, + new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + ChildrenEnumerable = createDummyActivities().Select(a => new DrawableRecentActivity(a)) + }, + } + }; + } + + private IEnumerable createDummyActivities() + { + var dummyBeatmap = new RecentActivity.RecentActivityBeatmap + { + Title = @"Dummy beatmap", + Url = "/b/1337", + }; + + var dummyUser = new RecentActivity.RecentActivityUser + { + Username = "DummyReborn", + Url = "/u/666", + PreviousUsername = "Dummy", + }; + + return new[] + { + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.Achievement, + Achievement = new RecentActivity.RecentActivityAchievement + { + Name = @"Feelin' It", + Slug = @"all-secret-feelinit", + }, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapPlaycount, + Count = 1337, + Beatmap = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetApprove, + Approval = BeatmapApproval.Qualified, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetDelete, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetRevive, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetRevive, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetUpdate, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetUpload, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.Rank, + Rank = 1, + Mode = "osu!", + Beatmap = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.RankLost, + Mode = "osu!", + Beatmap = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.UsernameChange, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.UserSupportAgain, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.UserSupportFirst, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.UserSupportGift, + }, + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseVolumePieces.cs b/osu.Game.Tests/Visual/TestCaseVolumePieces.cs new file mode 100644 index 0000000000..cfbf7fdb4d --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseVolumePieces.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2007-2018 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.Game.Overlays.Volume; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseVolumePieces : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(VolumeMeter), typeof(MuteButton) }; + + protected override void LoadComplete() + { + VolumeMeter meter; + MuteButton mute; + Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue)); + Add(mute = new MuteButton + { + Margin = new MarginPadding { Top = 200 } + }); + + AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1); + AddToggleStep("mute", b => mute.Current.Value = b); + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 14810abf84..1cfa7bc111 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -173,7 +173,9 @@ + + diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 16e6692887..38b84b4b03 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -29,8 +29,8 @@ namespace osu.Game.Beatmaps set => approachRate = value; } - public float SliderMultiplier { get; set; } = 1; - public float SliderTickRate { get; set; } = 1; + public double SliderMultiplier { get; set; } = 1; + public double SliderTickRate { get; set; } = 1; /// /// Maps a difficulty value [0, 10] to a two-piece linear range of values. diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 1d54bc4b0c..7d4f8b5bf5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -249,10 +249,10 @@ namespace osu.Game.Beatmaps.Formats difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"SliderMultiplier": - difficulty.SliderMultiplier = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + difficulty.SliderMultiplier = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"SliderTickRate": - difficulty.SliderTickRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + difficulty.SliderTickRate = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; } } diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 9f1b44af44..1d231ada23 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -90,6 +90,10 @@ namespace osu.Game.Graphics.Containers case LinkAction.External: Process.Start(url); break; + case LinkAction.OpenUserProfile: + if (long.TryParse(linkArgument, out long userId)) + game?.ShowUser(userId); + break; default: throw new NotImplementedException($"This {nameof(LinkAction)} ({linkType.ToString()}) is missing an associated action."); } diff --git a/osu.Game/Graphics/Containers/ReverseChildIDFillFlowContainer.cs b/osu.Game/Graphics/Containers/ReverseChildIDFillFlowContainer.cs index 9f028490ef..5803c8a5db 100644 --- a/osu.Game/Graphics/Containers/ReverseChildIDFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/ReverseChildIDFillFlowContainer.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; -using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,7 +9,5 @@ namespace osu.Game.Graphics.Containers public class ReverseChildIDFillFlowContainer : FillFlowContainer where T : Drawable { protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y); - - protected override IEnumerable FlowingChildren => base.FlowingChildren.Reverse(); } } diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 43a3f06236..6d9bf231c3 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -18,8 +18,6 @@ namespace osu.Game.Graphics.UserInterface public Action Exit; - public override bool HandleLeftRightArrows => false; - private bool focus; public bool HoldFocus { diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 7ad9bc73a8..20385a7dae 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; 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.Graphics.UserInterface; @@ -56,6 +57,14 @@ namespace osu.Game.Graphics.UserInterface } } + protected override TabFillFlowContainer CreateTabFlow() => new OsuTabFillFlowContainer + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.Both, + Depth = -1, + Masking = true + }; + public class OsuTabItem : TabItem, IHasAccentColour { protected readonly SpriteText Text; @@ -239,5 +248,10 @@ namespace osu.Game.Graphics.UserInterface } } } + + private class OsuTabFillFlowContainer : TabFillFlowContainer + { + protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y); + } } } diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index 9398eb55f3..28d33bbacd 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -12,6 +12,8 @@ namespace osu.Game.Graphics.UserInterface { protected virtual bool AllowCommit => false; + public override bool HandleLeftRightArrows => false; + public SearchTextBox() { Height = 35; diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs deleted file mode 100644 index ef3702fdf3..0000000000 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Bindings; -using osu.Game.Input.Bindings; - -namespace osu.Game.Graphics.UserInterface.Volume -{ - public class VolumeMeter : Container, IKeyBindingHandler - { - private readonly Box meterFill; - public BindableDouble Bindable { get; } = new BindableDouble(); - - public VolumeMeter(string meterName) - { - Size = new Vector2(40, 180); - Children = new Drawable[] - { - new Box - { - Colour = Color4.Black, - RelativeSizeAxes = Axes.Both - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.5f, 0.9f), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Children = new Drawable[] - { - new Box - { - Colour = Color4.DarkGray, - RelativeSizeAxes = Axes.Both - }, - meterFill = new Box - { - Colour = Color4.White, - Scale = new Vector2(1, 0), - RelativeSizeAxes = Axes.Both, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre - } - } - }, - new OsuSpriteText - { - Text = meterName, - Anchor = Anchor.BottomCentre, - Origin = Anchor.TopCentre - } - }; - - Bindable.ValueChanged += delegate { updateFill(); }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - updateFill(); - } - - public double Volume - { - get => Bindable.Value; - private set => Bindable.Value = value; - } - - public void Increase() - { - Volume += 0.05f; - } - - public void Decrease() - { - Volume -= 0.05f; - } - - private void updateFill() => meterFill.ScaleTo(new Vector2(1, (float)Volume), 300, Easing.OutQuint); - - public bool OnPressed(GlobalAction action) - { - if (!IsHovered) return false; - - switch (action) - { - case GlobalAction.DecreaseVolume: - Decrease(); - return true; - case GlobalAction.IncreaseVolume: - Increase(); - return true; - } - - return false; - } - - public bool OnReleased(GlobalAction action) => false; - } -} diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 90f3999ddd..1325179e0d 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -199,7 +199,7 @@ namespace osu.Game.Online.API } catch (WebException we) { - HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? HttpStatusCode.RequestTimeout; + HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout); switch (statusCode) { diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs new file mode 100644 index 0000000000..d1685b01f3 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -0,0 +1,130 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; +using osu.Game.Rulesets.Scoring; +using Humanizer; +using System; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserRecentActivitiesRequest : APIRequest> + { + private readonly long userId; + private readonly int offset; + + public GetUserRecentActivitiesRequest(long userId, int offset = 0) + { + this.userId = userId; + this.offset = offset; + } + + protected override string Target => $"users/{userId}/recent_activity?offset={offset}"; + } + + public class RecentActivity + { + [JsonProperty("id")] + public int ID; + + [JsonProperty("createdAt")] + public DateTimeOffset CreatedAt; + + [JsonProperty] + private string type + { + set => Type = (RecentActivityType)Enum.Parse(typeof(RecentActivityType), value.Pascalize()); + } + + public RecentActivityType Type; + + [JsonProperty] + private string scoreRank + { + set => ScoreRank = (ScoreRank)Enum.Parse(typeof(ScoreRank), value); + } + + public ScoreRank ScoreRank; + + [JsonProperty("rank")] + public int Rank; + + [JsonProperty("approval")] + public BeatmapApproval Approval; + + [JsonProperty("count")] + public int Count; + + [JsonProperty("mode")] + public string Mode; + + [JsonProperty("beatmap")] + public RecentActivityBeatmap Beatmap; + + [JsonProperty("beatmapset")] + public RecentActivityBeatmap Beatmapset; + + [JsonProperty("user")] + public RecentActivityUser User; + + [JsonProperty("achievement")] + public RecentActivityAchievement Achievement; + + public class RecentActivityBeatmap + { + [JsonProperty("title")] + public string Title; + + [JsonProperty("url")] + public string Url; + } + + public class RecentActivityUser + { + [JsonProperty("username")] + public string Username; + + [JsonProperty("url")] + public string Url; + + [JsonProperty("previousUsername")] + public string PreviousUsername; + } + + public class RecentActivityAchievement + { + [JsonProperty("slug")] + public string Slug; + + [JsonProperty("name")] + public string Name; + } + + } + + public enum RecentActivityType + { + Achievement, + BeatmapPlaycount, + BeatmapsetApprove, + BeatmapsetDelete, + BeatmapsetRevive, + BeatmapsetUpdate, + BeatmapsetUpload, + Medal, + Rank, + RankLost, + UserSupportAgain, + UserSupportFirst, + UserSupportGift, + UsernameChange, + } + + public enum BeatmapApproval + { + Ranked, + Approved, + Qualified, + } +} diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 906f42d50e..9966f78435 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -118,6 +118,8 @@ namespace osu.Game.Online.Chat case "beatmapsets": case "d": return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]); + case "u": + return new LinkDetails(LinkAction.OpenUserProfile, args[3]); } } @@ -146,6 +148,9 @@ namespace osu.Game.Online.Chat case "spectate": linkType = LinkAction.Spectate; break; + case "u": + linkType = LinkAction.OpenUserProfile; + break; default: linkType = LinkAction.External; break; @@ -205,6 +210,15 @@ namespace osu.Game.Online.Chat return inputMessage; } + public static MessageFormatterResult FormatText(string text) + { + var result = format(text); + + result.Links.Sort(); + + return result; + } + public class MessageFormatterResult { public List Links = new List(); @@ -239,6 +253,7 @@ namespace osu.Game.Online.Chat OpenEditorTimestamp, JoinMultiplayerMatch, Spectate, + OpenUserProfile, } public class Link : IComparable diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index aeb23dccd7..e656c7256e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays; using osu.Framework.Logging; -using osu.Game.Graphics.UserInterface.Volume; using osu.Framework.Allocation; using osu.Game.Overlays.Toolbar; using osu.Game.Screens; @@ -33,6 +32,7 @@ using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; using osu.Game.Skinning; using OpenTK.Graphics; +using osu.Game.Overlays.Volume; namespace osu.Game { @@ -75,7 +75,7 @@ namespace osu.Game private OsuScreen screenStack; - private VolumeControl volume; + private VolumeOverlay volume; private OnScreenDisplay onscreenDisplay; private Bindable configRuleset; @@ -155,6 +155,12 @@ namespace osu.Game /// The set to display. public void ShowBeatmapSet(int setId) => beatmapSetOverlay.ShowBeatmapSet(setId); + /// + /// Show a user's profile as an overlay. + /// + /// The user to display. + public void ShowUser(long userId) => userProfile.ShowUser(userId); + protected void LoadScore(Score s) { scoreLoad?.Cancel(); @@ -232,7 +238,7 @@ namespace osu.Game }, }, overlayContent.Add); - loadComponentSingleFile(volume = new VolumeControl(), Add); + loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add); loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add); //overlay elements diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index f028590bb4..1d3dab249d 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -53,9 +53,9 @@ namespace osu.Game.Overlays.Chat protected override void AddTabItem(TabItem item, bool addToDropdown = true) { - if (selectorTab.Depth < float.MaxValue) + if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue) // performTabSort might've made selectorTab's position wonky, fix it - TabContainer.ChangeChildDepth(selectorTab, float.MaxValue); + TabContainer.SetLayoutPosition(selectorTab, float.MaxValue); base.AddTabItem(item, addToDropdown); diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 31b7d0f9aa..03ce7fd88f 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -101,11 +101,10 @@ namespace osu.Game.Overlays.Music public void AddBeatmapSet(BeatmapSetInfo beatmapSet) { - items.Add(new PlaylistItem(beatmapSet) - { - OnSelect = set => OnSelect?.Invoke(set), - Depth = items.Count - }); + var newItem = new PlaylistItem(beatmapSet) { OnSelect = set => OnSelect?.Invoke(set) }; + + items.Add(newItem); + items.SetLayoutPosition(newItem, items.Count); } public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) @@ -197,7 +196,7 @@ namespace osu.Game.Overlays.Music { var itemsPos = items.ToLocalSpace(nativeDragPosition); - int srcIndex = (int)draggedItem.Depth; + int srcIndex = (int)items.GetLayoutPosition(draggedItem); // Find the last item with position < mouse position. Note we can't directly use // the item positions as they are being transformed @@ -219,15 +218,15 @@ namespace osu.Game.Overlays.Music if (srcIndex < dstIndex) { for (int i = srcIndex + 1; i <= dstIndex; i++) - items.ChangeChildDepth(items[i], i - 1); + items.SetLayoutPosition(items[i], i - 1); } else { for (int i = dstIndex; i < srcIndex; i++) - items.ChangeChildDepth(items[i], i + 1); + items.SetLayoutPosition(items[i], i + 1); } - items.ChangeChildDepth(draggedItem, dstIndex); + items.SetLayoutPosition(draggedItem, dstIndex); } private class ItemSearchContainer : FillFlowContainer, IHasFilterableChildren @@ -243,9 +242,6 @@ namespace osu.Game.Overlays.Music } } - // Compare with reversed ChildID and Depth - protected override int Compare(Drawable x, Drawable y) => base.Compare(y, x); - public IEnumerable FilterableChildren => Children; public ItemSearchContainer() diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 2f46bb4a71..48ad507d88 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -129,7 +129,6 @@ namespace osu.Game.Overlays public void Post(Notification notification) => postScheduler.Add(() => { ++runningDepth; - notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth; notification.Closed += notificationClosed; @@ -138,7 +137,9 @@ namespace osu.Game.Overlays hasCompletionTarget.CompletionTarget = Post; var ourType = notification.GetType(); - sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)))?.Add(notification); + + var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType))); + section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth); updateCounts(); }); diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index 13a69fbe3a..533f5326e3 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -25,10 +25,13 @@ namespace osu.Game.Overlays.Notifications private FlowContainer notifications; public int DisplayedCount => notifications.Count(n => !n.WasClosed); - public int UnreadCount => notifications.Count(n => !n.WasClosed && !n.Read); - public void Add(Notification notification) => notifications.Add(notification); + public void Add(Notification notification, float position) + { + notifications.Add(notification); + notifications.SetLayoutPosition(notification, position); + } public IEnumerable AcceptTypes; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index 51b202844a..bb1a409f2e 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -40,16 +40,18 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks [BackgroundDependencyLoader(true)] private void load(OsuColour colour) { - RightFlowContainer.Add(new OsuSpriteText + var text = new OsuSpriteText { Text = $"accuracy: {Score.Accuracy:P2}", Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Colour = colour.GrayA, TextSize = 11, - Font = "Exo2.0-RegularItalic", - Depth = -1, - }); + Font = "Exo2.0-RegularItalic" + }; + + RightFlowContainer.Add(text); + RightFlowContainer.SetLayoutPosition(text, 1); LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap)); LeftFlowContainer.Add(new OsuSpriteText diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs new file mode 100644 index 0000000000..2dde8a3d54 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs @@ -0,0 +1,165 @@ +// Copyright (c) 2007-2018 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.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Chat; +using osu.Game.Screens.Select.Leaderboards; + +namespace osu.Game.Overlays.Profile.Sections.Recent +{ + public class DrawableRecentActivity : DrawableProfileRow + { + private APIAccess api; + + private readonly RecentActivity activity; + + private LinkFlowContainer content; + + public DrawableRecentActivity(RecentActivity activity) + { + this.activity = activity; + } + + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + this.api = api; + + LeftFlowContainer.Padding = new MarginPadding { Left = 10, Right = 160 }; + + LeftFlowContainer.Add(content = new LinkFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }); + + RightFlowContainer.Add(new OsuSpriteText + { + Text = activity.CreatedAt.LocalDateTime.ToShortDateString(), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Font = "Exo2.0-RegularItalic", + TextSize = 12, + Colour = OsuColour.Gray(0xAA), + }); + + var formatted = createMessage(); + + content.AddLinks(formatted.Text, formatted.Links); + } + + protected override Drawable CreateLeftVisual() + { + switch (activity.Type) + { + case RecentActivityType.Rank: + return new DrawableRank(activity.ScoreRank) + { + RelativeSizeAxes = Axes.Y, + Width = 60, + FillMode = FillMode.Fit, + }; + + case RecentActivityType.Achievement: + return new MedalIcon(activity.Achievement.Slug) + { + RelativeSizeAxes = Axes.Y, + Width = 60, + FillMode = FillMode.Fit, + }; + + default: + return new Container + { + RelativeSizeAxes = Axes.Y, + Width = 60, + FillMode = FillMode.Fit, + }; + } + } + + private string toAbsoluteUrl(string url) => $"{api.Endpoint}{url}"; + + private MessageFormatter.MessageFormatterResult createMessage() + { + string userLinkTemplate() => $"[{toAbsoluteUrl(activity.User?.Url)} {activity.User?.Username}]"; + string beatmapLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmap?.Url)} {activity.Beatmap?.Title}]"; + string beatmapsetLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmapset?.Url)} {activity.Beatmapset?.Title}]"; + + string message; + + switch (activity.Type) + { + case RecentActivityType.Achievement: + message = $"{userLinkTemplate()} unlocked the {activity.Achievement.Name} medal!"; + break; + + case RecentActivityType.BeatmapPlaycount: + message = $"{beatmapLinkTemplate()} has been played {activity.Count} times!"; + break; + + case RecentActivityType.BeatmapsetApprove: + message = $"{beatmapsetLinkTemplate()} has been {activity.Approval.ToString().ToLowerInvariant()}!"; + break; + + case RecentActivityType.BeatmapsetDelete: + message = $"{beatmapsetLinkTemplate()} has been deleted."; + break; + + case RecentActivityType.BeatmapsetRevive: + message = $"{beatmapsetLinkTemplate()} has been revived from eternal slumber by {userLinkTemplate()}."; + break; + + case RecentActivityType.BeatmapsetUpdate: + message = $"{userLinkTemplate()} has updated the beatmap {beatmapsetLinkTemplate()}!"; + break; + + case RecentActivityType.BeatmapsetUpload: + message = $"{userLinkTemplate()} has submitted a new beatmap {beatmapsetLinkTemplate()}!"; + break; + + case RecentActivityType.Medal: + // apparently this shouldn't exist look at achievement instead (https://github.com/ppy/osu-web/blob/master/resources/assets/coffee/react/profile-page/recent-activity.coffee#L111) + message = string.Empty; + break; + + case RecentActivityType.Rank: + message = $"{userLinkTemplate()} achieved rank #{activity.Rank} on {beatmapLinkTemplate()} ({activity.Mode}!)"; + break; + + case RecentActivityType.RankLost: + message = $"{userLinkTemplate()} has lost first place on {beatmapLinkTemplate()} ({activity.Mode}!)"; + break; + + case RecentActivityType.UserSupportAgain: + message = $"{userLinkTemplate()} has once again chosen to support osu! - thanks for your generosity!"; + break; + + case RecentActivityType.UserSupportFirst: + message = $"{userLinkTemplate()} has become an osu! supporter - thanks for your generosity!"; + break; + + case RecentActivityType.UserSupportGift: + message = $"{userLinkTemplate()} has received the gift of osu! supporter!"; + break; + + case RecentActivityType.UsernameChange: + message = $"{activity.User?.PreviousUsername} has changed their username to {userLinkTemplate()}!"; + break; + + default: + message = string.Empty; + break; + } + + return MessageFormatter.FormatText(message); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs b/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs new file mode 100644 index 0000000000..6ffbe7193f --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Overlays.Profile.Sections.Recent +{ + public class MedalIcon : Container + { + private readonly string slug; + private readonly Sprite sprite; + + private string url => $@"https://s.ppy.sh/images/medals-client/{slug}@2x.png"; + + public MedalIcon(string slug) + { + this.slug = slug; + + Child = sprite = new Sprite + { + Height = 40, + Width = 40, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + sprite.Texture = textures.Get(url); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs new file mode 100644 index 0000000000..d479627cde --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -0,0 +1,48 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Game.Online.API.Requests; +using osu.Game.Users; +using System.Linq; + +namespace osu.Game.Overlays.Profile.Sections.Recent +{ + public class PaginatedRecentActivityContainer : PaginatedContainer + { + public PaginatedRecentActivityContainer(Bindable user, string header, string missing) + : base(user, header, missing) + { + ItemsPerPage = 5; + } + + protected override void ShowMore() + { + base.ShowMore(); + + var req = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); + + req.Success += activities => + { + ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); + ShowMoreLoading.Hide(); + + if (!activities.Any() && VisiblePages == 1) + { + MissingText.Show(); + return; + } + + MissingText.Hide(); + + foreach (RecentActivity activity in activities) + { + ItemsContainer.Add(new DrawableRecentActivity(activity)); + } + }; + + Api.Queue(req); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/RecentSection.cs b/osu.Game/Overlays/Profile/Sections/RecentSection.cs index 78b139efe8..84a941aa1a 100644 --- a/osu.Game/Overlays/Profile/Sections/RecentSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RecentSection.cs @@ -1,12 +1,22 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Overlays.Profile.Sections.Recent; + namespace osu.Game.Overlays.Profile.Sections { public class RecentSection : ProfileSection { public override string Title => "Recent"; - public override string Identifier => "recent_activities"; + public override string Identifier => "recent_activity"; + + public RecentSection() + { + Children = new[] + { + new PaginatedRecentActivityContainer(User, null, @"This user hasn't done anything notable recently!"), + }; + } } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 5afc415d83..cc290fe1bb 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -45,7 +45,8 @@ namespace osu.Game.Overlays.Settings if (text == null) { // construct lazily for cases where the label is not needed (may be provided by the Control). - Add(text = new OsuSpriteText { Depth = 1 }); + Add(text = new OsuSpriteText()); + FlowContent.SetLayoutPosition(text, -1); } text.Text = value; diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 59f940a19d..f3fd7aeac5 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -73,6 +73,14 @@ namespace osu.Game.Overlays FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); } + public void ShowUser(long userId) + { + if (userId == Header.User.Id) + return; + + ShowUser(new User { Id = userId }); + } + public void ShowUser(User user, bool fetchOnline = true) { userReq?.Cancel(); @@ -82,7 +90,7 @@ namespace osu.Game.Overlays sections = new ProfileSection[] { //new AboutSection(), - //new RecentSection(), + new RecentSection(), new RanksSection(), //new MedalsSection(), new HistoricalSection(), diff --git a/osu.Game/Overlays/Volume/MuteButton.cs b/osu.Game/Overlays/Volume/MuteButton.cs new file mode 100644 index 0000000000..adfc9c610f --- /dev/null +++ b/osu.Game/Overlays/Volume/MuteButton.cs @@ -0,0 +1,83 @@ +// Copyright (c) 2007-2018 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.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Volume +{ + public class MuteButton : Container, IHasCurrentValue + { + public Bindable Current { get; } = new Bindable(); + + private Color4 hoveredColour, unhoveredColour; + private const float width = 100; + public const float HEIGHT = 35; + + public MuteButton() + { + Masking = true; + BorderThickness = 3; + CornerRadius = HEIGHT / 2; + Size = new Vector2(width, HEIGHT); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoveredColour = colours.YellowDark; + BorderColour = unhoveredColour = colours.Gray1.Opacity(0.9f); + + SpriteIcon icon; + AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray1, + Alpha = 0.9f, + }, + icon = new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20), + } + }); + + Current.ValueChanged += newValue => + { + icon.Icon = newValue ? FontAwesome.fa_volume_off : FontAwesome.fa_volume_up; + icon.Margin = new MarginPadding { Left = newValue ? width / 2 - 15 : width / 2 - 10 }; //Magic numbers to line up both icons because they're different widths + }; + Current.TriggerChange(); + } + + protected override bool OnHover(InputState state) + { + this.TransformTo("BorderColour", hoveredColour, 500, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(InputState state) + { + this.TransformTo("BorderColour", unhoveredColour, 500, Easing.OutQuint); + } + + protected override bool OnClick(InputState state) + { + Current.Value = !Current.Value; + return true; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs similarity index 90% rename from osu.Game/Graphics/UserInterface/Volume/VolumeControlReceptor.cs rename to osu.Game/Overlays/Volume/VolumeControlReceptor.cs index 2328533665..a5be7dc445 100644 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -7,7 +7,7 @@ using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Game.Input.Bindings; -namespace osu.Game.Graphics.UserInterface.Volume +namespace osu.Game.Overlays.Volume { public class VolumeControlReceptor : Container, IKeyBindingHandler, IHandleGlobalInput { diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs new file mode 100644 index 0000000000..64b9e513c4 --- /dev/null +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -0,0 +1,186 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Globalization; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Bindings; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Volume +{ + public class VolumeMeter : Container, IKeyBindingHandler + { + private CircularProgress volumeCircle; + public BindableDouble Bindable { get; } = new BindableDouble { MinValue = 0, MaxValue = 1 }; + private readonly float circleSize; + private readonly Color4 meterColour; + private readonly string name; + + private OsuSpriteText text; + private BufferedContainer maxGlow; + + public VolumeMeter(string name, float circleSize, Color4 meterColour) + { + this.circleSize = circleSize; + this.meterColour = meterColour; + this.name = name; + + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Add(new Container + { + Size = new Vector2(120, 20), + CornerRadius = 10, + Masking = true, + Margin = new MarginPadding { Left = circleSize + 10 }, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray1, + Alpha = 0.9f, + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = "Exo2.0-Bold", + Text = name + } + } + }); + + CircularProgress bgProgress; + + Add(new CircularContainer + { + Masking = true, + Size = new Vector2(circleSize), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray1, + Alpha = 0.9f, + }, + bgProgress = new CircularProgress + { + RelativeSizeAxes = Axes.Both, + InnerRadius = 0.05f, + Rotation = 180, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colours.Gray2, + Size = new Vector2(0.8f) + }, + (volumeCircle = new CircularProgress + { + RelativeSizeAxes = Axes.Both, + InnerRadius = 0.05f, + Rotation = 180, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.8f) + }).WithEffect(new GlowEffect + { + Colour = meterColour, + Strength = 2 + }), + maxGlow = (text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = "Venera", + TextSize = 0.16f * circleSize + }).WithEffect(new GlowEffect + { + Colour = Color4.Transparent, + PadExtent = true, + }) + } + }); + + Bindable.ValueChanged += newVolume => { this.TransformTo("DisplayVolume", newVolume, 400, Easing.OutQuint); }; + bgProgress.Current.Value = 0.75f; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Bindable.TriggerChange(); + } + + private double displayVolume; + + protected double DisplayVolume + { + get => displayVolume; + set + { + displayVolume = value; + + if (displayVolume > 0.99f) + { + text.Text = "MAX"; + maxGlow.EffectColour = meterColour.Opacity(2f); + } + else + { + maxGlow.EffectColour = Color4.Transparent; + text.Text = Math.Round(displayVolume * 100).ToString(CultureInfo.CurrentCulture); + } + + volumeCircle.Current.Value = displayVolume * 0.75f; + } + } + + public double Volume + { + get => Bindable; + private set => Bindable.Value = value; + } + + public void Increase() => Volume += 0.05f; + + public void Decrease() => Volume -= 0.05f; + + public bool OnPressed(GlobalAction action) + { + if (!IsHovered) return false; + + switch (action) + { + case GlobalAction.DecreaseVolume: + Decrease(); + return true; + case GlobalAction.IncreaseVolume: + Increase(); + return true; + } + + return false; + } + + public bool OnReleased(GlobalAction action) => false; + } +} diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs b/osu.Game/Overlays/VolumeOverlay.cs similarity index 57% rename from osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs rename to osu.Game/Overlays/VolumeOverlay.cs index ccf70af6ed..17a4b139b0 100644 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -1,57 +1,84 @@ // Copyright (c) 2007-2018 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; -using osu.Framework.Threading; -using OpenTK; -using osu.Framework.Audio; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Threading; +using osu.Game.Graphics; using osu.Game.Input.Bindings; +using osu.Game.Overlays.Volume; +using OpenTK; +using OpenTK.Graphics; -namespace osu.Game.Graphics.UserInterface.Volume +namespace osu.Game.Overlays { - public class VolumeControl : OverlayContainer + public class VolumeOverlay : OverlayContainer { - private readonly VolumeMeter volumeMeterMaster; - private readonly IconButton muteIcon; + private const float offset = 10; + + private VolumeMeter volumeMeterMaster; + private VolumeMeter volumeMeterEffect; + private VolumeMeter volumeMeterMusic; + private MuteButton muteButton; protected override bool BlockPassThroughMouse => false; - public VolumeControl() - { - AutoSizeAxes = Axes.Both; - Anchor = Anchor.BottomRight; - Origin = Anchor.BottomRight; + private readonly BindableDouble muteAdjustment = new BindableDouble(); - Children = new Drawable[] + [BackgroundDependencyLoader] + private void load(AudioManager audio, OsuColour colours) + { + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + + AddRange(new Drawable[] { + new Box + { + RelativeSizeAxes = Axes.Y, + Width = 300, + Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.75f), Color4.Black.Opacity(0)) + }, new FillFlowContainer { + Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Both, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Margin = new MarginPadding { Left = 10, Right = 10, Top = 30, Bottom = 30 }, - Spacing = new Vector2(15, 0), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Spacing = new Vector2(0, offset), + Margin = new MarginPadding { Left = offset }, Children = new Drawable[] { - new Container + volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker) { - Size = new Vector2(IconButton.BUTTON_SIZE), - Child = muteIcon = new IconButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.fa_volume_up, - Action = () => Adjust(GlobalAction.ToggleMute), - } + Margin = new MarginPadding { Top = 100 + MuteButton.HEIGHT } //to counter the mute button and re-center the volume meters }, - volumeMeterMaster = new VolumeMeter("Master"), - volumeMeterEffect = new VolumeMeter("Effects"), - volumeMeterMusic = new VolumeMeter("Music") + volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker), + volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker), + muteButton = new MuteButton + { + Margin = new MarginPadding { Top = 100 } + } } - } + }, + }); + + volumeMeterMaster.Bindable.BindTo(audio.Volume); + volumeMeterEffect.Bindable.BindTo(audio.VolumeSample); + volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack); + + muteButton.Current.ValueChanged += mute => + { + if (mute) + audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment); + else + audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment); }; } @@ -62,7 +89,13 @@ namespace osu.Game.Graphics.UserInterface.Volume volumeMeterMaster.Bindable.ValueChanged += _ => settingChanged(); volumeMeterEffect.Bindable.ValueChanged += _ => settingChanged(); volumeMeterMusic.Bindable.ValueChanged += _ => settingChanged(); - muted.ValueChanged += _ => settingChanged(); + muteButton.Current.ValueChanged += _ => settingChanged(); + } + + private void settingChanged() + { + Show(); + schedulePopOut(); } public bool Adjust(GlobalAction action) @@ -83,50 +116,15 @@ namespace osu.Game.Graphics.UserInterface.Volume return true; case GlobalAction.ToggleMute: Show(); - muted.Toggle(); + muteButton.Current.Value = !muteButton.Current; return true; } return false; } - private void settingChanged() - { - Show(); - schedulePopOut(); - } - - private readonly BindableDouble muteAdjustment = new BindableDouble(); - - private readonly BindableBool muted = new BindableBool(); - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - volumeMeterMaster.Bindable.BindTo(audio.Volume); - volumeMeterEffect.Bindable.BindTo(audio.VolumeSample); - volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack); - - muted.ValueChanged += mute => - { - if (mute) - { - audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment); - muteIcon.Icon = FontAwesome.fa_volume_off; - } - else - { - audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment); - muteIcon.Icon = FontAwesome.fa_volume_up; - } - }; - } - private ScheduledDelegate popOutDelegate; - private readonly VolumeMeter volumeMeterEffect; - private readonly VolumeMeter volumeMeterMusic; - protected override void PopIn() { ClearTransforms(); diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index f9b64995f9..cba849a491 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets /// /// Do not override this unless you are a legacy mode. /// - public virtual int LegacyID => -1; + public virtual int? LegacyID => null; /// /// A unique short name to reference this ruleset in online requests. diff --git a/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs b/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs index 29ae35fca4..ae1e995373 100644 --- a/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs +++ b/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs @@ -4,8 +4,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using OpenTK; -using System.Collections.Generic; -using System.Linq; namespace osu.Game.Screens.Menu { @@ -22,8 +20,6 @@ namespace osu.Game.Screens.Menu protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y); - protected override IEnumerable FlowingChildren => base.FlowingChildren.Reverse(); - public override Anchor Origin => Anchor.Custom; public override Vector2 OriginPosition diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index 1a7b80ec9a..6c4d929c71 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -15,7 +15,6 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly PlayerSliderBar dimSliderBar; private readonly PlayerSliderBar blurSliderBar; private readonly PlayerCheckbox showStoryboardToggle; - private readonly PlayerCheckbox mouseWheelDisabledToggle; public VisualSettings() { @@ -35,8 +34,7 @@ namespace osu.Game.Screens.Play.PlayerSettings { Text = "Toggles:" }, - showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" }, - mouseWheelDisabledToggle = new PlayerCheckbox { LabelText = "Disable mouse wheel" } + showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" } }; } @@ -46,7 +44,6 @@ namespace osu.Game.Screens.Play.PlayerSettings dimSliderBar.Bindable = config.GetBindable(OsuSetting.DimLevel); blurSliderBar.Bindable = config.GetBindable(OsuSetting.BlurLevel); showStoryboardToggle.Bindable = config.GetBindable(OsuSetting.ShowStoryboard); - mouseWheelDisabledToggle.Bindable = config.GetBindable(OsuSetting.MouseDisableWheel); } } } diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 21e6108489..be83d7b500 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -41,19 +41,25 @@ namespace osu.Game.Screens.Select /// Higher depth to be put on the left, and lower to be put on the right. /// Notice this is different to ! /// - public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0) => buttons.Add(new FooterButton + public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0) { - Text = text, - Height = play_song_select_button_height, - Width = play_song_select_button_width, - Depth = depth, - SelectedColour = colour, - DeselectedColour = colour.Opacity(0.5f), - Hotkey = hotkey, - Hovered = updateModeLight, - HoverLost = updateModeLight, - Action = action, - }); + var button = new FooterButton + { + Text = text, + Height = play_song_select_button_height, + Width = play_song_select_button_width, + Depth = depth, + SelectedColour = colour, + DeselectedColour = colour.Opacity(0.5f), + Hotkey = hotkey, + Hovered = updateModeLight, + HoverLost = updateModeLight, + Action = action, + }; + + buttons.Add(button); + buttons.SetLayoutPosition(button, -depth); + } private readonly List overlays = new List(); diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index 2e8b2f9014..dee1ec4511 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -95,7 +95,7 @@ namespace osu.Game.Screens.Select.Options /// public void AddButton(string firstLine, string secondLine, FontAwesome icon, Color4 colour, Action action, Key? hotkey = null, float depth = 0) { - buttonsContainer.Add(new BeatmapOptionsButton + var button = new BeatmapOptionsButton { FirstLineText = firstLine, SecondLineText = secondLine, @@ -108,7 +108,10 @@ namespace osu.Game.Screens.Select.Options action?.Invoke(); }, HotKey = hotkey - }); + }; + + buttonsContainer.Add(button); + buttonsContainer.SetLayoutPosition(button, depth); } } } diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index a9b13e87bf..219d805bc1 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -109,10 +109,13 @@ namespace osu.Game.Tests.Beatmaps private Beatmap getBeatmap(string name) { - var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = openResource($"{resource_namespace}.{name}.osu")) using (var stream = new StreamReader(resStream)) + { + var decoder = Decoder.GetDecoder(stream); + ((LegacyBeatmapDecoder)decoder).ApplyOffsets = false; return decoder.DecodeBeatmap(stream); + } } private Stream openResource(string name) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 406e251899..fdcf1152d0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -294,12 +294,16 @@ + 20180125143340_Settings.cs + + + @@ -351,6 +355,10 @@ + + + + @@ -474,9 +482,6 @@ - - -