diff --git a/README.md b/README.md
index 9d19f16ebd..a1f478e39a 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,32 @@
# osu! [![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy)
-Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the beginning of an open era!
+Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commonly known by the codename "osu!lazer". Pew pew.
# Status
-This is still heavily under development and is not intended for end-user use. This repository is intended for developer collaboration. You're welcome to try and use it but please do not submit bug reports without a patch. Please do not ask for help building or using this software.
+This project is still heavily under development, but is in a state where users are encouraged to try it out and keep it installed alongside the stable osu! client. It will continue to evolve over the coming months and hopefully bring some new unique features to the table.
+
+We are accepting bug reports (please report with as much detail as possible). Feature requests are welcome as long as you read and understand the contribution guidelines listed below.
# Requirements
-- A desktop platform that can compile .NET 4.7.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here.
+- A desktop platform with the [.NET Core SDK 2.1](https://www.microsoft.com/net/learn/get-started) or higher installed.
+- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio Code](https://code.visualstudio.com/) (with the C# plugin installed) or [Jetbrains Rider](https://www.jetbrains.com/rider/) (commercial).
-# Getting Started
-- Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`)
-- Build in your IDE of choice (recommended IDEs automatically restore nuget packages; if you are using an alternative make sure to `nuget restore`)
+# Building and running
+
+If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled (download and run the install executable for your platform).
+
+Clone the repository including submodules
+
+`git clone --recurse-submodules https://github.com/ppy/osu`
+
+Build and run
+
+- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included)
+- From command line using `dotnet run --project osu.Desktop`
+
+If you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`). Visual Studio Code users must run `Restore` task from debug tab before attempt to build.
# Contributing
diff --git a/appveyor.yml b/appveyor.yml
index ac6d6ebff8..8c06b42fd2 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -2,20 +2,21 @@ clone_depth: 1
version: '{branch}-{build}'
image: Visual Studio 2017
configuration: Debug
+cache:
+ - C:\ProgramData\chocolatey\bin -> appveyor.yml
+ - C:\ProgramData\chocolatey\lib -> appveyor.yml
install:
- cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y
- cmd: choco install nvika -y
- - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.5/CodeFileSanity.exe
+ - cmd: dotnet tool install CodeFileSanity --version 0.0.16 --global
before_build:
- - cmd: CodeFileSanity.exe
+ - cmd: CodeFileSanity
- cmd: nuget restore -verbosity quiet
-environment:
- TargetFramework: net471
build:
project: osu.sln
parallel: true
verbosity: minimal
after_build:
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
- - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
\ No newline at end of file
+ - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml
index 0247714cdf..6d8d95e773 100644
--- a/appveyor_deploy.yml
+++ b/appveyor_deploy.yml
@@ -16,17 +16,15 @@ build_script:
- cd osu-deploy
- nuget restore -verbosity quiet
- msbuild osu.Desktop.Deploy.csproj
- - cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\net471\osu.Desktop.Deploy.exe.config
- - cd bin\Debug\net471\
- - osu.Desktop.Deploy.exe %code_signing_password% %APPVEYOR_REPO_TAG_NAME%
+ - cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\netcoreapp2.1\osu.Desktop.Deploy.dll.config
+ - dotnet bin/Debug/netcoreapp2.1/osu.Desktop.Deploy.dll %code_signing_password% %APPVEYOR_REPO_TAG_NAME%
environment:
- TargetFramework: net471
decode_secret:
secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY=
code_signing_password:
secure: 34tLNqvjmmZEi97MLKfrnQ==
artifacts:
- - path: 'Releases\*'
+ - path: 'osu-deploy/releases/*'
deploy:
- provider: Environment
name: github
\ No newline at end of file
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index 64adcecba4..79ac24a1da 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -12,6 +12,8 @@ using osu.Framework.Platform;
using osu.Game;
using OpenTK.Input;
using Microsoft.Win32;
+using osu.Desktop.Updater;
+using osu.Framework;
using osu.Framework.Platform.Windows;
namespace osu.Desktop
@@ -38,6 +40,51 @@ namespace osu.Desktop
}
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ if (!noVersionOverlay)
+ {
+ LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
+ {
+ Add(v);
+ v.State = Visibility.Visible;
+ });
+
+ if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
+ Add(new SquirrelUpdateManager());
+ else
+ Add(new SimpleUpdateManager());
+ }
+ }
+
+ public override void SetHost(GameHost host)
+ {
+ base.SetHost(host);
+ var desktopWindow = host.Window as DesktopGameWindow;
+ if (desktopWindow != null)
+ {
+ desktopWindow.CursorState |= CursorState.Hidden;
+
+ desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
+ desktopWindow.Title = Name;
+
+ desktopWindow.FileDrop += fileDrop;
+ }
+ }
+
+ private void fileDrop(object sender, FileDropEventArgs e)
+ {
+ var filePaths = new[] { e.FileName };
+
+ var firstExtension = Path.GetExtension(filePaths.First());
+
+ if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
+
+ Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
+ }
+
///
/// A method of accessing an osu-stable install in a controlled fashion.
///
@@ -77,45 +124,5 @@ namespace osu.Desktop
{
}
}
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- if (!noVersionOverlay)
- {
- LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
- {
- Add(v);
- v.State = Visibility.Visible;
- });
- }
- }
-
- public override void SetHost(GameHost host)
- {
- base.SetHost(host);
- var desktopWindow = host.Window as DesktopGameWindow;
- if (desktopWindow != null)
- {
- desktopWindow.CursorState |= CursorState.Hidden;
-
- desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
- desktopWindow.Title = Name;
-
- desktopWindow.FileDrop += fileDrop;
- }
- }
-
- private void fileDrop(object sender, FileDropEventArgs e)
- {
- var filePaths = new[] { e.FileName };
-
- var firstExtension = Path.GetExtension(filePaths.First());
-
- if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
-
- Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
- }
}
}
diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs
index bc1faec822..1129969694 100644
--- a/osu.Desktop/Overlays/VersionManager.cs
+++ b/osu.Desktop/Overlays/VersionManager.cs
@@ -91,10 +91,6 @@ namespace osu.Desktop.Overlays
}
}
};
-
-#if NET_FRAMEWORK
- Add(new SquirrelUpdateManager());
-#endif
}
protected override void LoadComplete()
diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs
index 61d2006315..257155478f 100644
--- a/osu.Desktop/Program.cs
+++ b/osu.Desktop/Program.cs
@@ -4,12 +4,13 @@
using System;
using System.IO;
using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
using osu.Framework;
+using osu.Framework.Development;
+using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.IPC;
-#if NET_FRAMEWORK
-using System.Runtime;
-#endif
namespace osu.Desktop
{
@@ -18,16 +19,13 @@ namespace osu.Desktop
[STAThread]
public static int Main(string[] args)
{
- // required to initialise native SQLite libraries on some platforms.
-
- if (!RuntimeInfo.IsMono)
- useMulticoreJit();
-
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
{
+ host.ExceptionThrown += handleException;
+
if (!host.IsPrimaryInstance)
{
var importer = new ArchiveImportIPCChannel(host);
@@ -54,13 +52,23 @@ namespace osu.Desktop
}
}
- private static void useMulticoreJit()
+ private static int allowableExceptions = DebugUtils.IsDebugBuild ? 0 : 1;
+
+ ///
+ /// Allow a maximum of one unhandled exception, per second of execution.
+ ///
+ ///
+ ///
+ private static bool handleException(Exception arg)
{
-#if NET_FRAMEWORK
- var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
- ProfileOptimization.SetProfileRoot(directory.FullName);
- ProfileOptimization.StartProfile("Startup.Profile");
-#endif
+ bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0;
+
+ Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} .");
+
+ // restore the stock of allowable exceptions after a short delay.
+ Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions));
+
+ return continueExecution;
}
}
}
diff --git a/osu.Desktop/Updater/SimpleUpdateManager.cs b/osu.Desktop/Updater/SimpleUpdateManager.cs
new file mode 100644
index 0000000000..6c363422f7
--- /dev/null
+++ b/osu.Desktop/Updater/SimpleUpdateManager.cs
@@ -0,0 +1,103 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using osu.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.IO.Network;
+using osu.Framework.Platform;
+using osu.Game;
+using osu.Game.Graphics;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Notifications;
+
+namespace osu.Desktop.Updater
+{
+ ///
+ /// An update manager that shows notifications if a newer release is detected.
+ /// Installation is left up to the user.
+ ///
+ internal class SimpleUpdateManager : CompositeDrawable
+ {
+ private NotificationOverlay notificationOverlay;
+ private string version;
+ private GameHost host;
+
+ [BackgroundDependencyLoader]
+ private void load(NotificationOverlay notification, OsuGameBase game, GameHost host)
+ {
+ notificationOverlay = notification;
+
+ this.host = host;
+ version = game.Version;
+
+ if (game.IsDeployedBuild)
+ Schedule(() => Task.Run(() => checkForUpdateAsync()));
+ }
+
+ private async void checkForUpdateAsync()
+ {
+ var releases = new JsonWebRequest("https://api.github.com/repos/ppy/osu/releases/latest");
+ await releases.PerformAsync();
+
+ var latest = releases.ResponseObject;
+
+ if (latest.TagName != version)
+ {
+ notificationOverlay.Post(new SimpleNotification
+ {
+ Text = $"A newer release of osu! has been found ({version} → {latest.TagName}).\n\n"
+ + "Click here to download the new version, which can be installed over the top of your existing installation",
+ Icon = FontAwesome.fa_upload,
+ Activated = () =>
+ {
+ host.OpenUrlExternally(getBestUrl(latest));
+ return true;
+ }
+ });
+ }
+ }
+
+ private string getBestUrl(GitHubRelease release)
+ {
+ GitHubAsset bestAsset = null;
+
+ switch (RuntimeInfo.OS)
+ {
+ case RuntimeInfo.Platform.Windows:
+ bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".exe"));
+ break;
+ case RuntimeInfo.Platform.MacOsx:
+ bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".app.zip"));
+ break;
+ }
+
+ return bestAsset?.BrowserDownloadUrl ?? release.HtmlUrl;
+ }
+
+ public class GitHubRelease
+ {
+ [JsonProperty("html_url")]
+ public string HtmlUrl { get; set; }
+
+ [JsonProperty("tag_name")]
+ public string TagName { get; set; }
+
+ [JsonProperty("assets")]
+ public List Assets { get; set; }
+ }
+
+ public class GitHubAsset
+ {
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("browser_download_url")]
+ public string BrowserDownloadUrl { get; set; }
+ }
+ }
+}
diff --git a/osu.Desktop/Overlays/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs
similarity index 97%
rename from osu.Desktop/Overlays/SquirrelUpdateManager.cs
rename to osu.Desktop/Updater/SquirrelUpdateManager.cs
index ea86d2f028..1f8bff74f4 100644
--- a/osu.Desktop/Overlays/SquirrelUpdateManager.cs
+++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs
@@ -1,8 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-#if NET_FRAMEWORK
using System;
+using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@@ -16,7 +16,7 @@ using OpenTK;
using OpenTK.Graphics;
using Squirrel;
-namespace osu.Desktop.Overlays
+namespace osu.Desktop.Updater
{
public class SquirrelUpdateManager : Component
{
@@ -35,7 +35,7 @@ namespace osu.Desktop.Overlays
notificationOverlay = notification;
if (game.IsDeployedBuild)
- Schedule(() => checkForUpdateAsync());
+ Schedule(() => Task.Run(() => checkForUpdateAsync()));
}
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
@@ -161,4 +161,3 @@ namespace osu.Desktop.Overlays
}
}
}
-#endif
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 0289db20f0..180abd7fec 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -1,7 +1,7 @@
- net471;netcoreapp2.1
+ netcoreapp2.1
WinExe
AnyCPU
true
@@ -13,9 +13,6 @@
0.0.0
0.0.0
-
- $(DefineConstants);NET_FRAMEWORK
-
osu.Desktop.Program
@@ -29,11 +26,12 @@
+
+
-
-
\ No newline at end of file
+
diff --git a/osu.Desktop/osu.nuspec b/osu.Desktop/osu.nuspec
index 316a5443ef..cdd232a9b2 100644
--- a/osu.Desktop/osu.nuspec
+++ b/osu.Desktop/osu.nuspec
@@ -3,7 +3,7 @@
osulazer
0.0.0
- osulazer
+ osu!lazer
ppy Pty Ltd
Dean Herbert
https://osu.ppy.sh/
diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json
index 2a82d65014..da9344b6a2 100644
--- a/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json
+++ b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json
@@ -2,35 +2,7 @@
"version": "0.2.0",
"configurations": [
{
- "name": "VisualTests (Debug, net471)",
- "windows": {
- "type": "clr"
- },
- "type": "mono",
- "request": "launch",
- "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe",
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug, msbuild)",
- "runtimeExecutable": null,
- "env": {},
- "console": "internalConsole"
- },
- {
- "name": "VisualTests (Release, net471)",
- "windows": {
- "type": "clr"
- },
- "type": "mono",
- "request": "launch",
- "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Catch.Tests.exe",
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release, msbuild)",
- "runtimeExecutable": null,
- "env": {},
- "console": "internalConsole"
- },
- {
- "name": "VisualTests (Debug, netcoreapp2.1)",
+ "name": "VisualTests (Debug)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -38,12 +10,12 @@
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug, dotnet)",
+ "preLaunchTask": "Build (Debug)",
"env": {},
"console": "internalConsole"
},
{
- "name": "VisualTests (Release, netcoreapp2.1)",
+ "name": "VisualTests (Release)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -51,7 +23,7 @@
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release, dotnet)",
+ "preLaunchTask": "Build (Release)",
"env": {},
"console": "internalConsole"
}
diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json
index 6c6d562512..18a6f8ca70 100644
--- a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json
+++ b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json
@@ -4,43 +4,13 @@
"version": "2.0.0",
"tasks": [
{
- "label": "Build (Debug, msbuild)",
- "type": "shell",
- "command": "msbuild",
- "args": [
- "osu.Game.Rulesets.Catch.Tests.csproj",
- "/p:TargetFramework=net471",
- "/p:GenerateFullPaths=true",
- "/m",
- "/verbosity:m"
- ],
- "group": "build",
- "problemMatcher": "$msCompile"
- },
- {
- "label": "Build (Release, msbuild)",
- "type": "shell",
- "command": "msbuild",
- "args": [
- "osu.Game.Rulesets.Catch.Tests.csproj",
- "/p:Configuration=Release",
- "/p:TargetFramework=net471",
- "/p:GenerateFullPaths=true",
- "/m",
- "/verbosity:m"
- ],
- "group": "build",
- "problemMatcher": "$msCompile"
- },
- {
- "label": "Build (Debug, dotnet)",
+ "label": "Build (Debug)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Catch.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -49,14 +19,13 @@
"problemMatcher": "$msCompile"
},
{
- "label": "Build (Release, dotnet)",
+ "label": "Build (Release)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Catch.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -66,16 +35,7 @@
"problemMatcher": "$msCompile"
},
{
- "label": "Restore (net471)",
- "type": "shell",
- "command": "nuget",
- "args": [
- "restore"
- ],
- "problemMatcher": []
- },
- {
- "label": "Restore (netcoreapp2.1)",
+ "label": "Restore",
"type": "shell",
"command": "dotnet",
"args": [
diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs
index 4b95a6754e..162624da57 100644
--- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs
@@ -28,18 +28,20 @@ namespace osu.Game.Rulesets.Catch.Tests
protected override IEnumerable CreateConvertValue(HitObject hitObject)
{
- if (hitObject is JuiceStream stream)
+ switch (hitObject)
{
- foreach (var nested in stream.NestedHitObjects)
- yield return new ConvertValue((CatchHitObject)nested);
+ case JuiceStream stream:
+ foreach (var nested in stream.NestedHitObjects)
+ yield return new ConvertValue((CatchHitObject)nested);
+ break;
+ case BananaShower shower:
+ foreach (var nested in shower.NestedHitObjects)
+ yield return new ConvertValue((CatchHitObject)nested);
+ break;
+ default:
+ yield return new ConvertValue((CatchHitObject)hitObject);
+ break;
}
- else if (hitObject is BananaShower shower)
- {
- foreach (var nested in shower.NestedHitObjects)
- yield return new ConvertValue((CatchHitObject)nested);
- }
- else
- yield return new ConvertValue((CatchHitObject)hitObject);
}
protected override Ruleset CreateRuleset() => new CatchRuleset();
diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs
index 0ba6398ced..25f7ca108d 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
- public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperdashState(status ? 2 : 1);
+ public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperDashState(status ? 2 : 1);
}
}
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs
similarity index 88%
rename from osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs
rename to osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs
index 896582bf0a..14487b2c7f 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs
@@ -8,9 +8,9 @@ using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
- public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
+ public class TestCaseHyperDash : Game.Tests.Visual.TestCasePlayer
{
- public TestCaseHyperdash()
+ public TestCaseHyperDash()
: base(new CatchRuleset())
{
}
diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
index 93fa2c4d67..51343d9e91 100644
--- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
+++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
@@ -1,8 +1,14 @@
+
+
+
+
+
+
WinExe
- netcoreapp2.1;net471
+ netcoreapp2.1
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
index 68a8dfb7d3..15d4edc411 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
@@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
RepeatCount = curveData.RepeatCount,
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset ?? 0
};
}
@@ -51,7 +52,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
StartTime = obj.StartTime,
Samples = obj.Samples,
Duration = endTime.Duration,
- NewCombo = comboData?.NewCombo ?? false
+ NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
};
}
else
@@ -61,6 +63,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
StartTime = obj.StartTime,
Samples = obj.Samples,
NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH
};
}
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index fc6e23c884..1f1d2475f6 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -93,13 +93,11 @@ namespace osu.Game.Rulesets.Catch
new CatchModHidden(),
new CatchModFlashlight(),
};
- case ModType.Special:
+ case ModType.Automation:
return new Mod[]
{
- new CatchModRelax(),
- null,
- null,
new MultiMod(new CatchModAutoplay(), new ModCinema()),
+ new CatchModRelax(),
};
default:
return new Mod[] { };
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
index 3d1013aad3..a763989750 100644
--- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
@@ -46,13 +46,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty
foreach (var hitObject in beatmap.HitObjects)
{
- // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
- if (hitObject is Fruit)
+ switch (hitObject)
{
- difficultyHitObjects.Add(new CatchDifficultyHitObject((CatchHitObject)hitObject, halfCatchWidth));
+ // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
+ case Fruit fruit:
+ difficultyHitObjects.Add(new CatchDifficultyHitObject(fruit, halfCatchWidth));
+ break;
+ case JuiceStream _:
+ difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
+ break;
}
- if (hitObject is JuiceStream)
- difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
}
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
@@ -61,12 +64,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty
return new CatchDifficultyAttributes(mods, 0);
// this is the same as osu!, so there's potential to share the implementation... maybe
- double preEmpt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
+ double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor;
return new CatchDifficultyAttributes(mods, starRating)
{
- ApproachRate = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0,
+ ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
MaxCombo = difficultyHitObjects.Count
};
}
diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs
index c39e663d75..f38009263f 100644
--- a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs
+++ b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Judgements
@@ -9,8 +10,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
public override bool AffectsCombo => false;
- public override bool ShouldExplode => true;
-
protected override int NumericResultFor(HitResult result)
{
switch (result)
@@ -32,5 +31,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 8;
}
}
+
+ public override bool ShouldExplodeFor(JudgementResult result) => true;
}
}
diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs
index 51d7d3b5cd..8a51867899 100644
--- a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs
+++ b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs
@@ -23,21 +23,10 @@ namespace osu.Game.Rulesets.Catch.Judgements
}
///
- /// The base health increase for the result achieved.
+ /// Retrieves the numeric health increase of a .
///
- public float HealthIncrease => HealthIncreaseFor(Result);
-
- ///
- /// Whether fruit on the platter should explode or drop.
- /// Note that this is only checked if the owning object is also
- ///
- public virtual bool ShouldExplode => IsHit;
-
- ///
- /// Convert a to a base health increase.
- ///
- /// The value to convert.
- /// The base health increase.
+ /// The to find the numeric health increase for.
+ /// The numeric health increase of .
protected virtual float HealthIncreaseFor(HitResult result)
{
switch (result)
@@ -48,5 +37,18 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 10.2f;
}
}
+
+ ///
+ /// Retrieves the numeric health increase of a .
+ ///
+ /// The to find the numeric health increase for.
+ /// The numeric health increase of .
+ public float HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type);
+
+ ///
+ /// Whether fruit on the platter should explode or drop.
+ /// Note that this is only checked if the owning object is also
+ ///
+ public virtual bool ShouldExplodeFor(JudgementResult result) => result.IsHit;
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Banana.cs b/osu.Game.Rulesets.Catch/Objects/Banana.cs
index f7c60a7a47..e1af4c1075 100644
--- a/osu.Game.Rulesets.Catch/Objects/Banana.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Banana.cs
@@ -1,10 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Catch.Judgements;
+using osu.Game.Rulesets.Judgements;
+
namespace osu.Game.Rulesets.Catch.Objects
{
public class Banana : Fruit
{
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
+
+ public override Judgement CreateJudgement() => new CatchBananaJudgement();
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index d55cdac115..621fc100c2 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.Objects
public virtual bool NewCombo { get; set; }
+ public int ComboOffset { get; set; }
+
public int IndexInCurrentCombo { get; set; }
public int ComboIndex { get; set; }
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs
index dd027abbe0..8756a5178f 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs
@@ -1,8 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Rulesets.Catch.Judgements;
-
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableBanana : DrawableFruit
@@ -11,7 +9,5 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
: base(h)
{
}
-
- protected override CatchJudgement CreateJudgement() => new CatchBananaJudgement();
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs
index f039504600..697fab85c9 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs
@@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddNested(getVisualRepresentation?.Invoke(b));
}
- protected override bool ProvidesJudgement => false;
-
protected override void AddNested(DrawableHitObject h)
{
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
index 6ce2e6a2ae..9e840301fd 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
@@ -5,7 +5,6 @@ using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
@@ -53,20 +52,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
public Func CheckPosition;
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (CheckPosition == null) return;
- if (timeOffset >= 0)
- {
- var judgement = CreateJudgement();
- judgement.Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss;
- AddJudgement(judgement);
- }
+ if (timeOffset >= 0 && Result != null)
+ ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss);
}
- protected virtual CatchJudgement CreateJudgement() => new CatchJudgement();
-
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
index 11d5ed1f92..5c8a7c4a7c 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
@@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using OpenTK;
using OpenTK.Graphics;
-using osu.Game.Rulesets.Catch.Judgements;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
@@ -24,8 +23,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Masking = false;
}
- protected override CatchJudgement CreateJudgement() => new CatchDropletJudgement();
-
[BackgroundDependencyLoader]
private void load()
{
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
index 854b63edeb..e66852c5c2 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
@@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddNested(getVisualRepresentation?.Invoke(o));
}
- protected override bool ProvidesJudgement => false;
-
protected override void AddNested(DrawableHitObject h)
{
var catchObject = (DrawableCatchHitObject)h;
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs
index 2232bb81a7..e0f02454c4 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs
@@ -2,18 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
-using osu.Game.Rulesets.Catch.Judgements;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableTinyDroplet : DrawableDroplet
{
- public DrawableTinyDroplet(Droplet h)
+ public DrawableTinyDroplet(TinyDroplet h)
: base(h)
{
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 8;
}
-
- protected override CatchJudgement CreateJudgement() => new CatchTinyDropletJudgement();
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Droplet.cs b/osu.Game.Rulesets.Catch/Objects/Droplet.cs
index f91a70c506..8b54922959 100644
--- a/osu.Game.Rulesets.Catch/Objects/Droplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Droplet.cs
@@ -1,9 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Catch.Judgements;
+using osu.Game.Rulesets.Judgements;
+
namespace osu.Game.Rulesets.Catch.Objects
{
public class Droplet : CatchHitObject
{
+ public override Judgement CreateJudgement() => new CatchDropletJudgement();
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Fruit.cs b/osu.Game.Rulesets.Catch/Objects/Fruit.cs
index fcbb339ffd..2c2cd013c3 100644
--- a/osu.Game.Rulesets.Catch/Objects/Fruit.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Fruit.cs
@@ -1,9 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Catch.Judgements;
+using osu.Game.Rulesets.Judgements;
+
namespace osu.Game.Rulesets.Catch.Objects
{
public class Fruit : CatchHitObject
{
+ public override Judgement CreateJudgement() => new CatchJudgement();
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs b/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs
index 76cc8d9808..39f1cadad5 100644
--- a/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs
@@ -1,9 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Catch.Judgements;
+using osu.Game.Rulesets.Judgements;
+
namespace osu.Game.Rulesets.Catch.Objects
{
public class TinyDroplet : Droplet
{
+ public override Judgement CreateJudgement() => new CatchTinyDropletJudgement();
}
}
diff --git a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs
index 8d3d898655..f05eb31454 100644
--- a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs
+++ b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
-using osu.Framework.Input;
+using osu.Framework.Input.StateChanges;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Replays;
diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index 5b69d836a3..403cedde8c 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
-using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
@@ -21,55 +20,28 @@ namespace osu.Game.Rulesets.Catch.Scoring
private float hpDrainRate;
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void ApplyBeatmap(Beatmap beatmap)
{
- hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
+ base.ApplyBeatmap(beatmap);
- foreach (var obj in beatmap.HitObjects)
- {
- switch (obj)
- {
- case JuiceStream stream:
- foreach (var nestedObject in stream.NestedHitObjects)
- switch (nestedObject)
- {
- case TinyDroplet _:
- AddJudgement(new CatchTinyDropletJudgement { Result = HitResult.Perfect });
- break;
- case Droplet _:
- AddJudgement(new CatchDropletJudgement { Result = HitResult.Perfect });
- break;
- case Fruit _:
- AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
- break;
- }
- break;
- case BananaShower shower:
- foreach (var _ in shower.NestedHitObjects.Cast())
- AddJudgement(new CatchBananaJudgement { Result = HitResult.Perfect });
- break;
- case Fruit _:
- AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
- break;
- }
- }
+ hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
}
private const double harshness = 0.01;
- protected override void OnNewJudgement(Judgement judgement)
+ protected override void ApplyResult(JudgementResult result)
{
- base.OnNewJudgement(judgement);
+ base.ApplyResult(result);
- if (judgement.Result == HitResult.Miss)
+ if (result.Type == HitResult.Miss)
{
- if (!judgement.IsBonus)
+ if (!result.Judgement.IsBonus)
Health.Value -= hpDrainRate * (harshness * 2);
return;
}
- if (judgement is CatchJudgement catchJudgement)
- Health.Value += Math.Max(catchJudgement.HealthIncrease - hpDrainRate, 0) * harshness;
+ if (result.Judgement is CatchJudgement catchJudgement)
+ Health.Value += Math.Max(catchJudgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
}
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index 9eca8f6871..d49be69856 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -23,8 +23,10 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly CatcherArea catcherArea;
public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation)
- : base(ScrollingDirection.Down, BASE_WIDTH)
+ : base(BASE_WIDTH)
{
+ Direction.Value = ScrollingDirection.Down;
+
Container explodingFruitContainer;
Anchor = Anchor.TopCentre;
@@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.UI
public override void Add(DrawableHitObject h)
{
- h.OnJudgement += onJudgement;
+ h.OnNewResult += onNewResult;
base.Add(h);
@@ -65,6 +67,7 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.CheckPosition = CheckIfWeCanCatch;
}
- private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement);
+ private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
+ => catcherArea.OnResult((DrawableCatchHitObject)judgedObject, result);
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index 9c376f340a..4327abb96f 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -48,10 +48,13 @@ namespace osu.Game.Rulesets.Catch.UI
private DrawableCatchHitObject lastPlateableFruit;
- public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
+ public void OnResult(DrawableCatchHitObject fruit, JudgementResult result)
{
void runAfterLoaded(Action action)
{
+ if (lastPlateableFruit == null)
+ return;
+
// this is required to make this run after the last caught fruit runs UpdateState at least once.
// TODO: find a better alternative
if (lastPlateableFruit.IsLoaded)
@@ -60,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.UI
lastPlateableFruit.OnLoadComplete = _ => action();
}
- if (judgement.IsHit && fruit.CanBePlated)
+ if (result.IsHit && fruit.CanBePlated)
{
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
@@ -83,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.UI
if (fruit.HitObject.LastInCombo)
{
- if (((CatchJudgement)judgement).ShouldExplode)
+ if (((CatchJudgement)result.Judgement).ShouldExplodeFor(result))
runAfterLoaded(() => MovableCatcher.Explode());
else
MovableCatcher.Drop();
@@ -255,11 +258,11 @@ namespace osu.Game.Rulesets.Catch.UI
double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition;
double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
- SetHyperdashState(Math.Abs(velocity), target.X);
+ SetHyperDashState(Math.Abs(velocity), target.X);
}
else
{
- SetHyperdashState();
+ SetHyperDashState();
}
return validCatch;
@@ -270,18 +273,18 @@ namespace osu.Game.Rulesets.Catch.UI
private float hyperDashTargetPosition;
///
- /// Whether we are hypderdashing or not.
+ /// Whether we are hyper-dashing or not.
///
public bool HyperDashing => hyperDashModifier != 1;
///
- /// Set hyperdash state.
+ /// Set hyper-dash state.
///
- /// The speed multiplier. If this is less or equals to 1, this catcher will be non-hyperdashing state.
- /// When this catcher crosses this position, this catcher ends hyperdashing.
- public void SetHyperdashState(double modifier = 1, float targetPosition = -1)
+ /// The speed multiplier. If this is less or equals to 1, this catcher will be non-hyper-dashing state.
+ /// When this catcher crosses this position, this catcher ends hyper-dashing.
+ public void SetHyperDashState(double modifier = 1, float targetPosition = -1)
{
- const float hyperdash_transition_length = 180;
+ const float hyper_dash_transition_length = 180;
bool previouslyHyperDashing = HyperDashing;
if (modifier <= 1 || X == targetPosition)
@@ -291,8 +294,8 @@ namespace osu.Game.Rulesets.Catch.UI
if (previouslyHyperDashing)
{
- this.FadeColour(Color4.White, hyperdash_transition_length, Easing.OutQuint);
- this.FadeTo(1, hyperdash_transition_length, Easing.OutQuint);
+ this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint);
+ this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint);
}
}
else
@@ -303,8 +306,8 @@ namespace osu.Game.Rulesets.Catch.UI
if (!previouslyHyperDashing)
{
- this.FadeColour(Color4.OrangeRed, hyperdash_transition_length, Easing.OutQuint);
- this.FadeTo(0.2f, hyperdash_transition_length, Easing.OutQuint);
+ this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint);
+ this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint);
Trail = true;
}
}
@@ -370,7 +373,7 @@ namespace osu.Game.Rulesets.Catch.UI
hyperDashDirection < 0 && hyperDashTargetPosition > X)
{
X = hyperDashTargetPosition;
- SetHyperdashState();
+ SetHyperDashState();
}
}
diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json
index bc41d4ccf9..c781b0e64e 100644
--- a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json
+++ b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json
@@ -2,35 +2,7 @@
"version": "0.2.0",
"configurations": [
{
- "name": "VisualTests (Debug, net471)",
- "windows": {
- "type": "clr"
- },
- "type": "mono",
- "request": "launch",
- "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe",
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug, msbuild)",
- "runtimeExecutable": null,
- "env": {},
- "console": "internalConsole"
- },
- {
- "name": "VisualTests (Release, net471)",
- "windows": {
- "type": "clr"
- },
- "type": "mono",
- "request": "launch",
- "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Mania.Tests.exe",
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release, msbuild)",
- "runtimeExecutable": null,
- "env": {},
- "console": "internalConsole"
- },
- {
- "name": "VisualTests (Debug, netcoreapp2.1)",
+ "name": "VisualTests (Debug)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -38,12 +10,12 @@
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug, dotnet)",
+ "preLaunchTask": "Build (Debug)",
"env": {},
"console": "internalConsole"
},
{
- "name": "VisualTests (Release, netcoreapp2.1)",
+ "name": "VisualTests (Release)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -51,7 +23,7 @@
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release, dotnet)",
+ "preLaunchTask": "Build (Release)",
"env": {},
"console": "internalConsole"
}
diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json
index 7fc2f7b2ef..608c4340ac 100644
--- a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json
+++ b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json
@@ -4,43 +4,13 @@
"version": "2.0.0",
"tasks": [
{
- "label": "Build (Debug, msbuild)",
- "type": "shell",
- "command": "msbuild",
- "args": [
- "osu.Game.Rulesets.Mania.Tests.csproj",
- "/p:TargetFramework=net471",
- "/p:GenerateFullPaths=true",
- "/m",
- "/verbosity:m"
- ],
- "group": "build",
- "problemMatcher": "$msCompile"
- },
- {
- "label": "Build (Release, msbuild)",
- "type": "shell",
- "command": "msbuild",
- "args": [
- "osu.Game.Rulesets.Mania.Tests.csproj",
- "/p:Configuration=Release",
- "/p:TargetFramework=net471",
- "/p:GenerateFullPaths=true",
- "/m",
- "/verbosity:m"
- ],
- "group": "build",
- "problemMatcher": "$msCompile"
- },
- {
- "label": "Build (Debug, dotnet)",
+ "label": "Build (Debug)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Mania.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -49,14 +19,13 @@
"problemMatcher": "$msCompile"
},
{
- "label": "Build (Release, dotnet)",
+ "label": "Build (Release)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Mania.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -66,16 +35,7 @@
"problemMatcher": "$msCompile"
},
{
- "label": "Restore (net471)",
- "type": "shell",
- "command": "nuget",
- "args": [
- "restore"
- ],
- "problemMatcher": []
- },
- {
- "label": "Restore (netcoreapp2.1)",
+ "label": "Restore",
"type": "shell",
"command": "dotnet",
"args": [
diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs
index 73555dcecb..58f2ab7747 100644
--- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs
@@ -5,6 +5,8 @@ using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.MathUtils;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@@ -13,11 +15,10 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
- public class ManiaBeatmapConversionTest : BeatmapConversionTest
+ public class ManiaBeatmapConversionTest : BeatmapConversionTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
- [NonParallelizable]
[TestCase("basic")]
public new void Test(string name)
{
@@ -34,9 +35,35 @@ namespace osu.Game.Rulesets.Mania.Tests
};
}
+ protected override ManiaConvertMapping CreateConvertMapping() => new ManiaConvertMapping(Converter);
+
protected override Ruleset CreateRuleset() => new ManiaRuleset();
}
+ public class ManiaConvertMapping : ConvertMapping, IEquatable
+ {
+ public uint RandomW;
+ public uint RandomX;
+ public uint RandomY;
+ public uint RandomZ;
+
+ public ManiaConvertMapping()
+ {
+ }
+
+ public ManiaConvertMapping(IBeatmapConverter converter)
+ {
+ var maniaConverter = (ManiaBeatmapConverter)converter;
+ RandomW = maniaConverter.Random.W;
+ RandomX = maniaConverter.Random.X;
+ RandomY = maniaConverter.Random.Y;
+ RandomZ = maniaConverter.Random.Z;
+ }
+
+ public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ;
+ public override bool Equals(ConvertMapping other) => base.Equals(other) && Equals(other as ManiaConvertMapping);
+ }
+
public struct ConvertValue : IEquatable
{
///
diff --git a/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs b/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs
index 78a98e83e8..29663c2093 100644
--- a/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs
+++ b/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs
@@ -14,24 +14,20 @@ namespace osu.Game.Rulesets.Mania.Tests
///
public class ScrollingTestContainer : Container
{
- private readonly ScrollingDirection direction;
+ [Cached(Type = typeof(IScrollingInfo))]
+ private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
public ScrollingTestContainer(ScrollingDirection direction)
{
- this.direction = direction;
+ scrollingInfo.Direction.Value = direction;
}
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
- {
- var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
- dependencies.CacheAs(new ScrollingInfo { Direction = { Value = direction }});
- return dependencies;
- }
+ public void Flip() => scrollingInfo.Direction.Value = scrollingInfo.Direction.Value == ScrollingDirection.Up ? ScrollingDirection.Down : ScrollingDirection.Up;
+ }
- private class ScrollingInfo : IScrollingInfo
- {
- public readonly Bindable Direction = new Bindable();
- IBindable IScrollingInfo.Direction => Direction;
- }
+ public class TestScrollingInfo : IScrollingInfo
+ {
+ public readonly Bindable Direction = new Bindable();
+ IBindable IScrollingInfo.Direction => Direction;
}
}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs
index de2bfaed9c..cceee718ca 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs
@@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Tests
private Drawable createColumn(ScrollingDirection direction, ManiaAction action)
{
- var column = new Column(direction)
+ var column = new Column
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs
new file mode 100644
index 0000000000..6c0f931cda
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs
@@ -0,0 +1,32 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Game.Rulesets.Mania.Configuration;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Mania.Tests
+{
+ [TestFixture]
+ public class TestCaseEditor : EditorTestCase
+ {
+ private readonly Bindable direction = new Bindable();
+
+ public TestCaseEditor()
+ : base(new ManiaRuleset())
+ {
+ AddStep("upwards scroll", () => direction.Value = ManiaScrollingDirection.Up);
+ AddStep("downwards scroll", () => direction.Value = ManiaScrollingDirection.Down);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(RulesetConfigCache configCache)
+ {
+ var config = (ManiaConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
+ config.BindWith(ManiaSetting.ScrollDirection, direction);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
index e827fa1fdc..a8b2b20fda 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
@@ -46,15 +46,20 @@ namespace osu.Game.Rulesets.Mania.Tests
Spacing = new Vector2(20),
Children = new[]
{
- createNoteDisplay(ScrollingDirection.Down),
- createNoteDisplay(ScrollingDirection.Up),
- createHoldNoteDisplay(ScrollingDirection.Down),
- createHoldNoteDisplay(ScrollingDirection.Up),
+ createNoteDisplay(ScrollingDirection.Down, 1, out var note1),
+ createNoteDisplay(ScrollingDirection.Up, 2, out var note2),
+ createHoldNoteDisplay(ScrollingDirection.Down, 1, out var holdNote1),
+ createHoldNoteDisplay(ScrollingDirection.Up, 2, out var holdNote2),
}
};
+
+ AddAssert("note 1 facing downwards", () => verifyAnchors(note1, Anchor.y2));
+ AddAssert("note 2 facing upwards", () => verifyAnchors(note2, Anchor.y0));
+ AddAssert("hold note 1 facing downwards", () => verifyAnchors(holdNote1, Anchor.y2));
+ AddAssert("hold note 2 facing upwards", () => verifyAnchors(holdNote2, Anchor.y0));
}
- private Drawable createNoteDisplay(ScrollingDirection direction)
+ private Drawable createNoteDisplay(ScrollingDirection direction, int identifier, out DrawableNote hitObject)
{
var note = new Note { StartTime = 999999999 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
@@ -62,24 +67,24 @@ namespace osu.Game.Rulesets.Mania.Tests
return new ScrollingTestContainer(direction)
{
AutoSizeAxes = Axes.Both,
- Child = new NoteContainer(direction, $"note, scrolling {direction.ToString().ToLower()}")
+ Child = new NoteContainer(direction, $"note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
{
- Child = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
+ Child = hitObject = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
}
};
}
- private Drawable createHoldNoteDisplay(ScrollingDirection direction)
+ private Drawable createHoldNoteDisplay(ScrollingDirection direction, int identifier, out DrawableHoldNote hitObject)
{
- var note = new HoldNote { StartTime = 999999999, Duration = 1000 };
+ var note = new HoldNote { StartTime = 999999999, Duration = 5000 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
return new ScrollingTestContainer(direction)
{
AutoSizeAxes = Axes.Both,
- Child = new NoteContainer(direction, $"hold note, scrolling {direction.ToString().ToLower()}")
+ Child = new NoteContainer(direction, $"hold note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
{
- Child = new DrawableHoldNote(note)
+ Child = hitObject = new DrawableHoldNote(note)
{
RelativeSizeAxes = Axes.Both,
AccentColour = Color4.OrangeRed,
@@ -88,6 +93,12 @@ namespace osu.Game.Rulesets.Mania.Tests
};
}
+ private bool verifyAnchors(DrawableHitObject hitObject, Anchor expectedAnchor)
+ => hitObject.Anchor.HasFlag(expectedAnchor) && hitObject.Origin.HasFlag(expectedAnchor);
+
+ private bool verifyAnchors(DrawableHoldNote holdNote, Anchor expectedAnchor)
+ => verifyAnchors((DrawableHitObject)holdNote, expectedAnchor) && holdNote.NestedHitObjects.All(n => verifyAnchors(n, expectedAnchor));
+
private class NoteContainer : Container
{
private readonly Container content;
@@ -137,9 +148,9 @@ namespace osu.Game.Rulesets.Mania.Tests
};
}
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs>(new Bindable());
return dependencies;
}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs
index 8046c46fc1..5c5d955168 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs
@@ -2,6 +2,7 @@
// 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.Allocation;
using osu.Framework.Graphics;
@@ -24,6 +25,8 @@ namespace osu.Game.Rulesets.Mania.Tests
private readonly List stages = new List();
+ private FillFlowContainer fill;
+
public TestCaseStage()
: base(columns)
{
@@ -32,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests
[BackgroundDependencyLoader]
private void load()
{
- Child = new FillFlowContainer
+ Child = fill = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
@@ -54,8 +57,22 @@ namespace osu.Game.Rulesets.Mania.Tests
AddStep("hold note", createHoldNote);
AddStep("minor bar line", () => createBarLine(false));
AddStep("major bar line", () => createBarLine(true));
+
+ AddAssert("check note anchors", () => notesInStageAreAnchored(stages[0], Anchor.TopCentre));
+ AddAssert("check note anchors", () => notesInStageAreAnchored(stages[1], Anchor.BottomCentre));
+
+ AddStep("flip direction", () =>
+ {
+ foreach (var c in fill.Children)
+ c.Flip();
+ });
+
+ AddAssert("check note anchors", () => notesInStageAreAnchored(stages[0], Anchor.BottomCentre));
+ AddAssert("check note anchors", () => notesInStageAreAnchored(stages[1], Anchor.TopCentre));
}
+ private bool notesInStageAreAnchored(ManiaStage stage, Anchor anchor) => stage.Columns.SelectMany(c => c.AllHitObjects).All(o => o.Anchor == anchor);
+
private void createNote()
{
foreach (var stage in stages)
@@ -101,11 +118,11 @@ namespace osu.Game.Rulesets.Mania.Tests
}
}
- private Drawable createStage(ScrollingDirection direction, ManiaAction action)
+ private ScrollingTestContainer createStage(ScrollingDirection direction, ManiaAction action)
{
var specialAction = ManiaAction.Special1;
- var stage = new ManiaStage(direction, 0, new StageDefinition { Columns = 2 }, ref action, ref specialAction) { VisibleTimeRange = { Value = 2000 } };
+ var stage = new ManiaStage(0, new StageDefinition { Columns = 2 }, ref action, ref specialAction) { VisibleTimeRange = { Value = 2000 } };
stages.Add(stage);
return new ScrollingTestContainer(direction)
diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
index 77504fdc3c..3165f69a6b 100644
--- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
+++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
@@ -1,8 +1,14 @@
+
+
+
+
+
+
WinExe
- netcoreapp2.1;net471
+ netcoreapp2.1
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index 19fef9eb54..c15b303048 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -5,6 +5,7 @@ using osu.Game.Rulesets.Mania.Objects;
using System;
using System.Linq;
using System.Collections.Generic;
+using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@@ -28,8 +29,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
public int TargetColumns;
public readonly bool IsForCurrentRuleset;
+ // Internal for testing purposes
+ internal FastRandom Random { get; private set; }
+
private Pattern lastPattern = new Pattern();
- private FastRandom random;
private ManiaBeatmap beatmap;
@@ -62,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
- random = new FastRandom(seed);
+ Random = new FastRandom(seed);
return base.ConvertBeatmap(original);
}
@@ -100,7 +103,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private double lastTime;
private Vector2 lastPosition;
- private PatternType lastStair;
+ private PatternType lastStair = PatternType.Stair;
private void recordNote(double time, Vector2 position)
{
lastTime = time;
@@ -115,12 +118,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// The hit objects generated.
private IEnumerable generateSpecific(HitObject original, IBeatmap originalBeatmap)
{
- var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
+ var generator = new SpecificBeatmapPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap);
- Pattern newPattern = generator.Generate();
- lastPattern = newPattern;
+ foreach (var newPattern in generator.Generate())
+ {
+ lastPattern = newPattern;
- return newPattern.HitObjects;
+ foreach (var obj in newPattern.HitObjects)
+ yield return obj;
+ }
}
///
@@ -138,27 +144,44 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
Patterns.PatternGenerator conversion = null;
if (distanceData != null)
- conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
+ {
+ var generator = new DistanceObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap);
+ conversion = generator;
+
+ for (double time = original.StartTime; !Precision.DefinitelyBigger(time, generator.EndTime); time += generator.SegmentDuration)
+ {
+ recordNote(time, positionData?.Position ?? Vector2.Zero);
+ computeDensity(time);
+ }
+ }
else if (endTimeData != null)
- conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap);
+ {
+ conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap);
+
+ recordNote(endTimeData.EndTime, new Vector2(256, 192));
+ computeDensity(endTimeData.EndTime);
+ }
else if (positionData != null)
{
computeDensity(original.StartTime);
- conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
+ conversion = new HitObjectPatternGenerator(Random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
recordNote(original.StartTime, positionData.Position);
}
if (conversion == null)
- return null;
+ yield break;
- Pattern newPattern = conversion.Generate();
+ foreach (var newPattern in conversion.Generate())
+ {
+ lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
+ lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
- lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
- lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
+ foreach (var obj in newPattern.HitObjects)
+ yield return obj;
- return newPattern.HitObjects;
+ }
}
///
@@ -171,7 +194,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
{
}
- public override Pattern Generate()
+ public override IEnumerable Generate()
+ {
+ yield return generate();
+ }
+
+ private Pattern generate()
{
var endTimeData = HitObject as IHasEndTime;
var positionData = HitObject as IHasXPosition;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index c6fa465a0f..010cf962cc 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using osu.Framework.MathUtils;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.MathUtils;
@@ -24,8 +25,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
private const float osu_base_scoring_distance = 100;
- private readonly double endTime;
- private readonly double segmentDuration;
+ public readonly double EndTime;
+ public readonly double SegmentDuration;
+
private readonly int spanCount;
private PatternType convertType;
@@ -52,77 +54,105 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// The duration of the osu! hit object
double osuDuration = distance / osuVelocity;
- endTime = hitObject.StartTime + osuDuration;
- segmentDuration = (endTime - HitObject.StartTime) / spanCount;
+ EndTime = hitObject.StartTime + osuDuration;
+ SegmentDuration = (EndTime - HitObject.StartTime) / spanCount;
}
- public override Pattern Generate()
+ public override IEnumerable Generate()
+ {
+ var originalPattern = generate();
+
+ if (originalPattern.HitObjects.Count() == 1)
+ {
+ yield return originalPattern;
+ yield break;
+ }
+
+ // We need to split the intermediate pattern into two new patterns:
+ // 1. A pattern containing all objects that do not end at our EndTime.
+ // 2. A pattern containing all objects that end at our EndTime. This will be used for further pattern generation.
+ var intermediatePattern = new Pattern();
+ var endTimePattern = new Pattern();
+
+ foreach (var obj in originalPattern.HitObjects)
+ {
+ if (!Precision.AlmostEquals(EndTime, (obj as IHasEndTime)?.EndTime ?? obj.StartTime))
+ intermediatePattern.Add(obj);
+ else
+ endTimePattern.Add(obj);
+ }
+
+ yield return intermediatePattern;
+ yield return endTimePattern;
+ }
+
+ private Pattern generate()
{
if (TotalColumns == 1)
{
var pattern = new Pattern();
- addToPattern(pattern, 0, HitObject.StartTime, endTime);
+ addToPattern(pattern, 0, HitObject.StartTime, EndTime);
return pattern;
}
if (spanCount > 1)
{
- if (segmentDuration <= 90)
+ if (SegmentDuration <= 90)
return generateRandomHoldNotes(HitObject.StartTime, 1);
- if (segmentDuration <= 120)
+ if (SegmentDuration <= 120)
{
convertType |= PatternType.ForceNotStack;
return generateRandomNotes(HitObject.StartTime, spanCount + 1);
}
- if (segmentDuration <= 160)
+ if (SegmentDuration <= 160)
return generateStair(HitObject.StartTime);
- if (segmentDuration <= 200 && ConversionDifficulty > 3)
+ if (SegmentDuration <= 200 && ConversionDifficulty > 3)
return generateRandomMultipleNotes(HitObject.StartTime);
- double duration = endTime - HitObject.StartTime;
+ double duration = EndTime - HitObject.StartTime;
if (duration >= 4000)
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
- if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
+ if (SegmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
return generateTiledHoldNotes(HitObject.StartTime);
return generateHoldAndNormalNotes(HitObject.StartTime);
}
- if (segmentDuration <= 110)
+ if (SegmentDuration <= 110)
{
if (PreviousPattern.ColumnWithObjects < TotalColumns)
convertType |= PatternType.ForceNotStack;
else
convertType &= ~PatternType.ForceNotStack;
- return generateRandomNotes(HitObject.StartTime, segmentDuration < 80 ? 1 : 2);
+ return generateRandomNotes(HitObject.StartTime, SegmentDuration < 80 ? 1 : 2);
}
if (ConversionDifficulty > 6.5)
{
- if ((convertType & PatternType.LowProbability) > 0)
+ if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.78, 0.3, 0);
return generateNRandomNotes(HitObject.StartTime, 0.85, 0.36, 0.03);
}
if (ConversionDifficulty > 4)
{
- if ((convertType & PatternType.LowProbability) > 0)
+ if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.43, 0.08, 0);
return generateNRandomNotes(HitObject.StartTime, 0.56, 0.18, 0);
}
if (ConversionDifficulty > 2.5)
{
- if ((convertType & PatternType.LowProbability) > 0)
+ if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.3, 0, 0);
return generateNRandomNotes(HitObject.StartTime, 0.37, 0.08, 0);
}
- if ((convertType & PatternType.LowProbability) > 0)
+ if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.17, 0, 0);
return generateNRandomNotes(HitObject.StartTime, 0.27, 0, 0);
}
@@ -146,17 +176,24 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = Random.Next(RandomStart, TotalColumns);
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
{
- while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
+ // Find available column
+ RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
- addToPattern(pattern, nextColumn, startTime, endTime);
+ });
+
+ addToPattern(pattern, nextColumn, startTime, EndTime);
}
// This is can't be combined with the above loop due to RNG
for (int i = 0; i < noteCount - usableColumns; i++)
{
- while (pattern.ColumnHasObject(nextColumn))
+ RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
- addToPattern(pattern, nextColumn, startTime, endTime);
+ });
+
+ addToPattern(pattern, nextColumn, startTime, EndTime);
}
return pattern;
@@ -179,21 +216,26 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern();
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
- if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
+ if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
- while (PreviousPattern.ColumnHasObject(nextColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
}
int lastColumn = nextColumn;
for (int i = 0; i < noteCount; i++)
{
addToPattern(pattern, nextColumn, startTime, startTime);
- while (nextColumn == lastColumn)
+
+ RunWhile(() => nextColumn == lastColumn, () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
lastColumn = nextColumn;
- startTime += segmentDuration;
+ startTime += SegmentDuration;
}
return pattern;
@@ -223,7 +265,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
for (int i = 0; i <= spanCount; i++)
{
addToPattern(pattern, column, startTime, startTime);
- startTime += segmentDuration;
+ startTime += SegmentDuration;
// Check if we're at the borders of the stage, and invert the pattern if so
if (increasing)
@@ -284,7 +326,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
addToPattern(pattern, nextColumn, startTime, startTime);
nextColumn = Random.Next(RandomStart, TotalColumns);
- startTime += segmentDuration;
+ startTime += SegmentDuration;
}
return pattern;
@@ -331,7 +373,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP || sample.Name == SampleInfo.HIT_FINISH;
- bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0;
+ bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
if (canGenerateTwoNotes)
@@ -361,19 +403,23 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int columnRepeat = Math.Min(spanCount, TotalColumns);
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
- if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
+ if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
- while (PreviousPattern.ColumnHasObject(nextColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
}
for (int i = 0; i < columnRepeat; i++)
{
- while (pattern.ColumnHasObject(nextColumn))
+ RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
- addToPattern(pattern, nextColumn, startTime, endTime);
- startTime += segmentDuration;
+ addToPattern(pattern, nextColumn, startTime, EndTime);
+ startTime += SegmentDuration;
}
return pattern;
@@ -395,14 +441,16 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern();
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
- if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
+ if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
- while (PreviousPattern.ColumnHasObject(holdColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(holdColumn), () =>
+ {
holdColumn = Random.Next(RandomStart, TotalColumns);
+ });
}
// Create the hold note
- addToPattern(pattern, holdColumn, startTime, endTime);
+ addToPattern(pattern, holdColumn, startTime, EndTime);
int nextColumn = Random.Next(RandomStart, TotalColumns);
int noteCount;
@@ -425,8 +473,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
for (int j = 0; j < noteCount; j++)
{
- while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn)
+ RunWhile(() => rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn, () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
+
addToPattern(rowPattern, nextColumn, startTime, startTime);
}
}
@@ -434,7 +485,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
pattern.Add(rowPattern);
rowPattern.Clear();
- startTime += segmentDuration;
+ startTime += SegmentDuration;
}
return pattern;
@@ -452,7 +503,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (curveData == null)
return HitObject.Samples;
- double segmentTime = (endTime - HitObject.StartTime) / spanCount;
+ double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
return curveData.RepeatSamples[index];
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
index 3f34afee85..eae9a0fc3b 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
@@ -19,26 +19,33 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap)
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
{
- var endtimeData = HitObject as IHasEndTime;
-
- endTime = endtimeData?.EndTime ?? 0;
+ endTime = (HitObject as IHasEndTime)?.EndTime ?? 0;
}
- public override Pattern Generate()
+ public override IEnumerable Generate()
+ {
+ yield return generate();
+ }
+
+ private Pattern generate()
{
var pattern = new Pattern();
bool generateHold = endTime - HitObject.StartTime >= 100;
- if (TotalColumns == 8)
+ switch (TotalColumns)
{
- if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000)
+ case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
addToPattern(pattern, 0, generateHold);
- else
+ break;
+ case 8:
addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold);
+ break;
+ default:
+ if (TotalColumns > 0)
+ addToPattern(pattern, getNextRandomColumn(0), generateHold);
+ break;
}
- else if (TotalColumns > 0)
- addToPattern(pattern, getNextRandomColumn(0), generateHold);
return pattern;
}
@@ -52,8 +59,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
int nextColumn = Random.Next(start, TotalColumns);
- while (PreviousPattern.ColumnHasObject(nextColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(start, TotalColumns);
+ });
return nextColumn;
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
index b4160dc98b..b2b9fe2446 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Game.Audio;
@@ -20,12 +21,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType;
- public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, IBeatmap originalBeatmap)
+ public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density,
+ PatternType lastStair, IBeatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
- 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);
@@ -78,131 +77,137 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
else
convertType |= PatternType.LowProbability;
- if ((convertType & PatternType.KeepSingle) == 0)
+ if (!convertType.HasFlag(PatternType.KeepSingle))
{
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
convertType |= PatternType.Mirror;
- else
+ else if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
convertType |= PatternType.Gathered;
}
}
- public override Pattern Generate()
+ public override IEnumerable Generate()
{
- if (TotalColumns == 1)
+ yield return generate();
+ }
+
+ private Pattern generate()
+ {
+ var pattern = new Pattern();
+
+ try
{
- var pattern = new Pattern();
- addToPattern(pattern, 0);
- return pattern;
- }
-
- int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
-
- if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
- {
- // Generate a new pattern by copying the last hit objects in reverse-column order
- var pattern = new Pattern();
-
- for (int i = RandomStart; i < TotalColumns; i++)
- if (PreviousPattern.ColumnHasObject(i))
- addToPattern(pattern, RandomStart + TotalColumns - i - 1);
-
- return pattern;
- }
-
- if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
- // If we convert to 7K + 1, let's not overload the special key
- && (TotalColumns != 8 || lastColumn != 0)
- // Make sure the last column was not the centre column
- && (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
- {
- // Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
- var pattern = new Pattern();
-
- int column = RandomStart + TotalColumns - lastColumn - 1;
- addToPattern(pattern, column);
-
- return pattern;
- }
-
- if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any())
- {
- // Generate a new pattern by placing on the already filled columns
- var pattern = new Pattern();
-
- for (int i = RandomStart; i < TotalColumns; i++)
- if (PreviousPattern.ColumnHasObject(i))
- addToPattern(pattern, i);
-
- return pattern;
- }
-
- if ((convertType & PatternType.Stair) > 0 && PreviousPattern.HitObjects.Count() == 1)
- {
- // Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
- var pattern = new Pattern();
-
- int targetColumn = lastColumn + 1;
- if (targetColumn == TotalColumns)
+ if (TotalColumns == 1)
{
- targetColumn = RandomStart;
- StairType = PatternType.ReverseStair;
+ addToPattern(pattern, 0);
+ return pattern;
}
- addToPattern(pattern, targetColumn);
- return pattern;
- }
+ int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
- if ((convertType & PatternType.ReverseStair) > 0 && PreviousPattern.HitObjects.Count() == 1)
- {
- // Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
- var pattern = new Pattern();
-
- int targetColumn = lastColumn - 1;
- if (targetColumn == RandomStart - 1)
+ if (convertType.HasFlag(PatternType.Reverse) && PreviousPattern.HitObjects.Any())
{
- targetColumn = TotalColumns - 1;
- StairType = PatternType.Stair;
+ // Generate a new pattern by copying the last hit objects in reverse-column order
+ for (int i = RandomStart; i < TotalColumns; i++)
+ if (PreviousPattern.ColumnHasObject(i))
+ addToPattern(pattern, RandomStart + TotalColumns - i - 1);
+
+ return pattern;
}
- addToPattern(pattern, targetColumn);
- return pattern;
- }
+ if (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
+ // If we convert to 7K + 1, let's not overload the special key
+ && (TotalColumns != 8 || lastColumn != 0)
+ // Make sure the last column was not the centre column
+ && (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
+ {
+ // Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
+ int column = RandomStart + TotalColumns - lastColumn - 1;
+ addToPattern(pattern, column);
- if ((convertType & PatternType.KeepSingle) > 0)
- return generateRandomNotes(1);
+ return pattern;
+ }
+
+ if (convertType.HasFlag(PatternType.ForceStack) && PreviousPattern.HitObjects.Any())
+ {
+ // Generate a new pattern by placing on the already filled columns
+ for (int i = RandomStart; i < TotalColumns; i++)
+ if (PreviousPattern.ColumnHasObject(i))
+ addToPattern(pattern, i);
+
+ return pattern;
+ }
+
+ if (PreviousPattern.HitObjects.Count() == 1)
+ {
+ if (convertType.HasFlag(PatternType.Stair))
+ {
+ // Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
+ int targetColumn = lastColumn + 1;
+ if (targetColumn == TotalColumns)
+ targetColumn = RandomStart;
+
+ addToPattern(pattern, targetColumn);
+ return pattern;
+ }
+
+ if (convertType.HasFlag(PatternType.ReverseStair))
+ {
+ // Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
+ int targetColumn = lastColumn - 1;
+ if (targetColumn == RandomStart - 1)
+ targetColumn = TotalColumns - 1;
+
+ addToPattern(pattern, targetColumn);
+ return pattern;
+ }
+ }
+
+ if (convertType.HasFlag(PatternType.KeepSingle))
+ return pattern = generateRandomNotes(1);
+
+ if (convertType.HasFlag(PatternType.Mirror))
+ {
+ if (ConversionDifficulty > 6.5)
+ return pattern = generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
+ if (ConversionDifficulty > 4)
+ return pattern = generateRandomPatternWithMirrored(0.12, 0.17, 0);
+ return pattern = generateRandomPatternWithMirrored(0.12, 0, 0);
+ }
- if ((convertType & PatternType.Mirror) > 0)
- {
if (ConversionDifficulty > 6.5)
- return generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
+ {
+ if (convertType.HasFlag(PatternType.LowProbability))
+ return pattern = generateRandomPattern(0.78, 0.42, 0, 0);
+ return pattern = generateRandomPattern(1, 0.62, 0, 0);
+ }
+
if (ConversionDifficulty > 4)
- return generateRandomPatternWithMirrored(0.12, 0.17, 0);
- return generateRandomPatternWithMirrored(0.12, 0, 0);
- }
+ {
+ if (convertType.HasFlag(PatternType.LowProbability))
+ return pattern = generateRandomPattern(0.35, 0.08, 0, 0);
+ return pattern = generateRandomPattern(0.52, 0.15, 0, 0);
+ }
- if (ConversionDifficulty > 6.5)
+ if (ConversionDifficulty > 2)
+ {
+ if (convertType.HasFlag(PatternType.LowProbability))
+ return pattern = generateRandomPattern(0.18, 0, 0, 0);
+ return pattern = generateRandomPattern(0.45, 0, 0, 0);
+ }
+
+ return pattern = generateRandomPattern(0, 0, 0, 0);
+ }
+ finally
{
- if ((convertType & PatternType.LowProbability) > 0)
- return generateRandomPattern(0.78, 0.42, 0, 0);
- return generateRandomPattern(1, 0.62, 0, 0);
+ foreach (var obj in pattern.HitObjects)
+ {
+ if (convertType.HasFlag(PatternType.Stair) && obj.Column == TotalColumns - 1)
+ StairType = PatternType.ReverseStair;
+ if (convertType.HasFlag(PatternType.ReverseStair) && obj.Column == RandomStart)
+ StairType = PatternType.Stair;
+ }
}
-
- if (ConversionDifficulty > 4)
- {
- if ((convertType & PatternType.LowProbability) > 0)
- return generateRandomPattern(0.35, 0.08, 0, 0);
- return generateRandomPattern(0.52, 0.15, 0, 0);
- }
-
- if (ConversionDifficulty > 2)
- {
- if ((convertType & PatternType.LowProbability) > 0)
- return generateRandomPattern(0.18, 0, 0, 0);
- return generateRandomPattern(0.45, 0, 0, 0);
- }
-
- return generateRandomPattern(0, 0, 0, 0);
}
///
@@ -218,7 +223,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
var pattern = new Pattern();
- bool allowStacking = (convertType & PatternType.ForceNotStack) == 0;
+ bool allowStacking = !convertType.HasFlag(PatternType.ForceNotStack);
if (!allowStacking)
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
@@ -226,9 +231,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i < noteCount; i++)
{
- while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking)
+ RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking, () =>
{
- if ((convertType & PatternType.Gathered) > 0)
+ if (convertType.HasFlag(PatternType.Gathered))
{
nextColumn++;
if (nextColumn == TotalColumns)
@@ -236,7 +241,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
}
else
nextColumn = Random.Next(RandomStart, TotalColumns);
- }
+ });
addToPattern(pattern, nextColumn);
}
@@ -278,6 +283,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// The containing the hit objects.
private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3)
{
+ if (convertType.HasFlag(PatternType.ForceNotStack))
+ return generateRandomPattern(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
+
var pattern = new Pattern();
bool addToCentre;
@@ -287,8 +295,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = Random.Next(RandomStart, columnLimit);
for (int i = 0; i < noteCount; i++)
{
- while (pattern.ColumnHasObject(nextColumn))
+ RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, columnLimit);
+ });
// Add normal note
addToPattern(pattern, nextColumn);
@@ -360,9 +370,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
addToCentre = false;
- if ((convertType & PatternType.ForceNotStack) > 0)
- return getRandomNoteCount(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
-
switch (TotalColumns)
{
case 2:
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
index 930597c1ad..55081e5822 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
@@ -103,17 +103,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault();
HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault();
- double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
- drainTime -= OriginalBeatmap.TotalBreakTime;
+ // Drain time in seconds
+ int drainTime = (int)(((lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0) - OriginalBeatmap.TotalBreakTime) / 1000);
if (drainTime == 0)
- drainTime = 10000000;
-
- // We need this in seconds
- drainTime /= 1000;
+ drainTime = 10000;
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
- conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15;
+ conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15;
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
return conversionDifficulty.Value;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
index 2bfcd52b6a..e51cbcdc60 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
@@ -2,6 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using JetBrains.Annotations;
+using osu.Framework.Logging;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
@@ -11,6 +15,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
///
internal abstract class PatternGenerator
{
+ ///
+ /// An arbitrary maximum amount of iterations to perform in .
+ /// The specific value is not super important - enough such that no false-positives occur.
+ ///
+ /// /b/933228 requires at least 23 iterations.
+ ///
+ private const int max_rng_iterations = 30;
+
///
/// The last pattern.
///
@@ -41,10 +53,44 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
TotalColumns = Beatmap.TotalColumns;
}
+ protected void RunWhile([InstantHandle] Func condition, Action action)
+ {
+ int iterations = 0;
+
+ while (condition())
+ {
+ if (iterations++ >= max_rng_iterations)
+ {
+ // log an error but don't throw. we want to continue execution.
+ Logger.Error(new ExceededAllowedIterationsException(new StackTrace(0)),
+ "Conversion encountered errors. The beatmap may not be correctly converted.");
+ return;
+ }
+
+ action();
+ }
+ }
+
///
- /// Generates the pattern for , filled with hit objects.
+ /// Generates the patterns for , each filled with hit objects.
///
- /// The containing the hit objects.
- public abstract Pattern Generate();
+ /// The s containing the hit objects.
+ public abstract IEnumerable Generate();
+
+ ///
+ /// Denotes when a single conversion operation is in an infinitely looping state.
+ ///
+ public class ExceededAllowedIterationsException : Exception
+ {
+ private readonly string stackTrace;
+
+ public ExceededAllowedIterationsException(StackTrace stackTrace)
+ {
+ this.stackTrace = stackTrace.ToString();
+ }
+
+ public override string StackTrace => stackTrace;
+ public override string ToString() => $"{GetType().Name}: {Message}\r\n{StackTrace}";
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs b/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs
new file mode 100644
index 0000000000..bfa6bc0a17
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs
@@ -0,0 +1,84 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Graphics;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.UI.Scrolling;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
+{
+ public class HoldNoteMask : HitObjectMask
+ {
+ public new DrawableHoldNote HitObject => (DrawableHoldNote)base.HitObject;
+
+ private readonly IBindable direction = new Bindable();
+
+ private readonly BodyPiece body;
+
+ public HoldNoteMask(DrawableHoldNote hold)
+ : base(hold)
+ {
+ InternalChildren = new Drawable[]
+ {
+ new HoldNoteNoteMask(hold.Head),
+ new HoldNoteNoteMask(hold.Tail),
+ body = new BodyPiece
+ {
+ AccentColour = Color4.Transparent
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, IScrollingInfo scrollingInfo)
+ {
+ body.BorderColour = colours.Yellow;
+
+ direction.BindTo(scrollingInfo.Direction);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ Size = HitObject.DrawSize + new Vector2(0, HitObject.Tail.DrawHeight);
+ Position = Parent.ToLocalSpace(HitObject.ScreenSpaceDrawQuad.TopLeft);
+
+ // This is a side-effect of not matching the hitobject's anchors/origins, which is kinda hard to do
+ // When scrolling upwards our origin is already at the top of the head note (which is the intended location),
+ // but when scrolling downwards our origin is at the _bottom_ of the tail note (where we need to be at the _top_ of the tail note)
+ if (direction.Value == ScrollingDirection.Down)
+ Y -= HitObject.Tail.DrawHeight;
+ }
+
+ private class HoldNoteNoteMask : NoteMask
+ {
+ public HoldNoteNoteMask(DrawableNote note)
+ : base(note)
+ {
+ Select();
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ Anchor = HitObject.Anchor;
+ Origin = HitObject.Origin;
+
+ Position = HitObject.DrawPosition;
+ }
+
+ // Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input.
+ public override bool HandleMouseInput => false;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/NoteMask.cs b/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/NoteMask.cs
new file mode 100644
index 0000000000..78f876cb14
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/NoteMask.cs
@@ -0,0 +1,39 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+
+namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
+{
+ public class NoteMask : HitObjectMask
+ {
+ public NoteMask(DrawableNote note)
+ : base(note)
+ {
+ Scale = note.Scale;
+
+ CornerRadius = 5;
+ Masking = true;
+
+ AddInternal(new NotePiece());
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Colour = colours.Yellow;
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ Size = HitObject.DrawSize;
+ Position = Parent.ToLocalSpace(HitObject.ScreenSpaceDrawQuad.TopLeft);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.cs b/osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.cs
new file mode 100644
index 0000000000..e7bc526471
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Mania.Beatmaps;
+using osu.Game.Rulesets.Mania.UI;
+using System.Collections.Generic;
+
+namespace osu.Game.Rulesets.Mania.Edit
+{
+ public class ManiaEditPlayfield : ManiaPlayfield
+ {
+ public ManiaEditPlayfield(List stages)
+ : base(stages)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
new file mode 100644
index 0000000000..a01947a60b
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
@@ -0,0 +1,27 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using OpenTK;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Rulesets.Mania.Edit
+{
+ public class ManiaEditRulesetContainer : ManiaRulesetContainer
+ {
+ public ManiaEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
+ : base(ruleset, beatmap)
+ {
+ }
+
+ protected override Playfield CreatePlayfield() => new ManiaEditPlayfield(Beatmap.Stages)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ };
+
+ protected override Vector2 PlayfieldArea => Vector2.One;
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
new file mode 100644
index 0000000000..f37d8134ce
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
@@ -0,0 +1,56 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Edit.Tools;
+using osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.UI;
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Game.Rulesets.Mania.Configuration;
+using osu.Game.Rulesets.Mania.UI;
+
+namespace osu.Game.Rulesets.Mania.Edit
+{
+ public class ManiaHitObjectComposer : HitObjectComposer
+ {
+ protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
+
+ public ManiaHitObjectComposer(Ruleset ruleset)
+ : base(ruleset)
+ {
+ }
+
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
+ {
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+ dependencies.CacheAs(new ManiaScrollingInfo(Config));
+ return dependencies;
+ }
+
+ protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new ManiaEditRulesetContainer(ruleset, beatmap);
+
+ protected override IReadOnlyList CompositionTools => new ICompositionTool[]
+ {
+ new HitObjectCompositionTool("Note"),
+ new HitObjectCompositionTool("Hold"),
+ };
+
+ public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
+ {
+ switch (hitObject)
+ {
+ case DrawableNote note:
+ return new NoteMask(note);
+ case DrawableHoldNote holdNote:
+ return new HoldNoteMask(holdNote);
+ }
+
+ return base.CreateMaskFor(hitObject);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
deleted file mode 100644
index 3a4beda77d..0000000000
--- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Game.Rulesets.Scoring;
-
-namespace osu.Game.Rulesets.Mania.Judgements
-{
- public class HoldNoteTailJudgement : ManiaJudgement
- {
- ///
- /// Whether the hold note has been released too early and shouldn't give full score for the release.
- ///
- public bool HasBroken;
-
- protected override int NumericResultFor(HitResult result)
- {
- switch (result)
- {
- default:
- return base.NumericResultFor(result);
- case HitResult.Great:
- case HitResult.Perfect:
- return base.NumericResultFor(HasBroken ? HitResult.Good : result);
- }
- }
- }
-}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 3d58e63da5..19e89c8ec5 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -19,9 +19,11 @@ using osu.Game.Configuration;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty;
+using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania
@@ -32,6 +34,8 @@ namespace osu.Game.Rulesets.Mania
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new ManiaPerformanceCalculator(this, beatmap, score);
+ public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this);
+
public override IEnumerable ConvertLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
@@ -120,7 +124,7 @@ namespace osu.Game.Rulesets.Mania
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
new ManiaModFlashlight(),
};
- case ModType.Special:
+ case ModType.Conversion:
return new Mod[]
{
new MultiMod(new ManiaModKey4(),
@@ -135,6 +139,10 @@ namespace osu.Game.Rulesets.Mania
new ManiaModRandom(),
new ManiaModDualStages(),
new ManiaModMirror(),
+ };
+ case ModType.Automation:
+ return new Mod[]
+ {
new MultiMod(new ManiaModAutoplay(), new ModCinema()),
};
default:
diff --git a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
index 54a7bf954d..783142fadc 100644
--- a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
+++ b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
@@ -19,14 +19,14 @@ namespace osu.Game.Rulesets.Mania
}
[BackgroundDependencyLoader]
- private void load(ManiaConfigManager config)
+ private void load()
{
Children = new Drawable[]
{
new SettingsEnumDropdown
{
LabelText = "Scrolling direction",
- Bindable = config.GetBindable(ManiaSetting.ScrollDirection)
+ Bindable = ((ManiaConfigManager)Config).GetBindable(ManiaSetting.ScrollDirection)
}
};
}
diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs
index a3efd5c2bd..785cd5ab06 100644
--- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs
+++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs
@@ -15,11 +15,15 @@ namespace osu.Game.Rulesets.Mania.MathUtils
private const uint y = 842502087;
private const uint z = 3579807591;
private const uint w = 273326509;
- private uint _x, _y = y, _z = z, _w = w;
+
+ internal uint X { get; private set; }
+ internal uint Y { get; private set; } = y;
+ internal uint Z { get; private set; } = z;
+ internal uint W { get; private set; } = w;
public FastRandom(int seed)
{
- _x = (uint)seed;
+ X = (uint)seed;
}
public FastRandom()
@@ -33,11 +37,11 @@ namespace osu.Game.Rulesets.Mania.MathUtils
/// The random value.
public uint NextUInt()
{
- uint t = _x ^ _x << 11;
- _x = _y;
- _y = _z;
- _z = _w;
- return _w = _w ^ _w >> 19 ^ t ^ t >> 8;
+ uint t = X ^ X << 11;
+ X = Y;
+ Y = Z;
+ Z = W;
+ return W = W ^ W >> 19 ^ t ^ t >> 8;
}
///
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs
index 6bfe295c3d..6d91c03c13 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs
@@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string ShortenedName => Name;
public abstract int KeyCount { get; }
+ public override ModType Type => ModType.Conversion;
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true;
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
index 256811b4c1..aecfb50fbe 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
@@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Dual Stages";
public override string ShortenedName => "DS";
public override string Description => @"Double the stages, double the fun!";
+ public override ModType Type => ModType.Conversion;
public override double ScoreMultiplier => 1;
private bool isForCurrentRuleset;
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs
index 4192ec78da..847b0037f1 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string Name => "Mirror";
public override string ShortenedName => "MR";
- public override ModType Type => ModType.Special;
+ public override ModType Type => ModType.Conversion;
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
index 2f951461c3..0915b80742 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
@@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string Name => "Random";
public override string ShortenedName => "RD";
+ public override ModType Type => ModType.Conversion;
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index 597450f223..af2a889f03 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
@@ -19,10 +18,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
public class DrawableHoldNote : DrawableManiaHitObject, IKeyBindingHandler
{
- public override bool DisplayJudgement => false;
+ public override bool DisplayResult => false;
- private readonly DrawableNote head;
- private readonly DrawableNote tail;
+ public readonly DrawableNote Head;
+ public readonly DrawableNote Tail;
private readonly BodyPiece bodyPiece;
@@ -57,12 +56,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
HoldStartTime = () => holdStartTime
})
},
- head = new DrawableHeadNote(this)
+ Head = new DrawableHeadNote(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
- tail = new DrawableTailNote(this)
+ Tail = new DrawableTailNote(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
@@ -72,8 +71,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
foreach (var tick in tickContainer)
AddNested(tick);
- AddNested(head);
- AddNested(tail);
+ AddNested(Head);
+ AddNested(Tail);
}
protected override void OnDirectionChanged(ScrollingDirection direction)
@@ -91,16 +90,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.AccentColour = value;
bodyPiece.AccentColour = value;
- head.AccentColour = value;
- tail.AccentColour = value;
+ Head.AccentColour = value;
+ Tail.AccentColour = value;
tickContainer.ForEach(t => t.AccentColour = value);
}
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
- if (tail.AllJudged)
- AddJudgement(new HoldNoteJudgement { Result = HitResult.Perfect });
+ if (Tail.AllJudged)
+ ApplyResult(r => r.Type = HitResult.Perfect);
}
protected override void Update()
@@ -108,8 +107,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.Update();
// Make the body piece not lie under the head note
- bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * head.Height / 2;
- bodyPiece.Height = DrawHeight - head.Height / 2 + tail.Height / 2;
+ bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2;
+ bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
}
public bool OnPressed(ManiaAction action)
@@ -141,7 +140,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
holdStartTime = null;
// If the key has been released too early, the user should not receive full score for the release
- if (!tail.IsHit)
+ if (!Tail.IsHit)
hasBroken = true;
return true;
@@ -166,7 +165,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return false;
// If the key has been released too early, the user should not receive full score for the release
- if (Judgements.Any(j => j.Result == HitResult.Miss))
+ if (Result.Type == HitResult.Miss)
holdNote.hasBroken = true;
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
@@ -197,7 +196,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
this.holdNote = holdNote;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
// Factor in the release lenience
timeOffset /= release_window_lenience;
@@ -205,13 +204,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- {
- AddJudgement(new HoldNoteTailJudgement
- {
- Result = HitResult.Miss,
- HasBroken = holdNote.hasBroken
- });
- }
+ ApplyResult(r => r.Type = HitResult.Miss);
return;
}
@@ -220,10 +213,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (result == HitResult.None)
return;
- AddJudgement(new HoldNoteTailJudgement
+ ApplyResult(r =>
{
- Result = result,
- HasBroken = holdNote.hasBroken
+ if (holdNote.hasBroken && (result == HitResult.Perfect || result == HitResult.Perfect))
+ result = HitResult.Good;
+
+ r.Type = result;
});
}
@@ -238,7 +233,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != Action.Value)
return false;
- UpdateJudgement(true);
+ UpdateResult(true);
// Handled by the hold note, which will set holding = false
return false;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
index 5df6079efa..01d5bc6fd4 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
@@ -7,7 +7,6 @@ using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Scoring;
@@ -72,29 +71,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
- if (!userTriggered)
- return;
-
if (Time.Current < HitObject.StartTime)
return;
- if (HoldStartTime?.Invoke() > HitObject.StartTime)
- return;
+ var startTime = HoldStartTime?.Invoke();
- AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
- }
-
- protected override void Update()
- {
- if (AllJudged)
- return;
-
- if (HoldStartTime?.Invoke() == null)
- return;
-
- UpdateJudgement(true);
+ if (startTime == null || startTime > HitObject.StartTime)
+ ApplyResult(r => r.Type = HitResult.Miss);
+ else
+ ApplyResult(r => r.Type = HitResult.Perfect);
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 18084c4c08..7567f40b2f 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -6,7 +6,6 @@ using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
-using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
@@ -56,12 +55,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- AddJudgement(new ManiaJudgement { Result = HitResult.Miss });
+ ApplyResult(r => r.Type = HitResult.Miss);
return;
}
@@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (result == HitResult.None)
return;
- AddJudgement(new ManiaJudgement { Result = result });
+ ApplyResult(r => r.Type = result);
}
public virtual bool OnPressed(ManiaAction action)
@@ -77,7 +76,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != Action.Value)
return false;
- return UpdateJudgement(true);
+ return UpdateResult(true);
}
public virtual bool OnReleased(ManiaAction action) => false;
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
index 22fa93a308..77562bb4c2 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
@@ -3,6 +3,8 @@
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Mania.Objects
@@ -55,7 +57,7 @@ namespace osu.Game.Rulesets.Mania.Objects
///
/// The tail note of the hold.
///
- public readonly Note Tail = new Note();
+ public readonly TailNote Tail = new TailNote();
///
/// The time between ticks of this hold.
@@ -68,9 +70,6 @@ namespace osu.Game.Rulesets.Mania.Objects
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
-
- Head.ApplyDefaults(controlPointInfo, difficulty);
- Tail.ApplyDefaults(controlPointInfo, difficulty);
}
protected override void CreateNestedHitObjects()
@@ -78,6 +77,9 @@ namespace osu.Game.Rulesets.Mania.Objects
base.CreateNestedHitObjects();
createTicks();
+
+ AddNested(Head);
+ AddNested(Tail);
}
private void createTicks()
@@ -94,5 +96,7 @@ namespace osu.Game.Rulesets.Mania.Objects
});
}
}
+
+ public override Judgement CreateJudgement() => new HoldNoteJudgement();
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
index d078c15c92..05959a31c0 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
@@ -1,6 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mania.Judgements;
+
namespace osu.Game.Rulesets.Mania.Objects
{
///
@@ -8,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Objects
///
public class HoldNoteTick : ManiaHitObject
{
+ public override Judgement CreateJudgement() => new HoldNoteTickJudgement();
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Note.cs b/osu.Game.Rulesets.Mania/Objects/Note.cs
index 975188e550..42877649d2 100644
--- a/osu.Game.Rulesets.Mania/Objects/Note.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Note.cs
@@ -1,6 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mania.Judgements;
+
namespace osu.Game.Rulesets.Mania.Objects
{
///
@@ -8,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Objects
///
public class Note : ManiaHitObject
{
+ public override Judgement CreateJudgement() => new ManiaJudgement();
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/TailNote.cs b/osu.Game.Rulesets.Mania/Objects/TailNote.cs
new file mode 100644
index 0000000000..9de542bcd3
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Objects/TailNote.cs
@@ -0,0 +1,13 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mania.Judgements;
+
+namespace osu.Game.Rulesets.Mania.Objects
+{
+ public class TailNote : Note
+ {
+ public override Judgement CreateJudgement() => new ManiaJudgement();
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
index d006a3e1c7..0eef540d97 100644
--- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
@@ -56,10 +56,15 @@ namespace osu.Game.Rulesets.Mania.Replays
{
foreach (var point in group)
{
- if (point is HitPoint)
- actions.Add(columnActions[point.Column]);
- if (point is ReleasePoint)
- actions.Remove(columnActions[point.Column]);
+ switch (point)
+ {
+ case HitPoint _:
+ actions.Add(columnActions[point.Column]);
+ break;
+ case ReleasePoint _:
+ actions.Remove(columnActions[point.Column]);
+ break;
+ }
}
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs
index 29eeb1cab5..ea239bf80f 100644
--- a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs
@@ -3,7 +3,7 @@
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Input;
+using osu.Framework.Input.StateChanges;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Mania.Replays
diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic-expected-conversion.json b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic-expected-conversion.json
index d593b2b052..753db99856 100644
--- a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic-expected-conversion.json
+++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic-expected-conversion.json
@@ -1,103 +1,132 @@
{
"Mappings": [{
- "StartTime": 500,
- "Objects": [{
- "StartTime": 500,
- "EndTime": 2500,
- "Column": 0
- },
- {
- "StartTime": 1500,
- "EndTime": 2500,
- "Column": 1
- }
- ]
- },
- {
- "StartTime": 3000,
- "Objects": [{
- "StartTime": 3000,
- "EndTime": 4000,
- "Column": 2
- }]
- },
- {
- "StartTime": 4500,
- "Objects": [{
- "StartTime": 4500,
- "EndTime": 5500,
- "Column": 4
- }]
- },
- {
- "StartTime": 6000,
- "Objects": [{
- "StartTime": 6000,
- "EndTime": 6500,
- "Column": 2
- }]
- },
- {
- "StartTime": 7000,
- "Objects": [{
- "StartTime": 7000,
- "EndTime": 8000,
- "Column": 2
- }]
- },
- {
- "StartTime": 8500,
- "Objects": [{
- "StartTime": 8500,
- "EndTime": 11000,
- "Column": 0
- }]
- },
- {
- "StartTime": 11500,
- "Objects": [{
- "StartTime": 11500,
- "EndTime": 12000,
- "Column": 1
- }]
- },
- {
- "StartTime": 12500,
- "Objects": [{
- "StartTime": 12500,
- "EndTime": 16500,
- "Column": 4
- }]
- },
- {
- "StartTime": 17000,
- "Objects": [{
- "StartTime": 17000,
- "EndTime": 18000,
- "Column": 2
- }]
- },
- {
- "StartTime": 18500,
- "Objects": [{
- "StartTime": 18500,
- "EndTime": 19450,
- "Column": 0
- }]
- },
- {
- "StartTime": 19875,
- "Objects": [{
- "StartTime": 19875,
- "EndTime": 23875,
- "Column": 1
- },
- {
- "StartTime": 19875,
- "EndTime": 23875,
- "Column": 0
- }
- ]
- }
- ]
+ "RandomW": 2659373485,
+ "RandomX": 3579807591,
+ "RandomY": 273326509,
+ "RandomZ": 272969173,
+ "StartTime": 500.0,
+ "Objects": [{
+ "StartTime": 500.0,
+ "EndTime": 2500.0,
+ "Column": 0
+ }, {
+ "StartTime": 1500.0,
+ "EndTime": 2500.0,
+ "Column": 1
+ }]
+ }, {
+ "RandomW": 3083803045,
+ "RandomX": 273326509,
+ "RandomY": 272969173,
+ "RandomZ": 2659373485,
+ "StartTime": 3000.0,
+ "Objects": [{
+ "StartTime": 3000.0,
+ "EndTime": 4000.0,
+ "Column": 2
+ }]
+ }, {
+ "RandomW": 4073554232,
+ "RandomX": 272969173,
+ "RandomY": 2659373485,
+ "RandomZ": 3083803045,
+ "StartTime": 4500.0,
+ "Objects": [{
+ "StartTime": 4500.0,
+ "EndTime": 5500.0,
+ "Column": 4
+ }]
+ }, {
+ "RandomW": 3420401969,
+ "RandomX": 2659373485,
+ "RandomY": 3083803045,
+ "RandomZ": 4073554232,
+ "StartTime": 6000.0,
+ "Objects": [{
+ "StartTime": 6000.0,
+ "EndTime": 6500.0,
+ "Column": 2
+ }]
+ }, {
+ "RandomW": 1129881182,
+ "RandomX": 3083803045,
+ "RandomY": 4073554232,
+ "RandomZ": 3420401969,
+ "StartTime": 7000.0,
+ "Objects": [{
+ "StartTime": 7000.0,
+ "EndTime": 8000.0,
+ "Column": 2
+ }]
+ }, {
+ "RandomW": 315568458,
+ "RandomX": 3420401969,
+ "RandomY": 1129881182,
+ "RandomZ": 2358617505,
+ "StartTime": 8500.0,
+ "Objects": [{
+ "StartTime": 8500.0,
+ "EndTime": 11000.0,
+ "Column": 0
+ }]
+ }, {
+ "RandomW": 548134043,
+ "RandomX": 1129881182,
+ "RandomY": 2358617505,
+ "RandomZ": 315568458,
+ "StartTime": 11500.0,
+ "Objects": [{
+ "StartTime": 11500.0,
+ "EndTime": 12000.0,
+ "Column": 1
+ }]
+ }, {
+ "RandomW": 3979422122,
+ "RandomX": 548134043,
+ "RandomY": 2810584254,
+ "RandomZ": 2250186050,
+ "StartTime": 12500.0,
+ "Objects": [{
+ "StartTime": 12500.0,
+ "EndTime": 16500.0,
+ "Column": 4
+ }]
+ }, {
+ "RandomW": 2466283411,
+ "RandomX": 2810584254,
+ "RandomY": 2250186050,
+ "RandomZ": 3979422122,
+ "StartTime": 17000.0,
+ "Objects": [{
+ "StartTime": 17000.0,
+ "EndTime": 18000.0,
+ "Column": 2
+ }]
+ }, {
+ "RandomW": 83157665,
+ "RandomX": 2250186050,
+ "RandomY": 3979422122,
+ "RandomZ": 2466283411,
+ "StartTime": 18500.0,
+ "Objects": [{
+ "StartTime": 18500.0,
+ "EndTime": 19450.0,
+ "Column": 0
+ }]
+ }, {
+ "RandomW": 2383087700,
+ "RandomX": 83157665,
+ "RandomY": 2055150192,
+ "RandomZ": 510071020,
+ "StartTime": 19875.0,
+ "Objects": [{
+ "StartTime": 19875.0,
+ "EndTime": 23875.0,
+ "Column": 1
+ }, {
+ "StartTime": 19875.0,
+ "EndTime": 23875.0,
+ "Column": 0
+ }]
+ }]
}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 4c955a680e..12b32c46ee 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
@@ -97,31 +96,20 @@ namespace osu.Game.Rulesets.Mania.Scoring
{
}
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void ApplyBeatmap(Beatmap beatmap)
{
+ base.ApplyBeatmap(beatmap);
+
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
+ }
+ protected override void SimulateAutoplay(Beatmap beatmap)
+ {
while (true)
{
- foreach (var obj in beatmap.HitObjects)
- {
- var holdNote = obj as HoldNote;
-
- if (holdNote != null)
- {
- // Head
- AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
-
- // Ticks
- int tickCount = holdNote.NestedHitObjects.OfType().Count();
- for (int i = 0; i < tickCount; i++)
- AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
- }
-
- AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
- }
+ base.SimulateAutoplay(beatmap);
if (!HasFailed)
break;
@@ -133,20 +121,20 @@ namespace osu.Game.Rulesets.Mania.Scoring
}
}
- protected override void OnNewJudgement(Judgement judgement)
+ protected override void ApplyResult(JudgementResult result)
{
- base.OnNewJudgement(judgement);
+ base.ApplyResult(result);
- bool isTick = judgement is HoldNoteTickJudgement;
+ bool isTick = result.Judgement is HoldNoteTickJudgement;
if (isTick)
{
- if (judgement.IsHit)
+ if (result.IsHit)
Health.Value += hpMultiplier * hp_increase_tick;
}
else
{
- switch (judgement.Result)
+ switch (result.Type)
{
case HitResult.Miss:
Health.Value += hpMissMultiplier * hp_increase_miss;
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index a19a6fb5d4..d489d48fc3 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -32,8 +32,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Container Content => hitObjectArea;
- public Column(ScrollingDirection direction)
- : base(direction)
+ public Column()
{
RelativeSizeAxes = Axes.Y;
Width = column_width;
@@ -118,9 +117,9 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs>(Action);
return dependencies;
}
@@ -132,14 +131,14 @@ namespace osu.Game.Rulesets.Mania.UI
public override void Add(DrawableHitObject hitObject)
{
hitObject.AccentColour = AccentColour;
- hitObject.OnJudgement += OnJudgement;
+ hitObject.OnNewResult += OnNewResult;
HitObjects.Add(hitObject);
}
- internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (!judgement.IsHit || !judgedObject.DisplayJudgement)
+ if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements)
return;
explosionContainer.Add(new HitExplosion(judgedObject)
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
index 6566d44ef5..dc66249cd9 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
@@ -8,10 +8,10 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
- internal class DrawableManiaJudgement : DrawableJudgement
+ public class DrawableManiaJudgement : DrawableJudgement
{
- public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject)
- : base(judgement, judgedObject)
+ public DrawableManiaJudgement(JudgementResult result, DrawableHitObject judgedObject)
+ : base(result, judgedObject)
{
}
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.UI
this.FadeInFromZero(50, Easing.OutQuint);
- if (Judgement.IsHit)
+ if (Result.IsHit)
{
this.ScaleTo(0.8f);
this.ScaleTo(1, 250, Easing.OutElastic);
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
index 2f8ad7b17e..6bf63443b5 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
@@ -1,28 +1,23 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Mania.Objects;
using osu.Framework.Graphics.Containers;
using System;
using System.Collections.Generic;
-using System.Linq;
-using osu.Framework.Allocation;
-using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Configuration;
-using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaPlayfield : ManiaScrollingPlayfield
{
- public List Columns => stages.SelectMany(x => x.Columns).ToList();
private readonly List stages = new List();
- public ManiaPlayfield(ScrollingDirection direction, List stageDefinitions)
- : base(direction)
+ public ManiaPlayfield(List stageDefinitions)
{
if (stageDefinitions == null)
throw new ArgumentNullException(nameof(stageDefinitions));
@@ -42,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.UI
int firstColumnIndex = 0;
for (int i = 0; i < stageDefinitions.Count; i++)
{
- var newStage = new ManiaStage(direction, firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
+ var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
newStage.VisibleTimeRange.BindTo(VisibleTimeRange);
playfieldGrid.Content[0][i] = newStage;
@@ -76,10 +71,5 @@ namespace osu.Game.Rulesets.Mania.UI
{
maniaConfig.BindWith(ManiaSetting.ScrollTime, VisibleTimeRange);
}
-
- internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
- {
- getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement);
- }
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
index e6ebf43c67..09ebde2799 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
@@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
@@ -35,8 +34,7 @@ namespace osu.Game.Rulesets.Mania.UI
public IEnumerable BarLines;
- private readonly Bindable configDirection = new Bindable();
- private ScrollingInfo scrollingInfo;
+ protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
@@ -70,24 +68,24 @@ namespace osu.Game.Rulesets.Mania.UI
}
[BackgroundDependencyLoader]
- private void load(ManiaConfigManager config)
+ private void load()
{
BarLines.ForEach(Playfield.Add);
-
- config.BindWith(ManiaSetting.ScrollDirection, configDirection);
- configDirection.BindValueChanged(d => scrollingInfo.Direction.Value = (ScrollingDirection)d, true);
}
private DependencyContainer dependencies;
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
- dependencies.CacheAs(scrollingInfo = new ScrollingInfo());
+ dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+
+ if (dependencies.Get() == null)
+ dependencies.CacheAs(new ManiaScrollingInfo(Config));
+
return dependencies;
}
- protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(scrollingInfo.Direction, Beatmap.Stages)
+ protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -115,11 +113,5 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
-
- private class ScrollingInfo : IScrollingInfo
- {
- public readonly Bindable Direction = new Bindable();
- IBindable IScrollingInfo.Direction => Direction;
- }
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaScrollingInfo.cs b/osu.Game.Rulesets.Mania/UI/ManiaScrollingInfo.cs
new file mode 100644
index 0000000000..624ea13e1b
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/ManiaScrollingInfo.cs
@@ -0,0 +1,23 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Configuration;
+using osu.Game.Rulesets.Mania.Configuration;
+using osu.Game.Rulesets.UI.Scrolling;
+
+namespace osu.Game.Rulesets.Mania.UI
+{
+ public class ManiaScrollingInfo : IScrollingInfo
+ {
+ private readonly Bindable configDirection = new Bindable();
+
+ public readonly Bindable Direction = new Bindable();
+ IBindable IScrollingInfo.Direction => Direction;
+
+ public ManiaScrollingInfo(ManiaConfigManager config)
+ {
+ config.BindWith(ManiaSetting.ScrollDirection, configDirection);
+ configDirection.BindValueChanged(v => Direction.Value = (ScrollingDirection)v, true);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
index f1ff0665cd..4d6c5a747a 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
@@ -11,11 +11,6 @@ namespace osu.Game.Rulesets.Mania.UI
{
private readonly IBindable direction = new Bindable();
- public ManiaScrollingPlayfield(ScrollingDirection direction)
- : base(direction)
- {
- }
-
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
{
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
index 4c7deb4567..f292d5ff16 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.UI
///
/// A collection of s.
///
- internal class ManiaStage : ManiaScrollingPlayfield
+ public class ManiaStage : ManiaScrollingPlayfield
{
public const float HIT_TARGET_POSITION = 50;
@@ -43,8 +43,7 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly int firstColumnIndex;
- public ManiaStage(ScrollingDirection direction, int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
- : base(direction)
+ public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
{
this.firstColumnIndex = firstColumnIndex;
@@ -124,7 +123,7 @@ namespace osu.Game.Rulesets.Mania.UI
for (int i = 0; i < definition.Columns; i++)
{
var isSpecial = definition.IsSpecialColumn(i);
- var column = new Column(direction)
+ var column = new Column
{
IsSpecial = isSpecial,
Action = { Value = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ }
@@ -157,18 +156,18 @@ namespace osu.Game.Rulesets.Mania.UI
var maniaObject = (ManiaHitObject)h.HitObject;
int columnIndex = maniaObject.Column - firstColumnIndex;
Columns.ElementAt(columnIndex).Add(h);
- h.OnJudgement += OnJudgement;
+ h.OnNewResult += OnNewResult;
}
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
- internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (!judgedObject.DisplayJudgement)
+ if (!judgedObject.DisplayResult || !DisplayJudgements)
return;
judgements.Clear();
- judgements.Add(new DrawableManiaJudgement(judgement, judgedObject)
+ judgements.Add(new DrawableManiaJudgement(result, judgedObject)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json
index 13aba025fd..fe3ecbec47 100644
--- a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json
+++ b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json
@@ -2,35 +2,7 @@
"version": "0.2.0",
"configurations": [
{
- "name": "VisualTests (Debug, net471)",
- "windows": {
- "type": "clr"
- },
- "type": "mono",
- "request": "launch",
- "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe",
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug, msbuild)",
- "runtimeExecutable": null,
- "env": {},
- "console": "internalConsole"
- },
- {
- "name": "VisualTests (Release, net471)",
- "windows": {
- "type": "clr"
- },
- "type": "mono",
- "request": "launch",
- "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Osu.Tests.exe",
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release, msbuild)",
- "runtimeExecutable": null,
- "env": {},
- "console": "internalConsole"
- },
- {
- "name": "VisualTests (Debug, netcoreapp2.1)",
+ "name": "VisualTests (Debug)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -38,12 +10,12 @@
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug, dotnet)",
+ "preLaunchTask": "Build (Debug)",
"env": {},
"console": "internalConsole"
},
{
- "name": "VisualTests (Release, netcoreapp2.1)",
+ "name": "VisualTests (Release)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -51,7 +23,7 @@
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release, dotnet)",
+ "preLaunchTask": "Build (Release)",
"env": {},
"console": "internalConsole"
}
diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json
index 62cf51382f..ed2a015e11 100644
--- a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json
+++ b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json
@@ -4,43 +4,13 @@
"version": "2.0.0",
"tasks": [
{
- "label": "Build (Debug, msbuild)",
- "type": "shell",
- "command": "msbuild",
- "args": [
- "osu.Game.Rulesets.Osu.Tests.csproj",
- "/p:TargetFramework=net471",
- "/p:GenerateFullPaths=true",
- "/m",
- "/verbosity:m"
- ],
- "group": "build",
- "problemMatcher": "$msCompile"
- },
- {
- "label": "Build (Release, msbuild)",
- "type": "shell",
- "command": "msbuild",
- "args": [
- "osu.Game.Rulesets.Osu.Tests.csproj",
- "/p:Configuration=Release",
- "/p:TargetFramework=net471",
- "/p:GenerateFullPaths=true",
- "/m",
- "/verbosity:m"
- ],
- "group": "build",
- "problemMatcher": "$msCompile"
- },
- {
- "label": "Build (Debug, dotnet)",
+ "label": "Build (Debug)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Osu.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -49,14 +19,13 @@
"problemMatcher": "$msCompile"
},
{
- "label": "Build (Release, dotnet)",
+ "label": "Build (Release)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Osu.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -66,16 +35,7 @@
"problemMatcher": "$msCompile"
},
{
- "label": "Restore (net471)",
- "type": "shell",
- "command": "nuget",
- "args": [
- "restore"
- ],
- "problemMatcher": []
- },
- {
- "label": "Restore (netcoreapp2.1)",
+ "label": "Restore",
"type": "shell",
"command": "dotnet",
"args": [
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
index 3fa039d946..3a551bbbcf 100644
--- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
@@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public struct ConvertValue : IEquatable
{
///
- /// A sane value to account for osu!stable using ints everwhere.
+ /// A sane value to account for osu!stable using s everywhere.
///
private const double conversion_lenience = 2;
diff --git a/osu.Game.Rulesets.Osu.Tests/StackingTest.cs b/osu.Game.Rulesets.Osu.Tests/StackingTest.cs
new file mode 100644
index 0000000000..579cb77084
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/StackingTest.cs
@@ -0,0 +1,64 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.IO;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Tests.Beatmaps;
+using Decoder = osu.Game.Beatmaps.Formats.Decoder;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [TestFixture]
+ public class StackingTest
+ {
+ [Test]
+ public void TestStacking()
+ {
+ using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(beatmap_data)))
+ using (var reader = new StreamReader(stream))
+ {
+ var beatmap = Decoder.GetDecoder(reader).Decode(reader);
+ var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo);
+
+ var objects = converted.HitObjects.ToList();
+
+ // The last hitobject triggers the stacking
+ for (int i = 0; i < objects.Count - 1; i++)
+ Assert.AreEqual(0, ((OsuHitObject)objects[i]).StackHeight);
+ }
+ }
+
+ private const string beatmap_data = @"
+osu file format v14
+
+[General]
+StackLeniency: 0.2
+
+[Difficulty]
+ApproachRate:9.2
+SliderMultiplier:1
+SliderTickRate:0.5
+
+[TimingPoints]
+217871,6400,4,2,1,20,1,0
+217871,-800,4,2,1,20,0,0
+218071,-787.5,4,2,1,20,0,0
+218271,-775,4,2,1,20,0,0
+218471,-762.5,4,2,1,20,0,0
+218671,-750,4,2,1,20,0,0
+240271,-10,4,2,0,5,0,0
+
+[HitObjects]
+311,185,217871,6,0,L|318:158,1,25
+311,185,218071,2,0,L|335:170,1,25
+311,185,218271,2,0,L|338:192,1,25
+311,185,218471,2,0,L|325:209,1,25
+311,185,218671,2,0,L|304:212,1,25
+311,185,240271,5,0,0:0:0:0:
+";
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs
index 49c6978cdc..6b67188791 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs
@@ -5,12 +5,10 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
using OpenTK;
-using osu.Game.Rulesets.Osu.Judgements;
using System.Collections.Generic;
using System;
using osu.Game.Rulesets.Mods;
@@ -96,21 +94,17 @@ namespace osu.Game.Rulesets.Osu.Tests
this.auto = auto;
}
- public void TriggerJudgement() => UpdateJudgement(true);
+ public void TriggerJudgement() => UpdateResult(true);
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (auto && !userTriggered && timeOffset > 0)
{
// force success
- AddJudgement(new OsuJudgement
- {
- Result = HitResult.Great
- });
- State.Value = ArmedState.Hit;
+ ApplyResult(r => r.Type = HitResult.Great);
}
else
- base.CheckForJudgements(userTriggered, timeOffset);
+ base.CheckForResult(userTriggered, timeOffset);
}
}
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
index cb1ea5cc5f..3f9464a98f 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
@@ -304,13 +304,13 @@ namespace osu.Game.Rulesets.Osu.Tests
foreach (var mod in Mods.OfType())
mod.ApplyToDrawableHitObjects(new[] { drawable });
- drawable.OnJudgement += onJudgement;
+ drawable.OnNewResult += onNewResult;
Add(drawable);
}
private float judgementOffsetDirection = 1;
- private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
var osuObject = judgedObject as DrawableOsuHitObject;
if (osuObject == null)
@@ -321,8 +321,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Text = judgement.IsHit ? "Hit!" : "Miss!",
- Colour = judgement.IsHit ? Color4.Green : Color4.Red,
+ Text = result.IsHit ? "Hit!" : "Miss!",
+ Colour = result.IsHit ? Color4.Green : Color4.Red,
TextSize = 30,
Position = osuObject.HitObject.StackedEndPosition + judgementOffsetDirection * new Vector2(0, 45)
});
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs
index b05a763e88..3b91ea93b8 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs
@@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests
this.auto = auto;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1)
{
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Tests
auto = false;
}
- base.CheckForJudgements(userTriggered, timeOffset);
+ base.CheckForResult(userTriggered, timeOffset);
}
}
}
diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
index c5d9b26145..247d5e18c1 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -1,8 +1,14 @@
+
+
+
+
+
+
WinExe
- netcoreapp2.1;net471
+ netcoreapp2.1
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
index 405493cde4..9e0e649eb2 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
@@ -12,7 +12,7 @@ using osu.Game.Rulesets.Osu.UI;
namespace osu.Game.Rulesets.Osu.Beatmaps
{
- internal class OsuBeatmapConverter : BeatmapConverter
+ public class OsuBeatmapConverter : BeatmapConverter
{
public OsuBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
@@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
RepeatCount = curveData.RepeatCount,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset
};
}
@@ -52,7 +53,9 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
StartTime = original.StartTime,
Samples = original.Samples,
EndTime = endTimeData.EndTime,
- Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2
+ Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2,
+ NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
};
}
else
@@ -62,7 +65,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
StartTime = original.StartTime,
Samples = original.Samples,
Position = positionData?.Position ?? Vector2.Zero,
- NewCombo = comboData?.NewCombo ?? false
+ NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
};
}
}
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
index bbe2d67baa..cfb1b0f050 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
@@ -8,16 +8,16 @@ using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Beatmaps
{
- internal class OsuBeatmapProcessor : BeatmapProcessor
+ public class OsuBeatmapProcessor : BeatmapProcessor
{
public OsuBeatmapProcessor(IBeatmap beatmap)
: base(beatmap)
{
}
- public override void PreProcess()
+ public override void PostProcess()
{
- base.PreProcess();
+ base.PostProcess();
applyStacking((Beatmap)Beatmap);
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 62fafd8196..5e91ed7a97 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -61,19 +61,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
- // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
+ // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
- double preEmpt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
+ double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
int maxCombo = beatmap.HitObjects.Count();
- // Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above)
+ // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1);
return new OsuDifficultyAttributes(mods, starRating)
{
AimStrain = aimRating,
SpeedStrain = speedRating,
- ApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5,
+ ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
OverallDifficulty = (80 - hitWindowGreat) / 6,
MaxCombo = maxCombo
};
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs
deleted file mode 100644
index 6213bb1329..0000000000
--- a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Game.Rulesets.Osu.UI;
-
-namespace osu.Game.Rulesets.Osu.Edit
-{
- public class OsuEditPlayfield : OsuPlayfield
- {
- protected override bool DisplayJudgements => false;
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
index ea33ec9ae0..6efa16bf56 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
@@ -4,7 +4,6 @@
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.UI;
-using osu.Game.Rulesets.UI;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Edit
@@ -16,8 +15,6 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
- protected override Playfield CreatePlayfield() => new OsuEditPlayfield();
-
protected override Vector2 PlayfieldArea => Vector2.One;
protected override CursorContainer CreateCursor() => null;
diff --git a/osu.Game.Rulesets.Osu/Judgements/ComboResult.cs b/osu.Game.Rulesets.Osu/Judgements/ComboResult.cs
new file mode 100644
index 0000000000..3000031c78
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Judgements/ComboResult.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.ComponentModel;
+
+namespace osu.Game.Rulesets.Osu.Judgements
+{
+ public enum ComboResult
+ {
+ [Description(@"")]
+ None,
+ [Description(@"Good")]
+ Good,
+ [Description(@"Amazing")]
+ Perfect
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
index 26becfdec9..b1c9760866 100644
--- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Judgements
@@ -25,7 +24,5 @@ namespace osu.Game.Rulesets.Osu.Judgements
return 300;
}
}
-
- public ComboResult Combo;
}
}
diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs
new file mode 100644
index 0000000000..17b8b4399f
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Judgements;
+
+namespace osu.Game.Rulesets.Osu.Judgements
+{
+ public class OsuJudgementResult : JudgementResult
+ {
+ public ComboResult ComboType;
+
+ public OsuJudgementResult(Judgement judgement)
+ : base(judgement)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs
index b2ddd65e38..37d5f57fcb 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs
@@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Name => "Autopilot";
public override string ShortenedName => "AP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
+ public override ModType Type => ModType.Automation;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) };
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
index 4220b72b16..4eff2a55c8 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override void ApplyToDrawableHitObjects(IEnumerable drawables)
{
- void adjustFadeIn(OsuHitObject h) => h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier;
+ void adjustFadeIn(OsuHitObject h) => h.TimeFadeIn = h.TimePreempt * fade_in_duration_multiplier;
foreach (var d in drawables.OfType())
{
@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods
var h = d.HitObject;
- var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadein;
+ var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadeIn;
var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier;
// new duration from completed fade in to end (before fading out)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
index 9f9c2a09b6..4047e057cb 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
@@ -2,14 +2,89 @@
// 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.Input.States;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.UI;
+using static osu.Game.Input.Handlers.ReplayInputHandler;
namespace osu.Game.Rulesets.Osu.Mods
{
- public class OsuModRelax : ModRelax
+ public class OsuModRelax : ModRelax, IApplicableFailOverride, IUpdatableByPlayfield, IApplicableToRulesetContainer
{
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
+
+ public bool AllowFail => false;
+
+ public void Update(Playfield playfield)
+ {
+ bool requiresHold = false;
+ bool requiresHit = false;
+
+ const float relax_leniency = 3;
+
+ foreach (var drawable in playfield.HitObjects.AliveObjects)
+ {
+ if (!(drawable is DrawableOsuHitObject osuHit))
+ continue;
+
+ double time = osuHit.Clock.CurrentTime;
+ double relativetime = time - osuHit.HitObject.StartTime;
+
+ if (time < osuHit.HitObject.StartTime - relax_leniency) continue;
+
+ if (osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime || osuHit.IsHit)
+ continue;
+
+ requiresHit |= osuHit is DrawableHitCircle && osuHit.IsHovered && osuHit.HitObject.HitWindows.CanBeHit(relativetime);
+ requiresHold |= osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered) || osuHit is DrawableSpinner;
+ }
+
+ if (requiresHit)
+ {
+ addAction(false);
+ addAction(true);
+ }
+
+ addAction(requiresHold);
+ }
+
+ private bool wasHit;
+ private bool wasLeft;
+
+ private OsuInputManager osuInputManager;
+
+ private void addAction(bool hitting)
+ {
+ if (wasHit == hitting)
+ return;
+
+ wasHit = hitting;
+
+ var state = new ReplayState
+ {
+ PressedActions = new List()
+ };
+
+ if (hitting)
+ {
+ state.PressedActions.Add(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
+ wasLeft = !wasLeft;
+ }
+
+ osuInputManager.HandleCustomInput(new InputState(), state);
+ }
+
+ public void ApplyToRulesetContainer(RulesetContainer rulesetContainer)
+ {
+ // grab the input manager for future use.
+ osuInputManager = (OsuInputManager)rulesetContainer.KeyBindingInputManager;
+ osuInputManager.AllowUserPresses = false;
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs
index d14af57bab..6aa864d9b2 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs
@@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Name => "Spun Out";
public override string ShortenedName => "SO";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
+ public override ModType Type => ModType.DifficultyReduction;
public override string Description => @"Spinners will be automatically completed.";
public override double ScoreMultiplier => 0.9;
public override bool Ranked => true;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs
index ce53857a09..139ce4cc4b 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Target";
public override string ShortenedName => "TP";
+ public override ModType Type => ModType.Conversion;
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
public override string Description => @"Practice keeping up with the beat of the song.";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
index 2c89ddc9cf..908b9cb3c6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
@@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
@@ -20,27 +21,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
Origin = Anchor.Centre;
- Masking = true;
- AutoSizeAxes = Axes.Both;
- CornerRadius = width / 2;
- EdgeEffect = new EdgeEffectParameters
+ Child = new SkinnableDrawable("Play/osu/followpoint", _ => new Container
{
- Type = EdgeEffectType.Glow,
- Colour = Color4.White.Opacity(0.2f),
- Radius = 4,
- };
-
- Children = new Drawable[]
- {
- new Box
+ Masking = true,
+ AutoSizeAxes = Axes.Both,
+ CornerRadius = width / 2,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = Color4.White.Opacity(0.2f),
+ Radius = 4,
+ },
+ Child = new Box
{
Size = new Vector2(width),
Blending = BlendingMode.Additive,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Alpha = 0.5f,
- },
- };
+ }
+ }, restrictSize: false);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
index 4653f45149..61a6e6404a 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
@@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
Vector2 distanceVector = endPosition - startPosition;
int distance = (int)distanceVector.Length;
- float rotation = (float)Math.Atan2(distanceVector.Y, distanceVector.X);
+ float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI));
double duration = endTime - startTime;
for (int d = (int)(PointDistance * 1.5); d < distance - PointDistance; d += PointDistance)
@@ -96,12 +96,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
using (fp.BeginAbsoluteSequence(fadeInTime))
{
- fp.FadeIn(currHitObject.TimeFadein);
- fp.ScaleTo(1, currHitObject.TimeFadein, Easing.Out);
+ fp.FadeIn(currHitObject.TimeFadeIn);
+ fp.ScaleTo(1, currHitObject.TimeFadeIn, Easing.Out);
- fp.MoveTo(pointEndPosition, currHitObject.TimeFadein, Easing.Out);
+ fp.MoveTo(pointEndPosition, currHitObject.TimeFadeIn, Easing.Out);
- fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadein);
+ fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadeIn);
}
fp.Expire(true);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 212bc01ef9..4bdddcef11 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics;
@@ -40,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (AllJudged)
return false;
- UpdateJudgement(true);
+ UpdateResult(true);
return true;
},
},
@@ -77,12 +76,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- AddJudgement(new OsuJudgement { Result = HitResult.Miss });
+ ApplyResult(r => r.Type = HitResult.Miss);
+
return;
}
@@ -93,17 +93,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return;
}
- AddJudgement(new OsuJudgement
- {
- Result = result,
- });
+ ApplyResult(r => r.Type = result);
}
protected override void UpdatePreemptState()
{
base.UpdatePreemptState();
- ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadein * 2, HitObject.TimePreempt));
+ ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt);
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 89b81a02ac..e69f340184 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -1,11 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.ComponentModel;
+using System;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
-using System.Linq;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using OpenTK.Graphics;
using osu.Game.Graphics.Containers;
@@ -42,7 +44,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
UpdatePreemptState();
- using (BeginDelayedSequence(HitObject.TimePreempt + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true))
+ var judgementOffset = Math.Min(HitObject.HitWindows.HalfWindowFor(HitResult.Miss), Result?.TimeOffset ?? 0);
+
+ using (BeginDelayedSequence(HitObject.TimePreempt + judgementOffset, true))
UpdateCurrentState(state);
}
}
@@ -55,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
- protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein);
+ protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn);
protected virtual void UpdateCurrentState(ArmedState state)
{
@@ -63,22 +67,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply
// transforms in the same way and don't rely on them not being cleared
- public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { }
- public override void ApplyTransformsAt(double time, bool propagateChildren = false) { }
+ public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
+ {
+ }
+
+ public override void ApplyTransformsAt(double time, bool propagateChildren = false)
+ {
+ }
private OsuInputManager osuActionInputManager;
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
- }
- public enum ComboResult
- {
- [Description(@"")]
- None,
- [Description(@"Good")]
- Good,
- [Description(@"Amazing")]
- Perfect
+ protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
index e8743281da..04ec3f13c7 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
@@ -11,14 +11,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuJudgement : DrawableJudgement
{
- public DrawableOsuJudgement(Judgement judgement, DrawableHitObject judgedObject)
- : base(judgement, judgedObject)
+ public DrawableOsuJudgement(JudgementResult result, DrawableHitObject judgedObject)
+ : base(result, judgedObject)
{
}
protected override void LoadComplete()
{
- if (Judgement.Result != HitResult.Miss)
+ if (Result.Type != HitResult.Miss)
JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
base.LoadComplete();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
index 26f3ee6bb4..dfe7937e81 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
@@ -8,8 +8,8 @@ using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -33,18 +33,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
InternalChildren = new Drawable[]
{
- new SpriteIcon
+ new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon
{
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.fa_chevron_right
- }
+ }, restrictSize: false)
};
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (repeatPoint.StartTime <= Time.Current)
- AddJudgement(new OsuJudgement { Result = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss });
+ ApplyResult(r => r.Type = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss);
}
protected override void UpdatePreemptState()
@@ -74,6 +74,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
+ private bool hasRotation;
+
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
{
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
@@ -87,15 +89,33 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
int searchStart = isRepeatAtEnd ? curve.Count - 1 : 0;
int direction = isRepeatAtEnd ? -1 : 1;
+ Vector2 aimRotationVector = Vector2.Zero;
+
// find the next vector2 in the curve which is not equal to our current position to infer a rotation.
for (int i = searchStart; i >= 0 && i < curve.Count; i += direction)
{
if (Precision.AlmostEquals(curve[i], Position))
continue;
- Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X));
+ aimRotationVector = curve[i];
break;
}
+
+
+ float aimRotation = MathHelper.RadiansToDegrees((float)Math.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
+ while (Math.Abs(aimRotation - Rotation) > 180)
+ aimRotation += aimRotation < Rotation ? 360 : -360;
+
+ if (!hasRotation)
+ {
+ Rotation = aimRotation;
+ hasRotation = true;
+ }
+ else
+ {
+ // If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly).
+ Rotation = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 100), Rotation, aimRotation, 0, 50, Easing.OutQuint);
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 464df56a3e..66f491532d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -9,7 +9,6 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics;
@@ -135,23 +134,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
- if (!userTriggered && Time.Current >= slider.EndTime)
+ if (userTriggered || Time.Current < slider.EndTime)
+ return;
+
+ ApplyResult(r =>
{
var judgementsCount = NestedHitObjects.Count();
var judgementsHit = NestedHitObjects.Count(h => h.IsHit);
var hitFraction = (double)judgementsHit / judgementsCount;
- if (hitFraction == 1 && HeadCircle.Judgements.Any(j => j.Result == HitResult.Great))
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
- else if (hitFraction >= 0.5 && HeadCircle.Judgements.Any(j => j.Result >= HitResult.Good))
- AddJudgement(new OsuJudgement { Result = HitResult.Good });
+
+ if (hitFraction == 1 && HeadCircle.Result.Type == HitResult.Great)
+ r.Type = HitResult.Great;
+ else if (hitFraction >= 0.5 && HeadCircle.Result.Type >= HitResult.Good)
+ r.Type = HitResult.Good;
else if (hitFraction > 0)
- AddJudgement(new OsuJudgement { Result = HitResult.Meh });
+ r.Type = HitResult.Meh;
else
- AddJudgement(new OsuJudgement { Result = HitResult.Miss });
- }
+ r.Type = HitResult.Miss;
+ });
}
protected override void UpdateCurrentState(ArmedState state)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
index fee663963e..45c925b87a 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
@@ -12,11 +11,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
///
/// The judgement text is provided by the .
///
- public override bool DisplayJudgement => false;
+ public override bool DisplayResult => false;
public bool Tracking { get; set; }
- public DrawableSliderTail(Slider slider, HitCircle hitCircle)
+ public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
: base(hitCircle)
{
Origin = Anchor.Centre;
@@ -29,10 +28,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Position = HitObject.Position - slider.Position;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered && timeOffset >= 0)
- AddJudgement(new OsuSliderTailJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
+ ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index db75321eb0..964c75131a 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -6,8 +6,9 @@ using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Skinning;
+using osu.Framework.Graphics.Containers;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -17,35 +18,39 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public bool Tracking { get; set; }
- public override bool DisplayJudgement => false;
+ public override bool DisplayResult => false;
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{
Size = new Vector2(16) * sliderTick.Scale;
-
- Masking = true;
- CornerRadius = Size.X / 2;
-
Origin = Anchor.Centre;
- BorderThickness = 2;
- BorderColour = Color4.White;
-
InternalChildren = new Drawable[]
{
- new Box
+ new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new Container
{
+ Masking = true,
RelativeSizeAxes = Axes.Both,
- Colour = AccentColour,
- Alpha = 0.3f,
- }
+ Origin = Anchor.Centre,
+ CornerRadius = Size.X / 2,
+
+ BorderThickness = 2,
+ BorderColour = Color4.White,
+
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = AccentColour,
+ Alpha = 0.3f,
+ }
+ }, restrictSize: false)
};
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
- AddJudgement(new OsuJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
+ ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
}
protected override void UpdatePreemptState()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index 10539f85a2..51b1990a21 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -11,7 +11,6 @@ using OpenTK.Graphics;
using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Screens.Ranking;
using osu.Game.Rulesets.Scoring;
@@ -117,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1);
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (Time.Current < HitObject.StartTime) return;
@@ -136,17 +135,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
glow.FadeColour(completeColour, duration);
}
- if (!userTriggered && Time.Current >= Spinner.EndTime)
+ if (userTriggered || Time.Current < Spinner.EndTime)
+ return;
+
+ ApplyResult(r =>
{
if (Progress >= 1)
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
+ r.Type = HitResult.Great;
else if (Progress > .9)
- AddJudgement(new OsuJudgement { Result = HitResult.Good });
+ r.Type = HitResult.Good;
else if (Progress > .75)
- AddJudgement(new OsuJudgement { Result = HitResult.Meh });
+ r.Type = HitResult.Meh;
else if (Time.Current >= Spinner.EndTime)
- AddJudgement(new OsuJudgement { Result = HitResult.Miss });
- }
+ r.Type = HitResult.Miss;
+ });
}
[BackgroundDependencyLoader]
@@ -167,7 +169,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
if (!spmCounter.IsPresent && Disc.Tracking)
- spmCounter.FadeIn(HitObject.TimeFadein);
+ spmCounter.FadeIn(HitObject.TimeFadeIn);
base.Update();
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index 278bb0dcb1..b79750a1b3 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -5,10 +5,11 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Rulesets.Objects.Types;
-using OpenTK;
using OpenTK.Graphics;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -17,6 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private const float width = 128;
private Color4 accentColour = Color4.Black;
+
///
/// The colour that is used for the slider ball.
///
@@ -26,14 +28,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
set
{
accentColour = value;
- if (ball != null)
- ball.Colour = value;
+ if (drawableBall != null)
+ drawableBall.Colour = value;
}
}
private readonly Slider slider;
- public readonly Box FollowCircle;
- private readonly Box ball;
+ public readonly Drawable FollowCircle;
+ private Drawable drawableBall;
private readonly DrawableSlider drawableSlider;
public SliderBall(Slider slider, DrawableSlider drawableSlider = null)
@@ -44,19 +46,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
AutoSizeAxes = Axes.Both;
Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
- BorderThickness = 10;
- BorderColour = Color4.Orange;
- Children = new Drawable[]
+ Children = new[]
{
- FollowCircle = new Box
+ FollowCircle = new Container
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- Colour = Color4.Orange,
Width = width,
Height = width,
Alpha = 0,
+ Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new CircularContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ BorderThickness = 5,
+ BorderColour = Color4.Orange,
+ Blending = BlendingMode.Additive,
+ Child = new Box
+ {
+ Colour = Color4.Orange,
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.2f,
+ }
+ }),
},
new CircularContainer
{
@@ -64,18 +77,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
AutoSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- BorderThickness = 10,
- BorderColour = Color4.White,
Alpha = 1,
- Children = new[]
+ Child = new Container
{
- ball = new Box
+ Width = width,
+ Height = width,
+ // TODO: support skin filename animation (sliderb0, sliderb1...)
+ Child = new SkinnableDrawable("Play/osu/sliderb", _ => new CircularContainer
{
- Colour = AccentColour,
- Alpha = 0.4f,
- Width = width,
- Height = width,
- },
+ Masking = true,
+ RelativeSizeAxes = Axes.Both,
+ BorderThickness = 10,
+ BorderColour = Color4.White,
+ Alpha = 1,
+ Child = drawableBall = new Box
+ {
+ Colour = AccentColour,
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.4f,
+ }
+ }),
}
}
};
@@ -101,9 +122,6 @@ 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);
-
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
{
// Consider the case of rewinding - children's transforms are handled internally, so propagating down
@@ -112,6 +130,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
private bool tracking;
+
public bool Tracking
{
get { return tracking; }
@@ -121,8 +140,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
tracking = value;
- FollowCircle.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
- FollowCircle.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint);
+ FollowCircle.ScaleTo(tracking ? 2f : 1, 300, Easing.OutQuint);
+ FollowCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
}
}
@@ -137,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
// Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
Tracking = canCurrentlyTrack
&& lastState != null
- && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
+ && ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
&& (drawableSlider?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
index 94a61e7904..283d6b91f6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
@@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
var spanProgress = slider.ProgressAt(completionProgress);
double start = 0;
- double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1;
+ double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
if (span >= slider.SpanCount() - 1)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
index bb5fa1b575..5aba60ba03 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
@@ -4,7 +4,7 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
diff --git a/osu.Game.Rulesets.Osu/Objects/HitCircle.cs b/osu.Game.Rulesets.Osu/Objects/HitCircle.cs
index 9e309a376d..d1656a9672 100644
--- a/osu.Game.Rulesets.Osu/Objects/HitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/HitCircle.cs
@@ -1,9 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
+
namespace osu.Game.Rulesets.Osu.Objects
{
public class HitCircle : OsuHitObject
{
+ public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index befbc01f3c..fdf5aaffa8 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public event Action PositionChanged;
public double TimePreempt = 600;
- public double TimeFadein = 400;
+ public double TimeFadeIn = 400;
private Vector2 position;
@@ -54,6 +54,8 @@ namespace osu.Game.Rulesets.Osu.Objects
public virtual bool NewCombo { get; set; }
+ public int ComboOffset { get; set; }
+
public virtual int IndexInCurrentCombo { get; set; }
public virtual int ComboIndex { get; set; }
@@ -65,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
- TimeFadein = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
+ TimeFadeIn = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}
diff --git a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
index 0b729f0956..c8621cdbcf 100644
--- a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
@@ -4,6 +4,8 @@
using System;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -16,10 +18,15 @@ namespace osu.Game.Rulesets.Osu.Objects
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
+ // Out preempt should be one span early to give the user ample warning.
+ TimePreempt += SpanDuration;
+
// We want to show the first RepeatPoint as the TimePreempt dictates but on short (and possibly fast) sliders
// we may need to cut down this time on following RepeatPoints to only show up to two RepeatPoints at any given time.
if (RepeatIndex > 0)
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
}
+
+ public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 698f9de787..7a0dcc77a6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -10,6 +10,8 @@ using System.Linq;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -94,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public double TickDistance;
public HitCircle HeadCircle;
- public HitCircle TailCircle;
+ public SliderTailCircle TailCircle;
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
@@ -133,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Objects
ComboIndex = ComboIndex,
};
- TailCircle = new SliderCircle(this)
+ TailCircle = new SliderTailCircle(this)
{
StartTime = EndTime,
Position = EndPosition,
@@ -211,5 +213,7 @@ namespace osu.Game.Rulesets.Osu.Objects
});
}
}
+
+ public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
new file mode 100644
index 0000000000..23616ea005
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
@@ -0,0 +1,18 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
+
+namespace osu.Game.Rulesets.Osu.Objects
+{
+ public class SliderTailCircle : SliderCircle
+ {
+ public SliderTailCircle(Slider slider)
+ : base(slider)
+ {
+ }
+
+ public override Judgement CreateJudgement() => new OsuSliderTailJudgement();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
index 4db6eb9883..906f0a0182 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
@@ -3,6 +3,8 @@
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -22,9 +24,11 @@ namespace osu.Game.Rulesets.Osu.Objects
// This is so on repeats ticks don't appear too late to be visually processed by the player.
offset = 200;
else
- offset = TimeFadein * 0.66f;
+ offset = TimeFadeIn * 0.66f;
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
}
+
+ public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
index 503ad85674..1c60fd4831 100644
--- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
@@ -5,6 +5,8 @@ using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -18,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.Objects
///
public int SpinsRequired { get; protected set; } = 1;
- public override bool NewCombo => true;
-
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
@@ -29,5 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects
// spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being.
SpinsRequired = (int)Math.Max(1, SpinsRequired * 0.6);
}
+
+ public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs
index d9ae836e0a..e7bbe755a0 100644
--- a/osu.Game.Rulesets.Osu/OsuInputManager.cs
+++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs
@@ -4,6 +4,8 @@
using System.Collections.Generic;
using System.ComponentModel;
using osu.Framework.Input.Bindings;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu
@@ -12,8 +14,35 @@ namespace osu.Game.Rulesets.Osu
{
public IEnumerable PressedActions => KeyBindingContainer.PressedActions;
- public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique)
+ public bool AllowUserPresses
{
+ set => ((OsuKeyBindingContainer)KeyBindingContainer).AllowUserPresses = value;
+ }
+
+ protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ => new OsuKeyBindingContainer(ruleset, variant, unique);
+
+ public OsuInputManager(RulesetInfo ruleset)
+ : base(ruleset, 0, SimultaneousBindingMode.Unique)
+ {
+ }
+
+ private class OsuKeyBindingContainer : RulesetKeyBindingContainer
+ {
+ public bool AllowUserPresses = true;
+
+ public OsuKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ : base(ruleset, variant, unique)
+ {
+ }
+
+ protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => AllowUserPresses && base.OnKeyDown(state, args);
+ protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => AllowUserPresses && base.OnKeyUp(state, args);
+ protected override bool OnJoystickPress(InputState state, JoystickEventArgs args) => AllowUserPresses && base.OnJoystickPress(state, args);
+ protected override bool OnJoystickRelease(InputState state, JoystickEventArgs args) => AllowUserPresses && base.OnJoystickRelease(state, args);
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => AllowUserPresses && base.OnMouseDown(state, args);
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => AllowUserPresses && base.OnMouseUp(state, args);
+ protected override bool OnScroll(InputState state) => AllowUserPresses && base.OnScroll(state);
}
}
@@ -21,6 +50,7 @@ namespace osu.Game.Rulesets.Osu
{
[Description("Left Button")]
LeftButton,
+
[Description("Right Button")]
RightButton
}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index ce80537b93..fa6e9a018a 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -31,8 +31,8 @@ namespace osu.Game.Rulesets.Osu
public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[]
{
- new KeyBinding(InputKey.Z, OsuAction.LeftButton),
- new KeyBinding(InputKey.X, OsuAction.RightButton),
+ new KeyBinding(InputKey.A, OsuAction.LeftButton),
+ new KeyBinding(InputKey.S, OsuAction.RightButton),
new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton),
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
};
@@ -94,6 +94,7 @@ namespace osu.Game.Rulesets.Osu
new OsuModEasy(),
new OsuModNoFail(),
new MultiMod(new OsuModHalfTime(), new OsuModDaycore()),
+ new OsuModSpunOut(),
};
case ModType.DifficultyIncrease:
return new Mod[]
@@ -104,14 +105,17 @@ namespace osu.Game.Rulesets.Osu
new OsuModHidden(),
new OsuModFlashlight(),
};
- case ModType.Special:
+ case ModType.Conversion:
return new Mod[]
{
+ new OsuModTarget(),
+ };
+ case ModType.Automation:
+ return new Mod[]
+ {
+ new MultiMod(new OsuModAutoplay(), new ModCinema()),
new OsuModRelax(),
new OsuModAutopilot(),
- new OsuModSpunOut(),
- new MultiMod(new OsuModAutoplay(), new ModCinema()),
- new OsuModTarget(),
};
default:
return new Mod[] { };
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
index ad4ea343d2..7322f65066 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
@@ -285,44 +285,45 @@ namespace osu.Game.Rulesets.Osu.Replays
AddFrameToReplay(startFrame);
- // We add intermediate frames for spinning / following a slider here.
- if (h is Spinner)
+ switch (h)
{
- Spinner s = h as Spinner;
-
- Vector2 difference = startPosition - SPINNER_CENTRE;
-
- float radius = difference.Length;
- float angle = radius == 0 ? 0 : (float)Math.Atan2(difference.Y, difference.X);
-
- double t;
-
- for (double j = h.StartTime + FrameDelay; j < s.EndTime; j += FrameDelay)
+ // We add intermediate frames for spinning / following a slider here.
+ case Spinner spinner:
{
- t = ApplyModsToTime(j - h.StartTime) * spinnerDirection;
+ Vector2 difference = startPosition - SPINNER_CENTRE;
- Vector2 pos = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
- AddFrameToReplay(new OsuReplayFrame((int)j, new Vector2(pos.X, pos.Y), action));
+ float radius = difference.Length;
+ float angle = radius == 0 ? 0 : (float)Math.Atan2(difference.Y, difference.X);
+
+ double t;
+
+ for (double j = h.StartTime + FrameDelay; j < spinner.EndTime; j += FrameDelay)
+ {
+ t = ApplyModsToTime(j - h.StartTime) * spinnerDirection;
+
+ Vector2 pos = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
+ AddFrameToReplay(new OsuReplayFrame((int)j, new Vector2(pos.X, pos.Y), action));
+ }
+
+ t = ApplyModsToTime(spinner.EndTime - h.StartTime) * spinnerDirection;
+ Vector2 endPosition = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
+
+ AddFrameToReplay(new OsuReplayFrame(spinner.EndTime, new Vector2(endPosition.X, endPosition.Y), action));
+
+ endFrame.Position = endPosition;
+ break;
}
-
- t = ApplyModsToTime(s.EndTime - h.StartTime) * spinnerDirection;
- Vector2 endPosition = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
-
- AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(endPosition.X, endPosition.Y), action));
-
- endFrame.Position = endPosition;
- }
- else if (h is Slider)
- {
- Slider s = h as Slider;
-
- for (double j = FrameDelay; j < s.Duration; j += FrameDelay)
+ case Slider slider:
{
- Vector2 pos = s.StackedPositionAt(j / s.Duration);
- AddFrameToReplay(new OsuReplayFrame(h.StartTime + j, new Vector2(pos.X, pos.Y), action));
- }
+ for (double j = FrameDelay; j < slider.Duration; j += FrameDelay)
+ {
+ Vector2 pos = slider.StackedPositionAt(j / slider.Duration);
+ AddFrameToReplay(new OsuReplayFrame(h.StartTime + j, new Vector2(pos.X, pos.Y), action));
+ }
- AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(s.StackedEndPosition.X, s.StackedEndPosition.Y), action));
+ AddFrameToReplay(new OsuReplayFrame(slider.EndTime, new Vector2(slider.StackedEndPosition.X, slider.StackedEndPosition.Y), action));
+ break;
+ }
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
index 2eed41d13f..5c07860b19 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
@@ -3,7 +3,7 @@
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Input;
+using osu.Framework.Input.StateChanges;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Replays;
using OpenTK;
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index 01b92255ae..a9d39e88b4 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -2,13 +2,11 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -26,28 +24,11 @@ namespace osu.Game.Rulesets.Osu.Scoring
private readonly Dictionary scoreResultCounts = new Dictionary();
private readonly Dictionary comboResultCounts = new Dictionary();
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void ApplyBeatmap(Beatmap beatmap)
{
+ base.ApplyBeatmap(beatmap);
+
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
-
- foreach (var obj in beatmap.HitObjects)
- {
- if (obj is Slider slider)
- {
- // Head
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
-
- // Ticks
- foreach (var unused in slider.NestedHitObjects.OfType())
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
-
- //Repeats
- foreach (var unused in slider.NestedHitObjects.OfType())
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
- }
-
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
- }
}
protected override void Reset(bool storeResults)
@@ -70,19 +51,19 @@ namespace osu.Game.Rulesets.Osu.Scoring
private const double harshness = 0.01;
- protected override void OnNewJudgement(Judgement judgement)
+ protected override void ApplyResult(JudgementResult result)
{
- base.OnNewJudgement(judgement);
+ base.ApplyResult(result);
- var osuJudgement = (OsuJudgement)judgement;
+ var osuResult = (OsuJudgementResult)result;
- if (judgement.Result != HitResult.None)
+ if (result.Type != HitResult.None)
{
- scoreResultCounts[judgement.Result] = scoreResultCounts.GetOrDefault(judgement.Result) + 1;
- comboResultCounts[osuJudgement.Combo] = comboResultCounts.GetOrDefault(osuJudgement.Combo) + 1;
+ scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
+ comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1;
}
- switch (judgement.Result)
+ switch (result.Type)
{
case HitResult.Great:
Health.Value += (10.2 - hpDrainRate) * harshness;
@@ -105,5 +86,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
break;
}
}
+
+ protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index 7bd80b5718..abcd1ddbda 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Timing;
using OpenTK;
using OpenTK.Graphics;
@@ -196,7 +197,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
if (Shared.VertexBuffer == null)
Shared.VertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw);
- Shader.GetUniform("g_FadeClock").Value = Time;
+ Shader.GetUniform("g_FadeClock").UpdateValue(ref Time);
int updateStart = -1, updateEnd = 0;
for (int i = 0; i < Parts.Length; ++i)
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
index 35146dfe29..4d6722b61b 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
@@ -39,6 +39,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private int downCount;
+ private const float pressed_scale = 1.2f;
+ private const float released_scale = 1f;
+
+ private float targetScale => downCount > 0 ? pressed_scale : released_scale;
+
public bool OnPressed(OsuAction action)
{
switch (action)
@@ -46,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton:
case OsuAction.RightButton:
downCount++;
- ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
+ ActiveCursor.ScaleTo(released_scale).ScaleTo(targetScale, 100, Easing.OutQuad);
break;
}
@@ -60,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton:
case OsuAction.RightButton:
if (--downCount == 0)
- ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
+ ActiveCursor.ScaleTo(targetScale, 200, Easing.OutQuad);
break;
}
@@ -72,13 +77,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override void PopIn()
{
fadeContainer.FadeTo(1, 300, Easing.OutQuint);
- ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
+ ActiveCursor.ScaleTo(targetScale, 400, Easing.OutQuint);
}
protected override void PopOut()
{
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
- ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
+ ActiveCursor.ScaleTo(targetScale * 0.8f, 450, Easing.OutQuint);
}
public class OsuCursor : Container
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index 04724931ae..703d8764fc 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.UI
private readonly JudgementContainer judgementLayer;
private readonly ConnectionRenderer connectionLayer;
- protected virtual bool DisplayJudgements => true;
-
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
public OsuPlayfield()
@@ -52,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.UI
public override void Add(DrawableHitObject h)
{
- h.OnJudgement += onJudgement;
+ h.OnNewResult += onNewResult;
var c = h as IDrawableHitObjectWithProxiedApproach;
if (c != null)
@@ -66,12 +64,12 @@ namespace osu.Game.Rulesets.Osu.UI
connectionLayer.HitObjects = HitObjects.Objects.Select(d => d.HitObject).OfType();
}
- private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (!judgedObject.DisplayJudgement || !DisplayJudgements)
+ if (!judgedObject.DisplayResult || !DisplayJudgements)
return;
- DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject)
+ DrawableOsuJudgement explosion = new DrawableOsuJudgement(result, judgedObject)
{
Origin = Anchor.Centre,
Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition
diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
index 33d4e16662..4bc6992445 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Linq;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using OpenTK;
@@ -33,19 +34,30 @@ namespace osu.Game.Rulesets.Osu.UI
protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h)
{
- if (h is HitCircle circle)
- return new DrawableHitCircle(circle);
+ switch (h)
+ {
+ case HitCircle circle:
+ return new DrawableHitCircle(circle);
+ case Slider slider:
+ return new DrawableSlider(slider);
+ case Spinner spinner:
+ return new DrawableSpinner(spinner);
+ }
- if (h is Slider slider)
- return new DrawableSlider(slider);
-
- if (h is Spinner spinner)
- return new DrawableSpinner(spinner);
return null;
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
+ public override double GameplayStartTime
+ {
+ get
+ {
+ var first = (OsuHitObject)Objects.First();
+ return first.StartTime - first.TimePreempt;
+ }
+ }
+
protected override Vector2 GetAspectAdjustedSize()
{
var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y);
diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json
index df49e177dc..de7bf77070 100644
--- a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json
+++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json
@@ -2,35 +2,7 @@
"version": "0.2.0",
"configurations": [
{
- "name": "VisualTests (Debug, net471)",
- "windows": {
- "type": "clr"
- },
- "type": "mono",
- "request": "launch",
- "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe",
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug, msbuild)",
- "runtimeExecutable": null,
- "env": {},
- "console": "internalConsole"
- },
- {
- "name": "VisualTests (Release, net471)",
- "windows": {
- "type": "clr"
- },
- "type": "mono",
- "request": "launch",
- "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Taiko.Tests.exe",
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release, msbuild)",
- "runtimeExecutable": null,
- "env": {},
- "console": "internalConsole"
- },
- {
- "name": "VisualTests (Debug, netcoreapp2.1)",
+ "name": "VisualTests (Debug)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -38,12 +10,12 @@
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll"
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug, dotnet)",
+ "preLaunchTask": "Build (Debug)",
"env": {},
"console": "internalConsole"
},
{
- "name": "VisualTests (Release, netcoreapp2.1)",
+ "name": "VisualTests (Release)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -51,7 +23,7 @@
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll"
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release, dotnet)",
+ "preLaunchTask": "Build (Release)",
"env": {},
"console": "internalConsole"
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json
index 7c8beed00f..9b91f2c9b9 100644
--- a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json
+++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json
@@ -4,43 +4,13 @@
"version": "2.0.0",
"tasks": [
{
- "label": "Build (Debug, msbuild)",
- "type": "shell",
- "command": "msbuild",
- "args": [
- "osu.Game.Rulesets.Taiko.Tests.csproj",
- "/p:TargetFramework=net471",
- "/p:GenerateFullPaths=true",
- "/m",
- "/verbosity:m"
- ],
- "group": "build",
- "problemMatcher": "$msCompile"
- },
- {
- "label": "Build (Release, msbuild)",
- "type": "shell",
- "command": "msbuild",
- "args": [
- "osu.Game.Rulesets.Taiko.Tests.csproj",
- "/p:Configuration=Release",
- "/p:TargetFramework=net471",
- "/p:GenerateFullPaths=true",
- "/m",
- "/verbosity:m"
- ],
- "group": "build",
- "problemMatcher": "$msCompile"
- },
- {
- "label": "Build (Debug, dotnet)",
+ "label": "Build (Debug)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Taiko.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -49,14 +19,13 @@
"problemMatcher": "$msCompile"
},
{
- "label": "Build (Release, dotnet)",
+ "label": "Build (Release)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Taiko.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -66,16 +35,7 @@
"problemMatcher": "$msCompile"
},
{
- "label": "Restore (net471)",
- "type": "shell",
- "command": "nuget",
- "args": [
- "restore"
- ],
- "problemMatcher": []
- },
- {
- "label": "Restore (netcoreapp2.1)",
+ "label": "Restore",
"type": "shell",
"command": "dotnet",
"args": [
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs
index 1bf24a46bc..fc103e4c72 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs
@@ -8,9 +8,9 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
-using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
@@ -39,8 +39,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
[BackgroundDependencyLoader]
private void load()
{
- AddStep("Hit!", () => addHitJudgement(false));
+ AddStep("Hit", () => addHitJudgement(false));
+ AddStep("Strong hit", () => addStrongHitJudgement(false));
AddStep("Kiai hit", () => addHitJudgement(true));
+ AddStep("Strong kiai hit", () => addStrongHitJudgement(true));
AddStep("Miss :(", addMissJudgement);
AddStep("DrumRoll", () => addDrumRoll(false));
AddStep("Strong DrumRoll", () => addDrumRoll(true));
@@ -78,15 +80,12 @@ namespace osu.Game.Rulesets.Taiko.Tests
ControlPointInfo = controlPointInfo
});
- var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
-
Add(playfieldContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = 768,
- Clock = new FramedClock(rateAdjustClock),
Children = new[] { rulesetContainer = new TaikoRulesetContainer(new TaikoRuleset(), beatmap) }
});
}
@@ -133,28 +132,35 @@ namespace osu.Game.Rulesets.Taiko.Tests
HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great;
var cpi = new ControlPointInfo();
- cpi.EffectPoints.Add(new EffectControlPoint
- {
- KiaiMode = kiai
- });
+ 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 == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
- ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult });
+ ((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement()) { Type = hitResult });
+ }
- if (RNG.Next(10) == 0)
- {
- ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult });
- ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoStrongHitJudgement());
- }
+ private void addStrongHitJudgement(bool kiai)
+ {
+ HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great;
+
+ 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 == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
+
+ ((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement()) { Type = hitResult });
+ ((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new TaikoStrongJudgement()) { Type = HitResult.Great });
}
private void addMissJudgement()
{
- ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss });
+ ((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(new DrawableTestHit(new Hit()), new JudgementResult(new TaikoJudgement()) { Type = HitResult.Miss });
}
private void addBarLine(bool major, double delay = scroll_time)
@@ -204,10 +210,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
- if (strong)
- rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h));
- else
- rulesetContainer.Playfield.Add(new DrawableCentreHit(h));
+ rulesetContainer.Playfield.Add(new DrawableCentreHit(h));
}
private void addRimHit(bool strong)
@@ -220,10 +223,17 @@ namespace osu.Game.Rulesets.Taiko.Tests
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
- if (strong)
- rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h));
- else
- rulesetContainer.Playfield.Add(new DrawableRimHit(h));
+ rulesetContainer.Playfield.Add(new DrawableRimHit(h));
+ }
+
+ private class TestStrongNestedHit : DrawableStrongNestedHit
+ {
+ public TestStrongNestedHit(DrawableHitObject mainObject)
+ : base(null, mainObject)
+ {
+ }
+
+ public override bool OnPressed(TaikoAction action) => false;
}
private class DrawableTestHit : DrawableHitObject
diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
index dea34d25e7..08a0579561 100644
--- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
@@ -1,8 +1,14 @@
+
+
+
+
+
+
WinExe
- netcoreapp2.1;net471
+ netcoreapp2.1
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index 41972b5d20..c2cde332e8 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>
{
TaikoHitObject first = x.First();
- if (x.Skip(1).Any())
+ if (x.Skip(1).Any() && !(first is Swell))
first.IsStrong = true;
return first;
}).ToList();
@@ -168,7 +168,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
{
StartTime = obj.StartTime,
Samples = obj.Samples,
- IsStrong = strong,
Duration = endTimeData.Duration,
RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier)
};
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoIntermediateSwellJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoIntermediateSwellJudgement.cs
index 608f1f9be2..81a1bd1344 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoIntermediateSwellJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoIntermediateSwellJudgement.cs
@@ -7,15 +7,10 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoIntermediateSwellJudgement : TaikoJudgement
{
- public override HitResult MaxResult => HitResult.Perfect;
+ public override HitResult MaxResult => HitResult.Great;
public override bool AffectsCombo => false;
- public TaikoIntermediateSwellJudgement()
- {
- Final = false;
- }
-
///
/// Computes the numeric result value for the combo portion of the score.
///
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongJudgement.cs
similarity index 64%
rename from osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs
rename to osu.Game.Rulesets.Taiko/Judgements/TaikoStrongJudgement.cs
index 288ad236aa..ccfdeb5b0e 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongJudgement.cs
@@ -3,13 +3,8 @@
namespace osu.Game.Rulesets.Taiko.Judgements
{
- public class TaikoStrongHitJudgement : TaikoJudgement
+ public class TaikoStrongJudgement : TaikoJudgement
{
public override bool AffectsCombo => false;
-
- public TaikoStrongHitJudgement()
- {
- Final = true;
- }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
index dda96c2caf..a6e9972dd3 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableCentreHit : DrawableHit
{
- protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
+ public 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
deleted file mode 100644
index a2dabf2b18..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Allocation;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables
-{
- public class DrawableCentreHitStrong : DrawableHitStrong
- {
- protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
-
- public DrawableCentreHitStrong(Hit hit)
- : base(hit)
- {
- MainPiece.Add(new CentreHitSymbolPiece());
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- MainPiece.AccentColour = colours.PinkDarker;
- }
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
index 00eac4adca..5142f125ac 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
@@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Taiko.Judgements;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
@@ -40,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
foreach (var tick in drumRoll.NestedHitObjects.OfType())
{
var newTick = new DrawableDrumRollTick(tick);
- newTick.OnJudgement += onTickJudgement;
+ newTick.OnNewResult += onNewTickResult;
AddNested(newTick);
tickContainer.Add(newTick);
@@ -61,9 +60,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
colourEngaged = colours.YellowDarker;
}
- private void onTickJudgement(DrawableHitObject obj, Judgement judgement)
+ private void onNewTickResult(DrawableHitObject obj, JudgementResult result)
{
- if (judgement.Result > HitResult.Miss)
+ if (result.Type > HitResult.Miss)
rollingHits++;
else
rollingHits--;
@@ -74,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
MainPiece.FadeAccent(newColour, 100);
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (userTriggered)
return;
@@ -84,13 +83,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
int countHit = NestedHitObjects.Count(o => o.IsHit);
if (countHit >= HitObject.RequiredGoodHits)
- {
- AddJudgement(new TaikoJudgement { Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good });
- if (HitObject.IsStrong)
- AddJudgement(new TaikoStrongHitJudgement());
- }
+ ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good);
else
- AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
+ ApplyResult(r => r.Type = HitResult.Miss);
}
protected override void UpdateState(ArmedState state)
@@ -103,5 +98,25 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
break;
}
}
+
+ protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
+
+ private class StrongNestedHit : DrawableStrongNestedHit
+ {
+ public StrongNestedHit(StrongHitObject strong, DrawableDrumRoll drumRoll)
+ : base(strong, drumRoll)
+ {
+ }
+
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
+ {
+ if (!MainObject.Judged)
+ return;
+
+ ApplyResult(r => r.Type = MainObject.IsHit ? HitResult.Great : HitResult.Miss);
+ }
+
+ public override bool OnPressed(TaikoAction action) => false;
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
index 7a57cf77b4..a70d7bde0e 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.Scoring;
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@@ -18,24 +17,26 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
FillMode = FillMode.Fit;
}
- public override bool DisplayJudgement => false;
+ public override bool DisplayResult => false;
protected override TaikoPiece CreateMainPiece() => new TickPiece
{
Filled = HitObject.FirstTick
};
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
+ {
+ if (timeOffset > HitObject.HitWindow)
+ ApplyResult(r => r.Type = HitResult.Miss);
+ return;
+ }
+
+ if (Math.Abs(timeOffset) > HitObject.HitWindow)
return;
- if (!(Math.Abs(timeOffset) < HitObject.HitWindow))
- return;
-
- AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
- if (HitObject.IsStrong)
- AddJudgement(new TaikoStrongHitJudgement());
+ ApplyResult(r => r.Type = HitResult.Great);
}
protected override void UpdateState(ArmedState state)
@@ -48,6 +49,26 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
}
- public override bool OnPressed(TaikoAction action) => UpdateJudgement(true);
+ public override bool OnPressed(TaikoAction action) => UpdateResult(true);
+
+ protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
+
+ private class StrongNestedHit : DrawableStrongNestedHit
+ {
+ public StrongNestedHit(StrongHitObject strong, DrawableDrumRollTick tick)
+ : base(strong, tick)
+ {
+ }
+
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
+ {
+ if (!MainObject.Judged)
+ return;
+
+ ApplyResult(r => r.Type = MainObject.IsHit ? HitResult.Great : HitResult.Miss);
+ }
+
+ public override bool OnPressed(TaikoAction action) => false;
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index bb9cd02b14..f59dc8c1ee 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -1,11 +1,11 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@@ -15,17 +15,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
///
/// A list of keys which can result in hits for this HitObject.
///
- protected abstract TaikoAction[] HitActions { get; }
+ public abstract TaikoAction[] HitActions { get; }
///
- /// Whether a second hit is allowed to be processed. This occurs once this hit object has been hit successfully.
+ /// The action that caused this to be hit.
///
- protected bool SecondHitAllowed { get; private set; }
+ public TaikoAction? HitAction { get; private set; }
- ///
- /// Whether the last key pressed is a valid hit key.
- ///
- private bool validKeyPressed;
+ private bool validActionPressed;
protected DrawableHit(Hit hit)
: base(hit)
@@ -33,12 +30,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
FillMode = FillMode.Fit;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
+ ApplyResult(r => r.Type = HitResult.Miss);
return;
}
@@ -46,26 +43,33 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (result == HitResult.None)
return;
- if (!validKeyPressed || result == HitResult.Miss)
- AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
+ if (!validActionPressed)
+ ApplyResult(r => r.Type = HitResult.Miss);
else
- {
- AddJudgement(new TaikoJudgement
- {
- Result = result,
- Final = !HitObject.IsStrong
- });
-
- SecondHitAllowed = true;
- }
+ ApplyResult(r => r.Type = result);
}
public override bool OnPressed(TaikoAction action)
{
- validKeyPressed = HitActions.Contains(action);
+ if (Judged)
+ return false;
+
+ validActionPressed = HitActions.Contains(action);
// Only count this as handled if the new judgement is a hit
- return UpdateJudgement(true);
+ var result = UpdateResult(true);
+
+ if (IsHit)
+ HitAction = action;
+
+ return result;
+ }
+
+ public override bool OnReleased(TaikoAction action)
+ {
+ if (action == HitAction)
+ HitAction = null;
+ return base.OnReleased(action);
}
protected override void Update()
@@ -86,8 +90,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (State.Value)
{
case ArmedState.Idle:
- SecondHitAllowed = false;
- validKeyPressed = false;
+ validActionPressed = false;
UnproxyContent();
this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire();
@@ -123,5 +126,65 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
}
}
+
+ protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
+
+ private class StrongNestedHit : DrawableStrongNestedHit
+ {
+ ///
+ /// The lenience for the second key press.
+ /// This does not adjust by map difficulty in ScoreV2 yet.
+ ///
+ private const double second_hit_window = 30;
+
+ public new DrawableHit MainObject => (DrawableHit)base.MainObject;
+
+ public StrongNestedHit(StrongHitObject strong, DrawableHit hit)
+ : base(strong, hit)
+ {
+ }
+
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
+ {
+ if (!MainObject.Result.HasResult)
+ {
+ base.CheckForResult(userTriggered, timeOffset);
+ return;
+ }
+
+ if (!MainObject.Result.IsHit)
+ {
+ ApplyResult(r => r.Type = HitResult.Miss);
+ return;
+ }
+
+ if (!userTriggered)
+ {
+ if (timeOffset > second_hit_window)
+ ApplyResult(r => r.Type = HitResult.Miss);
+ return;
+ }
+
+ if (Math.Abs(MainObject.Result.TimeOffset - timeOffset) < second_hit_window)
+ ApplyResult(r => r.Type = HitResult.Great);
+ }
+
+ public override bool OnPressed(TaikoAction action)
+ {
+ // Don't process actions until the main hitobject is hit
+ if (!MainObject.IsHit)
+ return false;
+
+ // Don't process actions if the pressed button was released
+ if (MainObject.HitAction == null)
+ return false;
+
+ // Don't handle invalid hit action presses
+ if (!MainObject.HitActions.Contains(action))
+ return false;
+
+ return UpdateResult(true);
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
deleted file mode 100644
index b431d35e16..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Linq;
-using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Judgements;
-
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables
-{
- public abstract class DrawableHitStrong : DrawableHit
- {
- ///
- /// The lenience for the second key press.
- /// This does not adjust by map difficulty in ScoreV2 yet.
- ///
- private const double second_hit_window = 30;
-
- private double firstHitTime;
- private bool firstKeyHeld;
- private TaikoAction firstHitAction;
-
- protected DrawableHitStrong(Hit hit)
- : base(hit)
- {
- }
-
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
- {
- if (!SecondHitAllowed)
- {
- base.CheckForJudgements(userTriggered, timeOffset);
- return;
- }
-
- if (!userTriggered)
- {
- if (timeOffset > second_hit_window)
- AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.None });
- return;
- }
-
- // If we get here, we're assured that the key pressed is the correct secondary key
-
- if (Math.Abs(firstHitTime - Time.Current) < second_hit_window)
- AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great });
- }
-
- protected override void UpdateState(ArmedState state)
- {
- base.UpdateState(state);
-
- switch (state)
- {
- case ArmedState.Idle:
- firstHitTime = 0;
- firstKeyHeld = false;
- break;
- }
- }
-
- public override bool OnReleased(TaikoAction action)
- {
- if (action == firstHitAction)
- firstKeyHeld = false;
- return base.OnReleased(action);
- }
-
- public override bool OnPressed(TaikoAction action)
- {
- if (AllJudged)
- return false;
-
- // Check if we've handled the first key
- if (!SecondHitAllowed)
- {
- // First key hasn't been handled yet, attempt to handle it
- bool handled = base.OnPressed(action);
-
- if (handled)
- {
- firstHitTime = Time.Current;
- firstHitAction = action;
- firstKeyHeld = true;
- }
-
- return handled;
- }
-
- // Don't handle represses of the first key
- if (firstHitAction == action)
- return false;
-
- // 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);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
index f2194c6d56..188cafe1db 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableRimHit : DrawableHit
{
- protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
+ public 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
deleted file mode 100644
index 728fe416f7..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Allocation;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables
-{
- public class DrawableRimHitStrong : DrawableHitStrong
- {
- protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
-
- public DrawableRimHitStrong(Hit hit)
- : base(hit)
- {
- MainPiece.Add(new RimHitSymbolPiece());
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- MainPiece.AccentColour = colours.BlueDarker;
- }
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs
new file mode 100644
index 0000000000..b27de3832a
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Taiko.Judgements;
+
+namespace osu.Game.Rulesets.Taiko.Objects.Drawables
+{
+ ///
+ /// Used as a nested hitobject to provide s for s.
+ ///
+ public abstract class DrawableStrongNestedHit : DrawableTaikoHitObject
+ {
+ public readonly DrawableHitObject MainObject;
+
+ protected DrawableStrongNestedHit(StrongHitObject strong, DrawableHitObject mainObject)
+ : base(strong)
+ {
+ MainObject = mainObject;
+ }
+
+ protected override void UpdateState(ArmedState state)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index 408b37e377..5059734663 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -2,6 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -12,23 +14,19 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableSwell : DrawableTaikoHitObject
{
- ///
- /// A judgement is only displayed when the user has complete the swell (either a hit or miss).
- ///
- public override bool DisplayJudgement => AllJudged;
-
private const float target_ring_thick_border = 1.4f;
private const float target_ring_thin_border = 1f;
private const float target_ring_scale = 5f;
private const float inner_ring_alpha = 0.65f;
+ private readonly List ticks = new List();
+
private readonly Container bodyContainer;
private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing;
@@ -106,6 +104,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
});
MainPiece.Add(symbol = new SwellSymbolPiece());
+
+ foreach (var tick in HitObject.NestedHitObjects.OfType())
+ {
+ var vis = new DrawableSwellTick(tick);
+
+ ticks.Add(vis);
+ AddInternal(vis);
+ AddNested(vis);
+ }
}
[BackgroundDependencyLoader]
@@ -124,13 +131,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Width *= Parent.RelativeChildSize.X;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (userTriggered)
{
- AddJudgement(new TaikoIntermediateSwellJudgement());
+ var nextTick = ticks.FirstOrDefault(j => !j.IsHit);
- var completion = (float)Judgements.Count / HitObject.RequiredHits;
+ nextTick?.TriggerResult(HitResult.Great);
+
+ var numHits = ticks.Count(r => r.IsHit);
+
+ var completion = (float)numHits / HitObject.RequiredHits;
expandingRing
.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50)
@@ -141,18 +152,30 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
- if (Judgements.Count == HitObject.RequiredHits)
- AddJudgement(new TaikoJudgement { Result = HitResult.Great });
+ if (numHits == HitObject.RequiredHits)
+ ApplyResult(r => r.Type = HitResult.Great);
}
else
{
if (timeOffset < 0)
return;
- //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
- AddJudgement(Judgements.Count > HitObject.RequiredHits / 2
- ? new TaikoJudgement { Result = HitResult.Good }
- : new TaikoJudgement { Result = HitResult.Miss });
+ int numHits = 0;
+
+ foreach (var tick in ticks)
+ {
+ if (tick.IsHit)
+ {
+ numHits++;
+ continue;
+ }
+
+ tick.TriggerResult(HitResult.Miss);
+ }
+
+ var hitResult = numHits > HitObject.RequiredHits / 2 ? HitResult.Good : HitResult.Miss;
+
+ ApplyResult(r => r.Type = hitResult);
}
}
@@ -208,7 +231,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return false;
lastWasCentre = isCentre;
- UpdateJudgement(true);
+ UpdateResult(true);
return true;
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs
new file mode 100644
index 0000000000..36c468c6d6
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs
@@ -0,0 +1,30 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Taiko.Objects.Drawables
+{
+ public class DrawableSwellTick : DrawableTaikoHitObject
+ {
+ public override bool DisplayResult => false;
+
+ public DrawableSwellTick(TaikoHitObject hitObject)
+ : base(hitObject)
+ {
+ }
+
+ public void TriggerResult(HitResult type) => ApplyResult(r => r.Type = type);
+
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
+ {
+ }
+
+ protected override void UpdateState(ArmedState state)
+ {
+ }
+
+ public override bool OnPressed(TaikoAction action) => false;
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index a6d61f1a5a..51e39dc648 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -101,6 +101,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Content.Add(MainPiece = CreateMainPiece());
MainPiece.KiaiMode = HitObject.Kiai;
+
+ var strongObject = HitObject.NestedHitObjects.OfType().FirstOrDefault();
+ if (strongObject != null)
+ {
+ var strongHit = CreateStrongHit(strongObject);
+
+ AddNested(strongHit);
+ AddInternal(strongHit);
+ }
}
// Normal and clap samples are handled by the drum
@@ -109,5 +118,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override string SampleNamespace => "Taiko";
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece();
+
+ ///
+ /// Creates the handler for this 's .
+ /// This is only invoked if is true for .
+ ///
+ /// The strong hitobject.
+ /// The strong hitobject handler.
+ protected virtual DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => null;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
index 4c9ec5473b..405ea85f0d 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
@@ -54,12 +54,12 @@ namespace osu.Game.Rulesets.Taiko.Objects
protected override void CreateNestedHitObjects()
{
- base.CreateNestedHitObjects();
-
createTicks();
RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * overallDifficulty);
RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * overallDifficulty);
+
+ base.CreateNestedHitObjects();
}
private void createTicks()
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
index e546d6427f..967d5acfd7 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
@@ -1,6 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Taiko.Judgements;
+
namespace osu.Game.Rulesets.Taiko.Objects
{
public class DrumRollTick : TaikoHitObject
@@ -20,5 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// The time allowed to hit this tick.
///
public double HitWindow => TickSpacing / 2;
+
+ public override Judgement CreateJudgement() => new TaikoDrumRollTickJudgement();
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs
new file mode 100644
index 0000000000..fac3705110
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs
@@ -0,0 +1,13 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Taiko.Judgements;
+
+namespace osu.Game.Rulesets.Taiko.Objects
+{
+ public class StrongHitObject : TaikoHitObject
+ {
+ public override Judgement CreateJudgement() => new TaikoStrongJudgement();
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
index eb6f931af4..702bf63bf5 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Taiko.Objects
@@ -15,5 +16,15 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// The number of hits required to complete the swell successfully.
///
public int RequiredHits = 10;
+
+ public override bool IsStrong { set => throw new NotSupportedException($"{nameof(Swell)} cannot be a strong hitobject."); }
+
+ protected override void CreateNestedHitObjects()
+ {
+ base.CreateNestedHitObjects();
+
+ for (int i = 0; i < RequiredHits; i++)
+ AddNested(new SwellTick());
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs
new file mode 100644
index 0000000000..49eb6d2a15
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs
@@ -0,0 +1,9 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Rulesets.Taiko.Objects
+{
+ public class SwellTick : TaikoHitObject
+ {
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
index ffbbe28f2e..9c86b60688 100644
--- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
@@ -1,7 +1,10 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
{
@@ -26,7 +29,17 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// Whether this HitObject is a "strong" type.
/// Strong hit objects give more points for hitting the hit object with both keys.
///
- public bool IsStrong;
+ public virtual bool IsStrong { get; set; }
+
+ protected override void CreateNestedHitObjects()
+ {
+ base.CreateNestedHitObjects();
+
+ if (IsStrong)
+ AddNested(new StrongHitObject { StartTime = (this as IHasEndTime)?.EndTime ?? StartTime });
+ }
+
+ public override Judgement CreateJudgement() => new TaikoJudgement();
protected override HitWindows CreateHitWindows() => new TaikoHitWindows();
}
diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs
index eae033401e..ab7856eb8f 100644
--- a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs
+++ b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs
@@ -4,7 +4,7 @@
using osu.Game.Rulesets.Replays;
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Input;
+using osu.Framework.Input.StateChanges;
namespace osu.Game.Rulesets.Taiko.Replays
{
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index c7f75e44fa..cf33141027 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
@@ -60,62 +59,31 @@ namespace osu.Game.Rulesets.Taiko.Scoring
private double hpIncreaseGood;
private double hpIncreaseMiss;
- public TaikoScoreProcessor()
- {
- }
-
public TaikoScoreProcessor(RulesetContainer rulesetContainer)
: base(rulesetContainer)
{
}
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void ApplyBeatmap(Beatmap beatmap)
{
+ base.ApplyBeatmap(beatmap);
+
double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
hpIncreaseTick = hp_hit_tick;
hpIncreaseGreat = hpMultiplierNormal * hp_hit_great;
hpIncreaseGood = hpMultiplierNormal * hp_hit_good;
hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);
-
- foreach (var obj in beatmap.HitObjects)
- {
- if (obj is Hit)
- {
- AddJudgement(new TaikoJudgement { Result = HitResult.Great });
- if (obj.IsStrong)
- AddJudgement(new TaikoStrongHitJudgement());
- }
- else if (obj is DrumRoll)
- {
- for (int i = 0; i < ((DrumRoll)obj).NestedHitObjects.OfType().Count(); i++)
- {
- AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
-
- if (obj.IsStrong)
- AddJudgement(new TaikoStrongHitJudgement());
- }
-
- AddJudgement(new TaikoJudgement { Result = HitResult.Great });
-
- if (obj.IsStrong)
- AddJudgement(new TaikoStrongHitJudgement());
- }
- else if (obj is Swell)
- {
- AddJudgement(new TaikoJudgement { Result = HitResult.Great });
- }
- }
}
- protected override void OnNewJudgement(Judgement judgement)
+ protected override void ApplyResult(JudgementResult result)
{
- base.OnNewJudgement(judgement);
+ base.ApplyResult(result);
- bool isTick = judgement is TaikoDrumRollTickJudgement;
+ bool isTick = result.Judgement is TaikoDrumRollTickJudgement;
// Apply HP changes
- switch (judgement.Result)
+ switch (result.Type)
{
case HitResult.Miss:
// Missing ticks shouldn't drop HP
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index 609fd27bb4..7d9bc98957 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -93,13 +93,11 @@ namespace osu.Game.Rulesets.Taiko
new TaikoModHidden(),
new TaikoModFlashlight(),
};
- case ModType.Special:
+ case ModType.Automation:
return new Mod[]
{
- new TaikoModRelax(),
- null,
- null,
new MultiMod(new TaikoModAutoplay(), new ModCinema()),
+ new TaikoModRelax(),
};
default:
return new Mod[] { };
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
index b07a3ce8df..4d660918b8 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
@@ -19,16 +19,16 @@ namespace osu.Game.Rulesets.Taiko.UI
/// Creates a new judgement text.
///
/// The object which is being judged.
- /// The judgement to visualise.
- public DrawableTaikoJudgement(Judgement judgement, DrawableHitObject judgedObject)
- : base(judgement, judgedObject)
+ /// The judgement to visualise.
+ public DrawableTaikoJudgement(JudgementResult result, DrawableHitObject judgedObject)
+ : base(result, judgedObject)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- switch (Judgement.Result)
+ switch (Result.Type)
{
case HitResult.Good:
Colour = colours.GreenLight;
@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override void LoadComplete()
{
- if (Judgement.IsHit)
+ if (Result.IsHit)
this.MoveToY(-100, 500);
base.LoadComplete();
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index 7fdd3cd1e2..325beb38a5 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -58,8 +58,9 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Box background;
public TaikoPlayfield(ControlPointInfo controlPoints)
- : base(ScrollingDirection.Left)
{
+ Direction.Value = ScrollingDirection.Left;
+
AddRangeInternal(new Drawable[]
{
backgroundContainer = new Container
@@ -208,45 +209,55 @@ namespace osu.Game.Rulesets.Taiko.UI
public override void Add(DrawableHitObject h)
{
- h.OnJudgement += OnJudgement;
+ h.OnNewResult += OnNewResult;
base.Add(h);
- var barline = h as DrawableBarLine;
- if (barline != null)
- barlineContainer.Add(barline.CreateProxy());
-
- var taikoObject = h as DrawableTaikoHitObject;
- if (taikoObject != null)
- topLevelHitContainer.Add(taikoObject.CreateProxiedContent());
+ switch (h)
+ {
+ case DrawableBarLine barline:
+ barlineContainer.Add(barline.CreateProxy());
+ break;
+ case DrawableTaikoHitObject taikoObject:
+ topLevelHitContainer.Add(taikoObject.CreateProxiedContent());
+ break;
+ }
}
- internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null)
- {
- judgementContainer.Add(new DrawableTaikoJudgement(judgement, judgedObject)
- {
- Anchor = judgement.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
- Origin = judgement.IsHit ? Anchor.BottomCentre : Anchor.Centre,
- RelativePositionAxes = Axes.X,
- X = judgement.IsHit ? judgedObject.Position.X : 0,
- });
- }
-
- if (!judgement.IsHit)
+ if (!DisplayJudgements)
return;
- bool isRim = judgedObject.HitObject is RimHit;
+ if (!judgedObject.DisplayResult)
+ return;
- if (judgement is TaikoStrongHitJudgement)
- hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == judgedObject)?.VisualiseSecondHit();
- else
+ switch (result.Judgement)
{
- hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
+ case TaikoStrongJudgement _:
+ if (result.IsHit)
+ hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit();
+ break;
+ default:
+ judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject)
+ {
+ Anchor = result.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
+ Origin = result.IsHit ? Anchor.BottomCentre : Anchor.Centre,
+ RelativePositionAxes = Axes.X,
+ X = result.IsHit ? judgedObject.Position.X : 0,
+ });
- if (judgedObject.HitObject.Kiai)
- kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));
+ if (!result.IsHit)
+ break;
+
+ bool isRim = judgedObject.HitObject is RimHit;
+
+ hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
+
+ if (judgedObject.HitObject.Kiai)
+ kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));
+
+ break;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
index e04b4d45f6..229ab69ceb 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
@@ -98,32 +98,18 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h)
{
- var centreHit = h as CentreHit;
- if (centreHit != null)
+ switch (h)
{
- if (h.IsStrong)
- return new DrawableCentreHitStrong(centreHit);
- return new DrawableCentreHit(centreHit);
+ case CentreHit centreHit:
+ return new DrawableCentreHit(centreHit);
+ case RimHit rimHit:
+ return new DrawableRimHit(rimHit);
+ case DrumRoll drumRoll:
+ return new DrawableDrumRoll(drumRoll);
+ case Swell swell:
+ return new DrawableSwell(swell);
}
- var rimHit = h as RimHit;
- if (rimHit != null)
- {
- if (h.IsStrong)
- return new DrawableRimHitStrong(rimHit);
- return new DrawableRimHit(rimHit);
- }
-
- var drumRoll = h as DrumRoll;
- if (drumRoll != null)
- {
- return new DrawableDrumRoll(drumRoll);
- }
-
- var swell = h as Swell;
- if (swell != null)
- return new DrawableSwell(swell);
-
return null;
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index ad203d2107..d3351f86f8 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -11,7 +11,9 @@ using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.Timing;
+using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Skinning;
namespace osu.Game.Tests.Beatmaps.Formats
@@ -186,6 +188,50 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
+ [Test]
+ public void TestDecodeBeatmapComboOffsetsOsu()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("hitobject-combo-offset.osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var beatmap = decoder.Decode(stream);
+
+ var converted = new OsuBeatmapConverter(beatmap).Convert();
+ new OsuBeatmapProcessor(converted).PreProcess();
+ new OsuBeatmapProcessor(converted).PostProcess();
+
+ Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndex);
+ Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndex);
+ Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndex);
+ Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndex);
+ Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndex);
+ Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndex);
+ }
+ }
+
+ [Test]
+ public void TestDecodeBeatmapComboOffsetsCatch()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("hitobject-combo-offset.osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var beatmap = decoder.Decode(stream);
+
+ var converted = new CatchBeatmapConverter(beatmap).Convert();
+ new CatchBeatmapProcessor(converted).PreProcess();
+ new CatchBeatmapProcessor(converted).PostProcess();
+
+ Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndex);
+ Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndex);
+ Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndex);
+ Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndex);
+ Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndex);
+ Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndex);
+ }
+ }
+
[Test]
public void TestDecodeBeatmapHitObjects()
{
@@ -214,39 +260,56 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
[Test]
- public void TestDecodeCustomSamples()
+ public void TestDecodeControlPointCustomSampleBank()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("custom-samples.osu"))
+ using (var resStream = Resource.OpenResource("controlpoint-custom-samplebank.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
- Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[0]).Name);
- Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[1]).Name);
- Assert.AreEqual("hitnormal2", getTestableSampleInfo(hitObjects[2]).Name);
- Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[3]).Name);
+ Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[0]).LookupNames.First());
+ Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
+ Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
+ Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
}
- SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" });
+ SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
[Test]
- public void TestDecodeCustomHitObjectSamples()
+ public void TestDecodeHitObjectCustomSampleBank()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("custom-hitobject-samples.osu"))
+ using (var resStream = Resource.OpenResource("hitobject-custom-samplebank.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
- Assert.AreEqual("hit_1.wav", hitObjects[0].Samples[0].LookupNames.First());
- Assert.AreEqual("hit_2.wav", hitObjects[1].Samples[0].LookupNames.First());
- Assert.AreEqual("hitnormal2", getTestableSampleInfo(hitObjects[2]).Name);
- Assert.AreEqual("hit_1.wav", hitObjects[3].Samples[0].LookupNames.First());
+ Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[0]).LookupNames.First());
+ Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
+ Assert.AreEqual("normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
}
- SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" });
+ SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
+ }
+
+ [Test]
+ public void TestDecodeHitObjectFileSamples()
+ {
+ var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
+ using (var resStream = Resource.OpenResource("hitobject-file-samples.osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var hitObjects = decoder.Decode(stream).HitObjects;
+
+ Assert.AreEqual("hit_1.wav", getTestableSampleInfo(hitObjects[0]).LookupNames.First());
+ Assert.AreEqual("hit_2.wav", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
+ Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
+ Assert.AreEqual("hit_1.wav", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
+ }
+
+ SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
}
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
index 3431be91f9..82adc88c6b 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
@@ -86,5 +86,19 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(78993, animation.StartTime);
}
}
+
+ [Test]
+ public void TestDecodeVariableWithSuffix()
+ {
+ var decoder = new LegacyStoryboardDecoder();
+ using (var resStream = Resource.OpenResource("variable-with-suffix.osb"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var storyboard = decoder.Decode(stream);
+
+ StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
+ Assert.AreEqual(123456, ((StoryboardSprite)background.Elements.Single()).InitialPosition.X);
+ }
+ }
}
}
diff --git a/osu.Game.Tests/Resources/custom-samples.osu b/osu.Game.Tests/Resources/controlpoint-custom-samplebank.osu
similarity index 100%
rename from osu.Game.Tests/Resources/custom-samples.osu
rename to osu.Game.Tests/Resources/controlpoint-custom-samplebank.osu
diff --git a/osu.Game.Tests/Resources/hitobject-combo-offset.osu b/osu.Game.Tests/Resources/hitobject-combo-offset.osu
new file mode 100644
index 0000000000..c1f0dab8e9
--- /dev/null
+++ b/osu.Game.Tests/Resources/hitobject-combo-offset.osu
@@ -0,0 +1,32 @@
+osu file format v14
+
+[HitObjects]
+// Circle with combo offset (3)
+255,193,1000,49,0,0:0:0:0:
+// Combo index = 4
+
+// Slider with new combo followed by circle with no new combo
+256,192,2000,12,0,2000,0:0:0:0:
+255,193,3000,1,0,0:0:0:0:
+// Combo index = 5
+
+// Slider without new combo followed by circle with no new combo
+256,192,4000,8,0,5000,0:0:0:0:
+255,193,6000,1,0,0:0:0:0:
+// Combo index = 5
+
+// Slider without new combo followed by circle with new combo
+256,192,7000,8,0,8000,0:0:0:0:
+255,193,9000,5,0,0:0:0:0:
+// Combo index = 6
+
+// Slider with new combo and offset (1) followed by circle with new combo and offset (3)
+256,192,10000,28,0,11000,0:0:0:0:
+255,193,12000,53,0,0:0:0:0:
+// Combo index = 11
+
+// Slider with new combo and offset (2) followed by slider with no new combo followed by circle with no new combo
+256,192,13000,44,0,14000,0:0:0:0:
+256,192,15000,8,0,16000,0:0:0:0:
+255,193,17000,1,0,0:0:0:0:
+// Combo index = 14
\ No newline at end of file
diff --git a/osu.Game.Tests/Resources/hitobject-custom-samplebank.osu b/osu.Game.Tests/Resources/hitobject-custom-samplebank.osu
new file mode 100644
index 0000000000..56b1a8762f
--- /dev/null
+++ b/osu.Game.Tests/Resources/hitobject-custom-samplebank.osu
@@ -0,0 +1,13 @@
+osu file format v14
+
+[General]
+SampleSet: Normal
+
+[TimingPoints]
+2170,468.75,4,1,0,40,1,0
+3107,-100,4,1,2,40,0,0
+
+[HitObjects]
+255,193,2170,1,0,0:0:0:0:
+256,191,3107,5,0,0:0:0:0:
+255,193,4000,1,0,0:0:3:70:
diff --git a/osu.Game.Tests/Resources/custom-hitobject-samples.osu b/osu.Game.Tests/Resources/hitobject-file-samples.osu
similarity index 100%
rename from osu.Game.Tests/Resources/custom-hitobject-samples.osu
rename to osu.Game.Tests/Resources/hitobject-file-samples.osu
diff --git a/osu.Game.Tests/Resources/variable-with-suffix.osb b/osu.Game.Tests/Resources/variable-with-suffix.osb
new file mode 100644
index 0000000000..5c9b46ca98
--- /dev/null
+++ b/osu.Game.Tests/Resources/variable-with-suffix.osb
@@ -0,0 +1,5 @@
+[Variables]
+$var=1234
+
+[Events]
+Sprite,Background,TopCentre,"img.jpg",$var56,240
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
index 6d2b37d981..db66c01814 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
@@ -530,7 +530,7 @@ namespace osu.Game.Tests.Visual
{
public new List Items => base.Items;
- public bool PendingFilterTask => FilterTask != null;
+ public bool PendingFilterTask => PendingFilter != null;
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
index ee66f53ddc..175db7d246 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
+using JetBrains.Annotations;
using NUnit.Framework;
using OpenTK;
using osu.Framework.Allocation;
@@ -110,42 +111,33 @@ namespace osu.Game.Tests.Visual
private void testInfoLabels(int expectedCount)
{
- AddAssert("check infolabels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any());
- AddAssert("check infolabels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount);
+ AddAssert("check info labels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any());
+ AddAssert("check info labels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount);
}
private void testNullBeatmap()
{
- selectNullBeatmap();
+ selectBeatmap(null);
AddAssert("check empty version", () => string.IsNullOrEmpty(infoWedge.Info.VersionLabel.Text));
AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Title);
AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Artist);
AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any());
- AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
+ AddAssert("check no info labels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
}
- private void selectBeatmap(IBeatmap b)
+ private void selectBeatmap([CanBeNull] IBeatmap b)
{
BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null;
- AddStep($"select {b.Metadata.Title} beatmap", () =>
+ AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () =>
{
infoBefore = infoWedge.Info;
- infoWedge.Beatmap = Beatmap.Value = new TestWorkingBeatmap(b);
+ infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : new TestWorkingBeatmap(b);
});
AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load");
}
- private void selectNullBeatmap()
- {
- AddStep("select null beatmap", () =>
- {
- Beatmap.Value = Beatmap.Default;
- infoWedge.Beatmap = Beatmap;
- });
- }
-
private IBeatmap createTestBeatmap(RulesetInfo ruleset)
{
List objects = new List();
diff --git a/osu.Game.Tests/Visual/TestCaseButtonSystem.cs b/osu.Game.Tests/Visual/TestCaseButtonSystem.cs
index 5eb81cdf9f..7f8133d638 100644
--- a/osu.Game.Tests/Visual/TestCaseButtonSystem.cs
+++ b/osu.Game.Tests/Visual/TestCaseButtonSystem.cs
@@ -1,6 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
+using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@@ -13,6 +16,13 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCaseButtonSystem : OsuTestCase
{
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ButtonSystem),
+ typeof(ButtonArea),
+ typeof(Button)
+ };
+
public TestCaseButtonSystem()
{
OsuLogo logo;
@@ -30,6 +40,9 @@ namespace osu.Game.Tests.Visual
};
buttons.SetOsuLogo(logo);
+
+ foreach (var s in Enum.GetValues(typeof(ButtonSystemState)).OfType().Skip(1))
+ AddStep($"State to {s}", () => buttons.State = s);
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseCursors.cs b/osu.Game.Tests/Visual/TestCaseCursors.cs
index 0aa8e9691e..361e255894 100644
--- a/osu.Game.Tests/Visual/TestCaseCursors.cs
+++ b/osu.Game.Tests/Visual/TestCaseCursors.cs
@@ -7,7 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.MathUtils;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Sprites;
diff --git a/osu.Game.Tests/Visual/TestCaseDisclaimer.cs b/osu.Game.Tests/Visual/TestCaseDisclaimer.cs
new file mode 100644
index 0000000000..a8253a991a
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseDisclaimer.cs
@@ -0,0 +1,28 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Screens.Menu;
+using OpenTK.Graphics;
+
+namespace osu.Game.Tests.Visual
+{
+ public class TestCaseDisclaimer : OsuTestCase
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ new Disclaimer()
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
index 130685a4cf..45b5ec2c11 100644
--- a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
@@ -8,7 +8,7 @@ using System.Linq;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
using osu.Framework.Logging;
using osu.Game.Screens.Play;
diff --git a/osu.Game.Tests/Visual/TestCaseIconButton.cs b/osu.Game.Tests/Visual/TestCaseIconButton.cs
index 16363da527..14cba71ec8 100644
--- a/osu.Game.Tests/Visual/TestCaseIconButton.cs
+++ b/osu.Game.Tests/Visual/TestCaseIconButton.cs
@@ -25,11 +25,7 @@ namespace osu.Game.Tests.Visual
Children = new[]
{
new NamedIconButton("No change", new IconButton()),
- new NamedIconButton("Background colours", new IconButton
- {
- FlashColour = Color4.DarkGreen,
- HoverColour = Color4.Green,
- }),
+ new NamedIconButton("Background colours", new ColouredIconButton()),
new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }),
new NamedIconButton("Unchanging size", new IconButton(), false),
new NamedIconButton("Icon colours", new IconButton
@@ -41,6 +37,15 @@ namespace osu.Game.Tests.Visual
};
}
+ private class ColouredIconButton : IconButton
+ {
+ public ColouredIconButton()
+ {
+ FlashColour = Color4.DarkGreen;
+ HoverColour = Color4.Green;
+ }
+ }
+
private class NamedIconButton : Container
{
public NamedIconButton(string name, IconButton button, bool allowSizeChange = true)
diff --git a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
index a20f67e336..b98875cd6a 100644
--- a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
+++ b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
@@ -18,7 +18,6 @@ namespace osu.Game.Tests.Visual
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- IsCounting = true,
Children = new KeyCounter[]
{
new KeyCounterKeyboard(Key.Z),
diff --git a/osu.Game.Tests/Visual/TestCaseLabelledTextBox.cs b/osu.Game.Tests/Visual/TestCaseLabelledTextBox.cs
new file mode 100644
index 0000000000..d41739bfb5
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseLabelledTextBox.cs
@@ -0,0 +1,41 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Screens.Edit.Screens.Setup.Components.LabelledComponents;
+using System;
+using System.Collections.Generic;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseLabelledTextBox : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(LabelledTextBox),
+ };
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Child = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ Padding = new MarginPadding { Left = 150, Right = 150 },
+ Child = new LabelledTextBox
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ LabelText = "Testing text",
+ PlaceholderText = "This is definitely working as intended",
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseMods.cs b/osu.Game.Tests/Visual/TestCaseMods.cs
index 73c37348d5..ab53dbd968 100644
--- a/osu.Game.Tests/Visual/TestCaseMods.cs
+++ b/osu.Game.Tests/Visual/TestCaseMods.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
-using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays.Mods;
@@ -13,10 +12,11 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using System.Linq;
using System.Collections.Generic;
-using osu.Game.Rulesets.Osu;
+using NUnit.Framework;
+using osu.Framework.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
-using osu.Game.Rulesets.Mania;
+using osu.Game.Overlays.Mods.Sections;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.UI;
using OpenTK.Graphics;
@@ -36,7 +36,9 @@ namespace osu.Game.Tests.Visual
typeof(ModButtonEmpty),
typeof(DifficultyReductionSection),
typeof(DifficultyIncreaseSection),
- typeof(SpecialSection),
+ typeof(AutomationSection),
+ typeof(ConversionSection),
+ typeof(FunSection),
};
private RulesetStore rulesets;
@@ -47,11 +49,6 @@ namespace osu.Game.Tests.Visual
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
Add(modSelect = new TestModSelectOverlay
{
@@ -68,34 +65,25 @@ namespace osu.Game.Tests.Visual
Position = new Vector2(0, 25),
});
+ modDisplay.Current.UnbindBindings();
modDisplay.Current.BindTo(modSelect.SelectedMods);
- AddStep("Toggle", modSelect.ToggleVisibility);
- AddStep("Hide", modSelect.Hide);
AddStep("Show", modSelect.Show);
-
- foreach (var rulesetInfo in rulesets.AvailableRulesets)
- {
- Ruleset ruleset = rulesetInfo.CreateInstance();
- AddStep($"switch to {ruleset.Description}", () => Ruleset.Value = rulesetInfo);
-
- switch (ruleset)
- {
- case OsuRuleset or:
- testOsuMods(or);
- break;
- case ManiaRuleset mr:
- testManiaMods(mr);
- break;
- }
- }
+ AddStep("Toggle", modSelect.ToggleVisibility);
+ AddStep("Toggle", modSelect.ToggleVisibility);
}
- private void testOsuMods(OsuRuleset ruleset)
+ [Test]
+ public void TestOsuMods()
{
- var easierMods = ruleset.GetModsFor(ModType.DifficultyReduction);
- var harderMods = ruleset.GetModsFor(ModType.DifficultyIncrease);
- var assistMods = ruleset.GetModsFor(ModType.Special);
+ var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 0);
+ AddStep("change ruleset", () => { Ruleset.Value = ruleset; });
+
+ var instance = ruleset.CreateInstance();
+
+ var easierMods = instance.GetModsFor(ModType.DifficultyReduction);
+ var harderMods = instance.GetModsFor(ModType.DifficultyIncrease);
+ var assistMods = instance.GetModsFor(ModType.Automation);
var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
@@ -114,12 +102,43 @@ namespace osu.Game.Tests.Visual
testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour);
testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour);
- testUnimplmentedMod(autoPilotMod);
+ testUnimplementedMod(autoPilotMod);
}
- private void testManiaMods(ManiaRuleset ruleset)
+ [Test]
+ public void TestManiaMods()
{
- testRankedText(ruleset.GetModsFor(ModType.Special).First(m => m is ManiaModRandom));
+ var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 3);
+ AddStep("change ruleset", () => { Ruleset.Value = ruleset; });
+
+ testRankedText(ruleset.CreateInstance().GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom));
+ }
+
+ [Test]
+ public void TestRulesetChanges()
+ {
+ var rulesetOsu = rulesets.AvailableRulesets.First(r => r.ID == 0);
+ var rulesetMania = rulesets.AvailableRulesets.First(r => r.ID == 3);
+
+ AddStep("change ruleset to null", () => { Ruleset.Value = null; });
+
+ var instance = rulesetOsu.CreateInstance();
+ var easierMods = instance.GetModsFor(ModType.DifficultyReduction);
+ var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
+
+ AddStep("set mods externally", () => { modDisplay.Current.Value = new[] { noFailMod }; });
+
+ AddStep("change ruleset to osu", () => { Ruleset.Value = rulesetOsu; });
+
+ AddAssert("ensure mods still selected", () => modDisplay.Current.Value.Single(m => m is OsuModNoFail) != null);
+
+ AddStep("change ruleset to mania", () => { Ruleset.Value = rulesetMania; });
+
+ AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any(m => m is OsuModNoFail));
+
+ AddStep("change ruleset to osu", () => { Ruleset.Value = rulesetOsu; });
+
+ AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any());
}
private void testSingleMod(Mod mod)
@@ -154,7 +173,7 @@ namespace osu.Game.Tests.Visual
checkNotSelected(mod);
}
- private void testUnimplmentedMod(Mod mod)
+ private void testUnimplementedMod(Mod mod)
{
selectNext(mod);
checkNotSelected(mod);
@@ -234,6 +253,8 @@ namespace osu.Game.Tests.Visual
private class TestModSelectOverlay : ModSelectOverlay
{
+ public new Bindable> SelectedMods => base.SelectedMods;
+
public ModButton GetModButton(Mod mod)
{
var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type);
diff --git a/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs b/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs
index 123c1fe055..bc232d814d 100644
--- a/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs
+++ b/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs
@@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual
private class TestOnScreenDisplay : OnScreenDisplay
{
- protected override void Display(Drawable toDisplay) => toDisplay.FadeIn().ResizeHeightTo(110);
+ protected override void DisplayTemporarily(Drawable toDisplay) => toDisplay.FadeIn().ResizeHeightTo(110);
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseOsuGame.cs b/osu.Game.Tests/Visual/TestCaseOsuGame.cs
index f1a21a58d5..7a4e4c1210 100644
--- a/osu.Game.Tests/Visual/TestCaseOsuGame.cs
+++ b/osu.Game.Tests/Visual/TestCaseOsuGame.cs
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Timing;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using OpenTK.Graphics;
@@ -23,19 +22,15 @@ namespace osu.Game.Tests.Visual
public TestCaseOsuGame()
{
- var rateAdjustClock = new StopwatchClock(true);
- var framedClock = new FramedClock(rateAdjustClock);
- framedClock.ProcessFrame();
-
- Add(new Box
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- });
-
- Add(new Loader());
-
- AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v);
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ new Loader()
+ };
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
index dab7f7e037..888bf6250f 100644
--- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
@@ -8,15 +8,18 @@ using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
-using osu.Game.Tests.Platform;
namespace osu.Game.Tests.Visual
{
@@ -28,6 +31,11 @@ namespace osu.Game.Tests.Visual
private RulesetStore rulesets;
private WorkingBeatmap defaultBeatmap;
+ private DatabaseContextFactory factory;
+
+ [Cached]
+ [Cached(Type = typeof(IBindable>))]
+ private readonly Bindable> selectedMods = new Bindable>(new Mod[] { });
public override IReadOnlyList RequiredTypes => new[]
{
@@ -49,63 +57,70 @@ namespace osu.Game.Tests.Visual
private class TestSongSelect : PlaySongSelect
{
+ public new Bindable Ruleset => base.Ruleset;
+
public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap;
public new BeatmapCarousel Carousel => base.Carousel;
}
+ private TestSongSelect songSelect;
+
+ protected override void Dispose(bool isDisposing)
+ {
+ factory.ResetDatabase();
+ base.Dispose(isDisposing);
+ }
+
[BackgroundDependencyLoader]
private void load()
{
- TestSongSelect songSelect = null;
+ factory = new DatabaseContextFactory(LocalStorage);
+ factory.ResetDatabase();
- var storage = new TestStorage(@"TestCasePlaySongSelect");
+ using (var usage = factory.Get())
+ usage.Migrate();
- // this is by no means clean. should be replacing inside of OsuGameBase somehow.
- IDatabaseContextFactory factory = new SingletonContextFactory(new OsuDbContext());
+ factory.ResetDatabase();
+
+ using (var usage = factory.Get())
+ usage.Migrate();
Dependencies.Cache(rulesets = new RulesetStore(factory));
- Dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null, null)
+ Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null)
{
DefaultBeatmap = defaultBeatmap = Beatmap.Default
});
- void loadNewSongSelect(bool deleteMaps = false) => AddStep("reload song select", () =>
- {
- if (deleteMaps)
- {
- manager.Delete(manager.GetAllUsableBeatmapSets());
- Beatmap.SetDefault();
- }
+ Beatmap.SetDefault();
+ }
- if (songSelect != null)
- {
- Remove(songSelect);
- songSelect.Dispose();
- }
-
- Add(songSelect = new TestSongSelect());
- });
-
- loadNewSongSelect(true);
-
- AddWaitStep(3);
+ [SetUp]
+ public virtual void SetUp()
+ {
+ manager?.Delete(manager.GetAllUsableBeatmapSets());
+ Child = songSelect = new TestSongSelect();
+ }
+ [Test]
+ public void TestDummy()
+ {
AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap);
AddAssert("dummy shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap);
- AddStep("import test maps", () =>
- {
- for (int i = 0; i < 100; i += 10)
- manager.Import(createTestBeatmapSet(i));
- });
-
+ addManyTestMaps();
AddWaitStep(3);
+
AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap);
+ }
- loadNewSongSelect();
+ [Test]
+ public void TestSorting()
+ {
+ addManyTestMaps();
AddWaitStep(3);
+
AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap);
AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
@@ -114,55 +129,115 @@ namespace osu.Game.Tests.Visual
AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
}
- private BeatmapSetInfo createTestBeatmapSet(int i)
+ [Test]
+ [Ignore("needs fixing")]
+ public void TestImportUnderDifferentRuleset()
{
+ changeRuleset(2);
+ importForRuleset(0);
+ AddUntilStep(() => songSelect.Carousel.SelectedBeatmap == null, "no selection");
+ }
+
+ [Test]
+ public void TestImportUnderCurrentRuleset()
+ {
+ changeRuleset(2);
+ importForRuleset(2);
+ importForRuleset(1);
+ AddUntilStep(() => songSelect.Carousel.SelectedBeatmap.RulesetID == 2, "has selection");
+
+ changeRuleset(1);
+ AddUntilStep(() => songSelect.Carousel.SelectedBeatmap.RulesetID == 1, "has selection");
+
+ changeRuleset(0);
+ AddUntilStep(() => songSelect.Carousel.SelectedBeatmap == null, "no selection");
+ }
+
+ [Test]
+ public void TestRulesetChangeResetsMods()
+ {
+ changeRuleset(0);
+
+ changeMods(new OsuModHardRock());
+
+ int actionIndex = 0;
+ int modChangeIndex = 0;
+ int rulesetChangeIndex = 0;
+
+ AddStep("change ruleset", () =>
+ {
+ songSelect.CurrentBeatmap.Mods.ValueChanged += onModChange;
+ songSelect.Ruleset.ValueChanged += onRulesetChange;
+
+ Ruleset.Value = new TaikoRuleset().RulesetInfo;
+
+ songSelect.CurrentBeatmap.Mods.ValueChanged -= onModChange;
+ songSelect.Ruleset.ValueChanged -= onRulesetChange;
+ });
+
+ AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex);
+ AddAssert("empty mods", () => !selectedMods.Value.Any());
+
+ void onModChange(IEnumerable mods) => modChangeIndex = actionIndex++;
+ void onRulesetChange(RulesetInfo ruleset) => rulesetChangeIndex = actionIndex--;
+ }
+
+ private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())));
+
+ private static int importId;
+ private int getImportId() => ++importId;
+
+ private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.ShortenedName))}", () => selectedMods.Value = mods);
+
+ private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));
+
+ private void addManyTestMaps()
+ {
+ AddStep("import test maps", () =>
+ {
+ var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
+
+ for (int i = 0; i < 100; i += 10)
+ manager.Import(createTestBeatmapSet(i, usableRulesets));
+ });
+ }
+
+ private BeatmapSetInfo createTestBeatmapSet(int setId, RulesetInfo[] rulesets)
+ {
+ int j = 0;
+ RulesetInfo getRuleset() => rulesets[j++ % rulesets.Length];
+
+ var beatmaps = new List();
+
+ for (int i = 0; i < 6; i++)
+ {
+ int beatmapId = setId * 10 + i;
+
+ beatmaps.Add(new BeatmapInfo
+ {
+ Ruleset = getRuleset(),
+ OnlineBeatmapID = beatmapId,
+ Path = "normal.osu",
+ Version = $"{beatmapId}",
+ BaseDifficulty = new BeatmapDifficulty
+ {
+ OverallDifficulty = 3.5f,
+ }
+ });
+ }
+
return new BeatmapSetInfo
{
- OnlineBeatmapSetID = 1234 + i,
+ OnlineBeatmapSetID = setId,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
- Artist = "MONACA " + RNG.Next(0, 9),
- Title = "Black Song " + RNG.Next(0, 9),
+ Artist = "Some Artist " + RNG.Next(0, 9),
+ Title = $"Some Song (set id {setId})",
AuthorString = "Some Guy " + RNG.Next(0, 9),
},
- Beatmaps = new List(new[]
- {
- new BeatmapInfo
- {
- OnlineBeatmapID = 1234 + i,
- Ruleset = rulesets.AvailableRulesets.First(),
- Path = "normal.osu",
- Version = "Normal",
- BaseDifficulty = new BeatmapDifficulty
- {
- OverallDifficulty = 3.5f,
- }
- },
- new BeatmapInfo
- {
- OnlineBeatmapID = 1235 + i,
- Ruleset = rulesets.AvailableRulesets.First(),
- Path = "hard.osu",
- Version = "Hard",
- BaseDifficulty = new BeatmapDifficulty
- {
- OverallDifficulty = 5,
- }
- },
- new BeatmapInfo
- {
- OnlineBeatmapID = 1236 + i,
- Ruleset = rulesets.AvailableRulesets.First(),
- Path = "insane.osu",
- Version = "Insane",
- BaseDifficulty = new BeatmapDifficulty
- {
- OverallDifficulty = 7,
- }
- },
- }),
+ Beatmaps = beatmaps
};
}
}
diff --git a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs
index 52a9db080d..de839a21af 100644
--- a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs
@@ -1,25 +1,61 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Threading;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
- public class TestCasePlayerLoader : OsuTestCase
+ public class TestCasePlayerLoader : ManualInputManagerTestCase
{
+ private PlayerLoader loader;
+
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
Beatmap.Value = new DummyWorkingBeatmap(game);
- AddStep("load dummy beatmap", () => Add(new PlayerLoader(new Player
+ AddStep("load dummy beatmap", () => Add(loader = new PlayerLoader(new Player
{
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
})));
+
+ AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
+
+ AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current");
+
+ AddStep("load slow dummy beatmap", () =>
+ {
+ SlowLoadPlayer slow;
+
+ Add(loader = new PlayerLoader(slow = new SlowLoadPlayer
+ {
+ AllowPause = false,
+ AllowLeadIn = false,
+ AllowResults = false,
+ }));
+
+ Scheduler.AddDelayed(() => slow.Ready = true, 5000);
+ });
+
+ AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current");
+ }
+
+ protected class SlowLoadPlayer : Player
+ {
+ public bool Ready;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ while (!Ready)
+ Thread.Sleep(1);
+ }
}
}
+
}
diff --git a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs
index d711d501fe..e4cb848d90 100644
--- a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs
+++ b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs
@@ -14,9 +14,9 @@ namespace osu.Game.Tests.Visual
{
private readonly PreviewTrackManager trackManager = new TestPreviewTrackManager();
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs(trackManager);
dependencies.CacheAs(this);
return dependencies;
@@ -101,9 +101,9 @@ namespace osu.Game.Tests.Visual
AddInternal(track);
}
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs(this);
return dependencies;
}
diff --git a/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs b/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs
index 83bbbfddd1..b70117a3ad 100644
--- a/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs
+++ b/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs
@@ -54,11 +54,11 @@ namespace osu.Game.Tests.Visual
breadcrumbs.Current.TriggerChange();
- assertCurrent();
+ waitForCurrent();
pushNext();
- assertCurrent();
+ waitForCurrent();
pushNext();
- assertCurrent();
+ waitForCurrent();
AddStep(@"make start current", () =>
{
@@ -66,8 +66,9 @@ namespace osu.Game.Tests.Visual
currentScreen = startScreen;
});
- assertCurrent();
+ waitForCurrent();
pushNext();
+ waitForCurrent();
AddAssert(@"only 2 items", () => breadcrumbs.Items.Count() == 2);
AddStep(@"exit current", () => changedScreen.Exit());
AddAssert(@"current screen is first", () => startScreen == changedScreen);
@@ -80,7 +81,7 @@ namespace osu.Game.Tests.Visual
}
private void pushNext() => AddStep(@"push next screen", () => currentScreen = ((TestScreen)currentScreen).PushNext());
- private void assertCurrent() => AddAssert(@"changedScreen correct", () => currentScreen == changedScreen);
+ private void waitForCurrent() => AddUntilStep(() => currentScreen.IsCurrentScreen, "current screen");
private abstract class TestScreen : OsuScreen
{
diff --git a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
index 9b48ec17bd..bbc9d2b860 100644
--- a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
+++ b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
@@ -117,7 +117,6 @@ namespace osu.Game.Tests.Visual
public new readonly ScrollingDirection Direction;
public TestPlayfield(ScrollingDirection direction)
- : base(direction)
{
Direction = direction;
diff --git a/osu.Game.Tests/Visual/TestCaseToolbar.cs b/osu.Game.Tests/Visual/TestCaseToolbar.cs
index fd218af054..96f14e6b32 100644
--- a/osu.Game.Tests/Visual/TestCaseToolbar.cs
+++ b/osu.Game.Tests/Visual/TestCaseToolbar.cs
@@ -16,8 +16,8 @@ namespace osu.Game.Tests.Visual
public override IReadOnlyList RequiredTypes => new[]
{
typeof(ToolbarButton),
- typeof(ToolbarModeSelector),
- typeof(ToolbarModeButton),
+ typeof(ToolbarRulesetSelector),
+ typeof(ToolbarRulesetButton),
typeof(ToolbarNotificationButton),
};
diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
index aca832110a..cb281d045b 100644
--- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs
+++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
@@ -53,7 +53,6 @@ namespace osu.Game.Tests.Visual
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
JoinDate = DateTimeOffset.Now.AddDays(-1),
LastVisit = DateTimeOffset.Now,
- Age = 1,
ProfileOrder = new[] { "me" },
Statistics = new UserStatistics
{
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 532915100b..d638af0c38 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -1,8 +1,14 @@
+
+
+
+
+
+
WinExe
- netcoreapp2.1;net471
+ netcoreapp2.1
diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs
index 7e329ac651..4345d09e05 100644
--- a/osu.Game/Audio/SampleInfo.cs
+++ b/osu.Game/Audio/SampleInfo.cs
@@ -29,6 +29,11 @@ namespace osu.Game.Audio
///
public string Name;
+ ///
+ /// An optional suffix to provide priority lookup. Falls back to non-suffixed .
+ ///
+ public string Suffix;
+
///
/// The sample volume.
///
@@ -42,9 +47,16 @@ namespace osu.Game.Audio
get
{
if (!string.IsNullOrEmpty(Namespace))
+ {
+ if (!string.IsNullOrEmpty(Suffix))
+ yield return $"{Namespace}/{Bank}-{Name}{Suffix}";
yield return $"{Namespace}/{Bank}-{Name}";
+ }
- yield return $"{Bank}-{Name}"; // Without namespace as a fallback even when we have a namespace
+ // check non-namespace as a fallback even when we have a namespace
+ if (!string.IsNullOrEmpty(Suffix))
+ yield return $"{Bank}-{Name}{Suffix}";
+ yield return $"{Bank}-{Name}";
}
}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index e488dacf80..1a2b5eadd9 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -45,6 +45,16 @@ namespace osu.Game.Beatmaps
///
public event Action BeatmapDownloadBegan;
+ ///
+ /// Fired when a beatmap download is interrupted, due to user cancellation or other failures.
+ ///
+ public event Action BeatmapDownloadFailed;
+
+ ///
+ /// Fired when a beatmap load is requested (into the interactive game UI).
+ ///
+ public Action PresentBeatmap;
+
///
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
///
@@ -79,33 +89,46 @@ namespace osu.Game.Beatmaps
this.audioManager = audioManager;
}
- protected override void Populate(BeatmapSetInfo model, ArchiveReader archive)
+ protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive)
{
- model.Beatmaps = createBeatmapDifficulties(archive);
+ if (archive != null)
+ beatmapSet.Beatmaps = createBeatmapDifficulties(archive);
- foreach (BeatmapInfo b in model.Beatmaps)
+ foreach (BeatmapInfo b in beatmapSet.Beatmaps)
{
// remove metadata from difficulties where it matches the set
- if (model.Metadata.Equals(b.Metadata))
+ if (beatmapSet.Metadata.Equals(b.Metadata))
b.Metadata = null;
- // by setting the model here, we can update the noline set id below.
- b.BeatmapSet = model;
-
- fetchAndPopulateOnlineIDs(b);
+ b.BeatmapSet = beatmapSet;
}
// check if a set already exists with the same online id, delete if it does.
- if (model.OnlineBeatmapSetID != null)
+ if (beatmapSet.OnlineBeatmapSetID != null)
{
- var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID);
+ var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID);
if (existingOnlineId != null)
{
Delete(existingOnlineId);
beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID);
- Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({model.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database);
+ Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database);
}
}
+
+ validateOnlineIds(beatmapSet.Beatmaps);
+
+ foreach (BeatmapInfo b in beatmapSet.Beatmaps)
+ fetchAndPopulateOnlineIDs(b, beatmapSet.Beatmaps);
+ }
+
+ private void validateOnlineIds(List beatmaps)
+ {
+ var beatmapIds = beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList();
+
+ // ensure all IDs are unique in this set and none match existing IDs in the local beatmap store.
+ if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1) || QueryBeatmaps(b => beatmapIds.Contains(b.OnlineBeatmapID)).Any())
+ // remove all online IDs if any problems were found.
+ beatmaps.ForEach(b => b.OnlineBeatmapID = null);
}
protected override BeatmapSetInfo CheckForExisting(BeatmapSetInfo model)
@@ -143,7 +166,7 @@ namespace osu.Game.Beatmaps
return;
}
- var downloadNotification = new ProgressNotification
+ var downloadNotification = new DownloadNotification
{
CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!",
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
@@ -163,18 +186,28 @@ namespace osu.Game.Beatmaps
Task.Factory.StartNew(() =>
{
+ BeatmapSetInfo importedBeatmap;
+
// This gets scheduled back to the update thread, but we want the import to run in the background.
using (var stream = new MemoryStream(data))
using (var archive = new ZipArchiveReader(stream, beatmapSetInfo.ToString()))
- Import(archive);
+ importedBeatmap = Import(archive);
+ downloadNotification.CompletionClickAction = () =>
+ {
+ PresentBeatmap?.Invoke(importedBeatmap);
+ return true;
+ };
downloadNotification.State = ProgressNotificationState.Completed;
+
currentDownloads.Remove(request);
}, TaskCreationOptions.LongRunning);
};
request.Failure += error =>
{
+ BeatmapDownloadFailed?.Invoke(request);
+
if (error is OperationCanceledException) return;
downloadNotification.State = ProgressNotificationState.Cancelled;
@@ -276,7 +309,7 @@ namespace osu.Game.Beatmaps
///
/// The query.
/// Results from the provided query.
- public IEnumerable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
+ public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
///
/// Denotes whether an osu-stable installation is present to perform automated imports from.
@@ -296,7 +329,7 @@ namespace osu.Game.Beatmaps
return;
}
- await Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs")), TaskCreationOptions.LongRunning);
+ await Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs").Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning);
}
///
@@ -317,7 +350,12 @@ namespace osu.Game.Beatmaps
{
// 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 this beatmap archive.");
+ if (string.IsNullOrEmpty(mapName))
+ {
+ // Todo: This is temporary for debugging purposes
+ var files = reader.Filenames.ToList();
+ throw new InvalidOperationException($"No beatmap files found in this beatmap archive. Files ({files.Count}): {string.Join(", ", files)}");
+ }
Beatmap beatmap;
using (var stream = new StreamReader(reader.GetStream(mapName)))
@@ -355,21 +393,10 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
- // check that no existing beatmap exists that is imported with the same online beatmap ID. if so, give it precedence.
- if (beatmap.BeatmapInfo.OnlineBeatmapID.HasValue && QueryBeatmap(b => b.OnlineBeatmapID.Value == beatmap.BeatmapInfo.OnlineBeatmapID.Value) != null)
- beatmap.BeatmapInfo.OnlineBeatmapID = null;
-
- RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
-
+ var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
beatmap.BeatmapInfo.Ruleset = ruleset;
-
- if (ruleset != null)
- {
- // TODO: this should be done in a better place once we actually need to dynamically update it.
- beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating;
- }
- else
- beatmap.BeatmapInfo.StarDifficulty = 0;
+ // TODO: this should be done in a better place once we actually need to dynamically update it.
+ beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0;
beatmapInfos.Add(beatmap.BeatmapInfo);
}
@@ -382,16 +409,17 @@ namespace osu.Game.Beatmaps
/// Query the API to populate mising OnlineBeatmapID / OnlineBeatmapSetID properties.
///
/// The beatmap to populate.
+ /// The other beatmaps contained within this set.
/// Whether to re-query if the provided beatmap already has populated values.
/// True if population was successful.
- private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, bool force = false)
+ private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable otherBeatmaps, bool force = false)
{
+ if (api?.State != APIState.Online)
+ return false;
+
if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null)
return true;
- if (api.State != APIState.Online)
- return false;
-
Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database);
try
@@ -404,6 +432,12 @@ namespace osu.Game.Beatmaps
Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database);
+ if (otherBeatmaps.Any(b => b.OnlineBeatmapID == res.OnlineBeatmapID))
+ {
+ Logger.Log("Another beatmap in the same set already mapped to this ID. We'll skip adding it this time.", LoggingTarget.Database);
+ return false;
+ }
+
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
return true;
@@ -432,5 +466,21 @@ namespace osu.Game.Beatmaps
protected override Texture GetBackground() => null;
protected override Track GetTrack() => null;
}
+
+ private class DownloadNotification : ProgressNotification
+ {
+ public override bool IsImportant => false;
+
+ protected override Notification CreateCompletionNotification() => new SilencedProgressCompletionNotification
+ {
+ Activated = CompletionClickAction,
+ Text = CompletionText
+ };
+
+ private class SilencedProgressCompletionNotification : ProgressCompletionNotification
+ {
+ public override bool IsImportant => false;
+ }
+ }
}
}
diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs
index 6c1bcd0531..57983ec568 100644
--- a/osu.Game/Beatmaps/BeatmapMetadata.cs
+++ b/osu.Game/Beatmaps/BeatmapMetadata.cs
@@ -14,7 +14,6 @@ namespace osu.Game.Beatmaps
public class BeatmapMetadata : IEquatable
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- [JsonIgnore]
public int ID { get; set; }
public string Title { get; set; }
diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs
index 0173125e8b..9d7cd673dc 100644
--- a/osu.Game/Beatmaps/BeatmapProcessor.cs
+++ b/osu.Game/Beatmaps/BeatmapProcessor.cs
@@ -27,11 +27,10 @@ namespace osu.Game.Beatmaps
if (obj.NewCombo)
{
obj.IndexInCurrentCombo = 0;
+ obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + obj.ComboOffset + 1;
+
if (lastObj != null)
- {
lastObj.LastInCombo = true;
- obj.ComboIndex = lastObj.ComboIndex + 1;
- }
}
else if (lastObj != null)
{
diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
index acccbcde46..18680f9985 100644
--- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
@@ -40,7 +40,6 @@ namespace osu.Game.Beatmaps.ControlPoints
{
var newSampleInfo = sampleInfo.Clone();
newSampleInfo.Bank = sampleInfo.Bank ?? SampleBank;
- newSampleInfo.Name = sampleInfo.Name;
newSampleInfo.Volume = sampleInfo.Volume > 0 ? sampleInfo.Volume : SampleVolume;
return newSampleInfo;
}
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs
index 6acb58e165..6f4d4c0d6f 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs
@@ -5,6 +5,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
+using osu.Game.Online.API.Requests;
namespace osu.Game.Beatmaps.Drawables
{
@@ -19,9 +20,9 @@ namespace osu.Game.Beatmaps.Drawables
private BeatmapManager beatmaps;
///
- /// Whether the associated beatmap set has been downloading (by this instance or any other instance).
+ /// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded.
///
- public readonly BindableBool Downloaded = new BindableBool();
+ public readonly Bindable DownloadState = new Bindable();
public BeatmapSetDownloader(BeatmapSetInfo set, bool noVideo = false)
{
@@ -36,10 +37,16 @@ namespace osu.Game.Beatmaps.Drawables
beatmaps.ItemAdded += setAdded;
beatmaps.ItemRemoved += setRemoved;
+ beatmaps.BeatmapDownloadBegan += downloadBegan;
+ beatmaps.BeatmapDownloadFailed += downloadFailed;
// initial value
- if (set.OnlineBeatmapSetID != null)
- Downloaded.Value = beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any();
+ if (set.OnlineBeatmapSetID != null && beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any())
+ DownloadState.Value = DownloadStatus.Downloaded;
+ else if (beatmaps.GetExistingDownload(set) != null)
+ DownloadState.Value = DownloadStatus.Downloading;
+ else
+ DownloadState.Value = DownloadStatus.NotDownloaded;
}
protected override void Dispose(bool isDisposing)
@@ -50,6 +57,8 @@ namespace osu.Game.Beatmaps.Drawables
{
beatmaps.ItemAdded -= setAdded;
beatmaps.ItemRemoved -= setRemoved;
+ beatmaps.BeatmapDownloadBegan -= downloadBegan;
+ beatmaps.BeatmapDownloadFailed -= downloadFailed;
}
}
@@ -57,28 +66,45 @@ namespace osu.Game.Beatmaps.Drawables
/// Begin downloading the associated beatmap set.
///
/// True if downloading began. False if an existing download is active or completed.
- public bool Download()
+ public void Download()
{
- if (Downloaded.Value)
- return false;
-
- if (beatmaps.GetExistingDownload(set) != null)
- return false;
+ if (DownloadState.Value > DownloadStatus.NotDownloaded)
+ return;
beatmaps.Download(set, noVideo);
- return true;
+
+ DownloadState.Value = DownloadStatus.Downloading;
}
private void setAdded(BeatmapSetInfo s)
{
if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
- Downloaded.Value = true;
+ DownloadState.Value = DownloadStatus.Downloaded;
}
private void setRemoved(BeatmapSetInfo s)
{
if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
- Downloaded.Value = false;
+ DownloadState.Value = DownloadStatus.NotDownloaded;
+ }
+
+ private void downloadBegan(DownloadBeatmapSetRequest d)
+ {
+ if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
+ DownloadState.Value = DownloadStatus.Downloading;
+ }
+
+ private void downloadFailed(DownloadBeatmapSetRequest d)
+ {
+ if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
+ DownloadState.Value = DownloadStatus.NotDownloaded;
+ }
+
+ public enum DownloadStatus
+ {
+ NotDownloaded,
+ Downloading,
+ Downloaded,
}
}
}
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
index 24e6021421..c7e97cef55 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Beatmaps.Drawables
if (value == status) return;
status = value;
- statusText.Text = Enum.GetName(typeof(BeatmapSetOnlineStatus), Status)?.ToUpper();
+ statusText.Text = Enum.GetName(typeof(BeatmapSetOnlineStatus), Status)?.ToUpperInvariant();
}
}
diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs
index 025ab0037f..7d00c35862 100644
--- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs
+++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs
@@ -9,14 +9,14 @@ using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
- public class DifficultyColouredContainer : Container, IHasAccentColour
+ public abstract class DifficultyColouredContainer : Container, IHasAccentColour
{
public Color4 AccentColour { get; set; }
private readonly BeatmapInfo beatmap;
private OsuColour palette;
- public DifficultyColouredContainer(BeatmapInfo beatmap)
+ protected DifficultyColouredContainer(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
}
diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
index 42c98aef24..24604711d4 100644
--- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
+++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
@@ -3,10 +3,14 @@
using System;
using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using OpenTK;
+using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
@@ -14,7 +18,8 @@ namespace osu.Game.Beatmaps.Drawables
{
private readonly BeatmapInfo beatmap;
- public DifficultyIcon(BeatmapInfo beatmap) : base(beatmap)
+ public DifficultyIcon(BeatmapInfo beatmap)
+ : base(beatmap)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
@@ -28,16 +33,29 @@ namespace osu.Game.Beatmaps.Drawables
{
Children = new Drawable[]
{
- new SpriteIcon
+ new CircularContainer
{
+ RelativeSizeAxes = Axes.Both,
+ Scale = new Vector2(0.84f),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- Colour = AccentColour,
- Icon = FontAwesome.fa_circle
+ Masking = true,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Colour = Color4.Black.Opacity(0.08f),
+ Type = EdgeEffectType.Shadow,
+ Radius = 5,
+ },
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = AccentColour,
+ },
},
new ConstrainedIconContainer
{
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
// the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment)
Icon = beatmap.Ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.fa_question_circle_o }
diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs
index 2927654f62..6f45718390 100644
--- a/osu.Game/Beatmaps/Formats/Decoder.cs
+++ b/osu.Game/Beatmaps/Formats/Decoder.cs
@@ -55,11 +55,11 @@ namespace osu.Game.Beatmaps.Formats
} while (line != null && line.Length == 0);
if (line == null)
- throw new IOException(@"Unknown file format");
+ throw new IOException(@"Unknown file format (null)");
- var decoder = typedDecoders.Select(d => line.StartsWith(d.Key) ? d.Value : null).FirstOrDefault();
+ var decoder = typedDecoders.Select(d => line.StartsWith(d.Key, StringComparison.InvariantCulture) ? d.Value : null).FirstOrDefault();
if (decoder == null)
- throw new IOException(@"Unknown file format");
+ throw new IOException($@"Unknown file format ({line})");
return (Decoder)decoder.Invoke(line);
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index c79938e613..181d17932d 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -62,28 +62,30 @@ namespace osu.Game.Beatmaps.Formats
protected override void ParseLine(Beatmap beatmap, Section section, string line)
{
+ var strippedLine = StripComments(line);
+
switch (section)
{
case Section.General:
- handleGeneral(line);
+ handleGeneral(strippedLine);
return;
case Section.Editor:
- handleEditor(line);
+ handleEditor(strippedLine);
return;
case Section.Metadata:
handleMetadata(line);
return;
case Section.Difficulty:
- handleDifficulty(line);
+ handleDifficulty(strippedLine);
return;
case Section.Events:
- handleEvent(line);
+ handleEvent(strippedLine);
return;
case Section.TimingPoints:
- handleTimingPoint(line);
+ handleTimingPoint(strippedLine);
return;
case Section.HitObjects:
- handleHitObject(line);
+ handleHitObject(strippedLine);
return;
}
@@ -124,16 +126,16 @@ namespace osu.Game.Beatmaps.Formats
switch (beatmap.BeatmapInfo.RulesetID)
{
case 0:
- parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
+ parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
break;
case 1:
- parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser();
+ parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
break;
case 2:
- parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser();
+ parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
break;
case 3:
- parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser();
+ parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
break;
}
@@ -305,12 +307,12 @@ namespace osu.Game.Beatmaps.Formats
bool omitFirstBarSignature = false;
if (split.Length >= 8)
{
- int effectFlags = int.Parse(split[7]);
- kiaiMode = (effectFlags & 1) > 0;
- omitFirstBarSignature = (effectFlags & 8) > 0;
+ EffectFlags effectFlags = (EffectFlags)int.Parse(split[7]);
+ kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai);
+ omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine);
}
- string stringSampleSet = sampleSet.ToString().ToLower();
+ string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
if (stringSampleSet == @"none")
stringSampleSet = @"normal";
@@ -352,6 +354,11 @@ namespace osu.Game.Beatmaps.Formats
private void handleTimingControlPoint(TimingControlPoint newPoint)
{
+ var existing = beatmap.ControlPointInfo.TimingPointAt(newPoint.Time);
+
+ if (existing.Time == newPoint.Time)
+ beatmap.ControlPointInfo.TimingPoints.Remove(existing);
+
beatmap.ControlPointInfo.TimingPoints.Add(newPoint);
}
@@ -362,7 +369,9 @@ namespace osu.Game.Beatmaps.Formats
if (newPoint.EquivalentTo(existing))
return;
- beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == newPoint.Time);
+ if (existing.Time == newPoint.Time)
+ beatmap.ControlPointInfo.DifficultyPoints.Remove(existing);
+
beatmap.ControlPointInfo.DifficultyPoints.Add(newPoint);
}
@@ -373,6 +382,9 @@ namespace osu.Game.Beatmaps.Formats
if (newPoint.EquivalentTo(existing))
return;
+ if (existing.Time == newPoint.Time)
+ beatmap.ControlPointInfo.EffectPoints.Remove(existing);
+
beatmap.ControlPointInfo.EffectPoints.Add(newPoint);
}
@@ -383,6 +395,9 @@ namespace osu.Game.Beatmaps.Formats
if (newPoint.EquivalentTo(existing))
return;
+ if (existing.Time == newPoint.Time)
+ beatmap.ControlPointInfo.SamplePoints.Remove(existing);
+
beatmap.ControlPointInfo.SamplePoints.Add(newPoint);
}
@@ -390,14 +405,11 @@ namespace osu.Game.Beatmaps.Formats
{
// If the ruleset wasn't specified, assume the osu!standard ruleset.
if (parser == null)
- parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
-
- var obj = parser.Parse(line, getOffsetTime());
+ parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
+ var obj = parser.Parse(line);
if (obj != null)
- {
beatmap.HitObjects.Add(obj);
- }
}
private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0);
@@ -405,5 +417,13 @@ namespace osu.Game.Beatmaps.Formats
private double getOffsetTime() => ApplyOffsets ? offset : 0;
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
+
+ [Flags]
+ internal enum EffectFlags
+ {
+ None = 0,
+ Kiai = 1,
+ OmitFirstBarLine = 8
+ }
}
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 22a6acf459..e9f37e583b 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Beatmaps.Formats
if (ShouldSkipLine(line))
continue;
- if (line.StartsWith(@"[") && line.EndsWith(@"]"))
+ if (line.StartsWith(@"[", StringComparison.Ordinal) && line.EndsWith(@"]", StringComparison.Ordinal))
{
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
{
@@ -53,10 +53,12 @@ namespace osu.Game.Beatmaps.Formats
}
}
- protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//");
+ protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//", StringComparison.Ordinal);
protected virtual void ParseLine(T output, Section section, string line)
{
+ line = StripComments(line);
+
switch (section)
{
case Section.Colours:
@@ -65,6 +67,14 @@ namespace osu.Game.Beatmaps.Formats
}
}
+ protected string StripComments(string line)
+ {
+ var index = line.IndexOf("//", StringComparison.Ordinal);
+ if (index > 0)
+ return line.Substring(0, index);
+ return line;
+ }
+
private bool hasComboColours;
private void handleColours(T output, string line)
@@ -178,8 +188,8 @@ namespace osu.Game.Beatmaps.Formats
{
var baseInfo = base.ApplyTo(sampleInfo);
- if (CustomSampleBank > 1)
- baseInfo.Name += CustomSampleBank;
+ if (string.IsNullOrEmpty(baseInfo.Suffix) && CustomSampleBank > 1)
+ baseInfo.Suffix = CustomSampleBank.ToString();
return baseInfo;
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index 868ae5206a..a73a32325a 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -42,6 +42,8 @@ namespace osu.Game.Beatmaps.Formats
protected override void ParseLine(Storyboard storyboard, Section section, string line)
{
+ line = StripComments(line);
+
switch (section)
{
case Section.Events:
@@ -58,7 +60,7 @@ namespace osu.Game.Beatmaps.Formats
private void handleEvents(string line)
{
var depth = 0;
- while (line.StartsWith(" ") || line.StartsWith("_"))
+ while (line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal))
{
++depth;
line = line.Substring(1);
@@ -267,9 +269,9 @@ namespace osu.Game.Beatmaps.Formats
return Anchor.BottomCentre;
case LegacyOrigins.BottomRight:
return Anchor.BottomRight;
+ default:
+ return Anchor.TopLeft;
}
-
- throw new InvalidDataException($@"Unknown origin: {value}");
}
private void handleVariables(string line)
@@ -287,15 +289,10 @@ namespace osu.Game.Beatmaps.Formats
while (line.IndexOf('$') >= 0)
{
string origLine = line;
- string[] split = line.Split(',');
- for (int i = 0; i < split.Length; i++)
- {
- var item = split[i];
- if (item.StartsWith("$") && variables.ContainsKey(item))
- split[i] = variables[item];
- }
- line = string.Join(",", split);
+ foreach (var v in variables)
+ line = line.Replace(v.Key, v.Value);
+
if (line == origLine)
break;
}
diff --git a/osu.Game/Beatmaps/IBeatmapConverter.cs b/osu.Game/Beatmaps/IBeatmapConverter.cs
index cbf9d184ac..a710afad16 100644
--- a/osu.Game/Beatmaps/IBeatmapConverter.cs
+++ b/osu.Game/Beatmaps/IBeatmapConverter.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Beatmaps
diff --git a/osu.Game/Beatmaps/IBeatmapProcessor.cs b/osu.Game/Beatmaps/IBeatmapProcessor.cs
index 282662373a..4d634d7912 100644
--- a/osu.Game/Beatmaps/IBeatmapProcessor.cs
+++ b/osu.Game/Beatmaps/IBeatmapProcessor.cs
@@ -1,6 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Objects;
+
namespace osu.Game.Beatmaps
{
///
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 74da978d9c..6c906bb1e4 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -14,6 +14,7 @@ using osu.Framework.IO.File;
using System.IO;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
@@ -133,6 +134,8 @@ namespace osu.Game.Beatmaps
return converted;
}
+ public override string ToString() => BeatmapInfo.ToString();
+
public bool BackgroundLoaded => background.IsResultAvailable;
public Texture Background => background.Value.Result;
public async Task GetBackgroundAsync() => await background.Value;
diff --git a/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs b/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs
index 0e0a9a3bb9..d2e6ac1db8 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs
@@ -3,6 +3,7 @@
using System.Linq;
using osu.Framework.Audio.Track;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Beatmaps
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index cbf0df3227..ac79a8f565 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using osu.Framework.IO.File;
using osu.Framework.Logging;
@@ -175,29 +176,46 @@ namespace osu.Game.Database
/// The archive to be imported.
public TModel Import(ArchiveReader archive)
{
- TModel item = null;
+ try
+ {
+ return Import(CreateModel(archive), archive);
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e, $"Model creation of {archive.Name} failed.", LoggingTarget.Database);
+ return null;
+ }
+ }
+
+ ///
+ /// Import an item from a .
+ ///
+ /// The model to be imported.
+ /// An optional archive to use for model population.
+ public TModel Import(TModel item, ArchiveReader archive = null)
+ {
delayEvents();
try
{
+ Logger.Log($"Importing {item}...", LoggingTarget.Database);
+
using (var write = ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes.
{
try
{
if (!write.IsTransactionLeader) throw new InvalidOperationException($"Ensure there is no parent transaction so errors can correctly be handled by {this}");
- // create a new model (don't yet add to database)
- item = CreateModel(archive);
-
var existing = CheckForExisting(item);
if (existing != null)
{
- Logger.Log($"Found existing {typeof(TModel)} for {archive.Name} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
+ Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
return existing;
}
- item.Files = createFileInfos(archive, Files);
+ if (archive != null)
+ item.Files = createFileInfos(archive, Files);
Populate(item, archive);
@@ -211,11 +229,11 @@ namespace osu.Game.Database
}
}
- Logger.Log($"Import of {archive.Name} successfully completed!", LoggingTarget.Database);
+ Logger.Log($"Import of {item} successfully completed!", LoggingTarget.Database);
}
catch (Exception e)
{
- Logger.Error(e, $"Import of {archive.Name} failed and has been rolled back.", LoggingTarget.Database);
+ Logger.Error(e, $"Import of {item} failed and has been rolled back.", LoggingTarget.Database);
item = null;
}
finally
@@ -227,12 +245,6 @@ namespace osu.Game.Database
return item;
}
- ///
- /// Import an item from a .
- ///
- /// The model to be imported.
- public void Import(TModel item) => ModelStore.Add(item);
-
///
/// Perform an update of the specified item.
/// TODO: Support file changes.
@@ -385,8 +397,8 @@ namespace osu.Game.Database
/// After this method, the model should be in a state ready to commit to a store.
///
/// The model to populate.
- /// The archive to use as a reference for population.
- protected virtual void Populate(TModel model, ArchiveReader archive)
+ /// The archive to use as a reference for population. May be null.
+ protected virtual void Populate(TModel model, [CanBeNull] ArchiveReader archive)
{
}
@@ -402,7 +414,7 @@ namespace osu.Game.Database
private ArchiveReader getReaderFrom(string path)
{
if (ZipUtils.IsZipArchive(path))
- return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path));
+ return new ZipArchiveReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read), Path.GetFileName(path));
if (Directory.Exists(path))
return new LegacyFilesystemReader(path);
throw new InvalidFormatException($"{path} is not a valid archive");
diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs
index 5160239c38..2037612a09 100644
--- a/osu.Game/Database/DatabaseContextFactory.cs
+++ b/osu.Game/Database/DatabaseContextFactory.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Database
{
public class DatabaseContextFactory : IDatabaseContextFactory
{
- private readonly GameHost host;
+ private readonly Storage storage;
private const string database_name = @"client";
@@ -26,9 +26,9 @@ namespace osu.Game.Database
private IDbContextTransaction currentWriteTransaction;
- public DatabaseContextFactory(GameHost host)
+ public DatabaseContextFactory(Storage storage)
{
- this.host = host;
+ this.storage = storage;
recycleThreadContexts();
}
@@ -115,9 +115,15 @@ namespace osu.Game.Database
}
}
- private void recycleThreadContexts() => threadContexts = new ThreadLocal(CreateContext);
+ private void recycleThreadContexts()
+ {
+ // Contexts for other threads are not disposed as they may be in use elsewhere. Instead, fresh contexts are exposed
+ // for other threads to use, and we rely on the finalizer inside OsuDbContext to handle their previous contexts
+ threadContexts?.Value.Dispose();
+ threadContexts = new ThreadLocal(CreateContext, true);
+ }
- protected virtual OsuDbContext CreateContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(database_name))
+ protected virtual OsuDbContext CreateContext() => new OsuDbContext(storage.GetDatabaseConnectionString(database_name))
{
Database = { AutoTransactionsEnabled = false }
};
@@ -127,9 +133,7 @@ namespace osu.Game.Database
lock (writeLock)
{
recycleThreadContexts();
- GC.Collect();
- GC.WaitForPendingFinalizers();
- host.Storage.DeleteDatabase(database_name);
+ storage.DeleteDatabase(database_name);
}
}
}
diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs
index bf57644caf..20e144c033 100644
--- a/osu.Game/Database/OsuDbContext.cs
+++ b/osu.Game/Database/OsuDbContext.cs
@@ -75,6 +75,13 @@ namespace osu.Game.Database
}
}
+ ~OsuDbContext()
+ {
+ // DbContext does not contain a finalizer (https://github.com/aspnet/EntityFrameworkCore/issues/8872)
+ // This is used to clean up previous contexts when fresh contexts are exposed via DatabaseContextFactory
+ Dispose();
+ }
+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
diff --git a/osu.Game/Database/SingletonContextFactory.cs b/osu.Game/Database/SingletonContextFactory.cs
deleted file mode 100644
index ce3fbf6881..0000000000
--- a/osu.Game/Database/SingletonContextFactory.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-namespace osu.Game.Database
-{
- public class SingletonContextFactory : IDatabaseContextFactory
- {
- private readonly OsuDbContext context;
-
- public SingletonContextFactory(OsuDbContext context)
- {
- this.context = context;
- }
-
- public OsuDbContext Get() => context;
-
- public DatabaseWriteUsage GetForWrite(bool withTransaction = true) => new DatabaseWriteUsage(context, null);
- }
-}
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index d6bc4a2095..d2ab8441eb 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -5,10 +5,10 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Input.Bindings;
+using osu.Framework.Input.States;
using osu.Game.Audio;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
@@ -22,13 +22,16 @@ namespace osu.Game.Graphics.Containers
protected virtual bool PlaySamplesOnStateChange => true;
+ protected override bool BlockPassThroughKeyboard => true;
+
private PreviewTrackManager previewTrackManager;
+
protected readonly Bindable OverlayActivationMode = new Bindable(OverlayActivation.All);
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs(this);
return dependencies;
}
@@ -69,10 +72,13 @@ namespace osu.Game.Graphics.Containers
public virtual bool OnPressed(GlobalAction action)
{
- if (action == GlobalAction.Back)
+ switch (action)
{
- State = Visibility.Hidden;
- return true;
+ case GlobalAction.Back:
+ State = Visibility.Hidden;
+ return true;
+ case GlobalAction.Select:
+ return true;
}
return false;
diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs
index e88dad93ef..12df19e7c0 100644
--- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs
@@ -6,7 +6,7 @@ using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
namespace osu.Game.Graphics.Containers
{
diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
index ebea9c49de..6d42be6fca 100644
--- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
@@ -2,7 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using OpenTK.Input;
namespace osu.Game.Graphics.Containers
diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs
index 517be4c055..b55e1aa5dd 100644
--- a/osu.Game/Graphics/Cursor/MenuCursor.cs
+++ b/osu.Game/Graphics/Cursor/MenuCursor.cs
@@ -8,11 +8,12 @@ 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.Game.Configuration;
using System;
using JetBrains.Annotations;
using osu.Framework.Graphics.Textures;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using OpenTK.Input;
namespace osu.Game.Graphics.Cursor
diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs
index c0e331148d..44156f6e83 100644
--- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs
+++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs
@@ -21,6 +21,8 @@ namespace osu.Game.Graphics.Cursor
{
}
+ protected override double AppearDelay => (1 - CurrentTooltip.Alpha) * base.AppearDelay; // reduce appear delay if the tooltip is already partly visible.
+
public class OsuTooltip : Tooltip
{
private readonly Box background;
diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs
index 90580c50df..bc30794298 100644
--- a/osu.Game/Graphics/ScreenshotManager.cs
+++ b/osu.Game/Graphics/ScreenshotManager.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
-using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -19,6 +18,7 @@ using osu.Game.Configuration;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
+using SixLabors.ImageSharp;
namespace osu.Game.Graphics
{
@@ -90,7 +90,7 @@ namespace osu.Game.Graphics
waitDelegate.Cancel();
}
- using (var bitmap = await host.TakeScreenshotAsync())
+ using (var image = await host.TakeScreenshotAsync())
{
Interlocked.Decrement(ref screenShotTasks);
@@ -102,10 +102,10 @@ namespace osu.Game.Graphics
switch (screenshotFormat.Value)
{
case ScreenshotFormat.Png:
- bitmap.Save(stream, ImageFormat.Png);
+ image.SaveAsPng(stream);
break;
case ScreenshotFormat.Jpg:
- bitmap.Save(stream, ImageFormat.Jpeg);
+ image.SaveAsJpeg(stream);
break;
default:
throw new ArgumentOutOfRangeException(nameof(screenshotFormat));
@@ -134,7 +134,7 @@ namespace osu.Game.Graphics
private string getFileName()
{
var dt = DateTime.Now;
- var fileExt = screenshotFormat.ToString().ToLower();
+ var fileExt = screenshotFormat.ToString().ToLowerInvariant();
var withoutIndex = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}.{fileExt}";
if (!storage.Exists(withoutIndex))
diff --git a/osu.Game/Graphics/UserInterface/BarGraph.cs b/osu.Game/Graphics/UserInterface/BarGraph.cs
index d065ecdd5f..0ba49929e9 100644
--- a/osu.Game/Graphics/UserInterface/BarGraph.cs
+++ b/osu.Game/Graphics/UserInterface/BarGraph.cs
@@ -26,10 +26,10 @@ namespace osu.Game.Graphics.UserInterface
set
{
direction = value;
- base.Direction = (direction & BarDirection.Horizontal) > 0 ? FillDirection.Vertical : FillDirection.Horizontal;
+ base.Direction = direction.HasFlag(BarDirection.Horizontal) ? FillDirection.Vertical : FillDirection.Horizontal;
foreach (var bar in Children)
{
- bar.Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1);
+ bar.Size = direction.HasFlag(BarDirection.Horizontal) ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1);
bar.Direction = direction;
}
}
@@ -56,14 +56,14 @@ namespace osu.Game.Graphics.UserInterface
if (bar.Bar != null)
{
bar.Bar.Length = length;
- bar.Bar.Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, size) : new Vector2(size, 1);
+ bar.Bar.Size = direction.HasFlag(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1);
}
else
{
Add(new Bar
{
RelativeSizeAxes = Axes.Both,
- Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, size) : new Vector2(size, 1),
+ Size = direction.HasFlag(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1),
Length = length,
Direction = Direction,
});
diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs
index 17d22587ad..ee2448ff02 100644
--- a/osu.Game/Graphics/UserInterface/DialogButton.cs
+++ b/osu.Game/Graphics/UserInterface/DialogButton.cs
@@ -13,7 +13,7 @@ using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Graphics.Containers;
using osu.Framework.Configuration;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{
diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
index be2412ccad..fe8995f310 100644
--- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
+++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Platform;
using OpenTK;
using OpenTK.Graphics;
diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
index 28d04c9a82..e4fd71e17e 100644
--- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
@@ -2,8 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
-using osu.Framework.Input;
using System;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Input.Bindings;
using OpenTK.Input;
diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
index 1a9d124dfb..27a06ba0b7 100644
--- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
+++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{
diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs
index dc35c75cd6..821305bc92 100644
--- a/osu.Game/Graphics/UserInterface/HoverSounds.cs
+++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs
@@ -8,7 +8,7 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{
diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs
index 5a25fe641d..be60812ba6 100644
--- a/osu.Game/Graphics/UserInterface/IconButton.cs
+++ b/osu.Game/Graphics/UserInterface/IconButton.cs
@@ -3,31 +3,17 @@
using OpenTK;
using OpenTK.Graphics;
-using osu.Framework.Allocation;
-using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
-using osu.Game.Graphics.Containers;
+using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{
- public class IconButton : OsuClickableContainer
+ public class IconButton : OsuAnimatedButton
{
public const float BUTTON_SIZE = 30;
- private Color4? flashColour;
- ///
- /// The colour that should be flashed when the is clicked.
- ///
- public Color4 FlashColour
- {
- get { return flashColour ?? Color4.White; }
- set { flashColour = value; }
- }
-
private Color4? iconColour;
+
///
/// The icon colour. This does not affect .
///
@@ -42,6 +28,7 @@ namespace osu.Game.Graphics.UserInterface
}
private Color4? iconHoverColour;
+
///
/// The icon colour while the is hovered.
///
@@ -51,20 +38,6 @@ namespace osu.Game.Graphics.UserInterface
set { iconHoverColour = value; }
}
- private Color4? hoverColour;
- ///
- /// The background colour of the while it is hovered.
- ///
- public Color4 HoverColour
- {
- get { return hoverColour ?? Color4.White; }
- set
- {
- hoverColour = value;
- hover.Colour = value;
- }
- }
-
///
/// The icon.
///
@@ -88,93 +61,39 @@ namespace osu.Game.Graphics.UserInterface
///
public Vector2 ButtonSize
{
- get { return content.Size; }
- set { content.Size = value; }
+ get => Content.Size;
+ set
+ {
+ Content.RelativeSizeAxes = Axes.None;
+ Content.Size = value;
+ }
}
- private readonly Container content;
private readonly SpriteIcon icon;
- private readonly Box hover;
public IconButton()
{
AutoSizeAxes = Axes.Both;
+ ButtonSize = new Vector2(BUTTON_SIZE);
- Children = new Drawable[]
+ Add(icon = new SpriteIcon
{
- content = new Container
- {
- Origin = Anchor.Centre,
- Anchor = Anchor.Centre,
- Size = new Vector2(BUTTON_SIZE),
- CornerRadius = 5,
- Masking = true,
- EdgeEffect = new EdgeEffectParameters
- {
- Colour = Color4.Black.Opacity(0.04f),
- Type = EdgeEffectType.Shadow,
- Radius = 5,
- },
- Children = new Drawable[]
- {
- hover = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Alpha = 0,
- },
- icon = new SpriteIcon
- {
- Origin = Anchor.Centre,
- Anchor = Anchor.Centre,
- Size = new Vector2(18),
- }
- }
- }
- };
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- if (hoverColour == null)
- HoverColour = colours.Yellow.Opacity(0.6f);
-
- if (flashColour == null)
- FlashColour = colours.Yellow;
-
- Enabled.ValueChanged += enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint);
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Size = new Vector2(18),
+ });
}
protected override bool OnHover(InputState state)
{
- hover.FadeIn(500, Easing.OutQuint);
icon.FadeColour(IconHoverColour, 500, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
- hover.FadeOut(500, Easing.OutQuint);
icon.FadeColour(IconColour, 500, Easing.OutQuint);
base.OnHoverLost(state);
}
-
- protected override bool OnClick(InputState state)
- {
- hover.FlashColour(FlashColour, 800, Easing.OutQuint);
- return base.OnClick(state);
- }
-
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
- {
- content.ScaleTo(0.75f, 2000, Easing.OutQuint);
- return base.OnMouseDown(state, args);
- }
-
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
- {
- content.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(state, args);
- }
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
new file mode 100644
index 0000000000..4428a058db
--- /dev/null
+++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
@@ -0,0 +1,110 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
+using osu.Game.Graphics.Containers;
+using OpenTK.Graphics;
+
+namespace osu.Game.Graphics.UserInterface
+{
+ ///
+ /// Highlight on hover, bounce on click.
+ ///
+ public class OsuAnimatedButton : OsuClickableContainer
+ {
+ ///
+ /// The colour that should be flashed when the is clicked.
+ ///
+ protected Color4 FlashColour = Color4.White.Opacity(0.3f);
+
+ private Color4 hoverColour = Color4.White.Opacity(0.1f);
+
+ ///
+ /// The background colour of the while it is hovered.
+ ///
+ protected Color4 HoverColour
+ {
+ get => hoverColour;
+ set
+ {
+ hoverColour = value;
+ hover.Colour = value;
+ }
+ }
+
+ protected override Container Content => content;
+
+ private readonly Container content;
+ private readonly Box hover;
+
+ public OsuAnimatedButton()
+ {
+ base.Content.Add(content = new Container
+ {
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ CornerRadius = 5,
+ Masking = true,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Colour = Color4.Black.Opacity(0.04f),
+ Type = EdgeEffectType.Shadow,
+ Radius = 5,
+ },
+ Children = new Drawable[]
+ {
+ hover = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = HoverColour,
+ Blending = BlendingMode.Additive,
+ Alpha = 0,
+ },
+ }
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Enabled.BindValueChanged(enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint), true);
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ hover.FadeIn(500, Easing.OutQuint);
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ hover.FadeOut(500, Easing.OutQuint);
+ base.OnHoverLost(state);
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ hover.FlashColour(FlashColour, 800, Easing.OutQuint);
+ return base.OnClick(state);
+ }
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ {
+ Content.ScaleTo(0.75f, 2000, Easing.OutQuint);
+ return base.OnMouseDown(state, args);
+ }
+
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ {
+ Content.ScaleTo(1, 1000, Easing.OutElastic);
+ return base.OnMouseUp(state, args);
+ }
+ }
+}
diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs
index bf3805a44d..bb6a032a12 100644
--- a/osu.Game/Graphics/UserInterface/OsuButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuButton.cs
@@ -1,7 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
+using osu.Game.Graphics.Sprites;
+using OpenTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
@@ -10,9 +19,73 @@ namespace osu.Game.Graphics.UserInterface
///
public class OsuButton : Button
{
+ private Box hover;
+
public OsuButton()
{
- Add(new HoverClickSounds(HoverSampleSet.Loud));
+ Height = 40;
+
+ Content.Masking = true;
+ Content.CornerRadius = 5;
}
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ BackgroundColour = colours.BlueDark;
+
+ AddRange(new Drawable[]
+ {
+ hover = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Blending = BlendingMode.Additive,
+ Colour = Color4.White.Opacity(0.1f),
+ Alpha = 0,
+ Depth = -1
+ },
+ new HoverClickSounds(HoverSampleSet.Loud),
+ });
+
+ Enabled.ValueChanged += enabled_ValueChanged;
+ Enabled.TriggerChange();
+ }
+
+ private void enabled_ValueChanged(bool enabled)
+ {
+ this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ hover.FadeIn(200);
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ hover.FadeOut(200);
+ base.OnHoverLost(state);
+ }
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ {
+ Content.ScaleTo(0.9f, 4000, Easing.OutQuint);
+ return base.OnMouseDown(state, args);
+ }
+
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ {
+ Content.ScaleTo(1, 1000, Easing.OutElastic);
+ return base.OnMouseUp(state, args);
+ }
+
+ protected override SpriteText CreateText() => new OsuSpriteText
+ {
+ Depth = -1,
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Font = @"Exo2.0-Bold",
+ };
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
index ea337d5f42..68f59bd8cd 100644
--- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
@@ -8,7 +8,7 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs
index 41aeb534f0..abb077e94f 100644
--- a/osu.Game/Graphics/UserInterface/OsuMenu.cs
+++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics.Sprites;
using OpenTK;
diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
index 07920865c0..75655ddb36 100644
--- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
@@ -9,7 +9,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Framework.Platform;
namespace osu.Game.Graphics.UserInterface
diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
index 7604009aab..b7b5319e06 100644
--- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
+++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
@@ -11,9 +11,10 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
index d015a563f6..e2a0b88b2a 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
@@ -14,7 +14,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.MathUtils;
using osu.Game.Graphics.Sprites;
@@ -37,7 +37,7 @@ namespace osu.Game.Graphics.UserInterface
{
TabContainer.Spacing = new Vector2(10f, 0f);
- Add(strip = new Box
+ AddInternal(strip = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
index 13740c935f..04ba111153 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{
diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs
index 88b0543de0..7a45ffdd4b 100644
--- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs
@@ -5,11 +5,11 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Input.Bindings;
+using osu.Framework.Input.States;
using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface
diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs
index 3a8c72725e..d203532f9c 100644
--- a/osu.Game/Graphics/UserInterface/PageTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface
diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs
index 7d53c9e9d9..6067481979 100644
--- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs
@@ -2,7 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using OpenTK;
using OpenTK.Input;
diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs
index a6492ddd63..683b442d93 100644
--- a/osu.Game/Graphics/UserInterface/TriangleButton.cs
+++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs
@@ -2,16 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
-using OpenTK.Graphics;
using osu.Framework.Allocation;
-using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
using osu.Game.Graphics.Backgrounds;
-using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface
{
@@ -20,79 +14,17 @@ namespace osu.Game.Graphics.UserInterface
///
public class TriangleButton : OsuButton, IFilterable
{
- private Box hover;
-
- protected Triangles Triangles;
-
- public TriangleButton()
- {
- Height = 40;
- }
-
- protected override SpriteText CreateText() => new OsuSpriteText
- {
- Depth = -1,
- Origin = Anchor.Centre,
- Anchor = Anchor.Centre,
- Font = @"Exo2.0-Bold",
- };
+ protected Triangles Triangles { get; private set; }
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- BackgroundColour = colours.BlueDark;
-
- Content.Masking = true;
- Content.CornerRadius = 5;
-
- AddRange(new Drawable[]
+ Add(Triangles = new Triangles
{
- Triangles = new Triangles
- {
- RelativeSizeAxes = Axes.Both,
- ColourDark = colours.BlueDarker,
- ColourLight = colours.Blue,
- },
- hover = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Blending = BlendingMode.Additive,
- Colour = Color4.White.Opacity(0.1f),
- Alpha = 0,
- },
+ RelativeSizeAxes = Axes.Both,
+ ColourDark = colours.BlueDarker,
+ ColourLight = colours.Blue,
});
-
- Enabled.ValueChanged += enabled_ValueChanged;
- Enabled.TriggerChange();
- }
-
- private void enabled_ValueChanged(bool enabled)
- {
- this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
- }
-
- protected override bool OnHover(InputState state)
- {
- hover.FadeIn(200);
- return base.OnHover(state);
- }
-
- protected override void OnHoverLost(InputState state)
- {
- hover.FadeOut(200);
- base.OnHoverLost(state);
- }
-
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
- {
- Content.ScaleTo(0.9f, 4000, Easing.OutQuint);
- return base.OnMouseDown(state, args);
- }
-
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
- {
- Content.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(state, args);
}
public IEnumerable FilterTerms => new[] { Text };
diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
index f490306acf..4e6361d1ae 100644
--- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
+++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
@@ -4,7 +4,6 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Graphics.Sprites;
@@ -14,6 +13,8 @@ using osu.Game.Beatmaps.ControlPoints;
using osu.Framework.Audio.Track;
using System;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{
@@ -56,15 +57,15 @@ namespace osu.Game.Graphics.UserInterface
set
{
base.Origin = value;
- c1.Origin = c1.Anchor = (value & Anchor.x2) > 0 ? Anchor.TopLeft : Anchor.TopRight;
- c2.Origin = c2.Anchor = (value & Anchor.x2) > 0 ? Anchor.TopRight : Anchor.TopLeft;
+ c1.Origin = c1.Anchor = value.HasFlag(Anchor.x2) ? Anchor.TopLeft : Anchor.TopRight;
+ c2.Origin = c2.Anchor = value.HasFlag(Anchor.x2) ? Anchor.TopRight : Anchor.TopLeft;
- X = (value & Anchor.x2) > 0 ? SIZE_RETRACTED.X * shear * 0.5f : 0;
+ X = value.HasFlag(Anchor.x2) ? SIZE_RETRACTED.X * shear * 0.5f : 0;
Remove(c1);
Remove(c2);
- c1.Depth = (value & Anchor.x2) > 0 ? 0 : 1;
- c2.Depth = (value & Anchor.x2) > 0 ? 1 : 0;
+ c1.Depth = value.HasFlag(Anchor.x2) ? 0 : 1;
+ c2.Depth = value.HasFlag(Anchor.x2) ? 1 : 0;
Add(c1);
Add(c2);
}
diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs
index 57a2e5df6d..5f53868b28 100644
--- a/osu.Game/Input/Handlers/ReplayInputHandler.cs
+++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs
@@ -3,8 +3,9 @@
using System;
using System.Collections.Generic;
-using osu.Framework.Input;
using osu.Framework.Input.Handlers;
+using osu.Framework.Input.StateChanges;
+using osu.Framework.Input.States;
using osu.Framework.Platform;
using OpenTK;
diff --git a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs
index 66ebda785b..084ae67940 100644
--- a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs
+++ b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs
@@ -1,6 +1,4 @@
using Microsoft.EntityFrameworkCore.Migrations;
-using System;
-using System.Collections.Generic;
namespace osu.Game.Migrations
{
diff --git a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs
index 5227d1f8f9..09cf0af89c 100644
--- a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs
+++ b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs
@@ -1,6 +1,4 @@
using Microsoft.EntityFrameworkCore.Migrations;
-using System;
-using System.Collections.Generic;
namespace osu.Game.Migrations
{
diff --git a/osu.Game/Migrations/20180125143340_Settings.cs b/osu.Game/Migrations/20180125143340_Settings.cs
index 20701d672a..2e2768dc7c 100644
--- a/osu.Game/Migrations/20180125143340_Settings.cs
+++ b/osu.Game/Migrations/20180125143340_Settings.cs
@@ -1,6 +1,4 @@
using Microsoft.EntityFrameworkCore.Migrations;
-using System;
-using System.Collections.Generic;
namespace osu.Game.Migrations
{
diff --git a/osu.Game/Migrations/20180131154205_AddMuteBinding.cs b/osu.Game/Migrations/20180131154205_AddMuteBinding.cs
index 374830ad93..5564a30bbf 100644
--- a/osu.Game/Migrations/20180131154205_AddMuteBinding.cs
+++ b/osu.Game/Migrations/20180131154205_AddMuteBinding.cs
@@ -1,6 +1,4 @@
using Microsoft.EntityFrameworkCore.Migrations;
-using System;
-using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using osu.Game.Database;
using osu.Game.Input.Bindings;
diff --git a/osu.Game/Migrations/20180219060912_AddSkins.cs b/osu.Game/Migrations/20180219060912_AddSkins.cs
index 6cce4354d9..a0270ab0fd 100644
--- a/osu.Game/Migrations/20180219060912_AddSkins.cs
+++ b/osu.Game/Migrations/20180219060912_AddSkins.cs
@@ -1,6 +1,4 @@
using Microsoft.EntityFrameworkCore.Migrations;
-using System;
-using System.Collections.Generic;
namespace osu.Game.Migrations
{
diff --git a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs
index fe8c983a3f..27269cc5fc 100644
--- a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs
+++ b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs
@@ -1,6 +1,4 @@
using Microsoft.EntityFrameworkCore.Migrations;
-using System;
-using System.Collections.Generic;
namespace osu.Game.Migrations
{
diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs
index 98ce5def08..71304ea979 100644
--- a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs
+++ b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs
@@ -1,5 +1,4 @@
using Microsoft.EntityFrameworkCore.Migrations;
-using osu.Framework.Logging;
namespace osu.Game.Migrations
{
@@ -8,7 +7,6 @@ namespace osu.Game.Migrations
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("DELETE FROM KeyBinding WHERE RulesetID = 1");
- Logger.Log("osu!taiko bindings have been reset due to new defaults", LoggingTarget.Runtime, LogLevel.Important);
}
protected override void Down(MigrationBuilder migrationBuilder)
diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
index bd80cb743b..6dbeaed62f 100644
--- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
+++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
@@ -1,8 +1,7 @@
//
-using System;
+
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
index ea14135a61..4d4aa4d957 100644
--- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Online.API.Requests
}
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
- protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLower()}?offset={offset}";
+ protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLowerInvariant()}?offset={offset}";
}
public enum ScoreType
diff --git a/osu.Game/Online/API/Requests/PostMessageRequest.cs b/osu.Game/Online/API/Requests/PostMessageRequest.cs
index 44429bdcd5..e0a9fb83b2 100644
--- a/osu.Game/Online/API/Requests/PostMessageRequest.cs
+++ b/osu.Game/Online/API/Requests/PostMessageRequest.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Online.API.Requests
req.Method = HttpMethod.POST;
req.AddParameter(@"target_type", message.TargetType.GetDescription());
req.AddParameter(@"target_id", message.TargetId.ToString());
- req.AddParameter(@"is_action", message.IsAction.ToString().ToLower());
+ req.AddParameter(@"is_action", message.IsAction.ToString().ToLowerInvariant());
req.AddParameter(@"message", message.Content);
return req;
diff --git a/osu.Game/Online/API/Requests/Responses/APIScore.cs b/osu.Game/Online/API/Requests/Responses/APIScore.cs
index a398bf46ee..25eb32a79f 100644
--- a/osu.Game/Online/API/Requests/Responses/APIScore.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIScore.cs
@@ -63,7 +63,14 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"beatmapset")]
private BeatmapMetadata metadata
{
- set => Beatmap.Metadata = value;
+ set
+ {
+ // extract the set ID to its correct place.
+ Beatmap.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = value.ID };
+ value.ID = 0;
+
+ Beatmap.Metadata = value;
+ }
}
[JsonProperty(@"statistics")]
diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
index 2a154d1230..3c808d1bee 100644
--- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
+++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Online.API.Requests
}
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
- protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)searchCategory}&sort={sortCriteria.ToString().ToLower()}_{directionString}";
+ protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)searchCategory}&sort={sortCriteria.ToString().ToLowerInvariant()}_{directionString}";
}
public enum BeatmapSearchCategory
diff --git a/osu.Game/Online/Chat/InfoMessage.cs b/osu.Game/Online/Chat/InfoMessage.cs
index 2be025e403..2ff901deb1 100644
--- a/osu.Game/Online/Chat/InfoMessage.cs
+++ b/osu.Game/Online/Chat/InfoMessage.cs
@@ -6,7 +6,7 @@ using osu.Game.Users;
namespace osu.Game.Online.Chat
{
- public class InfoMessage : Message
+ public class InfoMessage : LocalMessage
{
private static int infoID = -1;
diff --git a/osu.Game/Online/Chat/LocalEchoMessage.cs b/osu.Game/Online/Chat/LocalEchoMessage.cs
index 2e90b9d3fd..7d678029aa 100644
--- a/osu.Game/Online/Chat/LocalEchoMessage.cs
+++ b/osu.Game/Online/Chat/LocalEchoMessage.cs
@@ -3,7 +3,7 @@
namespace osu.Game.Online.Chat
{
- public class LocalEchoMessage : Message
+ public class LocalEchoMessage : LocalMessage
{
public LocalEchoMessage() : base(null)
{
diff --git a/osu.Game/Online/Chat/LocalMessage.cs b/osu.Game/Online/Chat/LocalMessage.cs
new file mode 100644
index 0000000000..93f1e7f9ea
--- /dev/null
+++ b/osu.Game/Online/Chat/LocalMessage.cs
@@ -0,0 +1,16 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Online.Chat
+{
+ ///
+ /// A message which is generated and displayed locally.
+ ///
+ public class LocalMessage : Message
+ {
+ protected LocalMessage(long? id)
+ : base(id)
+ {
+ }
+ }
+}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 922d228701..94678a9dde 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -19,10 +19,12 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Audio;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Platform;
using osu.Framework.Threading;
+using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
using osu.Game.Overlays.Notifications;
@@ -33,6 +35,9 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Skinning;
using OpenTK.Graphics;
using osu.Game.Overlays.Volume;
+using osu.Game.Screens.Select;
+using osu.Game.Utils;
+using LogLevel = osu.Framework.Logging.LogLevel;
namespace osu.Game
{
@@ -62,6 +67,8 @@ namespace osu.Game
private ScreenshotManager screenshotManager;
+ protected RavenLogger RavenLogger;
+
public virtual Storage GetStorageForStableInstall() => null;
private Intro intro
@@ -96,13 +103,17 @@ namespace osu.Game
private readonly List overlays = new List();
// todo: move this to SongSelect once Screen has the ability to unsuspend.
- public readonly Bindable> SelectedMods = new Bindable>(new List());
+ [Cached]
+ [Cached(Type = typeof(IBindable>))]
+ private readonly Bindable> selectedMods = new Bindable>(new Mod[] { });
public OsuGame(string[] args = null)
{
this.args = args;
forwardLoggedErrorsToNotifications();
+
+ RavenLogger = new RavenLogger(this);
}
public void ToggleSettings() => settings.ToggleVisibility();
@@ -122,8 +133,8 @@ namespace osu.Game
private DependencyContainer dependencies;
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
- dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
+ dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager frameworkConfig)
@@ -140,13 +151,15 @@ namespace osu.Game
if (args?.Length > 0)
{
- var paths = args.Where(a => !a.StartsWith(@"-"));
-
- Task.Run(() => Import(paths.ToArray()));
+ var paths = args.Where(a => !a.StartsWith(@"-")).ToArray();
+ if (paths.Length > 0)
+ Task.Run(() => Import(paths));
}
dependencies.CacheAs(this);
+ dependencies.Cache(RavenLogger);
+
dependencies.CacheAs(ruleset);
dependencies.CacheAs>(ruleset);
@@ -178,6 +191,47 @@ namespace osu.Game
/// The set to display.
public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
+ ///
+ /// Present a beatmap at song select.
+ ///
+ /// The beatmap to select.
+ public void PresentBeatmap(BeatmapSetInfo beatmap)
+ {
+ CloseAllOverlays(false);
+
+ void setBeatmap()
+ {
+ if (Beatmap.Disabled)
+ {
+ Schedule(setBeatmap);
+ return;
+ }
+
+ var databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
+
+ // Use first beatmap available for current ruleset, else switch ruleset.
+ var first = databasedSet.Beatmaps.FirstOrDefault(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
+
+ ruleset.Value = first.Ruleset;
+ Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
+ }
+
+ switch (currentScreen)
+ {
+ case SongSelect _:
+ break;
+ default:
+ // navigate to song select if we are not already there.
+ var menu = (MainMenu)intro.ChildScreen;
+
+ menu.MakeCurrent();
+ menu.LoadToSolo();
+ break;
+ }
+
+ setBeatmap();
+ }
+
///
/// Show a user's profile as an overlay.
///
@@ -227,6 +281,12 @@ namespace osu.Game
menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay)));
}
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ RavenLogger.Dispose();
+ }
+
protected override void LoadComplete()
{
// this needs to be cached before base.LoadComplete as it is used by MenuCursorContainer.
@@ -244,6 +304,7 @@ namespace osu.Game
BeatmapManager.PostNotification = n => notifications?.Post(n);
BeatmapManager.GetStableStorage = GetStorageForStableInstall;
+ BeatmapManager.PresentBeatmap = PresentBeatmap;
AddRange(new Drawable[]
{
@@ -322,24 +383,6 @@ namespace osu.Game
dependencies.Cache(notifications);
dependencies.Cache(dialogOverlay);
- // ensure only one of these overlays are open at once.
- var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct };
- overlays.AddRange(singleDisplayOverlays);
-
- foreach (var overlay in singleDisplayOverlays)
- {
- overlay.StateChanged += state =>
- {
- if (state == Visibility.Hidden) return;
-
- foreach (var c in singleDisplayOverlays)
- {
- if (c == overlay) continue;
- c.State = Visibility.Hidden;
- }
- };
- }
-
var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications };
overlays.AddRange(singleDisplaySideOverlays);
@@ -348,12 +391,7 @@ namespace osu.Game
overlay.StateChanged += state =>
{
if (state == Visibility.Hidden) return;
-
- foreach (var c in singleDisplaySideOverlays)
- {
- if (c == overlay) continue;
- c.State = Visibility.Hidden;
- }
+ singleDisplaySideOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
};
}
@@ -366,12 +404,24 @@ namespace osu.Game
overlay.StateChanged += state =>
{
if (state == Visibility.Hidden) return;
+ informationalOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
+ };
+ }
- foreach (var c in informationalOverlays)
- {
- if (c == overlay) continue;
- c.State = Visibility.Hidden;
- }
+ // ensure only one of these overlays are open at once.
+ var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct };
+ overlays.AddRange(singleDisplayOverlays);
+
+ foreach (var overlay in singleDisplayOverlays)
+ {
+ overlay.StateChanged += state =>
+ {
+ // informational overlays should be dismissed on a show or hide of a full overlay.
+ informationalOverlays.ForEach(o => o.Hide());
+
+ if (state == Visibility.Hidden) return;
+
+ singleDisplayOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
};
}
@@ -413,7 +463,7 @@ namespace osu.Game
Schedule(() => notifications.Post(new SimpleNotification
{
Icon = entry.Level == LogLevel.Important ? FontAwesome.fa_exclamation_circle : FontAwesome.fa_bomb,
- Text = entry.Message,
+ Text = entry.Message + (entry.Exception != null && IsDeployedBuild ? "\n\nThis error has been automatically reported to the devs." : string.Empty),
}));
}
else if (recentLogCount == short_term_display_limit)
@@ -454,7 +504,25 @@ namespace osu.Game
// schedule is here to ensure that all component loads are done after LoadComplete is run (and thus all dependencies are cached).
// with some better organisation of LoadComplete to do construction and dependency caching in one step, followed by calls to loadComponentSingleFile,
// we could avoid the need for scheduling altogether.
- Schedule(() => { asyncLoadStream = asyncLoadStream?.ContinueWith(t => LoadComponentAsync(d, add).Wait()) ?? LoadComponentAsync(d, add); });
+ Schedule(() =>
+ {
+ if (asyncLoadStream != null)
+ {
+ //chain with existing load stream
+ asyncLoadStream = asyncLoadStream.ContinueWith(async t =>
+ {
+ try
+ {
+ await LoadComponentAsync(d, add);
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ });
+ }
+ else
+ asyncLoadStream = LoadComponentAsync(d, add);
+ });
}
public bool OnPressed(GlobalAction action)
@@ -552,10 +620,10 @@ namespace osu.Game
// we only want to apply these restrictions when we are inside a screen stack.
// the use case for not applying is in visual/unit tests.
- bool applyRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false;
+ bool applyBeatmapRulesetRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false;
- ruleset.Disabled = applyRestrictions;
- Beatmap.Disabled = applyRestrictions;
+ ruleset.Disabled = applyBeatmapRulesetRestrictions;
+ Beatmap.Disabled = applyBeatmapRulesetRestrictions;
mainContent.Padding = new MarginPadding { Top = ToolbarOffset };
@@ -565,6 +633,7 @@ namespace osu.Game
private void screenAdded(Screen newScreen)
{
currentScreen = (OsuScreen)newScreen;
+ Logger.Log($"Screen changed → {currentScreen}");
newScreen.ModePushed += screenAdded;
newScreen.Exited += screenRemoved;
@@ -573,6 +642,7 @@ namespace osu.Game
private void screenRemoved(Screen newScreen)
{
currentScreen = (OsuScreen)newScreen;
+ Logger.Log($"Screen changed ← {currentScreen}");
if (newScreen == null)
Exit();
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 246229a794..bada2a794d 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -20,6 +20,7 @@ using osu.Game.Graphics.Cursor;
using osu.Game.Online.API;
using osu.Framework.Graphics.Performance;
using osu.Framework.Graphics.Textures;
+using osu.Framework.Input;
using osu.Framework.Logging;
using osu.Game.Audio;
using osu.Game.Database;
@@ -30,6 +31,7 @@ using osu.Game.IO;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
+using OpenTK.Input;
using DebugUtils = osu.Game.Utils.DebugUtils;
namespace osu.Game
@@ -93,17 +95,19 @@ namespace osu.Game
private DependencyContainer dependencies;
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
- dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
+ dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
private DatabaseContextFactory contextFactory;
+ protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager();
+
[BackgroundDependencyLoader]
private void load()
{
Resources.AddStore(new DllResourceStore(@"osu.Game.Resources.dll"));
- dependencies.Cache(contextFactory = new DatabaseContextFactory(Host));
+ dependencies.Cache(contextFactory = new DatabaseContextFactory(Host.Storage));
dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))));
@@ -220,7 +224,7 @@ namespace osu.Game
// todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this.
contextFactory.ResetDatabase();
- Logger.Log("Database purged successfully.", LoggingTarget.Database, LogLevel.Important);
+ Logger.Log("Database purged successfully.", LoggingTarget.Database);
// only run once more, then hard bail.
using (var db = contextFactory.GetForWrite(false))
@@ -267,5 +271,31 @@ namespace osu.Game
return copy;
}
}
+
+ private class OsuUserInputManager : UserInputManager
+ {
+ protected override MouseButtonEventManager CreateButtonManagerFor(MouseButton button)
+ {
+ switch (button)
+ {
+ case MouseButton.Right:
+ return new RightMouseManager(button);
+ }
+
+ return base.CreateButtonManagerFor(button);
+ }
+
+ private class RightMouseManager : MouseButtonEventManager
+ {
+ public RightMouseManager(MouseButton button)
+ : base(button)
+ {
+ }
+
+ public override bool EnableDrag => true; // allow right-mouse dragging for absolute scroll in scroll containers.
+ public override bool EnableClick => false;
+ public override bool ChangeFocusOnClick => false;
+ }
+ }
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
index 6b75ac095d..8186bf8685 100644
--- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
+++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
@@ -10,7 +10,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs
index 4fce6a49fb..f3e49f68b1 100644
--- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs
@@ -1,18 +1,28 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Online.API;
+using osu.Game.Users;
using OpenTK;
+using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet.Buttons
{
- public class DownloadButton : HeaderButton
+ public class DownloadButton : HeaderButton, IHasTooltip
{
+ public string TooltipText => Enabled ? null : "You gotta be an osu!supporter to download for now 'yo";
+
+ private readonly IBindable localUser = new Bindable();
+
public DownloadButton(BeatmapSetInfo set, bool noVideo = false)
{
Width = 120;
@@ -61,22 +71,42 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
Action = () =>
{
- if (!downloader.Download())
+ if (downloader.DownloadState.Value == BeatmapSetDownloader.DownloadStatus.Downloading)
{
Content.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine);
+ return;
}
+
+ downloader.Download();
};
- downloader.Downloaded.ValueChanged += d =>
+ downloader.DownloadState.ValueChanged += state =>
{
- if (d)
- this.FadeOut(200);
- else
- this.FadeIn(200);
+ switch (state)
+ {
+ case BeatmapSetDownloader.DownloadStatus.Downloaded:
+ this.FadeOut(200);
+ break;
+ case BeatmapSetDownloader.DownloadStatus.NotDownloaded:
+ this.FadeIn(200);
+ break;
+ }
};
}
+
+ [BackgroundDependencyLoader]
+ private void load(APIAccess api)
+ {
+ localUser.BindTo(api.LocalUser);
+ localUser.BindValueChanged(userChanged, true);
+ Enabled.BindValueChanged(enabledChanged, true);
+ }
+
+ private void userChanged(User user) => Enabled.Value = user.IsSupporter;
+
+ private void enabledChanged(bool enabled) => this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs
index 505b7a7540..18391c1177 100644
--- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs
@@ -7,7 +7,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs b/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs
index 1f1a2a68d2..dc350d2c4c 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs
@@ -3,10 +3,10 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Input.States;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
-using osu.Framework.Input;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs
index fc8b3a6800..d9af81c19a 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs
@@ -6,7 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
index b3ceffd35e..a1ebab09b1 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
@@ -8,7 +8,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs
index 88f0a72ddf..02cc89e57e 100644
--- a/osu.Game/Overlays/BeatmapSetOverlay.cs
+++ b/osu.Game/Overlays/BeatmapSetOverlay.cs
@@ -7,7 +7,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs
index 910c87e0a8..7a60bf9f47 100644
--- a/osu.Game/Overlays/Chat/ChannelListItem.cs
+++ b/osu.Game/Overlays/Chat/ChannelListItem.cs
@@ -9,7 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
diff --git a/osu.Game/Overlays/Chat/ChannelSection.cs b/osu.Game/Overlays/Chat/ChannelSection.cs
index 89d9d2231c..85fdecaaba 100644
--- a/osu.Game/Overlays/Chat/ChannelSection.cs
+++ b/osu.Game/Overlays/Chat/ChannelSection.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Chat
public string Header
{
get { return header.Text; }
- set { header.Text = value.ToUpper(); }
+ set { header.Text = value.ToUpperInvariant(); }
}
public IEnumerable Channels
diff --git a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs
index 57f2cd405d..4e4fe4f10a 100644
--- a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs
+++ b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs
@@ -10,7 +10,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs
index 91b790ece4..2e3c9f9c5f 100644
--- a/osu.Game/Overlays/Chat/ChatTabControl.cs
+++ b/osu.Game/Overlays/Chat/ChatTabControl.cs
@@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
@@ -17,6 +16,8 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using System;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Chat
diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs
index bcc8879902..c57e71b5ad 100644
--- a/osu.Game/Overlays/Chat/DrawableChannel.cs
+++ b/osu.Game/Overlays/Chat/DrawableChannel.cs
@@ -85,7 +85,7 @@ namespace osu.Game.Overlays.Chat
if (!IsLoaded) return;
- if (scroll.IsScrolledToEnd(10) || !flow.Children.Any())
+ if (scroll.IsScrolledToEnd(10) || !flow.Children.Any() || newMessages.Any(m => m is LocalMessage))
scrollToEnd();
var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray();
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index a2542c537f..8e20d76914 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -13,7 +13,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Threading;
using osu.Game.Configuration;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs
index e32d7fb036..c9c90b4555 100644
--- a/osu.Game/Overlays/Dialog/PopupDialog.cs
+++ b/osu.Game/Overlays/Dialog/PopupDialog.cs
@@ -8,7 +8,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs
index e286837746..eb940bff60 100644
--- a/osu.Game/Overlays/Direct/DirectGridPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs
@@ -11,7 +11,7 @@ using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
@@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Direct
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
{
- Width = 400;
+ Width = 380;
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
}
@@ -168,11 +168,10 @@ namespace osu.Game.Overlays.Direct
},
new DownloadButton(SetInfo)
{
- Size = new Vector2(30),
+ Size = new Vector2(50, 30),
Margin = new MarginPadding(horizontal_padding),
- Anchor = Anchor.CentreRight,
- Origin = Anchor.CentreRight,
- Colour = colours.Gray5,
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
},
},
},
diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs
index 812a0e2073..850ead37f6 100644
--- a/osu.Game/Overlays/Direct/DirectListPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectListPanel.cs
@@ -12,7 +12,6 @@ using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Localisation;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
using osu.Game.Beatmaps;
namespace osu.Game.Overlays.Direct
@@ -26,12 +25,14 @@ namespace osu.Game.Overlays.Direct
private PlayButton playButton;
private Box progressBar;
- private Container downloadContainer;
+
+ protected override bool FadePlayButton => false;
protected override PlayButton PlayButton => playButton;
protected override Box PreviewBar => progressBar;
- public DirectListPanel(BeatmapSetInfo beatmap) : base(beatmap)
+ public DirectListPanel(BeatmapSetInfo beatmap)
+ : base(beatmap)
{
RelativeSizeAxes = Axes.X;
Height = height;
@@ -66,30 +67,45 @@ namespace osu.Game.Overlays.Direct
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
- playButton = new PlayButton(SetInfo)
- {
- Origin = Anchor.CentreLeft,
- Anchor = Anchor.CentreLeft,
- Size = new Vector2(height / 2),
- FillMode = FillMode.Fit,
- Alpha = 0,
- },
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- new OsuSpriteText
+ new FillFlowContainer
{
- Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
- TextSize = 18,
- Font = @"Exo2.0-BoldItalic",
- },
- new OsuSpriteText
- {
- Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
- Font = @"Exo2.0-BoldItalic",
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ playButton = new PlayButton(SetInfo)
+ {
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
+ Size = new Vector2(height / 2),
+ FillMode = FillMode.Fit,
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
+ TextSize = 18,
+ Font = @"Exo2.0-BoldItalic",
+ },
+ new OsuSpriteText
+ {
+ Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
+ Font = @"Exo2.0-BoldItalic",
+ },
+ }
+ },
+ }
},
new FillFlowContainer
{
@@ -108,20 +124,17 @@ namespace osu.Game.Overlays.Direct
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
- LayoutEasing = Easing.OutQuint,
- LayoutDuration = transition_duration,
Children = new Drawable[]
{
- downloadContainer = new Container
+ new Container
{
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
- Alpha = 0,
Child = new DownloadButton(SetInfo)
{
- Size = new Vector2(height - vertical_padding * 2),
- Margin = new MarginPadding { Left = vertical_padding },
+ Size = new Vector2(height - vertical_padding * 3),
+ Margin = new MarginPadding { Left = vertical_padding, Right = vertical_padding },
},
},
new FillFlowContainer
@@ -184,17 +197,5 @@ namespace osu.Game.Overlays.Direct
},
});
}
-
- protected override bool OnHover(InputState state)
- {
- downloadContainer.FadeIn(transition_duration, Easing.InOutQuint);
- return base.OnHover(state);
- }
-
- protected override void OnHoverLost(InputState state)
- {
- downloadContainer.FadeOut(transition_duration, Easing.InOutQuint);
- base.OnHoverLost(state);
- }
}
}
diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs
index e63c290ce5..322db0b7d6 100644
--- a/osu.Game/Overlays/Direct/DirectPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectPanel.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
@@ -40,6 +40,8 @@ namespace osu.Game.Overlays.Direct
protected abstract PlayButton PlayButton { get; }
protected abstract Box PreviewBar { get; }
+ protected virtual bool FadePlayButton => true;
+
protected override Container Content => content;
protected DirectPanel(BeatmapSetInfo setInfo)
@@ -99,6 +101,7 @@ namespace osu.Game.Overlays.Direct
attachDownload(downloadRequest);
beatmaps.BeatmapDownloadBegan += attachDownload;
+ beatmaps.ItemAdded += setAdded;
}
public override bool DisposeOnDeathRemoval => true;
@@ -107,6 +110,7 @@ namespace osu.Game.Overlays.Direct
{
base.Dispose(isDisposing);
beatmaps.BeatmapDownloadBegan -= attachDownload;
+ beatmaps.ItemAdded -= setAdded;
}
protected override void Update()
@@ -123,7 +127,8 @@ namespace osu.Game.Overlays.Direct
{
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
- PlayButton.FadeIn(120, Easing.InOutQuint);
+ if (FadePlayButton)
+ PlayButton.FadeIn(120, Easing.InOutQuint);
return base.OnHover(state);
}
@@ -132,7 +137,7 @@ namespace osu.Game.Overlays.Direct
{
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
- if (!PreviewPlaying)
+ if (FadePlayButton && !PreviewPlaying)
PlayButton.FadeOut(120, Easing.InOutQuint);
base.OnHoverLost(state);
@@ -171,6 +176,12 @@ namespace osu.Game.Overlays.Direct
};
}
+ private void setAdded(BeatmapSetInfo s)
+ {
+ if (s.OnlineBeatmapSetID == SetInfo.OnlineBeatmapSetID)
+ progressBar.FadeOut(500);
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs
index 1ffa8dbd35..99a5881487 100644
--- a/osu.Game/Overlays/Direct/DownloadButton.cs
+++ b/osu.Game/Overlays/Direct/DownloadButton.cs
@@ -1,76 +1,112 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Input;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
-using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.UserInterface;
using OpenTK;
namespace osu.Game.Overlays.Direct
{
- public class DownloadButton : OsuClickableContainer
+ public class DownloadButton : OsuAnimatedButton
{
+ private readonly BeatmapSetInfo beatmapSet;
private readonly SpriteIcon icon;
+ private readonly SpriteIcon checkmark;
+ private readonly BeatmapSetDownloader downloader;
+ private readonly Box background;
- public DownloadButton(BeatmapSetInfo set, bool noVideo = false)
+ private OsuColour colours;
+
+ public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
{
- BeatmapSetDownloader downloader;
- Children = new Drawable[]
+ this.beatmapSet = beatmapSet;
+
+ AddRange(new Drawable[]
{
- downloader = new BeatmapSetDownloader(set, noVideo),
+ downloader = new BeatmapSetDownloader(beatmapSet, noVideo),
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Depth = float.MaxValue
+ },
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Size = new Vector2(30),
- Icon = FontAwesome.fa_osu_chevron_down_o,
+ Size = new Vector2(13),
+ Icon = FontAwesome.fa_download,
},
- };
+ checkmark = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ X = 8,
+ Size = Vector2.Zero,
+ Icon = FontAwesome.fa_check,
+ }
+ });
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ downloader.DownloadState.BindValueChanged(updateState, true);
+ FinishTransforms(true);
+ }
+
+ [BackgroundDependencyLoader(permitNulls: true)]
+ private void load(OsuColour colours, OsuGame game)
+ {
+ this.colours = colours;
Action = () =>
{
- if (!downloader.Download())
+ switch (downloader.DownloadState.Value)
{
- Content.MoveToX(-5, 50, Easing.OutSine).Then()
- .MoveToX(5, 100, Easing.InOutSine).Then()
- .MoveToX(-5, 100, Easing.InOutSine).Then()
- .MoveToX(0, 50, Easing.InSine);
+ case BeatmapSetDownloader.DownloadStatus.Downloading:
+ // todo: replace with ShakeContainer after https://github.com/ppy/osu/pull/2909 is merged.
+ Content.MoveToX(-5, 50, Easing.OutSine).Then()
+ .MoveToX(5, 100, Easing.InOutSine).Then()
+ .MoveToX(-5, 100, Easing.InOutSine).Then()
+ .MoveToX(0, 50, Easing.InSine);
+ break;
+ case BeatmapSetDownloader.DownloadStatus.Downloaded:
+ game.PresentBeatmap(beatmapSet);
+ break;
+ default:
+ downloader.Download();
+ break;
}
};
+ }
- downloader.Downloaded.ValueChanged += d =>
+ private void updateState(BeatmapSetDownloader.DownloadStatus state)
+ {
+ switch (state)
{
- if (d)
- this.FadeOut(200);
- else
- this.FadeIn(200);
- };
- }
+ case BeatmapSetDownloader.DownloadStatus.NotDownloaded:
+ background.FadeColour(colours.Gray4, 500, Easing.InOutExpo);
+ icon.MoveToX(0, 500, Easing.InOutExpo);
+ checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo);
+ break;
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
- {
- icon.ScaleTo(0.9f, 1000, Easing.Out);
- return base.OnMouseDown(state, args);
- }
+ case BeatmapSetDownloader.DownloadStatus.Downloading:
+ background.FadeColour(colours.Blue, 500, Easing.InOutExpo);
+ icon.MoveToX(0, 500, Easing.InOutExpo);
+ checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo);
+ break;
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
- {
- icon.ScaleTo(1f, 500, Easing.OutElastic);
- return base.OnMouseUp(state, args);
- }
-
- protected override bool OnHover(InputState state)
- {
- icon.ScaleTo(1.1f, 500, Easing.OutElastic);
- return base.OnHover(state);
- }
-
- protected override void OnHoverLost(InputState state)
- {
- icon.ScaleTo(1f, 500, Easing.OutElastic);
+ case BeatmapSetDownloader.DownloadStatus.Downloaded:
+ background.FadeColour(colours.Green, 500, Easing.InOutExpo);
+ icon.MoveToX(-8, 500, Easing.InOutExpo);
+ checkmark.ScaleTo(new Vector2(13), 500, Easing.InOutExpo);
+ break;
+ }
}
}
}
diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs
index 28cc484109..092c9ddbea 100644
--- a/osu.Game/Overlays/Direct/PlayButton.cs
+++ b/osu.Game/Overlays/Direct/PlayButton.cs
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
index 5d4d627995..86baaf3905 100644
--- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
+++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
@@ -9,14 +9,16 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
using osu.Framework.Input.Bindings;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Input;
using OpenTK.Graphics;
using OpenTK.Input;
+using JoystickEventArgs = osu.Framework.Input.EventArgs.JoystickEventArgs;
namespace osu.Game.Overlays.KeyBinding
{
@@ -102,6 +104,7 @@ namespace osu.Game.Overlays.KeyBinding
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding(padding),
+ Padding = new MarginPadding { Top = height },
Alpha = 0,
Colour = colours.YellowDark
}
@@ -229,7 +232,7 @@ namespace osu.Game.Overlays.KeyBinding
return true;
}
- protected override bool OnJoystickPress(InputState state, Framework.Input.JoystickEventArgs args)
+ protected override bool OnJoystickPress(InputState state, JoystickEventArgs args)
{
if (!HasFocus)
return false;
@@ -240,7 +243,7 @@ namespace osu.Game.Overlays.KeyBinding
return true;
}
- protected override bool OnJoystickRelease(InputState state, Framework.Input.JoystickEventArgs args)
+ protected override bool OnJoystickRelease(InputState state, JoystickEventArgs args)
{
if (!HasFocus)
return base.OnJoystickRelease(state, args);
@@ -267,7 +270,7 @@ namespace osu.Game.Overlays.KeyBinding
GetContainingInputManager().ChangeFocus(null);
pressAKey.FadeOut(300, Easing.OutQuint);
- pressAKey.Padding = new MarginPadding { Top = height, Bottom = -pressAKey.DrawHeight };
+ pressAKey.BypassAutoSizeAxes |= Axes.Y;
}
protected override void OnFocus(InputState state)
@@ -276,7 +279,7 @@ namespace osu.Game.Overlays.KeyBinding
AutoSizeEasing = Easing.OutQuint;
pressAKey.FadeIn(300, Easing.OutQuint);
- pressAKey.Padding = new MarginPadding { Top = height };
+ pressAKey.BypassAutoSizeAxes &= ~Axes.Y;
updateBindTarget();
base.OnFocus(state);
diff --git a/osu.Game/Overlays/MainSettings.cs b/osu.Game/Overlays/MainSettings.cs
index 99a86f19a1..6e8b4494dc 100644
--- a/osu.Game/Overlays/MainSettings.cs
+++ b/osu.Game/Overlays/MainSettings.cs
@@ -5,8 +5,9 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
using osu.Framework.Input.Bindings;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs
index 97a9217b23..bd67a718a7 100644
--- a/osu.Game/Overlays/MedalOverlay.cs
+++ b/osu.Game/Overlays/MedalOverlay.cs
@@ -16,11 +16,11 @@ using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Input;
using OpenTK.Input;
-using System.Linq;
using osu.Framework.Graphics.Shapes;
using System;
+using System.Linq;
+using osu.Framework.Input.States;
using osu.Framework.MathUtils;
namespace osu.Game.Overlays
diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
index ce79e70b1c..a27278e002 100644
--- a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
+++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
@@ -62,7 +62,7 @@ namespace osu.Game.Overlays.MedalSplash
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- Text = "Medal Unlocked".ToUpper(),
+ Text = "Medal Unlocked".ToUpperInvariant(),
TextSize = 24,
Font = @"Exo2.0-Light",
Alpha = 0f,
diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs
index f4e0e3db04..8024f9a732 100644
--- a/osu.Game/Overlays/Mods/ModButton.cs
+++ b/osu.Game/Overlays/Mods/ModButton.cs
@@ -7,13 +7,14 @@ using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System;
using System.Linq;
using osu.Framework.Graphics.Cursor;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Mods
diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs
index 48f51f72ca..37bffaaf12 100644
--- a/osu.Game/Overlays/Mods/ModSection.cs
+++ b/osu.Game/Overlays/Mods/ModSection.cs
@@ -2,16 +2,16 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
-using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using System;
using System.Linq;
using System.Collections.Generic;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
namespace osu.Game.Overlays.Mods
{
@@ -44,7 +44,6 @@ namespace osu.Game.Overlays.Mods
return new ModButton(m)
{
- SelectedColour = selectedColour,
SelectionChanged = Action,
};
}).ToArray();
@@ -56,25 +55,14 @@ namespace osu.Game.Overlays.Mods
private ModButton[] buttons = { };
- private Color4 selectedColour = Color4.White;
- public Color4 SelectedColour
- {
- get => selectedColour;
- set
- {
- if (value == selectedColour) return;
- selectedColour = value;
-
- foreach (ModButton button in buttons)
- button.SelectedColour = value;
- }
- }
-
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
- var index = Array.IndexOf(ToggleKeys, args.Key);
- if (index > -1 && index < buttons.Length)
- buttons[index].SelectNext(state.Keyboard.ShiftPressed ? -1 : 1);
+ if (ToggleKeys != null)
+ {
+ var index = Array.IndexOf(ToggleKeys, args.Key);
+ if (index > -1 && index < buttons.Length)
+ buttons[index].SelectNext(state.Keyboard.ShiftPressed ? -1 : 1);
+ }
return base.OnKeyDown(state, args);
}
@@ -124,6 +112,10 @@ namespace osu.Game.Overlays.Mods
protected ModSection()
{
AutoSizeAxes = Axes.Y;
+ RelativeSizeAxes = Axes.X;
+
+ Origin = Anchor.TopCentre;
+ Anchor = Anchor.TopCentre;
Children = new Drawable[]
{
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index c584a32a82..e83dedaf35 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -21,11 +21,17 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.Mods.Sections;
namespace osu.Game.Overlays.Mods
{
public class ModSelectOverlay : WaveOverlayContainer
{
+ ///
+ /// How much this container should overflow the sides of the screen to account for parallax shifting.
+ ///
+ private const float overflow_padding = 50;
+
private const float content_width = 0.8f;
protected Color4 LowMultiplierColour, HighMultiplierColour;
@@ -38,35 +44,32 @@ namespace osu.Game.Overlays.Mods
protected readonly FillFlowContainer ModSectionsContainer;
- public readonly Bindable> SelectedMods = new Bindable>();
+ protected readonly Bindable> SelectedMods = new Bindable>(new Mod[] { });
- public readonly IBindable Ruleset = new Bindable();
+ protected readonly IBindable Ruleset = new Bindable();
- private void rulesetChanged(RulesetInfo newRuleset)
+ [BackgroundDependencyLoader(true)]
+ private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> selectedMods)
{
- var instance = newRuleset.CreateInstance();
-
- foreach (ModSection section in ModSectionsContainer.Children)
- section.Mods = instance.GetModsFor(section.ModType);
- refreshSelectedMods();
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours, IBindable ruleset, AudioManager audio)
- {
- SelectedMods.ValueChanged += selectedModsChanged;
-
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
UnrankedLabel.Colour = colours.Blue;
Ruleset.BindTo(ruleset);
- Ruleset.BindValueChanged(rulesetChanged, true);
+ if (selectedMods != null) SelectedMods.BindTo(selectedMods);
sampleOn = audio.Sample.Get(@"UI/check-on");
sampleOff = audio.Sample.Get(@"UI/check-off");
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Ruleset.BindValueChanged(rulesetChanged, true);
+ SelectedMods.BindValueChanged(selectedModsChanged, true);
+ }
+
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
@@ -75,6 +78,24 @@ namespace osu.Game.Overlays.Mods
SelectedMods.UnbindAll();
}
+ private void rulesetChanged(RulesetInfo newRuleset)
+ {
+ if (newRuleset == null) return;
+
+ var instance = newRuleset.CreateInstance();
+
+ foreach (ModSection section in ModSectionsContainer.Children)
+ section.Mods = instance.GetModsFor(section.ModType);
+
+ // attempt to re-select any already selected mods.
+ // this may be the first time we are receiving the ruleset, in which case they will still match.
+ selectedModsChanged(SelectedMods.Value);
+
+ // write the mods back to the SelectedMods bindable in the case a change was not applicable.
+ // this generally isn't required as the previous line will perform deselection; just here for safety.
+ refreshSelectedMods();
+ }
+
private void selectedModsChanged(IEnumerable obj)
{
foreach (ModSection section in ModSectionsContainer.Children)
@@ -183,6 +204,12 @@ namespace osu.Game.Overlays.Mods
Waves.FourthWaveColour = OsuColour.FromHex(@"003a4e");
Height = 510;
+ Padding = new MarginPadding
+ {
+ Left = -overflow_padding,
+ Right = -overflow_padding
+ };
+
Children = new Drawable[]
{
new Container
@@ -206,178 +233,184 @@ namespace osu.Game.Overlays.Mods
},
},
},
- new FillFlowContainer
+ new GridContainer
{
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.Both,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
- Direction = FillDirection.Vertical,
- Spacing = new Vector2(0f, 10f),
- Children = new Drawable[]
+ RowDimensions = new[]
{
- // Header
- new Container
+ new Dimension(GridSizeMode.Absolute, 90),
+ new Dimension(GridSizeMode.Distributed),
+ new Dimension(GridSizeMode.Absolute, 70),
+ },
+ Content = new[]
+ {
+ new Drawable[]
{
- RelativeSizeAxes = Axes.X,
- Height = 82,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Children = new Drawable[]
+ new Container
{
- new Box
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Colour = OsuColour.Gray(10).Opacity(100),
- },
- new FillFlowContainer
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- Width = content_width,
- Padding = new MarginPadding
+ new Box
{
- Top = 10,
- Bottom = 10,
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.Gray(10).Opacity(100),
},
- Children = new Drawable[]
+ new FillFlowContainer
{
- new OsuSpriteText
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Width = content_width,
+ Padding = new MarginPadding
{
- Font = @"Exo2.0-Bold",
- Text = @"Gameplay Mods",
- TextSize = 22,
- Shadow = true,
- Margin = new MarginPadding
+ Left = overflow_padding,
+ Right = overflow_padding
+ },
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
{
- Bottom = 4,
+ Font = @"Exo2.0-Bold",
+ Text = @"Gameplay Mods",
+ TextSize = 22,
+ Shadow = true,
+ Margin = new MarginPadding
+ {
+ Bottom = 4,
+ },
+ },
+ new OsuTextFlowContainer(text =>
+ {
+ text.TextSize = 18;
+ text.Shadow = true;
+ })
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Text = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play.\nOthers are just for fun.",
},
- },
- new OsuSpriteText
- {
- Text = @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play.",
- TextSize = 18,
- Shadow = true,
- },
- new OsuSpriteText
- {
- Text = @"Others are just for fun.",
- TextSize = 18,
- Shadow = true,
},
},
},
},
},
- // Body
- ModSectionsContainer = new FillFlowContainer
+ new Drawable[]
{
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Spacing = new Vector2(0f, 10f),
- Width = content_width,
- Children = new ModSection[]
+ // Body
+ new OsuScrollContainer
{
- new DifficultyReductionSection
+ ScrollbarVisible = false,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding
+ {
+ Vertical = 10,
+ Left = overflow_padding,
+ Right = overflow_padding
+ },
+ Child = ModSectionsContainer = new FillFlowContainer
{
- RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
- Action = modButtonPressed,
- },
- new DifficultyIncreaseSection
- {
RelativeSizeAxes = Axes.X,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Action = modButtonPressed,
- },
- new SpecialSection
- {
- RelativeSizeAxes = Axes.X,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Action = modButtonPressed,
- },
- }
- },
- // Footer
- new Container
- {
- RelativeSizeAxes = Axes.X,
- Height = 70,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = new Color4(172, 20, 116, 255),
- Alpha = 0.5f,
- },
- footerContainer = new FillFlowContainer
- {
- Origin = Anchor.BottomCentre,
- Anchor = Anchor.BottomCentre,
AutoSizeAxes = Axes.Y,
- RelativeSizeAxes = Axes.X,
+ Spacing = new Vector2(0f, 10f),
Width = content_width,
- Direction = FillDirection.Horizontal,
- Padding = new MarginPadding
+ Children = new ModSection[]
{
- Vertical = 15
+ new DifficultyReductionSection { Action = modButtonPressed },
+ new DifficultyIncreaseSection { Action = modButtonPressed },
+ new AutomationSection { Action = modButtonPressed },
+ new ConversionSection { Action = modButtonPressed },
+ new FunSection { Action = modButtonPressed },
+ }
+ },
+ },
+ },
+ new Drawable[]
+ {
+ // Footer
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = new Color4(172, 20, 116, 255),
+ Alpha = 0.5f,
},
- Children = new Drawable[]
+ footerContainer = new FillFlowContainer
{
- DeselectAllButton = new TriangleButton
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Width = content_width,
+ Direction = FillDirection.Horizontal,
+ Padding = new MarginPadding
{
- Width = 180,
- Text = "Deselect All",
- Action = DeselectAll,
- Margin = new MarginPadding
- {
- Right = 20
- }
+ Vertical = 15,
+ Left = overflow_padding,
+ Right = overflow_padding
},
- new OsuSpriteText
+ Children = new Drawable[]
{
- Text = @"Score Multiplier:",
- TextSize = 30,
- Margin = new MarginPadding
+ DeselectAllButton = new TriangleButton
{
- Top = 5,
- Right = 10
- }
- },
- MultiplierLabel = new OsuSpriteText
- {
- Font = @"Exo2.0-Bold",
- TextSize = 30,
- Margin = new MarginPadding
+ Width = 180,
+ Text = "Deselect All",
+ Action = DeselectAll,
+ Margin = new MarginPadding
+ {
+ Right = 20
+ }
+ },
+ new OsuSpriteText
{
- Top = 5
- }
- },
- UnrankedLabel = new OsuSpriteText
- {
- Font = @"Exo2.0-Bold",
- Text = @"(Unranked)",
- TextSize = 30,
- Margin = new MarginPadding
+ Text = @"Score Multiplier:",
+ TextSize = 30,
+ Margin = new MarginPadding
+ {
+ Top = 5,
+ Right = 10
+ }
+ },
+ MultiplierLabel = new OsuSpriteText
{
- Top = 5,
- Left = 10
+ Font = @"Exo2.0-Bold",
+ TextSize = 30,
+ Margin = new MarginPadding
+ {
+ Top = 5
+ }
+ },
+ UnrankedLabel = new OsuSpriteText
+ {
+ Font = @"Exo2.0-Bold",
+ Text = @"(Unranked)",
+ TextSize = 30,
+ Margin = new MarginPadding
+ {
+ Top = 5,
+ Left = 10
+ }
}
}
}
- }
- },
+ },
+ }
},
},
},
diff --git a/osu.Game/Overlays/Mods/Sections/AutomationSection.cs b/osu.Game/Overlays/Mods/Sections/AutomationSection.cs
new file mode 100644
index 0000000000..2b509d539e
--- /dev/null
+++ b/osu.Game/Overlays/Mods/Sections/AutomationSection.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Mods;
+using OpenTK.Input;
+
+namespace osu.Game.Overlays.Mods.Sections
+{
+ public class AutomationSection : ModSection
+ {
+ protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M };
+ public override ModType ModType => ModType.Automation;
+
+ public AutomationSection()
+ {
+ Header = @"Automation";
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Mods/Sections/ConversionSection.cs b/osu.Game/Overlays/Mods/Sections/ConversionSection.cs
new file mode 100644
index 0000000000..568f0ecfce
--- /dev/null
+++ b/osu.Game/Overlays/Mods/Sections/ConversionSection.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Mods;
+using OpenTK.Input;
+
+namespace osu.Game.Overlays.Mods.Sections
+{
+ public class ConversionSection : ModSection
+ {
+ protected override Key[] ToggleKeys => null;
+ public override ModType ModType => ModType.Conversion;
+
+ public ConversionSection()
+ {
+ Header = @"Conversion";
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs b/osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs
similarity index 70%
rename from osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs
rename to osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs
index d7d9a90e77..5aced7ed5d 100644
--- a/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs
+++ b/osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs
@@ -1,24 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK.Input;
-using osu.Framework.Allocation;
-using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
+using OpenTK.Input;
-namespace osu.Game.Overlays.Mods
+namespace osu.Game.Overlays.Mods.Sections
{
public class DifficultyIncreaseSection : ModSection
{
protected override Key[] ToggleKeys => new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L };
public override ModType ModType => ModType.DifficultyIncrease;
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- SelectedColour = colours.YellowLight;
- }
-
public DifficultyIncreaseSection()
{
Header = @"Difficulty Increase";
diff --git a/osu.Game/Overlays/Mods/DifficultyReductionSection.cs b/osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs
similarity index 70%
rename from osu.Game/Overlays/Mods/DifficultyReductionSection.cs
rename to osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs
index 013deea579..29fae2c70a 100644
--- a/osu.Game/Overlays/Mods/DifficultyReductionSection.cs
+++ b/osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs
@@ -1,24 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK.Input;
-using osu.Framework.Allocation;
-using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
+using OpenTK.Input;
-namespace osu.Game.Overlays.Mods
+namespace osu.Game.Overlays.Mods.Sections
{
public class DifficultyReductionSection : ModSection
{
protected override Key[] ToggleKeys => new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P };
public override ModType ModType => ModType.DifficultyReduction;
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- SelectedColour = colours.GreenLight;
- }
-
public DifficultyReductionSection()
{
Header = @"Difficulty Reduction";
diff --git a/osu.Game/Overlays/Mods/Sections/FunSection.cs b/osu.Game/Overlays/Mods/Sections/FunSection.cs
new file mode 100644
index 0000000000..ef975d9d75
--- /dev/null
+++ b/osu.Game/Overlays/Mods/Sections/FunSection.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Mods;
+using OpenTK.Input;
+
+namespace osu.Game.Overlays.Mods.Sections
+{
+ public class FunSection : ModSection
+ {
+ protected override Key[] ToggleKeys => null;
+ public override ModType ModType => ModType.Fun;
+
+ public FunSection()
+ {
+ Header = @"Fun";
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Mods/SpecialSection.cs b/osu.Game/Overlays/Mods/SpecialSection.cs
deleted file mode 100644
index b3540cf915..0000000000
--- a/osu.Game/Overlays/Mods/SpecialSection.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using OpenTK.Input;
-using osu.Framework.Allocation;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.Mods;
-
-namespace osu.Game.Overlays.Mods
-{
- public class SpecialSection : ModSection
- {
- protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M };
- public override ModType ModType => ModType.Special;
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- SelectedColour = colours.BlueLight;
- }
-
- public SpecialSection()
- {
- Header = @"Special";
- }
- }
-}
diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs
index a630a86fce..62c9020160 100644
--- a/osu.Game/Overlays/Music/PlaylistItem.cs
+++ b/osu.Game/Overlays/Music/PlaylistItem.cs
@@ -8,7 +8,8 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs
index 19ce0cf932..32674f0a84 100644
--- a/osu.Game/Overlays/Music/PlaylistList.cs
+++ b/osu.Game/Overlays/Music/PlaylistList.cs
@@ -8,7 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using OpenTK;
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index a57d5fd183..886b5fb95b 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -13,7 +13,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Localisation;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
@@ -142,14 +142,14 @@ namespace osu.Game.Overlays
Anchor = Anchor.Centre,
Children = new[]
{
- prevButton = new IconButton
+ prevButton = new MusicIconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = prev,
Icon = FontAwesome.fa_step_backward,
},
- playButton = new IconButton
+ playButton = new MusicIconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -158,7 +158,7 @@ namespace osu.Game.Overlays
Action = play,
Icon = FontAwesome.fa_play_circle_o,
},
- nextButton = new IconButton
+ nextButton = new MusicIconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -167,7 +167,7 @@ namespace osu.Game.Overlays
},
}
},
- playlistButton = new IconButton
+ playlistButton = new MusicIconButton
{
Origin = Anchor.Centre,
Anchor = Anchor.CentreRight,
@@ -183,7 +183,7 @@ namespace osu.Game.Overlays
Anchor = Anchor.BottomCentre,
Height = progress_height,
FillColour = colours.Yellow,
- OnSeek = progress => current?.Track.Seek(progress)
+ OnSeek = attemptSeek
}
},
},
@@ -198,6 +198,12 @@ namespace osu.Game.Overlays
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
}
+ private void attemptSeek(double progress)
+ {
+ if (!beatmap.Disabled)
+ current?.Track.Seek(progress);
+ }
+
private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index)
{
beatmapSets.Remove(beatmapSetInfo);
@@ -219,6 +225,7 @@ namespace osu.Game.Overlays
if (disabled)
playlist.Hide();
+ playButton.Enabled.Value = !disabled;
prevButton.Enabled.Value = !disabled;
nextButton.Enabled.Value = !disabled;
playlistButton.Enabled.Value = !disabled;
@@ -405,6 +412,16 @@ namespace osu.Game.Overlays
Prev
}
+ private class MusicIconButton : IconButton
+ {
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ HoverColour = colours.YellowDark.Opacity(0.6f);
+ FlashColour = colours.Yellow;
+ }
+ }
+
private class Background : BufferedContainer
{
private readonly Sprite sprite;
diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs
index 3dc8f5ec15..78f8f57343 100644
--- a/osu.Game/Overlays/NotificationOverlay.cs
+++ b/osu.Game/Overlays/NotificationOverlay.cs
@@ -96,8 +96,7 @@ namespace osu.Game.Overlays
base.LoadComplete();
StateChanged += _ => updateProcessingMode();
- OverlayActivationMode.ValueChanged += _ => updateProcessingMode();
- OverlayActivationMode.TriggerChange();
+ OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true);
}
private int totalCount => sections.Select(c => c.DisplayedCount).Sum();
@@ -128,7 +127,8 @@ namespace osu.Game.Overlays
var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)));
section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth);
- State = Visibility.Visible;
+ if (notification.IsImportant)
+ State = Visibility.Visible;
updateCounts();
});
diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs
index d2b5ae1829..6798ae2bb2 100644
--- a/osu.Game/Overlays/Notifications/Notification.cs
+++ b/osu.Game/Overlays/Notifications/Notification.cs
@@ -7,11 +7,11 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.States;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Notifications
@@ -23,6 +23,11 @@ namespace osu.Game.Overlays.Notifications
///
public event Action Closed;
+ ///
+ /// Whether this notification should forcefully display itself.
+ ///
+ public virtual bool IsImportant => true;
+
///
/// Run on user activating the notification. Return true to close.
///
diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs
index c166624d2b..f41e3e876f 100644
--- a/osu.Game/Overlays/Notifications/NotificationSection.cs
+++ b/osu.Game/Overlays/Notifications/NotificationSection.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Notifications
set
{
title = value;
- if (titleText != null) titleText.Text = title.ToUpper();
+ if (titleText != null) titleText.Text = title.ToUpperInvariant();
}
}
@@ -101,7 +101,7 @@ namespace osu.Game.Overlays.Notifications
{
titleText = new OsuSpriteText
{
- Text = title.ToUpper(),
+ Text = title.ToUpperInvariant(),
Font = @"Exo2.0-Black",
},
countText = new OsuSpriteText
@@ -154,7 +154,7 @@ namespace osu.Game.Overlays.Notifications
public string Text
{
get { return text.Text; }
- set { text.Text = value.ToUpper(); }
+ set { text.Text = value.ToUpperInvariant(); }
}
}
diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs
index 1c80f2e626..041ceab365 100644
--- a/osu.Game/Overlays/OnScreenDisplay.cs
+++ b/osu.Game/Overlays/OnScreenDisplay.cs
@@ -14,6 +14,8 @@ using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics.Transforms;
+using osu.Framework.Threading;
using osu.Game.Configuration;
using osu.Game.Graphics.Sprites;
@@ -135,7 +137,7 @@ namespace osu.Game.Overlays
/// If is already being tracked from the same .
public void BeginTracking(object source, ITrackableConfigManager configManager)
{
- if (configManager == null) throw new ArgumentNullException(nameof(configManager));
+ if (configManager == null) throw new ArgumentNullException(nameof(configManager));
if (trackedConfigManagers.ContainsKey((source, configManager)))
throw new InvalidOperationException($"{nameof(configManager)} is already registered.");
@@ -159,7 +161,7 @@ namespace osu.Game.Overlays
/// If is not being tracked from the same .
public void StopTracking(object source, ITrackableConfigManager configManager)
{
- if (configManager == null) throw new ArgumentNullException(nameof(configManager));
+ if (configManager == null) throw new ArgumentNullException(nameof(configManager));
if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing))
throw new InvalidOperationException($"{nameof(configManager)} is not registered.");
@@ -174,28 +176,29 @@ namespace osu.Game.Overlays
{
Schedule(() =>
{
- textLine1.Text = description.Name.ToUpper();
+ textLine1.Text = description.Name.ToUpperInvariant();
textLine2.Text = description.Value;
- textLine3.Text = description.Shortcut.ToUpper();
+ textLine3.Text = description.Shortcut.ToUpperInvariant();
if (string.IsNullOrEmpty(textLine3.Text))
textLine3.Text = "NO KEY BOUND";
- Display(box);
+ DisplayTemporarily(box);
int optionCount = 0;
int selectedOption = -1;
- if (description.RawValue is bool)
+ switch (description.RawValue)
{
- optionCount = 1;
- if ((bool)description.RawValue) selectedOption = 0;
- }
- else if (description.RawValue is Enum)
- {
- var values = Enum.GetValues(description.RawValue.GetType());
- optionCount = values.Length;
- selectedOption = Convert.ToInt32(description.RawValue);
+ case bool val:
+ optionCount = 1;
+ if (val) selectedOption = 0;
+ break;
+ case Enum _:
+ var values = Enum.GetValues(description.RawValue.GetType());
+ optionCount = values.Length;
+ selectedOption = Convert.ToInt32(description.RawValue);
+ break;
}
textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre;
@@ -213,15 +216,29 @@ namespace osu.Game.Overlays
});
}
- protected virtual void Display(Drawable toDisplay)
+ private TransformSequence fadeIn;
+ private ScheduledDelegate fadeOut;
+
+ protected virtual void DisplayTemporarily(Drawable toDisplay)
{
- toDisplay.Animate(
- b => b.FadeIn(500, Easing.OutQuint),
- b => b.ResizeHeightTo(height, 500, Easing.OutQuint)
- ).Then(
- b => b.FadeOutFromOne(1500, Easing.InQuint),
- b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint)
- );
+ // avoid starting a new fade-in if one is already active.
+ if (fadeIn == null)
+ {
+ fadeIn = toDisplay.Animate(
+ b => b.FadeIn(500, Easing.OutQuint),
+ b => b.ResizeHeightTo(height, 500, Easing.OutQuint)
+ );
+
+ fadeIn.Finally(_ => fadeIn = null);
+ }
+
+ fadeOut?.Cancel();
+ fadeOut = Scheduler.AddDelayed(() =>
+ {
+ toDisplay.Animate(
+ b => b.FadeOutFromOne(1500, Easing.InQuint),
+ b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint));
+ }, 500);
}
private class OptionLight : Container
diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs
index f968f94187..bfade5e45c 100644
--- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs
+++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs
@@ -9,7 +9,7 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
diff --git a/osu.Game/Overlays/Profile/Header/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs
index 2c70507536..a059792796 100644
--- a/osu.Game/Overlays/Profile/Header/RankGraph.cs
+++ b/osu.Game/Overlays/Profile/Header/RankGraph.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index c72ff6131b..9d09836d25 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -158,6 +158,13 @@ namespace osu.Game.Overlays.Profile
}
}
},
+ new Box // this is a temporary workaround for incorrect masking behaviour of FillMode.Fill used in UserCoverBackground (see https://github.com/ppy/osu-framework/issues/1675)
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 1,
+ Y = cover_height,
+ Colour = OsuColour.Gray(34),
+ },
infoTextLeft = new LinkFlowContainer(t => t.TextSize = 14)
{
X = UserProfileOverlay.CONTENT_X_MARGIN,
@@ -360,11 +367,6 @@ namespace osu.Game.Overlays.Profile
Text = text
};
- if (user.Age != null)
- {
- infoTextLeft.AddText($"{user.Age} years old ", boldItalic);
- }
-
if (user.Country != null)
{
infoTextLeft.AddText("From ", lightText);
diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs
index 359bfc7564..1a1f13933d 100644
--- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs
+++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs
@@ -9,6 +9,7 @@ using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
+
namespace osu.Game.Overlays.Profile.Sections
{
///
@@ -32,7 +33,10 @@ namespace osu.Game.Overlays.Profile.Sections
{
Action = () =>
{
- if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null) beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value);
+ if (beatmap.OnlineBeatmapID != null)
+ beatmapSetOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value);
+ else if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null)
+ beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value);
};
Child = new FillFlowContainer
diff --git a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs
index 3c841cbf14..3a4bfc6804 100644
--- a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs
+++ b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs
@@ -6,7 +6,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
index 15b446efa5..38bc419838 100644
--- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
+++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
@@ -8,7 +8,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
index 47cdb4a765..f9e4a983bb 100644
--- a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
+++ b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
@@ -5,7 +5,7 @@ using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
diff --git a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs
index 05104018cd..60a1c7c125 100644
--- a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs
+++ b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs
@@ -3,6 +3,7 @@
using osu.Framework.Allocation;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Configuration;
namespace osu.Game.Overlays.Settings
{
@@ -14,6 +15,8 @@ namespace osu.Game.Overlays.Settings
{
private readonly Ruleset ruleset;
+ protected IRulesetConfigManager Config;
+
protected RulesetSettingsSubsection(Ruleset ruleset)
{
this.ruleset = ruleset;
@@ -21,13 +24,13 @@ namespace osu.Game.Overlays.Settings
private DependencyContainer dependencies;
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
- var config = dependencies.Get().GetConfigFor(ruleset);
- if (config != null)
- dependencies.Cache(config);
+ Config = dependencies.Get().GetConfigFor(ruleset);
+ if (Config != null)
+ dependencies.Cache(Config);
return dependencies;
}
diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs
index 8850f716e0..0fe41327db 100644
--- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
{
LabelText = "Audio Offset",
Bindable = config.GetBindable(OsuSetting.AudioOffset),
- KeyboardStep = 100f
+ KeyboardStep = 1f
},
new SettingsButton
{
diff --git a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs
index 80896c163f..369c751448 100644
--- a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs
@@ -17,10 +17,10 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
{
Children = new Drawable[]
{
- new SettingsSlider { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.1f },
- new SettingsSlider { LabelText = "Master (Window Inactive)", Bindable = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.1f },
- new SettingsSlider { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.1f },
- new SettingsSlider { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.1f },
+ new SettingsSlider { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f },
+ new SettingsSlider { LabelText = "Master (Window Inactive)", Bindable = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.01f },
+ new SettingsSlider { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.01f },
+ new SettingsSlider { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f },
};
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs
index 9f550413f3..b14a4b8773 100644
--- a/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs
@@ -13,15 +13,18 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
{
protected override string Header => "Garbage Collector";
+ private readonly Bindable latencyMode = new Bindable();
+ private Bindable configLatencyMode;
+
[BackgroundDependencyLoader]
private void load(FrameworkDebugConfigManager config)
{
Children = new Drawable[]
{
- new SettingsEnumDropdown
+ new SettingsEnumDropdown
{
LabelText = "Active mode",
- Bindable = config.GetBindable(DebugSetting.ActiveGCMode)
+ Bindable = latencyMode
},
new SettingsButton
{
@@ -29,6 +32,18 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
Action = GC.Collect
},
};
+
+ configLatencyMode = config.GetBindable(DebugSetting.ActiveGCMode);
+ configLatencyMode.BindValueChanged(v => latencyMode.Value = (LatencyMode)v, true);
+ latencyMode.BindValueChanged(v => configLatencyMode.Value = (GCLatencyMode)v);
+ }
+
+ private enum LatencyMode
+ {
+ Batch = GCLatencyMode.Batch,
+ Interactive = GCLatencyMode.Interactive,
+ LowLatency = GCLatencyMode.LowLatency,
+ SustainedLowLatency = GCLatencyMode.SustainedLowLatency
}
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
index 9c8f5e2643..21d5d452bf 100644
--- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
@@ -21,13 +21,13 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{
LabelText = "Background dim",
Bindable = config.GetBindable(OsuSetting.DimLevel),
- KeyboardStep = 0.1f
+ KeyboardStep = 0.01f
},
new SettingsSlider
{
LabelText = "Background blur",
Bindable = config.GetBindable(OsuSetting.BlurLevel),
- KeyboardStep = 0.1f
+ KeyboardStep = 0.01f
},
new SettingsCheckbox
{
diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
index 7893d76fb8..235ff0f319 100644
--- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
@@ -31,13 +31,13 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{
LabelText = "Display beatmaps from",
Bindable = config.GetBindable(OsuSetting.DisplayStarsMinimum),
- KeyboardStep = 1f
+ KeyboardStep = 0.1f
},
new SettingsSlider
{
LabelText = "up to",
Bindable = config.GetBindable(OsuSetting.DisplayStarsMaximum),
- KeyboardStep = 1f
+ KeyboardStep = 0.1f
},
new SettingsEnumDropdown
{
diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
index 9a42bdd2aa..c7f98f4107 100644
--- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
@@ -11,12 +11,12 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using OpenTK;
-using osu.Framework.Input;
using osu.Game.Users;
using System.ComponentModel;
using osu.Game.Graphics;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Input.States;
using RectangleF = osu.Framework.Graphics.Primitives.RectangleF;
using Container = osu.Framework.Graphics.Containers.Container;
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
index 1f87a635de..42028f6bd5 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
@@ -50,13 +50,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{
LabelText = "Horizontal position",
Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionX),
- KeyboardStep = 0.1f
+ KeyboardStep = 0.01f
},
new SettingsSlider
{
LabelText = "Vertical position",
Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionY),
- KeyboardStep = 0.1f
+ KeyboardStep = 0.01f
},
}
},
diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
index 51a624330b..71ab4d3782 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
@@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index 18a371e904..df8ebaf4aa 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -36,13 +36,13 @@ namespace osu.Game.Overlays.Settings.Sections
{
LabelText = "Menu cursor size",
Bindable = config.GetBindable(OsuSetting.MenuCursorSize),
- KeyboardStep = 0.1f
+ KeyboardStep = 0.01f
},
new SettingsSlider
{
LabelText = "Gameplay cursor size",
Bindable = config.GetBindable(OsuSetting.GameplayCursorSize),
- KeyboardStep = 0.1f
+ KeyboardStep = 0.01f
},
new SettingsCheckbox
{
diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs
index 4c1ea1f32e..0f8d3aa2ac 100644
--- a/osu.Game/Overlays/Settings/SettingsItem.cs
+++ b/osu.Game/Overlays/Settings/SettingsItem.cs
@@ -12,7 +12,8 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs
index 82589a99bd..9296972749 100644
--- a/osu.Game/Overlays/Settings/SettingsSubsection.cs
+++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs
@@ -51,7 +51,7 @@ namespace osu.Game.Overlays.Settings
{
new OsuSpriteText
{
- Text = Header.ToUpper(),
+ Text = Header.ToUpperInvariant(),
Margin = new MarginPadding { Bottom = 10, Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS },
Font = @"Exo2.0-Black",
},
diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs
index 50452a7110..fdda5b870e 100644
--- a/osu.Game/Overlays/Settings/Sidebar.cs
+++ b/osu.Game/Overlays/Settings/Sidebar.cs
@@ -9,7 +9,7 @@ using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Threading;
using osu.Game.Overlays.Toolbar;
diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs
index 0a3a30480b..28e2b773ec 100644
--- a/osu.Game/Overlays/Settings/SidebarButton.cs
+++ b/osu.Game/Overlays/Settings/SidebarButton.cs
@@ -9,14 +9,15 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
{
- public class SidebarButton : OsuButton
+ public class SidebarButton : Button
{
private readonly SpriteIcon drawableIcon;
private readonly SpriteText headerText;
@@ -97,7 +98,8 @@ namespace osu.Game.Overlays.Settings
Width = 5,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
- }
+ },
+ new HoverClickSounds(HoverSampleSet.Loud),
});
}
diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs
index 55326b53ed..83e121e998 100644
--- a/osu.Game/Overlays/SettingsOverlay.cs
+++ b/osu.Game/Overlays/SettingsOverlay.cs
@@ -11,7 +11,7 @@ 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.Input.States;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
diff --git a/osu.Game/Overlays/Social/SocialPanel.cs b/osu.Game/Overlays/Social/SocialPanel.cs
index 2411db7535..b0455f7edd 100644
--- a/osu.Game/Overlays/Social/SocialPanel.cs
+++ b/osu.Game/Overlays/Social/SocialPanel.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.Input;
+using osu.Framework.Input.States;
using osu.Game.Users;
namespace osu.Game.Overlays.Social
diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs
index 48d0674b3d..2eabf1eb74 100644
--- a/osu.Game/Overlays/Toolbar/Toolbar.cs
+++ b/osu.Game/Overlays/Toolbar/Toolbar.cs
@@ -6,12 +6,12 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
+using osu.Framework.Input.States;
namespace osu.Game.Overlays.Toolbar
{
@@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Toolbar
{
Action = () => OnHome?.Invoke()
},
- new ToolbarModeSelector()
+ new ToolbarRulesetSelector()
}
},
new FillFlowContainer
diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs
index c7870a72de..74af5d7e9c 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs
@@ -5,13 +5,14 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
@@ -118,9 +119,9 @@ namespace osu.Game.Overlays.Toolbar
{
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.Both, //stops us being considered in parent's autosize
- Anchor = (TooltipAnchor & Anchor.x0) > 0 ? Anchor.BottomLeft : Anchor.BottomRight,
+ Anchor = TooltipAnchor.HasFlag(Anchor.x0) ? Anchor.BottomLeft : Anchor.BottomRight,
Origin = TooltipAnchor,
- Position = new Vector2((TooltipAnchor & Anchor.x0) > 0 ? 5 : -5, 5),
+ Position = new Vector2(TooltipAnchor.HasFlag(Anchor.x0) ? 5 : -5, 5),
Alpha = 0,
Children = new[]
{
diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs
similarity index 96%
rename from osu.Game/Overlays/Toolbar/ToolbarModeButton.cs
rename to osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs
index 90b9abb2e4..bbdf796e7a 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs
@@ -7,7 +7,7 @@ using OpenTK.Graphics;
namespace osu.Game.Overlays.Toolbar
{
- public class ToolbarModeButton : ToolbarButton
+ public class ToolbarRulesetButton : ToolbarButton
{
private RulesetInfo ruleset;
public RulesetInfo Ruleset
diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
similarity index 92%
rename from osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs
rename to osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
index dae4f84b1a..f2744ae83f 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
@@ -6,28 +6,29 @@ using osu.Framework.Allocation;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
using OpenTK;
using OpenTK.Input;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.Toolbar
{
- public class ToolbarModeSelector : Container
+ public class ToolbarRulesetSelector : Container
{
private const float padding = 10;
private readonly FillFlowContainer modeButtons;
private readonly Drawable modeButtonLine;
- private ToolbarModeButton activeButton;
+ private ToolbarRulesetButton activeButton;
private RulesetStore rulesets;
private readonly Bindable ruleset = new Bindable();
- public ToolbarModeSelector()
+ public ToolbarRulesetSelector()
{
RelativeSizeAxes = Axes.Y;
@@ -73,7 +74,7 @@ namespace osu.Game.Overlays.Toolbar
this.rulesets = rulesets;
foreach (var r in rulesets.AvailableRulesets)
{
- modeButtons.Add(new ToolbarModeButton
+ modeButtons.Add(new ToolbarRulesetButton
{
Ruleset = r,
Action = delegate { ruleset.Value = r; }
@@ -115,7 +116,7 @@ namespace osu.Game.Overlays.Toolbar
private void rulesetChanged(RulesetInfo ruleset)
{
- foreach (ToolbarModeButton m in modeButtons.Children.Cast())
+ foreach (ToolbarRulesetButton m in modeButtons.Children.Cast())
{
bool isActive = m.Ruleset.ID == ruleset.ID;
m.Active = isActive;
diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs
index 745f2f3def..11b68b0e09 100644
--- a/osu.Game/Overlays/UserProfileOverlay.cs
+++ b/osu.Game/Overlays/UserProfileOverlay.cs
@@ -195,7 +195,7 @@ namespace osu.Game.Overlays
TabContainer.AutoSizeAxes |= Axes.X;
TabContainer.Anchor |= Anchor.x1;
TabContainer.Origin |= Anchor.x1;
- Add(bottom = new Box
+ AddInternal(bottom = new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
diff --git a/osu.Game/Overlays/Volume/MuteButton.cs b/osu.Game/Overlays/Volume/MuteButton.cs
index d0aa58e668..47169d7a7b 100644
--- a/osu.Game/Overlays/Volume/MuteButton.cs
+++ b/osu.Game/Overlays/Volume/MuteButton.cs
@@ -9,7 +9,7 @@ using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs
index 9aeca1f35f..a6c98aa97e 100644
--- a/osu.Game/Overlays/Volume/VolumeMeter.cs
+++ b/osu.Game/Overlays/Volume/VolumeMeter.cs
@@ -11,7 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs
index e40597b2d4..4dcdd23768 100644
--- a/osu.Game/Overlays/VolumeOverlay.cs
+++ b/osu.Game/Overlays/VolumeOverlay.cs
@@ -9,7 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Input.Bindings;
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index 8f9651ab09..0de0a620e3 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -62,15 +62,19 @@ namespace osu.Game.Rulesets.Difficulty
IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Mod[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0)
{
- // Initial-case: Empty current set
- if (currentSetCount == 0)
- yield return new NoModMod();
-
- if (currentSetCount == 1)
- yield return currentSet.Single();
-
- if (currentSetCount > 1)
- yield return new MultiMod(currentSet.ToArray());
+ switch (currentSetCount)
+ {
+ case 0:
+ // Initial-case: Empty current set
+ yield return new NoModMod();
+ break;
+ case 1:
+ yield return currentSet.Single();
+ break;
+ default:
+ yield return new MultiMod(currentSet.ToArray());
+ break;
+ }
// Apply mods in the adjustment set recursively. Using the entire adjustment set would result in duplicate multi-mod mod
// combinations in further recursions, so a moving subset is used to eliminate this effect
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 0c91c9f548..a3253250f2 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
@@ -23,12 +24,15 @@ namespace osu.Game.Rulesets.Edit
{
private readonly Ruleset ruleset;
+ public IEnumerable HitObjects => rulesetContainer.Playfield.AllHitObjects;
+
protected ICompositionTool CurrentTool { get; private set; }
+ protected IRulesetConfigManager Config { get; private set; }
+
+ private readonly List layerContainers = new List();
+ private readonly IBindable beatmap = new Bindable();
private RulesetContainer rulesetContainer;
- private readonly List layerContainers = new List();
-
- private readonly IBindable beatmap = new Bindable();
protected HitObjectComposer(Ruleset ruleset)
{
@@ -60,7 +64,7 @@ namespace osu.Game.Rulesets.Edit
};
var layerAboveRuleset = CreateLayerContainer();
- layerAboveRuleset.Child = new HitObjectMaskLayer(rulesetContainer.Playfield, this);
+ layerAboveRuleset.Child = new HitObjectMaskLayer();
layerContainers.Add(layerBelowRuleset);
layerContainers.Add(layerAboveRuleset);
@@ -110,6 +114,23 @@ namespace osu.Game.Rulesets.Edit
toolboxCollection.Items[0].Select();
}
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
+ {
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+
+ dependencies.CacheAs(this);
+ Config = dependencies.Get().GetConfigFor(ruleset);
+
+ return dependencies;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ rulesetContainer.Playfield.DisplayJudgements.Value = false;
+ }
+
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs
index 61fb700dd3..ada026b32f 100644
--- a/osu.Game/Rulesets/Edit/HitObjectMask.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs
@@ -5,7 +5,8 @@ using System;
using osu.Framework;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
diff --git a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs
index 2c3720fc8f..78ad236e74 100644
--- a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs
+++ b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs
@@ -8,6 +8,16 @@ namespace osu.Game.Rulesets.Edit.Tools
public class HitObjectCompositionTool : ICompositionTool
where T : HitObject
{
- public string Name => typeof(T).Name;
+ public string Name { get; }
+
+ public HitObjectCompositionTool()
+ : this(typeof(T).Name)
+ {
+ }
+
+ public HitObjectCompositionTool(string name)
+ {
+ Name = name;
+ }
}
}
diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
index 74ee025823..65b2ef75c4 100644
--- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
+++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Judgements
private OsuColour colours;
- protected readonly Judgement Judgement;
+ protected readonly JudgementResult Result;
public readonly DrawableHitObject JudgedObject;
@@ -34,11 +34,11 @@ namespace osu.Game.Rulesets.Judgements
///
/// Creates a drawable which visualises a .
///
- /// The judgement to visualise.
+ /// The judgement to visualise.
/// The object which was judged.
- public DrawableJudgement(Judgement judgement, DrawableHitObject judgedObject)
+ public DrawableJudgement(JudgementResult result, DrawableHitObject judgedObject)
{
- Judgement = judgement;
+ Result = result;
JudgedObject = judgedObject;
Size = new Vector2(judgement_size);
@@ -49,11 +49,11 @@ namespace osu.Game.Rulesets.Judgements
{
this.colours = colours;
- Child = new SkinnableDrawable($"Play/{Judgement.Result}", _ => JudgementText = new OsuSpriteText
+ Child = new SkinnableDrawable($"Play/{Result.Type}", _ => JudgementText = new OsuSpriteText
{
- Text = Judgement.Result.GetDescription().ToUpper(),
+ Text = Result.Type.GetDescription().ToUpperInvariant(),
Font = @"Venera",
- Colour = judgementColour(Judgement.Result),
+ Colour = judgementColour(Result.Type),
Scale = new Vector2(0.85f, 1),
TextSize = 12
}, restrictSize: false);
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Judgements
this.FadeInFromZero(100, Easing.OutQuint);
- switch (Judgement.Result)
+ switch (Result.Type)
{
case HitResult.None:
break;
diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs
index 129dd07c3e..c679df5900 100644
--- a/osu.Game/Rulesets/Judgements/Judgement.cs
+++ b/osu.Game/Rulesets/Judgements/Judgement.cs
@@ -1,74 +1,48 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Judgements
{
+ ///
+ /// The scoring information provided by a .
+ ///
public class Judgement
{
- ///
- /// Whether this judgement is the result of a hit or a miss.
- ///
- public HitResult Result;
-
///
/// The maximum that can be achieved.
///
public virtual HitResult MaxResult => HitResult.Perfect;
///
- /// The combo prior to this judgement occurring.
- ///
- public int ComboAtJudgement;
-
- ///
- /// The highest combo achieved prior to this judgement occurring.
- ///
- public int HighestComboAtJudgement;
-
- ///
- /// Whether a successful hit occurred.
- ///
- public bool IsHit => Result > HitResult.Miss;
-
- ///
- /// Whether this judgement is the final judgement for the hit object.
- ///
- public bool Final = true;
-
- ///
- /// The offset from a perfect hit at which this judgement occurred.
- /// Populated when added via .
- ///
- public double TimeOffset { get; set; }
-
- ///
- /// Whether the should affect the current combo.
+ /// Whether this should affect the current combo.
///
public virtual bool AffectsCombo => true;
///
- /// Whether the should be counted as base (combo) or bonus score.
+ /// Whether this should be counted as base (combo) or bonus score.
///
public virtual bool IsBonus => !AffectsCombo;
///
- /// The numeric representation for the result achieved.
- ///
- public int NumericResult => NumericResultFor(Result);
-
- ///
- /// The numeric representation for the maximum achievable result.
+ /// The numeric score representation for the maximum achievable result.
///
public int MaxNumericResult => NumericResultFor(MaxResult);
///
- /// Convert a to a numeric score representation.
+ /// Retrieves the numeric score representation of a .
///
- /// The value to convert.
- /// The number.
+ /// The to find the numeric score representation for.
+ /// The numeric score representation of .
protected virtual int NumericResultFor(HitResult result) => result > HitResult.Miss ? 1 : 0;
+
+ ///
+ /// Retrieves the numeric score representation of a .
+ ///
+ /// The to find the numeric score representation for.
+ /// The numeric score representation of .
+ public int NumericResultFor(JudgementResult result) => NumericResultFor(result.Type);
}
}
diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs
new file mode 100644
index 0000000000..5cadf7e2ee
--- /dev/null
+++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs
@@ -0,0 +1,59 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Judgements
+{
+ ///
+ /// The scoring result of a .
+ ///
+ public class JudgementResult
+ {
+ ///
+ /// Whether this is the result of a hit or a miss.
+ ///
+ public HitResult Type;
+
+ ///
+ /// The which this applies for.
+ ///
+ public readonly Judgement Judgement;
+
+ ///
+ /// The offset from a perfect hit at which this occurred.
+ /// Populated when this is applied via .
+ ///
+ public double TimeOffset { get; internal set; }
+
+ ///
+ /// The combo prior to this occurring.
+ ///
+ public int ComboAtJudgement { get; internal set; }
+
+ ///
+ /// The highest combo achieved prior to this occurring.
+ ///
+ public int HighestComboAtJudgement { get; internal set; }
+
+ ///
+ /// Whether a miss or hit occurred.
+ ///
+ public bool HasResult => Type > HitResult.None;
+
+ ///
+ /// Whether a successful hit occurred.
+ ///
+ public bool IsHit => Type > HitResult.Miss;
+
+ ///
+ /// Creates a new .
+ ///
+ /// The to refer to for scoring information.
+ public JudgementResult(Judgement judgement)
+ {
+ Judgement = judgement;
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Mods/IUpdatableByPlayfield.cs b/osu.Game/Rulesets/Mods/IUpdatableByPlayfield.cs
new file mode 100644
index 0000000000..be879759bd
--- /dev/null
+++ b/osu.Game/Rulesets/Mods/IUpdatableByPlayfield.cs
@@ -0,0 +1,12 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Rulesets.Mods
+{
+ public interface IUpdatableByPlayfield : IApplicableMod
+ {
+ void Update(Playfield playfield);
+ }
+}
diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs
index 9fb554b5c5..a991f7e7b0 100644
--- a/osu.Game/Rulesets/Mods/Mod.cs
+++ b/osu.Game/Rulesets/Mods/Mod.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mods
///
/// The type of this mod.
///
- public virtual ModType Type => ModType.Special;
+ public virtual ModType Type => ModType.Fun;
///
/// The user readable description of this mod.
diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs
index 7058d1bed6..5c03cb9736 100644
--- a/osu.Game/Rulesets/Mods/ModAutoplay.cs
+++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs
@@ -11,7 +11,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mods
{
- public class ModAutoplay : ModAutoplay, IApplicableToRulesetContainer
+ public abstract class ModAutoplay : ModAutoplay, IApplicableToRulesetContainer
where T : HitObject
{
protected virtual Score CreateReplayScore(Beatmap beatmap) => new Score { Replay = new Replay() };
@@ -26,6 +26,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Autoplay";
public override string ShortenedName => "AT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto;
+ public override ModType Type => ModType.Automation;
public override string Description => "Watch a perfect automated play through the song.";
public override double ScoreMultiplier => 1;
public bool AllowFail => false;
diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs
index 0bd9becd78..04aa295893 100644
--- a/osu.Game/Rulesets/Mods/ModRelax.cs
+++ b/osu.Game/Rulesets/Mods/ModRelax.cs
@@ -11,6 +11,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Relax";
public override string ShortenedName => "RX";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax;
+ public override ModType Type => ModType.Automation;
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) };
}
diff --git a/osu.Game/Rulesets/Mods/ModType.cs b/osu.Game/Rulesets/Mods/ModType.cs
index 913ba23701..9c962bbcd5 100644
--- a/osu.Game/Rulesets/Mods/ModType.cs
+++ b/osu.Game/Rulesets/Mods/ModType.cs
@@ -7,6 +7,8 @@ namespace osu.Game.Rulesets.Mods
{
DifficultyReduction,
DifficultyIncrease,
- Special
+ Conversion,
+ Automation,
+ Fun
}
}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index a22aaa784f..7e3e955740 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
+using osu.Framework.Extensions.TypeExtensions;
using osu.Game.Audio;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
@@ -35,34 +36,44 @@ namespace osu.Game.Rulesets.Objects.Drawables
private readonly Lazy> nestedHitObjects = new Lazy>();
public IEnumerable NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : Enumerable.Empty();
- public event Action OnJudgement;
- public event Action OnJudgementRemoved;
-
- public IReadOnlyList Judgements => judgements;
- private readonly List judgements = new List();
+ ///
+ /// Invoked when a has been applied by this or a nested .
+ ///
+ public event Action OnNewResult;
///
- /// Whether a visible judgement should be displayed when this representation is hit.
+ /// Invoked when a is being reverted by this or a nested .
///
- public virtual bool DisplayJudgement => true;
+ public event Action OnRevertResult;
///
- /// Whether this and all of its nested s have been hit.
+ /// Whether a visual indicator should be displayed when a scoring result occurs.
///
- public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && NestedHitObjects.All(n => n.IsHit);
+ public virtual bool DisplayResult => true;
///
/// Whether this and all of its nested s have been judged.
///
- public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && NestedHitObjects.All(h => h.AllJudged);
+ public bool AllJudged => Judged && NestedHitObjects.All(h => h.AllJudged);
///
- /// Whether this can be judged.
+ /// Whether this has been hit. This occurs if is .
+ /// Note: This does NOT include nested hitobjects.
///
- protected virtual bool ProvidesJudgement => true;
+ public bool IsHit => Result?.IsHit ?? false;
+
+ ///
+ /// Whether this has been judged.
+ /// Note: This does NOT include nested hitobjects.
+ ///
+ public bool Judged => Result?.HasResult ?? true;
+
+ ///
+ /// The scoring result of this .
+ ///
+ public JudgementResult Result { get; private set; }
private bool judgementOccurred;
- private bool judgementFinalized => judgements.LastOrDefault()?.Final == true;
public bool Interactive = true;
public override bool HandleKeyboardInput => Interactive;
@@ -82,6 +93,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
[BackgroundDependencyLoader]
private void load()
{
+ var judgement = HitObject.CreateJudgement();
+ if (judgement != null)
+ {
+ Result = CreateResult(judgement);
+ if (Result == null)
+ throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
+ }
+
var samples = GetSamples().ToArray();
if (samples.Any())
@@ -132,18 +151,17 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
base.Update();
- var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
-
- while (judgements.Count > 0)
+ if (Result != null && Result.HasResult)
{
- var lastJudgement = judgements[judgements.Count - 1];
- if (lastJudgement.TimeOffset + endTime <= Time.Current)
- break;
+ var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
- judgements.RemoveAt(judgements.Count - 1);
- State.Value = ArmedState.Idle;
+ if (Result.TimeOffset + endTime > Time.Current)
+ {
+ OnRevertResult?.Invoke(this, Result);
- OnJudgementRemoved?.Invoke(this, lastJudgement);
+ Result.Type = HitResult.None;
+ State.Value = ArmedState.Idle;
+ }
}
}
@@ -151,33 +169,37 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
base.UpdateAfterChildren();
- UpdateJudgement(false);
+ UpdateResult(false);
}
protected virtual void AddNested(DrawableHitObject h)
{
- h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
- h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j);
+ h.OnNewResult += (d, r) => OnNewResult?.Invoke(d, r);
+ h.OnRevertResult += (d, r) => OnRevertResult?.Invoke(d, r);
h.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j);
nestedHitObjects.Value.Add(h);
}
///
- /// Notifies that a new judgement has occurred for this .
+ /// Applies the of this , notifying responders such as
+ /// the of the .
///
- /// The .
- protected void AddJudgement(Judgement judgement)
+ /// The callback that applies changes to the .
+ protected void ApplyResult(Action application)
{
+ application?.Invoke(Result);
+
+ if (!Result.HasResult)
+ throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}.");
+
judgementOccurred = true;
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
- judgement.TimeOffset = Time.Current - endTime;
+ Result.TimeOffset = Time.Current - endTime;
- judgements.Add(judgement);
-
- switch (judgement.Result)
+ switch (Result.Type)
{
case HitResult.None:
break;
@@ -189,15 +211,15 @@ namespace osu.Game.Rulesets.Objects.Drawables
break;
}
- OnJudgement?.Invoke(this, judgement);
+ OnNewResult?.Invoke(this, Result);
}
///
- /// Processes this , checking if any judgements have occurred.
+ /// Processes this , checking if a scoring result has occurred.
///
/// Whether the user triggered this process.
- /// Whether a judgement has occurred from this or any nested s.
- protected bool UpdateJudgement(bool userTriggered)
+ /// Whether a scoring result has occurred from this or any nested .
+ protected bool UpdateResult(bool userTriggered)
{
judgementOccurred = false;
@@ -205,27 +227,35 @@ namespace osu.Game.Rulesets.Objects.Drawables
return false;
foreach (var d in NestedHitObjects)
- judgementOccurred |= d.UpdateJudgement(userTriggered);
+ judgementOccurred |= d.UpdateResult(userTriggered);
- if (!ProvidesJudgement || judgementFinalized || judgementOccurred)
+ if (judgementOccurred || Judged)
return judgementOccurred;
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
- CheckForJudgements(userTriggered, Time.Current - endTime);
+ CheckForResult(userTriggered, Time.Current - endTime);
return judgementOccurred;
}
///
- /// Checks if any judgements have occurred for this . This method must construct
- /// all s and notify of them through .
+ /// Checks if a scoring result has occurred for this .
///
+ ///
+ /// If a scoring result has occurred, this method must invoke to update the result and notify responders.
+ ///
/// Whether the user triggered this check.
- /// The offset from the end time at which this check occurred. A > 0
- /// implies that this check occurred after the end time of .
- protected virtual void CheckForJudgements(bool userTriggered, double timeOffset)
+ /// The offset from the end time of the at which this check occurred.
+ /// A > 0 implies that this check occurred after the end time of the .
+ protected virtual void CheckForResult(bool userTriggered, double timeOffset)
{
}
+
+ ///
+ /// Creates the that represents the scoring result for this .
+ ///
+ /// The that provides the scoring information.
+ protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(judgement);
}
public abstract class DrawableHitObject : DrawableHitObject
diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs
index 15c24e2975..beb9620f78 100644
--- a/osu.Game/Rulesets/Objects/HitObject.cs
+++ b/osu.Game/Rulesets/Objects/HitObject.cs
@@ -9,6 +9,7 @@ using osu.Framework.Lists;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Objects
@@ -105,6 +106,12 @@ namespace osu.Game.Rulesets.Objects
protected void AddNested(HitObject hitObject) => nestedHitObjects.Value.Add(hitObject);
+ ///
+ /// Creates the that represents the scoring information for this .
+ /// May be null.
+ ///
+ public virtual Judgement CreateJudgement() => null;
+
///
/// Creates the for this .
/// This can be null to indicate that the has no .
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs
index 50035ea116..0573a08361 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs
@@ -13,5 +13,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
public float X { get; set; }
public bool NewCombo { get; set; }
+
+ public int ComboOffset { get; set; }
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
index c7451dc978..802080aedb 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
@@ -13,21 +13,43 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
///
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
- protected override HitObject CreateHit(Vector2 position, bool newCombo)
+ public ConvertHitObjectParser(double offset, int formatVersion)
+ : base(offset, formatVersion)
{
+ }
+
+ private bool forceNewCombo;
+ private int extraComboOffset;
+
+ protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset)
+ {
+ newCombo |= forceNewCombo;
+ comboOffset += extraComboOffset;
+
+ forceNewCombo = false;
+ extraComboOffset = 0;
+
return new ConvertHit
{
X = position.X,
NewCombo = newCombo,
+ ComboOffset = comboOffset
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
+ newCombo |= forceNewCombo;
+ comboOffset += extraComboOffset;
+
+ forceNewCombo = false;
+ extraComboOffset = 0;
+
return new ConvertSlider
{
X = position.X,
- NewCombo = newCombo,
+ NewCombo = FirstObject || newCombo,
+ ComboOffset = comboOffset,
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
@@ -36,15 +58,20 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
};
}
- protected override HitObject CreateSpinner(Vector2 position, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
+ // Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo
+ // Their combo offset is still added to that next hitobject's combo index
+ forceNewCombo |= FormatVersion <= 8 || newCombo;
+ extraComboOffset += comboOffset;
+
return new ConvertSpinner
{
EndTime = endTime
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return null;
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs
index 73e277a125..a187caaa26 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs
@@ -13,5 +13,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
public float X { get; set; }
public bool NewCombo { get; set; }
+
+ public int ComboOffset { get; set; }
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs
index 9dfe12f25e..db79ca60f1 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs
@@ -8,10 +8,14 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
///
/// Legacy osu!catch Spinner-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSpinner : HitObject, IHasEndTime
+ internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasCombo
{
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
+
+ public bool NewCombo { get; set; }
+
+ public int ComboOffset { get; set; }
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index 95589d8953..72168a4cd2 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -10,6 +10,8 @@ using System.IO;
using osu.Game.Beatmaps.Formats;
using osu.Game.Audio;
using System.Linq;
+using JetBrains.Annotations;
+using osu.Framework.Logging;
using osu.Framework.MathUtils;
namespace osu.Game.Rulesets.Objects.Legacy
@@ -19,18 +21,38 @@ namespace osu.Game.Rulesets.Objects.Legacy
///
public abstract class ConvertHitObjectParser : HitObjectParser
{
- public override HitObject Parse(string text)
+ ///
+ /// The offset to apply to all time values.
+ ///
+ protected readonly double Offset;
+
+ ///
+ /// The beatmap version.
+ ///
+ protected readonly int FormatVersion;
+
+ protected bool FirstObject { get; private set; } = true;
+
+ protected ConvertHitObjectParser(double offset, int formatVersion)
{
- return Parse(text, 0);
+ Offset = offset;
+ FormatVersion = formatVersion;
}
- public HitObject Parse(string text, double offset)
+ [CanBeNull]
+ public override HitObject Parse(string text)
{
try
{
string[] split = text.Split(',');
- ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]) & ~ConvertHitObjectType.ColourHax;
+ Vector2 pos = new Vector2((int)Convert.ToSingle(split[0], CultureInfo.InvariantCulture), (int)Convert.ToSingle(split[1], CultureInfo.InvariantCulture));
+
+ ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]);
+
+ int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4;
+ type &= ~ConvertHitObjectType.ComboOffset;
+
bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
type &= ~ConvertHitObjectType.NewCombo;
@@ -39,17 +61,15 @@ namespace osu.Game.Rulesets.Objects.Legacy
HitObject result = null;
- if ((type & ConvertHitObjectType.Circle) > 0)
+ if (type.HasFlag(ConvertHitObjectType.Circle))
{
- result = CreateHit(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo);
+ result = CreateHit(pos, combo, comboOffset);
if (split.Length > 5)
readCustomSampleBanks(split[5], bankInfo);
}
- else if ((type & ConvertHitObjectType.Slider) > 0)
+ else if (type.HasFlag(ConvertHitObjectType.Slider))
{
- var pos = new Vector2(int.Parse(split[0]), int.Parse(split[1]));
-
CurveType curveType = CurveType.Catmull;
double length = 0;
var points = new List { Vector2.Zero };
@@ -148,16 +168,16 @@ namespace osu.Game.Rulesets.Objects.Legacy
for (int i = 0; i < nodes; i++)
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
- result = CreateSlider(pos, combo, points, length, curveType, repeatCount, nodeSamples);
+ result = CreateSlider(pos, combo, comboOffset, points, length, curveType, repeatCount, nodeSamples);
}
- else if ((type & ConvertHitObjectType.Spinner) > 0)
+ else if (type.HasFlag(ConvertHitObjectType.Spinner))
{
- result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture) + offset);
+ result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, Convert.ToDouble(split[5], CultureInfo.InvariantCulture) + Offset);
if (split.Length > 6)
readCustomSampleBanks(split[6], bankInfo);
}
- else if ((type & ConvertHitObjectType.Hold) > 0)
+ else if (type.HasFlag(ConvertHitObjectType.Hold))
{
// Note: Hold is generated by BMS converts
@@ -170,15 +190,20 @@ namespace osu.Game.Rulesets.Objects.Legacy
readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
}
- result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime + offset);
+ result = CreateHold(pos, combo, comboOffset, endTime + Offset);
}
if (result == null)
- throw new InvalidOperationException($@"Unknown hit object type {type}.");
+ {
+ Logger.Log($"Unknown hit object type: {type}. Skipped.", level: LogLevel.Error);
+ return null;
+ }
- result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + offset;
+ result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + Offset;
result.Samples = convertSoundType(soundType, bankInfo);
+ FirstObject = false;
+
return result;
}
catch (FormatException)
@@ -194,19 +219,22 @@ namespace osu.Game.Rulesets.Objects.Legacy
string[] split = str.Split(':');
- var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
- var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
+ var bank = (LegacyBeatmapDecoder.LegacySampleBank)int.Parse(split[0]);
+ var addbank = (LegacyBeatmapDecoder.LegacySampleBank)int.Parse(split[1]);
- string stringBank = bank.ToString().ToLower();
+ string stringBank = bank.ToString().ToLowerInvariant();
if (stringBank == @"none")
stringBank = null;
- string stringAddBank = addbank.ToString().ToLower();
+ string stringAddBank = addbank.ToString().ToLowerInvariant();
if (stringAddBank == @"none")
stringAddBank = null;
bankInfo.Normal = stringBank;
bankInfo.Add = stringAddBank;
+ if (split.Length > 2)
+ bankInfo.CustomSampleBank = int.Parse(split[2]);
+
if (split.Length > 3)
bankInfo.Volume = int.Parse(split[3]);
@@ -218,37 +246,42 @@ namespace osu.Game.Rulesets.Objects.Legacy
///
/// The position of the hit object.
/// Whether the hit object creates a new combo.
+ /// When starting a new combo, the offset of the new combo relative to the current one.
/// The hit object.
- protected abstract HitObject CreateHit(Vector2 position, bool newCombo);
+ protected abstract HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset);
///
/// Creats a legacy Slider-type hit object.
///
/// The position of the hit object.
/// Whether the hit object creates a new combo.
+ /// When starting a new combo, the offset of the new combo relative to the current one.
/// The slider control points.
/// The slider length.
/// The slider curve type.
/// The slider repeat count.
/// The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider.
/// The hit object.
- protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples);
+ protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples);
///
/// Creates a legacy Spinner-type hit object.
///
/// The position of the hit object.
+ /// Whether the hit object creates a new combo.
+ /// When starting a new combo, the offset of the new combo relative to the current one.
/// The spinner end time.
/// The hit object.
- protected abstract HitObject CreateSpinner(Vector2 position, double endTime);
+ protected abstract HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime);
///
/// Creates a legacy Hold-type hit object.
///
/// The position of the hit object.
/// Whether the hit object creates a new combo.
+ /// When starting a new combo, the offset of the new combo relative to the current one.
/// The hold end time.
- protected abstract HitObject CreateHold(Vector2 position, bool newCombo, double endTime);
+ protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime);
private List convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
{
@@ -258,41 +291,45 @@ namespace osu.Game.Rulesets.Objects.Legacy
var soundTypes = new List
{
- new SampleInfo
+ new LegacySampleInfo
{
Bank = bankInfo.Normal,
Name = SampleInfo.HIT_NORMAL,
- Volume = bankInfo.Volume
+ Volume = bankInfo.Volume,
+ CustomSampleBank = bankInfo.CustomSampleBank
}
};
- if ((type & LegacySoundType.Finish) > 0)
+ if (type.HasFlag(LegacySoundType.Finish))
{
- soundTypes.Add(new SampleInfo
+ soundTypes.Add(new LegacySampleInfo
{
Bank = bankInfo.Add,
Name = SampleInfo.HIT_FINISH,
- Volume = bankInfo.Volume
+ Volume = bankInfo.Volume,
+ CustomSampleBank = bankInfo.CustomSampleBank
});
}
- if ((type & LegacySoundType.Whistle) > 0)
+ if (type.HasFlag(LegacySoundType.Whistle))
{
- soundTypes.Add(new SampleInfo
+ soundTypes.Add(new LegacySampleInfo
{
Bank = bankInfo.Add,
Name = SampleInfo.HIT_WHISTLE,
- Volume = bankInfo.Volume
+ Volume = bankInfo.Volume,
+ CustomSampleBank = bankInfo.CustomSampleBank
});
}
- if ((type & LegacySoundType.Clap) > 0)
+ if (type.HasFlag(LegacySoundType.Clap))
{
- soundTypes.Add(new SampleInfo
+ soundTypes.Add(new LegacySampleInfo
{
Bank = bankInfo.Add,
Name = SampleInfo.HIT_CLAP,
- Volume = bankInfo.Volume
+ Volume = bankInfo.Volume,
+ CustomSampleBank = bankInfo.CustomSampleBank
});
}
@@ -307,9 +344,23 @@ namespace osu.Game.Rulesets.Objects.Legacy
public string Add;
public int Volume;
+ public int CustomSampleBank;
+
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
}
+ private class LegacySampleInfo : SampleInfo
+ {
+ public int CustomSampleBank
+ {
+ set
+ {
+ if (value > 1)
+ Suffix = value.ToString();
+ }
+ }
+ }
+
private class FileSampleInfo : SampleInfo
{
public string Filename;
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs
index c0626c3e56..fa47e56de7 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
Slider = 1 << 1,
NewCombo = 1 << 2,
Spinner = 1 << 3,
- ColourHax = 112,
+ ComboOffset = 1 << 4 | 1 << 5 | 1 << 6,
Hold = 1 << 7
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs
index ea4e7f6907..cbc8d2d4df 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs
@@ -8,12 +8,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
///
/// Legacy osu!mania Hit-type, used for parsing Beatmaps.
///
- internal sealed class ConvertHit : HitObject, IHasXPosition, IHasCombo
+ internal sealed class ConvertHit : HitObject, IHasXPosition
{
public float X { get; set; }
- public bool NewCombo { get; set; }
-
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
index 99ba1304e8..6f59965e18 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
@@ -13,21 +13,24 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
///
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
- protected override HitObject CreateHit(Vector2 position, bool newCombo)
+ public ConvertHitObjectParser(double offset, int formatVersion)
+ : base(offset, formatVersion)
+ {
+ }
+
+ protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset)
{
return new ConvertHit
{
- X = position.X,
- NewCombo = newCombo,
+ X = position.X
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
return new ConvertSlider
{
X = position.X,
- NewCombo = newCombo,
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
@@ -36,7 +39,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
};
}
- protected override HitObject CreateSpinner(Vector2 position, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return new ConvertSpinner
{
@@ -45,7 +48,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return new ConvertHold
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs
index a8d7b23df1..e1572889a3 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs
@@ -8,12 +8,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
///
/// Legacy osu!mania Slider-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo
+ internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition
{
public float X { get; set; }
- public bool NewCombo { get; set; }
-
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs
index f015272b2c..0062e83a28 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs
@@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public bool NewCombo { get; set; }
+ public int ComboOffset { get; set; }
+
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
index 801e4ea449..acd0de8688 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
@@ -14,21 +14,43 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
///
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
- protected override HitObject CreateHit(Vector2 position, bool newCombo)
+ public ConvertHitObjectParser(double offset, int formatVersion)
+ : base(offset, formatVersion)
{
+ }
+
+ private bool forceNewCombo;
+ private int extraComboOffset;
+
+ protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset)
+ {
+ newCombo |= forceNewCombo;
+ comboOffset += extraComboOffset;
+
+ forceNewCombo = false;
+ extraComboOffset = 0;
+
return new ConvertHit
{
Position = position,
- NewCombo = newCombo,
+ NewCombo = FirstObject || newCombo,
+ ComboOffset = comboOffset
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
+ newCombo |= forceNewCombo;
+ comboOffset += extraComboOffset;
+
+ forceNewCombo = false;
+ extraComboOffset = 0;
+
return new ConvertSlider
{
Position = position,
- NewCombo = newCombo,
+ NewCombo = FirstObject || newCombo,
+ ComboOffset = comboOffset,
ControlPoints = controlPoints,
Distance = Math.Max(0, length),
CurveType = curveType,
@@ -37,8 +59,13 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
};
}
- protected override HitObject CreateSpinner(Vector2 position, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
+ // Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo
+ // Their combo offset is still added to that next hitobject's combo index
+ forceNewCombo |= FormatVersion <= 8 || newCombo;
+ extraComboOffset += comboOffset;
+
return new ConvertSpinner
{
Position = position,
@@ -46,7 +73,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return null;
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs
index ec5a002bbb..45f7bc9e67 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs
@@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public bool NewCombo { get; set; }
+ public int ComboOffset { get; set; }
+
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
index 0141785f31..3b349d9704 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
///
/// Legacy osu! Spinner-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasPosition
+ internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasPosition, IHasCombo
{
public double EndTime { get; set; }
@@ -22,5 +22,9 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public float Y => Position.Y;
protected override HitWindows CreateHitWindows() => null;
+
+ public bool NewCombo { get; set; }
+
+ public int ComboOffset { get; set; }
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs
index 5e9786c84a..66e504bf22 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs
@@ -1,17 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Rulesets.Objects.Types;
-
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
///
/// Legacy osu!taiko Hit-type, used for parsing Beatmaps.
///
- internal sealed class ConvertHit : HitObject, IHasCombo
+ internal sealed class ConvertHit : HitObject
{
- public bool NewCombo { get; set; }
-
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
index 03b1a3187a..e5904825c2 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
@@ -13,19 +13,20 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
///
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
- protected override HitObject CreateHit(Vector2 position, bool newCombo)
+ public ConvertHitObjectParser(double offset, int formatVersion)
+ : base(offset, formatVersion)
{
- return new ConvertHit
- {
- NewCombo = newCombo,
- };
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
+ protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset)
+ {
+ return new ConvertHit();
+ }
+
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
return new ConvertSlider
{
- NewCombo = newCombo,
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
@@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
};
}
- protected override HitObject CreateSpinner(Vector2 position, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return new ConvertSpinner
{
@@ -42,7 +43,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return null;
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs
index 8a9a0db0a7..11c0a2baae 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs
@@ -1,17 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Rulesets.Objects.Types;
-
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
///
/// Legacy osu!taiko Slider-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasCombo
+ internal sealed class ConvertSlider : Legacy.ConvertSlider
{
- public bool NewCombo { get; set; }
-
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs
index cb8b6f495a..95f1a1cb3d 100644
--- a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs
+++ b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs
@@ -12,5 +12,10 @@ namespace osu.Game.Rulesets.Objects.Types
/// Whether the HitObject starts a new combo.
///
bool NewCombo { get; }
+
+ ///
+ /// When starting a new combo, the offset of the new combo relative to the current one.
+ ///
+ int ComboOffset { get; }
}
}
diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs
index f13d96b35e..edad5cff80 100644
--- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs
+++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs
@@ -3,12 +3,10 @@
using System;
using System.Collections.Generic;
-using osu.Framework.Input;
+using osu.Framework.Input.StateChanges;
using osu.Game.Input.Handlers;
using OpenTK;
using OpenTK.Input;
-using KeyboardState = osu.Framework.Input.KeyboardState;
-using MouseState = osu.Framework.Input.MouseState;
namespace osu.Game.Rulesets.Replays
{
@@ -107,7 +105,7 @@ namespace osu.Game.Rulesets.Replays
return CurrentTime = time;
}
- protected class ReplayMouseState : MouseState
+ protected class ReplayMouseState : osu.Framework.Input.States.MouseState
{
public ReplayMouseState(Vector2 position)
{
@@ -115,7 +113,7 @@ namespace osu.Game.Rulesets.Replays
}
}
- protected class ReplayKeyboardState : KeyboardState
+ protected class ReplayKeyboardState : osu.Framework.Input.States.KeyboardState
{
public ReplayKeyboardState(List keys)
{
diff --git a/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs b/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs
index d39d765bfe..5bd56e0cc0 100644
--- a/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs
+++ b/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs
@@ -15,10 +15,10 @@ namespace osu.Game.Rulesets.Replays.Legacy
public bool MouseLeft => MouseLeft1 || MouseLeft2;
public bool MouseRight => MouseRight1 || MouseRight2;
- public bool MouseLeft1 => (ButtonState & ReplayButtonState.Left1) > 0;
- public bool MouseRight1 => (ButtonState & ReplayButtonState.Right1) > 0;
- public bool MouseLeft2 => (ButtonState & ReplayButtonState.Left2) > 0;
- public bool MouseRight2 => (ButtonState & ReplayButtonState.Right2) > 0;
+ public bool MouseLeft1 => ButtonState.HasFlag(ReplayButtonState.Left1);
+ public bool MouseRight1 => ButtonState.HasFlag(ReplayButtonState.Right1);
+ public bool MouseLeft2 => ButtonState.HasFlag(ReplayButtonState.Left2);
+ public bool MouseRight2 => ButtonState.HasFlag(ReplayButtonState.Right2);
public ReplayButtonState ButtonState;
diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs
index 10463fd961..a6a311f6eb 100644
--- a/osu.Game/Rulesets/RulesetInfo.cs
+++ b/osu.Game/Rulesets/RulesetInfo.cs
@@ -25,5 +25,7 @@ namespace osu.Game.Rulesets
public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this);
public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo;
+
+ public override string ToString() => $"{Name} ({ShortName}) ID: {ID}";
}
}
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 9e8ea0f7c2..b0cea7009e 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using osu.Framework.Configuration;
+using osu.Framework.Extensions.TypeExtensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
@@ -28,7 +29,7 @@ namespace osu.Game.Rulesets.Scoring
///
/// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the .
///
- public event Action NewJudgement;
+ public event Action NewJudgement;
///
/// Additional conditions on top of that cause a failing state.
@@ -144,9 +145,10 @@ namespace osu.Game.Rulesets.Scoring
/// Notifies subscribers of that a new judgement has occurred.
///
/// The judgement to notify subscribers of.
- protected void NotifyNewJudgement(Judgement judgement)
+ /// The judgement scoring result to notify subscribers of.
+ protected void NotifyNewJudgement(JudgementResult result)
{
- NewJudgement?.Invoke(judgement);
+ NewJudgement?.Invoke(result);
if (HasCompleted)
AllJudged?.Invoke();
@@ -194,9 +196,10 @@ namespace osu.Game.Rulesets.Scoring
{
Debug.Assert(base_portion + combo_portion == 1.0);
- rulesetContainer.OnJudgement += AddJudgement;
- rulesetContainer.OnJudgementRemoved += RemoveJudgement;
+ rulesetContainer.OnNewResult += applyResult;
+ rulesetContainer.OnRevertResult += revertResult;
+ ApplyBeatmap(rulesetContainer.Beatmap);
SimulateAutoplay(rulesetContainer.Beatmap);
Reset(true);
@@ -210,46 +213,80 @@ namespace osu.Game.Rulesets.Scoring
}
///
- /// Simulates an autoplay of s that will be judged by this
- /// by adding s for each in the .
- ///
- /// This is required for to work, otherwise will be used.
- ///
+ /// Applies any properties of the which affect scoring to this .
///
- /// The containing the s that will be judged by this .
- protected virtual void SimulateAutoplay(Beatmap beatmap) { }
+ /// The to read properties from.
+ protected virtual void ApplyBeatmap(Beatmap beatmap)
+ {
+ }
///
- /// Adds a judgement to this ScoreProcessor.
+ /// Simulates an autoplay of the to determine scoring values.
///
- /// The judgement to add.
- protected void AddJudgement(Judgement judgement)
+ /// This provided temporarily. DO NOT USE.
+ /// The to simulate.
+ protected virtual void SimulateAutoplay(Beatmap beatmap)
{
- OnNewJudgement(judgement);
+ foreach (var obj in beatmap.HitObjects)
+ simulate(obj);
+
+ void simulate(HitObject obj)
+ {
+ foreach (var nested in obj.NestedHitObjects)
+ simulate(nested);
+
+ var judgement = obj.CreateJudgement();
+ if (judgement == null)
+ return;
+
+ var result = CreateResult(judgement);
+ if (result == null)
+ throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
+
+ result.Type = judgement.MaxResult;
+
+ applyResult(result);
+ }
+ }
+
+ ///
+ /// Applies the score change of a to this .
+ ///
+ /// The to apply.
+ private void applyResult(JudgementResult result)
+ {
+ ApplyResult(result);
updateScore();
UpdateFailed();
- NotifyNewJudgement(judgement);
+ NotifyNewJudgement(result);
}
- protected void RemoveJudgement(Judgement judgement)
+ ///
+ /// Reverts the score change of a that was applied to this .
+ ///
+ /// The judgement to remove.
+ /// The judgement scoring result.
+ private void revertResult(JudgementResult result)
{
- OnJudgementRemoved(judgement);
+ RevertResult(result);
updateScore();
}
///
- /// Applies a judgement.
+ /// Applies the score change of a to this .
///
- /// The judgement to apply/
- protected virtual void OnNewJudgement(Judgement judgement)
+ /// The to apply.
+ protected virtual void ApplyResult(JudgementResult result)
{
- judgement.ComboAtJudgement = Combo;
- judgement.HighestComboAtJudgement = HighestCombo;
+ result.ComboAtJudgement = Combo;
+ result.HighestComboAtJudgement = HighestCombo;
- if (judgement.AffectsCombo)
+ JudgedHits++;
+
+ if (result.Judgement.AffectsCombo)
{
- switch (judgement.Result)
+ switch (result.Type)
{
case HitResult.None:
break;
@@ -260,43 +297,41 @@ namespace osu.Game.Rulesets.Scoring
Combo.Value++;
break;
}
-
- JudgedHits++;
}
- if (judgement.IsBonus)
+ if (result.Judgement.IsBonus)
{
- if (judgement.IsHit)
- bonusScore += judgement.NumericResult;
+ if (result.IsHit)
+ bonusScore += result.Judgement.NumericResultFor(result);
}
else
{
- baseScore += judgement.NumericResult;
- rollingMaxBaseScore += judgement.MaxNumericResult;
+ baseScore += result.Judgement.NumericResultFor(result);
+ rollingMaxBaseScore += result.Judgement.MaxNumericResult;
}
}
///
- /// Removes a judgement. This should reverse everything in .
+ /// Reverts the score change of a that was applied to this .
///
/// The judgement to remove.
- protected virtual void OnJudgementRemoved(Judgement judgement)
+ /// The judgement scoring result.
+ protected virtual void RevertResult(JudgementResult result)
{
- Combo.Value = judgement.ComboAtJudgement;
- HighestCombo.Value = judgement.HighestComboAtJudgement;
+ Combo.Value = result.ComboAtJudgement;
+ HighestCombo.Value = result.HighestComboAtJudgement;
- if (judgement.AffectsCombo)
- JudgedHits--;
+ JudgedHits--;
- if (judgement.IsBonus)
+ if (result.Judgement.IsBonus)
{
- if (judgement.IsHit)
- bonusScore -= judgement.NumericResult;
+ if (result.IsHit)
+ bonusScore -= result.Judgement.NumericResultFor(result);
}
else
{
- baseScore -= judgement.NumericResult;
- rollingMaxBaseScore -= judgement.MaxNumericResult;
+ baseScore -= result.Judgement.NumericResultFor(result);
+ rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
}
}
@@ -333,6 +368,12 @@ namespace osu.Game.Rulesets.Scoring
rollingMaxBaseScore = 0;
bonusScore = 0;
}
+
+ ///
+ /// Creates the that represents the scoring result for a .
+ ///
+ /// The that provides the scoring information.
+ protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(judgement);
}
public enum ScoringMode
diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs
index cdc1248049..92e9a4831f 100644
--- a/osu.Game/Rulesets/UI/ModIcon.cs
+++ b/osu.Game/Rulesets/UI/ModIcon.cs
@@ -79,10 +79,18 @@ namespace osu.Game.Rulesets.UI
backgroundColour = colours.Green;
highlightedColour = colours.GreenLight;
break;
- case ModType.Special:
+ case ModType.Automation:
backgroundColour = colours.Blue;
highlightedColour = colours.BlueLight;
break;
+ case ModType.Conversion:
+ backgroundColour = colours.Purple;
+ highlightedColour = colours.PurpleLight;
+ break;
+ case ModType.Fun:
+ backgroundColour = colours.Pink;
+ highlightedColour = colours.PinkLight;
+ break;
}
applyStyle();
@@ -92,10 +100,7 @@ namespace osu.Game.Rulesets.UI
public bool Highlighted
{
- get
- {
- return highlighted;
- }
+ get { return highlighted; }
set
{
diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs
index f2c9b49900..da14fb54d6 100644
--- a/osu.Game/Rulesets/UI/Playfield.cs
+++ b/osu.Game/Rulesets/UI/Playfield.cs
@@ -1,25 +1,42 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Allocation;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Configuration;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.UI
{
public abstract class Playfield : ScalableContainer
{
///
- /// The HitObjects contained in this Playfield.
+ /// The contained in this Playfield.
///
public HitObjectContainer HitObjects { get; private set; }
///
- /// All the s nested inside this playfield.
+ /// All the s contained in this and all .
///
- public IReadOnlyList NestedPlayfields => nestedPlayfields;
- private List nestedPlayfields;
+ public IEnumerable AllHitObjects => HitObjects?.Objects.Concat(NestedPlayfields.SelectMany(p => p.AllHitObjects)) ?? Enumerable.Empty();
+
+ ///
+ /// All s nested inside this .
+ ///
+ public IEnumerable NestedPlayfields => nestedPlayfields.IsValueCreated ? nestedPlayfields.Value : Enumerable.Empty();
+
+ private readonly Lazy> nestedPlayfields = new Lazy>();
+
+ ///
+ /// Whether judgements should be displayed by this and and all nested s.
+ ///
+ public readonly BindableBool DisplayJudgements = new BindableBool(true);
///
/// A container for keeping track of DrawableHitObjects.
@@ -36,9 +53,13 @@ namespace osu.Game.Rulesets.UI
RelativeSizeAxes = Axes.Both;
}
+ private WorkingBeatmap beatmap;
+
[BackgroundDependencyLoader]
- private void load()
+ private void load(IBindableBeatmap beatmap)
{
+ this.beatmap = beatmap.Value;
+
HitObjects = CreateHitObjectContainer();
HitObjects.RelativeSizeAxes = Axes.Both;
@@ -48,7 +69,7 @@ namespace osu.Game.Rulesets.UI
///
/// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield.
///
- public virtual void PostProcess() => nestedPlayfields?.ForEach(p => p.PostProcess());
+ public virtual void PostProcess() => NestedPlayfields.ForEach(p => p.PostProcess());
///
/// Adds a DrawableHitObject to this Playfield.
@@ -69,15 +90,23 @@ namespace osu.Game.Rulesets.UI
/// The to add.
protected void AddNested(Playfield otherPlayfield)
{
- if (nestedPlayfields == null)
- nestedPlayfields = new List();
-
- nestedPlayfields.Add(otherPlayfield);
+ otherPlayfield.DisplayJudgements.BindTo(DisplayJudgements);
+ nestedPlayfields.Value.Add(otherPlayfield);
}
///
/// Creates the container that will be used to contain the s.
///
protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer();
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (beatmap != null)
+ foreach (var mod in beatmap.Mods.Value)
+ if (mod is IUpdatableByPlayfield updatable)
+ updatable.Update(this);
+ }
}
}
diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs
index fedb6abed3..64ee680d45 100644
--- a/osu.Game/Rulesets/UI/RulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/RulesetContainer.cs
@@ -56,6 +56,12 @@ namespace osu.Game.Rulesets.UI
public abstract IEnumerable Objects { get; }
+ ///
+ /// The point in time at which gameplay starts, including any required lead-in for display purposes.
+ /// Defaults to two seconds before the first . Override as necessary.
+ ///
+ public virtual double GameplayStartTime => Objects.First().StartTime - 2000;
+
private readonly Lazy playfield;
///
@@ -70,7 +76,8 @@ namespace osu.Game.Rulesets.UI
protected readonly Ruleset Ruleset;
- private IRulesetConfigManager rulesetConfig;
+ protected IRulesetConfigManager Config { get; private set; }
+
private OnScreenDisplay onScreenDisplay;
///
@@ -82,20 +89,28 @@ namespace osu.Game.Rulesets.UI
Ruleset = ruleset;
playfield = new Lazy(CreatePlayfield);
+ IsPaused.ValueChanged += paused =>
+ {
+ if (HasReplayLoaded)
+ return;
+
+ KeyBindingInputManager.UseParentInput = !paused;
+ };
+
Cursor = CreateCursor();
}
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
onScreenDisplay = dependencies.Get();
- rulesetConfig = dependencies.Get().GetConfigFor(Ruleset);
- if (rulesetConfig != null)
+ Config = dependencies.Get().GetConfigFor(Ruleset);
+ if (Config != null)
{
- dependencies.Cache(rulesetConfig);
- onScreenDisplay?.BeginTracking(this, rulesetConfig);
+ dependencies.Cache(Config);
+ onScreenDisplay?.BeginTracking(this, Config);
}
return dependencies;
@@ -113,6 +128,11 @@ namespace osu.Game.Rulesets.UI
public Replay Replay { get; private set; }
+ ///
+ /// Whether the game is paused. Used to block user input.
+ ///
+ public readonly BindableBool IsPaused = new BindableBool();
+
///
/// Sets a replay to be used, overriding local input.
///
@@ -143,10 +163,10 @@ namespace osu.Game.Rulesets.UI
{
base.Dispose(isDisposing);
- if (rulesetConfig != null)
+ if (Config != null)
{
- onScreenDisplay?.StopTracking(this, rulesetConfig);
- rulesetConfig = null;
+ onScreenDisplay?.StopTracking(this, Config);
+ Config = null;
}
}
}
@@ -162,8 +182,15 @@ namespace osu.Game.Rulesets.UI
public abstract class RulesetContainer : RulesetContainer
where TObject : HitObject
{
- public event Action OnJudgement;
- public event Action OnJudgementRemoved;
+ ///
+ /// Invoked when a has been applied by a .
+ ///
+ public event Action OnNewResult;
+
+ ///
+ /// Invoked when a is being reverted by a .
+ ///
+ public event Action OnRevertResult;
///
/// The Beatmap
@@ -270,8 +297,8 @@ namespace osu.Game.Rulesets.UI
if (drawableObject == null)
continue;
- drawableObject.OnJudgement += (d, j) => OnJudgement?.Invoke(j);
- drawableObject.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(j);
+ drawableObject.OnNewResult += (_, r) => OnNewResult?.Invoke(r);
+ drawableObject.OnRevertResult += (_, r) => OnRevertResult?.Invoke(r);
Playfield.Add(drawableObject);
}
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index 8046e9f9b4..b05efce146 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -9,6 +9,9 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.StateChanges;
+using osu.Framework.Input.States;
using osu.Framework.Timing;
using osu.Game.Configuration;
using osu.Game.Input.Bindings;
@@ -222,23 +225,16 @@ namespace osu.Game.Rulesets.UI
mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons);
}
- protected override void TransformState(InputState state)
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
- base.TransformState(state);
+ if (mouseDisabled.Value && (args.Button == MouseButton.Left || args.Button == MouseButton.Right)) return false;
+ return base.OnMouseDown(state, args);
+ }
- // we don't want to transform the state if a replay is present (for now, at least).
- if (replayInputHandler != null) return;
-
- var mouse = state.Mouse as Framework.Input.MouseState;
-
- if (mouse != null)
- {
- if (mouseDisabled.Value)
- {
- mouse.SetPressed(MouseButton.Left, false);
- mouse.SetPressed(MouseButton.Right, false);
- }
- }
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ {
+ if (!CurrentState.Mouse.IsPressed(args.Button)) return false;
+ return base.OnMouseUp(state, args);
}
#endregion
diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs
index cfcfca157b..7146ad8064 100644
--- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs
@@ -57,22 +57,23 @@ namespace osu.Game.Rulesets.UI.Scrolling
///
public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects;
+ ///
+ /// The direction in which s in this should scroll.
+ ///
protected readonly Bindable Direction = new Bindable();
///
/// Creates a new .
///
- /// The direction in which s in this container should scroll.
/// The width to scale the internal coordinate space to.
/// May be null if scaling based on is desired. If is also null, no scaling will occur.
///
/// The height to scale the internal coordinate space to.
/// May be null if scaling based on is desired. If is also null, no scaling will occur.
///
- protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null, float? customHeight = null)
+ protected ScrollingPlayfield(float? customWidth = null, float? customHeight = null)
: base(customWidth, customHeight)
{
- Direction.Value = direction;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs
index 9d9432b2ce..46d1260410 100644
--- a/osu.Game/Screens/BackgroundScreen.cs
+++ b/osu.Game/Screens/BackgroundScreen.cs
@@ -5,7 +5,8 @@ using System;
using System.Threading;
using osu.Framework.Screens;
using osu.Framework.Graphics;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using OpenTK;
namespace osu.Game.Screens
diff --git a/osu.Game/Screens/Edit/Components/CircularButton.cs b/osu.Game/Screens/Edit/Components/CircularButton.cs
new file mode 100644
index 0000000000..a8ad242772
--- /dev/null
+++ b/osu.Game/Screens/Edit/Components/CircularButton.cs
@@ -0,0 +1,25 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Graphics.UserInterface;
+using OpenTK;
+
+namespace osu.Game.Screens.Edit.Components
+{
+ public class CircularButton : OsuButton
+ {
+ private const float width = 125;
+ private const float height = 30;
+
+ public CircularButton()
+ {
+ Size = new Vector2(width, height);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+ Content.CornerRadius = DrawHeight / 2f;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs
index 5f62843faf..6cd7fd52d4 100644
--- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs
+++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs
@@ -8,7 +8,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Timing;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
index 0dc110951b..7ff3849361 100644
--- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
+++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
@@ -6,7 +6,8 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 7eeabd3e5e..7159cd919c 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -12,7 +12,7 @@ using osu.Game.Screens.Edit.Menus;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Framework.Allocation;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Platform;
using osu.Framework.Timing;
using osu.Game.Graphics.UserInterface;
@@ -42,8 +42,8 @@ namespace osu.Game.Screens.Edit
private DependencyContainer dependencies;
private GameHost host;
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
- => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
+ => dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
[BackgroundDependencyLoader]
private void load(OsuColour colours, GameHost host)
diff --git a/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs
index cb7c0fa803..c6351c8520 100644
--- a/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs
+++ b/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs
@@ -6,12 +6,12 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
+using osu.Framework.Input.States;
using osu.Game.Screens.Edit.Screens;
namespace osu.Game.Screens.Edit.Menus
diff --git a/osu.Game/Screens/Edit/Menus/ScreenSelectionTabControl.cs b/osu.Game/Screens/Edit/Menus/ScreenSelectionTabControl.cs
index 1471a37a29..f58e5b39eb 100644
--- a/osu.Game/Screens/Edit/Menus/ScreenSelectionTabControl.cs
+++ b/osu.Game/Screens/Edit/Menus/ScreenSelectionTabControl.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Screens.Edit.Menus
TabContainer.AutoSizeAxes = Axes.X;
TabContainer.Padding = new MarginPadding();
- Add(new Box
+ AddInternal(new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs
index 21e48d8b0a..63df143ca8 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs
@@ -12,7 +12,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using OpenTK;
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs
index d8200d6c37..c4392bbc60 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs
@@ -7,7 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Rulesets.Edit;
using OpenTK.Graphics;
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs
index b9a8e9914a..d212bbe7dd 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs
@@ -1,37 +1,31 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Screens.Edit.Screens.Compose.Layers
{
public class HitObjectMaskLayer : CompositeDrawable
{
- private readonly Playfield playfield;
- private readonly HitObjectComposer composer;
-
private MaskContainer maskContainer;
+ private HitObjectComposer composer;
- public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer)
+ public HitObjectMaskLayer()
{
- // we need the playfield as HitObjects may not be initialised until its BDL.
- this.playfield = playfield;
-
- this.composer = composer;
-
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
- private void load()
+ private void load(HitObjectComposer composer)
{
+ this.composer = composer;
+
maskContainer = new MaskContainer();
var maskSelection = composer.CreateMaskSelection();
@@ -54,7 +48,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
dragLayer.CreateProxy()
};
- foreach (var obj in playfield.HitObjects.Objects)
+ foreach (var obj in composer.HitObjects)
addMask(obj);
}
@@ -76,18 +70,5 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
maskContainer.Add(mask);
}
-
- ///
- /// Removes the mask for a .
- ///
- /// The to remove the mask for.
- private void removeMask(DrawableHitObject hitObject)
- {
- var mask = maskContainer.FirstOrDefault(h => h.HitObject == hitObject);
- if (mask == null)
- return;
-
- maskContainer.Remove(mask);
- }
}
}
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs
index df2691c28e..5ee31769a3 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Rulesets.Edit;
using RectangleF = osu.Framework.Graphics.Primitives.RectangleF;
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs
index 54697bad77..927e7a2342 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs
@@ -7,7 +7,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Types;
diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs
index 0a009d9958..803a1275d7 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs
index 8cb0fdd908..30205c5aa1 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs
@@ -7,7 +7,8 @@ using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs
index f46cc7ef9d..5928fbaa1b 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs
@@ -27,16 +27,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
public TimelineButton()
{
- InternalChild = button = new IconButton
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- IconColour = OsuColour.Gray(0.35f),
- IconHoverColour = Color4.White,
- HoverColour = OsuColour.Gray(0.25f),
- FlashColour = OsuColour.Gray(0.5f),
- Action = () => Action?.Invoke()
- };
+ InternalChild = button = new TimelineIconButton { Action = () => Action?.Invoke() };
button.Enabled.BindTo(Enabled);
Width = button.ButtonSize.X;
@@ -48,5 +39,18 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
button.ButtonSize = new Vector2(button.ButtonSize.X, DrawHeight);
}
+
+ private class TimelineIconButton : IconButton
+ {
+ public TimelineIconButton()
+ {
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ IconColour = OsuColour.Gray(0.35f);
+ IconHoverColour = Color4.White;
+ HoverColour = OsuColour.Gray(0.25f);
+ FlashColour = OsuColour.Gray(0.5f);
+ }
+ }
}
}
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ZoomableScrollContainer.cs
index 0bfea68e50..d30aaacc6a 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ZoomableScrollContainer.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ZoomableScrollContainer.cs
@@ -5,7 +5,7 @@ using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Transforms;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.MathUtils;
using OpenTK;
diff --git a/osu.Game/Screens/Edit/Screens/Setup/Components/LabelledComponents/LabelledTextBox.cs b/osu.Game/Screens/Edit/Screens/Setup/Components/LabelledComponents/LabelledTextBox.cs
new file mode 100644
index 0000000000..94200b7f4e
--- /dev/null
+++ b/osu.Game/Screens/Edit/Screens/Setup/Components/LabelledComponents/LabelledTextBox.cs
@@ -0,0 +1,137 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using OpenTK.Graphics;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+
+namespace osu.Game.Screens.Edit.Screens.Setup.Components.LabelledComponents
+{
+ public class LabelledTextBox : CompositeDrawable
+ {
+ private const float label_container_width = 150;
+ private const float corner_radius = 15;
+ private const float default_height = 40;
+ private const float default_label_left_padding = 15;
+ private const float default_label_top_padding = 12;
+ private const float default_label_text_size = 16;
+
+ public event TextBox.OnCommitHandler OnCommit;
+
+ public bool ReadOnly
+ {
+ get => textBox.ReadOnly;
+ set => textBox.ReadOnly = value;
+ }
+
+ public string LabelText
+ {
+ get => label.Text;
+ set => label.Text = value;
+ }
+
+ public float LabelTextSize
+ {
+ get => label.TextSize;
+ set => label.TextSize = value;
+ }
+
+ public string PlaceholderText
+ {
+ get => textBox.PlaceholderText;
+ set => textBox.PlaceholderText = value;
+ }
+
+ public string Text
+ {
+ get => textBox.Text;
+ set => textBox.Text = value;
+ }
+
+ public Color4 LabelTextColour
+ {
+ get => label.Colour;
+ set => label.Colour = value;
+ }
+
+ public Color4 BackgroundColour
+ {
+ get => content.Colour;
+ set => content.Colour = value;
+ }
+
+ private readonly OsuTextBox textBox;
+ private readonly Container content;
+ private readonly OsuSpriteText label;
+
+ public LabelledTextBox()
+ {
+ RelativeSizeAxes = Axes.X;
+ Height = default_height;
+ CornerRadius = corner_radius;
+ Masking = true;
+
+ InternalChild = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ CornerRadius = corner_radius,
+ Masking = true,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.FromHex("1c2125"),
+ },
+ new GridContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = default_height,
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ label = new OsuSpriteText
+ {
+ Anchor = Anchor.TopLeft,
+ Origin = Anchor.TopLeft,
+ Padding = new MarginPadding { Left = default_label_left_padding, Top = default_label_top_padding },
+ Colour = Color4.White,
+ TextSize = default_label_text_size,
+ Font = @"Exo2.0-Bold",
+ },
+ textBox = new OsuTextBox
+ {
+ Anchor = Anchor.TopLeft,
+ Origin = Anchor.TopLeft,
+ RelativeSizeAxes = Axes.Both,
+ Height = 1,
+ CornerRadius = corner_radius,
+ },
+ },
+ },
+ ColumnDimensions = new[]
+ {
+ new Dimension(GridSizeMode.Absolute, label_container_width),
+ new Dimension()
+ }
+ }
+ }
+ };
+
+ textBox.OnCommit += OnCommit;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ textBox.BorderColour = colours.Blue;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs
index c3b3e747fd..3cef20e510 100644
--- a/osu.Game/Screens/Loader.cs
+++ b/osu.Game/Screens/Loader.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shaders;
using osu.Game.Screens.Menu;
using OpenTK;
using osu.Framework.Screens;
+using osu.Game.Overlays;
namespace osu.Game.Screens
{
@@ -18,6 +19,8 @@ namespace osu.Game.Screens
protected override bool HideOverlaysOnEnter => true;
+ protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
+
protected override bool AllowBackButton => false;
public Loader()
diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs
index 542ddd2c92..e53905a102 100644
--- a/osu.Game/Screens/Menu/Button.cs
+++ b/osu.Game/Screens/Menu/Button.cs
@@ -9,7 +9,6 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
@@ -18,6 +17,8 @@ using OpenTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Graphics.Containers;
using osu.Framework.Audio.Track;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Screens.Menu
@@ -35,6 +36,12 @@ namespace osu.Game.Screens.Menu
private readonly Box boxHoverLayer;
private readonly SpriteIcon icon;
private readonly string sampleName;
+
+ ///
+ /// The menu state for which we are visible for.
+ ///
+ public ButtonSystemState VisibleState = ButtonSystemState.TopLevel;
+
private readonly Action clickAction;
private readonly Key triggerKey;
private SampleChannel sampleClick;
@@ -51,7 +58,7 @@ namespace osu.Game.Screens.Menu
AutoSizeAxes = Axes.Both;
Alpha = 0;
- Vector2 boxSize = new Vector2(ButtonSystem.BUTTON_WIDTH + Math.Abs(extraWidth), ButtonSystem.BUTTON_AREA_HEIGHT);
+ Vector2 boxSize = new Vector2(ButtonSystem.BUTTON_WIDTH + Math.Abs(extraWidth), ButtonArea.BUTTON_AREA_HEIGHT);
Children = new Drawable[]
{
@@ -260,6 +267,7 @@ namespace osu.Game.Screens.Menu
this.FadeOut(800);
break;
}
+
break;
case ButtonState.Expanded:
const int expand_duration = 500;
@@ -276,6 +284,33 @@ namespace osu.Game.Screens.Menu
StateChanged?.Invoke(State);
}
}
+
+ public ButtonSystemState ButtonSystemState
+ {
+ set
+ {
+ ContractStyle = 0;
+
+ switch (value)
+ {
+ case ButtonSystemState.Initial:
+ State = ButtonState.Contracted;
+ break;
+ case ButtonSystemState.EnteringMode:
+ ContractStyle = 1;
+ State = ButtonState.Contracted;
+ break;
+ default:
+ if (value == VisibleState)
+ State = ButtonState.Expanded;
+ else if (value < VisibleState)
+ State = ButtonState.Contracted;
+ else
+ State = ButtonState.Exploded;
+ break;
+ }
+ }
+ }
}
public enum ButtonState
diff --git a/osu.Game/Screens/Menu/ButtonArea.cs b/osu.Game/Screens/Menu/ButtonArea.cs
new file mode 100644
index 0000000000..06004405b6
--- /dev/null
+++ b/osu.Game/Screens/Menu/ButtonArea.cs
@@ -0,0 +1,148 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using OpenTK;
+
+namespace osu.Game.Screens.Menu
+{
+ public class ButtonArea : Container, IStateful
+ {
+ public FlowContainerWithOrigin Flow;
+
+ protected override Container Content => Flow;
+
+ private readonly ButtonAreaBackground buttonAreaBackground;
+ private Visibility state;
+
+ public const float BUTTON_AREA_HEIGHT = 100;
+
+ public ButtonArea()
+ {
+ RelativeSizeAxes = Axes.Both;
+ InternalChild = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ Size = new Vector2(1, BUTTON_AREA_HEIGHT),
+ Alpha = 0,
+ Children = new Drawable[]
+ {
+ buttonAreaBackground = new ButtonAreaBackground(),
+ Flow = new FlowContainerWithOrigin
+ {
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(-ButtonSystem.WEDGE_WIDTH, 0),
+ Anchor = Anchor.Centre,
+ AutoSizeAxes = Axes.Both,
+ }
+ }
+ };
+ }
+
+ public ButtonSystemState ButtonSystemState
+ {
+ set
+ {
+ switch (value)
+ {
+ case ButtonSystemState.Exit:
+ case ButtonSystemState.Initial:
+ case ButtonSystemState.EnteringMode:
+ State = Visibility.Hidden;
+ break;
+ case ButtonSystemState.TopLevel:
+ case ButtonSystemState.Play:
+ State = Visibility.Visible;
+ break;
+ }
+
+ buttonAreaBackground.ButtonSystemState = value;
+ }
+ }
+
+ public Visibility State
+ {
+ get => state;
+ set
+ {
+ if (value == state) return;
+
+ state = value;
+ InternalChild.FadeTo(state == Visibility.Hidden ? 0 : 1, 300);
+ StateChanged?.Invoke(state);
+ }
+ }
+
+ public event Action StateChanged;
+
+ private class ButtonAreaBackground : Box, IStateful
+ {
+ private ButtonAreaBackgroundState state;
+
+ public ButtonAreaBackground()
+ {
+ RelativeSizeAxes = Axes.Both;
+ Size = new Vector2(2, 1);
+ Colour = OsuColour.Gray(50);
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ }
+
+ public ButtonAreaBackgroundState State
+ {
+ get => state;
+ set
+ {
+ if (value == state) return;
+
+ state = value;
+
+ switch (state)
+ {
+ case ButtonAreaBackgroundState.Flat:
+ this.ScaleTo(new Vector2(2, 0), 300, Easing.InSine);
+ break;
+ case ButtonAreaBackgroundState.Normal:
+ this.ScaleTo(Vector2.One, 400, Easing.OutQuint);
+ break;
+ }
+
+ StateChanged?.Invoke(state);
+ }
+ }
+
+ public ButtonSystemState ButtonSystemState
+ {
+ set
+ {
+ switch (value)
+ {
+ default:
+ State = ButtonAreaBackgroundState.Normal;
+ break;
+ case ButtonSystemState.Initial:
+ case ButtonSystemState.Exit:
+ case ButtonSystemState.EnteringMode:
+ State = ButtonAreaBackgroundState.Flat;
+ break;
+ }
+ }
+ }
+
+ public event Action StateChanged;
+ }
+
+ public enum ButtonAreaBackgroundState
+ {
+ Normal,
+ Flat
+ }
+ }
+}
diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs
index 762f826425..b9a799328e 100644
--- a/osu.Game/Screens/Menu/ButtonSystem.cs
+++ b/osu.Game/Screens/Menu/ButtonSystem.cs
@@ -10,8 +10,8 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
+using osu.Framework.Logging;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Input.Bindings;
@@ -22,9 +22,9 @@ using OpenTK.Input;
namespace osu.Game.Screens.Menu
{
- public class ButtonSystem : Container, IStateful, IKeyBindingHandler
+ public class ButtonSystem : Container, IStateful, IKeyBindingHandler
{
- public event Action StateChanged;
+ public event Action StateChanged;
public Action OnEdit;
public Action OnExit;
@@ -33,12 +33,6 @@ namespace osu.Game.Screens.Menu
public Action OnSettings;
public Action OnMulti;
public Action OnChart;
- public Action OnTest;
-
- private readonly FlowContainerWithOrigin buttonFlow;
-
- //todo: make these non-internal somehow.
- public const float BUTTON_AREA_HEIGHT = 100;
public const float BUTTON_WIDTH = 140f;
public const float WEDGE_WIDTH = 20;
@@ -54,18 +48,16 @@ namespace osu.Game.Screens.Menu
this.logo.Action = onOsuLogo;
// osuLogo.SizeForFlow relies on loading to be complete.
- buttonFlow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0);
+ buttonArea.Flow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0);
updateLogoState();
}
}
private readonly Drawable iconFacade;
- private readonly Container buttonArea;
- private readonly Box buttonAreaBackground;
+ private readonly ButtonArea buttonArea;
private readonly Button backButton;
- private readonly Button settingsButton;
private readonly List
public class PauseContainer : Container
{
- public bool IsPaused { get; private set; }
+ public readonly BindableBool IsPaused = new BindableBool();
public Func CheckCanPause;
@@ -39,9 +40,6 @@ namespace osu.Game.Screens.Play
public Action OnRetry;
public Action OnQuit;
- public Action OnResume;
- public Action OnPause;
-
private readonly FramedClock framedClock;
private readonly DecoupleableInterpolatingFramedClock decoupledClock;
@@ -84,9 +82,8 @@ namespace osu.Game.Screens.Play
// stop the seekable clock (stops the audio eventually)
decoupledClock.Stop();
- IsPaused = true;
+ IsPaused.Value = true;
- OnPause?.Invoke();
pauseOverlay.Show();
lastPauseActionTime = Time.Current;
@@ -96,7 +93,7 @@ namespace osu.Game.Screens.Play
{
if (!IsPaused) return;
- IsPaused = false;
+ IsPaused.Value = false;
IsResuming = false;
lastPauseActionTime = Time.Current;
@@ -105,7 +102,6 @@ namespace osu.Game.Screens.Play
decoupledClock.Seek(decoupledClock.CurrentTime);
decoupledClock.Start();
- OnResume?.Invoke();
pauseOverlay.Hide();
}
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index b406bda411..5ad0130fd7 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -12,7 +12,7 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Framework.Threading;
@@ -124,7 +124,7 @@ namespace osu.Game.Screens.Play
if (!RulesetContainer.Objects.Any())
{
- Logger.Error(new InvalidOperationException("Beatmap contains no hit objects!"), "Beatmap contains no hit objects!");
+ Logger.Log("Beatmap contains no hit objects!", level: LogLevel.Error);
return;
}
}
@@ -138,10 +138,9 @@ namespace osu.Game.Screens.Play
sourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock();
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
- var firstObjectTime = RulesetContainer.Objects.First().StartTime;
adjustableClock.Seek(AllowLeadIn
- ? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))
- : firstObjectTime);
+ ? Math.Min(0, RulesetContainer.GameplayStartTime - beatmap.BeatmapInfo.AudioLeadIn)
+ : RulesetContainer.GameplayStartTime);
adjustableClock.ProcessFrame();
@@ -163,15 +162,10 @@ namespace osu.Game.Screens.Play
{
pauseContainer = new PauseContainer(offsetClock, adjustableClock)
{
+ Retries = RestartCount,
OnRetry = Restart,
OnQuit = Exit,
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
- OnPause = () =>
- {
- pauseContainer.Retries = RestartCount;
- hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
- },
- OnResume = () => hudOverlay.KeyCounter.IsCounting = true,
Children = new[]
{
storyboardContainer = new Container
@@ -199,7 +193,7 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
- new SkipOverlay(firstObjectTime)
+ new SkipOverlay(RulesetContainer.GameplayStartTime)
{
Clock = Clock, // skip button doesn't want to use the audio clock directly
ProcessCustomClock = false,
@@ -219,9 +213,7 @@ namespace osu.Game.Screens.Play
{
if (!IsCurrentScreen) return;
- //we want to hide the hitrenderer immediately (looks better).
- //we may be able to remove this once the mouse cursor trail is improved.
- RulesetContainer?.Hide();
+ fadeOut(true);
Restart();
},
}
@@ -230,6 +222,8 @@ namespace osu.Game.Screens.Play
hudOverlay.HoldToQuit.Action = Exit;
hudOverlay.KeyCounter.Visible.BindTo(RulesetContainer.HasReplayLoaded);
+ RulesetContainer.IsPaused.BindTo(pauseContainer.IsPaused);
+
if (ShowStoryboard)
initializeStoryboard(false);
@@ -284,6 +278,8 @@ namespace osu.Game.Screens.Play
ScoreProcessor.PopulateScore(score);
score.User = RulesetContainer.Replay?.User ?? api.LocalUser.Value;
Push(new Results(score));
+
+ onCompletionEvent = null;
});
}
}
@@ -346,6 +342,13 @@ namespace osu.Game.Screens.Play
protected override bool OnExiting(Screen next)
{
+ if (onCompletionEvent != null)
+ {
+ // Proceed to result screen if beatmap already finished playing
+ onCompletionEvent.RunTask();
+ return true;
+ }
+
if ((!AllowPause || HasFailed || !ValidForResume || pauseContainer?.IsPaused != false || RulesetContainer?.HasReplayLoaded != false) && (!pauseContainer?.IsResuming ?? true))
{
// In the case of replays, we may have changed the playback rate.
@@ -361,16 +364,10 @@ namespace osu.Game.Screens.Play
return true;
}
- private void fadeOut()
+ private void fadeOut(bool instant = false)
{
- const float fade_out_duration = 250;
-
- RulesetContainer?.FadeOut(fade_out_duration);
- Content.FadeOut(fade_out_duration);
-
- hudOverlay?.ScaleTo(0.7f, fade_out_duration * 3, Easing.In);
-
- Background?.FadeTo(1f, fade_out_duration);
+ float fadeOutDuration = instant ? 0 : 250;
+ Content.FadeOut(fadeOutDuration);
}
protected override bool OnScroll(InputState state) => mouseWheelDisabled.Value && !pauseContainer.IsPaused;
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index 6e1c3d24f0..fd4322c268 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -1,26 +1,31 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Localisation;
using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play.PlayerSettings;
using OpenTK;
+using OpenTK.Graphics;
namespace osu.Game.Screens.Play
{
public class PlayerLoader : ScreenWithBeatmapBackground
{
+ private static readonly Vector2 background_blur = new Vector2(15);
+
private Player player;
private BeatmapMetadataDisplay info;
@@ -61,26 +66,30 @@ namespace osu.Game.Screens.Play
Margin = new MarginPadding(25),
Children = new PlayerSettingsGroup[]
{
- new VisualSettings(),
+ visualSettings = new VisualSettings(),
new InputSettings()
}
});
- loadTask = LoadComponentAsync(player);
+ loadTask = LoadComponentAsync(player, playerLoaded);
}
+ private void playerLoaded(Player player) => info.Loading = false;
+
protected override void OnResuming(Screen last)
{
base.OnResuming(last);
contentIn();
+ info.Loading = true;
+
//we will only be resumed if the player has requested a re-run (see ValidForResume setting above)
loadTask = LoadComponentAsync(player = new Player
{
RestartCount = player.RestartCount + 1,
RestartRequested = player.RestartRequested,
- });
+ }, playerLoaded);
this.Delay(400).Schedule(pushWhenLoaded);
}
@@ -122,23 +131,33 @@ namespace osu.Game.Screens.Play
logo.Delay(resuming ? 0 : 500).MoveToOffset(new Vector2(0, -0.24f), 500, Easing.InOutExpo);
}
- private bool weHandledMouseDown;
-
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
- {
- weHandledMouseDown = true;
- return base.OnMouseDown(state, args);
- }
-
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
- {
- weHandledMouseDown = false;
- return base.OnMouseUp(state, args);
- }
-
private ScheduledDelegate pushDebounce;
+ private VisualSettings visualSettings;
- private bool readyForPush => player.LoadState == LoadState.Ready && IsHovered && (!GetContainingInputManager().CurrentState.Mouse.HasAnyButtonPressed || weHandledMouseDown);
+ private bool readyForPush => player.LoadState == LoadState.Ready && IsHovered && GetContainingInputManager()?.DraggedDrawable == null;
+
+ protected override bool OnHover(InputState state)
+ {
+ // restore our screen defaults
+ InitializeBackgroundElements();
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ if (GetContainingInputManager().HoveredDrawables.Contains(visualSettings))
+ {
+ // show user setting preview
+ UpdateBackgroundElements();
+ }
+ base.OnHoverLost(state);
+ }
+
+ protected override void InitializeBackgroundElements()
+ {
+ Background?.FadeTo(1, BACKGROUND_FADE_DURATION, Easing.OutQuint);
+ Background?.BlurTo(background_blur, BACKGROUND_FADE_DURATION, Easing.OutQuint);
+ }
private void pushWhenLoaded()
{
@@ -230,7 +249,7 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.TopCentre,
Origin = Anchor.TopRight,
Margin = new MarginPadding { Right = 5 },
- Colour = OsuColour.Gray(0.5f),
+ Colour = OsuColour.Gray(0.8f),
Text = left,
},
new OsuSpriteText
@@ -245,6 +264,25 @@ namespace osu.Game.Screens.Play
}
private readonly WorkingBeatmap beatmap;
+ private LoadingAnimation loading;
+ private Sprite backgroundSprite;
+
+ public bool Loading
+ {
+ set
+ {
+ if (value)
+ {
+ loading.Show();
+ backgroundSprite.FadeColour(OsuColour.Gray(0.5f), 400, Easing.OutQuint);
+ }
+ else
+ {
+ loading.Hide();
+ backgroundSprite.FadeColour(Color4.White, 400, Easing.OutQuint);
+ }
+ }
+ }
public BeatmapMetadataDisplay(WorkingBeatmap beatmap)
{
@@ -291,9 +329,9 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.TopCentre,
CornerRadius = 10,
Masking = true,
- Children = new[]
+ Children = new Drawable[]
{
- new Sprite
+ backgroundSprite = new Sprite
{
RelativeSizeAxes = Axes.Both,
Texture = beatmap?.Background,
@@ -301,6 +339,7 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.Centre,
FillMode = FillMode.Fill,
},
+ loading = new LoadingAnimation { Scale = new Vector2(1.3f) }
}
},
new OsuSpriteText
@@ -328,6 +367,8 @@ namespace osu.Game.Screens.Play
},
}
};
+
+ Loading = true;
}
}
}
diff --git a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs
index cff3eca895..64c49099f2 100644
--- a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs
+++ b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs
@@ -5,7 +5,8 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
@@ -93,7 +94,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
- Text = Title.ToUpper(),
+ Text = Title.ToUpperInvariant(),
TextSize = 17,
Font = @"Exo2.0-Bold",
Margin = new MarginPadding { Left = 10 },
@@ -127,6 +128,27 @@ namespace osu.Game.Screens.Play.PlayerSettings
};
}
+ private const float fade_duration = 800;
+ private const float inactive_alpha = 0.5f;
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ this.Delay(600).FadeTo(inactive_alpha, fade_duration, Easing.OutQuint);
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ this.FadeIn(fade_duration, Easing.OutQuint);
+ return true;
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ this.FadeTo(inactive_alpha, fade_duration, Easing.OutQuint);
+ base.OnHoverLost(state);
+ }
+
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
@@ -139,7 +161,6 @@ namespace osu.Game.Screens.Play.PlayerSettings
protected override Container Content => content;
- protected override bool OnHover(InputState state) => true;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
}
}
diff --git a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs
index 7f18305b1c..26d3218fbf 100644
--- a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs
+++ b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs
@@ -15,6 +15,8 @@ namespace osu.Game.Screens.Play
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
+ protected new BackgroundScreenBeatmap Background => (BackgroundScreenBeatmap)base.Background;
+
public override bool AllowBeatmapRulesetChange => false;
protected const float BACKGROUND_FADE_DURATION = 800;
@@ -43,21 +45,30 @@ namespace osu.Game.Screens.Play
DimLevel.ValueChanged += _ => UpdateBackgroundElements();
BlurLevel.ValueChanged += _ => UpdateBackgroundElements();
ShowStoryboard.ValueChanged += _ => UpdateBackgroundElements();
- UpdateBackgroundElements();
+ InitializeBackgroundElements();
}
protected override void OnResuming(Screen last)
{
base.OnResuming(last);
- UpdateBackgroundElements();
+ InitializeBackgroundElements();
}
+ ///
+ /// Called once on entering screen. By Default, performs a full call.
+ ///
+ protected virtual void InitializeBackgroundElements() => UpdateBackgroundElements();
+
+ ///
+ /// Called wen background elements require updates, usually due to a user changing a setting.
+ ///
+ ///
protected virtual void UpdateBackgroundElements()
{
if (!IsCurrentScreen) return;
Background?.FadeTo(BackgroundOpacity, BACKGROUND_FADE_DURATION, Easing.OutQuint);
- (Background as BackgroundScreenBeatmap)?.BlurTo(new Vector2((float)BlurLevel.Value * 25), BACKGROUND_FADE_DURATION, Easing.OutQuint);
+ Background?.BlurTo(new Vector2((float)BlurLevel.Value * 25), BACKGROUND_FADE_DURATION, Easing.OutQuint);
}
}
}
diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs
index 383addc607..06837c9274 100644
--- a/osu.Game/Screens/Play/SkipOverlay.cs
+++ b/osu.Game/Screens/Play/SkipOverlay.cs
@@ -4,9 +4,10 @@
using System;
using osu.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
using osu.Framework.Threading;
using osu.Framework.Timing;
using osu.Game.Graphics;
@@ -17,6 +18,8 @@ using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osu.Framework.Input.Bindings;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Input.Bindings;
namespace osu.Game.Screens.Play
@@ -213,17 +216,21 @@ namespace osu.Game.Screens.Play
private Box background;
private AspectContainer aspect;
+ private SampleChannel sampleConfirm;
+
public Button()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OsuColour colours, AudioManager audio)
{
colourNormal = colours.Yellow;
colourHover = colours.YellowDark;
+ sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection");
+
Children = new Drawable[]
{
background = new Box
@@ -310,6 +317,8 @@ namespace osu.Game.Screens.Play
if (!Enabled)
return false;
+ sampleConfirm.Play();
+
box.FlashColour(Color4.White, 500, Easing.OutQuint);
aspect.ScaleTo(1.2f, 2000, Easing.OutQuint);
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index 3c9a14e1f4..6e4454a311 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -8,7 +8,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Configuration;
-using osu.Framework.Input;
using OpenTK.Input;
using osu.Framework.MathUtils;
using System.Diagnostics;
@@ -18,6 +17,8 @@ using osu.Framework.Caching;
using osu.Framework.Threading;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
@@ -329,13 +330,13 @@ namespace osu.Game.Screens.Select
private FilterCriteria activeCriteria = new FilterCriteria();
- protected ScheduledDelegate FilterTask;
+ protected ScheduledDelegate PendingFilter;
public bool AllowSelection = true;
public void FlushPendingFilterOperations()
{
- if (FilterTask?.Completed == false)
+ if (PendingFilter?.Completed == false)
{
applyActiveCriteria(false, false);
Update();
@@ -356,18 +357,18 @@ namespace osu.Game.Screens.Select
void perform()
{
- FilterTask = null;
+ PendingFilter = null;
root.Filter(activeCriteria);
itemsCache.Invalidate();
if (scroll) scrollPositionCache.Invalidate();
}
- FilterTask?.Cancel();
- FilterTask = null;
+ PendingFilter?.Cancel();
+ PendingFilter = null;
if (debounce)
- FilterTask = Scheduler.AddDelayed(perform, 250);
+ PendingFilter = Scheduler.AddDelayed(perform, 250);
else
perform();
}
diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs
index 2118cfdc78..ea461e7bd5 100644
--- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs
+++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs
@@ -79,8 +79,13 @@ namespace osu.Game.Screens.Select.Carousel
public override void Filter(FilterCriteria criteria)
{
base.Filter(criteria);
- InternalChildren.Sort((x, y) => x.CompareTo(criteria, y));
- InternalChildren.ForEach(c => c.Filter(criteria));
+
+ var children = new List(InternalChildren);
+
+ children.Sort((x, y) => x.CompareTo(criteria, y));
+ children.ForEach(c => c.Filter(criteria));
+
+ InternalChildren = children;
}
protected virtual void ChildItemStateChanged(CarouselItem item, CarouselItemState value)
diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
index c5996327b9..6071e163cf 100644
--- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
+++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs
index b6dd6cad65..8a0052559e 100644
--- a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs
+++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs
@@ -8,7 +8,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using OpenTK;
diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs
index 278d32b2d5..9ba8b085f3 100644
--- a/osu.Game/Screens/Select/FilterControl.cs
+++ b/osu.Game/Screens/Select/FilterControl.cs
@@ -13,8 +13,9 @@ using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select.Filter;
using Container = osu.Framework.Graphics.Containers.Container;
-using osu.Framework.Input;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Configuration;
using osu.Game.Rulesets;
@@ -181,7 +182,7 @@ namespace osu.Game.Screens.Select
showConverted.ValueChanged += val => updateCriteria();
ruleset.BindTo(parentRuleset);
- ruleset.BindValueChanged(val => updateCriteria(), true);
+ ruleset.BindValueChanged(_ => updateCriteria(), true);
}
private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs
index 8f07e0a763..bc4d216f00 100644
--- a/osu.Game/Screens/Select/Footer.cs
+++ b/osu.Game/Screens/Select/Footer.cs
@@ -11,7 +11,8 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Select
diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs
index 3fa12283b5..1b0e3a1620 100644
--- a/osu.Game/Screens/Select/FooterButton.cs
+++ b/osu.Game/Screens/Select/FooterButton.cs
@@ -8,7 +8,8 @@ using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.Containers;
diff --git a/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs b/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs
index 2a11bf8346..0c4b369f36 100644
--- a/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs
+++ b/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs
@@ -46,7 +46,9 @@ namespace osu.Game.Screens.Select.Leaderboards
public void UpdateRank(ScoreRank newRank)
{
Rank = newRank;
- updateTexture();
+
+ if (LoadState >= LoadState.Ready)
+ updateTexture();
}
}
}
diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
index 19732107c7..95e9dde68e 100644
--- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
diff --git a/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs b/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs
index 99b0c53835..19cba72f1f 100644
--- a/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs
+++ b/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs
@@ -3,7 +3,8 @@
using System;
using osu.Framework.Graphics;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using OpenTK;
diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs
index d789d41ef4..d4cd882433 100644
--- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs
+++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs
@@ -5,7 +5,8 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs
index 8ce40fcfa0..e914eb365e 100644
--- a/osu.Game/Screens/Select/PlaySongSelect.cs
+++ b/osu.Game/Screens/Select/PlaySongSelect.cs
@@ -50,13 +50,14 @@ namespace osu.Game.Screens.Select
private SampleChannel sampleConfirm;
- public readonly Bindable> SelectedMods = new Bindable>(new List());
+ [Cached]
+ [Cached(Type = typeof(IBindable>))]
+ private readonly Bindable> selectedMods = new Bindable>(new Mod[] { });
[BackgroundDependencyLoader(true)]
- private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, OsuGame osu)
+ private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, Bindable> selectedMods)
{
- if (osu != null) SelectedMods.BindTo(osu.SelectedMods);
- modSelect.SelectedMods.BindTo(SelectedMods);
+ if (selectedMods != null) this.selectedMods.BindTo(selectedMods);
sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection");
@@ -84,9 +85,9 @@ namespace osu.Game.Screens.Select
protected override void UpdateBeatmap(WorkingBeatmap beatmap)
{
- base.UpdateBeatmap(beatmap);
+ beatmap.Mods.BindTo(selectedMods);
- beatmap.Mods.BindTo(SelectedMods);
+ base.UpdateBeatmap(beatmap);
BeatmapDetails.Beatmap = beatmap;
@@ -131,7 +132,7 @@ namespace osu.Game.Screens.Select
if (Beatmap.Value.Track != null)
Beatmap.Value.Track.Looping = false;
- SelectedMods.UnbindAll();
+ selectedMods.UnbindAll();
Beatmap.Value.Mods.Value = new Mod[] { };
return false;
@@ -147,10 +148,10 @@ namespace osu.Game.Screens.Select
var auto = Ruleset.Value.CreateInstance().GetAutoplayMod();
var autoType = auto.GetType();
- var mods = modSelect.SelectedMods.Value;
+ var mods = selectedMods.Value;
if (mods.All(m => m.GetType() != autoType))
{
- modSelect.SelectedMods.Value = mods.Append(auto);
+ selectedMods.Value = mods.Append(auto);
removeAutoModOnResume = true;
}
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 9c62f92311..dcc0760262 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Linq;
using OpenTK;
using OpenTK.Input;
using osu.Framework.Allocation;
@@ -11,7 +12,9 @@ using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Framework.Logging;
+using osu.Framework.Input.EventArgs;
+using osu.Framework.Input.States;
using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
@@ -20,6 +23,7 @@ using osu.Game.Graphics.Containers;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Menu;
@@ -69,8 +73,15 @@ namespace osu.Game.Screens.Select
private DependencyContainer dependencies;
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
- => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
+ {
+ dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+ dependencies.CacheAs(this);
+ dependencies.CacheAs(Ruleset);
+ dependencies.CacheAs>(Ruleset);
+
+ return dependencies;
+ }
protected SongSelect()
{
@@ -138,7 +149,11 @@ namespace osu.Game.Screens.Select
Height = filter_height,
FilterChanged = c => Carousel.Filter(c),
Background = { Width = 2 },
- Exit = Exit,
+ Exit = () =>
+ {
+ if (IsCurrentScreen)
+ Exit();
+ },
},
}
},
@@ -185,12 +200,6 @@ namespace osu.Game.Screens.Select
[BackgroundDependencyLoader(true)]
private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours)
{
- dependencies.CacheAs(this);
- dependencies.CacheAs(Ruleset);
- dependencies.CacheAs>(Ruleset);
-
- base.Ruleset.ValueChanged += r => updateSelectedBeatmap(beatmapNoDebounce);
-
if (Footer != null)
{
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
@@ -213,9 +222,6 @@ namespace osu.Game.Screens.Select
sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand");
Carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSetsEnumerable();
-
- Beatmap.BindDisabledChanged(disabled => Carousel.AllowSelection = !disabled, true);
- Beatmap.BindValueChanged(workingBeatmapChanged);
}
public void Edit(BeatmapInfo beatmap)
@@ -231,6 +237,10 @@ namespace osu.Game.Screens.Select
/// Whether to trigger .
public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true)
{
+ // avoid attempting to continue before a selection has been obtained.
+ // this could happen via a user interaction while the carousel is still in a loading state.
+ if (Carousel.SelectedBeatmap == null) return;
+
// if we have a pending filter operation, we want to run it now.
// it could change selection (ie. if the ruleset has been changed).
Carousel.FlushPendingFilterOperations();
@@ -265,7 +275,7 @@ namespace osu.Game.Screens.Select
// If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch
if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != Ruleset.Value)
{
- Ruleset.Value = beatmap.BeatmapInfo.Ruleset;
+ base.Ruleset.Value = beatmap.BeatmapInfo.Ruleset;
Carousel.SelectBeatmap(beatmap.BeatmapInfo);
}
}
@@ -274,52 +284,83 @@ namespace osu.Game.Screens.Select
private BeatmapInfo beatmapNoDebounce;
private RulesetInfo rulesetNoDebounce;
+ private void updateSelectedBeatmap(BeatmapInfo beatmap)
+ {
+ if (beatmap?.Equals(beatmapNoDebounce) == true)
+ return;
+
+ beatmapNoDebounce = beatmap;
+ performUpdateSelected();
+ }
+
+ private void updateSelectedRuleset(RulesetInfo ruleset)
+ {
+ if (ruleset?.Equals(rulesetNoDebounce) == true)
+ return;
+
+ rulesetNoDebounce = ruleset;
+ performUpdateSelected();
+ }
+
///
/// selection has been changed as the result of a user interaction.
///
- private void updateSelectedBeatmap(BeatmapInfo beatmap)
+ private void performUpdateSelected()
{
- var ruleset = base.Ruleset.Value;
+ var beatmap = beatmapNoDebounce;
+ var ruleset = rulesetNoDebounce;
- void performLoad()
+ void run()
{
- // We may be arriving here due to another component changing the bindable Beatmap.
- // In these cases, the other component has already loaded the beatmap, so we don't need to do so again.
- if (beatmap?.Equals(Beatmap.Value.BeatmapInfo) != true)
- {
- bool preview = beatmap?.BeatmapSetInfoID != Beatmap.Value?.BeatmapInfo.BeatmapSetInfoID;
+ Logger.Log($"updating selection with beatmap:{beatmap?.ID.ToString() ?? "null"} ruleset:{ruleset?.ID.ToString() ?? "null"}");
- Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
- ensurePlayingSelected(preview);
+ bool preview = false;
+
+ if (ruleset?.Equals(Ruleset.Value) == false)
+ {
+ Logger.Log($"ruleset changed from \"{Ruleset.Value}\" to \"{ruleset}\"");
+
+ Beatmap.Value.Mods.Value = Enumerable.Empty();
+ Ruleset.Value = ruleset;
+
+ // force a filter before attempting to change the beatmap.
+ // we may still be in the wrong ruleset as there is a debounce delay on ruleset changes.
+ Carousel.Filter(null, false);
+
+ // Filtering only completes after the carousel runs Update.
+ // If we also have a pending beatmap change we should delay it one frame.
+ selectionChangedDebounce = Schedule(run);
+ return;
}
- Ruleset.Value = ruleset;
+ // We may be arriving here due to another component changing the bindable Beatmap.
+ // In these cases, the other component has already loaded the beatmap, so we don't need to do so again.
+ if (!Equals(beatmap, Beatmap.Value.BeatmapInfo))
+ {
+ Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\"");
+ preview = beatmap?.BeatmapSetInfoID != Beatmap.Value?.BeatmapInfo.BeatmapSetInfoID;
+ Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
+
+ if (beatmap != null)
+ {
+ if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID)
+ sampleChangeDifficulty.Play();
+ else
+ sampleChangeBeatmap.Play();
+ }
+ }
+
+ ensurePlayingSelected(preview);
UpdateBeatmap(Beatmap.Value);
}
- if (beatmap?.Equals(beatmapNoDebounce) == true && ruleset?.Equals(rulesetNoDebounce) == true)
- return;
-
selectionChangedDebounce?.Cancel();
- beatmapNoDebounce = beatmap;
- rulesetNoDebounce = ruleset;
-
if (beatmap == null)
- performLoad();
+ run();
else
- {
- if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID)
- sampleChangeDifficulty.Play();
- else
- sampleChangeBeatmap.Play();
-
- if (beatmap == Beatmap.Value.BeatmapInfo)
- performLoad();
- else
- selectionChangedDebounce = Scheduler.AddDelayed(performLoad, 200);
- }
+ selectionChangedDebounce = Scheduler.AddDelayed(run, 200);
}
private void triggerRandom()
@@ -419,6 +460,8 @@ namespace osu.Game.Screens.Select
{
base.Dispose(isDisposing);
+ Ruleset.UnbindAll();
+
if (beatmaps != null)
{
beatmaps.ItemAdded -= onBeatmapSetAdded;
@@ -435,6 +478,8 @@ namespace osu.Game.Screens.Select
/// The working beatmap.
protected virtual void UpdateBeatmap(WorkingBeatmap beatmap)
{
+ Logger.Log($"working beatmap updated to {beatmap}");
+
if (Background is BackgroundScreenBeatmap backgroundModeBeatmap)
{
backgroundModeBeatmap.Beatmap = beatmap;
@@ -466,6 +511,17 @@ namespace osu.Game.Screens.Select
private void carouselBeatmapsLoaded()
{
+ if (rulesetNoDebounce == null)
+ {
+ // manual binding to parent ruleset to allow for delayed load in the incoming direction.
+ rulesetNoDebounce = Ruleset.Value = base.Ruleset.Value;
+ base.Ruleset.ValueChanged += updateSelectedRuleset;
+ Ruleset.ValueChanged += r => base.Ruleset.Value = r;
+
+ Beatmap.BindDisabledChanged(disabled => Carousel.AllowSelection = !disabled, true);
+ Beatmap.BindValueChanged(workingBeatmapChanged);
+ }
+
if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false
&& Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false))
return;
@@ -474,7 +530,7 @@ namespace osu.Game.Screens.Select
{
// in the case random selection failed, we want to trigger selectionChanged
// to show the dummy beatmap (we have nothing else to display).
- updateSelectedBeatmap(null);
+ performUpdateSelected();
}
}
diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs
index 29301899d5..63d29d5cd7 100644
--- a/osu.Game/Screens/Tournament/Drawings.cs
+++ b/osu.Game/Screens/Tournament/Drawings.cs
@@ -49,8 +49,8 @@ namespace osu.Game.Screens.Tournament
private DependencyContainer dependencies;
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
- dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
+ dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
[BackgroundDependencyLoader]
private void load(TextureStore textures, Storage storage)
@@ -323,7 +323,7 @@ namespace osu.Game.Screens.Tournament
if (string.IsNullOrEmpty(line))
continue;
- if (line.ToUpper().StartsWith("GROUP"))
+ if (line.ToUpperInvariant().StartsWith("GROUP"))
continue;
// ReSharper disable once AccessToModifiedClosure
diff --git a/osu.Game/Screens/Tournament/Group.cs b/osu.Game/Screens/Tournament/Group.cs
index b1bcd6052c..6845d8fc48 100644
--- a/osu.Game/Screens/Tournament/Group.cs
+++ b/osu.Game/Screens/Tournament/Group.cs
@@ -51,7 +51,7 @@ namespace osu.Game.Screens.Tournament
Position = new Vector2(0, 7f),
- Text = $"GROUP {name.ToUpper()}",
+ Text = $"GROUP {name.ToUpperInvariant()}",
TextSize = 8f,
Font = @"Exo2.0-Bold",
Colour = new Color4(255, 204, 34, 255),
@@ -161,7 +161,7 @@ namespace osu.Game.Screens.Tournament
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- Text = team.Acronym.ToUpper(),
+ Text = team.Acronym.ToUpperInvariant(),
TextSize = 10f,
Font = @"Exo2.0-Bold"
}
diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs
index 0ef54c7310..d4f1c5c6f1 100644
--- a/osu.Game/Skinning/LegacySkinDecoder.cs
+++ b/osu.Game/Skinning/LegacySkinDecoder.cs
@@ -14,6 +14,8 @@ namespace osu.Game.Skinning
protected override void ParseLine(SkinConfiguration skin, Section section, string line)
{
+ line = StripComments(line);
+
switch (section)
{
case Section.General:
diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs
index ad6a033936..25d9442e6f 100644
--- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs
+++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs
@@ -71,9 +71,9 @@ namespace osu.Game.Skinning
private void onSourceChanged() => SourceChanged?.Invoke();
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
fallbackSource = dependencies.Get();
dependencies.CacheAs(this);
@@ -85,12 +85,10 @@ namespace osu.Game.Skinning
private void load(OsuConfigManager config)
{
beatmapSkins = config.GetBindable(OsuSetting.BeatmapSkins);
- beatmapSkins.ValueChanged += val => onSourceChanged();
- beatmapSkins.TriggerChange();
+ beatmapSkins.BindValueChanged(_ => onSourceChanged());
beatmapHitsounds = config.GetBindable(OsuSetting.BeatmapHitsounds);
- beatmapHitsounds.ValueChanged += val => onSourceChanged();
- beatmapHitsounds.TriggerChange();
+ beatmapHitsounds.BindValueChanged(_ => onSourceChanged(), true);
}
protected override void LoadComplete()
diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs
index f1ee0db6ce..0b94697405 100644
--- a/osu.Game/Skinning/SkinReloadableDrawable.cs
+++ b/osu.Game/Skinning/SkinReloadableDrawable.cs
@@ -52,5 +52,13 @@ namespace osu.Game.Skinning
protected virtual void SkinChanged(ISkinSource skin, bool allowFallback)
{
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ if (skin != null)
+ skin.SourceChanged -= onChange;
+ }
}
}
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
index a2f4372323..d746bb90c4 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
@@ -36,8 +36,8 @@ namespace osu.Game.Storyboards.Drawables
public override bool RemoveCompletedTransforms => false;
private DependencyContainer dependencies;
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
- dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
+ dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
public DrawableStoryboard(Storyboard storyboard)
{
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs
new file mode 100644
index 0000000000..cdec8c042f
--- /dev/null
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs
@@ -0,0 +1,69 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.IO;
+using osu.Framework.Allocation;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
+
+namespace osu.Game.Storyboards.Drawables
+{
+ public class DrawableStoryboardSample : Component
+ {
+ ///
+ /// The amount of time allowable beyond the start time of the sample, for the sample to start.
+ ///
+ private const double allowable_late_start = 100;
+
+ private readonly StoryboardSample sample;
+ private SampleChannel channel;
+
+ public override bool RemoveWhenNotAlive => false;
+
+ public DrawableStoryboardSample(StoryboardSample sample)
+ {
+ this.sample = sample;
+ LifetimeStart = sample.Time;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(IBindableBeatmap beatmap)
+ {
+ // Try first with the full name, then attempt with no path
+ channel = beatmap.Value.Skin.GetSample(sample.Path) ?? beatmap.Value.Skin.GetSample(Path.ChangeExtension(sample.Path, null));
+
+ if (channel != null)
+ channel.Volume.Value = sample.Volume / 100;
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ // TODO: this logic will need to be consolidated with other game samples like hitsounds.
+ if (Time.Current < sample.Time)
+ {
+ // We've rewound before the start time of the sample
+ channel?.Stop();
+
+ // In the case that the user fast-forwards to a point far beyond the start time of the sample,
+ // we want to be able to fall into the if-conditional below (therefore we must not have a life time end)
+ LifetimeStart = sample.Time;
+ LifetimeEnd = double.MaxValue;
+ }
+ else if (Time.Current - Time.Elapsed < sample.Time)
+ {
+ // We've passed the start time of the sample. We only play the sample if we're within an allowable range
+ // from the sample's start, to reduce layering if we've been fast-forwarded far into the future
+ if (Time.Current - sample.Time < allowable_late_start)
+ channel?.Play();
+
+ // In the case that the user rewinds to a point far behind the start time of the sample,
+ // we want to be able to fall into the if-conditional above (therefore we must not have a life time start)
+ LifetimeStart = double.MinValue;
+ LifetimeEnd = sample.Time;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs
index d0555493a6..c34a39a7bf 100644
--- a/osu.Game/Storyboards/StoryboardSample.cs
+++ b/osu.Game/Storyboards/StoryboardSample.cs
@@ -2,14 +2,14 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using System;
+using osu.Game.Storyboards.Drawables;
namespace osu.Game.Storyboards
{
public class StoryboardSample : IStoryboardElement
{
public string Path { get; set; }
- public bool IsDrawable => false;
+ public bool IsDrawable => true;
public double Time;
public float Volume;
@@ -21,9 +21,6 @@ namespace osu.Game.Storyboards
Volume = volume;
}
- public Drawable CreateDrawable()
- {
- throw new InvalidOperationException();
- }
+ public Drawable CreateDrawable() => new DrawableStoryboardSample(this);
}
}
diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
index cf4dda52a8..ff57ef41c5 100644
--- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
+++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
@@ -25,6 +25,8 @@ namespace osu.Game.Tests.Beatmaps
protected abstract string ResourceAssembly { get; }
+ protected IBeatmapConverter Converter { get; private set; }
+
protected void Test(string name)
{
var ourResult = convert(name);
@@ -41,14 +43,22 @@ namespace osu.Game.Tests.Beatmaps
Assert.Fail($"A conversion did not generate any hitobjects, but should have, for hitobject at time: {expectedResult.Mappings[mappingCounter].StartTime}\n");
else if (mappingCounter >= expectedResult.Mappings.Count)
Assert.Fail($"A conversion generated hitobjects, but should not have, for hitobject at time: {ourResult.Mappings[mappingCounter].StartTime}\n");
+ else if (!expectedResult.Mappings[mappingCounter].Equals(ourResult.Mappings[mappingCounter]))
+ {
+ var expectedMapping = expectedResult.Mappings[mappingCounter];
+ var ourMapping = ourResult.Mappings[mappingCounter];
+
+ Assert.Fail($"The conversion mapping differed for object at time {expectedMapping.StartTime}:\n"
+ + $"Expected {JsonConvert.SerializeObject(expectedMapping)}\n"
+ + $"Received: {JsonConvert.SerializeObject(ourMapping)}\n");
+ }
else
{
- var counter = mappingCounter;
+ var ourMapping = ourResult.Mappings[mappingCounter];
+ var expectedMapping = expectedResult.Mappings[mappingCounter];
+
Assert.Multiple(() =>
{
- var ourMapping = ourResult.Mappings[counter];
- var expectedMapping = expectedResult.Mappings[counter];
-
int objectCounter = 0;
while (true)
{
@@ -60,10 +70,6 @@ namespace osu.Game.Tests.Beatmaps
else if (objectCounter >= expectedMapping.Objects.Count)
Assert.Fail($"The conversion generated a hitobject, but should not have, for hitobject at time: {ourMapping.StartTime}:\n"
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
- else if (!expectedMapping.Equals(ourMapping))
- Assert.Fail($"The conversion mapping differed for object at time {expectedMapping.StartTime}:\n"
- + $"Expected {JsonConvert.SerializeObject(expectedMapping)}\n"
- + $"Received: {JsonConvert.SerializeObject(ourMapping)}\n");
else if (!expectedMapping.Objects[objectCounter].Equals(ourMapping.Objects[objectCounter]))
{
Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}:\n"
@@ -88,10 +94,11 @@ namespace osu.Game.Tests.Beatmaps
var rulesetInstance = CreateRuleset();
beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo();
- var result = new ConvertResult();
- var converter = rulesetInstance.CreateBeatmapConverter(beatmap);
+ Converter = rulesetInstance.CreateBeatmapConverter(beatmap);
- converter.ObjectConverted += (orig, converted) =>
+ var result = new ConvertResult();
+
+ Converter.ObjectConverted += (orig, converted) =>
{
converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty));
@@ -103,7 +110,7 @@ namespace osu.Game.Tests.Beatmaps
result.Mappings.Add(mapping);
};
- IBeatmap convertedBeatmap = converter.Convert();
+ IBeatmap convertedBeatmap = Converter.Convert();
rulesetInstance.CreateBeatmapProcessor(convertedBeatmap)?.PostProcess();
return result;
diff --git a/osu.Game/Tests/Platform/TestStorage.cs b/osu.Game/Tests/Platform/TestStorage.cs
deleted file mode 100644
index 5b31c7b4d0..0000000000
--- a/osu.Game/Tests/Platform/TestStorage.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Platform;
-
-namespace osu.Game.Tests.Platform
-{
- public class TestStorage : DesktopStorage
- {
- public TestStorage(string baseName) : base(baseName, null)
- {
- }
-
- public override string GetDatabaseConnectionString(string name)
- {
- return "DataSource=:memory:";
- }
- }
-}
diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs
index 521b51529e..0ca7ff011f 100644
--- a/osu.Game/Tests/Visual/EditorClockTestCase.cs
+++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
-using osu.Framework.Input;
+using osu.Framework.Input.States;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
@@ -25,9 +25,9 @@ namespace osu.Game.Tests.Visual
Clock = new EditorClock(new ControlPointInfo(), 5000, BeatDivisor) { IsCoupled = false };
}
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.Cache(BeatDivisor);
dependencies.CacheAs(Clock);
diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs
index 5f70055021..67a13bd850 100644
--- a/osu.Game/Tests/Visual/OsuTestCase.cs
+++ b/osu.Game/Tests/Visual/OsuTestCase.cs
@@ -1,10 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Configuration;
+using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
@@ -20,9 +22,12 @@ namespace osu.Game.Tests.Visual
protected DependencyContainer Dependencies { get; private set; }
- protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ private readonly Lazy localStorage;
+ protected Storage LocalStorage => localStorage.Value;
+
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
- Dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ Dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
Dependencies.CacheAs(beatmap);
Dependencies.CacheAs(beatmap);
@@ -33,6 +38,11 @@ namespace osu.Game.Tests.Visual
return Dependencies;
}
+ protected OsuTestCase()
+ {
+ localStorage = new Lazy(() => new DesktopStorage($"{GetType().Name}-{Guid.NewGuid()}", null));
+ }
+
[BackgroundDependencyLoader]
private void load(AudioManager audioManager, RulesetStore rulesets)
{
@@ -50,6 +60,18 @@ namespace osu.Game.Tests.Visual
beatmap.Disabled = true;
beatmap.Value.Track.Stop();
}
+
+ if (localStorage.IsValueCreated)
+ {
+ try
+ {
+ localStorage.Value.DeleteDirectory(".");
+ }
+ catch
+ {
+ // we don't really care if this fails; it will just leave folders lying around from test runs.
+ }
+ }
}
protected override ITestCaseTestRunner CreateRunner() => new OsuTestCaseTestRunner();
diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs
index c9b577a62f..80039eadad 100644
--- a/osu.Game/Users/Country.cs
+++ b/osu.Game/Users/Country.cs
@@ -42,7 +42,9 @@ namespace osu.Game.Users
return;
country = value;
- sprite.Texture = getFlagTexture();
+
+ if (LoadState >= LoadState.Ready)
+ sprite.Texture = getFlagTexture();
}
}
diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs
index e1f68e1ce8..f42df4023f 100644
--- a/osu.Game/Users/User.cs
+++ b/osu.Game/Users/User.cs
@@ -23,9 +23,6 @@ namespace osu.Game.Users
public Bindable Status = new Bindable();
- [JsonProperty(@"age")]
- public int? Age;
-
//public Team Team;
[JsonProperty(@"profile_colour")]
diff --git a/osu.Game/Utils/RavenLogger.cs b/osu.Game/Utils/RavenLogger.cs
new file mode 100644
index 0000000000..b28dd1fb73
--- /dev/null
+++ b/osu.Game/Utils/RavenLogger.cs
@@ -0,0 +1,89 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using osu.Framework.Logging;
+using SharpRaven;
+using SharpRaven.Data;
+
+namespace osu.Game.Utils
+{
+ ///
+ /// Report errors to sentry.
+ ///
+ public class RavenLogger : IDisposable
+ {
+ private readonly RavenClient raven = new RavenClient("https://5e342cd55f294edebdc9ad604d28bbd3@sentry.io/1255255");
+
+ private readonly List tasks = new List();
+
+ private Exception lastException;
+
+ public RavenLogger(OsuGame game)
+ {
+ raven.Release = game.Version;
+
+ if (!game.IsDeployedBuild) return;
+
+ Logger.NewEntry += entry =>
+ {
+ if (entry.Level < LogLevel.Verbose) return;
+
+ var exception = entry.Exception;
+
+ if (exception != null)
+ {
+ // since we let unhandled exceptions go ignored at times, we want to ensure they don't get submitted on subsequent reports.
+ if (lastException != null &&
+ lastException.Message == exception.Message && exception.StackTrace.StartsWith(lastException.StackTrace))
+ {
+ return;
+ }
+
+ lastException = exception;
+ queuePendingTask(raven.CaptureAsync(new SentryEvent(exception)));
+ }
+ else
+ raven.AddTrail(new Breadcrumb(entry.Target.ToString(), BreadcrumbType.Navigation) { Message = entry.Message });
+ };
+ }
+
+ private void queuePendingTask(Task task)
+ {
+ lock (tasks) tasks.Add(task);
+ task.ContinueWith(_ =>
+ {
+ lock (tasks)
+ tasks.Remove(task);
+ });
+ }
+
+ #region Disposal
+
+ ~RavenLogger()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private bool isDisposed;
+
+ protected virtual void Dispose(bool isDisposing)
+ {
+ if (isDisposed)
+ return;
+
+ isDisposed = true;
+ lock (tasks) Task.WaitAll(tasks.ToArray(), 5000);
+ }
+
+ #endregion
+ }
+}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index da55726447..2115453c5e 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -14,13 +14,14 @@
-
+
-
-
+
+
+
\ No newline at end of file
diff --git a/osu.Game/osu.nuspec b/osu.Game/osu.nuspec
deleted file mode 100644
index bb7d382cee..0000000000
--- a/osu.Game/osu.nuspec
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
- osulazer
- 0.0.0
- osulazer
- ppy Pty Ltd
- Dean Herbert
- https://osu.ppy.sh/
- https://puu.sh/tYyXZ/9a01a5d1b0.ico
- false
- click the circles. to the beat.
- click the circles.
- testing
- Copyright ppy Pty Ltd 2007-2018
- en-AU
-
-
-
-
-
-
-
-
-
-
diff --git a/osu.TestProject.props b/osu.TestProject.props
index c2e6048a60..a73a4f8ce2 100644
--- a/osu.TestProject.props
+++ b/osu.TestProject.props
@@ -13,9 +13,6 @@
-
-
-
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index 0b631d008b..1f1b6a79b1 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -23,6 +23,7 @@
HINT
SUGGESTION
HINT
+ HINT
HINT
WARNING
WARNING
@@ -43,6 +44,7 @@
DO_NOT_SHOW
WARNING
WARNING
+ HINT
HINT
ERROR
HINT
@@ -133,6 +135,7 @@
WARNING
WARNING
WARNING
+ HINT
DO_NOT_SHOW
DO_NOT_SHOW
DO_NOT_SHOW
@@ -666,4 +669,22 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
True
True
True
- True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True