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 f285ebde67..3db43ca9bb 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -2,41 +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"
+ "/property:DebugType=portable",
+ "/verbosity:minimal",
+ "/m" //parallel compiling support.
],
- "windows": {
- "args": [
- "/property:GenerateFullPaths=true",
- "/property:DebugType=portable",
- "/m" //parallel compiling support. doesn't work well with mono atm
- ]
- },
- "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"
]
},
{
@@ -44,6 +44,19 @@
"args": [
"/target:Clean",
"/property:Configuration=Release"
+ ],
+ "problemMatcher": [
+ "$msCompile"
+ ]
+ },
+ {
+ "taskName": "Clean All",
+ "dependsOn": [
+ "Clean (Debug)",
+ "Clean (Release)"
+ ],
+ "problemMatcher": [
+ "$msCompile"
]
}
]
diff --git a/appveyor.yml b/appveyor.yml
index cc6dfb9c88..b26a895788 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -20,4 +20,4 @@ build:
verbosity: minimal
after_build:
- cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln
- - cmd: NVika parsereport "inspectcodereport.xml"
\ No newline at end of file
+ - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
\ No newline at end of file
diff --git a/osu-framework b/osu-framework
index c76d8b811b..ba70b8eaa9 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit c76d8b811b93d0c0862f342ebbab70a461006e13
+Subproject commit ba70b8eaa9b79d4248873d4399f3b9e918fc3c8f
diff --git a/osu-resources b/osu-resources
index 9f46a456dc..f6042e1cb3 160000
--- a/osu-resources
+++ b/osu-resources
@@ -1 +1 @@
-Subproject commit 9f46a456dc3a56dcbff09671a3f588b16a464106
+Subproject commit f6042e1cb37cfad6c879d0e1245f7880c7fcd5f5
diff --git a/osu.Desktop.Deploy/App.config b/osu.Desktop.Deploy/App.config
index 45685a74a8..6711f9c54e 100644
--- a/osu.Desktop.Deploy/App.config
+++ b/osu.Desktop.Deploy/App.config
@@ -31,6 +31,10 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
+
+
+
+
\ No newline at end of file
diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs
index 37776b329f..644987ce5a 100644
--- a/osu.Desktop.Deploy/Program.cs
+++ b/osu.Desktop.Deploy/Program.cs
@@ -17,8 +17,8 @@ namespace osu.Desktop.Deploy
{
internal static class Program
{
- private const string nuget_path = @"packages\NuGet.CommandLine.3.5.0\tools\NuGet.exe";
- private const string squirrel_path = @"packages\squirrel.windows.1.5.2\tools\Squirrel.exe";
+ private const string nuget_path = @"packages\NuGet.CommandLine.4.1.0\tools\NuGet.exe";
+ private const string squirrel_path = @"packages\squirrel.windows.1.7.5\tools\Squirrel.exe";
private const string msbuild_path = @"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe";
public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"];
diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
index 1f9726b573..c6474eae5a 100644
--- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
+++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
@@ -49,10 +49,6 @@
$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll
True
-
- $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\ICSharpCode.SharpZipLib.dll
- True
-
$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll
True
@@ -73,15 +69,19 @@
$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
- $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll
+ $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll
+ True
+
+
+ $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll
True
$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll
True
-
- $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\Squirrel.dll
+
+ $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll
True
diff --git a/osu.Desktop.Deploy/packages.config b/osu.Desktop.Deploy/packages.config
index 4878297be9..3c5ca9f9a3 100644
--- a/osu.Desktop.Deploy/packages.config
+++ b/osu.Desktop.Deploy/packages.config
@@ -7,7 +7,8 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-
+
+
-
+
\ No newline at end of file
diff --git a/osu.Desktop.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 92%
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.Tests/Visual/TestCaseBeatSyncedContainer.cs b/osu.Desktop.Tests/Visual/TestCaseBeatSyncedContainer.cs
new file mode 100644
index 0000000000..130a034133
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseBeatSyncedContainer.cs
@@ -0,0 +1,205 @@
+// 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.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.Tests.Visual
+{
+ internal class TestCaseBeatSyncedContainer : OsuTestCase
+ {
+ public override string Description => @"Tests beat synced containers.";
+
+ private readonly MusicController mc;
+
+ public TestCaseBeatSyncedContainer()
+ {
+ Clock = new FramedClock();
+ Clock.ProcessFrame();
+
+ Add(new BeatContainer
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ });
+
+ Add(mc = new MusicController
+ {
+ Origin = Anchor.TopRight,
+ Anchor = Anchor.TopRight,
+ });
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ mc.ToggleVisibility();
+ }
+
+ private class BeatContainer : BeatSyncedContainer
+ {
+ private const int flash_layer_heigth = 150;
+
+ private readonly InfoString timingPointCount;
+ private readonly InfoString currentTimingPoint;
+ private readonly InfoString beatCount;
+ private readonly InfoString currentBeat;
+ private readonly InfoString beatsPerMinute;
+ private readonly InfoString adjustedBeatLength;
+ private readonly InfoString timeUntilNextBeat;
+ private readonly InfoString timeSinceLastBeat;
+
+ private readonly Box flashLayer;
+
+ public BeatContainer()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Name = @"Info Layer",
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ AutoSizeAxes = Axes.Both,
+ Margin = new MarginPadding { Bottom = flash_layer_heigth },
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black.Opacity(150),
+ },
+ new FillFlowContainer
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ timingPointCount = new InfoString(@"Timing points amount"),
+ currentTimingPoint = new InfoString(@"Current timing point"),
+ beatCount = new InfoString(@"Beats amount (in the current timing point)"),
+ currentBeat = new InfoString(@"Current beat"),
+ beatsPerMinute = new InfoString(@"BPM"),
+ adjustedBeatLength = new InfoString(@"Adjusted beat length"),
+ timeUntilNextBeat = new InfoString(@"Time until next beat"),
+ timeSinceLastBeat = new InfoString(@"Time since last beat"),
+ }
+ }
+ }
+ },
+ new Container
+ {
+ Name = @"Color indicator",
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ RelativeSizeAxes = Axes.X,
+ Height = flash_layer_heigth,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ flashLayer = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.White,
+ Alpha = 0,
+ }
+ }
+ }
+ };
+
+ Beatmap.ValueChanged += delegate
+ {
+ timingPointCount.Value = 0;
+ currentTimingPoint.Value = 0;
+ beatCount.Value = 0;
+ currentBeat.Value = 0;
+ beatsPerMinute.Value = 0;
+ adjustedBeatLength.Value = 0;
+ timeUntilNextBeat.Value = 0;
+ timeSinceLastBeat.Value = 0;
+ };
+ }
+
+ private SortedList timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints;
+ private TimingControlPoint getNextTimingPoint(TimingControlPoint current)
+ {
+ if (timingPoints[timingPoints.Count - 1] == current)
+ return current;
+
+ return timingPoints[timingPoints.IndexOf(current) + 1];
+ }
+
+ private int calculateBeatCount(TimingControlPoint current)
+ {
+ if (timingPoints.Count == 0) return 0;
+
+ if (timingPoints[timingPoints.Count - 1] == current)
+ return (int)Math.Ceiling((Beatmap.Value.Track.Length - current.Time) / current.BeatLength);
+
+ return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+ timeUntilNextBeat.Value = TimeUntilNextBeat;
+ timeSinceLastBeat.Value = TimeSinceLastBeat;
+ }
+
+ protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
+ {
+ base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
+
+ timingPointCount.Value = timingPoints.Count;
+ currentTimingPoint.Value = timingPoints.IndexOf(timingPoint);
+ beatCount.Value = calculateBeatCount(timingPoint);
+ currentBeat.Value = beatIndex;
+ beatsPerMinute.Value = 60000 / timingPoint.BeatLength;
+ adjustedBeatLength.Value = timingPoint.BeatLength;
+
+ flashLayer.FadeOutFromOne(timingPoint.BeatLength);
+ }
+ }
+
+ private class InfoString : FillFlowContainer
+ {
+ private const int text_size = 20;
+ private const int margin = 7;
+
+ private readonly OsuSpriteText valueText;
+
+ public double Value
+ {
+ set { valueText.Text = $"{value:G}"; }
+ }
+
+ public InfoString(string header)
+ {
+ AutoSizeAxes = Axes.Both;
+ Direction = FillDirection.Horizontal;
+ Add(new OsuSpriteText { Text = header + @": ", TextSize = text_size });
+ Add(valueText = new OsuSpriteText { TextSize = text_size });
+ Margin = new MarginPadding(margin);
+ }
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs b/osu.Desktop.Tests/Visual/TestCaseBeatmapDetailArea.cs
similarity index 71%
rename from osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs
rename to osu.Desktop.Tests/Visual/TestCaseBeatmapDetailArea.cs
index e755924a15..23b4ece4ec 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseBeatmapDetailArea.cs
@@ -1,21 +1,20 @@
// 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";
- public override void Reset()
+ public TestCaseBeatmapDetailArea()
{
- base.Reset();
-
Add(new BeatmapDetailArea
{
Anchor = Anchor.Centre,
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs b/osu.Desktop.Tests/Visual/TestCaseBeatmapDetails.cs
similarity index 84%
rename from osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs
rename to osu.Desktop.Tests/Visual/TestCaseBeatmapDetails.cs
index 58cbad936a..11a15cf56f 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseBeatmapDetails.cs
@@ -1,24 +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.Graphics;
-using osu.Framework.Testing;
-using osu.Game.Database;
-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";
- private BeatmapDetails details;
+ private readonly BeatmapDetails details;
- public override void Reset()
+ public TestCaseBeatmapDetails()
{
- base.Reset();
-
Add(details = new BeatmapDetails
{
RelativeSizeAxes = Axes.Both,
@@ -41,7 +38,7 @@ namespace osu.Desktop.VisualTests.Tests
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
- Ratings = Enumerable.Range(0,10),
+ Ratings = Enumerable.Range(0, 10),
Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6),
},
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs b/osu.Desktop.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs
similarity index 81%
rename from osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs
rename to osu.Desktop.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs
index 7c211227c6..3265f8ec76 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs
@@ -1,22 +1,19 @@
// 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";
- public override void Reset()
+ public TestCaseBeatmapOptionsOverlay()
{
- base.Reset();
-
var overlay = new BeatmapOptionsOverlay();
overlay.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, Color4.Purple, null, Key.Number1);
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBreadcrumbs.cs b/osu.Desktop.Tests/Visual/TestCaseBreadcrumbs.cs
similarity index 81%
rename from osu.Desktop.VisualTests/Tests/TestCaseBreadcrumbs.cs
rename to osu.Desktop.Tests/Visual/TestCaseBreadcrumbs.cs
index 658d2f92b1..10e0c784ab 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseBreadcrumbs.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseBreadcrumbs.cs
@@ -1,20 +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.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";
- public override void Reset()
+ public TestCaseBreadcrumbs()
{
- base.Reset();
-
BreadcrumbControl c;
Add(c = new BreadcrumbControl
{
diff --git a/osu.Desktop.Tests/Visual/TestCaseCatcher.cs b/osu.Desktop.Tests/Visual/TestCaseCatcher.cs
new file mode 100644
index 0000000000..3f57b5eeb9
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseCatcher.cs
@@ -0,0 +1,35 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Catch;
+using osu.Game.Rulesets.Catch.UI;
+using OpenTK;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseCatcher : OsuTestCase
+ {
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ Children = new Drawable[]
+ {
+ new CatchInputManager(rulesets.GetRuleset(2))
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new CatcherArea
+ {
+ RelativePositionAxes = Axes.Both,
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Size = new Vector2(1, 0.2f),
+ }
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs b/osu.Desktop.Tests/Visual/TestCaseChatDisplay.cs
similarity index 68%
rename from osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs
rename to osu.Desktop.Tests/Visual/TestCaseChatDisplay.cs
index 2663c952cf..ae051a1e40 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseChatDisplay.cs
@@ -1,20 +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.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";
- public override void Reset()
+ public TestCaseChatDisplay()
{
- base.Reset();
-
Add(new ChatOverlay
{
State = Visibility.Visible
diff --git a/osu.Desktop.Tests/Visual/TestCaseContextMenu.cs b/osu.Desktop.Tests/Visual/TestCaseContextMenu.cs
new file mode 100644
index 0000000000..f0894f794a
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseContextMenu.cs
@@ -0,0 +1,97 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Graphics.UserInterface;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseContextMenu : OsuTestCase
+ {
+ public override string Description => @"Menu visible on right click";
+
+ private const int start_time = 0;
+ private const int duration = 1000;
+
+ private readonly Container container;
+
+ public TestCaseContextMenu()
+ {
+ Add(container = new MyContextMenuContainer
+ {
+ Size = new Vector2(200),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Green,
+ }
+ }
+ });
+
+ Add(new AnotherContextMenuContainer
+ {
+ Size = new Vector2(200),
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Red,
+ }
+ }
+ });
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // Move box along a square trajectory
+ container.Loop(c => c
+ .MoveTo(new Vector2(0, 100), duration).Then()
+ .MoveTo(new Vector2(100, 100), duration).Then()
+ .MoveTo(new Vector2(100, 0), duration).Then()
+ .MoveTo(Vector2.Zero, duration)
+ );
+ }
+
+ private class MyContextMenuContainer : Container, IHasContextMenu
+ {
+ public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
+ {
+ new OsuContextMenuItem(@"Some option"),
+ new OsuContextMenuItem(@"Highlighted option", MenuItemType.Highlighted),
+ new OsuContextMenuItem(@"Another option"),
+ new OsuContextMenuItem(@"Choose me please"),
+ new OsuContextMenuItem(@"And me too"),
+ new OsuContextMenuItem(@"Trying to fill"),
+ new OsuContextMenuItem(@"Destructive option", MenuItemType.Destructive),
+ };
+ }
+
+ private class AnotherContextMenuContainer : Container, IHasContextMenu
+ {
+ public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
+ {
+ new OsuContextMenuItem(@"Simple option"),
+ new OsuContextMenuItem(@"Simple very very long option"),
+ new OsuContextMenuItem(@"Change width", MenuItemType.Highlighted) { Action = () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint) },
+ new OsuContextMenuItem(@"Change height", MenuItemType.Highlighted) { Action = () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint) },
+ new OsuContextMenuItem(@"Change width back", MenuItemType.Destructive) { Action = () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint) },
+ new OsuContextMenuItem(@"Change height back", MenuItemType.Destructive) { Action = () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint) },
+ };
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs b/osu.Desktop.Tests/Visual/TestCaseDialogOverlay.cs
similarity index 88%
rename from osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs
rename to osu.Desktop.Tests/Visual/TestCaseDialogOverlay.cs
index 90e214c3c9..df15350453 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseDialogOverlay.cs
@@ -1,22 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Framework.Testing;
using osu.Game.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";
- private DialogOverlay overlay;
-
- public override void Reset()
+ public TestCaseDialogOverlay()
{
- base.Reset();
+ DialogOverlay overlay;
Add(overlay = new DialogOverlay());
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs b/osu.Desktop.Tests/Visual/TestCaseDirect.cs
similarity index 66%
rename from osu.Desktop.VisualTests/Tests/TestCaseDirect.cs
rename to osu.Desktop.Tests/Visual/TestCaseDirect.cs
index 4cda14559f..b78ea02767 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseDirect.cs
@@ -3,22 +3,22 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
-using osu.Framework.Testing;
-using osu.Game.Database;
+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";
private DirectOverlay direct;
- private RulesetDatabase rulesets;
+ private RulesetStore rulesets;
- public override void Reset()
+ protected override void LoadComplete()
{
- base.Reset();
+ base.LoadComplete();
Add(direct = new DirectOverlay());
newBeatmaps();
@@ -28,7 +28,7 @@ namespace osu.Desktop.VisualTests.Tests
}
[BackgroundDependencyLoader]
- private void load(RulesetDatabase rulesets)
+ private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
@@ -48,6 +48,17 @@ namespace osu.Desktop.VisualTests.Tests
Author = @"RLC",
Source = @"",
},
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Card = @"https://assets.ppy.sh/beatmaps/578332/covers/card.jpg?1494591390",
+ Cover = @"https://assets.ppy.sh/beatmaps/578332/covers/cover.jpg?1494591390",
+ },
+ Preview = @"https://b.ppy.sh/preview/578332.mp3",
+ PlayCount = 97,
+ FavouriteCount = 72,
+ },
Beatmaps = new List
{
new BeatmapInfo
@@ -55,13 +66,6 @@ namespace osu.Desktop.VisualTests.Tests
Ruleset = ruleset,
StarDifficulty = 5.35f,
Metadata = new BeatmapMetadata(),
- OnlineInfo = new BeatmapOnlineInfo
- {
- Covers = new[] { @"https://assets.ppy.sh//beatmaps/578332/covers/cover.jpg?1494591390" },
- Preview = @"https://b.ppy.sh/preview/578332.mp3",
- PlayCount = 97,
- FavouriteCount = 72,
- },
},
},
},
@@ -74,6 +78,17 @@ namespace osu.Desktop.VisualTests.Tests
Author = @"Sotarks",
Source = @"ぎんぎつね",
},
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Card = @"https://assets.ppy.sh/beatmaps/599627/covers/card.jpg?1494539318",
+ Cover = @"https://assets.ppy.sh/beatmaps/599627/covers/cover.jpg?1494539318",
+ },
+ Preview = @"https//b.ppy.sh/preview/599627.mp3",
+ PlayCount = 3082,
+ FavouriteCount = 14,
+ },
Beatmaps = new List
{
new BeatmapInfo
@@ -81,13 +96,6 @@ namespace osu.Desktop.VisualTests.Tests
Ruleset = ruleset,
StarDifficulty = 5.81f,
Metadata = new BeatmapMetadata(),
- OnlineInfo = new BeatmapOnlineInfo
- {
- Covers = new[] { @"https://assets.ppy.sh//beatmaps/599627/covers/cover.jpg?1494539318" },
- Preview = @"https//b.ppy.sh/preview/599627.mp3",
- PlayCount = 3082,
- FavouriteCount = 14,
- },
},
},
},
@@ -100,6 +108,17 @@ namespace osu.Desktop.VisualTests.Tests
Author = @"Cerulean Veyron",
Source = @"",
},
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Card = @"https://assets.ppy.sh/beatmaps/513268/covers/card.jpg?1494502863",
+ Cover = @"https://assets.ppy.sh/beatmaps/513268/covers/cover.jpg?1494502863",
+ },
+ Preview = @"https//b.ppy.sh/preview/513268.mp3",
+ PlayCount = 2762,
+ FavouriteCount = 15,
+ },
Beatmaps = new List
{
new BeatmapInfo
@@ -107,13 +126,6 @@ namespace osu.Desktop.VisualTests.Tests
Ruleset = ruleset,
StarDifficulty = 0.9f,
Metadata = new BeatmapMetadata(),
- OnlineInfo = new BeatmapOnlineInfo
- {
- Covers = new[] { @"https://assets.ppy.sh//beatmaps/513268/covers/cover.jpg?1494502863" },
- Preview = @"https//b.ppy.sh/preview/513268.mp3",
- PlayCount = 2762,
- FavouriteCount = 15,
- },
},
new BeatmapInfo
{
@@ -141,6 +153,17 @@ namespace osu.Desktop.VisualTests.Tests
Author = @"[Kamiya]",
Source = @"小林さんちのメイドラゴン",
},
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Card = @"https://assets.ppy.sh/beatmaps/586841/covers/card.jpg?1494052741",
+ Cover = @"https://assets.ppy.sh/beatmaps/586841/covers/cover.jpg?1494052741",
+ },
+ Preview = @"https//b.ppy.sh/preview/586841.mp3",
+ PlayCount = 62317,
+ FavouriteCount = 161,
+ },
Beatmaps = new List
{
new BeatmapInfo
@@ -148,13 +171,6 @@ namespace osu.Desktop.VisualTests.Tests
Ruleset = ruleset,
StarDifficulty = 1.26f,
Metadata = new BeatmapMetadata(),
- OnlineInfo = new BeatmapOnlineInfo
- {
- Covers = new[] { @"https://assets.ppy.sh//beatmaps/586841/covers/cover.jpg?1494052741" },
- Preview = @"https//b.ppy.sh/preview/586841.mp3",
- PlayCount = 62317,
- FavouriteCount = 161,
- },
},
new BeatmapInfo
{
diff --git a/osu.Desktop.Tests/Visual/TestCaseDrawableRoom.cs b/osu.Desktop.Tests/Visual/TestCaseDrawableRoom.cs
new file mode 100644
index 0000000000..04d551afe4
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseDrawableRoom.cs
@@ -0,0 +1,132 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using 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.Tests.Visual
+{
+ internal class TestCaseDrawableRoom : OsuTestCase
+ {
+ public override string Description => @"Select your favourite room";
+
+ private RulesetStore rulesets;
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ DrawableRoom first;
+ Add(new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Y,
+ Width = 580f,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ first = new DrawableRoom(new Room
+ {
+ Name = { Value = @"Great Room Right Here" },
+ Host = { Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" } } },
+ Status = { Value = new RoomStatusOpen() },
+ Type = { Value = new GameTypeTeamVersus() },
+ Beatmap =
+ {
+ Value = new BeatmapInfo
+ {
+ StarDifficulty = 4.65,
+ Ruleset = rulesets.GetRuleset(3),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"Critical Crystal",
+ Artist = @"Seiryu",
+ },
+ BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh//beatmaps/376340/covers/cover.jpg?1456478455",
+ },
+ },
+ },
+ },
+ },
+ Participants =
+ {
+ Value = new[]
+ {
+ new User { GlobalRank = 1355 },
+ new User { GlobalRank = 8756 },
+ },
+ },
+ }),
+ new DrawableRoom(new Room
+ {
+ Name = { Value = @"Relax It's The Weekend" },
+ Host = { Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" } } },
+ Status = { Value = new RoomStatusPlaying() },
+ Type = { Value = new GameTypeTagTeam() },
+ Beatmap =
+ {
+ Value = new BeatmapInfo
+ {
+ StarDifficulty = 1.96,
+ Ruleset = rulesets.GetRuleset(0),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"Serendipity",
+ Artist = @"ZAQ",
+ },
+ BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh//beatmaps/526839/covers/cover.jpg?1493815706",
+ },
+ },
+ },
+ },
+ },
+ Participants =
+ {
+ Value = new[]
+ {
+ new User { GlobalRank = 578975 },
+ new User { GlobalRank = 24554 },
+ },
+ },
+ }),
+ }
+ });
+
+ AddStep(@"change title", () => first.Room.Name.Value = @"I Changed Name");
+ AddStep(@"change host", () => first.Room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } });
+ AddStep(@"change status", () => first.Room.Status.Value = new RoomStatusPlaying());
+ AddStep(@"change type", () => first.Room.Type.Value = new GameTypeVersus());
+ AddStep(@"change beatmap", () => first.Room.Beatmap.Value = null);
+ AddStep(@"change participants", () => first.Room.Participants.Value = new[]
+ {
+ new User { GlobalRank = 1254 },
+ new User { GlobalRank = 123189 },
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ this.rulesets = rulesets;
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs b/osu.Desktop.Tests/Visual/TestCaseDrawings.cs
similarity index 89%
rename from osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs
rename to osu.Desktop.Tests/Visual/TestCaseDrawings.cs
index ebc9930f93..81fd8cad03 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseDrawings.cs
@@ -2,20 +2,17 @@
// 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";
- public override void Reset()
+ public TestCaseDrawings()
{
- base.Reset();
-
Add(new Drawings
{
TeamList = new TestTeamList(),
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs b/osu.Desktop.Tests/Visual/TestCaseGamefield.cs
similarity index 77%
rename from osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs
rename to osu.Desktop.Tests/Visual/TestCaseGamefield.cs
index e2cd2bf67b..73c61f5669 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseGamefield.cs
@@ -1,42 +1,45 @@
// 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.Database;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
+using osu.Game.Rulesets.Taiko;
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 OpenTK;
-namespace osu.Desktop.VisualTests.Tests
+namespace osu.Desktop.Tests.Visual
{
- internal class TestCaseGamefield : TestCase
+ internal class TestCaseGamefield : OsuTestCase
{
- private RulesetDatabase rulesets;
+ private RulesetStore rulesets;
public override string Description => @"Showing hitobjects and what not.";
[BackgroundDependencyLoader]
- private void load(RulesetDatabase rulesets)
+ private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
- public override void Reset()
+ protected override void LoadComplete()
{
- base.Reset();
+ base.LoadComplete();
List objects = new List();
@@ -76,7 +79,7 @@ namespace osu.Desktop.VisualTests.Tests
ControlPointInfo = controlPointInfo
});
- Add(new Drawable[]
+ AddRange(new Drawable[]
{
new Container
{
@@ -85,25 +88,25 @@ namespace osu.Desktop.VisualTests.Tests
Clock = new FramedClock(),
Children = new Drawable[]
{
- new OsuHitRenderer(beatmap, false)
+ new OsuRulesetContainer(new OsuRuleset(new RulesetInfo()), beatmap, false)
{
Scale = new Vector2(0.5f),
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft
},
- new TaikoHitRenderer(beatmap, false)
+ new TaikoRulesetContainer(new TaikoRuleset(new RulesetInfo()),beatmap, false)
{
Scale = new Vector2(0.5f),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight
},
- new CatchHitRenderer(beatmap, false)
+ new CatchRulesetContainer(new CatchRuleset(new RulesetInfo()),beatmap, false)
{
Scale = new Vector2(0.5f),
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft
},
- new ManiaHitRenderer(beatmap, false)
+ new ManiaRulesetContainer(new ManiaRuleset(new RulesetInfo()),beatmap, false)
{
Scale = new Vector2(0.5f),
Anchor = Anchor.BottomRight,
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGraph.cs b/osu.Desktop.Tests/Visual/TestCaseGraph.cs
similarity index 81%
rename from osu.Desktop.VisualTests/Tests/TestCaseGraph.cs
rename to osu.Desktop.Tests/Visual/TestCaseGraph.cs
index 7ac795f6f9..13342d1ff1 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseGraph.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseGraph.cs
@@ -1,23 +1,20 @@
// 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";
- private BarGraph graph;
-
- public override void Reset()
+ public TestCaseGraph()
{
- base.Reset();
+ BarGraph graph;
Children = new[]
{
@@ -30,7 +27,7 @@ namespace osu.Desktop.VisualTests.Tests
},
};
- AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1,10).Select(i => (float)i));
+ AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i));
AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i));
AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i));
AddStep("Bottom to top", () => graph.Direction = BarDirection.BottomToTop);
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.Tests/Visual/TestCaseHitObjects.cs
similarity index 64%
rename from osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs
rename to osu.Desktop.Tests/Visual/TestCaseHitObjects.cs
index 8c913ae95e..13e05d6477 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseHitObjects.cs
@@ -1,43 +1,62 @@
// 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 System.Collections.Generic;
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 osu.Game.Rulesets.Osu;
+using osu.Framework.Allocation;
+using osu.Game.Rulesets;
-namespace osu.Desktop.VisualTests.Tests
+namespace osu.Desktop.Tests.Visual
{
- internal class TestCaseHitObjects : TestCase
+ internal class TestCaseHitObjects : OsuTestCase
{
- private readonly FramedClock framedClock;
+ private FramedClock framedClock;
private bool auto;
- public TestCaseHitObjects()
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
{
var rateAdjustClock = new StopwatchClock(true);
framedClock = new FramedClock(rateAdjustClock);
- playbackSpeed.ValueChanged += delegate { rateAdjustClock.Rate = playbackSpeed.Value; };
+
+ AddStep(@"circles", () => loadHitobjects(HitObjectType.Circle));
+ AddStep(@"slider", () => loadHitobjects(HitObjectType.Slider));
+ AddStep(@"spinner", () => loadHitobjects(HitObjectType.Spinner));
+
+ AddToggleStep("Auto", state => { auto = state; loadHitobjects(mode); });
+ AddSliderStep("Playback speed", 0.0, 2.0, 0.5, v => rateAdjustClock.Rate = v);
+
+ framedClock.ProcessFrame();
+
+ var clockAdjustContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Clock = framedClock,
+ Children = new[]
+ {
+ playfieldContainer = new OsuInputManager(rulesets.GetRuleset(0)) { RelativeSizeAxes = Axes.Both },
+ approachContainer = new Container { RelativeSizeAxes = Axes.Both }
+ }
+ };
+
+ Add(clockAdjustContainer);
}
private HitObjectType mode = HitObjectType.Slider;
- private readonly BindableNumber playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 };
private Container playfieldContainer;
private Container approachContainer;
- private void load(HitObjectType mode)
+ private void loadHitobjects(HitObjectType mode)
{
this.mode = mode;
@@ -83,54 +102,6 @@ namespace osu.Desktop.VisualTests.Tests
}
}
- public override void Reset()
- {
- base.Reset();
-
- playbackSpeed.TriggerChange();
-
- AddStep(@"circles", () => load(HitObjectType.Circle));
- AddStep(@"slider", () => load(HitObjectType.Slider));
- AddStep(@"spinner", () => load(HitObjectType.Spinner));
-
- AddToggleStep(@"auto", state => { auto = state; load(mode); });
-
- BasicSliderBar sliderBar;
- Add(new Container
- {
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- AutoSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- new SpriteText { Text = "Playback Speed" },
- sliderBar = new BasicSliderBar
- {
- Width = 150,
- Height = 10,
- SelectionColor = Color4.Orange,
- }
- }
- });
-
- sliderBar.Current.BindTo(playbackSpeed);
-
- framedClock.ProcessFrame();
-
- var clockAdjustContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- Clock = framedClock,
- Children = new[]
- {
- playfieldContainer = new Container { RelativeSizeAxes = Axes.Both },
- approachContainer = new Container { RelativeSizeAxes = Axes.Both }
- }
- };
-
- Add(clockAdjustContainer);
- }
-
private int depth;
private void add(DrawableOsuHitObject h)
diff --git a/osu.Desktop.Tests/Visual/TestCaseKeyConfiguration.cs b/osu.Desktop.Tests/Visual/TestCaseKeyConfiguration.cs
new file mode 100644
index 0000000000..cab285c72e
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseKeyConfiguration.cs
@@ -0,0 +1,25 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Overlays;
+
+namespace osu.Desktop.Tests.Visual
+{
+ public class TestCaseKeyConfiguration : OsuTestCase
+ {
+ private readonly KeyBindingOverlay overlay;
+
+ public override string Description => @"Key configuration";
+
+ public TestCaseKeyConfiguration()
+ {
+ Child = overlay = new KeyBindingOverlay();
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ overlay.Show();
+ }
+ }
+}
diff --git a/osu.Desktop.Tests/Visual/TestCaseKeyCounter.cs b/osu.Desktop.Tests/Visual/TestCaseKeyCounter.cs
new file mode 100644
index 0000000000..ffe37c83a6
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseKeyCounter.cs
@@ -0,0 +1,41 @@
+// 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.MathUtils;
+using osu.Game.Screens.Play;
+using OpenTK.Input;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseKeyCounter : OsuTestCase
+ {
+ public override string Description => @"Tests key counter";
+
+ public TestCaseKeyCounter()
+ {
+ KeyCounterCollection kc = new KeyCounterCollection
+ {
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ IsCounting = true,
+ Children = new KeyCounter[]
+ {
+ new KeyCounterKeyboard(Key.Z),
+ new KeyCounterKeyboard(Key.X),
+ new KeyCounterMouse(MouseButton.Left),
+ new KeyCounterMouse(MouseButton.Right),
+ },
+ };
+
+ AddStep("Add random", () =>
+ {
+ Key key = (Key)((int)Key.A + RNG.Next(26));
+ kc.Add(new KeyCounterKeyboard(key));
+ });
+ AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v);
+
+ Add(kc);
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs b/osu.Desktop.Tests/Visual/TestCaseLeaderboard.cs
similarity index 94%
rename from osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs
rename to osu.Desktop.Tests/Visual/TestCaseLeaderboard.cs
index 39010baf91..5f8ee8795c 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseLeaderboard.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 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";
- private Leaderboard leaderboard;
+ private readonly Leaderboard leaderboard;
private void newScores()
{
@@ -207,10 +206,8 @@ namespace osu.Desktop.VisualTests.Tests
leaderboard.Scores = scores;
}
- public override void Reset()
+ public TestCaseLeaderboard()
{
- base.Reset();
-
Add(leaderboard = new Leaderboard
{
Origin = Anchor.Centre,
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs b/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs
similarity index 86%
rename from osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs
rename to osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs
index 3113b63db1..7dcc48c778 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseManiaHitObjects.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs
@@ -3,20 +3,17 @@
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 override void Reset()
+ public TestCaseManiaHitObjects()
{
- base.Reset();
-
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
@@ -40,7 +37,7 @@ namespace osu.Desktop.VisualTests.Tests
{
Name = "Timing section",
RelativeSizeAxes = Axes.Both,
- RelativeCoordinateSpace = new Vector2(1, 10000),
+ RelativeChildSize = new Vector2(1, 10000),
Children = new[]
{
new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red },
@@ -62,7 +59,7 @@ namespace osu.Desktop.VisualTests.Tests
{
Name = "Timing section",
RelativeSizeAxes = Axes.Both,
- RelativeCoordinateSpace = new Vector2(1, 10000),
+ RelativeChildSize = new Vector2(1, 10000),
Children = new[]
{
new DrawableHoldNote(new HoldNote
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs
similarity index 61%
rename from osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs
rename to osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs
index 95287c3199..ed0e5d81e9 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs
@@ -1,36 +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.Input;
-using osu.Framework.Testing;
-using osu.Framework.Graphics;
-using osu.Game.Rulesets.Mania.UI;
using System;
-using System.Collections.Generic;
-using OpenTK;
-using osu.Game.Rulesets.Mania.Objects.Drawables;
-using osu.Game.Rulesets.Mania.Objects;
-using osu.Game.Rulesets.Mania.Timing;
+using System.Linq;
using osu.Framework.Configuration;
-using OpenTK.Input;
+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";
protected override double TimePerAction => 200;
- public override void Reset()
+ public TestCaseManiaPlayfield()
{
- base.Reset();
-
Action createPlayfield = (cols, pos) =>
{
Clear();
- Add(new ManiaPlayfield(cols, new List())
+ Add(new ManiaPlayfield(cols)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -39,37 +38,22 @@ namespace osu.Desktop.VisualTests.Tests
});
};
- Action createPlayfieldWithNotes = (cols, pos) =>
+ const double start_time = 500;
+ const double duration = 500;
+
+ Func createTimingChange = (time, gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
+ {
+ TimingPoint = { BeatLength = 1000 }
+ }, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
+
+ Action createPlayfieldWithNotes = gravity =>
{
Clear();
- ManiaPlayfield playField;
- Add(playField = new ManiaPlayfield(cols, new List { new TimingChange { BeatLength = 200 } })
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- SpecialColumnPosition = pos,
- Scale = new Vector2(1, -1)
- });
-
- for (int i = 0; i < cols; i++)
- {
- playField.Add(new DrawableNote(new Note
- {
- StartTime = Time.Current + 1000,
- Column = i
- }));
- }
- };
-
- Action createPlayfieldWithNotesAcceptingInput = () =>
- {
- Clear();
-
- var rateAdjustClock = new StopwatchClock(true) { Rate = 0.5 };
+ var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
ManiaPlayfield playField;
- Add(playField = new ManiaPlayfield(4, new List { new TimingChange { BeatLength = 200 } })
+ Add(playField = new ManiaPlayfield(4)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -77,14 +61,23 @@ namespace osu.Desktop.VisualTests.Tests
Clock = new FramedClock(rateAdjustClock)
});
- for (int t = 1000; t <= 2000; t += 100)
+ if (!gravity)
+ playField.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
+
+ for (double t = start_time; t <= start_time + duration; t += 100)
{
+ if (gravity)
+ playField.Columns.ElementAt(0).Add(createTimingChange(t, true));
+
playField.Add(new DrawableNote(new Note
{
StartTime = t,
Column = 0
}, new Bindable(Key.D)));
+ if (gravity)
+ playField.Columns.ElementAt(3).Add(createTimingChange(t, true));
+
playField.Add(new DrawableNote(new Note
{
StartTime = t,
@@ -92,17 +85,23 @@ namespace osu.Desktop.VisualTests.Tests
}, new Bindable(Key.K)));
}
- playField.Add(new DrawableHoldNote(new HoldNote
- {
- StartTime = 1000,
- Duration = 1000,
- Column = 1
- }, new Bindable(Key.F)));
+ if (gravity)
+ playField.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
playField.Add(new DrawableHoldNote(new HoldNote
{
- StartTime = 1000,
- Duration = 1000,
+ StartTime = start_time,
+ Duration = duration,
+ Column = 1
+ }, new Bindable(Key.F)));
+
+ if (gravity)
+ playField.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
+
+ playField.Add(new DrawableHoldNote(new HoldNote
+ {
+ StartTime = start_time,
+ Duration = duration,
Column = 2
}, new Bindable(Key.J)));
};
@@ -116,16 +115,11 @@ namespace osu.Desktop.VisualTests.Tests
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
- AddStep("Normal special style", () => createPlayfield(4, SpecialColumnPosition.Normal));
+ AddStep("Notes with input", () => createPlayfieldWithNotes(false));
+ AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
- AddStep("Notes", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Normal));
- AddWaitStep(10);
- AddStep("Left special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Left));
- AddWaitStep(10);
- AddStep("Right special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Right));
- AddWaitStep(10);
-
- AddStep("Notes with input", () => createPlayfieldWithNotesAcceptingInput());
+ AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
+ AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
}
private void triggerKeyDown(Column column)
diff --git a/osu.Desktop.Tests/Visual/TestCaseMedalOverlay.cs b/osu.Desktop.Tests/Visual/TestCaseMedalOverlay.cs
new file mode 100644
index 0000000000..747f2190b0
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseMedalOverlay.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Overlays;
+using osu.Game.Users;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseMedalOverlay : OsuTestCase
+ {
+ public override string Description => @"medal get!";
+
+ public TestCaseMedalOverlay()
+ {
+ AddStep(@"display", () =>
+ {
+ LoadComponentAsync(new MedalOverlay(new Medal
+ {
+ Name = @"Animations",
+ InternalName = @"all-intro-doubletime",
+ Description = @"More complex than you think.",
+ }), Add);
+ });
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs b/osu.Desktop.Tests/Visual/TestCaseMenuButtonSystem.cs
similarity index 59%
rename from osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs
rename to osu.Desktop.Tests/Visual/TestCaseMenuButtonSystem.cs
index ddb62598cf..3c7ee343bb 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseMenuButtonSystem.cs
@@ -1,25 +1,22 @@
// 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.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Screens.Menu;
using OpenTK.Graphics;
-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";
- public override void Reset()
+ public TestCaseMenuButtonSystem()
{
- base.Reset();
-
Add(new Box
{
- ColourInfo = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke),
+ Colour = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke),
RelativeSizeAxes = Framework.Graphics.Axes.Both,
});
Add(new ButtonSystem());
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs b/osu.Desktop.Tests/Visual/TestCaseMenuOverlays.cs
similarity index 71%
rename from osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs
rename to osu.Desktop.Tests/Visual/TestCaseMenuOverlays.cs
index 23fe8f16db..1f4ad9d3da 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseMenuOverlays.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseMenuOverlays.cs
@@ -3,24 +3,20 @@
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";
- private PauseContainer.PauseOverlay pauseOverlay;
- private FailOverlay failOverlay;
- private int retryCount;
-
- public override void Reset()
+ public TestCaseMenuOverlays()
{
- base.Reset();
+ FailOverlay failOverlay;
+ PauseContainer.PauseOverlay pauseOverlay;
- retryCount = 0;
+ var retryCount = 0;
Add(pauseOverlay = new PauseContainer.PauseOverlay
{
@@ -34,14 +30,16 @@ namespace osu.Desktop.VisualTests.Tests
OnQuit = () => Logger.Log(@"Quit"),
});
- AddStep(@"Pause", delegate {
- if(failOverlay.State == Visibility.Visible)
+ AddStep(@"Pause", delegate
+ {
+ if (failOverlay.State == Visibility.Visible)
{
failOverlay.Hide();
}
pauseOverlay.Show();
});
- AddStep("Fail", delegate {
+ AddStep("Fail", delegate
+ {
if (pauseOverlay.State == Visibility.Visible)
{
pauseOverlay.Hide();
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMods.cs b/osu.Desktop.Tests/Visual/TestCaseMods.cs
similarity index 79%
rename from osu.Desktop.VisualTests/Tests/TestCaseMods.cs
rename to osu.Desktop.Tests/Visual/TestCaseMods.cs
index 3f3a9d82f5..f48030067d 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseMods.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseMods.cs
@@ -4,32 +4,31 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays.Mods;
-using osu.Framework.Testing;
-using osu.Game.Database;
+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";
private ModSelectOverlay modSelect;
private ModDisplay modDisplay;
- private RulesetDatabase rulesets;
+ private RulesetStore rulesets;
[BackgroundDependencyLoader]
- private void load(RulesetDatabase rulesets)
+ private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
- public override void Reset()
+ protected override void LoadComplete()
{
- base.Reset();
+ base.LoadComplete();
Add(modSelect = new ModSelectOverlay
{
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs b/osu.Desktop.Tests/Visual/TestCaseMusicController.cs
similarity index 57%
rename from osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs
rename to osu.Desktop.Tests/Visual/TestCaseMusicController.cs
index 5665bf859a..8d71527a21 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseMusicController.cs
@@ -1,30 +1,28 @@
// 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 MusicController mc;
+ private readonly Bindable beatmapBacking = new Bindable();
public TestCaseMusicController()
{
Clock = new FramedClock();
- }
- public override void Reset()
- {
- base.Reset();
- Clock.ProcessFrame();
- mc = new MusicController
+ var mc = new MusicController
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre
@@ -33,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 84%
rename from osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs
rename to osu.Desktop.Tests/Visual/TestCaseNotificationOverlay.cs
index 8972040b06..1e46fbda35 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseNotificationOverlay.cs
@@ -2,29 +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 NotificationManager manager;
+ private readonly NotificationOverlay manager;
- public override void Reset()
+ public TestCaseNotificationOverlay()
{
- base.Reset();
-
progressingNotifications.Clear();
- Content.Add(manager = new NotificationManager
+ Content.Add(manager = new NotificationOverlay
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
@@ -58,10 +57,7 @@ namespace osu.Desktop.VisualTests.Tests
}
if (remaining > 0)
- {
- Delay(80);
- Schedule(() => sendBarrage(remaining - 1));
- }
+ Scheduler.AddDelayed(() => sendBarrage(remaining - 1), 80);
}
protected override void Update()
@@ -72,7 +68,7 @@ namespace osu.Desktop.VisualTests.Tests
while (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3)
{
- var p = progressingNotifications.FirstOrDefault(n => n.IsLoaded && n.State == ProgressNotificationState.Queued);
+ var p = progressingNotifications.FirstOrDefault(n => n.IsAlive && n.State == ProgressNotificationState.Queued);
if (p == null)
break;
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseOnScreenDisplay.cs b/osu.Desktop.Tests/Visual/TestCaseOnScreenDisplay.cs
similarity index 83%
rename from osu.Desktop.VisualTests/Tests/TestCaseOnScreenDisplay.cs
rename to osu.Desktop.Tests/Visual/TestCaseOnScreenDisplay.cs
index 3cefb8a3d2..0b7a822e1d 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseOnScreenDisplay.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseOnScreenDisplay.cs
@@ -3,21 +3,20 @@
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;
public override string Description => @"Make it easier to see setting changes";
- public override void Reset()
+ protected override void LoadComplete()
{
- base.Reset();
+ base.LoadComplete();
Add(new OnScreenDisplay());
diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.Tests/Visual/TestCasePlaySongSelect.cs
similarity index 73%
rename from osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs
rename to osu.Desktop.Tests/Visual/TestCasePlaySongSelect.cs
index 35eb6d0ff9..379100b543 100644
--- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs
+++ b/osu.Desktop.Tests/Visual/TestCasePlaySongSelect.cs
@@ -2,43 +2,40 @@
// 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;
+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 BeatmapDatabase db;
- private TestStorage storage;
- private PlaySongSelect songSelect;
+ private readonly BeatmapManager manager;
public override string Description => @"with fake data";
- private RulesetDatabase rulesets;
+ private readonly RulesetStore rulesets;
- public override void Reset()
+ public TestCasePlaySongSelect()
{
- base.Reset();
- if (db == null)
+ PlaySongSelect songSelect;
+
+ if (manager == null)
{
- storage = new TestStorage(@"TestCasePlaySongSelect");
+ var storage = new TestStorage(@"TestCasePlaySongSelect");
var backingDatabase = storage.GetDatabase(@"client");
+ backingDatabase.CreateTable();
- rulesets = new RulesetDatabase(storage, backingDatabase);
- db = new BeatmapDatabase(storage, backingDatabase, rulesets);
-
- var sets = new List();
+ rulesets = new RulesetStore(backingDatabase);
+ manager = new BeatmapManager(storage, null, backingDatabase, rulesets);
for (int i = 0; i < 100; i += 10)
- sets.Add(createTestBeatmapSet(i));
-
- db.Import(sets);
+ manager.Import(createTestBeatmapSet(i));
}
Add(songSelect = new PlaySongSelect());
@@ -49,21 +46,12 @@ namespace osu.Desktop.VisualTests.Tests
AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
}
- //protected override void Dispose(bool isDisposing)
- //{
- // if (oldDb != null)
- // db = null;
-
- // base.Dispose(isDisposing);
- //}
-
private BeatmapSetInfo createTestBeatmapSet(int i)
{
return new BeatmapSetInfo
{
OnlineBeatmapSetID = 1234 + i,
Hash = "d8e8fca2dc0f896fd7cb4cb0031ba249",
- Path = string.Empty,
Metadata = new BeatmapMetadata
{
OnlineBeatmapSetID = 1234 + i,
diff --git a/osu.Desktop.Tests/Visual/TestCasePlayer.cs b/osu.Desktop.Tests/Visual/TestCasePlayer.cs
new file mode 100644
index 0000000000..f5d02d3f2b
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCasePlayer.cs
@@ -0,0 +1,87 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using osu.Desktop.Tests.Beatmaps;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.UI;
+using osu.Game.Screens.Play;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCasePlayer : OsuTestCase
+ {
+ protected Player Player;
+ private RulesetStore rulesets;
+
+ public override string Description => @"Showing everything to play the game.";
+
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ this.rulesets = rulesets;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ var objects = new List();
+
+ int time = 1500;
+ for (int i = 0; i < 50; i++)
+ {
+ objects.Add(new HitCircle
+ {
+ StartTime = time,
+ Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : OsuPlayfield.BASE_SIZE.X,
+ i % 4 < 2 ? 0 : OsuPlayfield.BASE_SIZE.Y),
+ NewCombo = i % 4 == 0
+ });
+
+ time += 500;
+ }
+
+ Beatmap b = new Beatmap
+ {
+ HitObjects = objects,
+ BeatmapInfo = new BeatmapInfo
+ {
+ Difficulty = new BeatmapDifficulty(),
+ Ruleset = rulesets.Query().First(),
+ Metadata = new BeatmapMetadata
+ {
+ Artist = @"Unknown",
+ Title = @"Sample Beatmap",
+ Author = @"peppy",
+ }
+ }
+ };
+
+ WorkingBeatmap beatmap = new TestWorkingBeatmap(b);
+
+ Add(new Box
+ {
+ RelativeSizeAxes = Framework.Graphics.Axes.Both,
+ Colour = Color4.Black,
+ });
+
+ Add(Player = CreatePlayer(beatmap));
+ }
+
+ protected virtual Player CreatePlayer(WorkingBeatmap beatmap)
+ {
+ return new Player
+ {
+ InitialBeatmap = beatmap
+ };
+ }
+ }
+}
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.Tests/Visual/TestCaseReplaySettingsOverlay.cs b/osu.Desktop.Tests/Visual/TestCaseReplaySettingsOverlay.cs
new file mode 100644
index 0000000000..ab865dcab0
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseReplaySettingsOverlay.cs
@@ -0,0 +1,52 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Screens.Play;
+using osu.Game.Screens.Play.ReplaySettings;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseReplaySettingsOverlay : OsuTestCase
+ {
+ public override string Description => @"Settings visible in replay/auto";
+
+ public TestCaseReplaySettingsOverlay()
+ {
+ ExampleContainer container;
+
+ Add(new ReplaySettingsOverlay
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ });
+
+ Add(container = new ExampleContainer());
+
+ AddStep(@"Add button", () => container.Add(new OsuButton
+ {
+ RelativeSizeAxes = Axes.X,
+ Text = @"Button",
+ }));
+
+ AddStep(@"Add checkbox", () => container.Add(new ReplayCheckbox
+ {
+ LabelText = "Checkbox",
+ }));
+
+ AddStep(@"Add textbox", () => container.Add(new FocusedTextBox
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 30,
+ PlaceholderText = "Textbox",
+ HoldFocus = false,
+ }));
+ }
+
+ private class ExampleContainer : ReplayGroup
+ {
+ protected override string Title => @"example";
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs b/osu.Desktop.Tests/Visual/TestCaseResults.cs
similarity index 64%
rename from osu.Desktop.VisualTests/Tests/TestCaseResults.cs
rename to osu.Desktop.Tests/Visual/TestCaseResults.cs
index f8c93e9a73..a0622b302a 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseResults.cs
@@ -3,44 +3,39 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Testing;
using osu.Game.Beatmaps;
-using osu.Game.Database;
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 BeatmapDatabase db;
+ private BeatmapManager beatmaps;
public override string Description => @"Results after playing.";
[BackgroundDependencyLoader]
- private void load(BeatmapDatabase db)
+ private void load(BeatmapManager beatmaps)
{
- this.db = db;
+ this.beatmaps = beatmaps;
}
private WorkingBeatmap beatmap;
- public override void Reset()
+ protected override void LoadComplete()
{
- base.Reset();
+ base.LoadComplete();
if (beatmap == null)
{
- var beatmapInfo = db.Query().FirstOrDefault(b => b.RulesetID == 0);
+ var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
if (beatmapInfo != null)
- beatmap = db.GetWorkingBeatmap(beatmapInfo);
+ beatmap = beatmaps.GetWorkingBeatmap(beatmapInfo);
}
- base.Reset();
-
Add(new Results(new Score
{
TotalScore = 2845370,
@@ -48,7 +43,7 @@ namespace osu.Desktop.VisualTests.Tests
MaxCombo = 123,
Rank = ScoreRank.A,
Date = DateTimeOffset.Now,
- Statistics = new Dictionary()
+ Statistics = new Dictionary
{
{ "300", 50 },
{ "100", 20 },
@@ -61,7 +56,7 @@ namespace osu.Desktop.VisualTests.Tests
}
})
{
- Beatmap = beatmap
+ InitialBeatmap = beatmap
});
}
}
diff --git a/osu.Desktop.Tests/Visual/TestCaseRoomInspector.cs b/osu.Desktop.Tests/Visual/TestCaseRoomInspector.cs
new file mode 100644
index 0000000000..db557baed2
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseRoomInspector.cs
@@ -0,0 +1,143 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Rulesets;
+using osu.Game.Screens.Multiplayer;
+using osu.Game.Users;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseRoomInspector : OsuTestCase
+ {
+ public override string Description => @"from the multiplayer lobby";
+
+ private RulesetStore rulesets;
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ var room = new Room
+ {
+ Name = { Value = @"My Awesome Room" },
+ Host = { Value = new User { Username = @"flyte", Id = 3103765, Country = new Country { FlagName = @"JP" } } },
+ Status = { Value = new RoomStatusOpen() },
+ Type = { Value = new GameTypeTeamVersus() },
+ Beatmap =
+ {
+ Value = new BeatmapInfo
+ {
+ StarDifficulty = 3.7,
+ Ruleset = rulesets.GetRuleset(3),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"Platina",
+ Artist = @"Maaya Sakamoto",
+ Author = @"uwutm8",
+ },
+ BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/560573/covers/cover.jpg?1492722343",
+ },
+ },
+ },
+ }
+ },
+ MaxParticipants = { Value = 200 },
+ Participants =
+ {
+ Value = new[]
+ {
+ new User { Username = @"flyte", Id = 3103765, GlobalRank = 1425 },
+ new User { Username = @"Cookiezi", Id = 124493, GlobalRank = 5466 },
+ new User { Username = @"Angelsim", Id = 1777162, GlobalRank = 2873 },
+ new User { Username = @"Rafis", Id = 2558286, GlobalRank = 4687 },
+ new User { Username = @"hvick225", Id = 50265, GlobalRank = 3258 },
+ new User { Username = @"peppy", Id = 2, GlobalRank = 6251 }
+ }
+ }
+ };
+
+ RoomInspector inspector;
+ Add(inspector = new RoomInspector
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Room = room,
+ });
+
+ AddStep(@"change title", () => room.Name.Value = @"A Better Room Than The Above");
+ AddStep(@"change host", () => room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } });
+ AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying());
+ AddStep(@"change type", () => room.Type.Value = new GameTypeTag());
+ AddStep(@"change beatmap", () => room.Beatmap.Value = null);
+ AddStep(@"change max participants", () => room.MaxParticipants.Value = null);
+ AddStep(@"change participants", () => room.Participants.Value = new[]
+ {
+ new User { Username = @"filsdelama", Id = 2831793, GlobalRank = 8542 },
+ new User { Username = @"_index", Id = 652457, GlobalRank = 15024 }
+ });
+
+ AddStep(@"change room", () =>
+ {
+ var newRoom = new Room
+ {
+ Name = { Value = @"My New, Better Than Ever Room" },
+ Host = { Value = new User { Username = @"Angelsim", Id = 1777162, Country = new Country { FlagName = @"KR" } } },
+ Status = { Value = new RoomStatusOpen() },
+ Type = { Value = new GameTypeTagTeam() },
+ Beatmap =
+ {
+ Value = new BeatmapInfo
+ {
+ StarDifficulty = 7.07,
+ Ruleset = rulesets.GetRuleset(0),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"FREEDOM DIVE",
+ Artist = @"xi",
+ Author = @"Nakagawa-Kanon",
+ },
+ BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/39804/covers/cover.jpg?1456506845",
+ },
+ },
+ },
+ },
+ },
+ MaxParticipants = { Value = 10 },
+ Participants =
+ {
+ Value = new[]
+ {
+ new User { Username = @"Angelsim", Id = 1777162, GlobalRank = 4 },
+ new User { Username = @"HappyStick", Id = 256802, GlobalRank = 752 },
+ new User { Username = @"-Konpaku-", Id = 2258797, GlobalRank = 571 }
+ }
+ }
+ };
+
+ inspector.Room = newRoom;
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ this.rulesets = rulesets;
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs b/osu.Desktop.Tests/Visual/TestCaseScoreCounter.cs
similarity index 81%
rename from osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs
rename to osu.Desktop.Tests/Visual/TestCaseScoreCounter.cs
index f86fa4dab5..ac96deb209 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseScoreCounter.cs
@@ -1,24 +1,21 @@
// 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";
- public override void Reset()
+ public TestCaseScoreCounter()
{
- base.Reset();
-
int numerator = 0, denominator = 0;
ScoreCounter score = new ScoreCounter(7)
@@ -52,7 +49,7 @@ namespace osu.Desktop.VisualTests.Tests
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Position = new Vector2(20, -160),
- Count = 5,
+ CountStars = 5,
};
Add(stars);
@@ -61,7 +58,7 @@ namespace osu.Desktop.VisualTests.Tests
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Position = new Vector2(20, -190),
- Text = stars.Count.ToString("0.00"),
+ Text = stars.CountStars.ToString("0.00"),
};
Add(starsLabel);
@@ -71,15 +68,16 @@ namespace osu.Desktop.VisualTests.Tests
comboCounter.Current.Value = 0;
numerator = denominator = 0;
accuracyCounter.SetFraction(0, 0);
- stars.Count = 0;
- starsLabel.Text = stars.Count.ToString("0.00");
+ stars.CountStars = 0;
+ starsLabel.Text = stars.CountStars.ToString("0.00");
});
AddStep(@"Hit! :D", delegate
{
score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current > 0 ? comboCounter.Current - 1 : 0) / 25.0);
comboCounter.Increment();
- numerator++; denominator++;
+ numerator++;
+ denominator++;
accuracyCounter.SetFraction(numerator, denominator);
});
@@ -92,8 +90,8 @@ namespace osu.Desktop.VisualTests.Tests
AddStep(@"Alter stars", delegate
{
- stars.Count = RNG.NextSingle() * (stars.StarCount + 1);
- starsLabel.Text = stars.Count.ToString("0.00");
+ stars.CountStars = RNG.NextSingle() * (stars.StarCount + 1);
+ starsLabel.Text = stars.CountStars.ToString("0.00");
});
AddStep(@"Stop counters", delegate
diff --git a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs
new file mode 100644
index 0000000000..2d49b2d0f9
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs
@@ -0,0 +1,226 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+using OpenTK;
+using osu.Desktop.Tests.Beatmaps;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input;
+using osu.Framework.Timing;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Beatmaps;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.Timing;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Desktop.Tests.Visual
+{
+ ///
+ /// The most minimal implementation of a playfield with scrolling hit objects.
+ ///
+ [TestFixture]
+ public class TestCaseScrollingPlayfield : OsuTestCase
+ {
+ public TestCaseScrollingPlayfield()
+ {
+ Clock = new FramedClock();
+
+ var objects = new List();
+
+ int time = 1500;
+ for (int i = 0; i < 50; i++)
+ {
+ objects.Add(new TestHitObject { StartTime = time });
+
+ time += 500;
+ }
+
+ Beatmap b = new Beatmap
+ {
+ HitObjects = objects,
+ BeatmapInfo = new BeatmapInfo
+ {
+ Difficulty = new BeatmapDifficulty(),
+ Metadata = new BeatmapMetadata()
+ }
+ };
+
+ WorkingBeatmap beatmap = new TestWorkingBeatmap(b);
+
+ TestRulesetContainer horizontalRulesetContainer;
+ Add(horizontalRulesetContainer = new TestRulesetContainer(Axes.X, beatmap, true));
+
+ TestRulesetContainer verticalRulesetContainer;
+ Add(verticalRulesetContainer = new TestRulesetContainer(Axes.Y, beatmap, true));
+
+ AddStep("Reverse direction", () =>
+ {
+ horizontalRulesetContainer.Playfield.Reversed.Toggle();
+ verticalRulesetContainer.Playfield.Reversed.Toggle();
+ });
+ }
+
+ [Test]
+ public void TestSpeedAdjustmentOrdering()
+ {
+ var hitObjectContainer = new ScrollingPlayfield.ScrollingHitObjectContainer(Axes.X);
+
+ var speedAdjustments = new[]
+ {
+ new SpeedAdjustmentContainer(new MultiplierControlPoint()),
+ new SpeedAdjustmentContainer(new MultiplierControlPoint(1000)
+ {
+ TimingPoint = new TimingControlPoint { BeatLength = 500 }
+ }),
+ new SpeedAdjustmentContainer(new MultiplierControlPoint(2000)
+ {
+ TimingPoint = new TimingControlPoint { BeatLength = 1000 },
+ DifficultyPoint = new DifficultyControlPoint { SpeedMultiplier = 2}
+ }),
+ new SpeedAdjustmentContainer(new MultiplierControlPoint(3000)
+ {
+ TimingPoint = new TimingControlPoint { BeatLength = 1000 },
+ DifficultyPoint = new DifficultyControlPoint { SpeedMultiplier = 1}
+ }),
+ };
+
+ var hitObjects = new[]
+ {
+ new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = -1000 }),
+ new DrawableTestHitObject(Axes.X, new TestHitObject()),
+ new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 1000 }),
+ new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 2000 }),
+ new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 3000 }),
+ new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 4000 }),
+ };
+
+ hitObjects.ForEach(h => hitObjectContainer.Add(h));
+ speedAdjustments.ForEach(hitObjectContainer.AddSpeedAdjustment);
+
+ // The 0th index in hitObjectContainer.SpeedAdjustments is the "default" control point
+ // Check multiplier of the default speed adjustment
+ Assert.AreEqual(1, hitObjectContainer.SpeedAdjustments[0].ControlPoint.Multiplier);
+ Assert.AreEqual(1, speedAdjustments[0].ControlPoint.Multiplier);
+ Assert.AreEqual(2, speedAdjustments[1].ControlPoint.Multiplier);
+ Assert.AreEqual(2, speedAdjustments[2].ControlPoint.Multiplier);
+ Assert.AreEqual(1, speedAdjustments[3].ControlPoint.Multiplier);
+
+ // Check insertion of hit objects
+ Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[0]));
+ Assert.IsTrue(speedAdjustments[0].Contains(hitObjects[1]));
+ Assert.IsTrue(speedAdjustments[1].Contains(hitObjects[2]));
+ Assert.IsTrue(speedAdjustments[2].Contains(hitObjects[3]));
+ Assert.IsTrue(speedAdjustments[3].Contains(hitObjects[4]));
+ Assert.IsTrue(speedAdjustments[3].Contains(hitObjects[5]));
+
+ hitObjectContainer.RemoveSpeedAdjustment(speedAdjustments[1]);
+
+ // The hit object contained in this speed adjustment should be resorted into the previous one
+
+ Assert.IsTrue(speedAdjustments[0].Contains(hitObjects[2]));
+ }
+
+ private class TestRulesetContainer : ScrollingRulesetContainer
+ {
+ private readonly Axes scrollingAxes;
+
+ public TestRulesetContainer(Axes scrollingAxes, WorkingBeatmap beatmap, bool isForCurrentRuleset)
+ : base(null, beatmap, isForCurrentRuleset)
+ {
+ this.scrollingAxes = scrollingAxes;
+ }
+
+ public new TestPlayfield Playfield => base.Playfield;
+
+ public override ScoreProcessor CreateScoreProcessor() => new TestScoreProcessor();
+
+ public override PassThroughInputManager CreateInputManager() => new PassThroughInputManager();
+
+ protected override BeatmapConverter CreateBeatmapConverter() => new TestBeatmapConverter();
+
+ protected override Playfield CreatePlayfield() => new TestPlayfield(scrollingAxes);
+
+ protected override DrawableHitObject GetVisualRepresentation(TestHitObject h) => new DrawableTestHitObject(scrollingAxes, h);
+ }
+
+ private class TestScoreProcessor : ScoreProcessor
+ {
+ protected override void OnNewJudgement(TestJudgement judgement)
+ {
+ }
+ }
+
+ private class TestBeatmapConverter : BeatmapConverter
+ {
+ protected override IEnumerable ValidConversionTypes => new[] { typeof(HitObject) };
+
+ protected override IEnumerable ConvertHitObject(HitObject original, Beatmap beatmap)
+ {
+ yield return original as TestHitObject;
+ }
+ }
+
+ private class DrawableTestHitObject : DrawableScrollingHitObject
+ {
+ public DrawableTestHitObject(Axes scrollingAxes, TestHitObject hitObject)
+ : base(hitObject)
+ {
+ Anchor = scrollingAxes == Axes.Y ? Anchor.TopCentre : Anchor.CentreLeft;
+ Origin = Anchor.Centre;
+
+ AutoSizeAxes = Axes.Both;
+
+ Add(new Circle
+ {
+ Size = new Vector2(50)
+ });
+ }
+
+ protected override TestJudgement CreateJudgement() => new TestJudgement();
+
+ protected override void UpdateState(ArmedState state)
+ {
+ }
+ }
+
+ private class TestPlayfield : ScrollingPlayfield
+ {
+ protected override Container Content => content;
+ private readonly Container content;
+
+ public TestPlayfield(Axes scrollingAxes)
+ : base(scrollingAxes)
+ {
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.2f
+ },
+ content = new Container { RelativeSizeAxes = Axes.Both }
+ };
+ }
+ }
+
+
+ private class TestHitObject : HitObject
+ {
+ }
+
+ private class TestJudgement : Judgement
+ {
+ public override string ResultString { get { throw new NotImplementedException(); } }
+ public override string MaxResultString { get { throw new NotImplementedException(); } }
+ }
+ }
+}
diff --git a/osu.Desktop.Tests/Visual/TestCaseSettings.cs b/osu.Desktop.Tests/Visual/TestCaseSettings.cs
new file mode 100644
index 0000000000..1f4b88e9e3
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseSettings.cs
@@ -0,0 +1,25 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Overlays;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseSettings : OsuTestCase
+ {
+ public override string Description => @"Tests the settings overlay";
+
+ private readonly SettingsOverlay settings;
+
+ public TestCaseSettings()
+ {
+ Children = new[] { settings = new MainSettings() };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ settings.ToggleVisibility();
+ }
+ }
+}
diff --git a/osu.Desktop.Tests/Visual/TestCaseSkipButton.cs b/osu.Desktop.Tests/Visual/TestCaseSkipButton.cs
new file mode 100644
index 0000000000..0e73314850
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseSkipButton.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Screens.Play;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseSkipButton : OsuTestCase
+ {
+ public override string Description => @"Skip skip skippediskip";
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Add(new SkipButton(Clock.CurrentTime + 5000));
+ }
+ }
+}
diff --git a/osu.Desktop.Tests/Visual/TestCaseSocial.cs b/osu.Desktop.Tests/Visual/TestCaseSocial.cs
new file mode 100644
index 0000000000..da60c82cea
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseSocial.cs
@@ -0,0 +1,82 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Overlays;
+using osu.Game.Users;
+
+namespace osu.Desktop.Tests.Visual
+{
+ public class TestCaseSocial : OsuTestCase
+ {
+ public override string Description => @"social browser overlay";
+
+ public TestCaseSocial()
+ {
+ SocialOverlay s = new SocialOverlay
+ {
+ Users = new[]
+ {
+ new User
+ {
+ Username = @"flyte",
+ Id = 3103765,
+ Country = new Country { FlagName = @"JP" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
+ },
+ new User
+ {
+ Username = @"Cookiezi",
+ Id = 124493,
+ Country = new Country { FlagName = @"KR" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
+ },
+ new User
+ {
+ Username = @"Angelsim",
+ Id = 1777162,
+ Country = new Country { FlagName = @"KR" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
+ },
+ new User
+ {
+ Username = @"Rafis",
+ Id = 2558286,
+ Country = new Country { FlagName = @"PL" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg",
+ },
+ new User
+ {
+ Username = @"hvick225",
+ Id = 50265,
+ Country = new Country { FlagName = @"TW" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c5.jpg",
+ },
+ new User
+ {
+ Username = @"peppy",
+ Id = 2,
+ Country = new Country { FlagName = @"AU" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
+ },
+ new User
+ {
+ Username = @"filsdelama",
+ Id = 2831793,
+ Country = new Country { FlagName = @"FR" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c7.jpg"
+ },
+ new User
+ {
+ Username = @"_index",
+ Id = 652457,
+ Country = new Country { FlagName = @"RU" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c8.jpg"
+ },
+ },
+ };
+ Add(s);
+
+ AddStep(@"toggle", s.ToggleVisibility);
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSongProgress.cs b/osu.Desktop.Tests/Visual/TestCaseSongProgress.cs
similarity index 82%
rename from osu.Desktop.VisualTests/Tests/TestCaseSongProgress.cs
rename to osu.Desktop.Tests/Visual/TestCaseSongProgress.cs
index e3c343f5f8..fceb773ae6 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseSongProgress.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseSongProgress.cs
@@ -4,26 +4,23 @@
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";
- private SongProgress progress;
- private SongProgressGraph graph;
+ private readonly SongProgress progress;
+ private readonly SongProgressGraph graph;
- private StopwatchClock clock;
+ private readonly StopwatchClock clock;
- public override void Reset()
+ public TestCaseSongProgress()
{
- base.Reset();
-
clock = new StopwatchClock(true);
Add(progress = new SongProgress
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs b/osu.Desktop.Tests/Visual/TestCaseTabControl.cs
similarity index 83%
rename from osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs
rename to osu.Desktop.Tests/Visual/TestCaseTabControl.cs
index 96933a15e7..4bdea2615d 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseTabControl.cs
@@ -2,22 +2,19 @@
// 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";
- public override void Reset()
+ public TestCaseTabControl()
{
- base.Reset();
-
OsuSpriteText text;
OsuTabControl filter;
Add(filter = new OsuTabControl
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs
similarity index 58%
rename from osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs
rename to osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs
index 259d0267db..681805ced0 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs
@@ -1,24 +1,30 @@
// 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;
+using osu.Desktop.Tests.Beatmaps;
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Objects;
-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";
@@ -26,14 +32,14 @@ namespace osu.Desktop.VisualTests.Tests
protected override double TimePerAction => default_duration * 2;
private readonly Random rng = new Random(1337);
- private TaikoPlayfield playfield;
+ private TaikoRulesetContainer rulesetContainer;
private Container playfieldContainer;
- public override void Reset()
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
{
- base.Reset();
-
- AddStep("Hit!", addHitJudgement);
+ AddStep("Hit!", () => addHitJudgement(false));
+ AddStep("Kiai hit", () => addHitJudgement(true));
AddStep("Miss :(", addMissJudgement);
AddStep("DrumRoll", () => addDrumRoll(false));
AddStep("Strong DrumRoll", () => addDrumRoll(true));
@@ -51,6 +57,25 @@ namespace osu.Desktop.VisualTests.Tests
AddStep("Height test 5", () => changePlayfieldSize(5));
AddStep("Reset height", () => changePlayfieldSize(6));
+ var controlPointInfo = new ControlPointInfo();
+ controlPointInfo.TimingPoints.Add(new TimingControlPoint());
+
+ WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
+ {
+ HitObjects = new List { new CentreHit() },
+ BeatmapInfo = new BeatmapInfo
+ {
+ Difficulty = new BeatmapDifficulty(),
+ Metadata = new BeatmapMetadata
+ {
+ Artist = @"Unknown",
+ Title = @"Sample Beatmap",
+ Author = @"peppy",
+ },
+ },
+ ControlPointInfo = controlPointInfo
+ });
+
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
Add(playfieldContainer = new Container
@@ -58,17 +83,16 @@ namespace osu.Desktop.VisualTests.Tests
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
- Height = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT,
+ Height = 768,
Clock = new FramedClock(rateAdjustClock),
- Children = new[]
- {
- playfield = new TaikoPlayfield()
- }
+ Children = new[] { rulesetContainer = new TaikoRulesetContainer(rulesets.GetRuleset(1).CreateInstance(), beatmap, true) }
});
}
private void changePlayfieldSize(int step)
{
+ double delay = 0;
+
// Add new hits
switch (step)
{
@@ -85,8 +109,8 @@ namespace osu.Desktop.VisualTests.Tests
addDrumRoll(true);
break;
case 5:
- addSwell(1000);
- playfieldContainer.Delay(scroll_time - 100);
+ addSwell();
+ delay = scroll_time - 100;
break;
}
@@ -94,19 +118,28 @@ namespace osu.Desktop.VisualTests.Tests
switch (step)
{
default:
- playfieldContainer.ResizeTo(new Vector2(1, rng.Next(25, 400)), 500);
+ playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, rng.Next(25, 400)), 500);
break;
case 6:
- playfieldContainer.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
@@ -117,18 +150,18 @@ namespace osu.Desktop.VisualTests.Tests
}
};
- playfield.OnJudgement(h);
+ rulesetContainer.Playfield.OnJudgement(h);
if (RNG.Next(10) == 0)
{
h.Judgement.SecondHit = true;
- playfield.OnJudgement(h);
+ rulesetContainer.Playfield.OnJudgement(h);
}
}
private void addMissJudgement()
{
- playfield.OnJudgement(new DrawableTestHit(new Hit())
+ rulesetContainer.Playfield.OnJudgement(new DrawableTestHit(new Hit())
{
Judgement = new TaikoJudgement
{
@@ -140,22 +173,17 @@ namespace osu.Desktop.VisualTests.Tests
private void addBarLine(bool major, double delay = scroll_time)
{
- BarLine bl = new BarLine
- {
- StartTime = playfield.Time.Current + delay,
- ScrollTime = scroll_time
- };
+ BarLine bl = new BarLine { StartTime = rulesetContainer.Playfield.Time.Current + delay };
- playfield.AddBarLine(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
+ rulesetContainer.Playfield.Add(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
}
private void addSwell(double duration = default_duration)
{
- playfield.Add(new DrawableSwell(new Swell
+ rulesetContainer.Playfield.Add(new DrawableSwell(new Swell
{
- StartTime = playfield.Time.Current + scroll_time,
+ StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
Duration = duration,
- ScrollTime = scroll_time
}));
}
@@ -166,41 +194,40 @@ namespace osu.Desktop.VisualTests.Tests
var d = new DrumRoll
{
- StartTime = playfield.Time.Current + scroll_time,
+ StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
IsStrong = strong,
Duration = duration,
- ScrollTime = scroll_time,
};
- playfield.Add(new DrawableDrumRoll(d));
+ rulesetContainer.Playfield.Add(new DrawableDrumRoll(d));
}
private void addCentreHit(bool strong)
{
Hit h = new Hit
{
- StartTime = playfield.Time.Current + scroll_time,
- ScrollTime = scroll_time
+ StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
+ IsStrong = strong
};
if (strong)
- playfield.Add(new DrawableCentreHitStrong(h));
+ rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h));
else
- playfield.Add(new DrawableCentreHit(h));
+ rulesetContainer.Playfield.Add(new DrawableCentreHit(h));
}
private void addRimHit(bool strong)
{
Hit h = new Hit
{
- StartTime = playfield.Time.Current + scroll_time,
- ScrollTime = scroll_time
+ StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
+ IsStrong = strong
};
if (strong)
- playfield.Add(new DrawableRimHitStrong(h));
+ rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h));
else
- playfield.Add(new DrawableRimHit(h));
+ rulesetContainer.Playfield.Add(new DrawableRimHit(h));
}
private class DrawableTestHit : DrawableHitObject
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs b/osu.Desktop.Tests/Visual/TestCaseTextAwesome.cs
similarity index 79%
rename from osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs
rename to osu.Desktop.Tests/Visual/TestCaseTextAwesome.cs
index 7182ee7c06..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,16 +9,14 @@ 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";
- public override void Reset()
+ public TestCaseTextAwesome()
{
- base.Reset();
-
FillFlowContainer flow;
Add(flow = new FillFlowContainer
@@ -33,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.Tests/Visual/TestCaseTwoLayerButton.cs b/osu.Desktop.Tests/Visual/TestCaseTwoLayerButton.cs
new file mode 100644
index 0000000000..ac641d75a2
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseTwoLayerButton.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Graphics.UserInterface;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseTwoLayerButton : OsuTestCase
+ {
+ public override string Description => @"Mostly back button";
+
+ public TestCaseTwoLayerButton()
+ {
+ Add(new BackButton());
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseUserPanel.cs b/osu.Desktop.Tests/Visual/TestCaseUserPanel.cs
similarity index 89%
rename from osu.Desktop.VisualTests/Tests/TestCaseUserPanel.cs
rename to osu.Desktop.Tests/Visual/TestCaseUserPanel.cs
index 92d58c10c9..b42fd3136d 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseUserPanel.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseUserPanel.cs
@@ -1,22 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Framework.Testing;
using osu.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";
- public override void Reset()
+ public TestCaseUserPanel()
{
- base.Reset();
-
UserPanel flyte;
UserPanel peppy;
Add(new FillFlowContainer
diff --git a/osu.Desktop.Tests/Visual/TestCaseUserProfile.cs b/osu.Desktop.Tests/Visual/TestCaseUserProfile.cs
new file mode 100644
index 0000000000..e5955441dc
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseUserProfile.cs
@@ -0,0 +1,63 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Linq;
+using osu.Game.Overlays;
+using osu.Game.Users;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseUserProfile : OsuTestCase
+ {
+ public override string Description => "Tests user's profile page.";
+
+ public TestCaseUserProfile()
+ {
+ var profile = new UserProfileOverlay();
+ Add(profile);
+
+ AddStep("Show offline dummy", () => profile.ShowUser(new User
+ {
+ Username = @"Somebody",
+ Id = 1,
+ Country = new Country { FullName = @"Alien" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
+ JoinDate = DateTimeOffset.Now.AddDays(-1),
+ LastVisit = DateTimeOffset.Now,
+ Age = 1,
+ ProfileOrder = new[] { "me" },
+ CountryRank = 1,
+ Statistics = new UserStatistics
+ {
+ Rank = 2148,
+ PP = 4567.89m
+ },
+ AllRankHistories = new User.RankHistories
+ {
+ Osu = new User.RankHistory
+ {
+ Mode = @"osu",
+ Data = Enumerable.Range(2345,45).Concat(Enumerable.Range(2109,40)).ToArray()
+ }
+ }
+ }, false));
+ AddStep("Show ppy", () => profile.ShowUser(new User
+ {
+ Username = @"peppy",
+ Id = 2,
+ Country = new Country { FullName = @"Australia", FlagName = @"AU" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
+ }));
+ AddStep("Show flyte", () => profile.ShowUser(new User
+ {
+ Username = @"flyte",
+ Id = 3103765,
+ Country = new Country { FullName = @"Japan", FlagName = @"JP" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
+ }));
+ AddStep("Hide", profile.Hide);
+ AddStep("Show without reload", profile.Show);
+ }
+ }
+}
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/app.config b/osu.Desktop.Tests/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Desktop.Tests/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Desktop.Tests/osu.Desktop.Tests.csproj b/osu.Desktop.Tests/osu.Desktop.Tests.csproj
index f0620c98ef..24d112a45c 100644
--- a/osu.Desktop.Tests/osu.Desktop.Tests.csproj
+++ b/osu.Desktop.Tests/osu.Desktop.Tests.csproj
@@ -37,8 +37,13 @@
$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
-
- $(SolutionDir)\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll
+
+ $(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll
+ True
+
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ True
False
@@ -54,15 +59,68 @@
$(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
@@ -71,10 +129,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
@@ -100,6 +154,7 @@
osu.licenseheader
+
diff --git a/osu.Desktop.Tests/packages.config b/osu.Desktop.Tests/packages.config
index ad51a60195..ed487e5cd5 100644
--- a/osu.Desktop.Tests/packages.config
+++ b/osu.Desktop.Tests/packages.config
@@ -5,7 +5,8 @@ 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/TestCaseDrawableRoom.cs b/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs
deleted file mode 100644
index de58323abe..0000000000
--- a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics;
-using osu.Framework.Testing;
-using osu.Game.Screens.Multiplayer;
-using osu.Game.Online.Multiplayer;
-using osu.Game.Users;
-using osu.Game.Database;
-
-namespace osu.Desktop.VisualTests.Tests
-{
- internal class TestCaseDrawableRoom : TestCase
- {
- public override string Description => @"Select your favourite room";
-
- public override void Reset()
- {
- base.Reset();
-
- DrawableRoom first;
- DrawableRoom second;
- Add(new FillFlowContainer
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- AutoSizeAxes = Axes.Y,
- Width = 500f,
- Direction = FillDirection.Vertical,
- Children = new Drawable[]
- {
- first = new DrawableRoom(new Room()),
- second = new DrawableRoom(new Room()),
- }
- });
-
- first.Room.Name.Value = @"Great Room Right Here";
- first.Room.Host.Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" }};
- first.Room.Status.Value = new RoomStatusOpen();
- first.Room.Beatmap.Value = new BeatmapMetadata { Title = @"Seiryu", Artist = @"Critical Crystal" };
-
- second.Room.Name.Value = @"Relax It's The Weekend";
- second.Room.Host.Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" }};
- second.Room.Status.Value = new RoomStatusPlaying();
- second.Room.Beatmap.Value = new BeatmapMetadata { Title = @"ZAQ", Artist = @"Serendipity" };
-
- AddStep(@"change state", () =>
- {
- first.Room.Status.Value = new RoomStatusPlaying();
- });
-
- AddStep(@"change name", () =>
- {
- first.Room.Name.Value = @"I Changed Name";
- });
-
- AddStep(@"change host", () =>
- {
- first.Room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } };
- });
-
- AddStep(@"change beatmap", () =>
- {
- first.Room.Beatmap.Value = null;
- });
-
- AddStep(@"change state", () =>
- {
- first.Room.Status.Value = new RoomStatusOpen();
- });
- }
- }
-}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs b/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs
deleted file mode 100644
index b1b9ddbcda..0000000000
--- a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs
+++ /dev/null
@@ -1,104 +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.Framework.Graphics;
-using OpenTK.Input;
-using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Configuration;
-using osu.Framework.Graphics.Containers;
-using OpenTK;
-using OpenTK.Graphics;
-using osu.Framework.MathUtils;
-using osu.Framework.Graphics.Sprites;
-using osu.Game.Screens.Play;
-
-namespace osu.Desktop.VisualTests.Tests
-{
- internal class TestCaseKeyCounter : TestCase
- {
- public override string Description => @"Tests key counter";
-
- public override void Reset()
- {
- base.Reset();
-
- KeyCounterCollection kc = new KeyCounterCollection
- {
- Origin = Anchor.Centre,
- Anchor = Anchor.Centre,
- IsCounting = true,
- Children = new KeyCounter[]
- {
- new KeyCounterKeyboard(Key.Z),
- new KeyCounterKeyboard(Key.X),
- new KeyCounterMouse(MouseButton.Left),
- new KeyCounterMouse(MouseButton.Right),
- },
- };
- BindableInt bindable = new BindableInt { MinValue = 0, MaxValue = 200, Default = 50 };
- bindable.ValueChanged += delegate { kc.FadeTime = bindable.Value; };
- AddStep("Add Random", () =>
- {
- Key key = (Key)((int)Key.A + RNG.Next(26));
- kc.Add(new KeyCounterKeyboard(key));
- });
-
- TestSliderBar sliderBar;
-
- Add(new Container
- {
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- AutoSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- new SpriteText { Text = "FadeTime" },
- sliderBar =new TestSliderBar
- {
- Width = 150,
- Height = 10,
- SelectionColor = Color4.Orange,
- }
- }
- });
-
- sliderBar.Current.BindTo(bindable);
-
- Add(kc);
- }
- private class TestSliderBar : SliderBar where T : struct
- {
- public Color4 Color
- {
- get { return Box.Colour; }
- set { Box.Colour = value; }
- }
-
- public Color4 SelectionColor
- {
- get { return SelectionBox.Colour; }
- set { SelectionBox.Colour = value; }
- }
-
- protected readonly Box SelectionBox;
- protected readonly Box Box;
-
- public TestSliderBar()
- {
- Children = new Drawable[]
- {
- Box = new Box { RelativeSizeAxes = Axes.Both },
- SelectionBox = new Box { RelativeSizeAxes = Axes.Both }
- };
- }
-
- protected override void UpdateValue(float value)
- {
- SelectionBox.ScaleTo(
- new Vector2(value, 1),
- 300, EasingTypes.OutQuint);
- }
- }
- }
-}
diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs
deleted file mode 100644
index f28cdd6a7e..0000000000
--- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System.Collections.Generic;
-using System.Linq;
-using osu.Framework.Allocation;
-using osu.Framework.Testing;
-using osu.Game.Beatmaps;
-using OpenTK;
-using osu.Framework.Graphics.Sprites;
-using osu.Game.Database;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Screens.Play;
-using OpenTK.Graphics;
-using osu.Desktop.VisualTests.Beatmaps;
-using osu.Game.Rulesets.Osu.UI;
-
-namespace osu.Desktop.VisualTests.Tests
-{
- internal class TestCasePlayer : TestCase
- {
- protected Player Player;
- private BeatmapDatabase db;
- private RulesetDatabase rulesets;
-
- public override string Description => @"Showing everything to play the game.";
-
- [BackgroundDependencyLoader]
- private void load(BeatmapDatabase db, RulesetDatabase rulesets)
- {
- this.rulesets = rulesets;
- this.db = db;
- }
-
- public override void Reset()
- {
- base.Reset();
-
- WorkingBeatmap beatmap = null;
-
- var beatmapInfo = db.Query().FirstOrDefault(b => b.RulesetID == 0);
- if (beatmapInfo != null)
- beatmap = db.GetWorkingBeatmap(beatmapInfo);
-
- if (beatmap?.Track == null)
- {
- var objects = new List();
-
- int time = 1500;
- for (int i = 0; i < 50; i++)
- {
- objects.Add(new HitCircle
- {
- StartTime = time,
- Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : OsuPlayfield.BASE_SIZE.X,
- i % 4 < 2 ? 0 : OsuPlayfield.BASE_SIZE.Y),
- NewCombo = i % 4 == 0
- });
-
- time += 500;
- }
-
- Beatmap b = new Beatmap
- {
- HitObjects = objects,
- BeatmapInfo = new BeatmapInfo
- {
- Difficulty = new BeatmapDifficulty(),
- Ruleset = rulesets.Query().First(),
- Metadata = new BeatmapMetadata
- {
- Artist = @"Unknown",
- Title = @"Sample Beatmap",
- Author = @"peppy",
- }
- }
- };
-
- beatmap = new TestWorkingBeatmap(b);
- }
-
- Add(new Box
- {
- RelativeSizeAxes = Framework.Graphics.Axes.Both,
- Colour = Color4.Black,
- });
-
- Add(Player = CreatePlayer(beatmap));
- }
-
- protected virtual Player CreatePlayer(WorkingBeatmap beatmap)
- {
- return new Player
- {
- Beatmap = beatmap
- };
- }
- }
-}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSettings.cs b/osu.Desktop.VisualTests/Tests/TestCaseSettings.cs
deleted file mode 100644
index 660085e558..0000000000
--- a/osu.Desktop.VisualTests/Tests/TestCaseSettings.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Testing;
-using osu.Game.Overlays;
-
-namespace osu.Desktop.VisualTests.Tests
-{
- internal class TestCaseSettings : TestCase
- {
- public override string Description => @"Tests the settings overlay";
-
- private SettingsOverlay settings;
-
- public override void Reset()
- {
- base.Reset();
-
- Children = new[] { settings = new SettingsOverlay() };
- settings.ToggleVisibility();
- }
- }
-}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs
deleted file mode 100644
index d769071bd9..0000000000
--- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs
+++ /dev/null
@@ -1,121 +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 override void Reset()
- {
- base.Reset();
-
- 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/Tests/TestCaseTwoLayerButton.cs b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs
deleted file mode 100644
index ba17cfc3d8..0000000000
--- a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.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 osu.Framework.Testing;
-using osu.Game.Graphics.UserInterface;
-using osu.Game.Screens.Play;
-
-namespace osu.Desktop.VisualTests.Tests
-{
- internal class TestCaseTwoLayerButton : TestCase
- {
- public override string Description => @"Back and skip and what not";
-
- public override void Reset()
- {
- base.Reset();
-
- Add(new BackButton());
- Add(new SkipButton(Clock.CurrentTime + 5000));
- }
- }
-}
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 850c38cc24..8bba59207f 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}
@@ -87,11 +87,13 @@
$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
-
- $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ True
-
- $(SolutionDir)\packages\SharpCompress.0.15.2\lib\net45\SharpCompress.dll
+
+ $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll
+ True
False
@@ -183,46 +185,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -247,4 +211,4 @@
-
\ No newline at end of file
+
diff --git a/osu.Desktop.VisualTests/packages.config b/osu.Desktop.VisualTests/packages.config
index cad2ffff0d..2fb1023253 100644
--- a/osu.Desktop.VisualTests/packages.config
+++ b/osu.Desktop.VisualTests/packages.config
@@ -5,8 +5,8 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-->
-
-
+
+
diff --git a/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs b/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs
deleted file mode 100644
index 8772fc9f28..0000000000
--- a/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System.IO;
-using System.Linq;
-using osu.Game.Beatmaps.IO;
-
-namespace osu.Desktop.Beatmaps.IO
-{
- ///
- /// Reads an extracted legacy beatmap from disk.
- ///
- public class LegacyFilesystemReader : ArchiveReader
- {
- public static void Register() => AddReader((storage, path) => Directory.Exists(path));
-
- private readonly string basePath;
-
- public LegacyFilesystemReader(string path)
- {
- basePath = path;
-
- BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(Path.GetFileName).ToArray();
-
- if (BeatmapFilenames.Length == 0)
- throw new FileNotFoundException(@"This directory contains no beatmaps");
-
- StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(Path.GetFileName).FirstOrDefault();
- }
-
- public override Stream GetStream(string name)
- {
- return File.OpenRead(Path.Combine(basePath, name));
- }
-
- public override void Dispose()
- {
- // no-op
- }
-
- public override Stream GetUnderlyingStream() => null;
- }
-}
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index 299f64d998..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,8 @@ 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;
namespace osu.Desktop
@@ -22,18 +25,74 @@ namespace osu.Desktop
public OsuGameDesktop(string[] args = null)
: base(args)
{
- versionManager = new VersionManager { Depth = int.MinValue };
+ versionManager = new VersionManager
+ {
+ Depth = int.MinValue,
+ State = Visibility.Hidden
+ };
+ }
+
+ 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();
- LoadComponentAsync(versionManager);
+ LoadComponentAsync(versionManager, Add);
ScreenChanged += s =>
{
- if (!versionManager.IsAlive && s is Intro)
- Add(versionManager);
+ if (!versionManager.IsPresent && s is Intro)
+ versionManager.State = Visibility.Visible;
};
}
@@ -45,7 +104,7 @@ namespace osu.Desktop
{
desktopWindow.CursorState |= CursorState.Hidden;
- desktopWindow.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
+ desktopWindow.Icon = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
desktopWindow.Title = Name;
desktopWindow.DragEnter += dragEnter;
@@ -60,11 +119,11 @@ namespace osu.Desktop
var filePaths = dropData.Select(f => f.ToString()).ToArray();
if (filePaths.All(f => Path.GetExtension(f) == @".osz"))
- Task.Run(() => BeatmapDatabase.Import(filePaths));
+ Task.Run(() => BeatmapManager.Import(filePaths));
else if (filePaths.All(f => Path.GetExtension(f) == @".osr"))
Task.Run(() =>
{
- var score = ScoreDatabase.ReadReplayFile(filePaths.First());
+ var score = ScoreStore.ReadReplayFile(filePaths.First());
Schedule(() => LoadScore(score));
});
}
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 9532652bfe..b445340f50 100644
--- a/osu.Desktop/Overlays/VersionManager.cs
+++ b/osu.Desktop/Overlays/VersionManager.cs
@@ -10,6 +10,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using Squirrel;
+using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
@@ -24,16 +25,14 @@ namespace osu.Desktop.Overlays
public class VersionManager : OverlayContainer
{
private UpdateManager updateManager;
- private NotificationManager notificationManager;
-
- protected override bool HideOnEscape => false;
+ private NotificationOverlay notificationOverlay;
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;
@@ -92,12 +91,6 @@ namespace osu.Desktop.Overlays
checkForUpdateAsync();
}
- protected override void LoadComplete()
- {
- base.LoadComplete();
- State = Visibility.Visible;
- }
-
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
@@ -121,7 +114,7 @@ namespace osu.Desktop.Overlays
if (notification == null)
{
notification = new UpdateProgressNotification { State = ProgressNotificationState.Active };
- Schedule(() => notificationManager.Post(notification));
+ Schedule(() => notificationOverlay.Post(notification));
}
Schedule(() =>
@@ -180,7 +173,7 @@ namespace osu.Desktop.Overlays
protected override void PopIn()
{
- FadeIn(1000);
+ this.FadeIn(1000);
}
protected override void PopOut()
@@ -191,7 +184,7 @@ namespace osu.Desktop.Overlays
{
private OsuGame game;
- protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification()
+ protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification
{
Text = @"Update ready to install. Click to restart!",
Activated = () =>
@@ -207,20 +200,20 @@ namespace osu.Desktop.Overlays
{
this.game = game;
- IconContent.Add(new Drawable[]
+ IconContent.AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
- ColourInfo = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow)
+ 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 210f780078..1fab92e020 100644
--- a/osu.Desktop/Program.cs
+++ b/osu.Desktop/Program.cs
@@ -3,7 +3,7 @@
using System;
using System.IO;
-using osu.Desktop.Beatmaps.IO;
+using System.Linq;
using osu.Framework.Desktop;
using osu.Framework.Desktop.Platform;
using osu.Game.IPC;
@@ -15,8 +15,6 @@ namespace osu.Desktop
[STAThread]
public static int Main(string[] args)
{
- LegacyFilesystemReader.Register();
-
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
@@ -36,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 4f66dfd3eb..bbca4145c6 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -90,6 +90,21 @@
Properties\app.manifest
+
+ true
+ bin\Debug\
+ DEBUG
+ true
+ 0
+ true
+ full
+ AnyCPU
+ false
+ 6
+ prompt
+ AllRules.ruleset
+ --tests
+
$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.dll
@@ -121,18 +136,23 @@
- $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll
+ $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll
True
-
- $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ True
+
+
+ $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll
+ True
$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll
True
-
- $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\Squirrel.dll
+
+ $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll
True
@@ -190,6 +210,10 @@
{65dc628f-a640-4111-ab35-3a5652bc1e17}
osu.Framework.Desktop
+
+ {007B2356-AB6F-4BD9-96D5-116FC2DCE69A}
+ osu.Framework.Testing
+
{c76bf5b3-985e-4d39-95fe-97c9c879b83a}
osu.Framework
@@ -198,6 +222,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
@@ -221,13 +249,13 @@
+
-
-
+
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index 0ed2a0ba6f..be670936fd 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -10,7 +10,6 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
using osu.Game.Rulesets.Mania.MathUtils;
-using osu.Game.Database;
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
using OpenTK;
using osu.Game.Audio;
@@ -29,12 +28,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private Pattern lastPattern = new Pattern();
private FastRandom random;
private Beatmap beatmap;
- private bool isForCurrentRuleset;
- protected override Beatmap ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
+ private readonly int availableColumns;
+ private readonly bool isForCurrentRuleset;
+
+ public ManiaBeatmapConverter(bool isForCurrentRuleset, int availableColumns)
{
- this.isForCurrentRuleset = isForCurrentRuleset;
+ if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
+ this.isForCurrentRuleset = isForCurrentRuleset;
+ this.availableColumns = availableColumns;
+ }
+
+ protected override Beatmap ConvertBeatmap(Beatmap original)
+ {
beatmap = original;
BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty;
@@ -42,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
random = new FastRandom(seed);
- return base.ConvertBeatmap(original, isForCurrentRuleset);
+ return base.ConvertBeatmap(original);
}
protected override IEnumerable ConvertHitObject(HitObject original, Beatmap beatmap)
@@ -90,7 +97,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// The hit objects generated.
private IEnumerable generateSpecific(HitObject original)
{
- var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern);
+ var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
Pattern newPattern = generator.Generate();
lastPattern = newPattern;
@@ -114,14 +121,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
Patterns.PatternGenerator conversion = null;
if (distanceData != null)
- conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern);
+ conversion = new DistanceObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
else if (endTimeData != null)
- conversion = new EndTimeObjectPatternGenerator(random, original, beatmap);
+ conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, availableColumns);
else if (positionData != null)
{
computeDensity(original.StartTime);
- conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair);
+ conversion = new HitObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern, lastTime, lastPosition, density, lastStair);
recordNote(original.StartTime, positionData.Position);
}
@@ -143,8 +150,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
///
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{
- public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
- : base(random, hitObject, beatmap, previousPattern)
+ public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
+ : base(random, hitObject, beatmap, availableColumns, previousPattern)
{
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index 2d1f75e196..20966a75f7 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -29,8 +29,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private PatternType convertType;
- public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
- : base(random, hitObject, beatmap, previousPattern)
+ public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
+ : base(random, hitObject, beatmap, availableColumns, previousPattern)
{
convertType = PatternType.None;
if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
@@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// The true distance, accounting for any repeats
double distance = (distanceData?.Distance ?? 0) * repeatCount;
// The velocity of the osu! hit object - calculated as the velocity of a slider
- double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier / (timingPoint.BeatLength * difficultyPoint.SpeedMultiplier);
+ double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
// The duration of the osu! hit object
double osuDuration = distance / osuVelocity;
@@ -448,7 +448,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return curveData.RepeatSamples[index];
}
-
///
/// Constructs and adds a note to a pattern.
///
@@ -480,7 +479,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
Tail = { Samples = sampleInfoListAt(endTime) }
};
-
newObject = holdNote;
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
index 6ad7489e0f..c353496410 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
@@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
private readonly double endTime;
- public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap)
- : base(random, hitObject, beatmap, new Pattern())
+ public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns)
+ : base(random, hitObject, beatmap, availableColumns, new Pattern())
{
var endtimeData = HitObject as IHasEndTime;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
index b1ba99d98b..077b926635 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
@@ -20,9 +20,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType;
- public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
- : base(random, hitObject, beatmap, previousPattern)
+ public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
+ : base(random, hitObject, beatmap, availableColumns, previousPattern)
{
+ if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
+ if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
+
StairType = lastStair;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
@@ -33,12 +36,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
float positionSeparation = ((positionData?.Position ?? Vector2.Zero) - previousPosition).Length;
double timeSeparation = hitObject.StartTime - previousTime;
- if (timeSeparation <= 125)
- {
- // More than 120 BPM
- convertType |= PatternType.ForceNotStack;
- }
-
if (timeSeparation <= 80)
{
// More than 187 BPM
@@ -64,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// More than 111 BPM stream
convertType |= PatternType.Cycle | PatternType.KeepSingle;
}
- else if (timeSeparation <= 150 & positionSeparation < 20)
+ else if (timeSeparation <= 150 && positionSeparation < 20)
{
// More than 100 BPM stream
convertType |= PatternType.ForceStack | PatternType.LowProbability;
@@ -401,4 +398,4 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
});
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
index e6e3f1d07f..a3173f9784 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
@@ -4,7 +4,6 @@
using System;
using System.Linq;
using osu.Game.Beatmaps;
-using osu.Game.Database;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects;
using OpenTK;
@@ -26,11 +25,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
protected readonly FastRandom Random;
- protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
- : base(hitObject, beatmap, previousPattern)
+ protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
+ : base(hitObject, beatmap, availableColumns, previousPattern)
{
- Random = random;
+ if (random == null) throw new ArgumentNullException(nameof(random));
+ if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
+ if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
+ if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
+ Random = random;
RandomStart = AvailableColumns == 8 ? 1 : 0;
}
@@ -63,6 +66,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// The amount of notes to be generated.
protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0)
{
+ if (p2 < 0 || p2 > 1) throw new ArgumentOutOfRangeException(nameof(p2));
+ if (p3 < 0 || p3 > 1) throw new ArgumentOutOfRangeException(nameof(p3));
+ if (p4 < 0 || p4 > 1) throw new ArgumentOutOfRangeException(nameof(p4));
+ if (p5 < 0 || p5 > 1) throw new ArgumentOutOfRangeException(nameof(p5));
+ if (p6 < 0 || p6 > 1) throw new ArgumentOutOfRangeException(nameof(p6));
+
double val = Random.NextDouble();
if (val >= 1 - p6)
return 6;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
index dda4d07182..ef321232c8 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
@@ -32,13 +32,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
///
protected readonly Beatmap Beatmap;
- protected PatternGenerator(HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
+ protected PatternGenerator(HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
{
- PreviousPattern = previousPattern;
+ if (hitObject == null) throw new ArgumentNullException(nameof(hitObject));
+ if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
+ if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
+ if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
+
HitObject = hitObject;
Beatmap = beatmap;
-
- AvailableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize);
+ AvailableColumns = availableColumns;
+ PreviousPattern = previousPattern;
}
///
diff --git a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs
index 674d83f6f2..52b55a4ff5 100644
--- a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs
@@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Database;
+using osu.Game.Beatmaps;
namespace osu.Game.Rulesets.Mania.Judgements
{
diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs
index aaba4d94f0..1f01750f44 100644
--- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs
@@ -6,6 +6,7 @@ using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using System.Collections.Generic;
+using System;
namespace osu.Game.Rulesets.Mania
{
@@ -21,6 +22,6 @@ namespace osu.Game.Rulesets.Mania
return 0;
}
- protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter();
+ protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize)));
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Mania/ManiaInputManager.cs b/osu.Game.Rulesets.Mania/ManiaInputManager.cs
new file mode 100644
index 0000000000..b608e4d8d6
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/ManiaInputManager.cs
@@ -0,0 +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.Input.Bindings;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Rulesets.Mania
+{
+ public class ManiaInputManager : RulesetInputManager
+ {
+ public ManiaInputManager(RulesetInfo ruleset)
+ : base(ruleset, 0, SimultaneousBindingMode.Unique)
+ {
+ }
+ }
+
+ public enum ManiaAction
+ {
+ // placeholder
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 30d1846746..c7809f83ed 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -2,13 +2,13 @@
// 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;
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania
{
public class ManiaRuleset : Ruleset
{
- public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaHitRenderer(beatmap, isForCurrentRuleset);
+ public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset);
public override IEnumerable GetModsFor(ModType type)
{
@@ -27,7 +27,14 @@ namespace osu.Game.Rulesets.Mania
{
new ManiaModEasy(),
new ManiaModNoFail(),
- new ManiaModHalfTime(),
+ new MultiMod
+ {
+ Mods = new Mod[]
+ {
+ new ManiaModHalfTime(),
+ new ManiaModDaycore(),
+ },
+ },
};
case ModType.DifficultyIncrease:
@@ -89,6 +96,7 @@ namespace osu.Game.Rulesets.Mania
new ModCinema(),
},
},
+ new ManiaModGravity()
};
default:
@@ -96,16 +104,21 @@ 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 IEnumerable CreateGameplayKeys() => new KeyCounter[] { /* Todo: Should be keymod specific */ };
+ public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap);
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor();
public override int LegacyID => 3;
+
+ public ManiaRuleset(RulesetInfo rulesetInfo)
+ : base(rulesetInfo)
+ {
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs
index ff3fd8e4b7..f31873d1c8 100644
--- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs
+++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs
@@ -87,6 +87,5 @@ namespace osu.Game.Rulesets.Mania.MathUtils
bitIndex++;
return ((bitBuffer >>= 1) & 1) == 1;
}
-
}
}
diff --git a/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs b/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs
new file mode 100644
index 0000000000..954ee3f481
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs
@@ -0,0 +1,23 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.Timing;
+
+namespace osu.Game.Rulesets.Mania.Mods
+{
+ ///
+ /// A type of mod which generates speed adjustments that scroll the hit objects and bar lines.
+ ///
+ internal interface IGenerateSpeedAdjustments
+ {
+ ///
+ /// Applies this mod to a hit renderer.
+ ///
+ /// The hit renderer to apply to.
+ /// The per-column list of speed adjustments for hit objects.
+ /// The list of speed adjustments for bar lines.
+ void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges);
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
index b402d3a010..f44ad6fd60 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
@@ -34,6 +34,11 @@ namespace osu.Game.Rulesets.Mania.Mods
}
+ public class ManiaModDaycore : ModDaycore
+ {
+ public override double ScoreMultiplier => 0.3;
+ }
+
public class ManiaModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.0;
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs
new file mode 100644
index 0000000000..a054e0db56
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs
@@ -0,0 +1,46 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Mania.Timing;
+using osu.Game.Rulesets.Timing;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Mania.Mods
+{
+ public class ManiaModGravity : Mod, IGenerateSpeedAdjustments
+ {
+ public override string Name => "Gravity";
+
+ public override double ScoreMultiplier => 0;
+
+ public override FontAwesome Icon => FontAwesome.fa_sort_desc;
+
+ public void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges)
+ {
+ // We have to generate one speed adjustment per hit object for gravity
+ foreach (ManiaHitObject obj in rulesetContainer.Objects)
+ {
+ MultiplierControlPoint controlPoint = rulesetContainer.CreateControlPointAt(obj.StartTime);
+ // Beat length has too large of an effect for gravity, so we'll force it to a constant value for now
+ controlPoint.TimingPoint.BeatLength = 1000;
+
+ hitObjectTimingChanges[obj.Column].Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity));
+ }
+
+ // Like with hit objects, we need to generate one speed adjustment per bar line
+ foreach (DrawableBarLine barLine in rulesetContainer.BarLines)
+ {
+ var controlPoint = rulesetContainer.CreateControlPointAt(barLine.HitObject.StartTime);
+ // Beat length has too large of an effect for gravity, so we'll force it to a constant value for now
+ controlPoint.TimingPoint.BeatLength = 1000;
+
+ barlineTimingChanges.Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
index 0b4d8b2d4e..fc5ea4e116 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
@@ -3,7 +3,7 @@
using OpenTK;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index 5d7f3314cd..17b0b0a607 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeSizeAxes = Axes.Both;
Height = (float)HitObject.Duration;
- Add(new Drawable[]
+ AddRange(new Drawable[]
{
// For now the body piece covers the entire height of the container
// whereas possibly in the future we don't want to extend under the head/tail.
@@ -55,7 +55,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
tickContainer = new Container
{
RelativeSizeAxes = Axes.Both,
- RelativeCoordinateSpace = new Vector2(1, (float)HitObject.Duration)
+ RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
+ RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
},
head = new DrawableHeadNote(this, key)
{
@@ -76,9 +77,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
HoldStartTime = () => holdStartTime
};
- // To make the ticks relative to ourselves we need to offset them backwards
- drawableTick.Y -= (float)HitObject.StartTime;
-
tickContainer.Add(drawableTick);
AddNested(drawableTick);
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
index 9ecc77d3fc..39abbb6b3d 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
@@ -7,9 +7,9 @@ using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
@@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
base.AccentColour = value;
- glowContainer.EdgeEffect = new EdgeEffect
+ glowContainer.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = 2f,
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
index 4e276fddb7..10dc607ec3 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
@@ -4,13 +4,12 @@
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Configuration;
-using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
- public abstract class DrawableManiaHitObject : DrawableHitObject
+ public abstract class DrawableManiaHitObject : DrawableScrollingHitObject
where TObject : ManiaHitObject
{
///
@@ -27,9 +26,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (key != null)
Key.BindTo(key);
-
- RelativePositionAxes = Axes.Y;
- Y = (float)HitObject.StartTime;
}
public override Color4 AccentColour
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 658d409bb8..9322fed3eb 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public DrawableNote(Note hitObject, Bindable key = null)
: base(hitObject, key)
{
- RelativeSizeAxes = Axes.Both;
+ RelativeSizeAxes = Axes.X;
Height = 100;
Add(headPiece = new NotePiece
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
index c10aa9994b..04e8df4ae2 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
@@ -4,7 +4,7 @@
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
index e01199e929..3df085c346 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
@@ -5,7 +5,7 @@ using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
index fa32d46a88..c3a29b39a8 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
@@ -2,8 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Database;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Mania.Objects
diff --git a/osu.Game.Rulesets.Mania/Objects/Note.cs b/osu.Game.Rulesets.Mania/Objects/Note.cs
index 6c0cacd277..3c4ff4216f 100644
--- a/osu.Game.Rulesets.Mania/Objects/Note.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Note.cs
@@ -1,8 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Database;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.Objects
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 798d4b8c5b..63b443319f 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -4,7 +4,6 @@
using System;
using System.Linq;
using osu.Game.Beatmaps;
-using osu.Game.Database;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Drawables;
@@ -156,8 +155,8 @@ namespace osu.Game.Rulesets.Mania.Scoring
{
}
- public ManiaScoreProcessor(HitRenderer hitRenderer)
- : base(hitRenderer)
+ public ManiaScoreProcessor(RulesetContainer rulesetContainer)
+ : base(rulesetContainer)
{
}
diff --git a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs b/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs
deleted file mode 100644
index 0a8bc2d44a..0000000000
--- a/osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using OpenTK;
-using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Rulesets.Objects;
-
-namespace osu.Game.Rulesets.Mania.Timing
-{
- ///
- /// A container in which added drawables are put into a relative coordinate space spanned by a length of time.
- ///
- /// This container contains s which scroll inside this container.
- /// Drawables added to this container are moved inside the relevant ,
- /// and as such, will scroll along with the s.
- ///
- ///
- public class ControlPointContainer : Container
- {
- ///
- /// The amount of time which this container spans.
- ///
- public double TimeSpan { get; set; }
-
- private readonly List drawableControlPoints;
-
- public ControlPointContainer(IEnumerable timingChanges)
- {
- drawableControlPoints = timingChanges.Select(t => new DrawableControlPoint(t)).ToList();
- Children = drawableControlPoints;
- }
-
- ///
- /// Adds a drawable to this container. Note that the drawable added must have its Y-position be
- /// an absolute unit of time that is _not_ relative to .
- ///
- /// The drawable to add.
- public override void Add(Drawable drawable)
- {
- // Always add timing sections to ourselves
- if (drawable is DrawableControlPoint)
- {
- base.Add(drawable);
- return;
- }
-
- var controlPoint = drawableControlPoints.LastOrDefault(t => t.CanContain(drawable)) ?? drawableControlPoints.FirstOrDefault();
-
- if (controlPoint == null)
- throw new InvalidOperationException("Could not find suitable timing section to add object to.");
-
- controlPoint.Add(drawable);
- }
-
- ///
- /// A container that contains drawables within the time span of a timing section.
- ///
- /// The content of this container will scroll relative to the current time.
- ///
- ///
- private class DrawableControlPoint : Container
- {
- private readonly TimingChange timingChange;
-
- protected override Container Content => content;
- private readonly Container content;
-
- ///
- /// Creates a drawable control point. The height of this container will be proportional
- /// to the beat length of the control point it is initialized with such that, e.g. a beat length
- /// of 500ms results in this container being twice as high as its parent, which further means that
- /// the content container will scroll at twice the normal rate.
- ///
- /// The control point to create the drawable control point for.
- public DrawableControlPoint(TimingChange timingChange)
- {
- this.timingChange = timingChange;
-
- RelativeSizeAxes = Axes.Both;
-
- AddInternal(content = new AutoTimeRelativeContainer
- {
- RelativeSizeAxes = Axes.Both,
- RelativePositionAxes = Axes.Both,
- Y = (float)timingChange.Time
- });
- }
-
- protected override void Update()
- {
- var parent = (ControlPointContainer)Parent;
-
- // Adjust our height to account for the speed changes
- Height = (float)(1000 / timingChange.BeatLength / timingChange.SpeedMultiplier);
- RelativeCoordinateSpace = new Vector2(1, (float)parent.TimeSpan);
-
- // Scroll the content
- content.Y = (float)(timingChange.Time - Time.Current);
- }
-
- public override void Add(Drawable drawable)
- {
- // The previously relatively-positioned drawable will now become relative to content, but since the drawable has no knowledge of content,
- // we need to offset it back by content's position position so that it becomes correctly relatively-positioned to content
- // This can be removed if hit objects were stored such that either their StartTime or their "beat offset" was relative to the timing change
- // they belonged to, but this requires a radical change to the beatmap format which we're not ready to do just yet
- drawable.Y -= (float)timingChange.Time;
-
- base.Add(drawable);
- }
-
- ///
- /// Whether this control point can contain a drawable. This control point can contain a drawable if the drawable is positioned "after" this control point.
- ///
- /// The drawable to check.
- public bool CanContain(Drawable drawable) => content.Y <= drawable.Y;
-
- ///
- /// A container which always keeps its height and relative coordinate space "auto-sized" to its children.
- ///
- /// This is used in the case where children are relatively positioned/sized to time values (e.g. notes/bar lines) to keep
- /// such children wrapped inside a container, otherwise they would disappear due to container flattening.
- ///
- ///
- private class AutoTimeRelativeContainer : Container
- {
- protected override IComparer DepthComparer => new HitObjectReverseStartTimeComparer();
-
- public override void InvalidateFromChild(Invalidation invalidation)
- {
- // We only want to re-compute our size when a child's size or position has changed
- if ((invalidation & Invalidation.Geometry) == 0)
- {
- base.InvalidateFromChild(invalidation);
- return;
- }
-
- if (!Children.Any())
- return;
-
- float height = Children.Select(child => child.Y + child.Height).Max();
-
- Height = height;
- RelativeCoordinateSpace = new Vector2(1, height);
-
- base.InvalidateFromChild(invalidation);
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Timing/GravityScrollingContainer.cs b/osu.Game.Rulesets.Mania/Timing/GravityScrollingContainer.cs
new file mode 100644
index 0000000000..699acc477b
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Timing/GravityScrollingContainer.cs
@@ -0,0 +1,60 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Timing;
+
+namespace osu.Game.Rulesets.Mania.Timing
+{
+ ///
+ /// A that emulates a form of gravity where hit objects speed up over time.
+ ///
+ internal class GravityScrollingContainer : ScrollingContainer
+ {
+ private readonly MultiplierControlPoint controlPoint;
+
+ public GravityScrollingContainer(MultiplierControlPoint controlPoint)
+ {
+ this.controlPoint = controlPoint;
+ }
+
+ protected override void UpdateAfterChildren()
+ {
+ base.UpdateAfterChildren();
+
+ // The gravity-adjusted start position
+ float startPos = (float)computeGravityTime(controlPoint.StartTime);
+ // The gravity-adjusted end position
+ float endPos = (float)computeGravityTime(controlPoint.StartTime + RelativeChildSize.Y);
+
+ Y = startPos;
+ Height = endPos - startPos;
+ }
+
+ ///
+ /// Applies gravity to a time value based on the current time.
+ ///
+ /// The time value gravity should be applied to.
+ /// The time after gravity is applied to .
+ private double computeGravityTime(double time)
+ {
+ double relativeTime = relativeTimeAt(time);
+
+ // The sign of the relative time, this is used to apply backwards acceleration leading into startTime
+ double sign = relativeTime < 0 ? -1 : 1;
+
+ return VisibleTimeRange - acceleration * relativeTime * relativeTime * sign;
+ }
+
+ ///
+ /// The acceleration due to "gravity" of the content of this container.
+ ///
+ private double acceleration => 1 / VisibleTimeRange;
+
+ ///
+ /// Computes the current time relative to , accounting for .
+ ///
+ /// The non-offset time.
+ /// The current time relative to - .
+ private double relativeTimeAt(double time) => Time.Current - time + VisibleTimeRange;
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Timing/ManiaSpeedAdjustmentContainer.cs b/osu.Game.Rulesets.Mania/Timing/ManiaSpeedAdjustmentContainer.cs
new file mode 100644
index 0000000000..321b4ee92b
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Timing/ManiaSpeedAdjustmentContainer.cs
@@ -0,0 +1,29 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Timing;
+
+namespace osu.Game.Rulesets.Mania.Timing
+{
+ public class ManiaSpeedAdjustmentContainer : SpeedAdjustmentContainer
+ {
+ private readonly ScrollingAlgorithm scrollingAlgorithm;
+
+ public ManiaSpeedAdjustmentContainer(MultiplierControlPoint timingSection, ScrollingAlgorithm scrollingAlgorithm)
+ : base(timingSection)
+ {
+ this.scrollingAlgorithm = scrollingAlgorithm;
+ }
+
+ protected override ScrollingContainer CreateScrollingContainer()
+ {
+ switch (scrollingAlgorithm)
+ {
+ default:
+ return base.CreateScrollingContainer();
+ case ScrollingAlgorithm.Gravity:
+ return new GravityScrollingContainer(ControlPoint);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Timing/ScrollingAlgorithm.cs b/osu.Game.Rulesets.Mania/Timing/ScrollingAlgorithm.cs
new file mode 100644
index 0000000000..72e096f5aa
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Timing/ScrollingAlgorithm.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Rulesets.Mania.Timing
+{
+ public enum ScrollingAlgorithm
+ {
+ ///
+ /// Basic scrolling algorithm based on the timing section time. This is the default algorithm.
+ ///
+ Basic,
+ ///
+ /// Emulating a form of gravity where hit objects speed up over time.
+ ///
+ Gravity
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Timing/TimingChange.cs b/osu.Game.Rulesets.Mania/Timing/TimingChange.cs
deleted file mode 100644
index 9153ba6991..0000000000
--- a/osu.Game.Rulesets.Mania/Timing/TimingChange.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-namespace osu.Game.Rulesets.Mania.Timing
-{
- public class TimingChange
- {
- ///
- /// The time at which this timing change happened.
- ///
- public double Time;
-
- ///
- /// The beat length.
- ///
- public double BeatLength = 500;
-
- ///
- /// The speed multiplier.
- ///
- public double SpeedMultiplier = 1;
- }
-}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 6dfd5000d4..9fbc9ba5e7 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -7,21 +7,20 @@ using OpenTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Colour;
using osu.Framework.Input;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Mania.Timing;
-using System.Collections.Generic;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Mania.Objects;
-using osu.Game.Rulesets.Mania.Judgements;
using System;
using osu.Framework.Configuration;
+using osu.Game.Rulesets.UI;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.UI
{
- public class Column : Container, IHasAccentColour
+ public class Column : ScrollingPlayfield, IHasAccentColour
{
private const float key_icon_size = 10;
private const float key_icon_corner_radius = 3;
@@ -42,14 +41,15 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Container hitTargetBar;
private readonly Container keyIcon;
- public readonly ControlPointContainer ControlPointContainer;
+ protected override Container Content => content;
+ private readonly Container content;
- public Column(IEnumerable timingChanges)
+ public Column()
+ : base(Axes.Y)
{
- RelativeSizeAxes = Axes.Y;
Width = column_width;
- Children = new Drawable[]
+ InternalChildren = new Drawable[]
{
background = new Box
{
@@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
Name = "Hit target + hit objects",
RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION},
+ Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION },
Children = new Drawable[]
{
new Container
@@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
},
- ControlPointContainer = new ControlPointContainer(timingChanges)
+ content = new Container
{
Name = "Hit objects",
RelativeSizeAxes = Axes.Both,
@@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
Name = "Key gradient",
RelativeSizeAxes = Axes.Both,
- ColourInfo = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)),
+ Colour = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)),
Alpha = 0.5f
},
keyIcon = new Container
@@ -145,6 +145,8 @@ namespace osu.Game.Rulesets.Mania.UI
};
}
+ public override Axes RelativeSizeAxes => Axes.Y;
+
private bool isSpecial;
public bool IsSpecial
{
@@ -171,14 +173,14 @@ namespace osu.Game.Rulesets.Mania.UI
background.Colour = accentColour;
- hitTargetBar.EdgeEffect = new EdgeEffect
+ hitTargetBar.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Opacity(0.5f),
};
- keyIcon.EdgeEffect = new EdgeEffect
+ keyIcon.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = 5,
@@ -187,10 +189,14 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
- public void Add(DrawableHitObject hitObject)
+ ///
+ /// Adds a DrawableHitObject to this Playfield.
+ ///
+ /// The DrawableHitObject to add.
+ public override void Add(DrawableHitObject hitObject)
{
hitObject.AccentColour = AccentColour;
- ControlPointContainer.Add(hitObject);
+ HitObjects.Add(hitObject);
}
private bool onKeyDown(InputState state, KeyDownEventArgs args)
@@ -200,8 +206,8 @@ namespace osu.Game.Rulesets.Mania.UI
if (args.Key == Key)
{
- background.FadeTo(background.Alpha + 0.2f, 50, EasingTypes.OutQuint);
- keyIcon.ScaleTo(1.4f, 50, EasingTypes.OutQuint);
+ background.FadeTo(background.Alpha + 0.2f, 50, Easing.OutQuint);
+ keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint);
}
return false;
@@ -211,8 +217,8 @@ namespace osu.Game.Rulesets.Mania.UI
{
if (args.Key == Key)
{
- background.FadeTo(0.2f, 800, EasingTypes.OutQuart);
- keyIcon.ScaleTo(1f, 400, EasingTypes.OutQuart);
+ background.FadeTo(0.2f, 800, Easing.OutQuart);
+ keyIcon.ScaleTo(1f, 400, Easing.OutQuart);
}
return false;
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs
deleted file mode 100644
index 57477147d5..0000000000
--- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using OpenTK;
-using OpenTK.Input;
-using osu.Framework.Allocation;
-using osu.Framework.Configuration;
-using osu.Framework.Graphics;
-using osu.Framework.Lists;
-using osu.Framework.MathUtils;
-using osu.Game.Beatmaps;
-using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Rulesets.Beatmaps;
-using osu.Game.Rulesets.Mania.Beatmaps;
-using osu.Game.Rulesets.Mania.Judgements;
-using osu.Game.Rulesets.Mania.Objects;
-using osu.Game.Rulesets.Mania.Objects.Drawables;
-using osu.Game.Rulesets.Mania.Scoring;
-using osu.Game.Rulesets.Mania.Timing;
-using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Objects.Types;
-using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.UI;
-
-namespace osu.Game.Rulesets.Mania.UI
-{
- public class ManiaHitRenderer : HitRenderer
- {
- public int? Columns;
-
- public ManiaHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
- : base(beatmap, isForCurrentRuleset)
- {
- }
-
- protected override Playfield CreatePlayfield()
- {
- double lastSpeedMultiplier = 1;
- double lastBeatLength = 500;
-
- // Merge timing + difficulty points
- var allPoints = new SortedList(Comparer.Default);
- allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
- allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);
-
- // Generate the timing points, making non-timing changes use the previous timing change
- var timingChanges = allPoints.Select(c =>
- {
- var timingPoint = c as TimingControlPoint;
- var difficultyPoint = c as DifficultyControlPoint;
-
- if (timingPoint != null)
- lastBeatLength = timingPoint.BeatLength;
-
- if (difficultyPoint != null)
- lastSpeedMultiplier = difficultyPoint.SpeedMultiplier;
-
- return new TimingChange
- {
- Time = c.Time,
- BeatLength = lastBeatLength,
- SpeedMultiplier = lastSpeedMultiplier
- };
- });
-
- double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
-
- // Perform some post processing of the timing changes
- timingChanges = timingChanges
- // Collapse sections after the last hit object
- .Where(s => s.Time <= lastObjectTime)
- // Collapse sections with the same start time
- .GroupBy(s => s.Time).Select(g => g.Last()).OrderBy(s => s.Time)
- // Collapse sections with the same beat length
- .GroupBy(s => s.BeatLength * s.SpeedMultiplier).Select(g => g.First())
- .ToList();
-
- return new ManiaPlayfield(Columns ?? (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize), timingChanges)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- // Invert by default for now (should be moved to config/skin later)
- Scale = new Vector2(1, -1)
- };
- }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- var maniaPlayfield = (ManiaPlayfield)Playfield;
-
- double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
-
- SortedList timingPoints = Beatmap.ControlPointInfo.TimingPoints;
- for (int i = 0; i < timingPoints.Count; i++)
- {
- TimingControlPoint point = timingPoints[i];
-
- // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
- double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;
-
- int index = 0;
- for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
- {
- maniaPlayfield.Add(new DrawableBarLine(new BarLine
- {
- StartTime = t,
- ControlPoint = point,
- BeatIndex = index
- }));
- }
- }
- }
-
- public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
-
- protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter();
-
- protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h)
- {
- var maniaPlayfield = Playfield as ManiaPlayfield;
- if (maniaPlayfield == null)
- return null;
-
- Bindable key = maniaPlayfield.Columns.ElementAt(h.Column).Key;
-
- var holdNote = h as HoldNote;
- if (holdNote != null)
- return new DrawableHoldNote(holdNote, key);
-
- var note = h as Note;
- if (note != null)
- return new DrawableNote(note, key);
-
- return null;
- }
-
- protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
- }
-}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
index 2e6b63579e..0c9351cad2 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI;
using OpenTK;
@@ -15,25 +14,16 @@ using osu.Framework.Allocation;
using OpenTK.Input;
using System.Linq;
using System.Collections.Generic;
-using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Mania.Timing;
-using osu.Framework.Input;
-using osu.Framework.Graphics.Transforms;
-using osu.Framework.MathUtils;
using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Mania.UI
{
- public class ManiaPlayfield : Playfield
+ public class ManiaPlayfield : ScrollingPlayfield
{
public const float HIT_TARGET_POSITION = 50;
- private const float time_span_default = 5000;
- private const float time_span_min = 10;
- private const float time_span_max = 50000;
- private const float time_span_step = 200;
-
///
/// Default column keys, expanding outwards from the middle as more column are added.
/// E.g. 2 columns use FJ, 4 columns use DFJK, 6 use SDFJKL, etc...
@@ -58,21 +48,23 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly FlowContainer columns;
public IEnumerable Columns => columns.Children;
- private readonly ControlPointContainer barLineContainer;
+ protected override Container Content => content;
+ private readonly Container content;
private List normalColumnColours = new List();
private Color4 specialColumnColour;
private readonly int columnCount;
- public ManiaPlayfield(int columnCount, IEnumerable timingChanges)
+ public ManiaPlayfield(int columnCount)
+ : base(Axes.Y)
{
this.columnCount = columnCount;
if (columnCount <= 0)
throw new ArgumentException("Can't have zero or fewer columns.");
- Children = new Drawable[]
+ InternalChildren = new Drawable[]
{
new Container
{
@@ -116,12 +108,12 @@ namespace osu.Game.Rulesets.Mania.UI
Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
Children = new[]
{
- barLineContainer = new ControlPointContainer(timingChanges)
+ content = new Container
{
Name = "Bar lines",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- RelativeSizeAxes = Axes.Y
+ RelativeSizeAxes = Axes.Y,
// Width is set in the Update method
}
}
@@ -131,9 +123,14 @@ namespace osu.Game.Rulesets.Mania.UI
};
for (int i = 0; i < columnCount; i++)
- columns.Add(new Column(timingChanges));
+ {
+ var c = new Column();
+ c.Reversed.BindTo(Reversed);
+ c.VisibleTimeRange.BindTo(VisibleTimeRange);
- TimeSpan = time_span_default;
+ columns.Add(c);
+ AddNested(c);
+ }
}
[BackgroundDependencyLoader]
@@ -207,79 +204,13 @@ namespace osu.Game.Rulesets.Mania.UI
}
public override void Add(DrawableHitObject h) => Columns.ElementAt(h.HitObject.Column).Add(h);
- public void Add(DrawableBarLine barline) => barLineContainer.Add(barline);
-
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
- {
- if (state.Keyboard.ControlPressed)
- {
- switch (args.Key)
- {
- case Key.Minus:
- transformTimeSpanTo(TimeSpan + time_span_step, 200, EasingTypes.OutQuint);
- break;
- case Key.Plus:
- transformTimeSpanTo(TimeSpan - time_span_step, 200, EasingTypes.OutQuint);
- break;
- }
- }
-
- return false;
- }
-
- private double timeSpan;
- ///
- /// The amount of time which the length of the playfield spans.
- ///
- public double TimeSpan
- {
- get { return timeSpan; }
- set
- {
- if (timeSpan == value)
- return;
- timeSpan = value;
-
- timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max);
-
- barLineContainer.TimeSpan = value;
- Columns.ForEach(c => c.ControlPointContainer.TimeSpan = value);
- }
- }
-
- private void transformTimeSpanTo(double newTimeSpan, double duration = 0, EasingTypes easing = EasingTypes.None)
- {
- TransformTo(() => TimeSpan, newTimeSpan, duration, easing, new TransformTimeSpan());
- }
+ public void Add(DrawableBarLine barline) => HitObjects.Add(barline);
protected override void Update()
{
// Due to masking differences, it is not possible to get the width of the columns container automatically
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
- barLineContainer.Width = columns.Width;
- }
-
- private class TransformTimeSpan : Transform
- {
- public override double CurrentValue
- {
- get
- {
- double time = Time?.Current ?? 0;
- if (time < StartTime) return StartValue;
- if (time >= EndTime) return EndValue;
-
- return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
- }
- }
-
- public override void Apply(Drawable d)
- {
- base.Apply(d);
-
- var p = (ManiaPlayfield)d;
- p.TimeSpan = CurrentValue;
- }
+ content.Width = columns.Width;
}
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
new file mode 100644
index 0000000000..0e750a348e
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
@@ -0,0 +1,129 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using OpenTK;
+using OpenTK.Input;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Input;
+using osu.Framework.Lists;
+using osu.Framework.MathUtils;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Beatmaps;
+using osu.Game.Rulesets.Mania.Beatmaps;
+using osu.Game.Rulesets.Mania.Judgements;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Scoring;
+using osu.Game.Rulesets.Mania.Timing;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.Timing;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Rulesets.Mania.UI
+{
+ public class ManiaRulesetContainer : ScrollingRulesetContainer
+ {
+ ///
+ /// The number of columns which the should display, and which
+ /// the beatmap converter will attempt to convert beatmaps to use.
+ ///
+ private int availableColumns;
+
+ public IEnumerable BarLines;
+
+ public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
+ : base(ruleset, beatmap, isForCurrentRuleset)
+ {
+ // Generate the bar lines
+ double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
+
+ SortedList timingPoints = Beatmap.ControlPointInfo.TimingPoints;
+ var barLines = new List();
+
+ for (int i = 0; i < timingPoints.Count; i++)
+ {
+ TimingControlPoint point = timingPoints[i];
+
+ // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
+ double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;
+
+ int index = 0;
+ for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
+ {
+ barLines.Add(new DrawableBarLine(new BarLine
+ {
+ StartTime = t,
+ ControlPoint = point,
+ BeatIndex = index
+ }));
+ }
+ }
+
+ BarLines = barLines;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ BarLines.ForEach(Playfield.Add);
+ }
+
+ protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(availableColumns)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ };
+
+ public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
+
+ public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo);
+
+ protected override BeatmapConverter CreateBeatmapConverter()
+ {
+ if (IsForCurrentRuleset)
+ availableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize));
+ else
+ {
+ float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count;
+ if (percentSliderOrSpinner < 0.2)
+ availableColumns = 7;
+ else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize) >= 5)
+ availableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 5 ? 7 : 6;
+ else if (percentSliderOrSpinner > 0.6)
+ availableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 4 ? 5 : 4;
+ else
+ availableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) + 1, 7));
+ }
+
+ return new ManiaBeatmapConverter(IsForCurrentRuleset, availableColumns);
+ }
+
+ protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h)
+ {
+ Bindable key = Playfield.Columns.ElementAt(h.Column).Key;
+
+ var holdNote = h as HoldNote;
+ if (holdNote != null)
+ return new DrawableHoldNote(holdNote, key);
+
+ var note = h as Note;
+ if (note != null)
+ return new DrawableNote(note, key);
+
+ return null;
+ }
+
+ protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
+
+ protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
index 3d5614bd90..890c9116cf 100644
--- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
+++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
@@ -33,8 +33,9 @@
false
-
- $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ True
@@ -62,6 +63,7 @@
+
@@ -77,14 +79,17 @@
+
+
+
-
+
+
-
-
+
diff --git a/osu.Game.Rulesets.Mania/packages.config b/osu.Game.Rulesets.Mania/packages.config
index 634d0b51f6..8add43d5d5 100644
--- a/osu.Game.Rulesets.Mania/packages.config
+++ b/osu.Game.Rulesets.Mania/packages.config
@@ -1,9 +1,8 @@
-
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
index cc06946d38..3b0cfc1ef1 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
@@ -39,6 +39,11 @@ namespace osu.Game.Rulesets.Osu.Mods
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
}
+ public class OsuModDaycore : ModDaycore
+ {
+ public override double ScoreMultiplier => 0.5;
+ }
+
public class OsuModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.12;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
index 9f8ff17853..30e6172802 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
@@ -6,7 +6,7 @@ using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
Masking = true;
AutoSizeAxes = Axes.Both;
CornerRadius = width / 2;
- EdgeEffect = new EdgeEffect
+ EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = Color4.White.Opacity(0.2f),
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
index 925767b851..2396e5d129 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
@@ -94,12 +94,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
using (fp.BeginAbsoluteSequence(fadeInTime))
{
fp.FadeIn(DrawableOsuHitObject.TIME_FADEIN);
- fp.ScaleTo(1, DrawableOsuHitObject.TIME_FADEIN, EasingTypes.Out);
+ fp.ScaleTo(1, DrawableOsuHitObject.TIME_FADEIN, Easing.Out);
- fp.MoveTo(pointEndPosition, DrawableOsuHitObject.TIME_FADEIN, EasingTypes.Out);
+ fp.MoveTo(pointEndPosition, DrawableOsuHitObject.TIME_FADEIN, Easing.Out);
- fp.Delay(fadeOutTime - fadeInTime);
- fp.FadeOut(DrawableOsuHitObject.TIME_FADEIN);
+ fp.Delay(fadeOutTime - fadeInTime).FadeOut(DrawableOsuHitObject.TIME_FADEIN);
}
fp.Expire(true);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 09bfffeefe..f68a7a765b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -89,11 +89,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.UpdateInitialState();
- //sane defaults
- ring.Alpha = circle.Alpha = number.Alpha = glow.Alpha = 1;
- ApproachCircle.Alpha = 0;
- ApproachCircle.Scale = new Vector2(4);
- explode.Alpha = 0;
+ // sane defaults
+ ring.Show();
+ circle.Show();
+ number.Show();
+ glow.Show();
+
+ ApproachCircle.Hide();
+ ApproachCircle.ScaleTo(new Vector2(4));
+ explode.Hide();
}
protected override void UpdatePreemptState()
@@ -106,43 +110,42 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateCurrentState(ArmedState state)
{
- ApproachCircle.FadeOut();
+ double duration = ((HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime) - HitObject.StartTime;
- double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
- double duration = endTime - HitObject.StartTime;
-
- glow.Delay(duration);
- glow.FadeOut(400);
+ glow.Delay(duration).FadeOut(400);
switch (state)
{
case ArmedState.Idle:
- Delay(duration + TIME_PREEMPT);
- FadeOut(TIME_FADEOUT);
+ this.Delay(duration + TIME_PREEMPT).FadeOut(TIME_FADEOUT);
Expire(true);
break;
case ArmedState.Miss:
- FadeOut(TIME_FADEOUT / 5);
+ ApproachCircle.FadeOut(50);
+ this.FadeOut(TIME_FADEOUT / 5);
Expire();
break;
case ArmedState.Hit:
- const double flash_in = 40;
+ ApproachCircle.FadeOut(50);
- flash.FadeTo(0.8f, flash_in);
- flash.Delay(flash_in);
- flash.FadeOut(100);
+ const double flash_in = 40;
+ flash.FadeTo(0.8f, flash_in)
+ .Then()
+ .FadeOut(100);
explode.FadeIn(flash_in);
- Delay(flash_in, true);
+ using (BeginDelayedSequence(flash_in, true))
+ {
+ //after the flash, we can hide some elements that were behind it
+ ring.FadeOut();
+ circle.FadeOut();
+ number.FadeOut();
- //after the flash, we can hide some elements that were behind it
- ring.FadeOut();
- circle.FadeOut();
- number.FadeOut();
+ this.FadeOut(800)
+ .ScaleTo(Scale * 1.5f, 400, Easing.OutQuad);
+ }
- FadeOut(800);
- ScaleTo(Scale * 1.5f, 400, EasingTypes.OutQuad);
Expire();
break;
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 57a9804330..b3043d18f6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -4,6 +4,7 @@
using System.ComponentModel;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
+using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -17,18 +18,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
: base(hitObject)
{
AccentColour = HitObject.ComboColour;
+ Alpha = 0;
}
protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.Hit300 };
protected sealed override void UpdateState(ArmedState state)
{
- Flush();
-
- UpdateInitialState();
+ FinishTransforms();
using (BeginAbsoluteSequence(HitObject.StartTime - TIME_PREEMPT, true))
{
+ UpdateInitialState();
+
UpdatePreemptState();
using (BeginDelayedSequence(TIME_PREEMPT + Judgement.TimeOffset, true))
@@ -36,19 +38,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
- protected virtual void UpdateCurrentState(ArmedState state)
+ protected virtual void UpdateInitialState()
{
+ Hide();
}
protected virtual void UpdatePreemptState()
{
- FadeIn(TIME_FADEIN);
+ this.FadeIn(TIME_FADEIN);
}
- protected virtual void UpdateInitialState()
+ protected virtual void UpdateCurrentState(ArmedState state)
{
- Alpha = 0;
}
+
+ private OsuInputManager osuActionInputManager;
+ internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
}
public enum ComboResult
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
index eaa0bb7d27..892d106b02 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void LoadComplete()
{
if (Judgement.Result != HitResult.Miss)
- JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint);
+ JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
base.LoadComplete();
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index b80f1d7178..d5583b0d9d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -28,10 +28,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public DrawableSlider(Slider s) : base(s)
{
- // Since the DrawableSlider itself is just a container without a size we need to
- // pass all input through.
- AlwaysReceiveInput = true;
-
SliderBouncer bouncer1;
slider = s;
@@ -129,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
if (!userTriggered && Time.Current >= slider.EndTime)
{
- var ticksCount = ticks.Children.Count() + 1;
+ var ticksCount = ticks.Children.Count + 1;
var ticksHit = ticks.Children.Count(t => t.Judgement.Result == HitResult.Hit);
if (initialCircle.Judgement.Result == HitResult.Hit)
ticksHit++;
@@ -162,14 +158,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
ball.FadeIn();
- Delay(slider.Duration, true);
+ using (BeginDelayedSequence(slider.Duration, true))
+ {
+ body.FadeOut(160);
+ ball.FadeOut(160);
- body.FadeOut(160);
- ball.FadeOut(160);
-
- FadeOut(800);
-
- Expire();
+ this.FadeOut(800)
+ .Expire();
+ }
}
public Drawable ProxiedLayer => initialCircle.ApproachCircle;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index 6b4d40e080..2a50b23047 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -3,11 +3,11 @@
using System;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using OpenTK;
using OpenTK.Graphics;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -62,14 +62,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
var animIn = Math.Min(150, sliderTick.StartTime - FadeInTime);
- ScaleTo(0.5f);
- ScaleTo(1.2f, animIn);
- FadeIn(animIn);
-
- Delay(animIn);
- ScaleTo(1, 150, EasingTypes.Out);
-
- Delay(-animIn);
+ this.Animate(
+ d => d.FadeIn(animIn),
+ d => d.ScaleTo(0.5f).ScaleTo(1.2f, animIn)
+ ).Then(
+ d => d.ScaleTo(1, 150, Easing.Out)
+ );
}
protected override void UpdateCurrentState(ArmedState state)
@@ -77,16 +75,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
switch (state)
{
case ArmedState.Idle:
- Delay(FadeOutTime - sliderTick.StartTime);
- FadeOut();
+ this.Delay(FadeOutTime - sliderTick.StartTime).FadeOut();
break;
case ArmedState.Miss:
- FadeOut(160);
- FadeColour(Color4.Red, 80);
+ this.FadeOut(160)
+ .FadeColour(Color4.Red, 80);
break;
case ArmedState.Hit:
- FadeOut(120, EasingTypes.OutQuint);
- ScaleTo(Scale * 1.5f, 120, EasingTypes.OutQuint);
+ this.FadeOut(120, Easing.OutQuint)
+ .ScaleTo(Scale * 1.5f, 120, Easing.OutQuint);
break;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index 3722d13ffc..8473cc2453 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.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.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
@@ -28,7 +29,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");
@@ -38,8 +39,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public DrawableSpinner(Spinner s) : base(s)
{
- AlwaysReceiveInput = true;
-
Origin = Anchor.Centre;
Position = s.Position;
@@ -66,12 +65,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,
},
@@ -168,6 +166,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
glow.Colour = colours.BlueDark;
}
+ protected override void Update()
+ {
+ disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
+
+ base.Update();
+ }
+
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
@@ -176,9 +181,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ticks.Rotation = disc.Rotation;
float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
- disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, EasingTypes.OutQuint);
+ disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
- symbol.RotateTo(disc.Rotation / 2, 500, EasingTypes.OutQuint);
+ symbol.RotateTo(disc.Rotation / 2, 500, Easing.OutQuint);
}
protected override void UpdatePreemptState()
@@ -186,35 +191,33 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.UpdatePreemptState();
circleContainer.ScaleTo(spinner.Scale * 0.3f);
- circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, EasingTypes.OutQuint);
+ circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, Easing.OutQuint);
disc.RotateTo(-720);
symbol.RotateTo(-720);
- mainContainer.ScaleTo(0);
- mainContainer.ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, EasingTypes.OutQuint);
-
- mainContainer.Delay(TIME_PREEMPT - 150);
- mainContainer.ScaleTo(1, 500, EasingTypes.OutQuint);
+ mainContainer
+ .ScaleTo(0)
+ .ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, Easing.OutQuint)
+ .Then()
+ .ScaleTo(1, 500, Easing.OutQuint);
}
protected override void UpdateCurrentState(ArmedState state)
{
- Delay(spinner.Duration, true);
-
- FadeOut(160);
+ var sequence = this.Delay(spinner.Duration).FadeOut(160);
switch (state)
{
case ArmedState.Hit:
- ScaleTo(Scale * 1.2f, 320, EasingTypes.Out);
- Expire();
+ sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out);
break;
case ArmedState.Miss:
- ScaleTo(Scale * 0.8f, 320, EasingTypes.In);
- Expire();
+ sequence.ScaleTo(Scale * 0.8f, 320, Easing.In);
break;
}
+
+ Expire();
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
index 3004dafda7..0c3c4f0a6d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
@@ -7,12 +7,12 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Input;
+using osu.Framework.Input.Bindings;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
- public class CirclePiece : Container
+ public class CirclePiece : Container, IKeyBindingHandler
{
private readonly Sprite disc;
@@ -49,9 +49,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
disc.Texture = textures.Get(@"Play/osu/disc");
}
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ public bool OnPressed(OsuAction action)
{
- return Hit?.Invoke() ?? false;
+ switch (action)
+ {
+ case OsuAction.LeftButton:
+ case OsuAction.RightButton:
+ return IsHovered && (Hit?.Invoke() ?? false);
+ }
+
+ return false;
}
+
+ public bool OnReleased(OsuAction action) => false;
}
}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs
index 68ffb756d4..97d7de35cf 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs
@@ -3,8 +3,8 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using OpenTK;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
index 07b21657a5..28e54c3b4e 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
@@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -31,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
Masking = true,
Origin = Anchor.Centre,
- EdgeEffect = new EdgeEffect
+ EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = 60,
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs
index a04d3e7a0a..f66099e1c3 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs
@@ -3,9 +3,9 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index 4cffc1def3..0bcb8a4986 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.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 osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
+using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@@ -96,18 +98,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return base.OnMouseMove(state);
}
+ // If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position.
+ public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos);
+
private bool tracking;
public bool Tracking
{
get { return tracking; }
- set
+ private set
{
if (value == tracking) return;
tracking = value;
- follow.ScaleTo(tracking ? 2.8f : 1, 300, EasingTypes.OutQuint);
- follow.FadeTo(tracking ? 0.2f : 0, 300, EasingTypes.OutQuint);
+ follow.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
+ follow.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint);
}
}
@@ -117,8 +122,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
base.Update();
+ // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
if (Time.Current < slider.EndTime)
- Tracking = canCurrentlyTrack && lastState != null && Contains(lastState.Mouse.NativeState.Position) && lastState.Mouse.HasMainButtonPressed;
+ Tracking = canCurrentlyTrack && lastState != null && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
}
public void UpdateProgress(double progress, int repeat)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
index 8b9441ea65..680f987274 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
accentColour = value;
- if (LoadState == LoadState.Loaded)
+ if (LoadState == LoadState.Ready)
Schedule(reloadTexture);
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBouncer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBouncer.cs
index 65679dd7d3..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),
}
};
}
@@ -37,8 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
protected override void LoadComplete()
{
base.LoadComplete();
- icon.RotateTo(360, 1000);
- icon.Loop();
+ icon.Spin(1000, RotationDirection.Clockwise);
}
public void UpdateProgress(double progress, int repeat)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
index 66cf7758b9..bdd5b71211 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
@@ -4,7 +4,7 @@
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
Disc.Colour = value;
- EdgeEffect = new EdgeEffect
+ EdgeEffect = new EdgeEffectParameters
{
Hollow = true,
Type = EdgeEffectType.Glow,
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
index 29d6d1f147..6577c7fd50 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
@@ -4,7 +4,6 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
@@ -31,7 +30,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
spinner = s;
- AlwaysReceiveInput = true;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
@@ -40,6 +38,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
};
}
+ public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
+
private bool tracking;
public bool Tracking
{
@@ -66,21 +66,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
}
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
- {
- Tracking |= state.Mouse.HasMainButtonPressed;
- return base.OnMouseDown(state, args);
- }
-
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
- {
- Tracking &= state.Mouse.HasMainButtonPressed;
- return base.OnMouseUp(state, args);
- }
-
protected override bool OnMouseMove(InputState state)
{
- Tracking |= state.Mouse.HasMainButtonPressed;
mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position);
return base.OnMouseMove(state);
}
@@ -126,13 +113,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
if (Complete && updateCompleteTick())
{
- background.Flush(flushType: typeof(TransformAlpha));
- background.FadeTo(tracking_alpha + 0.2f, 60, EasingTypes.OutExpo);
- background.Delay(60);
- background.FadeTo(tracking_alpha, 250, EasingTypes.OutQuint);
+ background.FinishTransforms(false, nameof(Alpha));
+ background
+ .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo)
+ .Then()
+ .FadeTo(tracking_alpha, 250, Easing.OutQuint);
}
- RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, EasingTypes.OutExpo);
+ this.RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, Easing.OutExpo);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs
index 4dbb6bd4d6..fc0d436788 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs
@@ -5,9 +5,9 @@ using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
Colour = Color4.Black,
Alpha = 0.4f,
- EdgeEffect = new EdgeEffect
+ EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = 10,
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index e6fd82e6c8..7a311f1467 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -1,12 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using OpenTK;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
-using osu.Game.Database;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Osu.Objects
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObjectDifficulty.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObjectDifficulty.cs
deleted file mode 100644
index 1786771dca..0000000000
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObjectDifficulty.cs
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using OpenTK;
-using System;
-using System.Diagnostics;
-using System.Linq;
-
-namespace osu.Game.Rulesets.Osu.Objects
-{
- internal class OsuHitObjectDifficulty
- {
- ///
- /// Factor by how much speed / aim strain decays per second.
- ///
- ///
- /// These values are results of tweaking a lot and taking into account general feedback.
- /// Opinionated observation: Speed is easier to maintain than accurate jumps.
- ///
- internal static readonly double[] DECAY_BASE = { 0.3, 0.15 };
-
- ///
- /// Pseudo threshold values to distinguish between "singles" and "streams"
- ///
- ///
- /// Of course the border can not be defined clearly, therefore the algorithm has a smooth transition between those values.
- /// They also are based on tweaking and general feedback.
- ///
- private const double stream_spacing_threshold = 110,
- single_spacing_threshold = 125;
-
- ///
- /// Scaling values for weightings to keep aim and speed difficulty in balance.
- ///
- ///
- /// Found from testing a very large map pool (containing all ranked maps) and keeping the average values the same.
- ///
- private static readonly double[] spacing_weight_scaling = { 1400, 26.25 };
-
- ///
- /// Almost the normed diameter of a circle (104 osu pixel). That is -after- position transforming.
- ///
- private const double almost_diameter = 90;
-
- internal OsuHitObject BaseHitObject;
- internal double[] Strains = { 1, 1 };
-
- internal int MaxCombo = 1;
-
- private readonly float scalingFactor;
- private float lazySliderLength;
-
- private readonly Vector2 startPosition;
- private readonly Vector2 endPosition;
-
- internal OsuHitObjectDifficulty(OsuHitObject baseHitObject)
- {
- BaseHitObject = baseHitObject;
- float circleRadius = baseHitObject.Scale * 64;
-
- Slider slider = BaseHitObject as Slider;
- if (slider != null)
- MaxCombo += slider.Ticks.Count();
-
- // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
- scalingFactor = 52.0f / circleRadius;
- if (circleRadius < 30)
- {
- float smallCircleBonus = Math.Min(30.0f - circleRadius, 5.0f) / 50.0f;
- scalingFactor *= 1.0f + smallCircleBonus;
- }
-
- lazySliderLength = 0;
- startPosition = baseHitObject.StackedPosition;
-
- // Calculate approximation of lazy movement on the slider
- if (slider != null)
- {
- float sliderFollowCircleRadius = circleRadius * 3; // Not sure if this is correct, but here we do not need 100% exact values. This comes pretty darn close in my tests.
-
- // For simplifying this step we use actual osu! coordinates and simply scale the length, that we obtain by the ScalingFactor later
- Vector2 cursorPos = startPosition;
-
- Action addSliderVertex = delegate (Vector2 pos)
- {
- Vector2 difference = pos - cursorPos;
- float distance = difference.Length;
-
- // Did we move away too far?
- if (distance > sliderFollowCircleRadius)
- {
- // Yep, we need to move the cursor
- difference.Normalize(); // Obtain the direction of difference. We do no longer need the actual difference
- distance -= sliderFollowCircleRadius;
- cursorPos += difference * distance; // We move the cursor just as far as needed to stay in the follow circle
- lazySliderLength += distance;
- }
- };
-
- // Actual computation of the first lazy curve
- foreach (var tick in slider.Ticks)
- addSliderVertex(tick.StackedPosition);
-
- addSliderVertex(baseHitObject.StackedEndPosition);
-
- lazySliderLength *= scalingFactor;
- endPosition = cursorPos;
- }
- // We have a normal HitCircle or a spinner
- else
- endPosition = startPosition;
- }
-
- internal void CalculateStrains(OsuHitObjectDifficulty previousHitObject, double timeRate)
- {
- calculateSpecificStrain(previousHitObject, OsuDifficultyCalculator.DifficultyType.Speed, timeRate);
- calculateSpecificStrain(previousHitObject, OsuDifficultyCalculator.DifficultyType.Aim, timeRate);
- }
-
- // Caution: The subjective values are strong with this one
- private static double spacingWeight(double distance, OsuDifficultyCalculator.DifficultyType type)
- {
- switch (type)
- {
- case OsuDifficultyCalculator.DifficultyType.Speed:
- if (distance > single_spacing_threshold)
- return 2.5;
- else if (distance > stream_spacing_threshold)
- return 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold);
- else if (distance > almost_diameter)
- return 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter);
- else if (distance > almost_diameter / 2)
- return 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2);
- else
- return 0.95;
-
- case OsuDifficultyCalculator.DifficultyType.Aim:
- return Math.Pow(distance, 0.99);
- }
-
- Debug.Assert(false, "Invalid osu difficulty hit object type.");
- return 0;
- }
-
- private void calculateSpecificStrain(OsuHitObjectDifficulty previousHitObject, OsuDifficultyCalculator.DifficultyType type, double timeRate)
- {
- double addition = 0;
- double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
- double decay = Math.Pow(DECAY_BASE[(int)type], timeElapsed / 1000);
-
- if (BaseHitObject is Spinner)
- {
- // Do nothing for spinners
- }
- else if (BaseHitObject is Slider)
- {
- switch (type)
- {
- case OsuDifficultyCalculator.DifficultyType.Speed:
-
- // For speed strain we treat the whole slider as a single spacing entity, since "Speed" is about how hard it is to click buttons fast.
- // The spacing weight exists to differentiate between being able to easily alternate or having to single.
- addition =
- spacingWeight(previousHitObject.lazySliderLength +
- DistanceTo(previousHitObject), type) *
- spacing_weight_scaling[(int)type];
-
- break;
- case OsuDifficultyCalculator.DifficultyType.Aim:
-
- // For Aim strain we treat each slider segment and the jump after the end of the slider as separate jumps, since movement-wise there is no difference
- // to multiple jumps.
- addition =
- (
- spacingWeight(previousHitObject.lazySliderLength, type) +
- spacingWeight(DistanceTo(previousHitObject), type)
- ) *
- spacing_weight_scaling[(int)type];
-
- break;
- }
- }
- else if (BaseHitObject is HitCircle)
- {
- addition = spacingWeight(DistanceTo(previousHitObject), type) * spacing_weight_scaling[(int)type];
- }
-
- // Scale addition by the time, that elapsed. Filter out HitObjects that are too close to be played anyway to avoid crazy values by division through close to zero.
- // You will never find maps that require this amongst ranked maps.
- addition /= Math.Max(timeElapsed, 50);
-
- Strains[(int)type] = previousHitObject.Strains[(int)type] * decay + addition;
- }
-
- internal double DistanceTo(OsuHitObjectDifficulty other)
- {
- // Scale the distance by circle size.
- return (startPosition - other.endPosition).Length * scalingFactor;
- }
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 3b44e38d5e..056bde4005 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -6,9 +6,9 @@ using osu.Game.Rulesets.Objects.Types;
using System;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects;
-using osu.Game.Database;
using System.Linq;
using osu.Game.Audio;
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Osu.Objects
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Objects
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
- double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier / difficultyPoint.SpeedMultiplier;
+ double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
Velocity = scoringDistance / timingPoint.BeatLength;
TickDistance = scoringDistance / difficulty.SliderTickRate;
diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
index 6ba499739a..c4f5dfe97a 100644
--- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
@@ -1,8 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
-using osu.Game.Database;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Osu.Objects
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs
new file mode 100644
index 0000000000..a164566263
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs
@@ -0,0 +1,73 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Beatmaps;
+using osu.Game.Rulesets.Osu.Beatmaps;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
+using osu.Game.Rulesets.Osu.OsuDifficulty.Skills;
+
+namespace osu.Game.Rulesets.Osu.OsuDifficulty
+{
+ public class OsuDifficultyCalculator : DifficultyCalculator
+ {
+ private const int section_length = 400;
+ private const double difficulty_multiplier = 0.0675;
+
+ public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap)
+ {
+ }
+
+ protected override void PreprocessHitObjects()
+ {
+ foreach (OsuHitObject h in Objects)
+ (h as Slider)?.Curve?.Calculate();
+ }
+
+ protected override double CalculateInternal(Dictionary categoryDifficulty)
+ {
+ OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Objects);
+ Skill[] skills =
+ {
+ new Aim(),
+ new Speed()
+ };
+
+ double sectionEnd = section_length / TimeRate;
+ foreach (OsuDifficultyHitObject h in beatmap)
+ {
+ while (h.BaseObject.StartTime > sectionEnd)
+ {
+ foreach (Skill s in skills)
+ {
+ s.SaveCurrentPeak();
+ s.StartNewSectionFrom(sectionEnd);
+ }
+
+ sectionEnd += section_length;
+ }
+
+ foreach (Skill s in skills)
+ s.Process(h);
+ }
+
+ double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
+ double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
+
+ double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
+
+ if (categoryDifficulty != null)
+ {
+ categoryDifficulty.Add("Aim", aimRating.ToString("0.00"));
+ categoryDifficulty.Add("Speed", speedRating.ToString("0.00"));
+ }
+
+ return starRating;
+ }
+
+ protected override BeatmapConverter CreateBeatmapConverter() => new OsuBeatmapConverter();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs
new file mode 100644
index 0000000000..72ba421344
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs
@@ -0,0 +1,93 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections;
+using System.Collections.Generic;
+using osu.Game.Rulesets.Osu.Objects;
+
+namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
+{
+ ///
+ /// An enumerable container wrapping input as
+ /// which contains extra data required for difficulty calculation.
+ ///
+ public class OsuDifficultyBeatmap : IEnumerable
+ {
+ private readonly IEnumerator difficultyObjects;
+ private readonly Queue onScreen = new Queue();
+
+ ///
+ /// Creates an enumerator, which preprocesses a list of s recieved as input, wrapping them as
+ /// which contains extra data required for difficulty calculation.
+ ///
+ public OsuDifficultyBeatmap(List objects)
+ {
+ // Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
+ // This should probably happen before the objects reach the difficulty calculator.
+ objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
+ difficultyObjects = createDifficultyObjectEnumerator(objects);
+ }
+
+ ///
+ /// Returns an enumerator that enumerates all s in the .
+ /// The inner loop adds objects that appear on screen into a queue until we need to hit the next object.
+ /// The outer loop returns objects from this queue one at a time, only after they had to be hit, and should no longer be on screen.
+ /// This means that we can loop through every object that is on screen at the time when a new one appears,
+ /// allowing us to determine a reading strain for the object that just appeared.
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ while (true)
+ {
+ // Add upcoming objects to the queue until we have at least one object that had been hit and can be dequeued.
+ // This means there is always at least one object in the queue unless we reached the end of the map.
+ do
+ {
+ if (!difficultyObjects.MoveNext())
+ break; // New objects can't be added anymore, but we still need to dequeue and return the ones already on screen.
+
+ OsuDifficultyHitObject latest = difficultyObjects.Current;
+ // Calculate flow values here
+
+ foreach (OsuDifficultyHitObject h in onScreen)
+ {
+ h.TimeUntilHit -= latest.DeltaTime;
+ // Calculate reading strain here
+ }
+
+ onScreen.Enqueue(latest);
+ }
+ while (onScreen.Peek().TimeUntilHit > 0); // Keep adding new objects on screen while there is still time before we have to hit the next one.
+
+ if (onScreen.Count == 0) break; // We have reached the end of the map and enumerated all the objects.
+ yield return onScreen.Dequeue(); // Remove and return objects one by one that had to be hit before the latest one appeared.
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ private IEnumerator createDifficultyObjectEnumerator(List objects)
+ {
+ // We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object.
+ OsuHitObject[] triangle = new OsuHitObject[3];
+
+ // OsuDifficultyHitObject construction requires three components, an extra copy of the first OsuHitObject is used at the beginning.
+ if (objects.Count > 1)
+ {
+ triangle[1] = objects[0]; // This copy will get shifted to the last spot in the triangle.
+ triangle[0] = objects[0]; // This component corresponds to the real first OsuHitOject.
+ }
+
+ // The final component of the first triangle will be the second OsuHitOject of the map, which forms the first jump.
+ // If the map has less than two OsuHitObjects, the enumerator will not return anything.
+ for (int i = 1; i < objects.Count; ++i)
+ {
+ triangle[2] = triangle[1];
+ triangle[1] = triangle[0];
+ triangle[0] = objects[i];
+
+ yield return new OsuDifficultyHitObject(triangle);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs
new file mode 100644
index 0000000000..bdeb62df3e
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -0,0 +1,70 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Game.Rulesets.Osu.Objects;
+
+namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
+{
+ ///
+ /// A wrapper around extending it with additional data required for difficulty calculation.
+ ///
+ public class OsuDifficultyHitObject
+ {
+ ///
+ /// The this refers to.
+ ///
+ public OsuHitObject BaseObject { get; }
+
+ ///
+ /// Normalized distance from the of the previous .
+ ///
+ public double Distance { get; private set; }
+
+ ///
+ /// Milliseconds elapsed since the StartTime of the previous .
+ ///
+ public double DeltaTime { get; private set; }
+
+ ///
+ /// Number of milliseconds until the has to be hit.
+ ///
+ public double TimeUntilHit { get; set; }
+
+ private const int normalized_radius = 52;
+
+ private readonly OsuHitObject[] t;
+
+ ///
+ /// Initializes the object calculating extra data required for difficulty calculation.
+ ///
+ public OsuDifficultyHitObject(OsuHitObject[] triangle)
+ {
+ t = triangle;
+ BaseObject = t[0];
+ setDistances();
+ setTimingValues();
+ // Calculate angle here
+ }
+
+ private void setDistances()
+ {
+ // We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps.
+ double scalingFactor = normalized_radius / BaseObject.Radius;
+ if (BaseObject.Radius < 30)
+ {
+ double smallCircleBonus = Math.Min(30 - BaseObject.Radius, 5) / 50;
+ scalingFactor *= 1 + smallCircleBonus;
+ }
+
+ Distance = (t[0].StackedPosition - t[1].StackedPosition).Length * scalingFactor;
+ }
+
+ private void setTimingValues()
+ {
+ // Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure.
+ DeltaTime = Math.Max(40, t[0].StartTime - t[1].StartTime);
+ TimeUntilHit = 450; // BaseObject.PreEmpt;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs
new file mode 100644
index 0000000000..aad53f6fe8
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
+
+namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
+{
+ ///
+ /// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
+ ///
+ public class Aim : Skill
+ {
+ protected override double SkillMultiplier => 26.25;
+ protected override double StrainDecayBase => 0.15;
+
+ protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime;
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs
new file mode 100644
index 0000000000..b9632e18e2
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs
@@ -0,0 +1,100 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
+using osu.Game.Rulesets.Osu.OsuDifficulty.Utils;
+
+namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
+{
+ ///
+ /// Used to processes strain values of s, keep track of strain levels caused by the processed objects
+ /// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
+ ///
+ public abstract class Skill
+ {
+ ///
+ /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
+ ///
+ protected abstract double SkillMultiplier { get; }
+
+ ///
+ /// Determines how quickly strain decays for the given skill.
+ /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second.
+ ///
+ protected abstract double StrainDecayBase { get; }
+
+ ///
+ /// s that were processed previously. They can affect the strain values of the following objects.
+ ///
+ protected readonly History Previous = new History(2); // Contained objects not used yet
+
+ private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap.
+ private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section.
+ private readonly List strainPeaks = new List();
+
+ ///
+ /// Process an and update current strain values accordingly.
+ ///
+ public void Process(OsuDifficultyHitObject current)
+ {
+ currentStrain *= strainDecay(current.DeltaTime);
+ if (!(current.BaseObject is Spinner))
+ currentStrain += StrainValueOf(current) * SkillMultiplier;
+
+ currentSectionPeak = Math.Max(currentStrain, currentSectionPeak);
+
+ Previous.Push(current);
+ }
+
+ ///
+ /// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
+ ///
+ public void SaveCurrentPeak()
+ {
+ if (Previous.Count > 0)
+ strainPeaks.Add(currentSectionPeak);
+ }
+
+ ///
+ /// Sets the initial strain level for a new section.
+ ///
+ /// The beginning of the new section in milliseconds
+ public void StartNewSectionFrom(double offset)
+ {
+ // The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries.
+ // This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level.
+ if (Previous.Count > 0)
+ currentSectionPeak = currentStrain * strainDecay(offset - Previous[0].BaseObject.StartTime);
+ }
+
+ ///
+ /// Returns the calculated difficulty value representing all processed s.
+ ///
+ public double DifficultyValue()
+ {
+ strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
+
+ double difficulty = 0;
+ double weight = 1;
+
+ // Difficulty is the weighted sum of the highest strains from every section.
+ foreach (double strain in strainPeaks)
+ {
+ difficulty += strain * weight;
+ weight *= 0.9;
+ }
+
+ return difficulty;
+ }
+
+ ///
+ /// Calculates the strain value of an . This value is affected by previously processed objects.
+ ///
+ protected abstract double StrainValueOf(OsuDifficultyHitObject current);
+
+ private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs
new file mode 100644
index 0000000000..6c43c53e35
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs
@@ -0,0 +1,39 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
+
+namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
+{
+ ///
+ /// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit.
+ ///
+ public class Speed : Skill
+ {
+ protected override double SkillMultiplier => 1400;
+ protected override double StrainDecayBase => 0.3;
+
+ private const double single_spacing_threshold = 125;
+ private const double stream_spacing_threshold = 110;
+ private const double almost_diameter = 90;
+
+ protected override double StrainValueOf(OsuDifficultyHitObject current)
+ {
+ double distance = current.Distance;
+
+ double speedValue;
+ if (distance > single_spacing_threshold)
+ speedValue = 2.5;
+ else if (distance > stream_spacing_threshold)
+ speedValue = 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold);
+ else if (distance > almost_diameter)
+ speedValue = 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter);
+ else if (distance > almost_diameter / 2)
+ speedValue = 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2);
+ else
+ speedValue = 0.95;
+
+ return speedValue / current.DeltaTime;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs
new file mode 100644
index 0000000000..d2c2e1d774
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs
@@ -0,0 +1,86 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace osu.Game.Rulesets.Osu.OsuDifficulty.Utils
+{
+ ///
+ /// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full.
+ /// Indexing starts at the top of the stack.
+ ///
+ public class History : IEnumerable
+ {
+ public int Count { get; private set; }
+
+ private readonly T[] array;
+ private readonly int capacity;
+ private int marker; // Marks the position of the most recently added item.
+
+ ///
+ /// Initializes a new instance of the History class that is empty and has the specified capacity.
+ ///
+ /// The number of items the History can hold.
+ public History(int capacity)
+ {
+ if (capacity < 0)
+ throw new ArgumentOutOfRangeException();
+
+ this.capacity = capacity;
+ array = new T[capacity];
+ marker = capacity; // Set marker to the end of the array, outside of the indexed range by one.
+ }
+
+ ///
+ /// The most recently added item is returned at index 0.
+ ///
+ public T this[int i]
+ {
+ get
+ {
+ if (i < 0 || i > Count - 1)
+ throw new IndexOutOfRangeException();
+
+ i += marker;
+ if (i > capacity - 1)
+ i -= capacity;
+
+ return array[i];
+ }
+ }
+
+ ///
+ /// Adds the item as the most recent one in the history.
+ /// The oldest item is disposed if the history is full.
+ ///
+ public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition.
+ {
+ if (marker == 0)
+ marker = capacity - 1;
+ else
+ --marker;
+
+ array[marker] = item;
+
+ if (Count < capacity)
+ ++Count;
+ }
+
+ ///
+ /// Returns an enumerator which enumerates items in the history starting from the most recently added one.
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ for (int i = marker; i < capacity; ++i)
+ yield return array[i];
+
+ if (Count == capacity)
+ for (int i = 0; i < marker; ++i)
+ yield return array[i];
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/OsuDifficultyCalculator.cs
deleted file mode 100644
index 5669993e67..0000000000
--- a/osu.Game.Rulesets.Osu/OsuDifficultyCalculator.cs
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Beatmaps;
-using osu.Game.Rulesets.Osu.Beatmaps;
-using osu.Game.Rulesets.Osu.Objects;
-using System;
-using System.Collections.Generic;
-
-namespace osu.Game.Rulesets.Osu
-{
- public class OsuDifficultyCalculator : DifficultyCalculator
- {
- private const double star_scaling_factor = 0.0675;
- private const double extreme_scaling_factor = 0.5;
-
- ///
- /// HitObjects are stored as a member variable.
- ///
- internal List DifficultyHitObjects = new List();
-
- public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap)
- {
- }
-
- protected override void PreprocessHitObjects()
- {
- foreach (var h in Objects)
- (h as Slider)?.Curve?.Calculate();
- }
-
- protected override double CalculateInternal(Dictionary categoryDifficulty)
- {
- // Fill our custom DifficultyHitObject class, that carries additional information
- DifficultyHitObjects.Clear();
-
- foreach (var hitObject in Objects)
- DifficultyHitObjects.Add(new OsuHitObjectDifficulty(hitObject));
-
- // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
- DifficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
-
- if (!CalculateStrainValues()) return 0;
-
- double speedDifficulty = CalculateDifficulty(DifficultyType.Speed);
- double aimDifficulty = CalculateDifficulty(DifficultyType.Aim);
-
- // OverallDifficulty is not considered in this algorithm and neither is HpDrainRate. That means, that in this form the algorithm determines how hard it physically is
- // to play the map, assuming, that too much of an error will not lead to a death.
- // It might be desirable to include OverallDifficulty into map difficulty, but in my personal opinion it belongs more to the weighting of the actual peformance
- // and is superfluous in the beatmap difficulty rating.
- // If it were to be considered, then I would look at the hit window of normal HitCircles only, since Sliders and Spinners are (almost) "free" 300s and take map length
- // into account as well.
-
- // The difficulty can be scaled by any desired metric.
- // In osu!tp it gets squared to account for the rapid increase in difficulty as the limit of a human is approached. (Of course it also gets scaled afterwards.)
- // It would not be suitable for a star rating, therefore:
-
- // The following is a proposal to forge a star rating from 0 to 5. It consists of taking the square root of the difficulty, since by simply scaling the easier
- // 5-star maps would end up with one star.
- double speedStars = Math.Sqrt(speedDifficulty) * star_scaling_factor;
- double aimStars = Math.Sqrt(aimDifficulty) * star_scaling_factor;
-
- if (categoryDifficulty != null)
- {
- categoryDifficulty.Add("Aim", aimStars.ToString("0.00"));
- categoryDifficulty.Add("Speed", speedStars.ToString("0.00"));
-
- double hitWindow300 = 30/*HitObjectManager.HitWindow300*/ / TimeRate;
- double preEmpt = 450/*HitObjectManager.PreEmpt*/ / TimeRate;
-
- categoryDifficulty.Add("OD", (-(hitWindow300 - 80.0) / 6.0).ToString("0.00"));
- categoryDifficulty.Add("AR", (preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0).ToString("0.00"));
-
- int maxCombo = 0;
- foreach (OsuHitObjectDifficulty hitObject in DifficultyHitObjects)
- maxCombo += hitObject.MaxCombo;
-
- categoryDifficulty.Add("Max combo", maxCombo.ToString());
- }
-
- // Again, from own observations and from the general opinion of the community a map with high speed and low aim (or vice versa) difficulty is harder,
- // than a map with mediocre difficulty in both. Therefore we can not just add both difficulties together, but will introduce a scaling that favors extremes.
- double starRating = speedStars + aimStars + Math.Abs(speedStars - aimStars) * extreme_scaling_factor;
- // Another approach to this would be taking Speed and Aim separately to a chosen power, which again would be equivalent. This would be more convenient if
- // the hit window size is to be considered as well.
-
- // Note: The star rating is tuned extremely tight! Airman (/b/104229) and Freedom Dive (/b/126645), two of the hardest ranked maps, both score ~4.66 stars.
- // Expect the easier kind of maps that officially get 5 stars to obtain around 2 by this metric. The tutorial still scores about half a star.
- // Tune by yourself as you please. ;)
-
- return starRating;
- }
-
- protected bool CalculateStrainValues()
- {
- // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
- using (List.Enumerator hitObjectsEnumerator = DifficultyHitObjects.GetEnumerator())
- {
-
- if (!hitObjectsEnumerator.MoveNext()) return false;
-
- OsuHitObjectDifficulty current = hitObjectsEnumerator.Current;
-
- // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
- while (hitObjectsEnumerator.MoveNext())
- {
- var next = hitObjectsEnumerator.Current;
- next?.CalculateStrains(current, TimeRate);
- current = next;
- }
-
- return true;
- }
- }
-
- ///
- /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
- /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
- /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
- ///
- protected const double STRAIN_STEP = 400;
-
- ///
- /// The weighting of each strain value decays to this number * it's previous value
- ///
- protected const double DECAY_WEIGHT = 0.9;
-
- protected double CalculateDifficulty(DifficultyType type)
- {
- double actualStrainStep = STRAIN_STEP * TimeRate;
-
- // Find the highest strain value within each strain step
- List highestStrains = new List();
- double intervalEndTime = actualStrainStep;
- double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
-
- OsuHitObjectDifficulty previousHitObject = null;
- foreach (OsuHitObjectDifficulty hitObject in DifficultyHitObjects)
- {
- // While we are beyond the current interval push the currently available maximum to our strain list
- while (hitObject.BaseHitObject.StartTime > intervalEndTime)
- {
- highestStrains.Add(maximumStrain);
-
- // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
- // until the beginning of the next interval.
- if (previousHitObject == null)
- {
- maximumStrain = 0;
- }
- else
- {
- double decay = Math.Pow(OsuHitObjectDifficulty.DECAY_BASE[(int)type], (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
- maximumStrain = previousHitObject.Strains[(int)type] * decay;
- }
-
- // Go to the next time interval
- intervalEndTime += actualStrainStep;
- }
-
- // Obtain maximum strain
- maximumStrain = Math.Max(hitObject.Strains[(int)type], maximumStrain);
-
- previousHitObject = hitObject;
- }
-
- // Build the weighted sum over the highest strains for each interval
- double difficulty = 0;
- double weight = 1;
- highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
-
- foreach (double strain in highestStrains)
- {
- difficulty += weight * strain;
- weight *= DECAY_WEIGHT;
- }
-
- return difficulty;
- }
-
- protected override BeatmapConverter CreateBeatmapConverter() => new OsuBeatmapConverter();
-
- // Those values are used as array indices. Be careful when changing them!
- public enum DifficultyType
- {
- Speed = 0,
- Aim,
- };
- }
-}
diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs
new file mode 100644
index 0000000000..a65d28cec0
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs
@@ -0,0 +1,24 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.ComponentModel;
+using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Rulesets.Osu
+{
+ public class OsuInputManager : RulesetInputManager
+ {
+ public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique)
+ {
+ }
+ }
+
+ public enum OsuAction
+ {
+ [Description("Left Button")]
+ LeftButton,
+ [Description("Right Button")]
+ RightButton
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs b/osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs
deleted file mode 100644
index d60aab90fb..0000000000
--- a/osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs
+++ /dev/null
@@ -1,40 +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 osu.Framework.Input;
-using osu.Game.Screens.Play;
-using OpenTK.Input;
-using KeyboardState = osu.Framework.Input.KeyboardState;
-using MouseState = osu.Framework.Input.MouseState;
-
-namespace osu.Game.Rulesets.Osu
-{
- public class OsuKeyConversionInputManager : KeyConversionInputManager
- {
- private bool leftViaKeyboard;
- private bool rightViaKeyboard;
-
- protected override void TransformState(InputState state)
- {
- base.TransformState(state);
-
- var mouse = state.Mouse as MouseState;
- var keyboard = state.Keyboard as KeyboardState;
-
- if (keyboard != null)
- {
- leftViaKeyboard = keyboard.Keys.Contains(Key.Z);
- rightViaKeyboard = keyboard.Keys.Contains(Key.X);
- }
-
- if (mouse != null)
- {
- if (leftViaKeyboard)
- mouse.SetPressed(MouseButton.Left, true);
- if (rightViaKeyboard)
- mouse.SetPressed(MouseButton.Right, true);
- }
- }
- }
-}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index af4a099e0d..00413331e0 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -1,28 +1,38 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.OsuDifficulty;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI;
-using osu.Game.Screens.Play;
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;
+using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Osu
{
public class OsuRuleset : Ruleset
{
- public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuHitRenderer(beatmap, isForCurrentRuleset);
+ public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuRulesetContainer(this, beatmap, isForCurrentRuleset);
+
+ public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[]
+ {
+ new KeyBinding(InputKey.Z, OsuAction.LeftButton),
+ new KeyBinding(InputKey.X, OsuAction.RightButton),
+ new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton),
+ new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
+ };
public override IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
- {
+ {
new BeatmapStatistic
{
Name = @"Circle count",
@@ -46,7 +56,14 @@ namespace osu.Game.Rulesets.Osu
{
new OsuModEasy(),
new OsuModNoFail(),
- new OsuModHalfTime(),
+ new MultiMod
+ {
+ Mods = new Mod[]
+ {
+ new OsuModHalfTime(),
+ new OsuModDaycore(),
+ },
+ },
};
case ModType.DifficultyIncrease:
@@ -95,22 +112,23 @@ 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);
public override string Description => "osu!";
- public override IEnumerable CreateGameplayKeys() => new KeyCounter[]
- {
- new KeyCounterKeyboard(Key.Z),
- new KeyCounterKeyboard(Key.X),
- new KeyCounterMouse(MouseButton.Left),
- new KeyCounterMouse(MouseButton.Right)
- };
-
public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor();
+ public override SettingsSubsection CreateSettings() => new OsuSettings();
+
public override int LegacyID => 0;
+
+ public OsuRuleset(RulesetInfo rulesetInfo)
+ : base(rulesetInfo)
+ {
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
index 5ede3f56f5..b92f1bc60a 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Replays
///
/// What easing to use when moving between hitobjects
///
- private EasingTypes preferredEasing => DelayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out;
+ private Easing preferredEasing => DelayedMovements ? Easing.InOutCubic : Easing.Out;
#endregion
@@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Replays
{
// Default values for circles/sliders
Vector2 startPosition = h.StackedPosition;
- EasingTypes easing = preferredEasing;
+ Easing easing = preferredEasing;
float spinnerDirection = -1;
// The startPosition for the slider should not be its .Position, but the point on the circle whose tangent crosses the current cursor position
@@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Replays
if (spinCentreOffset.Length > SPIN_RADIUS)
{
// If moving in from the outside, don't ease out (default eases out). This means auto will "start" spinning immediately after moving into position.
- easing = EasingTypes.In;
+ easing = Easing.In;
}
}
@@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Replays
}
}
- private void moveToHitObject(double targetTime, Vector2 targetPos, double hitObjectRadius, EasingTypes easing)
+ private void moveToHitObject(double targetTime, Vector2 targetPos, double hitObjectRadius, Easing easing)
{
ReplayFrame lastFrame = Frames[Frames.Count - 1];
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index 079ee928af..856ca0c98d 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -18,8 +18,8 @@ namespace osu.Game.Rulesets.Osu.Scoring
{
}
- public OsuScoreProcessor(HitRenderer hitRenderer)
- : base(hitRenderer)
+ public OsuScoreProcessor(RulesetContainer rulesetContainer)
+ : base(rulesetContainer)
{
}
diff --git a/osu.Game/Graphics/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
similarity index 94%
rename from osu.Game/Graphics/Cursor/CursorTrail.cs
rename to osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index 11475a0f19..a5be6a7952 100644
--- a/osu.Game/Graphics/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -1,22 +1,22 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
+using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.OpenGL.Buffers;
+using osu.Framework.Graphics.OpenGL.Vertices;
+using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
-using OpenTK;
-using System;
-using osu.Framework.Graphics.OpenGL;
-using osu.Framework.Graphics.OpenGL.Buffers;
-using OpenTK.Graphics.ES30;
-using osu.Framework.Graphics.Primitives;
-using osu.Framework.Graphics.Colour;
using osu.Framework.Timing;
-using System.Diagnostics;
+using OpenTK;
+using OpenTK.Graphics.ES30;
-namespace osu.Game.Graphics.Cursor
+namespace osu.Game.Rulesets.Osu.UI.Cursor
{
internal class CursorTrail : Drawable
{
@@ -65,7 +65,6 @@ namespace osu.Game.Graphics.Cursor
// as we are currently very dependent on having a running clock, let's make our own clock for the time being.
Clock = new FramedClock();
- AlwaysReceiveInput = true;
RelativeSizeAxes = Axes.Both;
for (int i = 0; i < max_sprites; i++)
@@ -75,6 +74,8 @@ namespace osu.Game.Graphics.Cursor
}
}
+ public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
+
[BackgroundDependencyLoader]
private void load(ShaderManager shaders, TextureStore textures)
{
diff --git a/osu.Game/Graphics/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
similarity index 81%
rename from osu.Game/Graphics/Cursor/GameplayCursor.cs
rename to osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
index 801fe1d011..adfc946f86 100644
--- a/osu.Game/Graphics/Cursor/GameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.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.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Bindings;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
-using osu.Game.Database;
+using OpenTK;
+using OpenTK.Graphics;
-namespace osu.Game.Graphics.Cursor
+namespace osu.Game.Rulesets.Osu.UI.Cursor
{
- public class GameplayCursor : CursorContainer
+ public class GameplayCursor : CursorContainer, IKeyBindingHandler
{
protected override Drawable CreateCursor() => new OsuCursor();
@@ -26,19 +25,7 @@ namespace osu.Game.Graphics.Cursor
Add(new CursorTrail { Depth = 1 });
}
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
- {
- ActiveCursor.Scale = new Vector2(1);
- ActiveCursor.ScaleTo(1.2f, 100, EasingTypes.OutQuad);
- return base.OnMouseDown(state, args);
- }
-
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
- {
- if (!state.Mouse.HasMainButtonPressed)
- ActiveCursor.ScaleTo(1, 200, EasingTypes.OutQuad);
- return base.OnMouseUp(state, args);
- }
+ private int downCount;
public class OsuCursor : Container
{
@@ -67,7 +54,7 @@ namespace osu.Game.Graphics.Cursor
Masking = true,
BorderThickness = Size.X / 6,
BorderColour = Color4.White,
- EdgeEffect = new EdgeEffect
+ EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Pink.Opacity(0.5f),
@@ -144,5 +131,33 @@ namespace osu.Game.Graphics.Cursor
cursorContainer.Scale = new Vector2(scale);
}
}
+
+ public bool OnPressed(OsuAction action)
+ {
+ switch (action)
+ {
+ case OsuAction.LeftButton:
+ case OsuAction.RightButton:
+ downCount++;
+ ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
+ break;
+ }
+
+ return false;
+ }
+
+ public bool OnReleased(OsuAction action)
+ {
+ switch (action)
+ {
+ case OsuAction.LeftButton:
+ case OsuAction.RightButton:
+ if (--downCount == 0)
+ ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
+ break;
+ }
+
+ return false;
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index 53eedea073..9b88c9d1b3 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -10,8 +10,8 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
using osu.Game.Rulesets.UI;
using System.Linq;
-using osu.Game.Graphics.Cursor;
using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Osu.UI.Cursor;
namespace osu.Game.Rulesets.Osu.UI
{
@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.UI
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- Add(new Drawable[]
+ AddRange(new Drawable[]
{
connectionLayer = new FollowPointRenderer
{
@@ -80,9 +80,9 @@ namespace osu.Game.Rulesets.Osu.UI
public override void PostProcess()
{
- connectionLayer.HitObjects = HitObjects.Children
+ connectionLayer.HitObjects = HitObjects.Objects
.Select(d => d.HitObject)
- .OrderBy(h => h.StartTime);
+ .OrderBy(h => h.StartTime).OfType();
}
public override void OnJudgement(DrawableHitObject judgedObject)
diff --git a/osu.Game.Rulesets.Osu/UI/OsuHitRenderer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
similarity index 79%
rename from osu.Game.Rulesets.Osu/UI/OsuHitRenderer.cs
rename to osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
index e582d2fcd3..083f11945c 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuHitRenderer.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.Input;
using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Beatmaps;
@@ -12,14 +13,13 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
-using osu.Game.Screens.Play;
namespace osu.Game.Rulesets.Osu.UI
{
- public class OsuHitRenderer : HitRenderer
+ public class OsuRulesetContainer : RulesetContainer
{
- public OsuHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
- : base(beatmap, isForCurrentRuleset)
+ public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
+ : base(ruleset, beatmap, isForCurrentRuleset)
{
}
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.UI
protected override Playfield CreatePlayfield() => new OsuPlayfield();
- protected override KeyConversionInputManager CreateKeyConversionInputManager() => new OsuKeyConversionInputManager();
+ public override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo);
protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h)
{
diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettings.cs b/osu.Game.Rulesets.Osu/UI/OsuSettings.cs
new file mode 100644
index 0000000000..b820f33b39
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/UI/OsuSettings.cs
@@ -0,0 +1,33 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Configuration;
+using osu.Game.Overlays.Settings;
+
+namespace osu.Game.Rulesets.Osu.UI
+{
+ public class OsuSettings : SettingsSubsection
+ {
+ protected override string Header => "osu!";
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config)
+ {
+ Children = new Drawable[]
+ {
+ new SettingsCheckbox
+ {
+ LabelText = "Snaking in sliders",
+ Bindable = config.GetBindable(OsuSetting.SnakingInSliders)
+ },
+ new SettingsCheckbox
+ {
+ LabelText = "Snaking out sliders",
+ Bindable = config.GetBindable(OsuSetting.SnakingOutSliders)
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index b91bdc6a78..1422ded407 100644
--- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
+++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
@@ -34,8 +34,9 @@
false
-
- $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ True
@@ -68,12 +69,20 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/osu.Game.Rulesets.Osu/packages.config b/osu.Game.Rulesets.Osu/packages.config
index 634d0b51f6..fa6edb9c8f 100644
--- a/osu.Game.Rulesets.Osu/packages.config
+++ b/osu.Game.Rulesets.Osu/packages.config
@@ -5,5 +5,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd .
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index 5d44da78f9..2bf058fc2b 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -8,7 +8,6 @@ using osu.Game.Rulesets.Taiko.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
-using osu.Game.Database;
using osu.Game.IO.Serialization;
using osu.Game.Audio;
using osu.Game.Rulesets.Beatmaps;
@@ -40,15 +39,22 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
///
private const float taiko_base_distance = 100;
+ private readonly bool isForCurrentRuleset;
+
protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(HitObject) };
- protected override Beatmap ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
+ public TaikoBeatmapConverter(bool isForCurrentRuleset)
+ {
+ this.isForCurrentRuleset = isForCurrentRuleset;
+ }
+
+ protected override Beatmap ConvertBeatmap(Beatmap original)
{
// Rewrite the beatmap info to add the slider velocity multiplier
BeatmapInfo info = original.BeatmapInfo.DeepClone();
info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
- Beatmap converted = base.ConvertBeatmap(original, isForCurrentRuleset);
+ Beatmap converted = base.ConvertBeatmap(original);
// Post processing step to transform hit objects with the same start time into strong hits
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>
@@ -82,7 +88,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
double speedAdjustment = difficultyPoint.SpeedMultiplier;
- double speedAdjustedBeatLength = timingPoint.BeatLength * speedAdjustment;
+ double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
double distance = distanceData.Distance * repeats * legacy_velocity_multiplier;
@@ -95,7 +101,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
// For some reason, old osu! always uses speedAdjustment to determine the taiko velocity, but
// only uses it to determine osu! velocity if beatmap version < 8. Let's account for that here.
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
- speedAdjustedBeatLength /= speedAdjustment;
+ speedAdjustedBeatLength *= speedAdjustment;
// The velocity of the osu! hit object - calculated as the velocity of a slider
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
@@ -105,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.Difficulty.SliderTickRate, taikoDuration / repeats);
- if (tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
+ if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{
List allSamples = curveData != null ? curveData.RepeatSamples : new List(new[] { samples });
diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoMod.cs
index 8b7a099b9a..abaa8c1bc1 100644
--- a/osu.Game.Rulesets.Taiko/Mods/TaikoMod.cs
+++ b/osu.Game.Rulesets.Taiko/Mods/TaikoMod.cs
@@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Taiko.Mods
}
+ public class TaikoModDaycore : ModDaycore
+ {
+ public override double ScoreMultiplier => 0.5;
+ }
+
public class TaikoModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.12;
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
index 4c83e08bab..9df1b41a8b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
@@ -2,16 +2,17 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using OpenTK;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
///
/// A line that scrolls alongside hit objects in the playfield and visualises control points.
///
- public class DrawableBarLine : Container
+ public class DrawableBarLine : DrawableScrollingHitObject
{
///
/// The width of the line tracker.
@@ -34,15 +35,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected readonly BarLine BarLine;
public DrawableBarLine(BarLine barLine)
+ : base(barLine)
{
BarLine = barLine;
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
- RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
-
Width = tracker_width;
Children = new[]
@@ -56,25 +56,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Alpha = 0.75f
}
};
-
- LifetimeStart = BarLine.StartTime - BarLine.ScrollTime * 2;
- LifetimeEnd = BarLine.StartTime + BarLine.ScrollTime;
}
- protected override void LoadComplete()
+ protected override TaikoJudgement CreateJudgement() => null;
+
+ protected override void UpdateState(ArmedState state)
{
- base.LoadComplete();
- Delay(BarLine.StartTime - Time.Current);
- FadeOut(base_fadeout_time * BarLine.ScrollTime / 1000);
- }
-
- private void updateScrollPosition(double time) => MoveToX((float)((BarLine.StartTime - time) / BarLine.ScrollTime));
-
- protected override void Update()
- {
- base.Update();
-
- updateScrollPosition(Time.Current);
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs
index e64682a1e4..7a4cf1f1f7 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs
@@ -3,8 +3,8 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using OpenTK;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -20,10 +20,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
///
private const float triangle_size = 20f;
+ private readonly Container triangleContainer;
+
public DrawableBarLineMajor(BarLine barLine)
: base(barLine)
{
- Add(new Container
+ Add(triangleContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -53,5 +55,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Tracker.Alpha = 1f;
}
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ using (triangleContainer.BeginAbsoluteSequence(HitObject.StartTime))
+ triangleContainer.FadeOut(150);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
index 8bb78669ca..26aa20f1af 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
@@ -4,13 +4,12 @@
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableCentreHit : DrawableHit
{
- protected override Key[] HitKeys { get; } = { Key.F, Key.J };
+ protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
public DrawableCentreHit(Hit hit)
: base(hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs
index 434fb9377f..1dbab408de 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs
@@ -4,13 +4,12 @@
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableCentreHitStrong : DrawableHitStrong
{
- protected override Key[] HitKeys { get; } = { Key.F, Key.J };
+ protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
public DrawableCentreHitStrong(Hit hit)
: base(hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
index 4562501ed1..5d6ea8f51e 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
@@ -10,6 +10,8 @@ using osu.Game.Rulesets.Taiko.Judgements;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -30,27 +32,31 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public DrawableDrumRoll(DrumRoll drumRoll)
: base(drumRoll)
{
+ Width = (float)HitObject.Duration;
+
+ Container tickContainer;
+ MainPiece.Add(tickContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ RelativeChildOffset = new Vector2((float)HitObject.StartTime, 0),
+ RelativeChildSize = new Vector2((float)HitObject.Duration, 1)
+ });
+
foreach (var tick in drumRoll.Ticks)
{
- var newTick = new DrawableDrumRollTick(tick)
- {
- X = (float)((tick.StartTime - HitObject.StartTime) / HitObject.Duration)
- };
-
+ var newTick = new DrawableDrumRollTick(tick);
newTick.OnJudgement += onTickJudgement;
AddNested(newTick);
- MainPiece.Add(newTick);
+ tickContainer.Add(newTick);
}
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = HitObject.IsStrong };
- protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(HitObject.IsStrong)
- {
- Length = (float)(HitObject.Duration / HitObject.ScrollTime),
- PlayfieldLengthReference = () => Parent.DrawSize.X
- };
+ protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece();
+
+ public override bool OnPressed(TaikoAction action) => false;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
@@ -59,17 +65,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
accentDarkColour = colours.YellowDarker;
}
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- // This is naive, however it's based on the reasoning that the hit target
- // is further than mid point of the play field, so the time taken to scroll in should always
- // be greater than the time taken to scroll out to the left of the screen.
- // Thus, using PreEmpt here is enough for the drum roll to completely scroll out.
- LifetimeEnd = HitObject.EndTime + HitObject.ScrollTime;
- }
-
private void onTickJudgement(DrawableHitObject obj)
{
if (obj.Judgement.Result == HitResult.Hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
index 56a747467e..e94886df16 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
@@ -5,7 +5,6 @@ using System;
using osu.Framework.Graphics;
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
@@ -15,6 +14,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public DrawableDrumRollTick(DrumRollTick tick)
: base(tick)
{
+ // Because ticks aren't added by the ScrollingPlayfield, we need to set the following properties ourselves
+ RelativePositionAxes = Axes.X;
+ X = (float)tick.StartTime;
+
+ FillMode = FillMode.Fit;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
+ Width *= Parent.RelativeChildSize.X;
}
protected override TaikoPiece CreateMainPiece() => new TickPiece
@@ -41,17 +53,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (state)
{
case ArmedState.Hit:
- Content.ScaleTo(0, 100, EasingTypes.OutQuint);
+ Content.ScaleTo(0, 100, Easing.OutQuint);
break;
}
}
- protected override void UpdateScrollPosition(double time)
- {
- // Ticks don't move
- }
-
- protected override bool HandleKeyPress(Key key)
+ public override bool OnPressed(TaikoAction action)
{
return Judgement.Result == HitResult.None && UpdateJudgement(true);
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index a4a46e3b48..5fcae7d0f4 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -16,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
///
/// A list of keys which can result in hits for this HitObject.
///
- protected abstract Key[] HitKeys { get; }
+ protected abstract TaikoAction[] HitActions { get; }
///
/// Whether the last key pressed is a valid hit key.
@@ -26,6 +25,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected DrawableHit(Hit hit)
: base(hit)
{
+ FillMode = FillMode.Fit;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
+ Width *= Parent.RelativeChildSize.X;
}
protected override void CheckJudgement(bool userTriggered)
@@ -53,57 +61,55 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Judgement.Result = HitResult.Miss;
}
- protected override bool HandleKeyPress(Key key)
+ public override bool OnPressed(TaikoAction action)
{
if (Judgement.Result != HitResult.None)
return false;
- validKeyPressed = HitKeys.Contains(key);
+ validKeyPressed = HitActions.Contains(action);
return UpdateJudgement(true);
}
protected override void UpdateState(ArmedState state)
{
- Delay(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true);
-
var circlePiece = MainPiece as CirclePiece;
+ circlePiece?.FlashBox.FinishTransforms();
- circlePiece?.FlashBox.Flush();
-
- switch (State)
+ using (BeginDelayedSequence(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true))
{
- case ArmedState.Idle:
- Delay(HitObject.HitWindowMiss);
- break;
- case ArmedState.Miss:
- FadeOut(100);
- break;
- case ArmedState.Hit:
- FadeOut(600);
+ switch (State)
+ {
+ case ArmedState.Idle:
+ this.Delay(HitObject.HitWindowMiss).Expire();
+ break;
+ case ArmedState.Miss:
+ this.FadeOut(100)
+ .Expire();
+ break;
+ case ArmedState.Hit:
+ var flash = circlePiece?.FlashBox;
+ if (flash != null)
+ {
+ flash.FadeTo(0.9f);
+ flash.FadeOut(300);
+ }
- var flash = circlePiece?.FlashBox;
- if (flash != null)
- {
- flash.FadeTo(0.9f);
- flash.FadeOut(300);
- }
+ const float gravity_time = 300;
+ const float gravity_travel_height = 200;
+ Content.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
- FadeOut(800);
+ this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out)
+ .Then()
+ .MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In);
- const float gravity_time = 300;
- const float gravity_travel_height = 200;
+ this.FadeOut(800)
+ .Expire();
- Content.ScaleTo(0.8f, gravity_time * 2, EasingTypes.OutQuad);
-
- MoveToY(-gravity_travel_height, gravity_time, EasingTypes.Out);
- Delay(gravity_time, true);
- MoveToY(gravity_travel_height * 2, gravity_time * 2, EasingTypes.In);
- break;
+ break;
+ }
}
-
- Expire();
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
index 1c6b12ea43..256c50d33a 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
@@ -3,11 +3,8 @@
using System;
using System.Linq;
-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
{
@@ -21,15 +18,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private double firstHitTime;
private bool firstKeyHeld;
- private Key firstHitKey;
+ private TaikoAction firstHitAction;
protected DrawableHitStrong(Hit hit)
: base(hit)
{
}
- protected override TaikoPiece CreateMainPiece() => new CirclePiece(true);
-
protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement();
protected override void CheckJudgement(bool userTriggered)
@@ -49,18 +44,26 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Judgement.SecondHit = true;
}
- protected override bool HandleKeyPress(Key key)
+ public override bool OnReleased(TaikoAction action)
+ {
+ if (action == firstHitAction)
+ firstKeyHeld = false;
+ return base.OnReleased(action);
+ }
+
+ public override bool OnPressed(TaikoAction action)
{
// Check if we've handled the first key
if (Judgement.Result == HitResult.None)
{
// First key hasn't been handled yet, attempt to handle it
- bool handled = base.HandleKeyPress(key);
+ bool handled = base.OnPressed(action);
if (handled)
{
firstHitTime = Time.Current;
- firstHitKey = key;
+ firstHitAction = action;
+ firstKeyHeld = true;
}
return handled;
@@ -71,22 +74,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return false;
// Don't handle represses of the first key
- if (firstHitKey == key)
+ if (firstHitAction == action)
return false;
- // Don't handle invalid hit key presses
- if (!HitKeys.Contains(key))
+ // Don't handle invalid hit action presses
+ if (!HitActions.Contains(action))
return false;
// Assume the intention was to hit the strong hit with both keys only if the first key is still being held down
return firstKeyHeld && UpdateJudgement(true);
}
-
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
- {
- firstKeyHeld = state.Keyboard.Keys.Contains(firstHitKey);
-
- return base.OnKeyDown(state, args);
- }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
index 20e8d36105..9f0cd3e061 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
@@ -4,13 +4,12 @@
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableRimHit : DrawableHit
{
- protected override Key[] HitKeys { get; } = { Key.D, Key.K };
+ protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
public DrawableRimHit(Hit hit)
: base(hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs
index 4b1bb62bab..294198f3ee 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs
@@ -4,13 +4,12 @@
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableRimHitStrong : DrawableHitStrong
{
- protected override Key[] HitKeys { get; } = { Key.D, Key.K };
+ protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
public DrawableRimHitStrong(Hit hit)
: base(hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index 37efd8aba4..fc3bc82520 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -7,14 +7,13 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Graphics;
-using OpenTK.Input;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -35,11 +34,9 @@ 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;
+ private readonly TaikoAction[] rimActions = { TaikoAction.LeftRim, TaikoAction.RightRim };
+ private readonly TaikoAction[] centreActions = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
+ private TaikoAction[] lastAction;
///
/// The amount of times the user has hit this swell.
@@ -52,93 +49,92 @@ 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()
- }
}
}
}
- };
+ });
- circlePiece.KiaiMode = HitObject.Kiai;
+ MainPiece.Add(symbol = new SwellSymbolPiece());
}
[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);
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
+ Width *= Parent.RelativeChildSize.X;
+ }
+
protected override void CheckJudgement(bool userTriggered)
{
if (userTriggered)
@@ -147,13 +143,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
var completion = (float)userHits / HitObject.RequiredHits;
- expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50);
- using (expandingRing.BeginDelayedSequence(50))
- expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint);
+ expandingRing
+ .FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50)
+ .Then()
+ .FadeTo(completion / 8, 2000, Easing.OutQuint);
- symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, EasingTypes.OutQuint);
+ symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, Easing.OutQuint);
- expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint);
+ expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
if (userHits == HitObject.RequiredHits)
{
@@ -180,44 +177,40 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void UpdateState(ArmedState state)
{
const float preempt = 100;
-
- Delay(HitObject.StartTime - Time.Current - preempt, true);
-
- targetRing.ScaleTo(target_ring_scale, preempt * 4, EasingTypes.OutQuint);
-
- Delay(preempt, true);
-
- Delay(Judgement.TimeOffset + HitObject.Duration, true);
-
const float out_transition_time = 300;
+ double untilStartTime = HitObject.StartTime - Time.Current;
+ double untilJudgement = untilStartTime + Judgement.TimeOffset + HitObject.Duration;
+
+ targetRing.Delay(untilStartTime - preempt).ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint);
+ this.Delay(untilJudgement).FadeOut(out_transition_time, Easing.Out);
+
switch (state)
{
case ArmedState.Hit:
- bodyContainer.ScaleTo(1.4f, out_transition_time);
+ bodyContainer.Delay(untilJudgement).ScaleTo(1.4f, out_transition_time);
break;
}
- FadeOut(out_transition_time, EasingTypes.Out);
-
Expire();
}
- protected override void UpdateScrollPosition(double time)
+ protected override void Update()
{
- // Make the swell stop at the hit target
- double t = Math.Min(HitObject.StartTime, time);
+ base.Update();
+ // Make the swell stop at the hit target
+ X = (float)Math.Max(Time.Current, HitObject.StartTime);
+
+ double t = Math.Min(HitObject.StartTime, Time.Current);
if (t == HitObject.StartTime && !hasStarted)
{
OnStart?.Invoke();
hasStarted = true;
}
-
- base.UpdateScrollPosition(t);
}
- protected override bool HandleKeyPress(Key key)
+ public override bool OnPressed(TaikoAction action)
{
if (Judgement.Result != HitResult.None)
return false;
@@ -227,12 +220,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return false;
// Find the keyset which this key corresponds to
- var keySet = rimKeys.Contains(key) ? rimKeys : centreKeys;
+ var keySet = rimActions.Contains(action) ? rimActions : centreActions;
// Ensure alternating keysets
- if (keySet == lastKeySet)
+ if (keySet == lastAction)
return false;
- lastKeySet = keySet;
+ lastAction = keySet;
UpdateJudgement(true);
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index 24aa366944..765002fb34 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -1,35 +1,23 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Collections.Generic;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using OpenTK;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
- public abstract class DrawableTaikoHitObject : DrawableHitObject
- where TaikoHitType : TaikoHitObject
+ public abstract class DrawableTaikoHitObject
+ : DrawableScrollingHitObject, IKeyBindingHandler
+ where TaikoHitType : TaikoHitObject
{
- ///
- /// A list of keys which this hit object will accept. These are the standard Taiko keys for now.
- /// These should be moved to bindings later.
- ///
- private readonly List validKeys = new List(new[] { Key.D, Key.F, Key.J, Key.K });
-
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,54 +28,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Anchor = Anchor.CentreLeft;
Origin = Anchor.Custom;
- AutoSizeAxes = Axes.Both;
-
- RelativePositionAxes = Axes.X;
-
- AddInternal(bodyContainer = new Container
- {
- AutoSizeAxes = Axes.Both,
- Children = new[]
- {
- MainPiece = CreateMainPiece()
- }
- });
+ RelativeSizeAxes = Axes.Both;
+ Size = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE);
+ Add(MainPiece = CreateMainPiece());
MainPiece.KiaiMode = HitObject.Kiai;
-
- LifetimeStart = HitObject.StartTime - HitObject.ScrollTime * 2;
}
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
- /// a time value and the HitObject's StartTime.
- ///
- ///
- protected virtual void UpdateScrollPosition(double time) => X = (float)((HitObject.StartTime - time) / HitObject.ScrollTime);
+ public abstract bool OnPressed(TaikoAction action);
- protected override void Update()
- {
- UpdateScrollPosition(Time.Current);
- }
-
- protected virtual bool HandleKeyPress(Key key) => false;
-
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
- {
- // Make sure we don't handle held-down keys
- if (args.Repeat)
- return false;
-
- // Check if we've pressed a valid taiko key
- if (!validKeys.Contains(args.Key))
- return false;
-
- // Handle it!
- return HandleKeyPress(args.Key);
- }
+ public virtual bool OnReleased(TaikoAction action) => false;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs
index ddf1492ecc..f4c78251d0 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs
@@ -3,27 +3,32 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using OpenTK;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{
///
/// 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 3ea05b6558..ba717371dd 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
@@ -4,7 +4,7 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Backgrounds;
using OpenTK.Graphics;
using osu.Game.Beatmaps.ControlPoints;
@@ -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,11 +63,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
public Box FlashBox;
- public CirclePiece(bool isStrong = false)
+ public CirclePiece()
{
EarlyActivationMilliseconds = pre_beat_transition_time;
- AddInternal(new Drawable[]
+ AddRangeInternal(new Drawable[]
{
background = new CircularContainer
{
@@ -120,35 +119,19 @@ 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;
private void resetEdgeEffects()
{
- background.EdgeEffect = new EdgeEffect
+ background.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = AccentColour.Opacity(KiaiMode ? edge_alpha_kiai : 1f),
@@ -166,9 +149,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
double duration = timingPoint.BeatLength * 2;
- background.FadeEdgeEffectTo(1, pre_beat_transition_time, EasingTypes.OutQuint);
- using (background.BeginDelayedSequence(pre_beat_transition_time))
- background.FadeEdgeEffectTo(edge_alpha_kiai, duration, EasingTypes.OutQuint);
+ background
+ .FadeEdgeEffectTo(1, pre_beat_transition_time, Easing.OutQuint)
+ .Then()
+ .FadeEdgeEffectTo(edge_alpha_kiai, duration, Easing.OutQuint);
}
}
}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs
index f607e2040f..4642e318c4 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs
@@ -1,25 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{
public class ElongatedCirclePiece : CirclePiece
{
- ///
- /// As we are being used to define the absolute size of hits, we need to be given a relative reference of our containing playfield container.
- ///
- public Func PlayfieldLengthReference;
-
- ///
- /// The length of this piece as a multiple of the value returned by
- ///
- public float Length;
-
- public ElongatedCirclePiece(bool isStrong = false) : base(isStrong)
+ public ElongatedCirclePiece()
{
+ RelativeSizeAxes = Axes.Y;
}
protected override void Update()
@@ -34,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
Right = padding,
};
- Width = (PlayfieldLengthReference?.Invoke() ?? 0) * Length + DrawHeight;
+ Width = Parent.DrawSize.X + DrawHeight;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs
index 4146edbdf7..60224a291d 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs
@@ -3,9 +3,9 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{
@@ -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 1a0d0156e8..211bf910a4 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs
@@ -3,9 +3,9 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{
@@ -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/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
index 18e3016fc3..526d23f51a 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
@@ -5,8 +5,8 @@ using osu.Game.Rulesets.Objects.Types;
using System;
using System.Collections.Generic;
using System.Linq;
-using osu.Game.Database;
using osu.Game.Audio;
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Taiko.Objects
@@ -80,7 +80,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
ret.Add(new DrumRollTick
{
FirstTick = first,
- ScrollTime = ScrollTime,
TickSpacing = tickSpacing,
StartTime = t,
IsStrong = IsStrong,
diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs
index f31472d0fd..03b9be4157 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs
@@ -1,8 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Database;
namespace osu.Game.Rulesets.Taiko.Objects
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
index 79bb901112..f1c0afc675 100644
--- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
@@ -1,44 +1,28 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Database;
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;
-
- ///
- /// The time taken from the initial (off-screen) spawn position to the centre of the hit target for a of 1000ms.
- ///
- private const double scroll_time = 6000;
-
- ///
- /// Our adjusted taking into consideration local and other speed multipliers.
- ///
- public double ScrollTime;
+ public const float DEFAULT_STRONG_SIZE = DEFAULT_SIZE * STRONG_SCALE;
///
/// Whether this HitObject is a "strong" type.
@@ -55,12 +39,8 @@ namespace osu.Game.Rulesets.Taiko.Objects
{
base.ApplyDefaults(controlPointInfo, difficulty);
- TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
- DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime);
- ScrollTime = scroll_time * (timingPoint.BeatLength * difficultyPoint.SpeedMultiplier / 1000) / difficulty.SliderMultiplier;
-
Kiai |= effectPoint.KiaiMode;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index f5e2094cbf..647a1381c6 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -3,7 +3,6 @@
using System;
using osu.Game.Beatmaps;
-using osu.Game.Database;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
@@ -114,8 +113,8 @@ namespace osu.Game.Rulesets.Taiko.Scoring
{
}
- public TaikoScoreProcessor(HitRenderer hitRenderer)
- : base(hitRenderer)
+ public TaikoScoreProcessor(RulesetContainer rulesetContainer)
+ : base(rulesetContainer)
{
}
diff --git a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs
index 33e9510f1c..35f849c704 100644
--- a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs
@@ -135,6 +135,6 @@ namespace osu.Game.Rulesets.Taiko
return difficulty;
}
- protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter();
+ protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter(true);
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
new file mode 100644
index 0000000000..1e3c0fbcf2
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
@@ -0,0 +1,29 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.ComponentModel;
+using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Rulesets.Taiko
+{
+ public class TaikoInputManager : RulesetInputManager
+ {
+ public TaikoInputManager(RulesetInfo ruleset)
+ : base(ruleset, 0, SimultaneousBindingMode.Unique)
+ {
+ }
+ }
+
+ public enum TaikoAction
+ {
+ [Description("Left (Rim)")]
+ LeftRim,
+ [Description("Left (Centre)")]
+ LeftCentre,
+ [Description("Right (Centre)")]
+ RightCentre,
+ [Description("Right (Rim)")]
+ RightRim
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index 7c169f820b..f457732085 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -1,23 +1,35 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Mods;
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;
+using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Taiko
{
public class TaikoRuleset : Ruleset
{
- public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoHitRenderer(beatmap, isForCurrentRuleset);
+ public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoRulesetContainer(this, beatmap, isForCurrentRuleset);
+
+ public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[]
+ {
+ new KeyBinding(InputKey.D, TaikoAction.LeftRim),
+ new KeyBinding(InputKey.F, TaikoAction.LeftCentre),
+ new KeyBinding(InputKey.J, TaikoAction.RightCentre),
+ new KeyBinding(InputKey.K, TaikoAction.RightRim),
+ new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre),
+ new KeyBinding(InputKey.MouseLeft, TaikoAction.RightCentre),
+ new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim),
+ new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim),
+ };
public override IEnumerable GetModsFor(ModType type)
{
@@ -28,7 +40,14 @@ namespace osu.Game.Rulesets.Taiko
{
new TaikoModEasy(),
new TaikoModNoFail(),
- new TaikoModHalfTime(),
+ new MultiMod
+ {
+ Mods = new Mod[]
+ {
+ new TaikoModHalfTime(),
+ new TaikoModDaycore(),
+ },
+ },
};
case ModType.DifficultyIncrease:
@@ -76,22 +95,21 @@ 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 IEnumerable CreateGameplayKeys() => new KeyCounter[]
- {
- new KeyCounterKeyboard(Key.D),
- new KeyCounterKeyboard(Key.F),
- new KeyCounterKeyboard(Key.J),
- new KeyCounterKeyboard(Key.K)
- };
+ public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap);
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor();
public override int LegacyID => 1;
+
+ public TaikoRuleset(RulesetInfo rulesetInfo)
+ : base(rulesetInfo)
+ {
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
index 08fd8dbecc..779471b8dc 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
@@ -6,6 +6,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
+using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Taiko.UI
{
@@ -47,7 +48,7 @@ namespace osu.Game.Rulesets.Taiko.UI
switch (Judgement.Result)
{
case HitResult.Hit:
- MoveToY(-100, 500);
+ this.MoveToY(-100, 500);
break;
}
diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs
index c0c329c870..cb849a11c7 100644
--- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs
@@ -6,7 +6,7 @@ using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
@@ -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;
@@ -62,8 +63,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{
base.LoadComplete();
- ScaleTo(3f, 1000, EasingTypes.OutQuint);
- FadeOut(500);
+ this.ScaleTo(3f, 1000, Easing.OutQuint);
+ this.FadeOut(500);
Expire();
}
@@ -73,7 +74,7 @@ namespace osu.Game.Rulesets.Taiko.UI
///
public void VisualiseSecondHit()
{
- 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 fde2623246..8f3b6840f3 100644
--- a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs
+++ b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs
@@ -5,7 +5,7 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.UI
@@ -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 999d76ab0b..659f421ebe 100644
--- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
+++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
@@ -3,13 +3,12 @@
using System;
using OpenTK;
-using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Input;
+using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Taiko.UI
@@ -21,9 +20,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,9 +33,10 @@ 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
+ RimAction = TaikoAction.LeftRim,
+ CentreAction = TaikoAction.LeftCentre
},
new TaikoHalfDrum(true)
{
@@ -43,10 +44,10 @@ 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
+ RimAction = TaikoAction.RightRim,
+ CentreAction = TaikoAction.RightCentre
}
};
}
@@ -54,17 +55,17 @@ namespace osu.Game.Rulesets.Taiko.UI
///
/// A half-drum. Contains one centre and one rim hit.
///
- private class TaikoHalfDrum : Container
+ private class TaikoHalfDrum : Container, IKeyBindingHandler
{
///
/// The key to be used for the rim of the half-drum.
///
- public Key RimKey;
+ public TaikoAction RimAction;
///
/// The key to be used for the centre of the half-drum.
///
- public Key CentreKey;
+ public TaikoAction CentreAction;
private readonly Sprite rim;
private readonly Sprite rimHit;
@@ -122,20 +123,17 @@ namespace osu.Game.Rulesets.Taiko.UI
centreHit.Colour = colours.Pink;
}
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ public bool OnPressed(TaikoAction action)
{
- if (args.Repeat)
- return false;
-
Drawable target = null;
Drawable back = null;
- if (args.Key == CentreKey)
+ if (action == CentreAction)
{
target = centreHit;
back = centre;
}
- else if (args.Key == RimKey)
+ else if (action == RimAction)
{
target = rimHit;
back = rim;
@@ -149,19 +147,23 @@ namespace osu.Game.Rulesets.Taiko.UI
const float down_time = 40;
const float up_time = 1000;
- back.ScaleTo(target.Scale.X - scale_amount, down_time, EasingTypes.OutQuint);
- back.Delay(down_time);
- back.ScaleTo(1, up_time, EasingTypes.OutQuint);
+ back.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint)
+ .Then()
+ .ScaleTo(1, up_time, Easing.OutQuint);
- target.ScaleTo(target.Scale.X - scale_amount, down_time, EasingTypes.OutQuint);
- target.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, EasingTypes.OutQuint);
- target.Delay(down_time);
- target.ScaleTo(1, up_time, EasingTypes.OutQuint);
- target.FadeOut(up_time, EasingTypes.OutQuint);
+ target.Animate(
+ t => t.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint),
+ t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.OutQuint)
+ ).Then(
+ t => t.ScaleTo(1, up_time, Easing.OutQuint),
+ t => t.FadeOut(up_time, Easing.OutQuint)
+ );
}
return false;
}
+
+ public bool OnReleased(TaikoAction action) => false;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs
index e0da3ed3db..bac956a25b 100644
--- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs
@@ -5,7 +5,7 @@ using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
@@ -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;
@@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Taiko.UI
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- EdgeEffect = new EdgeEffect
+ EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = isRim ? colours.BlueDarker : colours.PinkDarker,
@@ -59,8 +59,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{
base.LoadComplete();
- ScaleTo(new Vector2(1, 3f), 500, EasingTypes.OutQuint);
- FadeOut(250);
+ this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint);
+ this.FadeOut(250);
Expire();
}
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index c7bd4a6704..678de7f713 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -3,7 +3,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI;
using OpenTK;
@@ -15,37 +15,38 @@ 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
+ public class TaikoPlayfield : ScrollingPlayfield
{
///
- /// 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.
///
private const float left_area_size = 240;
- protected override Container Content => hitObjectContainer;
private readonly Container hitExplosionContainer;
private readonly Container kiaiExplosionContainer;
- private readonly Container barLineContainer;
private readonly Container judgementContainer;
- private readonly Container hitObjectContainer;
+ protected override Container Content => content;
+ private readonly Container content;
+
private readonly Container topLevelHitContainer;
+ private readonly Container barlineContainer;
+
private readonly Container overlayBackgroundContainer;
private readonly Container backgroundContainer;
@@ -53,115 +54,132 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Box background;
public TaikoPlayfield()
+ : base(Axes.X)
{
- AddInternal(new Drawable[]
+ 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 EdgeEffect
- {
- 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 before hit objects",
RelativeSizeAxes = Axes.Both,
- Margin = new MarginPadding { Left = left_area_size },
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Masking = true,
Children = new Drawable[]
{
- 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,
- },
- }
- },
- 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
+ hitExplosionContainer = new Container
{
RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit,
+ BlendingMode = BlendingMode.Additive,
},
- 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)
- },
- new Box
- {
- Anchor = Anchor.TopRight,
- RelativeSizeAxes = Axes.Y,
- Width = 10,
- ColourInfo = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
- },
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit
+ }
}
},
+ barlineContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }
+ },
+ content = new Container
+ {
+ Name = "Hit objects",
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Masking = true
+ },
+ 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
@@ -170,6 +188,8 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both,
}
});
+
+ VisibleTimeRange.Value = 6000;
}
[BackgroundDependencyLoader]
@@ -188,17 +208,16 @@ namespace osu.Game.Rulesets.Taiko.UI
base.Add(h);
+ var barline = h as DrawableBarLine;
+ if (barline != null)
+ barlineContainer.Add(barline.CreateProxy());
+
// Swells should be moved at the very top of the playfield when they reach the hit target
var swell = h as DrawableSwell;
if (swell != null)
swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy());
}
- public void AddBarLine(DrawableBarLine barLine)
- {
- barLineContainer.Add(barLine);
- }
-
public override void OnJudgement(DrawableHitObject judgedObject)
{
bool wasHit = judgedObject.Judgement.Result == HitResult.Hit;
@@ -219,7 +238,7 @@ namespace osu.Game.Rulesets.Taiko.UI
if (!secondHit)
{
- if (judgedObject.X >= -0.05f && !(judgedObject is DrawableSwell))
+ if (judgedObject.X >= -0.05f && judgedObject is DrawableHit)
{
// If we're far enough away from the left stage, we should bring outselves in front of it
topLevelHitContainer.Add(judgedObject.CreateProxy());
@@ -229,61 +248,9 @@ namespace osu.Game.Rulesets.Taiko.UI
if (judgedObject.HitObject.Kiai)
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject.Judgement, isRim));
-
}
else
hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit();
}
-
- ///
- /// 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.Rulesets.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
similarity index 84%
rename from osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs
rename to osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
index 662cace511..96e5df12cd 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
@@ -18,13 +18,14 @@ using osu.Game.Rulesets.Taiko.Replays;
using OpenTK;
using osu.Game.Rulesets.Beatmaps;
using System.Linq;
+using osu.Framework.Input;
namespace osu.Game.Rulesets.Taiko.UI
{
- public class TaikoHitRenderer : HitRenderer
+ public class TaikoRulesetContainer : ScrollingRulesetContainer
{
- public TaikoHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
- : base(beatmap, isForCurrentRuleset)
+ public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
+ : base(ruleset, beatmap, isForCurrentRuleset)
{
}
@@ -36,11 +37,6 @@ namespace osu.Game.Rulesets.Taiko.UI
private void loadBarLines()
{
- var taikoPlayfield = Playfield as TaikoPlayfield;
-
- if (taikoPlayfield == null)
- return;
-
TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1];
double lastHitTime = 1 + (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime;
@@ -72,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.UI
barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty);
bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0;
- taikoPlayfield.AddBarLine(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
+ Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
double bl = currentPoint.BeatLength;
if (bl < 800)
@@ -85,7 +81,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;
@@ -93,10 +89,11 @@ namespace osu.Game.Rulesets.Taiko.UI
return new Vector2(1, default_relative_height * aspectAdjust);
}
-
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
- protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter();
+ protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter(IsForCurrentRuleset);
+
+ public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
protected override Playfield CreatePlayfield() => new TaikoPlayfield
{
diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
index 8d6fcb503c..33748a267f 100644
--- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
+++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
@@ -33,8 +33,9 @@
false
-
- $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ True
@@ -85,12 +86,13 @@
+
-
+
diff --git a/osu.Game.Rulesets.Taiko/packages.config b/osu.Game.Rulesets.Taiko/packages.config
index dc059c684b..8add43d5d5 100644
--- a/osu.Game.Rulesets.Taiko/packages.config
+++ b/osu.Game.Rulesets.Taiko/packages.config
@@ -4,5 +4,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd .
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
-
+
\ No newline at end of file
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs
index 4814af984e..da3b448f74 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs
@@ -16,12 +16,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
[TestFixture]
public class OsuLegacyDecoderTest
{
- [OneTimeSetUpAttribute]
- public void SetUp()
- {
- OsuLegacyDecoder.Register();
- }
-
[Test]
public void TestDecodeMetadata()
{
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index 7fb01cedc0..ecaf0db096 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -10,8 +10,10 @@ using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Desktop.Platform;
using osu.Framework.Platform;
-using osu.Game.Database;
using osu.Game.IPC;
+using osu.Framework.Allocation;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
namespace osu.Game.Tests.Beatmaps.IO
{
@@ -26,15 +28,15 @@ namespace osu.Game.Tests.Beatmaps.IO
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new HeadlessGameHost())
{
- loadOsu(host);
+ var osu = loadOsu(host);
var temp = prepareTempCopy(osz_path);
Assert.IsTrue(File.Exists(temp));
- host.Dependencies.Get().Import(temp);
+ osu.Dependencies.Get().Import(temp);
- ensureLoaded(host);
+ ensureLoaded(osu);
Assert.IsFalse(File.Exists(temp));
}
@@ -49,7 +51,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(host.IsPrimaryInstance);
Assert.IsTrue(!client.IsPrimaryInstance);
- loadOsu(host);
+ var osu = loadOsu(host);
var temp = prepareTempCopy(osz_path);
@@ -59,7 +61,7 @@ namespace osu.Game.Tests.Beatmaps.IO
if (!importer.ImportAsync(temp).Wait(10000))
Assert.Fail(@"IPC took too long to send");
- ensureLoaded(host);
+ ensureLoaded(osu);
Assert.IsFalse(File.Exists(temp));
}
@@ -71,22 +73,20 @@ namespace osu.Game.Tests.Beatmaps.IO
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new HeadlessGameHost())
{
- loadOsu(host);
+ var osu = loadOsu(host);
var temp = prepareTempCopy(osz_path);
- Assert.IsTrue(File.Exists(temp));
+ Assert.IsTrue(File.Exists(temp), "Temporary file copy never substantiated");
using (File.OpenRead(temp))
- host.Dependencies.Get().Import(temp);
+ osu.Dependencies.Get().Import(temp);
- ensureLoaded(host);
-
- Assert.IsTrue(File.Exists(temp));
+ ensureLoaded(osu);
File.Delete(temp);
- Assert.IsFalse(File.Exists(temp));
+ Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't");
}
}
@@ -105,20 +105,21 @@ namespace osu.Game.Tests.Beatmaps.IO
Thread.Sleep(1);
//reset beatmap database (sqlite and storage backing)
- host.Dependencies.Get().Reset();
- host.Dependencies.Get().Reset();
+ osu.Dependencies.Get().Reset();
+ osu.Dependencies.Get().Reset();
return osu;
}
- private void ensureLoaded(GameHost host, int timeout = 10000)
+ private void ensureLoaded(OsuGameBase osu, int timeout = 60000)
{
IEnumerable resultSets = null;
+ var store = osu.Dependencies.Get();
+
Action waitAction = () =>
{
- while (!(resultSets = host.Dependencies.Get()
- .Query().Where(s => s.OnlineBeatmapSetID == 241526)).Any())
+ while (!(resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any())
Thread.Sleep(50);
};
@@ -134,15 +135,14 @@ namespace osu.Game.Tests.Beatmaps.IO
//if we don't re-check here, the set will be inserted but the beatmaps won't be present yet.
waitAction = () =>
{
- while ((resultBeatmaps = host.Dependencies.Get()
- .GetAllWithChildren(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12)
+ while ((resultBeatmaps = store.QueryBeatmaps(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12)
Thread.Sleep(50);
};
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout),
@"Beatmaps did not import to the database in allocated time");
- var set = host.Dependencies.Get().GetChildren(resultSets.First());
+ var set = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526).First();
Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count(),
$@"Incorrect database beatmap count post-import ({resultBeatmaps.Count()} but should be {set.Beatmaps.Count}).");
@@ -152,18 +152,17 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(set.Beatmaps.Count > 0);
- var beatmap = host.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
+ var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
- beatmap = host.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap;
+ beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
- beatmap = host.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap;
+ beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
- beatmap = host.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap;
+ beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
}
}
}
-
diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
index 03d09e24e0..7a7a8a58bc 100644
--- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
@@ -2,23 +2,18 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.IO;
+using System.Linq;
using NUnit.Framework;
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.IO;
using osu.Game.Tests.Resources;
using osu.Game.Beatmaps.Formats;
-using osu.Game.Database;
namespace osu.Game.Tests.Beatmaps.IO
{
[TestFixture]
public class OszArchiveReaderTest
{
- [OneTimeSetUpAttribute]
- public void SetUp()
- {
- OszArchiveReader.Register();
- }
-
[Test]
public void TestReadBeatmaps()
{
@@ -40,7 +35,7 @@ namespace osu.Game.Tests.Beatmaps.IO
"Soleily - Renatus (MMzz) [Muzukashii].osu",
"Soleily - Renatus (MMzz) [Oni].osu"
};
- var maps = reader.BeatmapFilenames;
+ var maps = reader.Filenames.ToArray();
foreach (var map in expected)
Assert.Contains(map, maps);
}
@@ -86,4 +81,3 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
}
-
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index b8fcb80aaf..8ec68b41be 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -30,11 +30,13 @@
false
-
- $(SolutionDir)\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll
+
+ $(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll
+ True
-
- $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ True
diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config
index 9972fb41a1..9ad76308d7 100644
--- a/osu.Game.Tests/packages.config
+++ b/osu.Game.Tests/packages.config
@@ -4,8 +4,8 @@ Copyright (c) 2007-2017 ppy Pty Ltd .
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
-
-
+
+
\ No newline at end of file
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 0368455b92..82777734bb 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -3,11 +3,11 @@
using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing;
-using osu.Game.Database;
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
{
@@ -46,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/Database/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs
similarity index 96%
rename from osu.Game/Database/BeatmapDifficulty.cs
rename to osu.Game/Beatmaps/BeatmapDifficulty.cs
index cf1305f705..7c2294cae9 100644
--- a/osu.Game/Database/BeatmapDifficulty.cs
+++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs
@@ -3,7 +3,7 @@
using SQLite.Net.Attributes;
-namespace osu.Game.Database
+namespace osu.Game.Beatmaps
{
public class BeatmapDifficulty
{
@@ -39,4 +39,3 @@ namespace osu.Game.Database
}
}
}
-
diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
similarity index 85%
rename from osu.Game/Database/BeatmapInfo.cs
rename to osu.Game/Beatmaps/BeatmapInfo.cs
index 9f253f6055..ebf77bf9df 100644
--- a/osu.Game/Database/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.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 Newtonsoft.Json;
-using osu.Game.IO.Serialization;
-using SQLite.Net.Attributes;
-using SQLiteNetExtensions.Attributes;
using System;
using System.Linq;
+using Newtonsoft.Json;
+using osu.Game.IO.Serialization;
+using osu.Game.Rulesets;
+using SQLite.Net.Attributes;
+using SQLiteNetExtensions.Attributes;
-namespace osu.Game.Database
+namespace osu.Game.Beatmaps
{
public class BeatmapInfo : IEquatable, IJsonSerializable
{
@@ -48,9 +49,16 @@ namespace osu.Game.Database
public string Path { get; set; }
- [JsonProperty("file_md5")]
+ [JsonProperty("file_sha2")]
public string Hash { get; set; }
+ ///
+ /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.).
+ ///
+ [Indexed]
+ [JsonProperty("file_md5")]
+ public string MD5Hash { get; set; }
+
// General
public int AudioLeadIn { get; set; }
public bool Countdown { get; set; }
@@ -60,7 +68,7 @@ namespace osu.Game.Database
[ForeignKey(typeof(RulesetInfo))]
public int RulesetID { get; set; }
- [OneToOne(CascadeOperations = CascadeOperation.All)]
+ [OneToOne(CascadeOperations = CascadeOperation.CascadeRead)]
public RulesetInfo Ruleset { get; set; }
public bool LetterboxInBreaks { get; set; }
@@ -68,7 +76,7 @@ namespace osu.Game.Database
// 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]
@@ -94,11 +102,11 @@ namespace osu.Game.Database
}
public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
- BeatmapSet.Path == other.BeatmapSet.Path &&
+ BeatmapSet.Hash == other.BeatmapSet.Hash &&
(Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile;
public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
- BeatmapSet.Path == other.BeatmapSet.Path &&
+ BeatmapSet.Hash == other.BeatmapSet.Hash &&
(Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile;
}
}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
new file mode 100644
index 0000000000..1dcab6cb5d
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -0,0 +1,514 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using Ionic.Zip;
+using osu.Framework.Audio.Track;
+using osu.Framework.Extensions;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.IO.Stores;
+using osu.Framework.Logging;
+using osu.Framework.Platform;
+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;
+
+namespace osu.Game.Beatmaps
+{
+ ///
+ /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps.
+ ///
+ public class BeatmapManager
+ {
+ ///
+ /// Fired when a new becomes available in the database.
+ ///
+ public event Action BeatmapSetAdded;
+
+ ///
+ /// Fired when a is removed from the database.
+ ///
+ public event Action BeatmapSetRemoved;
+
+ ///
+ /// A default representation of a WorkingBeatmap to use when no beatmap is available.
+ ///
+ public WorkingBeatmap DefaultBeatmap { private get; set; }
+
+ private readonly Storage storage;
+
+ private readonly FileStore files;
+
+ private readonly SQLiteConnection connection;
+
+ private readonly RulesetStore rulesets;
+
+ private readonly BeatmapStore 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);
+ beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
+ beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
+
+ this.storage = storage;
+ this.files = files;
+ this.connection = connection;
+ this.rulesets = rulesets;
+
+ if (importHost != null)
+ ipc = new BeatmapIPCChannel(importHost, this);
+ }
+
+ ///
+ /// Import one or more from filesystem .
+ /// This will post a notification tracking import progress.
+ ///
+ /// 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
+ {
+ if (File.Exists(path))
+ File.Delete(path);
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})");
+ }
+ }
+ catch (Exception e)
+ {
+ e = e.InnerException ?? e;
+ 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 = null;
+
+ // let's only allow one concurrent import at a time for now.
+ lock (importLock)
+ connection.RunInTransaction(() => Import(set = importToStorage(archiveReader)));
+
+ return set;
+ }
+
+ ///
+ /// Import a beatmap from a .
+ ///
+ /// The beatmap to be imported.
+ public void Import(BeatmapSetInfo beatmapSetInfo)
+ {
+ // If we have an ID then we already exist in the database.
+ if (beatmapSetInfo.ID != 0) return;
+
+ lock (beatmaps)
+ beatmaps.Add(beatmapSetInfo);
+ }
+
+ ///
+ /// Delete a beatmap from the manager.
+ /// Is a no-op for already deleted beatmaps.
+ ///
+ /// The beatmap to delete.
+ public void Delete(BeatmapSetInfo beatmapSet)
+ {
+ lock (beatmaps)
+ if (!beatmaps.Delete(beatmapSet)) return;
+
+ if (!beatmapSet.Protected)
+ files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
+ }
+
+ ///
+ /// Returns a to a usable state if it has previously been deleted but not yet purged.
+ /// Is a no-op for already usable beatmaps.
+ ///
+ /// The beatmap to restore.
+ public void Undelete(BeatmapSetInfo beatmapSet)
+ {
+ lock (beatmaps)
+ if (!beatmaps.Undelete(beatmapSet)) return;
+
+ if (!beatmapSet.Protected)
+ files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
+ }
+
+ ///
+ /// Retrieve a instance for the provided
+ ///
+ /// The beatmap to lookup.
+ /// The currently loaded . Allows for optimisation where elements are shared with the new beatmap.
+ /// A instance correlating to the provided .
+ public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null)
+ {
+ if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
+ return DefaultBeatmap;
+
+ lock (beatmaps)
+ beatmaps.Populate(beatmapInfo);
+
+ if (beatmapInfo.BeatmapSet == null)
+ throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database.");
+
+ if (beatmapInfo.Metadata == null)
+ beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
+
+ WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(files.Store, beatmapInfo);
+
+ previous?.TransferTo(working);
+
+ return working;
+ }
+
+ ///
+ /// Reset the manager to an empty state.
+ ///
+ public void Reset()
+ {
+ lock (beatmaps)
+ beatmaps.Reset();
+ }
+
+ ///
+ /// Perform a lookup query on available s.
+ ///
+ /// The query.
+ /// The first result for the provided query, or null if no results were found.
+ public BeatmapSetInfo QueryBeatmapSet(Func query)
+ {
+ lock (beatmaps)
+ {
+ BeatmapSetInfo set = beatmaps.Query().FirstOrDefault(query);
+
+ if (set != null)
+ beatmaps.Populate(set);
+
+ return set;
+ }
+ }
+
+ ///
+ /// Perform a lookup query on available s.
+ ///
+ /// The query.
+ /// Results from the provided query.
+ public List QueryBeatmapSets(Expression> query)
+ {
+ lock (beatmaps) return beatmaps.QueryAndPopulate(query);
+ }
+
+ ///
+ /// Perform a lookup query on available s.
+ ///
+ /// The query.
+ /// The first result for the provided query, or null if no results were found.
+ public BeatmapInfo QueryBeatmap(Func query)
+ {
+ lock (beatmaps)
+ {
+ BeatmapInfo set = beatmaps.Query().FirstOrDefault(query);
+
+ if (set != null)
+ beatmaps.Populate(set);
+
+ return set;
+ }
+ }
+
+ ///
+ /// Perform a lookup query on available s.
+ ///
+ /// The query.
+ /// Results from the provided query.
+ public List QueryBeatmaps(Expression> query)
+ {
+ lock (beatmaps) return beatmaps.QueryAndPopulate(query);
+ }
+
+ ///
+ /// Creates an from a valid storage path.
+ ///
+ /// A file or folder path resolving the beatmap content.
+ /// A reader giving access to the beatmap's content.
+ private ArchiveReader getReaderFrom(string path)
+ {
+ if (ZipFile.IsZipFile(path))
+ return new OszArchiveReader(storage.GetStream(path));
+ else
+ return new LegacyFilesystemReader(path);
+ }
+
+ ///
+ /// Import a beamap into our local storage.
+ /// If the beatmap is already imported, the existing instance will be returned.
+ ///
+ /// The beatmap archive to be read.
+ /// The imported beatmap, or an existing instance if it is already present.
+ private BeatmapSetInfo importToStorage(ArchiveReader reader)
+ {
+ // let's make sure there are actually .osu files to import.
+ string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));
+ if (string.IsNullOrEmpty(mapName))
+ throw new InvalidOperationException("No beatmap files found in the map folder.");
+
+ // for now, concatenate all .osu files in the set to create a unique hash.
+ MemoryStream hashable = new MemoryStream();
+ foreach (string file in reader.Filenames.Where(f => f.EndsWith(".osu")))
+ using (Stream s = reader.GetStream(file))
+ s.CopyTo(hashable);
+
+ var hash = hashable.ComputeSHA2Hash();
+
+ // check if this beatmap has already been imported and exit early if so.
+ BeatmapSetInfo beatmapSet;
+ lock (beatmaps)
+ beatmapSet = beatmaps.QueryAndPopulate(b => b.Hash == hash).FirstOrDefault();
+
+ if (beatmapSet != null)
+ {
+ Undelete(beatmapSet);
+ return beatmapSet;
+ }
+
+ List fileInfos = new List();
+
+ // import files to manager
+ foreach (string file in reader.Filenames)
+ using (Stream s = reader.GetStream(file))
+ fileInfos.Add(new BeatmapSetFileInfo
+ {
+ Filename = file,
+ FileInfo = files.Add(s)
+ });
+
+ BeatmapMetadata metadata;
+
+ using (var stream = new StreamReader(reader.GetStream(mapName)))
+ metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
+
+ beatmapSet = new BeatmapSetInfo
+ {
+ OnlineBeatmapSetID = metadata.OnlineBeatmapSetID,
+ Beatmaps = new List(),
+ Hash = hash,
+ Files = fileInfos,
+ Metadata = metadata
+ };
+
+ var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu"));
+
+ foreach (var name in mapNames)
+ {
+ using (var raw = reader.GetStream(name))
+ using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit
+ using (var sr = new StreamReader(ms))
+ {
+ raw.CopyTo(ms);
+ ms.Position = 0;
+
+ var decoder = BeatmapDecoder.GetDecoder(sr);
+ Beatmap beatmap = decoder.Decode(sr);
+
+ beatmap.BeatmapInfo.Path = name;
+ beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
+ beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
+
+ // TODO: Diff beatmap metadata with set metadata and leave it here if necessary
+ beatmap.BeatmapInfo.Metadata = null;
+
+ // TODO: this should be done in a better place once we actually need to dynamically update it.
+ beatmap.BeatmapInfo.Ruleset = rulesets.Query().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID);
+ beatmap.BeatmapInfo.StarDifficulty = rulesets.Query().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID)?.CreateInstance()?.CreateDifficultyCalculator(beatmap)
+ .Calculate() ?? 0;
+
+ beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
+ }
+ }
+
+ return beatmapSet;
+ }
+
+ ///
+ /// Returns a list of all usable s.
+ ///
+ /// Whether returned objects should be pre-populated with all data.
+ /// A list of available .
+ public List GetAllUsableBeatmapSets(bool populate = true)
+ {
+ lock (beatmaps)
+ {
+ if (populate)
+ return beatmaps.QueryAndPopulate(b => !b.DeletePending).ToList();
+ else
+ return beatmaps.Query(b => !b.DeletePending).ToList();
+ }
+ }
+
+ protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap
+ {
+ private readonly IResourceStore store;
+
+ public BeatmapManagerWorkingBeatmap(IResourceStore store, BeatmapInfo beatmapInfo)
+ : base(beatmapInfo)
+ {
+ this.store = store;
+ }
+
+ protected override Beatmap GetBeatmap()
+ {
+ try
+ {
+ Beatmap beatmap;
+
+ BeatmapDecoder decoder;
+ using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
+ {
+ decoder = BeatmapDecoder.GetDecoder(stream);
+ beatmap = decoder.Decode(stream);
+ }
+
+ if (beatmap == null || BeatmapSetInfo.StoryboardFile == null)
+ return beatmap;
+
+ using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
+ decoder.Decode(stream, beatmap);
+
+
+ return beatmap;
+ }
+ catch { return null; }
+ }
+
+ private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => f.Filename == filename).FileInfo.StoragePath;
+
+ protected override Texture GetBackground()
+ {
+ if (Metadata?.BackgroundFile == null)
+ return null;
+
+ try
+ {
+ return new TextureStore(new RawTextureLoaderStore(store), false).Get(getPathForFile(Metadata.BackgroundFile));
+ }
+ catch { return null; }
+ }
+
+ protected override Track GetTrack()
+ {
+ try
+ {
+ var trackData = store.GetStream(getPathForFile(Metadata.AudioFile));
+ return trackData == null ? null : new TrackBass(trackData);
+ }
+ 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/Database/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs
similarity index 86%
rename from osu.Game/Database/BeatmapMetadata.cs
rename to osu.Game/Beatmaps/BeatmapMetadata.cs
index 04700b3caa..cc9a51b4e2 100644
--- a/osu.Game/Database/BeatmapMetadata.cs
+++ b/osu.Game/Beatmaps/BeatmapMetadata.cs
@@ -2,9 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
+using Newtonsoft.Json;
using SQLite.Net.Attributes;
-namespace osu.Game.Database
+namespace osu.Game.Beatmaps
{
public class BeatmapMetadata
{
@@ -17,8 +18,13 @@ namespace osu.Game.Database
public string TitleUnicode { get; set; }
public string Artist { get; set; }
public string ArtistUnicode { get; set; }
+
+ [JsonProperty(@"creator")]
public string Author { get; set; }
+
public string Source { get; set; }
+
+ [JsonProperty(@"tags")]
public string Tags { get; set; }
public int PreviewTime { get; set; }
public string AudioFile { get; set; }
@@ -35,4 +41,4 @@ namespace osu.Game.Database
Tags
}.Where(s => !string.IsNullOrEmpty(s)).ToArray();
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Database/BeatmapMetrics.cs b/osu.Game/Beatmaps/BeatmapMetrics.cs
similarity index 94%
rename from osu.Game/Database/BeatmapMetrics.cs
rename to osu.Game/Beatmaps/BeatmapMetrics.cs
index 25de0f0a8d..730cf635da 100644
--- a/osu.Game/Database/BeatmapMetrics.cs
+++ b/osu.Game/Beatmaps/BeatmapMetrics.cs
@@ -4,7 +4,7 @@
using System.Collections.Generic;
using Newtonsoft.Json;
-namespace osu.Game.Database
+namespace osu.Game.Beatmaps
{
///
/// Beatmap metrics based on acculumated online data from community plays.
diff --git a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs
new file mode 100644
index 0000000000..e8f40a7e07
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs
@@ -0,0 +1,25 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using Newtonsoft.Json;
+
+namespace osu.Game.Beatmaps
+{
+ ///
+ /// Beatmap info retrieved for previewing locally without having the beatmap downloaded.
+ ///
+ public class BeatmapOnlineInfo
+ {
+ ///
+ /// The amount of plays this beatmap has.
+ ///
+ [JsonProperty(@"playcount")]
+ public int PlayCount { get; set; }
+
+ ///
+ /// The amount of passes this beatmap has.
+ ///
+ [JsonProperty(@"passcount")]
+ public int PassCount { get; set; }
+ }
+}
diff --git a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs
new file mode 100644
index 0000000000..a05362b32d
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs
@@ -0,0 +1,27 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.IO;
+using SQLite.Net.Attributes;
+using SQLiteNetExtensions.Attributes;
+
+namespace osu.Game.Beatmaps
+{
+ public class BeatmapSetFileInfo
+ {
+ [PrimaryKey, AutoIncrement]
+ public int ID { get; set; }
+
+ [ForeignKey(typeof(BeatmapSetInfo)), NotNull]
+ public int BeatmapSetInfoID { get; set; }
+
+ [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/Database/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs
similarity index 69%
rename from osu.Game/Database/BeatmapSetInfo.cs
rename to osu.Game/Beatmaps/BeatmapSetInfo.cs
index 0875d3c01f..f47affcab8 100644
--- a/osu.Game/Database/BeatmapSetInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs
@@ -6,7 +6,7 @@ using System.Linq;
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
-namespace osu.Game.Database
+namespace osu.Game.Beatmaps
{
public class BeatmapSetInfo
{
@@ -24,6 +24,9 @@ namespace osu.Game.Database
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List Beatmaps { get; set; }
+ [Ignore]
+ public BeatmapSetOnlineInfo OnlineInfo { get; set; }
+
public double MaxStarDifficulty => Beatmaps.Max(b => b.StarDifficulty);
[Indexed]
@@ -31,9 +34,11 @@ namespace osu.Game.Database
public string Hash { get; set; }
- public string Path { get; set; }
+ public string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename;
- public string StoryboardFile { get; set; }
+ [OneToMany(CascadeOperations = CascadeOperation.All)]
+ public List Files { get; set; }
+
+ public bool Protected { get; set; }
}
}
-
diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
new file mode 100644
index 0000000000..e5a1984f50
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
@@ -0,0 +1,55 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using Newtonsoft.Json;
+
+namespace osu.Game.Beatmaps
+{
+ ///
+ /// Beatmap set info retrieved for previewing locally without having the set downloaded.
+ ///
+ public class BeatmapSetOnlineInfo
+ {
+ ///
+ /// The different sizes of cover art for this beatmap set.
+ ///
+ [JsonProperty(@"covers")]
+ public BeatmapSetOnlineCovers Covers { get; set; }
+
+ ///
+ /// A small sample clip of this beatmap set's song.
+ ///
+ [JsonProperty(@"previewUrl")]
+ public string Preview { get; set; }
+
+ ///
+ /// The amount of plays this beatmap set has.
+ ///
+ [JsonProperty(@"play_count")]
+ public int PlayCount { get; set; }
+
+ ///
+ /// The amount of people who have favourited this beatmap set.
+ ///
+ [JsonProperty(@"favourite_count")]
+ public int FavouriteCount { get; set; }
+ }
+
+ public class BeatmapSetOnlineCovers
+ {
+ public string CoverLowRes { get; set; }
+
+ [JsonProperty(@"cover@2x")]
+ public string Cover { get; set; }
+
+ public string CardLowRes { get; set; }
+
+ [JsonProperty(@"card@2x")]
+ public string Card { get; set; }
+
+ public string ListLowRes { get; set; }
+
+ [JsonProperty(@"list@2x")]
+ public string List { get; set; }
+ }
+}
diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs
new file mode 100644
index 0000000000..8212712bf9
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapStore.cs
@@ -0,0 +1,143 @@
+// 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.Database;
+using SQLite.Net;
+using SQLiteNetExtensions.Extensions;
+
+namespace osu.Game.Beatmaps
+{
+ ///
+ /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing
+ ///
+ public class BeatmapStore : DatabaseBackedStore
+ {
+ public event Action BeatmapSetAdded;
+ public event Action BeatmapSetRemoved;
+
+ ///
+ /// The current version of this store. Used for migrations (see ).
+ /// The initial version is 1.
+ ///
+ protected override int StoreVersion => 3;
+
+ public BeatmapStore(SQLiteConnection connection)
+ : base(connection)
+ {
+ }
+
+ protected override Type[] ValidTypes => new[]
+ {
+ typeof(BeatmapSetInfo),
+ typeof(BeatmapInfo),
+ typeof(BeatmapMetadata),
+ typeof(BeatmapDifficulty),
+ };
+
+ protected override void Prepare(bool reset = false)
+ {
+ if (reset)
+ {
+ Connection.DropTable();
+ Connection.DropTable();
+ Connection.DropTable();
+ Connection.DropTable();
+ Connection.DropTable();
+ }
+
+ Connection.CreateTable();
+ Connection.CreateTable();
+ Connection.CreateTable