1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-14 03:02:37 +08:00

Compare commits

..

374 Commits

222 changed files with 3702 additions and 2947 deletions
+1 -4
View File
@@ -1,6 +1,3 @@
[submodule "osu-framework"]
path = osu-framework
url = https://github.com/ppy/osu-framework
[submodule "osu-resources"]
path = osu-resources
url = https://github.com/ppy/osu-resources
url = https://github.com/ppy/osu-resources
@@ -1,21 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (catch)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
@@ -1,21 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (mania)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
@@ -1,21 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (osu!)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
@@ -1,21 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (taiko)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
@@ -1,12 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="VisualTests (netcoreapp2.1)" type="DotNetProject" factoryName=".NET Project">
<configuration default="false" name="VisualTests" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp2.1/osu.Game.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs />
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Tests/osu.Game.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
@@ -1,18 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="VisualTests (net471)" type="DotNetProject" factoryName=".NET Project" singleton="true">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs />
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Tests/osu.Game.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<method />
</configuration>
</component>
@@ -1,12 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! (netcoreapp2.1)" type="DotNetProject" factoryName=".NET Project">
<configuration default="false" name="osu!" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs />
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Desktop/osu.Desktop.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
@@ -1,18 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! (net471)" type="DotNetProject" factoryName=".NET Project" singleton="true">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net471/osu!.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs />
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Desktop/osu.Desktop.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<method />
</configuration>
</component>
+8 -64
View File
@@ -2,63 +2,7 @@
"version": "0.2.0",
"configurations": [
{
"name": "VisualTests (Debug, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net471/osu.Game.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}/osu.Game.Tests/bin/Release/net471/osu.Game.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "osu! (Debug, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Desktop/bin/Debug/net471/osu!.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "osu! (Release, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Desktop/bin/Release/net471/osu!.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",
@@ -66,12 +10,12 @@
"${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.1/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Debug, dotnet)",
"preLaunchTask": "Build tests (Debug)",
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release, netcoreapp2.1)",
"name": "VisualTests (Release)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -79,12 +23,12 @@
"${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.1/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Release, dotnet)",
"preLaunchTask": "Build tests (Release)",
"env": {},
"console": "internalConsole"
},
{
"name": "osu! (Debug, netcoreapp2.1)",
"name": "osu! (Debug)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -92,12 +36,12 @@
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug, dotnet)",
"preLaunchTask": "Build osu! (Debug)",
"env": {},
"console": "internalConsole"
},
{
"name": "osu! (Release, netcoreapp2.1)",
"name": "osu! (Release)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -105,7 +49,7 @@
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1/osu!.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release, dotnet)",
"preLaunchTask": "Build osu! (Release)",
"env": {},
"console": "internalConsole"
}
+4 -40
View File
@@ -4,34 +4,7 @@
"version": "2.0.0",
"tasks": [
{
"label": "Build (Debug, msbuild)",
"type": "shell",
"command": "msbuild",
"args": [
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build (Release, msbuild)",
"type": "shell",
"command": "msbuild",
"args": [
"/p:Configuration=Release",
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build osu! (Debug, dotnet)",
"label": "Build osu! (Debug)",
"type": "shell",
"command": "dotnet",
"args": [
@@ -47,7 +20,7 @@
"problemMatcher": "$msCompile"
},
{
"label": "Build osu! (Release, dotnet)",
"label": "Build osu! (Release)",
"type": "shell",
"command": "dotnet",
"args": [
@@ -64,7 +37,7 @@
"problemMatcher": "$msCompile"
},
{
"label": "Build tests (Debug, dotnet)",
"label": "Build tests (Debug)",
"type": "shell",
"command": "dotnet",
"args": [
@@ -80,7 +53,7 @@
"problemMatcher": "$msCompile"
},
{
"label": "Build tests (Release, dotnet)",
"label": "Build tests (Release)",
"type": "shell",
"command": "dotnet",
"args": [
@@ -96,15 +69,6 @@
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Restore (net471)",
"type": "shell",
"command": "nuget",
"args": [
"restore"
],
"problemMatcher": []
},
{
"label": "Restore (netcoreapp2.1)",
"type": "shell",
-36
View File
@@ -1,36 +0,0 @@
# Linux
### 1. Requirements:
Mono >= 5.4.0 (>= 5.8.0 recommended)
Please check [here](http://www.mono-project.com/download/) for stable or [here](http://www.mono-project.com/download/alpha/) for an alpha release.
NuGet >= 4.4.0
msbuild
git
### 2. Cloning project
Clone the entire repository with submodules using
```
git clone https://github.com/ppy/osu --recursive
```
Then restore NuGet packages from the repository
```
nuget restore
```
### 3. Compiling
Simply run `msbuild` where `osu.sln` is located, this will create all binaries in `osu/osu.Desktop/bin/Debug`.
### 4. Optimizing
If you want additional performance you can change build type to Release with
```
msbuild -p:Configuration=Release
```
Additionally, mono provides an AOT utility which attempts to precompile binaries. You can utilize that by running
```
mono --aot ./osu\!.exe
```
### 5. Troubleshooting
You may run into trouble with NuGet versioning, as the one in packaging system is almost always out of date. Simply run
```
nuget
sudo nuget update -self
```
**Warning** NuGet creates few config files when it's run for the first time.
Do not run NuGet as root on the first run or you might run into very peculiar issues.
+1 -6
View File
@@ -1,12 +1,7 @@
clone_depth: 1
version: '{branch}-{build}'
image: Visual Studio 2017 preview
image: Visual Studio 2017
configuration: Debug
cache:
- C:\ProgramData\chocolatey\bin -> appveyor.yml
- C:\ProgramData\chocolatey\lib -> appveyor.yml
- inspectcode -> appveyor.yml
- packages -> **\packages.config
install:
- cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y
+2 -4
View File
@@ -1,9 +1,7 @@
clone_depth: 1
version: '{build}'
# skip_non_tags: true
skip_non_tags: true
image: Visual Studio 2017
cache:
- '%USERPROFILE%\.nuget\packages -> **\*.csproj'
install:
- git clone https://github.com/ppy/osu-deploy
before_build:
@@ -18,7 +16,7 @@ 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-deploy.exe.config
- 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%
environment:
Submodule osu-framework deleted from b963ce8250
+9 -6
View File
@@ -1,19 +1,20 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Diagnostics;
using System;
using osu.Framework.Allocation;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Platform;
using osu.Game;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Utils;
using OpenTK;
using OpenTK.Graphics;
@@ -24,16 +25,18 @@ namespace osu.Desktop.Overlays
private OsuConfigManager config;
private OsuGameBase game;
private NotificationOverlay notificationOverlay;
private GameHost host;
public override bool HandleKeyboardInput => false;
public override bool HandleMouseInput => false;
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config, GameHost host)
{
notificationOverlay = notification;
this.config = config;
this.game = game;
this.host = host;
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomCentre;
@@ -106,19 +109,19 @@ namespace osu.Desktop.Overlays
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
if (!string.IsNullOrEmpty(lastVersion))
notificationOverlay.Post(new UpdateCompleteNotification(version));
notificationOverlay.Post(new UpdateCompleteNotification(version, host.OpenUrlExternally));
}
}
private class UpdateCompleteNotification : SimpleNotification
{
public UpdateCompleteNotification(string version)
public UpdateCompleteNotification(string version, Action<string> openUrl = null)
{
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
Icon = FontAwesome.fa_check_square;
Activated = delegate
{
Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}");
openUrl?.Invoke($"https://osu.ppy.sh/home/changelog/lazer/{version}");
return true;
};
}
+2 -3
View File
@@ -20,17 +20,16 @@
<StartupObject>osu.Desktop.Program</StartupObject>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj" />
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0" />
<PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
</ItemGroup>
<ItemGroup Label="Resources">
@@ -53,10 +53,10 @@ namespace osu.Game.Rulesets.Catch.Tests
return beatmap;
}
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
protected override Player CreatePlayer(Ruleset ruleset)
{
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
return base.CreatePlayer(beatmap, ruleset);
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
return base.CreatePlayer(ruleset);
}
}
}
@@ -1,16 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()
: base(new CatchRuleset())
{
}
}
}
+5 -36
View File
@@ -82,56 +82,25 @@ namespace osu.Game.Rulesets.Catch
{
new CatchModEasy(),
new CatchModNoFail(),
new MultiMod
{
Mods = new Mod[]
{
new CatchModHalfTime(),
new CatchModDaycore(),
},
},
new MultiMod(new CatchModHalfTime(), new CatchModDaycore())
};
case ModType.DifficultyIncrease:
return new Mod[]
{
new CatchModHardRock(),
new MultiMod
{
Mods = new Mod[]
{
new CatchModSuddenDeath(),
new CatchModPerfect(),
},
},
new MultiMod
{
Mods = new Mod[]
{
new CatchModDoubleTime(),
new CatchModNightcore(),
},
},
new MultiMod(new CatchModSuddenDeath(), new CatchModPerfect()),
new MultiMod(new CatchModDoubleTime(), new CatchModNightcore()),
new CatchModHidden(),
new CatchModFlashlight(),
};
case ModType.Special:
return new Mod[]
{
new CatchModRelax(),
null,
null,
new MultiMod
{
Mods = new Mod[]
{
new CatchModAutoplay(),
new ModCinema(),
},
},
new MultiMod(new CatchModAutoplay(), new ModCinema()),
};
default:
return new Mod[] { };
}
@@ -143,7 +112,7 @@ namespace osu.Game.Rulesets.Catch
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
public override int? LegacyID => 2;
@@ -1,18 +1,19 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Difficulty
{
public class CatchDifficultyCalculator : DifficultyCalculator
{
public CatchDifficultyCalculator(IBeatmap beatmap) : base(beatmap)
public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) => new DifficultyAttributes(mods, 0);
}
}
@@ -42,6 +42,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public virtual bool CanBePlated => false;
public virtual bool StaysOnPlate => CanBePlated;
protected DrawableCatchHitObject(CatchHitObject hitObject)
: base(hitObject)
{
@@ -13,6 +13,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
private Pulp pulp;
public override bool StaysOnPlate => false;
public DrawableDroplet(Droplet h)
: base(h)
{
@@ -124,6 +124,9 @@ namespace osu.Game.Rulesets.Catch.Objects
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
});
}
if (NestedHitObjects.LastOrDefault() is IHasComboInformation lastNested)
lastNested.LastInCombo = LastInCombo;
}
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
+36 -27
View File
@@ -48,6 +48,16 @@ namespace osu.Game.Rulesets.Catch.UI
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
{
void runAfterLoaded(Action action)
{
// 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)
action();
else
lastPlateableFruit.OnLoadComplete = _ => action();
}
if (judgement.IsHit && fruit.CanBePlated)
{
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
@@ -63,21 +73,17 @@ namespace osu.Game.Rulesets.Catch.UI
caughtFruit.LifetimeEnd = double.MaxValue;
MovableCatcher.Add(caughtFruit);
lastPlateableFruit = caughtFruit;
if (!fruit.StaysOnPlate)
runAfterLoaded(() => MovableCatcher.Explode(caughtFruit));
}
if (fruit.HitObject.LastInCombo)
{
if (judgement.IsHit)
{
// 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)
MovableCatcher.Explode();
else
lastPlateableFruit.OnLoadComplete = _ => { MovableCatcher.Explode(); };
}
runAfterLoaded(() => MovableCatcher.Explode());
else
MovableCatcher.Drop();
}
@@ -378,28 +384,31 @@ namespace osu.Game.Rulesets.Catch.UI
var fruit = caughtFruit.ToArray();
foreach (var f in fruit)
Explode(f);
}
public void Explode(DrawableHitObject fruit)
{
var originalX = fruit.X * Scale.X;
if (ExplodingFruitTarget != null)
{
var originalX = f.X * Scale.X;
fruit.Anchor = Anchor.TopLeft;
fruit.Position = caughtFruit.ToSpaceOfOtherDrawable(fruit.DrawPosition, ExplodingFruitTarget);
if (ExplodingFruitTarget != null)
{
f.Anchor = Anchor.TopLeft;
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
caughtFruit.Remove(fruit);
caughtFruit.Remove(f);
ExplodingFruitTarget.Add(f);
}
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
.Then()
.MoveToY(f.Y + 50, 500, Easing.InSine);
f.MoveToX(f.X + originalX * 6, 1000);
f.FadeOut(750);
f.Expire();
ExplodingFruitTarget.Add(fruit);
}
fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine)
.Then()
.MoveToY(fruit.Y + 50, 500, Easing.InSine);
fruit.MoveToX(fruit.X + originalX * 6, 1000);
fruit.FadeOut(750);
fruit.Expire();
}
private class CatcherSprite : Sprite
@@ -98,17 +98,12 @@ namespace osu.Game.Rulesets.Mania.Tests
});
}
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets, SettingsStore settings)
{
maniaRuleset = rulesets.GetRuleset(3);
dependencies.Cache(new ManiaConfigManager(settings, maniaRuleset, 4));
Dependencies.Cache(new ManiaConfigManager(settings, maniaRuleset, 4));
}
private ManiaPlayfield createPlayfield(int cols, bool inverted = false)
@@ -1,16 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()
: base(new ManiaRuleset())
{
}
}
}
@@ -58,6 +58,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
public override Pattern Generate()
{
if (TotalColumns == 1)
{
var pattern = new Pattern();
addToPattern(pattern, 0, HitObject.StartTime, endTime);
return pattern;
}
if (spanCount > 1)
{
if (segmentDuration <= 90)
@@ -77,10 +77,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
}
else
convertType |= PatternType.LowProbability;
if ((convertType & PatternType.KeepSingle) == 0)
{
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
convertType |= PatternType.Mirror;
else
convertType |= PatternType.Gathered;
}
}
public override Pattern Generate()
{
if (TotalColumns == 1)
{
var pattern = new Pattern();
addToPattern(pattern, 0);
return pattern;
}
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
@@ -346,7 +361,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
addToCentre = false;
if ((convertType & PatternType.ForceNotStack) > 0)
return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3);
return getRandomNoteCount(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
switch (TotalColumns)
{
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
{
public class ManiaConfigManager : RulesetConfigManager<ManiaSetting>
{
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant)
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
: base(settings, ruleset, variant)
{
}
@@ -7,6 +7,7 @@ using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
@@ -28,47 +29,36 @@ namespace osu.Game.Rulesets.Mania.Difficulty
/// </summary>
private const double decay_weight = 0.9;
/// <summary>
/// HitObjects are stored as a member variable.
/// </summary>
private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
private readonly bool isForCurrentRuleset;
public ManiaDifficultyCalculator(IBeatmap beatmap)
: base(beatmap)
public ManiaDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
}
public ManiaDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
: base(beatmap, mods)
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
{
}
var difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
{
// Fill our custom DifficultyHitObject class, that carries additional information
difficultyHitObjects.Clear();
int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7;
int columnCount = ((ManiaBeatmap)beatmap).TotalColumns;
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
// Note: Stable sort is done so that the ordering of hitobjects with equal start times doesn't change
difficultyHitObjects.AddRange(Beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime));
difficultyHitObjects.AddRange(beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime));
if (!calculateStrainValues())
return 0;
if (!calculateStrainValues(difficultyHitObjects, timeRate))
return new DifficultyAttributes(mods, 0);
double starRating = calculateDifficulty() * star_scaling_factor;
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
if (categoryDifficulty != null)
categoryDifficulty["Strain"] = starRating;
return starRating;
return new DifficultyAttributes(mods, starRating);
}
private bool calculateStrainValues()
private bool calculateStrainValues(List<ManiaHitObjectDifficulty> objects, double timeRate)
{
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
using (List<ManiaHitObjectDifficulty>.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator())
using (var hitObjectsEnumerator = objects.GetEnumerator())
{
if (!hitObjectsEnumerator.MoveNext())
return false;
@@ -79,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
while (hitObjectsEnumerator.MoveNext())
{
var next = hitObjectsEnumerator.Current;
next?.CalculateStrains(current, TimeRate);
next?.CalculateStrains(current, timeRate);
current = next;
}
@@ -87,9 +77,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty
}
}
private double calculateDifficulty()
private double calculateDifficulty(List<ManiaHitObjectDifficulty> objects, double timeRate)
{
double actualStrainStep = strain_step * TimeRate;
double actualStrainStep = strain_step * timeRate;
// Find the highest strain value within each strain step
List<double> highestStrains = new List<double>();
@@ -97,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
ManiaHitObjectDifficulty previousHitObject = null;
foreach (var hitObject in difficultyHitObjects)
foreach (var hitObject in objects)
{
// While we are beyond the current interval push the currently available maximum to our strain list
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
@@ -141,5 +131,36 @@ namespace osu.Game.Rulesets.Mania.Difficulty
return difficulty;
}
protected override Mod[] DifficultyAdjustmentMods
{
get
{
var mods = new Mod[]
{
new ManiaModDoubleTime(),
new ManiaModHalfTime(),
new ManiaModEasy(),
new ManiaModHardRock(),
};
if (isForCurrentRuleset)
return mods;
// if we are a convert, we can be played in any key mod.
return mods.Concat(new Mod[]
{
new ManiaModKey1(),
new ManiaModKey2(),
new ManiaModKey3(),
new ManiaModKey4(),
new ManiaModKey5(),
new ManiaModKey6(),
new ManiaModKey7(),
new ManiaModKey8(),
new ManiaModKey9(),
}).ToArray();
}
}
}
}
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
private int countMeh;
private int countMiss;
public ManiaPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
public ManiaPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
: base(ruleset, beatmap, score)
{
}
@@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
private double computeStrainValue()
{
// Obtain strain difficulty
double strainValue = Math.Pow(5 * Math.Max(1, Attributes["Strain"] / 0.2) - 4.0, 2.2) / 135.0;
double strainValue = Math.Pow(5 * Math.Max(1, Attributes.StarRating / 0.2) - 4.0, 2.2) / 135.0;
// Longer maps are worth more
strainValue *= 1.0 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
+21 -60
View File
@@ -15,8 +15,11 @@ using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Configuration;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Rulesets.Scoring;
@@ -26,7 +29,7 @@ namespace osu.Game.Rulesets.Mania
{
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new ManiaRulesetContainer(this, beatmap);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => new ManiaPerformanceCalculator(this, beatmap, score);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new ManiaPerformanceCalculator(this, beatmap, score);
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{
@@ -105,78 +108,34 @@ namespace osu.Game.Rulesets.Mania
{
new ManiaModEasy(),
new ManiaModNoFail(),
new MultiMod
{
Mods = new Mod[]
{
new ManiaModHalfTime(),
new ManiaModDaycore(),
},
},
new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()),
};
case ModType.DifficultyIncrease:
return new Mod[]
{
new ManiaModHardRock(),
new MultiMod
{
Mods = new Mod[]
{
new ManiaModSuddenDeath(),
new ManiaModPerfect(),
},
},
new MultiMod
{
Mods = new Mod[]
{
new ManiaModDoubleTime(),
new ManiaModNightcore(),
},
},
new MultiMod
{
Mods = new Mod[]
{
new ManiaModFadeIn(),
new ManiaModHidden(),
}
},
new MultiMod(new ManiaModSuddenDeath(), new ManiaModPerfect()),
new MultiMod(new ManiaModDoubleTime(), new ManiaModNightcore()),
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
new ManiaModFlashlight(),
};
case ModType.Special:
return new Mod[]
{
new MultiMod
{
Mods = new Mod[]
{
new ManiaModKey4(),
new ManiaModKey5(),
new ManiaModKey6(),
new ManiaModKey7(),
new ManiaModKey8(),
new ManiaModKey9(),
new ManiaModKey1(),
new ManiaModKey2(),
new ManiaModKey3(),
},
},
new MultiMod(new ManiaModKey4(),
new ManiaModKey5(),
new ManiaModKey6(),
new ManiaModKey7(),
new ManiaModKey8(),
new ManiaModKey9(),
new ManiaModKey1(),
new ManiaModKey2(),
new ManiaModKey3()),
new ManiaModRandom(),
new ManiaModDualStages(),
new ManiaModMirror(),
new MultiMod
{
Mods = new Mod[]
{
new ManiaModAutoplay(),
new ModCinema(),
},
},
new MultiMod(new ManiaModAutoplay(), new ModCinema()),
};
default:
return new Mod[] { };
}
@@ -188,12 +147,14 @@ namespace osu.Game.Rulesets.Mania
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap);
public override int? LegacyID => 3;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaConfigManager(settings, RulesetInfo);
public ManiaRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{
@@ -1,6 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// 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.Mania.Beatmaps;
using osu.Game.Rulesets.Mods;
@@ -24,5 +26,18 @@ namespace osu.Game.Rulesets.Mania.Mods
mbc.TargetColumns = KeyCount;
}
public override Type[] IncompatibleMods => new[]
{
typeof(ManiaModKey1),
typeof(ManiaModKey2),
typeof(ManiaModKey3),
typeof(ManiaModKey4),
typeof(ManiaModKey5),
typeof(ManiaModKey6),
typeof(ManiaModKey7),
typeof(ManiaModKey8),
typeof(ManiaModKey9),
}.Except(new[] { GetType() }).ToArray();
}
}
@@ -5,6 +5,7 @@ using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
@@ -28,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
: base(barLine)
{
RelativeSizeAxes = Axes.X;
Height = 1;
Height = 2f;
AddInternal(new Box
{
@@ -36,6 +37,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Colour = new Color4(255, 204, 33, 255),
});
bool isMajor = barLine.BeatIndex % (int)barLine.ControlPoint.TimeSignature == 0;
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
@@ -23,9 +23,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
private readonly DrawableNote head;
private readonly DrawableNote tail;
private readonly GlowPiece glowPiece;
private readonly BodyPiece bodyPiece;
private readonly Container fullHeightContainer;
/// <summary>
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
@@ -37,25 +35,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary>
private bool hasBroken;
private readonly Container<DrawableHoldNoteTick> tickContainer;
public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
: base(hitObject, action)
{
Container<DrawableHoldNoteTick> tickContainer;
RelativeSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
// The hit object itself cannot be used for various elements because the tail overshoots it
// So a specialized container that is updated to contain the tail height is used
fullHeightContainer = new Container
{
RelativeSizeAxes = Axes.X,
Child = glowPiece = new GlowPiece()
},
bodyPiece = new BodyPiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
},
tickContainer = new Container<DrawableHoldNoteTick>
@@ -92,21 +82,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
base.AccentColour = value;
glowPiece.AccentColour = value;
bodyPiece.AccentColour = value;
head.AccentColour = value;
tail.AccentColour = value;
}
}
protected override void UpdateState(ArmedState state)
{
switch (state)
{
case ArmedState.Hit:
// Good enough for now, we just want them to have a lifetime end
this.Delay(2000).Expire();
break;
tickContainer.ForEach(t => t.AccentColour = value);
}
}
@@ -121,12 +100,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.Update();
// Make the body piece not lie under the head note
bodyPiece.Y = head.Height;
bodyPiece.Height = DrawHeight - head.Height;
// Make the fullHeightContainer "contain" the height of the tail note, keeping in mind
// that the tail note overshoots the height of this hit object
fullHeightContainer.Height = DrawHeight + tail.Height;
bodyPiece.Y = head.Height / 2;
bodyPiece.Height = DrawHeight - head.Height / 2 + tail.Height / 2;
}
public bool OnPressed(ManiaAction action)
@@ -175,8 +150,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
: base(holdNote.HitObject.Head, action)
{
this.holdNote = holdNote;
GlowPiece.Alpha = 0;
}
public override bool OnPressed(ManiaAction action)
@@ -194,11 +167,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return true;
}
protected override void UpdateState(ArmedState state)
{
// The holdnote keeps scrolling through for now, so having the head disappear looks weird
}
}
/// <summary>
@@ -219,8 +187,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
: base(holdNote.HitObject.Tail, action)
{
this.holdNote = holdNote;
GlowPiece.Alpha = 0;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
@@ -253,11 +219,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
});
}
protected override void UpdateState(ArmedState state)
{
// The holdnote keeps scrolling through, so having the tail disappear looks weird
}
public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down
public override bool OnReleased(ManiaAction action)
@@ -8,7 +8,6 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Scoring;
@@ -87,16 +86,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
}
protected override void UpdateState(ArmedState state)
{
switch (State.Value)
{
case ArmedState.Hit:
AccentColour = Color4.Green;
break;
}
}
protected override void Update()
{
if (AllJudged)
@@ -27,5 +27,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != null)
Action = action.Value;
}
protected override void UpdateState(ArmedState state)
{
switch (state)
{
case ArmedState.Miss:
this.FadeOut(150, Easing.In).Expire();
break;
case ArmedState.Hit:
this.FadeOut(150, Easing.OutQuint).Expire();
break;
}
}
}
}
@@ -1,12 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
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.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
@@ -16,9 +17,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary>
public class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHandler<ManiaAction>
{
protected readonly GlowPiece GlowPiece;
private readonly LaneGlowPiece laneGlowPiece;
private readonly NotePiece headPiece;
public DrawableNote(Note hitObject, ManiaAction action)
@@ -27,14 +25,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
CornerRadius = 5;
Masking = true;
InternalChildren = new Drawable[]
{
laneGlowPiece = new LaneGlowPiece
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
GlowPiece = new GlowPiece(),
headPiece = new NotePiece
{
Anchor = Anchor.TopCentre,
@@ -49,9 +44,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
set
{
base.AccentColour = value;
laneGlowPiece.AccentColour = AccentColour;
GlowPiece.AccentColour = AccentColour;
headPiece.AccentColour = AccentColour;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = AccentColour.Lighten(1f).Opacity(0.6f),
Radius = 10,
};
}
}
@@ -71,17 +71,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
AddJudgement(new ManiaJudgement { Result = result });
}
protected override void UpdateState(ArmedState state)
{
switch (state)
{
case ArmedState.Hit:
case ArmedState.Miss:
this.FadeOut(100).Expire();
break;
}
}
public virtual bool OnPressed(ManiaAction action)
{
if (action != Action)
@@ -123,8 +123,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
if (!IsLoaded)
return;
foreground.Colour = AccentColour.Opacity(0.4f);
background.Colour = AccentColour.Opacity(0.2f);
foreground.Colour = AccentColour.Opacity(0.9f);
background.Colour = AccentColour.Opacity(0.6f);
subtractionCache.Invalidate();
}
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
/// </summary>
internal class NotePiece : Container, IHasAccentColour
{
private const float head_height = 10;
public const float NOTE_HEIGHT = 10;
private const float head_colour_height = 6;
private readonly Box colouredBox;
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
public NotePiece()
{
RelativeSizeAxes = Axes.X;
Height = head_height;
Height = NOTE_HEIGHT;
Children = new[]
{
+27 -10
View File
@@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Mania.UI
public ManiaAction Action;
private readonly Box background;
private readonly Box backgroundOverlay;
private readonly Container hitTargetBar;
private readonly Container keyIcon;
@@ -42,22 +43,32 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content;
private const float opacity_released = 0.1f;
private const float opacity_pressed = 0.25f;
public Column()
: base(ScrollingDirection.Up)
{
RelativeSizeAxes = Axes.Y;
Width = column_width;
Masking = true;
CornerRadius = 5;
InternalChildren = new Drawable[]
{
background = new Box
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
Alpha = opacity_released
Alpha = 0.3f
},
backgroundOverlay = new Box
{
Name = "Background Gradient Overlay",
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Blending = BlendingMode.Additive,
Alpha = 0
},
new Container
{
@@ -182,6 +193,7 @@ namespace osu.Game.Rulesets.Mania.UI
accentColour = value;
background.Colour = accentColour;
backgroundOverlay.Colour = ColourInfo.GradientVertical(accentColour.Opacity(0.6f), accentColour.Opacity(0));
hitTargetBar.EdgeEffect = new EdgeEffectParameters
{
@@ -223,8 +235,8 @@ namespace osu.Game.Rulesets.Mania.UI
{
if (action == Action)
{
background.FadeTo(opacity_pressed, 50, Easing.OutQuint);
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint);
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint).Then().ScaleTo(1.3f, 250, Easing.OutQuint);
}
return false;
@@ -234,8 +246,8 @@ namespace osu.Game.Rulesets.Mania.UI
{
if (action == Action)
{
background.FadeTo(opacity_released, 800, Easing.OutQuart);
keyIcon.ScaleTo(1f, 400, Easing.OutQuart);
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
}
return false;
@@ -265,8 +277,13 @@ namespace osu.Game.Rulesets.Mania.UI
if (action != Action)
return false;
var hitObject = HitObjects.Objects.LastOrDefault(h => h.HitObject.StartTime > Time.Current) ?? HitObjects.Objects.FirstOrDefault();
hitObject?.PlaySamples();
var nextObject =
HitObjects.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ??
// fallback to non-alive objects to find next off-screen object
HitObjects.Objects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ??
HitObjects.Objects.LastOrDefault();
nextObject?.PlaySamples();
return true;
}
+23 -20
View File
@@ -1,19 +1,23 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
namespace osu.Game.Rulesets.Mania.UI
{
internal class HitExplosion : CompositeDrawable
{
private readonly Box inner;
public override bool RemoveWhenNotAlive => true;
private readonly CircularContainer circle;
public HitExplosion(DrawableHitObject judgedObject)
{
@@ -22,33 +26,32 @@ namespace osu.Game.Rulesets.Mania.UI
Anchor = Anchor.TopCentre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
Size = new Vector2(isTick ? 0.5f : 1);
FillMode = FillMode.Fit;
RelativeSizeAxes = Axes.X;
Y = NotePiece.NOTE_HEIGHT / 2;
Height = NotePiece.NOTE_HEIGHT;
Blending = BlendingMode.Additive;
// scale roughly in-line with visual appearance of notes
Scale = new Vector2(isTick ? 0.4f : 0.8f);
Color4 accent = isTick ? Color4.White : judgedObject.AccentColour;
InternalChild = new CircularContainer
InternalChild = circle = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = 1,
BorderColour = accent,
// we want our size to be very small so the glow dominates it.
Size = new Vector2(0.1f),
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = accent,
Radius = 10,
Hollow = true
Colour = Interpolation.ValueAt(0.1f, judgedObject.AccentColour, Color4.White, 0, 1),
Radius = 100,
},
Child = inner = new Box
Child = new Box
{
Alpha = 0,
RelativeSizeAxes = Axes.Both,
Colour = accent,
Alpha = 1,
AlwaysPresent = true,
AlwaysPresent = true
}
};
}
@@ -57,8 +60,8 @@ namespace osu.Game.Rulesets.Mania.UI
{
base.LoadComplete();
this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500);
inner.FadeOut(250);
circle.ResizeTo(circle.Size * new Vector2(4, 20), 1000, Easing.OutQuint);
this.FadeIn(16).Then().FadeOut(500, Easing.OutQuint);
Expire(true);
}
@@ -10,12 +10,9 @@ using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration;
using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
@@ -103,7 +100,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);
protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant);
}
}
+5 -5
View File
@@ -9,7 +9,6 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
@@ -78,6 +77,7 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Masking = true,
CornerRadius = 5,
Children = new Drawable[]
{
new Box
@@ -183,15 +183,15 @@ namespace osu.Game.Rulesets.Mania.UI
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load()
{
normalColumnColours = new List<Color4>
{
colours.RedDark,
colours.GreenDark
new Color4(94, 0, 57, 255),
new Color4(6, 84, 0, 255)
};
specialColumnColour = colours.BlueDark;
specialColumnColour = new Color4(0, 48, 63, 255);
// Set the special column + colour + key
foreach (var column in Columns)
@@ -1,16 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()
: base(new OsuRuleset())
{
}
}
}
@@ -0,0 +1,19 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Difficulty
{
public class OsuDifficultyAttributes : DifficultyAttributes
{
public double AimStrain;
public double SpeedStrain;
public OsuDifficultyAttributes(Mod[] mods, double starRating)
: base(mods, starRating)
{
}
}
}
@@ -2,12 +2,13 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Difficulty.Skills;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Difficulty
@@ -17,31 +18,26 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private const int section_length = 400;
private const double difficulty_multiplier = 0.0675;
public OsuDifficultyCalculator(IBeatmap beatmap)
: base(beatmap)
public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
public OsuDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
: base(beatmap, mods)
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
{
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
{
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap((List<OsuHitObject>)Beatmap.HitObjects, TimeRate);
OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast<OsuHitObject>().ToList(), timeRate);
Skill[] skills =
{
new Aim(),
new Speed()
};
double sectionLength = section_length * TimeRate;
double sectionLength = section_length * timeRate;
// The first object doesn't generate a strain, so we begin with an incremented section end
double currentSectionEnd = 2 * sectionLength;
foreach (OsuDifficultyHitObject h in beatmap)
foreach (OsuDifficultyHitObject h in difficultyBeatmap)
{
while (h.BaseObject.StartTime > currentSectionEnd)
{
@@ -60,16 +56,21 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
if (categoryDifficulty != null)
return new OsuDifficultyAttributes(mods, starRating)
{
categoryDifficulty.Add("Aim", aimRating);
categoryDifficulty.Add("Speed", speedRating);
}
return starRating;
AimStrain = aimRating,
SpeedStrain = speedRating
};
}
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
{
new OsuModDoubleTime(),
new OsuModHalfTime(),
new OsuModEasy(),
new OsuModHardRock(),
};
}
}
@@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
public class OsuPerformanceCalculator : PerformanceCalculator
{
public new OsuDifficultyAttributes Attributes => (OsuDifficultyAttributes)base.Attributes;
private readonly int countHitCircles;
private readonly int beatmapMaxCombo;
@@ -37,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private int countMeh;
private int countMiss;
public OsuPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
public OsuPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
: base(ruleset, beatmap, score)
{
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
@@ -102,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private double computeAimValue()
{
double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Aim"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes.AimStrain / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
// Longer maps are worth more
double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
@@ -151,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private double computeSpeedValue()
{
double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Speed"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes.SpeedStrain / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
// Longer maps are worth more
speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
+10 -9
View File
@@ -7,33 +7,34 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects
public class OsuModHidden : ModHidden
{
public override string Description => @"Play with no approach circles and fading circles/sliders.";
public override double ScoreMultiplier => 1.06;
private const double fade_in_duration_multiplier = 0.4;
private const double fade_out_duration_multiplier = 0.3;
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
public override void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
void adjustFadeIn(OsuHitObject h) => h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier;
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
{
d.ApplyCustomUpdateState += ApplyHiddenState;
d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier;
adjustFadeIn(d.HitObject);
foreach (var h in d.HitObject.NestedHitObjects.OfType<OsuHitObject>())
h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier;
adjustFadeIn(h);
}
base.ApplyToDrawableHitObjects(drawables);
}
protected void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
{
if (!(drawable is DrawableOsuHitObject d))
return;
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using osu.Game.Graphics;
@@ -89,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// 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 (curve[i] == Position)
if (Precision.AlmostEquals(curve[i], Position))
continue;
Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X));
@@ -139,8 +139,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
var texture = new Texture(textureWidth, 1);
//initialise background
var upload = new TextureUpload(textureWidth * 4);
var bytes = upload.Data;
var raw = new RawTexture(textureWidth, 1);
var bytes = raw.Data;
const float aa_portion = 0.02f;
const float border_portion = 0.128f;
@@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
}
texture.SetData(upload);
texture.SetData(new TextureUpload(raw));
path.Texture = texture;
container.ForceRedraw();
+7 -38
View File
@@ -93,57 +93,26 @@ namespace osu.Game.Rulesets.Osu
{
new OsuModEasy(),
new OsuModNoFail(),
new MultiMod
{
Mods = new Mod[]
{
new OsuModHalfTime(),
new OsuModDaycore(),
},
},
new MultiMod(new OsuModHalfTime(), new OsuModDaycore()),
};
case ModType.DifficultyIncrease:
return new Mod[]
{
new OsuModHardRock(),
new MultiMod
{
Mods = new Mod[]
{
new OsuModSuddenDeath(),
new OsuModPerfect(),
},
},
new MultiMod
{
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModNightcore(),
},
},
new MultiMod(new OsuModSuddenDeath(), new OsuModPerfect()),
new MultiMod(new OsuModDoubleTime(), new OsuModNightcore()),
new OsuModHidden(),
new OsuModFlashlight(),
};
case ModType.Special:
return new Mod[]
{
new OsuModRelax(),
new OsuModAutopilot(),
new OsuModSpunOut(),
new MultiMod
{
Mods = new Mod[]
{
new OsuModAutoplay(),
new ModCinema(),
},
},
new MultiMod(new OsuModAutoplay(), new ModCinema()),
new OsuModTarget(),
};
default:
return new Mod[] { };
}
@@ -151,9 +120,9 @@ namespace osu.Game.Rulesets.Osu
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o };
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score);
public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this);
@@ -161,7 +130,7 @@ namespace osu.Game.Rulesets.Osu
public override string ShortName => "osu";
public override SettingsSubsection CreateSettings() => new OsuSettings();
public override RulesetSettingsSubsection CreateSettings() => new OsuSettings(this);
public override int? LegacyID => 0;
@@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private Bindable<double> cursorScale;
private Bindable<bool> autoCursorScale;
private Bindable<WorkingBeatmap> beatmap;
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
public OsuCursor()
{
@@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, OsuGameBase game)
private void load(OsuConfigManager config, IBindableBeatmap beatmap)
{
Child = cursorContainer = new SkinnableDrawable("cursor", _ => new CircularContainer
{
@@ -160,8 +160,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
RelativeSizeAxes = Axes.Both,
};
beatmap = game.Beatmap.GetBoundCopy();
beatmap.ValueChanged += v => calculateScale();
this.beatmap.BindTo(beatmap);
this.beatmap.ValueChanged += v => calculateScale();
cursorScale = config.GetBindable<double>(OsuSetting.GameplayCursorSize);
cursorScale.ValueChanged += v => calculateScale();
+1 -3
View File
@@ -64,9 +64,7 @@ namespace osu.Game.Rulesets.Osu.UI
public override void PostProcess()
{
connectionLayer.HitObjects = HitObjects.Objects
.Select(d => d.HitObject)
.OrderBy(h => h.StartTime).OfType<OsuHitObject>();
connectionLayer.HitObjects = HitObjects.Objects.Select(d => d.HitObject).OfType<OsuHitObject>();
}
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
+6 -1
View File
@@ -8,10 +8,15 @@ using osu.Game.Overlays.Settings;
namespace osu.Game.Rulesets.Osu.UI
{
public class OsuSettings : SettingsSubsection
public class OsuSettings : RulesetSettingsSubsection
{
protected override string Header => "osu!";
public OsuSettings(Ruleset ruleset)
: base(ruleset)
{
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
@@ -1,16 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
namespace osu.Game.Rulesets.Taiko.Tests
{
[TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()
: base(new TaikoRuleset())
{
}
}
}
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.Difficulty
@@ -26,46 +27,33 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
/// </summary>
private const double decay_weight = 0.9;
/// <summary>
/// HitObjects are stored as a member variable.
/// </summary>
private readonly List<TaikoHitObjectDifficulty> difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
public TaikoDifficultyCalculator(IBeatmap beatmap)
: base(beatmap)
public TaikoDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
public TaikoDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
: base(beatmap, mods)
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
{
}
var difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
{
// Fill our custom DifficultyHitObject class, that carries additional information
difficultyHitObjects.Clear();
foreach (var hitObject in Beatmap.HitObjects)
foreach (var hitObject in beatmap.HitObjects)
difficultyHitObjects.Add(new TaikoHitObjectDifficulty((TaikoHitObject)hitObject));
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
if (!calculateStrainValues()) return 0;
if (!calculateStrainValues(difficultyHitObjects, timeRate))
return new DifficultyAttributes(mods, 0);
double starRating = calculateDifficulty() * star_scaling_factor;
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
if (categoryDifficulty != null)
categoryDifficulty["Strain"] = starRating;
return starRating;
return new DifficultyAttributes(mods, starRating);
}
private bool calculateStrainValues()
private bool calculateStrainValues(List<TaikoHitObjectDifficulty> objects, double timeRate)
{
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
using (List<TaikoHitObjectDifficulty>.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator())
using (var hitObjectsEnumerator = objects.GetEnumerator())
{
if (!hitObjectsEnumerator.MoveNext()) return false;
@@ -75,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
while (hitObjectsEnumerator.MoveNext())
{
var next = hitObjectsEnumerator.Current;
next?.CalculateStrains(current, TimeRate);
next?.CalculateStrains(current, timeRate);
current = next;
}
@@ -83,9 +71,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
}
}
private double calculateDifficulty()
private double calculateDifficulty(List<TaikoHitObjectDifficulty> objects, double timeRate)
{
double actualStrainStep = strain_step * TimeRate;
double actualStrainStep = strain_step * timeRate;
// Find the highest strain value within each strain step
List<double> highestStrains = new List<double>();
@@ -93,7 +81,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
TaikoHitObjectDifficulty previousHitObject = null;
foreach (var hitObject in difficultyHitObjects)
foreach (var hitObject in objects)
{
// While we are beyond the current interval push the currently available maximum to our strain list
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
@@ -135,5 +123,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
return difficulty;
}
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
{
new TaikoModDoubleTime(),
new TaikoModHalfTime(),
new TaikoModEasy(),
new TaikoModHardRock(),
};
}
}
@@ -22,10 +22,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private int countMeh;
private int countMiss;
public TaikoPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
public TaikoPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
: base(ruleset, beatmap, score)
{
beatmapMaxCombo = beatmap.HitObjects.Count(h => h is Hit);
beatmapMaxCombo = Beatmap.HitObjects.Count(h => h is Hit);
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
@@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private double computeStrainValue()
{
double strainValue = Math.Pow(5.0 * Math.Max(1.0, Attributes["Strain"] / 0.0075) - 4.0, 2.0) / 100000.0;
double strainValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.0;
// Longer maps are worth more
double lengthBonus = 1 + 0.1f * Math.Min(1.0, totalHits / 1500.0);
@@ -86,6 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (State.Value)
{
case ArmedState.Idle:
UnproxyContent();
this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire();
break;
case ArmedState.Miss:
@@ -93,6 +94,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
.Expire();
break;
case ArmedState.Hit:
// If we're far enough away from the left stage, we should bring outselves in front of it
if (X >= -0.05f)
ProxyContent();
var flash = circlePiece?.FlashBox;
if (flash != null)
{
@@ -20,12 +20,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableSwell : DrawableTaikoHitObject<Swell>
{
/// <summary>
/// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime.
/// This is only ever invoked once.
/// </summary>
public event Action OnStart;
private const float target_ring_thick_border = 1.4f;
private const float target_ring_thin_border = 1f;
private const float target_ring_scale = 5f;
@@ -40,7 +34,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// </summary>
private int userHits;
private bool hasStarted;
private readonly SwellSymbolPiece symbol;
public DrawableSwell(Swell swell)
@@ -48,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
FillMode = FillMode.Fit;
AddInternal(bodyContainer = new Container
Content.Add(bodyContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Depth = 1,
@@ -177,6 +170,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (state)
{
case ArmedState.Idle:
UnproxyContent();
break;
case ArmedState.Hit:
bodyContainer.Delay(untilJudgement).ScaleTo(1.4f, out_transition_time);
break;
@@ -195,11 +191,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
X = Math.Max(0, X);
double t = Math.Min(HitObject.StartTime, Time.Current);
if (t == HitObject.StartTime && !hasStarted)
{
OnStart?.Invoke();
hasStarted = true;
}
if (t == HitObject.StartTime)
ProxyContent();
}
private bool? lastWasCentre;
@@ -9,10 +9,75 @@ using OpenTK;
using System.Linq;
using osu.Game.Audio;
using System.Collections.Generic;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableHitObject<TaikoHitObject>, IKeyBindingHandler<TaikoAction>
public abstract class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject>, IKeyBindingHandler<TaikoAction>
{
protected readonly Container Content;
private readonly Container proxiedContent;
private readonly Container nonProxiedContent;
protected DrawableTaikoHitObject(TaikoHitObject hitObject)
: base(hitObject)
{
InternalChildren = new[]
{
nonProxiedContent = new Container
{
RelativeSizeAxes = Axes.Both,
Child = Content = new Container { RelativeSizeAxes = Axes.Both }
},
proxiedContent = new Container { RelativeSizeAxes = Axes.Both }
};
}
/// <summary>
/// <see cref="proxiedContent"/> is proxied into an upper layer. We don't want to get masked away otherwise <see cref="proxiedContent"/> would too.
/// </summary>
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
private bool isProxied;
/// <summary>
/// Moves <see cref="Content"/> to a layer proxied above the playfield.
/// Does nothing is content is already proxied.
/// </summary>
protected void ProxyContent()
{
if (isProxied) return;
isProxied = true;
nonProxiedContent.Remove(Content);
proxiedContent.Add(Content);
}
/// <summary>
/// Moves <see cref="Content"/> to the normal hitobject layer.
/// Does nothing is content is not currently proxied.
/// </summary>
protected void UnproxyContent()
{
if (!isProxied) return;
isProxied = false;
proxiedContent.Remove(Content);
nonProxiedContent.Add(Content);
}
/// <summary>
/// Creates a proxy for the content of this <see cref="DrawableTaikoHitObject"/>.
/// </summary>
public Drawable CreateProxiedContent() => proxiedContent.CreateProxy();
public abstract bool OnPressed(TaikoAction action);
public virtual bool OnReleased(TaikoAction action) => false;
}
public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableTaikoHitObject
where TaikoHitType : TaikoHitObject
{
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
@@ -34,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
RelativeSizeAxes = Axes.Both;
Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE);
InternalChild = MainPiece = CreateMainPiece();
Content.Add(MainPiece = CreateMainPiece());
MainPiece.KiaiMode = HitObject.Kiai;
}
@@ -44,9 +109,5 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override string SampleNamespace => "Taiko";
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece();
public abstract bool OnPressed(TaikoAction action);
public virtual bool OnReleased(TaikoAction action) => false;
}
}
+6 -37
View File
@@ -84,56 +84,25 @@ namespace osu.Game.Rulesets.Taiko
{
new TaikoModEasy(),
new TaikoModNoFail(),
new MultiMod
{
Mods = new Mod[]
{
new TaikoModHalfTime(),
new TaikoModDaycore(),
},
},
new MultiMod(new TaikoModHalfTime(), new TaikoModDaycore()),
};
case ModType.DifficultyIncrease:
return new Mod[]
{
new TaikoModHardRock(),
new MultiMod
{
Mods = new Mod[]
{
new TaikoModSuddenDeath(),
new TaikoModPerfect(),
},
},
new MultiMod
{
Mods = new Mod[]
{
new TaikoModDoubleTime(),
new TaikoModNightcore(),
},
},
new MultiMod(new TaikoModSuddenDeath(), new TaikoModPerfect()),
new MultiMod(new TaikoModDoubleTime(), new TaikoModNightcore()),
new TaikoModHidden(),
new TaikoModFlashlight(),
};
case ModType.Special:
return new Mod[]
{
new TaikoModRelax(),
null,
null,
new MultiMod
{
Mods = new Mod[]
{
new TaikoModAutoplay(),
new ModCinema(),
},
},
new MultiMod(new TaikoModAutoplay(), new ModCinema()),
};
default:
return new Mod[] { };
}
@@ -145,9 +114,9 @@ namespace osu.Game.Rulesets.Taiko
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap, mods);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => new TaikoPerformanceCalculator(this, beatmap, score);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new TaikoPerformanceCalculator(this, beatmap, score);
public override int? LegacyID => 1;
+3 -1
View File
@@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </summary>
internal class HitExplosion : CircularContainer
{
public override bool RemoveWhenNotAlive => true;
public readonly DrawableHitObject JudgedObject;
private readonly Box innerFill;
@@ -66,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.UI
this.ScaleTo(3f, 1000, Easing.OutQuint);
this.FadeOut(500);
Expire();
Expire(true);
}
/// <summary>
@@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{
public class KiaiHitExplosion : CircularContainer
{
public override bool RemoveWhenNotAlive => true;
public readonly DrawableHitObject JudgedObject;
private readonly bool isRim;
@@ -62,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.UI
this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint);
this.FadeOut(250);
Expire();
Expire(true);
}
}
}
+3 -17
View File
@@ -216,10 +216,9 @@ namespace osu.Game.Rulesets.Taiko.UI
if (barline != null)
barlineContainer.Add(barline.CreateProxy());
// Swells should be moved at the very top of the playfield when they reach the hit target
var swell = h as DrawableSwell;
if (swell != null)
swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy());
var taikoObject = h as DrawableTaikoHitObject;
if (taikoObject != null)
topLevelHitContainer.Add(taikoObject.CreateProxiedContent());
}
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
@@ -244,19 +243,6 @@ namespace osu.Game.Rulesets.Taiko.UI
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == judgedObject)?.VisualiseSecondHit();
else
{
if (judgedObject.X >= -0.05f && judgedObject is DrawableHit)
{
// If we're far enough away from the left stage, we should bring outselves in front of it
// Todo: The following try-catch is temporary for replay rewinding support
try
{
topLevelHitContainer.Add(judgedObject.CreateProxy());
}
catch
{
}
}
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
if (judgedObject.HitObject.Kiai)
@@ -86,7 +86,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(string.Empty, metadata.Source);
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", metadata.Tags);
Assert.AreEqual(557821, beatmapInfo.OnlineBeatmapID);
Assert.AreEqual(241526, metadata.OnlineBeatmapSetID);
Assert.AreEqual(241526, beatmapInfo.BeatmapSet.OnlineBeatmapSetID);
}
}
@@ -28,7 +28,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
var beatmap = decodeAsJson(normal);
var meta = beatmap.BeatmapInfo.Metadata;
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID);
Assert.AreEqual("Soleily", meta.Artist);
Assert.AreEqual("Soleily", meta.ArtistUnicode);
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[TestFixture]
public class ImportBeatmapTest
{
private const string osz_path = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
public const string TEST_OSZ_PATH = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
[Test]
public void TestImportWhenClosed()
@@ -264,7 +264,8 @@ namespace osu.Game.Tests.Beatmaps.IO
private string createTemporaryBeatmap()
{
var temp = new FileInfo(osz_path).CopyTo(Path.GetTempFileName(), true).FullName;
var temp = Path.GetTempFileName() + ".osz";
File.Copy(TEST_OSZ_PATH, temp, true);
Assert.IsTrue(File.Exists(temp));
return temp;
}
@@ -344,12 +345,12 @@ namespace osu.Game.Tests.Beatmaps.IO
private void waitForOrAssert(Func<bool> result, string failureMessage, int timeout = 60000)
{
Action waitAction = () =>
Task task = Task.Run(() =>
{
while (!result()) Thread.Sleep(200);
};
});
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), failureMessage);
Assert.IsTrue(task.Wait(timeout), failureMessage);
}
}
}
@@ -48,17 +48,20 @@ namespace osu.Game.Tests.Beatmaps.IO
{
var reader = new ZipArchiveReader(osz);
BeatmapMetadata meta;
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
meta = Decoder.GetDecoder<Beatmap>(stream).Decode(stream).Metadata;
Beatmap beatmap;
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
beatmap = Decoder.GetDecoder<Beatmap>(stream).Decode(stream);
var meta = beatmap.Metadata;
Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID);
Assert.AreEqual("Soleily", meta.Artist);
Assert.AreEqual("Soleily", meta.ArtistUnicode);
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
Assert.AreEqual("Deif", meta.AuthorString);
Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile);
Assert.AreEqual(164471 + LegacyBeatmapDecoder.UniversalOffset, meta.PreviewTime);
Assert.AreEqual(164471, meta.PreviewTime);
Assert.AreEqual(string.Empty, meta.Source);
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags);
Assert.AreEqual("Renatus", meta.Title);
@@ -0,0 +1,152 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Tests.NonVisual
{
[TestFixture]
public class DifficultyAdjustmentModCombinationsTest
{
[Test]
public void TestNoMods()
{
var combinations = new TestDifficultyCalculator().CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(1, combinations.Length);
Assert.IsTrue(combinations[0] is NoModMod);
}
[Test]
public void TestSingleMod()
{
var combinations = new TestDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(2, combinations.Length);
Assert.IsTrue(combinations[0] is NoModMod);
Assert.IsTrue(combinations[1] is ModA);
}
[Test]
public void TestDoubleMod()
{
var combinations = new TestDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(4, combinations.Length);
Assert.IsTrue(combinations[0] is NoModMod);
Assert.IsTrue(combinations[1] is ModA);
Assert.IsTrue(combinations[2] is MultiMod);
Assert.IsTrue(combinations[3] is ModB);
Assert.IsTrue(((MultiMod)combinations[2]).Mods[0] is ModA);
Assert.IsTrue(((MultiMod)combinations[2]).Mods[1] is ModB);
}
[Test]
public void TestIncompatibleMods()
{
var combinations = new TestDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(3, combinations.Length);
Assert.IsTrue(combinations[0] is NoModMod);
Assert.IsTrue(combinations[1] is ModA);
Assert.IsTrue(combinations[2] is ModIncompatibleWithA);
}
[Test]
public void TestDoubleIncompatibleMods()
{
var combinations = new TestDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(8, combinations.Length);
Assert.IsTrue(combinations[0] is NoModMod);
Assert.IsTrue(combinations[1] is ModA);
Assert.IsTrue(combinations[2] is MultiMod);
Assert.IsTrue(combinations[3] is ModB);
Assert.IsTrue(combinations[4] is MultiMod);
Assert.IsTrue(combinations[5] is ModIncompatibleWithA);
Assert.IsTrue(combinations[6] is MultiMod);
Assert.IsTrue(combinations[7] is ModIncompatibleWithAAndB);
Assert.IsTrue(((MultiMod)combinations[2]).Mods[0] is ModA);
Assert.IsTrue(((MultiMod)combinations[2]).Mods[1] is ModB);
Assert.IsTrue(((MultiMod)combinations[4]).Mods[0] is ModB);
Assert.IsTrue(((MultiMod)combinations[4]).Mods[1] is ModIncompatibleWithA);
Assert.IsTrue(((MultiMod)combinations[6]).Mods[0] is ModIncompatibleWithA);
Assert.IsTrue(((MultiMod)combinations[6]).Mods[1] is ModIncompatibleWithAAndB);
}
[Test]
public void TestIncompatibleThroughBaseType()
{
var combinations = new TestDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(3, combinations.Length);
Assert.IsTrue(combinations[0] is NoModMod);
Assert.IsTrue(combinations[1] is ModAofA);
Assert.IsTrue(combinations[2] is ModIncompatibleWithAofA);
}
private class ModA : Mod
{
public override string Name => nameof(ModA);
public override string ShortenedName => nameof(ModA);
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) };
}
private class ModB : Mod
{
public override string Name => nameof(ModB);
public override string ShortenedName => nameof(ModB);
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithAAndB) };
}
private class ModIncompatibleWithA : Mod
{
public override string Name => $"Incompatible With {nameof(ModA)}";
public override string ShortenedName => $"Incompatible With {nameof(ModA)}";
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModA) };
}
private class ModAofA : ModA
{
}
private class ModIncompatibleWithAofA : ModIncompatibleWithA
{
// Incompatible through base type
}
private class ModIncompatibleWithAAndB : Mod
{
public override string Name => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
public override string ShortenedName => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) };
}
private class TestDifficultyCalculator : DifficultyCalculator
{
public TestDifficultyCalculator(params Mod[] mods)
: base(null, null)
{
DifficultyAdjustmentMods = mods;
}
protected override Mod[] DifficultyAdjustmentMods { get; }
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) => throw new NotImplementedException();
}
}
}
+2 -4
View File
@@ -3,7 +3,6 @@
using System.ComponentModel;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
@@ -13,12 +12,11 @@ namespace osu.Game.Tests.Visual
[Description("Player instantiated with an autoplay mod.")]
public class TestCaseAutoplay : TestCasePlayer
{
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
protected override Player CreatePlayer(Ruleset ruleset)
{
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
return new ScoreAccessiblePlayer
{
InitialBeatmap = beatmap,
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
@@ -449,7 +449,6 @@ namespace osu.Game.Tests.Visual
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
OnlineBeatmapSetID = id,
// Create random metadata, then we can check if sorting works based on these
Artist = $"peppy{id.ToString().PadLeft(6, '0')}",
Title = $"test set #{id}!",
@@ -503,7 +502,6 @@ namespace osu.Game.Tests.Visual
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
OnlineBeatmapSetID = id,
// Create random metadata, then we can check if sorting works based on these
Artist = $"peppy{id.ToString().PadLeft(6, '0')}",
Title = $"test set #{id}!",
@@ -6,7 +6,6 @@ using System.Linq;
using NUnit.Framework;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
@@ -29,14 +28,11 @@ namespace osu.Game.Tests.Visual
private RulesetStore rulesets;
private TestBeatmapInfoWedge infoWedge;
private readonly List<IBeatmap> beatmaps = new List<IBeatmap>();
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
[BackgroundDependencyLoader]
private void load(OsuGameBase game, RulesetStore rulesets)
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
beatmap.BindTo(game.Beatmap);
}
protected override void LoadComplete()
@@ -53,11 +49,11 @@ namespace osu.Game.Tests.Visual
AddStep("show", () =>
{
infoWedge.State = Visibility.Visible;
infoWedge.Beatmap = beatmap;
infoWedge.Beatmap = Beatmap;
});
// select part is redundant, but wait for load isn't
selectBeatmap(beatmap.Value.Beatmap);
selectBeatmap(Beatmap.Value.Beatmap);
AddWaitStep(3);
@@ -120,8 +116,8 @@ namespace osu.Game.Tests.Visual
{
selectNullBeatmap();
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 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());
}
@@ -133,7 +129,7 @@ namespace osu.Game.Tests.Visual
AddStep($"select {b.Metadata.Title} beatmap", () =>
{
infoBefore = infoWedge.Info;
infoWedge.Beatmap = beatmap.Value = new TestWorkingBeatmap(b);
infoWedge.Beatmap = Beatmap.Value = new TestWorkingBeatmap(b);
});
AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load");
@@ -143,8 +139,8 @@ namespace osu.Game.Tests.Visual
{
AddStep("select null beatmap", () =>
{
beatmap.Value = beatmap.Default;
infoWedge.Beatmap = beatmap;
Beatmap.Value = Beatmap.Default;
infoWedge.Beatmap = Beatmap;
});
}
@@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.BeatmapSet.Scores;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
@@ -15,6 +14,7 @@ using osu.Game.Users;
using System.Collections.Generic;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Osu;
namespace osu.Game.Tests.Visual
@@ -22,9 +22,9 @@ namespace osu.Game.Tests.Visual
[System.ComponentModel.Description("in BeatmapOverlay")]
public class TestCaseBeatmapScoresContainer : OsuTestCase
{
private readonly IEnumerable<OnlineScore> scores;
private readonly IEnumerable<OnlineScore> anotherScores;
private readonly OnlineScore topScore;
private readonly IEnumerable<APIScore> scores;
private readonly IEnumerable<APIScore> anotherScores;
private readonly APIScore topScore;
private readonly Box background;
public TestCaseBeatmapScoresContainer()
@@ -52,12 +52,12 @@ namespace osu.Game.Tests.Visual
AddStep("remove scores", () => scoresContainer.Scores = null);
AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapSetID = 1, OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
scores = new[]
{
new OnlineScore
new APIScore
{
User = new User
{
@@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual
TotalScore = 1234567890,
Accuracy = 1,
},
new OnlineScore
new APIScore
{
User = new User
{
@@ -102,7 +102,7 @@ namespace osu.Game.Tests.Visual
TotalScore = 1234789,
Accuracy = 0.9997,
},
new OnlineScore
new APIScore
{
User = new User
{
@@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual
TotalScore = 12345678,
Accuracy = 0.9854,
},
new OnlineScore
new APIScore
{
User = new User
{
@@ -143,7 +143,7 @@ namespace osu.Game.Tests.Visual
TotalScore = 1234567,
Accuracy = 0.8765,
},
new OnlineScore
new APIScore
{
User = new User
{
@@ -169,7 +169,7 @@ namespace osu.Game.Tests.Visual
anotherScores = new[]
{
new OnlineScore
new APIScore
{
User = new User
{
@@ -191,7 +191,7 @@ namespace osu.Game.Tests.Visual
TotalScore = 1234789,
Accuracy = 0.9997,
},
new OnlineScore
new APIScore
{
User = new User
{
@@ -214,7 +214,7 @@ namespace osu.Game.Tests.Visual
TotalScore = 1234567890,
Accuracy = 1,
},
new OnlineScore
new APIScore
{
User = new User
{
@@ -230,7 +230,7 @@ namespace osu.Game.Tests.Visual
TotalScore = 123456,
Accuracy = 0.6543,
},
new OnlineScore
new APIScore
{
User = new User
{
@@ -251,7 +251,7 @@ namespace osu.Game.Tests.Visual
TotalScore = 12345678,
Accuracy = 0.9854,
},
new OnlineScore
new APIScore
{
User = new User
{
@@ -279,7 +279,7 @@ namespace osu.Game.Tests.Visual
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
}
topScore = new OnlineScore
topScore = new APIScore
{
User = new User
{
+1 -4
View File
@@ -35,9 +35,6 @@ namespace osu.Game.Tests.Visual
typeof(MessageFormatter)
};
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
public TestCaseChatLink()
{
Add(textContainer = new TestChatLineContainer
@@ -53,7 +50,7 @@ namespace osu.Game.Tests.Visual
private void load(OsuColour colours)
{
linkColour = colours.Blue;
dependencies.Cache(new ChatOverlay
Dependencies.Cache(new ChatOverlay
{
AvailableChannels =
{
@@ -17,14 +17,10 @@ namespace osu.Game.Tests.Visual
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Compose) };
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame)
private void load()
{
osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
var compose = new Compose();
compose.Beatmap.BindTo(osuGame.Beatmap);
Child = compose;
Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
Child = new Compose();
}
}
}
@@ -4,45 +4,137 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Screens.Edit.Screens.Compose.Timeline;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseEditorComposeTimeline : OsuTestCase
public class TestCaseEditorComposeTimeline : EditorClockTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(ScrollableTimeline), typeof(ScrollingTimelineContainer), typeof(BeatmapWaveformGraph), typeof(TimelineButton) };
private readonly ScrollableTimeline timeline;
public TestCaseEditorComposeTimeline()
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(TimelineArea),
typeof(Timeline),
typeof(TimelineButton),
typeof(CentreMarker)
};
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = new WaveformTestBeatmap();
Children = new Drawable[]
{
new MusicController
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
State = Visibility.Visible
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
new StartStopButton(),
new AudioVisualiser(),
}
},
timeline = new ScrollableTimeline
new TimelineArea
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(1000, 100)
RelativeSizeAxes = Axes.X,
Size = new Vector2(0.8f, 100)
}
};
}
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame)
private class AudioVisualiser : CompositeDrawable
{
timeline.Beatmap.BindTo(osuGame.Beatmap);
private readonly Drawable marker;
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private IAdjustableClock adjustableClock;
public AudioVisualiser()
{
Size = new Vector2(250, 25);
InternalChildren = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.25f,
},
marker = new Box
{
RelativePositionAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Width = 2,
}
};
}
[BackgroundDependencyLoader]
private void load(IAdjustableClock adjustableClock, IBindableBeatmap beatmap)
{
this.adjustableClock = adjustableClock;
this.beatmap.BindTo(beatmap);
}
protected override void Update()
{
base.Update();
if (beatmap.Value.Track.IsLoaded)
marker.X = (float)(adjustableClock.CurrentTime / beatmap.Value.Track.Length);
}
}
private class StartStopButton : Button
{
private IAdjustableClock adjustableClock;
private bool started;
public StartStopButton()
{
BackgroundColour = Color4.SlateGray;
Size = new Vector2(100, 50);
Text = "Start";
Action = onClick;
}
[BackgroundDependencyLoader]
private void load(IAdjustableClock adjustableClock)
{
this.adjustableClock = adjustableClock;
}
private void onClick()
{
if (started)
{
adjustableClock.Stop();
Text = "Start";
}
else
{
adjustableClock.Start();
Text = "Stop";
}
started = !started;
}
}
}
}
@@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual
}
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame)
private void load()
{
var testBeatmap = new Beatmap
{
@@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual
}
};
osuGame.Beatmap.Value = new TestWorkingBeatmap(testBeatmap);
Beatmap.Value = new TestWorkingBeatmap(testBeatmap);
Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock };
@@ -19,19 +19,16 @@ namespace osu.Game.Tests.Visual
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(SummaryTimeline) };
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame)
private void load()
{
osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
SummaryTimeline summaryTimeline;
Add(summaryTimeline = new SummaryTimeline
Add(new SummaryTimeline
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 50)
});
summaryTimeline.Beatmap.BindTo(osuGame.Beatmap);
}
}
}
@@ -32,15 +32,10 @@ namespace osu.Game.Tests.Visual
typeof(NotNullAttribute)
};
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(parent);
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame)
private void load()
{
osuGame.Beatmap.Value = new TestWorkingBeatmap(new Beatmap
Beatmap.Value = new TestWorkingBeatmap(new Beatmap
{
HitObjects = new List<HitObject>
{
@@ -63,8 +58,8 @@ namespace osu.Game.Tests.Visual
});
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
dependencies.CacheAs<IAdjustableClock>(clock);
dependencies.CacheAs<IFrameBasedClock>(clock);
Dependencies.CacheAs<IAdjustableClock>(clock);
Dependencies.CacheAs<IFrameBasedClock>(clock);
Child = new OsuHitObjectComposer(new OsuRuleset());
}
@@ -2,12 +2,9 @@
// 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.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual
@@ -15,8 +12,6 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCaseMusicController : OsuTestCase
{
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
public TestCaseMusicController()
{
Clock = new FramedClock();
@@ -30,13 +25,7 @@ namespace osu.Game.Tests.Visual
AddToggleStep(@"toggle visibility", state => mc.State = state ? Visibility.Visible : Visibility.Hidden);
AddStep(@"show", () => mc.State = Visibility.Visible);
AddToggleStep(@"toggle beatmap lock", state => beatmapBacking.Disabled = state);
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
beatmapBacking.BindTo(game.Beatmap);
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
}
}
}
@@ -27,7 +27,6 @@ namespace osu.Game.Tests.Visual
private RulesetStore rulesets;
private DependencyContainer dependencies;
private WorkingBeatmap defaultBeatmap;
public override IReadOnlyList<Type> RequiredTypes => new[]
@@ -48,8 +47,6 @@ namespace osu.Game.Tests.Visual
typeof(DrawableCarouselBeatmapSet),
};
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
private class TestSongSelect : PlaySongSelect
{
public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
@@ -58,7 +55,7 @@ namespace osu.Game.Tests.Visual
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
private void load()
{
TestSongSelect songSelect = null;
@@ -67,10 +64,10 @@ namespace osu.Game.Tests.Visual
// this is by no means clean. should be replacing inside of OsuGameBase somehow.
IDatabaseContextFactory factory = new SingletonContextFactory(new OsuDbContext());
dependencies.Cache(rulesets = new RulesetStore(factory));
dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null, null)
Dependencies.Cache(rulesets = new RulesetStore(factory));
Dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null, null)
{
DefaultBeatmap = defaultBeatmap = game.Beatmap.Default
DefaultBeatmap = defaultBeatmap = Beatmap.Default
});
void loadNewSongSelect(bool deleteMaps = false) => AddStep("reload song select", () =>
@@ -78,7 +75,7 @@ namespace osu.Game.Tests.Visual
if (deleteMaps)
{
manager.Delete(manager.GetAllUsableBeatmapSets());
game.Beatmap.SetDefault();
Beatmap.SetDefault();
}
if (songSelect != null)
@@ -125,7 +122,6 @@ namespace osu.Game.Tests.Visual
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
OnlineBeatmapSetID = 1234 + i,
// 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),
@@ -15,17 +15,12 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCasePlaybackControl : OsuTestCase
{
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(parent);
[BackgroundDependencyLoader]
private void load()
{
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
dependencies.CacheAs<IAdjustableClock>(clock);
dependencies.CacheAs<IFrameBasedClock>(clock);
Dependencies.CacheAs<IAdjustableClock>(clock);
Dependencies.CacheAs<IFrameBasedClock>(clock);
var playback = new PlaybackControl
{
@@ -34,7 +29,7 @@ namespace osu.Game.Tests.Visual
Size = new Vector2(200,100)
};
playback.Beatmap.Value = new TestWorkingBeatmap(new Beatmap());
Beatmap.Value = new TestWorkingBeatmap(new Beatmap());
Child = playback;
}
@@ -12,9 +12,10 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
Beatmap.Value = new DummyWorkingBeatmap(game);
AddStep("load dummy beatmap", () => Add(new PlayerLoader(new Player
{
InitialBeatmap = new DummyWorkingBeatmap(game),
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
+5 -9
View File
@@ -3,7 +3,6 @@
using System.ComponentModel;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
@@ -13,23 +12,20 @@ namespace osu.Game.Tests.Visual
[Description("Player instantiated with a replay.")]
public class TestCaseReplay : TestCasePlayer
{
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
protected override Player CreatePlayer(Ruleset ruleset)
{
// We create a dummy RulesetContainer just to get the replay - we don't want to use mods here
// to simulate setting a replay rather than having the replay already set for us
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(beatmap);
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(Beatmap.Value);
// We have the replay
var replay = dummyRulesetContainer.Replay;
// Reset the mods
beatmap.Mods.Value = beatmap.Mods.Value.Where(m => !(m is ModAutoplay));
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Where(m => !(m is ModAutoplay));
return new ReplayPlayer(replay)
{
InitialBeatmap = beatmap
};
return new ReplayPlayer(replay);
}
}
}
+4 -12
View File
@@ -32,18 +32,13 @@ namespace osu.Game.Tests.Visual
this.beatmaps = beatmaps;
}
private WorkingBeatmap beatmap;
protected override void LoadComplete()
{
base.LoadComplete();
if (beatmap == null)
{
var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
if (beatmapInfo != null)
beatmap = beatmaps.GetWorkingBeatmap(beatmapInfo);
}
var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
Add(new Results(new Score
{
@@ -63,10 +58,7 @@ namespace osu.Game.Tests.Visual
{
Username = "peppy",
}
})
{
InitialBeatmap = beatmap
});
}));
}
}
}
+1 -5
View File
@@ -14,10 +14,6 @@ namespace osu.Game.Tests.Visual
private readonly SettingsOverlay settings;
private readonly DialogOverlay dialogOverlay;
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
public TestCaseSettings()
{
settings = new MainSettings
@@ -33,7 +29,7 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load()
{
dependencies.Cache(dialogOverlay);
Dependencies.Cache(dialogOverlay);
Add(settings);
}
+6 -9
View File
@@ -3,7 +3,6 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -18,8 +17,6 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCaseStoryboard : OsuTestCase
{
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
private readonly Container<DrawableStoryboard> storyboardContainer;
private DrawableStoryboard storyboard;
@@ -43,6 +40,7 @@ namespace osu.Game.Tests.Visual
},
},
});
Add(new MusicController
{
Origin = Anchor.TopRight,
@@ -55,10 +53,9 @@ namespace osu.Game.Tests.Visual
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
private void load()
{
beatmapBacking.BindTo(game.Beatmap);
beatmapBacking.ValueChanged += beatmapChanged;
Beatmap.ValueChanged += beatmapChanged;
}
private void beatmapChanged(WorkingBeatmap working)
@@ -66,10 +63,10 @@ namespace osu.Game.Tests.Visual
private void restart()
{
var track = beatmapBacking.Value.Track;
var track = Beatmap.Value.Track;
track.Reset();
loadStoryboard(beatmapBacking.Value);
loadStoryboard(Beatmap);
track.Start();
}
@@ -81,7 +78,7 @@ namespace osu.Game.Tests.Visual
var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
storyboardContainer.Clock = decoupledClock;
storyboard = working.Storyboard.CreateDrawable(beatmapBacking);
storyboard = working.Storyboard.CreateDrawable(Beatmap);
storyboard.Passing = false;
storyboardContainer.Add(storyboard);
@@ -12,6 +12,7 @@ using osu.Game.Overlays.Profile.Sections.Recent;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Tests.Visual
{
@@ -49,15 +50,15 @@ namespace osu.Game.Tests.Visual
};
}
private IEnumerable<RecentActivity> createDummyActivities()
private IEnumerable<APIRecentActivity> createDummyActivities()
{
var dummyBeatmap = new RecentActivity.RecentActivityBeatmap
var dummyBeatmap = new APIRecentActivity.RecentActivityBeatmap
{
Title = @"Dummy beatmap",
Url = "/b/1337",
};
var dummyUser = new RecentActivity.RecentActivityUser
var dummyUser = new APIRecentActivity.RecentActivityUser
{
Username = "DummyReborn",
Url = "/u/666",
@@ -66,61 +67,61 @@ namespace osu.Game.Tests.Visual
return new[]
{
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.Achievement,
Achievement = new RecentActivity.RecentActivityAchievement
Achievement = new APIRecentActivity.RecentActivityAchievement
{
Name = @"Feelin' It",
Slug = @"all-secret-feelinit",
},
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapPlaycount,
Count = 1337,
Beatmap = dummyBeatmap,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetApprove,
Approval = BeatmapApproval.Qualified,
Beatmapset = dummyBeatmap,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetDelete,
Beatmapset = dummyBeatmap,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetRevive,
Beatmapset = dummyBeatmap,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetRevive,
Beatmapset = dummyBeatmap,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetUpdate,
Beatmapset = dummyBeatmap,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetUpload,
Beatmapset = dummyBeatmap,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.Rank,
@@ -128,29 +129,29 @@ namespace osu.Game.Tests.Visual
Mode = "osu!",
Beatmap = dummyBeatmap,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.RankLost,
Mode = "osu!",
Beatmap = dummyBeatmap,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.UsernameChange,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.UserSupportAgain,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.UserSupportFirst,
},
new RecentActivity
new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.UserSupportGift,
+11 -2
View File
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Overlays.Volume;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
@@ -17,13 +18,21 @@ namespace osu.Game.Tests.Visual
{
VolumeMeter meter;
MuteButton mute;
Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue));
Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue) { Position = new Vector2(10) });
AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1);
Add(new VolumeMeter("BIG", 250, Color4.Red)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Position = new Vector2(10),
});
Add(mute = new MuteButton
{
Margin = new MarginPadding { Top = 200 }
});
AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1);
AddToggleStep("mute", b => mute.Current.Value = b);
}
}
+8 -24
View File
@@ -5,52 +5,39 @@ using NUnit.Framework;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Screens.Edit.Screens.Compose.Timeline;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseWaveform : OsuTestCase
{
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
public TestCaseWaveform()
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = new WaveformTestBeatmap();
FillFlowContainer flow;
Child = flow = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
new MusicController
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Y = 100,
State = Visibility.Visible
},
}
};
for (int i = 1; i <= 16; i *= 2)
{
var newDisplay = new BeatmapWaveformGraph
var newDisplay = new WaveformGraph
{
RelativeSizeAxes = Axes.Both,
Resolution = 1f / i
Resolution = 1f / i,
Waveform = Beatmap.Value.Waveform,
};
newDisplay.Beatmap.BindTo(beatmapBacking);
flow.Add(new Container
{
RelativeSizeAxes = Axes.X,
@@ -83,8 +70,5 @@ namespace osu.Game.Tests.Visual
});
}
}
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame) => beatmapBacking.BindTo(osuGame.Beatmap);
}
}
@@ -0,0 +1,129 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Screens.Edit.Screens.Compose.Timeline;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCaseZoomableScrollContainer : ManualInputManagerTestCase
{
private readonly ZoomableScrollContainer scrollContainer;
private readonly Drawable innerBox;
public TestCaseZoomableScrollContainer()
{
Children = new Drawable[]
{
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = 250,
Width = 0.75f,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(30)
},
scrollContainer = new ZoomableScrollContainer { RelativeSizeAxes = Axes.Both }
}
},
new MenuCursor()
};
scrollContainer.Add(innerBox = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(new Color4(0.8f, 0.6f, 0.4f, 1f), new Color4(0.4f, 0.6f, 0.8f, 1f))
});
}
[Test]
public void TestZoom0()
{
reset();
AddAssert("Box at 0", () => Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
AddAssert("Box width = 1x", () => Precision.AlmostEquals(boxQuad.Size, scrollQuad.Size));
}
[Test]
public void TestZoom10()
{
reset();
AddStep("Set zoom = 10", () => scrollContainer.Zoom = 10);
AddAssert("Box at 1/2", () => Precision.AlmostEquals(boxQuad.Centre, scrollQuad.Centre, 1));
AddAssert("Box width = 10x", () => Precision.AlmostEquals(boxQuad.Size.X, 10 * scrollQuad.Size.X));
}
[Test]
public void TestMouseZoomInOnceOutOnce()
{
reset();
// Scroll in at 0.25
AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
AddStep("Scroll by 3", () => InputManager.ScrollBy(new Vector2(0, 3)));
AddAssert("Box not at 0", () => !Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
AddAssert("Box 1/4 at 1/4", () => Precision.AlmostEquals(boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X));
// Scroll out at 0.25
AddStep("Scroll by -3", () => InputManager.ScrollBy(new Vector2(0, -3)));
AddAssert("Box at 0", () => Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
AddAssert("Box 1/4 at 1/4", () => Precision.AlmostEquals(boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X));
}
[Test]
public void TestMouseZoomInTwiceOutTwice()
{
reset();
// Scroll in at 0.25
AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
AddStep("Scroll by 1", () => InputManager.ScrollBy(new Vector2(0, 1)));
// Scroll in at 0.6
AddStep("Move mouse to 0.75x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.75f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
AddStep("Scroll by 1", () => InputManager.ScrollBy(new Vector2(0, 1)));
AddAssert("Box not at 0", () => !Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
// Very hard to determine actual position, so approximate
AddAssert("Box at correct position (1)", () => Precision.DefinitelyBigger(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X));
AddAssert("Box at correct position (2)", () => Precision.DefinitelyBigger(scrollQuad.TopLeft.X + 0.6f * scrollQuad.Size.X, boxQuad.TopLeft.X + 0.3f * boxQuad.Size.X));
AddAssert("Box at correct position (3)", () => Precision.DefinitelyBigger(boxQuad.TopLeft.X + 0.6f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.6f * scrollQuad.Size.X));
// Scroll out at 0.6
AddStep("Scroll by -1", () => InputManager.ScrollBy(new Vector2(0, -1)));
// Scroll out at 0.25
AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
AddStep("Scroll by -1", () => InputManager.ScrollBy(new Vector2(0, -1)));
AddAssert("Box at 0", () => Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
}
private void reset()
{
AddStep("Reset", () =>
{
scrollContainer.Zoom = 0;
scrollContainer.ScrollTo(0, false);
});
}
private Quad scrollQuad => scrollContainer.ScreenSpaceDrawQuad;
private Quad boxQuad => innerBox.ScreenSpaceDrawQuad;
}
}
+55
View File
@@ -0,0 +1,55 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.IO;
using System.Linq;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO.Archives;
using osu.Game.Tests.Beatmaps.IO;
namespace osu.Game.Tests
{
/// <summary>
/// A <see cref="WorkingBeatmap"/> that is used for testcases that include waveforms.
/// </summary>
public class WaveformTestBeatmap : WorkingBeatmap
{
private readonly ZipArchiveReader reader;
private readonly FileStream stream;
public WaveformTestBeatmap()
: base(new BeatmapInfo())
{
stream = File.OpenRead(ImportBeatmapTest.TEST_OSZ_PATH);
reader = new ZipArchiveReader(stream);
}
public override void Dispose()
{
base.Dispose();
stream?.Dispose();
reader?.Dispose();
}
protected override IBeatmap GetBeatmap() => createTestBeatmap();
protected override Texture GetBackground() => null;
protected override Waveform GetWaveform() => new Waveform(getAudioStream());
protected override Track GetTrack() => new TrackBass(getAudioStream());
private Stream getAudioStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".mp3")));
private Stream getBeatmapStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu")));
private Beatmap createTestBeatmap()
{
using (var beatmapStream = getBeatmapStream())
using (var beatmapReader = new StreamReader(beatmapStream))
return Decoder.GetDecoder<Beatmap>(beatmapReader).Decode(beatmapReader);
}
}
}
+2 -12
View File
@@ -23,7 +23,6 @@ namespace osu.Game.Beatmaps
public int BeatmapVersion;
private int? onlineBeatmapID;
private int? onlineBeatmapSetID;
[JsonProperty("id")]
public int? OnlineBeatmapID
@@ -32,19 +31,10 @@ namespace osu.Game.Beatmaps
set { onlineBeatmapID = value > 0 ? value : null; }
}
[JsonProperty("beatmapset_id")]
[NotMapped]
public int? OnlineBeatmapSetID
{
get { return onlineBeatmapSetID; }
set { onlineBeatmapSetID = value > 0 ? value : null; }
}
[JsonIgnore]
public int BeatmapSetInfoID { get; set; }
[Required]
[JsonIgnore]
public BeatmapSetInfo BeatmapSet { get; set; }
public BeatmapMetadata Metadata { get; set; }
@@ -141,8 +131,8 @@ namespace osu.Game.Beatmaps
(Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile;
public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
BeatmapSet.Hash == other.BeatmapSet.Hash &&
(Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile;
BeatmapSet.Hash == other.BeatmapSet.Hash &&
(Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile;
/// <summary>
/// Returns a shallow-clone of this <see cref="BeatmapInfo"/>.
+62 -26
View File
@@ -81,12 +81,31 @@ namespace osu.Game.Beatmaps
protected override void Populate(BeatmapSetInfo model, ArchiveReader archive)
{
model.Beatmaps = createBeatmapDifficulties(model, archive);
model.Beatmaps = createBeatmapDifficulties(archive);
// remove metadata from difficulties where it matches the set
foreach (BeatmapInfo b in model.Beatmaps)
{
// remove metadata from difficulties where it matches the set
if (model.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);
}
// check if a set already exists with the same online id, delete if it does.
if (model.OnlineBeatmapSetID != null)
{
var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == model.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);
}
}
}
protected override BeatmapSetInfo CheckForExisting(BeatmapSetInfo model)
@@ -99,18 +118,6 @@ namespace osu.Game.Beatmaps
return existingHashMatch;
}
// check if a set already exists with the same online id
if (model.OnlineBeatmapSetID != null)
{
var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == model.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);
}
}
return null;
}
@@ -306,29 +313,29 @@ namespace osu.Game.Beatmaps
return hashable.ComputeSHA2Hash();
}
protected override BeatmapSetInfo CreateModel(ArchiveReader reader)
protected override BeatmapSetInfo CreateModel(ArchiveReader reader)
{
// let's make sure there are actually .osu files to import.
string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));
if (string.IsNullOrEmpty(mapName)) throw new InvalidOperationException("No beatmap files found in this beatmap archive.");
BeatmapMetadata metadata;
Beatmap beatmap;
using (var stream = new StreamReader(reader.GetStream(mapName)))
metadata = Decoder.GetDecoder<Beatmap>(stream).Decode(stream).Metadata;
beatmap = Decoder.GetDecoder<Beatmap>(stream).Decode(stream);
return new BeatmapSetInfo
{
OnlineBeatmapSetID = metadata.OnlineBeatmapSetID,
OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID,
Beatmaps = new List<BeatmapInfo>(),
Hash = computeBeatmapSetHash(reader),
Metadata = metadata
Metadata = beatmap.Metadata
};
}
/// <summary>
/// Create all required <see cref="BeatmapInfo"/>s for the provided archive.
/// </summary>
private List<BeatmapInfo> createBeatmapDifficulties(BeatmapSetInfo model, ArchiveReader reader)
private List<BeatmapInfo> createBeatmapDifficulties(ArchiveReader reader)
{
var beatmapInfos = new List<BeatmapInfo>();
@@ -348,10 +355,6 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
// ensure we have the same online set ID as the set itself.
beatmap.BeatmapInfo.OnlineBeatmapSetID = model.OnlineBeatmapSetID;
beatmap.BeatmapInfo.Metadata.OnlineBeatmapSetID = model.OnlineBeatmapSetID;
// 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;
@@ -363,8 +366,7 @@ namespace osu.Game.Beatmaps
if (ruleset != null)
{
// TODO: this should be done in a better place once we actually need to dynamically update it.
var converted = new DummyConversionBeatmap(beatmap).GetPlayableBeatmap(ruleset);
beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(converted).Calculate();
beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating;
}
else
beatmap.BeatmapInfo.StarDifficulty = 0;
@@ -376,6 +378,40 @@ namespace osu.Game.Beatmaps
return beatmapInfos;
}
/// <summary>
/// Query the API to populate mising OnlineBeatmapID / OnlineBeatmapSetID properties.
/// </summary>
/// <param name="beatmap">The beatmap to populate.</param>
/// <param name="force">Whether to re-query if the provided beatmap already has populated values.</param>
/// <returns>True if population was successful.</returns>
private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, bool force = false)
{
if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null)
return true;
Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database);
try
{
var req = new GetBeatmapRequest(beatmap);
req.Perform(api);
var res = req.Result;
Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database);
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
return true;
}
catch (Exception e)
{
Logger.Log($"Failed ({e})", LoggingTarget.Database);
return false;
}
}
/// <summary>
/// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation.
/// </summary>
+1 -12
View File
@@ -17,16 +17,6 @@ namespace osu.Game.Beatmaps
[JsonIgnore]
public int ID { get; set; }
private int? onlineBeatmapSetID;
[NotMapped]
[JsonProperty(@"id")]
public int? OnlineBeatmapSetID
{
get { return onlineBeatmapSetID; }
set { onlineBeatmapSetID = value > 0 ? value : null; }
}
public string Title { get; set; }
public string TitleUnicode { get; set; }
public string Artist { get; set; }
@@ -82,8 +72,7 @@ namespace osu.Game.Beatmaps
if (other == null)
return false;
return onlineBeatmapSetID == other.onlineBeatmapSetID
&& Title == other.Title
return Title == other.Title
&& TitleUnicode == other.TitleUnicode
&& Artist == other.Artist
&& ArtistUnicode == other.ArtistUnicode
+3 -3
View File
@@ -22,18 +22,18 @@ namespace osu.Game.Beatmaps
[NotMapped]
public BeatmapSetOnlineInfo OnlineInfo { get; set; }
public double MaxStarDifficulty => Beatmaps.Max(b => b.StarDifficulty);
public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0;
[NotMapped]
public bool DeletePending { get; set; }
public string Hash { get; set; }
public string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename;
public string StoryboardFile => Files?.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename;
public List<BeatmapSetFileInfo> Files { get; set; }
public override string ToString() => Metadata.ToString();
public override string ToString() => Metadata?.ToString() ?? base.ToString();
public bool Protected { get; set; }
}
+75
View File
@@ -0,0 +1,75 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
namespace osu.Game.Beatmaps
{
/// <summary>
/// A <see cref="Bindable{WorkingBeatmap}"/> for the <see cref="OsuGame"/> beatmap.
/// This should be used sparingly in-favour of <see cref="IBindableBeatmap"/>.
/// </summary>
public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap>, IBindableBeatmap
{
private AudioManager audioManager;
private WorkingBeatmap lastBeatmap;
protected BindableBeatmap(WorkingBeatmap defaultValue)
: base(defaultValue)
{
}
/// <summary>
/// Registers an <see cref="AudioManager"/> for <see cref="Track"/>s to be added to.
/// </summary>
/// <param name="audioManager">The <see cref="AudioManager"/> to register.</param>
protected void RegisterAudioManager([NotNull] AudioManager audioManager)
{
if (this.audioManager != null) throw new InvalidOperationException($"Cannot register multiple {nameof(AudioManager)}s.");
this.audioManager = audioManager;
ValueChanged += registerAudioTrack;
// If the track has changed prior to this being called, let's register it
if (Value != Default)
registerAudioTrack(Value);
}
private void registerAudioTrack(WorkingBeatmap beatmap)
{
var trackLoaded = lastBeatmap?.TrackLoaded ?? false;
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
if (!trackLoaded || lastBeatmap?.Track != beatmap.Track)
{
if (trackLoaded)
{
Debug.Assert(lastBeatmap != null);
Debug.Assert(lastBeatmap.Track != null);
lastBeatmap.RecycleTrack();
}
audioManager.Track.AddItem(beatmap.Track);
}
lastBeatmap = beatmap;
}
[NotNull]
IBindableBeatmap IBindableBeatmap.GetBoundCopy() => GetBoundCopy();
/// <summary>
/// Retrieve a new <see cref="BindableBeatmap"/> instance weakly bound to this <see cref="BindableBeatmap"/>.
/// If you are further binding to events of the retrieved <see cref="BindableBeatmap"/>, ensure a local reference is held.
/// </summary>
[NotNull]
public abstract BindableBeatmap GetBoundCopy();
}
}
@@ -0,0 +1,84 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
/// <summary>
/// A component to allow downloading of a beatmap set. Automatically handles state syncing between other instances.
/// </summary>
public class BeatmapSetDownloader : Component
{
private readonly BeatmapSetInfo set;
private readonly bool noVideo;
private BeatmapManager beatmaps;
/// <summary>
/// Whether the associated beatmap set has been downloading (by this instance or any other instance).
/// </summary>
public readonly BindableBool Downloaded = new BindableBool();
public BeatmapSetDownloader(BeatmapSetInfo set, bool noVideo = false)
{
this.set = set;
this.noVideo = noVideo;
}
[BackgroundDependencyLoader]
private void load(BeatmapManager beatmaps)
{
this.beatmaps = beatmaps;
beatmaps.ItemAdded += setAdded;
beatmaps.ItemRemoved += setRemoved;
// initial value
if (set.OnlineBeatmapSetID != null)
Downloaded.Value = beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (beatmaps != null)
{
beatmaps.ItemAdded -= setAdded;
beatmaps.ItemRemoved -= setRemoved;
}
}
/// <summary>
/// Begin downloading the associated beatmap set.
/// </summary>
/// <returns>True if downloading began. False if an existing download is active or completed.</returns>
public bool Download()
{
if (Downloaded.Value)
return false;
if (beatmaps.GetExistingDownload(set) != null)
return false;
beatmaps.Download(set, noVideo);
return true;
}
private void setAdded(BeatmapSetInfo s)
{
if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
Downloaded.Value = true;
}
private void setRemoved(BeatmapSetInfo s)
{
if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
Downloaded.Value = false;
}
}
}

Some files were not shown because too many files have changed in this diff Show More