1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-18 03:05:10 +08:00

Merge pull request #1 from ppy/master

sync fork with base
This commit is contained in:
Nitrous 2019-02-19 06:11:53 +08:00 committed by GitHub
commit 2483f0ebc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
207 changed files with 3882 additions and 2245 deletions

5
.gitignore vendored
View File

@ -11,8 +11,9 @@
*.userprefs
### Cake ###
tools/*
!tools/cakebuild.csproj
tools/**
build/tools/**
# Build results
bin/[Dd]ebug/

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "osu-resources"]
path = osu-resources
url = https://github.com/ppy/osu-resources

14
.vscode/launch.json vendored
View File

@ -68,6 +68,20 @@
}
},
"console": "internalConsole"
},
{
"name": "Cake: Debug Script",
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/build/tools/Cake.CoreCLR/0.30.0/Cake.dll",
"args": [
"${workspaceRoot}/build/build.cake",
"--debug",
"--verbosity=diagnostic"
],
"cwd": "${workspaceRoot}/build",
"stopAtEntry": true,
"externalConsole": false
}
]
}

3
.vscode/tasks.json vendored
View File

@ -70,7 +70,8 @@
"type": "shell",
"command": "dotnet",
"args": [
"restore"
"restore",
"osu.sln"
],
"problemMatcher": []
}

View File

@ -31,18 +31,14 @@ If your platform is not listed above, there is still a chance you can manually b
Clone the repository **including submodules**:
```shell
git clone --recurse-submodules https://github.com/ppy/osu
git clone https://github.com/ppy/osu
cd osu
```
> If you forgot the `--recurse-submodules` option, run this command inside the `osu` directory:
>
> `git submodule update --init --recursive`
To update the source code to the latest commit, run the following command inside the `osu` directory:
```shell
git pull --recurse-submodules
git pull
```
## Building
@ -73,9 +69,13 @@ For example, you can run osu! with the following command:
LD_LIBRARY_PATH="$(pwd)/osu.Desktop/bin/Debug/netcoreapp2.2" dotnet run --project osu.Desktop
```
## Testing with resource/framework modifications
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be achieved by running some commands as documented on the [osu-resources](https://github.com/ppy/osu-resources/wiki/Testing-local-resources-checkout-with-other-projects) and [osu-framework](https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects) wiki pages.
## Code analysis
Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternative, you can install resharper or use rider to get inline support in your IDE of choice.
Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install resharper or use rider to get inline support in your IDE of choice.
# Contributing

View File

@ -41,27 +41,28 @@ Param(
[switch]$ShowDescription,
[Alias("WhatIf", "Noop")]
[switch]$DryRun,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[Parameter(Position = 0, Mandatory = $false, ValueFromRemainingArguments = $true)]
[string[]]$ScriptArgs
)
Write-Host "Preparing to run build script..."
# Determine the script root for resolving other paths.
if(!$PSScriptRoot){
if(!$PSScriptRoot) {
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
}
# Resolve the paths for resources used for debugging.
$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
$CAKE_CSPROJ = Join-Path $TOOLS_DIR "cakebuild.csproj"
$BUILD_DIR = Join-Path $PSScriptRoot "build"
$TOOLS_DIR = Join-Path $BUILD_DIR "tools"
$CAKE_CSPROJ = Join-Path $BUILD_DIR "cakebuild.csproj"
# Install the required tools locally.
Write-Host "Restoring cake tools..."
Invoke-Expression "dotnet restore `"$CAKE_CSPROJ`" --packages `"$TOOLS_DIR`"" | Out-Null
# Find the Cake executable
$CAKE_EXECUTABLE = (Get-ChildItem -Path ./tools/cake.coreclr/ -Filter Cake.dll -Recurse).FullName
$CAKE_EXECUTABLE = (Get-ChildItem -Path "$TOOLS_DIR/cake.coreclr/" -Filter Cake.dll -Recurse).FullName
# Build Cake arguments
$cakeArguments = @("$Script");
@ -75,5 +76,7 @@ $cakeArguments += $ScriptArgs
# Start Cake
Write-Host "Running build script..."
Push-Location -Path $BUILD_DIR
Invoke-Expression "dotnet `"$CAKE_EXECUTABLE`" $cakeArguments"
Pop-Location
exit $LASTEXITCODE

View File

@ -6,12 +6,13 @@
echo "Preparing to run build script..."
cd build
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
TOOLS_DIR=$SCRIPT_DIR/tools
CAKE_BINARY_PATH=$TOOLS_DIR/"cake.coreclr"
SCRIPT="build.cake"
CAKE_CSPROJ=$TOOLS_DIR/"cakebuild.csproj"
CAKE_CSPROJ=$SCRIPT_DIR/"cakebuild.csproj"
# Parse arguments.
CAKE_ARGUMENTS=()

View File

@ -1,6 +1,7 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.21"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.2"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
///////////////////////////////////////////////////////////////////////////////
// ARGUMENTS
@ -9,30 +10,24 @@
var target = Argument("target", "Build");
var configuration = Argument("configuration", "Release");
var osuSolution = new FilePath("./osu.sln");
var rootDirectory = new DirectoryPath("..");
var solution = rootDirectory.CombineWithFilePath("osu.sln");
///////////////////////////////////////////////////////////////////////////////
// TASKS
///////////////////////////////////////////////////////////////////////////////
Task("Restore")
.Does(() => {
DotNetCoreRestore(osuSolution.FullPath);
});
Task("Compile")
.IsDependentOn("Restore")
.Does(() => {
DotNetCoreBuild(osuSolution.FullPath, new DotNetCoreBuildSettings {
DotNetCoreBuild(solution.FullPath, new DotNetCoreBuildSettings {
Configuration = configuration,
NoRestore = true,
});
});
Task("Test")
.IsDependentOn("Compile")
.Does(() => {
var testAssemblies = GetFiles("**/*.Tests/bin/**/*.Tests.dll");
var testAssemblies = GetFiles(rootDirectory + "/**/*.Tests/bin/**/*.Tests.dll");
DotNetCoreVSTest(testAssemblies, new DotNetCoreVSTestSettings {
Logger = AppVeyor.IsRunningOnAppVeyor ? "Appveyor" : $"trx",
@ -46,9 +41,7 @@ Task("InspectCode")
.WithCriteria(IsRunningOnWindows())
.IsDependentOn("Compile")
.Does(() => {
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
InspectCode(osuSolution, new InspectCodeSettings {
InspectCode(solution, new InspectCodeSettings {
CachesHome = "inspectcode",
OutputFile = "inspectcodereport.xml",
});
@ -59,7 +52,7 @@ Task("InspectCode")
Task("CodeFileSanity")
.Does(() => {
ValidateCodeSanity(new ValidateCodeSanitySettings {
RootDirectory = ".",
RootDirectory = rootDirectory.FullPath,
IsAppveyorBuild = AppVeyor.IsRunningOnAppVeyor
});
});

@ -1 +0,0 @@
Subproject commit 677897728f4332fa1200e0280ca02c4b987c6c47

View File

@ -16,7 +16,6 @@ using osu.Desktop.Updater;
using osu.Framework;
using osu.Framework.Platform.Windows;
using osu.Framework.Screens;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
namespace osu.Desktop
@ -63,9 +62,10 @@ namespace osu.Desktop
}
}
protected override void ScreenChanged(OsuScreen current, Screen newScreen)
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
{
base.ScreenChanged(current, newScreen);
base.ScreenChanged(lastScreen, newScreen);
switch (newScreen)
{
case Intro _:

View File

@ -22,7 +22,6 @@
<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.5.0" />
</ItemGroup>
<ItemGroup Label="Package References">

View File

@ -39,10 +39,6 @@
<Project>{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}</Project>
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
<Project>{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}</Project>
<Name>osu.Game.Resources</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Import Project="..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets')" />

View File

@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Catch.Tests
[TestCase("basic")]
[TestCase("spinner")]
[TestCase("spinner-and-circles")]
[TestCase("slider")]
public new void Test(string name)
{
base.Test(name);

View File

@ -0,0 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Difficulty;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Catch.Tests
{
public class CatchDifficultyCalculatorTest : DifficultyCalculatorTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
[TestCase(3.8664391043534758, "diffcalc-test")]
public void Test(double expected, string name)
=> base.Test(expected, name);
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset(), beatmap);
protected override Ruleset CreateRuleset() => new CatchRuleset();
}
}

View File

@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
private const float preempt = 1000;

View File

@ -55,6 +55,13 @@ namespace osu.Game.Rulesets.Catch.Objects
var minDistanceFromEnd = Velocity * 0.01;
var tickSamples = Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}).ToList();
AddNested(new Fruit
{
Samples = Samples,
@ -62,15 +69,22 @@ namespace osu.Game.Rulesets.Catch.Objects
X = X
});
double lastDropletTime = StartTime;
double lastTickTime = StartTime;
for (int span = 0; span < this.SpanCount(); span++)
{
var spanStartTime = StartTime + span * spanDuration;
var reversed = span % 2 == 1;
for (double d = 0; d <= length; d += tickDistance)
for (double d = tickDistance;; d += tickDistance)
{
bool isLastTick = false;
if (d + minDistanceFromEnd >= length)
{
d = length;
isLastTick = true;
}
var timeProgress = d / length;
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
@ -79,47 +93,42 @@ namespace osu.Game.Rulesets.Catch.Objects
if (LegacyLastTickOffset != null)
{
// If we're the last tick, apply the legacy offset
if (span == this.SpanCount() - 1 && d + tickDistance > length)
if (span == this.SpanCount() - 1 && isLastTick)
time = Math.Max(StartTime + Duration / 2, time - LegacyLastTickOffset.Value);
}
double tinyTickInterval = time - lastDropletTime;
while (tinyTickInterval > 100)
tinyTickInterval /= 2;
for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
int tinyTickCount = 1;
double tinyTickInterval = time - lastTickTime;
while (tinyTickInterval > 100 && tinyTickCount < 10000)
{
tinyTickInterval /= 2;
tinyTickCount *= 2;
}
for (int tinyTickIndex = 0; tinyTickIndex < tinyTickCount - 1; tinyTickIndex++)
{
var t = lastTickTime + (tinyTickIndex + 1) * tinyTickInterval;
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
AddNested(new TinyDroplet
{
StartTime = t,
X = X + Path.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}))
Samples = tickSamples
});
}
if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
lastTickTime = time;
if (isLastTick)
break;
AddNested(new Droplet
{
AddNested(new Droplet
{
StartTime = time,
X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}))
});
}
lastDropletTime = time;
StartTime = time,
X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
Samples = tickSamples
});
}
AddNested(new Fruit

View File

@ -0,0 +1,138 @@
osu file format v14
[General]
StackLeniency: 0.3
Mode: 2
[Difficulty]
CircleSize:4
OverallDifficulty:7
ApproachRate:8.3
SliderMultiplier:1.6
SliderTickRate:1
[TimingPoints]
500,500,4,2,1,50,1,0
34500,-50,4,2,1,50,0,0
[HitObjects]
// fruits spaced 1/1 beat apart
32,128,0,5,0,0:0:0:0:
96,128,500,1,0,0:0:0:0:
160,128,1000,1,0,0:0:0:0:
224,128,1500,1,0,0:0:0:0:
288,128,2000,1,0,0:0:0:0:
352,128,2500,1,0,0:0:0:0:
416,128,3000,1,0,0:0:0:0:
480,128,3500,1,0,0:0:0:0:
// fruits spaced 1/2 beat apart
32,160,4500,1,0,0:0:0:0:
64,160,4750,1,0,0:0:0:0:
96,160,5000,1,0,0:0:0:0:
128,160,5250,1,0,0:0:0:0:
160,160,5500,1,0,0:0:0:0:
192,160,5750,1,0,0:0:0:0:
224,160,6000,1,0,0:0:0:0:
256,160,6250,1,0,0:0:0:0:
288,160,6500,1,0,0:0:0:0:
// fruits spaced 1/4 beat apart
96,128,7500,1,0,0:0:0:0:
128,128,7625,1,0,0:0:0:0:
160,128,7750,1,0,0:0:0:0:
192,128,7875,1,0,0:0:0:0:
224,128,8000,1,0,0:0:0:0:
256,128,8125,1,0,0:0:0:0:
288,128,8250,1,0,0:0:0:0:
320,128,8375,1,0,0:0:0:0:
352,128,8500,1,0,0:0:0:0:
// fruit hyperdashes, spaced 1/2 beat apart
32,160,9500,1,0,0:0:0:0:
480,160,9750,1,0,0:0:0:0:
32,160,10000,1,0,0:0:0:0:
480,160,10250,1,0,0:0:0:0:
32,160,10500,1,0,0:0:0:0:
480,160,10750,1,0,0:0:0:0:
32,160,11000,1,0,0:0:0:0:
// fruit hyperdashes, spaced 1/4 beat apart
32,192,12000,1,0,0:0:0:0:
480,192,12125,1,0,0:0:0:0:
32,192,12250,1,0,0:0:0:0:
480,192,12375,1,0,0:0:0:0:
32,192,12500,1,0,0:0:0:0:
480,192,12625,1,0,0:0:0:0:
32,192,12750,1,0,0:0:0:0:
480,192,12875,1,0,0:0:0:0:
32,192,13000,1,0,0:0:0:0:
// stream + hyperdash + stream, spaced 1/4 beat apart
32,192,14000,1,0,0:0:0:0:
64,192,14125,1,0,0:0:0:0:
96,192,14250,1,0,0:0:0:0:
128,192,14375,1,0,0:0:0:0:
480,192,14500,1,0,0:0:0:0:
448,192,14625,1,0,0:0:0:0:
416,192,14750,1,0,0:0:0:0:
384,192,14875,1,0,0:0:0:0:
32,192,15000,1,0,0:0:0:0:
// basic sliders
32,192,16000,2,0,L|192:192,1,160
224,192,17000,2,0,L|384:192,1,160
416,192,17875,2,0,L|480:192,1,40
// slider hyperdashes, spaced 1/4 beat apart
32,192,19000,2,0,L|128:192,1,80
480,192,19375,2,0,L|384:192,1,80
352,192,19750,2,0,L|256:192,1,80
0,192,20125,2,0,L|128:192,1,120
// stream + slider hyperdashes, spaced 1/4 beat apart
32,192,21500,1,0,0:0:0:0:
64,192,21625,1,0,0:0:0:0:
96,192,21750,1,0,0:0:0:0:
512,192,21875,2,0,L|320:192,1,160
320,192,22500,1,0,0:0:0:0:
288,192,22625,1,0,0:0:0:0:
256,192,22750,1,0,0:0:0:0:
0,192,22875,2,0,L|64:192,1,40
// streams, spaced 1/4 beat apart
64,192,24000,1,0,0:0:0:0:
160,192,24125,1,0,0:0:0:0:
64,192,24250,1,0,0:0:0:0:
160,192,24375,1,0,0:0:0:0:
64,192,24500,1,0,0:0:0:0:
160,192,24625,1,0,0:0:0:0:
64,192,24750,1,0,0:0:0:0:
160,192,24875,1,0,0:0:0:0:
64,192,25000,1,0,0:0:0:0:
160,192,25125,1,0,0:0:0:0:
64,192,25250,1,0,0:0:0:0:
160,192,25375,1,0,0:0:0:0:
64,192,25500,1,0,0:0:0:0:
// stream + spinner combo, spaced 1/4 beat apart
256,192,26500,12,0,27000,0:0:0:0:
128,192,27250,5,0,0:0:0:0:
128,192,27375,1,0,0:0:0:0:
160,192,27500,1,0,0:0:0:0:
192,192,27625,1,0,0:0:0:0:
256,192,27750,12,0,28500,0:0:0:0:
192,192,28625,5,0,0:0:0:0:
224,192,28750,1,0,0:0:0:0:
256,192,28875,1,0,0:0:0:0:
256,192,29000,1,0,0:0:0:0:
256,192,29125,12,0,29500,0:0:0:0:
// long slow slider
0,192,30500,6,0,B|480:192|480:192|0:192,2,960
// long fast slider
0,192,37500,6,0,B|480:192|480:192|0:192,2,960
// long hyperdash slider
0,192,41500,2,0,P|544:192|544:192,5,480

View File

@ -0,0 +1 @@
{"Mappings":[{"StartTime":19184.0,"Objects":[{"StartTime":19184.0,"Position":320.0},{"StartTime":19263.0,"Position":311.730255},{"StartTime":19343.0,"Position":324.6205},{"StartTime":19423.0,"Position":343.0907},{"StartTime":19503.0,"Position":372.2917},{"StartTime":19582.0,"Position":385.194733},{"StartTime":19662.0,"Position":379.0426},{"StartTime":19742.0,"Position":385.1066},{"StartTime":19822.0,"Position":391.624664},{"StartTime":19919.0,"Position":386.27832},{"StartTime":20016.0,"Position":380.117035},{"StartTime":20113.0,"Position":381.664154},{"StartTime":20247.0,"Position":370.872864}]}]}

View File

@ -0,0 +1,18 @@
osu file format v14
[General]
Mode: 2
[Difficulty]
HPDrainRate:3
CircleSize:2
OverallDifficulty:4
ApproachRate:4
SliderMultiplier:0.9
SliderTickRate:1
[TimingPoints]
35.4473684210527,638.298947368422,4,2,1,60,1,0
[HitObjects]
320,176,19184,2,8,P|384:168|368:232,1,150

View File

@ -43,6 +43,6 @@ namespace osu.Game.Rulesets.Catch.Scoring
Health.Value += Math.Max(result.Judgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
}
protected override HitWindows CreateHitWindows() => new CatchHitWindows();
public override HitWindows CreateHitWindows() => new CatchHitWindows();
}
}

View File

@ -39,10 +39,6 @@
<Project>{48F4582B-7687-4621-9CBE-5C24197CB536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
<Project>{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}</Project>
<Name>osu.Game.Resources</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Import Project="..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets')" />

View File

@ -0,0 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Mania.Tests
{
public class ManiaDifficultyCalculatorTest : DifficultyCalculatorTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
[TestCase(2.2676066895468976, "diffcalc-test")]
public void Test(double expected, string name)
=> base.Test(expected, name);
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset(), beatmap);
protected override Ruleset CreateRuleset() => new ManiaRuleset();
}
}

View File

@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
difficultyHitObjects.AddRange(beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime));
if (!calculateStrainValues(difficultyHitObjects, timeRate))
return new DifficultyAttributes(mods, 0);
return new ManiaDifficultyAttributes(mods, 0);
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;

View File

@ -0,0 +1,180 @@
osu file format v14
[General]
Mode: 3
[Difficulty]
CircleSize:4
OverallDifficulty:7
ApproachRate:8.3
SliderMultiplier:1.6
SliderTickRate:1
[TimingPoints]
500,500,4,2,1,50,1,0
37500,-50,4,2,1,50,0,0
41500,-25,4,2,1,50,0,0
[HitObjects]
// jacks spaced 1/1 beat apart
64,192,0,1,0,0:0:0:0:
64,192,500,1,0,0:0:0:0:
64,192,1000,1,0,0:0:0:0:
64,192,1500,1,0,0:0:0:0:
64,192,2000,1,0,0:0:0:0:
64,192,2500,1,0,0:0:0:0:
// jacks spaced 1/2 beat apart
64,192,3500,1,0,0:0:0:0:
64,192,3750,1,0,0:0:0:0:
64,192,4000,1,0,0:0:0:0:
64,192,4250,1,0,0:0:0:0:
64,192,4500,1,0,0:0:0:0:
64,192,4750,1,0,0:0:0:0:
64,192,5000,1,0,0:0:0:0:
64,192,6000,1,0,0:0:0:0:
// doubles jacks spaced 1/2 beat apart
192,192,6000,1,0,0:0:0:0:
64,192,6250,1,0,0:0:0:0:
192,192,6250,1,0,0:0:0:0:
64,192,6500,1,0,0:0:0:0:
192,192,6500,1,0,0:0:0:0:
64,192,6750,1,0,0:0:0:0:
192,192,6750,1,0,0:0:0:0:
64,192,7000,1,0,0:0:0:0:
192,192,7000,1,0,0:0:0:0:
64,192,7250,1,0,0:0:0:0:
192,192,7250,1,0,0:0:0:0:
64,192,7500,1,0,0:0:0:0:
192,192,7500,1,0,0:0:0:0:
// trill spaced 1/2 beat apart
64,192,8500,1,0,0:0:0:0:
192,192,8750,1,0,0:0:0:0:
64,192,9000,1,0,0:0:0:0:
192,192,9250,1,0,0:0:0:0:
64,192,9500,1,0,0:0:0:0:
192,192,9750,1,0,0:0:0:0:
64,192,10000,1,0,0:0:0:0:
192,192,10250,1,0,0:0:0:0:
64,192,10500,1,0,0:0:0:0:
// stair spaced 1/4 apart
64,192,11500,1,0,0:0:0:0:
192,192,11625,1,0,0:0:0:0:
320,192,11750,1,0,0:0:0:0:
448,192,11875,1,0,0:0:0:0:
320,192,12000,1,0,0:0:0:0:
192,192,12125,1,0,0:0:0:0:
64,192,12250,1,0,0:0:0:0:
192,192,12375,1,0,0:0:0:0:
320,192,12500,1,0,0:0:0:0:
448,192,12625,1,0,0:0:0:0:
// jumpstreams?
64,192,13500,1,0,0:0:0:0:
192,192,13625,1,0,0:0:0:0:
320,192,13750,1,0,0:0:0:0:
448,192,13875,1,0,0:0:0:0:
320,192,14000,1,0,0:0:0:0:
192,192,14000,1,0,0:0:0:0:
64,192,14125,1,0,0:0:0:0:
192,192,14250,1,0,0:0:0:0:
320,192,14250,1,0,0:0:0:0:
448,192,14250,1,0,0:0:0:0:
64,192,14375,1,0,0:0:0:0:
64,192,14500,1,0,0:0:0:0:
320,192,14625,1,0,0:0:0:0:
448,192,14625,1,0,0:0:0:0:
192,192,14625,1,0,0:0:0:0:
192,192,14750,1,0,0:0:0:0:
64,192,14875,1,0,0:0:0:0:
192,192,15000,1,0,0:0:0:0:
320,192,15125,1,0,0:0:0:0:
448,192,15125,1,0,0:0:0:0:
// double... jumps?
64,192,16000,1,0,0:0:0:0:
64,192,16250,1,0,0:0:0:0:
192,192,16250,1,0,0:0:0:0:
192,192,16500,1,0,0:0:0:0:
320,192,16500,1,0,0:0:0:0:
320,192,16750,1,0,0:0:0:0:
448,192,16750,1,0,0:0:0:0:
448,192,17000,1,0,0:0:0:0:
// notes alongside hold
64,192,18000,128,0,18500:0:0:0:0:
192,192,18000,1,0,0:0:0:0:
192,192,18250,1,0,0:0:0:0:
192,192,18500,1,0,0:0:0:0:
// notes overlapping hold
64,192,19500,1,0,0:0:0:0:
192,192,19625,128,0,20875:0:0:0:0:
64,192,19750,1,0,0:0:0:0:
64,192,20000,1,0,0:0:0:0:
64,192,20250,1,0,0:0:0:0:
64,192,20500,1,0,0:0:0:0:
64,192,20750,1,0,0:0:0:0:
64,192,21000,1,0,0:0:0:0:
// simultaneous holds
64,192,22000,128,0,23000:0:0:0:0:
192,192,22000,128,0,23000:0:0:0:0:
320,192,22000,128,0,23000:0:0:0:0:
448,192,22000,128,0,23000:0:0:0:0:
// hold stairs
64,192,24500,128,0,25500:0:0:0:0:
192,192,24625,128,0,25375:0:0:0:0:
320,192,24750,128,0,25250:0:0:0:0:
448,192,24875,128,0,25125:0:0:0:0:
448,192,25375,128,0,26375:0:0:0:0:
320,192,25500,128,0,26250:0:0:0:0:
192,192,25625,128,0,26125:0:0:0:0:
64,192,25750,128,0,26000:0:0:0:0:
// quads
64,192,26500,1,0,0:0:0:0:
64,192,27500,1,0,0:0:0:0:
192,192,27500,1,0,0:0:0:0:
320,192,27500,1,0,0:0:0:0:
448,192,27500,1,0,0:0:0:0:
64,192,27750,1,0,0:0:0:0:
192,192,27750,1,0,0:0:0:0:
320,192,27750,1,0,0:0:0:0:
448,192,27750,1,0,0:0:0:0:
64,192,28000,1,0,0:0:0:0:
192,192,28000,1,0,0:0:0:0:
320,192,28000,1,0,0:0:0:0:
448,192,28000,1,0,0:0:0:0:
64,192,28250,1,0,0:0:0:0:
192,192,28250,1,0,0:0:0:0:
320,192,28250,1,0,0:0:0:0:
448,192,28250,1,0,0:0:0:0:
64,192,28500,1,0,0:0:0:0:
192,192,28500,1,0,0:0:0:0:
320,192,28500,1,0,0:0:0:0:
448,192,28500,1,0,0:0:0:0:
// double-trills
64,192,29500,1,0,0:0:0:0:
192,192,29500,1,0,0:0:0:0:
320,192,29625,1,0,0:0:0:0:
448,192,29625,1,0,0:0:0:0:
64,192,29750,1,0,0:0:0:0:
192,192,29750,1,0,0:0:0:0:
320,192,29875,1,0,0:0:0:0:
448,192,29875,1,0,0:0:0:0:
64,192,30000,1,0,0:0:0:0:
192,192,30000,1,0,0:0:0:0:
320,192,30125,1,0,0:0:0:0:
448,192,30125,1,0,0:0:0:0:
64,192,30250,1,0,0:0:0:0:
192,192,30250,1,0,0:0:0:0:
320,192,30375,1,0,0:0:0:0:
448,192,30375,1,0,0:0:0:0:
64,192,30500,1,0,0:0:0:0:
192,192,30500,1,0,0:0:0:0:

View File

@ -159,6 +159,6 @@ namespace osu.Game.Rulesets.Mania.Scoring
}
}
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
public override HitWindows CreateHitWindows() => new ManiaHitWindows();
}
}

View File

@ -39,10 +39,6 @@
<Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
<Project>{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}</Project>
<Name>osu.Game.Resources</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Import Project="..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets')" />

View File

@ -0,0 +1,25 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Osu.Difficulty;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class OsuDifficultyCalculatorTest : DifficultyCalculatorTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
[TestCase(6.931145117263422, "diffcalc-test")]
public void Test(double expected, string name)
=> base.Test(expected, name);
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap);
protected override Ruleset CreateRuleset() => new OsuRuleset();
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCaseHitCircleLongCombo : Game.Tests.Visual.TestCasePlayer
{
public TestCaseHitCircleLongCombo()
: base(new OsuRuleset())
{
}
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo
}
};
for (int i = 0; i < 512; i++)
beatmap.HitObjects.Add(new HitCircle { Position = new Vector2(256, 192), StartTime = i * 100 });
return beatmap;
}
}
}

View File

@ -143,7 +143,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
var scoringTimes = slider.NestedHitObjects.Skip(1).Select(t => t.StartTime);
foreach (var time in scoringTimes)
computeVertex(time);
computeVertex(slider.EndTime);
}
private Vector2 getEndCursorPosition(OsuHitObject hitObject)

View File

@ -0,0 +1,74 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Mods
{
internal class OsuModGrow : Mod, IApplicableToDrawableHitObjects
{
public override string Name => "Grow";
public override string Acronym => "GR";
public override FontAwesome Icon => FontAwesome.fa_arrows_v;
public override ModType Type => ModType.Fun;
public override string Description => "Hit them at the right size!";
public override double ScoreMultiplier => 1;
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
foreach (var drawable in drawables)
{
switch (drawable)
{
case DrawableSpinner _:
continue;
default:
drawable.ApplyCustomUpdateState += ApplyCustomState;
break;
}
}
}
protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state)
{
var h = (OsuHitObject)drawable.HitObject;
// apply grow effect
switch (drawable)
{
case DrawableSliderHead _:
case DrawableSliderTail _:
// special cases we should *not* be scaling.
break;
case DrawableSlider _:
case DrawableHitCircle _:
{
using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
drawable.ScaleTo(0.5f).Then().ScaleTo(1, h.TimePreempt, Easing.OutSine);
break;
}
}
// remove approach circles
switch (drawable)
{
case DrawableHitCircle circle:
// we don't want to see the approach circle
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
circle.ApproachCircle.Hide();
break;
}
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osuTK;
@ -27,40 +28,58 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
private readonly IBindable<float> scaleBindable = new Bindable<float>();
private readonly Container explodeContainer;
private readonly Container scaleContainer;
public DrawableHitCircle(HitCircle h)
: base(h)
{
Origin = Anchor.Centre;
Position = HitObject.StackedPosition;
Scale = new Vector2(h.Scale);
InternalChildren = new Drawable[]
{
glow = new GlowPiece(),
circle = new CirclePiece
scaleContainer = new Container
{
Hit = () =>
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Child = explodeContainer = new Container
{
if (AllJudged)
return false;
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new Drawable[]
{
glow = new GlowPiece(),
circle = new CirclePiece
{
Hit = () =>
{
if (AllJudged)
return false;
UpdateResult(true);
return true;
},
UpdateResult(true);
return true;
},
},
number = new NumberPiece
{
Text = (HitObject.IndexInCurrentCombo + 1).ToString(),
},
ring = new RingPiece(),
flash = new FlashPiece(),
explode = new ExplodePiece(),
ApproachCircle = new ApproachCircle
{
Alpha = 0,
Scale = new Vector2(4),
}
}
}
},
number = new NumberPiece
{
Text = (HitObject.IndexInCurrentCombo + 1).ToString(),
},
ring = new RingPiece(),
flash = new FlashPiece(),
explode = new ExplodePiece(),
ApproachCircle = new ApproachCircle
{
Alpha = 0,
Scale = new Vector2(4),
}
};
//may not be so correct
@ -72,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
scaleBindable.BindValueChanged(v => Scale = new Vector2(v));
scaleBindable.BindValueChanged(v => scaleContainer.Scale = new Vector2(v), true);
positionBindable.BindTo(HitObject.PositionBindable);
stackHeightBindable.BindTo(HitObject.StackHeightBindable);
@ -156,8 +175,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
circle.FadeOut();
number.FadeOut();
this.FadeOut(800)
.ScaleTo(Scale * 1.5f, 400, Easing.OutQuad);
this.FadeOut(800);
explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad);
}
Expire();

View File

@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn);

View File

@ -156,9 +156,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.SkinChanged(skin, allowFallback);
Body.AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : Body.AccentColour);
Body.BorderColour = skin.GetValue<SkinConfiguration, Color4>(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : Body.BorderColour);
Ball.AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : Ball.AccentColour);
Body.AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? Body.AccentColour;
Body.BorderColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Body.BorderColour;
Ball.AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Ball.AccentColour;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)

View File

@ -124,6 +124,7 @@ namespace osu.Game.Rulesets.Osu
return new Mod[] {
new OsuModTransform(),
new OsuModWiggle(),
new OsuModGrow()
};
default:
return new Mod[] { };

View File

@ -0,0 +1,179 @@
osu file format v14
[General]
StackLeniency: 0.3
Mode: 0
[Difficulty]
CircleSize:4
OverallDifficulty:7
ApproachRate:8.3
SliderMultiplier:1.6
SliderTickRate:1
[TimingPoints]
500,500,4,2,1,50,1,0
62500,-500,4,2,1,50,0,0
71000,-100,4,2,1,50,0,0
[HitObjects]
// Circles spaced 1 beat apart, with increasing jump distance
126,112,500,5,0,0:0:0:0:
130,155,1000,1,0,0:0:0:0:
131,269,1500,1,0,0:0:0:0:
341,269,2000,1,0,0:0:0:0:
113,95,2500,1,0,0:0:0:0:
// Circles spaced 1/2 beat apart, with increasing jump distance
108,104,3500,5,0,0:0:0:0:
110,145,3750,1,0,0:0:0:0:
115,262,4000,1,0,0:0:0:0:
285,265,4250,1,0,0:0:0:0:
458,48,4500,1,0,0:0:0:0:
35,199,4750,1,0,0:0:0:0:
251,340,5000,1,0,0:0:0:0:
20,352,5250,1,0,0:0:0:0:
426,62,5500,1,0,0:0:0:0:
// Circles spaced 1/4 beat apart, with increasing jump distances
211,138,6500,5,0,0:0:0:0:
99,256,6625,1,0,0:0:0:0:
68,129,6750,1,0,0:0:0:0:
371,340,6875,1,0,0:0:0:0:
241,219,7000,1,0,0:0:0:0:
252,148,7125,1,0,0:0:0:0:
434,97,7250,1,0,0:0:0:0:
40,38,7375,1,0,0:0:0:0:
114,334,7500,1,0,0:0:0:0:
301,19,7625,1,0,0:0:0:0:
441,241,7750,1,0,0:0:0:0:
121,91,7875,1,0,0:0:0:0:
270,384,8000,1,0,0:0:0:0:
488,92,8125,1,0,0:0:0:0:
332,82,8250,1,0,0:0:0:0:
108,240,8375,1,0,0:0:0:0:
281,268,8500,1,0,0:0:0:0:
// Constant spaced circles spaced 1/2 beat apart, small jump distances, changing angles
252,191,9500,5,0,0:0:0:0:
356,191,9750,1,0,0:0:0:0:
311,268,10000,1,0,0:0:0:0:
190,270,10250,1,0,0:0:0:0:
107,199,10500,1,0,0:0:0:0:
172,105,10750,1,0,0:0:0:0:
297,102,11000,1,0,0:0:0:0:
373,178,11250,1,0,0:0:0:0:
252,195,11500,1,0,0:0:0:0:
// Constant spaced circles spaced 1/2 beat apart, large jump distances, changing angles
140,187,12500,5,0,0:0:0:0:
451,331,12750,1,0,0:0:0:0:
46,338,13000,1,0,0:0:0:0:
204,50,13250,1,0,0:0:0:0:
464,162,13500,1,0,0:0:0:0:
252,346,13750,1,0,0:0:0:0:
13,175,14000,1,0,0:0:0:0:
488,181,14250,1,0,0:0:0:0:
251,187,14500,1,0,0:0:0:0:
// Constant spaced circles spaced 1/4 beat apart, small jump distances, changing angles
188,192,15500,5,0,0:0:0:0:
298,194,15625,1,0,0:0:0:0:
317,84,15750,1,0,0:0:0:0:
185,85,15875,1,0,0:0:0:0:
77,200,16000,1,0,0:0:0:0:
184,303,16125,1,0,0:0:0:0:
295,225,16250,1,0,0:0:0:0:
300,84,16375,1,0,0:0:0:0:
144,82,16500,1,0,0:0:0:0:
141,215,16625,1,0,0:0:0:0:
314,184,16750,1,0,0:0:0:0:
188,192,16875,1,0,0:0:0:0:
188,192,17000,1,0,0:0:0:0:
// Constant spaced circles spaced 1/4 beat apart, large jump distances, changing angles
97,192,18000,5,0,0:0:0:0:
336,38,18125,1,0,0:0:0:0:
440,322,18250,1,0,0:0:0:0:
39,331,18375,1,0,0:0:0:0:
98,39,18500,1,0,0:0:0:0:
460,179,18625,1,0,0:0:0:0:
245,338,18750,1,0,0:0:0:0:
12,184,18875,1,0,0:0:0:0:
250,41,19000,1,0,0:0:0:0:
265,193,19125,1,0,0:0:0:0:
486,22,19250,1,0,0:0:0:0:
411,205,19375,1,0,0:0:0:0:
107,198,19500,1,0,0:0:0:0:
// Short sliders spaced 1 beat apart
28,108,20500,2,0,L|196:107,1,160
25,177,21500,2,0,L|193:176,1,160
26,308,22500,2,0,L|194:307,1,160
320,89,23500,2,0,L|488:88,1,160
// Short sliders spaced 1/2 beat apart
28,108,25000,6,0,L|196:107,1,160
27,173,25750,2,0,L|195:172,1,160
25,292,26500,2,0,L|193:291,1,160
340,213,27250,2,0,L|508:212,1,160
21,44,28000,2,0,L|189:43,1,160
// Short sliders spaced 1/4 beat apart
28,108,29500,6,0,L|196:107,1,160
30,169,30125,2,0,L|198:168,1,160
35,282,30750,2,0,L|203:281,1,160
327,286,31375,2,0,L|495:285,1,160
51,61,32000,2,0,L|219:60,1,160
// Large, medium-paced slider shapes
// PerfectCurve
66,86,33500,6,0,P|246:348|427:44,1,800
66,86,36500,2,0,P|246:348|427:44,1,800
66,86,39500,2,0,P|246:348|427:44,1,800
// Linear
66,72,42500,2,0,B|419:65|419:65|66:316|66:316|426:318,1,1120
66,72,46500,2,0,B|419:65|419:65|66:316|66:316|426:318,1,1120
66,72,50500,2,0,B|419:65|419:65|66:316|66:316|426:318,1,1120
// Bezier
76,287,54500,2,0,B|440:325|138:128|470:302|500:30|130:85|66:82,1,640
76,287,57000,2,0,B|440:325|138:128|470:302|500:30|130:85|66:82,1,640
76,287,59500,2,0,B|440:325|138:128|470:302|500:30|130:85|66:82,1,640
// Large slow slider with many ticks
81,170,62500,6,0,P|263:78|168:268,1,480
// Fast slider with many repeats
102,152,71000,6,0,L|175:153,18,64
// Slider-circle combos, spaced 1/2 beat apart
106,204,75500,6,0,P|275:33|171:304,1,800
255,179,78250,1,0,0:0:0:0:
106,204,78500,2,0,P|275:33|171:304,1,800
255,179,81250,1,0,0:0:0:0:
106,204,81500,2,0,P|275:33|171:304,1,800
// Circle-spinner combos, spaced 1/2 beat apart
82,69,85000,5,0,0:0:0:0:
256,192,85250,8,0,86000,0:0:0:0:
384,189,86250,5,0,0:0:0:0:
256,192,86500,12,0,87000,0:0:0:0:
// Spinner-spinner combos, spaced 1/2 beat apart
256,192,88000,12,0,89000,0:0:0:0:
256,192,89250,12,0,90250,0:0:0:0:
256,192,90500,12,0,91500,0:0:0:0:
256,192,91750,12,0,92750,0:0:0:0:
256,192,93000,12,0,94000,0:0:0:0:
// Slider-spinner combos, spaced 1/2 beat apart
49,89,95000,6,0,L|214:87,1,160
256,192,95625,12,0,96500,0:0:0:0:
12,299,96625,6,0,L|177:297,1,160
256,192,97250,12,0,98125,0:0:0:0:
295,107,98250,6,0,L|460:105,1,160
256,192,98875,12,0,99750,0:0:0:0:
279,325,99875,6,0,L|444:323,1,160
256,192,100500,12,0,101375,0:0:0:0:
197,197,101500,6,0,L|362:195,1,160
256,192,102125,12,0,103000,0:0:0:0:

View File

@ -74,6 +74,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
protected override HitWindows CreateHitWindows() => new OsuHitWindows();
public override HitWindows CreateHitWindows() => new OsuHitWindows();
}
}

View File

@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, IBindableBeatmap beatmap)
private void load(OsuConfigManager config, IBindable<WorkingBeatmap> beatmap)
{
InternalChild = expandTarget = new Container
{

View File

@ -39,10 +39,6 @@
<Project>{F167E17A-7DE6-4AF5-B920-A5112296C695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
<Project>{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}</Project>
<Name>osu.Game.Resources</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Import Project="..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets')" />

View File

@ -0,0 +1,25 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Taiko.Difficulty;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Taiko.Tests
{
public class TaikoDifficultyCalculatorTest : DifficultyCalculatorTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
[TestCase(2.9811336589467095, "diffcalc-test")]
[TestCase(2.9811336589467095, "diffcalc-test-strong")]
public void Test(double expected, string name)
=> base.Test(expected, name);
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap);
protected override Ruleset CreateRuleset() => new TaikoRuleset();
}
}

View File

@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
if (!calculateStrainValues(difficultyHitObjects, timeRate))
return new DifficultyAttributes(mods, 0);
return new TaikoDifficultyAttributes(mods, 0);
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;

View File

@ -0,0 +1,257 @@
osu file format v14
[General]
Mode: 1
[Difficulty]
CircleSize:4
OverallDifficulty:7
ApproachRate:8.3
SliderMultiplier:1.6
SliderTickRate:1
[TimingPoints]
500,500,4,2,1,50,1,0
62500,-500,4,2,1,50,0,0
71000,-100,4,2,1,50,0,0
[HitObjects]
// Same as diffcalc-test with finishers on every note
142,122,0,5,4,0:0:0:0:
142,122,125,1,4,0:0:0:0:
142,122,250,1,4,0:0:0:0:
142,122,375,1,4,0:0:0:0:
142,122,500,1,4,0:0:0:0:
142,122,625,1,4,0:0:0:0:
142,122,750,1,4,0:0:0:0:
142,122,875,1,4,0:0:0:0:
142,122,1000,1,4,0:0:0:0:
142,122,1125,1,4,0:0:0:0:
142,122,1250,1,4,0:0:0:0:
142,122,1375,1,4,0:0:0:0:
142,122,1500,1,4,0:0:0:0:
119,106,2500,1,6,0:0:0:0:
119,106,2625,1,6,0:0:0:0:
119,106,2750,1,6,0:0:0:0:
119,106,2875,1,6,0:0:0:0:
119,106,3000,1,6,0:0:0:0:
119,106,3125,1,6,0:0:0:0:
119,106,3250,1,6,0:0:0:0:
119,106,3375,1,6,0:0:0:0:
119,106,3500,1,6,0:0:0:0:
119,106,3625,1,6,0:0:0:0:
119,106,3750,1,6,0:0:0:0:
119,106,3875,1,6,0:0:0:0:
119,106,4000,1,6,0:0:0:0:
136,90,5000,1,4,0:0:0:0:
136,90,5125,1,6,0:0:0:0:
136,90,5250,1,4,0:0:0:0:
136,90,5375,1,6,0:0:0:0:
136,90,5500,1,4,0:0:0:0:
136,90,5625,1,6,0:0:0:0:
136,90,5750,1,4,0:0:0:0:
136,90,5875,1,6,0:0:0:0:
136,90,6000,1,4,0:0:0:0:
136,90,6125,1,6,0:0:0:0:
136,90,6250,1,4,0:0:0:0:
136,90,6375,1,6,0:0:0:0:
136,90,6500,1,4,0:0:0:0:
86,113,7500,1,4,0:0:0:0:
86,113,7625,1,4,0:0:0:0:
86,113,7750,1,6,0:0:0:0:
86,113,7875,1,6,0:0:0:0:
86,113,8000,1,4,0:0:0:0:
86,113,8125,1,4,0:0:0:0:
86,113,8250,1,6,0:0:0:0:
86,113,8375,1,6,0:0:0:0:
86,113,8500,1,4,0:0:0:0:
86,113,8625,1,4,0:0:0:0:
86,113,8750,1,6,0:0:0:0:
86,113,8875,1,6,0:0:0:0:
86,113,9000,1,4,0:0:0:0:
146,90,10000,1,4,0:0:0:0:
146,90,10125,1,4,0:0:0:0:
146,90,10250,1,4,0:0:0:0:
146,90,10375,1,6,0:0:0:0:
146,90,10500,1,6,0:0:0:0:
146,90,10625,1,6,0:0:0:0:
146,90,10750,1,4,0:0:0:0:
146,90,10875,1,4,0:0:0:0:
146,90,11000,1,4,0:0:0:0:
146,90,11125,1,6,0:0:0:0:
146,90,11250,1,6,0:0:0:0:
146,90,11375,1,6,0:0:0:0:
146,90,11500,1,4,0:0:0:0:
146,90,11625,1,4,0:0:0:0:
146,90,11750,1,4,0:0:0:0:
146,90,11875,1,6,0:0:0:0:
146,90,12000,1,6,0:0:0:0:
146,90,12125,1,6,0:0:0:0:
146,90,12250,1,4,0:0:0:0:
146,90,12375,1,4,0:0:0:0:
146,90,12500,1,4,0:0:0:0:
69,99,13500,1,4,0:0:0:0:
69,99,13625,1,4,0:0:0:0:
69,99,13750,1,4,0:0:0:0:
69,99,13875,1,6,0:0:0:0:
69,99,14000,1,4,0:0:0:0:
69,99,14125,1,4,0:0:0:0:
69,99,14250,1,4,0:0:0:0:
69,99,14375,1,6,0:0:0:0:
69,99,14500,1,4,0:0:0:0:
69,99,14625,1,4,0:0:0:0:
69,99,14750,1,4,0:0:0:0:
69,99,14875,1,6,0:0:0:0:
69,99,15000,1,4,0:0:0:0:
69,99,15125,1,4,0:0:0:0:
69,99,15250,1,4,0:0:0:0:
69,99,15375,1,6,0:0:0:0:
69,99,15500,1,4,0:0:0:0:
83,89,16500,1,4,0:0:0:0:
83,89,16625,1,6,0:0:0:0:
83,89,16750,1,6,0:0:0:0:
83,89,16875,1,4,0:0:0:0:
83,89,17000,1,4,0:0:0:0:
83,89,17125,1,4,0:0:0:0:
83,89,17250,1,6,0:0:0:0:
83,89,17375,1,6,0:0:0:0:
83,89,17500,1,6,0:0:0:0:
83,89,17625,1,6,0:0:0:0:
83,89,17750,1,4,0:0:0:0:
83,89,17875,1,4,0:0:0:0:
83,89,18000,1,4,0:0:0:0:
83,89,18125,1,4,0:0:0:0:
83,89,18250,1,4,0:0:0:0:
83,89,18375,1,6,0:0:0:0:
83,89,18500,1,6,0:0:0:0:
83,89,18625,1,6,0:0:0:0:
83,89,18750,1,6,0:0:0:0:
83,89,18875,1,4,0:0:0:0:
83,89,19000,1,4,0:0:0:0:
83,89,19125,1,4,0:0:0:0:
83,89,19250,1,4,0:0:0:0:
83,89,19375,1,6,0:0:0:0:
83,89,19500,1,6,0:0:0:0:
83,89,19625,1,4,0:0:0:0:
84,122,20500,1,4,0:0:0:0:
84,122,20625,2,4,L|217:123,1,120
84,122,21125,1,4,0:0:0:0:
84,122,21250,2,4,L|217:123,1,120
84,122,21750,1,4,0:0:0:0:
84,122,21875,2,4,L|217:123,1,120
84,122,22375,1,4,0:0:0:0:
84,122,22500,2,4,L|217:123,1,120
84,122,23000,1,4,0:0:0:0:
84,122,23125,2,4,L|217:123,1,120
99,106,24500,1,4,0:0:0:0:
99,106,24625,1,4,0:0:0:0:
99,106,24750,2,4,L|194:107,1,80
99,106,25125,1,4,0:0:0:0:
99,106,25250,1,4,0:0:0:0:
99,106,25375,2,4,L|194:107,1,80
99,106,25750,1,4,0:0:0:0:
99,106,25875,1,4,0:0:0:0:
99,106,26000,2,4,L|194:107,1,80
99,106,26375,1,4,0:0:0:0:
99,106,26500,1,4,0:0:0:0:
99,106,26625,2,4,L|194:107,1,80
99,106,27000,1,4,0:0:0:0:
99,106,27125,1,4,0:0:0:0:
99,106,27250,2,4,L|194:107,1,80
121,103,28500,1,4,0:0:0:0:
121,103,28625,1,4,0:0:0:0:
121,103,28750,1,4,0:0:0:0:
121,103,28875,2,4,L|190:103,1,40
121,103,29125,1,4,0:0:0:0:
121,103,29250,1,4,0:0:0:0:
121,103,29375,1,4,0:0:0:0:
121,103,29500,2,4,L|190:103,1,40
121,103,29750,1,4,0:0:0:0:
121,103,29875,1,4,0:0:0:0:
121,103,30000,1,4,0:0:0:0:
121,103,30125,2,4,L|190:103,1,40
121,103,30375,1,4,0:0:0:0:
121,103,30500,1,4,0:0:0:0:
121,103,30625,1,4,0:0:0:0:
121,103,30750,2,4,L|190:103,1,40
121,103,31000,1,4,0:0:0:0:
121,103,31125,1,4,0:0:0:0:
121,103,31250,1,4,0:0:0:0:
121,103,31375,2,4,L|190:103,1,40
121,103,32500,1,4,0:0:0:0:
121,103,32625,1,6,0:0:0:0:
121,103,32750,1,4,0:0:0:0:
121,103,32875,2,4,L|190:103,1,40
121,103,33125,1,4,0:0:0:0:
121,103,33250,1,6,0:0:0:0:
121,103,33375,1,4,0:0:0:0:
121,103,33500,2,4,L|190:103,1,40
121,103,33750,1,4,0:0:0:0:
121,103,33875,1,6,0:0:0:0:
121,103,34000,1,4,0:0:0:0:
121,103,34125,2,4,L|190:103,1,40
121,103,34375,1,4,0:0:0:0:
121,103,34500,1,6,0:0:0:0:
121,103,34625,1,4,0:0:0:0:
121,103,34750,2,4,L|190:103,1,40
121,103,35000,1,4,0:0:0:0:
121,103,35125,1,6,0:0:0:0:
121,103,35250,1,4,0:0:0:0:
121,103,35375,2,4,L|190:103,1,40
121,103,36500,1,4,0:0:0:0:
121,103,36625,1,4,0:0:0:0:
121,103,36750,1,6,0:0:0:0:
121,103,36875,2,4,L|190:103,1,40
121,103,37125,1,4,0:0:0:0:
121,103,37250,1,4,0:0:0:0:
121,103,37375,1,6,0:0:0:0:
121,103,37500,2,4,L|190:103,1,40
121,103,37750,1,4,0:0:0:0:
121,103,37875,1,4,0:0:0:0:
121,103,38000,1,6,0:0:0:0:
121,103,38125,2,4,L|190:103,1,40
121,103,38375,1,4,0:0:0:0:
121,103,38500,1,4,0:0:0:0:
121,103,38625,1,6,0:0:0:0:
121,103,38750,2,4,L|190:103,1,40
121,103,39000,1,4,0:0:0:0:
121,103,39125,1,4,0:0:0:0:
121,103,39250,1,6,0:0:0:0:
121,103,39375,2,4,L|190:103,1,40
107,106,40500,1,4,0:0:0:0:
107,106,40625,1,4,0:0:0:0:
107,106,40750,1,6,0:0:0:0:
107,106,40875,1,6,0:0:0:0:
46,112,41000,2,4,L|214:112,1,160
107,106,41625,1,4,0:0:0:0:
107,106,41750,1,4,0:0:0:0:
107,106,41875,1,6,0:0:0:0:
107,106,42000,1,6,0:0:0:0:
46,112,42125,2,4,L|214:112,1,160
107,106,42750,1,4,0:0:0:0:
107,106,42875,1,4,0:0:0:0:
107,106,43000,1,6,0:0:0:0:
107,106,43125,1,6,0:0:0:0:
46,112,43250,2,4,L|214:112,1,160
107,106,43875,1,4,0:0:0:0:
107,106,44000,1,4,0:0:0:0:
107,106,44125,1,6,0:0:0:0:
107,106,44250,1,6,0:0:0:0:
46,112,44375,2,4,L|214:112,1,160
107,106,45000,1,4,0:0:0:0:
107,106,45125,1,4,0:0:0:0:
107,106,45250,1,6,0:0:0:0:
107,106,45375,1,6,0:0:0:0:
46,112,45500,2,4,L|214:112,1,160
256,192,47000,12,4,47500,0:0:0:0:
256,192,47625,12,4,48000,0:0:0:0:
256,192,48125,12,4,48500,0:0:0:0:
256,192,48625,12,4,49000,0:0:0:0:
256,192,50000,12,4,50500,0:0:0:0:
183,143,50625,5,4,0:0:0:0:
256,192,50750,12,4,51250,0:0:0:0:
114,106,51375,5,4,0:0:0:0:
256,192,51625,12,4,52125,0:0:0:0:
154,143,52250,5,4,0:0:0:0:
256,192,52375,12,4,52875,0:0:0:0:
116,111,53000,5,4,0:0:0:0:

View File

@ -0,0 +1,285 @@
osu file format v14
[General]
Mode: 1
[Difficulty]
CircleSize:4
OverallDifficulty:7
ApproachRate:8.3
SliderMultiplier:1.6
SliderTickRate:1
[TimingPoints]
500,500,4,2,1,50,1,0
62500,-500,4,2,1,50,0,0
71000,-100,4,2,1,50,0,0
[HitObjects]
// dd, spaced 1/4 beat apart
142,122,0,5,0,0:0:0:0:
142,122,125,1,0,0:0:0:0:
142,122,250,1,0,0:0:0:0:
142,122,375,1,0,0:0:0:0:
142,122,500,1,0,0:0:0:0:
142,122,625,1,0,0:0:0:0:
142,122,750,1,0,0:0:0:0:
142,122,875,1,0,0:0:0:0:
142,122,1000,1,0,0:0:0:0:
142,122,1125,1,0,0:0:0:0:
142,122,1250,1,0,0:0:0:0:
142,122,1375,1,0,0:0:0:0:
142,122,1500,1,0,0:0:0:0:
// kk, spaced 1/4 beat apart
119,106,2500,1,2,0:0:0:0:
119,106,2625,1,2,0:0:0:0:
119,106,2750,1,2,0:0:0:0:
119,106,2875,1,2,0:0:0:0:
119,106,3000,1,2,0:0:0:0:
119,106,3125,1,2,0:0:0:0:
119,106,3250,1,2,0:0:0:0:
119,106,3375,1,2,0:0:0:0:
119,106,3500,1,2,0:0:0:0:
119,106,3625,1,2,0:0:0:0:
119,106,3750,1,2,0:0:0:0:
119,106,3875,1,2,0:0:0:0:
119,106,4000,1,2,0:0:0:0:
// dk, spaced 1/4 beat apart
136,90,5000,1,0,0:0:0:0:
136,90,5125,1,2,0:0:0:0:
136,90,5250,1,0,0:0:0:0:
136,90,5375,1,2,0:0:0:0:
136,90,5500,1,0,0:0:0:0:
136,90,5625,1,2,0:0:0:0:
136,90,5750,1,0,0:0:0:0:
136,90,5875,1,2,0:0:0:0:
136,90,6000,1,0,0:0:0:0:
136,90,6125,1,2,0:0:0:0:
136,90,6250,1,0,0:0:0:0:
136,90,6375,1,2,0:0:0:0:
136,90,6500,1,0,0:0:0:0:
// ddkk, spaced 1/4 beat apart
86,113,7500,1,0,0:0:0:0:
86,113,7625,1,0,0:0:0:0:
86,113,7750,1,2,0:0:0:0:
86,113,7875,1,2,0:0:0:0:
86,113,8000,1,0,0:0:0:0:
86,113,8125,1,0,0:0:0:0:
86,113,8250,1,2,0:0:0:0:
86,113,8375,1,2,0:0:0:0:
86,113,8500,1,0,0:0:0:0:
86,113,8625,1,0,0:0:0:0:
86,113,8750,1,2,0:0:0:0:
86,113,8875,1,2,0:0:0:0:
86,113,9000,1,0,0:0:0:0:
// dddkkk, spaced 1/4 beat apart
146,90,10000,1,0,0:0:0:0:
146,90,10125,1,0,0:0:0:0:
146,90,10250,1,0,0:0:0:0:
146,90,10375,1,2,0:0:0:0:
146,90,10500,1,2,0:0:0:0:
146,90,10625,1,2,0:0:0:0:
146,90,10750,1,0,0:0:0:0:
146,90,10875,1,0,0:0:0:0:
146,90,11000,1,0,0:0:0:0:
146,90,11125,1,2,0:0:0:0:
146,90,11250,1,2,0:0:0:0:
146,90,11375,1,2,0:0:0:0:
146,90,11500,1,0,0:0:0:0:
146,90,11625,1,0,0:0:0:0:
146,90,11750,1,0,0:0:0:0:
146,90,11875,1,2,0:0:0:0:
146,90,12000,1,2,0:0:0:0:
146,90,12125,1,2,0:0:0:0:
146,90,12250,1,0,0:0:0:0:
146,90,12375,1,0,0:0:0:0:
146,90,12500,1,0,0:0:0:0:
// dddk, spaced 1/4 beat apart
69,99,13500,1,0,0:0:0:0:
69,99,13625,1,0,0:0:0:0:
69,99,13750,1,0,0:0:0:0:
69,99,13875,1,2,0:0:0:0:
69,99,14000,1,0,0:0:0:0:
69,99,14125,1,0,0:0:0:0:
69,99,14250,1,0,0:0:0:0:
69,99,14375,1,2,0:0:0:0:
69,99,14500,1,0,0:0:0:0:
69,99,14625,1,0,0:0:0:0:
69,99,14750,1,0,0:0:0:0:
69,99,14875,1,2,0:0:0:0:
69,99,15000,1,0,0:0:0:0:
69,99,15125,1,0,0:0:0:0:
69,99,15250,1,0,0:0:0:0:
69,99,15375,1,2,0:0:0:0:
69,99,15500,1,0,0:0:0:0:
// arbitrary pattern, spaced 1/4 beat apart
83,89,16500,1,0,0:0:0:0:
83,89,16625,1,2,0:0:0:0:
83,89,16750,1,2,0:0:0:0:
83,89,16875,1,0,0:0:0:0:
83,89,17000,1,0,0:0:0:0:
83,89,17125,1,0,0:0:0:0:
83,89,17250,1,2,0:0:0:0:
83,89,17375,1,2,0:0:0:0:
83,89,17500,1,2,0:0:0:0:
83,89,17625,1,2,0:0:0:0:
83,89,17750,1,0,0:0:0:0:
83,89,17875,1,0,0:0:0:0:
83,89,18000,1,0,0:0:0:0:
83,89,18125,1,0,0:0:0:0:
83,89,18250,1,0,0:0:0:0:
83,89,18375,1,2,0:0:0:0:
83,89,18500,1,2,0:0:0:0:
83,89,18625,1,2,0:0:0:0:
83,89,18750,1,2,0:0:0:0:
83,89,18875,1,0,0:0:0:0:
83,89,19000,1,0,0:0:0:0:
83,89,19125,1,0,0:0:0:0:
83,89,19250,1,0,0:0:0:0:
83,89,19375,1,2,0:0:0:0:
83,89,19500,1,2,0:0:0:0:
83,89,19625,1,0,0:0:0:0:
// d-slider pattern, spaced 1/4 beat apart
84,122,20500,1,0,0:0:0:0:
84,122,20625,2,0,L|217:123,1,120
84,122,21125,1,0,0:0:0:0:
84,122,21250,2,0,L|217:123,1,120
84,122,21750,1,0,0:0:0:0:
84,122,21875,2,0,L|217:123,1,120
84,122,22375,1,0,0:0:0:0:
84,122,22500,2,0,L|217:123,1,120
84,122,23000,1,0,0:0:0:0:
84,122,23125,2,0,L|217:123,1,120
// dd-slider pattern, spaced 1/4 beat apart
99,106,24500,1,0,0:0:0:0:
99,106,24625,1,0,0:0:0:0:
99,106,24750,2,0,L|194:107,1,80
99,106,25125,1,0,0:0:0:0:
99,106,25250,1,0,0:0:0:0:
99,106,25375,2,0,L|194:107,1,80
99,106,25750,1,0,0:0:0:0:
99,106,25875,1,0,0:0:0:0:
99,106,26000,2,0,L|194:107,1,80
99,106,26375,1,0,0:0:0:0:
99,106,26500,1,0,0:0:0:0:
99,106,26625,2,0,L|194:107,1,80
99,106,27000,1,0,0:0:0:0:
99,106,27125,1,0,0:0:0:0:
99,106,27250,2,0,L|194:107,1,80
// ddd-slider pattern, spaced 1/4 beat apart
121,103,28500,1,0,0:0:0:0:
121,103,28625,1,0,0:0:0:0:
121,103,28750,1,0,0:0:0:0:
121,103,28875,2,0,L|190:103,1,40
121,103,29125,1,0,0:0:0:0:
121,103,29250,1,0,0:0:0:0:
121,103,29375,1,0,0:0:0:0:
121,103,29500,2,0,L|190:103,1,40
121,103,29750,1,0,0:0:0:0:
121,103,29875,1,0,0:0:0:0:
121,103,30000,1,0,0:0:0:0:
121,103,30125,2,0,L|190:103,1,40
121,103,30375,1,0,0:0:0:0:
121,103,30500,1,0,0:0:0:0:
121,103,30625,1,0,0:0:0:0:
121,103,30750,2,0,L|190:103,1,40
121,103,31000,1,0,0:0:0:0:
121,103,31125,1,0,0:0:0:0:
121,103,31250,1,0,0:0:0:0:
121,103,31375,2,0,L|190:103,1,40
// dkd-slider pattern, spaced 1/4 beat apart
121,103,32500,1,0,0:0:0:0:
121,103,32625,1,2,0:0:0:0:
121,103,32750,1,0,0:0:0:0:
121,103,32875,2,0,L|190:103,1,40
121,103,33125,1,0,0:0:0:0:
121,103,33250,1,2,0:0:0:0:
121,103,33375,1,0,0:0:0:0:
121,103,33500,2,0,L|190:103,1,40
121,103,33750,1,0,0:0:0:0:
121,103,33875,1,2,0:0:0:0:
121,103,34000,1,0,0:0:0:0:
121,103,34125,2,0,L|190:103,1,40
121,103,34375,1,0,0:0:0:0:
121,103,34500,1,2,0:0:0:0:
121,103,34625,1,0,0:0:0:0:
121,103,34750,2,0,L|190:103,1,40
121,103,35000,1,0,0:0:0:0:
121,103,35125,1,2,0:0:0:0:
121,103,35250,1,0,0:0:0:0:
121,103,35375,2,0,L|190:103,1,40
//ddk-slider pattern, spaced 1/4 beat apart
121,103,36500,1,0,0:0:0:0:
121,103,36625,1,0,0:0:0:0:
121,103,36750,1,2,0:0:0:0:
121,103,36875,2,0,L|190:103,1,40
121,103,37125,1,0,0:0:0:0:
121,103,37250,1,0,0:0:0:0:
121,103,37375,1,2,0:0:0:0:
121,103,37500,2,0,L|190:103,1,40
121,103,37750,1,0,0:0:0:0:
121,103,37875,1,0,0:0:0:0:
121,103,38000,1,2,0:0:0:0:
121,103,38125,2,0,L|190:103,1,40
121,103,38375,1,0,0:0:0:0:
121,103,38500,1,0,0:0:0:0:
121,103,38625,1,2,0:0:0:0:
121,103,38750,2,0,L|190:103,1,40
121,103,39000,1,0,0:0:0:0:
121,103,39125,1,0,0:0:0:0:
121,103,39250,1,2,0:0:0:0:
121,103,39375,2,0,L|190:103,1,40
//ddkk-slider pattern, spaced 1/4 beat apart
107,106,40500,1,0,0:0:0:0:
107,106,40625,1,0,0:0:0:0:
107,106,40750,1,2,0:0:0:0:
107,106,40875,1,2,0:0:0:0:
46,112,41000,2,0,L|214:112,1,160
107,106,41625,1,0,0:0:0:0:
107,106,41750,1,0,0:0:0:0:
107,106,41875,1,2,0:0:0:0:
107,106,42000,1,2,0:0:0:0:
46,112,42125,2,0,L|214:112,1,160
107,106,42750,1,0,0:0:0:0:
107,106,42875,1,0,0:0:0:0:
107,106,43000,1,2,0:0:0:0:
107,106,43125,1,2,0:0:0:0:
46,112,43250,2,0,L|214:112,1,160
107,106,43875,1,0,0:0:0:0:
107,106,44000,1,0,0:0:0:0:
107,106,44125,1,2,0:0:0:0:
107,106,44250,1,2,0:0:0:0:
46,112,44375,2,0,L|214:112,1,160
107,106,45000,1,0,0:0:0:0:
107,106,45125,1,0,0:0:0:0:
107,106,45250,1,2,0:0:0:0:
107,106,45375,1,2,0:0:0:0:
46,112,45500,2,0,L|214:112,1,160
// spinner-spinner pattern, spaced 1/4 beat apart
256,192,47000,12,0,47500,0:0:0:0:
256,192,47625,12,0,48000,0:0:0:0:
256,192,48125,12,0,48500,0:0:0:0:
256,192,48625,12,0,49000,0:0:0:0:
// spinner-d pattern, spaced 1/4 beat apart
256,192,50000,12,0,50500,0:0:0:0:
183,143,50625,5,0,0:0:0:0:
256,192,50750,12,0,51250,0:0:0:0:
114,106,51375,5,0,0:0:0:0:
256,192,51625,12,0,52125,0:0:0:0:
154,143,52250,5,0,0:0:0:0:
256,192,52375,12,0,52875,0:0:0:0:
116,111,53000,5,0,0:0:0:0:

View File

@ -67,6 +67,6 @@ namespace osu.Game.Rulesets.Taiko.Scoring
Health.Value = 0;
}
protected override HitWindows CreateHitWindows() => new TaikoHitWindows();
public override HitWindows CreateHitWindows() => new TaikoHitWindows();
}
}

View File

@ -51,10 +51,6 @@
<Project>{F167E17A-7DE6-4AF5-B920-A5112296C695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
<Project>{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}</Project>
<Name>osu.Game.Resources</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="DeepEqual" Version="2.0.0" />

View File

@ -27,7 +27,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
[Test]
public void TestDecodeBeatmapVersion()
{
using (var resStream = Resource.OpenResource("beatmap-version.osu"))
using (var resStream = TestResources.OpenResource("beatmap-version.osu"))
using (var stream = new StreamReader(resStream))
{
var decoder = Decoder.GetDecoder<Beatmap>(stream);
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapGeneral()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapEditor()
{
var decoder = new LegacyBeatmapDecoder();
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmapInfo = decoder.Decode(stream).BeatmapInfo;
@ -95,7 +95,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapMetadata()
{
var decoder = new LegacyBeatmapDecoder();
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@ -119,7 +119,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapDifficulty()
{
var decoder = new LegacyBeatmapDecoder();
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var difficulty = decoder.Decode(stream).BeatmapInfo.BaseDifficulty;
@ -137,7 +137,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapEvents()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@ -155,7 +155,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapTimingPoints()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@ -190,7 +190,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapColours()
{
var decoder = new LegacySkinDecoder();
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var comboColors = decoder.Decode(stream).ComboColours;
@ -215,7 +215,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapComboOffsetsOsu()
{
var decoder = new LegacyBeatmapDecoder();
using (var resStream = Resource.OpenResource("hitobject-combo-offset.osu"))
using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@ -237,7 +237,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapComboOffsetsCatch()
{
var decoder = new LegacyBeatmapDecoder();
using (var resStream = Resource.OpenResource("hitobject-combo-offset.osu"))
using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@ -259,7 +259,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapHitObjects()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
@ -286,7 +286,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeControlPointCustomSampleBank()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("controlpoint-custom-samplebank.osu"))
using (var resStream = TestResources.OpenResource("controlpoint-custom-samplebank.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
@ -307,7 +307,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeHitObjectCustomSampleBank()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("hitobject-custom-samplebank.osu"))
using (var resStream = TestResources.OpenResource("hitobject-custom-samplebank.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
@ -324,7 +324,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeHitObjectFileSamples()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("hitobject-file-samples.osu"))
using (var resStream = TestResources.OpenResource("hitobject-file-samples.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
@ -342,7 +342,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeSliderSamples()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("slider-samples.osu"))
using (var resStream = TestResources.OpenResource("slider-samples.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
@ -385,7 +385,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeHitObjectNullAdditionBank()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("hitobject-no-addition-bank.osu"))
using (var resStream = TestResources.OpenResource("hitobject-no-addition-bank.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;

View File

@ -19,7 +19,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeStoryboardEvents()
{
var decoder = new LegacyStoryboardDecoder();
using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
using (var resStream = TestResources.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
using (var stream = new StreamReader(resStream))
{
var storyboard = decoder.Decode(stream);
@ -91,7 +91,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeVariableWithSuffix()
{
var decoder = new LegacyStoryboardDecoder();
using (var resStream = Resource.OpenResource("variable-with-suffix.osb"))
using (var resStream = TestResources.OpenResource("variable-with-suffix.osb"))
using (var stream = new StreamReader(resStream))
{
var storyboard = decoder.Decode(stream);

View File

@ -146,7 +146,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
/// <returns>The <see cref="Beatmap"/> after being decoded by an <see cref="LegacyBeatmapDecoder"/>.</returns>
private Beatmap decode(string filename, out Beatmap jsonDecoded)
{
using (var stream = Resource.OpenResource(filename))
using (var stream = TestResources.OpenResource(filename))
using (var sr = new StreamReader(stream))
{
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);

View File

@ -12,6 +12,7 @@ using osu.Framework.Platform;
using osu.Game.IPC;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Tests.Resources;
using SharpCompress.Archives.Zip;
namespace osu.Game.Tests.Beatmaps.IO
@ -19,8 +20,6 @@ namespace osu.Game.Tests.Beatmaps.IO
[TestFixture]
public class ImportBeatmapTest
{
public const string TEST_OSZ_PATH = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
[Test]
public void TestImportWhenClosed()
{
@ -114,7 +113,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.AreEqual(0, fireCount -= 2);
var breakTemp = createTemporaryBeatmap();
var breakTemp = TestResources.GetTestBeatmapForImport();
MemoryStream brokenOsu = new MemoryStream(new byte[] { 1, 3, 3, 7 });
MemoryStream brokenOsz = new MemoryStream(File.ReadAllBytes(breakTemp));
@ -223,7 +222,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var osu = loadOsu(host);
var temp = createTemporaryBeatmap();
var temp = TestResources.GetTestBeatmapForImport();
var importer = new ArchiveImportIPCChannel(client);
if (!importer.ImportAsync(temp).Wait(10000))
@ -248,7 +247,7 @@ namespace osu.Game.Tests.Beatmaps.IO
try
{
var osu = loadOsu(host);
var temp = createTemporaryBeatmap();
var temp = TestResources.GetTestBeatmapForImport();
using (File.OpenRead(temp))
osu.Dependencies.Get<BeatmapManager>().Import(temp);
ensureLoaded(osu);
@ -262,17 +261,9 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
private static string createTemporaryBeatmap()
{
var temp = Path.GetTempFileName() + ".osz";
File.Copy(TEST_OSZ_PATH, temp, true);
Assert.IsTrue(File.Exists(temp));
return temp;
}
public static BeatmapSetInfo LoadOszIntoOsu(OsuGameBase osu, string path = null)
{
var temp = path ?? createTemporaryBeatmap();
var temp = path ?? TestResources.GetTestBeatmapForImport();
var manager = osu.Dependencies.Get<BeatmapManager>();

View File

@ -17,7 +17,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public void TestReadBeatmaps()
{
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
using (var osz = TestResources.GetTestBeatmapStream())
{
var reader = new ZipArchiveReader(osz);
string[] expected =
@ -44,7 +44,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public void TestReadMetadata()
{
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
using (var osz = TestResources.GetTestBeatmapStream())
{
var reader = new ZipArchiveReader(osz);
@ -72,7 +72,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public void TestReadFile()
{
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
using (var osz = TestResources.GetTestBeatmapStream())
{
var reader = new ZipArchiveReader(osz);
using (var stream = new StreamReader(

View File

@ -1,20 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.IO;
using System.Reflection;
namespace osu.Game.Tests.Resources
{
public static class Resource
{
public static Stream OpenResource(string name)
{
var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path));
return Assembly.GetExecutingAssembly().GetManifestResourceStream($@"osu.Game.Tests.Resources.{name}") ??
Assembly.LoadFrom(Path.Combine(localPath, @"osu.Game.Resources.dll")).GetManifestResourceStream($@"osu.Game.Resources.{name}");
}
}
}

View File

@ -0,0 +1,28 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.IO;
using NUnit.Framework;
using osu.Framework.IO.Stores;
namespace osu.Game.Tests.Resources
{
public static class TestResources
{
public static Stream OpenResource(string name) => new DllResourceStore("osu.Game.Tests.dll").GetStream($"Resources/{name}");
public static Stream GetTestBeatmapStream() => new DllResourceStore("osu.Game.Resources.dll").GetStream("Beatmaps/241526 Soleily - Renatus.osz");
public static string GetTestBeatmapForImport()
{
var temp = Path.GetTempFileName() + ".osz";
using (var stream = GetTestBeatmapStream())
using (var newFile = File.Create(temp))
stream.CopyTo(newFile);
Assert.IsTrue(File.Exists(temp));
return temp;
}
}
}

View File

@ -0,0 +1,2 @@
[General]
Name: test skin

View File

@ -0,0 +1,8 @@
[General]
Name: test skin
[Colours]
Combo1 : 142,199,255
Combo2 : 255,128,128
Combo3 : 128,255,255
Combo7 : 100,100,100,100

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -16,14 +15,13 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Tests.Resources;
using osu.Game.Users;
namespace osu.Game.Tests.Scores.IO
{
public class ImportScoreTest
{
public const string TEST_OSZ_PATH = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
[Test]
public void TestBasicImport()
{
@ -132,21 +130,13 @@ namespace osu.Game.Tests.Scores.IO
return scoreManager.GetAllUsableScores().First();
}
private string createTemporaryBeatmap()
{
var temp = Path.GetTempFileName() + ".osz";
File.Copy(TEST_OSZ_PATH, temp, true);
Assert.IsTrue(File.Exists(temp));
return temp;
}
private OsuGameBase loadOsu(GameHost host)
{
var osu = new OsuGameBase();
Task.Run(() => host.Run(osu));
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
var beatmapFile = createTemporaryBeatmap();
var beatmapFile = TestResources.GetTestBeatmapForImport();
var beatmapManager = osu.Dependencies.Get<BeatmapManager>();
beatmapManager.Import(beatmapFile);

View File

@ -0,0 +1,44 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
using osu.Game.Skinning;
using osu.Game.Tests.Resources;
using osuTK.Graphics;
namespace osu.Game.Tests.Skins
{
[TestFixture]
public class LegacySkinDecoderTest
{
[TestCase(true)]
[TestCase(false)]
public void TestDecodeSkinColours(bool hasColours)
{
var decoder = new LegacySkinDecoder();
using (var resStream = TestResources.OpenResource(hasColours ? "skin.ini" : "skin-empty.ini"))
using (var stream = new StreamReader(resStream))
{
var comboColors = decoder.Decode(stream).ComboColours;
List<Color4> expectedColors;
if (hasColours)
expectedColors = new List<Color4>
{
new Color4(142, 199, 255, 255),
new Color4(255, 128, 128, 255),
new Color4(128, 255, 255, 255),
new Color4(100, 100, 100, 100),
};
else
expectedColors = new DefaultSkin().Configuration.ComboColours;
Assert.AreEqual(expectedColors.Count, comboColors.Count);
for (int i = 0; i < expectedColors.Count; i++)
Assert.AreEqual(expectedColors[i], comboColors[i]);
}
}
}
}

View File

@ -2,27 +2,16 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Screens.Menu;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCaseDisclaimer : OsuTestCase
public class TestCaseDisclaimer : ScreenTestCase
{
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
new Disclaimer()
};
LoadScreen(new Disclaimer());
}
}
}

View File

@ -9,11 +9,11 @@ using osu.Game.Screens.Tournament.Teams;
namespace osu.Game.Tests.Visual
{
[Description("for tournament use")]
public class TestCaseDrawings : OsuTestCase
public class TestCaseDrawings : ScreenTestCase
{
public TestCaseDrawings()
{
Add(new Drawings
LoadScreen(new Drawings
{
TeamList = new TestTeamList(),
});

View File

@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual
}
[BackgroundDependencyLoader]
private void load(IAdjustableClock adjustableClock, IBindableBeatmap beatmap)
private void load(IAdjustableClock adjustableClock, IBindable<WorkingBeatmap> beatmap)
{
this.adjustableClock = adjustableClock;
this.beatmap.BindTo(beatmap);

View File

@ -4,6 +4,7 @@
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osuTK.Graphics;
@ -57,7 +58,7 @@ namespace osu.Game.Tests.Visual
public OsuLogo Logo;
private TestScreen screen;
public bool ScreenLoaded => screen.IsCurrentScreen;
public bool ScreenLoaded => screen.IsCurrentScreen();
public TestLoader(double delay)
{
@ -96,7 +97,7 @@ namespace osu.Game.Tests.Visual
{
public TestScreen()
{
Child = new Box
InternalChild = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.DarkSlateGray,
@ -107,7 +108,7 @@ namespace osu.Game.Tests.Visual
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
Child.FadeInFromZero(200);
InternalChild.FadeInFromZero(200);
}
}
}

View File

@ -16,7 +16,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCaseLoungeRoomsContainer : OsuTestCase
public class TestCaseLoungeRoomsContainer : MultiplayerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0));
AddStep("select first room", () => container.Rooms.First().Action?.Invoke());
AddAssert("first room selected", () => container.SelectedRoom.Value == roomManager.Rooms.First());
AddAssert("first room selected", () => Room == roomManager.Rooms.First());
AddStep("join first room", () => container.Rooms.First().Action?.Invoke());
AddAssert("first room joined", () => roomManager.Rooms.First().Status.Value is JoinedRoomStatus);
@ -71,7 +71,11 @@ namespace osu.Game.Tests.Visual
private class TestRoomManager : IRoomManager
{
public event Action RoomsUpdated;
public event Action RoomsUpdated
{
add { }
remove { }
}
public readonly BindableList<Room> Rooms = new BindableList<Room>();
IBindableList<Room> IRoomManager.Rooms => Rooms;
@ -85,10 +89,6 @@ namespace osu.Game.Tests.Visual
public void PartRoom()
{
}
public void Filter(FilterCriteria criteria)
{
}
}
private class JoinedRoomStatus : RoomStatus

View File

@ -12,7 +12,7 @@ using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
public class TestCaseMatchHeader : OsuTestCase
public class TestCaseMatchHeader : MultiplayerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -21,11 +21,7 @@ namespace osu.Game.Tests.Visual
public TestCaseMatchHeader()
{
var room = new Room();
var header = new Header(room);
room.Playlist.Add(new PlaylistItem
Room.Playlist.Add(new PlaylistItem
{
Beatmap = new BeatmapInfo
{
@ -46,9 +42,9 @@ namespace osu.Game.Tests.Visual
}
});
room.Type.Value = new GameTypeTimeshift();
Room.Type.Value = new GameTypeTimeshift();
Child = header;
Child = new Header();
}
}
}

View File

@ -14,7 +14,7 @@ using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseMatchInfo : OsuTestCase
public class TestCaseMatchInfo : MultiplayerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -27,18 +27,15 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
var room = new Room();
Add(new Info());
Info info = new Info(room);
Add(info);
AddStep(@"set name", () => room.Name.Value = @"Room Name?");
AddStep(@"set availability", () => room.Availability.Value = RoomAvailability.FriendsOnly);
AddStep(@"set status", () => room.Status.Value = new RoomStatusPlaying());
AddStep(@"set name", () => Room.Name.Value = @"Room Name?");
AddStep(@"set availability", () => Room.Availability.Value = RoomAvailability.FriendsOnly);
AddStep(@"set status", () => Room.Status.Value = new RoomStatusPlaying());
AddStep(@"set beatmap", () =>
{
room.Playlist.Clear();
room.Playlist.Add(new PlaylistItem
Room.Playlist.Clear();
Room.Playlist.Add(new PlaylistItem
{
Beatmap = new BeatmapInfo
{
@ -54,14 +51,14 @@ namespace osu.Game.Tests.Visual
});
});
AddStep(@"change name", () => room.Name.Value = @"Room Name!");
AddStep(@"change availability", () => room.Availability.Value = RoomAvailability.InviteOnly);
AddStep(@"change status", () => room.Status.Value = new RoomStatusOpen());
AddStep(@"null beatmap", () => room.Playlist.Clear());
AddStep(@"change name", () => Room.Name.Value = @"Room Name!");
AddStep(@"change availability", () => Room.Availability.Value = RoomAvailability.InviteOnly);
AddStep(@"change status", () => Room.Status.Value = new RoomStatusOpen());
AddStep(@"null beatmap", () => Room.Playlist.Clear());
AddStep(@"change beatmap", () =>
{
room.Playlist.Clear();
room.Playlist.Add(new PlaylistItem
Room.Playlist.Clear();
Room.Playlist.Add(new PlaylistItem
{
Beatmap = new BeatmapInfo
{

View File

@ -6,24 +6,24 @@ using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual
{
public class TestCaseMatchLeaderboard : OsuTestCase
public class TestCaseMatchLeaderboard : MultiplayerTestCase
{
public TestCaseMatchLeaderboard()
{
Room.RoomID.Value = 3;
Add(new MatchLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = MatchLeaderboardScope.Overall,
Room = new Room { RoomID = { Value = 3 } }
});
}

View File

@ -1,9 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Users;
@ -11,22 +9,14 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseMatchParticipants : OsuTestCase
public class TestCaseMatchParticipants : MultiplayerTestCase
{
private readonly Bindable<int?> maxParticipants = new Bindable<int?>();
private readonly Bindable<IEnumerable<User>> users = new Bindable<IEnumerable<User>>();
public TestCaseMatchParticipants()
{
Participants participants;
Add(new Participants { RelativeSizeAxes = Axes.Both });
Add(participants = new Participants { RelativeSizeAxes = Axes.Both });
participants.MaxParticipants.BindTo(maxParticipants);
participants.Users.BindTo(users);
AddStep(@"set max to null", () => maxParticipants.Value = null);
AddStep(@"set users", () => users.Value = new[]
AddStep(@"set max to null", () => Room.MaxParticipants.Value = null);
AddStep(@"set users", () => Room.Participants.Value = new[]
{
new User
{
@ -54,9 +44,9 @@ namespace osu.Game.Tests.Visual
},
});
AddStep(@"set max", () => maxParticipants.Value = 10);
AddStep(@"clear users", () => users.Value = new User[] { });
AddStep(@"set max to null", () => maxParticipants.Value = null);
AddStep(@"set max", () => Room.MaxParticipants.Value = 10);
AddStep(@"clear users", () => Room.Participants.Value = new User[] { });
AddStep(@"set max to null", () => Room.MaxParticipants.Value = null);
}
}
}

View File

@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Screens.Multi.Ranking;
@ -19,7 +18,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
public class TestCaseMatchResults : OsuTestCase
public class TestCaseMatchResults : MultiplayerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -38,68 +37,52 @@ namespace osu.Game.Tests.Visual
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
Child = new TestMatchResults(new ScoreInfo
Room.RoomID.Value = 1;
Room.Name.Value = "an awesome room";
LoadScreen(new TestMatchResults(new ScoreInfo
{
User = new User { Id = 10 },
});
}));
}
private class TestMatchResults : MatchResults
{
private readonly Room room;
public TestMatchResults(ScoreInfo score)
: this(score, new Room
{
RoomID = { Value = 1 },
Name = { Value = "an awesome room" }
})
: base(score)
{
}
public TestMatchResults(ScoreInfo score, Room room)
: base(score, room)
{
this.room = room;
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap, room) };
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value) };
}
private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo
{
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
private readonly Room room;
public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap, Room room)
: base(score, beatmap, room)
public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
this.score = score;
this.beatmap = beatmap;
this.room = room;
}
public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap, room);
public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap);
}
private class TestRoomLeaderboardPage : RoomLeaderboardPage
{
public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap, Room room)
: base(score, beatmap, room)
public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
}
protected override MatchLeaderboard CreateLeaderboard(Room room) => new TestMatchLeaderboard(room);
protected override MatchLeaderboard CreateLeaderboard() => new TestMatchLeaderboard();
}
private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard
{
public TestMatchLeaderboard(Room room)
: base(room)
{
}
protected override APIRequest FetchScores(Action<IEnumerable<APIRoomScoreInfo>> scoresCallback)
{
var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray();

View File

@ -13,12 +13,11 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi;
using osu.Game.Screens.Multi.Lounge.Components;
using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
public class TestCaseMatchSettingsOverlay : OsuTestCase
public class TestCaseMatchSettingsOverlay : MultiplayerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -28,14 +27,14 @@ namespace osu.Game.Tests.Visual
[Cached(Type = typeof(IRoomManager))]
private TestRoomManager roomManager = new TestRoomManager();
private Room room;
private TestRoomSettings settings;
[SetUp]
public void Setup() => Schedule(() =>
{
room = new Room();
settings = new TestRoomSettings(room)
Room = new Room();
settings = new TestRoomSettings
{
RelativeSizeAxes = Axes.Both,
State = Visibility.Visible
@ -49,19 +48,19 @@ namespace osu.Game.Tests.Visual
{
AddStep("clear name and beatmap", () =>
{
room.Name.Value = "";
room.Playlist.Clear();
Room.Name.Value = "";
Room.Playlist.Clear();
});
AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
AddStep("set name", () => room.Name.Value = "Room name");
AddStep("set name", () => Room.Name.Value = "Room name");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
AddStep("set beatmap", () => room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo }));
AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo }));
AddAssert("button enabled", () => settings.ApplyButton.Enabled);
AddStep("clear name", () => room.Name.Value = "");
AddStep("clear name", () => Room.Name.Value = "");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
}
@ -117,17 +116,12 @@ namespace osu.Game.Tests.Visual
private class TestRoomSettings : MatchSettingsOverlay
{
public new TriangleButton ApplyButton => base.ApplyButton;
public TriangleButton ApplyButton => Settings.ApplyButton;
public new OsuTextBox NameField => base.NameField;
public new OsuDropdown<TimeSpan> DurationField => base.DurationField;
public OsuTextBox NameField => Settings.NameField;
public OsuDropdown<TimeSpan> DurationField => Settings.DurationField;
public new OsuSpriteText ErrorText => base.ErrorText;
public TestRoomSettings(Room room)
: base(room)
{
}
public OsuSpriteText ErrorText => Settings.ErrorText;
}
private class TestRoomManager : IRoomManager
@ -136,7 +130,11 @@ namespace osu.Game.Tests.Visual
public Func<Room, bool> CreateRequested;
public event Action RoomsUpdated;
public event Action RoomsUpdated
{
add { }
remove { }
}
public IBindableList<Room> Rooms { get; } = null;
@ -154,8 +152,6 @@ namespace osu.Game.Tests.Visual
public void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) => throw new NotImplementedException();
public void PartRoom() => throw new NotImplementedException();
public void Filter(FilterCriteria criteria) => throw new NotImplementedException();
}
}
}

View File

@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Screens;
using osu.Game.Screens.Multi;
@ -15,15 +16,15 @@ namespace osu.Game.Tests.Visual
{
int index = 0;
OsuScreen currentScreen = new TestMultiplayerSubScreen(index);
ScreenStack screenStack = new ScreenStack(new TestMultiplayerSubScreen(index)) { RelativeSizeAxes = Axes.Both };
Children = new Drawable[]
{
currentScreen,
new Header(currentScreen)
screenStack,
new Header(screenStack)
};
AddStep("push multi screen", () => currentScreen.Push(currentScreen = new TestMultiplayerSubScreen(++index)));
AddStep("push multi screen", () => screenStack.CurrentScreen.Push(new TestMultiplayerSubScreen(++index)));
}
private class TestMultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Screens;
using osu.Game.Screens.Multi;
using osu.Game.Screens.Multi.Lounge;
using osu.Game.Screens.Multi.Lounge.Components;
@ -11,7 +12,7 @@ using osu.Game.Screens.Multi.Lounge.Components;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseMultiScreen : OsuTestCase
public class TestCaseMultiScreen : ScreenTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -24,7 +25,7 @@ namespace osu.Game.Tests.Visual
{
Multiplayer multi = new Multiplayer();
AddStep(@"show", () => Add(multi));
AddStep(@"show", () => LoadScreen(multi));
AddWaitStep(5);
AddStep(@"exit", multi.Exit);
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osuTK.Graphics;
@ -29,7 +30,10 @@ namespace osu.Game.Tests.Visual
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
new Loader()
new ScreenStack(new Loader())
{
RelativeSizeAxes = Axes.Both,
}
};
}
}

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Backgrounds;
@ -14,7 +16,10 @@ namespace osu.Game.Tests.Visual
Add(parallax = new ParallaxContainer
{
Child = new BackgroundScreenDefault { Alpha = 0.8f }
Child = new ScreenStack(new BackgroundScreenDefault { Alpha = 0.8f })
{
RelativeSizeAxes = Axes.Both,
}
});
AddStep("default parallax", () => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT);

View File

@ -12,6 +12,7 @@ using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.MathUtils;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
@ -25,7 +26,7 @@ using osu.Game.Screens.Select.Filter;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCasePlaySongSelect : OsuTestCase
public class TestCasePlaySongSelect : ScreenTestCase
{
private BeatmapManager manager;
@ -102,21 +103,16 @@ namespace osu.Game.Tests.Visual
}
[SetUp]
public virtual void SetUp()
{
Schedule(() =>
{
manager?.Delete(manager.GetAllUsableBeatmapSets());
Child = songSelect = new TestSongSelect();
});
}
public virtual void SetUp() =>
Schedule(() => { manager?.Delete(manager.GetAllUsableBeatmapSets()); });
[Test]
public void TestDummy()
{
createSongSelect();
AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap);
AddAssert("dummy shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap);
AddUntilStep(() => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap, "dummy shown on wedge");
addManyTestMaps();
AddWaitStep(3);
@ -127,6 +123,7 @@ namespace osu.Game.Tests.Visual
[Test]
public void TestSorting()
{
createSongSelect();
addManyTestMaps();
AddWaitStep(3);
@ -142,6 +139,7 @@ namespace osu.Game.Tests.Visual
[Ignore("needs fixing")]
public void TestImportUnderDifferentRuleset()
{
createSongSelect();
changeRuleset(2);
importForRuleset(0);
AddUntilStep(() => songSelect.Carousel.SelectedBeatmap == null, "no selection");
@ -150,6 +148,7 @@ namespace osu.Game.Tests.Visual
[Test]
public void TestImportUnderCurrentRuleset()
{
createSongSelect();
changeRuleset(2);
importForRuleset(2);
importForRuleset(1);
@ -165,6 +164,7 @@ namespace osu.Game.Tests.Visual
[Test]
public void TestRulesetChangeResetsMods()
{
createSongSelect();
changeRuleset(0);
changeMods(new OsuModHardRock());
@ -194,6 +194,7 @@ namespace osu.Game.Tests.Visual
[Test]
public void TestStartAfterUnMatchingFilterDoesNotStart()
{
createSongSelect();
addManyTestMaps();
AddUntilStep(() => songSelect.Carousel.SelectedBeatmap != null, "has selection");
@ -221,6 +222,12 @@ namespace osu.Game.Tests.Visual
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));
private void createSongSelect()
{
AddStep("create song select", () => LoadScreen(songSelect = new TestSongSelect()));
AddUntilStep(() => songSelect.IsCurrentScreen(), "wait for present");
}
private void addManyTestMaps()
{
AddStep("import test maps", () =>

View File

@ -3,6 +3,8 @@
using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
@ -11,28 +13,37 @@ namespace osu.Game.Tests.Visual
public class TestCasePlayerLoader : ManualInputManagerTestCase
{
private PlayerLoader loader;
private ScreenStack stack;
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
Beatmap.Value = new DummyWorkingBeatmap(game);
AddStep("load dummy beatmap", () => Add(loader = new PlayerLoader(() => new Player
InputManager.Add(stack = new ScreenStack { RelativeSizeAxes = Axes.Both });
AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player
{
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
})));
AddUntilStep(() => loader.IsCurrentScreen(), "wait for current");
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current");
AddUntilStep(() => !loader.IsCurrentScreen(), "wait for no longer current");
AddStep("exit loader", () => loader.Exit());
AddUntilStep(() => !loader.IsAlive, "wait for no longer alive");
AddStep("load slow dummy beatmap", () =>
{
SlowLoadPlayer slow = null;
Add(loader = new PlayerLoader(() => slow = new SlowLoadPlayer
stack.Push(loader = new PlayerLoader(() => slow = new SlowLoadPlayer
{
AllowPause = false,
AllowLeadIn = false,
@ -42,7 +53,7 @@ namespace osu.Game.Tests.Visual
Scheduler.AddDelayed(() => slow.Ready = true, 5000);
});
AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current");
AddUntilStep(() => !loader.IsCurrentScreen(), "wait for no longer current");
}
protected class SlowLoadPlayer : Player

View File

@ -16,7 +16,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseResults : OsuTestCase
public class TestCaseResults : ScreenTestCase
{
private BeatmapManager beatmaps;
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
Add(new SoloResults(new ScoreInfo
LoadScreen(new SoloResults(new ScoreInfo
{
TotalScore = 2845370,
Accuracy = 0.98,

View File

@ -19,16 +19,18 @@ namespace osu.Game.Tests.Visual
public class TestCaseScreenBreadcrumbControl : OsuTestCase
{
private readonly ScreenBreadcrumbControl breadcrumbs;
private Screen currentScreen, changedScreen;
private readonly ScreenStack screenStack;
public TestCaseScreenBreadcrumbControl()
{
TestScreen startScreen;
OsuSpriteText titleText;
IScreen startScreen = new TestScreenOne();
screenStack = new ScreenStack(startScreen) { RelativeSizeAxes = Axes.Both };
Children = new Drawable[]
{
currentScreen = startScreen = new TestScreenOne(),
screenStack,
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
@ -37,7 +39,7 @@ namespace osu.Game.Tests.Visual
Spacing = new Vector2(10),
Children = new Drawable[]
{
breadcrumbs = new ScreenBreadcrumbControl(startScreen)
breadcrumbs = new ScreenBreadcrumbControl(screenStack)
{
RelativeSizeAxes = Axes.X,
},
@ -46,12 +48,7 @@ namespace osu.Game.Tests.Visual
},
};
breadcrumbs.Current.ValueChanged += s =>
{
titleText.Text = $"Changed to {s.ToString()}";
changedScreen = s;
};
breadcrumbs.Current.ValueChanged += s => titleText.Text = $"Changed to {s.ToString()}";
breadcrumbs.Current.TriggerChange();
waitForCurrent();
@ -60,18 +57,14 @@ namespace osu.Game.Tests.Visual
pushNext();
waitForCurrent();
AddStep(@"make start current", () =>
{
startScreen.MakeCurrent();
currentScreen = startScreen;
});
AddStep(@"make start current", () => startScreen.MakeCurrent());
waitForCurrent();
pushNext();
waitForCurrent();
AddAssert(@"only 2 items", () => breadcrumbs.Items.Count() == 2);
AddStep(@"exit current", () => changedScreen.Exit());
AddAssert(@"current screen is first", () => startScreen == changedScreen);
AddStep(@"exit current", () => screenStack.CurrentScreen.Exit());
AddAssert(@"current screen is first", () => startScreen == screenStack.CurrentScreen);
}
[BackgroundDependencyLoader]
@ -80,8 +73,8 @@ namespace osu.Game.Tests.Visual
breadcrumbs.StripColour = colours.Blue;
}
private void pushNext() => AddStep(@"push next screen", () => currentScreen = ((TestScreen)currentScreen).PushNext());
private void waitForCurrent() => AddUntilStep(() => currentScreen.IsCurrentScreen, "current screen");
private void pushNext() => AddStep(@"push next screen", () => ((TestScreen)screenStack.CurrentScreen).PushNext());
private void waitForCurrent() => AddUntilStep(() => screenStack.CurrentScreen.IsCurrentScreen(), "current screen");
private abstract class TestScreen : OsuScreen
{
@ -91,14 +84,14 @@ namespace osu.Game.Tests.Visual
public TestScreen PushNext()
{
TestScreen screen = CreateNextScreen();
Push(screen);
this.Push(screen);
return screen;
}
protected TestScreen()
{
Child = new FillFlowContainer
InternalChild = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -8,7 +8,7 @@ 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;
using osu.Game.Tests.Resources;
namespace osu.Game.Tests
{
@ -18,12 +18,12 @@ namespace osu.Game.Tests
public class WaveformTestBeatmap : WorkingBeatmap
{
private readonly ZipArchiveReader reader;
private readonly FileStream stream;
private readonly Stream stream;
public WaveformTestBeatmap()
: base(new BeatmapInfo())
{
stream = File.OpenRead(ImportBeatmapTest.TEST_OSZ_PATH);
stream = TestResources.GetTestBeatmapStream();
reader = new ZipArchiveReader(stream);
}

View File

@ -12,9 +12,9 @@ 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"/>.
/// This should be used sparingly in-favour of <see cref="IBindable<WorkingBeatmap>"/>.
/// </summary>
public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap>, IBindableBeatmap
public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap>
{
private AudioManager audioManager;
private WorkingBeatmap lastBeatmap;
@ -62,9 +62,6 @@ namespace osu.Game.Beatmaps
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.

View File

@ -1,112 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
namespace osu.Game.Beatmaps.Drawables
{
/// <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>
/// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded.
/// </summary>
public readonly Bindable<DownloadStatus> DownloadState = new Bindable<DownloadStatus>();
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;
beatmaps.BeatmapDownloadBegan += downloadBegan;
beatmaps.BeatmapDownloadFailed += downloadFailed;
// initial value
if (set.OnlineBeatmapSetID != null && beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any())
DownloadState.Value = DownloadStatus.Downloaded;
else if (beatmaps.GetExistingDownload(set) != null)
DownloadState.Value = DownloadStatus.Downloading;
else
DownloadState.Value = DownloadStatus.NotDownloaded;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (beatmaps != null)
{
beatmaps.ItemAdded -= setAdded;
beatmaps.ItemRemoved -= setRemoved;
beatmaps.BeatmapDownloadBegan -= downloadBegan;
beatmaps.BeatmapDownloadFailed -= downloadFailed;
}
}
/// <summary>
/// Begin downloading the associated beatmap set.
/// </summary>
/// <returns>True if downloading began. False if an existing download is active or completed.</returns>
public void Download()
{
if (DownloadState.Value > DownloadStatus.NotDownloaded)
return;
if (beatmaps.Download(set, noVideo))
{
// Only change state if download can happen
DownloadState.Value = DownloadStatus.Downloading;
}
}
private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => Schedule(() =>
{
if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
DownloadState.Value = DownloadStatus.Downloaded;
});
private void setRemoved(BeatmapSetInfo s) => Schedule(() =>
{
if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
DownloadState.Value = DownloadStatus.NotDownloaded;
});
private void downloadBegan(DownloadBeatmapSetRequest d)
{
if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
DownloadState.Value = DownloadStatus.Downloading;
}
private void downloadFailed(DownloadBeatmapSetRequest d)
{
if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
DownloadState.Value = DownloadStatus.NotDownloaded;
}
public enum DownloadStatus
{
NotDownloaded,
Downloading,
Downloaded,
}
}
}

View File

@ -13,7 +13,7 @@ namespace osu.Game.Beatmaps.Drawables
/// </summary>
public class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable<BeatmapInfo>
{
public readonly IBindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
[Resolved]
private BeatmapManager beatmaps { get; set; }

View File

@ -1,19 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Configuration;
namespace osu.Game.Beatmaps
{
/// <summary>
/// Read-only interface for the <see cref="OsuGame"/> beatmap.
/// </summary>
public interface IBindableBeatmap : IBindable<WorkingBeatmap>
{
/// <summary>
/// Retrieve a new <see cref="IBindableBeatmap"/> instance weakly bound to this <see cref="IBindableBeatmap"/>.
/// If you are further binding to events of the retrieved <see cref="IBindableBeatmap"/>, ensure a local reference is held.
/// </summary>
IBindableBeatmap GetBoundCopy();
}
}

View File

@ -74,7 +74,7 @@ namespace osu.Game.Graphics.Containers
}
[BackgroundDependencyLoader]
private void load(IBindableBeatmap beatmap)
private void load(IBindable<WorkingBeatmap> beatmap)
{
Beatmap.BindTo(beatmap);
}

View File

@ -50,6 +50,7 @@ namespace osu.Game.Graphics.Cursor
if (!CanShowCursor)
{
currentTarget?.Cursor?.Hide();
currentTarget = null;
return;
}

View File

@ -9,12 +9,13 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Platform;
namespace osu.Game.Graphics.UserInterface
{
public class OsuPasswordTextBox : OsuTextBox
public class OsuPasswordTextBox : OsuTextBox, ISuppressKeyEventLogging
{
protected override Drawable GetDrawableCharacter(char c) => new PasswordMaskChar(CalculatedTextSize);

View File

@ -10,45 +10,30 @@ namespace osu.Game.Graphics.UserInterface
/// <summary>
/// A <see cref="BreadcrumbControl"/> which follows the active screen (and allows navigation) in a <see cref="Screen"/> stack.
/// </summary>
public class ScreenBreadcrumbControl : BreadcrumbControl<Screen>
public class ScreenBreadcrumbControl : BreadcrumbControl<IScreen>
{
private Screen last;
public ScreenBreadcrumbControl(Screen initialScreen)
public ScreenBreadcrumbControl(ScreenStack stack)
{
Current.ValueChanged += newScreen =>
{
if (last != newScreen && !newScreen.IsCurrentScreen)
newScreen.MakeCurrent();
};
stack.ScreenPushed += onPushed;
stack.ScreenExited += onExited;
onPushed(initialScreen);
onPushed(null, stack.CurrentScreen);
Current.ValueChanged += newScreen => newScreen.MakeCurrent();
}
private void screenChanged(Screen newScreen)
private void onPushed(IScreen lastScreen, IScreen newScreen)
{
if (newScreen == null) return;
if (last != null)
{
last.Exited -= screenChanged;
last.ModePushed -= onPushed;
}
last = newScreen;
newScreen.Exited += screenChanged;
newScreen.ModePushed += onPushed;
AddItem(newScreen);
Current.Value = newScreen;
}
private void onPushed(Screen screen)
private void onExited(IScreen lastScreen, IScreen newScreen)
{
Items.ToList().SkipWhile(i => i != Current.Value).Skip(1).ForEach(RemoveItem);
AddItem(screen);
if (newScreen != null)
Current.Value = newScreen;
screenChanged(screen);
Items.ToList().SkipWhile(s => s != Current.Value).Skip(1).ForEach(RemoveItem);
}
}
}

View File

@ -355,11 +355,7 @@ namespace osu.Game.Online.API
State = APIState.Offline;
}
private static User createGuestUser() => new User
{
Username = @"Guest",
Id = 1,
};
private static User createGuestUser() => new GuestUser();
protected override void Dispose(bool isDisposing)
{
@ -370,6 +366,15 @@ namespace osu.Game.Online.API
}
}
internal class GuestUser : User
{
public GuestUser()
{
Username = @"Guest";
Id = 1;
}
}
public enum APIState
{
/// <summary>

View File

@ -17,7 +17,7 @@ namespace osu.Game.Online.API
return request;
}
private void request_Progress(long current, long total) => API.Schedule(() => Progress?.Invoke(current, total));
private void request_Progress(long current, long total) => API.Schedule(() => Progressed?.Invoke(current, total));
protected APIDownloadRequest()
{
@ -29,7 +29,7 @@ namespace osu.Game.Online.API
Success?.Invoke(filename);
}
public event APIProgressHandler Progress;
public event APIProgressHandler Progressed;
public new event APISuccessHandler<string> Success;
}

View File

@ -10,7 +10,9 @@ namespace osu.Game.Online.API.Requests
{
public readonly BeatmapSetInfo BeatmapSet;
public Action<float> DownloadProgressed;
public float Progress;
public event Action<float> DownloadProgressed;
private readonly bool noVideo;
@ -19,7 +21,7 @@ namespace osu.Game.Online.API.Requests
this.noVideo = noVideo;
BeatmapSet = set;
Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total);
Progressed += (current, total) => DownloadProgressed?.Invoke(Progress = (float)current / total);
}
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";

View File

@ -1,9 +1,9 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Configuration;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
@ -37,10 +37,10 @@ namespace osu.Game.Online.Multiplayer
public RulesetInfo Ruleset { get; set; }
[JsonIgnore]
public readonly BindableList<Mod> AllowedMods = new BindableList<Mod>();
public readonly List<Mod> AllowedMods = new List<Mod>();
[JsonIgnore]
public readonly BindableList<Mod> RequiredMods = new BindableList<Mod>();
public readonly List<Mod> RequiredMods = new List<Mod>();
[JsonProperty("beatmap")]
private APIBeatmap apiBeatmap { get; set; }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Game.Online.Multiplayer.GameTypes;
using osu.Game.Online.Multiplayer.RoomStatuses;
@ -14,44 +15,73 @@ namespace osu.Game.Online.Multiplayer
{
public class Room
{
[Cached]
[JsonProperty("id")]
public Bindable<int?> RoomID { get; private set; } = new Bindable<int?>();
[Cached]
[JsonProperty("name")]
public Bindable<string> Name { get; private set; } = new Bindable<string>();
[Cached]
[JsonProperty("host")]
public Bindable<User> Host { get; private set; } = new Bindable<User>();
[Cached]
[JsonProperty("playlist")]
public BindableList<PlaylistItem> Playlist { get; set; } = new BindableList<PlaylistItem>();
public BindableList<PlaylistItem> Playlist { get; private set; } = new BindableList<PlaylistItem>();
[Cached]
[JsonIgnore]
public Bindable<PlaylistItem> CurrentItem { get; private set; } = new Bindable<PlaylistItem>();
[Cached]
[JsonProperty("channel_id")]
public Bindable<int> ChannelId { get; private set; } = new Bindable<int>();
[Cached]
[JsonIgnore]
public Bindable<TimeSpan> Duration { get; private set; } = new Bindable<TimeSpan>(TimeSpan.FromMinutes(30));
[Cached]
[JsonIgnore]
public Bindable<int?> MaxAttempts { get; private set; } = new Bindable<int?>();
[Cached]
[JsonIgnore]
public Bindable<RoomStatus> Status { get; private set; } = new Bindable<RoomStatus>(new RoomStatusOpen());
[Cached]
[JsonIgnore]
public Bindable<RoomAvailability> Availability { get; private set; } = new Bindable<RoomAvailability>();
[Cached]
[JsonIgnore]
public Bindable<GameType> Type { get; private set; } = new Bindable<GameType>(new GameTypeTimeshift());
[Cached]
[JsonIgnore]
public Bindable<int?> MaxParticipants { get; private set; } = new Bindable<int?>();
[Cached]
[JsonIgnore]
public Bindable<IEnumerable<User>> Participants { get; private set; } = new Bindable<IEnumerable<User>>(Enumerable.Empty<User>());
[Cached]
public Bindable<int> ParticipantCount { get; private set; } = new Bindable<int>();
public Room()
{
Playlist.ItemsAdded += updateCurrent;
Playlist.ItemsRemoved += updateCurrent;
updateCurrent(Playlist);
}
private void updateCurrent(IEnumerable<PlaylistItem> playlist)
{
CurrentItem.Value = playlist.FirstOrDefault();
}
// todo: TEMPORARY
[JsonProperty("participant_count")]
private int? participantCount
@ -68,6 +98,7 @@ namespace osu.Game.Online.Multiplayer
}
// Only supports retrieval for now
[Cached]
[JsonProperty("ends_at")]
public Bindable<DateTimeOffset> EndDate { get; private set; } = new Bindable<DateTimeOffset>();
@ -83,7 +114,7 @@ namespace osu.Game.Online.Multiplayer
/// The position of this <see cref="Room"/> in the list. This is not read from or written to the API.
/// </summary>
[JsonIgnore]
public int Position = -1;
public Bindable<int> Position { get; private set; } = new Bindable<int>(-1);
public void CopyFrom(Room other)
{

View File

@ -79,27 +79,23 @@ namespace osu.Game
public virtual Storage GetStorageForStableInstall() => null;
private Intro intro
{
get
{
Screen screen = screenStack;
while (screen != null && !(screen is Intro))
screen = screen.ChildScreen;
return screen as Intro;
}
}
public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight;
private IdleTracker idleTracker;
public readonly Bindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>();
private OsuScreen screenStack;
private BackgroundScreenStack backgroundStack;
private ParallaxContainer backgroundParallax;
private ScreenStack screenStack;
private VolumeOverlay volume;
private OnScreenDisplay onscreenDisplay;
private OsuLogo osuLogo;
private MainMenu menuScreen;
private Intro introScreen;
private Bindable<int> configRuleset;
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
@ -173,6 +169,8 @@ namespace osu.Game
dependencies.CacheAs(ruleset);
dependencies.CacheAs<IBindable<RulesetInfo>>(ruleset);
dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 });
// bind config int to database RulesetInfo
configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset);
ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First();
@ -211,6 +209,12 @@ namespace osu.Game
/// <param name="beatmap">The beatmap to select.</param>
public void PresentBeatmap(BeatmapSetInfo beatmap)
{
if (menuScreen == null)
{
Schedule(() => PresentBeatmap(beatmap));
return;
}
CloseAllOverlays(false);
void setBeatmap()
@ -233,16 +237,15 @@ namespace osu.Game
}
}
switch (currentScreen)
switch (screenStack.CurrentScreen)
{
case SongSelect _:
break;
default:
// navigate to song select if we are not already there.
var menu = (MainMenu)intro.ChildScreen;
menu.MakeCurrent();
menu.LoadToSolo();
menuScreen.MakeCurrent();
menuScreen.LoadToSolo();
break;
}
@ -268,9 +271,7 @@ namespace osu.Game
scoreLoad?.Cancel();
var menu = intro.ChildScreen;
if (menu == null)
if (menuScreen == null)
{
scoreLoad = Schedule(() => LoadScore(score, false));
return;
@ -291,7 +292,7 @@ namespace osu.Game
return;
}
if (!currentScreen.AllowExternalScreenChange)
if ((screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false)
{
notifications.Post(new SimpleNotification
{
@ -310,9 +311,9 @@ namespace osu.Game
void loadScore()
{
if (!menu.IsCurrentScreen)
if (!menuScreen.IsCurrentScreen() || Beatmap.Disabled)
{
menu.MakeCurrent();
menuScreen.MakeCurrent();
this.Delay(500).Schedule(loadScore, out scoreLoad);
return;
}
@ -322,7 +323,7 @@ namespace osu.Game
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
Beatmap.Value.Mods.Value = databasedScoreInfo.Mods;
currentScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore)));
menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore)));
}
}
@ -339,7 +340,7 @@ namespace osu.Game
// The next time this is updated is in UpdateAfterChildren, which occurs too late and results
// in the cursor being shown for a few frames during the intro.
// This prevents the cursor from showing until we have a screen with CursorVisible = true
MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
MenuCursorContainer.CanShowCursor = menuScreen?.CursorVisible ?? false;
// todo: all archive managers should be able to be looped here.
SkinManager.PostNotification = n => notifications?.Post(n);
@ -350,6 +351,8 @@ namespace osu.Game
BeatmapManager.PresentBeatmap = PresentBeatmap;
Container logoContainer;
AddRange(new Drawable[]
{
new VolumeControlReceptor
@ -361,6 +364,16 @@ namespace osu.Game
screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays)
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
backgroundParallax = new ParallaxContainer
{
RelativeSizeAxes = Axes.Both,
Child = backgroundStack = new BackgroundScreenStack { RelativeSizeAxes = Axes.Both },
},
screenStack = new ScreenStack { RelativeSizeAxes = Axes.Both },
logoContainer = new Container { RelativeSizeAxes = Axes.Both },
}
},
overlayContent = new Container
{
@ -370,12 +383,17 @@ namespace osu.Game
idleTracker = new GameIdleTracker(6000)
});
loadComponentSingleFile(screenStack = new Loader(), d =>
dependencies.Cache(backgroundStack);
screenStack.ScreenPushed += screenPushed;
screenStack.ScreenExited += screenExited;
loadComponentSingleFile(osuLogo, logoContainer.Add);
loadComponentSingleFile(new Loader
{
screenStack.ModePushed += screenAdded;
screenStack.Exited += screenRemoved;
screenContainer.Add(screenStack);
});
RelativeSizeAxes = Axes.Both
}, screenStack.Push);
loadComponentSingleFile(Toolbar = new Toolbar
{
@ -383,7 +401,7 @@ namespace osu.Game
OnHome = delegate
{
CloseAllOverlays(false);
intro?.ChildScreen?.MakeCurrent();
menuScreen?.MakeCurrent();
},
}, floatingOverlayContent.Add);
@ -617,7 +635,7 @@ namespace osu.Game
public bool OnPressed(GlobalAction action)
{
if (intro == null) return false;
if (introScreen == null) return false;
switch (action)
{
@ -674,19 +692,20 @@ namespace osu.Game
private Container floatingOverlayContent;
private OsuScreen currentScreen;
private FrameworkConfigManager frameworkConfig;
private ScalingContainer screenContainer;
protected override bool OnExiting()
{
if (screenStack.ChildScreen == null) return false;
if (screenStack.CurrentScreen is Loader)
return false;
if (intro == null) return true;
if (introScreen == null)
return true;
if (!intro.DidLoadMenu || intro.ChildScreen != null)
if (!introScreen.DidLoadMenu || !(screenStack.CurrentScreen is Intro))
{
Scheduler.Add(intro.MakeCurrent);
Scheduler.Add(introScreen.MakeCurrent);
return true;
}
@ -709,63 +728,47 @@ namespace osu.Game
{
base.UpdateAfterChildren();
// we only want to apply these restrictions when we are inside a screen stack.
// the use case for not applying is in visual/unit tests.
bool applyBeatmapRulesetRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false;
ruleset.Disabled = applyBeatmapRulesetRestrictions;
Beatmap.Disabled = applyBeatmapRulesetRestrictions;
screenContainer.Padding = new MarginPadding { Top = ToolbarOffset };
overlayContent.Padding = new MarginPadding { Top = ToolbarOffset };
MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
MenuCursorContainer.CanShowCursor = (screenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
}
/// <summary>
/// Sets <see cref="Beatmap"/> while ignoring any beatmap.
/// </summary>
/// <param name="beatmap">The beatmap to set.</param>
public void ForcefullySetBeatmap(WorkingBeatmap beatmap)
protected virtual void ScreenChanged(IScreen current, IScreen newScreen)
{
var beatmapDisabled = Beatmap.Disabled;
switch (newScreen)
{
case Intro intro:
introScreen = intro;
break;
case MainMenu menu:
menuScreen = menu;
break;
}
Beatmap.Disabled = false;
Beatmap.Value = beatmap;
Beatmap.Disabled = beatmapDisabled;
if (newScreen is IOsuScreen newOsuScreen)
{
backgroundParallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * newOsuScreen.BackgroundParallaxAmount;
OverlayActivationMode.Value = newOsuScreen.InitialOverlayActivationMode;
if (newOsuScreen.HideOverlaysOnEnter)
CloseAllOverlays();
else
Toolbar.State = Visibility.Visible;
}
}
/// <summary>
/// Sets <see cref="Ruleset"/> while ignoring any ruleset restrictions.
/// </summary>
/// <param name="beatmap">The beatmap to set.</param>
public void ForcefullySetRuleset(RulesetInfo ruleset)
private void screenPushed(IScreen lastScreen, IScreen newScreen)
{
var rulesetDisabled = this.ruleset.Disabled;
this.ruleset.Disabled = false;
this.ruleset.Value = ruleset;
this.ruleset.Disabled = rulesetDisabled;
}
protected virtual void ScreenChanged(OsuScreen current, Screen newScreen)
{
currentScreen = (OsuScreen)newScreen;
}
private void screenAdded(Screen newScreen)
{
ScreenChanged(currentScreen, newScreen);
ScreenChanged(lastScreen, newScreen);
Logger.Log($"Screen changed → {newScreen}");
newScreen.ModePushed += screenAdded;
newScreen.Exited += screenRemoved;
}
private void screenRemoved(Screen newScreen)
private void screenExited(IScreen lastScreen, IScreen newScreen)
{
ScreenChanged(currentScreen, newScreen);
Logger.Log($"Screen changed ← {currentScreen}");
ScreenChanged(lastScreen, newScreen);
Logger.Log($"Screen changed ← {newScreen}");
if (newScreen == null)
Exit();

View File

@ -69,8 +69,9 @@ namespace osu.Game
protected override Container<Drawable> Content => content;
private OsuBindableBeatmap beatmap;
protected BindableBeatmap Beatmap => beatmap;
private Bindable<WorkingBeatmap> beatmap;
protected Bindable<WorkingBeatmap> Beatmap => beatmap;
private Bindable<bool> fpsDisplayVisible;
@ -155,7 +156,6 @@ namespace osu.Game
dependencies.CacheAs<IAPIProvider>(API);
var defaultBeatmap = new DummyWorkingBeatmap(this);
beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio);
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
@ -174,8 +174,10 @@ namespace osu.Game
// this adds a global reduction of track volume for the time being.
Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
dependencies.CacheAs<BindableBeatmap>(beatmap);
dependencies.CacheAs<IBindableBeatmap>(beatmap);
beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio);
dependencies.CacheAs<IBindable<WorkingBeatmap>>(beatmap);
dependencies.CacheAs(beatmap);
FileStore.Cleanup();

View File

@ -8,22 +8,22 @@ namespace osu.Game.Overlays.AccountCreation
{
public abstract class AccountCreationScreen : Screen
{
protected override void OnEntering(Screen last)
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
Content.FadeOut().Delay(200).FadeIn(200);
this.FadeOut().Delay(200).FadeIn(200);
}
protected override void OnResuming(Screen last)
public override void OnResuming(IScreen last)
{
base.OnResuming(last);
Content.FadeIn(200);
this.FadeIn(200);
}
protected override void OnSuspending(Screen next)
public override void OnSuspending(IScreen next)
{
base.OnSuspending(next);
Content.FadeOut(200);
this.FadeOut(200);
}
}
}

View File

@ -10,6 +10,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
@ -38,13 +39,15 @@ namespace osu.Game.Overlays.AccountCreation
private OsuTextBox[] textboxes;
private ProcessingOverlay processingOverlay;
private GameHost host;
[BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api)
private void load(OsuColour colours, APIAccess api, GameHost host)
{
this.api = api;
this.host = host;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
@ -139,11 +142,11 @@ namespace osu.Game.Overlays.AccountCreation
{
base.Update();
if (!textboxes.Any(t => t.HasFocus))
if (host?.OnScreenKeyboardOverlapsGameWindow != true && !textboxes.Any(t => t.HasFocus))
focusNextTextbox();
}
protected override void OnEntering(Screen last)
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
processingOverlay.Hide();

View File

@ -26,12 +26,12 @@ namespace osu.Game.Overlays.AccountCreation
private const string help_centre_url = "/help/wiki/Help_Centre#login";
protected override void OnEntering(Screen last)
public override void OnEntering(IScreen last)
{
if (string.IsNullOrEmpty(api.ProvidedUsername))
{
Content.FadeOut();
Push(new ScreenEntry());
this.FadeOut();
this.Push(new ScreenEntry());
return;
}
@ -46,7 +46,7 @@ namespace osu.Game.Overlays.AccountCreation
if (string.IsNullOrEmpty(api.ProvidedUsername))
return;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new Sprite
{
@ -104,7 +104,7 @@ namespace osu.Game.Overlays.AccountCreation
new DangerousSettingsButton
{
Text = "I understand. This account isn't for me.",
Action = () => Push(new ScreenEntry())
Action = () => this.Push(new ScreenEntry())
},
furtherAssistance = new LinkFlowContainer(cp => { cp.TextSize = 12; })
{

View File

@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Settings;
using osu.Game.Screens.Menu;
@ -16,7 +17,7 @@ namespace osu.Game.Overlays.AccountCreation
[BackgroundDependencyLoader]
private void load()
{
Child = new FillFlowContainer
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
@ -56,7 +57,7 @@ namespace osu.Game.Overlays.AccountCreation
{
Text = "Let's create an account!",
Margin = new MarginPadding { Vertical = 120 },
Action = () => Push(new ScreenWarning())
Action = () => this.Push(new ScreenWarning())
}
}
};

View File

@ -6,6 +6,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
@ -69,7 +70,10 @@ namespace osu.Game.Overlays
Colour = Color4.Black,
Alpha = 0.9f,
},
welcomeScreen = new ScreenWelcome(),
new ScreenStack(welcomeScreen = new ScreenWelcome())
{
RelativeSizeAxes = Axes.Both,
},
}
}
}
@ -82,7 +86,7 @@ namespace osu.Game.Overlays
base.PopIn();
this.FadeIn(transition_time, Easing.OutQuint);
if (welcomeScreen.ChildScreen != null)
if (welcomeScreen.GetChildScreen() != null)
welcomeScreen.MakeCurrent();
}

View File

@ -7,42 +7,138 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
using osu.Game.Overlays.Direct;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet.Buttons
{
public class DownloadButton : HeaderButton, IHasTooltip
public class DownloadButton : DownloadTrackingComposite, IHasTooltip
{
public string TooltipText => "Download this beatmap";
private readonly bool noVideo;
public string TooltipText => button.Enabled ? "Download this beatmap" : "Login to download";
private readonly IBindable<User> localUser = new Bindable<User>();
public DownloadButton(BeatmapSetInfo set, bool noVideo = false)
{
Width = 120;
private ShakeContainer shakeContainer;
private HeaderButton button;
BeatmapSetDownloader downloader;
Add(new Container
public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
: base(beatmapSet)
{
this.noVideo = noVideo;
Width = 120;
RelativeSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load(APIAccess api, BeatmapManager beatmaps)
{
FillFlowContainer textSprites;
AddRangeInternal(new Drawable[]
{
Depth = -1,
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 10 },
Children = new Drawable[]
shakeContainer = new ShakeContainer
{
downloader = new BeatmapSetDownloader(set, noVideo),
new FillFlowContainer
Depth = -1,
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 5,
Children = new Drawable[]
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new[]
button = new HeaderButton { RelativeSizeAxes = Axes.Both },
new Container
{
// cannot nest inside here due to the structure of button (putting things in its own content).
// requires framework fix.
Padding = new MarginPadding { Horizontal = 10 },
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
textSprites = new FillFlowContainer
{
Depth = -1,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
AutoSizeDuration = 500,
AutoSizeEasing = Easing.OutQuint,
Direction = FillDirection.Vertical,
},
new SpriteIcon
{
Depth = -1,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Icon = FontAwesome.fa_download,
Size = new Vector2(16),
Margin = new MarginPadding { Right = 5 },
},
}
},
new DownloadProgressBar(BeatmapSet)
{
Depth = -2,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
},
},
},
});
button.Action = () =>
{
if (State.Value != DownloadState.NotDownloaded)
{
shakeContainer.Shake();
return;
}
beatmaps.Download(BeatmapSet, noVideo);
};
localUser.BindTo(api.LocalUser);
localUser.BindValueChanged(userChanged, true);
button.Enabled.BindValueChanged(enabledChanged, true);
State.BindValueChanged(state =>
{
switch (state)
{
case DownloadState.Downloading:
textSprites.Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Downloading...",
TextSize = 13,
Font = @"Exo2.0-Bold",
},
};
break;
case DownloadState.Downloaded:
textSprites.Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Importing...",
TextSize = 13,
Font = @"Exo2.0-Bold",
},
};
break;
case DownloadState.LocallyAvailable:
this.FadeOut(200);
break;
case DownloadState.NotDownloaded:
textSprites.Children = new Drawable[]
{
new OsuSpriteText
{
@ -52,57 +148,18 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
},
new OsuSpriteText
{
Text = set.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty,
Text = BeatmapSet.Value.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty,
TextSize = 11,
Font = @"Exo2.0-Bold",
},
},
},
new SpriteIcon
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Icon = FontAwesome.fa_download,
Size = new Vector2(16),
Margin = new MarginPadding { Right = 5 },
},
},
});
Action = () =>
{
if (downloader.DownloadState.Value == BeatmapSetDownloader.DownloadStatus.Downloading)
{
Content.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine);
return;
}
downloader.Download();
};
downloader.DownloadState.ValueChanged += state =>
{
switch (state)
{
case BeatmapSetDownloader.DownloadStatus.Downloaded:
this.FadeOut(200);
break;
case BeatmapSetDownloader.DownloadStatus.NotDownloaded:
};
this.FadeIn(200);
break;
}
};
}, true);
}
[BackgroundDependencyLoader]
private void load(APIAccess api)
{
localUser.BindTo(api.LocalUser);
Enabled.BindValueChanged(enabledChanged, true);
}
private void userChanged(User user) => button.Enabled.Value = !(user is GuestUser);
private void enabledChanged(bool enabled) => this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
}

View File

@ -13,12 +13,14 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Overlays.Direct;
using osuTK;
using osuTK.Graphics;
using DownloadButton = osu.Game.Overlays.BeatmapSet.Buttons.DownloadButton;
namespace osu.Game.Overlays.BeatmapSet
{
public class Header : Container
public class Header : DownloadTrackingComposite
{
private const float transition_duration = 200;
private const float tabs_height = 50;
@ -28,76 +30,23 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Box tabsBg;
private readonly UpdateableBeatmapSetCover cover;
private readonly OsuSpriteText title, artist;
private readonly Container noVideoButtons;
private readonly FillFlowContainer videoButtons;
private readonly AuthorInfo author;
private readonly Container downloadButtonsContainer;
private readonly FillFlowContainer downloadButtonsContainer;
private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
public Details Details;
public readonly BeatmapPicker Picker;
private BeatmapSetInfo beatmapSet;
private readonly FavouriteButton favouriteButton;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet;
updateDisplay();
}
}
private void updateDisplay()
{
title.Text = BeatmapSet?.Metadata.Title ?? string.Empty;
artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty;
onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
cover.BeatmapSet = BeatmapSet;
if (BeatmapSet != null)
{
downloadButtonsContainer.FadeIn(transition_duration);
favouriteButton.FadeIn(transition_duration);
if (BeatmapSet.OnlineInfo.HasVideo)
{
videoButtons.Children = new[]
{
new DownloadButton(BeatmapSet),
new DownloadButton(BeatmapSet, true),
};
videoButtons.FadeIn(transition_duration);
noVideoButtons.FadeOut(transition_duration);
}
else
{
noVideoButtons.Child = new DownloadButton(BeatmapSet);
noVideoButtons.FadeIn(transition_duration);
videoButtons.FadeOut(transition_duration);
}
}
else
{
downloadButtonsContainer.FadeOut(transition_duration);
favouriteButton.FadeOut(transition_duration);
}
}
public Header()
{
ExternalLinkButton externalLink;
RelativeSizeAxes = Axes.X;
Height = 400;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
@ -105,7 +54,8 @@ namespace osu.Game.Overlays.BeatmapSet
Radius = 3,
Offset = new Vector2(0f, 1f),
};
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new Container
{
@ -196,24 +146,11 @@ namespace osu.Game.Overlays.BeatmapSet
Children = new Drawable[]
{
favouriteButton = new FavouriteButton(),
downloadButtonsContainer = new Container
downloadButtonsContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
Children = new Drawable[]
{
noVideoButtons = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
},
videoButtons = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Spacing = new Vector2(buttons_spacing),
Alpha = 0f,
},
},
Spacing = new Vector2(buttons_spacing),
},
},
},
@ -245,14 +182,64 @@ namespace osu.Game.Overlays.BeatmapSet
};
Picker.Beatmap.ValueChanged += b => Details.Beatmap = b;
Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet?.OnlineBeatmapSetID}#{b?.Ruleset.ShortName}/{b?.OnlineBeatmapID}";
Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b?.Ruleset.ShortName}/{b?.OnlineBeatmapID}";
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabsBg.Colour = colours.Gray3;
updateDisplay();
State.BindValueChanged(_ => updateDownloadButtons());
BeatmapSet.BindValueChanged(beatmapSet =>
{
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = beatmapSet;
title.Text = beatmapSet?.Metadata.Title ?? string.Empty;
artist.Text = beatmapSet?.Metadata.Artist ?? string.Empty;
onlineStatusPill.Status = beatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
cover.BeatmapSet = beatmapSet;
if (beatmapSet != null)
{
downloadButtonsContainer.FadeIn(transition_duration);
favouriteButton.FadeIn(transition_duration);
}
else
{
downloadButtonsContainer.FadeOut(transition_duration);
favouriteButton.FadeOut(transition_duration);
}
updateDownloadButtons();
}, true);
}
private void updateDownloadButtons()
{
if (BeatmapSet.Value == null) return;
switch (State.Value)
{
case DownloadState.LocallyAvailable:
// temporary for UX until new design is implemented.
downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet)
{
Width = 50,
RelativeSizeAxes = Axes.Y
};
break;
case DownloadState.Downloading:
case DownloadState.Downloaded:
// temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design.
downloadButtonsContainer.Child = new DownloadButton(BeatmapSet);
break;
default:
downloadButtonsContainer.Child = new DownloadButton(BeatmapSet);
if (BeatmapSet.Value.OnlineInfo.HasVideo)
downloadButtonsContainer.Add(new DownloadButton(BeatmapSet, true));
break;
}
}
}
}

View File

@ -46,7 +46,7 @@ namespace osu.Game.Overlays
if (value == beatmapSet)
return;
header.BeatmapSet = info.BeatmapSet = beatmapSet = value;
header.BeatmapSet.Value = info.BeatmapSet = beatmapSet = value;
}
}
@ -140,7 +140,7 @@ namespace osu.Game.Overlays
req.Success += res =>
{
BeatmapSet = res.ToBeatmapSet(rulesets);
header.Picker.Beatmap.Value = header.BeatmapSet.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
header.Picker.Beatmap.Value = header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
};
api.Queue(req);
Show();

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