diff --git a/appveyor.yml b/appveyor.yml index cc6dfb9c88..b26a895788 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,4 +20,4 @@ build: verbosity: minimal after_build: - cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln - - cmd: NVika parsereport "inspectcodereport.xml" \ No newline at end of file + - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors \ No newline at end of file diff --git a/osu-framework b/osu-framework index 0f3db5da09..97ff3376d1 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 0f3db5da09d0e7c4d2ef3057030e018f34ba536e +Subproject commit 97ff3376d1bdac3703d442e62f5ee6a36eb3b73f diff --git a/osu-resources b/osu-resources index 9f46a456dc..10fda22522 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 9f46a456dc3a56dcbff09671a3f588b16a464106 +Subproject commit 10fda22522ffadbdbc43fa0f3683a065e536f7d1 diff --git a/osu.Desktop.Deploy/App.config b/osu.Desktop.Deploy/App.config index 45685a74a8..6711f9c54e 100644 --- a/osu.Desktop.Deploy/App.config +++ b/osu.Desktop.Deploy/App.config @@ -31,6 +31,10 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste + + + + \ No newline at end of file diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj index 1f9726b573..c6474eae5a 100644 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj @@ -49,10 +49,6 @@ $(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll True - - $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\ICSharpCode.SharpZipLib.dll - True - $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll True @@ -73,15 +69,19 @@ $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll + $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll + True + + + $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll True $(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll True - - $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\Squirrel.dll + + $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll True diff --git a/osu.Desktop.Deploy/packages.config b/osu.Desktop.Deploy/packages.config index 4878297be9..3c5ca9f9a3 100644 --- a/osu.Desktop.Deploy/packages.config +++ b/osu.Desktop.Deploy/packages.config @@ -7,7 +7,8 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - + + - + \ No newline at end of file diff --git a/osu.Desktop.Tests/app.config b/osu.Desktop.Tests/app.config new file mode 100644 index 0000000000..faeaf001de --- /dev/null +++ b/osu.Desktop.Tests/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/osu.Desktop.Tests/osu.Desktop.Tests.csproj b/osu.Desktop.Tests/osu.Desktop.Tests.csproj index f0620c98ef..f940e4be9e 100644 --- a/osu.Desktop.Tests/osu.Desktop.Tests.csproj +++ b/osu.Desktop.Tests/osu.Desktop.Tests.csproj @@ -37,8 +37,9 @@ $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - - $(SolutionDir)\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll + + $(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll + True False @@ -100,6 +101,7 @@ osu.licenseheader + diff --git a/osu.Desktop.Tests/packages.config b/osu.Desktop.Tests/packages.config index ad51a60195..7bd35a3abe 100644 --- a/osu.Desktop.Tests/packages.config +++ b/osu.Desktop.Tests/packages.config @@ -5,7 +5,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste --> - + diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs index 58cbad936a..df80ffdf53 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs @@ -41,7 +41,7 @@ namespace osu.Desktop.VisualTests.Tests StarDifficulty = 5.3f, Metrics = new BeatmapMetrics { - Ratings = Enumerable.Range(0,10), + Ratings = Enumerable.Range(0, 10), Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6), }, diff --git a/osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs b/osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs new file mode 100644 index 0000000000..808e9b5d19 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs @@ -0,0 +1,125 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseContextMenu : TestCase + { + public override string Description => @"Menu visible on right click"; + + private const int start_time = 0; + private const int duration = 1000; + + private MyContextMenuContainer container; + + public override void Reset() + { + base.Reset(); + + Add(container = new MyContextMenuContainer + { + Size = new Vector2(200), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Green, + } + } + }); + + Add(new AnotherContextMenuContainer + { + Size = new Vector2(200), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Red, + } + } + }); + + container.Transforms.Add(new TransformPosition + { + StartValue = Vector2.Zero, + EndValue = new Vector2(0, 100), + StartTime = start_time, + EndTime = start_time + duration, + LoopCount = -1, + LoopDelay = duration * 3 + }); + container.Transforms.Add(new TransformPosition + { + StartValue = new Vector2(0, 100), + EndValue = new Vector2(100, 100), + StartTime = start_time + duration, + EndTime = start_time + duration * 2, + LoopCount = -1, + LoopDelay = duration * 3 + }); + container.Transforms.Add(new TransformPosition + { + StartValue = new Vector2(100, 100), + EndValue = new Vector2(100, 0), + StartTime = start_time + duration * 2, + EndTime = start_time + duration * 3, + LoopCount = -1, + LoopDelay = duration * 3 + }); + container.Transforms.Add(new TransformPosition + { + StartValue = new Vector2(100, 0), + EndValue = Vector2.Zero, + StartTime = start_time + duration * 3, + EndTime = start_time + duration * 4, + LoopCount = -1, + LoopDelay = duration * 3 + }); + } + + private class MyContextMenuContainer : Container, IHasContextMenu + { + public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[] + { + new OsuContextMenuItem(@"Some option"), + new OsuContextMenuItem(@"Highlighted option", MenuItemType.Highlighted), + new OsuContextMenuItem(@"Another option"), + new OsuContextMenuItem(@"Choose me please"), + new OsuContextMenuItem(@"And me too"), + new OsuContextMenuItem(@"Trying to fill"), + new OsuContextMenuItem(@"Destructive option", MenuItemType.Destructive), + }; + } + + private class AnotherContextMenuContainer : Container, IHasContextMenu + { + public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[] + { + new OsuContextMenuItem(@"Simple option"), + new OsuContextMenuItem(@"Simple very very long option"), + new OsuContextMenuItem(@"Change width", MenuItemType.Highlighted) { Action = () => ResizeWidthTo(Width * 2, 100, EasingTypes.OutQuint) }, + new OsuContextMenuItem(@"Change height", MenuItemType.Highlighted) { Action = () => ResizeHeightTo(Height * 2, 100, EasingTypes.OutQuint) }, + new OsuContextMenuItem(@"Change width back", MenuItemType.Destructive) { Action = () => ResizeWidthTo(Width / 2, 100, EasingTypes.OutQuint) }, + new OsuContextMenuItem(@"Change height back", MenuItemType.Destructive) { Action = () => ResizeHeightTo(Height / 2, 100, EasingTypes.OutQuint) }, + }; + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs b/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs index 32af330f44..43a8069720 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs @@ -36,7 +36,7 @@ namespace osu.Desktop.VisualTests.Tests }); first.Room.Name.Value = @"Great Room Right Here"; - first.Room.Host.Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" }}; + first.Room.Host.Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" } }; first.Room.Status.Value = new RoomStatusOpen(); first.Room.Beatmap.Value = new BeatmapInfo { @@ -48,7 +48,7 @@ namespace osu.Desktop.VisualTests.Tests }; second.Room.Name.Value = @"Relax It's The Weekend"; - second.Room.Host.Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" }}; + second.Room.Host.Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" } }; second.Room.Status.Value = new RoomStatusPlaying(); second.Room.Beatmap.Value = new BeatmapInfo { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGraph.cs b/osu.Desktop.VisualTests/Tests/TestCaseGraph.cs index 7ac795f6f9..f653e2b9b4 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseGraph.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseGraph.cs @@ -30,7 +30,7 @@ namespace osu.Desktop.VisualTests.Tests }, }; - AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1,10).Select(i => (float)i)); + AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i)); AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i)); AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i)); AddStep("Bottom to top", () => graph.Direction = BarDirection.BottomToTop); diff --git a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs b/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs index b1b9ddbcda..a28176b512 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using OpenTK; using OpenTK.Graphics; using osu.Framework.MathUtils; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Screens.Play; @@ -54,7 +55,7 @@ namespace osu.Desktop.VisualTests.Tests Children = new Drawable[] { new SpriteText { Text = "FadeTime" }, - sliderBar =new TestSliderBar + sliderBar = new TestSliderBar { Width = 150, Height = 10, diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs index 3113b63db1..c66b0b4db4 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs @@ -40,7 +40,7 @@ namespace osu.Desktop.VisualTests.Tests { Name = "Timing section", RelativeSizeAxes = Axes.Both, - RelativeCoordinateSpace = new Vector2(1, 10000), + RelativeChildSize = new Vector2(1, 10000), Children = new[] { new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red }, @@ -62,7 +62,7 @@ namespace osu.Desktop.VisualTests.Tests { Name = "Timing section", RelativeSizeAxes = Axes.Both, - RelativeCoordinateSpace = new Vector2(1, 10000), + RelativeChildSize = new Vector2(1, 10000), Children = new[] { new DrawableHoldNote(new HoldNote diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs index 95287c3199..352b6cdc81 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs @@ -6,14 +6,16 @@ using osu.Framework.Testing; using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.UI; using System; -using System.Collections.Generic; using OpenTK; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Timing; using osu.Framework.Configuration; using OpenTK.Input; using osu.Framework.Timing; +using osu.Framework.Extensions.IEnumerableExtensions; +using System.Linq; +using osu.Game.Rulesets.Mania.Timing; +using osu.Game.Rulesets.Timing; namespace osu.Desktop.VisualTests.Tests { @@ -30,7 +32,7 @@ namespace osu.Desktop.VisualTests.Tests Action createPlayfield = (cols, pos) => { Clear(); - Add(new ManiaPlayfield(cols, new List()) + Add(new ManiaPlayfield(cols) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -39,37 +41,22 @@ namespace osu.Desktop.VisualTests.Tests }); }; - Action createPlayfieldWithNotes = (cols, pos) => + const double start_time = 500; + const double duration = 500; + + Func createTimingChange = (time, gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time) + { + TimingPoint = { BeatLength = 1000 } + }, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic); + + Action createPlayfieldWithNotes = gravity => { Clear(); - ManiaPlayfield playField; - Add(playField = new ManiaPlayfield(cols, new List { new TimingChange { BeatLength = 200 } }) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - SpecialColumnPosition = pos, - Scale = new Vector2(1, -1) - }); - - for (int i = 0; i < cols; i++) - { - playField.Add(new DrawableNote(new Note - { - StartTime = Time.Current + 1000, - Column = i - })); - } - }; - - Action createPlayfieldWithNotesAcceptingInput = () => - { - Clear(); - - var rateAdjustClock = new StopwatchClock(true) { Rate = 0.5 }; + var rateAdjustClock = new StopwatchClock(true) { Rate = 1 }; ManiaPlayfield playField; - Add(playField = new ManiaPlayfield(4, new List { new TimingChange { BeatLength = 200 } }) + Add(playField = new ManiaPlayfield(4) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -77,14 +64,23 @@ namespace osu.Desktop.VisualTests.Tests Clock = new FramedClock(rateAdjustClock) }); - for (int t = 1000; t <= 2000; t += 100) + if (!gravity) + playField.Columns.ForEach(c => c.Add(createTimingChange(0, false))); + + for (double t = start_time; t <= start_time + duration; t += 100) { + if (gravity) + playField.Columns.ElementAt(0).Add(createTimingChange(t, true)); + playField.Add(new DrawableNote(new Note { StartTime = t, Column = 0 }, new Bindable(Key.D))); + if (gravity) + playField.Columns.ElementAt(3).Add(createTimingChange(t, true)); + playField.Add(new DrawableNote(new Note { StartTime = t, @@ -92,17 +88,23 @@ namespace osu.Desktop.VisualTests.Tests }, new Bindable(Key.K))); } - playField.Add(new DrawableHoldNote(new HoldNote - { - StartTime = 1000, - Duration = 1000, - Column = 1 - }, new Bindable(Key.F))); + if (gravity) + playField.Columns.ElementAt(1).Add(createTimingChange(start_time, true)); playField.Add(new DrawableHoldNote(new HoldNote { - StartTime = 1000, - Duration = 1000, + StartTime = start_time, + Duration = duration, + Column = 1 + }, new Bindable(Key.F))); + + if (gravity) + playField.Columns.ElementAt(2).Add(createTimingChange(start_time, true)); + + playField.Add(new DrawableHoldNote(new HoldNote + { + StartTime = start_time, + Duration = duration, Column = 2 }, new Bindable(Key.J))); }; @@ -116,16 +118,11 @@ namespace osu.Desktop.VisualTests.Tests AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left)); AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right)); - AddStep("Normal special style", () => createPlayfield(4, SpecialColumnPosition.Normal)); + AddStep("Notes with input", () => createPlayfieldWithNotes(false)); + AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction)); - AddStep("Notes", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Normal)); - AddWaitStep(10); - AddStep("Left special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Left)); - AddWaitStep(10); - AddStep("Right special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Right)); - AddWaitStep(10); - - AddStep("Notes with input", () => createPlayfieldWithNotesAcceptingInput()); + AddStep("Notes with gravity", () => createPlayfieldWithNotes(true)); + AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction)); } private void triggerKeyDown(Column column) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs b/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs index ddb62598cf..0caa518a0b 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs @@ -3,9 +3,9 @@ using osu.Framework.Testing; using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Sprites; using osu.Game.Screens.Menu; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Desktop.VisualTests.Tests { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs b/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs index 23fe8f16db..4de8b297eb 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs @@ -35,7 +35,7 @@ namespace osu.Desktop.VisualTests.Tests }); AddStep(@"Pause", delegate { - if(failOverlay.State == Visibility.Visible) + if (failOverlay.State == Visibility.Visible) { failOverlay.Hide(); } diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs index f28cdd6a7e..d922f3bb4b 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; using OpenTK; -using osu.Framework.Graphics.Sprites; using osu.Game.Database; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; @@ -15,6 +14,7 @@ using osu.Game.Screens.Play; using OpenTK.Graphics; using osu.Desktop.VisualTests.Beatmaps; using osu.Game.Rulesets.Osu.UI; +using osu.Framework.Graphics.Shapes; namespace osu.Desktop.VisualTests.Tests { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseReplaySettingsOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseReplaySettingsOverlay.cs new file mode 100644 index 0000000000..b2c211b7f0 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseReplaySettingsOverlay.cs @@ -0,0 +1,55 @@ +// 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.Testing; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Play; +using osu.Game.Screens.Play.ReplaySettings; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseReplaySettingsOverlay : TestCase + { + public override string Description => @"Settings visible in replay/auto"; + + private ExampleContainer container; + + public override void Reset() + { + base.Reset(); + + Add(new ReplaySettingsOverlay() + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }); + + Add(container = new ExampleContainer()); + + AddStep(@"Add button", () => container.Add(new OsuButton + { + RelativeSizeAxes = Axes.X, + Text = @"Button", + })); + + AddStep(@"Add checkbox", () => container.Add(new ReplayCheckbox + { + LabelText = "Checkbox", + })); + + AddStep(@"Add textbox", () => container.Add(new FocusedTextBox + { + RelativeSizeAxes = Axes.X, + Height = 30, + PlaceholderText = "Textbox", + HoldFocus = false, + })); + } + + private class ExampleContainer : ReplayGroup + { + protected override string Title => @"example"; + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs b/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs index f86fa4dab5..45ae82109f 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs @@ -79,7 +79,8 @@ namespace osu.Desktop.VisualTests.Tests { score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current > 0 ? comboCounter.Current - 1 : 0) / 25.0); comboCounter.Increment(); - numerator++; denominator++; + numerator++; + denominator++; accuracyCounter.SetFraction(numerator, denominator); }); diff --git a/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs new file mode 100644 index 0000000000..4ddc35eb84 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs @@ -0,0 +1,211 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Testing; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Timing; + +namespace osu.Desktop.VisualTests.Tests +{ + public class TestCaseScrollingHitObjects : TestCase + { + public override string Description => "SpeedAdjustmentContainer/DrawableTimingSection"; + + private SpeedAdjustmentCollection adjustmentCollection; + + private BindableDouble timeRangeBindable; + private OsuSpriteText timeRangeText; + private OsuSpriteText bottomLabel; + private SpriteText topTime, bottomTime; + + public override void Reset() + { + base.Reset(); + + timeRangeBindable = new BindableDouble(2000) + { + MinValue = 200, + MaxValue = 4000, + }; + + SliderBar timeRange; + Add(timeRange = new BasicSliderBar + { + Size = new Vector2(200, 20), + SelectionColor = Color4.Pink, + KeyboardStep = 100 + }); + + Add(timeRangeText = new OsuSpriteText + { + X = 210, + TextSize = 16, + }); + + timeRange.Current.BindTo(timeRangeBindable); + timeRangeBindable.ValueChanged += v => timeRangeText.Text = $"Visible Range: {v:#,#.#}"; + timeRangeBindable.ValueChanged += v => bottomLabel.Text = $"t minus {v:#,#}"; + + Add(new Drawable[] + { + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(100, 500), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.25f + }, + adjustmentCollection = new SpeedAdjustmentCollection(Axes.Y) + { + RelativeSizeAxes = Axes.Both, + VisibleTimeRange = timeRangeBindable, + Masking = true, + }, + new OsuSpriteText + { + Text = "t minus 0", + Margin = new MarginPadding(2), + TextSize = 14, + Anchor = Anchor.TopRight, + }, + bottomLabel = new OsuSpriteText + { + Text = "t minus x", + Margin = new MarginPadding(2), + TextSize = 14, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomLeft, + }, + topTime = new OsuSpriteText + { + Margin = new MarginPadding(2), + TextSize = 14, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopRight, + }, + bottomTime = new OsuSpriteText + { + Margin = new MarginPadding(2), + TextSize = 14, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomRight, + }, + } + } + }); + + timeRangeBindable.TriggerChange(); + + adjustmentCollection.Add(new TestSpeedAdjustmentContainer(new MultiplierControlPoint())); + + AddStep("Add hit object", () => adjustmentCollection.Add(new TestDrawableHitObject(new HitObject { StartTime = Time.Current + 2000 }))); + } + + protected override void Update() + { + base.Update(); + + topTime.Text = Time.Current.ToString("#,#"); + bottomTime.Text = (Time.Current + timeRangeBindable.Value).ToString("#,#"); + } + + private class TestSpeedAdjustmentContainer : SpeedAdjustmentContainer + { + public override bool RemoveWhenNotAlive => false; + + public TestSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) + : base(controlPoint) + { + } + + protected override DrawableTimingSection CreateTimingSection() => new TestDrawableTimingSection(ControlPoint); + + private class TestDrawableTimingSection : DrawableTimingSection + { + private readonly MultiplierControlPoint controlPoint; + + public TestDrawableTimingSection(MultiplierControlPoint controlPoint) + { + this.controlPoint = controlPoint; + } + + protected override void Update() + { + base.Update(); + + Y = (float)(controlPoint.StartTime - Time.Current); + } + } + } + + private class TestDrawableHitObject : DrawableHitObject, IScrollingHitObject + { + private readonly Box background; + private const float height = 14; + + public BindableDouble LifetimeOffset { get; } = new BindableDouble(); + + public TestDrawableHitObject(HitObject hitObject) + : base(hitObject) + { + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + RelativePositionAxes = Axes.Y; + + Y = (float)hitObject.StartTime; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.X, + Height = height, + }, + new Box + { + RelativeSizeAxes = Axes.X, + Colour = Color4.Cyan, + Height = 1, + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.Black, + TextSize = height, + Font = @"Exo2.0-BoldItalic", + Text = $"{hitObject.StartTime:#,#}" + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + FadeInFromZero(250, EasingTypes.OutQuint); + } + + protected override void Update() + { + base.Update(); + if (Time.Current >= HitObject.StartTime) + background.Colour = Color4.Red; + } + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSkipButton.cs b/osu.Desktop.VisualTests/Tests/TestCaseSkipButton.cs new file mode 100644 index 0000000000..fb5be719c1 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseSkipButton.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.Testing; +using osu.Game.Screens.Play; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseSkipButton : TestCase + { + public override string Description => @"Skip skip skippediskip"; + + public override void Reset() + { + base.Reset(); + Add(new SkipButton(Clock.CurrentTime + 5000)); + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSocial.cs b/osu.Desktop.VisualTests/Tests/TestCaseSocial.cs new file mode 100644 index 0000000000..eb7df96355 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseSocial.cs @@ -0,0 +1,85 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Testing; +using osu.Game.Overlays; +using osu.Game.Users; + +namespace osu.Desktop.VisualTests.Tests +{ + public class TestCaseSocial : TestCase + { + public override string Description => @"social browser overlay"; + + public override void Reset() + { + base.Reset(); + + SocialOverlay s = new SocialOverlay + { + Users = new[] + { + new User + { + Username = @"flyte", + Id = 3103765, + Country = new Country { FlagName = @"JP" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", + }, + new User + { + Username = @"Cookiezi", + Id = 124493, + Country = new Country { FlagName = @"KR" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg", + }, + new User + { + Username = @"Angelsim", + Id = 1777162, + Country = new Country { FlagName = @"KR" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }, + new User + { + Username = @"Rafis", + Id = 2558286, + Country = new Country { FlagName = @"PL" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg", + }, + new User + { + Username = @"hvick225", + Id = 50265, + Country = new Country { FlagName = @"TW" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c5.jpg", + }, + new User + { + Username = @"peppy", + Id = 2, + Country = new Country { FlagName = @"AU" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" + }, + new User + { + Username = @"filsdelama", + Id = 2831793, + Country = new Country { FlagName = @"FR" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c7.jpg" + }, + new User + { + Username = @"_index", + Id = 652457, + Country = new Country { FlagName = @"RU" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c8.jpg" + }, + }, + }; + Add(s); + + AddStep(@"toggle", s.ToggleVisibility); + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTooltip.cs b/osu.Desktop.VisualTests/Tests/TestCaseTooltip.cs deleted file mode 100644 index c536672314..0000000000 --- a/osu.Desktop.VisualTests/Tests/TestCaseTooltip.cs +++ /dev/null @@ -1,92 +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; -using osu.Framework.Testing; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Framework.Configuration; -using OpenTK; -using osu.Game.Graphics; - -namespace osu.Desktop.VisualTests.Tests -{ - internal class TestCaseTooltip : TestCase - { - public override string Description => "tests tooltips on various elements"; - - public override void Reset() - { - base.Reset(); - OsuSliderBar slider; - OsuSliderBar sliderDouble; - - const float width = 400; - - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), - Children = new Drawable[] - { - new TooltipTextContainer("text with a tooltip"), - new TooltipTextContainer("more text with another tooltip"), - new TooltipTextbox - { - Text = "a textbox with a tooltip", - Size = new Vector2(width,30), - }, - slider = new OsuSliderBar - { - Width = width, - }, - sliderDouble = new OsuSliderBar - { - Width = width, - }, - }, - }, - }; - - slider.Current.BindTo(new BindableInt(5) - { - MaxValue = 10, - MinValue = 0 - }); - - sliderDouble.Current.BindTo(new BindableDouble(0.5) - { - MaxValue = 1, - MinValue = 0 - }); - } - - private class TooltipTextContainer : Container, IHasTooltip - { - private readonly OsuSpriteText text; - - public string TooltipText => text.Text; - - public TooltipTextContainer(string tooltipText) - { - AutoSizeAxes = Axes.Both; - Children = new[] - { - text = new OsuSpriteText - { - Text = tooltipText, - } - }; - } - } - - private class TooltipTextbox : OsuTextBox, IHasTooltip - { - public string TooltipText => Text; - } - } -} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs index ba17cfc3d8..2decb4c469 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs @@ -3,20 +3,18 @@ using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Play; namespace osu.Desktop.VisualTests.Tests { internal class TestCaseTwoLayerButton : TestCase { - public override string Description => @"Back and skip and what not"; + public override string Description => @"Mostly back button"; public override void Reset() { base.Reset(); Add(new BackButton()); - Add(new SkipButton(Clock.CurrentTime + 5000)); } } } diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index 466c9336f0..b21a0d10b8 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -87,11 +87,13 @@ $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll + + $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll + True - - $(SolutionDir)\packages\SharpCompress.0.15.2\lib\net45\SharpCompress.dll + + $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll + True False @@ -187,6 +189,7 @@ + @@ -196,6 +199,7 @@ + @@ -203,12 +207,13 @@ + + - @@ -223,6 +228,7 @@ + diff --git a/osu.Desktop.VisualTests/packages.config b/osu.Desktop.VisualTests/packages.config index cad2ffff0d..2fb1023253 100644 --- a/osu.Desktop.VisualTests/packages.config +++ b/osu.Desktop.VisualTests/packages.config @@ -5,8 +5,8 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste --> - - + + diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 9532652bfe..b53c4ab3d4 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -10,6 +10,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using Squirrel; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 4f66dfd3eb..5ac888b515 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -121,18 +121,23 @@ - $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll + $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll True - - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll + + $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll + True + + + $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll + True $(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll True - - $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\Squirrel.dll + + $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll True diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config index 60e8182c82..3ad2106d2b 100644 --- a/osu.Desktop/packages.config +++ b/osu.Desktop/packages.config @@ -7,7 +7,8 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - + + - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 53449fd5f5..df212f7df7 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -28,7 +28,14 @@ namespace osu.Game.Rulesets.Catch { new CatchModEasy(), new CatchModNoFail(), - new CatchModHalfTime(), + new MultiMod + { + Mods = new Mod[] + { + new CatchModHalfTime(), + new CatchModDaycore(), + }, + }, }; case ModType.DifficultyIncrease: diff --git a/osu.Game.Rulesets.Catch/Mods/CatchMod.cs b/osu.Game.Rulesets.Catch/Mods/CatchMod.cs index 64a0c51b72..b0880d7e1d 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchMod.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchMod.cs @@ -32,6 +32,11 @@ namespace osu.Game.Rulesets.Catch.Mods } + public class CatchModDaycore : ModDaycore + { + public override double ScoreMultiplier => 0.5; + } + public class CatchModDoubleTime : ModDoubleTime { public override double ScoreMultiplier => 1.06; diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 4b1e6e93cd..8be52150fc 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.UI; using OpenTK; diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 281d2b5a79..83996df41a 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -33,8 +33,9 @@ false - - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll + + $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll + True diff --git a/osu.Game.Rulesets.Catch/packages.config b/osu.Game.Rulesets.Catch/packages.config index 634d0b51f6..fa6edb9c8f 100644 --- a/osu.Game.Rulesets.Catch/packages.config +++ b/osu.Game.Rulesets.Catch/packages.config @@ -5,5 +5,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd . Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE --> - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 2d1f75e196..7e9615a703 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -448,7 +448,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy return curveData.RepeatSamples[index]; } - /// /// Constructs and adds a note to a pattern. /// @@ -480,7 +479,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy Tail = { Samples = sampleInfoListAt(endTime) } }; - newObject = holdNote; } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 30d1846746..8be3870ebe 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -27,7 +27,14 @@ namespace osu.Game.Rulesets.Mania { new ManiaModEasy(), new ManiaModNoFail(), - new ManiaModHalfTime(), + new MultiMod + { + Mods = new Mod[] + { + new ManiaModHalfTime(), + new ManiaModDaycore(), + }, + }, }; case ModType.DifficultyIncrease: @@ -89,6 +96,7 @@ namespace osu.Game.Rulesets.Mania new ModCinema(), }, }, + new ManiaModGravity() }; default: diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs index ff3fd8e4b7..f31873d1c8 100644 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs @@ -87,6 +87,5 @@ namespace osu.Game.Rulesets.Mania.MathUtils bitIndex++; return ((bitBuffer >>= 1) & 1) == 1; } - } } diff --git a/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs b/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs new file mode 100644 index 0000000000..f179aa2ff8 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs @@ -0,0 +1,23 @@ +// 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.Mania.UI; +using osu.Game.Rulesets.Timing; + +namespace osu.Game.Rulesets.Mania.Mods +{ + /// + /// A type of mod which generates speed adjustments that scroll the hit objects and bar lines. + /// + internal interface IGenerateSpeedAdjustments + { + /// + /// Applies this mod to a hit renderer. + /// + /// The hit renderer to apply to. + /// The per-column list of speed adjustments for hit objects. + /// The list of speed adjustments for bar lines. + void ApplyToHitRenderer(ManiaHitRenderer hitRenderer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges); + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs index b402d3a010..f44ad6fd60 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs @@ -34,6 +34,11 @@ namespace osu.Game.Rulesets.Mania.Mods } + public class ManiaModDaycore : ModDaycore + { + public override double ScoreMultiplier => 0.3; + } + public class ManiaModDoubleTime : ModDoubleTime { public override double ScoreMultiplier => 1.0; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs new file mode 100644 index 0000000000..1ba8ac4710 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs @@ -0,0 +1,46 @@ +// 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.Mania.Objects; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Timing; +using osu.Game.Rulesets.Timing; +using osu.Game.Rulesets.Mania.Objects.Drawables; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModGravity : Mod, IGenerateSpeedAdjustments + { + public override string Name => "Gravity"; + + public override double ScoreMultiplier => 0; + + public override FontAwesome Icon => FontAwesome.fa_sort_desc; + + public void ApplyToHitRenderer(ManiaHitRenderer hitRenderer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges) + { + // We have to generate one speed adjustment per hit object for gravity + foreach (ManiaHitObject obj in hitRenderer.Objects) + { + MultiplierControlPoint controlPoint = hitRenderer.CreateControlPointAt(obj.StartTime); + // Beat length has too large of an effect for gravity, so we'll force it to a constant value for now + controlPoint.TimingPoint.BeatLength = 1000; + + hitObjectTimingChanges[obj.Column].Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity)); + } + + // Like with hit objects, we need to generate one speed adjustment per bar line + foreach (DrawableBarLine barLine in hitRenderer.BarLines) + { + var controlPoint = hitRenderer.CreateControlPointAt(barLine.HitObject.StartTime); + // Beat length has too large of an effect for gravity, so we'll force it to a constant value for now + controlPoint.TimingPoint.BeatLength = 1000; + + barlineTimingChanges.Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity)); + } + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs index 0b4d8b2d4e..fc5ea4e116 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs @@ -3,7 +3,7 @@ using OpenTK; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Objects.Drawables diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 5d7f3314cd..e52fb1362f 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -55,7 +55,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables tickContainer = new Container { RelativeSizeAxes = Axes.Both, - RelativeCoordinateSpace = new Vector2(1, (float)HitObject.Duration) + RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime), + RelativeChildSize = new Vector2(1, (float)HitObject.Duration) }, head = new DrawableHeadNote(this, key) { @@ -76,9 +77,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables HoldStartTime = () => holdStartTime }; - // To make the ticks relative to ourselves we need to offset them backwards - drawableTick.Y -= (float)HitObject.StartTime; - tickContainer.Add(drawableTick); AddNested(drawableTick); } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index 9ecc77d3fc..39abbb6b3d 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -7,9 +7,9 @@ using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Objects.Drawables; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Mania.Objects.Drawables { @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { base.AccentColour = value; - glowContainer.EdgeEffect = new EdgeEffect + glowContainer.EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Radius = 2f, diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 4e276fddb7..cb1352fc4a 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Objects.Drawables { - public abstract class DrawableManiaHitObject : DrawableHitObject + public abstract class DrawableManiaHitObject : DrawableScrollingHitObject where TObject : ManiaHitObject { /// diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 658d409bb8..9322fed3eb 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public DrawableNote(Note hitObject, Bindable key = null) : base(hitObject, key) { - RelativeSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.X; Height = 100; Add(headPiece = new NotePiece diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs index c10aa9994b..04e8df4ae2 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs @@ -4,7 +4,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs index e01199e929..3df085c346 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs @@ -5,7 +5,7 @@ using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces diff --git a/osu.Game.Rulesets.Mania/Timing/BasicScrollingDrawableTimingSection.cs b/osu.Game.Rulesets.Mania/Timing/BasicScrollingDrawableTimingSection.cs new file mode 100644 index 0000000000..e485581d9f --- /dev/null +++ b/osu.Game.Rulesets.Mania/Timing/BasicScrollingDrawableTimingSection.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Timing; + +namespace osu.Game.Rulesets.Mania.Timing +{ + /// + /// A which scrolls relative to the control point start time. + /// + internal class BasicScrollingDrawableTimingSection : DrawableTimingSection + { + private readonly MultiplierControlPoint controlPoint; + + public BasicScrollingDrawableTimingSection(MultiplierControlPoint controlPoint) + { + this.controlPoint = controlPoint; + } + + protected override void Update() + { + base.Update(); + + Y = (float)(controlPoint.StartTime - Time.Current); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs b/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs deleted file mode 100644 index 0a8bc2d44a..0000000000 --- a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs +++ /dev/null @@ -1,156 +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.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Mania.Timing -{ - /// - /// A container in which added drawables are put into a relative coordinate space spanned by a length of time. - /// - /// This container contains s which scroll inside this container. - /// Drawables added to this container are moved inside the relevant , - /// and as such, will scroll along with the s. - /// - /// - public class ControlPointContainer : Container - { - /// - /// The amount of time which this container spans. - /// - public double TimeSpan { get; set; } - - private readonly List drawableControlPoints; - - public ControlPointContainer(IEnumerable timingChanges) - { - drawableControlPoints = timingChanges.Select(t => new DrawableControlPoint(t)).ToList(); - Children = drawableControlPoints; - } - - /// - /// Adds a drawable to this container. Note that the drawable added must have its Y-position be - /// an absolute unit of time that is _not_ relative to . - /// - /// The drawable to add. - public override void Add(Drawable drawable) - { - // Always add timing sections to ourselves - if (drawable is DrawableControlPoint) - { - base.Add(drawable); - return; - } - - var controlPoint = drawableControlPoints.LastOrDefault(t => t.CanContain(drawable)) ?? drawableControlPoints.FirstOrDefault(); - - if (controlPoint == null) - throw new InvalidOperationException("Could not find suitable timing section to add object to."); - - controlPoint.Add(drawable); - } - - /// - /// A container that contains drawables within the time span of a timing section. - /// - /// The content of this container will scroll relative to the current time. - /// - /// - private class DrawableControlPoint : Container - { - private readonly TimingChange timingChange; - - protected override Container Content => content; - private readonly Container content; - - /// - /// Creates a drawable control point. The height of this container will be proportional - /// to the beat length of the control point it is initialized with such that, e.g. a beat length - /// of 500ms results in this container being twice as high as its parent, which further means that - /// the content container will scroll at twice the normal rate. - /// - /// The control point to create the drawable control point for. - public DrawableControlPoint(TimingChange timingChange) - { - this.timingChange = timingChange; - - RelativeSizeAxes = Axes.Both; - - AddInternal(content = new AutoTimeRelativeContainer - { - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Both, - Y = (float)timingChange.Time - }); - } - - protected override void Update() - { - var parent = (ControlPointContainer)Parent; - - // Adjust our height to account for the speed changes - Height = (float)(1000 / timingChange.BeatLength / timingChange.SpeedMultiplier); - RelativeCoordinateSpace = new Vector2(1, (float)parent.TimeSpan); - - // Scroll the content - content.Y = (float)(timingChange.Time - Time.Current); - } - - public override void Add(Drawable drawable) - { - // The previously relatively-positioned drawable will now become relative to content, but since the drawable has no knowledge of content, - // we need to offset it back by content's position position so that it becomes correctly relatively-positioned to content - // This can be removed if hit objects were stored such that either their StartTime or their "beat offset" was relative to the timing change - // they belonged to, but this requires a radical change to the beatmap format which we're not ready to do just yet - drawable.Y -= (float)timingChange.Time; - - base.Add(drawable); - } - - /// - /// Whether this control point can contain a drawable. This control point can contain a drawable if the drawable is positioned "after" this control point. - /// - /// The drawable to check. - public bool CanContain(Drawable drawable) => content.Y <= drawable.Y; - - /// - /// A container which always keeps its height and relative coordinate space "auto-sized" to its children. - /// - /// This is used in the case where children are relatively positioned/sized to time values (e.g. notes/bar lines) to keep - /// such children wrapped inside a container, otherwise they would disappear due to container flattening. - /// - /// - private class AutoTimeRelativeContainer : Container - { - protected override IComparer DepthComparer => new HitObjectReverseStartTimeComparer(); - - public override void InvalidateFromChild(Invalidation invalidation) - { - // We only want to re-compute our size when a child's size or position has changed - if ((invalidation & Invalidation.Geometry) == 0) - { - base.InvalidateFromChild(invalidation); - return; - } - - if (!Children.Any()) - return; - - float height = Children.Select(child => child.Y + child.Height).Max(); - - Height = height; - RelativeCoordinateSpace = new Vector2(1, height); - - base.InvalidateFromChild(invalidation); - } - } - } - } -} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Timing/GravityScrollingDrawableTimingSection.cs b/osu.Game.Rulesets.Mania/Timing/GravityScrollingDrawableTimingSection.cs new file mode 100644 index 0000000000..730daa9ffd --- /dev/null +++ b/osu.Game.Rulesets.Mania/Timing/GravityScrollingDrawableTimingSection.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.Game.Rulesets.Timing; + +namespace osu.Game.Rulesets.Mania.Timing +{ + /// + /// A that emulates a form of gravity where hit objects speed up over time. + /// + internal class GravityScrollingDrawableTimingSection : DrawableTimingSection + { + private readonly MultiplierControlPoint controlPoint; + + public GravityScrollingDrawableTimingSection(MultiplierControlPoint controlPoint) + { + this.controlPoint = controlPoint; + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + // The gravity-adjusted start position + float startPos = (float)computeGravityTime(controlPoint.StartTime); + // The gravity-adjusted end position + float endPos = (float)computeGravityTime(controlPoint.StartTime + RelativeChildSize.Y); + + Y = startPos; + Height = endPos - startPos; + } + + /// + /// Applies gravity to a time value based on the current time. + /// + /// The time value gravity should be applied to. + /// The time after gravity is applied to . + private double computeGravityTime(double time) + { + double relativeTime = relativeTimeAt(time); + + // The sign of the relative time, this is used to apply backwards acceleration leading into startTime + double sign = relativeTime < 0 ? -1 : 1; + + return VisibleTimeRange - acceleration * relativeTime * relativeTime * sign; + } + + /// + /// The acceleration due to "gravity" of the content of this container. + /// + private double acceleration => 1 / VisibleTimeRange; + + /// + /// Computes the current time relative to , accounting for . + /// + /// The non-offset time. + /// The current time relative to - . + private double relativeTimeAt(double time) => Time.Current - time + VisibleTimeRange; + } +} diff --git a/osu.Game.Rulesets.Mania/Timing/ManiaSpeedAdjustmentContainer.cs b/osu.Game.Rulesets.Mania/Timing/ManiaSpeedAdjustmentContainer.cs new file mode 100644 index 0000000000..ed22264d74 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Timing/ManiaSpeedAdjustmentContainer.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Timing; + +namespace osu.Game.Rulesets.Mania.Timing +{ + public class ManiaSpeedAdjustmentContainer : SpeedAdjustmentContainer + { + private readonly ScrollingAlgorithm scrollingAlgorithm; + + public ManiaSpeedAdjustmentContainer(MultiplierControlPoint timingSection, ScrollingAlgorithm scrollingAlgorithm) + : base(timingSection) + { + this.scrollingAlgorithm = scrollingAlgorithm; + } + + protected override DrawableTimingSection CreateTimingSection() + { + switch (scrollingAlgorithm) + { + default: + case ScrollingAlgorithm.Basic: + return new BasicScrollingDrawableTimingSection(ControlPoint); + case ScrollingAlgorithm.Gravity: + return new GravityScrollingDrawableTimingSection(ControlPoint); + } + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Timing/ScrollingAlgorithm.cs b/osu.Game.Rulesets.Mania/Timing/ScrollingAlgorithm.cs new file mode 100644 index 0000000000..72e096f5aa --- /dev/null +++ b/osu.Game.Rulesets.Mania/Timing/ScrollingAlgorithm.cs @@ -0,0 +1,17 @@ +// 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.Timing +{ + public enum ScrollingAlgorithm + { + /// + /// Basic scrolling algorithm based on the timing section time. This is the default algorithm. + /// + Basic, + /// + /// Emulating a form of gravity where hit objects speed up over time. + /// + Gravity + } +} diff --git a/osu.Game.Rulesets.Mania/Timing/TimingChange.cs b/osu.Game.Rulesets.Mania/Timing/TimingChange.cs deleted file mode 100644 index 9153ba6991..0000000000 --- a/osu.Game.Rulesets.Mania/Timing/TimingChange.cs +++ /dev/null @@ -1,23 +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.Timing -{ - public class TimingChange - { - /// - /// The time at which this timing change happened. - /// - public double Time; - - /// - /// The beat length. - /// - public double BeatLength = 500; - - /// - /// The speed multiplier. - /// - public double SpeedMultiplier = 1; - } -} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 6dfd5000d4..12b6f61a12 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -7,17 +7,14 @@ using OpenTK.Input; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Colour; using osu.Framework.Input; using osu.Game.Graphics; -using osu.Game.Rulesets.Mania.Timing; -using System.Collections.Generic; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Judgements; using System; using osu.Framework.Configuration; +using osu.Game.Rulesets.Timing; namespace osu.Game.Rulesets.Mania.UI { @@ -33,6 +30,13 @@ namespace osu.Game.Rulesets.Mania.UI private const float column_width = 45; private const float special_column_width = 70; + private readonly BindableDouble visibleTimeRange = new BindableDouble(); + public BindableDouble VisibleTimeRange + { + get { return visibleTimeRange; } + set { visibleTimeRange.BindTo(value); } + } + /// /// The key that will trigger input actions for this column and hit objects contained inside it. /// @@ -42,9 +46,9 @@ namespace osu.Game.Rulesets.Mania.UI private readonly Container hitTargetBar; private readonly Container keyIcon; - public readonly ControlPointContainer ControlPointContainer; + private readonly SpeedAdjustmentCollection speedAdjustments; - public Column(IEnumerable timingChanges) + public Column() { RelativeSizeAxes = Axes.Y; Width = column_width; @@ -61,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.UI { Name = "Hit target + hit objects", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION}, + Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION }, Children = new Drawable[] { new Container @@ -93,10 +97,11 @@ namespace osu.Game.Rulesets.Mania.UI } } }, - ControlPointContainer = new ControlPointContainer(timingChanges) + speedAdjustments = new SpeedAdjustmentCollection(Axes.Y) { Name = "Hit objects", RelativeSizeAxes = Axes.Both, + VisibleTimeRange = VisibleTimeRange }, // For column lighting, we need to capture input events before the notes new InputTarget @@ -171,14 +176,14 @@ namespace osu.Game.Rulesets.Mania.UI background.Colour = accentColour; - hitTargetBar.EdgeEffect = new EdgeEffect + hitTargetBar.EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Radius = 5, Colour = accentColour.Opacity(0.5f), }; - keyIcon.EdgeEffect = new EdgeEffect + keyIcon.EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Radius = 5, @@ -187,10 +192,11 @@ namespace osu.Game.Rulesets.Mania.UI } } - public void Add(DrawableHitObject hitObject) + public void Add(SpeedAdjustmentContainer speedAdjustment) => speedAdjustments.Add(speedAdjustment); + public void Add(DrawableHitObject hitObject) { hitObject.AccentColour = AccentColour; - ControlPointContainer.Add(hitObject); + speedAdjustments.Add(hitObject); } private bool onKeyDown(InputState state, KeyDownEventArgs args) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index 57477147d5..dcb2a29556 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -8,6 +8,7 @@ using OpenTK; using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Lists; using osu.Framework.MathUtils; @@ -16,6 +17,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Judgements; +using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Scoring; @@ -23,78 +25,44 @@ using osu.Game.Rulesets.Mania.Timing; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Timing; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.UI { - public class ManiaHitRenderer : HitRenderer + public class ManiaHitRenderer : SpeedAdjustedHitRenderer { - public int? Columns; + /// + /// Preferred column count. This will only have an effect during the initialization of the play field. + /// + public int PreferredColumns; + + public IEnumerable BarLines; + + /// + /// Per-column timing changes. + /// + private readonly List[] hitObjectSpeedAdjustments; + + /// + /// Bar line timing changes. + /// + private readonly List barLineSpeedAdjustments = new List(); public ManiaHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(beatmap, isForCurrentRuleset) { - } - - protected override Playfield CreatePlayfield() - { - double lastSpeedMultiplier = 1; - double lastBeatLength = 500; - - // Merge timing + difficulty points - var allPoints = new SortedList(Comparer.Default); - allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints); - allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints); - - // Generate the timing points, making non-timing changes use the previous timing change - var timingChanges = allPoints.Select(c => - { - var timingPoint = c as TimingControlPoint; - var difficultyPoint = c as DifficultyControlPoint; - - if (timingPoint != null) - lastBeatLength = timingPoint.BeatLength; - - if (difficultyPoint != null) - lastSpeedMultiplier = difficultyPoint.SpeedMultiplier; - - return new TimingChange - { - Time = c.Time, - BeatLength = lastBeatLength, - SpeedMultiplier = lastSpeedMultiplier - }; - }); - - double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; - - // Perform some post processing of the timing changes - timingChanges = timingChanges - // Collapse sections after the last hit object - .Where(s => s.Time <= lastObjectTime) - // Collapse sections with the same start time - .GroupBy(s => s.Time).Select(g => g.Last()).OrderBy(s => s.Time) - // Collapse sections with the same beat length - .GroupBy(s => s.BeatLength * s.SpeedMultiplier).Select(g => g.First()) - .ToList(); - - return new ManiaPlayfield(Columns ?? (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize), timingChanges) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - // Invert by default for now (should be moved to config/skin later) - Scale = new Vector2(1, -1) - }; - } - - [BackgroundDependencyLoader] - private void load() - { - var maniaPlayfield = (ManiaPlayfield)Playfield; + // Generate the speed adjustment container lists + hitObjectSpeedAdjustments = new List[PreferredColumns]; + for (int i = 0; i < PreferredColumns; i++) + hitObjectSpeedAdjustments[i] = new List(); + // Generate the bar lines double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; SortedList timingPoints = Beatmap.ControlPointInfo.TimingPoints; + var barLines = new List(); + for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint point = timingPoints[i]; @@ -105,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.UI int index = 0; for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) { - maniaPlayfield.Add(new DrawableBarLine(new BarLine + barLines.Add(new DrawableBarLine(new BarLine { StartTime = t, ControlPoint = point, @@ -113,17 +81,78 @@ namespace osu.Game.Rulesets.Mania.UI })); } } + + BarLines = barLines; + + // Generate speed adjustments from mods first + bool useDefaultSpeedAdjustments = true; + + if (Mods != null) + { + foreach (var speedAdjustmentMod in Mods.OfType()) + { + useDefaultSpeedAdjustments = false; + speedAdjustmentMod.ApplyToHitRenderer(this, ref hitObjectSpeedAdjustments, ref barLineSpeedAdjustments); + } + } + + // Generate the default speed adjustments + if (useDefaultSpeedAdjustments) + generateDefaultSpeedAdjustments(); } + [BackgroundDependencyLoader] + private void load() + { + var maniaPlayfield = (ManiaPlayfield)Playfield; + + BarLines.ForEach(maniaPlayfield.Add); + } + + protected override void ApplyBeatmap() + { + base.ApplyBeatmap(); + + PreferredColumns = (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize); + } + + protected override void ApplySpeedAdjustments() + { + var maniaPlayfield = (ManiaPlayfield)Playfield; + + for (int i = 0; i < PreferredColumns; i++) + foreach (var change in hitObjectSpeedAdjustments[i]) + maniaPlayfield.Columns.ElementAt(i).Add(change); + + foreach (var change in barLineSpeedAdjustments) + maniaPlayfield.Add(change); + } + + private void generateDefaultSpeedAdjustments() + { + DefaultControlPoints.ForEach(c => + { + foreach (List t in hitObjectSpeedAdjustments) + t.Add(new ManiaSpeedAdjustmentContainer(c, ScrollingAlgorithm.Basic)); + barLineSpeedAdjustments.Add(new ManiaSpeedAdjustmentContainer(c, ScrollingAlgorithm.Basic)); + }); + } + + protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(PreferredColumns) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + // Invert by default for now (should be moved to config/skin later) + Scale = new Vector2(1, -1) + }; + public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(); protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h) { - var maniaPlayfield = Playfield as ManiaPlayfield; - if (maniaPlayfield == null) - return null; + var maniaPlayfield = (ManiaPlayfield)Playfield; Bindable key = maniaPlayfield.Columns.ElementAt(h.Column).Key; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 2e6b63579e..a0c683143c 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.UI; using OpenTK; @@ -15,13 +14,14 @@ using osu.Framework.Allocation; using OpenTK.Input; using System.Linq; using System.Collections.Generic; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Mania.Timing; using osu.Framework.Input; using osu.Framework.Graphics.Transforms; using osu.Framework.MathUtils; using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Timing; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Mania.UI { @@ -29,10 +29,10 @@ namespace osu.Game.Rulesets.Mania.UI { public const float HIT_TARGET_POSITION = 50; - private const float time_span_default = 5000; - private const float time_span_min = 10; - private const float time_span_max = 50000; - private const float time_span_step = 200; + private const double time_span_default = 1500; + private const double time_span_min = 50; + private const double time_span_max = 10000; + private const double time_span_step = 50; /// /// Default column keys, expanding outwards from the middle as more column are added. @@ -58,14 +58,20 @@ namespace osu.Game.Rulesets.Mania.UI private readonly FlowContainer columns; public IEnumerable Columns => columns.Children; - private readonly ControlPointContainer barLineContainer; + private readonly BindableDouble visibleTimeRange = new BindableDouble(time_span_default) + { + MinValue = time_span_min, + MaxValue = time_span_max + }; + + private readonly SpeedAdjustmentCollection barLineContainer; private List normalColumnColours = new List(); private Color4 specialColumnColour; private readonly int columnCount; - public ManiaPlayfield(int columnCount, IEnumerable timingChanges) + public ManiaPlayfield(int columnCount) { this.columnCount = columnCount; @@ -116,12 +122,13 @@ namespace osu.Game.Rulesets.Mania.UI Padding = new MarginPadding { Top = HIT_TARGET_POSITION }, Children = new[] { - barLineContainer = new ControlPointContainer(timingChanges) + barLineContainer = new SpeedAdjustmentCollection(Axes.Y) { Name = "Bar lines", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Y + RelativeSizeAxes = Axes.Y, + VisibleTimeRange = visibleTimeRange // Width is set in the Update method } } @@ -131,9 +138,7 @@ namespace osu.Game.Rulesets.Mania.UI }; for (int i = 0; i < columnCount; i++) - columns.Add(new Column(timingChanges)); - - TimeSpan = time_span_default; + columns.Add(new Column { VisibleTimeRange = visibleTimeRange }); } [BackgroundDependencyLoader] @@ -208,6 +213,7 @@ namespace osu.Game.Rulesets.Mania.UI public override void Add(DrawableHitObject h) => Columns.ElementAt(h.HitObject.Column).Add(h); public void Add(DrawableBarLine barline) => barLineContainer.Add(barline); + public void Add(SpeedAdjustmentContainer speedAdjustment) => barLineContainer.Add(speedAdjustment); protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { @@ -216,10 +222,10 @@ namespace osu.Game.Rulesets.Mania.UI switch (args.Key) { case Key.Minus: - transformTimeSpanTo(TimeSpan + time_span_step, 200, EasingTypes.OutQuint); + transformVisibleTimeRangeTo(visibleTimeRange + time_span_step, 200, EasingTypes.OutQuint); break; case Key.Plus: - transformTimeSpanTo(TimeSpan - time_span_step, 200, EasingTypes.OutQuint); + transformVisibleTimeRangeTo(visibleTimeRange - time_span_step, 200, EasingTypes.OutQuint); break; } } @@ -227,29 +233,9 @@ namespace osu.Game.Rulesets.Mania.UI return false; } - private double timeSpan; - /// - /// The amount of time which the length of the playfield spans. - /// - public double TimeSpan + private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, EasingTypes easing = EasingTypes.None) { - get { return timeSpan; } - set - { - if (timeSpan == value) - return; - timeSpan = value; - - timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max); - - barLineContainer.TimeSpan = value; - Columns.ForEach(c => c.ControlPointContainer.TimeSpan = value); - } - } - - private void transformTimeSpanTo(double newTimeSpan, double duration = 0, EasingTypes easing = EasingTypes.None) - { - TransformTo(() => TimeSpan, newTimeSpan, duration, easing, new TransformTimeSpan()); + TransformTo(() => visibleTimeRange.Value, newTimeRange, duration, easing, new TransformTimeSpan()); } protected override void Update() @@ -259,7 +245,7 @@ namespace osu.Game.Rulesets.Mania.UI barLineContainer.Width = columns.Width; } - private class TransformTimeSpan : Transform + private class TransformTimeSpan : Transform { public override double CurrentValue { @@ -278,7 +264,7 @@ namespace osu.Game.Rulesets.Mania.UI base.Apply(d); var p = (ManiaPlayfield)d; - p.TimeSpan = CurrentValue; + p.visibleTimeRange.Value = (float)CurrentValue; } } } diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 3d5614bd90..88d1ad7ad8 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -33,8 +33,9 @@ false - - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll + + $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll + True @@ -62,6 +63,7 @@ + @@ -77,14 +79,17 @@ + + + + - - + diff --git a/osu.Game.Rulesets.Mania/packages.config b/osu.Game.Rulesets.Mania/packages.config index 634d0b51f6..8add43d5d5 100644 --- a/osu.Game.Rulesets.Mania/packages.config +++ b/osu.Game.Rulesets.Mania/packages.config @@ -1,9 +1,8 @@  - - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs index cc06946d38..3b0cfc1ef1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs @@ -39,6 +39,11 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); } + public class OsuModDaycore : ModDaycore + { + public override double ScoreMultiplier => 0.5; + } + public class OsuModDoubleTime : ModDoubleTime { public override double ScoreMultiplier => 1.12; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs index 9f8ff17853..30e6172802 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs @@ -6,7 +6,7 @@ using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections { @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections Masking = true; AutoSizeAxes = Axes.Both; CornerRadius = width / 2; - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Colour = Color4.White.Opacity(0.2f), diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 6b4d40e080..28fbf46a92 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -3,11 +3,11 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Osu.Objects.Drawables { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs index 68ffb756d4..97d7de35cf 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs @@ -3,8 +3,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using OpenTK; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs index 07b21657a5..28e54c3b4e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -31,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { Masking = true, Origin = Anchor.Centre, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Radius = 60, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs index a04d3e7a0a..f66099e1c3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs @@ -3,9 +3,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 4cffc1def3..4857f5c0d2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -3,7 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using OpenTK.Graphics; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs index 66cf7758b9..bdd5b71211 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs @@ -4,7 +4,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { Disc.Colour = value; - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Hollow = true, Type = EdgeEffectType.Glow, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs index 4dbb6bd4d6..fc0d436788 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs @@ -5,9 +5,9 @@ using System; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { Colour = Color4.Black, Alpha = 0.4f, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Radius = 10, diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObjectDifficulty.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObjectDifficulty.cs deleted file mode 100644 index 1786771dca..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObjectDifficulty.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using System; -using System.Diagnostics; -using System.Linq; - -namespace osu.Game.Rulesets.Osu.Objects -{ - internal class OsuHitObjectDifficulty - { - /// - /// Factor by how much speed / aim strain decays per second. - /// - /// - /// These values are results of tweaking a lot and taking into account general feedback. - /// Opinionated observation: Speed is easier to maintain than accurate jumps. - /// - internal static readonly double[] DECAY_BASE = { 0.3, 0.15 }; - - /// - /// Pseudo threshold values to distinguish between "singles" and "streams" - /// - /// - /// Of course the border can not be defined clearly, therefore the algorithm has a smooth transition between those values. - /// They also are based on tweaking and general feedback. - /// - private const double stream_spacing_threshold = 110, - single_spacing_threshold = 125; - - /// - /// Scaling values for weightings to keep aim and speed difficulty in balance. - /// - /// - /// Found from testing a very large map pool (containing all ranked maps) and keeping the average values the same. - /// - private static readonly double[] spacing_weight_scaling = { 1400, 26.25 }; - - /// - /// Almost the normed diameter of a circle (104 osu pixel). That is -after- position transforming. - /// - private const double almost_diameter = 90; - - internal OsuHitObject BaseHitObject; - internal double[] Strains = { 1, 1 }; - - internal int MaxCombo = 1; - - private readonly float scalingFactor; - private float lazySliderLength; - - private readonly Vector2 startPosition; - private readonly Vector2 endPosition; - - internal OsuHitObjectDifficulty(OsuHitObject baseHitObject) - { - BaseHitObject = baseHitObject; - float circleRadius = baseHitObject.Scale * 64; - - Slider slider = BaseHitObject as Slider; - if (slider != null) - MaxCombo += slider.Ticks.Count(); - - // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. - scalingFactor = 52.0f / circleRadius; - if (circleRadius < 30) - { - float smallCircleBonus = Math.Min(30.0f - circleRadius, 5.0f) / 50.0f; - scalingFactor *= 1.0f + smallCircleBonus; - } - - lazySliderLength = 0; - startPosition = baseHitObject.StackedPosition; - - // Calculate approximation of lazy movement on the slider - if (slider != null) - { - float sliderFollowCircleRadius = circleRadius * 3; // Not sure if this is correct, but here we do not need 100% exact values. This comes pretty darn close in my tests. - - // For simplifying this step we use actual osu! coordinates and simply scale the length, that we obtain by the ScalingFactor later - Vector2 cursorPos = startPosition; - - Action addSliderVertex = delegate (Vector2 pos) - { - Vector2 difference = pos - cursorPos; - float distance = difference.Length; - - // Did we move away too far? - if (distance > sliderFollowCircleRadius) - { - // Yep, we need to move the cursor - difference.Normalize(); // Obtain the direction of difference. We do no longer need the actual difference - distance -= sliderFollowCircleRadius; - cursorPos += difference * distance; // We move the cursor just as far as needed to stay in the follow circle - lazySliderLength += distance; - } - }; - - // Actual computation of the first lazy curve - foreach (var tick in slider.Ticks) - addSliderVertex(tick.StackedPosition); - - addSliderVertex(baseHitObject.StackedEndPosition); - - lazySliderLength *= scalingFactor; - endPosition = cursorPos; - } - // We have a normal HitCircle or a spinner - else - endPosition = startPosition; - } - - internal void CalculateStrains(OsuHitObjectDifficulty previousHitObject, double timeRate) - { - calculateSpecificStrain(previousHitObject, OsuDifficultyCalculator.DifficultyType.Speed, timeRate); - calculateSpecificStrain(previousHitObject, OsuDifficultyCalculator.DifficultyType.Aim, timeRate); - } - - // Caution: The subjective values are strong with this one - private static double spacingWeight(double distance, OsuDifficultyCalculator.DifficultyType type) - { - switch (type) - { - case OsuDifficultyCalculator.DifficultyType.Speed: - if (distance > single_spacing_threshold) - return 2.5; - else if (distance > stream_spacing_threshold) - return 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold); - else if (distance > almost_diameter) - return 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter); - else if (distance > almost_diameter / 2) - return 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2); - else - return 0.95; - - case OsuDifficultyCalculator.DifficultyType.Aim: - return Math.Pow(distance, 0.99); - } - - Debug.Assert(false, "Invalid osu difficulty hit object type."); - return 0; - } - - private void calculateSpecificStrain(OsuHitObjectDifficulty previousHitObject, OsuDifficultyCalculator.DifficultyType type, double timeRate) - { - double addition = 0; - double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; - double decay = Math.Pow(DECAY_BASE[(int)type], timeElapsed / 1000); - - if (BaseHitObject is Spinner) - { - // Do nothing for spinners - } - else if (BaseHitObject is Slider) - { - switch (type) - { - case OsuDifficultyCalculator.DifficultyType.Speed: - - // For speed strain we treat the whole slider as a single spacing entity, since "Speed" is about how hard it is to click buttons fast. - // The spacing weight exists to differentiate between being able to easily alternate or having to single. - addition = - spacingWeight(previousHitObject.lazySliderLength + - DistanceTo(previousHitObject), type) * - spacing_weight_scaling[(int)type]; - - break; - case OsuDifficultyCalculator.DifficultyType.Aim: - - // For Aim strain we treat each slider segment and the jump after the end of the slider as separate jumps, since movement-wise there is no difference - // to multiple jumps. - addition = - ( - spacingWeight(previousHitObject.lazySliderLength, type) + - spacingWeight(DistanceTo(previousHitObject), type) - ) * - spacing_weight_scaling[(int)type]; - - break; - } - } - else if (BaseHitObject is HitCircle) - { - addition = spacingWeight(DistanceTo(previousHitObject), type) * spacing_weight_scaling[(int)type]; - } - - // Scale addition by the time, that elapsed. Filter out HitObjects that are too close to be played anyway to avoid crazy values by division through close to zero. - // You will never find maps that require this amongst ranked maps. - addition /= Math.Max(timeElapsed, 50); - - Strains[(int)type] = previousHitObject.Strains[(int)type] * decay + addition; - } - - internal double DistanceTo(OsuHitObjectDifficulty other) - { - // Scale the distance by circle size. - return (startPosition - other.endPosition).Length * scalingFactor; - } - } -} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs new file mode 100644 index 0000000000..a164566263 --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs @@ -0,0 +1,73 @@ +// 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.Beatmaps; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; +using osu.Game.Rulesets.Osu.OsuDifficulty.Skills; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty +{ + public class OsuDifficultyCalculator : DifficultyCalculator + { + private const int section_length = 400; + private const double difficulty_multiplier = 0.0675; + + public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap) + { + } + + protected override void PreprocessHitObjects() + { + foreach (OsuHitObject h in Objects) + (h as Slider)?.Curve?.Calculate(); + } + + protected override double CalculateInternal(Dictionary categoryDifficulty) + { + OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Objects); + Skill[] skills = + { + new Aim(), + new Speed() + }; + + double sectionEnd = section_length / TimeRate; + foreach (OsuDifficultyHitObject h in beatmap) + { + while (h.BaseObject.StartTime > sectionEnd) + { + foreach (Skill s in skills) + { + s.SaveCurrentPeak(); + s.StartNewSectionFrom(sectionEnd); + } + + sectionEnd += section_length; + } + + foreach (Skill s in skills) + s.Process(h); + } + + double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; + double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; + + double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; + + if (categoryDifficulty != null) + { + categoryDifficulty.Add("Aim", aimRating.ToString("0.00")); + categoryDifficulty.Add("Speed", speedRating.ToString("0.00")); + } + + return starRating; + } + + protected override BeatmapConverter CreateBeatmapConverter() => new OsuBeatmapConverter(); + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs new file mode 100644 index 0000000000..72ba421344 --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs @@ -0,0 +1,93 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections; +using System.Collections.Generic; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing +{ + /// + /// An enumerable container wrapping input as + /// which contains extra data required for difficulty calculation. + /// + public class OsuDifficultyBeatmap : IEnumerable + { + private readonly IEnumerator difficultyObjects; + private readonly Queue onScreen = new Queue(); + + /// + /// Creates an enumerator, which preprocesses a list of s recieved as input, wrapping them as + /// which contains extra data required for difficulty calculation. + /// + public OsuDifficultyBeatmap(List objects) + { + // Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases. + // This should probably happen before the objects reach the difficulty calculator. + objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime)); + difficultyObjects = createDifficultyObjectEnumerator(objects); + } + + /// + /// Returns an enumerator that enumerates all s in the . + /// The inner loop adds objects that appear on screen into a queue until we need to hit the next object. + /// The outer loop returns objects from this queue one at a time, only after they had to be hit, and should no longer be on screen. + /// This means that we can loop through every object that is on screen at the time when a new one appears, + /// allowing us to determine a reading strain for the object that just appeared. + /// + public IEnumerator GetEnumerator() + { + while (true) + { + // Add upcoming objects to the queue until we have at least one object that had been hit and can be dequeued. + // This means there is always at least one object in the queue unless we reached the end of the map. + do + { + if (!difficultyObjects.MoveNext()) + break; // New objects can't be added anymore, but we still need to dequeue and return the ones already on screen. + + OsuDifficultyHitObject latest = difficultyObjects.Current; + // Calculate flow values here + + foreach (OsuDifficultyHitObject h in onScreen) + { + h.TimeUntilHit -= latest.DeltaTime; + // Calculate reading strain here + } + + onScreen.Enqueue(latest); + } + while (onScreen.Peek().TimeUntilHit > 0); // Keep adding new objects on screen while there is still time before we have to hit the next one. + + if (onScreen.Count == 0) break; // We have reached the end of the map and enumerated all the objects. + yield return onScreen.Dequeue(); // Remove and return objects one by one that had to be hit before the latest one appeared. + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private IEnumerator createDifficultyObjectEnumerator(List objects) + { + // We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object. + OsuHitObject[] triangle = new OsuHitObject[3]; + + // OsuDifficultyHitObject construction requires three components, an extra copy of the first OsuHitObject is used at the beginning. + if (objects.Count > 1) + { + triangle[1] = objects[0]; // This copy will get shifted to the last spot in the triangle. + triangle[0] = objects[0]; // This component corresponds to the real first OsuHitOject. + } + + // The final component of the first triangle will be the second OsuHitOject of the map, which forms the first jump. + // If the map has less than two OsuHitObjects, the enumerator will not return anything. + for (int i = 1; i < objects.Count; ++i) + { + triangle[2] = triangle[1]; + triangle[1] = triangle[0]; + triangle[0] = objects[i]; + + yield return new OsuDifficultyHitObject(triangle); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs new file mode 100644 index 0000000000..bdeb62df3e --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing +{ + /// + /// A wrapper around extending it with additional data required for difficulty calculation. + /// + public class OsuDifficultyHitObject + { + /// + /// The this refers to. + /// + public OsuHitObject BaseObject { get; } + + /// + /// Normalized distance from the of the previous . + /// + public double Distance { get; private set; } + + /// + /// Milliseconds elapsed since the StartTime of the previous . + /// + public double DeltaTime { get; private set; } + + /// + /// Number of milliseconds until the has to be hit. + /// + public double TimeUntilHit { get; set; } + + private const int normalized_radius = 52; + + private readonly OsuHitObject[] t; + + /// + /// Initializes the object calculating extra data required for difficulty calculation. + /// + public OsuDifficultyHitObject(OsuHitObject[] triangle) + { + t = triangle; + BaseObject = t[0]; + setDistances(); + setTimingValues(); + // Calculate angle here + } + + private void setDistances() + { + // We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps. + double scalingFactor = normalized_radius / BaseObject.Radius; + if (BaseObject.Radius < 30) + { + double smallCircleBonus = Math.Min(30 - BaseObject.Radius, 5) / 50; + scalingFactor *= 1 + smallCircleBonus; + } + + Distance = (t[0].StackedPosition - t[1].StackedPosition).Length * scalingFactor; + } + + private void setTimingValues() + { + // Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure. + DeltaTime = Math.Max(40, t[0].StartTime - t[1].StartTime); + TimeUntilHit = 450; // BaseObject.PreEmpt; + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs new file mode 100644 index 0000000000..aad53f6fe8 --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.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 System; +using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills +{ + /// + /// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances. + /// + public class Aim : Skill + { + protected override double SkillMultiplier => 26.25; + protected override double StrainDecayBase => 0.15; + + protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime; + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs new file mode 100644 index 0000000000..b9632e18e2 --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs @@ -0,0 +1,100 @@ +// 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.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; +using osu.Game.Rulesets.Osu.OsuDifficulty.Utils; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills +{ + /// + /// Used to processes strain values of s, keep track of strain levels caused by the processed objects + /// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects. + /// + public abstract class Skill + { + /// + /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. + /// + protected abstract double SkillMultiplier { get; } + + /// + /// Determines how quickly strain decays for the given skill. + /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second. + /// + protected abstract double StrainDecayBase { get; } + + /// + /// s that were processed previously. They can affect the strain values of the following objects. + /// + protected readonly History Previous = new History(2); // Contained objects not used yet + + private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap. + private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section. + private readonly List strainPeaks = new List(); + + /// + /// Process an and update current strain values accordingly. + /// + public void Process(OsuDifficultyHitObject current) + { + currentStrain *= strainDecay(current.DeltaTime); + if (!(current.BaseObject is Spinner)) + currentStrain += StrainValueOf(current) * SkillMultiplier; + + currentSectionPeak = Math.Max(currentStrain, currentSectionPeak); + + Previous.Push(current); + } + + /// + /// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty. + /// + public void SaveCurrentPeak() + { + if (Previous.Count > 0) + strainPeaks.Add(currentSectionPeak); + } + + /// + /// Sets the initial strain level for a new section. + /// + /// The beginning of the new section in milliseconds + public void StartNewSectionFrom(double offset) + { + // The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries. + // This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level. + if (Previous.Count > 0) + currentSectionPeak = currentStrain * strainDecay(offset - Previous[0].BaseObject.StartTime); + } + + /// + /// Returns the calculated difficulty value representing all processed s. + /// + public double DifficultyValue() + { + strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + double difficulty = 0; + double weight = 1; + + // Difficulty is the weighted sum of the highest strains from every section. + foreach (double strain in strainPeaks) + { + difficulty += strain * weight; + weight *= 0.9; + } + + return difficulty; + } + + /// + /// Calculates the strain value of an . This value is affected by previously processed objects. + /// + protected abstract double StrainValueOf(OsuDifficultyHitObject current); + + private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000); + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs new file mode 100644 index 0000000000..6c43c53e35 --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills +{ + /// + /// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit. + /// + public class Speed : Skill + { + protected override double SkillMultiplier => 1400; + protected override double StrainDecayBase => 0.3; + + private const double single_spacing_threshold = 125; + private const double stream_spacing_threshold = 110; + private const double almost_diameter = 90; + + protected override double StrainValueOf(OsuDifficultyHitObject current) + { + double distance = current.Distance; + + double speedValue; + if (distance > single_spacing_threshold) + speedValue = 2.5; + else if (distance > stream_spacing_threshold) + speedValue = 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold); + else if (distance > almost_diameter) + speedValue = 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter); + else if (distance > almost_diameter / 2) + speedValue = 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2); + else + speedValue = 0.95; + + return speedValue / current.DeltaTime; + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs new file mode 100644 index 0000000000..d2c2e1d774 --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs @@ -0,0 +1,86 @@ +// 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; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Utils +{ + /// + /// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full. + /// Indexing starts at the top of the stack. + /// + public class History : IEnumerable + { + public int Count { get; private set; } + + private readonly T[] array; + private readonly int capacity; + private int marker; // Marks the position of the most recently added item. + + /// + /// Initializes a new instance of the History class that is empty and has the specified capacity. + /// + /// The number of items the History can hold. + public History(int capacity) + { + if (capacity < 0) + throw new ArgumentOutOfRangeException(); + + this.capacity = capacity; + array = new T[capacity]; + marker = capacity; // Set marker to the end of the array, outside of the indexed range by one. + } + + /// + /// The most recently added item is returned at index 0. + /// + public T this[int i] + { + get + { + if (i < 0 || i > Count - 1) + throw new IndexOutOfRangeException(); + + i += marker; + if (i > capacity - 1) + i -= capacity; + + return array[i]; + } + } + + /// + /// Adds the item as the most recent one in the history. + /// The oldest item is disposed if the history is full. + /// + public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition. + { + if (marker == 0) + marker = capacity - 1; + else + --marker; + + array[marker] = item; + + if (Count < capacity) + ++Count; + } + + /// + /// Returns an enumerator which enumerates items in the history starting from the most recently added one. + /// + public IEnumerator GetEnumerator() + { + for (int i = marker; i < capacity; ++i) + yield return array[i]; + + if (Count == capacity) + for (int i = 0; i < marker; ++i) + yield return array[i]; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/OsuDifficultyCalculator.cs deleted file mode 100644 index 5669993e67..0000000000 --- a/osu.Game.Rulesets.Osu/OsuDifficultyCalculator.cs +++ /dev/null @@ -1,192 +0,0 @@ -// 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.Beatmaps; -using osu.Game.Rulesets.Osu.Beatmaps; -using osu.Game.Rulesets.Osu.Objects; -using System; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Osu -{ - public class OsuDifficultyCalculator : DifficultyCalculator - { - private const double star_scaling_factor = 0.0675; - private const double extreme_scaling_factor = 0.5; - - /// - /// HitObjects are stored as a member variable. - /// - internal List DifficultyHitObjects = new List(); - - public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap) - { - } - - protected override void PreprocessHitObjects() - { - foreach (var h in Objects) - (h as Slider)?.Curve?.Calculate(); - } - - protected override double CalculateInternal(Dictionary categoryDifficulty) - { - // Fill our custom DifficultyHitObject class, that carries additional information - DifficultyHitObjects.Clear(); - - foreach (var hitObject in Objects) - DifficultyHitObjects.Add(new OsuHitObjectDifficulty(hitObject)); - - // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. - DifficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); - - if (!CalculateStrainValues()) return 0; - - double speedDifficulty = CalculateDifficulty(DifficultyType.Speed); - double aimDifficulty = CalculateDifficulty(DifficultyType.Aim); - - // OverallDifficulty is not considered in this algorithm and neither is HpDrainRate. That means, that in this form the algorithm determines how hard it physically is - // to play the map, assuming, that too much of an error will not lead to a death. - // It might be desirable to include OverallDifficulty into map difficulty, but in my personal opinion it belongs more to the weighting of the actual peformance - // and is superfluous in the beatmap difficulty rating. - // If it were to be considered, then I would look at the hit window of normal HitCircles only, since Sliders and Spinners are (almost) "free" 300s and take map length - // into account as well. - - // The difficulty can be scaled by any desired metric. - // In osu!tp it gets squared to account for the rapid increase in difficulty as the limit of a human is approached. (Of course it also gets scaled afterwards.) - // It would not be suitable for a star rating, therefore: - - // The following is a proposal to forge a star rating from 0 to 5. It consists of taking the square root of the difficulty, since by simply scaling the easier - // 5-star maps would end up with one star. - double speedStars = Math.Sqrt(speedDifficulty) * star_scaling_factor; - double aimStars = Math.Sqrt(aimDifficulty) * star_scaling_factor; - - if (categoryDifficulty != null) - { - categoryDifficulty.Add("Aim", aimStars.ToString("0.00")); - categoryDifficulty.Add("Speed", speedStars.ToString("0.00")); - - double hitWindow300 = 30/*HitObjectManager.HitWindow300*/ / TimeRate; - double preEmpt = 450/*HitObjectManager.PreEmpt*/ / TimeRate; - - categoryDifficulty.Add("OD", (-(hitWindow300 - 80.0) / 6.0).ToString("0.00")); - categoryDifficulty.Add("AR", (preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0).ToString("0.00")); - - int maxCombo = 0; - foreach (OsuHitObjectDifficulty hitObject in DifficultyHitObjects) - maxCombo += hitObject.MaxCombo; - - categoryDifficulty.Add("Max combo", maxCombo.ToString()); - } - - // Again, from own observations and from the general opinion of the community a map with high speed and low aim (or vice versa) difficulty is harder, - // than a map with mediocre difficulty in both. Therefore we can not just add both difficulties together, but will introduce a scaling that favors extremes. - double starRating = speedStars + aimStars + Math.Abs(speedStars - aimStars) * extreme_scaling_factor; - // Another approach to this would be taking Speed and Aim separately to a chosen power, which again would be equivalent. This would be more convenient if - // the hit window size is to be considered as well. - - // Note: The star rating is tuned extremely tight! Airman (/b/104229) and Freedom Dive (/b/126645), two of the hardest ranked maps, both score ~4.66 stars. - // Expect the easier kind of maps that officially get 5 stars to obtain around 2 by this metric. The tutorial still scores about half a star. - // Tune by yourself as you please. ;) - - return starRating; - } - - protected bool CalculateStrainValues() - { - // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. - using (List.Enumerator hitObjectsEnumerator = DifficultyHitObjects.GetEnumerator()) - { - - if (!hitObjectsEnumerator.MoveNext()) return false; - - OsuHitObjectDifficulty current = hitObjectsEnumerator.Current; - - // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. - while (hitObjectsEnumerator.MoveNext()) - { - var next = hitObjectsEnumerator.Current; - next?.CalculateStrains(current, TimeRate); - current = next; - } - - return true; - } - } - - /// - /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. - /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. - /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. - /// - protected const double STRAIN_STEP = 400; - - /// - /// The weighting of each strain value decays to this number * it's previous value - /// - protected const double DECAY_WEIGHT = 0.9; - - protected double CalculateDifficulty(DifficultyType type) - { - double actualStrainStep = STRAIN_STEP * TimeRate; - - // Find the highest strain value within each strain step - List highestStrains = new List(); - double intervalEndTime = actualStrainStep; - double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval - - OsuHitObjectDifficulty previousHitObject = null; - foreach (OsuHitObjectDifficulty hitObject in DifficultyHitObjects) - { - // While we are beyond the current interval push the currently available maximum to our strain list - while (hitObject.BaseHitObject.StartTime > intervalEndTime) - { - highestStrains.Add(maximumStrain); - - // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay - // until the beginning of the next interval. - if (previousHitObject == null) - { - maximumStrain = 0; - } - else - { - double decay = Math.Pow(OsuHitObjectDifficulty.DECAY_BASE[(int)type], (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); - maximumStrain = previousHitObject.Strains[(int)type] * decay; - } - - // Go to the next time interval - intervalEndTime += actualStrainStep; - } - - // Obtain maximum strain - maximumStrain = Math.Max(hitObject.Strains[(int)type], maximumStrain); - - previousHitObject = hitObject; - } - - // Build the weighted sum over the highest strains for each interval - double difficulty = 0; - double weight = 1; - highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - foreach (double strain in highestStrains) - { - difficulty += weight * strain; - weight *= DECAY_WEIGHT; - } - - return difficulty; - } - - protected override BeatmapConverter CreateBeatmapConverter() => new OsuBeatmapConverter(); - - // Those values are used as array indices. Be careful when changing them! - public enum DifficultyType - { - Speed = 0, - Aim, - }; - } -} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index af4a099e0d..63fe6aaa59 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -7,6 +7,7 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.OsuDifficulty; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; @@ -46,7 +47,14 @@ namespace osu.Game.Rulesets.Osu { new OsuModEasy(), new OsuModNoFail(), - new OsuModHalfTime(), + new MultiMod + { + Mods = new Mod[] + { + new OsuModHalfTime(), + new OsuModDaycore(), + }, + }, }; case ModType.DifficultyIncrease: diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index b91bdc6a78..f6f565c502 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -34,8 +34,9 @@ false - - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll + + $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll + True @@ -68,9 +69,14 @@ - - + + + + + + + diff --git a/osu.Game.Rulesets.Osu/packages.config b/osu.Game.Rulesets.Osu/packages.config index 634d0b51f6..fa6edb9c8f 100644 --- a/osu.Game.Rulesets.Osu/packages.config +++ b/osu.Game.Rulesets.Osu/packages.config @@ -5,5 +5,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd . Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE --> - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoMod.cs index 8b7a099b9a..abaa8c1bc1 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoMod.cs @@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Taiko.Mods } + public class TaikoModDaycore : ModDaycore + { + public override double ScoreMultiplier => 0.5; + } + public class TaikoModDoubleTime : ModDoubleTime { public override double ScoreMultiplier => 1.12; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs index 4c83e08bab..5d627f2b50 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs @@ -3,7 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using OpenTK; namespace osu.Game.Rulesets.Taiko.Objects.Drawables diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs index e64682a1e4..c07be915d5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs @@ -3,8 +3,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using OpenTK; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 37efd8aba4..1c72f2a7dd 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; @@ -15,6 +14,7 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER), BlendingMode = BlendingMode.Additive, Masking = true, - Children = new [] + Children = new[] { new Box { @@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Children = new [] + Children = new[] { symbol = new SwellSymbolPiece() } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs index ddf1492ecc..cc88105e03 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs @@ -3,8 +3,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using OpenTK; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index 3ea05b6558..709343d086 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -4,7 +4,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Backgrounds; using OpenTK.Graphics; using osu.Game.Beatmaps.ControlPoints; @@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces private void resetEdgeEffects() { - background.EdgeEffect = new EdgeEffect + background.EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Colour = AccentColour.Opacity(KiaiMode ? edge_alpha_kiai : 1f), diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs index 4146edbdf7..704a27a96d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs @@ -3,9 +3,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs index 1a0d0156e8..2af65f2ed7 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs @@ -3,9 +3,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 7c169f820b..303d936fb9 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -28,7 +28,14 @@ namespace osu.Game.Rulesets.Taiko { new TaikoModEasy(), new TaikoModNoFail(), - new TaikoModHalfTime(), + new MultiMod + { + Mods = new Mod[] + { + new TaikoModHalfTime(), + new TaikoModDaycore(), + }, + }, }; case ModType.DifficultyIncrease: diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index c0c329c870..4d39ba0ead 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -6,7 +6,7 @@ using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; diff --git a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs index fde2623246..1ca6e4eb34 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs @@ -5,7 +5,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.UI diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index e0da3ed3db..b4aec7057b 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -5,7 +5,7 @@ using OpenTK; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load(OsuColour colours) { - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Colour = isRim ? colours.BlueDarker : colours.PinkDarker, diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index c7bd4a6704..0ad7969c78 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -3,7 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.UI; using OpenTK; @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Both, BorderThickness = 2, Masking = true, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.2f), @@ -229,7 +229,6 @@ namespace osu.Game.Rulesets.Taiko.UI if (judgedObject.HitObject.Kiai) kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject.Judgement, isRim)); - } else hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit(); diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 8d6fcb503c..f719f2002b 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -33,8 +33,9 @@ false - - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll + + $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll + True diff --git a/osu.Game.Rulesets.Taiko/packages.config b/osu.Game.Rulesets.Taiko/packages.config index dc059c684b..8add43d5d5 100644 --- a/osu.Game.Rulesets.Taiko/packages.config +++ b/osu.Game.Rulesets.Taiko/packages.config @@ -4,5 +4,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd . Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE --> - + \ No newline at end of file diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 0e456941a1..4a6b972f4a 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(File.Exists(temp)); var importer = new BeatmapIPCChannel(client); - if (!importer.ImportAsync(temp).Wait(5000)) + if (!importer.ImportAsync(temp).Wait(10000)) Assert.Fail(@"IPC took too long to send"); ensureLoaded(host); @@ -166,4 +166,3 @@ namespace osu.Game.Tests.Beatmaps.IO } } } - diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 03d09e24e0..c9b3b1b922 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -86,4 +86,3 @@ namespace osu.Game.Tests.Beatmaps.IO } } } - diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index b8fcb80aaf..8ec68b41be 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -30,11 +30,13 @@ false - - $(SolutionDir)\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll + + $(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll + True - - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll + + $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll + True diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config index 9972fb41a1..9ad76308d7 100644 --- a/osu.Game.Tests/packages.config +++ b/osu.Game.Tests/packages.config @@ -4,8 +4,8 @@ Copyright (c) 2007-2017 ppy Pty Ltd . Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE --> - - + + \ No newline at end of file diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 6f7e38c163..c7bdbb5dd6 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -15,6 +15,6 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The beat length at this control point. /// - public double BeatLength = 500; + public double BeatLength = 1000; } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs index cb929dcca5..f4ac01c0f1 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs @@ -14,6 +14,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Input; using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Beatmaps.Drawables { diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs index 2ab5487082..1ac6261621 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Beatmaps.Drawables { diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 9df1f0f284..14298c4172 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -10,7 +10,6 @@ using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - public class DifficultyIcon : DifficultyColouredContainer { private readonly BeatmapInfo beatmap; diff --git a/osu.Game/Beatmaps/Drawables/Panel.cs b/osu.Game/Beatmaps/Drawables/Panel.cs index f7349d981a..3dac50732c 100644 --- a/osu.Game/Beatmaps/Drawables/Panel.cs +++ b/osu.Game/Beatmaps/Drawables/Panel.cs @@ -88,7 +88,7 @@ namespace osu.Game.Beatmaps.Drawables protected virtual void Selected() { nestedContainer.BorderThickness = 2.5f; - nestedContainer.EdgeEffect = new EdgeEffect + nestedContainer.EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Colour = new Color4(130, 204, 255, 150), @@ -100,7 +100,7 @@ namespace osu.Game.Beatmaps.Drawables protected virtual void Deselected() { nestedContainer.BorderThickness = 0; - nestedContainer.EdgeEffect = new EdgeEffect + nestedContainer.EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Offset = new Vector2(1), diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index c6aac3bb71..4c540fa8cf 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.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.Globalization; using System.IO; using OpenTK.Graphics; @@ -31,6 +32,8 @@ namespace osu.Game.Beatmaps.Formats private ConvertHitObjectParser parser; + private readonly Dictionary variables = new Dictionary(); + private LegacySampleBank defaultSampleBank; private int defaultSampleVolume = 100; @@ -56,36 +59,39 @@ namespace osu.Game.Beatmaps.Formats TimingPoints, Colours, HitObjects, + Variables, } - private void handleGeneral(Beatmap beatmap, string key, string val) + private void handleGeneral(Beatmap beatmap, string line) { + var pair = splitKeyVal(line, ':'); + var metadata = beatmap.BeatmapInfo.Metadata; - switch (key) + switch (pair.Key) { case @"AudioFilename": - metadata.AudioFile = val; + metadata.AudioFile = pair.Value; break; case @"AudioLeadIn": - beatmap.BeatmapInfo.AudioLeadIn = int.Parse(val); + beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); break; case @"PreviewTime": - metadata.PreviewTime = int.Parse(val); + metadata.PreviewTime = int.Parse(pair.Value); break; case @"Countdown": - beatmap.BeatmapInfo.Countdown = int.Parse(val) == 1; + beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; break; case @"SampleSet": - defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), val); + defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); break; case @"SampleVolume": - defaultSampleVolume = int.Parse(val); + defaultSampleVolume = int.Parse(pair.Value); break; case @"StackLeniency": - beatmap.BeatmapInfo.StackLeniency = float.Parse(val, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"Mode": - beatmap.BeatmapInfo.RulesetID = int.Parse(val); + beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); switch (beatmap.BeatmapInfo.RulesetID) { @@ -104,107 +110,135 @@ namespace osu.Game.Beatmaps.Formats } break; case @"LetterboxInBreaks": - beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(val) == 1; + beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; break; case @"SpecialStyle": - beatmap.BeatmapInfo.SpecialStyle = int.Parse(val) == 1; + beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; break; case @"WidescreenStoryboard": - beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(val) == 1; + beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; break; } } - private void handleEditor(Beatmap beatmap, string key, string val) + private void handleEditor(Beatmap beatmap, string line) { - switch (key) + var pair = splitKeyVal(line, ':'); + + switch (pair.Key) { case @"Bookmarks": - beatmap.BeatmapInfo.StoredBookmarks = val; + beatmap.BeatmapInfo.StoredBookmarks = pair.Value; break; case @"DistanceSpacing": - beatmap.BeatmapInfo.DistanceSpacing = double.Parse(val, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"BeatDivisor": - beatmap.BeatmapInfo.BeatDivisor = int.Parse(val); + beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); break; case @"GridSize": - beatmap.BeatmapInfo.GridSize = int.Parse(val); + beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); break; case @"TimelineZoom": - beatmap.BeatmapInfo.TimelineZoom = double.Parse(val, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; } } - private void handleMetadata(Beatmap beatmap, string key, string val) + private void handleMetadata(Beatmap beatmap, string line) { + var pair = splitKeyVal(line, ':'); + var metadata = beatmap.BeatmapInfo.Metadata; - switch (key) + switch (pair.Key) { case @"Title": - metadata.Title = val; + metadata.Title = pair.Value; break; case @"TitleUnicode": - metadata.TitleUnicode = val; + metadata.TitleUnicode = pair.Value; break; case @"Artist": - metadata.Artist = val; + metadata.Artist = pair.Value; break; case @"ArtistUnicode": - metadata.ArtistUnicode = val; + metadata.ArtistUnicode = pair.Value; break; case @"Creator": - metadata.Author = val; + metadata.Author = pair.Value; break; case @"Version": - beatmap.BeatmapInfo.Version = val; + beatmap.BeatmapInfo.Version = pair.Value; break; case @"Source": - beatmap.BeatmapInfo.Metadata.Source = val; + beatmap.BeatmapInfo.Metadata.Source = pair.Value; break; case @"Tags": - beatmap.BeatmapInfo.Metadata.Tags = val; + beatmap.BeatmapInfo.Metadata.Tags = pair.Value; break; case @"BeatmapID": - beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(val); + beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); break; case @"BeatmapSetID": - beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(val); - metadata.OnlineBeatmapSetID = int.Parse(val); + beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); + metadata.OnlineBeatmapSetID = int.Parse(pair.Value); break; } } - private void handleDifficulty(Beatmap beatmap, string key, string val) + private void handleDifficulty(Beatmap beatmap, string line) { + var pair = splitKeyVal(line, ':'); + var difficulty = beatmap.BeatmapInfo.Difficulty; - switch (key) + switch (pair.Key) { case @"HPDrainRate": - difficulty.DrainRate = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.DrainRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"CircleSize": - difficulty.CircleSize = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.CircleSize = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"OverallDifficulty": - difficulty.OverallDifficulty = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.OverallDifficulty = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"ApproachRate": - difficulty.ApproachRate = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"SliderMultiplier": - difficulty.SliderMultiplier = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.SliderMultiplier = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"SliderTickRate": - difficulty.SliderTickRate = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.SliderTickRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; } } - private void handleEvents(Beatmap beatmap, string val) + /// + /// Decodes any beatmap variables present in a line into their real values. + /// + /// The line which may contains variables. + private void decodeVariables(ref string line) { - string[] split = val.Split(','); + while (line.IndexOf('$') >= 0) + { + 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); + } + } + + private void handleEvents(Beatmap beatmap, string line) + { + decodeVariables(ref line); + + string[] split = line.Split(','); EventType type; if (!Enum.TryParse(split[0], out type)) @@ -236,9 +270,9 @@ namespace osu.Game.Beatmaps.Formats } } - private void handleTimingPoints(Beatmap beatmap, string val) + private void handleTimingPoints(Beatmap beatmap, string line) { - string[] split = val.Split(','); + string[] split = line.Split(','); double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo); double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); @@ -321,12 +355,14 @@ namespace osu.Game.Beatmaps.Formats } } - private void handleColours(Beatmap beatmap, string key, string val, ref bool hasCustomColours) + private void handleColours(Beatmap beatmap, string line, ref bool hasCustomColours) { - string[] split = val.Split(','); + 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): {val}"); + 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)) @@ -339,7 +375,7 @@ namespace osu.Game.Beatmaps.Formats } // Note: the combo index specified in the beatmap is discarded - if (key.StartsWith(@"Combo")) + if (pair.Key.StartsWith(@"Combo")) { beatmap.ComboColors.Add(new Color4 { @@ -351,6 +387,12 @@ namespace osu.Game.Beatmaps.Formats } } + private void handleVariables(string line) + { + var pair = splitKeyVal(line, '='); + variables[pair.Key] = pair.Value; + } + protected override Beatmap ParseFile(StreamReader stream) { return new LegacyBeatmap(base.ParseFile(stream)); @@ -390,42 +432,39 @@ namespace osu.Game.Beatmaps.Formats continue; } - string val = line, key = null; - if (section != Section.Events && section != Section.TimingPoints && section != Section.HitObjects) - { - key = val.Remove(val.IndexOf(':')).Trim(); - val = val.Substring(val.IndexOf(':') + 1).Trim(); - } switch (section) { case Section.General: - handleGeneral(beatmap, key, val); + handleGeneral(beatmap, line); break; case Section.Editor: - handleEditor(beatmap, key, val); + handleEditor(beatmap, line); break; case Section.Metadata: - handleMetadata(beatmap, key, val); + handleMetadata(beatmap, line); break; case Section.Difficulty: - handleDifficulty(beatmap, key, val); + handleDifficulty(beatmap, line); break; case Section.Events: - handleEvents(beatmap, val); + handleEvents(beatmap, line); break; case Section.TimingPoints: - handleTimingPoints(beatmap, val); + handleTimingPoints(beatmap, line); break; case Section.Colours: - handleColours(beatmap, key, val, ref hasCustomColours); + handleColours(beatmap, line, ref hasCustomColours); break; case Section.HitObjects: - var obj = parser.Parse(val); + var obj = parser.Parse(line); if (obj != null) beatmap.HitObjects.Add(obj); break; + case Section.Variables: + handleVariables(line); + break; } } @@ -433,6 +472,15 @@ namespace osu.Game.Beatmaps.Formats hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty); } + private KeyValuePair splitKeyVal(string line, char separator) + { + return new KeyValuePair + ( + line.Remove(line.IndexOf(separator)).Trim(), + line.Substring(line.IndexOf(separator) + 1).Trim() + ); + } + internal enum LegacySampleBank { None = 0, diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 8f177d6b56..6b07d5c967 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -13,17 +13,17 @@ namespace osu.Game.Configuration protected override void InitialiseDefaults() { // UI/selection defaults - Set(OsuSetting.Ruleset, 0, 0, int.MaxValue); Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details); Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10); Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10); + Set(OsuSetting.SelectionRandomType, SelectionRandomType.RandomPermutation); + Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1); // Online settings - Set(OsuSetting.Username, string.Empty); Set(OsuSetting.Token, string.Empty); @@ -38,14 +38,12 @@ namespace osu.Game.Configuration }; // Audio - Set(OsuSetting.MenuVoice, true); Set(OsuSetting.MenuMusic, true); Set(OsuSetting.AudioOffset, 0, -500.0, 500.0); // Input - Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2); Set(OsuSetting.GameplayCursorSize, 1.0, 0.5f, 2); Set(OsuSetting.AutoCursorSize, false); @@ -54,7 +52,6 @@ namespace osu.Game.Configuration Set(OsuSetting.MouseDisableWheel, false); // Graphics - Set(OsuSetting.ShowFpsDisplay, false); Set(OsuSetting.MenuParallax, true); @@ -63,14 +60,15 @@ namespace osu.Game.Configuration Set(OsuSetting.SnakingOutSliders, true); // Gameplay - Set(OsuSetting.DimLevel, 0.3, 0, 1); Set(OsuSetting.ShowInterface, true); Set(OsuSetting.KeyOverlay, false); - // Update + Set(OsuSetting.FloatingComments, false); + Set(OsuSetting.PlaybackSpeed, 1.0, 0.5f, 2); + // Update Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); } @@ -88,6 +86,8 @@ namespace osu.Game.Configuration AutoCursorSize, DimLevel, KeyOverlay, + FloatingComments, + PlaybackSpeed, ShowInterface, MouseDisableButtons, MouseDisableWheel, @@ -102,6 +102,7 @@ namespace osu.Game.Configuration SaveUsername, DisplayStarsMinimum, DisplayStarsMaximum, + SelectionRandomType, SnakingInSliders, SnakingOutSliders, ShowFpsDisplay, diff --git a/osu.Game/Configuration/SelectionRandomType.cs b/osu.Game/Configuration/SelectionRandomType.cs new file mode 100644 index 0000000000..298ee71e36 --- /dev/null +++ b/osu.Game/Configuration/SelectionRandomType.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; + +namespace osu.Game.Configuration +{ + public enum SelectionRandomType + { + [Description("Never repeat")] + RandomPermutation, + [Description("Random")] + Random + } +} \ No newline at end of file diff --git a/osu.Game/Database/BeatmapDifficulty.cs b/osu.Game/Database/BeatmapDifficulty.cs index cf1305f705..87c651aa88 100644 --- a/osu.Game/Database/BeatmapDifficulty.cs +++ b/osu.Game/Database/BeatmapDifficulty.cs @@ -39,4 +39,3 @@ namespace osu.Game.Database } } } - diff --git a/osu.Game/Database/BeatmapSetInfo.cs b/osu.Game/Database/BeatmapSetInfo.cs index 0875d3c01f..aa5fa21394 100644 --- a/osu.Game/Database/BeatmapSetInfo.cs +++ b/osu.Game/Database/BeatmapSetInfo.cs @@ -36,4 +36,3 @@ namespace osu.Game.Database public string StoryboardFile { get; set; } } } - diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 7a2345a80c..08745ce6ba 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -2,12 +2,10 @@ // 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.MathUtils; using OpenTK; using OpenTK.Graphics; using System; -using osu.Framework.Graphics.OpenGL; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; using OpenTK.Graphics.ES30; @@ -16,6 +14,7 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Allocation; using System.Collections.Generic; using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Lists; namespace osu.Game.Graphics.Backgrounds @@ -56,7 +55,7 @@ namespace osu.Game.Graphics.Backgrounds /// /// Whether we should drop-off alpha values of triangles more quickly to improve /// the visual appearance of fading. This defaults to on as it is generally more - /// aesthetically pleasing, but should be turned off in s. + /// aesthetically pleasing, but should be turned off in buffered containers. /// public bool HideAlphaDiscrepancies = true; diff --git a/osu.Game/Graphics/Containers/ReverseDepthFillFlowContainer.cs b/osu.Game/Graphics/Containers/ReverseDepthFillFlowContainer.cs index 2b52b06abc..0b38bf5fe0 100644 --- a/osu.Game/Graphics/Containers/ReverseDepthFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/ReverseDepthFillFlowContainer.cs @@ -10,7 +10,7 @@ namespace osu.Game.Graphics.Containers { public class ReverseDepthFillFlowContainer : FillFlowContainer where T : Drawable { - protected override IComparer DepthComparer => new ReverseCreationOrderDepthComparer(); - protected override IEnumerable FlowingChildren => base.FlowingChildren.Reverse(); + protected override IComparer DepthComparer => new ReverseCreationOrderDepthComparer(); + protected override IEnumerable FlowingChildren => base.FlowingChildren.Reverse(); } } diff --git a/osu.Game/Graphics/Cursor/CursorTrail.cs b/osu.Game/Graphics/Cursor/CursorTrail.cs index 11475a0f19..183679fbd3 100644 --- a/osu.Game/Graphics/Cursor/CursorTrail.cs +++ b/osu.Game/Graphics/Cursor/CursorTrail.cs @@ -8,13 +8,13 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Input; using OpenTK; using System; -using osu.Framework.Graphics.OpenGL; using osu.Framework.Graphics.OpenGL.Buffers; using OpenTK.Graphics.ES30; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Colour; using osu.Framework.Timing; using System.Diagnostics; +using osu.Framework.Graphics.OpenGL.Vertices; namespace osu.Game.Graphics.Cursor { diff --git a/osu.Game/Graphics/Cursor/GameplayCursor.cs b/osu.Game/Graphics/Cursor/GameplayCursor.cs index 801fe1d011..da3975f606 100644 --- a/osu.Game/Graphics/Cursor/GameplayCursor.cs +++ b/osu.Game/Graphics/Cursor/GameplayCursor.cs @@ -9,7 +9,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -67,7 +67,7 @@ namespace osu.Game.Graphics.Cursor Masking = true, BorderThickness = Size.X / 6, BorderColour = Color4.White, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Pink.Opacity(0.5f), diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index b48ab879a6..82ae424ab0 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -93,6 +93,7 @@ namespace osu.Game.Graphics.Cursor { private Container cursorContainer; private Bindable cursorScale; + private const float base_scale = 0.15f; public Sprite AdditiveLayer; @@ -108,17 +109,15 @@ namespace osu.Game.Graphics.Cursor { cursorContainer = new Container { - Size = new Vector2(32), + AutoSizeAxes = Axes.Both, Children = new Drawable[] { new Sprite { - FillMode = FillMode.Fit, Texture = textures.Get(@"Cursor/menu-cursor"), }, AdditiveLayer = new Sprite { - FillMode = FillMode.Fit, BlendingMode = BlendingMode.Additive, Colour = colour.Pink, Alpha = 0, @@ -129,7 +128,7 @@ namespace osu.Game.Graphics.Cursor }; cursorScale = config.GetBindable(OsuSetting.MenuCursorSize); - cursorScale.ValueChanged += newScale => cursorContainer.Scale = new Vector2((float)newScale); + cursorScale.ValueChanged += newScale => cursorContainer.Scale = new Vector2((float)newScale * base_scale); cursorScale.TriggerChange(); } } diff --git a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs new file mode 100644 index 0000000000..2cc6c3a46a --- /dev/null +++ b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs @@ -0,0 +1,18 @@ +// 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.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Graphics.Cursor +{ + public class OsuContextMenuContainer : ContextMenuContainer + { + protected override ContextMenu CreateContextMenu() => new OsuContextMenu(); + + public OsuContextMenuContainer(CursorContainer cursor) : base(cursor) + { + } + } +} \ No newline at end of file diff --git a/osu.Game/Graphics/Cursor/OsuCursorContainer.cs b/osu.Game/Graphics/Cursor/OsuCursorContainer.cs deleted file mode 100644 index 8b71182263..0000000000 --- a/osu.Game/Graphics/Cursor/OsuCursorContainer.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -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.Cursor; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Transforms; -using osu.Framework.Input; -using osu.Game.Configuration; -using System; - -namespace osu.Game.Graphics.Cursor -{ - public class OsuCursorContainer : CursorContainer - { - protected override Drawable CreateCursor() => new OsuCursor(); - - public OsuCursorContainer() - { - Add(new CursorTrail { Depth = 1 }); - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - ActiveCursor.Scale = new Vector2(1); - ActiveCursor.ScaleTo(1.2f, 100, EasingTypes.OutQuad); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - if (!state.Mouse.HasMainButtonPressed) - ActiveCursor.ScaleTo(1, 200, EasingTypes.OutQuad); - return base.OnMouseUp(state, args); - } - - public class OsuCursor : Container - { - private Container cursorContainer; - private Bindable cursorScale; - - public OsuCursor() - { - Origin = Anchor.Centre; - Size = new Vector2(42); - } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - cursorScale = config.GetBindable(OsuConfig.CursorSize); - - Children = new Drawable[] - { - cursorContainer = new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Scale = new Vector2((float)cursorScale), - Masking = true, - BorderThickness = Size.X / 6, - BorderColour = Color4.White, - EdgeEffect = new EdgeEffect { - Type = EdgeEffectType.Shadow, - Colour = Color4.Pink.Opacity(0.5f), - Radius = 5, - }, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, - new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = Size.X / 3, - BorderColour = Color4.White.Opacity(0.5f), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, - }, - }, - new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.1f), - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - }, - }, - }, - } - }, - }; - cursorScale.ValueChanged += scaleChanged; - } - - private void scaleChanged(object sender, EventArgs e) - { - cursorContainer.Scale = new Vector2((float)cursorScale); - } - } - } -} diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs new file mode 100644 index 0000000000..133caf8040 --- /dev/null +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -0,0 +1,109 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.Cursor +{ + public class OsuTooltipContainer : TooltipContainer + { + protected override Tooltip CreateTooltip() => new OsuTooltip(); + + public OsuTooltipContainer(CursorContainer cursor) : base(cursor) + { + } + + public class OsuTooltip : Tooltip + { + private readonly Box background; + private readonly OsuSpriteText text; + private bool instantMovement = true; + + public override string TooltipText + { + set + { + if (value == text.Text) return; + + text.Text = value; + if (IsPresent) + { + AutoSizeDuration = 250; + background.FlashColour(OsuColour.Gray(0.4f), 1000, EasingTypes.OutQuint); + } + else + AutoSizeDuration = 0; + } + } + + private const float text_size = 16; + + public OsuTooltip() + { + AutoSizeEasing = EasingTypes.OutQuint; + + CornerRadius = 5; + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(40), + Radius = 5, + }; + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.9f, + }, + text = new OsuSpriteText + { + TextSize = text_size, + Padding = new MarginPadding(5), + Font = @"Exo2.0-Regular", + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + background.Colour = colour.Gray3; + } + + protected override void PopIn() + { + instantMovement |= !IsPresent; + FadeIn(500, EasingTypes.OutQuint); + } + + protected override void PopOut() + { + using (BeginDelayedSequence(150)) + FadeOut(500, EasingTypes.OutQuint); + } + + public override void Move(Vector2 pos) + { + if (instantMovement) + { + Position = pos; + instantMovement = false; + } + else + { + MoveTo(pos, 200, EasingTypes.OutQuint); + } + } + } + } +} diff --git a/osu.Game/Graphics/Cursor/TooltipContainer.cs b/osu.Game/Graphics/Cursor/TooltipContainer.cs deleted file mode 100644 index c5b8382816..0000000000 --- a/osu.Game/Graphics/Cursor/TooltipContainer.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -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.Cursor; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using osu.Framework.Threading; -using osu.Game.Graphics.Sprites; -using System.Linq; - -namespace osu.Game.Graphics.Cursor -{ - public class TooltipContainer : Container - { - private readonly CursorContainer cursor; - private readonly Tooltip tooltip; - - private ScheduledDelegate findTooltipTask; - private UserInputManager inputManager; - - private const int default_appear_delay = 220; - - private IHasTooltip currentlyDisplayed; - - public TooltipContainer(CursorContainer cursor) - { - this.cursor = cursor; - AlwaysPresent = true; - RelativeSizeAxes = Axes.Both; - Add(tooltip = new Tooltip { Alpha = 0 }); - } - - [BackgroundDependencyLoader] - private void load(UserInputManager input) - { - inputManager = input; - } - - protected override void Update() - { - if (tooltip.IsPresent) - { - if (currentlyDisplayed != null) - tooltip.TooltipText = currentlyDisplayed.TooltipText; - - //update the position of the displayed tooltip. - tooltip.Position = ToLocalSpace(cursor.ActiveCursor.ScreenSpaceDrawQuad.Centre) + new Vector2(10); - } - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - updateTooltipState(state); - return base.OnMouseUp(state, args); - } - - protected override bool OnMouseMove(InputState state) - { - updateTooltipState(state); - return base.OnMouseMove(state); - } - - private void updateTooltipState(InputState state) - { - if (currentlyDisplayed?.Hovering != true) - { - if (currentlyDisplayed != null && !state.Mouse.HasMainButtonPressed) - { - tooltip.Delay(150); - tooltip.FadeOut(500, EasingTypes.OutQuint); - currentlyDisplayed = null; - } - - findTooltipTask?.Cancel(); - findTooltipTask = Scheduler.AddDelayed(delegate - { - var tooltipTarget = inputManager.HoveredDrawables.OfType().FirstOrDefault(); - - if (tooltipTarget == null) return; - - tooltip.TooltipText = tooltipTarget.TooltipText; - tooltip.FadeIn(500, EasingTypes.OutQuint); - - currentlyDisplayed = tooltipTarget; - }, (1 - tooltip.Alpha) * default_appear_delay); - } - } - - public class Tooltip : Container - { - private readonly Box background; - private readonly OsuSpriteText text; - - public string TooltipText - { - set - { - if (value == text.Text) return; - - text.Text = value; - if (Alpha > 0) - { - AutoSizeDuration = 250; - background.FlashColour(OsuColour.Gray(0.4f), 1000, EasingTypes.OutQuint); - } - else - AutoSizeDuration = 0; - } - } - - public override bool HandleInput => false; - - private const float text_size = 16; - - public Tooltip() - { - AutoSizeEasing = EasingTypes.OutQuint; - AutoSizeAxes = Axes.Both; - - CornerRadius = 5; - Masking = true; - EdgeEffect = new EdgeEffect - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(40), - Radius = 5, - }; - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.9f, - }, - text = new OsuSpriteText - { - TextSize = text_size, - Padding = new MarginPadding(5), - Font = @"Exo2.0-Regular", - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - background.Colour = colour.Gray3; - } - } - } -} diff --git a/osu.Game/Graphics/IHasAccentColour.cs b/osu.Game/Graphics/IHasAccentColour.cs index e4647f22fd..672c59a935 100644 --- a/osu.Game/Graphics/IHasAccentColour.cs +++ b/osu.Game/Graphics/IHasAccentColour.cs @@ -3,6 +3,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; using osu.Game.Graphics.Transforms; namespace osu.Game.Graphics @@ -26,7 +27,8 @@ namespace osu.Game.Graphics /// The new accent colour. /// The tween duration. /// The tween easing. - public static void FadeAccent(this IHasAccentColour accentedDrawable, Color4 newColour, double duration = 0, EasingTypes easing = EasingTypes.None) + public static void FadeAccent(this T accentedDrawable, Color4 newColour, double duration = 0, EasingTypes easing = EasingTypes.None) + where T : Transformable, IHasAccentColour { accentedDrawable.TransformTo(() => accentedDrawable.AccentColour, newColour, duration, easing, new TransformAccent()); } diff --git a/osu.Game/Graphics/IHasTooltip.cs b/osu.Game/Graphics/IHasTooltip.cs deleted file mode 100644 index dd51d68c41..0000000000 --- a/osu.Game/Graphics/IHasTooltip.cs +++ /dev/null @@ -1,15 +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; - -namespace osu.Game.Graphics -{ - public interface IHasTooltip : IDrawable - { - /// - /// Tooltip that shows when hovering the drawable - /// - string TooltipText { get; } - } -} diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 3d83668d07..d2f4d4768c 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -87,5 +87,7 @@ namespace osu.Game.Graphics public readonly Color4 RedDarker = FromHex(@"870000"); public readonly Color4 ChatBlue = FromHex(@"17292e"); + + public readonly Color4 ContextMenuGray = FromHex(@"223034"); } } diff --git a/osu.Game/Graphics/Transforms/TransformAccent.cs b/osu.Game/Graphics/Transforms/TransformAccent.cs index 406d1ea9eb..d49f969c20 100644 --- a/osu.Game/Graphics/Transforms/TransformAccent.cs +++ b/osu.Game/Graphics/Transforms/TransformAccent.cs @@ -8,7 +8,7 @@ using osu.Framework.MathUtils; namespace osu.Game.Graphics.Transforms { - public class TransformAccent : Transform + public class TransformAccent : Transform { /// /// Current value of the transformed colour in linear colour space. diff --git a/osu.Game/Graphics/UserInterface/Bar.cs b/osu.Game/Graphics/UserInterface/Bar.cs index 76b75f1084..1da73a60a2 100644 --- a/osu.Game/Graphics/UserInterface/Bar.cs +++ b/osu.Game/Graphics/UserInterface/Bar.cs @@ -5,7 +5,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using System; namespace osu.Game.Graphics.UserInterface @@ -81,7 +81,7 @@ namespace osu.Game.Graphics.UserInterface background = new Box { RelativeSizeAxes = Axes.Both, - Colour = new Color4(0,0,0,0) + Colour = new Color4(0, 0, 0, 0) }, bar = new Box { diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index c935d58a4b..10c821e9bd 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -5,6 +5,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Containers; using osu.Framework.Audio.Sample; @@ -222,7 +223,7 @@ namespace osu.Game.Graphics.UserInterface Width = 0.8f, Masking = true, MaskingSmoothness = 2, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.2f), diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index fe1d255bba..42fff0f258 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -37,11 +37,10 @@ namespace osu.Game.Graphics.UserInterface this.inputManager = inputManager; } - protected override bool OnFocus(InputState state) + protected override void OnFocus(InputState state) { - var result = base.OnFocus(state); + base.OnFocus(state); BorderThickness = 0; - return result; } protected override void OnFocusLost(InputState state) @@ -56,6 +55,6 @@ namespace osu.Game.Graphics.UserInterface base.OnFocusLost(state); } - public override bool RequestingFocus => HoldFocus; + public override bool RequestsFocus => HoldFocus; } } diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs new file mode 100644 index 0000000000..8540b35702 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/IconButton.cs @@ -0,0 +1,114 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +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.Input; + +namespace osu.Game.Graphics.UserInterface +{ + public class IconButton : ClickableContainer + { + private readonly TextAwesome icon; + private readonly Box hover; + private readonly Container content; + + public FontAwesome Icon + { + get { return icon.Icon; } + set { icon.Icon = value; } + } + + private const float button_size = 30; + private Color4 flashColour; + + public Vector2 IconScale + { + get { return icon.Scale; } + set { icon.Scale = value; } + } + + public IconButton() + { + AutoSizeAxes = Axes.Both; + + Origin = Anchor.Centre; + Anchor = Anchor.Centre; + + Children = new Drawable[] + { + content = new Container + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2 (button_size), + + CornerRadius = 5, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.04f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }, + Children = new Drawable[] + { + hover = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + icon = new TextAwesome + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + TextSize = 18, + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hover.Colour = colours.Yellow.Opacity(0.6f); + flashColour = colours.Yellow; + } + + protected override bool OnHover(InputState state) + { + hover.FadeIn(500, EasingTypes.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + hover.FadeOut(500, EasingTypes.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + hover.FlashColour(flashColour, 800, EasingTypes.OutQuint); + return base.OnClick(state); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + content.ScaleTo(0.75f, 2000, EasingTypes.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + content.ScaleTo(1, 1000, EasingTypes.OutElastic); + return base.OnMouseUp(state, args); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/LoadingAnimation.cs b/osu.Game/Graphics/UserInterface/LoadingAnimation.cs index 61ec859b44..27a888f0b5 100644 --- a/osu.Game/Graphics/UserInterface/LoadingAnimation.cs +++ b/osu.Game/Graphics/UserInterface/LoadingAnimation.cs @@ -1,15 +1,48 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; namespace osu.Game.Graphics.UserInterface { - public class LoadingAnimation : SpriteText + public class LoadingAnimation : VisibilityContainer { + private readonly TextAwesome spinner; + public LoadingAnimation() { - Text = "Loading"; + Size = new Vector2(20); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Children = new Drawable[] + { + spinner = new TextAwesome + { + TextSize = 20, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_spinner + } + }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + spinner.RotateTo(360, 2000); + using (spinner.BeginDelayedSequence(2000)) + spinner.Loop(); + } + + private const float transition_duration = 500; + + protected override void PopIn() => FadeIn(transition_duration * 5, EasingTypes.OutQuint); + + protected override void PopOut() => FadeOut(transition_duration, EasingTypes.OutQuint); } -} \ No newline at end of file +} diff --git a/osu.Game/Screens/Menu/MenuVisualisation.cs b/osu.Game/Graphics/UserInterface/MenuItemType.cs similarity index 54% rename from osu.Game/Screens/Menu/MenuVisualisation.cs rename to osu.Game/Graphics/UserInterface/MenuItemType.cs index 85c65b460d..bd89dbfced 100644 --- a/osu.Game/Screens/Menu/MenuVisualisation.cs +++ b/osu.Game/Graphics/UserInterface/MenuItemType.cs @@ -1,11 +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; - -namespace osu.Game.Screens.Menu +namespace osu.Game.Graphics.UserInterface { - internal class MenuVisualisation : Drawable + public enum MenuItemType { + Standard, + Highlighted, + Destructive, } -} +} \ No newline at end of file diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 82ede8f079..d5059945c6 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -7,18 +7,17 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterface { - public class Nub : CircularContainer, IHasCurrentValue + public class Nub : CircularContainer, IHasCurrentValue, IHasAccentColour { public const float COLLAPSED_SIZE = 20; public const float EXPANDED_SIZE = 40; private const float border_width = 3; - private Color4 glowingColour, idleColour; public Nub() { @@ -53,33 +52,41 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - Colour = idleColour = colours.Pink; - glowingColour = colours.PinkLighter; + AccentColour = colours.Pink; + GlowingAccentColour = colours.PinkLighter; + GlowColour = colours.PinkDarker; - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { - Colour = colours.PinkDarker, + Colour = GlowColour, Type = EdgeEffectType.Glow, Radius = 10, Roundness = 8, }; + } + protected override void LoadComplete() + { FadeEdgeEffectTo(0); } + private bool glowing; public bool Glowing { + get { return glowing; } set { + glowing = value; + if (value) { - FadeColour(glowingColour, 500, EasingTypes.OutQuint); + FadeColour(GlowingAccentColour, 500, EasingTypes.OutQuint); FadeEdgeEffectTo(1, 500, EasingTypes.OutQuint); } else { FadeEdgeEffectTo(0, 500); - FadeColour(idleColour, 500); + FadeColour(AccentColour, 500); } } } @@ -93,5 +100,43 @@ namespace osu.Game.Graphics.UserInterface } public Bindable Current { get; } = new Bindable(); + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + if (!Glowing) + Colour = value; + } + } + + private Color4 glowingAccentColour; + public Color4 GlowingAccentColour + { + get { return glowingAccentColour; } + set + { + glowingAccentColour = value; + if (Glowing) + Colour = value; + } + } + + private Color4 glowColour; + public Color4 GlowColour + { + get { return glowColour; } + set + { + glowColour = value; + + var effect = EdgeEffect; + effect.Colour = value; + EdgeEffect = effect; + } + } } } diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index bee12133ff..7bd58d38a5 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -5,6 +5,7 @@ using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 85231ffab9..198a01b5a4 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -51,7 +51,8 @@ namespace osu.Game.Graphics.UserInterface } } - private readonly Nub nub; + protected readonly Nub Nub; + private readonly SpriteText labelSpriteText; private SampleChannel sampleChecked; private SampleChannel sampleUnchecked; @@ -64,7 +65,7 @@ namespace osu.Game.Graphics.UserInterface Children = new Drawable[] { labelSpriteText = new OsuSpriteText(), - nub = new Nub + Nub = new Nub { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, @@ -72,7 +73,7 @@ namespace osu.Game.Graphics.UserInterface } }; - nub.Current.BindTo(Current); + Nub.Current.BindTo(Current); Current.ValueChanged += newValue => { @@ -90,15 +91,15 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(InputState state) { - nub.Glowing = true; - nub.Expanded = true; + Nub.Glowing = true; + Nub.Expanded = true; return base.OnHover(state); } protected override void OnHoverLost(InputState state) { - nub.Glowing = false; - nub.Expanded = false; + Nub.Glowing = false; + Nub.Expanded = false; base.OnHoverLost(state); } diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs new file mode 100644 index 0000000000..e17ce2a5b2 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.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 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.UserInterface; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuContextMenu : ContextMenu + where TItem : ContextMenuItem + { + protected override Menu CreateMenu() => new CustomMenu(); + + public class CustomMenu : Menu + { + private const int fade_duration = 250; + + public CustomMenu() + { + CornerRadius = 5; + ItemsContainer.Padding = new MarginPadding { Vertical = OsuContextMenuItem.MARGIN_VERTICAL }; + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.1f), + Radius = 4, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Background.Colour = colours.ContextMenuGray; + } + + protected override void AnimateOpen() => FadeIn(fade_duration, EasingTypes.OutQuint); + protected override void AnimateClose() => FadeOut(fade_duration, EasingTypes.OutQuint); + + protected override void UpdateContentHeight() + { + var actualHeight = (RelativeSizeAxes & Axes.Y) > 0 ? 1 : ContentHeight; + ResizeTo(new Vector2(1, State == MenuState.Opened ? actualHeight : 0), 300, EasingTypes.OutQuint); + } + } + } +} \ No newline at end of file diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenuItem.cs b/osu.Game/Graphics/UserInterface/OsuContextMenuItem.cs new file mode 100644 index 0000000000..769df18566 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuContextMenuItem.cs @@ -0,0 +1,114 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuContextMenuItem : ContextMenuItem + { + private const int transition_length = 80; + private const int margin_horizontal = 17; + public const int MARGIN_VERTICAL = 4; + private const int text_size = 17; + + private OsuSpriteText text; + private OsuSpriteText textBold; + + private SampleChannel sampleClick; + private SampleChannel sampleHover; + + private readonly MenuItemType type; + + protected override Container CreateTextContainer(string title) => new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Children = new Drawable[] + { + text = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = text_size, + Text = title, + Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL }, + }, + textBold = new OsuSpriteText + { + AlwaysPresent = true, + Alpha = 0, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = text_size, + Text = title, + Font = @"Exo2.0-Bold", + Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL }, + } + } + }; + + public OsuContextMenuItem(string title, MenuItemType type = MenuItemType.Standard) : base(title) + { + this.type = type; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleHover = audio.Sample.Get(@"Menu/menuclick"); + sampleClick = audio.Sample.Get(@"Menu/menuback"); + + BackgroundColour = Color4.Transparent; + BackgroundColourHover = OsuColour.FromHex(@"172023"); + + updateTextColour(); + } + + private void updateTextColour() + { + switch (type) + { + case MenuItemType.Standard: + textBold.Colour = text.Colour = Color4.White; + break; + case MenuItemType.Destructive: + textBold.Colour = text.Colour = Color4.Red; + break; + case MenuItemType.Highlighted: + textBold.Colour = text.Colour = OsuColour.FromHex(@"ffcc22"); + break; + } + } + + protected override bool OnHover(InputState state) + { + sampleHover.Play(); + textBold.FadeIn(transition_length, EasingTypes.OutQuint); + text.FadeOut(transition_length, EasingTypes.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + textBold.FadeOut(transition_length, EasingTypes.OutQuint); + text.FadeIn(transition_length, EasingTypes.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + sampleClick.Play(); + return base.OnClick(state); + } + } +} \ No newline at end of file diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 14483f3bfb..6dadd63ac4 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -72,7 +72,7 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, }, - new OsuSpriteText { + Label = new OsuSpriteText { Text = text, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, @@ -85,6 +85,7 @@ namespace osu.Game.Graphics.UserInterface private Color4? accentColour; protected readonly TextAwesome Chevron; + protected readonly OsuSpriteText Label; protected override void FormatForeground(bool hover = false) { @@ -170,4 +171,4 @@ namespace osu.Game.Graphics.UserInterface } } } -} \ No newline at end of file +} diff --git a/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs b/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs index 5de6507bb3..bf835f0165 100644 --- a/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs @@ -16,7 +16,7 @@ namespace osu.Game.Graphics.UserInterface throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument"); List> items = new List>(); - foreach(var val in (T[])Enum.GetValues(typeof(T))) + foreach (var val in (T[])Enum.GetValues(typeof(T))) { var field = typeof(T).GetField(Enum.GetName(typeof(T), val)); items.Add( diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs index 904aa14aa7..dc52fd0f62 100644 --- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs @@ -3,9 +3,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Graphics.UserInterface { diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 6cf7b2dfa5..4f0ac28731 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -3,25 +3,27 @@ using System; using OpenTK; +using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Graphics.UserInterface { - public class OsuSliderBar : SliderBar, IHasTooltip + public class OsuSliderBar : SliderBar, IHasTooltip, IHasAccentColour where T : struct, IEquatable { private SampleChannel sample; private double lastSampleTime; private T lastSampleValue; - private readonly Nub nub; + protected readonly Nub Nub; private readonly Box leftBox; private readonly Box rightBox; @@ -45,6 +47,18 @@ namespace osu.Game.Graphics.UserInterface } } + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + leftBox.Colour = value; + rightBox.Colour = value; + } + } + public OsuSliderBar() { Height = 12; @@ -70,7 +84,7 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.CentreRight, Alpha = 0.5f, }, - nub = new Nub + Nub = new Nub { Origin = Anchor.TopCentre, Expanded = true, @@ -87,19 +101,18 @@ namespace osu.Game.Graphics.UserInterface private void load(AudioManager audio, OsuColour colours) { sample = audio.Sample.Get(@"Sliderbar/sliderbar"); - leftBox.Colour = colours.Pink; - rightBox.Colour = colours.Pink; + AccentColour = colours.Pink; } protected override bool OnHover(InputState state) { - nub.Glowing = true; + Nub.Glowing = true; return base.OnHover(state); } protected override void OnHoverLost(InputState state) { - nub.Glowing = false; + Nub.Glowing = false; base.OnHoverLost(state); } @@ -132,13 +145,13 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - nub.Current.Value = true; + Nub.Current.Value = true; return base.OnMouseDown(state, args); } protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - nub.Current.Value = false; + Nub.Current.Value = false; return base.OnMouseUp(state, args); } @@ -146,14 +159,14 @@ namespace osu.Game.Graphics.UserInterface { base.UpdateAfterChildren(); leftBox.Scale = new Vector2(MathHelper.Clamp( - nub.DrawPosition.X - nub.DrawWidth / 2, 0, DrawWidth), 1); + Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); rightBox.Scale = new Vector2(MathHelper.Clamp( - DrawWidth - nub.DrawPosition.X - nub.DrawWidth / 2, 0, DrawWidth), 1); + DrawWidth - Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); } protected override void UpdateValue(float value) { - nub.MoveToX(RangePadding + UsableWidth * value, 250, EasingTypes.OutQuint); + Nub.MoveToX(RangePadding + UsableWidth * value, 250, EasingTypes.OutQuint); } } } diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 4bbae4efd1..37bf30646d 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.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; @@ -74,21 +75,6 @@ namespace osu.Game.Graphics.UserInterface } } - public override bool Active - { - get { return base.Active; } - set - { - if (Active == value) return; - - if (value) - fadeActive(); - else - fadeInactive(); - base.Active = value; - } - } - private const float transition_length = 500; private void fadeActive() @@ -150,6 +136,10 @@ namespace osu.Game.Graphics.UserInterface } }; } + + protected override void OnActivated() => fadeActive(); + + protected override void OnDeactivated() => fadeInactive(); } private class OsuTabDropdown : OsuDropdown diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index 6fc3875f61..940d9b4943 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -6,10 +6,11 @@ using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; -using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface { diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs index 97c38f6b85..3512b4cdb1 100644 --- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs @@ -45,11 +45,10 @@ namespace osu.Game.Graphics.UserInterface BorderColour = colour.Yellow; } - protected override bool OnFocus(InputState state) + protected override void OnFocus(InputState state) { BorderThickness = 3; - - return base.OnFocus(state); + base.OnFocus(state); } protected override void OnFocusLost(InputState state) diff --git a/osu.Game/Overlays/Direct/SortTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs similarity index 67% rename from osu.Game/Overlays/Direct/SortTabControl.cs rename to osu.Game/Graphics/UserInterface/PageTabControl.cs index 4d4e02d875..7f2e6c9c8c 100644 --- a/osu.Game/Overlays/Direct/SortTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -7,46 +7,29 @@ using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; -using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Graphics.UserInterface { - public class SortTabControl : OsuTabControl + public class PageTabControl : OsuTabControl { - protected override TabItem CreateTabItem(SortCriteria value) => new SortTabItem(value); + protected override TabItem CreateTabItem(T value) => new PageTabItem(value); - public SortTabControl() + public PageTabControl() { Height = 30; } - private class SortTabItem : TabItem + private class PageTabItem : TabItem { private const float transition_duration = 100; private readonly Box box; - public override bool Active - { - get { return base.Active; } - set - { - if (Active == value) return; - - if (value) - slideActive(); - else - slideInactive(); - base.Active = value; - } - } - - public SortTabItem(SortCriteria value) : base(value) + public PageTabItem(T value) : base(value) { AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; @@ -102,16 +85,10 @@ namespace osu.Game.Overlays.Direct { box.ScaleTo(new Vector2(1f, 0f), transition_duration); } + + protected override void OnActivated() => slideActive(); + + protected override void OnDeactivated() => slideInactive(); } } - - public enum SortCriteria - { - Title, - Artist, - Creator, - Difficulty, - Ranked, - Rating, - } } diff --git a/osu.Game/Graphics/UserInterface/PercentageCounter.cs b/osu.Game/Graphics/UserInterface/PercentageCounter.cs index c32b654840..79cdc9effe 100644 --- a/osu.Game/Graphics/UserInterface/PercentageCounter.cs +++ b/osu.Game/Graphics/UserInterface/PercentageCounter.cs @@ -45,7 +45,7 @@ namespace osu.Game.Graphics.UserInterface Current.Value = Current + amount; } - protected class TransformAccuracy : Transform + protected class TransformAccuracy : Transform { public override double CurrentValue { diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index bdb2054ca4..4338dd23eb 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -27,7 +27,7 @@ namespace osu.Game.Graphics.UserInterface /// /// Must be a subclass of Transform(T) /// - protected virtual Type TransformType => typeof(Transform); + protected virtual Type TransformType => typeof(Transform); protected SpriteText DisplayedCountSpriteText; @@ -181,17 +181,17 @@ namespace osu.Game.Graphics.UserInterface protected virtual void TransformCount(T currentValue, T newValue) { Debug.Assert( - typeof(Transform).IsAssignableFrom(TransformType), + typeof(Transform).IsAssignableFrom(TransformType), @"transformType should be a subclass of Transform." ); - TransformCount((Transform)Activator.CreateInstance(TransformType), currentValue, newValue); + TransformCount((Transform)Activator.CreateInstance(TransformType), currentValue, newValue); } /// /// Intended to be used by TransformCount(T currentValue, T newValue). /// - protected void TransformCount(Transform transform, T currentValue, T newValue) + protected void TransformCount(Transform transform, T currentValue, T newValue) { Type type = transform.GetType(); diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index 3e01b9e4f4..f98e84852a 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -56,7 +56,7 @@ namespace osu.Game.Graphics.UserInterface Current.Value = Current + amount; } - protected class TransformScore : Transform + protected class TransformScore : Transform { public override double CurrentValue { diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index 0a37024d0f..39db8d8be7 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -45,6 +45,7 @@ namespace osu.Game.Graphics.UserInterface case Key.Up: case Key.Down: return false; + case Key.KeypadEnter: case Key.Enter: if (!AllowCommit) return false; break; diff --git a/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs b/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs index 8537e80f63..bee1a71894 100644 --- a/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs +++ b/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs @@ -37,7 +37,7 @@ namespace osu.Game.Graphics.UserInterface Current.Value = Current + amount; } - private class TransformCounterCount : Transform + private class TransformCounterCount : Transform { public override int CurrentValue { diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index ebaef661c4..b504c70be7 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Beatmaps.ControlPoints; using osu.Framework.Audio.Track; using System; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Graphics.UserInterface { @@ -62,8 +63,12 @@ namespace osu.Game.Graphics.UserInterface X = (value & Anchor.x2) > 0 ? SIZE_RETRACTED.X * shear * 0.5f : 0; + Remove(c1); + Remove(c2); c1.Depth = (value & Anchor.x2) > 0 ? 0 : 1; c2.Depth = (value & Anchor.x2) > 0 ? 1 : 0; + Add(c1); + Add(c2); } } @@ -79,18 +84,21 @@ namespace osu.Game.Graphics.UserInterface Width = 0.4f, Children = new Drawable[] { - new Container { + new Container + { RelativeSizeAxes = Axes.Both, Shear = new Vector2(shear, 0), Masking = true, MaskingSmoothness = 2, - EdgeEffect = new EdgeEffect { + EdgeEffect = new EdgeEffectParameters + { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.2f), Offset = new Vector2(2, 0), Radius = 2, }, - Children = new [] { + Children = new[] + { IconLayer = new Box { RelativeSizeAxes = Axes.Both, @@ -113,18 +121,21 @@ namespace osu.Game.Graphics.UserInterface Width = 0.6f, Children = new Drawable[] { - new Container { + new Container + { RelativeSizeAxes = Axes.Both, Shear = new Vector2(shear, 0), Masking = true, MaskingSmoothness = 2, - EdgeEffect = new EdgeEffect { + EdgeEffect = new EdgeEffectParameters + { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.2f), Offset = new Vector2(2, 0), Radius = 2, }, - Children = new [] { + Children = new[] + { TextLayer = new Box { Origin = Anchor.TopLeft, diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs index fd1d890330..57eea71086 100644 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs +++ b/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs @@ -4,11 +4,11 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Graphics.UserInterface.Volume { diff --git a/osu.Game/Online/API/OAuthToken.cs b/osu.Game/Online/API/OAuthToken.cs index 328888ab8a..1788adbf56 100644 --- a/osu.Game/Online/API/OAuthToken.cs +++ b/osu.Game/Online/API/OAuthToken.cs @@ -59,7 +59,6 @@ namespace osu.Game.Online.API { } - return null; } } diff --git a/osu.Game/Online/API/Requests/GetUsersRequest.cs b/osu.Game/Online/API/Requests/GetUsersRequest.cs new file mode 100644 index 0000000000..5fb8606e1e --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUsersRequest.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.Collections.Generic; +using Newtonsoft.Json; +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests +{ + public class GetUsersRequest : APIRequest> + { + protected override string Target => @"rankings/osu/performance"; + } + + public class RankingEntry + { + [JsonProperty] + public User User; + } +} diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 93fd0a8956..01685cc7dc 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; +using osu.Framework.Configuration; using osu.Framework.Lists; namespace osu.Game.Online.Chat @@ -25,7 +26,7 @@ namespace osu.Game.Online.Chat public readonly SortedList Messages = new SortedList(Comparer.Default); - //internal bool Joined; + public Bindable Joined = new Bindable(); public bool ReadOnly => Name != "#lazer"; diff --git a/osu.Game/Online/Multiplayer/GameType.cs b/osu.Game/Online/Multiplayer/GameType.cs index 2f4c92d289..4d1a6c4839 100644 --- a/osu.Game/Online/Multiplayer/GameType.cs +++ b/osu.Game/Online/Multiplayer/GameType.cs @@ -5,7 +5,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; namespace osu.Game.Online.Multiplayer diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2c952ee514..7e5b913d10 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -43,6 +43,8 @@ namespace osu.Game private DirectOverlay direct; + private SocialOverlay social; + private Intro intro { get @@ -75,8 +77,10 @@ namespace osu.Game public void ToggleDirect() => direct.ToggleVisibility(); [BackgroundDependencyLoader] - private void load() + private void load(FrameworkConfigManager frameworkConfig) { + this.frameworkConfig = frameworkConfig; + if (!Host.IsPrimaryInstance) { Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error); @@ -148,7 +152,7 @@ namespace osu.Game RelativeSizeAxes = Axes.Both, }, volume = new VolumeControl(), - overlayContent = new Container{ RelativeSizeAxes = Axes.Both }, + overlayContent = new Container { RelativeSizeAxes = Axes.Both }, new OnScreenDisplay(), new GlobalHotkeys //exists because UserInputManager is at a level below us. { @@ -165,6 +169,7 @@ namespace osu.Game //overlay elements LoadComponentAsync(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); + LoadComponentAsync(social = new SocialOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(settings = new SettingsOverlay { Depth = -1 }, overlayContent.Add); LoadComponentAsync(musicController = new MusicController @@ -198,11 +203,16 @@ namespace osu.Game }; Dependencies.Cache(settings); + Dependencies.Cache(social); Dependencies.Cache(chat); Dependencies.Cache(musicController); Dependencies.Cache(notificationManager); Dependencies.Cache(dialogOverlay); + // ensure both overlays aren't presented at the same time + chat.StateChanged += (container, state) => social.State = state == Visibility.Visible ? Visibility.Hidden : social.State; + social.StateChanged += (container, state) => chat.State = state == Visibility.Visible ? Visibility.Hidden : chat.State; + LoadComponentAsync(Toolbar = new Toolbar { Depth = -3, @@ -234,6 +244,9 @@ namespace osu.Game case Key.F8: chat.ToggleVisibility(); return true; + case Key.F9: + social.ToggleVisibility(); + return true; case Key.PageUp: case Key.PageDown: var swClock = (Clock as ThrottledFrameClock)?.Source as StopwatchClock; @@ -248,6 +261,19 @@ namespace osu.Game { switch (args.Key) { + case Key.R: + if (state.Keyboard.AltPressed) + { + var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); + + sensitivity.Disabled = false; + sensitivity.Value = 1; + sensitivity.Disabled = true; + + frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); + return true; + } + break; case Key.T: Toolbar.ToggleVisibility(); return true; @@ -273,6 +299,7 @@ namespace osu.Game private Container overlayContent; private OsuScreen currentScreen; + private FrameworkConfigManager frameworkConfig; private void screenChanged(Screen newScreen) { @@ -292,6 +319,7 @@ namespace osu.Game musicController.State = Visibility.Hidden; chat.State = Visibility.Hidden; direct.State = Visibility.Hidden; + social.State = Visibility.Hidden; } else { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 2c39a82245..306cdaddf0 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -158,7 +158,8 @@ namespace osu.Game Children = new Drawable[] { Cursor = new MenuCursor(), - new TooltipContainer(Cursor) { Depth = -1 }, + new OsuContextMenuContainer(Cursor) { Depth = -2 }, + new OsuTooltipContainer(Cursor) { Depth = -1 }, } }, } diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs new file mode 100644 index 0000000000..9aa11cdd4f --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelListItem.cs @@ -0,0 +1,188 @@ +// 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.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat +{ + public class ChannelListItem : ClickableContainer, IFilterable + { + private const float width_padding = 5; + private const float channel_width = 150; + private const float text_size = 15; + private const float transition_duration = 100; + + private readonly Channel channel; + + private readonly Bindable joinedBind = new Bindable(); + private readonly OsuSpriteText name; + private readonly OsuSpriteText topic; + private readonly TextAwesome joinedCheckmark; + + private Color4 joinedColour; + private Color4 topicColour; + private Color4 hoverColour; + + public string[] FilterTerms => new[] { channel.Name }; + public bool MatchingFilter + { + set + { + FadeTo(value ? 1f : 0f, 100); + } + } + + public Action OnRequestJoin; + public Action OnRequestLeave; + + public ChannelListItem(Channel channel) + { + this.channel = channel; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Action = () => { (channel.Joined ? OnRequestLeave : OnRequestJoin)?.Invoke(channel); }; + + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new Container + { + Children = new[] + { + joinedCheckmark = new TextAwesome + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Icon = FontAwesome.fa_check_circle, + TextSize = text_size, + Shadow = false, + Margin = new MarginPadding { Right = 10f }, + Alpha = 0f, + }, + }, + }, + new Container + { + Width = channel_width, + AutoSizeAxes = Axes.Y, + Children = new[] + { + name = new OsuSpriteText + { + Text = channel.ToString(), + TextSize = text_size, + Font = @"Exo2.0-Bold", + Shadow = false, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + Width = 0.7f, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Left = width_padding }, + Children = new[] + { + topic = new OsuSpriteText + { + Text = channel.Topic, + TextSize = text_size, + Font = @"Exo2.0-SemiBold", + Shadow = false, + Alpha = 0.8f, + }, + }, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Left = width_padding }, + Spacing = new Vector2(3f, 0f), + Children = new Drawable[] + { + new TextAwesome + { + Icon = FontAwesome.fa_user, + TextSize = text_size - 2, + Shadow = false, + Margin = new MarginPadding { Top = 1 }, + }, + new OsuSpriteText + { + Text = @"0", + TextSize = text_size, + Font = @"Exo2.0-SemiBold", + Shadow = false, + }, + }, + }, + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + topicColour = colours.Gray9; + joinedColour = colours.Blue; + hoverColour = colours.Yellow; + + joinedBind.ValueChanged += updateColour; + joinedBind.BindTo(channel.Joined); + } + + protected override bool OnHover(InputState state) + { + if (!channel.Joined.Value) + name.FadeColour(hoverColour, 50, EasingTypes.OutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (!channel.Joined.Value) + name.FadeColour(Color4.White, transition_duration); + } + + private void updateColour(bool joined) + { + if (joined) + { + name.FadeColour(Color4.White, transition_duration); + joinedCheckmark.FadeTo(1f, transition_duration); + topic.FadeTo(0.8f, transition_duration); + topic.FadeColour(Color4.White, transition_duration); + FadeColour(joinedColour, transition_duration); + } + else + { + joinedCheckmark.FadeTo(0f, transition_duration); + topic.FadeTo(1f, transition_duration); + topic.FadeColour(topicColour, transition_duration); + FadeColour(Color4.White, transition_duration); + } + } + } +} diff --git a/osu.Game/Overlays/Chat/ChannelSection.cs b/osu.Game/Overlays/Chat/ChannelSection.cs new file mode 100644 index 0000000000..f12ec53605 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelSection.cs @@ -0,0 +1,63 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat +{ + public class ChannelSection : Container, IHasFilterableChildren + { + private readonly OsuSpriteText header; + + public readonly FillFlowContainer ChannelFlow; + + public IEnumerable FilterableChildren => ChannelFlow.Children; + public string[] FilterTerms => new[] { Header }; + public bool MatchingFilter + { + set + { + FadeTo(value ? 1f : 0f, 100); + } + } + + public string Header + { + get { return header.Text; } + set { header.Text = value.ToUpper(); } + } + + public IEnumerable Channels + { + set { ChannelFlow.Children = value.Select(c => new ChannelListItem(c)); } + } + + public ChannelSection() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + header = new OsuSpriteText + { + TextSize = 15, + Font = @"Exo2.0-Bold", + }, + ChannelFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 25 }, + Spacing = new Vector2(0f, 5f), + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs new file mode 100644 index 0000000000..6ff3cc7be5 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs @@ -0,0 +1,184 @@ +// 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 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.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat +{ + public class ChannelSelectionOverlay : FocusedOverlayContainer + { + public static readonly float WIDTH_PADDING = 170; + + private const float transition_duration = 500; + + private readonly Box bg; + private readonly Triangles triangles; + private readonly Box headerBg; + private readonly SearchTextBox search; + private readonly SearchContainer sectionsFlow; + + public Action OnRequestJoin; + public Action OnRequestLeave; + + public IEnumerable Sections + { + set + { + sectionsFlow.Children = value; + + foreach (ChannelSection s in sectionsFlow.Children) + { + foreach (ChannelListItem c in s.ChannelFlow.Children) + { + c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); }; + c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); }; + } + } + } + } + + public ChannelSelectionOverlay() + { + RelativeSizeAxes = Axes.X; + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] + { + bg = new Box + { + RelativeSizeAxes = Axes.Both, + }, + triangles = new Triangles + { + RelativeSizeAxes = Axes.Both, + TriangleScale = 5, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 85, Right = WIDTH_PADDING }, + Children = new[] + { + new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + sectionsFlow = new SearchContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + LayoutDuration = 200, + LayoutEasing = EasingTypes.OutQuint, + Spacing = new Vector2(0f, 20f), + Padding = new MarginPadding { Vertical = 20, Left = WIDTH_PADDING }, + }, + }, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + headerBg = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 10f), + Padding = new MarginPadding { Top = 10f, Bottom = 10f, Left = WIDTH_PADDING, Right = WIDTH_PADDING }, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"Chat Channels", + TextSize = 20, + Shadow = false, + }, + search = new HeaderSearchTextBox + { + RelativeSizeAxes = Axes.X, + PlaceholderText = @"Search", + Exit = Hide, + }, + }, + }, + }, + }, + }; + + search.Current.ValueChanged += newValue => sectionsFlow.SearchTerm = newValue; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + bg.Colour = colours.Gray3; + triangles.ColourDark = colours.Gray3; + triangles.ColourLight = OsuColour.FromHex(@"353535"); + + headerBg.Colour = colours.Gray2.Opacity(0.75f); + } + + protected override void OnFocus(InputState state) + { + InputManager.ChangeFocus(search); + base.OnFocus(state); + } + + protected override void PopIn() + { + if (Alpha == 0) MoveToY(DrawHeight); + + FadeIn(transition_duration, EasingTypes.OutQuint); + MoveToY(0, transition_duration, EasingTypes.OutQuint); + + search.HoldFocus = true; + base.PopIn(); + } + + protected override void PopOut() + { + FadeOut(transition_duration, EasingTypes.InSine); + MoveToY(DrawHeight, transition_duration, EasingTypes.InSine); + + search.HoldFocus = false; + base.PopOut(); + } + + private class HeaderSearchTextBox : SearchTextBox + { + protected override Color4 BackgroundFocused => Color4.Black.Opacity(0.2f); + protected override Color4 BackgroundUnfocused => Color4.Black.Opacity(0.2f); + } + } +} diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index a281cff7db..92147db57f 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -5,6 +5,7 @@ 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.Graphics.UserInterface; using osu.Framework.Input; @@ -26,6 +27,8 @@ namespace osu.Game.Overlays.Chat public readonly Bindable ChannelSelectorActive = new Bindable(); + private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab; + public ChatTabControl() { TabContainer.Margin = new MarginPadding { Left = 50 }; @@ -41,7 +44,22 @@ namespace osu.Game.Overlays.Chat Padding = new MarginPadding(10), }); - AddTabItem(new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" }, ChannelSelectorActive)); + AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" })); + + ChannelSelectorActive.BindTo(selectorTab.Active); + } + + protected override void SelectTab(TabItem tab) + { + if (tab is ChannelTabItem.ChannelSelectorTabItem) + { + tab.Active.Toggle(); + return; + } + + selectorTab.Active.Value = false; + + base.SelectTab(tab); } private class ChannelTabItem : TabItem @@ -56,18 +74,6 @@ namespace osu.Game.Overlays.Chat private readonly Box highlightBox; private readonly TextAwesome icon; - public override bool Active - { - get { return base.Active; } - set - { - if (Active == value) return; - - base.Active = value; - updateState(); - } - } - private void updateState() { if (Active) @@ -141,7 +147,7 @@ namespace osu.Game.Overlays.Chat Shear = new Vector2(shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0); Masking = true; - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Radius = 10, @@ -205,21 +211,8 @@ namespace osu.Game.Overlays.Chat public class ChannelSelectorTabItem : ChannelTabItem { - public override bool Active + public ChannelSelectorTabItem(Channel value) : base(value) { - get { return base.Active; } - set - { - activeBindable.Value = value; - base.Active = value; - } - } - - private readonly Bindable activeBindable; - - public ChannelSelectorTabItem(Channel value, Bindable active) : base(value) - { - activeBindable = active; Depth = float.MaxValue; Width = 45; @@ -236,6 +229,10 @@ namespace osu.Game.Overlays.Chat backgroundActive = colour.Gray3; } } + + protected override void OnActivated() => updateState(); + + protected override void OnDeactivated() => updateState(); } } } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 50c849f00e..e8bde11a3c 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.Chat; @@ -43,11 +44,15 @@ namespace osu.Game.Overlays.Chat channel.NewMessagesArrived += newMessagesArrived; } + [BackgroundDependencyLoader] + private void load() + { + newMessagesArrived(Channel.Messages); + } + protected override void LoadComplete() { base.LoadComplete(); - - newMessagesArrived(Channel.Messages); scrollToEnd(); } @@ -59,13 +64,13 @@ namespace osu.Game.Overlays.Chat private void newMessagesArrived(IEnumerable newMessages) { - if (!IsLoaded) return; - var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY)); //up to last Channel.MAX_HISTORY messages flow.Add(displayMessages.Select(m => new ChatLine(m))); + if (!IsLoaded) return; + if (scroll.IsScrolledToEnd(10) || !flow.Children.Any()) scrollToEnd(); @@ -81,6 +86,6 @@ namespace osu.Game.Overlays.Chat } } - private void scrollToEnd() => Scheduler.AddDelayed(() => scroll.ScrollToEnd(), 50); + private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd()); } } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 5b2c01151c..23b8aac6a7 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -10,9 +10,8 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Threading; -using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; @@ -29,10 +28,13 @@ namespace osu.Game.Overlays public class ChatOverlay : FocusedOverlayContainer, IOnlineComponent { private const float textbox_height = 60; + private const float channel_selection_min_height = 0.3f; private ScheduledDelegate messageRequest; - private readonly Container currentChannelContainer; + private readonly Container currentChannelContainer; + + private readonly LoadingAnimation loading; private readonly FocusedTextBox inputTextBox; @@ -48,16 +50,21 @@ namespace osu.Game.Overlays private readonly ChatTabControl channelTabs; + private readonly Container chatContainer; private readonly Box chatBackground; private readonly Box tabBackground; private Bindable chatHeight; + private readonly Container channelSelectionContainer; + private readonly ChannelSelectionOverlay channelSelection; + + protected override bool InternalContains(Vector2 screenSpacePos) => chatContainer.Contains(screenSpacePos) || channelSelection.State == Visibility.Visible && channelSelection.Contains(screenSpacePos); + public ChatOverlay() { RelativeSizeAxes = Axes.Both; RelativePositionAxes = Axes.Both; - Size = new Vector2(1, DEFAULT_HEIGHT); Anchor = Anchor.BottomLeft; Origin = Anchor.BottomLeft; @@ -65,74 +72,121 @@ namespace osu.Game.Overlays Children = new Drawable[] { - new Container + channelSelectionContainer = new Container { - Name = @"chat area", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = TAB_AREA_HEIGHT }, - Children = new Drawable[] + Height = 1f - DEFAULT_HEIGHT, + Masking = true, + Children = new[] { - chatBackground = new Box + channelSelection = new ChannelSelectionOverlay { RelativeSizeAxes = Axes.Both, }, - currentChannelContainer = new Container + }, + }, + chatContainer = new Container + { + Name = @"chat container", + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Height = DEFAULT_HEIGHT, + Children = new[] + { + new Container { + Name = @"chat area", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding + Padding = new MarginPadding { Top = TAB_AREA_HEIGHT }, + Children = new Drawable[] { - Bottom = textbox_height + padding - }, + chatBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + currentChannelContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Bottom = textbox_height + padding + }, + }, + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = textbox_height, + Padding = new MarginPadding + { + Top = padding * 2, + Bottom = padding * 2, + Left = ChatLine.LEFT_PADDING + padding * 2, + Right = padding * 2, + }, + Children = new Drawable[] + { + inputTextBox = new FocusedTextBox + { + RelativeSizeAxes = Axes.Both, + Height = 1, + PlaceholderText = "type your message", + Exit = () => State = Visibility.Hidden, + OnCommit = postMessage, + ReleaseFocusOnCommit = false, + HoldFocus = true, + } + } + }, + loading = new LoadingAnimation(), + } }, new Container { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, + Name = @"tabs area", RelativeSizeAxes = Axes.X, - Height = textbox_height, - Padding = new MarginPadding - { - Top = padding * 2, - Bottom = padding * 2, - Left = ChatLine.LEFT_PADDING + padding * 2, - Right = padding * 2, - }, + Height = TAB_AREA_HEIGHT, Children = new Drawable[] { - inputTextBox = new FocusedTextBox + tabBackground = new Box { RelativeSizeAxes = Axes.Both, - Height = 1, - PlaceholderText = "type your message", - Exit = () => State = Visibility.Hidden, - OnCommit = postMessage, - HoldFocus = true, - } + Colour = Color4.Black, + }, + channelTabs = new ChatTabControl + { + RelativeSizeAxes = Axes.Both, + }, } - } - } - }, - new Container - { - Name = @"tabs area", - RelativeSizeAxes = Axes.X, - Height = TAB_AREA_HEIGHT, - Children = new Drawable[] - { - tabBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, }, - channelTabs = new ChatTabControl - { - RelativeSizeAxes = Axes.Both, - }, - } + }, }, }; channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel; + channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; + channelSelection.StateChanged += (overlay, state) => + { + channelTabs.ChannelSelectorActive.Value = state == Visibility.Visible; + + if (state == Visibility.Visible) + { + inputTextBox.HoldFocus = false; + if (1f - chatHeight.Value < channel_selection_min_height) + { + chatContainer.ResizeHeightTo(1f - channel_selection_min_height, 800, EasingTypes.OutQuint); + channelSelectionContainer.ResizeHeightTo(channel_selection_min_height, 800, EasingTypes.OutQuint); + channelSelection.Show(); + chatHeight.Value = 1f - channel_selection_min_height; + } + } + else + { + inputTextBox.HoldFocus = true; + } + }; } private double startDragChatHeight; @@ -167,11 +221,15 @@ namespace osu.Game.Overlays } } - protected override bool OnFocus(InputState state) + public override bool AcceptsFocus => true; + + protected override bool OnClick(InputState state) => true; + + protected override void OnFocus(InputState state) { //this is necessary as inputTextBox is masked away and therefore can't get focus :( InputManager.ChangeFocus(inputTextBox); - return false; + base.OnFocus(state); } protected override void PopIn() @@ -201,8 +259,9 @@ namespace osu.Game.Overlays chatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); chatHeight.ValueChanged += h => { - Height = (float)h; - tabBackground.FadeTo(Height == 1 ? 1 : 0.8f, 200); + chatContainer.Height = (float)h; + channelSelectionContainer.Height = 1f - (float)h; + tabBackground.FadeTo(h == 1 ? 1 : 0.8f, 200); }; chatHeight.TriggerChange(); @@ -217,14 +276,7 @@ namespace osu.Game.Overlays private void initializeChannels() { - SpriteText loading; - Add(loading = new OsuSpriteText - { - Text = @"initialising chat...", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - TextSize = 40, - }); + loading.Show(); messageRequest?.Cancel(); @@ -233,12 +285,19 @@ namespace osu.Game.Overlays { Scheduler.Add(delegate { - loading.FadeOut(100); - loading.Expire(); - addChannel(channels.Find(c => c.Name == @"#lazer")); addChannel(channels.Find(c => c.Name == @"#osu")); addChannel(channels.Find(c => c.Name == @"#lobby")); + + channelSelection.OnRequestJoin = addChannel; + channelSelection.Sections = new[] + { + new ChannelSection + { + Header = "All Channels", + Channels = channels, + }, + }; }); messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true); @@ -258,24 +317,36 @@ namespace osu.Game.Overlays set { - if (currentChannel == value) return; - - if (channelTabs.ChannelSelectorActive) return; - - if (currentChannel != null) - currentChannelContainer.Clear(false); + if (currentChannel == value || value == null) return; currentChannel = value; + inputTextBox.Current.Disabled = currentChannel.ReadOnly; + channelTabs.Current.Value = value; + var loaded = loadedChannels.Find(d => d.Channel == value); if (loaded == null) - loadedChannels.Add(loaded = new DrawableChannel(currentChannel)); + { + currentChannelContainer.FadeOut(500, EasingTypes.OutQuint); + loading.Show(); - inputTextBox.Current.Disabled = currentChannel.ReadOnly; + loaded = new DrawableChannel(currentChannel); + loadedChannels.Add(loaded); + LoadComponentAsync(loaded, l => + { + if (currentChannel.Messages.Any()) + loading.Hide(); - currentChannelContainer.Add(loaded); - - channelTabs.Current.Value = value; + currentChannelContainer.Clear(false); + currentChannelContainer.Add(loaded); + currentChannelContainer.FadeIn(500, EasingTypes.OutQuint); + }); + } + else + { + currentChannelContainer.Clear(false); + currentChannelContainer.Add(loaded); + } } } @@ -292,7 +363,6 @@ namespace osu.Game.Overlays } else { - careChannels.Add(channel); channelTabs.AddItem(channel); } @@ -302,6 +372,8 @@ namespace osu.Game.Overlays if (CurrentChannel == null) CurrentChannel = channel; + + channel.Joined.Value = true; } private void fetchInitialMessages(Channel channel) @@ -310,6 +382,7 @@ namespace osu.Game.Overlays req.Success += delegate (List messages) { + loading.Hide(); channel.AddNewMessages(messages.ToArray()); Debug.Write("success!"); }; diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 42819f7f87..c8ca50823f 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays.Dialog { @@ -78,7 +79,7 @@ namespace osu.Game.Overlays.Dialog { if (args.Repeat) return false; - if (args.Key == Key.Enter) + if (args.Key == Key.Enter || args.Key == Key.KeypadEnter) { Buttons.OfType().FirstOrDefault()?.TriggerOnClick(); return true; @@ -145,7 +146,7 @@ namespace osu.Game.Overlays.Dialog { RelativeSizeAxes = Axes.Both, Masking = true, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.5f), diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 9454272728..757781a4ab 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -4,9 +4,9 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Overlays.Dialog; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays { @@ -29,7 +29,7 @@ namespace osu.Game.Overlays State = Visibility.Visible; } - private void onDialogOnStateChanged(OverlayContainer dialog, Visibility v) + private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v) { if (v != Visibility.Hidden) return; diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 4be68157d7..a670b27540 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -8,12 +8,12 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays.Direct { @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Direct CornerRadius = 4; Masking = true; - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Offset = new Vector2(0f, 1f), diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 48636a5228..2cede1e574 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -6,7 +6,6 @@ using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -16,6 +15,7 @@ using osu.Framework.Localisation; using osu.Framework.Graphics.Textures; using System.Linq; using osu.Framework.Input; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays.Direct { @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Direct Height = height; CornerRadius = 5; Masking = true; - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Offset = new Vector2(0f, 1f), diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 735e14b8c1..455d0ab77b 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -5,117 +5,35 @@ using OpenTK; using OpenTK.Graphics; 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.Sprites; using osu.Game.Database; using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.SearchableList; namespace osu.Game.Overlays.Direct { - public class FilterControl : Container + public class FilterControl : SearchableListFilterControl { - public static readonly float HEIGHT = 35 + 32 + 30 + padding * 2; // search + mode toggle buttons + sort tabs + padding + private FillFlowContainer modeButtons; - private const float padding = 10; - - private readonly Box tabStrip; - private readonly FillFlowContainer modeButtons; - - public readonly SearchTextBox Search; - public readonly SortTabControl SortTabs; - public readonly OsuEnumDropdown RankStatusDropdown; - public readonly Bindable DisplayStyle = new Bindable(); - - protected override bool InternalContains(Vector2 screenSpacePos) => base.InternalContains(screenSpacePos) || RankStatusDropdown.Contains(screenSpacePos); - - public FilterControl() + protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552"); + protected override DirectSortCritera DefaultTab => DirectSortCritera.Title; + protected override Drawable CreateSupplementaryControls() { - RelativeSizeAxes = Axes.X; - Height = HEIGHT; - DisplayStyle.Value = DirectOverlay.PanelDisplayStyle.Grid; - - Children = new Drawable[] + modeButtons = new FillFlowContainer { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"384552"), - Alpha = 0.9f, - }, - tabStrip = new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft, - RelativeSizeAxes = Axes.X, - Height = 1, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = DirectOverlay.WIDTH_PADDING, Right = DirectOverlay.WIDTH_PADDING }, - Children = new Drawable[] - { - Search = new DirectSearchTextBox - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = padding }, - }, - modeButtons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(padding, 0f), - Margin = new MarginPadding { Top = padding }, - }, - SortTabs = new SortTabControl - { - RelativeSizeAxes = Axes.X, - }, - }, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Spacing = new Vector2(10f, 0f), - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Top = HEIGHT - SlimEnumDropdown.HEIGHT - padding, Right = DirectOverlay.WIDTH_PADDING }, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5f, 0f), - Direction = FillDirection.Horizontal, - Children = new[] - { - new DisplayStyleToggleButton(FontAwesome.fa_th_large, DirectOverlay.PanelDisplayStyle.Grid, DisplayStyle), - new DisplayStyleToggleButton(FontAwesome.fa_list_ul, DirectOverlay.PanelDisplayStyle.List, DisplayStyle), - }, - }, - RankStatusDropdown = new SlimEnumDropdown - { - RelativeSizeAxes = Axes.None, - Width = 160f, - }, - }, - }, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(10f, 0f), }; - RankStatusDropdown.Current.Value = RankStatus.RankedApproved; - SortTabs.Current.Value = SortCriteria.Title; - SortTabs.Current.TriggerChange(); + return modeButtons; } [BackgroundDependencyLoader(true)] private void load(OsuGame game, RulesetDatabase rulesets, OsuColour colours) { - tabStrip.Colour = colours.Yellow; - RankStatusDropdown.AccentColour = colours.BlueDark; + DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; var b = new Bindable(); //backup bindable incase the game is null foreach (var r in rulesets.AllRulesets) @@ -124,20 +42,6 @@ namespace osu.Game.Overlays.Direct } } - private class DirectSearchTextBox : SearchTextBox - { - protected override Color4 BackgroundUnfocused => backgroundColour; - protected override Color4 BackgroundFocused => backgroundColour; - - private Color4 backgroundColour; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - backgroundColour = colours.Gray2.Opacity(0.9f); - } - } - private class RulesetToggleButton : ClickableContainer { private readonly TextAwesome icon; @@ -188,46 +92,15 @@ namespace osu.Game.Overlays.Direct base.Dispose(isDisposing); } } + } - private class DisplayStyleToggleButton : ClickableContainer - { - private readonly TextAwesome icon; - private readonly DirectOverlay.PanelDisplayStyle style; - private readonly Bindable bindable; - - public DisplayStyleToggleButton(FontAwesome icon, DirectOverlay.PanelDisplayStyle style, Bindable bindable) - { - this.bindable = bindable; - this.style = style; - Size = new Vector2(SlimEnumDropdown.HEIGHT); - - Children = new Drawable[] - { - this.icon = new TextAwesome - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = icon, - TextSize = 18, - UseFullGlyphHeight = false, - Alpha = 0.5f, - }, - }; - - bindable.ValueChanged += Bindable_ValueChanged; - Bindable_ValueChanged(bindable.Value); - Action = () => bindable.Value = this.style; - } - - private void Bindable_ValueChanged(DirectOverlay.PanelDisplayStyle style) - { - icon.FadeTo(style == this.style ? 1.0f : 0.5f, 100); - } - - protected override void Dispose(bool isDisposing) - { - bindable.ValueChanged -= Bindable_ValueChanged; - } - } + public enum DirectSortCritera + { + Title, + Artist, + Creator, + Difficulty, + Ranked, + Rating, } } diff --git a/osu.Game/Overlays/Direct/Header.cs b/osu.Game/Overlays/Direct/Header.cs index 8e4ede48d5..000ef473cc 100644 --- a/osu.Game/Overlays/Direct/Header.cs +++ b/osu.Game/Overlays/Direct/Header.cs @@ -2,113 +2,28 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.ComponentModel; -using OpenTK; using OpenTK.Graphics; -using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; - -using Container = osu.Framework.Graphics.Containers.Container; +using osu.Game.Overlays.SearchableList; namespace osu.Game.Overlays.Direct { - public class Header : Container + public class Header : SearchableListHeader { - public static readonly float HEIGHT = 90; + protected override Color4 BackgroundColour => OsuColour.FromHex(@"252f3a"); + protected override float TabStripWidth => 298; - private readonly Box tabStrip; - - public readonly OsuTabControl Tabs; + protected override DirectTab DefaultTab => DirectTab.Search; + protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", TextSize = 25 }; + protected override FontAwesome Icon => FontAwesome.fa_osu_chevron_down_o; public Header() { - Height = HEIGHT; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"252f3a"), - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = DirectOverlay.WIDTH_PADDING, Right = DirectOverlay.WIDTH_PADDING }, - Children = new Drawable[] - { - new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.BottomLeft, - Position = new Vector2(-35f, 5f), - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10f, 0f), - Children = new Drawable[] - { - new TextAwesome - { - TextSize = 25, - Icon = FontAwesome.fa_osu_chevron_down_o, - }, - new OsuSpriteText - { - TextSize = 25, - Text = @"osu!direct", - }, - }, - }, - tabStrip = new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Width = 282, //todo: make this actually match the tab control's width instead of hardcoding - Height = 1, - }, - Tabs = new DirectTabControl - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - }, - }, - }, - }; - Tabs.Current.Value = DirectTab.Search; Tabs.Current.TriggerChange(); } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - tabStrip.Colour = colours.Green; - } - - private class DirectTabControl : OsuTabControl - { - protected override TabItem CreateTabItem(DirectTab value) => new DirectTabItem(value); - - public DirectTabControl() - { - Height = 25; - AccentColour = Color4.White; - } - - private class DirectTabItem : OsuTabItem - { - public DirectTabItem(DirectTab value) : base(value) - { - Text.TextSize = 15; - } - } - } } public enum DirectTab diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index b1bc7e0c04..93c440384b 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -7,28 +7,30 @@ using OpenTK; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; using osu.Game.Database; using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Direct; - -using Container = osu.Framework.Graphics.Containers.Container; +using osu.Game.Overlays.SearchableList; +using OpenTK.Graphics; namespace osu.Game.Overlays { - public class DirectOverlay : WaveOverlayContainer + public class DirectOverlay : SearchableListOverlay { - public static readonly int WIDTH_PADDING = 80; private const float panel_padding = 10f; - private readonly FilterControl filter; private readonly FillFlowContainer resultCountsContainer; private readonly OsuSpriteText resultCountsText; private readonly FillFlowContainer panels; + protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74"); + protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71"); + protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265"); + + protected override SearchableListHeader CreateHeader() => new Header(); + protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); + private IEnumerable beatmapSets; public IEnumerable BeatmapSets { @@ -38,7 +40,7 @@ namespace osu.Game.Overlays if (beatmapSets?.Equals(value) ?? false) return; beatmapSets = value; - recreatePanels(filter.DisplayStyle.Value); + recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); } } @@ -66,96 +68,39 @@ namespace osu.Game.Overlays ThirdWaveColour = OsuColour.FromHex(@"005774"); FourthWaveColour = OsuColour.FromHex(@"003a4e"); - Header header; - Children = new Drawable[] + ScrollFlow.Children = new Drawable[] { - new Box + resultCountsContainer = new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"485e74"), - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new[] + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Top = 5 }, + Children = new Drawable[] { - new Triangles + new OsuSpriteText { - RelativeSizeAxes = Axes.Both, - TriangleScale = 5, - ColourLight = OsuColour.FromHex(@"465b71"), - ColourDark = OsuColour.FromHex(@"3f5265"), + Text = "Found ", + TextSize = 15, }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = Header.HEIGHT + FilterControl.HEIGHT }, - Children = new[] - { - new ScrollContainer + resultCountsText = new OsuSpriteText { - RelativeSizeAxes = Axes.Both, - ScrollDraggerVisible = false, - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - resultCountsContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Left = WIDTH_PADDING, Top = 6 }, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "Found ", - TextSize = 15, - }, - resultCountsText = new OsuSpriteText - { - TextSize = 15, - Font = @"Exo2.0-Bold", - }, - } - }, - panels = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Top = panel_padding, Bottom = panel_padding, Left = WIDTH_PADDING, Right = WIDTH_PADDING }, - Spacing = new Vector2(panel_padding), - }, - }, - }, - }, + TextSize = 15, + Font = @"Exo2.0-Bold", }, - }, + } }, - filter = new FilterControl - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = Header.HEIGHT }, - }, - header = new Header + panels = new FillFlowContainer { RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(panel_padding), + Margin = new MarginPadding { Top = 10 }, }, }; - header.Tabs.Current.ValueChanged += tab => { if (tab != DirectTab.Search) filter.Search.Current.Value = string.Empty; }; - - filter.Search.Exit = Hide; - filter.Search.Current.ValueChanged += text => { if (text != string.Empty) header.Tabs.Current.Value = DirectTab.Search; }; - filter.DisplayStyle.ValueChanged += recreatePanels; + Header.Tabs.Current.ValueChanged += tab => { if (tab != DirectTab.Search) Filter.Search.Text = string.Empty; }; + Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; }; + Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels; updateResultCounts(); } @@ -187,26 +132,6 @@ namespace osu.Game.Overlays panels.Children = BeatmapSets.Select(b => displayStyle == PanelDisplayStyle.Grid ? (DirectPanel)new DirectGridPanel(b) { Width = 400 } : new DirectListPanel(b)); } - protected override bool OnFocus(InputState state) - { - InputManager.ChangeFocus(filter.Search); - return false; - } - - protected override void PopIn() - { - base.PopIn(); - - filter.Search.HoldFocus = true; - } - - protected override void PopOut() - { - base.PopOut(); - - filter.Search.HoldFocus = false; - } - public class ResultCounts { public readonly int Artists; @@ -220,11 +145,5 @@ namespace osu.Game.Overlays Tags = tags; } } - - public enum PanelDisplayStyle - { - Grid, - List, - } } } diff --git a/osu.Game/Overlays/DragBar.cs b/osu.Game/Overlays/DragBar.cs index bb28f08553..07e0c76396 100644 --- a/osu.Game/Overlays/DragBar.cs +++ b/osu.Game/Overlays/DragBar.cs @@ -4,10 +4,10 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Transforms; using osu.Framework.Input; using OpenTK; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays { @@ -98,7 +98,7 @@ namespace osu.Game.Overlays return true; } - private class TransformSeek : TransformFloat + private class TransformSeek : TransformFloat { public override void Apply(Drawable d) { diff --git a/osu.Game/Overlays/LoginOverlay.cs b/osu.Game/Overlays/LoginOverlay.cs index c3f41270ce..b688bafd4d 100644 --- a/osu.Game/Overlays/LoginOverlay.cs +++ b/osu.Game/Overlays/LoginOverlay.cs @@ -4,10 +4,10 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Overlays.Settings.Sections.General; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays { @@ -27,7 +27,8 @@ namespace osu.Game.Overlays { Children = new Drawable[] { - new Box { + new Box + { RelativeSizeAxes = Axes.Both, Colour = Color4.Black, Alpha = 0.6f, diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index f7df67b66d..8a04d91cfb 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -16,11 +16,10 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using System; using System.Linq; -using osu.Game.Graphics; +using osu.Framework.Graphics.Cursor; namespace osu.Game.Overlays.Mods { - /// /// Represents a clickable button which can cycle through one of more mods. /// @@ -80,7 +79,7 @@ namespace osu.Game.Overlays.Mods backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); backgroundIcon.Icon = modAfter.Icon; - using (iconsContainer.BeginDelayedSequence(mod_switch_duration, true)) + using (BeginDelayedSequence(mod_switch_duration, true)) { foregroundIcon.RotateTo(-rotate_angle * direction); foregroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing); @@ -88,7 +87,7 @@ namespace osu.Game.Overlays.Mods backgroundIcon.RotateTo(rotate_angle * direction); backgroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing); - iconsContainer.Schedule(() => displayMod(modAfter)); + Schedule(() => displayMod(modAfter)); } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index c001d613e4..ca74189c86 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -8,7 +8,6 @@ using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; @@ -17,6 +16,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Database; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays.Mods { diff --git a/osu.Game/Overlays/Music/CollectionsDropdown.cs b/osu.Game/Overlays/Music/CollectionsDropdown.cs new file mode 100644 index 0000000000..f6016fd1db --- /dev/null +++ b/osu.Game/Overlays/Music/CollectionsDropdown.cs @@ -0,0 +1,73 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +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.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Music +{ + public class CollectionsDropdown : OsuDropdown + { + protected override DropdownHeader CreateHeader() => new CollectionsHeader { AccentColour = AccentColour }; + protected override Menu CreateMenu() => new CollectionsMenu(); + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Gray6; + } + + private class CollectionsHeader : OsuDropdownHeader + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Gray4; + } + + public CollectionsHeader() + { + CornerRadius = 5; + Height = 30; + Icon.TextSize = 14; + Icon.Margin = new MarginPadding(0); + Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 10, Right = 10 }; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.3f), + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + } + } + + private class CollectionsMenu : OsuMenu + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Background.Colour = colours.Gray4; + } + + public CollectionsMenu() + { + CornerRadius = 5; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.3f), + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + } + } + } +} diff --git a/osu.Game/Overlays/Music/FilterControl.cs b/osu.Game/Overlays/Music/FilterControl.cs index 6d8790a9e8..56cd6e864b 100644 --- a/osu.Game/Overlays/Music/FilterControl.cs +++ b/osu.Game/Overlays/Music/FilterControl.cs @@ -3,10 +3,8 @@ using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; @@ -74,63 +72,5 @@ namespace osu.Game.Overlays.Music backgroundColour = colours.Gray2; } } - - private class CollectionsDropdown : OsuDropdown - { - protected override DropdownHeader CreateHeader() => new CollectionsHeader { AccentColour = AccentColour }; - protected override Menu CreateMenu() => new CollectionsMenu(); - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Gray6; - } - - private class CollectionsHeader : OsuDropdownHeader - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Gray4; - } - - public CollectionsHeader() - { - CornerRadius = 5; - Height = 30; - Icon.TextSize = 14; - Icon.Margin = new MarginPadding(0); - Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 10, Right = 10 }; - EdgeEffect = new EdgeEffect - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.3f), - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - } - } - - private class CollectionsMenu : OsuMenu - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Background.Colour = colours.Gray4; - } - - public CollectionsMenu() - { - CornerRadius = 5; - EdgeEffect = new EdgeEffect - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.3f), - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - } - } - } } } diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 9b72cfce42..69cb9c3464 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Music private Color4 artistColour; private TextAwesome handle; - private Paragraph text; + private TextFlowContainer text; private IEnumerable titleSprites; private UnicodeBindableString titleBind; private UnicodeBindableString artistBind; @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Music Margin = new MarginPadding { Left = 5 }, Padding = new MarginPadding { Top = 2 }, }, - text = new Paragraph + text = new TextFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -135,8 +135,9 @@ namespace osu.Game.Overlays.Music private bool matching = true; - public bool MatchingCurrentFilter + public bool MatchingFilter { + get { return matching; } set { if (matching == value) return; @@ -145,10 +146,6 @@ namespace osu.Game.Overlays.Music FadeTo(matching ? 1 : 0, 200); } - get - { - return matching; - } } } } diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index ffe59a9d93..eeb072fb00 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Music } } - public BeatmapSetInfo FirstVisibleSet => items.Children.FirstOrDefault(i => i.MatchingCurrentFilter)?.BeatmapSetInfo; + public BeatmapSetInfo FirstVisibleSet => items.Children.FirstOrDefault(i => i.MatchingFilter)?.BeatmapSetInfo; private void itemSelected(BeatmapSetInfo b) { @@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Music private class ItemSearchContainer : FillFlowContainer, IHasFilterableChildren { public string[] FilterTerms => new string[] { }; - public bool MatchingCurrentFilter + public bool MatchingFilter { set { diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 82596252b3..e5dc66fc75 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -10,7 +10,6 @@ using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Graphics; @@ -18,6 +17,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Extensions; using osu.Framework.Input; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays.Music { @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Music RelativeSizeAxes = Axes.Both, CornerRadius = 5, Masking = true, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(40), diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index f1be55801f..e1e920ec0f 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -22,6 +22,8 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Threading; using osu.Game.Overlays.Music; +using osu.Game.Graphics.UserInterface; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays { @@ -38,8 +40,8 @@ namespace osu.Game.Overlays private Drawable currentBackground; private DragBar progressBar; - private Button playButton; - private Button playlistButton; + private IconButton playButton; + private IconButton playlistButton; private SpriteText title, artist; @@ -105,7 +107,7 @@ namespace osu.Game.Overlays Height = player_height, Masking = true, CornerRadius = 5, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(40), @@ -143,7 +145,7 @@ namespace osu.Game.Overlays Anchor = Anchor.BottomCentre, Children = new Drawable[] { - new FillFlowContainer public Beatmap Beatmap; + /// + /// All the converted hit objects contained by this hit renderer. + /// + public override IEnumerable Objects => Beatmap.HitObjects; + + /// + /// The mods which are to be applied. + /// + protected IEnumerable Mods; + /// /// Creates a hit renderer for a beatmap. /// @@ -129,6 +139,8 @@ namespace osu.Game.Rulesets.UI { Debug.Assert(beatmap != null, "HitRenderer initialized with a null beatmap."); + Mods = beatmap.Mods.Value; + RelativeSizeAxes = Axes.Both; BeatmapConverter converter = CreateBeatmapConverter(); @@ -148,6 +160,8 @@ namespace osu.Game.Rulesets.UI // Post-process the beatmap processor.PostProcess(Beatmap); + ApplyBeatmap(); + // Add mods, should always be the last thing applied to give full control to mods applyMods(beatmap.Mods.Value); } @@ -165,6 +179,11 @@ namespace osu.Game.Rulesets.UI mod.ApplyToHitRenderer(this); } + /// + /// Called when the beatmap of this hit renderer has been set. Used to apply any default values from the beatmap. + /// + protected virtual void ApplyBeatmap() { } + /// /// Creates a processor to perform post-processing operations /// on HitObjects in converted Beatmaps. @@ -192,7 +211,10 @@ namespace osu.Game.Rulesets.UI public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor; - public override IEnumerable Objects => Beatmap.HitObjects; + /// + /// All the converted hit objects contained by this hit renderer. + /// + public new IEnumerable Objects => Beatmap.HitObjects; protected override bool AllObjectsJudged => drawableObjects.All(h => h.Judged); @@ -234,6 +256,9 @@ namespace osu.Game.Rulesets.UI InputManager.ReplayInputHandler.ToScreenSpace = Playfield.ScaledContent.ToScreenSpace; } + /// + /// Creates and adds drawable representations of hit objects to the play field. + /// private void loadObjects() { drawableObjects.Capacity = Beatmap.HitObjects.Count; diff --git a/osu.Game/Rulesets/UI/SpeedAdjustedHitRenderer.cs b/osu.Game/Rulesets/UI/SpeedAdjustedHitRenderer.cs new file mode 100644 index 0000000000..42dadb047d --- /dev/null +++ b/osu.Game/Rulesets/UI/SpeedAdjustedHitRenderer.cs @@ -0,0 +1,104 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Lists; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.IO.Serialization; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Timing; + +namespace osu.Game.Rulesets.UI +{ + /// + /// A type of that supports speed adjustments in some capacity. + /// + public abstract class SpeedAdjustedHitRenderer : HitRenderer + where TObject : HitObject + where TJudgement : Judgement + { + protected readonly SortedList DefaultControlPoints = new SortedList(Comparer.Default); + + protected SpeedAdjustedHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(beatmap, isForCurrentRuleset) + { + } + + [BackgroundDependencyLoader] + private void load() + { + ApplySpeedAdjustments(); + } + + protected override void ApplyBeatmap() + { + // Calculate default multiplier control points + var lastTimingPoint = new TimingControlPoint(); + var lastDifficultyPoint = new DifficultyControlPoint(); + + // Merge timing + difficulty points + var allPoints = new SortedList(Comparer.Default); + allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints); + allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints); + + // Generate the timing points, making non-timing changes use the previous timing change + var timingChanges = allPoints.Select(c => + { + var timingPoint = c as TimingControlPoint; + var difficultyPoint = c as DifficultyControlPoint; + + if (timingPoint != null) + lastTimingPoint = timingPoint; + + if (difficultyPoint != null) + lastDifficultyPoint = difficultyPoint; + + return new MultiplierControlPoint(c.Time) + { + TimingPoint = lastTimingPoint, + DifficultyPoint = lastDifficultyPoint + }; + }); + + double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; + + // Perform some post processing of the timing changes + timingChanges = timingChanges + // Collapse sections after the last hit object + .Where(s => s.StartTime <= lastObjectTime) + // Collapse sections with the same start time + .GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime) + // Collapse sections with the same beat length + .GroupBy(s => s.TimingPoint.BeatLength * s.DifficultyPoint.SpeedMultiplier).Select(g => g.First()); + + DefaultControlPoints.AddRange(timingChanges); + } + + /// + /// Generates a control point with the default timing change/difficulty change from the beatmap at a time. + /// + /// The time to create the control point at. + /// The at . + public MultiplierControlPoint CreateControlPointAt(double time) + { + if (DefaultControlPoints.Count == 0) + return new MultiplierControlPoint(time); + + int index = DefaultControlPoints.BinarySearch(new MultiplierControlPoint(time)); + if (index < 0) + return new MultiplierControlPoint(time); + + return new MultiplierControlPoint(time, DefaultControlPoints[index].DeepClone()); + } + + /// + /// Applies speed changes to the playfield. + /// + protected abstract void ApplySpeedAdjustments(); + } +} diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 850c640770..2582c68296 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -25,13 +25,13 @@ namespace osu.Game.Screens.Edit protected override void OnEntering(Screen last) { base.OnEntering(last); - Background.Schedule(() => Background.FadeColour(Color4.DarkGray, 500)); + Background.FadeColour(Color4.DarkGray, 500); Beatmap?.Track?.Stop(); } protected override bool OnExiting(Screen next) { - Background.Schedule(() => Background.FadeColour(Color4.White, 500)); + Background.FadeColour(Color4.White, 500); Beatmap?.Track?.Start(); return base.OnExiting(next); } diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index fe9f7b4bf6..19bb084af4 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -8,7 +8,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Transforms; using osu.Framework.Input; using osu.Game.Graphics; @@ -54,7 +54,7 @@ namespace osu.Game.Screens.Menu { Masking = true, MaskingSmoothness = 2, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.2f), @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Menu Scale = new Vector2(0, 1), Size = boxSize, Shear = new Vector2(ButtonSystem.WEDGE_WIDTH / boxSize.Y, 0), - Children = new [] + Children = new[] { new Box { @@ -283,9 +283,9 @@ namespace osu.Game.Screens.Menu public ButtonState State { get { return state; } + set { - if (state == value) return; diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 52039a8417..82b7335a9b 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -8,7 +8,7 @@ using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Overlays.Toolbar; @@ -192,10 +192,8 @@ namespace osu.Game.Screens.Menu public MenuState State { - get - { - return state; - } + get { return state; } + set { if (state == value) return; diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs new file mode 100644 index 0000000000..7fa1eda880 --- /dev/null +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -0,0 +1,223 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Graphics.ES30; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using System; + +namespace osu.Game.Screens.Menu +{ + internal class LogoVisualisation : Drawable, IHasAccentColour + { + private readonly Bindable beatmap = new Bindable(); + + /// + /// The number of bars to jump each update iteration. + /// + private const int index_change = 5; + + /// + /// The maximum length of each bar in the visualiser. Will be reduced when kiai is not activated. + /// + private const float bar_length = 600; + + /// + /// The number of bars in one rotation of the visualiser. + /// + private const int bars_per_visualiser = 200; + + /// + /// How many times we should stretch around the circumference (overlapping overselves). + /// + private const float visualiser_rounds = 5; + + /// + /// How much should each bar go down each milisecond (based on a full bar). + /// + private const float decay_per_milisecond = 0.0024f; + + /// + /// Number of milliseconds between each amplitude update. + /// + private const float time_between_updates = 50; + + /// + /// The minimum amplitude to show a bar. + /// + private const float amplitude_dead_zone = 1f / bar_length; + + private int indexOffset; + + public Color4 AccentColour { get; set; } + + private readonly float[] frequencyAmplitudes = new float[256]; + + public override bool HandleInput => false; + + private Shader shader; + private readonly Texture texture; + + public LogoVisualisation() + { + texture = Texture.WhitePixel; + AccentColour = new Color4(1, 1, 1, 0.2f); + BlendingMode = BlendingMode.Additive; + } + + [BackgroundDependencyLoader(true)] + private void load(ShaderManager shaders, OsuGame game) + { + if (game?.Beatmap != null) + beatmap.BindTo(game.Beatmap); + shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + } + + private void updateAmplitudes() + { + float[] temporalAmplitudes = beatmap.Value?.Track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256]; + + var effect = beatmap.Value?.Beatmap.ControlPointInfo.EffectPointAt(beatmap.Value.Track?.CurrentTime ?? Time.Current); + + for (int i = 0; i < bars_per_visualiser; i++) + { + if (beatmap?.Value?.Track?.IsRunning ?? false) + { + float targetAmplitude = temporalAmplitudes[(i + indexOffset) % bars_per_visualiser] * (effect?.KiaiMode == true ? 1 : 0.5f); + if (targetAmplitude > frequencyAmplitudes[i]) + frequencyAmplitudes[i] = targetAmplitude; + } + else + { + int index = (i + index_change) % bars_per_visualiser; + if (frequencyAmplitudes[index] > frequencyAmplitudes[i]) + frequencyAmplitudes[i] = frequencyAmplitudes[index]; + } + } + + indexOffset = (indexOffset + index_change) % bars_per_visualiser; + Scheduler.AddDelayed(updateAmplitudes, time_between_updates); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateAmplitudes(); + } + + protected override void Update() + { + base.Update(); + + float decayFactor = (float)Time.Elapsed * decay_per_milisecond; + for (int i = 0; i < bars_per_visualiser; i++) + { + //3% of extra bar length to make it a little faster when bar is almost at it's minimum + frequencyAmplitudes[i] -= decayFactor * (frequencyAmplitudes[i] + 0.03f); + if (frequencyAmplitudes[i] < 0) + frequencyAmplitudes[i] = 0; + } + + Invalidate(Invalidation.DrawNode, shallPropagate: false); + } + + protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(); + + private readonly VisualiserSharedData sharedData = new VisualiserSharedData(); + + protected override void ApplyDrawNode(DrawNode node) + { + base.ApplyDrawNode(node); + + var visNode = (VisualisationDrawNode)node; + + visNode.Shader = shader; + visNode.Texture = texture; + visNode.Size = DrawSize.X; + visNode.Shared = sharedData; + visNode.Colour = AccentColour; + visNode.AudioData = frequencyAmplitudes; + } + + private class VisualiserSharedData + { + public readonly LinearBatch VertexBatch = new LinearBatch(100 * 4, 10, PrimitiveType.Quads); + } + + private class VisualisationDrawNode : DrawNode + { + public Shader Shader; + public Texture Texture; + public VisualiserSharedData Shared; + //Asuming the logo is a circle, we don't need a second dimension. + public float Size; + + public Color4 Colour; + public float[] AudioData; + + public override void Draw(Action vertexAction) + { + base.Draw(vertexAction); + + Shader.Bind(); + Texture.TextureGL.Bind(); + + Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; + + ColourInfo colourInfo = DrawInfo.Colour; + colourInfo.ApplyChild(Colour); + + if (AudioData != null) + { + for (int j = 0; j < visualiser_rounds; j++) + { + for (int i = 0; i < bars_per_visualiser; i++) + { + if (AudioData[i] < amplitude_dead_zone) + continue; + + float rotation = MathHelper.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); + float rotationCos = (float)Math.Cos(rotation); + float rotationSin = (float)Math.Sin(rotation); + //taking the cos and sin to the 0..1 range + var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * Size; + + var barSize = new Vector2(Size * (float)Math.Sqrt(2 * (1 - Math.Cos(MathHelper.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * AudioData[i]); + //The distance between the position and the sides of the bar. + var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); + //The distance between the bottom side of the bar and the top side. + var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); + + var rectangle = new Quad( + (barPosition - bottomOffset) * DrawInfo.Matrix, + (barPosition - bottomOffset + amplitudeOffset) * DrawInfo.Matrix, + (barPosition + bottomOffset) * DrawInfo.Matrix, + (barPosition + bottomOffset + amplitudeOffset) * DrawInfo.Matrix + ); + + Texture.DrawQuad( + rectangle, + colourInfo, + null, + Shared.VertexBatch.Add, + //barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. + Vector2.Divide(inflation, barSize.Yx)); + } + } + } + Shader.Unbind(); + } + } + } +} diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index 77239726e8..065c7c5be0 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -8,7 +8,7 @@ using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 44b7b6bceb..78bcbc5c63 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -8,6 +8,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input; @@ -34,8 +35,10 @@ namespace osu.Game.Screens.Menu private readonly Container logoBeatContainer; private readonly Container logoAmplitudeContainer; private readonly Container logoHoverContainer; + private readonly LogoVisualisation visualizer; private SampleChannel sampleClick; + private SampleChannel sampleBeat; private readonly Container colourAndTriangles; @@ -51,10 +54,7 @@ namespace osu.Game.Screens.Menu public bool Triangles { - set - { - colourAndTriangles.Alpha = value ? 1 : 0; - } + set { colourAndTriangles.Alpha = value ? 1 : 0; } } protected override bool InternalContains(Vector2 screenSpacePos) => logoContainer.Contains(screenSpacePos); @@ -62,10 +62,7 @@ namespace osu.Game.Screens.Menu public bool Ripple { get { return rippleContainer.Alpha > 0; } - set - { - rippleContainer.Alpha = value ? 1 : 0; - } + set { rippleContainer.Alpha = value ? 1 : 0; } } public bool Interactive = true; @@ -126,6 +123,14 @@ namespace osu.Game.Screens.Menu AutoSizeAxes = Axes.Both, Children = new Drawable[] { + visualizer = new LogoVisualisation + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Alpha = 0.5f, + Size = new Vector2(0.96f) + }, new BufferedContainer { AutoSizeAxes = Axes.Both, @@ -195,14 +200,6 @@ namespace osu.Game.Screens.Menu Alpha = 0, } } - }, - new MenuVisualisation - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - BlendingMode = BlendingMode.Additive, - Alpha = 0.2f, } } } @@ -219,6 +216,7 @@ namespace osu.Game.Screens.Menu private void load(TextureStore textures, AudioManager audio) { sampleClick = audio.Sample.Get(@"Menu/menuhit"); + sampleBeat = audio.Sample.Get(@"Menu/heartbeat"); logo.Texture = textures.Get(@"Menu/logo"); ripple.Texture = textures.Get(@"Menu/logo"); } @@ -229,6 +227,9 @@ namespace osu.Game.Screens.Menu { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + if (Hovering) + sampleBeat.Play(); + lastBeatIndex = beatIndex; var beatLength = timingPoint.BeatLength; @@ -252,10 +253,14 @@ namespace osu.Game.Screens.Menu if (effectPoint.KiaiMode && flashLayer.Alpha < 0.4f) { flashLayer.ClearTransforms(); + visualizer.ClearTransforms(); flashLayer.FadeTo(0.2f * amplitudeAdjust, beat_in_time, EasingTypes.Out); + visualizer.FadeTo(0.9f * amplitudeAdjust, beat_in_time, EasingTypes.Out); using (flashLayer.BeginDelayedSequence(beat_in_time)) flashLayer.FadeOut(beatLength); + using (visualizer.BeginDelayedSequence(beat_in_time)) + visualizer.FadeTo(0.5f, beatLength); } } diff --git a/osu.Game/Screens/Multiplayer/DrawableGameType.cs b/osu.Game/Screens/Multiplayer/DrawableGameType.cs index ccc98c3ccd..5b85fcc4da 100644 --- a/osu.Game/Screens/Multiplayer/DrawableGameType.cs +++ b/osu.Game/Screens/Multiplayer/DrawableGameType.cs @@ -4,7 +4,8 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Online.Multiplayer; diff --git a/osu.Game/Screens/Multiplayer/DrawableRoom.cs b/osu.Game/Screens/Multiplayer/DrawableRoom.cs index bbe279946a..71114dd3a3 100644 --- a/osu.Game/Screens/Multiplayer/DrawableRoom.cs +++ b/osu.Game/Screens/Multiplayer/DrawableRoom.cs @@ -7,7 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Game.Database; using osu.Game.Graphics; @@ -47,7 +47,7 @@ namespace osu.Game.Screens.Multiplayer Height = height; CornerRadius = 5; Masking = true; - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(40), @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Multiplayer }, avatar = new UpdateableAvatar { - Size = new Vector2(Height - content_padding* 2), + Size = new Vector2(Height - content_padding * 2), Masking = true, CornerRadius = 5f, Margin = new MarginPadding { Left = content_padding * 2, Top = content_padding }, diff --git a/osu.Game/Screens/Multiplayer/Match.cs b/osu.Game/Screens/Multiplayer/Match.cs index 7da6ef800e..ec6a66062d 100644 --- a/osu.Game/Screens/Multiplayer/Match.cs +++ b/osu.Game/Screens/Multiplayer/Match.cs @@ -24,12 +24,12 @@ namespace osu.Game.Screens.Multiplayer { base.OnEntering(last); - Background.Schedule(() => Background.FadeColour(Color4.DarkGray, 500)); + Background.FadeColour(Color4.DarkGray, 500); } protected override bool OnExiting(Screen next) { - Background.Schedule(() => Background.FadeColour(Color4.White, 500)); + Background.FadeColour(Color4.White, 500); return base.OnExiting(next); } } diff --git a/osu.Game/Screens/Multiplayer/RoomInspector.cs b/osu.Game/Screens/Multiplayer/RoomInspector.cs index 25a4740698..b7eca2dc8a 100644 --- a/osu.Game/Screens/Multiplayer/RoomInspector.cs +++ b/osu.Game/Screens/Multiplayer/RoomInspector.cs @@ -10,7 +10,8 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Game.Beatmaps.Drawables; diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 6a0e37ca6f..de9c698f2a 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -131,7 +131,15 @@ namespace osu.Game.Screens Background.Exit(); } - return base.OnExiting(next); + if (base.OnExiting(next)) + return true; + + // while this is not necessary as we are constructing our own bindable, there are cases where + // the GC doesn't run as fast as expected and this is triggered post-exit. + // added to resolve https://github.com/ppy/osu/issues/829 + beatmap.ValueChanged -= OnBeatmapChanged; + + return false; } } } diff --git a/osu.Game/Screens/Play/HUD/ComboCounter.cs b/osu.Game/Screens/Play/HUD/ComboCounter.cs index 46c7084cec..3793181ecd 100644 --- a/osu.Game/Screens/Play/HUD/ComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ComboCounter.cs @@ -205,7 +205,7 @@ namespace osu.Game.Screens.Play.HUD Transforms.Add(transform); } - protected class TransformComboRoll : Transform + protected class TransformComboRoll : Transform { public override int CurrentValue { diff --git a/osu.Game/Screens/Play/HUD/ComboResultCounter.cs b/osu.Game/Screens/Play/HUD/ComboResultCounter.cs index a1a166f944..a4a20c1fd0 100644 --- a/osu.Game/Screens/Play/HUD/ComboResultCounter.cs +++ b/osu.Game/Screens/Play/HUD/ComboResultCounter.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Play.HUD Current.Value = Current + amount; } - protected class TransformComboResult : Transform + protected class TransformComboResult : Transform { public override ulong CurrentValue { diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 4a10438cdb..921accf6ac 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -54,7 +54,6 @@ namespace osu.Game.Screens.Play.HUD { AutoSizeAxes = Axes.Both, Scale = new Vector2(0.6f), - }); } diff --git a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs index 7ba5dfe1b7..cac1f1a71b 100644 --- a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs @@ -5,12 +5,12 @@ using System; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Play.HUD { @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Play.HUD return; glowColour = value; - fill.EdgeEffect = new EdgeEffect + fill.EdgeEffect = new EdgeEffectParameters { Colour = glowColour.Opacity(base_glow_opacity), Radius = 8, diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 115611c244..3248495b61 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -7,21 +7,24 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play.HUD; +using OpenTK; using OpenTK.Input; namespace osu.Game.Screens.Play { - public abstract class HUDOverlay : Container + public class HUDOverlay : Container { private const int duration = 100; private readonly Container content; + public readonly KeyCounterCollection KeyCounter; public readonly RollingCounter ComboCounter; public readonly ScoreCounter ScoreCounter; @@ -31,18 +34,11 @@ namespace osu.Game.Screens.Play public readonly ModDisplay ModDisplay; private Bindable showHud; + private bool replayLoaded; private static bool hasShownNotificationOnce; - protected abstract KeyCounterCollection CreateKeyCounter(); - protected abstract RollingCounter CreateComboCounter(); - protected abstract RollingCounter CreateAccuracyCounter(); - protected abstract ScoreCounter CreateScoreCounter(); - protected abstract HealthDisplay CreateHealthDisplay(); - protected abstract SongProgress CreateProgress(); - protected abstract ModDisplay CreateModsContainer(); - - protected HUDOverlay() + public HUDOverlay() { RelativeSizeAxes = Axes.Both; @@ -59,12 +55,13 @@ namespace osu.Game.Screens.Play HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), + //ReplaySettingsOverlay = CreateReplaySettingsOverlay(), } }); } [BackgroundDependencyLoader(true)] - private void load(OsuConfigManager config, NotificationManager notificationManager) + private void load(OsuConfigManager config, NotificationManager notificationManager, OsuColour colours) { showHud = config.GetBindable(OsuSetting.ShowInterface); showHud.ValueChanged += hudVisibility => content.FadeTo(hudVisibility ? 1 : 0, duration); @@ -79,24 +76,32 @@ namespace osu.Game.Screens.Play Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab." }); } - } - public virtual void BindProcessor(ScoreProcessor processor) - { - ScoreCounter?.Current.BindTo(processor.TotalScore); - AccuracyCounter?.Current.BindTo(processor.Accuracy); - ComboCounter?.Current.BindTo(processor.Combo); - HealthDisplay?.Current.BindTo(processor.Health); + // todo: the stuff below should probably not be in this base implementation, but in each individual class. + ComboCounter.AccentColour = colours.BlueLighter; + AccuracyCounter.AccentColour = colours.BlueLighter; + ScoreCounter.AccentColour = colours.BlueLighter; + + var shd = HealthDisplay as StandardHealthDisplay; + if (shd != null) + { + shd.AccentColour = colours.BlueLighter; + shd.GlowColour = colours.BlueDarker; + } } public virtual void BindHitRenderer(HitRenderer hitRenderer) { hitRenderer.InputManager.Add(KeyCounter.GetReceptor()); + replayLoaded = hitRenderer.HasReplayLoaded; + // in the case a replay isn't loaded, we want some elements to only appear briefly. - if (!hitRenderer.HasReplayLoaded) + if (!replayLoaded) + { using (ModDisplay.BeginDelayedSequence(2000)) ModDisplay.FadeOut(200); + } } protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) @@ -115,5 +120,82 @@ namespace osu.Game.Screens.Play return base.OnKeyDown(state, args); } + + protected virtual RollingCounter CreateAccuracyCounter() => new PercentageCounter + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopRight, + Position = new Vector2(0, 35), + TextSize = 20, + Margin = new MarginPadding { Right = 140 }, + }; + + protected virtual RollingCounter CreateComboCounter() => new SimpleComboCounter + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopLeft, + Position = new Vector2(0, 35), + Margin = new MarginPadding { Left = 140 }, + TextSize = 20, + }; + + protected virtual HealthDisplay CreateHealthDisplay() => new StandardHealthDisplay + { + Size = new Vector2(1, 5), + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 20 } + }; + + protected virtual KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection + { + IsCounting = true, + FadeTime = 50, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding(10), + Y = -TwoLayerButton.SIZE_RETRACTED.Y, + }; + + protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + TextSize = 40, + Position = new Vector2(0, 30), + }; + + protected virtual SongProgress CreateProgress() => new SongProgress + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + }; + + protected virtual ModDisplay CreateModsContainer() => new ModDisplay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 20, Right = 10 }, + }; + + //protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay + //{ + // Anchor = Anchor.TopRight, + // Origin = Anchor.TopRight, + // Margin = new MarginPadding { Top = 100, Right = 10 }, + //}; + + public virtual void BindProcessor(ScoreProcessor processor) + { + ScoreCounter?.Current.BindTo(processor.TotalScore); + AccuracyCounter?.Current.BindTo(processor.Accuracy); + ComboCounter?.Current.BindTo(processor.Combo); + HealthDisplay?.Current.BindTo(processor.Health); + + var shd = HealthDisplay as StandardHealthDisplay; + if (shd != null) + processor.NewJudgement += shd.Flash; + } } } diff --git a/osu.Game/Screens/Play/HotkeyRetryOverlay.cs b/osu.Game/Screens/Play/HotkeyRetryOverlay.cs index 16062bebe5..f673965c2b 100644 --- a/osu.Game/Screens/Play/HotkeyRetryOverlay.cs +++ b/osu.Game/Screens/Play/HotkeyRetryOverlay.cs @@ -9,7 +9,7 @@ using osu.Framework.Audio; using System; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using OpenTK.Graphics; namespace osu.Game.Screens.Play diff --git a/osu.Game/Screens/Play/MenuOverlay.cs b/osu.Game/Screens/Play/MenuOverlay.cs index 424b26350f..1d557269c4 100644 --- a/osu.Game/Screens/Play/MenuOverlay.cs +++ b/osu.Game/Screens/Play/MenuOverlay.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Graphics.Sprites; using OpenTK; @@ -14,6 +13,7 @@ using osu.Game.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Game.Graphics.UserInterface; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Play { @@ -164,7 +164,7 @@ namespace osu.Game.Screens.Play AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Masking = true, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.6f), diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 707d026e2b..c38ea65f90 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -170,7 +170,7 @@ namespace osu.Game.Screens.Play HitRenderer, } }, - hudOverlay = new StandardHUDOverlay + hudOverlay = new HUDOverlay { Anchor = Anchor.Centre, Origin = Anchor.Centre @@ -278,7 +278,6 @@ namespace osu.Game.Screens.Play { if (!pauseContainer.IsPaused) decoupledClock.Start(); - }); pauseContainer.Alpha = 0; diff --git a/osu.Game/Screens/Play/ReplaySettings/CollectionSettings.cs b/osu.Game/Screens/Play/ReplaySettings/CollectionSettings.cs new file mode 100644 index 0000000000..482581abca --- /dev/null +++ b/osu.Game/Screens/Play/ReplaySettings/CollectionSettings.cs @@ -0,0 +1,33 @@ +// 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.Game.Graphics.Sprites; +using osu.Game.Overlays.Music; +using System.Collections.Generic; + +namespace osu.Game.Screens.Play.ReplaySettings +{ + public class CollectionSettings : ReplayGroup + { + protected override string Title => @"collections"; + + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"Add current song to", + }, + new CollectionsDropdown + { + RelativeSizeAxes = Axes.X, + Items = new[] { new KeyValuePair(@"All", PlaylistCollection.All) }, + }, + }; + } + } +} diff --git a/osu.Game/Screens/Play/ReplaySettings/DiscussionSettings.cs b/osu.Game/Screens/Play/ReplaySettings/DiscussionSettings.cs new file mode 100644 index 0000000000..9c4fb03517 --- /dev/null +++ b/osu.Game/Screens/Play/ReplaySettings/DiscussionSettings.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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Play.ReplaySettings +{ + public class DiscussionSettings : ReplayGroup + { + protected override string Title => @"discussions"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new ReplayCheckbox + { + LabelText = "Show floating comments", + Bindable = config.GetBindable(OsuSetting.FloatingComments) + }, + new FocusedTextBox + { + RelativeSizeAxes = Axes.X, + Height = 30, + PlaceholderText = "Add Comment", + HoldFocus = false, + }, + }; + } + } +} diff --git a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs new file mode 100644 index 0000000000..7f69d3109c --- /dev/null +++ b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs @@ -0,0 +1,27 @@ +// 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.Game.Configuration; +using osu.Framework.Graphics; + +namespace osu.Game.Screens.Play.ReplaySettings +{ + public class PlaybackSettings : ReplayGroup + { + protected override string Title => @"playback"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new ReplaySliderBar() + { + LabelText = "Playback speed", + Bindable = config.GetBindable(OsuSetting.PlaybackSpeed) + } + }; + } + } +} diff --git a/osu.Game/Screens/Play/ReplaySettings/ReplayCheckbox.cs b/osu.Game/Screens/Play/ReplaySettings/ReplayCheckbox.cs new file mode 100644 index 0000000000..7ad1e4f527 --- /dev/null +++ b/osu.Game/Screens/Play/ReplaySettings/ReplayCheckbox.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 osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Play.ReplaySettings +{ + public class ReplayCheckbox : OsuCheckbox + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Nub.AccentColour = colours.Yellow; + Nub.GlowingAccentColour = colours.YellowLighter; + Nub.GlowColour = colours.YellowDarker; + } + } +} diff --git a/osu.Game/Screens/Play/ReplaySettings/ReplayGroup.cs b/osu.Game/Screens/Play/ReplaySettings/ReplayGroup.cs new file mode 100644 index 0000000000..a2494d3a69 --- /dev/null +++ b/osu.Game/Screens/Play/ReplaySettings/ReplayGroup.cs @@ -0,0 +1,132 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Play.ReplaySettings +{ + public abstract class ReplayGroup : Container + { + /// + /// The title to be displayed in the header of this group. + /// + protected abstract string Title { get; } + + private const float transition_duration = 250; + private const int container_width = 270; + private const int border_thickness = 2; + private const int header_height = 30; + private const int corner_radius = 5; + + private readonly FillFlowContainer content; + private readonly IconButton button; + + private bool expanded = true; + + private Color4 buttonActiveColour; + + protected ReplayGroup() + { + AutoSizeAxes = Axes.Y; + Width = container_width; + Masking = true; + CornerRadius = corner_radius; + BorderColour = Color4.Black; + BorderThickness = border_thickness; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Container + { + Name = @"Header", + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Height = header_height, + Children = new Drawable[] + { + new OsuSpriteText + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = Title.ToUpper(), + TextSize = 17, + Font = @"Exo2.0-Bold", + Margin = new MarginPadding { Left = 10 }, + }, + button = new IconButton + { + Origin = Anchor.Centre, + Anchor = Anchor.CentreRight, + Position = new Vector2(-15, 0), + Icon = FontAwesome.fa_bars, + Scale = new Vector2(0.75f), + Action = toggleContentVisibility, + }, + } + }, + content = new FillFlowContainer + { + Name = @"Content", + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeDuration = transition_duration, + AutoSizeEasing = EasingTypes.OutQuint, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(15), + Spacing = new Vector2(0, 15), + } + } + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + button.Colour = buttonActiveColour = colours.Yellow; + } + + protected override Container Content => content; + + private void toggleContentVisibility() + { + content.ClearTransforms(); + + expanded = !expanded; + + if (expanded) + content.AutoSizeAxes = Axes.Y; + else + { + content.AutoSizeAxes = Axes.None; + content.ResizeHeightTo(0, transition_duration, EasingTypes.OutQuint); + } + + button.FadeColour(expanded ? buttonActiveColour : Color4.White, 200, EasingTypes.OutQuint); + } + } +} diff --git a/osu.Game/Screens/Play/ReplaySettings/ReplaySliderBar.cs b/osu.Game/Screens/Play/ReplaySettings/ReplaySliderBar.cs new file mode 100644 index 0000000000..4992c3a1e7 --- /dev/null +++ b/osu.Game/Screens/Play/ReplaySettings/ReplaySliderBar.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 osu.Framework.Allocation; +using osu.Game.Graphics.UserInterface; +using System; +using osu.Game.Graphics; +using osu.Framework.Graphics; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Screens.Play.ReplaySettings +{ + public class ReplaySliderBar : SettingsSlider + where T : struct, IEquatable + { + protected override Drawable CreateControl() => new Sliderbar() + { + Margin = new MarginPadding { Top = 5, Bottom = 5 }, + RelativeSizeAxes = Axes.X + }; + + private class Sliderbar : OsuSliderBar + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Yellow; + Nub.AccentColour = colours.Yellow; + Nub.GlowingAccentColour = colours.YellowLighter; + Nub.GlowColour = colours.YellowDarker; + } + } + } +} diff --git a/osu.Game/Screens/Play/ReplaySettingsOverlay.cs b/osu.Game/Screens/Play/ReplaySettingsOverlay.cs new file mode 100644 index 0000000000..415f70005d --- /dev/null +++ b/osu.Game/Screens/Play/ReplaySettingsOverlay.cs @@ -0,0 +1,24 @@ +// 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; +using osu.Game.Screens.Play.ReplaySettings; +using OpenTK; + +namespace osu.Game.Screens.Play +{ + public class ReplaySettingsOverlay : FillFlowContainer + { + public ReplaySettingsOverlay() + { + Direction = FillDirection.Vertical; + AutoSizeAxes = Axes.Both; + Spacing = new Vector2(0, 20); + + Add(new CollectionSettings()); + Add(new DiscussionSettings()); + Add(new PlaybackSettings()); + } + } +} diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index ee11fc0ca6..d110f8ecac 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Threading; using osu.Framework.Timing; @@ -18,6 +17,7 @@ using OpenTK; using OpenTK.Graphics; using OpenTK.Input; using osu.Framework.Audio.Sample; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Play { @@ -232,7 +232,7 @@ namespace osu.Game.Screens.Play AutoSizeAxes = Axes.Both, Origin = Anchor.Centre, Direction = FillDirection.Horizontal, - Children = new [] + Children = new[] { new TextAwesome { Icon = FontAwesome.fa_chevron_right }, new TextAwesome { Icon = FontAwesome.fa_chevron_right }, diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 6b3f81628c..e5b18292ab 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -146,7 +146,7 @@ namespace osu.Game.Screens.Play double progress = ((audioClock?.CurrentTime ?? Time.Current) - firstHitTime) / (lastHitTime - firstHitTime); - if(progress < 1) + if (progress < 1) { bar.UpdatePosition((float)progress); graph.Progress = (int)(graph.ColumnCount * progress); diff --git a/osu.Game/Screens/Play/SongProgressBar.cs b/osu.Game/Screens/Play/SongProgressBar.cs index d0fb3c8a3d..730cf471da 100644 --- a/osu.Game/Screens/Play/SongProgressBar.cs +++ b/osu.Game/Screens/Play/SongProgressBar.cs @@ -6,7 +6,7 @@ using OpenTK.Graphics; using osu.Game.Overlays; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Play { diff --git a/osu.Game/Screens/Play/SongProgressGraph.cs b/osu.Game/Screens/Play/SongProgressGraph.cs index 4e56f60c31..ea7c465e4d 100644 --- a/osu.Game/Screens/Play/SongProgressGraph.cs +++ b/osu.Game/Screens/Play/SongProgressGraph.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Play { IHasEndTime end = h as IHasEndTime; - int startRange = (int)((h.StartTime - firstHit)/ interval); + int startRange = (int)((h.StartTime - firstHit) / interval); int endRange = (int)(((end?.EndTime > 0 ? end.EndTime : h.StartTime) - firstHit) / interval); for (int i = startRange; i <= endRange; i++) values[i]++; diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 43a8253b53..cd1e9c78c6 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -8,9 +8,9 @@ using osu.Framework.Caching; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Play { @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Play public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) { - if ((invalidation & Invalidation.SizeInParentSpace) > 0) + if ((invalidation & Invalidation.DrawSize) > 0) layout.Invalidate(); return base.Invalidate(invalidation, source, shallPropagate); } diff --git a/osu.Game/Screens/Play/StandardHUDOverlay.cs b/osu.Game/Screens/Play/StandardHUDOverlay.cs deleted file mode 100644 index 87fbea6810..0000000000 --- a/osu.Game/Screens/Play/StandardHUDOverlay.cs +++ /dev/null @@ -1,98 +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.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play.HUD; -using OpenTK; - -namespace osu.Game.Screens.Play -{ - public class StandardHUDOverlay : HUDOverlay - { - protected override RollingCounter CreateAccuracyCounter() => new PercentageCounter - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopRight, - Position = new Vector2(0, 35), - TextSize = 20, - Margin = new MarginPadding { Right = 140 }, - }; - - protected override RollingCounter CreateComboCounter() => new SimpleComboCounter - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopLeft, - Position = new Vector2(0, 35), - Margin = new MarginPadding { Left = 140 }, - TextSize = 20, - }; - - protected override HealthDisplay CreateHealthDisplay() => new StandardHealthDisplay - { - Size = new Vector2(1, 5), - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 20 } - }; - - protected override KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection - { - IsCounting = true, - FadeTime = 50, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Margin = new MarginPadding(10), - Y = - TwoLayerButton.SIZE_RETRACTED.Y, - }; - - protected override ScoreCounter CreateScoreCounter() => new ScoreCounter(6) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - TextSize = 40, - Position = new Vector2(0, 30), - }; - - protected override SongProgress CreateProgress() => new SongProgress - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - }; - - protected override ModDisplay CreateModsContainer() => new ModDisplay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 20, Right = 10 }, - }; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - ComboCounter.AccentColour = colours.BlueLighter; - AccuracyCounter.AccentColour = colours.BlueLighter; - ScoreCounter.AccentColour = colours.BlueLighter; - - var shd = HealthDisplay as StandardHealthDisplay; - if (shd != null) - { - shd.AccentColour = colours.BlueLighter; - shd.GlowColour = colours.BlueDarker; - } - } - - public override void BindProcessor(ScoreProcessor processor) - { - base.BindProcessor(processor); - - var shd = HealthDisplay as StandardHealthDisplay; - if (shd != null) - processor.NewJudgement += shd.Flash; - } - } -} diff --git a/osu.Game/Screens/Ranking/ResultModeButton.cs b/osu.Game/Screens/Ranking/ResultModeButton.cs index fc62342860..4789a5abb7 100644 --- a/osu.Game/Screens/Ranking/ResultModeButton.cs +++ b/osu.Game/Screens/Ranking/ResultModeButton.cs @@ -5,11 +5,11 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Ranking { @@ -36,19 +36,6 @@ namespace osu.Game.Screens.Ranking } } - public override bool Active - { - get - { - return base.Active; - } - set - { - base.Active = value; - colouredPart.FadeColour(Active ? activeColour : inactiveColour, 200, EasingTypes.OutQuint); - } - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -60,7 +47,7 @@ namespace osu.Game.Screens.Ranking activeColour = colours.PinkDarker; inactiveColour = OsuColour.Gray(0.8f); - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0.4f), Type = EdgeEffectType.Shadow, @@ -104,5 +91,9 @@ namespace osu.Game.Screens.Ranking } }; } + + protected override void OnActivated() => colouredPart.FadeColour(activeColour, 200, EasingTypes.OutQuint); + + protected override void OnDeactivated() => colouredPart.FadeColour(inactiveColour, 200, EasingTypes.OutQuint); } } diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index f3dae710b2..2bff535603 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -16,6 +16,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Ranking { @@ -71,7 +72,6 @@ namespace osu.Game.Screens.Ranking using (BeginDelayedSequence(transition_time * 0.25f, true)) { - circleOuter.ScaleTo(1, transition_time, EasingTypes.OutQuint); circleOuter.FadeTo(1, transition_time, EasingTypes.OutQuint); @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Ranking circleOuter = new CircularContainer { Size = new Vector2(circle_outer_scale), - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0.4f), Type = EdgeEffectType.Shadow, @@ -226,7 +226,7 @@ namespace osu.Game.Screens.Ranking circleInner = new CircularContainer { Size = new Vector2(0.6f), - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0.4f), Type = EdgeEffectType.Shadow, diff --git a/osu.Game/Screens/Ranking/ResultsPage.cs b/osu.Game/Screens/Ranking/ResultsPage.cs index 02eae3643b..d0a1c49119 100644 --- a/osu.Game/Screens/Ranking/ResultsPage.cs +++ b/osu.Game/Screens/Ranking/ResultsPage.cs @@ -5,7 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Scoring; @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Ranking }, new CircularContainer { - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Colour = colours.GrayF.Opacity(0.8f), Type = EdgeEffectType.Shadow, @@ -64,7 +64,8 @@ namespace osu.Game.Screens.Ranking Origin = Anchor.Centre, Children = new Drawable[] { - new Box{ + new Box + { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true @@ -73,7 +74,7 @@ namespace osu.Game.Screens.Ranking }, content = new CircularContainer { - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0.2f), Type = EdgeEffectType.Shadow, @@ -87,6 +88,5 @@ namespace osu.Game.Screens.Ranking } }); } - } } diff --git a/osu.Game/Screens/Ranking/ResultsPageRanking.cs b/osu.Game/Screens/Ranking/ResultsPageRanking.cs index 827abd556e..d316dc7fb4 100644 --- a/osu.Game/Screens/Ranking/ResultsPageRanking.cs +++ b/osu.Game/Screens/Ranking/ResultsPageRanking.cs @@ -3,12 +3,12 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select.Leaderboards; using OpenTK; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Ranking { diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index 70542dab8b..ac333e47ff 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -23,6 +23,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Ranking { diff --git a/osu.Game/Screens/ScreenWhiteBox.cs b/osu.Game/Screens/ScreenWhiteBox.cs index 04432058dc..986947a517 100644 --- a/osu.Game/Screens/ScreenWhiteBox.cs +++ b/osu.Game/Screens/ScreenWhiteBox.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using osu.Framework.Screens; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; using osu.Game.Screens.Backgrounds; using osu.Game.Graphics.UserInterface; @@ -16,6 +15,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens { diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 130642b9c7..3f8d6af320 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps.Drawables; +using osu.Game.Configuration; using osu.Framework.Input; using OpenTK.Input; using System.Collections; @@ -17,6 +18,7 @@ using System.Diagnostics; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Threading; +using osu.Framework.Configuration; namespace osu.Game.Screens.Select { @@ -70,6 +72,9 @@ namespace osu.Game.Screens.Select private readonly List groups = new List(); + private Bindable randomType; + private readonly List seenGroups = new List(); + private readonly List panels = new List(); private BeatmapGroup selectedGroup; @@ -167,11 +172,26 @@ namespace osu.Game.Screens.Select public void SelectRandom() { - List visibleGroups = groups.Where(selectGroup => selectGroup.State != BeatmapGroupState.Hidden).ToList(); - if (visibleGroups.Count < 1) + IEnumerable visibleGroups = groups.Where(selectGroup => selectGroup.State != BeatmapGroupState.Hidden); + if (!visibleGroups.Any()) return; - BeatmapGroup group = visibleGroups[RNG.Next(visibleGroups.Count)]; + BeatmapGroup group; + if (randomType == SelectionRandomType.RandomPermutation) + { + IEnumerable notSeenGroups = visibleGroups.Except(seenGroups); + if (!notSeenGroups.Any()) + { + seenGroups.Clear(); + notSeenGroups = visibleGroups; + } + + group = notSeenGroups.ElementAt(RNG.Next(notSeenGroups.Count())); + seenGroups.Add(group); + } + else + group = visibleGroups.ElementAt(RNG.Next(visibleGroups.Count())); + BeatmapPanel panel = group.BeatmapPanels[RNG.Next(group.BeatmapPanels.Count)]; selectGroup(group, panel); @@ -224,7 +244,7 @@ namespace osu.Game.Screens.Select private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet) { - foreach(var b in beatmapSet.Beatmaps) + foreach (var b in beatmapSet.Beatmaps) { if (b.Metadata == null) b.Metadata = beatmapSet.Metadata; @@ -239,9 +259,10 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapDatabase database) + private void load(BeatmapDatabase database, OsuConfigManager config) { this.database = database; + randomType = config.GetBindable(OsuSetting.SelectionRandomType); } private void addGroup(BeatmapGroup group) diff --git a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs index e3c95d42a1..bd0f745016 100644 --- a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs +++ b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs @@ -8,10 +8,10 @@ using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Select { diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index abe54375cc..daf28c8f1b 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -6,7 +6,6 @@ using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -16,6 +15,7 @@ using System.Linq; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Framework.Threading; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Select { @@ -47,6 +47,7 @@ namespace osu.Game.Screens.Select public BeatmapInfo Beatmap { get { return beatmap; } + set { if (beatmap == value) return; @@ -79,8 +80,8 @@ namespace osu.Game.Screens.Select lookup.Success += res => { if (beatmap != requestedBeatmap) - //the beatmap has been changed since we started the lookup. - return; + //the beatmap has been changed since we started the lookup. + return; requestedBeatmap.Metrics = res; Schedule(() => updateMetrics(res)); @@ -88,6 +89,7 @@ namespace osu.Game.Screens.Select lookup.Failure += e => updateMetrics(null); api.Queue(lookup); + loading.Show(); } updateMetrics(requestedBeatmap.Metrics, false); @@ -103,6 +105,9 @@ namespace osu.Game.Screens.Select var hasRatings = metrics?.Ratings.Any() ?? false; var hasRetriesFails = (metrics?.Retries.Any() ?? false) && metrics.Fails.Any(); + if (failOnMissing) + loading.Hide(); + if (hasRatings) { var ratings = metrics.Ratings.ToList(); @@ -165,7 +170,7 @@ namespace osu.Game.Screens.Select Direction = FillDirection.Vertical, LayoutDuration = 200, LayoutEasing = EasingTypes.OutQuint, - Children = new [] + Children = new[] { description = new MetadataSegment("Description"), source = new MetadataSegment("Source"), @@ -199,9 +204,9 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, - Spacing = new Vector2(0,5), + Spacing = new Vector2(0, 5), Padding = new MarginPadding(10), - Children = new [] + Children = new[] { circleSize = new DifficultyRow("Circle Size", 7), drainRate = new DifficultyRow("HP Drain"), @@ -319,11 +324,13 @@ namespace osu.Game.Screens.Select } }, }, - } + }, + loading = new LoadingAnimation() }; } private APIAccess api; + private readonly LoadingAnimation loading; [BackgroundDependencyLoader] private void load(OsuColour colour, APIAccess api) @@ -479,7 +486,7 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Full, - Spacing = new Vector2(5,0), + Spacing = new Vector2(5, 0), Margin = new MarginPadding { Top = header.TextSize } } }; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 2d002597ab..264fe6643b 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -11,7 +11,6 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -21,6 +20,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Select { @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Select Masking = true; BorderColour = new Color4(221, 255, 255, 255); BorderThickness = 2.5f; - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Colour = new Color4(130, 204, 255, 150), diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 94bc60a6ca..51937df189 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -16,6 +15,7 @@ using osu.Game.Screens.Select.Filter; using Container = osu.Framework.Graphics.Containers.Container; using osu.Framework.Input; using osu.Game.Database; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Select { diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 32e09a5f28..882aa482da 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -8,7 +8,7 @@ using OpenTK.Input; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Menu; diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index 58df409ca1..9be940e06a 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -7,6 +7,7 @@ using OpenTK.Graphics; using OpenTK.Input; 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.Sprites; diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index a7aa752d65..e560cfe413 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -12,6 +12,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Threading; using osu.Game.Database; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Scoring; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -25,6 +26,8 @@ namespace osu.Game.Screens.Select.Leaderboards public Action ScoreSelected; + private readonly LoadingAnimation loading; + private IEnumerable scores; public IEnumerable Scores { @@ -74,7 +77,7 @@ namespace osu.Game.Screens.Select.Leaderboards scrollContainer = new ScrollContainer { RelativeSizeAxes = Axes.Both, - ScrollDraggerVisible = false, + ScrollbarVisible = false, Children = new Drawable[] { scrollFlow = new FillFlowContainer @@ -86,6 +89,7 @@ namespace osu.Game.Screens.Select.Leaderboards }, }, }, + loading = new LoadingAnimation() }; } @@ -117,6 +121,7 @@ namespace osu.Game.Screens.Select.Leaderboards } private GetScoresRequest getScoresRequest; + private void updateScores() { if (!IsLoaded) return; @@ -126,8 +131,14 @@ namespace osu.Game.Screens.Select.Leaderboards if (api == null || Beatmap == null) return; + loading.Show(); + getScoresRequest = new GetScoresRequest(Beatmap); - getScoresRequest.Success += r => Scores = r.Scores; + getScoresRequest.Success += r => + { + Scores = r.Scores; + loading.Hide(); + }; api.Queue(getScoresRequest); } diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs index c5fa0a5011..32e6d9630b 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs @@ -5,7 +5,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.Color4Extensions; @@ -147,7 +147,7 @@ namespace osu.Game.Screens.Select.Leaderboards CornerRadius = corner_radius, Masking = true, OnLoadComplete = d => d.FadeInFromZero(200), - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Radius = 1, diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs index 182f778dd4..db8ab439eb 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -4,7 +4,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -99,7 +99,7 @@ namespace osu.Game.Screens.Select.Options RelativeSizeAxes = Axes.Both, Shear = new Vector2(0.2f, 0f), Masking = true, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.2f), diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index be2f5196ef..a6ad56b86f 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -5,7 +5,7 @@ using System; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 41fa53e8a3..dc6dfdfd81 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -371,6 +371,7 @@ namespace osu.Game.Screens.Select switch (args.Key) { + case Key.KeypadEnter: case Key.Enter: raiseSelect(); return true; diff --git a/osu.Game/Screens/Select/WedgeBackground.cs b/osu.Game/Screens/Select/WedgeBackground.cs index af25e9b9ae..4841fcdd09 100644 --- a/osu.Game/Screens/Select/WedgeBackground.cs +++ b/osu.Game/Screens/Select/WedgeBackground.cs @@ -4,9 +4,9 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Select { diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index 05bf3a250f..7efb86a403 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -21,6 +21,7 @@ using osu.Game.Screens.Tournament.Teams; using OpenTK; using OpenTK.Graphics; using osu.Framework.IO.Stores; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Tournament { @@ -335,7 +336,6 @@ namespace osu.Game.Screens.Tournament { Logger.Error(ex, "Failed to read last drawings results."); } - } else { diff --git a/osu.Game/Screens/Tournament/Group.cs b/osu.Game/Screens/Tournament/Group.cs index 90ee90901f..441a5c7bcd 100644 --- a/osu.Game/Screens/Tournament/Group.cs +++ b/osu.Game/Screens/Tournament/Group.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; using osu.Game.Screens.Tournament.Teams; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Tournament { diff --git a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs b/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs index 3eea239f55..a7019f1e35 100644 --- a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs +++ b/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Transforms; @@ -83,6 +84,7 @@ namespace osu.Game.Screens.Tournament private ScrollState scrollState { get { return _scrollState; } + set { if (_scrollState == value) @@ -306,7 +308,7 @@ namespace osu.Game.Screens.Tournament Scrolling } - public class TransformScrollSpeed : TransformFloat + public class TransformScrollSpeed : TransformFloat { public override void Apply(Drawable d) { @@ -329,6 +331,7 @@ namespace osu.Game.Screens.Tournament public bool Selected { get { return selected; } + set { selected = value; diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index bdfe6d1c8e..8cff3517a3 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -8,6 +8,7 @@ using osu.Framework.Configuration; 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.Textures; using osu.Game.Graphics; @@ -34,7 +35,7 @@ namespace osu.Game.Users Height = height - status_height; Masking = true; CornerRadius = 5; - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.25f), @@ -68,7 +69,7 @@ namespace osu.Game.Users User = user, Masking = true, CornerRadius = 5, - EdgeEffect = new EdgeEffect + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.25f), diff --git a/osu.Game/Users/UserStatus.cs b/osu.Game/Users/UserStatus.cs index 461008db0f..37b796630b 100644 --- a/osu.Game/Users/UserStatus.cs +++ b/osu.Game/Users/UserStatus.cs @@ -48,7 +48,7 @@ namespace osu.Game.Users public override string Message => @"Solo Game"; } - public class UserStatusMultiplayerGame: UserStatusBusy + public class UserStatusMultiplayerGame : UserStatusBusy { public override string Message => @"Multiplaying"; } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5e673e0477..64b81ddc6a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -39,11 +39,13 @@ $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll + + $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll + True - - $(SolutionDir)\packages\SharpCompress.0.15.2\lib\net45\SharpCompress.dll + + True + $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll $(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll @@ -74,9 +76,17 @@ + + + + + + + + @@ -85,6 +95,7 @@ + @@ -104,7 +115,6 @@ - @@ -117,7 +127,7 @@ - + @@ -125,6 +135,7 @@ + @@ -174,6 +185,7 @@ + @@ -190,6 +202,10 @@ + + + + @@ -208,6 +224,7 @@ + @@ -239,11 +256,17 @@ + + + + + + + - @@ -255,7 +278,7 @@ - + @@ -293,6 +316,7 @@ + @@ -433,6 +457,9 @@ + + + @@ -441,16 +468,24 @@ - - + + + + + + + + + + diff --git a/osu.Game/packages.config b/osu.Game/packages.config index d51b0033ed..434f9328ea 100644 --- a/osu.Game/packages.config +++ b/osu.Game/packages.config @@ -6,8 +6,8 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - - + +