1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-03 08:09:54 +08:00

Compare commits

...

546 Commits

304 changed files with 8462 additions and 3846 deletions
@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (catch)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (mania)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Mania.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (osu!)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Osu.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (taiko)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Taiko.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
+1 -1
View File
@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="VisualTests" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp2.1/osu.Game.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
+8 -8
View File
@@ -7,13 +7,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.1/osu.Game.Tests.dll"
"${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -24,13 +24,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.1/osu.Game.Tests.dll"
"${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.2/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -41,13 +41,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll"
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.2/osu!.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -58,13 +58,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1/osu!.dll"
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.2/osu!.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
+1 -5
View File
@@ -11,7 +11,6 @@
"build",
"--no-restore",
"osu.Desktop",
"/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -27,7 +26,6 @@
"build",
"--no-restore",
"osu.Desktop",
"/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -44,7 +42,6 @@
"build",
"--no-restore",
"osu.Game.Tests",
"/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -60,7 +57,6 @@
"build",
"--no-restore",
"osu.Game.Tests",
"/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -70,7 +66,7 @@
"problemMatcher": "$msCompile"
},
{
"label": "Restore (netcoreapp2.1)",
"label": "Restore (netcoreapp2.2)",
"type": "shell",
"command": "dotnet",
"args": [
+57 -12
View File
@@ -10,26 +10,71 @@ We are accepting bug reports (please report with as much detail as possible). Fe
# Requirements
- A desktop platform with the [.NET Core SDK 2.1](https://www.microsoft.com/net/learn/get-started) or higher installed.
- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio Code](https://code.visualstudio.com/) (with the C# plugin installed) or [Jetbrains Rider](https://www.jetbrains.com/rider/) (commercial).
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
# Building and running
# Running osu!
If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled (download and run the install executable for your platform).
## Releases
Clone the repository including submodules
If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled.
`git clone --recurse-submodules https://github.com/ppy/osu`
- Windows (x64) users should download and run `install.exe`.
- macOS users (10.12 "Sierra" and higher) should download and run `osu.app.zip`.
- iOS users can join the [TestFlight beta program](https://t.co/xQJmHkfC18).
Build and run
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included)
- From command line using `dotnet run --project osu.Desktop`. When building for non-development purposes, add `-c Release` to gain higher performance.
- To run with code analysis, instead use `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.
## Downloading the source code
Note: If you run from command line under linux, you will need to prefix the output folder to your `LD_LIBRARY_PATH`. See `.vscode/launch.json` for an example
Clone the repository **including submodules**:
If you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`). Visual Studio Code users must run `Restore` task from debug tab before attempt to build.
```shell
git clone --recurse-submodules 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
```
## Building
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided below.
> Visual Studio Code users must run the `Restore` task before any build attempt.
You can also build and run osu! from the command-line with a single command:
```shell
dotnet run --project osu.Desktop
```
If you are not interested in debugging osu!, you can add `-c Release` to gain performance. In this case, you must replace `Debug` with `Release` in any commands mentioned in this document.
If the build fails, try to restore nuget packages with `dotnet restore`.
### A note for Linux users
On Linux, the environment variable `LD_LIBRARY_PATH` must point to the build directory, located at `osu.Desktop/bin/Debug/$NETCORE_VERSION`.
`$NETCORE_VERSION` is the version of .NET Core SDK. You can have it with `grep TargetFramework osu.Desktop/osu.Desktop.csproj | sed -r 's/.*>(.*)<\/.*/\1/'`.
For example, you can run osu! with the following command:
```shell
LD_LIBRARY_PATH="$(pwd)/osu.Desktop/bin/Debug/netcoreapp2.2" dotnet run --project osu.Desktop
```
## 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.
# Contributing
+23 -2
View File
@@ -15,12 +15,16 @@ using Microsoft.Win32;
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
{
internal class OsuGameDesktop : OsuGame
{
private readonly bool noVersionOverlay;
private VersionManager versionManager;
public OsuGameDesktop(string[] args = null)
: base(args)
@@ -46,7 +50,7 @@ namespace osu.Desktop
if (!noVersionOverlay)
{
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, v =>
{
Add(v);
v.State = Visibility.Visible;
@@ -59,6 +63,23 @@ namespace osu.Desktop
}
}
protected override void ScreenChanged(OsuScreen current, Screen newScreen)
{
base.ScreenChanged(current, newScreen);
switch (newScreen)
{
case Intro _:
case MainMenu _:
if (versionManager != null)
versionManager.State = Visibility.Visible;
break;
default:
if (versionManager != null)
versionManager.State = Visibility.Hidden;
break;
}
}
public override void SetHost(GameHost host)
{
base.SetHost(host);
@@ -76,7 +97,7 @@ namespace osu.Desktop
private void fileDrop(object sender, FileDropEventArgs e)
{
var filePaths = new[] { e.FileName };
var filePaths = e.FileNames;
var firstExtension = Path.GetExtension(filePaths.First());
+2 -1
View File
@@ -128,11 +128,12 @@ namespace osu.Desktop.Overlays
protected override void PopIn()
{
this.FadeIn(1000);
this.FadeIn(1400, Easing.OutQuint);
}
protected override void PopOut()
{
this.FadeOut(500, Easing.OutQuint);
}
}
}
+2 -3
View File
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using osu.Framework;
@@ -77,10 +76,10 @@ namespace osu.Desktop.Updater
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.Windows:
bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".exe"));
bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".exe"));
break;
case RuntimeInfo.Platform.MacOsx:
bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".app.zip"));
bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".app.zip"));
break;
}
+2 -2
View File
@@ -28,8 +28,8 @@
<ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.1" />
</ItemGroup>
<ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" />
+2 -2
View File
@@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
@@ -26,7 +26,6 @@ namespace osu.Game.Rulesets.Catch.Tests
}
};
for (int i = 0; i < 512; i++)
beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });
@@ -19,7 +19,6 @@ namespace osu.Game.Rulesets.Catch.Tests
{
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
for (int i = 0; i < 512; i++)
if (i % 5 < 3)
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });
@@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">
@@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty
{
public class CatchDifficultyCalculator : DifficultyCalculator
{
/// <summary>
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
@@ -0,0 +1,23 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Objects
{
public class CatchHitWindows : HitWindows
{
public override bool IsHitResultAllowed(HitResult result)
{
switch (result)
{
case HitResult.Perfect:
case HitResult.Miss:
return true;
}
return false;
}
}
}
@@ -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?)null) ?? Color4.White;
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
}
private const float preempt = 1000;
@@ -5,6 +5,7 @@ using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -41,5 +42,7 @@ namespace osu.Game.Rulesets.Catch.Scoring
Health.Value += Math.Max(result.Judgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
}
protected override HitWindows CreateHitWindows() => new CatchHitWindows();
}
}
+2 -2
View File
@@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
@@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">
@@ -180,7 +180,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
foreach (var obj in newPattern.HitObjects)
yield return obj;
}
}
@@ -53,7 +53,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
if (!calculateStrainValues(difficultyHitObjects, timeRate))
return new DifficultyAttributes(mods, 0);
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
return new ManiaDifficultyAttributes(mods, starRating)
@@ -3,14 +3,14 @@
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModMirror : Mod, IApplicableToRulesetContainer<ManiaHitObject>
public class ManiaModMirror : Mod, IApplicableToBeatmap<ManiaHitObject>
{
public override string Name => "Mirror";
public override string Acronym => "MR";
@@ -18,11 +18,11 @@ namespace osu.Game.Rulesets.Mania.Mods
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
{
var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns;
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = availableColumns - 1 - h.Column);
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = availableColumns - 1 - h.Column);
}
}
}
@@ -4,15 +4,15 @@
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModRandom : Mod, IApplicableToRulesetContainer<ManiaHitObject>
public class ManiaModRandom : Mod, IApplicableToBeatmap<ManiaHitObject>
{
public override string Name => "Random";
public override string Acronym => "RD";
@@ -21,12 +21,12 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
{
var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns;
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
}
}
}
@@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Mania.Objects
private readonly int beatmapColumnCount;
private readonly double endTime;
private readonly double[] heldUntil;
@@ -5,6 +5,7 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -157,5 +158,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
}
}
}
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
}
}
+2 -2
View File
@@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
@@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
@@ -23,60 +24,70 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
var osuBeatmap = (Beatmap<OsuHitObject>)Beatmap;
// Reset stacking
foreach (var h in osuBeatmap.HitObjects)
h.StackHeight = 0;
if (osuBeatmap.HitObjects.Count > 0)
{
// Reset stacking
foreach (var h in osuBeatmap.HitObjects)
h.StackHeight = 0;
if (Beatmap.BeatmapInfo.BeatmapVersion >= 6)
applyStacking(osuBeatmap);
else
applyStackingOld(osuBeatmap);
if (Beatmap.BeatmapInfo.BeatmapVersion >= 6)
applyStacking(osuBeatmap, 0, osuBeatmap.HitObjects.Count - 1);
else
applyStackingOld(osuBeatmap);
}
}
private void applyStacking(Beatmap<OsuHitObject> beatmap)
private void applyStacking(Beatmap<OsuHitObject> beatmap, int startIndex, int endIndex)
{
// Extend the end index to include objects they are stacked on
int extendedEndIndex = beatmap.HitObjects.Count - 1;
for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--)
if (startIndex > endIndex) throw new ArgumentOutOfRangeException(nameof(startIndex), $"{nameof(startIndex)} cannot be greater than {nameof(endIndex)}.");
if (startIndex < 0) throw new ArgumentOutOfRangeException(nameof(startIndex), $"{nameof(startIndex)} cannot be less than 0.");
if (endIndex < 0) throw new ArgumentOutOfRangeException(nameof(endIndex), $"{nameof(endIndex)} cannot be less than 0.");
int extendedEndIndex = endIndex;
if (endIndex < beatmap.HitObjects.Count - 1)
{
int stackBaseIndex = i;
for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++)
// Extend the end index to include objects they are stacked on
for (int i = endIndex; i >= startIndex; i--)
{
OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex];
if (stackBaseObject is Spinner) break;
OsuHitObject objectN = beatmap.HitObjects[n];
if (objectN is Spinner)
continue;
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency;
if (objectN.StartTime - endTime > stackThreshold)
//We are no longer within stacking range of the next object.
break;
if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
int stackBaseIndex = i;
for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++)
{
stackBaseIndex = n;
OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex];
if (stackBaseObject is Spinner) break;
// HitObjects after the specified update range haven't been reset yet
objectN.StackHeight = 0;
OsuHitObject objectN = beatmap.HitObjects[n];
if (objectN is Spinner)
continue;
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency;
if (objectN.StartTime - endTime > stackThreshold)
//We are no longer within stacking range of the next object.
break;
if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance
|| stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
{
stackBaseIndex = n;
// HitObjects after the specified update range haven't been reset yet
objectN.StackHeight = 0;
}
}
}
if (stackBaseIndex > extendedEndIndex)
{
extendedEndIndex = stackBaseIndex;
if (extendedEndIndex == beatmap.HitObjects.Count - 1)
break;
if (stackBaseIndex > extendedEndIndex)
{
extendedEndIndex = stackBaseIndex;
if (extendedEndIndex == beatmap.HitObjects.Count - 1)
break;
}
}
}
//Reverse pass for stack calculation.
int extendedStartIndex = 0;
for (int i = extendedEndIndex; i > 0; i--)
int extendedStartIndex = startIndex;
for (int i = extendedEndIndex; i > startIndex; i--)
{
int n = i;
/* We should check every note which has not yet got a stack.
@@ -155,7 +166,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
/* We have hit the first slider in a possible stack.
* From this point on, we ALWAYS stack positive regardless.
*/
while (--n >= 0)
while (--n >= startIndex)
{
OsuHitObject objectN = beatmap.HitObjects[n];
if (objectN is Spinner) continue;
@@ -57,6 +57,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
s.Process(h);
}
// The peak strain will not be saved for the last section in the above loop
foreach (Skill s in skills)
s.SaveCurrentPeak();
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
@@ -105,13 +105,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double approachRateFactor = 1.0f;
if (Attributes.ApproachRate > 10.33f)
approachRateFactor += 0.45f * (Attributes.ApproachRate - 10.33f);
approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
else if (Attributes.ApproachRate < 8.0f)
{
// HD is worth more with lower ar!
if (mods.Any(h => h is OsuModHidden))
approachRateFactor += 0.02f * (8.0f - Attributes.ApproachRate);
else
approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate);
}
@@ -119,12 +115,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
if (mods.Any(h => h is OsuModHidden))
aimValue *= 1.02 + (11.0f - Attributes.ApproachRate) / 50.0; // Gives a 1.04 bonus for AR10, a 1.06 bonus for AR9, a 1.02 bonus for AR11.
aimValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
if (mods.Any(h => h is OsuModFlashlight))
{
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
aimValue *= 1.45f * lengthBonus;
// Apply object-based bonus for flashlight.
aimValue *= 1.0f + 0.35f * Math.Min(1.0f, totalHits / 200.0f) +
(totalHits > 200 ? 0.3f * Math.Min(1.0f, (totalHits - 200) / 300.0f) +
(totalHits > 500 ? (totalHits - 500) / 1200.0f : 0.0f) : 0.0f);
}
// Scale the aim value with accuracy _slightly_
@@ -150,13 +148,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (beatmapMaxCombo > 0)
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
double approachRateFactor = 1.0f;
if (Attributes.ApproachRate > 10.33f)
approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
speedValue *= approachRateFactor;
if (mods.Any(m => m is OsuModHidden))
speedValue *= 1.18f;
speedValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
// Scale the speed value with accuracy _slightly_
speedValue *= 0.5f + accuracy / 2.0f;
speedValue *= 0.02f + accuracy;
// It is important to also consider accuracy difficulty when doing that
speedValue *= 0.98f + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
speedValue *= 0.96f + Math.Pow(Attributes.OverallDifficulty, 2) / 1600;
return speedValue;
}
@@ -184,7 +188,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
if (mods.Any(m => m is OsuModHidden))
accuracyValue *= 1.02f;
accuracyValue *= 1.08f;
if (mods.Any(m => m is OsuModFlashlight))
accuracyValue *= 1.02f;
+194
View File
@@ -0,0 +1,194 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModBlinds : Mod, IApplicableToRulesetContainer<OsuHitObject>, IApplicableToScoreProcessor
{
public override string Name => "Blinds";
public override string Description => "Play with blinds on your screen.";
public override string Acronym => "BL";
public override FontAwesome Icon => FontAwesome.fa_adjust;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => false;
public override double ScoreMultiplier => 1.12;
private DrawableOsuBlinds blinds;
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
{
rulesetContainer.Overlays.Add(blinds = new DrawableOsuBlinds(rulesetContainer.Playfield.HitObjectContainer, rulesetContainer.Beatmap));
}
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{
scoreProcessor.Health.ValueChanged += val => { blinds.AnimateClosedness((float)val); };
}
/// <summary>
/// Element for the Blinds mod drawing 2 black boxes covering the whole screen which resize inside a restricted area with some leniency.
/// </summary>
public class DrawableOsuBlinds : Container
{
/// <summary>
/// Black background boxes behind blind panel textures.
/// </summary>
private Box blackBoxLeft, blackBoxRight;
private Drawable panelLeft, panelRight, bgPanelLeft, bgPanelRight;
private readonly Beatmap<OsuHitObject> beatmap;
/// <summary>
/// Value between 0 and 1 setting a maximum "closedness" for the blinds.
/// Useful for animating how far the blinds can be opened while keeping them at the original position if they are wider open than this.
/// </summary>
private const float target_clamp = 1;
private readonly float targetBreakMultiplier = 0;
private readonly float easing = 1;
private readonly CompositeDrawable restrictTo;
/// <summary>
/// <para>
/// Percentage of playfield to extend blinds over. Basically moves the origin points where the blinds start.
/// </para>
/// <para>
/// -1 would mean the blinds always cover the whole screen no matter health.
/// 0 would mean the blinds will only ever be on the edge of the playfield on 0% health.
/// 1 would mean the blinds are fully outside the playfield on 50% health.
/// Infinity would mean the blinds are always outside the playfield except on 100% health.
/// </para>
/// </summary>
private const float leniency = 0.1f;
public DrawableOsuBlinds(CompositeDrawable restrictTo, Beatmap<OsuHitObject> beatmap)
{
this.restrictTo = restrictTo;
this.beatmap = beatmap;
}
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.Both;
Children = new[]
{
blackBoxLeft = new Box
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Y,
},
blackBoxRight = new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Y,
},
bgPanelLeft = new ModBlindsPanel
{
Origin = Anchor.TopRight,
Colour = Color4.Gray,
},
panelLeft = new ModBlindsPanel { Origin = Anchor.TopRight, },
bgPanelRight = new ModBlindsPanel { Colour = Color4.Gray },
panelRight = new ModBlindsPanel()
};
}
private float calculateGap(float value) => MathHelper.Clamp(value, 0, target_clamp) * targetBreakMultiplier;
// lagrange polinominal for (0,0) (0.6,0.4) (1,1) should make a good curve
private static float applyAdjustmentCurve(float value) => 0.6f * value * value + 0.4f * value;
protected override void Update()
{
float start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X;
float end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X;
float rawWidth = end - start;
start -= rawWidth * leniency * 0.5f;
end += rawWidth * leniency * 0.5f;
float width = (end - start) * 0.5f * applyAdjustmentCurve(calculateGap(easing));
// different values in case the playfield ever moves from center to somewhere else.
blackBoxLeft.Width = start + width;
blackBoxRight.Width = DrawWidth - end + width;
panelLeft.X = start + width;
panelRight.X = end - width;
bgPanelLeft.X = start;
bgPanelRight.X = end;
}
protected override void LoadComplete()
{
const float break_open_early = 500;
const float break_close_late = 250;
base.LoadComplete();
var firstObj = beatmap.HitObjects[0];
var startDelay = firstObj.StartTime - firstObj.TimePreempt;
using (BeginAbsoluteSequence(startDelay + break_close_late, true))
leaveBreak();
foreach (var breakInfo in beatmap.Breaks)
{
if (breakInfo.HasEffect)
{
using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early, true))
{
enterBreak();
using (BeginDelayedSequence(breakInfo.Duration + break_open_early + break_close_late, true))
leaveBreak();
}
}
}
}
private void enterBreak() => this.TransformTo(nameof(targetBreakMultiplier), 0f, 1000, Easing.OutSine);
private void leaveBreak() => this.TransformTo(nameof(targetBreakMultiplier), 1f, 2500, Easing.OutBounce);
/// <summary>
/// 0 is open, 1 is closed.
/// </summary>
public void AnimateClosedness(float value) => this.TransformTo(nameof(easing), value, 200, Easing.OutQuint);
public class ModBlindsPanel : Sprite
{
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get("Play/osu/blinds-panel");
}
}
}
}
}
@@ -26,9 +26,6 @@ namespace osu.Game.Rulesets.Osu.Mods
if (slider == null)
return;
slider.HeadCircle.Position = new Vector2(slider.HeadCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.HeadCircle.Position.Y);
slider.TailCircle.Position = new Vector2(slider.TailCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.TailCircle.Position.Y);
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
@@ -21,7 +21,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject)
{
base.AddInternal(shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both });
base.AddInternal(shakeContainer = new ShakeContainer
{
ShakeDuration = 30,
RelativeSizeAxes = Axes.Both
});
Alpha = 0;
}
@@ -54,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?)null) ?? Color4.White;
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
}
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn);
@@ -101,7 +101,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break;
}
float aimRotation = MathHelper.RadiansToDegrees((float)Math.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
while (Math.Abs(aimRotation - Rotation) > 180)
aimRotation += aimRotation < Rotation ? 360 : -360;
@@ -14,6 +14,7 @@ using osu.Game.Configuration;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -151,6 +152,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
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);
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (userTriggered || Time.Current < slider.EndTime)
+29 -5
View File
@@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects;
using System.Linq;
using osu.Framework.Caching;
using osu.Framework.Configuration;
using osu.Game.Audio;
using osu.Game.Beatmaps;
@@ -26,8 +27,11 @@ namespace osu.Game.Rulesets.Osu.Objects
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
public double Duration => EndTime - StartTime;
private Cached<Vector2> endPositionCache;
public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1);
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
public override Vector2 EndPosition => Position + this.CurvePositionAt(1);
public override int ComboIndex
{
@@ -56,7 +60,11 @@ namespace osu.Game.Rulesets.Osu.Objects
public SliderPath Path
{
get => PathBindable.Value;
set => PathBindable.Value = value;
set
{
PathBindable.Value = value;
endPositionCache.Invalidate();
}
}
public double Distance => Path.Distance;
@@ -73,6 +81,8 @@ namespace osu.Game.Rulesets.Osu.Objects
if (TailCircle != null)
TailCircle.Position = EndPosition;
endPositionCache.Invalidate();
}
}
@@ -92,7 +102,17 @@ namespace osu.Game.Rulesets.Osu.Objects
public List<List<SampleInfo>> NodeSamples { get; set; } = new List<List<SampleInfo>>();
public int RepeatCount { get; set; }
private int repeatCount;
public int RepeatCount
{
get => repeatCount;
set
{
repeatCount = value;
endPositionCache.Invalidate();
}
}
/// <summary>
/// The length of one span of this <see cref="Slider"/>.
@@ -169,7 +189,11 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createTicks()
{
var length = Path.Distance;
// A very lenient maximum length of a slider for ticks to be generated.
// This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage.
const double max_length = 100000;
var length = Math.Min(max_length, Path.Distance);
var tickDistance = MathHelper.Clamp(TickDistance, 0, length);
if (tickDistance == 0) return;
@@ -191,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.Objects
var distanceProgress = d / length;
var timeProgress = reversed ? 1 - distanceProgress : distanceProgress;
var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL)
var firstSample = Samples.Find(s => s.Name == SampleInfo.HIT_NORMAL)
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
var sampleList = new List<SampleInfo>();
+3 -3
View File
@@ -31,8 +31,8 @@ namespace osu.Game.Rulesets.Osu
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{
new KeyBinding(InputKey.A, OsuAction.LeftButton),
new KeyBinding(InputKey.S, OsuAction.RightButton),
new KeyBinding(InputKey.Z, OsuAction.LeftButton),
new KeyBinding(InputKey.X, OsuAction.RightButton),
new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton),
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
};
@@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu
new MultiMod(new OsuModSuddenDeath(), new OsuModPerfect()),
new MultiMod(new OsuModDoubleTime(), new OsuModNightcore()),
new OsuModHidden(),
new OsuModFlashlight(),
new MultiMod(new OsuModFlashlight(), new OsuModBlinds()),
};
case ModType.Conversion:
return new Mod[]
@@ -5,11 +5,11 @@ using System.Collections.Generic;
using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Osu.Scoring
{
@@ -22,7 +22,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
private float hpDrainRate;
private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
private readonly Dictionary<ComboResult, int> comboResultCounts = new Dictionary<ComboResult, int>();
protected override void ApplyBeatmap(Beatmap<OsuHitObject> beatmap)
@@ -35,21 +34,9 @@ namespace osu.Game.Rulesets.Osu.Scoring
protected override void Reset(bool storeResults)
{
base.Reset(storeResults);
scoreResultCounts.Clear();
comboResultCounts.Clear();
}
public override void PopulateScore(ScoreInfo score)
{
base.PopulateScore(score);
score.Statistics[HitResult.Great] = scoreResultCounts.GetOrDefault(HitResult.Great);
score.Statistics[HitResult.Good] = scoreResultCounts.GetOrDefault(HitResult.Good);
score.Statistics[HitResult.Meh] = scoreResultCounts.GetOrDefault(HitResult.Meh);
score.Statistics[HitResult.Miss] = scoreResultCounts.GetOrDefault(HitResult.Miss);
}
private const double harshness = 0.01;
protected override void ApplyResult(JudgementResult result)
@@ -59,10 +46,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
var osuResult = (OsuJudgementResult)result;
if (result.Type != HitResult.None)
{
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1;
}
switch (result.Type)
{
@@ -89,5 +73,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
}
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
protected override HitWindows CreateHitWindows() => new OsuHitWindows();
}
}
@@ -39,10 +39,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private int downCount;
private const float pressed_scale = 1.2f;
private const float released_scale = 1f;
private float targetScale => downCount > 0 ? pressed_scale : released_scale;
private void updateExpandedState()
{
if (downCount > 0)
(ActiveCursor as OsuCursor)?.Expand();
else
(ActiveCursor as OsuCursor)?.Contract();
}
public bool OnPressed(OsuAction action)
{
@@ -51,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton:
case OsuAction.RightButton:
downCount++;
ActiveCursor.ScaleTo(released_scale).ScaleTo(targetScale, 100, Easing.OutQuad);
updateExpandedState();
break;
}
@@ -65,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton:
case OsuAction.RightButton:
if (--downCount == 0)
ActiveCursor.ScaleTo(targetScale, 200, Easing.OutQuad);
updateExpandedState();
break;
}
@@ -77,92 +80,106 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override void PopIn()
{
fadeContainer.FadeTo(1, 300, Easing.OutQuint);
ActiveCursor.ScaleTo(targetScale, 400, Easing.OutQuint);
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
}
protected override void PopOut()
{
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(targetScale * 0.8f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
}
public class OsuCursor : Container
public class OsuCursor : SkinReloadableDrawable
{
private Drawable cursorContainer;
private bool cursorExpand;
private Bindable<double> cursorScale;
private Bindable<bool> autoCursorScale;
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private Container expandTarget;
private Drawable scaleTarget;
public OsuCursor()
{
Origin = Anchor.Centre;
Size = new Vector2(42);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, IBindableBeatmap beatmap)
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
Child = cursorContainer = new SkinnableDrawable("cursor", _ => new CircularContainer
cursorExpand = skin.GetValue<SkinConfiguration, bool>(s => s.CursorExpand ?? true);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, IBindable<WorkingBeatmap> beatmap)
{
InternalChild = expandTarget = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 6,
BorderColour = Color4.White,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Pink.Opacity(0.5f),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 3,
BorderColour = Color4.White.Opacity(0.5f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
},
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.1f),
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
},
},
}
}, restrictSize: false)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Child = scaleTarget = new SkinnableDrawable("cursor", _ => new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 6,
BorderColour = Color4.White,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Pink.Opacity(0.5f),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 3,
BorderColour = Color4.White.Opacity(0.5f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
},
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.1f),
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
},
},
}
}, restrictSize: false)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
}
};
this.beatmap.BindTo(beatmap);
@@ -187,8 +204,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
}
cursorContainer.Scale = new Vector2(scale);
scaleTarget.Scale = new Vector2(scale);
}
private const float pressed_scale = 1.2f;
private const float released_scale = 1f;
public void Expand()
{
if (!cursorExpand) return;
expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad);
}
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
}
}
}
+2 -2
View File
@@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Taiko.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Taiko.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
@@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">
@@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
if (userTriggered)
{
var nextTick = ticks.FirstOrDefault(j => !j.IsHit);
var nextTick = ticks.Find(j => !j.IsHit);
nextTick?.TriggerResult(HitResult.Great);
@@ -3,6 +3,7 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI;
@@ -65,5 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
Health.Value = 0;
}
protected override HitWindows CreateHitWindows() => new TaikoHitWindows();
}
}
@@ -149,7 +149,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var stream = Resource.OpenResource(filename))
using (var sr = new StreamReader(stream))
{
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms))
+13 -13
View File
@@ -29,7 +29,7 @@ namespace osu.Game.Tests.Beatmaps.IO
{
try
{
loadOszIntoOsu(loadOsu(host));
LoadOszIntoOsu(loadOsu(host));
}
finally
{
@@ -48,7 +48,7 @@ namespace osu.Game.Tests.Beatmaps.IO
{
var osu = loadOsu(host);
var imported = loadOszIntoOsu(osu);
var imported = LoadOszIntoOsu(osu);
deleteBeatmapSet(imported, osu);
}
@@ -69,8 +69,8 @@ namespace osu.Game.Tests.Beatmaps.IO
{
var osu = loadOsu(host);
var imported = loadOszIntoOsu(osu);
var importedSecondTime = loadOszIntoOsu(osu);
var imported = LoadOszIntoOsu(osu);
var importedSecondTime = LoadOszIntoOsu(osu);
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
Assert.IsTrue(imported.ID == importedSecondTime.ID);
@@ -105,7 +105,7 @@ namespace osu.Game.Tests.Beatmaps.IO
manager.ItemAdded += (_, __, ___) => fireCount++;
manager.ItemRemoved += _ => fireCount++;
var imported = loadOszIntoOsu(osu);
var imported = LoadOszIntoOsu(osu);
Assert.AreEqual(0, fireCount -= 1);
@@ -160,12 +160,12 @@ namespace osu.Game.Tests.Beatmaps.IO
var osu = loadOsu(host);
var manager = osu.Dependencies.Get<BeatmapManager>();
var imported = loadOszIntoOsu(osu);
var imported = LoadOszIntoOsu(osu);
imported.Hash += "-changed";
manager.Update(imported);
var importedSecondTime = loadOszIntoOsu(osu);
var importedSecondTime = LoadOszIntoOsu(osu);
Assert.IsTrue(imported.ID != importedSecondTime.ID);
Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID);
@@ -191,11 +191,11 @@ namespace osu.Game.Tests.Beatmaps.IO
{
var osu = loadOsu(host);
var imported = loadOszIntoOsu(osu);
var imported = LoadOszIntoOsu(osu);
deleteBeatmapSet(imported, osu);
var importedSecondTime = loadOszIntoOsu(osu);
var importedSecondTime = LoadOszIntoOsu(osu);
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
Assert.IsTrue(imported.ID == importedSecondTime.ID);
@@ -262,7 +262,7 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
private string createTemporaryBeatmap()
private static string createTemporaryBeatmap()
{
var temp = Path.GetTempFileName() + ".osz";
File.Copy(TEST_OSZ_PATH, temp, true);
@@ -270,7 +270,7 @@ namespace osu.Game.Tests.Beatmaps.IO
return temp;
}
private BeatmapSetInfo loadOszIntoOsu(OsuGameBase osu, string path = null)
public static BeatmapSetInfo LoadOszIntoOsu(OsuGameBase osu, string path = null)
{
var temp = path ?? createTemporaryBeatmap();
@@ -305,7 +305,7 @@ namespace osu.Game.Tests.Beatmaps.IO
return osu;
}
private void ensureLoaded(OsuGameBase osu, int timeout = 60000)
private static void ensureLoaded(OsuGameBase osu, int timeout = 60000)
{
IEnumerable<BeatmapSetInfo> resultSets = null;
var store = osu.Dependencies.Get<BeatmapManager>();
@@ -343,7 +343,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(beatmap?.HitObjects.Any() == true);
}
private void waitForOrAssert(Func<bool> result, string failureMessage, int timeout = 60000)
private static void waitForOrAssert(Func<bool> result, string failureMessage, int timeout = 60000)
{
Task task = Task.Run(() =>
{
@@ -0,0 +1,32 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Overlays.AccountCreation;
namespace osu.Game.Tests.Visual
{
public class TestCaseAccountCreationOverlay : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ErrorTextFlowContainer),
typeof(AccountCreationBackground),
typeof(ScreenEntry),
typeof(ScreenWarning),
typeof(ScreenWelcome),
typeof(AccountCreationScreen),
};
public TestCaseAccountCreationOverlay()
{
var accountCreation = new AccountCreationOverlay();
Child = accountCreation;
accountCreation.State = Visibility.Visible;
}
}
}
@@ -40,7 +40,6 @@ namespace osu.Game.Tests.Visual
typeof(DrawableCarouselBeatmapSet),
};
private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>();
private readonly HashSet<int> eagerSelectedIDs = new HashSet<int>();
@@ -148,7 +147,7 @@ namespace osu.Game.Tests.Visual
private bool selectedBeatmapVisible()
{
var currentlySelected = carousel.Items.FirstOrDefault(s => s.Item is CarouselBeatmap && s.Item.State == CarouselItemState.Selected);
var currentlySelected = carousel.Items.Find(s => s.Item is CarouselBeatmap && s.Item.State == CarouselItemState.Selected);
if (currentlySelected == null)
return true;
return currentlySelected.Item.Visible;
@@ -55,7 +55,6 @@ namespace osu.Game.Tests.Visual
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
scores = new[]
{
new APIScoreInfo
@@ -16,7 +16,6 @@ namespace osu.Game.Tests.Visual
public TestCaseBreadcrumbs()
{
Add(breadcrumbs = new BreadcrumbControl<BreadcrumbTab>
{
Anchor = Anchor.Centre,
+1 -1
View File
@@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual
linkColour = colours.Blue;
var chatManager = new ChannelManager();
BindableCollection<Channel> availableChannels = (BindableCollection<Channel>)chatManager.AvailableChannels;
BindableList<Channel> availableChannels = (BindableList<Channel>)chatManager.AvailableChannels;
availableChannels.Add(new Channel { Name = "#english"});
availableChannels.Add(new Channel { Name = "#japanese" });
Dependencies.Cache(chatManager);
@@ -0,0 +1,67 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCaseDrawableDate : OsuTestCase
{
public TestCaseDrawableDate()
{
Child = new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new Drawable[]
{
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(60))),
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(55))),
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(50))),
new PokeyDrawableDate(DateTimeOffset.Now),
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(60))),
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(65))),
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(70))),
}
};
}
private class PokeyDrawableDate : CompositeDrawable
{
public PokeyDrawableDate(DateTimeOffset date)
{
const float box_size = 10;
DrawableDate drawableDate;
Box flash;
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
flash = new Box
{
Colour = Color4.Yellow,
Size = new Vector2(box_size),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Alpha = 0
},
drawableDate = new DrawableDate(date)
{
X = box_size + 2,
}
};
drawableDate.Current.ValueChanged += v => flash.FadeOutFromOne(500);
}
}
}
}
@@ -1,135 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Screens.Multi.Components;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseDrawableRoom : OsuTestCase
{
private RulesetStore rulesets;
protected override void LoadComplete()
{
base.LoadComplete();
DrawableRoom first;
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Y,
Width = 580f,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
first = new DrawableRoom(new Room
{
Name = { Value = @"Great Room Right Here" },
Host = { Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" } } },
Status = { Value = new RoomStatusOpen() },
Type = { Value = new GameTypeTeamVersus() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 4.65,
Ruleset = rulesets.GetRuleset(3),
Metadata = new BeatmapMetadata
{
Title = @"Critical Crystal",
Artist = @"Seiryu",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh//beatmaps/376340/covers/cover.jpg?1456478455",
},
},
},
},
},
Participants =
{
Value = new[]
{
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1355 } } },
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 8756 } } },
},
},
}),
new DrawableRoom(new Room
{
Name = { Value = @"Relax It's The Weekend" },
Host = { Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" } } },
Status = { Value = new RoomStatusPlaying() },
Type = { Value = new GameTypeTagTeam() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 1.96,
Ruleset = rulesets.GetRuleset(0),
Metadata = new BeatmapMetadata
{
Title = @"Serendipity",
Artist = @"ZAQ",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh//beatmaps/526839/covers/cover.jpg?1493815706",
},
},
},
},
},
Participants =
{
Value = new[]
{
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 578975 } } },
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24554 } } },
},
},
}),
}
});
AddStep(@"select", () => first.State = SelectionState.Selected);
AddStep(@"change title", () => first.Room.Name.Value = @"I Changed Name");
AddStep(@"change host", () => first.Room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } });
AddStep(@"change status", () => first.Room.Status.Value = new RoomStatusPlaying());
AddStep(@"change type", () => first.Room.Type.Value = new GameTypeVersus());
AddStep(@"change beatmap", () => first.Room.Beatmap.Value = null);
AddStep(@"change participants", () => first.Room.Participants.Value = new[]
{
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1254 } } },
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 123189 } } },
});
AddStep(@"deselect", () => first.State = SelectionState.NotSelected);
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
}
}
@@ -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);
@@ -39,7 +39,6 @@ namespace osu.Game.Tests.Visual
},
};
AddStep("Add random", () =>
{
Key key = (Key)((int)Key.A + RNG.Next(26));
+3 -2
View File
@@ -11,6 +11,7 @@ using osu.Framework.Allocation;
using osuTK;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets;
using osu.Game.Scoring;
@@ -36,7 +37,7 @@ namespace osu.Game.Tests.Visual
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = LeaderboardScope.Global,
Scope = BeatmapLeaderboardScope.Global,
});
AddStep(@"New Scores", newScores);
@@ -275,7 +276,7 @@ namespace osu.Game.Tests.Visual
};
}
private class FailableLeaderboard : Leaderboard
private class FailableLeaderboard : BeatmapLeaderboard
{
public void SetRetrievalState(PlaceholderState state)
{
-216
View File
@@ -1,216 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Screens;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Multi.Components;
using osu.Game.Screens.Multi.Screens.Lounge;
using osu.Game.Users;
using osuTK.Input;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseLounge : ManualInputManagerTestCase
{
private TestLounge lounge;
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
lounge = new TestLounge();
Room[] rooms =
{
new Room
{
Name = { Value = @"Just Another Room" },
Host = { Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } } },
Status = { Value = new RoomStatusPlaying() },
Availability = { Value = RoomAvailability.Public },
Type = { Value = new GameTypeTagTeam() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 5.65,
Ruleset = rulesets.GetRuleset(0),
Metadata = new BeatmapMetadata
{
Title = @"Sidetracked Day (Short Ver.)",
Artist = @"VINXIS",
AuthorString = @"Hobbes2",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/767600/covers/cover.jpg?1526243446",
},
},
},
}
},
MaxParticipants = { Value = 10 },
Participants =
{
Value = new[]
{
new User { Username = @"flyte", Id = 3103765, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 142 } } },
new User { Username = @"Cookiezi", Id = 124493, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 546 } } },
new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 287 } } },
new User { Username = @"Rafis", Id = 2558286, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 468 } } },
new User { Username = @"hvick225", Id = 50265, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 325 } } },
new User { Username = @"peppy", Id = 2, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 625 } } },
}
}
},
new Room
{
Name = { Value = @"Not Just Any Room" },
Host = { Value = new User { Username = @"Monstrata", Id = 2706438, Country = new Country { FlagName = @"CA" } } },
Status = { Value = new RoomStatusOpen() },
Availability = { Value = RoomAvailability.FriendsOnly },
Type = { Value = new GameTypeTeamVersus() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 2.73,
Ruleset = rulesets.GetRuleset(0),
Metadata = new BeatmapMetadata
{
Title = @"lit(var)",
Artist = @"kensuke ushio",
AuthorString = @"Monstrata",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/623972/covers/cover.jpg?1521167183",
},
},
},
}
},
Participants =
{
Value = new[]
{
new User { Username = @"Jeby", Id = 3136279, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 3497 } } },
new User { Username = @"DualAkira", Id = 5220933, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 643 } } },
new User { Username = @"Datenshi Yohane", Id = 7171857, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 10555 } } },
}
}
},
new Room
{
Name = { Value = @"room THE FINAL" },
Host = { Value = new User { Username = @"Delis", Id = 1603923, Country = new Country { FlagName = @"JP" } } },
Status = { Value = new RoomStatusPlaying() },
Availability = { Value = RoomAvailability.Public },
Type = { Value = new GameTypeTagTeam() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 4.48,
Ruleset = rulesets.GetRuleset(3),
Metadata = new BeatmapMetadata
{
Title = @"ONIGIRI FREEWAY",
Artist = @"OISHII",
AuthorString = @"Mentholzzz",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/663098/covers/cover.jpg?1521898837",
},
},
},
}
},
MaxParticipants = { Value = 30 },
Participants =
{
Value = new[]
{
new User { Username = @"KizuA", Id = 6510442, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 5372 } } },
new User { Username = @"Colored", Id = 827563, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 810 } } },
new User { Username = @"Beryl", Id = 3817591, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 10096 } } },
}
}
},
};
AddStep(@"show", () => Add(lounge));
AddStep(@"set rooms", () => lounge.Rooms = rooms);
selectAssert(0);
AddStep(@"clear rooms", () => lounge.Rooms = new Room[] {});
AddAssert(@"no room selected", () => lounge.SelectedRoom == null);
AddStep(@"set rooms", () => lounge.Rooms = rooms);
selectAssert(1);
AddStep(@"open room 1", () => clickRoom(1));
AddUntilStep(() => lounge.ChildScreen?.IsCurrentScreen == true, "wait until room current");
AddStep(@"make lounge current", lounge.MakeCurrent);
filterAssert(@"THE FINAL", LoungeTab.Public, 1);
filterAssert(string.Empty, LoungeTab.Public, 2);
filterAssert(string.Empty, LoungeTab.Private, 1);
filterAssert(string.Empty, LoungeTab.Public, 2);
filterAssert(@"no matches", LoungeTab.Public, 0);
AddStep(@"clear rooms", () => lounge.Rooms = new Room[] {});
AddStep(@"set rooms", () => lounge.Rooms = rooms);
AddAssert(@"no matches after clear", () => !lounge.ChildRooms.Any());
filterAssert(string.Empty, LoungeTab.Public, 2);
AddStep(@"exit", lounge.Exit);
}
private void clickRoom(int n)
{
InputManager.MoveMouseTo(lounge.ChildRooms.ElementAt(n));
InputManager.Click(MouseButton.Left);
}
private void selectAssert(int n)
{
AddStep($@"select room {n}", () => clickRoom(n));
AddAssert($@"room {n} selected", () => lounge.SelectedRoom == lounge.ChildRooms.ElementAt(n).Room);
}
private void filterAssert(string filter, LoungeTab tab, int endCount)
{
AddStep($@"filter '{filter}', {tab}", () => lounge.SetFilter(filter, tab));
AddAssert(@"filtered correctly", () => lounge.ChildRooms.Count() == endCount);
}
private class TestLounge : Lounge
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault();
public IEnumerable<DrawableRoom> ChildRooms => RoomsContainer.Children.Where(r => r.MatchingFilter);
public Room SelectedRoom => Inspector.Room;
public void SetFilter(string filter, LoungeTab tab)
{
Filter.Search.Current.Value = filter;
Filter.Tabs.Current.Value = tab;
}
}
}
}
@@ -0,0 +1,101 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi;
using osu.Game.Screens.Multi.Lounge.Components;
using osu.Game.Users;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCaseLoungeRoomsContainer : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(RoomsContainer),
typeof(DrawableRoom)
};
[Cached(Type = typeof(IRoomManager))]
private TestRoomManager roomManager = new TestRoomManager();
public TestCaseLoungeRoomsContainer()
{
RoomsContainer container;
Child = container = new RoomsContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 0.5f,
JoinRequested = joinRequested
};
AddStep("clear rooms", () => roomManager.Rooms.Clear());
AddStep("add rooms", () =>
{
for (int i = 0; i < 3; i++)
{
roomManager.Rooms.Add(new Room
{
RoomID = { Value = i },
Name = { Value = $"Room {i}" },
Host = { Value = new User { Username = "Host" } },
EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }
});
}
});
AddAssert("has 2 rooms", () => container.Rooms.Count == 3);
AddStep("remove first room", () => roomManager.Rooms.Remove(roomManager.Rooms.FirstOrDefault()));
AddAssert("has 2 rooms", () => container.Rooms.Count == 2);
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());
AddStep("join first room", () => container.Rooms.First().Action?.Invoke());
AddAssert("first room joined", () => roomManager.Rooms.First().Status.Value is JoinedRoomStatus);
}
private void joinRequested(Room room) => room.Status.Value = new JoinedRoomStatus();
private class TestRoomManager : IRoomManager
{
public event Action RoomsUpdated;
public readonly BindableList<Room> Rooms = new BindableList<Room>();
IBindableList<Room> IRoomManager.Rooms => Rooms;
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) => Rooms.Add(room);
public void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{
}
public void PartRoom()
{
}
public void Filter(FilterCriteria criteria)
{
}
}
private class JoinedRoomStatus : RoomStatus
{
public override string Message => "Joined";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.Yellow;
}
}
}
-142
View File
@@ -1,142 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Screens.Multi.Screens.Match;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseMatch : OsuTestCase
{
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
Room room = new Room
{
Name = { Value = @"One Awesome Room" },
Status = { Value = new RoomStatusOpen() },
Availability = { Value = RoomAvailability.Public },
Type = { Value = new GameTypeTeamVersus() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 5.02,
Ruleset = rulesets.GetRuleset(1),
Metadata = new BeatmapMetadata
{
Title = @"Paradigm Shift",
Artist = @"Morimori Atsushi",
AuthorString = @"eiri-",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/765055/covers/cover.jpg?1526955337",
},
},
},
},
},
MaxParticipants = { Value = 5 },
Participants =
{
Value = new[]
{
new User
{
Username = @"eiri-",
Id = 3388410,
Country = new Country { FlagName = @"US" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/3388410/00a8486a247831e1cc4375db519f611ac970bda8bc0057d78b0f540ea38c3e58.jpeg",
IsSupporter = true,
},
new User
{
Username = @"Nepuri",
Id = 6637817,
Country = new Country { FlagName = @"DE" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/6637817/9085fc60248b6b5327a72c1dcdecf2dbedba810ae0ab6bcf7224e46b1339632a.jpeg",
IsSupporter = true,
},
new User
{
Username = @"goheegy",
Id = 8057655,
Country = new Country { FlagName = @"GB" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8057655/21cec27c25a11dc197a4ec6a74253dbabb495949b0e0697113352f12007018c5.jpeg",
},
new User
{
Username = @"Alumetri",
Id = 5371497,
Country = new Country { FlagName = @"RU" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/5371497/e023b8c7fbe3613e64bd4856703517ea50fbed8a5805dc9acda9efe9897c67e2.jpeg",
},
}
},
};
Match match = new Match(room);
AddStep(@"show", () => Add(match));
AddStep(@"null beatmap", () => room.Beatmap.Value = null);
AddStep(@"change name", () => room.Name.Value = @"Two Awesome Rooms");
AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying());
AddStep(@"change availability", () => room.Availability.Value = RoomAvailability.FriendsOnly);
AddStep(@"change type", () => room.Type.Value = new GameTypeTag());
AddStep(@"change beatmap", () => room.Beatmap.Value = new BeatmapInfo
{
StarDifficulty = 4.33,
Ruleset = rulesets.GetRuleset(2),
Metadata = new BeatmapMetadata
{
Title = @"Yasashisa no Riyuu",
Artist = @"ChouCho",
AuthorString = @"celerih",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/685391/covers/cover.jpg?1524597970",
},
},
},
});
AddStep(@"null max participants", () => room.MaxParticipants.Value = null);
AddStep(@"change participants", () => room.Participants.Value = new[]
{
new User
{
Username = @"Spectator",
Id = 702598,
Country = new Country { FlagName = @"KR" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/702598/3bbf4cb8b8d2cf8b03145000a975ff27e191ab99b0920832e7dd67386280e288.jpeg",
IsSupporter = true,
},
new User
{
Username = @"celerih",
Id = 4696296,
Country = new Country { FlagName = @"CA" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/4696296/7f8500731d0ac66d5472569d146a7be07d9460273361913f22c038867baddaef.jpeg",
},
});
AddStep(@"exit", match.Exit);
}
}
}
+31 -20
View File
@@ -1,43 +1,54 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Screens.Multi.Screens.Match;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.GameTypes;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseMatchHeader : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Header)
};
public TestCaseMatchHeader()
{
Header header = new Header();
Add(header);
var room = new Room();
AddStep(@"set beatmap set", () => header.BeatmapSet = new BeatmapSetInfo
var header = new Header(room);
room.Playlist.Add(new PlaylistItem
{
OnlineInfo = new BeatmapSetOnlineInfo
Beatmap = new BeatmapInfo
{
Covers = new BeatmapSetOnlineCovers
Metadata = new BeatmapMetadata
{
Cover = @"https://assets.ppy.sh/beatmaps/760757/covers/cover.jpg?1526944540",
Title = "Title",
Artist = "Artist",
AuthorString = "Author",
},
Version = "Version",
Ruleset = new OsuRuleset().RulesetInfo
},
RequiredMods =
{
new OsuModDoubleTime(),
new OsuModNoFail(),
new OsuModRelax(),
}
});
AddStep(@"change beatmap set", () => header.BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/761883/covers/cover.jpg?1525557400",
},
},
});
room.Type.Value = new GameTypeTimeshift();
AddStep(@"null beatmap set", () => header.BeatmapSet = null);
Child = header;
}
}
}
@@ -0,0 +1,35 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
public class TestCaseMatchHostInfo : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(HostInfo)
};
private readonly Bindable<User> host = new Bindable<User>(new User { Username = "SomeHost" });
public TestCaseMatchHostInfo()
{
HostInfo hostInfo;
Child = hostInfo = new HostInfo
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
};
hostInfo.Host.BindTo(host);
}
}
}
+52 -28
View File
@@ -1,56 +1,80 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.RoomStatuses;
using osu.Game.Rulesets;
using osu.Game.Screens.Multi.Screens.Match;
using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseMatchInfo : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Info),
typeof(HeaderButton),
typeof(ReadyButton),
typeof(ViewBeatmapButton)
};
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
Info info = new Info();
var room = new Room();
Info info = new Info(room);
Add(info);
AddStep(@"set name", () => info.Name = @"Room Name?");
AddStep(@"set availability", () => info.Availability = RoomAvailability.FriendsOnly);
AddStep(@"set status", () => info.Status = new RoomStatusPlaying());
AddStep(@"set beatmap", () => info.Beatmap = new BeatmapInfo
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", () =>
{
StarDifficulty = 2.4,
Ruleset = rulesets.GetRuleset(0),
Metadata = new BeatmapMetadata
room.Playlist.Clear();
room.Playlist.Add(new PlaylistItem
{
Title = @"My Song",
Artist = @"VisualTests",
AuthorString = @"osu!lazer",
},
Beatmap = new BeatmapInfo
{
StarDifficulty = 2.4,
Ruleset = rulesets.GetRuleset(0),
Metadata = new BeatmapMetadata
{
Title = @"My Song",
Artist = @"VisualTests",
AuthorString = @"osu!lazer",
},
}
});
});
AddStep(@"set type", () => info.Type = new GameTypeTagTeam());
AddStep(@"change name", () => info.Name = @"Room Name!");
AddStep(@"change availability", () => info.Availability = RoomAvailability.InviteOnly);
AddStep(@"change status", () => info.Status = new RoomStatusOpen());
AddStep(@"null beatmap", () => info.Beatmap = null);
AddStep(@"change type", () => info.Type = new GameTypeTeamVersus());
AddStep(@"change beatmap", () => info.Beatmap = new BeatmapInfo
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", () =>
{
StarDifficulty = 4.2,
Ruleset = rulesets.GetRuleset(3),
Metadata = new BeatmapMetadata
room.Playlist.Clear();
room.Playlist.Add(new PlaylistItem
{
Title = @"Your Song",
Artist = @"Tester",
AuthorString = @"Someone",
},
Beatmap = new BeatmapInfo
{
StarDifficulty = 4.2,
Ruleset = rulesets.GetRuleset(3),
Metadata = new BeatmapMetadata
{
Title = @"Your Song",
Artist = @"Tester",
AuthorString = @"Someone",
},
}
});
});
}
}
@@ -0,0 +1,69 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using 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 TestCaseMatchLeaderboard()
{
Add(new MatchLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = MatchLeaderboardScope.Overall,
Room = new Room { RoomID = { Value = 3 } }
});
}
[Resolved]
private APIAccess api { get; set; }
[BackgroundDependencyLoader]
private void load()
{
var req = new GetRoomScoresRequest();
req.Success += v => { };
req.Failure += _ => { };
api.Queue(req);
}
private class GetRoomScoresRequest : APIRequest<List<RoomScore>>
{
protected override string Target => "rooms/3/leaderboard";
}
private class RoomScore
{
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("accuracy")]
public double Accuracy { get; set; }
[JsonProperty("total_score")]
public int TotalScore { get; set; }
[JsonProperty("pp")]
public double PP { get; set; }
[JsonProperty("attempts")]
public int TotalAttempts { get; set; }
[JsonProperty("completed")]
public int CompletedAttempts { get; set; }
}
}
}
@@ -1,9 +1,11 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Screens.Multi.Screens.Match;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
@@ -11,16 +13,20 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCaseMatchParticipants : OsuTestCase
{
private readonly Bindable<int?> maxParticipants = new Bindable<int?>();
private readonly Bindable<IEnumerable<User>> users = new Bindable<IEnumerable<User>>();
public TestCaseMatchParticipants()
{
Participants participants;
Add(participants = new Participants
{
RelativeSizeAxes = Axes.Both,
});
AddStep(@"set max to null", () => participants.Max = null);
AddStep(@"set users", () => participants.Users = new[]
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[]
{
new User
{
@@ -48,9 +54,9 @@ namespace osu.Game.Tests.Visual
},
});
AddStep(@"set max", () => participants.Max = 10);
AddStep(@"clear users", () => participants.Users = new User[] { });
AddStep(@"set max to null", () => participants.Max = null);
AddStep(@"set max", () => maxParticipants.Value = 10);
AddStep(@"clear users", () => users.Value = new User[] { });
AddStep(@"set max to null", () => maxParticipants.Value = null);
}
}
}
@@ -0,0 +1,123 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
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;
using osu.Game.Screens.Multi.Ranking.Pages;
using osu.Game.Screens.Multi.Ranking.Types;
using osu.Game.Screens.Ranking;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
public class TestCaseMatchResults : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(MatchResults),
typeof(RoomLeaderboardPageInfo),
typeof(RoomLeaderboardPage)
};
[Resolved]
private BeatmapManager beatmaps { get; set; }
[BackgroundDependencyLoader]
private void load()
{
var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
Child = 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" }
})
{
}
public TestMatchResults(ScoreInfo score, Room room)
: base(score, room)
{
this.room = room;
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value, room) };
}
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)
{
this.score = score;
this.beatmap = beatmap;
this.room = room;
}
public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap, room);
}
private class TestRoomLeaderboardPage : RoomLeaderboardPage
{
public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap, Room room)
: base(score, beatmap, room)
{
}
protected override MatchLeaderboard CreateLeaderboard(Room room) => new TestMatchLeaderboard(room);
}
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();
scoresCallback?.Invoke(scores);
ScoresLoaded?.Invoke(scores);
return null;
}
private APIRoomScoreInfo createRoomScore(int id) => new APIRoomScoreInfo
{
User = new User { Id = id, Username = $"User {id}" },
Accuracy = 0.98,
TotalScore = 987654,
TotalAttempts = 13,
CompletedBeatmaps = 5
};
}
}
}
@@ -0,0 +1,161 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
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 override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(MatchSettingsOverlay)
};
[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)
{
RelativeSizeAxes = Axes.Both,
State = Visibility.Visible
};
Child = settings;
});
[Test]
public void TestButtonEnabledOnlyWithNameAndBeatmap()
{
AddStep("clear name and beatmap", () =>
{
room.Name.Value = "";
room.Playlist.Clear();
});
AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
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 }));
AddAssert("button enabled", () => settings.ApplyButton.Enabled);
AddStep("clear name", () => room.Name.Value = "");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
}
[Test]
public void TestCorrectSettingsApplied()
{
const string expected_name = "expected name";
TimeSpan expectedDuration = TimeSpan.FromMinutes(15);
Room createdRoom = null;
AddStep("setup", () =>
{
settings.NameField.Current.Value = expected_name;
settings.DurationField.Current.Value = expectedDuration;
roomManager.CreateRequested = r =>
{
createdRoom = r;
return true;
};
});
AddStep("create room", () => settings.ApplyButton.Action.Invoke());
AddAssert("has correct name", () => createdRoom.Name.Value == expected_name);
AddAssert("has correct duration", () => createdRoom.Duration.Value == expectedDuration);
}
[Test]
public void TestCreationFailureDisplaysError()
{
bool fail;
AddStep("setup", () =>
{
fail = true;
roomManager.CreateRequested = _ => !fail;
});
AddAssert("error not displayed", () => !settings.ErrorText.IsPresent);
AddStep("create room", () => settings.ApplyButton.Action.Invoke());
AddAssert("error displayed", () => settings.ErrorText.IsPresent);
AddAssert("error has correct text", () => settings.ErrorText.Text == TestRoomManager.FAILED_TEXT);
AddStep("create room no fail", () =>
{
fail = false;
settings.ApplyButton.Action.Invoke();
});
AddUntilStep(() => !settings.ErrorText.IsPresent, "error not displayed");
}
private class TestRoomSettings : MatchSettingsOverlay
{
public new TriangleButton ApplyButton => base.ApplyButton;
public new OsuTextBox NameField => base.NameField;
public new OsuDropdown<TimeSpan> DurationField => base.DurationField;
public new OsuSpriteText ErrorText => base.ErrorText;
public TestRoomSettings(Room room)
: base(room)
{
}
}
private class TestRoomManager : IRoomManager
{
public const string FAILED_TEXT = "failed";
public Func<Room, bool> CreateRequested;
public event Action RoomsUpdated;
public IBindableList<Room> Rooms { get; } = null;
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{
if (CreateRequested == null)
return;
if (!CreateRequested.Invoke(room))
onError?.Invoke(FAILED_TEXT);
else
onSuccess?.Invoke(room);
}
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();
}
}
}
+23 -7
View File
@@ -3,8 +3,8 @@
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Screens;
using osu.Game.Screens.Multi;
using osu.Game.Screens.Multi.Screens.Lounge;
namespace osu.Game.Tests.Visual
{
@@ -13,15 +13,31 @@ namespace osu.Game.Tests.Visual
{
public TestCaseMultiHeader()
{
Lounge lounge;
int index = 0;
OsuScreen currentScreen = new TestMultiplayerSubScreen(index);
Children = new Drawable[]
{
lounge = new Lounge
{
Padding = new MarginPadding { Top = Header.HEIGHT },
},
new Header(lounge),
currentScreen,
new Header(currentScreen)
};
AddStep("push multi screen", () => currentScreen.Push(currentScreen = new TestMultiplayerSubScreen(++index)));
}
private class TestMultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen
{
private readonly int index;
public string ShortTitle => $"Screen {index}";
public TestMultiplayerSubScreen(int index)
{
this.index = index;
}
public override string ToString() => ShortTitle;
}
}
}
@@ -1,14 +1,25 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Game.Screens.Multi;
using osu.Game.Screens.Multi.Lounge;
using osu.Game.Screens.Multi.Lounge.Components;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseMultiScreen : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Multiplayer),
typeof(LoungeSubScreen),
typeof(FilterControl)
};
public TestCaseMultiScreen()
{
Multiplayer multi = new Multiplayer();
@@ -49,7 +49,6 @@ namespace osu.Game.Tests.Visual
manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; };
setState(Visibility.Visible);
AddStep(@"simple #1", sendHelloNotification);
AddStep(@"simple #2", sendAmazingNotification);
@@ -75,7 +74,6 @@ namespace osu.Game.Tests.Visual
checkProgressingCount(0);
setState(Visibility.Visible);
//AddStep(@"barrage", () => sendBarrage());
@@ -111,7 +109,7 @@ namespace osu.Game.Tests.Visual
if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3)
{
var p = progressingNotifications.FirstOrDefault(n => n.State == ProgressNotificationState.Queued);
var p = progressingNotifications.Find(n => n.State == ProgressNotificationState.Queued);
if (p != null)
p.State = ProgressNotificationState.Active;
}
@@ -57,11 +57,19 @@ namespace osu.Game.Tests.Visual
private class TestSongSelect : PlaySongSelect
{
public Action StartRequested;
public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap;
public new BeatmapCarousel Carousel => base.Carousel;
protected override bool OnStart()
{
StartRequested?.Invoke();
return base.OnStart();
}
}
private TestSongSelect songSelect;
@@ -87,10 +95,7 @@ namespace osu.Game.Tests.Visual
usage.Migrate();
Dependencies.Cache(rulesets = new RulesetStore(factory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null)
{
DefaultBeatmap = defaultBeatmap = Beatmap.Default
});
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null, null, defaultBeatmap = Beatmap.Default));
Beatmap.SetDefault();
}
@@ -185,6 +190,27 @@ namespace osu.Game.Tests.Visual
void onRulesetChange(RulesetInfo ruleset) => rulesetChangeIndex = actionIndex--;
}
[Test]
public void TestStartAfterUnMatchingFilterDoesNotStart()
{
addManyTestMaps();
AddUntilStep(() => songSelect.Carousel.SelectedBeatmap != null, "has selection");
bool startRequested = false;
AddStep("set filter and finalize", () =>
{
songSelect.StartRequested = () => startRequested = true;
songSelect.Carousel.Filter(new FilterCriteria { SearchText = "somestringthatshouldn'tbematchable" });
songSelect.FinaliseSelection();
songSelect.StartRequested = null;
});
AddAssert("start not requested", () => !startRequested);
}
private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())));
private static int importId;
@@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual
{
Beatmap.Value = new DummyWorkingBeatmap(game);
AddStep("load dummy beatmap", () => Add(loader = new PlayerLoader(new Player
AddStep("load dummy beatmap", () => Add(loader = new PlayerLoader(() => new Player
{
AllowPause = false,
AllowLeadIn = false,
@@ -30,9 +30,9 @@ namespace osu.Game.Tests.Visual
AddStep("load slow dummy beatmap", () =>
{
SlowLoadPlayer slow;
SlowLoadPlayer slow = null;
Add(loader = new PlayerLoader(slow = new SlowLoadPlayer
Add(loader = new PlayerLoader(() => slow = new SlowLoadPlayer
{
AllowPause = false,
AllowLeadIn = false,
@@ -57,5 +57,4 @@ namespace osu.Game.Tests.Visual
}
}
}
}
+1 -2
View File
@@ -5,7 +5,6 @@ using System.ComponentModel;
using System.Linq;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
@@ -23,7 +22,7 @@ namespace osu.Game.Tests.Visual
// Reset the mods
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Where(m => !(m is ModAutoplay));
return new ReplayPlayer(new Score { Replay = dummyRulesetContainer.Replay });
return new ReplayPlayer(dummyRulesetContainer.ReplayScore);
}
}
}
+5 -3
View File
@@ -8,7 +8,9 @@ using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Pages;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
@@ -23,8 +25,8 @@ namespace osu.Game.Tests.Visual
typeof(ScoreInfo),
typeof(Results),
typeof(ResultsPage),
typeof(ResultsPageScore),
typeof(ResultsPageRanking)
typeof(ScoreResultsPage),
typeof(LocalLeaderboardPage)
};
[BackgroundDependencyLoader]
@@ -41,7 +43,7 @@ namespace osu.Game.Tests.Visual
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
Add(new Results(new ScoreInfo
Add(new SoloResults(new ScoreInfo
{
TotalScore = 2845370,
Accuracy = 0.98,
@@ -1,147 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Screens.Multi.Components;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseRoomInspector : OsuTestCase
{
private RulesetStore rulesets;
protected override void LoadComplete()
{
base.LoadComplete();
Room room = new Room
{
Name = { Value = @"My Awesome Room" },
Host = { Value = new User { Username = @"flyte", Id = 3103765, Country = new Country { FlagName = @"JP" } } },
Status = { Value = new RoomStatusOpen() },
Type = { Value = new GameTypeTeamVersus() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 3.7,
Ruleset = rulesets.GetRuleset(3),
Metadata = new BeatmapMetadata
{
Title = @"Platina",
Artist = @"Maaya Sakamoto",
AuthorString = @"uwutm8",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/560573/covers/cover.jpg?1492722343",
},
},
},
}
},
MaxParticipants = { Value = 200 },
Participants =
{
Value = new[]
{
new User { Username = @"flyte", Id = 3103765, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 142 } } },
new User { Username = @"Cookiezi", Id = 124493, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 546 } } },
new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 287 } } },
new User { Username = @"Rafis", Id = 2558286, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 468 } } },
new User { Username = @"hvick225", Id = 50265, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 325 } } },
new User { Username = @"peppy", Id = 2, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 625 } } },
}
}
};
RoomInspector inspector;
Add(inspector = new RoomInspector
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Width = 0.5f,
});
AddStep(@"set room", () => inspector.Room = room);
AddStep(@"null room", () => inspector.Room = null);
AddStep(@"set room", () => inspector.Room = room);
AddStep(@"change title", () => room.Name.Value = @"A Better Room Than The Above");
AddStep(@"change host", () => room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } });
AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying());
AddStep(@"change type", () => room.Type.Value = new GameTypeTag());
AddStep(@"change beatmap", () => room.Beatmap.Value = null);
AddStep(@"change max participants", () => room.MaxParticipants.Value = null);
AddStep(@"change participants", () => room.Participants.Value = new[]
{
new User { Username = @"filsdelama", Id = 2831793, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 854 } } },
new User { Username = @"_index", Id = 652457, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 150 } } }
});
AddStep(@"change room", () =>
{
Room newRoom = new Room
{
Name = { Value = @"My New, Better Than Ever Room" },
Host = { Value = new User { Username = @"Angelsim", Id = 1777162, Country = new Country { FlagName = @"KR" } } },
Status = { Value = new RoomStatusOpen() },
Type = { Value = new GameTypeTagTeam() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 7.07,
Ruleset = rulesets.GetRuleset(0),
Metadata = new BeatmapMetadata
{
Title = @"FREEDOM DIVE",
Artist = @"xi",
AuthorString = @"Nakagawa-Kanon",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/39804/covers/cover.jpg?1456506845",
},
},
},
},
},
MaxParticipants = { Value = 10 },
Participants =
{
Value = new[]
{
new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 4 } } },
new User { Username = @"HappyStick", Id = 256802, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 752 } } },
new User { Username = @"-Konpaku-", Id = 2258797, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 571 } } }
}
}
};
inspector.Room = newRoom;
});
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
}
}
@@ -1,119 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Testing.Input;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Screens.Match.Settings;
using osuTK.Input;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseRoomSettings : ManualInputManagerTestCase
{
private readonly Room room;
private readonly TestRoomSettingsOverlay overlay;
public TestCaseRoomSettings()
{
room = new Room
{
Name = { Value = "One Testing Room" },
Availability = { Value = RoomAvailability.Public },
Type = { Value = new GameTypeTeamVersus() },
MaxParticipants = { Value = 10 },
};
Add(overlay = new TestRoomSettingsOverlay(room)
{
RelativeSizeAxes = Axes.Both,
Height = 0.75f,
});
AddStep(@"show", overlay.Show);
assertAll();
AddStep(@"set name", () => overlay.CurrentName = @"Two Testing Room");
AddStep(@"set max", () => overlay.CurrentMaxParticipants = null);
AddStep(@"set availability", () => overlay.CurrentAvailability = RoomAvailability.InviteOnly);
AddStep(@"set type", () => overlay.CurrentType = new GameTypeTagTeam());
apply();
assertAll();
AddStep(@"show", overlay.Show);
AddStep(@"set room name", () => room.Name.Value = @"Room Changed Name!");
AddStep(@"set room availability", () => room.Availability.Value = RoomAvailability.Public);
AddStep(@"set room type", () => room.Type.Value = new GameTypeTag());
AddStep(@"set room max", () => room.MaxParticipants.Value = 100);
assertAll();
AddStep(@"set name", () => overlay.CurrentName = @"Unsaved Testing Room");
AddStep(@"set max", () => overlay.CurrentMaxParticipants = 20);
AddStep(@"set availability", () => overlay.CurrentAvailability = RoomAvailability.FriendsOnly);
AddStep(@"set type", () => overlay.CurrentType = new GameTypeVersus());
AddStep(@"hide", overlay.Hide);
AddWaitStep(5);
AddStep(@"show", overlay.Show);
assertAll();
AddStep(@"hide", overlay.Hide);
}
private void apply()
{
AddStep(@"apply", () =>
{
overlay.ClickApplyButton(InputManager);
});
}
private void assertAll()
{
AddAssert(@"name == room name", () => overlay.CurrentName == room.Name.Value);
AddAssert(@"max == room max", () => overlay.CurrentMaxParticipants == room.MaxParticipants.Value);
AddAssert(@"availability == room availability", () => overlay.CurrentAvailability == room.Availability.Value);
AddAssert(@"type == room type", () => Equals(overlay.CurrentType, room.Type.Value));
}
private class TestRoomSettingsOverlay : RoomSettingsOverlay
{
public string CurrentName
{
get => NameField.Text;
set => NameField.Text = value;
}
public int? CurrentMaxParticipants
{
get
{
if (int.TryParse(MaxParticipantsField.Text, out int max))
return max;
return null;
}
set => MaxParticipantsField.Text = value?.ToString();
}
public RoomAvailability CurrentAvailability
{
get => AvailabilityPicker.Current.Value;
set => AvailabilityPicker.Current.Value = value;
}
public GameType CurrentType
{
get => TypePicker.Current.Value;
set => TypePicker.Current.Value = value;
}
public TestRoomSettingsOverlay(Room room) : base(room)
{
}
public void ClickApplyButton(ManualInputManager inputManager)
{
inputManager.MoveMouseTo(ApplyButton);
inputManager.Click(MouseButton.Left);
}
}
}
}
@@ -0,0 +1,50 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.RoomStatuses;
using osu.Game.Screens.Multi.Lounge.Components;
namespace osu.Game.Tests.Visual
{
public class TestCaseRoomStatus : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(RoomStatusEnded),
typeof(RoomStatusOpen),
typeof(RoomStatusPlaying)
};
public TestCaseRoomStatus()
{
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Width = 0.5f,
Children = new Drawable[]
{
new DrawableRoom(new Room
{
Name = { Value = "Room 1" },
Status = { Value = new RoomStatusOpen() }
}),
new DrawableRoom(new Room
{
Name = { Value = "Room 2" },
Status = { Value = new RoomStatusPlaying() }
}),
new DrawableRoom(new Room
{
Name = { Value = "Room 3" },
Status = { Value = new RoomStatusEnded() }
}),
}
};
}
}
}
@@ -7,7 +7,7 @@ using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseSkipButton : OsuTestCase
public class TestCaseSkipOverlay : OsuTestCase
{
protected override void LoadComplete()
{
@@ -0,0 +1,104 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Online.Chat;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual
{
public class TestCaseStandAloneChatDisplay : OsuTestCase
{
private readonly Channel testChannel = new Channel();
private readonly User admin = new User
{
Username = "HappyStick",
Id = 2,
Colour = "f2ca34"
};
private readonly User redUser = new User
{
Username = "BanchoBot",
Id = 3,
};
private readonly User blueUser = new User
{
Username = "Zallius",
Id = 4,
};
[Cached]
private ChannelManager channelManager = new ChannelManager();
private readonly StandAloneChatDisplay chatDisplay;
private readonly StandAloneChatDisplay chatDisplay2;
public TestCaseStandAloneChatDisplay()
{
Add(channelManager);
Add(chatDisplay = new StandAloneChatDisplay
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Margin = new MarginPadding(20),
Size = new Vector2(400, 80)
});
Add(chatDisplay2 = new StandAloneChatDisplay(true)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding(20),
Size = new Vector2(400, 150)
});
}
protected override void LoadComplete()
{
base.LoadComplete();
channelManager.CurrentChannel.Value = testChannel;
chatDisplay.Channel.Value = testChannel;
chatDisplay2.Channel.Value = testChannel;
int sequence = 0;
AddStep("message from admin", () => testChannel.AddNewMessages(new Message(sequence++)
{
Sender = admin,
Content = "I am a wang!"
}));
AddStep("message from team red", () => testChannel.AddNewMessages(new Message(sequence++)
{
Sender = redUser,
Content = "I am team red."
}));
AddStep("message from team red", () => testChannel.AddNewMessages(new Message(sequence++)
{
Sender = redUser,
Content = "I plan to win!"
}));
AddStep("message from team blue", () => testChannel.AddNewMessages(new Message(sequence++)
{
Sender = blueUser,
Content = "Not on my watch. Prepare to eat saaaaaaaaaand. Lots and lots of saaaaaaand."
}));
AddStep("message from admin", () => testChannel.AddNewMessages(new Message(sequence++)
{
Sender = admin,
Content = "Okay okay, calm down guys. Let's do this!"
}));
}
}
}
@@ -0,0 +1,57 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
using osu.Game.Tests.Beatmaps.IO;
namespace osu.Game.Tests.Visual
{
public class TestCaseUpdateableBeatmapBackgroundSprite : OsuTestCase
{
private UpdateableBeatmapBackgroundSprite backgroundSprite;
[Resolved]
private BeatmapManager beatmaps { get; set; }
[BackgroundDependencyLoader]
private void load(OsuGameBase osu, APIAccess api, RulesetStore rulesets)
{
Bindable<BeatmapInfo> beatmapBindable = new Bindable<BeatmapInfo>();
var imported = ImportBeatmapTest.LoadOszIntoOsu(osu);
Child = backgroundSprite = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both };
backgroundSprite.Beatmap.BindTo(beatmapBindable);
var req = new GetBeatmapSetRequest(1);
api.Queue(req);
AddStep("null", () => beatmapBindable.Value = null);
AddStep("imported", () => beatmapBindable.Value = imported.Beatmaps.First());
if (api.IsLoggedIn)
{
AddUntilStep(() => req.Result != null, "wait for api response");
AddStep("online", () => beatmapBindable.Value = new BeatmapInfo
{
BeatmapSet = req.Result?.ToBeatmapSet(rulesets)
});
}
else
{
AddStep("online (login first)", () => { });
}
}
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
<PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">
+7
View File
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
@@ -9,6 +10,7 @@ using Newtonsoft.Json;
using osu.Game.Database;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
using osu.Game.Scoring;
namespace osu.Game.Beatmaps
{
@@ -112,6 +114,11 @@ namespace osu.Game.Beatmaps
[JsonProperty("difficulty_rating")]
public double StarDifficulty { get; set; }
/// <summary>
/// Currently only populated for beatmap deletion. Use <see cref="ScoreManager"/> to query scores.
/// </summary>
public List<ScoreInfo> Scores { get; set; }
public override string ToString() => $"{Metadata} [{Version}]";
public bool Equals(BeatmapInfo other)
+9 -6
View File
@@ -59,7 +59,7 @@ namespace osu.Game.Beatmaps
/// <summary>
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
/// </summary>
public WorkingBeatmap DefaultBeatmap { private get; set; }
public readonly WorkingBeatmap DefaultBeatmap;
public override string[] HandledExtensions => new[] { ".osz" };
@@ -77,16 +77,19 @@ namespace osu.Game.Beatmaps
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null)
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null,
WorkingBeatmap defaultBeatmap = null)
: base(storage, contextFactory, new BeatmapStore(contextFactory), importHost)
{
beatmaps = (BeatmapStore)ModelStore;
beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
this.rulesets = rulesets;
this.api = api;
this.audioManager = audioManager;
DefaultBeatmap = defaultBeatmap;
beatmaps = (BeatmapStore)ModelStore;
beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
}
protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive)
+1 -1
View File
@@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps
public string Hash { get; set; }
public string StoryboardFile => Files?.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename;
public string StoryboardFile => Files?.Find(f => f.Filename.EndsWith(".osb"))?.Filename;
public List<BeatmapSetFileInfo> Files { get; set; }
+2 -1
View File
@@ -64,7 +64,8 @@ namespace osu.Game.Beatmaps
base.AddIncludesForDeletion(query)
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
.Include(s => s.Metadata);
.Include(s => s.Metadata)
.Include(s => s.Beatmaps).ThenInclude(b => b.Scores);
protected override IQueryable<BeatmapSetInfo> AddIncludesForConsumption(IQueryable<BeatmapSetInfo> query) =>
base.AddIncludesForConsumption(query)
+2 -5
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.
@@ -23,7 +23,7 @@ namespace osu.Game.Beatmaps.Drawables
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
private void load(LargeTextureStore textures)
{
string resource = null;
@@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets;
using osuTK;
using osuTK.Graphics;
@@ -16,15 +17,16 @@ namespace osu.Game.Beatmaps.Drawables
{
public class DifficultyIcon : DifficultyColouredContainer
{
private readonly BeatmapInfo beatmap;
private readonly RulesetInfo ruleset;
public DifficultyIcon(BeatmapInfo beatmap)
public DifficultyIcon(BeatmapInfo beatmap, RulesetInfo ruleset = null)
: base(beatmap)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
this.beatmap = beatmap;
this.ruleset = ruleset ?? beatmap.Ruleset;
Size = new Vector2(20);
}
@@ -58,7 +60,7 @@ namespace osu.Game.Beatmaps.Drawables
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
// the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment)
Icon = beatmap.Ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.fa_question_circle_o }
Icon = ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.fa_question_circle_o }
}
};
}
@@ -0,0 +1,50 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Beatmaps.Drawables
{
/// <summary>
/// Display a baetmap background from a local source, but fallback to online source if not available.
/// </summary>
public class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable<BeatmapInfo>
{
public readonly IBindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
[Resolved]
private BeatmapManager beatmaps { get; set; }
public UpdateableBeatmapBackgroundSprite()
{
Beatmap.BindValueChanged(b => Model = b);
}
protected override Drawable CreateDrawable(BeatmapInfo model)
{
return new DelayedLoadUnloadWrapper(() => {
Drawable drawable;
var localBeatmap = beatmaps.GetWorkingBeatmap(model);
if (localBeatmap.BeatmapInfo.ID == 0 && model?.BeatmapSet?.OnlineInfo != null)
drawable = new BeatmapSetCover(model.BeatmapSet);
else
drawable = new BeatmapBackgroundSprite(localBeatmap);
drawable.RelativeSizeAxes = Axes.Both;
drawable.Anchor = Anchor.Centre;
drawable.Origin = Anchor.Centre;
drawable.FillMode = FillMode.Fill;
drawable.OnLoadComplete = d => d.FadeInFromZero(400);
return drawable;
}, 500, 10000);
}
protected override double FadeDuration => 0;
}
}
-19
View File
@@ -1,19 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using 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();
}
}
+1 -1
View File
@@ -151,7 +151,7 @@ namespace osu.Game.Beatmaps
public bool WaveformLoaded => waveform.IsResultAvailable;
public Waveform Waveform => waveform.Value;
protected virtual Waveform GetWaveform() => new Waveform();
protected virtual Waveform GetWaveform() => new Waveform(null);
private readonly RecyclableLazy<Waveform> waveform;
public bool StoryboardLoaded => storyboard.IsResultAvailable;
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration;
using osu.Game.Rulesets;
@@ -43,7 +42,7 @@ namespace osu.Game.Configuration
{
base.AddBindable(lookup, bindable);
var setting = databasedSettings.FirstOrDefault(s => (int)s.Key == (int)(object)lookup);
var setting = databasedSettings.Find(s => (int)s.Key == (int)(object)lookup);
if (setting != null)
{
bindable.Parse(setting.Value);
+22 -3
View File
@@ -3,6 +3,7 @@
using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Extensions;
using osu.Framework.Platform;
using osu.Game.Overlays;
using osu.Game.Rulesets.Scoring;
@@ -96,15 +97,27 @@ namespace osu.Game.Configuration
Set(OsuSetting.ScreenshotCaptureMenuCursor, false);
Set(OsuSetting.SongSelectRightMouseScroll, false);
Set(OsuSetting.Scaling, ScalingMode.Off);
Set(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f);
Set(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f);
Set(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f);
Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f);
Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
}
public OsuConfigManager(Storage storage) : base(storage)
public OsuConfigManager(Storage storage)
: base(storage)
{
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
{
new TrackedSetting<bool>(OsuSetting.MouseDisableButtons, v => new SettingDescription(!v, "gameplay mouse buttons", v ? "disabled" : "enabled"))
new TrackedSetting<bool>(OsuSetting.MouseDisableButtons, v => new SettingDescription(!v, "gameplay mouse buttons", v ? "disabled" : "enabled")),
new TrackedSetting<ScalingMode>(OsuSetting.Scaling, m => new SettingDescription(m, "scaling", m.GetDescription())),
};
}
@@ -151,6 +164,12 @@ namespace osu.Game.Configuration
BeatmapHitsounds,
IncreaseFirstObjectVisibility,
ScoreDisplayMode,
ExternalLinkWarning
ExternalLinkWarning,
Scaling,
ScalingPositionX,
ScalingPositionY,
ScalingSizeX,
ScalingSizeY,
UIScale
}
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Configuration
{
public enum ScalingMode
{
Off,
Everything,
[Description("Excluding overlays")]
ExcludeOverlays,
Gameplay,
}
}
@@ -74,7 +74,7 @@ namespace osu.Game.Graphics.Containers
}
[BackgroundDependencyLoader]
private void load(IBindableBeatmap beatmap)
private void load(IBindable<WorkingBeatmap> beatmap)
{
Beatmap.BindTo(beatmap);
}
@@ -7,6 +7,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
@@ -61,11 +62,26 @@ namespace osu.Game.Graphics.Containers
}
public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action<SpriteText> creationParameters = null)
=> createLink(AddText(text, creationParameters), text, url, linkType, linkArgument, tooltipText);
public void AddLink(string text, Action action, string tooltipText = null, Action<SpriteText> creationParameters = null)
=> createLink(AddText(text, creationParameters), text, tooltipText: tooltipText, action: action);
public void AddLink(IEnumerable<SpriteText> text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null)
{
AddInternal(new DrawableLinkCompiler(AddText(text, creationParameters).ToList())
foreach (var t in text)
AddArbitraryDrawable(t);
createLink(text, null, url, linkType, linkArgument, tooltipText);
}
private void createLink(IEnumerable<Drawable> drawables, string text, string url = null, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action action = null)
{
AddInternal(new DrawableLinkCompiler(drawables.OfType<SpriteText>().ToList())
{
RelativeSizeAxes = Axes.Both,
TooltipText = tooltipText ?? (url != text ? url : string.Empty),
Action = () =>
Action = action ?? (() =>
{
switch (linkType)
{
@@ -104,8 +120,13 @@ namespace osu.Game.Graphics.Containers
default:
throw new NotImplementedException($"This {nameof(LinkAction)} ({linkType.ToString()}) is missing an associated action.");
}
},
}),
});
}
// We want the compilers to always be visible no matter where they are, so RelativeSizeAxes is used.
// However due to https://github.com/ppy/osu-framework/issues/2073, it's possible for the compilers to be relative size in the flow's auto-size axes - an unsupported operation.
// Since the compilers don't display any content and don't affect the layout, it's simplest to exclude them from the flow.
public override IEnumerable<Drawable> FlowingChildren => base.FlowingChildren.Where(c => !(c is DrawableLinkCompiler));
}
}
@@ -26,7 +26,6 @@ namespace osu.Game.Graphics.Containers
private PreviewTrackManager previewTrackManager;
protected readonly Bindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>(OverlayActivation.All);
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
@@ -0,0 +1,161 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
using osu.Game.Graphics.Backgrounds;
using osuTK;
namespace osu.Game.Graphics.Containers
{
/// <summary>
/// Handles user-defined scaling, allowing application at multiple levels defined by <see cref="ScalingMode"/>.
/// </summary>
public class ScalingContainer : Container
{
private Bindable<float> sizeX;
private Bindable<float> sizeY;
private Bindable<float> posX;
private Bindable<float> posY;
private readonly ScalingMode? targetMode;
private Bindable<ScalingMode> scalingMode;
private readonly Container content;
protected override Container<Drawable> Content => content;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
private readonly Container sizableContainer;
private Drawable backgroundLayer;
/// <summary>
/// Create a new instance.
/// </summary>
/// <param name="targetMode">The mode which this container should be handling. Handles all modes if null.</param>
public ScalingContainer(ScalingMode? targetMode = null)
{
this.targetMode = targetMode;
RelativeSizeAxes = Axes.Both;
InternalChild = sizableContainer = new AlwaysInputContainer
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
CornerRadius = 10,
Child = content = new ScalingDrawSizePreservingFillContainer(targetMode != ScalingMode.Gameplay)
};
}
private class ScalingDrawSizePreservingFillContainer : DrawSizePreservingFillContainer
{
private readonly bool applyUIScale;
private Bindable<float> uiScale;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
public ScalingDrawSizePreservingFillContainer(bool applyUIScale)
{
this.applyUIScale = applyUIScale;
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager osuConfig)
{
if (applyUIScale)
{
uiScale = osuConfig.GetBindable<float>(OsuSetting.UIScale);
uiScale.BindValueChanged(scaleChanged, true);
}
}
private void scaleChanged(float value)
{
this.ScaleTo(new Vector2(value), 500, Easing.Out);
this.ResizeTo(new Vector2(1 / value), 500, Easing.Out);
}
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
scalingMode = config.GetBindable<ScalingMode>(OsuSetting.Scaling);
scalingMode.ValueChanged += _ => updateSize();
sizeX = config.GetBindable<float>(OsuSetting.ScalingSizeX);
sizeX.ValueChanged += _ => updateSize();
sizeY = config.GetBindable<float>(OsuSetting.ScalingSizeY);
sizeY.ValueChanged += _ => updateSize();
posX = config.GetBindable<float>(OsuSetting.ScalingPositionX);
posX.ValueChanged += _ => updateSize();
posY = config.GetBindable<float>(OsuSetting.ScalingPositionY);
posY.ValueChanged += _ => updateSize();
}
protected override void LoadComplete()
{
base.LoadComplete();
updateSize();
sizableContainer.FinishTransforms();
}
private bool requiresBackgroundVisible => (scalingMode == ScalingMode.Everything || scalingMode == ScalingMode.ExcludeOverlays) && (sizeX.Value != 1 || sizeY.Value != 1);
private void updateSize()
{
if (targetMode == ScalingMode.Everything)
{
// the top level scaling container manages the background to be displayed while scaling.
if (requiresBackgroundVisible)
{
if (backgroundLayer == null)
LoadComponentAsync(backgroundLayer = new Background("Menu/menu-background-1")
{
Colour = OsuColour.Gray(0.1f),
Alpha = 0,
Depth = float.MaxValue
}, d =>
{
AddInternal(d);
d.FadeTo(requiresBackgroundVisible ? 1 : 0, 4000, Easing.OutQuint);
});
else
backgroundLayer.FadeIn(500);
}
else
backgroundLayer?.FadeOut(500);
}
bool scaling = targetMode == null || scalingMode.Value == targetMode;
var targetSize = scaling ? new Vector2(sizeX, sizeY) : Vector2.One;
var targetPosition = scaling ? new Vector2(posX, posY) * (Vector2.One - targetSize) : Vector2.Zero;
bool requiresMasking = scaling && targetSize != Vector2.One;
if (requiresMasking)
sizableContainer.Masking = true;
sizableContainer.MoveTo(targetPosition, 500, Easing.OutQuart);
sizableContainer.ResizeTo(targetSize, 500, Easing.OutQuart).OnComplete(_ => { sizableContainer.Masking = requiresMasking; });
}
private class AlwaysInputContainer : Container
{
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
public AlwaysInputContainer()
{
RelativeSizeAxes = Axes.Both;
}
}
}
}

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