mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 12:53:11 +08:00
Merge remote-tracking branch 'origin/master' into xexxar-angles
This commit is contained in:
commit
722210d8bb
@ -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,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" />
|
||||
|
16
.vscode/launch.json
vendored
16
.vscode/launch.json
vendored
@ -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"
|
||||
|
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@ -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": [
|
||||
|
@ -10,7 +10,7 @@ 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.
|
||||
- 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 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).
|
||||
|
||||
# Building and running
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 694cb03f19c93106ed0f2593f3e506e835fb652a
|
||||
Subproject commit 9880089b4e8fcd78d68f30c8a40d43bf8dccca86
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)",
|
||||
|
23
osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs
Normal file
23
osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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)",
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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)",
|
||||
|
@ -123,8 +123,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
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_
|
||||
|
194
osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
Normal file
194
osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
Normal 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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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)",
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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(() =>
|
||||
{
|
||||
|
32
osu.Game.Tests/Visual/TestCaseAccountCreationOverlay.cs
Normal file
32
osu.Game.Tests/Visual/TestCaseAccountCreationOverlay.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
67
osu.Game.Tests/Visual/TestCaseDrawableDate.cs
Normal file
67
osu.Game.Tests/Visual/TestCaseDrawableDate.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs
Normal file
101
osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs
Normal file
@ -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 BindableCollection<Room> Rooms = new BindableCollection<Room>();
|
||||
IBindableCollection<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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
35
osu.Game.Tests/Visual/TestCaseMatchHostInfo.cs
Normal file
35
osu.Game.Tests/Visual/TestCaseMatchHostInfo.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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",
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
68
osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs
Normal file
68
osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs
Normal file
@ -0,0 +1,68 @@
|
||||
// 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(new Room { RoomID = { Value = 3 } })
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(550f, 450f),
|
||||
Scope = MatchLeaderboardScope.Overall,
|
||||
});
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
123
osu.Game.Tests/Visual/TestCaseMatchResults.cs
Normal file
123
osu.Game.Tests/Visual/TestCaseMatchResults.cs
Normal file
@ -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, 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
161
osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs
Normal file
161
osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs
Normal file
@ -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 IBindableCollection<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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -87,10 +87,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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
50
osu.Game.Tests/Visual/TestCaseRoomStatus.cs
Normal file
50
osu.Game.Tests/Visual/TestCaseRoomStatus.cs
Normal file
@ -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() }
|
||||
}),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
104
osu.Game.Tests/Visual/TestCaseStandAloneChatDisplay.cs
Normal file
104
osu.Game.Tests/Visual/TestCaseStandAloneChatDisplay.cs
Normal file
@ -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)", () => { });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -149,8 +149,10 @@ namespace osu.Game.Database
|
||||
try
|
||||
{
|
||||
notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}";
|
||||
|
||||
TModel import;
|
||||
using (ArchiveReader reader = getReaderFrom(path))
|
||||
imported.Add(Import(reader));
|
||||
imported.Add(import = Import(reader));
|
||||
|
||||
notification.Progress = (float)current / paths.Length;
|
||||
|
||||
@ -160,7 +162,7 @@ namespace osu.Game.Database
|
||||
// TODO: Add a check to prevent files from storage to be deleted.
|
||||
try
|
||||
{
|
||||
if (File.Exists(path))
|
||||
if (import != null && File.Exists(path))
|
||||
File.Delete(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -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,25 @@ 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())
|
||||
{
|
||||
TooltipText = tooltipText ?? (url != text ? url : string.Empty),
|
||||
Action = () =>
|
||||
Action = action ?? (() =>
|
||||
{
|
||||
switch (linkType)
|
||||
{
|
||||
@ -104,7 +119,7 @@ namespace osu.Game.Graphics.Containers
|
||||
default:
|
||||
throw new NotImplementedException($"This {nameof(LinkAction)} ({linkType.ToString()}) is missing an associated action.");
|
||||
}
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -11,29 +11,43 @@ namespace osu.Game.Graphics.Containers
|
||||
/// </summary>
|
||||
public class ShakeContainer : Container
|
||||
{
|
||||
/// <summary>
|
||||
/// The length of a single shake.
|
||||
/// </summary>
|
||||
public float ShakeDuration = 80;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of shakes. May be shortened if possible.
|
||||
/// </summary>
|
||||
public float TotalShakes = 4;
|
||||
|
||||
/// <summary>
|
||||
/// Pixels of displacement per shake.
|
||||
/// </summary>
|
||||
public float ShakeMagnitude = 8;
|
||||
|
||||
/// <summary>
|
||||
/// Shake the contents of this container.
|
||||
/// </summary>
|
||||
/// <param name="maximumLength">The maximum length the shake should last.</param>
|
||||
public void Shake(double maximumLength)
|
||||
public void Shake(double? maximumLength = null)
|
||||
{
|
||||
const float shake_amount = 8;
|
||||
const float shake_duration = 30;
|
||||
|
||||
// if we don't have enough time, don't bother shaking.
|
||||
if (maximumLength < shake_duration * 2)
|
||||
if (maximumLength < ShakeDuration * 2)
|
||||
return;
|
||||
|
||||
var sequence = this.MoveToX(shake_amount, shake_duration / 2, Easing.OutSine).Then()
|
||||
.MoveToX(-shake_amount, shake_duration, Easing.InOutSine).Then();
|
||||
var sequence = this.MoveToX(shake_amount, ShakeDuration / 2, Easing.OutSine).Then()
|
||||
.MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then();
|
||||
|
||||
// if we don't have enough time for the second shake, skip it.
|
||||
if (maximumLength > shake_duration * 4)
|
||||
if (!maximumLength.HasValue || maximumLength >= ShakeDuration * 4)
|
||||
sequence = sequence
|
||||
.MoveToX(shake_amount, shake_duration, Easing.InOutSine).Then()
|
||||
.MoveToX(-shake_amount, shake_duration, Easing.InOutSine).Then();
|
||||
.MoveToX(shake_amount, ShakeDuration, Easing.InOutSine).Then()
|
||||
.MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then();
|
||||
|
||||
sequence.MoveToX(0, shake_duration / 2, Easing.InSine);
|
||||
sequence.MoveToX(0, ShakeDuration / 2, Easing.InSine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using Humanizer;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
@ -11,13 +12,27 @@ namespace osu.Game.Graphics
|
||||
{
|
||||
public class DrawableDate : OsuSpriteText, IHasTooltip
|
||||
{
|
||||
protected readonly DateTimeOffset Date;
|
||||
private DateTimeOffset date;
|
||||
|
||||
public DateTimeOffset Date
|
||||
{
|
||||
get => date;
|
||||
set
|
||||
{
|
||||
if (date == value)
|
||||
return;
|
||||
date = value.ToLocalTime();
|
||||
|
||||
if (LoadState >= LoadState.Ready)
|
||||
updateTime();
|
||||
}
|
||||
}
|
||||
|
||||
public DrawableDate(DateTimeOffset date)
|
||||
{
|
||||
Font = "Exo2.0-RegularItalic";
|
||||
|
||||
Date = date.ToLocalTime();
|
||||
Date = date;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -39,14 +54,14 @@ namespace osu.Game.Graphics
|
||||
var diffToNow = DateTimeOffset.Now.Subtract(Date);
|
||||
|
||||
double timeUntilNextUpdate = 1000;
|
||||
if (diffToNow.TotalSeconds > 60)
|
||||
if (Math.Abs(diffToNow.TotalSeconds) > 120)
|
||||
{
|
||||
timeUntilNextUpdate *= 60;
|
||||
if (diffToNow.TotalMinutes > 60)
|
||||
if (Math.Abs(diffToNow.TotalMinutes) > 120)
|
||||
{
|
||||
timeUntilNextUpdate *= 60;
|
||||
|
||||
if (diffToNow.TotalHours > 24)
|
||||
if (Math.Abs(diffToNow.TotalHours) > 48)
|
||||
timeUntilNextUpdate *= 24;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// A loading spinner.
|
||||
/// </summary>
|
||||
public class LoadingAnimation : VisibilityContainer
|
||||
{
|
||||
private readonly SpriteIcon spinner;
|
||||
|
@ -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 osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
@ -99,7 +100,20 @@ namespace osu.Game.Graphics.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
public Bindable<bool> Current { get; } = new Bindable<bool>();
|
||||
private readonly Bindable<bool> current = new Bindable<bool>();
|
||||
|
||||
public Bindable<bool> Current
|
||||
{
|
||||
get => current;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
|
||||
current.UnbindBindings();
|
||||
current.BindTo(value);
|
||||
}
|
||||
}
|
||||
|
||||
private Color4 accentColour;
|
||||
public Color4 AccentColour
|
||||
|
56
osu.Game/Graphics/UserInterface/ProcessingOverlay.cs
Normal file
56
osu.Game/Graphics/UserInterface/ProcessingOverlay.cs
Normal file
@ -0,0 +1,56 @@
|
||||
// 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.Input.Events;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// An overlay that will consume all available space and block input when required.
|
||||
/// Useful for disabling all elements in a form and showing we are waiting on a response, for instance.
|
||||
/// </summary>
|
||||
public class ProcessingOverlay : VisibilityContainer
|
||||
{
|
||||
private const float transition_duration = 200;
|
||||
|
||||
public ProcessingOverlay()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.9f,
|
||||
},
|
||||
new LoadingAnimation { State = Visibility.Visible }
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool Handle(UIEvent e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
this.FadeIn(transition_duration * 2, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
this.FadeOut(transition_duration, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
@ -102,7 +104,7 @@ namespace osu.Game.Online.API
|
||||
if (queue.Count == 0)
|
||||
{
|
||||
log.Add(@"Queueing a ping request");
|
||||
Queue(new ListChannelsRequest { Timeout = 5000 });
|
||||
Queue(new GetUserRequest());
|
||||
}
|
||||
|
||||
break;
|
||||
@ -141,9 +143,10 @@ namespace osu.Game.Online.API
|
||||
State = APIState.Online;
|
||||
};
|
||||
|
||||
if (!handleRequest(userReq, out _))
|
||||
if (!handleRequest(userReq))
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
if (State == APIState.Connecting)
|
||||
State = APIState.Failing;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -159,7 +162,7 @@ namespace osu.Game.Online.API
|
||||
//hard bail if we can't get a valid access token.
|
||||
if (authentication.RequestAccessToken() == null)
|
||||
{
|
||||
Logout(false);
|
||||
Logout();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -170,15 +173,10 @@ namespace osu.Game.Online.API
|
||||
lock (queue)
|
||||
{
|
||||
if (queue.Count == 0) break;
|
||||
req = queue.Peek();
|
||||
req = queue.Dequeue();
|
||||
}
|
||||
|
||||
// TODO: handle failures better
|
||||
handleRequest(req, out var removeFromQueue);
|
||||
|
||||
if (removeFromQueue)
|
||||
lock (queue)
|
||||
queue.Dequeue();
|
||||
handleRequest(req);
|
||||
}
|
||||
|
||||
Thread.Sleep(50);
|
||||
@ -193,48 +191,64 @@ namespace osu.Game.Online.API
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single API request.
|
||||
/// </summary>
|
||||
/// <param name="req">The request.</param>
|
||||
/// <returns>true if the request succeeded.</returns>
|
||||
private bool handleRequest(APIRequest req, out bool removeFromQueue)
|
||||
public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password)
|
||||
{
|
||||
removeFromQueue = true;
|
||||
Debug.Assert(State == APIState.Offline);
|
||||
|
||||
var req = new RegistrationRequest
|
||||
{
|
||||
Url = $@"{Endpoint}/users",
|
||||
Method = HttpMethod.Post,
|
||||
Username = username,
|
||||
Email = email,
|
||||
Password = password
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Log($@"Performing request {req}", LoggingTarget.Network);
|
||||
req.Failure += ex =>
|
||||
req.Perform();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ex is WebException we)
|
||||
handleWebException(we);
|
||||
};
|
||||
return JObject.Parse(req.ResponseString).SelectToken("form_error", true).ToObject<RegistrationRequest.RegistrationRequestErrors>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if we couldn't deserialize the error message let's throw the original exception outwards.
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single API request.
|
||||
/// Ensures all exceptions are caught and dealt with correctly.
|
||||
/// </summary>
|
||||
/// <param name="req">The request.</param>
|
||||
/// <returns>true if the request succeeded.</returns>
|
||||
private bool handleRequest(APIRequest req)
|
||||
{
|
||||
try
|
||||
{
|
||||
req.Perform(this);
|
||||
|
||||
//we could still be in initialisation, at which point we don't want to say we're Online yet.
|
||||
if (IsLoggedIn)
|
||||
State = APIState.Online;
|
||||
if (IsLoggedIn) State = APIState.Online;
|
||||
|
||||
failureCount = 0;
|
||||
return true;
|
||||
}
|
||||
catch (WebException we)
|
||||
{
|
||||
removeFromQueue = handleWebException(we);
|
||||
|
||||
if (removeFromQueue)
|
||||
req.Fail(we);
|
||||
|
||||
handleWebException(we);
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is TimeoutException)
|
||||
log.Add(@"API level timeout exception was hit");
|
||||
|
||||
req.Fail(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -280,7 +294,7 @@ namespace osu.Game.Online.API
|
||||
switch (statusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
Logout(false);
|
||||
Logout();
|
||||
return true;
|
||||
case HttpStatusCode.RequestTimeout:
|
||||
failureCount++;
|
||||
@ -290,8 +304,12 @@ namespace osu.Game.Online.API
|
||||
//we might try again at an api level.
|
||||
return false;
|
||||
|
||||
State = APIState.Failing;
|
||||
flushQueue();
|
||||
if (State == APIState.Online)
|
||||
{
|
||||
State = APIState.Failing;
|
||||
flushQueue();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -325,10 +343,9 @@ namespace osu.Game.Online.API
|
||||
}
|
||||
}
|
||||
|
||||
public void Logout(bool clearUsername = true)
|
||||
public void Logout()
|
||||
{
|
||||
flushQueue();
|
||||
if (clearUsername) ProvidedUsername = null;
|
||||
password = null;
|
||||
authentication.Clear();
|
||||
LocalUser.Value = createGuestUser();
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Framework.Logging;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
@ -35,23 +36,12 @@ namespace osu.Game.Online.API
|
||||
/// </summary>
|
||||
public abstract class APIRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum amount of time before this request will fail.
|
||||
/// </summary>
|
||||
public int Timeout = WebRequest.DEFAULT_TIMEOUT;
|
||||
|
||||
protected virtual string Target => string.Empty;
|
||||
protected abstract string Target { get; }
|
||||
|
||||
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);
|
||||
|
||||
protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}";
|
||||
|
||||
private double remainingTime => Math.Max(0, Timeout - (DateTimeOffset.UtcNow - (startTime ?? DateTimeOffset.MinValue)).TotalMilliseconds);
|
||||
|
||||
public bool ExceededTimeout => remainingTime == 0;
|
||||
|
||||
private DateTimeOffset? startTime;
|
||||
|
||||
protected APIAccess API;
|
||||
protected WebRequest WebRequest;
|
||||
|
||||
@ -75,27 +65,24 @@ namespace osu.Game.Online.API
|
||||
{
|
||||
API = api;
|
||||
|
||||
if (checkAndProcessFailure())
|
||||
if (checkAndScheduleFailure())
|
||||
return;
|
||||
|
||||
if (startTime == null)
|
||||
startTime = DateTimeOffset.UtcNow;
|
||||
|
||||
if (remainingTime <= 0)
|
||||
throw new TimeoutException(@"API request timeout hit");
|
||||
|
||||
WebRequest = CreateWebRequest();
|
||||
WebRequest.Failed += Fail;
|
||||
WebRequest.AllowRetryOnTimeout = false;
|
||||
WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}");
|
||||
|
||||
if (checkAndProcessFailure())
|
||||
if (checkAndScheduleFailure())
|
||||
return;
|
||||
|
||||
if (!WebRequest.Aborted) //could have been aborted by a Cancel() call
|
||||
{
|
||||
Logger.Log($@"Performing request {this}", LoggingTarget.Network);
|
||||
WebRequest.Perform();
|
||||
}
|
||||
|
||||
if (checkAndProcessFailure())
|
||||
if (checkAndScheduleFailure())
|
||||
return;
|
||||
|
||||
api.Schedule(delegate { Success?.Invoke(); });
|
||||
@ -105,19 +92,25 @@ namespace osu.Game.Online.API
|
||||
|
||||
public void Fail(Exception e)
|
||||
{
|
||||
cancelled = true;
|
||||
if (WebRequest?.Completed == true)
|
||||
return;
|
||||
|
||||
if (cancelled)
|
||||
return;
|
||||
|
||||
cancelled = true;
|
||||
WebRequest?.Abort();
|
||||
|
||||
Logger.Log($@"Failing request {this} ({e})", LoggingTarget.Network);
|
||||
pendingFailure = () => Failure?.Invoke(e);
|
||||
checkAndProcessFailure();
|
||||
checkAndScheduleFailure();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checked for cancellation or error. Also queues up the Failed event if we can.
|
||||
/// </summary>
|
||||
/// <returns>Whether we are in a failed or cancelled state.</returns>
|
||||
private bool checkAndProcessFailure()
|
||||
private bool checkAndScheduleFailure()
|
||||
{
|
||||
if (API == null || pendingFailure == null) return cancelled;
|
||||
|
||||
|
41
osu.Game/Online/API/RegistrationRequest.cs
Normal file
41
osu.Game/Online/API/RegistrationRequest.cs
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.IO.Network;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
public class RegistrationRequest : WebRequest
|
||||
{
|
||||
internal string Username;
|
||||
internal string Email;
|
||||
internal string Password;
|
||||
|
||||
protected override void PrePerform()
|
||||
{
|
||||
AddParameter("user[username]", Username);
|
||||
AddParameter("user[user_email]", Email);
|
||||
AddParameter("user[password]", Password);
|
||||
|
||||
base.PrePerform();
|
||||
}
|
||||
|
||||
public class RegistrationRequestErrors
|
||||
{
|
||||
public UserErrors User;
|
||||
|
||||
public class UserErrors
|
||||
{
|
||||
[JsonProperty("username")]
|
||||
public string[] Username;
|
||||
|
||||
[JsonProperty("user_email")]
|
||||
public string[] Email;
|
||||
|
||||
[JsonProperty("password")]
|
||||
public string[] Password;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
osu.Game/Online/API/Requests/CreateRoomRequest.cs
Normal file
35
osu.Game/Online/API/Requests/CreateRoomRequest.cs
Normal file
@ -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.Net.Http;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class CreateRoomRequest : APIRequest<APICreatedRoom>
|
||||
{
|
||||
private readonly Room room;
|
||||
|
||||
public CreateRoomRequest(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
|
||||
req.ContentType = "application/json";
|
||||
req.Method = HttpMethod.Post;
|
||||
|
||||
req.AddRaw(JsonConvert.SerializeObject(room));
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => "rooms";
|
||||
}
|
||||
}
|
30
osu.Game/Online/API/Requests/CreateRoomScoreRequest.cs
Normal file
30
osu.Game/Online/API/Requests/CreateRoomScoreRequest.cs
Normal file
@ -0,0 +1,30 @@
|
||||
// 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.Net.Http;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class CreateRoomScoreRequest : APIRequest<APIScoreToken>
|
||||
{
|
||||
private readonly int roomId;
|
||||
private readonly int playlistItemId;
|
||||
|
||||
public CreateRoomScoreRequest(int roomId, int playlistItemId)
|
||||
{
|
||||
this.roomId = roomId;
|
||||
this.playlistItemId = playlistItemId;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
req.Method = HttpMethod.Post;
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores";
|
||||
}
|
||||
}
|
20
osu.Game/Online/API/Requests/GetRoomScoresRequest.cs
Normal file
20
osu.Game/Online/API/Requests/GetRoomScoresRequest.cs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetRoomScoresRequest : APIRequest<List<APIRoomScoreInfo>>
|
||||
{
|
||||
private readonly int roomId;
|
||||
|
||||
public GetRoomScoresRequest(int roomId)
|
||||
{
|
||||
this.roomId = roomId;
|
||||
}
|
||||
|
||||
protected override string Target => $@"rooms/{roomId}/leaderboard";
|
||||
}
|
||||
}
|
44
osu.Game/Online/API/Requests/GetRoomsRequest.cs
Normal file
44
osu.Game/Online/API/Requests/GetRoomsRequest.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Screens.Multi.Lounge.Components;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetRoomsRequest : APIRequest<List<Room>>
|
||||
{
|
||||
private readonly PrimaryFilter primaryFilter;
|
||||
|
||||
public GetRoomsRequest(PrimaryFilter primaryFilter)
|
||||
{
|
||||
this.primaryFilter = primaryFilter;
|
||||
}
|
||||
|
||||
protected override string Target
|
||||
{
|
||||
get
|
||||
{
|
||||
string target = "rooms";
|
||||
|
||||
switch (primaryFilter)
|
||||
{
|
||||
case PrimaryFilter.Open:
|
||||
break;
|
||||
case PrimaryFilter.Owned:
|
||||
target += "/owned";
|
||||
break;
|
||||
case PrimaryFilter.Participated:
|
||||
target += "/participated";
|
||||
break;
|
||||
case PrimaryFilter.RecentlyEnded:
|
||||
target += "/ended";
|
||||
break;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -13,15 +13,15 @@ namespace osu.Game.Online.API.Requests
|
||||
public class GetScoresRequest : APIRequest<APIScores>
|
||||
{
|
||||
private readonly BeatmapInfo beatmap;
|
||||
private readonly LeaderboardScope scope;
|
||||
private readonly BeatmapLeaderboardScope scope;
|
||||
private readonly RulesetInfo ruleset;
|
||||
|
||||
public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global)
|
||||
public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global)
|
||||
{
|
||||
if (!beatmap.OnlineBeatmapID.HasValue)
|
||||
throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}.");
|
||||
|
||||
if (scope == LeaderboardScope.Local)
|
||||
if (scope == BeatmapLeaderboardScope.Local)
|
||||
throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard");
|
||||
|
||||
this.beatmap = beatmap;
|
||||
|
31
osu.Game/Online/API/Requests/JoinRoomRequest.cs
Normal file
31
osu.Game/Online/API/Requests/JoinRoomRequest.cs
Normal file
@ -0,0 +1,31 @@
|
||||
// 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.Net.Http;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class JoinRoomRequest : APIRequest
|
||||
{
|
||||
private readonly Room room;
|
||||
private readonly User user;
|
||||
|
||||
public JoinRoomRequest(Room room, User user)
|
||||
{
|
||||
this.room = room;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
req.Method = HttpMethod.Put;
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $"rooms/{room.RoomID.Value}/users/{user.Id}";
|
||||
}
|
||||
}
|
31
osu.Game/Online/API/Requests/PartRoomRequest.cs
Normal file
31
osu.Game/Online/API/Requests/PartRoomRequest.cs
Normal file
@ -0,0 +1,31 @@
|
||||
// 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.Net.Http;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class PartRoomRequest : APIRequest
|
||||
{
|
||||
private readonly Room room;
|
||||
private readonly User user;
|
||||
|
||||
public PartRoomRequest(Room room, User user)
|
||||
{
|
||||
this.room = room;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
req.Method = HttpMethod.Delete;
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $"rooms/{room.RoomID.Value}/users/{user.Id}";
|
||||
}
|
||||
}
|
@ -59,19 +59,17 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
|
||||
public BeatmapInfo ToBeatmap(RulesetStore rulesets)
|
||||
{
|
||||
var set = BeatmapSet?.ToBeatmapSet(rulesets);
|
||||
|
||||
return new BeatmapInfo
|
||||
{
|
||||
Metadata = this,
|
||||
Metadata = set?.Metadata ?? this,
|
||||
Ruleset = rulesets.GetRuleset(ruleset),
|
||||
StarDifficulty = starDifficulty,
|
||||
OnlineBeatmapID = OnlineBeatmapID,
|
||||
Version = version,
|
||||
Status = Status,
|
||||
BeatmapSet = new BeatmapSetInfo
|
||||
{
|
||||
OnlineBeatmapSetID = OnlineBeatmapSetID,
|
||||
Status = BeatmapSet?.Status ?? BeatmapSetOnlineStatus.None
|
||||
},
|
||||
BeatmapSet = set,
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
DrainRate = drainRate,
|
||||
|
14
osu.Game/Online/API/Requests/Responses/APICreatedRoom.cs
Normal file
14
osu.Game/Online/API/Requests/Responses/APICreatedRoom.cs
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APICreatedRoom : Room
|
||||
{
|
||||
[JsonProperty("error")]
|
||||
public string Error { get; set; }
|
||||
}
|
||||
}
|
12
osu.Game/Online/API/Requests/Responses/APIMod.cs
Normal file
12
osu.Game/Online/API/Requests/Responses/APIMod.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// 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.Mods;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APIMod : IMod
|
||||
{
|
||||
public string Acronym { get; set; }
|
||||
}
|
||||
}
|
17
osu.Game/Online/API/Requests/Responses/APIRoomScoreInfo.cs
Normal file
17
osu.Game/Online/API/Requests/Responses/APIRoomScoreInfo.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APIRoomScoreInfo : ScoreInfo
|
||||
{
|
||||
[JsonProperty("attempts")]
|
||||
public int TotalAttempts { get; set; }
|
||||
|
||||
[JsonProperty("completed")]
|
||||
public int CompletedBeatmaps { get; set; }
|
||||
}
|
||||
}
|
13
osu.Game/Online/API/Requests/Responses/APIScoreToken.cs
Normal file
13
osu.Game/Online/API/Requests/Responses/APIScoreToken.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APIScoreToken
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public int ID { get; set; }
|
||||
}
|
||||
}
|
40
osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs
Normal file
40
osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs
Normal file
@ -0,0 +1,40 @@
|
||||
// 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.Net.Http;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class SubmitRoomScoreRequest : APIRequest
|
||||
{
|
||||
private readonly int scoreId;
|
||||
private readonly int roomId;
|
||||
private readonly int playlistItemId;
|
||||
private readonly ScoreInfo scoreInfo;
|
||||
|
||||
public SubmitRoomScoreRequest(int scoreId, int roomId, int playlistItemId, ScoreInfo scoreInfo)
|
||||
{
|
||||
this.scoreId = scoreId;
|
||||
this.roomId = roomId;
|
||||
this.playlistItemId = playlistItemId;
|
||||
this.scoreInfo = scoreInfo;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
|
||||
req.ContentType = "application/json";
|
||||
req.Method = HttpMethod.Put;
|
||||
|
||||
req.AddRaw(JsonConvert.SerializeObject(scoreInfo));
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores/{scoreId}";
|
||||
}
|
||||
}
|
@ -96,12 +96,14 @@ namespace osu.Game.Online.Chat
|
||||
/// </summary>
|
||||
/// <param name="text">The message text that is going to be posted</param>
|
||||
/// <param name="isAction">Is true if the message is an action, e.g.: user is currently eating </param>
|
||||
public void PostMessage(string text, bool isAction = false)
|
||||
/// <param name="target">An optional target channel. If null, <see cref="CurrentChannel"/> will be used.</param>
|
||||
public void PostMessage(string text, bool isAction = false, Channel target = null)
|
||||
{
|
||||
if (CurrentChannel.Value == null)
|
||||
return;
|
||||
if (target == null)
|
||||
target = CurrentChannel.Value;
|
||||
|
||||
var currentChannel = CurrentChannel.Value;
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
void dequeueAndRun()
|
||||
{
|
||||
@ -113,7 +115,7 @@ namespace osu.Game.Online.Chat
|
||||
{
|
||||
if (!api.IsLoggedIn)
|
||||
{
|
||||
currentChannel.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!"));
|
||||
target.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -121,29 +123,29 @@ namespace osu.Game.Online.Chat
|
||||
{
|
||||
Sender = api.LocalUser.Value,
|
||||
Timestamp = DateTimeOffset.Now,
|
||||
ChannelId = CurrentChannel.Value.Id,
|
||||
ChannelId = target.Id,
|
||||
IsAction = isAction,
|
||||
Content = text
|
||||
};
|
||||
|
||||
currentChannel.AddLocalEcho(message);
|
||||
target.AddLocalEcho(message);
|
||||
|
||||
// if this is a PM and the first message, we need to do a special request to create the PM channel
|
||||
if (currentChannel.Type == ChannelType.PM && !currentChannel.Joined)
|
||||
if (target.Type == ChannelType.PM && !target.Joined)
|
||||
{
|
||||
var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(currentChannel.Users.First(), message);
|
||||
var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(target.Users.First(), message);
|
||||
|
||||
createNewPrivateMessageRequest.Success += createRes =>
|
||||
{
|
||||
currentChannel.Id = createRes.ChannelID;
|
||||
currentChannel.ReplaceMessage(message, createRes.Message);
|
||||
target.Id = createRes.ChannelID;
|
||||
target.ReplaceMessage(message, createRes.Message);
|
||||
dequeueAndRun();
|
||||
};
|
||||
|
||||
createNewPrivateMessageRequest.Failure += exception =>
|
||||
{
|
||||
Logger.Error(exception, "Posting message failed.");
|
||||
currentChannel.ReplaceMessage(message, null);
|
||||
target.ReplaceMessage(message, null);
|
||||
dequeueAndRun();
|
||||
};
|
||||
|
||||
@ -155,14 +157,14 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
req.Success += m =>
|
||||
{
|
||||
currentChannel.ReplaceMessage(message, m);
|
||||
target.ReplaceMessage(message, m);
|
||||
dequeueAndRun();
|
||||
};
|
||||
|
||||
req.Failure += exception =>
|
||||
{
|
||||
Logger.Error(exception, "Posting message failed.");
|
||||
currentChannel.ReplaceMessage(message, null);
|
||||
target.ReplaceMessage(message, null);
|
||||
dequeueAndRun();
|
||||
};
|
||||
|
||||
@ -178,9 +180,13 @@ namespace osu.Game.Online.Chat
|
||||
/// Posts a command locally. Commands like /help will result in a help message written in the current channel.
|
||||
/// </summary>
|
||||
/// <param name="text">the text containing the command identifier and command parameters.</param>
|
||||
public void PostCommand(string text)
|
||||
/// <param name="target">An optional target channel. If null, <see cref="CurrentChannel"/> will be used.</param>
|
||||
public void PostCommand(string text, Channel target = null)
|
||||
{
|
||||
if (CurrentChannel.Value == null)
|
||||
if (target == null)
|
||||
target = CurrentChannel.Value;
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
var parameters = text.Split(new[] { ' ' }, 2);
|
||||
@ -192,7 +198,7 @@ namespace osu.Game.Online.Chat
|
||||
case "me":
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
CurrentChannel.Value.AddNewMessages(new ErrorMessage("Usage: /me [action]"));
|
||||
target.AddNewMessages(new ErrorMessage("Usage: /me [action]"));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -200,11 +206,11 @@ namespace osu.Game.Online.Chat
|
||||
break;
|
||||
|
||||
case "help":
|
||||
CurrentChannel.Value.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]"));
|
||||
target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]"));
|
||||
break;
|
||||
|
||||
default:
|
||||
CurrentChannel.Value.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help"));
|
||||
target.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
152
osu.Game/Online/Chat/StandAloneChatDisplay.cs
Normal file
152
osu.Game/Online/Chat/StandAloneChatDisplay.cs
Normal file
@ -0,0 +1,152 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Chat;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
/// <summary>
|
||||
/// Display a chat channel in an insolated region.
|
||||
/// </summary>
|
||||
public class StandAloneChatDisplay : CompositeDrawable
|
||||
{
|
||||
public readonly Bindable<Channel> Channel = new Bindable<Channel>();
|
||||
|
||||
public Action Exit;
|
||||
|
||||
private readonly FocusedTextBox textbox;
|
||||
|
||||
protected ChannelManager ChannelManager;
|
||||
|
||||
private ScrollContainer scroll;
|
||||
|
||||
private DrawableChannel drawableChannel;
|
||||
|
||||
private readonly bool postingTextbox;
|
||||
|
||||
private const float textbox_height = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new instance.
|
||||
/// </summary>
|
||||
/// <param name="postingTextbox">Whether a textbox for posting new messages should be displayed.</param>
|
||||
public StandAloneChatDisplay(bool postingTextbox = false)
|
||||
{
|
||||
this.postingTextbox = postingTextbox;
|
||||
CornerRadius = 10;
|
||||
Masking = true;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.8f,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
};
|
||||
|
||||
if (postingTextbox)
|
||||
{
|
||||
AddInternal(textbox = new FocusedTextBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = textbox_height,
|
||||
PlaceholderText = "type your message",
|
||||
OnCommit = postMessage,
|
||||
ReleaseFocusOnCommit = false,
|
||||
HoldFocus = true,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
});
|
||||
|
||||
textbox.Exit += () => Exit?.Invoke();
|
||||
}
|
||||
|
||||
Channel.BindValueChanged(channelChanged);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(ChannelManager manager)
|
||||
{
|
||||
if (ChannelManager == null)
|
||||
ChannelManager = manager;
|
||||
}
|
||||
|
||||
private void postMessage(TextBox sender, bool newtext)
|
||||
{
|
||||
var text = textbox.Text.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return;
|
||||
|
||||
if (text[0] == '/')
|
||||
ChannelManager?.PostCommand(text.Substring(1), Channel);
|
||||
else
|
||||
ChannelManager?.PostMessage(text, target: Channel);
|
||||
|
||||
textbox.Text = string.Empty;
|
||||
}
|
||||
|
||||
public void Contract()
|
||||
{
|
||||
this.FadeIn(300);
|
||||
this.MoveToY(0, 500, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public void Expand()
|
||||
{
|
||||
this.FadeOut(200);
|
||||
this.MoveToY(100, 500, Easing.In);
|
||||
}
|
||||
|
||||
protected virtual ChatLine CreateMessage(Message message) => new StandAloneMessage(message);
|
||||
|
||||
private void channelChanged(Channel channel)
|
||||
{
|
||||
drawableChannel?.Expire();
|
||||
|
||||
if (channel == null) return;
|
||||
|
||||
AddInternal(drawableChannel = new StandAloneDrawableChannel(channel)
|
||||
{
|
||||
CreateChatLineAction = CreateMessage,
|
||||
Padding = new MarginPadding { Bottom = postingTextbox ? textbox_height : 0 }
|
||||
});
|
||||
}
|
||||
|
||||
protected class StandAloneDrawableChannel : DrawableChannel
|
||||
{
|
||||
public Func<Message,ChatLine> CreateChatLineAction;
|
||||
|
||||
protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m);
|
||||
|
||||
public StandAloneDrawableChannel(Channel channel)
|
||||
: base(channel)
|
||||
{
|
||||
ChatLineFlow.Padding = new MarginPadding { Horizontal = 0 };
|
||||
}
|
||||
}
|
||||
|
||||
protected class StandAloneMessage : ChatLine
|
||||
{
|
||||
protected override float TextSize => 15;
|
||||
|
||||
protected override float HorizontalPadding => 10;
|
||||
protected override float MessagePadding => 120;
|
||||
|
||||
public StandAloneMessage(Message message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,14 +2,14 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Select.Leaderboards
|
||||
namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
public class DrawableRank : Container
|
||||
{
|
@ -3,27 +3,22 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using System.Linq;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Select.Leaderboards
|
||||
namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
public class Leaderboard : Container
|
||||
public abstract class Leaderboard<TScope, ScoreInfo> : Container, IOnlineComponent
|
||||
{
|
||||
private const double fade_duration = 300;
|
||||
|
||||
@ -32,10 +27,6 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
|
||||
private FillFlowContainer<LeaderboardScore> scrollFlow;
|
||||
|
||||
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||
|
||||
public Action<ScoreInfo> ScoreSelected;
|
||||
|
||||
private readonly LoadingAnimation loading;
|
||||
|
||||
private ScheduledDelegate showScoresDelegate;
|
||||
@ -64,14 +55,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
// ensure placeholder is hidden when displaying scores
|
||||
PlaceholderState = PlaceholderState.Successful;
|
||||
|
||||
var flow = scrollFlow = new FillFlowContainer<LeaderboardScore>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0f, 5f),
|
||||
Padding = new MarginPadding { Top = 10, Bottom = 5 },
|
||||
ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
|
||||
};
|
||||
scrollFlow = CreateScoreFlow();
|
||||
scrollFlow.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1));
|
||||
|
||||
// schedule because we may not be loaded yet (LoadComponentAsync complains).
|
||||
showScoresDelegate?.Cancel();
|
||||
@ -80,12 +65,12 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
else
|
||||
showScores();
|
||||
|
||||
void showScores() => LoadComponentAsync(flow, _ =>
|
||||
void showScores() => LoadComponentAsync(scrollFlow, _ =>
|
||||
{
|
||||
scrollContainer.Add(flow);
|
||||
scrollContainer.Add(scrollFlow);
|
||||
|
||||
int i = 0;
|
||||
foreach (var s in flow.Children)
|
||||
foreach (var s in scrollFlow.Children)
|
||||
{
|
||||
using (s.BeginDelayedSequence(i++ * 50, true))
|
||||
s.Show();
|
||||
@ -96,18 +81,27 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
}
|
||||
}
|
||||
|
||||
private LeaderboardScope scope;
|
||||
protected virtual FillFlowContainer<LeaderboardScore> CreateScoreFlow()
|
||||
=> new FillFlowContainer<LeaderboardScore>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0f, 5f),
|
||||
Padding = new MarginPadding { Top = 10, Bottom = 5 },
|
||||
};
|
||||
|
||||
public LeaderboardScope Scope
|
||||
private TScope scope;
|
||||
|
||||
public TScope Scope
|
||||
{
|
||||
get { return scope; }
|
||||
set
|
||||
{
|
||||
if (value == scope)
|
||||
if (value.Equals(scope))
|
||||
return;
|
||||
|
||||
scope = value;
|
||||
updateScores();
|
||||
UpdateScores();
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +131,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
case PlaceholderState.NetworkFailure:
|
||||
replacePlaceholder(new RetrievalFailurePlaceholder
|
||||
{
|
||||
OnRetry = updateScores,
|
||||
OnRetry = UpdateScores,
|
||||
});
|
||||
break;
|
||||
case PlaceholderState.Unavailable:
|
||||
@ -159,7 +153,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
}
|
||||
}
|
||||
|
||||
public Leaderboard()
|
||||
protected Leaderboard()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -177,63 +171,33 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
}
|
||||
|
||||
private APIAccess api;
|
||||
private BeatmapInfo beatmap;
|
||||
|
||||
[Resolved]
|
||||
private ScoreManager scoreManager { get; set; }
|
||||
|
||||
private ScheduledDelegate pendingUpdateScores;
|
||||
|
||||
public BeatmapInfo Beatmap
|
||||
{
|
||||
get { return beatmap; }
|
||||
set
|
||||
{
|
||||
if (beatmap == value)
|
||||
return;
|
||||
|
||||
beatmap = value;
|
||||
Scores = null;
|
||||
|
||||
updateScores();
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(APIAccess api, IBindable<RulesetInfo> parentRuleset)
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(APIAccess api)
|
||||
{
|
||||
this.api = api;
|
||||
|
||||
ruleset.BindTo(parentRuleset);
|
||||
ruleset.ValueChanged += _ => updateScores();
|
||||
|
||||
if (api != null)
|
||||
api.OnStateChange += handleApiStateChange;
|
||||
api?.Register(this);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (api != null)
|
||||
api.OnStateChange -= handleApiStateChange;
|
||||
api?.Unregister(this);
|
||||
}
|
||||
|
||||
public void RefreshScores() => updateScores();
|
||||
public void RefreshScores() => UpdateScores();
|
||||
|
||||
private GetScoresRequest getScoresRequest;
|
||||
private APIRequest getScoresRequest;
|
||||
|
||||
private void handleApiStateChange(APIState oldState, APIState newState)
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
if (Scope == LeaderboardScope.Local)
|
||||
// No need to respond to API state change while current scope is local
|
||||
return;
|
||||
|
||||
if (newState == APIState.Online)
|
||||
updateScores();
|
||||
if (state == APIState.Online)
|
||||
UpdateScores();
|
||||
}
|
||||
|
||||
private void updateScores()
|
||||
protected void UpdateScores()
|
||||
{
|
||||
// don't display any scores or placeholder until the first Scores_Set has been called.
|
||||
// this avoids scope changes flickering a "no scores" placeholder before initialisation of song select is finished.
|
||||
@ -245,40 +209,23 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
pendingUpdateScores?.Cancel();
|
||||
pendingUpdateScores = Schedule(() =>
|
||||
{
|
||||
if (Scope == LeaderboardScope.Local)
|
||||
{
|
||||
Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray();
|
||||
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Beatmap?.OnlineBeatmapID == null)
|
||||
{
|
||||
PlaceholderState = PlaceholderState.Unavailable;
|
||||
return;
|
||||
}
|
||||
|
||||
if (api?.IsLoggedIn != true)
|
||||
{
|
||||
PlaceholderState = PlaceholderState.NotLoggedIn;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
|
||||
{
|
||||
PlaceholderState = PlaceholderState.NotSupporter;
|
||||
return;
|
||||
}
|
||||
|
||||
PlaceholderState = PlaceholderState.Retrieving;
|
||||
loading.Show();
|
||||
|
||||
getScoresRequest = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope);
|
||||
getScoresRequest.Success += r => Schedule(() =>
|
||||
getScoresRequest = FetchScores(scores => Schedule(() =>
|
||||
{
|
||||
Scores = r.Scores;
|
||||
Scores = scores;
|
||||
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
|
||||
});
|
||||
}));
|
||||
|
||||
if (getScoresRequest == null)
|
||||
return;
|
||||
|
||||
getScoresRequest.Failure += e => Schedule(() =>
|
||||
{
|
||||
@ -292,6 +239,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback);
|
||||
|
||||
private Placeholder currentPlaceholder;
|
||||
|
||||
private void replacePlaceholder(Placeholder placeholder)
|
||||
@ -315,14 +264,18 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
currentPlaceholder = placeholder;
|
||||
}
|
||||
|
||||
protected virtual bool FadeBottom => true;
|
||||
protected virtual bool FadeTop => false;
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
var fadeStart = scrollContainer.Current + scrollContainer.DrawHeight;
|
||||
var fadeBottom = scrollContainer.Current + scrollContainer.DrawHeight;
|
||||
var fadeTop = scrollContainer.Current + LeaderboardScore.HEIGHT;
|
||||
|
||||
if (!scrollContainer.IsScrolledToEnd())
|
||||
fadeStart -= LeaderboardScore.HEIGHT;
|
||||
fadeBottom -= LeaderboardScore.HEIGHT;
|
||||
|
||||
if (scrollFlow == null)
|
||||
return;
|
||||
@ -332,17 +285,27 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
var topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scrollFlow).Y;
|
||||
var bottomY = topY + LeaderboardScore.HEIGHT;
|
||||
|
||||
if (bottomY < fadeStart)
|
||||
bool requireTopFade = FadeTop && topY <= fadeTop;
|
||||
bool requireBottomFade = FadeBottom && bottomY >= fadeBottom;
|
||||
|
||||
if (!requireTopFade && !requireBottomFade)
|
||||
c.Colour = Color4.White;
|
||||
else if (topY > fadeStart + LeaderboardScore.HEIGHT)
|
||||
else if (topY > fadeBottom + LeaderboardScore.HEIGHT || bottomY < fadeTop - LeaderboardScore.HEIGHT)
|
||||
c.Colour = Color4.Transparent;
|
||||
else
|
||||
{
|
||||
c.Colour = ColourInfo.GradientVertical(
|
||||
Color4.White.Opacity(Math.Min(1 - (topY - fadeStart) / LeaderboardScore.HEIGHT, 1)),
|
||||
Color4.White.Opacity(Math.Min(1 - (bottomY - fadeStart) / LeaderboardScore.HEIGHT, 1)));
|
||||
if (bottomY - fadeBottom > 0 && FadeBottom)
|
||||
c.Colour = ColourInfo.GradientVertical(
|
||||
Color4.White.Opacity(Math.Min(1 - (topY - fadeBottom) / LeaderboardScore.HEIGHT, 1)),
|
||||
Color4.White.Opacity(Math.Min(1 - (bottomY - fadeBottom) / LeaderboardScore.HEIGHT, 1)));
|
||||
else if (FadeTop)
|
||||
c.Colour = ColourInfo.GradientVertical(
|
||||
Color4.White.Opacity(Math.Min(1 - (fadeTop - topY) / LeaderboardScore.HEIGHT, 1)),
|
||||
Color4.White.Opacity(Math.Min(1 - (fadeTop - bottomY) / LeaderboardScore.HEIGHT, 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract LeaderboardScore CreateDrawableScore(ScoreInfo model, int index);
|
||||
}
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -17,35 +16,40 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Select.Leaderboards
|
||||
namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
public class LeaderboardScore : OsuClickableContainer
|
||||
{
|
||||
public static readonly float HEIGHT = 60;
|
||||
|
||||
public readonly int RankPosition;
|
||||
public readonly ScoreInfo Score;
|
||||
|
||||
public const float HEIGHT = 60;
|
||||
|
||||
private const float corner_radius = 5;
|
||||
private const float edge_margin = 5;
|
||||
private const float background_alpha = 0.25f;
|
||||
private const float rank_width = 30;
|
||||
|
||||
protected Container RankContainer { get; private set; }
|
||||
|
||||
private readonly ScoreInfo score;
|
||||
|
||||
private Box background;
|
||||
private Container content;
|
||||
private Drawable avatar;
|
||||
private DrawableRank scoreRank;
|
||||
private Drawable scoreRank;
|
||||
private OsuSpriteText nameLabel;
|
||||
private GlowingSpriteText scoreLabel;
|
||||
private ScoreComponentLabel maxCombo;
|
||||
private ScoreComponentLabel accuracy;
|
||||
private Container flagBadgeContainer;
|
||||
private FillFlowContainer<ModIcon> modsContainer;
|
||||
|
||||
private List<ScoreComponentLabel> statisticsLabels;
|
||||
|
||||
public LeaderboardScore(ScoreInfo score, int rank)
|
||||
{
|
||||
Score = score;
|
||||
this.score = score;
|
||||
RankPosition = rank;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@ -55,6 +59,10 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var user = score.User;
|
||||
|
||||
statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s)).ToList();
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
@ -65,8 +73,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Font = @"Exo2.0-MediumItalic",
|
||||
TextSize = 22,
|
||||
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
|
||||
@ -102,7 +110,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
Children = new[]
|
||||
{
|
||||
avatar = new DelayedLoadWrapper(
|
||||
new Avatar(Score.User)
|
||||
new Avatar(user)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CornerRadius = corner_radius,
|
||||
@ -128,7 +136,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
{
|
||||
nameLabel = new OsuSpriteText
|
||||
{
|
||||
Text = Score.User.Username,
|
||||
Text = user.Username,
|
||||
Font = @"Exo2.0-BoldItalic",
|
||||
TextSize = 23,
|
||||
},
|
||||
@ -149,7 +157,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DrawableFlag(Score.User?.Country)
|
||||
new DrawableFlag(user.Country)
|
||||
{
|
||||
Width = 30,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
@ -164,11 +172,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10f, 0f),
|
||||
Margin = new MarginPadding { Left = edge_margin },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
maxCombo = new ScoreComponentLabel(FontAwesome.fa_link, Score.MaxCombo.ToString(), "Max Combo"),
|
||||
accuracy = new ScoreComponentLabel(FontAwesome.fa_crosshairs, string.Format(Score.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", Score.Accuracy), "Accuracy"),
|
||||
},
|
||||
Children = statisticsLabels
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -183,17 +187,17 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
Spacing = new Vector2(5f, 0f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
scoreLabel = new GlowingSpriteText(Score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")),
|
||||
new Container
|
||||
scoreLabel = new GlowingSpriteText(score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")),
|
||||
RankContainer = new Container
|
||||
{
|
||||
Size = new Vector2(40f, 20f),
|
||||
Children = new[]
|
||||
{
|
||||
scoreRank = new DrawableRank(Score.Rank)
|
||||
scoreRank = new DrawableRank(score.Rank)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(40f),
|
||||
Size = new Vector2(40f)
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -205,7 +209,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
Origin = Anchor.BottomRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
ChildrenEnumerable = Score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
|
||||
ChildrenEnumerable = score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -216,7 +220,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
|
||||
public override void Show()
|
||||
{
|
||||
foreach (var d in new[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer })
|
||||
foreach (var d in new[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, modsContainer }.Concat(statisticsLabels))
|
||||
d.FadeOut();
|
||||
|
||||
Alpha = 0;
|
||||
@ -243,7 +247,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
|
||||
using (BeginDelayedSequence(50, true))
|
||||
{
|
||||
var drawables = new Drawable[] { flagBadgeContainer, maxCombo, accuracy, modsContainer, };
|
||||
var drawables = new Drawable[] { flagBadgeContainer, modsContainer }.Concat(statisticsLabels).ToArray();
|
||||
for (int i = 0; i < drawables.Length; i++)
|
||||
drawables[i].FadeIn(100 + i * 50);
|
||||
}
|
||||
@ -251,6 +255,12 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[]
|
||||
{
|
||||
new LeaderboardScoreStatistic(FontAwesome.fa_link, "Max Combo", model.MaxCombo.ToString()),
|
||||
new LeaderboardScoreStatistic(FontAwesome.fa_crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy))
|
||||
};
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
background.FadeTo(0.5f, 300, Easing.OutQuint);
|
||||
@ -321,11 +331,10 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
|
||||
public string TooltipText => name;
|
||||
|
||||
public ScoreComponentLabel(FontAwesome icon, string value, string name)
|
||||
public ScoreComponentLabel(LeaderboardScoreStatistic statistic)
|
||||
{
|
||||
this.name = name;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Width = 60;
|
||||
name = statistic.Name;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Child = content = new FillFlowContainer
|
||||
{
|
||||
@ -356,11 +365,11 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(icon_size - 6),
|
||||
Colour = OsuColour.FromHex(@"a4edff"),
|
||||
Icon = icon,
|
||||
Icon = statistic.Icon,
|
||||
},
|
||||
},
|
||||
},
|
||||
new GlowingSpriteText(value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa"))
|
||||
new GlowingSpriteText(statistic.Value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa"))
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
@ -369,5 +378,19 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class LeaderboardScoreStatistic
|
||||
{
|
||||
public FontAwesome Icon;
|
||||
public string Value;
|
||||
public string Name;
|
||||
|
||||
public LeaderboardScoreStatistic(FontAwesome icon, string name, string value)
|
||||
{
|
||||
Icon = icon;
|
||||
Name = name;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Select.Leaderboards
|
||||
namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
public class MessagePlaceholder : Placeholder
|
||||
{
|
@ -5,7 +5,7 @@ using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Screens.Select.Leaderboards
|
||||
namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
public abstract class Placeholder : OsuTextFlowContainer, IEquatable<Placeholder>
|
||||
{
|
@ -1,7 +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
|
||||
|
||||
namespace osu.Game.Screens.Select.Leaderboards
|
||||
namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
public enum PlaceholderState
|
||||
{
|
@ -8,7 +8,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Select.Leaderboards
|
||||
namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
public class RetrievalFailurePlaceholder : Placeholder
|
||||
{
|
@ -1,11 +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 osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
@ -13,137 +9,10 @@ namespace osu.Game.Online.Multiplayer
|
||||
public abstract class GameType
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
|
||||
public abstract Drawable GetIcon(OsuColour colours, float size);
|
||||
|
||||
public override int GetHashCode() => GetType().GetHashCode();
|
||||
public override bool Equals(object obj) => GetType() == obj?.GetType();
|
||||
}
|
||||
|
||||
public class GameTypeTag : GameType
|
||||
{
|
||||
public override string Name => "Tag";
|
||||
public override Drawable GetIcon(OsuColour colours, float size)
|
||||
{
|
||||
return new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.fa_refresh,
|
||||
Size = new Vector2(size),
|
||||
Colour = colours.Blue,
|
||||
Shadow = false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class GameTypeVersus : GameType
|
||||
{
|
||||
public override string Name => "Versus";
|
||||
public override Drawable GetIcon(OsuColour colours, float size)
|
||||
{
|
||||
return new VersusRow(colours.Blue, colours.Blue, size * 0.6f)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class GameTypeTagTeam : GameType
|
||||
{
|
||||
public override string Name => "Tag Team";
|
||||
public override Drawable GetIcon(OsuColour colours, float size)
|
||||
{
|
||||
return new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(2f),
|
||||
Children = new[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.fa_refresh,
|
||||
Size = new Vector2(size * 0.75f),
|
||||
Colour = colours.Blue,
|
||||
Shadow = false,
|
||||
},
|
||||
new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.fa_refresh,
|
||||
Size = new Vector2(size * 0.75f),
|
||||
Colour = colours.Pink,
|
||||
Shadow = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class GameTypeTeamVersus : GameType
|
||||
{
|
||||
public override string Name => "Team Versus";
|
||||
public override Drawable GetIcon(OsuColour colours, float size)
|
||||
{
|
||||
return new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(2f),
|
||||
Children = new[]
|
||||
{
|
||||
new VersusRow(colours.Blue, colours.Pink, size * 0.5f),
|
||||
new VersusRow(colours.Blue, colours.Pink, size * 0.5f),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class VersusRow : FillFlowContainer
|
||||
{
|
||||
public VersusRow(Color4 first, Color4 second, float size)
|
||||
{
|
||||
var triangleSize = new Vector2(size);
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Spacing = new Vector2(2f, 0f);
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Size = triangleSize,
|
||||
Colour = first,
|
||||
Children = new[]
|
||||
{
|
||||
new EquilateralTriangle
|
||||
{
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Rotation = 90,
|
||||
EdgeSmoothness = new Vector2(1f),
|
||||
},
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Size = triangleSize,
|
||||
Colour = second,
|
||||
Children = new[]
|
||||
{
|
||||
new EquilateralTriangle
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Rotation = -90,
|
||||
EdgeSmoothness = new Vector2(1f),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
27
osu.Game/Online/Multiplayer/GameTypes/GameTypeTag.cs
Normal file
27
osu.Game/Online/Multiplayer/GameTypes/GameTypeTag.cs
Normal file
@ -0,0 +1,27 @@
|
||||
// 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.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer.GameTypes
|
||||
{
|
||||
public class GameTypeTag : GameType
|
||||
{
|
||||
public override string Name => "Tag";
|
||||
|
||||
public override Drawable GetIcon(OsuColour colours, float size)
|
||||
{
|
||||
return new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.fa_refresh,
|
||||
Size = new Vector2(size),
|
||||
Colour = colours.Blue,
|
||||
Shadow = false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
44
osu.Game/Online/Multiplayer/GameTypes/GameTypeTagTeam.cs
Normal file
44
osu.Game/Online/Multiplayer/GameTypes/GameTypeTagTeam.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer.GameTypes
|
||||
{
|
||||
public class GameTypeTagTeam : GameType
|
||||
{
|
||||
public override string Name => "Tag Team";
|
||||
|
||||
public override Drawable GetIcon(OsuColour colours, float size)
|
||||
{
|
||||
return new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(2f),
|
||||
Children = new[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.fa_refresh,
|
||||
Size = new Vector2(size * 0.75f),
|
||||
Colour = colours.Blue,
|
||||
Shadow = false,
|
||||
},
|
||||
new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.fa_refresh,
|
||||
Size = new Vector2(size * 0.75f),
|
||||
Colour = colours.Pink,
|
||||
Shadow = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
32
osu.Game/Online/Multiplayer/GameTypes/GameTypeTeamVersus.cs
Normal file
32
osu.Game/Online/Multiplayer/GameTypes/GameTypeTeamVersus.cs
Normal file
@ -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 osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer.GameTypes
|
||||
{
|
||||
public class GameTypeTeamVersus : GameType
|
||||
{
|
||||
public override string Name => "Team Versus";
|
||||
|
||||
public override Drawable GetIcon(OsuColour colours, float size)
|
||||
{
|
||||
return new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(2f),
|
||||
Children = new[]
|
||||
{
|
||||
new VersusRow(colours.Blue, colours.Pink, size * 0.5f),
|
||||
new VersusRow(colours.Blue, colours.Pink, size * 0.5f),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
24
osu.Game/Online/Multiplayer/GameTypes/GameTypeTimeshift.cs
Normal file
24
osu.Game/Online/Multiplayer/GameTypes/GameTypeTimeshift.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// 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.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer.GameTypes
|
||||
{
|
||||
public class GameTypeTimeshift : GameType
|
||||
{
|
||||
public override string Name => "Timeshift";
|
||||
|
||||
public override Drawable GetIcon(OsuColour colours, float size) => new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.fa_clock_o,
|
||||
Size = new Vector2(size),
|
||||
Colour = colours.Blue,
|
||||
Shadow = false
|
||||
};
|
||||
}
|
||||
}
|
22
osu.Game/Online/Multiplayer/GameTypes/GameTypeVersus.cs
Normal file
22
osu.Game/Online/Multiplayer/GameTypes/GameTypeVersus.cs
Normal file
@ -0,0 +1,22 @@
|
||||
// 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.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer.GameTypes
|
||||
{
|
||||
public class GameTypeVersus : GameType
|
||||
{
|
||||
public override string Name => "Versus";
|
||||
|
||||
public override Drawable GetIcon(OsuColour colours, float size)
|
||||
{
|
||||
return new VersusRow(colours.Blue, colours.Blue, size * 0.6f)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
55
osu.Game/Online/Multiplayer/GameTypes/VersusRow.cs
Normal file
55
osu.Game/Online/Multiplayer/GameTypes/VersusRow.cs
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer.GameTypes
|
||||
{
|
||||
public class VersusRow : FillFlowContainer
|
||||
{
|
||||
public VersusRow(Color4 first, Color4 second, float size)
|
||||
{
|
||||
var triangleSize = new Vector2(size);
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Spacing = new Vector2(2f, 0f);
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Size = triangleSize,
|
||||
Colour = first,
|
||||
Children = new[]
|
||||
{
|
||||
new EquilateralTriangle
|
||||
{
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Rotation = 90,
|
||||
EdgeSmoothness = new Vector2(1f),
|
||||
},
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Size = triangleSize,
|
||||
Colour = second,
|
||||
Children = new[]
|
||||
{
|
||||
new EquilateralTriangle
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Rotation = -90,
|
||||
EdgeSmoothness = new Vector2(1f),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
93
osu.Game/Online/Multiplayer/PlaylistItem.cs
Normal file
93
osu.Game/Online/Multiplayer/PlaylistItem.cs
Normal file
@ -0,0 +1,93 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
{
|
||||
public class PlaylistItem
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public int ID { get; set; }
|
||||
|
||||
[JsonProperty("beatmap_id")]
|
||||
public int BeatmapID { get; set; }
|
||||
|
||||
[JsonProperty("ruleset_id")]
|
||||
public int RulesetID { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public BeatmapInfo Beatmap
|
||||
{
|
||||
get => beatmap;
|
||||
set
|
||||
{
|
||||
beatmap = value;
|
||||
BeatmapID = value?.OnlineBeatmapID ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public RulesetInfo Ruleset { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public readonly BindableCollection<Mod> AllowedMods = new BindableCollection<Mod>();
|
||||
|
||||
[JsonIgnore]
|
||||
public readonly BindableCollection<Mod> RequiredMods = new BindableCollection<Mod>();
|
||||
|
||||
[JsonProperty("beatmap")]
|
||||
private APIBeatmap apiBeatmap { get; set; }
|
||||
|
||||
[JsonProperty("allowed_mods")]
|
||||
private APIMod[] allowedMods
|
||||
{
|
||||
get => AllowedMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
|
||||
set => _allowedMods = value;
|
||||
}
|
||||
|
||||
[JsonProperty("required_mods")]
|
||||
private APIMod[] requiredMods
|
||||
{
|
||||
get => RequiredMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
|
||||
set => _requiredMods = value;
|
||||
}
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
private APIMod[] _allowedMods;
|
||||
private APIMod[] _requiredMods;
|
||||
|
||||
public void MapObjects(BeatmapManager beatmaps, RulesetStore rulesets)
|
||||
{
|
||||
// If we don't have an api beatmap, the request occurred as a result of room creation, so we can query the local beatmap instead
|
||||
// Todo: Is this a bug? Room creation only returns the beatmap ID
|
||||
Beatmap = apiBeatmap == null ? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == BeatmapID) : apiBeatmap.ToBeatmap(rulesets);
|
||||
Ruleset = rulesets.GetRuleset(RulesetID);
|
||||
|
||||
if (_allowedMods != null)
|
||||
{
|
||||
AllowedMods.Clear();
|
||||
AllowedMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => _allowedMods.Any(m => m.Acronym == mod.Acronym)));
|
||||
|
||||
_allowedMods = null;
|
||||
}
|
||||
|
||||
if (_requiredMods != null)
|
||||
{
|
||||
RequiredMods.Clear();
|
||||
RequiredMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => _requiredMods.Any(m => m.Acronym == mod.Acronym)));
|
||||
|
||||
_requiredMods = null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldSerializeID() => false;
|
||||
public bool ShouldSerializeapiBeatmap() => false;
|
||||
}
|
||||
}
|
@ -1,22 +1,120 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Multiplayer.GameTypes;
|
||||
using osu.Game.Online.Multiplayer.RoomStatuses;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
{
|
||||
public class Room
|
||||
{
|
||||
public Bindable<string> Name = new Bindable<string>();
|
||||
public Bindable<User> Host = new Bindable<User>();
|
||||
public Bindable<RoomStatus> Status = new Bindable<RoomStatus>();
|
||||
public Bindable<RoomAvailability> Availability = new Bindable<RoomAvailability>();
|
||||
public Bindable<GameType> Type = new Bindable<GameType>();
|
||||
public Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
|
||||
public Bindable<int?> MaxParticipants = new Bindable<int?>();
|
||||
public Bindable<IEnumerable<User>> Participants = new Bindable<IEnumerable<User>>();
|
||||
[JsonProperty("id")]
|
||||
public Bindable<int?> RoomID { get; private set; } = new Bindable<int?>();
|
||||
|
||||
[JsonProperty("name")]
|
||||
public Bindable<string> Name { get; private set; } = new Bindable<string>();
|
||||
|
||||
[JsonProperty("host")]
|
||||
public Bindable<User> Host { get; private set; } = new Bindable<User>();
|
||||
|
||||
[JsonProperty("playlist")]
|
||||
public BindableCollection<PlaylistItem> Playlist { get; set; } = new BindableCollection<PlaylistItem>();
|
||||
|
||||
[JsonProperty("channel_id")]
|
||||
public Bindable<int> ChannelId { get; private set; } = new Bindable<int>();
|
||||
|
||||
[JsonIgnore]
|
||||
public Bindable<TimeSpan> Duration { get; private set; } = new Bindable<TimeSpan>(TimeSpan.FromMinutes(30));
|
||||
|
||||
[JsonIgnore]
|
||||
public Bindable<int?> MaxAttempts { get; private set; } = new Bindable<int?>();
|
||||
|
||||
[JsonIgnore]
|
||||
public Bindable<RoomStatus> Status { get; private set; } = new Bindable<RoomStatus>(new RoomStatusOpen());
|
||||
|
||||
[JsonIgnore]
|
||||
public Bindable<RoomAvailability> Availability { get; private set; } = new Bindable<RoomAvailability>();
|
||||
|
||||
[JsonIgnore]
|
||||
public Bindable<GameType> Type { get; private set; } = new Bindable<GameType>(new GameTypeTimeshift());
|
||||
|
||||
[JsonIgnore]
|
||||
public Bindable<int?> MaxParticipants { get; private set; } = new Bindable<int?>();
|
||||
|
||||
[JsonIgnore]
|
||||
public Bindable<IEnumerable<User>> Participants { get; private set; } = new Bindable<IEnumerable<User>>(Enumerable.Empty<User>());
|
||||
|
||||
public Bindable<int> ParticipantCount { get; private set; } = new Bindable<int>();
|
||||
|
||||
// todo: TEMPORARY
|
||||
[JsonProperty("participant_count")]
|
||||
private int? participantCount
|
||||
{
|
||||
get => ParticipantCount;
|
||||
set => ParticipantCount.Value = value ?? 0;
|
||||
}
|
||||
|
||||
[JsonProperty("duration")]
|
||||
private int duration
|
||||
{
|
||||
get => (int)Duration.Value.TotalMinutes;
|
||||
set => Duration.Value = TimeSpan.FromMinutes(value);
|
||||
}
|
||||
|
||||
// Only supports retrieval for now
|
||||
[JsonProperty("ends_at")]
|
||||
public Bindable<DateTimeOffset> EndDate { get; private set; } = new Bindable<DateTimeOffset>();
|
||||
|
||||
// Todo: Find a better way to do this (https://github.com/ppy/osu-framework/issues/1930)
|
||||
[JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
private int? maxAttempts
|
||||
{
|
||||
get => MaxAttempts;
|
||||
set => MaxAttempts.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of this <see cref="Room"/> in the list. This is not read from or written to the API.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public int Position = -1;
|
||||
|
||||
public void CopyFrom(Room other)
|
||||
{
|
||||
RoomID.Value = other.RoomID;
|
||||
Name.Value = other.Name;
|
||||
|
||||
if (other.Host.Value != null && Host.Value?.Id != other.Host.Value.Id)
|
||||
Host.Value = other.Host;
|
||||
|
||||
Status.Value = other.Status;
|
||||
Availability.Value = other.Availability;
|
||||
Type.Value = other.Type;
|
||||
MaxParticipants.Value = other.MaxParticipants;
|
||||
ParticipantCount.Value = other.ParticipantCount.Value;
|
||||
Participants.Value = other.Participants.Value.ToArray();
|
||||
EndDate.Value = other.EndDate;
|
||||
|
||||
if (DateTimeOffset.Now >= EndDate.Value)
|
||||
Status.Value = new RoomStatusEnded();
|
||||
|
||||
// Todo: Temporary, should only remove/add new items (requires framework changes)
|
||||
if (Playlist.Count == 0)
|
||||
Playlist.AddRange(other.Playlist);
|
||||
else if (other.Playlist.Count > 0)
|
||||
Playlist.First().ID = other.Playlist.First().ID;
|
||||
|
||||
Position = other.Position;
|
||||
}
|
||||
|
||||
public bool ShouldSerializeRoomID() => false;
|
||||
public bool ShouldSerializeHost() => false;
|
||||
public bool ShouldSerializeEndDate() => false;
|
||||
}
|
||||
}
|
||||
|
@ -10,17 +10,8 @@ namespace osu.Game.Online.Multiplayer
|
||||
{
|
||||
public abstract string Message { get; }
|
||||
public abstract Color4 GetAppropriateColour(OsuColour colours);
|
||||
}
|
||||
|
||||
public class RoomStatusOpen : RoomStatus
|
||||
{
|
||||
public override string Message => @"Welcoming Players";
|
||||
public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenLight;
|
||||
}
|
||||
|
||||
public class RoomStatusPlaying : RoomStatus
|
||||
{
|
||||
public override string Message => @"Now Playing";
|
||||
public override Color4 GetAppropriateColour(OsuColour colours) => colours.Purple;
|
||||
public override int GetHashCode() => GetType().GetHashCode();
|
||||
public override bool Equals(object obj) => GetType() == obj?.GetType();
|
||||
}
|
||||
}
|
||||
|
14
osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusEnded.cs
Normal file
14
osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusEnded.cs
Normal file
@ -0,0 +1,14 @@
|
||||
// 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.Graphics;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer.RoomStatuses
|
||||
{
|
||||
public class RoomStatusEnded : RoomStatus
|
||||
{
|
||||
public override string Message => @"Ended";
|
||||
public override Color4 GetAppropriateColour(OsuColour colours) => colours.YellowDarker;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user