diff --git a/.vscode/launch.json b/.vscode/launch.json index b3b86da42f..f1083179b8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,34 +1,22 @@ { "version": "0.2.0", - "configurations": [ - { - "name": "VisualTests (debug)", + "configurations": [{ + "name": "osu! (VisualTests)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop.VisualTests/bin/Debug/osu!.exe", + "program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe", + "args": [ + "--tests" + ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug)", "runtimeExecutable": null, "env": {}, "console": "internalConsole" }, - { - "name": "VisualTests (release)", - "windows": { - "type": "clr" - }, - "type": "mono", - "request": "launch", - "program": "${workspaceRoot}/osu.Desktop.VisualTests/bin/Release/osu!.exe", - "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Release)", - "runtimeExecutable": null, - "env": {}, - "console": "internalConsole" - }, { "name": "osu! (debug)", "windows": { diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b0fd5fbb0d..3db43ca9bb 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,35 +2,41 @@ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", - "problemMatcher": "$msCompile", - "isShellCommand": true, "command": "msbuild", + "type": "shell", "suppressTaskName": true, - "showOutput": "silent", "args": [ "/property:GenerateFullPaths=true", "/property:DebugType=portable", + "/verbosity:minimal", "/m" //parallel compiling support. ], - "tasks": [ - { + "tasks": [{ "taskName": "Build (Debug)", - "isBuildCommand": true + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$msCompile" + ] }, { "taskName": "Build (Release)", "args": [ "/property:Configuration=Release" + ], + "problemMatcher": [ + "$msCompile" ] }, - { - "taskName": "Clean All", - "dependsOn": ["Clean (Debug)", "Clean (Release)"] - }, { "taskName": "Clean (Debug)", "args": [ "/target:Clean" + ], + "problemMatcher": [ + "$msCompile" ] }, { @@ -38,6 +44,19 @@ "args": [ "/target:Clean", "/property:Configuration=Release" + ], + "problemMatcher": [ + "$msCompile" + ] + }, + { + "taskName": "Clean All", + "dependsOn": [ + "Clean (Debug)", + "Clean (Release)" + ], + "problemMatcher": [ + "$msCompile" ] } ] diff --git a/osu-framework b/osu-framework index 5a9ca94fc3..107c551767 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 5a9ca94fc31bc796b45572eb3d0b27b46556c586 +Subproject commit 107c5517670ca88dbe8c83a97e37e99ac5742ee6 diff --git a/osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs b/osu.Desktop.Tests/Beatmaps/TestWorkingBeatmap.cs similarity index 91% rename from osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs rename to osu.Desktop.Tests/Beatmaps/TestWorkingBeatmap.cs index b45574b761..084cfab309 100644 --- a/osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Desktop.Tests/Beatmaps/TestWorkingBeatmap.cs @@ -5,7 +5,7 @@ using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; -namespace osu.Desktop.VisualTests.Beatmaps +namespace osu.Desktop.Tests.Beatmaps { public class TestWorkingBeatmap : WorkingBeatmap { diff --git a/osu.Desktop.VisualTests/Platform/TestStorage.cs b/osu.Desktop.Tests/Platform/TestStorage.cs similarity index 91% rename from osu.Desktop.VisualTests/Platform/TestStorage.cs rename to osu.Desktop.Tests/Platform/TestStorage.cs index f711ddac24..39e4d8049f 100644 --- a/osu.Desktop.VisualTests/Platform/TestStorage.cs +++ b/osu.Desktop.Tests/Platform/TestStorage.cs @@ -8,7 +8,7 @@ using SQLite.Net.Interop; using SQLite.Net.Platform.Generic; using SQLite.Net.Platform.Win32; -namespace osu.Desktop.VisualTests.Platform +namespace osu.Desktop.Tests.Platform { public class TestStorage : DesktopStorage { diff --git a/osu.Desktop.Tests/Visual/OsuTestCase.cs b/osu.Desktop.Tests/Visual/OsuTestCase.cs new file mode 100644 index 0000000000..e54f7dbeb5 --- /dev/null +++ b/osu.Desktop.Tests/Visual/OsuTestCase.cs @@ -0,0 +1,37 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Desktop.Platform; +using osu.Framework.Testing; +using osu.Game; + +namespace osu.Desktop.Tests.Visual +{ + [TestFixture] + public abstract class OsuTestCase : TestCase + { + [Test] + public override void RunTest() + { + using (var host = new HeadlessGameHost()) + host.Run(new OsuTestCaseTestRunner(this)); + } + + public class OsuTestCaseTestRunner : OsuGameBase + { + private readonly OsuTestCase testCase; + + public OsuTestCaseTestRunner(OsuTestCase testCase) + { + this.testCase = testCase; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Add(new TestCaseTestRunner.TestRunner(testCase)); + } + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatSyncedContainer.cs b/osu.Desktop.Tests/Visual/TestCaseBeatSyncedContainer.cs similarity index 96% rename from osu.Desktop.VisualTests/Tests/TestCaseBeatSyncedContainer.cs rename to osu.Desktop.Tests/Visual/TestCaseBeatSyncedContainer.cs index 50d1df9964..130a034133 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseBeatSyncedContainer.cs +++ b/osu.Desktop.Tests/Visual/TestCaseBeatSyncedContainer.cs @@ -1,24 +1,23 @@ // 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.Framework.Graphics; -using osu.Framework.Timing; -using osu.Game.Overlays; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Containers; -using osu.Framework.Audio.Track; -using osu.Game.Beatmaps.ControlPoints; -using osu.Framework.Graphics.Shapes; -using OpenTK.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Framework.Lists; using System; +using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Lists; +using osu.Framework.Timing; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using OpenTK.Graphics; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseBeatSyncedContainer : TestCase + internal class TestCaseBeatSyncedContainer : OsuTestCase { public override string Description => @"Tests beat synced containers."; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs b/osu.Desktop.Tests/Visual/TestCaseBeatmapDetailArea.cs similarity index 78% rename from osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs rename to osu.Desktop.Tests/Visual/TestCaseBeatmapDetailArea.cs index e0a503bc76..23b4ece4ec 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs +++ b/osu.Desktop.Tests/Visual/TestCaseBeatmapDetailArea.cs @@ -1,14 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; +using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Testing; using osu.Game.Screens.Select; +using OpenTK; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseBeatmapDetailArea : TestCase + [TestFixture] + internal class TestCaseBeatmapDetailArea : OsuTestCase { public override string Description => @"Beatmap details in song select"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs b/osu.Desktop.Tests/Visual/TestCaseBeatmapDetails.cs similarity index 92% rename from osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs rename to osu.Desktop.Tests/Visual/TestCaseBeatmapDetails.cs index 111bc03377..11a15cf56f 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs +++ b/osu.Desktop.Tests/Visual/TestCaseBeatmapDetails.cs @@ -1,15 +1,14 @@ // 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.Screens.Select; using System.Linq; +using osu.Framework.Graphics; using osu.Game.Beatmaps; +using osu.Game.Screens.Select; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseBeatmapDetails : TestCase + internal class TestCaseBeatmapDetails : OsuTestCase { public override string Description => "BeatmapDetails tab of BeatmapDetailArea"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs b/osu.Desktop.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs similarity index 86% rename from osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs rename to osu.Desktop.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs index c9c1740856..3265f8ec76 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs +++ b/osu.Desktop.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs @@ -1,15 +1,14 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; -using OpenTK.Input; -using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Screens.Select.Options; +using OpenTK.Graphics; +using OpenTK.Input; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseBeatmapOptionsOverlay : TestCase + internal class TestCaseBeatmapOptionsOverlay : OsuTestCase { public override string Description => @"Beatmap options in song select"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBreadcrumbs.cs b/osu.Desktop.Tests/Visual/TestCaseBreadcrumbs.cs similarity index 86% rename from osu.Desktop.VisualTests/Tests/TestCaseBreadcrumbs.cs rename to osu.Desktop.Tests/Visual/TestCaseBreadcrumbs.cs index f2dd454d65..10e0c784ab 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseBreadcrumbs.cs +++ b/osu.Desktop.Tests/Visual/TestCaseBreadcrumbs.cs @@ -1,13 +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.Testing; -using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseBreadcrumbs : TestCase + internal class TestCaseBreadcrumbs : OsuTestCase { public override string Description => @"breadcrumb > control"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs b/osu.Desktop.Tests/Visual/TestCaseChatDisplay.cs similarity index 77% rename from osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs rename to osu.Desktop.Tests/Visual/TestCaseChatDisplay.cs index 751b979bad..ae051a1e40 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs +++ b/osu.Desktop.Tests/Visual/TestCaseChatDisplay.cs @@ -1,13 +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.Testing; using osu.Framework.Graphics.Containers; using osu.Game.Overlays; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseChatDisplay : TestCase + internal class TestCaseChatDisplay : OsuTestCase { public override string Description => @"Testing chat api and overlay"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs b/osu.Desktop.Tests/Visual/TestCaseContextMenu.cs similarity index 94% rename from osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs rename to osu.Desktop.Tests/Visual/TestCaseContextMenu.cs index 0b4e8a5799..f0894f794a 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs +++ b/osu.Desktop.Tests/Visual/TestCaseContextMenu.cs @@ -1,19 +1,18 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using 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.UserInterface; -using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; +using OpenTK; +using OpenTK.Graphics; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseContextMenu : TestCase + internal class TestCaseContextMenu : OsuTestCase { public override string Description => @"Menu visible on right click"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs b/osu.Desktop.Tests/Visual/TestCaseDialogOverlay.cs similarity index 92% rename from osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs rename to osu.Desktop.Tests/Visual/TestCaseDialogOverlay.cs index 6924817827..df15350453 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs +++ b/osu.Desktop.Tests/Visual/TestCaseDialogOverlay.cs @@ -1,14 +1,13 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Dialog; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseDialogOverlay : TestCase + internal class TestCaseDialogOverlay : OsuTestCase { public override string Description => @"Display dialogs"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs b/osu.Desktop.Tests/Visual/TestCaseDirect.cs similarity index 96% rename from osu.Desktop.VisualTests/Tests/TestCaseDirect.cs rename to osu.Desktop.Tests/Visual/TestCaseDirect.cs index 4a5ff1b576..b78ea02767 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs +++ b/osu.Desktop.Tests/Visual/TestCaseDirect.cs @@ -3,14 +3,13 @@ using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Rulesets; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - public class TestCaseDirect : TestCase + public class TestCaseDirect : OsuTestCase { public override string Description => @"osu!direct overlay"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs b/osu.Desktop.Tests/Visual/TestCaseDrawableRoom.cs similarity index 95% rename from osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs rename to osu.Desktop.Tests/Visual/TestCaseDrawableRoom.cs index 38cf03d60e..04d551afe4 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs +++ b/osu.Desktop.Tests/Visual/TestCaseDrawableRoom.cs @@ -1,19 +1,18 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osu.Framework.Testing; -using osu.Game.Screens.Multiplayer; -using osu.Game.Online.Multiplayer; -using osu.Game.Users; using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; +using osu.Game.Screens.Multiplayer; +using osu.Game.Users; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseDrawableRoom : TestCase + internal class TestCaseDrawableRoom : OsuTestCase { public override string Description => @"Select your favourite room"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs b/osu.Desktop.Tests/Visual/TestCaseDrawings.cs similarity index 92% rename from osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs rename to osu.Desktop.Tests/Visual/TestCaseDrawings.cs index 63ec06963c..81fd8cad03 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs +++ b/osu.Desktop.Tests/Visual/TestCaseDrawings.cs @@ -2,13 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using osu.Framework.Testing; using osu.Game.Screens.Tournament; using osu.Game.Screens.Tournament.Teams; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseDrawings : TestCase + internal class TestCaseDrawings : OsuTestCase { public override string Description => "Tournament drawings"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs b/osu.Desktop.Tests/Visual/TestCaseGamefield.cs similarity index 93% rename from osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs rename to osu.Desktop.Tests/Visual/TestCaseGamefield.cs index 0b08065241..9d61c9ab0c 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseGamefield.cs @@ -1,28 +1,27 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; +using System.Collections.Generic; +using osu.Desktop.Tests.Beatmaps; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; -using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Taiko.UI; -using System.Collections.Generic; -using osu.Desktop.VisualTests.Beatmaps; -using osu.Framework.Allocation; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets; +using OpenTK; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseGamefield : TestCase + internal class TestCaseGamefield : OsuTestCase { private RulesetStore rulesets; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGraph.cs b/osu.Desktop.Tests/Visual/TestCaseGraph.cs similarity index 90% rename from osu.Desktop.VisualTests/Tests/TestCaseGraph.cs rename to osu.Desktop.Tests/Visual/TestCaseGraph.cs index d969decaa5..13342d1ff1 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseGraph.cs +++ b/osu.Desktop.Tests/Visual/TestCaseGraph.cs @@ -1,15 +1,14 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Testing; -using osu.Game.Graphics.UserInterface; using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using OpenTK; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseGraph : TestCase + internal class TestCaseGraph : OsuTestCase { public override string Description => "graph"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.Tests/Visual/TestCaseHitObjects.cs similarity index 94% rename from osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs rename to osu.Desktop.Tests/Visual/TestCaseHitObjects.cs index 33841cae90..df5f9f65b8 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.Tests/Visual/TestCaseHitObjects.cs @@ -1,24 +1,23 @@ // 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 System.Collections.Generic; 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.Framework.Testing; using osu.Framework.Timing; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using System.Collections.Generic; +using OpenTK; +using OpenTK.Graphics; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseHitObjects : TestCase + internal class TestCaseHitObjects : OsuTestCase { private readonly FramedClock framedClock; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs b/osu.Desktop.Tests/Visual/TestCaseKeyCounter.cs similarity index 93% rename from osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs rename to osu.Desktop.Tests/Visual/TestCaseKeyCounter.cs index f65dbe7a64..efb662d3b9 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs +++ b/osu.Desktop.Tests/Visual/TestCaseKeyCounter.cs @@ -1,22 +1,21 @@ // 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.Framework.Graphics; -using OpenTK.Input; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Configuration; +using osu.Framework.Graphics; 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.Framework.Graphics.UserInterface; +using osu.Framework.MathUtils; using osu.Game.Screens.Play; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseKeyCounter : TestCase + internal class TestCaseKeyCounter : OsuTestCase { public override string Description => @"Tests key counter"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs b/osu.Desktop.Tests/Visual/TestCaseLeaderboard.cs similarity index 95% rename from osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs rename to osu.Desktop.Tests/Visual/TestCaseLeaderboard.cs index 12d01ecc79..5f8ee8795c 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs +++ b/osu.Desktop.Tests/Visual/TestCaseLeaderboard.cs @@ -1,18 +1,17 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; using osu.Framework.Graphics; -using osu.Framework.Testing; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; +using OpenTK; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseLeaderboard : TestCase + internal class TestCaseLeaderboard : OsuTestCase { public override string Description => @"From song select"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs b/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs similarity index 93% rename from osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs rename to osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs index 30346e90c9..7dcc48c778 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs +++ b/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs @@ -3,15 +3,14 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Testing; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; -using OpenTK.Graphics; using OpenTK; +using OpenTK.Graphics; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseManiaHitObjects : TestCase + internal class TestCaseManiaHitObjects : OsuTestCase { public TestCaseManiaHitObjects() { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs similarity index 95% rename from osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs rename to osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs index adaae91815..ed0e5d81e9 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs @@ -1,25 +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.Input; -using osu.Framework.Testing; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Mania.UI; using System; -using OpenTK; -using osu.Game.Rulesets.Mania.Objects.Drawables; -using osu.Game.Rulesets.Mania.Objects; -using osu.Framework.Configuration; -using OpenTK.Input; -using osu.Framework.Timing; -using osu.Framework.Extensions.IEnumerableExtensions; using System.Linq; +using osu.Framework.Configuration; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Framework.Timing; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Timing; +using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Timing; +using OpenTK; +using OpenTK.Input; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseManiaPlayfield : TestCase + internal class TestCaseManiaPlayfield : OsuTestCase { public override string Description => @"Mania playfield"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMedalOverlay.cs b/osu.Desktop.Tests/Visual/TestCaseMedalOverlay.cs similarity index 82% rename from osu.Desktop.VisualTests/Tests/TestCaseMedalOverlay.cs rename to osu.Desktop.Tests/Visual/TestCaseMedalOverlay.cs index 1533f2141e..747f2190b0 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMedalOverlay.cs +++ b/osu.Desktop.Tests/Visual/TestCaseMedalOverlay.cs @@ -1,13 +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.Testing; using osu.Game.Overlays; using osu.Game.Users; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseMedalOverlay : TestCase + internal class TestCaseMedalOverlay : OsuTestCase { public override string Description => @"medal get!"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs b/osu.Desktop.Tests/Visual/TestCaseMenuButtonSystem.cs similarity index 81% rename from osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs rename to osu.Desktop.Tests/Visual/TestCaseMenuButtonSystem.cs index ea2f0464c4..3c7ee343bb 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs +++ b/osu.Desktop.Tests/Visual/TestCaseMenuButtonSystem.cs @@ -1,15 +1,14 @@ // 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.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; using osu.Game.Screens.Menu; using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseMenuButtonSystem : TestCase + internal class TestCaseMenuButtonSystem : OsuTestCase { public override string Description => @"Main menu button system"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs b/osu.Desktop.Tests/Visual/TestCaseMenuOverlays.cs similarity index 90% rename from osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs rename to osu.Desktop.Tests/Visual/TestCaseMenuOverlays.cs index 0187c0e629..1f4ad9d3da 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs +++ b/osu.Desktop.Tests/Visual/TestCaseMenuOverlays.cs @@ -3,12 +3,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Logging; -using osu.Framework.Testing; using osu.Game.Screens.Play; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseMenuOverlays : TestCase + internal class TestCaseMenuOverlays : OsuTestCase { public override string Description => @"Tests pause and fail overlays"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMods.cs b/osu.Desktop.Tests/Visual/TestCaseMods.cs similarity index 90% rename from osu.Desktop.VisualTests/Tests/TestCaseMods.cs rename to osu.Desktop.Tests/Visual/TestCaseMods.cs index 1604be603a..f48030067d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMods.cs +++ b/osu.Desktop.Tests/Visual/TestCaseMods.cs @@ -4,14 +4,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Overlays.Mods; -using osu.Framework.Testing; using osu.Game.Rulesets; using osu.Game.Screens.Play.HUD; using OpenTK; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseMods : TestCase + internal class TestCaseMods : OsuTestCase { public override string Description => @"Mod select overlay and in-game display"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs b/osu.Desktop.Tests/Visual/TestCaseMusicController.cs similarity index 58% rename from osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs rename to osu.Desktop.Tests/Visual/TestCaseMusicController.cs index cbb2775234..8d71527a21 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs +++ b/osu.Desktop.Tests/Visual/TestCaseMusicController.cs @@ -1,18 +1,23 @@ // 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.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Timing; -using osu.Game.Overlays; using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game; +using osu.Game.Beatmaps; +using osu.Game.Overlays; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseMusicController : TestCase + internal class TestCaseMusicController : OsuTestCase { public override string Description => @"Tests music controller ui."; + private readonly Bindable beatmapBacking = new Bindable(); + public TestCaseMusicController() { Clock = new FramedClock(); @@ -26,6 +31,13 @@ namespace osu.Desktop.VisualTests.Tests AddToggleStep(@"toggle visibility", state => mc.State = state ? Visibility.Visible : Visibility.Hidden); AddStep(@"show", () => mc.State = Visibility.Visible); + AddToggleStep(@"toggle beatmap lock", state => beatmapBacking.Disabled = state); + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + beatmapBacking.BindTo(game.Beatmap); } } } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs b/osu.Desktop.Tests/Visual/TestCaseNotificationOverlay.cs similarity index 89% rename from osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs rename to osu.Desktop.Tests/Visual/TestCaseNotificationOverlay.cs index 849df1263e..b9e492593f 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs +++ b/osu.Desktop.Tests/Visual/TestCaseNotificationOverlay.cs @@ -2,27 +2,28 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Testing; +using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; using osu.Game.Overlays; -using System.Linq; using osu.Game.Overlays.Notifications; -using osu.Framework.Graphics.Containers; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseNotificationManager : TestCase + [TestFixture] + internal class TestCaseNotificationOverlay : OsuTestCase { public override string Description => @"I handle notifications"; - private readonly NotificationManager manager; + private readonly NotificationOverlay manager; - public TestCaseNotificationManager() + public TestCaseNotificationOverlay() { progressingNotifications.Clear(); - Content.Add(manager = new NotificationManager + Content.Add(manager = new NotificationOverlay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, diff --git a/osu.Desktop.VisualTests/Tests/TestCaseOnScreenDisplay.cs b/osu.Desktop.Tests/Visual/TestCaseOnScreenDisplay.cs similarity index 88% rename from osu.Desktop.VisualTests/Tests/TestCaseOnScreenDisplay.cs rename to osu.Desktop.Tests/Visual/TestCaseOnScreenDisplay.cs index f2b4ed7918..0b7a822e1d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseOnScreenDisplay.cs +++ b/osu.Desktop.Tests/Visual/TestCaseOnScreenDisplay.cs @@ -3,12 +3,11 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Testing; using osu.Game.Overlays; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseOnScreenDisplay : TestCase + internal class TestCaseOnScreenDisplay : OsuTestCase { private FrameworkConfigManager config; private Bindable frameSyncMode; diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.Tests/Visual/TestCasePlaySongSelect.cs similarity index 93% rename from osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs rename to osu.Desktop.Tests/Visual/TestCasePlaySongSelect.cs index 51c78ff442..379100b543 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.Tests/Visual/TestCasePlaySongSelect.cs @@ -2,8 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using osu.Desktop.VisualTests.Platform; -using osu.Framework.Testing; +using osu.Desktop.Tests.Platform; using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Database; @@ -11,9 +10,9 @@ using osu.Game.Rulesets; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCasePlaySongSelect : TestCase + internal class TestCasePlaySongSelect : OsuTestCase { private readonly BeatmapManager manager; diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.Tests/Visual/TestCasePlayer.cs similarity index 90% rename from osu.Desktop.VisualTests/Tests/TestCasePlayer.cs rename to osu.Desktop.Tests/Visual/TestCasePlayer.cs index f38cabd618..f5d02d3f2b 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs +++ b/osu.Desktop.Tests/Visual/TestCasePlayer.cs @@ -2,22 +2,21 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Desktop.Tests.Beatmaps; using osu.Framework.Allocation; -using osu.Framework.Testing; +using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; -using OpenTK; +using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Screens.Play; -using OpenTK.Graphics; -using osu.Desktop.VisualTests.Beatmaps; using osu.Game.Rulesets.Osu.UI; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets; +using osu.Game.Screens.Play; +using OpenTK; +using OpenTK.Graphics; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCasePlayer : TestCase + internal class TestCasePlayer : OsuTestCase { protected Player Player; private RulesetStore rulesets; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseReplay.cs b/osu.Desktop.Tests/Visual/TestCaseReplay.cs similarity index 90% rename from osu.Desktop.VisualTests/Tests/TestCaseReplay.cs rename to osu.Desktop.Tests/Visual/TestCaseReplay.cs index e00a912278..9b2f59e444 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseReplay.cs +++ b/osu.Desktop.Tests/Visual/TestCaseReplay.cs @@ -6,7 +6,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Play; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { internal class TestCaseReplay : TestCasePlayer { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseReplaySettingsOverlay.cs b/osu.Desktop.Tests/Visual/TestCaseReplaySettingsOverlay.cs similarity index 88% rename from osu.Desktop.VisualTests/Tests/TestCaseReplaySettingsOverlay.cs rename to osu.Desktop.Tests/Visual/TestCaseReplaySettingsOverlay.cs index 00a9774067..ab865dcab0 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseReplaySettingsOverlay.cs +++ b/osu.Desktop.Tests/Visual/TestCaseReplaySettingsOverlay.cs @@ -2,14 +2,13 @@ // 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 +namespace osu.Desktop.Tests.Visual { - internal class TestCaseReplaySettingsOverlay : TestCase + internal class TestCaseReplaySettingsOverlay : OsuTestCase { public override string Description => @"Settings visible in replay/auto"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs b/osu.Desktop.Tests/Visual/TestCaseResults.cs similarity index 90% rename from osu.Desktop.VisualTests/Tests/TestCaseResults.cs rename to osu.Desktop.Tests/Visual/TestCaseResults.cs index 288fff346f..a0622b302a 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs +++ b/osu.Desktop.Tests/Visual/TestCaseResults.cs @@ -4,15 +4,14 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Users; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseResults : TestCase + internal class TestCaseResults : OsuTestCase { private BeatmapManager beatmaps; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs b/osu.Desktop.Tests/Visual/TestCaseRoomInspector.cs similarity index 95% rename from osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs rename to osu.Desktop.Tests/Visual/TestCaseRoomInspector.cs index 043f072b25..db557baed2 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs +++ b/osu.Desktop.Tests/Visual/TestCaseRoomInspector.cs @@ -1,18 +1,17 @@ // 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.Framework.Graphics; -using osu.Game.Screens.Multiplayer; -using osu.Game.Online.Multiplayer; -using osu.Game.Users; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; +using osu.Game.Screens.Multiplayer; +using osu.Game.Users; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseRoomInspector : TestCase + internal class TestCaseRoomInspector : OsuTestCase { public override string Description => @"from the multiplayer lobby"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs b/osu.Desktop.Tests/Visual/TestCaseScoreCounter.cs similarity index 93% rename from osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs rename to osu.Desktop.Tests/Visual/TestCaseScoreCounter.cs index fc29e8481e..ac96deb209 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs +++ b/osu.Desktop.Tests/Visual/TestCaseScoreCounter.cs @@ -1,17 +1,16 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; -using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play.HUD; +using OpenTK; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseScoreCounter : TestCase + internal class TestCaseScoreCounter : OsuTestCase { public override string Description => @"Tests multiple counters"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs b/osu.Desktop.Tests/Visual/TestCaseScrollingHitObjects.cs similarity index 95% rename from osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs rename to osu.Desktop.Tests/Visual/TestCaseScrollingHitObjects.cs index 43f30a96b0..efc129a678 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs +++ b/osu.Desktop.Tests/Visual/TestCaseScrollingHitObjects.cs @@ -1,23 +1,22 @@ // 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; +using OpenTK; +using OpenTK.Graphics; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - public class TestCaseScrollingHitObjects : TestCase + public class TestCaseScrollingHitObjects : OsuTestCase { public override string Description => "SpeedAdjustmentContainer/DrawableTimingSection"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSettings.cs b/osu.Desktop.Tests/Visual/TestCaseSettings.cs similarity index 80% rename from osu.Desktop.VisualTests/Tests/TestCaseSettings.cs rename to osu.Desktop.Tests/Visual/TestCaseSettings.cs index 3d21f0e3b1..7b35009aef 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseSettings.cs +++ b/osu.Desktop.Tests/Visual/TestCaseSettings.cs @@ -1,12 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Testing; using osu.Game.Overlays; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseSettings : TestCase + internal class TestCaseSettings : OsuTestCase { public override string Description => @"Tests the settings overlay"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSkipButton.cs b/osu.Desktop.Tests/Visual/TestCaseSkipButton.cs similarity index 75% rename from osu.Desktop.VisualTests/Tests/TestCaseSkipButton.cs rename to osu.Desktop.Tests/Visual/TestCaseSkipButton.cs index 1f81226a8e..0e73314850 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseSkipButton.cs +++ b/osu.Desktop.Tests/Visual/TestCaseSkipButton.cs @@ -1,12 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Testing; using osu.Game.Screens.Play; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseSkipButton : TestCase + internal class TestCaseSkipButton : OsuTestCase { public override string Description => @"Skip skip skippediskip"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSocial.cs b/osu.Desktop.Tests/Visual/TestCaseSocial.cs similarity index 94% rename from osu.Desktop.VisualTests/Tests/TestCaseSocial.cs rename to osu.Desktop.Tests/Visual/TestCaseSocial.cs index 34209119bd..da60c82cea 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseSocial.cs +++ b/osu.Desktop.Tests/Visual/TestCaseSocial.cs @@ -1,13 +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.Testing; using osu.Game.Overlays; using osu.Game.Users; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - public class TestCaseSocial : TestCase + public class TestCaseSocial : OsuTestCase { public override string Description => @"social browser overlay"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSongProgress.cs b/osu.Desktop.Tests/Visual/TestCaseSongProgress.cs similarity index 91% rename from osu.Desktop.VisualTests/Tests/TestCaseSongProgress.cs rename to osu.Desktop.Tests/Visual/TestCaseSongProgress.cs index 3368224be1..fceb773ae6 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseSongProgress.cs +++ b/osu.Desktop.Tests/Visual/TestCaseSongProgress.cs @@ -4,14 +4,13 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.MathUtils; -using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Play; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseSongProgress : TestCase + internal class TestCaseSongProgress : OsuTestCase { public override string Description => @"With fake data"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs b/osu.Desktop.Tests/Visual/TestCaseTabControl.cs similarity index 88% rename from osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs rename to osu.Desktop.Tests/Visual/TestCaseTabControl.cs index c0c01a6daa..4bdea2615d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs +++ b/osu.Desktop.Tests/Visual/TestCaseTabControl.cs @@ -2,15 +2,14 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using OpenTK; -using osu.Framework.Testing; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Select.Filter; +using OpenTK; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - public class TestCaseTabControl : TestCase + public class TestCaseTabControl : OsuTestCase { public override string Description => @"Filter for song select"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs similarity index 84% rename from osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs rename to osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs index 8ca129eb91..45be9e800d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs @@ -1,24 +1,25 @@ // 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 osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; -using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.UI; -using System; +using OpenTK; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseTaikoPlayfield : TestCase + internal class TestCaseTaikoPlayfield : OsuTestCase { - private const double default_duration = 300; + private const double default_duration = 1000; private const float scroll_time = 1000; public override string Description => "Taiko playfield"; @@ -31,7 +32,8 @@ namespace osu.Desktop.VisualTests.Tests public TestCaseTaikoPlayfield() { - AddStep("Hit!", addHitJudgement); + AddStep("Hit!", () => addHitJudgement(false)); + AddStep("Kiai hit", () => addHitJudgement(true)); AddStep("Miss :(", addMissJudgement); AddStep("DrumRoll", () => addDrumRoll(false)); AddStep("Strong DrumRoll", () => addDrumRoll(true)); @@ -56,7 +58,7 @@ namespace osu.Desktop.VisualTests.Tests Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, - Height = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT, + Height = TaikoPlayfield.DEFAULT_HEIGHT, Clock = new FramedClock(rateAdjustClock), Children = new[] { @@ -85,7 +87,7 @@ namespace osu.Desktop.VisualTests.Tests addDrumRoll(true); break; case 5: - addSwell(1000); + addSwell(); delay = scroll_time - 100; break; } @@ -97,16 +99,25 @@ namespace osu.Desktop.VisualTests.Tests playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, rng.Next(25, 400)), 500); break; case 6: - playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT), 500); + playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_HEIGHT), 500); break; } } - private void addHitJudgement() + private void addHitJudgement(bool kiai) { TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great; - var h = new DrawableTestHit(new Hit()) + var cpi = new ControlPointInfo(); + cpi.EffectPoints.Add(new EffectControlPoint + { + KiaiMode = kiai + }); + + Hit hit = new Hit(); + hit.ApplyDefaults(cpi, new BeatmapDifficulty()); + + var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f), Judgement = new TaikoJudgement @@ -180,7 +191,8 @@ namespace osu.Desktop.VisualTests.Tests Hit h = new Hit { StartTime = playfield.Time.Current + scroll_time, - ScrollTime = scroll_time + ScrollTime = scroll_time, + IsStrong = strong }; if (strong) @@ -194,7 +206,8 @@ namespace osu.Desktop.VisualTests.Tests Hit h = new Hit { StartTime = playfield.Time.Current + scroll_time, - ScrollTime = scroll_time + ScrollTime = scroll_time, + IsStrong = strong }; if (strong) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs b/osu.Desktop.Tests/Visual/TestCaseTextAwesome.cs similarity index 83% rename from osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs rename to osu.Desktop.Tests/Visual/TestCaseTextAwesome.cs index 2824c0416f..b98c0f700d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs +++ b/osu.Desktop.Tests/Visual/TestCaseTextAwesome.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using osu.Framework.Testing; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; @@ -10,9 +9,9 @@ using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseTextAwesome : TestCase + internal class TestCaseTextAwesome : OsuTestCase { public override string Description => @"Tests display of icons"; @@ -31,10 +30,10 @@ namespace osu.Desktop.VisualTests.Tests int i = 50; foreach (FontAwesome fa in Enum.GetValues(typeof(FontAwesome))) { - flow.Add(new TextAwesome + flow.Add(new SpriteIcon { Icon = fa, - TextSize = 60, + Size = new Vector2(60), Colour = new Color4( Math.Max(0.5f, RNG.NextSingle()), Math.Max(0.5f, RNG.NextSingle()), diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs b/osu.Desktop.Tests/Visual/TestCaseTwoLayerButton.cs similarity index 72% rename from osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs rename to osu.Desktop.Tests/Visual/TestCaseTwoLayerButton.cs index 0c35a4b8aa..ac641d75a2 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs +++ b/osu.Desktop.Tests/Visual/TestCaseTwoLayerButton.cs @@ -1,12 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseTwoLayerButton : TestCase + internal class TestCaseTwoLayerButton : OsuTestCase { public override string Description => @"Mostly back button"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseUserPanel.cs b/osu.Desktop.Tests/Visual/TestCaseUserPanel.cs similarity index 92% rename from osu.Desktop.VisualTests/Tests/TestCaseUserPanel.cs rename to osu.Desktop.Tests/Visual/TestCaseUserPanel.cs index 22cdf42f7d..b42fd3136d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseUserPanel.cs +++ b/osu.Desktop.Tests/Visual/TestCaseUserPanel.cs @@ -1,15 +1,14 @@ // 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.Framework.Graphics; -using osu.Game.Users; using osu.Framework.Graphics.Containers; +using osu.Game.Users; using OpenTK; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseUserPanel : TestCase + internal class TestCaseUserPanel : OsuTestCase { public override string Description => @"Panels for displaying a user's status"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseUserProfile.cs b/osu.Desktop.Tests/Visual/TestCaseUserProfile.cs similarity index 92% rename from osu.Desktop.VisualTests/Tests/TestCaseUserProfile.cs rename to osu.Desktop.Tests/Visual/TestCaseUserProfile.cs index d7a2c8e47d..e5955441dc 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseUserProfile.cs +++ b/osu.Desktop.Tests/Visual/TestCaseUserProfile.cs @@ -3,13 +3,12 @@ using System; using System.Linq; -using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Users; -namespace osu.Desktop.VisualTests.Tests +namespace osu.Desktop.Tests.Visual { - internal class TestCaseUserProfile : TestCase + internal class TestCaseUserProfile : OsuTestCase { public override string Description => "Tests user's profile page."; diff --git a/osu.Desktop.Tests/VisualTests.cs b/osu.Desktop.Tests/VisualTests.cs deleted file mode 100644 index 6ef924e873..0000000000 --- a/osu.Desktop.Tests/VisualTests.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Desktop.VisualTests; -using osu.Framework.Desktop.Platform; - -namespace osu.Desktop.Tests -{ - [TestFixture] - public class VisualTests - { - [Test] - public void TestVisualTests() - { - using (var host = new HeadlessGameHost()) - { - host.Run(new AutomatedVisualTestGame()); - } - } - } -} diff --git a/osu.Desktop.Tests/osu.Desktop.Tests.csproj b/osu.Desktop.Tests/osu.Desktop.Tests.csproj index f940e4be9e..aa862498d1 100644 --- a/osu.Desktop.Tests/osu.Desktop.Tests.csproj +++ b/osu.Desktop.Tests/osu.Desktop.Tests.csproj @@ -41,6 +41,10 @@ $(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll True + + $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll + True + False $(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll @@ -55,15 +59,66 @@ $(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {65DC628F-A640-4111-AB35-3A5652BC1E17} osu.Framework.Desktop + + {007b2356-ab6f-4bd9-96d5-116fc2dce69a} + osu.Framework.Testing + {C76BF5B3-985E-4D39-95FE-97C9C879B83A} osu.Framework @@ -72,10 +127,6 @@ {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} osu.Game.Resources - - {69051C69-12AE-4E7D-A3E6-460D2E282312} - osu.Desktop.VisualTests - {58F6C80C-1253-4A0E-A465-B8C85EBEADF3} osu.Game.Rulesets.Catch diff --git a/osu.Desktop.Tests/packages.config b/osu.Desktop.Tests/packages.config index 7bd35a3abe..ed487e5cd5 100644 --- a/osu.Desktop.Tests/packages.config +++ b/osu.Desktop.Tests/packages.config @@ -6,6 +6,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste + diff --git a/osu.Desktop.VisualTests/AutomatedVisualTestGame.cs b/osu.Desktop.VisualTests/AutomatedVisualTestGame.cs deleted file mode 100644 index b08588b29c..0000000000 --- a/osu.Desktop.VisualTests/AutomatedVisualTestGame.cs +++ /dev/null @@ -1,20 +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.Testing; -using osu.Game; - -namespace osu.Desktop.VisualTests -{ - public class AutomatedVisualTestGame : OsuGameBase - { - protected override void LoadComplete() - { - base.LoadComplete(); - - // Have to construct this here, rather than in the constructor, because - // we depend on some dependencies to be loaded within OsuGameBase.load(). - Add(new TestRunner(new TestBrowser())); - } - } -} \ No newline at end of file diff --git a/osu.Desktop.VisualTests/Program.cs b/osu.Desktop.VisualTests/Program.cs index 03d1588b78..62465c69d2 100644 --- a/osu.Desktop.VisualTests/Program.cs +++ b/osu.Desktop.VisualTests/Program.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Desktop; using osu.Framework.Platform; +using osu.Framework.VisualTests; namespace osu.Desktop.VisualTests { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs deleted file mode 100644 index d98e39ae7b..0000000000 --- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Testing; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; - -namespace osu.Desktop.VisualTests.Tests -{ - internal class TestCaseTaikoHitObjects : TestCase - { - public override string Description => "Taiko hit objects"; - - private bool kiai; - - public TestCaseTaikoHitObjects() - { - AddToggleStep("Kiai", b => - { - kiai = !kiai; - updateKiaiState(); - }); - - Add(new CirclePiece - { - Position = new Vector2(100, 100), - AccentColour = Color4.DarkRed, - KiaiMode = kiai, - Children = new[] - { - new CentreHitSymbolPiece() - } - }); - - Add(new CirclePiece(true) - { - Position = new Vector2(350, 100), - AccentColour = Color4.DarkRed, - KiaiMode = kiai, - Children = new[] - { - new CentreHitSymbolPiece() - } - }); - - Add(new CirclePiece - { - Position = new Vector2(100, 300), - AccentColour = Color4.DarkBlue, - KiaiMode = kiai, - Children = new[] - { - new RimHitSymbolPiece() - } - }); - - Add(new CirclePiece(true) - { - Position = new Vector2(350, 300), - AccentColour = Color4.DarkBlue, - KiaiMode = kiai, - Children = new[] - { - new RimHitSymbolPiece() - } - }); - - Add(new CirclePiece - { - Position = new Vector2(100, 500), - AccentColour = Color4.Orange, - KiaiMode = kiai, - Children = new[] - { - new SwellSymbolPiece() - } - }); - - Add(new ElongatedCirclePiece - { - Position = new Vector2(575, 100), - AccentColour = Color4.Orange, - KiaiMode = kiai, - Length = 0.10f, - PlayfieldLengthReference = () => DrawSize.X - }); - - Add(new ElongatedCirclePiece(true) - { - Position = new Vector2(575, 300), - AccentColour = Color4.Orange, - KiaiMode = kiai, - Length = 0.10f, - PlayfieldLengthReference = () => DrawSize.X - }); - } - - private void updateKiaiState() - { - foreach (var c in Children.OfType()) - c.KiaiMode = kiai; - } - - private abstract class BaseCircle : Container - { - protected readonly CirclePiece Piece; - - protected BaseCircle(CirclePiece piece) - { - Piece = piece; - - Add(Piece); - } - } - } -} diff --git a/osu.Desktop.VisualTests/VisualTestGame.cs b/osu.Desktop.VisualTests/VisualTestGame.cs index 5c5bcd9e21..7655f6a59d 100644 --- a/osu.Desktop.VisualTests/VisualTestGame.cs +++ b/osu.Desktop.VisualTests/VisualTestGame.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Platform; -using osu.Framework.Testing; +using osu.Framework.VisualTests; using osu.Game; using osu.Game.Screens.Backgrounds; diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index 00bcaca1e8..64fe3d4b95 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -1,4 +1,4 @@ - + {69051C69-12AE-4E7D-A3E6-460D2E282312} @@ -185,56 +185,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -259,4 +212,4 @@ - \ No newline at end of file + diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index bd5c6c6790..88c8a206c8 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Game; using System.Linq; using System.Windows.Forms; @@ -11,6 +12,7 @@ using System.Reflection; using System.Drawing; using System.IO; using System.Threading.Tasks; +using Microsoft.Win32; using osu.Framework.Graphics.Containers; using osu.Game.Screens.Menu; @@ -30,6 +32,58 @@ namespace osu.Desktop }; } + public override Storage GetStorageForStableInstall() + { + try + { + return new StableStorage(); + } + catch + { + return null; + } + } + + /// + /// A method of accessing an osu-stable install in a controlled fashion. + /// + private class StableStorage : DesktopStorage + { + protected override string LocateBasePath() + { + Func checkExists = p => Directory.Exists(Path.Combine(p, "Songs")); + + string stableInstallPath; + + try + { + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(String.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + + public StableStorage() + : base(string.Empty) + { + } + } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Desktop/OsuTestBrowser.cs b/osu.Desktop/OsuTestBrowser.cs new file mode 100644 index 0000000000..50af9bd317 --- /dev/null +++ b/osu.Desktop/OsuTestBrowser.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.Platform; +using osu.Framework.Testing; +using osu.Game; +using osu.Game.Screens.Backgrounds; + +namespace osu.Desktop +{ + internal class OsuTestBrowser : OsuGameBase + { + protected override void LoadComplete() + { + base.LoadComplete(); + + LoadComponentAsync(new BackgroundScreenDefault { Depth = 10 }, AddInternal); + + // Have to construct this here, rather than in the constructor, because + // we depend on some dependencies to be loaded within OsuGameBase.load(). + Add(new TestBrowser()); + } + + public override void SetHost(GameHost host) + { + base.SetHost(host); + + host.UpdateThread.InactiveHz = host.UpdateThread.ActiveHz; + host.DrawThread.InactiveHz = host.DrawThread.ActiveHz; + host.InputThread.InactiveHz = host.InputThread.ActiveHz; + + host.Window.CursorState |= CursorState.Hidden; + } + } +} \ No newline at end of file diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 71ae5a6697..18fa43ab5c 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -25,16 +25,16 @@ namespace osu.Desktop.Overlays public class VersionManager : OverlayContainer { private UpdateManager updateManager; - private NotificationManager notificationManager; + private NotificationOverlay notificationOverlay; protected override bool HideOnEscape => false; public override bool HandleInput => false; [BackgroundDependencyLoader] - private void load(NotificationManager notification, OsuColour colours, TextureStore textures, OsuGameBase game) + private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game) { - notificationManager = notification; + notificationOverlay = notification; AutoSizeAxes = Axes.Both; Anchor = Anchor.BottomCentre; @@ -116,7 +116,7 @@ namespace osu.Desktop.Overlays if (notification == null) { notification = new UpdateProgressNotification { State = ProgressNotificationState.Active }; - Schedule(() => notificationManager.Post(notification)); + Schedule(() => notificationOverlay.Post(notification)); } Schedule(() => @@ -209,13 +209,13 @@ namespace osu.Desktop.Overlays RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow) }, - new TextAwesome + new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.fa_upload, Colour = Color4.White, - TextSize = 20 + Size = new Vector2(20), } }); } diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 3b63239525..1fab92e020 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Linq; using osu.Framework.Desktop; using osu.Framework.Desktop.Platform; using osu.Game.IPC; @@ -33,7 +34,16 @@ namespace osu.Desktop } else { - host.Run(new OsuGameDesktop(args)); + switch (args.FirstOrDefault() ?? string.Empty) + { + case "--tests": + host.Run(new OsuTestBrowser()); + break; + default: + host.Run(new OsuGameDesktop(args)); + break; + } + } return 0; } diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 82fbefec7a..8ffa6b62f7 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -90,6 +90,20 @@ Properties\app.manifest + + true + bin\Debug\ + DEBUG + true + 0 + true + full + AnyCPU + false + 6 + prompt + AllRules.ruleset + $(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.dll @@ -195,6 +209,10 @@ {65dc628f-a640-4111-ab35-3a5652bc1e17} osu.Framework.Desktop + + {007B2356-AB6F-4BD9-96D5-116FC2DCE69A} + osu.Framework.Testing + {c76bf5b3-985e-4d39-95fe-97c9c879b83a} osu.Framework @@ -203,6 +221,10 @@ {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} osu.Game.Resources + + {230ac4f3-7783-49fb-9aec-b83cda3b9f3d} + osu.Desktop.Tests + {c92a607b-1fdd-4954-9f92-03ff547d9080} osu.Game.Rulesets.Osu @@ -226,6 +248,7 @@ + diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index df212f7df7..dc13329bde 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using System.Collections.Generic; +using osu.Framework.Graphics; using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Scoring; @@ -83,9 +84,11 @@ namespace osu.Game.Rulesets.Catch } } + public override Mod GetAutoplayMod() => new ModAutoplay(); + public override string Description => "osu!catch"; - public override FontAwesome Icon => FontAwesome.fa_osu_fruits_o; + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; public override IEnumerable CreateGameplayKeys() => new KeyCounter[] { diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 8be3870ebe..a8a89a57e0 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -2,13 +2,14 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps; -using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Scoring; @@ -104,9 +105,11 @@ namespace osu.Game.Rulesets.Mania } } + public override Mod GetAutoplayMod() => new ModAutoplay(); + public override string Description => "osu!mania"; - public override FontAwesome Icon => FontAwesome.fa_osu_mania_o; + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; public override IEnumerable CreateGameplayKeys() => new KeyCounter[] { /* Todo: Should be keymod specific */ }; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 2aae1bb24e..4a0b8422f1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly CirclePiece circle; private readonly GlowPiece glow; - private readonly TextAwesome symbol; + private readonly SpriteIcon symbol; private readonly Color4 baseColour = OsuColour.FromHex(@"002c3c"); private readonly Color4 fillColour = OsuColour.FromHex(@"005b7c"); @@ -64,12 +64,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Anchor = Anchor.Centre, }, new RingPiece(), - symbol = new TextAwesome + symbol = new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, - UseFullGlyphHeight = true, - TextSize = 48, + Size = new Vector2(48), Icon = FontAwesome.fa_asterisk, Shadow = false, }, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBouncer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBouncer.cs index 10d14b5485..942f166241 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBouncer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBouncer.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; +using OpenTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -11,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { private readonly Slider slider; private readonly bool isEnd; - private readonly TextAwesome icon; + private readonly SpriteIcon icon; public SliderBouncer(Slider slider, bool isEnd) { @@ -24,12 +25,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Children = new Drawable[] { - icon = new TextAwesome + icon = new SpriteIcon { Icon = FontAwesome.fa_eercast, Anchor = Anchor.Centre, Origin = Anchor.Centre, - TextSize = 48, + Size = new Vector2(48), } }; } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 8e8e186d40..9a1971d791 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Overlays.Settings; @@ -104,7 +105,9 @@ namespace osu.Game.Rulesets.Osu } } - public override FontAwesome Icon => FontAwesome.fa_osu_osu_o; + public override Mod GetAutoplayMod() => new OsuModAutoplay(); + + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o }; public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 4562501ed1..74f0f2326d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Taiko.Judgements; using OpenTK; using OpenTK.Graphics; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; +using osu.Framework.Graphics; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -30,6 +31,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) { + RelativeSizeAxes = Axes.Y; + AutoSizeAxes = Axes.X; + foreach (var tick in drumRoll.Ticks) { var newTick = new DrawableDrumRollTick(tick) @@ -46,7 +50,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = HitObject.IsStrong }; - protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(HitObject.IsStrong) + protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece { Length = (float)(HitObject.Duration / HitObject.ScrollTime), PlayfieldLengthReference = () => Parent.DrawSize.X diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index b64bc64d9b..0e1cd05de3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public DrawableDrumRollTick(DrumRollTick tick) : base(tick) { + FillMode = FillMode.Fit; } protected override TaikoPiece CreateMainPiece() => new TickPiece diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 0d5c8d2a25..5a17355cdb 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -26,6 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected DrawableHit(Hit hit) : base(hit) { + FillMode = FillMode.Fit; } protected override void CheckJudgement(bool userTriggered) @@ -92,12 +93,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Content.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad); - this.FadeOut(800) - .MoveToY(-gravity_travel_height, gravity_time, Easing.Out) + this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out) .Then() .MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In); - Expire(); + this.FadeOut(800) + .Expire(); + break; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs index 1c6b12ea43..57e7eea470 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -7,7 +7,6 @@ using osu.Framework.Input; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; using OpenTK.Input; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -28,8 +27,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } - protected override TaikoPiece CreateMainPiece() => new CirclePiece(true); - protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement(); protected override void CheckJudgement(bool userTriggered) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index a565f92fac..e861af03cf 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -35,8 +35,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private readonly CircularContainer targetRing; private readonly CircularContainer expandingRing; - private readonly CirclePiece circlePiece; - private readonly Key[] rimKeys = { Key.D, Key.K }; private readonly Key[] centreKeys = { Key.F, Key.J }; private Key[] lastKeySet; @@ -52,89 +50,81 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public DrawableSwell(Swell swell) : base(swell) { - Children = new Drawable[] + FillMode = FillMode.Fit; + + Add(bodyContainer = new Container { - bodyContainer = new Container + RelativeSizeAxes = Axes.Both, + Depth = 1, + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + expandingRing = new CircularContainer { - expandingRing = new CircularContainer + Name = "Expanding ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0, + RelativeSizeAxes = Axes.Both, + BlendingMode = BlendingMode.Additive, + Masking = true, + Children = new[] { - Name = "Expanding ring", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Alpha = 0, - Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER), - BlendingMode = BlendingMode.Additive, - Masking = true, - Children = new[] + new Box { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = inner_ring_alpha, - } + RelativeSizeAxes = Axes.Both, + Alpha = inner_ring_alpha, } - }, - targetRing = new CircularContainer + } + }, + targetRing = new CircularContainer + { + Name = "Target ring (thick border)", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = target_ring_thick_border, + BlendingMode = BlendingMode.Additive, + Children = new Drawable[] { - Name = "Target ring (thick border)", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER), - Masking = true, - BorderThickness = target_ring_thick_border, - BlendingMode = BlendingMode.Additive, - Children = new Drawable[] + new Box { - new Box + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + }, + new CircularContainer + { + Name = "Target ring (thin border)", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = target_ring_thin_border, + BorderColour = Color4.White, + Children = new[] { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - }, - new CircularContainer - { - Name = "Target ring (thin border)", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = target_ring_thin_border, - BorderColour = Color4.White, - Children = new[] + new Box { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true } } } - }, - circlePiece = new CirclePiece - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Children = new[] - { - symbol = new SwellSymbolPiece() - } } } } - }; + }); + + MainPiece.Add(symbol = new SwellSymbolPiece()); - circlePiece.KiaiMode = HitObject.Kiai; } [BackgroundDependencyLoader] private void load(OsuColour colours) { - circlePiece.AccentColour = colours.YellowDark; + MainPiece.AccentColour = colours.YellowDark; expandingRing.Colour = colours.YellowLight; targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 24aa366944..510994ed70 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; @@ -24,12 +23,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); - protected override Container Content => bodyContainer; - protected readonly TaikoPiece MainPiece; - private readonly Container bodyContainer; - public new TaikoHitType HitObject; protected DrawableTaikoHitObject(TaikoHitType hitObject) @@ -40,19 +35,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Anchor = Anchor.CentreLeft; Origin = Anchor.Custom; - AutoSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.Both; + Size = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); RelativePositionAxes = Axes.X; - AddInternal(bodyContainer = new Container - { - AutoSizeAxes = Axes.Both, - Children = new[] - { - MainPiece = CreateMainPiece() - } - }); - + Add(MainPiece = CreateMainPiece()); MainPiece.KiaiMode = HitObject.Kiai; LifetimeStart = HitObject.StartTime - HitObject.ScrollTime * 2; @@ -60,7 +48,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override TaikoJudgement CreateJudgement() => new TaikoJudgement(); - protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(HitObject.IsStrong); + protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(); /// /// Sets the scroll position of the DrawableHitObject relative to the offset between diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs index cc88105e03..f4c78251d0 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs @@ -11,19 +11,24 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces /// /// The symbol used for centre hit pieces. /// - public class CentreHitSymbolPiece : CircularContainer + public class CentreHitSymbolPiece : Container { public CentreHitSymbolPiece() { Anchor = Anchor.Centre; Origin = Anchor.Centre; - Size = new Vector2(CirclePiece.SYMBOL_INNER_SIZE); - Masking = true; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(CirclePiece.SYMBOL_SIZE); + Padding = new MarginPadding(CirclePiece.SYMBOL_BORDER); + Children = new[] { - new Box + new CircularContainer { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new[] { new Box { RelativeSizeAxes = Axes.Both } } } }; } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index 698939e278..ba717371dd 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -21,9 +21,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces /// public class CirclePiece : TaikoPiece { - public const float SYMBOL_SIZE = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER * 0.45f; + public const float SYMBOL_SIZE = 0.45f; public const float SYMBOL_BORDER = 8; - public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER; private const double pre_beat_transition_time = 80; /// @@ -64,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces public Box FlashBox; - public CirclePiece(bool isStrong = false) + public CirclePiece() { EarlyActivationMilliseconds = pre_beat_transition_time; @@ -120,28 +119,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces }, content = new Container { - RelativeSizeAxes = Axes.Both, Name = "Content", Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, } }); - - if (isStrong) - { - Size *= TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE; - - //default for symbols etc. - Content.Scale *= TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE; - } - } - - protected override void Update() - { - base.Update(); - - //we want to allow for width of content to remain mapped to the area inside us, regardless of the scale applied above. - Content.Width = 1 / Content.Scale.X; } private const float edge_alpha_kiai = 0.5f; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs index f607e2040f..2f5b4eefd6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs @@ -18,8 +18,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces /// public float Length; - public ElongatedCirclePiece(bool isStrong = false) : base(isStrong) + public ElongatedCirclePiece() { + RelativeSizeAxes = Axes.Y; } protected override void Update() diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs index 704a27a96d..60224a291d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs @@ -18,7 +18,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { Anchor = Anchor.Centre; Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; Size = new Vector2(CirclePiece.SYMBOL_SIZE); + BorderThickness = CirclePiece.SYMBOL_BORDER; BorderColour = Color4.White; Masking = true; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs index 0f703837a9..c3fdc671a4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs @@ -1,7 +1,9 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces @@ -9,16 +11,26 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces /// /// The symbol used for swell pieces. /// - public class SwellSymbolPiece : TextAwesome + public class SwellSymbolPiece : Container { public SwellSymbolPiece() { Anchor = Anchor.Centre; Origin = Anchor.Centre; - UseFullGlyphHeight = true; - TextSize = CirclePiece.SYMBOL_INNER_SIZE; - Icon = FontAwesome.fa_asterisk; - Shadow = false; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(CirclePiece.SYMBOL_SIZE); + Padding = new MarginPadding(CirclePiece.SYMBOL_BORDER); + + Children = new[] + { + new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.fa_asterisk, + Shadow = false + } + }; } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs index 5e7e9e6350..4b40bbf384 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs @@ -2,9 +2,9 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Graphics; -using OpenTK; using OpenTK.Graphics; using osu.Game.Graphics.Containers; +using osu.Framework.Graphics; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { @@ -35,8 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces public TaikoPiece() { - //just a default - Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER); + RelativeSizeAxes = Axes.Both; } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs index 2af65f2ed7..211bf910a4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs @@ -15,12 +15,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces /// Any tick that is not the first for a drumroll is not filled, but is instead displayed /// as a hollow circle. This is what controls the border width of that circle. /// - private const float tick_border_width = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER / 16; + private const float tick_border_width = 5; /// /// The size of a tick. /// - private const float tick_size = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER / 6; + private const float tick_size = 0.35f; private bool filled; public bool Filled @@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces public TickPiece() { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; Size = new Vector2(tick_size); Add(new CircularContainer diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index db368cb112..7e7dc3662f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -4,31 +4,25 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Objects { public abstract class TaikoHitObject : HitObject { /// - /// Diameter of a circle relative to the size of the . + /// Default size of a drawable taiko hit object. /// - public const float PLAYFIELD_RELATIVE_DIAMETER = 0.45f; + public const float DEFAULT_SIZE = 0.45f; /// - /// Scale multiplier for a strong circle. + /// Scale multiplier for a strong drawable taiko hit object. /// - public const float STRONG_CIRCLE_DIAMETER_SCALE = 1.4f; + public const float STRONG_SCALE = 1.4f; /// - /// Default circle diameter. + /// Default size of a strong drawable taiko hit object. /// - public const float DEFAULT_CIRCLE_DIAMETER = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT * PLAYFIELD_RELATIVE_DIAMETER; - - /// - /// Default strong circle diameter. - /// - public const float DEFAULT_STRONG_CIRCLE_DIAMETER = DEFAULT_CIRCLE_DIAMETER * STRONG_CIRCLE_DIAMETER_SCALE; + public const float DEFAULT_STRONG_SIZE = DEFAULT_SIZE * STRONG_SCALE; /// /// The time taken from the initial (off-screen) spawn position to the centre of the hit target for a of 1000ms. diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 303d936fb9..83db9b35af 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using System.Collections.Generic; +using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Scoring; @@ -83,9 +84,11 @@ namespace osu.Game.Rulesets.Taiko } } + public override Mod GetAutoplayMod() => new TaikoModAutoplay(); + public override string Description => "osu!taiko"; - public override FontAwesome Icon => FontAwesome.fa_osu_taiko_o; + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; public override IEnumerable CreateGameplayKeys() => new KeyCounter[] { diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index c372ac1809..cb849a11c7 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -30,10 +30,11 @@ namespace osu.Game.Rulesets.Taiko.UI Judgement = judgement; - Anchor = Anchor.Centre; + Anchor = Anchor.CentreLeft; Origin = Anchor.Centre; - Size = new Vector2(TaikoPlayfield.HIT_TARGET_OFFSET + TaikoHitObject.DEFAULT_CIRCLE_DIAMETER); + RelativeSizeAxes = Axes.Both; + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE); RelativePositionAxes = Axes.Both; @@ -73,7 +74,7 @@ namespace osu.Game.Rulesets.Taiko.UI /// public void VisualiseSecondHit() { - this.ResizeTo(new Vector2(TaikoPlayfield.HIT_TARGET_OFFSET + TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER), 50); + this.ResizeTo(new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE), 50); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs index 1ca6e4eb34..8f3b6840f3 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs @@ -15,11 +15,6 @@ namespace osu.Game.Rulesets.Taiko.UI /// internal class HitTarget : Container { - /// - /// The 1px inner border of the taiko playfield. - /// - private const float border_offset = 1; - /// /// Thickness of all drawn line pieces. /// @@ -27,8 +22,6 @@ namespace osu.Game.Rulesets.Taiko.UI public HitTarget() { - Size = new Vector2(TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT); - Children = new Drawable[] { new Box @@ -36,8 +29,8 @@ namespace osu.Game.Rulesets.Taiko.UI Name = "Bar Upper", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Y = border_offset, - Size = new Vector2(border_thickness, (TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT - TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER) / 2f - border_offset), + RelativeSizeAxes = Axes.Y, + Size = new Vector2(border_thickness, (1 - TaikoHitObject.DEFAULT_STRONG_SIZE) / 2f), Alpha = 0.1f }, new CircularContainer @@ -45,7 +38,9 @@ namespace osu.Game.Rulesets.Taiko.UI Name = "Strong Hit Ring", Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER), + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE), Masking = true, BorderColour = Color4.White, BorderThickness = border_thickness, @@ -65,7 +60,9 @@ namespace osu.Game.Rulesets.Taiko.UI Name = "Normal Hit Ring", Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER), + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(TaikoHitObject.DEFAULT_SIZE), Masking = true, BorderColour = Color4.White, BorderThickness = border_thickness, @@ -85,8 +82,8 @@ namespace osu.Game.Rulesets.Taiko.UI Name = "Bar Lower", Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, - Y = -border_offset, - Size = new Vector2(border_thickness, (TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT - TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER) / 2f - border_offset), + RelativeSizeAxes = Axes.Y, + Size = new Vector2(border_thickness, (1 - TaikoHitObject.DEFAULT_STRONG_SIZE) / 2f), Alpha = 0.1f }, }; diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 60881f3c24..0255171284 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -21,9 +21,10 @@ namespace osu.Game.Rulesets.Taiko.UI { public InputDrum() { - Size = new Vector2(TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT); + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; - const float middle_split = 10; + const float middle_split = 0.025f; Children = new Drawable[] { @@ -33,6 +34,7 @@ namespace osu.Game.Rulesets.Taiko.UI Anchor = Anchor.Centre, Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.X, X = -middle_split / 2, RimKey = Key.D, CentreKey = Key.F @@ -43,8 +45,8 @@ namespace osu.Game.Rulesets.Taiko.UI Anchor = Anchor.Centre, Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.X, X = middle_split / 2, - Position = new Vector2(-1f, 0), RimKey = Key.K, CentreKey = Key.J } diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index 524490fac3..bac956a25b 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -24,11 +24,11 @@ namespace osu.Game.Rulesets.Taiko.UI Judgement = judgement; - Anchor = Anchor.Centre; + Anchor = Anchor.CentreLeft; Origin = Anchor.Centre; - RelativeSizeAxes = Axes.Y; - Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER, 1); + RelativeSizeAxes = Axes.Both; + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1); Masking = true; Alpha = 0.25f; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs index 662cace511..570b4be488 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected override Vector2 GetPlayfieldAspectAdjust() { - const float default_relative_height = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT / 768; + const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768; const float default_aspect = 16f / 9f; float aspectAdjust = MathHelper.Clamp(DrawWidth / DrawHeight, 0.4f, 4) / default_aspect; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index b0fb9d7f28..dca339f734 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -15,21 +15,20 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Extensions.Color4Extensions; using System.Linq; using osu.Game.Rulesets.Taiko.Objects.Drawables; -using System; namespace osu.Game.Rulesets.Taiko.UI { public class TaikoPlayfield : Playfield { /// - /// The default play field height. + /// Default height of a when inside a . /// - public const float DEFAULT_PLAYFIELD_HEIGHT = 178f; + public const float DEFAULT_HEIGHT = 178; /// /// The offset from which the center of the hit target lies at. /// - public const float HIT_TARGET_OFFSET = TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER / 2f + 40; + public const float HIT_TARGET_OFFSET = 100; /// /// The size of the left area of the playfield. This area contains the input drum. @@ -56,112 +55,124 @@ namespace osu.Game.Rulesets.Taiko.UI { AddRangeInternal(new Drawable[] { - new ScaleFixContainer + backgroundContainer = new Container { - RelativeSizeAxes = Axes.X, - Height = DEFAULT_PLAYFIELD_HEIGHT, - Children = new[] + Name = "Transparent playfield background", + RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffectParameters { - backgroundContainer = new Container + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Radius = 5, + }, + Children = new Drawable[] + { + background = new Box { - Name = "Transparent playfield background", RelativeSizeAxes = Axes.Both, - BorderThickness = 2, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.2f), - Radius = 5, - }, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.6f - }, - } + Alpha = 0.6f }, + } + }, + new Container + { + Name = "Right area", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = left_area_size }, + Children = new Drawable[] + { new Container { - Name = "Right area", + Name = "Masked elements", RelativeSizeAxes = Axes.Both, - Margin = new MarginPadding { Left = left_area_size }, + Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Masking = true, Children = new Drawable[] { - new Container + hitExplosionContainer = new Container { - Name = "Masked elements", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Masking = true, - Children = new Drawable[] - { - hitExplosionContainer = new Container - { - RelativeSizeAxes = Axes.Y, - BlendingMode = BlendingMode.Additive, - }, - barLineContainer = new Container - { - RelativeSizeAxes = Axes.Both, - }, - new HitTarget - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - }, - hitObjectContainer = new Container - { - RelativeSizeAxes = Axes.Both, - }, - } + FillMode = FillMode.Fit, + BlendingMode = BlendingMode.Additive, }, - kiaiExplosionContainer = new Container - { - Name = "Kiai hit explosions", - RelativeSizeAxes = Axes.Y, - Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - BlendingMode = BlendingMode.Additive - }, - judgementContainer = new Container - { - Name = "Judgements", - RelativeSizeAxes = Axes.Y, - Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - BlendingMode = BlendingMode.Additive - }, - } - }, - overlayBackgroundContainer = new Container - { - Name = "Left overlay", - Size = new Vector2(left_area_size, DEFAULT_PLAYFIELD_HEIGHT), - BorderThickness = 1, - Children = new Drawable[] - { - overlayBackground = new Box + barLineContainer = new Container { RelativeSizeAxes = Axes.Both, }, - new InputDrum + new HitTarget { - Anchor = Anchor.Centre, + Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, - RelativePositionAxes = Axes.X, - Position = new Vector2(0.10f, 0), - Scale = new Vector2(0.9f) + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit }, - new Box + hitObjectContainer = new Container { - Anchor = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = 10, - Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), + RelativeSizeAxes = Axes.Both, }, } }, + kiaiExplosionContainer = new Container + { + Name = "Kiai hit explosions", + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, + BlendingMode = BlendingMode.Additive + }, + judgementContainer = new Container + { + Name = "Judgements", + RelativeSizeAxes = Axes.Y, + Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, + BlendingMode = BlendingMode.Additive + }, + } + }, + overlayBackgroundContainer = new Container + { + Name = "Left overlay", + RelativeSizeAxes = Axes.Y, + Size = new Vector2(left_area_size, 1), + Children = new Drawable[] + { + overlayBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new InputDrum + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Scale = new Vector2(0.9f), + Margin = new MarginPadding { Right = 20 } + }, + new Box + { + Anchor = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = 10, + Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), + }, + } + }, + new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + MaskingSmoothness = 0, + BorderThickness = 2, + AlwaysPresent = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } } }, topLevelHitContainer = new Container @@ -233,56 +244,5 @@ namespace osu.Game.Rulesets.Taiko.UI else hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit(); } - - /// - /// This is a very special type of container. It serves a similar purpose to , however unlike , - /// this will only adjust the scale relative to the height of its parent and will maintain the original width relative to its parent. - /// - /// - /// By adjusting the scale relative to the height of its parent, the aspect ratio of this container's children is maintained, however this is undesirable - /// in the case where the hit object container should not have its width adjusted by scale. To counteract this, another container is nested inside this - /// container which takes care of reversing the width adjustment while appearing transparent to the user. - /// - /// - private class ScaleFixContainer : Container - { - protected override Container Content => widthAdjustmentContainer; - private readonly WidthAdjustmentContainer widthAdjustmentContainer; - - /// - /// We only want to apply DrawScale in the Y-axis to preserve aspect ratio and doesn't care about having its width adjusted. - /// - protected override Vector2 DrawScale => Scale * RelativeToAbsoluteFactor.Y / DrawHeight; - - public ScaleFixContainer() - { - AddInternal(widthAdjustmentContainer = new WidthAdjustmentContainer { ParentDrawScaleReference = () => DrawScale.X }); - } - - /// - /// The container type that reverses the width adjustment. - /// - private class WidthAdjustmentContainer : Container - { - /// - /// This container needs to know its parent's so it can reverse the width adjustment caused by . - /// - public Func ParentDrawScaleReference; - - public WidthAdjustmentContainer() - { - // This container doesn't care about height, it should always fill its parent - RelativeSizeAxes = Axes.Y; - } - - protected override void Update() - { - base.Update(); - - // Reverse the DrawScale adjustment - Width = Parent.DrawSize.X / ParentDrawScaleReference(); - } - } - } } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 8668b0c995..82777734bb 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.IO.Serialization; namespace osu.Game.Beatmaps { @@ -45,7 +46,7 @@ namespace osu.Game.Beatmaps /// The original beatmap to use the parameters of. public Beatmap(Beatmap original = null) { - BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo; + BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo; ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; Breaks = original?.Breaks ?? Breaks; ComboColors = original?.ComboColors ?? ComboColors; diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 6f4a4a8a69..ec90cabf02 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps // Editor // This bookmarks stuff is necessary because DB doesn't know how to store int[] - public string StoredBookmarks { get; internal set; } + public string StoredBookmarks { get; set; } [Ignore] [JsonIgnore] diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 99117afe35..bbb6c975d0 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -17,9 +17,9 @@ using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.IO; using osu.Game.IO; using osu.Game.IPC; +using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using SQLite.Net; -using FileInfo = osu.Game.IO.FileInfo; namespace osu.Game.Beatmaps { @@ -47,6 +47,8 @@ namespace osu.Game.Beatmaps private readonly FileStore files; + private readonly SQLiteConnection connection; + private readonly RulesetStore rulesets; private readonly BeatmapStore beatmaps; @@ -54,6 +56,16 @@ namespace osu.Game.Beatmaps // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private BeatmapIPCChannel ipc; + /// + /// Set an endpoint for notifications to be posted to. + /// + public Action PostNotification { private get; set; } + + /// + /// Set a storage with access to an osu-stable install for import purposes. + /// + public Func GetStableStorage { private get; set; } + public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, IIpcHost importHost = null) { beatmaps = new BeatmapStore(connection); @@ -62,6 +74,7 @@ namespace osu.Game.Beatmaps this.storage = storage; this.files = files; + this.connection = connection; this.rulesets = rulesets; if (importHost != null) @@ -69,29 +82,48 @@ namespace osu.Game.Beatmaps } /// - /// Import multiple from filesystem . + /// Import one or more from filesystem . + /// This will post a notification tracking import progress. /// - /// Multiple locations on disk. + /// One or more beatmap locations on disk. public void Import(params string[] paths) { + var notification = new ProgressNotification + { + Text = "Beatmap import is initialising...", + Progress = 0, + State = ProgressNotificationState.Active, + }; + + PostNotification?.Invoke(notification); + + int i = 0; foreach (string path in paths) { + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; + try { + notification.Text = $"Importing ({i} of {paths.Length})\n{Path.GetFileName(path)}"; using (ArchiveReader reader = getReaderFrom(path)) Import(reader); + notification.Progress = (float)++i / paths.Length; + // We may or may not want to delete the file depending on where it is stored. // e.g. reconstructing/repairing database with beatmaps from default storage. // Also, not always a single file, i.e. for LegacyFilesystemReader // TODO: Add a check to prevent files from storage to be deleted. try { - File.Delete(path); + if (File.Exists(path)) + File.Delete(path); } catch (Exception e) { - Logger.Error(e, $@"Could not delete file at {path}"); + Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})"); } } catch (Exception e) @@ -100,16 +132,24 @@ namespace osu.Game.Beatmaps Logger.Error(e, @"Could not import beatmap set"); } } + + notification.State = ProgressNotificationState.Completed; } + private readonly object importLock = new object(); + /// /// Import a beatmap from an . /// /// The beatmap to be imported. public BeatmapSetInfo Import(ArchiveReader archiveReader) { - BeatmapSetInfo set = importToStorage(archiveReader); - Import(set); + BeatmapSetInfo set = null; + + // let's only allow one concurrent import at a time for now. + lock (importLock) + connection.RunInTransaction(() => Import(set = importToStorage(archiveReader))); + return set; } @@ -122,7 +162,8 @@ namespace osu.Game.Beatmaps // If we have an ID then we already exist in the database. if (beatmapSetInfo.ID != 0) return; - beatmaps.Add(beatmapSetInfo); + lock (beatmaps) + beatmaps.Add(beatmapSetInfo); } /// @@ -132,10 +173,11 @@ namespace osu.Game.Beatmaps /// The beatmap to delete. public void Delete(BeatmapSetInfo beatmapSet) { - if (!beatmaps.Delete(beatmapSet)) return; + lock (beatmaps) + if (!beatmaps.Delete(beatmapSet)) return; if (!beatmapSet.Protected) - files.Dereference(beatmapSet.Files); + files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); } /// @@ -145,9 +187,11 @@ namespace osu.Game.Beatmaps /// The beatmap to restore. public void Undelete(BeatmapSetInfo beatmapSet) { - if (!beatmaps.Undelete(beatmapSet)) return; + lock (beatmaps) + if (!beatmaps.Undelete(beatmapSet)) return; - files.Reference(beatmapSet.Files); + if (!beatmapSet.Protected) + files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); } /// @@ -161,7 +205,8 @@ namespace osu.Game.Beatmaps if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - beatmaps.Populate(beatmapInfo); + lock (beatmaps) + beatmaps.Populate(beatmapInfo); if (beatmapInfo.BeatmapSet == null) throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database."); @@ -181,7 +226,8 @@ namespace osu.Game.Beatmaps /// public void Reset() { - beatmaps.Reset(); + lock (beatmaps) + beatmaps.Reset(); } /// @@ -191,12 +237,15 @@ namespace osu.Game.Beatmaps /// The first result for the provided query, or null if no results were found. public BeatmapSetInfo QueryBeatmapSet(Func query) { - BeatmapSetInfo set = beatmaps.Query().FirstOrDefault(query); + lock (beatmaps) + { + BeatmapSetInfo set = beatmaps.Query().FirstOrDefault(query); - if (set != null) - beatmaps.Populate(set); + if (set != null) + beatmaps.Populate(set); - return set; + return set; + } } /// @@ -204,7 +253,10 @@ namespace osu.Game.Beatmaps /// /// The query. /// Results from the provided query. - public List QueryBeatmapSets(Expression> query) => beatmaps.QueryAndPopulate(query); + public List QueryBeatmapSets(Expression> query) + { + lock (beatmaps) return beatmaps.QueryAndPopulate(query); + } /// /// Perform a lookup query on available s. @@ -213,12 +265,15 @@ namespace osu.Game.Beatmaps /// The first result for the provided query, or null if no results were found. public BeatmapInfo QueryBeatmap(Func query) { - BeatmapInfo set = beatmaps.Query().FirstOrDefault(query); + lock (beatmaps) + { + BeatmapInfo set = beatmaps.Query().FirstOrDefault(query); - if (set != null) - beatmaps.Populate(set); + if (set != null) + beatmaps.Populate(set); - return set; + return set; + } } /// @@ -226,7 +281,10 @@ namespace osu.Game.Beatmaps /// /// The query. /// Results from the provided query. - public List QueryBeatmaps(Expression> query) => beatmaps.QueryAndPopulate(query); + public List QueryBeatmaps(Expression> query) + { + lock (beatmaps) return beatmaps.QueryAndPopulate(query); + } /// /// Creates an from a valid storage path. @@ -258,19 +316,26 @@ namespace osu.Game.Beatmaps var hash = hashable.ComputeSHA2Hash(); // check if this beatmap has already been imported and exit early if so. - var beatmapSet = beatmaps.QueryAndPopulate().FirstOrDefault(b => b.Hash == hash); + BeatmapSetInfo beatmapSet; + lock (beatmaps) + beatmapSet = beatmaps.QueryAndPopulate(b => b.Hash == hash).FirstOrDefault(); + if (beatmapSet != null) { Undelete(beatmapSet); return beatmapSet; } - List fileInfos = new List(); + List fileInfos = new List(); // import files to manager foreach (string file in reader.Filenames) using (Stream s = reader.GetStream(file)) - fileInfos.Add(files.Add(s, file)); + fileInfos.Add(new BeatmapSetFileInfo + { + Filename = file, + FileInfo = files.Add(s) + }); BeatmapMetadata metadata; @@ -325,10 +390,13 @@ namespace osu.Game.Beatmaps /// A list of available . public List GetAllUsableBeatmapSets(bool populate = true) { - if (populate) - return beatmaps.QueryAndPopulate(b => !b.DeletePending).ToList(); - else - return beatmaps.Query(b => !b.DeletePending).ToList(); + lock (beatmaps) + { + if (populate) + return beatmaps.QueryAndPopulate(b => !b.DeletePending).ToList(); + else + return beatmaps.Query(b => !b.DeletePending).ToList(); + } } protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap @@ -366,7 +434,7 @@ namespace osu.Game.Beatmaps catch { return null; } } - private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => f.Filename == filename).StoragePath; + private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => f.Filename == filename).FileInfo.StoragePath; protected override Texture GetBackground() { @@ -390,5 +458,51 @@ namespace osu.Game.Beatmaps catch { return new TrackVirtual(); } } } + + /// + /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. + /// + public void ImportFromStable() + { + var stable = GetStableStorage?.Invoke(); + + if (stable == null) + { + Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); + return; + } + + Import(stable.GetDirectories("Songs")); + } + + public void DeleteAll() + { + var maps = GetAllUsableBeatmapSets().ToArray(); + + if (maps.Length == 0) return; + + var notification = new ProgressNotification + { + Progress = 0, + State = ProgressNotificationState.Active, + }; + + PostNotification?.Invoke(notification); + + int i = 0; + + foreach (var b in maps) + { + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; + + notification.Text = $"Deleting ({i} of {maps.Length})"; + notification.Progress = (float)++i / maps.Length; + Delete(b); + } + + notification.State = ProgressNotificationState.Completed; + } } } diff --git a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs index d18b1e833b..a05362b32d 100644 --- a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs @@ -2,16 +2,26 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.IO; +using SQLite.Net.Attributes; using SQLiteNetExtensions.Attributes; namespace osu.Game.Beatmaps { public class BeatmapSetFileInfo { - [ForeignKey(typeof(BeatmapSetInfo))] + [PrimaryKey, AutoIncrement] + public int ID { get; set; } + + [ForeignKey(typeof(BeatmapSetInfo)), NotNull] public int BeatmapSetInfoID { get; set; } - [ForeignKey(typeof(FileInfo))] + [ForeignKey(typeof(FileInfo)), NotNull] public int FileInfoID { get; set; } + + [OneToOne(CascadeOperations = CascadeOperation.CascadeRead)] + public FileInfo FileInfo { get; set; } + + [NotNull] + public string Filename { get; set; } } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index a99ba94e9a..f47affcab8 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using osu.Game.IO; using SQLite.Net.Attributes; using SQLiteNetExtensions.Attributes; @@ -37,8 +36,8 @@ namespace osu.Game.Beatmaps public string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename; - [ManyToMany(typeof(BeatmapSetFileInfo), CascadeOperations = CascadeOperation.CascadeRead)] - public List Files { get; set; } + [OneToMany(CascadeOperations = CascadeOperation.All)] + public List Files { get; set; } public bool Protected { get; set; } } diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 102900ae81..2ec9a7d759 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using osu.Framework.Logging; using osu.Game.Database; using SQLite.Net; using SQLiteNetExtensions.Extensions; @@ -21,7 +20,7 @@ namespace osu.Game.Beatmaps /// The current version of this store. Used for migrations (see ). /// The initial version is 1. /// - protected override int StoreVersion => 1; + protected override int StoreVersion => 2; public BeatmapStore(SQLiteConnection connection) : base(connection) @@ -52,7 +51,11 @@ namespace osu.Game.Beatmaps Connection.CreateTable(); Connection.CreateTable(); Connection.CreateTable(); + } + protected override void StartupTasks() + { + base.StartupTasks(); cleanupPendingDeletions(); } @@ -60,24 +63,19 @@ namespace osu.Game.Beatmaps /// Perform migrations between two store versions. /// /// The current store version. This will be zero on a fresh database initialisation. - /// The target version which we are migrating to (equal to the current ). - protected override void PerformMigration(int currentVersion, int newVersion) + /// The target version which we are migrating to (equal to the current ). + protected override void PerformMigration(int currentVersion, int targetVersion) { - base.PerformMigration(currentVersion, newVersion); + base.PerformMigration(currentVersion, targetVersion); - while (currentVersion++ < newVersion) + while (currentVersion++ < targetVersion) { switch (currentVersion) { case 1: - // initialising from a version before we had versioning (or a fresh install). - - // force adding of Protected column (not automatically migrated). - Connection.MigrateTable(); - - // remove all existing beatmaps. - foreach (var b in Connection.GetAllWithChildren(null, true)) - Connection.Delete(b, true); + case 2: + // cannot migrate; breaking underlying changes. + Reset(); break; } } @@ -131,23 +129,11 @@ namespace osu.Game.Beatmaps private void cleanupPendingDeletions() { - foreach (var b in QueryAndPopulate(b => b.DeletePending && !b.Protected)) + Connection.RunInTransaction(() => { - try - { - // many-to-many join table entries are not automatically tidied. - Connection.Table().Delete(f => f.BeatmapSetInfoID == b.ID); + foreach (var b in QueryAndPopulate(b => b.DeletePending && !b.Protected)) Connection.Delete(b, true); - } - catch (Exception e) - { - Logger.Error(e, $@"Could not delete beatmap {b}"); - } - } - - //this is required because sqlite migrations don't work, initially inserting nulls into this field. - //see https://github.com/praeclarum/sqlite-net/issues/326 - Connection.Query("UPDATE BeatmapSetInfo SET DeletePending = 0 WHERE DeletePending IS NULL"); + }); } } } diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index f61380bfb8..42db025a40 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -4,8 +4,8 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using OpenTK; -using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { @@ -22,23 +22,20 @@ namespace osu.Game.Beatmaps.Drawables [BackgroundDependencyLoader] private void load() { - Children = new[] + Children = new Drawable[] { - new TextAwesome + new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, - TextSize = Size.X, + RelativeSizeAxes = Axes.Both, Colour = AccentColour, Icon = FontAwesome.fa_circle }, - new TextAwesome + new ConstrainedIconContainer { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - TextSize = Size.X, - Colour = Color4.White, - Icon = beatmap.Ruleset.CreateInstance().Icon + RelativeSizeAxes = Axes.Both, + Icon = beatmap.Ruleset.CreateInstance().CreateIcon() } }; } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 0885fb05e5..bd53a80555 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -60,6 +60,8 @@ namespace osu.Game.Beatmaps { public override IEnumerable GetModsFor(ModType type) => new Mod[] { }; + public override Mod GetAutoplayMod() => new ModAutoplay(); + public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) { throw new NotImplementedException(); diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 4c540fa8cf..433c23284f 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -456,6 +456,11 @@ namespace osu.Game.Beatmaps.Formats handleColours(beatmap, line, ref hasCustomColours); break; case Section.HitObjects: + + // If the ruleset wasn't specified, assume the osu!standard ruleset. + if (parser == null) + parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); + var obj = parser.Parse(line); if (obj != null) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index a6a3b23d5d..462f94ed7c 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -52,7 +52,14 @@ namespace osu.Game.Beatmaps { lock (beatmapLock) { - return beatmap ?? (beatmap = GetBeatmap()); + if (beatmap != null) return beatmap; + + beatmap = GetBeatmap(); + + // use the database-backed info. + beatmap.BeatmapInfo = BeatmapInfo; + + return beatmap; } } } diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index bb61fc1870..d8e2e35bd7 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -41,7 +41,7 @@ namespace osu.Game.Database { var storeName = GetType().Name; - var reportedVersion = Connection.Table().FirstOrDefault(s => s.StoreName == storeName) ?? new StoreVersion + var reportedVersion = Connection.Table().Where(s => s.StoreName == storeName).FirstOrDefault() ?? new StoreVersion { StoreName = storeName, Version = 0 @@ -51,14 +51,30 @@ namespace osu.Game.Database PerformMigration(reportedVersion.Version, reportedVersion.Version = StoreVersion); Connection.InsertOrReplace(reportedVersion); + + StartupTasks(); } - protected virtual void PerformMigration(int currentVersion, int newVersion) + /// + /// Called when the database version of this store doesn't match the local version. + /// Any manual migration operations should be performed in this. + /// + /// The current store version. This will be zero on a fresh database initialisation. + /// The target version which we are migrating to (equal to the current ). + protected virtual void PerformMigration(int currentVersion, int targetVersion) { } /// - /// Prepare this database for use. + /// Perform any common startup tasks. Runs after and . + /// + protected virtual void StartupTasks() + { + + } + + /// + /// Prepare this database for use. Tables should be created here. /// protected abstract void Prepare(bool reset = false); @@ -83,9 +99,9 @@ namespace osu.Game.Database /// /// Query and populate results. /// - /// An optional filter to refine results. + /// An filter to refine results. /// - public List QueryAndPopulate(Expression> filter = null) + public List QueryAndPopulate(Expression> filter) where T : class { checkType(typeof(T)); @@ -101,7 +117,6 @@ namespace osu.Game.Database public void Populate(T item, bool recursive = true) { checkType(item.GetType()); - Connection.GetChildren(item, recursive); } diff --git a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs new file mode 100644 index 0000000000..dd2a265a0f --- /dev/null +++ b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs @@ -0,0 +1,62 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Graphics.Containers +{ + /// + /// Display an icon that is forced to scale to the size of this container. + /// + public class ConstrainedIconContainer : CompositeDrawable + { + public Drawable Icon + { + get + { + return InternalChild; + } + + set + { + InternalChild = value; + } + } + + /// + /// Determines an edge effect of this . + /// Edge effects are e.g. glow or a shadow. + /// Only has an effect when is true. + /// + public new EdgeEffectParameters EdgeEffect + { + get { return base.EdgeEffect; } + set { base.EdgeEffect = value; } + } + + protected override void Update() + { + base.Update(); + if (InternalChildren.Count > 0 && InternalChild.DrawSize.X > 0) + { + // We're modifying scale here for a few reasons + // - Guarantees correctness if BorderWidth is being used + // - If we were to use RelativeSize/FillMode, we'd need to set the Icon's RelativeSizeAxes directly. + // We can't do this because we would need access to AutoSizeAxes to set it to none. + // Other issues come up along the way too, so it's not a good solution. + var fitScale = Math.Min(DrawSize.X / InternalChild.DrawSize.X, DrawSize.Y / InternalChild.DrawSize.Y); + InternalChild.Scale = new Vector2(fitScale); + InternalChild.Anchor = Anchor.Centre; + InternalChild.Origin = Anchor.Centre; + } + } + + public ConstrainedIconContainer() + { + Masking = true; + } + } +} diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 6dadf0b0b0..6e5c3c8183 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -128,13 +128,11 @@ namespace osu.Game.Graphics.Containers RelativeSizeAxes = Axes.Both, Masking = true, ScrollbarVisible = false, - Children = new Drawable[] { scrollContentContainer = CreateScrollContentContainer() }, - Depth = float.MaxValue + Children = new Drawable[] { scrollContentContainer = CreateScrollContentContainer() } }); AddInternal(headerBackgroundContainer = new Container { - RelativeSizeAxes = Axes.X, - Depth = float.MaxValue / 2 + RelativeSizeAxes = Axes.X }); originalSectionsMargin = scrollContentContainer.Margin; } diff --git a/osu.Game/Graphics/IHasAccentColour.cs b/osu.Game/Graphics/IHasAccentColour.cs index ac2117c517..0c6bf98db4 100644 --- a/osu.Game/Graphics/IHasAccentColour.cs +++ b/osu.Game/Graphics/IHasAccentColour.cs @@ -20,14 +20,19 @@ namespace osu.Game.Graphics public static class AccentedColourExtensions { /// - /// Tweens the accent colour of a drawable to another colour. + /// Smoothly adjusts over time. /// - /// The drawable to apply the accent colour to. - /// The new accent colour. - /// The tween duration. - /// The tween easing. + /// A to which further transforms can be added. public static TransformSequence FadeAccent(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None) where T : IHasAccentColour => accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing); + + /// + /// Smoothly adjusts over time. + /// + /// A to which further transforms can be added. + public static TransformSequence FadeAccent(this TransformSequence t, Color4 newColour, double duration = 0, Easing easing = Easing.None) + where T : Drawable, IHasAccentColour + => t.Append(o => o.FadeAccent(newColour, duration, easing)); } } diff --git a/osu.Game/Graphics/TextAwesome.cs b/osu.Game/Graphics/SpriteIcon.cs similarity index 86% rename from osu.Game/Graphics/TextAwesome.cs rename to osu.Game/Graphics/SpriteIcon.cs index 69b0217444..d49814952c 100644 --- a/osu.Game/Graphics/TextAwesome.cs +++ b/osu.Game/Graphics/SpriteIcon.cs @@ -1,13 +1,105 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics.Sprites; +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics; +using osu.Framework.IO.Stores; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Caching; namespace osu.Game.Graphics { - public class TextAwesome : OsuSpriteText + public class SpriteIcon : CompositeDrawable { - //public override FontFace FontFace => (int)Icon < 0xf000 ? FontFace.OsuFont : FontFace.FontAwesome; + private readonly Sprite spriteShadow; + private readonly Sprite spriteMain; + + private Cached layout = new Cached(); + private readonly Container shadowVisibility; + + public SpriteIcon() + { + InternalChildren = new Drawable[] + { + shadowVisibility = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Child = spriteShadow = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Y = 2, + Colour = new Color4(0f, 0f, 0f, 0.2f), + }, + Alpha = 0, + }, + spriteMain = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit + }, + }; + } + + private FontStore store; + + [BackgroundDependencyLoader] + private void load(FontStore store) + { + this.store = store; + updateTexture(); + } + + private void updateTexture() + { + var texture = store?.Get(((char)icon).ToString()); + + spriteMain.Texture = texture; + spriteShadow.Texture = texture; + + if (Size == Vector2.Zero) + Size = new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0); + } + + public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) + { + if ((invalidation & Invalidation.Colour) > 0 && Shadow) + layout.Invalidate(); + return base.Invalidate(invalidation, source, shallPropagate); + } + + protected override void Update() + { + if (!layout.IsValid) + { + //adjust shadow alpha based on highest component intensity to avoid muddy display of darker text. + //squared result for quadratic fall-off seems to give the best result. + var avgColour = (Color4)DrawInfo.Colour.AverageColour; + + spriteShadow.Alpha = (float)Math.Pow(Math.Max(Math.Max(avgColour.R, avgColour.G), avgColour.B), 2); + + layout.Validate(); + } + } + + public bool Shadow + { + get { return spriteShadow.IsPresent; } + set + { + shadowVisibility.Alpha = value ? 1 : 0; + } + } private FontAwesome icon; @@ -23,7 +115,8 @@ namespace osu.Game.Graphics if (icon == value) return; icon = value; - Text = ((char)icon).ToString(); + if (IsLoaded) + updateTexture(); } } } diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs index f61192a1a6..b3e53280fb 100644 --- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs @@ -35,7 +35,7 @@ namespace osu.Game.Graphics.UserInterface private class BreadcrumbTabItem : OsuTabItem, IStateful { - public readonly TextAwesome Chevron; + public readonly SpriteIcon Chevron; //don't allow clicking between transitions and don't make the chevron clickable public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceiveMouseInputAt(screenSpacePos); @@ -69,11 +69,11 @@ namespace osu.Game.Graphics.UserInterface { Text.TextSize = 16; Padding = new MarginPadding { Right = padding + 8 }; //padding + chevron width - Add(Chevron = new TextAwesome + Add(Chevron = new SpriteIcon { Anchor = Anchor.CentreRight, Origin = Anchor.CentreLeft, - TextSize = 12, + Size = new Vector2(12), Icon = FontAwesome.fa_chevron_right, Margin = new MarginPadding { Left = padding }, Alpha = 0f, diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 42fff0f258..447daca75e 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -10,6 +10,9 @@ using System.Linq; namespace osu.Game.Graphics.UserInterface { + /// + /// A textbox which holds focus eagerly. + /// public class FocusedTextBox : OsuTextBox { protected override Color4 BackgroundUnfocused => new Color4(10, 10, 10, 255); diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs index 1598ace9df..1808dc4b6c 100644 --- a/osu.Game/Graphics/UserInterface/IconButton.cs +++ b/osu.Game/Graphics/UserInterface/IconButton.cs @@ -15,7 +15,7 @@ namespace osu.Game.Graphics.UserInterface { public class IconButton : OsuClickableContainer { - private readonly TextAwesome icon; + private readonly SpriteIcon icon; private readonly Box hover; private readonly Container content; @@ -47,7 +47,7 @@ namespace osu.Game.Graphics.UserInterface { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Size = new Vector2 (button_size), + Size = new Vector2(button_size), CornerRadius = 5, Masking = true, @@ -64,11 +64,11 @@ namespace osu.Game.Graphics.UserInterface RelativeSizeAxes = Axes.Both, Alpha = 0, }, - icon = new TextAwesome + icon = new SpriteIcon { Origin = Anchor.Centre, Anchor = Anchor.Centre, - TextSize = 18, + Size = new Vector2(18), } } } @@ -80,6 +80,8 @@ namespace osu.Game.Graphics.UserInterface { hover.Colour = colours.Yellow.Opacity(0.6f); flashColour = colours.Yellow; + + Enabled.ValueChanged += enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint); } protected override bool OnHover(InputState state) diff --git a/osu.Game/Graphics/UserInterface/LoadingAnimation.cs b/osu.Game/Graphics/UserInterface/LoadingAnimation.cs index 56ee47a7e6..eb8ff5be86 100644 --- a/osu.Game/Graphics/UserInterface/LoadingAnimation.cs +++ b/osu.Game/Graphics/UserInterface/LoadingAnimation.cs @@ -9,7 +9,7 @@ namespace osu.Game.Graphics.UserInterface { public class LoadingAnimation : VisibilityContainer { - private readonly TextAwesome spinner; + private readonly SpriteIcon spinner; public LoadingAnimation() { @@ -20,9 +20,9 @@ namespace osu.Game.Graphics.UserInterface Children = new Drawable[] { - spinner = new TextAwesome + spinner = new SpriteIcon { - TextSize = 20, + Size = new Vector2(20), Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.fa_spinner diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index ecbf51f8b9..3c454f2af2 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -68,6 +68,14 @@ namespace osu.Game.Graphics.UserInterface sampleClick = audio.Sample.Get(@"UI/generic-click"); sampleHover = audio.Sample.Get(@"UI/generic-hover"); + + Enabled.ValueChanged += enabled_ValueChanged; + Enabled.TriggerChange(); + } + + private void enabled_ValueChanged(bool enabled) + { + this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint); } protected override bool OnClick(InputState state) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 6dadd63ac4..f5a4219707 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Sprites; +using OpenTK; namespace osu.Game.Graphics.UserInterface { @@ -60,14 +61,13 @@ namespace osu.Game.Graphics.UserInterface AutoSizeAxes = Axes.Y, Children = new Drawable[] { - Chevron = new TextAwesome + Chevron = new SpriteIcon { AlwaysPresent = true, Icon = FontAwesome.fa_chevron_right, - UseFullGlyphHeight = false, Colour = Color4.Black, Alpha = 0.5f, - TextSize = 8, + Size = new Vector2(8), Margin = new MarginPadding { Left = 3, Right = 3 }, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, @@ -84,7 +84,7 @@ namespace osu.Game.Graphics.UserInterface private Color4? accentColour; - protected readonly TextAwesome Chevron; + protected readonly SpriteIcon Chevron; protected readonly OsuSpriteText Label; protected override void FormatForeground(bool hover = false) @@ -123,7 +123,7 @@ namespace osu.Game.Graphics.UserInterface set { Text.Text = value; } } - protected readonly TextAwesome Icon; + protected readonly SpriteIcon Icon; private Color4? accentColour; public virtual Color4 AccentColour @@ -152,13 +152,13 @@ namespace osu.Game.Graphics.UserInterface Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, - Icon = new TextAwesome + Icon = new SpriteIcon { Icon = FontAwesome.fa_chevron_down, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding { Right = 4 }, - TextSize = 20 + Size = new Vector2(20), } }; } diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 8c16c048ea..5ad412965c 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -209,10 +209,10 @@ namespace osu.Game.Graphics.UserInterface Foreground.Children = new Drawable[] { - new TextAwesome + new SpriteIcon { Icon = FontAwesome.fa_ellipsis_h, - TextSize = 14, + Size = new Vector2(14), Origin = Anchor.Centre, Anchor = Anchor.Centre, } diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index 57a87dc74b..a0d48e5ebe 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -21,7 +21,7 @@ namespace osu.Game.Graphics.UserInterface { private readonly Box box; private readonly SpriteText text; - private readonly TextAwesome icon; + private readonly SpriteIcon icon; private Color4? accentColour; public Color4 AccentColour @@ -99,9 +99,9 @@ namespace osu.Game.Graphics.UserInterface TextSize = 14, Font = @"Exo2.0-Bold", }, - icon = new TextAwesome + icon = new SpriteIcon { - TextSize = 14, + Size = new Vector2(14), Icon = FontAwesome.fa_circle_o, Shadow = true, }, diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index 0d852e4276..ee5d3baf66 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -3,13 +3,11 @@ using osu.Framework.Graphics; using osu.Framework.Input; +using OpenTK; using OpenTK.Input; namespace osu.Game.Graphics.UserInterface { - /// - /// A textbox which holds focus eagerly. - /// public class SearchTextBox : FocusedTextBox { protected virtual bool AllowCommit => false; @@ -19,13 +17,13 @@ namespace osu.Game.Graphics.UserInterface Height = 35; AddRange(new Drawable[] { - new TextAwesome + new SpriteIcon { Icon = FontAwesome.fa_search, Origin = Anchor.CentreRight, Anchor = Anchor.CentreRight, Margin = new MarginPadding { Right = 10 }, - TextSize = 20 + Size = new Vector2(20), } }); @@ -45,10 +43,16 @@ namespace osu.Game.Graphics.UserInterface case Key.Up: case Key.Down: return false; + } + } + + if (!AllowCommit) + { + switch (args.Key) + { case Key.KeypadEnter: case Key.Enter: - if (!AllowCommit) return false; - break; + return false; } } diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs index 6c5204fed4..e581d19d54 100644 --- a/osu.Game/Graphics/UserInterface/StarCounter.cs +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -142,16 +142,16 @@ namespace osu.Game.Graphics.UserInterface private class Star : Container { - public readonly TextAwesome Icon; + public readonly SpriteIcon Icon; public Star() { Size = new Vector2(star_size); Children = new[] { - Icon = new TextAwesome + Icon = new SpriteIcon { - TextSize = star_size, + Size = new Vector2(star_size), Icon = FontAwesome.fa_star, Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index f290f4fadd..f17b307826 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -215,7 +215,7 @@ namespace osu.Game.Graphics.UserInterface { private const double beat_in_time = 60; - private readonly TextAwesome icon; + private readonly SpriteIcon icon; public FontAwesome Icon { set { icon.Icon = value; } } @@ -226,11 +226,11 @@ namespace osu.Game.Graphics.UserInterface Children = new Drawable[] { - icon = new TextAwesome + icon = new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, - TextSize = 25 + Size = new Vector2(25), } }; } diff --git a/osu.Game/IO/FileInfo.cs b/osu.Game/IO/FileInfo.cs index 6f4c4b26e8..367fd68f7b 100644 --- a/osu.Game/IO/FileInfo.cs +++ b/osu.Game/IO/FileInfo.cs @@ -11,8 +11,6 @@ namespace osu.Game.IO [PrimaryKey, AutoIncrement] public int ID { get; set; } - public string Filename { get; set; } - [Indexed(Unique = true)] public string Hash { get; set; } diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index e8cabafe17..1011fa3236 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using System.IO; using System.Linq; using osu.Framework.Extensions; @@ -23,6 +22,8 @@ namespace osu.Game.IO public readonly ResourceStore Store; + protected override int StoreVersion => 2; + public FileStore(SQLiteConnection connection, Storage storage) : base(connection, storage) { Store = new NamespacedResourceStore(new StorageBackedResourceStore(storage), prefix); @@ -35,25 +36,55 @@ namespace osu.Game.IO protected override void Prepare(bool reset = false) { if (reset) + { + // in earlier versions we stored beatmaps as solid archives, but not any more. + if (Storage.ExistsDirectory("beatmaps")) + Storage.DeleteDirectory("beatmaps"); + + if (Storage.ExistsDirectory(prefix)) + Storage.DeleteDirectory(prefix); + Connection.DropTable(); + } Connection.CreateTable(); + } + protected override void StartupTasks() + { + base.StartupTasks(); deletePending(); } - public FileInfo Add(Stream data, string filename = null) + /// + /// Perform migrations between two store versions. + /// + /// The current store version. This will be zero on a fresh database initialisation. + /// The target version which we are migrating to (equal to the current ). + protected override void PerformMigration(int currentVersion, int targetVersion) + { + base.PerformMigration(currentVersion, targetVersion); + + while (currentVersion++ < targetVersion) + { + switch (currentVersion) + { + case 1: + case 2: + // cannot migrate; breaking underlying changes. + Reset(); + break; + } + } + } + + public FileInfo Add(Stream data) { string hash = data.ComputeSHA2Hash(); - var info = new FileInfo - { - Filename = filename, - Hash = hash, - }; - - var existing = Connection.Table().FirstOrDefault(f => f.Hash == info.Hash); + var existing = Connection.Table().Where(f => f.Hash == hash).FirstOrDefault(); + var info = existing ?? new FileInfo { Hash = hash }; if (existing != null) { info = existing; @@ -73,42 +104,57 @@ namespace osu.Game.IO Connection.Insert(info); } - Reference(new[] { info }); + Reference(info); return info; } - public void Reference(IEnumerable files) + public void Reference(params FileInfo[] files) { - foreach (var f in files) + Connection.RunInTransaction(() => { - f.ReferenceCount++; - Connection.Update(f); - } + var incrementedFiles = files.GroupBy(f => f.ID).Select(f => + { + var accurateRefCount = Connection.Get(f.First().ID); + accurateRefCount.ReferenceCount += f.Count(); + return accurateRefCount; + }); + + Connection.UpdateAll(incrementedFiles); + }); } - public void Dereference(IEnumerable files) + public void Dereference(params FileInfo[] files) { - foreach (var f in files) + Connection.RunInTransaction(() => { - f.ReferenceCount--; - Connection.Update(f); - } + var incrementedFiles = files.GroupBy(f => f.ID).Select(f => + { + var accurateRefCount = Connection.Get(f.First().ID); + accurateRefCount.ReferenceCount -= f.Count(); + return accurateRefCount; + }); + + Connection.UpdateAll(incrementedFiles); + }); } private void deletePending() { - foreach (var f in QueryAndPopulate(f => f.ReferenceCount < 1)) + Connection.RunInTransaction(() => { - try + foreach (var f in Query(f => f.ReferenceCount < 1)) { - Connection.Delete(f); - Storage.Delete(Path.Combine(prefix, f.Hash)); + try + { + Storage.Delete(Path.Combine(prefix, f.StoragePath)); + Connection.Delete(f); + } + catch (Exception e) + { + Logger.Error(e, $@"Could not delete beatmap {f}"); + } } - catch (Exception e) - { - Logger.Error(e, $@"Could not delete beatmap {f}"); - } - } + }); } } } \ No newline at end of file diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs index 7dbc860979..e725742726 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/IJsonSerializable.cs @@ -13,7 +13,7 @@ namespace osu.Game.IO.Serialization { public static string Serialize(this IJsonSerializable obj) { - return JsonConvert.SerializeObject(obj); + return JsonConvert.SerializeObject(obj, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); } public static T Deserialize(this string objString) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 7e3bb44465..57f5c54a18 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -117,7 +117,7 @@ namespace osu.Game.Online.API if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, Password)) { //todo: this fails even on network-related issues. we should probably handle those differently. - //NotificationManager.ShowMessage("Login failed!"); + //NotificationOverlay.ShowMessage("Login failed!"); log.Add(@"Login failed!"); Password = null; continue; @@ -254,7 +254,7 @@ namespace osu.Game.Online.API { //OsuGame.Scheduler.Add(delegate { - //NotificationManager.ShowMessage($@"We just went {newState}!", newState == APIState.Online ? Color4.YellowGreen : Color4.OrangeRed, 5000); + //NotificationOverlay.ShowMessage($@"We just went {newState}!", newState == APIState.Online ? Color4.YellowGreen : Color4.OrangeRed, 5000); log.Add($@"We just went {newState}!"); Scheduler.Add(delegate { diff --git a/osu.Game/Online/Multiplayer/GameType.cs b/osu.Game/Online/Multiplayer/GameType.cs index 22e2ffac31..c94b409d1b 100644 --- a/osu.Game/Online/Multiplayer/GameType.cs +++ b/osu.Game/Online/Multiplayer/GameType.cs @@ -21,15 +21,14 @@ namespace osu.Game.Online.Multiplayer public override string Name => "Tag"; public override Drawable GetIcon(OsuColour colours, float size) { - return new TextAwesome + return new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.fa_refresh, - TextSize = size, + Size = new Vector2(size), Colour = colours.Blue, Shadow = false, - UseFullGlyphHeight = false, }; } } @@ -61,21 +60,19 @@ namespace osu.Game.Online.Multiplayer Spacing = new Vector2(2f), Children = new[] { - new TextAwesome + new SpriteIcon { Icon = FontAwesome.fa_refresh, - TextSize = size * 0.75f, + Size = new Vector2(size * 0.75f), Colour = colours.Blue, Shadow = false, - UseFullGlyphHeight = false, }, - new TextAwesome + new SpriteIcon { Icon = FontAwesome.fa_refresh, - TextSize = size * 0.75f, + Size = new Vector2(size * 0.75f), Colour = colours.Pink, Shadow = false, - UseFullGlyphHeight = false, }, }, }; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4f4c2e2883..8d8c5cf26e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -20,6 +20,7 @@ using osu.Game.Screens.Menu; using OpenTK; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Rulesets.Scoring; @@ -37,7 +38,7 @@ namespace osu.Game private MusicController musicController; - private NotificationManager notificationManager; + private NotificationOverlay notificationOverlay; private DialogOverlay dialogOverlay; @@ -47,6 +48,8 @@ namespace osu.Game private UserProfileOverlay userProfile; + public virtual Storage GetStorageForStableInstall() => null; + private Intro intro { get @@ -132,7 +135,7 @@ namespace osu.Game if (s.Beatmap == null) { - notificationManager.Post(new SimpleNotification + notificationOverlay.Post(new SimpleNotification { Text = @"Tried to load a score for a beatmap we don't have!", Icon = FontAwesome.fa_life_saver, @@ -149,6 +152,10 @@ namespace osu.Game { base.LoadComplete(); + // hook up notifications to components. + BeatmapManager.PostNotification = n => notificationOverlay?.Post(n); + BeatmapManager.GetStableStorage = GetStorageForStableInstall; + AddRange(new Drawable[] { new VolumeControlReceptor { @@ -189,7 +196,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, overlayContent.Add); - LoadComponentAsync(notificationManager = new NotificationManager + LoadComponentAsync(notificationOverlay = new NotificationOverlay { Depth = -3, Anchor = Anchor.TopRight, @@ -205,7 +212,7 @@ namespace osu.Game { if (entry.Level < LogLevel.Important) return; - notificationManager.Post(new SimpleNotification + notificationOverlay.Post(new SimpleNotification { Text = $@"{entry.Level}: {entry.Message}" }); @@ -216,7 +223,7 @@ namespace osu.Game dependencies.Cache(chat); dependencies.Cache(userProfile); dependencies.Cache(musicController); - dependencies.Cache(notificationManager); + dependencies.Cache(notificationOverlay); dependencies.Cache(dialogOverlay); // ensure both overlays aren't presented at the same time @@ -331,6 +338,7 @@ namespace osu.Game direct.State = Visibility.Hidden; social.State = Visibility.Hidden; userProfile.State = Visibility.Hidden; + notificationOverlay.State = Visibility.Hidden; } else { diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs index 791377187b..f43154ea20 100644 --- a/osu.Game/Overlays/Chat/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelListItem.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Chat private readonly Bindable joinedBind = new Bindable(); private readonly OsuSpriteText name; private readonly OsuSpriteText topic; - private readonly TextAwesome joinedCheckmark; + private readonly SpriteIcon joinedCheckmark; private Color4 joinedColour; private Color4 topicColour; @@ -68,12 +68,12 @@ namespace osu.Game.Overlays.Chat { Children = new[] { - joinedCheckmark = new TextAwesome + joinedCheckmark = new SpriteIcon { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Icon = FontAwesome.fa_check_circle, - TextSize = text_size, + Size = new Vector2(text_size), Shadow = false, Margin = new MarginPadding { Right = 10f }, Alpha = 0f, @@ -121,10 +121,10 @@ namespace osu.Game.Overlays.Chat Spacing = new Vector2(3f, 0f), Children = new Drawable[] { - new TextAwesome + new SpriteIcon { Icon = FontAwesome.fa_user, - TextSize = text_size - 2, + Size = new Vector2(text_size - 2), Shadow = false, Margin = new MarginPadding { Top = 1 }, }, diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index c3c930eba0..4ff9169877 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -35,13 +35,13 @@ namespace osu.Game.Overlays.Chat TabContainer.Spacing = new Vector2(-shear_width, 0); TabContainer.Masking = false; - AddInternal(new TextAwesome + AddInternal(new SpriteIcon { Icon = FontAwesome.fa_comments, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - TextSize = 20, - Padding = new MarginPadding(10), + Size = new Vector2(20), + Margin = new MarginPadding(10), }); AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" })); @@ -72,7 +72,7 @@ namespace osu.Game.Overlays.Chat private readonly SpriteText textBold; private readonly Box box; private readonly Box highlightBox; - private readonly TextAwesome icon; + private readonly SpriteIcon icon; private void updateState() { @@ -176,7 +176,7 @@ namespace osu.Game.Overlays.Chat RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - icon = new TextAwesome + icon = new SpriteIcon { Icon = FontAwesome.fa_hashtag, Anchor = Anchor.CentreLeft, @@ -184,7 +184,7 @@ namespace osu.Game.Overlays.Chat Colour = Color4.Black, X = -10, Alpha = 0.2f, - TextSize = ChatOverlay.TAB_AREA_HEIGHT, + Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), }, text = new OsuSpriteText { diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 97aae2f49c..9b19b8150e 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -30,14 +30,14 @@ namespace osu.Game.Overlays.Dialog private readonly Container content; private readonly Container ring; private readonly FillFlowContainer buttonsContainer; - private readonly TextAwesome iconText; + private readonly SpriteIcon icon; private readonly SpriteText header; private readonly SpriteText body; public FontAwesome Icon { - get { return iconText.Icon; } - set { iconText.Icon = value; } + get { return icon.Icon; } + set { icon.Icon = value; } } public string HeaderText @@ -205,12 +205,12 @@ namespace osu.Game.Overlays.Dialog RelativeSizeAxes = Axes.Both, Colour = Color4.Black.Opacity(0), }, - iconText = new TextAwesome + icon = new SpriteIcon { Origin = Anchor.Centre, Anchor = Anchor.Centre, Icon = FontAwesome.fa_close, - TextSize = 50, + Size = new Vector2(50), }, }, }, diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index b9063a5c82..5b45fc7725 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -152,18 +152,17 @@ namespace osu.Game.Overlays.Direct private class DownloadButton : OsuClickableContainer { - private readonly TextAwesome icon; + private readonly SpriteIcon icon; public DownloadButton() { Children = new Drawable[] { - icon = new TextAwesome + icon = new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, - UseFullGlyphHeight = false, - TextSize = 30, + Size = new Vector2(30), Icon = FontAwesome.fa_osu_chevron_down_o, }, }; diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 75619d9ba4..2f048b0e3d 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -75,11 +75,11 @@ namespace osu.Game.Overlays.Direct { Font = @"Exo2.0-SemiBoldItalic", }, - new TextAwesome + new SpriteIcon { Icon = icon, Shadow = true, - TextSize = 14, + Size = new Vector2(14), Margin = new MarginPadding { Top = 1 }, }, }; diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 4f815f220c..28d26d0641 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -47,7 +47,11 @@ namespace osu.Game.Overlays.Direct private class RulesetToggleButton : OsuClickableContainer { - private readonly TextAwesome icon; + private Drawable icon + { + get { return iconContainer.Icon; } + set { iconContainer.Icon = value; } + } private RulesetInfo ruleset; public RulesetInfo Ruleset @@ -56,15 +60,17 @@ namespace osu.Game.Overlays.Direct set { ruleset = value; - icon.Icon = Ruleset.CreateInstance().Icon; + icon = Ruleset.CreateInstance().CreateIcon(); } } private readonly Bindable bindable; + private readonly ConstrainedIconContainer iconContainer; + private void Bindable_ValueChanged(RulesetInfo obj) { - icon.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100); + iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100); } public RulesetToggleButton(Bindable bindable, RulesetInfo ruleset) @@ -74,11 +80,11 @@ namespace osu.Game.Overlays.Direct Children = new[] { - icon = new TextAwesome + iconContainer = new ConstrainedIconContainer { Origin = Anchor.TopLeft, Anchor = Anchor.TopLeft, - TextSize = 32, + Size = new Vector2(32), } }; diff --git a/osu.Game/Overlays/Music/CollectionsDropdown.cs b/osu.Game/Overlays/Music/CollectionsDropdown.cs index f6016fd1db..0c0a636be8 100644 --- a/osu.Game/Overlays/Music/CollectionsDropdown.cs +++ b/osu.Game/Overlays/Music/CollectionsDropdown.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Music { CornerRadius = 5; Height = 30; - Icon.TextSize = 14; + Icon.Size = new Vector2(14); Icon.Margin = new MarginPadding(0); Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 10, Right = 10 }; EdgeEffect = new EdgeEffectParameters diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 1e3e48b17a..4145a8d1f0 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -12,6 +12,7 @@ using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using OpenTK; namespace osu.Game.Overlays.Music { @@ -22,7 +23,7 @@ namespace osu.Game.Overlays.Music private Color4 hoverColour; private Color4 artistColour; - private TextAwesome handle; + private SpriteIcon handle; private TextFlowContainer text; private IEnumerable titleSprites; private UnicodeBindableString titleBind; @@ -67,16 +68,15 @@ namespace osu.Game.Overlays.Music Children = new Drawable[] { - handle = new TextAwesome + handle = new SpriteIcon { Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, - TextSize = 12, + Size = new Vector2(12), Colour = colours.Gray5, Icon = FontAwesome.fa_bars, Alpha = 0f, - Margin = new MarginPadding { Left = 5 }, - Padding = new MarginPadding { Top = 2 }, + Margin = new MarginPadding { Left = 5, Top = 2 }, }, text = new OsuTextFlowContainer { diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 88f499f9a6..3dd514edeb 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Music public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) { - PlaylistItem itemToRemove = items.Children.FirstOrDefault(item => item.BeatmapSetInfo == beatmapSet); + PlaylistItem itemToRemove = items.Children.FirstOrDefault(item => item.BeatmapSetInfo.ID == beatmapSet.ID); if (itemToRemove != null) items.Remove(itemToRemove); } diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 31fe755d2b..942633b35e 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -6,14 +6,14 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Input; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; -using osu.Framework.Input; -using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays.Music { @@ -77,11 +77,11 @@ namespace osu.Game.Overlays.Music }, }; + beatmaps.BeatmapSetAdded += s => Schedule(() => list.AddBeatmapSet(s)); + beatmaps.BeatmapSetRemoved += s => Schedule(() => list.RemoveBeatmapSet(s)); + list.BeatmapSets = BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); - // todo: these should probably be above the query. - beatmaps.BeatmapSetAdded += s => list.AddBeatmapSet(s); - beatmaps.BeatmapSetRemoved += s => list.RemoveBeatmapSet(s); beatmapBacking.BindTo(game.Beatmap); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 2807a02543..d970089942 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -12,18 +12,18 @@ 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.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Localisation; +using osu.Framework.Threading; using osu.Game.Beatmaps; 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; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays @@ -41,7 +41,9 @@ namespace osu.Game.Overlays private Drawable currentBackground; private ProgressBar progressBar; + private IconButton prevButton; private IconButton playButton; + private IconButton nextButton; private IconButton playlistButton; private SpriteText title, artist; @@ -158,7 +160,7 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Children = new[] { - new IconButton + prevButton = new IconButton { Action = prev, Icon = FontAwesome.fa_step_backward, @@ -170,7 +172,7 @@ namespace osu.Game.Overlays Action = play, Icon = FontAwesome.fa_play_circle_o, }, - new IconButton + nextButton = new IconButton { Action = next, Icon = FontAwesome.fa_step_forward, @@ -209,11 +211,22 @@ namespace osu.Game.Overlays protected override void LoadComplete() { beatmapBacking.ValueChanged += beatmapChanged; + beatmapBacking.DisabledChanged += beatmapDisabledChanged; beatmapBacking.TriggerChange(); base.LoadComplete(); } + private void beatmapDisabledChanged(bool disabled) + { + if (disabled) + playlist.Hide(); + + prevButton.Enabled.Value = !disabled; + nextButton.Enabled.Value = !disabled; + playlistButton.Enabled.Value = !disabled; + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -233,7 +246,8 @@ namespace osu.Game.Overlays playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o; - if (track.HasCompleted && !track.Looping) next(); + if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled) + next(); } else playButton.Icon = FontAwesome.fa_play_circle_o; @@ -245,7 +259,8 @@ namespace osu.Game.Overlays if (track == null) { - playlist.PlayNext(); + if (!beatmapBacking.Disabled) + playlist.PlayNext(); return; } @@ -257,16 +272,12 @@ namespace osu.Game.Overlays private void prev() { - if (beatmapBacking.Disabled) return; - queuedDirection = TransformDirection.Prev; playlist.PlayPrevious(); } private void next() { - if (beatmapBacking.Disabled) return; - queuedDirection = TransformDirection.Next; playlist.PlayNext(); } diff --git a/osu.Game/Overlays/NotificationManager.cs b/osu.Game/Overlays/NotificationOverlay.cs similarity index 79% rename from osu.Game/Overlays/NotificationManager.cs rename to osu.Game/Overlays/NotificationOverlay.cs index ad0236ae1f..7eabb592c6 100644 --- a/osu.Game/Overlays/NotificationManager.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Overlays { - public class NotificationManager : OsuFocusedOverlayContainer + public class NotificationOverlay : OsuFocusedOverlayContainer { private const float width = 320; @@ -28,6 +28,8 @@ namespace osu.Game.Overlays Width = width; RelativeSizeAxes = Axes.Y; + AlwaysPresent = true; + Children = new Drawable[] { new Box @@ -72,17 +74,20 @@ namespace osu.Game.Overlays public void Post(Notification notification) { - State = Visibility.Visible; + Schedule(() => + { + State = Visibility.Visible; - ++runningDepth; - notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth; + ++runningDepth; + notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth; - var hasCompletionTarget = notification as IHasCompletionTarget; - if (hasCompletionTarget != null) - hasCompletionTarget.CompletionTarget = Post; + var hasCompletionTarget = notification as IHasCompletionTarget; + if (hasCompletionTarget != null) + hasCompletionTarget.CompletionTarget = Post; - var ourType = notification.GetType(); - sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)))?.Add(notification); + var ourType = notification.GetType(); + sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)))?.Add(notification); + }); } protected override void PopIn() @@ -109,4 +114,4 @@ namespace osu.Game.Overlays this.FadeTo(0, TRANSITION_LENGTH / 2); } } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index a590507f41..14446a468c 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -63,6 +63,8 @@ namespace osu.Game.Overlays.Notifications Masking = true, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + AutoSizeDuration = 400, + AutoSizeEasing = Easing.OutQuint, Children = new Drawable[] { new Box @@ -74,7 +76,7 @@ namespace osu.Game.Overlays.Notifications { RelativeSizeAxes = Axes.X, Padding = new MarginPadding(5), - Height = 60, + AutoSizeAxes = Axes.Y, Children = new Drawable[] { IconContent = new Container @@ -163,12 +165,12 @@ namespace osu.Game.Overlays.Notifications Children = new[] { - new TextAwesome + new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.fa_times_circle, - TextSize = 20 + Size = new Vector2(20), } }; } diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 98aac3a02d..f42b4b6cb3 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -6,9 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; @@ -18,10 +16,9 @@ namespace osu.Game.Overlays.Notifications { public string Text { - get { return textDrawable.Text; } set { - textDrawable.Text = value; + Schedule(() => textDrawable.Text = value); } } @@ -90,7 +87,7 @@ namespace osu.Game.Overlays.Notifications protected virtual Notification CreateCompletionNotification() => new ProgressCompletionNotification { Activated = CompletionClickAction, - Text = $"Task \"{Text}\" has completed!" + Text = "Task has completed!" }; protected virtual void Completed() @@ -106,7 +103,7 @@ namespace osu.Game.Overlays.Notifications private Color4 colourActive; private Color4 colourCancelled; - private readonly SpriteText textDrawable; + private readonly TextFlowContainer textDrawable; public ProgressNotification() { @@ -115,9 +112,11 @@ namespace osu.Game.Overlays.Notifications RelativeSizeAxes = Axes.Both, }); - Content.Add(textDrawable = new OsuSpriteText + Content.Add(textDrawable = new TextFlowContainer(t => + { + t.TextSize = 16; + }) { - TextSize = 16, Colour = OsuColour.Gray(128), AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, @@ -131,6 +130,9 @@ namespace osu.Game.Overlays.Notifications }); State = ProgressNotificationState.Queued; + + // don't close on click by default. + Activated = () => false; } [BackgroundDependencyLoader] @@ -167,7 +169,7 @@ namespace osu.Game.Overlays.Notifications private class ProgressBar : Container { - private Box box; + private readonly Box box; private Color4 colourActive; private Color4 colourInactive; @@ -197,15 +199,8 @@ namespace osu.Game.Overlays.Notifications } } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) + public ProgressBar() { - colourActive = colours.Blue; - Colour = colourInactive = OsuColour.Gray(0.5f); - - Height = 5; - Children = new[] { box = new Box @@ -215,6 +210,15 @@ namespace osu.Game.Overlays.Notifications } }; } + + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + colourActive = colours.Blue; + Colour = colourInactive = OsuColour.Gray(0.5f); + Height = 5; + } } } diff --git a/osu.Game/Overlays/Notifications/SimpleNotification.cs b/osu.Game/Overlays/Notifications/SimpleNotification.cs index 44e6d92aef..e10cc26546 100644 --- a/osu.Game/Overlays/Notifications/SimpleNotification.cs +++ b/osu.Game/Overlays/Notifications/SimpleNotification.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using OpenTK; namespace osu.Game.Overlays.Notifications { @@ -36,7 +37,7 @@ namespace osu.Game.Overlays.Notifications } private readonly SpriteText textDrawable; - private readonly TextAwesome iconDrawable; + private readonly SpriteIcon iconDrawable; protected Box IconBackgound; @@ -49,12 +50,12 @@ namespace osu.Game.Overlays.Notifications RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.6f)) }, - iconDrawable = new TextAwesome + iconDrawable = new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = icon, - TextSize = 20 + Size = new Vector2(20), } }); diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 891487cfa7..f3e8dc5562 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -111,12 +111,12 @@ namespace osu.Game.Overlays.Profile Alpha = 0, AlwaysPresent = true }, - new TextAwesome + new SpriteIcon { Icon = FontAwesome.fa_heart, Anchor = Anchor.Centre, Origin = Anchor.Centre, - TextSize = 12 + Size = new Vector2(12), } } }, diff --git a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs index 7157861632..9fa266c5fe 100644 --- a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs +++ b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.SearchableList private class DisplayStyleToggleButton : OsuClickableContainer { - private readonly TextAwesome icon; + private readonly SpriteIcon icon; private readonly PanelDisplayStyle style; private readonly Bindable bindable; @@ -67,13 +67,12 @@ namespace osu.Game.Overlays.SearchableList Children = new Drawable[] { - this.icon = new TextAwesome + this.icon = new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = icon, - TextSize = 18, - UseFullGlyphHeight = false, + Size = new Vector2(18), Alpha = 0.5f, }, }; diff --git a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs index af99a39cc4..4239a123b8 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs @@ -55,9 +55,9 @@ namespace osu.Game.Overlays.SearchableList Spacing = new Vector2(10f, 0f), Children = new[] { - new TextAwesome + new SpriteIcon { - TextSize = 25, + Size = new Vector2(25), Icon = Icon, }, CreateHeaderText(), diff --git a/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs b/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs index e2eec0214f..38e3e44911 100644 --- a/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs +++ b/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs @@ -6,6 +6,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; +using OpenTK; namespace osu.Game.Overlays.SearchableList { @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.SearchableList public SlimDropdownHeader() { Height = 25; - Icon.TextSize = 16; + Icon.Size = new Vector2(16); Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 }; } diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 6268a9753a..d07f156673 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -286,7 +286,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { public const float LABEL_LEFT_MARGIN = 20; - private readonly TextAwesome statusIcon; + private readonly SpriteIcon statusIcon; public Color4 StatusColour { set @@ -308,15 +308,15 @@ namespace osu.Game.Overlays.Settings.Sections.General Radius = 4, }; - Icon.TextSize = 14; + Icon.Size = new Vector2(14); Icon.Margin = new MarginPadding(0); - Foreground.Add(statusIcon = new TextAwesome + Foreground.Add(statusIcon = new SpriteIcon { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Icon = FontAwesome.fa_circle_o, - TextSize = 14, + Size = new Vector2(14), }); Text.Margin = new MarginPadding { Left = LABEL_LEFT_MARGIN }; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs new file mode 100644 index 0000000000..9d13a2ae2f --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -0,0 +1,47 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings.Sections.Maintenance +{ + public class GeneralSettings : SettingsSubsection + { + private OsuButton importButton; + private OsuButton deleteButton; + + protected override string Header => "General"; + + [BackgroundDependencyLoader] + private void load(BeatmapManager beatmaps) + { + Children = new Drawable[] + { + importButton = new OsuButton + { + RelativeSizeAxes = Axes.X, + Text = "Import beatmaps from stable", + Action = () => + { + importButton.Enabled.Value = false; + Task.Run(() => beatmaps.ImportFromStable()).ContinueWith(t => Schedule(() => importButton.Enabled.Value = true)); + } + }, + deleteButton = new OsuButton + { + RelativeSizeAxes = Axes.X, + Text = "Delete ALL beatmaps", + Action = () => + { + deleteButton.Enabled.Value = false; + Task.Run(() => beatmaps.DeleteAll()).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true)); + } + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs index 529cec79c1..b42c64d324 100644 --- a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs +++ b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics; +using osu.Game.Overlays.Settings.Sections.Maintenance; using OpenTK; namespace osu.Game.Overlays.Settings.Sections @@ -17,6 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections FlowContent.Spacing = new Vector2(0, 5); Children = new Drawable[] { + new GeneralSettings() }; } } diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index 6c25b146a1..aef9f071db 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using OpenTK; @@ -27,12 +28,14 @@ namespace osu.Game.Overlays.Settings foreach (var ruleset in rulesets.AllRulesets) { - modes.Add(new TextAwesome + var icon = new ConstrainedIconContainer { - Icon = ruleset.CreateInstance().Icon, + Icon = ruleset.CreateInstance().CreateIcon(), Colour = Color4.Gray, - TextSize = 20 - }); + Size = new Vector2(20), + }; + + modes.Add(icon); } Children = new Drawable[] diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index 309216dd91..7af7363dda 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings { public class SidebarButton : Container { - private readonly TextAwesome drawableIcon; + private readonly SpriteIcon drawableIcon; private readonly SpriteText headerText; private readonly Box backgroundBox; private readonly Box selectionIndicator; @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Settings Width = Sidebar.DEFAULT_WIDTH, RelativeSizeAxes = Axes.Y, Colour = OsuColour.Gray(0.6f), - Children = new[] + Children = new Drawable[] { headerText = new OsuSpriteText { @@ -85,11 +85,11 @@ namespace osu.Game.Overlays.Settings Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, - drawableIcon = new TextAwesome + drawableIcon = new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, - TextSize = 20 + Size = new Vector2(20), }, } }, diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index b5e832d381..f2df2721d3 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -20,10 +20,21 @@ namespace osu.Game.Overlays.Toolbar { public const float WIDTH = Toolbar.HEIGHT * 1.4f; + public void SetIcon(Drawable icon) + { + IconContainer.Icon = icon; + IconContainer.Show(); + } + + public void SetIcon(FontAwesome icon) => SetIcon(new SpriteIcon + { + Size = new Vector2(20), + Icon = icon + }); + public FontAwesome Icon { - get { return DrawableIcon.Icon; } - set { DrawableIcon.Icon = value; } + set { SetIcon(value); } } public string Text @@ -55,7 +66,7 @@ namespace osu.Game.Overlays.Toolbar protected virtual Anchor TooltipAnchor => Anchor.TopLeft; - protected TextAwesome DrawableIcon; + protected ConstrainedIconContainer IconContainer; protected SpriteText DrawableText; protected Box HoverBackground; private readonly FillFlowContainer tooltipContainer; @@ -88,11 +99,12 @@ namespace osu.Game.Overlays.Toolbar AutoSizeAxes = Axes.X, Children = new Drawable[] { - DrawableIcon = new TextAwesome + IconContainer = new ConstrainedIconContainer { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - TextSize = 20 + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20), + Alpha = 0, }, DrawableText = new OsuSpriteText { diff --git a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs index 39909b8d5b..2e2786851c 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs @@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Toolbar { public ToolbarChatButton() { - Icon = FontAwesome.fa_comments; + SetIcon(FontAwesome.fa_comments); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs index 2c50897e1f..b615cd3303 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Toolbar TooltipMain = rInstance.Description; TooltipSub = $"Play some {rInstance.Description}"; - Icon = rInstance.Icon; + SetIcon(rInstance.CreateIcon()); } } @@ -31,9 +31,8 @@ namespace osu.Game.Overlays.Toolbar { if (value) { - DrawableIcon.Colour = Color4.White; - DrawableIcon.Masking = true; - DrawableIcon.EdgeEffect = new EdgeEffectParameters + IconContainer.Colour = Color4.White; + IconContainer.EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Colour = new Color4(255, 194, 224, 100), @@ -43,8 +42,8 @@ namespace osu.Game.Overlays.Toolbar } else { - DrawableIcon.Masking = false; - DrawableIcon.Colour = new Color4(255, 194, 224, 255); + IconContainer.Colour = new Color4(255, 194, 224, 255); + IconContainer.EdgeEffect = new EdgeEffectParameters(); } } } @@ -52,7 +51,7 @@ namespace osu.Game.Overlays.Toolbar protected override void LoadComplete() { base.LoadComplete(); - DrawableIcon.TextSize *= 1.4f; + IconContainer.Scale *= 1.4f; } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs index 5126f6a2a4..dcadc4bf56 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs @@ -19,9 +19,9 @@ namespace osu.Game.Overlays.Toolbar } [BackgroundDependencyLoader] - private void load(NotificationManager notificationManager) + private void load(NotificationOverlay notificationOverlay) { - StateContainer = notificationManager; + StateContainer = notificationOverlay; } } } \ No newline at end of file diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index f604eb5cd2..f03ef3f1ed 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -169,13 +169,11 @@ namespace osu.Game.Overlays { header.User = user; - for (int i = 0; i < user.ProfileOrder.Length; i++) + foreach (string id in user.ProfileOrder) { - string id = user.ProfileOrder[i]; var sec = sections.FirstOrDefault(s => s.Identifier == id); if (sec != null) { - sec.Depth = -i; sectionsContainer.Add(sec); tabs.AddItem(sec); } diff --git a/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs b/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs new file mode 100644 index 0000000000..58f5defb5e --- /dev/null +++ b/osu.Game/Rulesets/Mods/IApplicableToDifficulty.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 osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for mods that make general adjustments to difficulty. + /// + public interface IApplicableToDifficulty + { + void ApplyToDifficulty(BeatmapDifficulty difficulty); + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index c00847b7d8..f54267af29 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -45,5 +45,10 @@ namespace osu.Game.Rulesets.Mods /// The mods this mod cannot be enabled with. /// public virtual Type[] IncompatibleMods => new Type[] { }; + + /// + /// Whether we should allow failing at the current point in time. + /// + public virtual bool AllowFail => true; } } diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs index 8eef9c70d6..075a62b0d7 100644 --- a/osu.Game/Rulesets/Mods/ModEasy.cs +++ b/osu.Game/Rulesets/Mods/ModEasy.cs @@ -2,11 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Game.Beatmaps; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModEasy : Mod + public abstract class ModEasy : Mod, IApplicableToDifficulty { public override string Name => "Easy"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy; @@ -15,5 +16,14 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 0.5; public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; + + public void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + const float ratio = 0.5f; + difficulty.CircleSize *= ratio; + difficulty.ApproachRate *= ratio; + difficulty.DrainRate *= ratio; + difficulty.OverallDifficulty *= ratio; + } } } \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index 2516c02526..f33f46a207 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -2,16 +2,26 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Game.Beatmaps; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModHardRock : Mod + public abstract class ModHardRock : Mod, IApplicableToDifficulty { public override string Name => "Hard Rock"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock; public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Everything just got a bit harder..."; public override Type[] IncompatibleMods => new[] { typeof(ModEasy) }; + + public void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + const float ratio = 1.4f; + difficulty.CircleSize *= 1.3f; // CS uses a custom 1.3 ratio. + difficulty.ApproachRate *= ratio; + difficulty.DrainRate *= ratio; + difficulty.OverallDifficulty *= ratio; + } } -} \ No newline at end of file +} diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 613e1e1d4d..d41c4e3956 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -15,5 +15,10 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 0.5; public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) }; + + /// + /// We never fail, 'yo. + /// + public override bool AllowFail => false; } } \ No newline at end of file diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 3dbb39d894..b3be36a983 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using System.Collections.Generic; +using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; using osu.Game.Overlays.Settings; @@ -18,6 +19,8 @@ namespace osu.Game.Rulesets public abstract IEnumerable GetModsFor(ModType type); + public abstract Mod GetAutoplayMod(); + /// /// Attempt to create a hit renderer for a beatmap /// @@ -31,7 +34,7 @@ namespace osu.Game.Rulesets public abstract ScoreProcessor CreateScoreProcessor(); - public virtual FontAwesome Icon => FontAwesome.fa_question_circle; + public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle }; public abstract string Description { get; } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 88aee2bffc..1564df1366 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets { var us = createRulesetInfo(r); - var existing = Query().FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo); + var existing = Query().Where(ri => ri.InstantiationInfo == us.InstantiationInfo).FirstOrDefault(); if (existing == null) Connection.Insert(us); diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 79fb34a523..cd9089c859 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -16,8 +16,9 @@ namespace osu.Game.Rulesets.Scoring { /// /// Invoked when the ScoreProcessor is in a failed state. + /// Return true if the fail was permitted. /// - public event Action Failed; + public event Func Failed; /// /// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the . @@ -106,8 +107,8 @@ namespace osu.Game.Rulesets.Scoring if (alreadyFailed || !HasFailed) return; - alreadyFailed = true; - Failed?.Invoke(); + if (Failed?.Invoke() != false) + alreadyFailed = true; } /// diff --git a/osu.Game/Rulesets/UI/HitRenderer.cs b/osu.Game/Rulesets/UI/HitRenderer.cs index 8fe1dd3e0a..2ce868b22e 100644 --- a/osu.Game/Rulesets/UI/HitRenderer.cs +++ b/osu.Game/Rulesets/UI/HitRenderer.cs @@ -153,6 +153,10 @@ namespace osu.Game.Rulesets.UI // Convert the beatmap Beatmap = converter.Convert(beatmap.Beatmap, isForCurrentRuleset); + // Apply difficulty adjustments from mods before using Difficulty. + foreach (var mod in Mods.OfType()) + mod.ApplyToDifficulty(Beatmap.BeatmapInfo.Difficulty); + // Apply defaults foreach (var h in Beatmap.HitObjects) h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty); @@ -163,7 +167,7 @@ namespace osu.Game.Rulesets.UI ApplyBeatmap(); // Add mods, should always be the last thing applied to give full control to mods - applyMods(beatmap.Mods.Value); + applyMods(Mods); } /// diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index b23028098f..986d8c92dc 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -8,13 +8,14 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; +using OpenTK; namespace osu.Game.Rulesets.UI { public class ModIcon : Container { - private readonly TextAwesome modIcon; - private readonly TextAwesome background; + private readonly SpriteIcon modIcon; + private readonly SpriteIcon background; private const float icon_size = 80; @@ -34,20 +35,21 @@ namespace osu.Game.Rulesets.UI Children = new Drawable[] { - background = new TextAwesome + background = new SpriteIcon { Origin = Anchor.Centre, Anchor = Anchor.Centre, - TextSize = icon_size, + Size = new Vector2(icon_size), Icon = FontAwesome.fa_osu_mod_bg, Shadow = true, }, - modIcon = new TextAwesome + modIcon = new SpriteIcon { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, Colour = OsuColour.Gray(84), - TextSize = icon_size - 35, + Size = new Vector2(icon_size - 35), + Y = 25, Icon = mod.Icon }, }; diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index e55c4ef4fe..0898c079ce 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Menu private readonly Container iconText; private readonly Container box; private readonly Box boxHoverLayer; - private readonly TextAwesome icon; + private readonly SpriteIcon icon; private readonly string sampleName; private readonly Action clickAction; private readonly Key triggerKey; @@ -95,12 +95,12 @@ namespace osu.Game.Screens.Menu Origin = Anchor.Centre, Children = new Drawable[] { - icon = new TextAwesome + icon = new SpriteIcon { Shadow = true, Anchor = Anchor.Centre, Origin = Anchor.Centre, - TextSize = 30, + Size = new Vector2(30), Position = new Vector2(0, 0), Icon = symbol }, diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 6a176898a2..1ac5823ec4 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.Menu internal class Disclaimer : OsuScreen { private Intro intro; - private readonly TextAwesome icon; + private readonly SpriteIcon icon; private Color4 iconColour; internal override bool ShowOverlays => false; @@ -37,12 +37,12 @@ namespace osu.Game.Screens.Menu Spacing = new Vector2(0, 2), Children = new Drawable[] { - icon = new TextAwesome + icon = new SpriteIcon { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Icon = FontAwesome.fa_warning, - TextSize = 30, + Size = new Vector2(30), }, new OsuSpriteText { diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index d215385ece..6e79d2b427 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -47,7 +47,7 @@ namespace osu.Game.Screens } } - private readonly Bindable ruleset = new Bindable(); + protected readonly Bindable Ruleset = new Bindable(); private SampleChannel sampleExit; @@ -64,7 +64,7 @@ namespace osu.Game.Screens } if (osuGame != null) - ruleset.BindTo(osuGame.Ruleset); + Ruleset.BindTo(osuGame.Ruleset); sampleExit = audio.Sample.Get(@"UI/melodic-1"); } @@ -77,7 +77,7 @@ namespace osu.Game.Screens { // we only want to apply these restrictions when we are inside a screen stack. // the use case for not applying is in visual/unit tests. - ruleset.Disabled = !AllowBeatmapRulesetChange; + Ruleset.Disabled = !AllowBeatmapRulesetChange; Beatmap.Disabled = !AllowBeatmapRulesetChange; } } diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 70093a1407..ea75c140db 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader(true)] - private void load(OsuConfigManager config, NotificationManager notificationManager, OsuColour colours) + private void load(OsuConfigManager config, NotificationOverlay notificationOverlay, OsuColour colours) { showHud = config.GetBindable(OsuSetting.ShowInterface); showHud.ValueChanged += hudVisibility => content.FadeTo(hudVisibility ? 1 : 0, duration); @@ -71,7 +71,7 @@ namespace osu.Game.Screens.Play { hasShownNotificationOnce = true; - notificationManager?.Post(new SimpleNotification + notificationOverlay?.Post(new SimpleNotification { Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab." }); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 84261d509e..a13e9ed369 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -4,7 +4,6 @@ using OpenTK; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -23,6 +22,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; using osu.Framework.Audio.Sample; +using osu.Game.Beatmaps; namespace osu.Game.Screens.Play { @@ -78,23 +78,28 @@ namespace osu.Game.Screens.Play Ruleset rulesetInstance; + WorkingBeatmap working = Beatmap.Value; + Beatmap beatmap; + try { - if (Beatmap.Value.Beatmap == null) + beatmap = working.Beatmap; + + if (beatmap == null) throw new InvalidOperationException("Beatmap was not loaded"); - ruleset = osu?.Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; + ruleset = osu?.Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); try { - HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, ruleset.ID == Beatmap.Value.BeatmapInfo.Ruleset.ID); + HitRenderer = rulesetInstance.CreateHitRendererWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID); } catch (BeatmapInvalidForRulesetException) { // we may fail to create a HitRenderer if the beatmap cannot be loaded with the user's preferred ruleset // let's try again forcing the beatmap's ruleset. - ruleset = Beatmap.Value.BeatmapInfo.Ruleset; + ruleset = beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, true); } @@ -111,17 +116,11 @@ namespace osu.Game.Screens.Play return; } - Track track = Beatmap.Value.Track; - - if (track != null) - adjustableSourceClock = track; - - adjustableSourceClock = (IAdjustableClock)track ?? new StopwatchClock(); - + adjustableSourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock(); decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; var firstObjectTime = HitRenderer.Objects.First().StartTime; - decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, Beatmap.Value.BeatmapInfo.AudioLeadIn))); + decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))); decoupledClock.ProcessFrame(); offsetClock = new FramedOffsetClock(decoupledClock); @@ -134,7 +133,7 @@ namespace osu.Game.Screens.Play { adjustableSourceClock.Reset(); - foreach (var mod in Beatmap.Value.Mods.Value.OfType()) + foreach (var mod in working.Mods.Value.OfType()) mod.ApplyToClock(adjustableSourceClock); decoupledClock.ChangeSource(adjustableSourceClock); @@ -202,7 +201,7 @@ namespace osu.Game.Screens.Play hudOverlay.Progress.AllowSeeking = HitRenderer.HasReplayLoaded; hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos); - hudOverlay.ModDisplay.Current.BindTo(Beatmap.Value.Mods); + hudOverlay.ModDisplay.Current.BindTo(working.Mods); //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation) HitRenderer.OnAllJudged += onCompletion; @@ -245,13 +244,17 @@ namespace osu.Game.Screens.Play } } - private void onFail() + private bool onFail() { + if (Beatmap.Value.Mods.Value.Any(m => !m.AllowFail)) + return false; + decoupledClock.Stop(); HasFailed = true; failOverlay.Retries = RestartCount; failOverlay.Show(); + return true; } protected override void OnEntering(Screen last) diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index b2d5abe71a..a67cf4e5ea 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -227,9 +227,9 @@ namespace osu.Game.Screens.Play Direction = FillDirection.Horizontal, Children = new[] { - new TextAwesome { Icon = FontAwesome.fa_chevron_right }, - new TextAwesome { Icon = FontAwesome.fa_chevron_right }, - new TextAwesome { Icon = FontAwesome.fa_chevron_right }, + new SpriteIcon { Icon = FontAwesome.fa_chevron_right }, + new SpriteIcon { Icon = FontAwesome.fa_chevron_right }, + new SpriteIcon { Icon = FontAwesome.fa_chevron_right }, } }, new OsuSpriteText diff --git a/osu.Game/Screens/Ranking/ResultModeButton.cs b/osu.Game/Screens/Ranking/ResultModeButton.cs index 50be5c8d00..d38611c45a 100644 --- a/osu.Game/Screens/Ranking/ResultModeButton.cs +++ b/osu.Game/Screens/Ranking/ResultModeButton.cs @@ -78,14 +78,14 @@ namespace osu.Game.Screens.Ranking RelativeSizeAxes = Axes.Both, Colour = Color4.Transparent, }, - new TextAwesome + new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, Shadow = false, Colour = OsuColour.Gray(0.95f), Icon = icon, - TextSize = 20, + Size = new Vector2(20), } } } diff --git a/osu.Game/Screens/ScreenWhiteBox.cs b/osu.Game/Screens/ScreenWhiteBox.cs index 5596f345d5..408dbd8f16 100644 --- a/osu.Game/Screens/ScreenWhiteBox.cs +++ b/osu.Game/Screens/ScreenWhiteBox.cs @@ -108,14 +108,14 @@ namespace osu.Game.Screens Anchor = Anchor.Centre, Origin = Anchor.Centre, Direction = FillDirection.Vertical, - Children = new[] + Children = new Drawable[] { - new TextAwesome + new SpriteIcon { Icon = FontAwesome.fa_universal_access, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - TextSize = 50, + Size = new Vector2(50), }, new OsuSpriteText { diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 743a0a0f63..264636b258 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -107,6 +107,14 @@ namespace osu.Game.Screens.Select }); } + public void RemoveBeatmap(BeatmapSetInfo beatmapSet) + { + Schedule(delegate + { + removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID)); + }); + } + public void SelectBeatmap(BeatmapInfo beatmap, bool animated = true) { if (beatmap == null) @@ -128,8 +136,6 @@ namespace osu.Game.Screens.Select } } - public void RemoveBeatmap(BeatmapSetInfo info) => removeGroup(groups.Find(b => b.BeatmapSet.ID == info.ID)); - public Action SelectionChanged; public Action StartRequested; @@ -181,12 +187,12 @@ namespace osu.Game.Screens.Select if (groups.Count == 0) return; - randomSelectedBeatmaps.Push(new KeyValuePair(selectedGroup, selectedGroup.SelectedPanel)); - var visibleGroups = getVisibleGroups(); if (!visibleGroups.Any()) return; + randomSelectedBeatmaps.Push(new KeyValuePair(selectedGroup, selectedGroup.SelectedPanel)); + BeatmapGroup group; if (randomType == SelectionRandomType.RandomPermutation) @@ -281,6 +287,12 @@ namespace osu.Game.Screens.Select perform(); } + public void ScrollToSelected(bool animated = true) + { + float selectedY = computeYPositions(animated); + ScrollTo(selectedY, animated); + } + private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet) { foreach (var b in beatmapSet.Beatmaps) @@ -420,8 +432,7 @@ namespace osu.Game.Screens.Select } finally { - float selectedY = computeYPositions(animated); - ScrollTo(selectedY, animated); + ScrollToSelected(animated); } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 385492980f..1fb9c707f0 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -247,21 +247,21 @@ namespace osu.Game.Screens.Select AutoSizeAxes = Axes.Both; Children = new Drawable[] { - new TextAwesome + new SpriteIcon { Icon = FontAwesome.fa_square, Origin = Anchor.Centre, Colour = new Color4(68, 17, 136, 255), Rotation = 45, - TextSize = 20 + Size = new Vector2(20), }, - new TextAwesome + new SpriteIcon { Icon = statistic.Icon, Origin = Anchor.Centre, Colour = new Color4(255, 221, 85, 255), Scale = new Vector2(0.8f), - TextSize = 20 + Size = new Vector2(20), }, new OsuSpriteText { diff --git a/osu.Game/Screens/Select/EditSongSelect.cs b/osu.Game/Screens/Select/EditSongSelect.cs index 1a9d37f069..907c080729 100644 --- a/osu.Game/Screens/Select/EditSongSelect.cs +++ b/osu.Game/Screens/Select/EditSongSelect.cs @@ -1,12 +1,14 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Input; + namespace osu.Game.Screens.Select { public class EditSongSelect : SongSelect { protected override bool ShowFooter => false; - protected override void OnSelected() => Exit(); + protected override void OnSelected(InputState state) => Exit(); } } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index c406e7c44d..838e6f7123 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -121,7 +121,7 @@ namespace osu.Game.Screens.Select //{ // Font = @"Exo2.0-Bold", // Text = "Sort results by", - // TextSize = 14, + // Size = 14, // Margin = new MarginPadding // { // Top = 5, diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs index 39c948f8d3..ac3b0b5c3b 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs @@ -334,25 +334,23 @@ namespace osu.Game.Screens.Select.Leaderboards Children = new[] { - new TextAwesome + new SpriteIcon { Origin = Anchor.Centre, Anchor = Anchor.Centre, Icon = FontAwesome.fa_osu_mod_bg, Colour = colour, Shadow = true, - TextSize = 30, - UseFullGlyphHeight = false, + Size = new Vector2(30), }, - new TextAwesome + new SpriteIcon { Origin = Anchor.Centre, Anchor = Anchor.Centre, Icon = icon, Colour = OsuColour.Gray(84), - TextSize = 18, + Size = new Vector2(18), Position = new Vector2(0f, 2f), - UseFullGlyphHeight = false, }, }; } @@ -369,7 +367,7 @@ namespace osu.Game.Screens.Select.Leaderboards Children = new Drawable[] { - new TextAwesome + new SpriteIcon { Origin = Anchor.Centre, Icon = FontAwesome.fa_square, @@ -377,7 +375,7 @@ namespace osu.Game.Screens.Select.Leaderboards Rotation = 45, Shadow = true, }, - new TextAwesome + new SpriteIcon { Origin = Anchor.Centre, Icon = icon, diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 282cd06126..2d3b198478 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -1,10 +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.Input; + namespace osu.Game.Screens.Select { public class MatchSongSelect : SongSelect { - protected override void OnSelected() => Exit(); + protected override void OnSelected(InputState state) => Exit(); } } diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs index ab02e8678f..306e7fb3dc 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Select.Options private readonly Box background; private readonly Box flash; - private readonly TextAwesome iconText; + private readonly SpriteIcon iconText; private readonly OsuSpriteText firstLine; private readonly OsuSpriteText secondLine; private readonly Container box; @@ -134,11 +134,11 @@ namespace osu.Game.Screens.Select.Options Direction = FillDirection.Vertical, Children = new Drawable[] { - iconText = new TextAwesome + iconText = new SpriteIcon { Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, - TextSize = 30, + Size = new Vector2(30), Shadow = true, Icon = FontAwesome.fa_close, Margin = new MarginPadding diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index e393caf931..662e1d55a2 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -1,10 +1,12 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -20,6 +22,7 @@ namespace osu.Game.Screens.Select private OsuScreen player; private readonly ModSelectOverlay modSelect; private readonly BeatmapDetailArea beatmapDetails; + private bool removeAutoModOnResume; public PlaySongSelect() { @@ -71,6 +74,13 @@ namespace osu.Game.Screens.Select { player = null; + if (removeAutoModOnResume) + { + var autoType = Ruleset.Value.CreateInstance().GetAutoplayMod().GetType(); + modSelect.SelectedMods.Value = modSelect.SelectedMods.Value.Where(m => m.GetType() != autoType).ToArray(); + removeAutoModOnResume = false; + } + Beatmap.Value.Track.Looping = true; base.OnResuming(last); @@ -100,10 +110,23 @@ namespace osu.Game.Screens.Select return false; } - protected override void OnSelected() + protected override void OnSelected(InputState state) { if (player != null) return; + if (state?.Keyboard.ControlPressed == true) + { + var auto = Ruleset.Value.CreateInstance().GetAutoplayMod(); + var autoType = auto.GetType(); + + var mods = modSelect.SelectedMods.Value; + if (mods.All(m => m.GetType() != autoType)) + { + modSelect.SelectedMods.Value = mods.Concat(new[] { auto }); + removeAutoModOnResume = true; + } + } + Beatmap.Value.Track.Looping = false; Beatmap.Disabled = true; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index bbd292870e..8f545240c8 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; @@ -19,7 +18,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays; -using osu.Game.Rulesets; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Select.Options; @@ -27,7 +25,6 @@ namespace osu.Game.Screens.Select { public abstract class SongSelect : OsuScreen { - private readonly Bindable ruleset = new Bindable(); private BeatmapManager manager; protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(); @@ -109,7 +106,7 @@ namespace osu.Game.Screens.Select Origin = Anchor.CentreRight, SelectionChanged = carouselSelectionChanged, BeatmapsChanged = carouselBeatmapsLoaded, - StartRequested = carouselRaisedStart, + StartRequested = () => carouselRaisedStart(), }); Add(FilterControl = new FilterControl { @@ -129,6 +126,11 @@ namespace osu.Game.Screens.Select Right = left_area_padding, }, }); + Add(new ResetScrollContainer(() => carousel.ScrollToSelected()) + { + RelativeSizeAxes = Axes.Y, + Width = 250, + }); if (ShowFooter) { @@ -146,7 +148,7 @@ namespace osu.Game.Screens.Select Add(Footer = new Footer { OnBack = Exit, - OnStart = carouselRaisedStart, + OnStart = () => carouselRaisedStart(), }); FooterPanels.Add(BeatmapOptions = new BeatmapOptionsOverlay()); @@ -168,7 +170,7 @@ namespace osu.Game.Screens.Select manager = beatmaps; if (osu != null) - ruleset.BindTo(osu.Ruleset); + Ruleset.BindTo(osu.Ruleset); manager.BeatmapSetAdded += onBeatmapSetAdded; manager.BeatmapSetRemoved += onBeatmapSetRemoved; @@ -196,7 +198,7 @@ namespace osu.Game.Screens.Select carousel.SelectNext(); } - private void carouselRaisedStart() + private void carouselRaisedStart(InputState state = null) { // if we have a pending filter operation, we want to run it now. // it could change selection (ie. if the ruleset has been changed). @@ -209,7 +211,7 @@ namespace osu.Game.Screens.Select selectionChangedDebounce = null; } - OnSelected(); + OnSelected(state); } private ScheduledDelegate selectionChangedDebounce; @@ -251,7 +253,7 @@ namespace osu.Game.Screens.Select } else { - ruleset.Value = beatmap.Ruleset; + Ruleset.Value = beatmap.Ruleset; if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID) sampleChangeDifficulty.Play(); @@ -273,14 +275,14 @@ namespace osu.Game.Screens.Select carousel.SelectNextRandom(); } - protected abstract void OnSelected(); + protected abstract void OnSelected(InputState state); private void filterChanged(FilterCriteria criteria, bool debounce = true) { carousel.Filter(criteria, debounce); } - private void onBeatmapSetAdded(BeatmapSetInfo s) => carousel.AddBeatmap(s); + private void onBeatmapSetAdded(BeatmapSetInfo s) => Schedule(() => addBeatmapSet(s)); private void onBeatmapSetRemoved(BeatmapSetInfo s) => Schedule(() => removeBeatmapSet(s)); @@ -375,6 +377,11 @@ namespace osu.Game.Screens.Select } } + private void addBeatmapSet(BeatmapSetInfo beatmapSet) + { + carousel.AddBeatmap(beatmapSet); + } + private void removeBeatmapSet(BeatmapSetInfo beatmapSet) { carousel.RemoveBeatmap(beatmapSet); @@ -396,7 +403,7 @@ namespace osu.Game.Screens.Select { case Key.KeypadEnter: case Key.Enter: - carouselRaisedStart(); + carouselRaisedStart(state); return true; case Key.Delete: if (state.Keyboard.ShiftPressed) @@ -409,5 +416,21 @@ namespace osu.Game.Screens.Select return base.OnKeyDown(state, args); } + + private class ResetScrollContainer : Container + { + private readonly Action onHoverAction; + + public ResetScrollContainer(Action onHoverAction) + { + this.onHoverAction = onHoverAction; + } + + protected override bool OnHover(InputState state) + { + onHoverAction?.Invoke(); + return base.OnHover(state); + } + } } } diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index cd9ca582fc..89bd4b68d2 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -142,15 +142,15 @@ namespace osu.Game.Users Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, Spacing = new Vector2(5f, 0f), - Children = new[] + Children = new Drawable[] { - new TextAwesome + new SpriteIcon { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Icon = FontAwesome.fa_circle_o, Shadow = true, - TextSize = 14, + Size = new Vector2(14), }, statusMessage = new OsuSpriteText { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8cbaee3072..69460e378d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -106,6 +106,7 @@ + @@ -117,6 +118,8 @@ + + @@ -252,7 +255,7 @@ - + @@ -350,7 +353,7 @@ - + diff --git a/osu.sln b/osu.sln index 317cfe7da4..3799c890b9 100644 --- a/osu.sln +++ b/osu.sln @@ -17,8 +17,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Framework", "Framework", "{ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.Desktop", "osu-framework\osu.Framework.Desktop\osu.Framework.Desktop.csproj", "{65DC628F-A640-4111-AB35-3A5652BC1E17}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Desktop.VisualTests", "osu.Desktop.VisualTests\osu.Desktop.VisualTests.csproj", "{69051C69-12AE-4E7D-A3E6-460D2E282312}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tests", "osu.Game.Tests\osu.Game.Tests.csproj", "{54377672-20B1-40AF-8087-5CF73BF3953A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{C92A607B-1FDD-4954-9F92-03FF547D9080}" @@ -43,62 +41,84 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU + VisualTests|Any CPU = VisualTests|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.Build.0 = Release|Any CPU + {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.VisualTests|Any CPU.ActiveCfg = VisualTests|Any CPU + {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.VisualTests|Any CPU.Build.0 = VisualTests|Any CPU {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.ActiveCfg = Release|Any CPU {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.Build.0 = Release|Any CPU + {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}.Debug|Any CPU.Build.0 = Debug|Any CPU {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}.Release|Any CPU.ActiveCfg = Release|Any CPU {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}.Release|Any CPU.Build.0 = Release|Any CPU + {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.Build.0 = Debug|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Release|Any CPU.ActiveCfg = Release|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Release|Any CPU.Build.0 = Release|Any CPU + {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {65DC628F-A640-4111-AB35-3A5652BC1E17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {65DC628F-A640-4111-AB35-3A5652BC1E17}.Debug|Any CPU.Build.0 = Debug|Any CPU {65DC628F-A640-4111-AB35-3A5652BC1E17}.Release|Any CPU.ActiveCfg = Release|Any CPU {65DC628F-A640-4111-AB35-3A5652BC1E17}.Release|Any CPU.Build.0 = Release|Any CPU - {69051C69-12AE-4E7D-A3E6-460D2E282312}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {69051C69-12AE-4E7D-A3E6-460D2E282312}.Debug|Any CPU.Build.0 = Debug|Any CPU - {69051C69-12AE-4E7D-A3E6-460D2E282312}.Release|Any CPU.ActiveCfg = Release|Any CPU - {69051C69-12AE-4E7D-A3E6-460D2E282312}.Release|Any CPU.Build.0 = Release|Any CPU + {65DC628F-A640-4111-AB35-3A5652BC1E17}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {65DC628F-A640-4111-AB35-3A5652BC1E17}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.Build.0 = Debug|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Release|Any CPU.ActiveCfg = Release|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Release|Any CPU.Build.0 = Release|Any CPU + {54377672-20B1-40AF-8087-5CF73BF3953A}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {54377672-20B1-40AF-8087-5CF73BF3953A}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Debug|Any CPU.Build.0 = Debug|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Release|Any CPU.ActiveCfg = Release|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Release|Any CPU.Build.0 = Release|Any CPU + {C92A607B-1FDD-4954-9F92-03FF547D9080}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {C92A607B-1FDD-4954-9F92-03FF547D9080}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Release|Any CPU.ActiveCfg = Release|Any CPU {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Release|Any CPU.Build.0 = Release|Any CPU + {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {F167E17A-7DE6-4AF5-B920-A5112296C695}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F167E17A-7DE6-4AF5-B920-A5112296C695}.Debug|Any CPU.Build.0 = Debug|Any CPU {F167E17A-7DE6-4AF5-B920-A5112296C695}.Release|Any CPU.ActiveCfg = Release|Any CPU {F167E17A-7DE6-4AF5-B920-A5112296C695}.Release|Any CPU.Build.0 = Release|Any CPU + {F167E17A-7DE6-4AF5-B920-A5112296C695}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {F167E17A-7DE6-4AF5-B920-A5112296C695}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.Build.0 = Debug|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.ActiveCfg = Release|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.Build.0 = Release|Any CPU + {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}.Release|Any CPU.Build.0 = Release|Any CPU + {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU {007B2356-AB6F-4BD9-96D5-116FC2DCE69A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {007B2356-AB6F-4BD9-96D5-116FC2DCE69A}.Debug|Any CPU.Build.0 = Debug|Any CPU {007B2356-AB6F-4BD9-96D5-116FC2DCE69A}.Release|Any CPU.ActiveCfg = Release|Any CPU {007B2356-AB6F-4BD9-96D5-116FC2DCE69A}.Release|Any CPU.Build.0 = Release|Any CPU + {007B2356-AB6F-4BD9-96D5-116FC2DCE69A}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {007B2356-AB6F-4BD9-96D5-116FC2DCE69A}.VisualTests|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -109,7 +129,6 @@ Global {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} {65DC628F-A640-4111-AB35-3A5652BC1E17} = {7A75DFA2-DE65-4458-98AF-26AF96FFD6DC} - {69051C69-12AE-4E7D-A3E6-460D2E282312} = {80683232-505E-41EA-A37C-2CFCF1C88C57} {54377672-20B1-40AF-8087-5CF73BF3953A} = {80683232-505E-41EA-A37C-2CFCF1C88C57} {C92A607B-1FDD-4954-9F92-03FF547D9080} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} {58F6C80C-1253-4A0E-A465-B8C85EBEADF3} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index e3eae96ca8..06d160ad31 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -115,7 +115,7 @@ WARNING WARNING WARNING - WARNING + HINT WARNING WARNING WARNING