1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 12:23:22 +08:00

Merge branch 'master' into improve-text-search

This commit is contained in:
Dan Balasescu 2018-12-22 16:38:50 +09:00 committed by GitHub
commit e6c197e9aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1174 additions and 366 deletions

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (catch)" type="DotNetProject" factoryName=".NET Project"> <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="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (mania)" type="DotNetProject" factoryName=".NET Project"> <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="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (osu!)" type="DotNetProject" factoryName=".NET Project"> <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="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (taiko)" type="DotNetProject" factoryName=".NET Project"> <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="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="VisualTests" type="DotNetProject" factoryName=".NET Project"> <configuration default="false" name="VisualTests" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp2.1/osu.Game.Tests.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />

16
.vscode/launch.json vendored
View File

@ -7,13 +7,13 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "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}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Debug)", "preLaunchTask": "Build tests (Debug)",
"linux": { "linux": {
"env": { "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" "console": "internalConsole"
@ -24,13 +24,13 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "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}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Release)", "preLaunchTask": "Build tests (Release)",
"linux": { "linux": {
"env": { "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" "console": "internalConsole"
@ -41,13 +41,13 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll" "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.2/osu!.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)", "preLaunchTask": "Build osu! (Debug)",
"linux": { "linux": {
"env": { "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" "console": "internalConsole"
@ -58,13 +58,13 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1/osu!.dll" "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.2/osu!.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)", "preLaunchTask": "Build osu! (Release)",
"linux": { "linux": {
"env": { "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" "console": "internalConsole"

6
.vscode/tasks.json vendored
View File

@ -11,7 +11,6 @@
"build", "build",
"--no-restore", "--no-restore",
"osu.Desktop", "osu.Desktop",
"/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -27,7 +26,6 @@
"build", "build",
"--no-restore", "--no-restore",
"osu.Desktop", "osu.Desktop",
"/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release", "/p:Configuration=Release",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
@ -44,7 +42,6 @@
"build", "build",
"--no-restore", "--no-restore",
"osu.Game.Tests", "osu.Game.Tests",
"/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -60,7 +57,6 @@
"build", "build",
"--no-restore", "--no-restore",
"osu.Game.Tests", "osu.Game.Tests",
"/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release", "/p:Configuration=Release",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
@ -70,7 +66,7 @@
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{ {
"label": "Restore (netcoreapp2.1)", "label": "Restore (netcoreapp2.2)",
"type": "shell", "type": "shell",
"command": "dotnet", "command": "dotnet",
"args": [ "args": [

View File

@ -10,7 +10,7 @@ We are accepting bug reports (please report with as much detail as possible). Fe
# Requirements # 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). - 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 # Building and running

@ -1 +1 @@
Subproject commit 694cb03f19c93106ed0f2593f3e506e835fb652a Subproject commit 9880089b4e8fcd78d68f30c8a40d43bf8dccca86

View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "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}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)", "preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "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}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)", "preLaunchTask": "Build (Release)",

View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "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}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)", "preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "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}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)", "preLaunchTask": "Build (Release)",

View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "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}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)", "preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "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}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)", "preLaunchTask": "Build (Release)",

View File

@ -0,0 +1,194 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModBlinds : Mod, IApplicableToRulesetContainer<OsuHitObject>, IApplicableToScoreProcessor
{
public override string Name => "Blinds";
public override string Description => "Play with blinds on your screen.";
public override string Acronym => "BL";
public override FontAwesome Icon => FontAwesome.fa_adjust;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => false;
public override double ScoreMultiplier => 1.12;
private DrawableOsuBlinds blinds;
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
{
rulesetContainer.Overlays.Add(blinds = new DrawableOsuBlinds(rulesetContainer.Playfield.HitObjectContainer, rulesetContainer.Beatmap));
}
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{
scoreProcessor.Health.ValueChanged += val => { blinds.AnimateClosedness((float)val); };
}
/// <summary>
/// Element for the Blinds mod drawing 2 black boxes covering the whole screen which resize inside a restricted area with some leniency.
/// </summary>
public class DrawableOsuBlinds : Container
{
/// <summary>
/// Black background boxes behind blind panel textures.
/// </summary>
private Box blackBoxLeft, blackBoxRight;
private Drawable panelLeft, panelRight, bgPanelLeft, bgPanelRight;
private readonly Beatmap<OsuHitObject> beatmap;
/// <summary>
/// Value between 0 and 1 setting a maximum "closedness" for the blinds.
/// Useful for animating how far the blinds can be opened while keeping them at the original position if they are wider open than this.
/// </summary>
private const float target_clamp = 1;
private readonly float targetBreakMultiplier = 0;
private readonly float easing = 1;
private readonly CompositeDrawable restrictTo;
/// <summary>
/// <para>
/// Percentage of playfield to extend blinds over. Basically moves the origin points where the blinds start.
/// </para>
/// <para>
/// -1 would mean the blinds always cover the whole screen no matter health.
/// 0 would mean the blinds will only ever be on the edge of the playfield on 0% health.
/// 1 would mean the blinds are fully outside the playfield on 50% health.
/// Infinity would mean the blinds are always outside the playfield except on 100% health.
/// </para>
/// </summary>
private const float leniency = 0.1f;
public DrawableOsuBlinds(CompositeDrawable restrictTo, Beatmap<OsuHitObject> beatmap)
{
this.restrictTo = restrictTo;
this.beatmap = beatmap;
}
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.Both;
Children = new[]
{
blackBoxLeft = new Box
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Y,
},
blackBoxRight = new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Y,
},
bgPanelLeft = new ModBlindsPanel
{
Origin = Anchor.TopRight,
Colour = Color4.Gray,
},
panelLeft = new ModBlindsPanel { Origin = Anchor.TopRight, },
bgPanelRight = new ModBlindsPanel { Colour = Color4.Gray },
panelRight = new ModBlindsPanel()
};
}
private float calculateGap(float value) => MathHelper.Clamp(value, 0, target_clamp) * targetBreakMultiplier;
// lagrange polinominal for (0,0) (0.6,0.4) (1,1) should make a good curve
private static float applyAdjustmentCurve(float value) => 0.6f * value * value + 0.4f * value;
protected override void Update()
{
float start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X;
float end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X;
float rawWidth = end - start;
start -= rawWidth * leniency * 0.5f;
end += rawWidth * leniency * 0.5f;
float width = (end - start) * 0.5f * applyAdjustmentCurve(calculateGap(easing));
// different values in case the playfield ever moves from center to somewhere else.
blackBoxLeft.Width = start + width;
blackBoxRight.Width = DrawWidth - end + width;
panelLeft.X = start + width;
panelRight.X = end - width;
bgPanelLeft.X = start;
bgPanelRight.X = end;
}
protected override void LoadComplete()
{
const float break_open_early = 500;
const float break_close_late = 250;
base.LoadComplete();
var firstObj = beatmap.HitObjects[0];
var startDelay = firstObj.StartTime - firstObj.TimePreempt;
using (BeginAbsoluteSequence(startDelay + break_close_late, true))
leaveBreak();
foreach (var breakInfo in beatmap.Breaks)
{
if (breakInfo.HasEffect)
{
using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early, true))
{
enterBreak();
using (BeginDelayedSequence(breakInfo.Duration + break_open_early + break_close_late, true))
leaveBreak();
}
}
}
}
private void enterBreak() => this.TransformTo(nameof(targetBreakMultiplier), 0f, 1000, Easing.OutSine);
private void leaveBreak() => this.TransformTo(nameof(targetBreakMultiplier), 1f, 2500, Easing.OutBounce);
/// <summary>
/// 0 is open, 1 is closed.
/// </summary>
public void AnimateClosedness(float value) => this.TransformTo(nameof(easing), value, 200, Easing.OutQuint);
public class ModBlindsPanel : Sprite
{
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get("Play/osu/blinds-panel");
}
}
}
}
}

View File

@ -26,9 +26,6 @@ namespace osu.Game.Rulesets.Osu.Mods
if (slider == null) if (slider == null)
return; 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<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)); slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));

View File

@ -31,8 +31,8 @@ namespace osu.Game.Rulesets.Osu
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[] public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{ {
new KeyBinding(InputKey.A, OsuAction.LeftButton), new KeyBinding(InputKey.Z, OsuAction.LeftButton),
new KeyBinding(InputKey.S, OsuAction.RightButton), new KeyBinding(InputKey.X, OsuAction.RightButton),
new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton), new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton),
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton), new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
}; };
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu
new MultiMod(new OsuModSuddenDeath(), new OsuModPerfect()), new MultiMod(new OsuModSuddenDeath(), new OsuModPerfect()),
new MultiMod(new OsuModDoubleTime(), new OsuModNightcore()), new MultiMod(new OsuModDoubleTime(), new OsuModNightcore()),
new OsuModHidden(), new OsuModHidden(),
new OsuModFlashlight(), new MultiMod(new OsuModFlashlight(), new OsuModBlinds()),
}; };
case ModType.Conversion: case ModType.Conversion:
return new Mod[] return new Mod[]

View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "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}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)", "preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "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}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)", "preLaunchTask": "Build (Release)",

View 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);
}
}
}
}

View File

@ -11,6 +11,7 @@ using osu.Framework.Allocation;
using osuTK; using osuTK;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Scoring; using osu.Game.Scoring;
@ -36,7 +37,7 @@ namespace osu.Game.Tests.Visual
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f), Size = new Vector2(550f, 450f),
Scope = LeaderboardScope.Global, Scope = BeatmapLeaderboardScope.Global,
}); });
AddStep(@"New Scores", newScores); 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) public void SetRetrievalState(PlaceholderState state)
{ {

View File

@ -8,7 +8,9 @@ using osu.Framework.Allocation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Pages;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
@ -23,8 +25,8 @@ namespace osu.Game.Tests.Visual
typeof(ScoreInfo), typeof(ScoreInfo),
typeof(Results), typeof(Results),
typeof(ResultsPage), typeof(ResultsPage),
typeof(ResultsPageScore), typeof(ScoreResultsPage),
typeof(ResultsPageRanking) typeof(RankingResultsPage)
}; };
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -41,7 +43,7 @@ namespace osu.Game.Tests.Visual
if (beatmapInfo != null) if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
Add(new Results(new ScoreInfo Add(new SoloResults(new ScoreInfo
{ {
TotalScore = 2845370, TotalScore = 2845370,
Accuracy = 0.98, Accuracy = 0.98,

View 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!"
}));
}
}
}

View File

@ -4,6 +4,7 @@
using System; using System;
using Humanizer; using Humanizer;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -11,13 +12,27 @@ namespace osu.Game.Graphics
{ {
public class DrawableDate : OsuSpriteText, IHasTooltip 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) public DrawableDate(DateTimeOffset date)
{ {
Font = "Exo2.0-RegularItalic"; Font = "Exo2.0-RegularItalic";
Date = date.ToLocalTime(); Date = date;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -39,14 +54,14 @@ namespace osu.Game.Graphics
var diffToNow = DateTimeOffset.Now.Subtract(Date); var diffToNow = DateTimeOffset.Now.Subtract(Date);
double timeUntilNextUpdate = 1000; double timeUntilNextUpdate = 1000;
if (diffToNow.TotalSeconds > 60) if (Math.Abs(diffToNow.TotalSeconds) > 120)
{ {
timeUntilNextUpdate *= 60; timeUntilNextUpdate *= 60;
if (diffToNow.TotalMinutes > 60) if (Math.Abs(diffToNow.TotalMinutes) > 120)
{ {
timeUntilNextUpdate *= 60; timeUntilNextUpdate *= 60;
if (diffToNow.TotalHours > 24) if (Math.Abs(diffToNow.TotalHours) > 48)
timeUntilNextUpdate *= 24; timeUntilNextUpdate *= 24;
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Allocation; 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; private Color4 accentColour;
public Color4 AccentColour public Color4 AccentColour

View File

@ -13,15 +13,15 @@ namespace osu.Game.Online.API.Requests
public class GetScoresRequest : APIRequest<APIScores> public class GetScoresRequest : APIRequest<APIScores>
{ {
private readonly BeatmapInfo beatmap; private readonly BeatmapInfo beatmap;
private readonly LeaderboardScope scope; private readonly BeatmapLeaderboardScope scope;
private readonly RulesetInfo ruleset; 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) if (!beatmap.OnlineBeatmapID.HasValue)
throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); 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"); throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard");
this.beatmap = beatmap; this.beatmap = beatmap;

View File

@ -96,12 +96,14 @@ namespace osu.Game.Online.Chat
/// </summary> /// </summary>
/// <param name="text">The message text that is going to be posted</param> /// <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> /// <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) if (target == null)
return; target = CurrentChannel.Value;
var currentChannel = CurrentChannel.Value; if (target == null)
return;
void dequeueAndRun() void dequeueAndRun()
{ {
@ -113,7 +115,7 @@ namespace osu.Game.Online.Chat
{ {
if (!api.IsLoggedIn) 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; return;
} }
@ -121,29 +123,29 @@ namespace osu.Game.Online.Chat
{ {
Sender = api.LocalUser.Value, Sender = api.LocalUser.Value,
Timestamp = DateTimeOffset.Now, Timestamp = DateTimeOffset.Now,
ChannelId = CurrentChannel.Value.Id, ChannelId = target.Id,
IsAction = isAction, IsAction = isAction,
Content = text 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 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 => createNewPrivateMessageRequest.Success += createRes =>
{ {
currentChannel.Id = createRes.ChannelID; target.Id = createRes.ChannelID;
currentChannel.ReplaceMessage(message, createRes.Message); target.ReplaceMessage(message, createRes.Message);
dequeueAndRun(); dequeueAndRun();
}; };
createNewPrivateMessageRequest.Failure += exception => createNewPrivateMessageRequest.Failure += exception =>
{ {
Logger.Error(exception, "Posting message failed."); Logger.Error(exception, "Posting message failed.");
currentChannel.ReplaceMessage(message, null); target.ReplaceMessage(message, null);
dequeueAndRun(); dequeueAndRun();
}; };
@ -155,14 +157,14 @@ namespace osu.Game.Online.Chat
req.Success += m => req.Success += m =>
{ {
currentChannel.ReplaceMessage(message, m); target.ReplaceMessage(message, m);
dequeueAndRun(); dequeueAndRun();
}; };
req.Failure += exception => req.Failure += exception =>
{ {
Logger.Error(exception, "Posting message failed."); Logger.Error(exception, "Posting message failed.");
currentChannel.ReplaceMessage(message, null); target.ReplaceMessage(message, null);
dequeueAndRun(); 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. /// Posts a command locally. Commands like /help will result in a help message written in the current channel.
/// </summary> /// </summary>
/// <param name="text">the text containing the command identifier and command parameters.</param> /// <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; return;
var parameters = text.Split(new[] { ' ' }, 2); var parameters = text.Split(new[] { ' ' }, 2);
@ -192,7 +198,7 @@ namespace osu.Game.Online.Chat
case "me": case "me":
if (string.IsNullOrWhiteSpace(content)) if (string.IsNullOrWhiteSpace(content))
{ {
CurrentChannel.Value.AddNewMessages(new ErrorMessage("Usage: /me [action]")); target.AddNewMessages(new ErrorMessage("Usage: /me [action]"));
break; break;
} }
@ -200,11 +206,11 @@ namespace osu.Game.Online.Chat
break; break;
case "help": case "help":
CurrentChannel.Value.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]"));
break; break;
default: 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; break;
} }
} }

View File

@ -0,0 +1,148 @@
// 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
{
private readonly bool postingTextbox;
public readonly Bindable<Channel> Channel = new Bindable<Channel>();
private readonly FocusedTextBox textbox;
protected ChannelManager ChannelManager;
private ScrollContainer scroll;
private DrawableChannel drawableChannel;
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,
});
}
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)
{
}
}
}
}

View File

@ -2,14 +2,14 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Extensions;
using osu.Game.Scoring; using osu.Game.Scoring;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public class DrawableRank : Container public class DrawableRank : Container
{ {

View File

@ -3,27 +3,22 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osuTK; using System.Linq;
using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osuTK;
using System.Linq; using osuTK.Graphics;
using osu.Framework.Configuration;
using osu.Game.Rulesets;
using osu.Game.Scoring;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public class Leaderboard : Container public abstract class Leaderboard<TScope, ScoreInfo> : Container
{ {
private const double fade_duration = 300; private const double fade_duration = 300;
@ -32,10 +27,6 @@ namespace osu.Game.Screens.Select.Leaderboards
private FillFlowContainer<LeaderboardScore> scrollFlow; private FillFlowContainer<LeaderboardScore> scrollFlow;
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
public Action<ScoreInfo> ScoreSelected;
private readonly LoadingAnimation loading; private readonly LoadingAnimation loading;
private ScheduledDelegate showScoresDelegate; private ScheduledDelegate showScoresDelegate;
@ -70,7 +61,7 @@ namespace osu.Game.Screens.Select.Leaderboards
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 5f), Spacing = new Vector2(0f, 5f),
Padding = new MarginPadding { Top = 10, Bottom = 5 }, Padding = new MarginPadding { Top = 10, Bottom = 5 },
ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) }) ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1))
}; };
// schedule because we may not be loaded yet (LoadComponentAsync complains). // schedule because we may not be loaded yet (LoadComponentAsync complains).
@ -96,18 +87,18 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
} }
private LeaderboardScope scope; private TScope scope;
public LeaderboardScope Scope public TScope Scope
{ {
get { return scope; } get { return scope; }
set set
{ {
if (value == scope) if (value.Equals(scope))
return; return;
scope = value; scope = value;
updateScores(); UpdateScores();
} }
} }
@ -137,7 +128,7 @@ namespace osu.Game.Screens.Select.Leaderboards
case PlaceholderState.NetworkFailure: case PlaceholderState.NetworkFailure:
replacePlaceholder(new RetrievalFailurePlaceholder replacePlaceholder(new RetrievalFailurePlaceholder
{ {
OnRetry = updateScores, OnRetry = UpdateScores,
}); });
break; break;
case PlaceholderState.Unavailable: case PlaceholderState.Unavailable:
@ -159,7 +150,7 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
} }
public Leaderboard() protected Leaderboard()
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
@ -177,36 +168,14 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
private APIAccess api; private APIAccess api;
private BeatmapInfo beatmap;
[Resolved]
private ScoreManager scoreManager { get; set; }
private ScheduledDelegate pendingUpdateScores; private ScheduledDelegate pendingUpdateScores;
public BeatmapInfo Beatmap [BackgroundDependencyLoader(true)]
{ private void load(APIAccess api)
get { return beatmap; }
set
{
if (beatmap == value)
return;
beatmap = value;
Scores = null;
updateScores();
}
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, IBindable<RulesetInfo> parentRuleset)
{ {
this.api = api; this.api = api;
ruleset.BindTo(parentRuleset);
ruleset.ValueChanged += _ => updateScores();
if (api != null) if (api != null)
api.OnStateChange += handleApiStateChange; api.OnStateChange += handleApiStateChange;
} }
@ -219,21 +188,17 @@ namespace osu.Game.Screens.Select.Leaderboards
api.OnStateChange -= handleApiStateChange; api.OnStateChange -= handleApiStateChange;
} }
public void RefreshScores() => updateScores(); public void RefreshScores() => UpdateScores();
private GetScoresRequest getScoresRequest; private APIRequest getScoresRequest;
private void handleApiStateChange(APIState oldState, APIState newState) private void handleApiStateChange(APIState oldState, APIState newState)
{ {
if (Scope == LeaderboardScope.Local)
// No need to respond to API state change while current scope is local
return;
if (newState == APIState.Online) if (newState == APIState.Online)
updateScores(); UpdateScores();
} }
private void updateScores() protected void UpdateScores()
{ {
// don't display any scores or placeholder until the first Scores_Set has been called. // 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. // this avoids scope changes flickering a "no scores" placeholder before initialisation of song select is finished.
@ -245,40 +210,23 @@ namespace osu.Game.Screens.Select.Leaderboards
pendingUpdateScores?.Cancel(); pendingUpdateScores?.Cancel();
pendingUpdateScores = Schedule(() => 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) if (api?.IsLoggedIn != true)
{ {
PlaceholderState = PlaceholderState.NotLoggedIn; PlaceholderState = PlaceholderState.NotLoggedIn;
return; return;
} }
if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
{
PlaceholderState = PlaceholderState.NotSupporter;
return;
}
PlaceholderState = PlaceholderState.Retrieving; PlaceholderState = PlaceholderState.Retrieving;
loading.Show(); loading.Show();
getScoresRequest = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope); getScoresRequest = FetchScores(scores => Schedule(() =>
getScoresRequest.Success += r => Schedule(() =>
{ {
Scores = r.Scores; Scores = scores;
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
}); }));
if (getScoresRequest == null)
return;
getScoresRequest.Failure += e => Schedule(() => getScoresRequest.Failure += e => Schedule(() =>
{ {
@ -292,6 +240,8 @@ namespace osu.Game.Screens.Select.Leaderboards
}); });
} }
protected abstract APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback);
private Placeholder currentPlaceholder; private Placeholder currentPlaceholder;
private void replacePlaceholder(Placeholder placeholder) private void replacePlaceholder(Placeholder placeholder)
@ -344,5 +294,7 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
} }
} }
protected abstract LeaderboardScore CreateDrawableScore(ScoreInfo model, int index);
} }
} }

View File

@ -1,9 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq; using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -17,35 +16,40 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Users; 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 class LeaderboardScore : OsuClickableContainer
{ {
public static readonly float HEIGHT = 60;
public readonly int RankPosition; public readonly int RankPosition;
public readonly ScoreInfo Score;
public const float HEIGHT = 60;
private const float corner_radius = 5; private const float corner_radius = 5;
private const float edge_margin = 5; private const float edge_margin = 5;
private const float background_alpha = 0.25f; private const float background_alpha = 0.25f;
private const float rank_width = 30; private const float rank_width = 30;
protected Container RankContainer { get; private set; }
private readonly ScoreInfo score;
private Box background; private Box background;
private Container content; private Container content;
private Drawable avatar; private Drawable avatar;
private DrawableRank scoreRank; private Drawable scoreRank;
private OsuSpriteText nameLabel; private OsuSpriteText nameLabel;
private GlowingSpriteText scoreLabel; private GlowingSpriteText scoreLabel;
private ScoreComponentLabel maxCombo;
private ScoreComponentLabel accuracy;
private Container flagBadgeContainer; private Container flagBadgeContainer;
private FillFlowContainer<ModIcon> modsContainer; private FillFlowContainer<ModIcon> modsContainer;
private List<ScoreComponentLabel> statisticsLabels;
public LeaderboardScore(ScoreInfo score, int rank) public LeaderboardScore(ScoreInfo score, int rank)
{ {
Score = score; this.score = score;
RankPosition = rank; RankPosition = rank;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -55,6 +59,10 @@ namespace osu.Game.Screens.Select.Leaderboards
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
var user = score.User;
statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s)).ToList();
Children = new Drawable[] Children = new Drawable[]
{ {
new Container new Container
@ -102,7 +110,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Children = new[] Children = new[]
{ {
avatar = new DelayedLoadWrapper( avatar = new DelayedLoadWrapper(
new Avatar(Score.User) new Avatar(user)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
CornerRadius = corner_radius, CornerRadius = corner_radius,
@ -128,7 +136,7 @@ namespace osu.Game.Screens.Select.Leaderboards
{ {
nameLabel = new OsuSpriteText nameLabel = new OsuSpriteText
{ {
Text = Score.User.Username, Text = user.Username,
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
TextSize = 23, TextSize = 23,
}, },
@ -149,7 +157,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Masking = true, Masking = true,
Children = new Drawable[] Children = new Drawable[]
{ {
new DrawableFlag(Score.User?.Country) new DrawableFlag(user.Country)
{ {
Width = 30, Width = 30,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
@ -164,11 +172,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f), Spacing = new Vector2(10f, 0f),
Margin = new MarginPadding { Left = edge_margin }, Margin = new MarginPadding { Left = edge_margin },
Children = new Drawable[] Children = statisticsLabels
{
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"),
},
}, },
}, },
}, },
@ -183,17 +187,17 @@ namespace osu.Game.Screens.Select.Leaderboards
Spacing = new Vector2(5f, 0f), Spacing = new Vector2(5f, 0f),
Children = new Drawable[] Children = new Drawable[]
{ {
scoreLabel = new GlowingSpriteText(Score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")), scoreLabel = new GlowingSpriteText(score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")),
new Container RankContainer = new Container
{ {
Size = new Vector2(40f, 20f), Size = new Vector2(40f, 20f),
Children = new[] Children = new[]
{ {
scoreRank = new DrawableRank(Score.Rank) scoreRank = new DrawableRank(score.Rank)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = 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, Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal, 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() 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(); d.FadeOut();
Alpha = 0; Alpha = 0;
@ -243,7 +247,7 @@ namespace osu.Game.Screens.Select.Leaderboards
using (BeginDelayedSequence(50, true)) 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++) for (int i = 0; i < drawables.Length; i++)
drawables[i].FadeIn(100 + i * 50); 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) protected override bool OnHover(HoverEvent e)
{ {
background.FadeTo(0.5f, 300, Easing.OutQuint); background.FadeTo(0.5f, 300, Easing.OutQuint);
@ -321,11 +331,10 @@ namespace osu.Game.Screens.Select.Leaderboards
public string TooltipText => name; public string TooltipText => name;
public ScoreComponentLabel(FontAwesome icon, string value, string name) public ScoreComponentLabel(LeaderboardScoreStatistic statistic)
{ {
this.name = name; name = statistic.Name;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Both;
Width = 60;
Child = content = new FillFlowContainer Child = content = new FillFlowContainer
{ {
@ -356,11 +365,11 @@ namespace osu.Game.Screens.Select.Leaderboards
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(icon_size - 6), Size = new Vector2(icon_size - 6),
Colour = OsuColour.FromHex(@"a4edff"), 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, Anchor = Anchor.CentreLeft,
Origin = 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;
}
}
} }
} }

View File

@ -4,7 +4,7 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public class MessagePlaceholder : Placeholder public class MessagePlaceholder : Placeholder
{ {

View File

@ -5,7 +5,7 @@ using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public abstract class Placeholder : OsuTextFlowContainer, IEquatable<Placeholder> public abstract class Placeholder : OsuTextFlowContainer, IEquatable<Placeholder>
{ {

View File

@ -1,7 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // 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 public enum PlaceholderState
{ {

View File

@ -8,7 +8,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osuTK; using osuTK;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public class RetrievalFailurePlaceholder : Placeholder public class RetrievalFailurePlaceholder : Placeholder
{ {

View File

@ -9,7 +9,6 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
@ -20,7 +19,6 @@ namespace osu.Game.Overlays.BeatmapSet
private const float height = 50; private const float height = 50;
private readonly UpdateableAvatar avatar; private readonly UpdateableAvatar avatar;
private readonly ClickableArea clickableArea;
private readonly FillFlowContainer fields; private readonly FillFlowContainer fields;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
@ -73,7 +71,7 @@ namespace osu.Game.Overlays.BeatmapSet
Children = new Drawable[] Children = new Drawable[]
{ {
clickableArea = new ClickableArea new Container
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
CornerRadius = 3, CornerRadius = 3,
@ -100,14 +98,8 @@ namespace osu.Game.Overlays.BeatmapSet
}; };
} }
[BackgroundDependencyLoader(true)] private void load()
private void load(UserProfileOverlay profile)
{ {
clickableArea.Action = () =>
{
if (avatar.User != null) profile?.ShowUser(avatar.User);
};
updateDisplay(); updateDisplay();
} }

View File

@ -10,11 +10,11 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores namespace osu.Game.Overlays.BeatmapSet.Scores

View File

@ -12,12 +12,12 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores namespace osu.Game.Overlays.BeatmapSet.Scores

View File

@ -1,72 +1,38 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using System.Linq; using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using osu.Game.Users; using osu.Game.Users;
using osu.Framework.Graphics.Cursor; using osuTK;
using osu.Framework.Graphics.UserInterface; using osuTK.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Chat namespace osu.Game.Overlays.Chat
{ {
public class ChatLine : Container public class ChatLine : CompositeDrawable
{ {
private static readonly Color4[] username_colours = public const float LEFT_PADDING = default_message_padding + default_horizontal_padding * 2;
{
OsuColour.FromHex("588c7e"),
OsuColour.FromHex("b2a367"),
OsuColour.FromHex("c98f65"),
OsuColour.FromHex("bc5151"),
OsuColour.FromHex("5c8bd6"),
OsuColour.FromHex("7f6ab7"),
OsuColour.FromHex("a368ad"),
OsuColour.FromHex("aa6880"),
OsuColour.FromHex("6fad9b"), private const float default_message_padding = 200;
OsuColour.FromHex("f2e394"),
OsuColour.FromHex("f2ae72"),
OsuColour.FromHex("f98f8a"),
OsuColour.FromHex("7daef4"),
OsuColour.FromHex("a691f2"),
OsuColour.FromHex("c894d3"),
OsuColour.FromHex("d895b0"),
OsuColour.FromHex("53c4a1"), protected virtual float MessagePadding => default_message_padding;
OsuColour.FromHex("eace5c"),
OsuColour.FromHex("ea8c47"),
OsuColour.FromHex("fc4f4f"),
OsuColour.FromHex("3d94ea"),
OsuColour.FromHex("7760ea"),
OsuColour.FromHex("af52c6"),
OsuColour.FromHex("e25696"),
OsuColour.FromHex("677c66"), private const float default_horizontal_padding = 15;
OsuColour.FromHex("9b8732"),
OsuColour.FromHex("8c5129"),
OsuColour.FromHex("8c3030"),
OsuColour.FromHex("1f5d91"),
OsuColour.FromHex("4335a5"),
OsuColour.FromHex("812a96"),
OsuColour.FromHex("992861"),
};
public const float LEFT_PADDING = message_padding + padding * 2; protected virtual float HorizontalPadding => default_horizontal_padding;
private const float padding = 15; protected virtual float TextSize => 20;
private const float message_padding = 200;
private const float action_padding = 3;
private const float text_size = 20;
private Color4 customUsernameColour; private Color4 customUsernameColour;
@ -75,14 +41,13 @@ namespace osu.Game.Overlays.Chat
public ChatLine(Message message) public ChatLine(Message message)
{ {
Message = message; Message = message;
Padding = new MarginPadding { Left = HorizontalPadding, Right = HorizontalPadding };
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Left = padding, Right = padding };
} }
private ChannelManager chatManager; [Resolved(CanBeNull = true)]
private ChannelManager chatManager { get; set; }
private Message message; private Message message;
private OsuSpriteText username; private OsuSpriteText username;
@ -106,10 +71,9 @@ namespace osu.Game.Overlays.Chat
} }
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader]
private void load(OsuColour colours, ChannelManager chatManager) private void load(OsuColour colours)
{ {
this.chatManager = chatManager;
customUsernameColour = colours.ChatBlue; customUsernameColour = colours.ChatBlue;
} }
@ -125,7 +89,7 @@ namespace osu.Game.Overlays.Chat
{ {
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length], Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length],
TextSize = text_size, TextSize = TextSize,
}; };
if (hasBackground) if (hasBackground)
@ -163,11 +127,11 @@ namespace osu.Game.Overlays.Chat
}; };
} }
Children = new Drawable[] InternalChildren = new Drawable[]
{ {
new Container new Container
{ {
Size = new Vector2(message_padding, text_size), Size = new Vector2(MessagePadding, TextSize),
Children = new Drawable[] Children = new Drawable[]
{ {
timestamp = new OsuSpriteText timestamp = new OsuSpriteText
@ -176,7 +140,7 @@ namespace osu.Game.Overlays.Chat
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Font = @"Exo2.0-SemiBold", Font = @"Exo2.0-SemiBold",
FixedWidth = true, FixedWidth = true,
TextSize = text_size * 0.75f, TextSize = TextSize * 0.75f,
}, },
new MessageSender(message.Sender) new MessageSender(message.Sender)
{ {
@ -191,7 +155,7 @@ namespace osu.Game.Overlays.Chat
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = message_padding + padding }, Padding = new MarginPadding { Left = MessagePadding + HorizontalPadding },
Children = new Drawable[] Children = new Drawable[]
{ {
contentFlow = new LinkFlowContainer(t => contentFlow = new LinkFlowContainer(t =>
@ -204,7 +168,7 @@ namespace osu.Game.Overlays.Chat
t.Colour = OsuColour.FromHex(message.Sender.Colour); t.Colour = OsuColour.FromHex(message.Sender.Colour);
} }
t.TextSize = text_size; t.TextSize = TextSize;
}) })
{ {
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
@ -257,5 +221,44 @@ namespace osu.Game.Overlays.Chat
new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction), new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction),
}; };
} }
private static readonly Color4[] username_colours =
{
OsuColour.FromHex("588c7e"),
OsuColour.FromHex("b2a367"),
OsuColour.FromHex("c98f65"),
OsuColour.FromHex("bc5151"),
OsuColour.FromHex("5c8bd6"),
OsuColour.FromHex("7f6ab7"),
OsuColour.FromHex("a368ad"),
OsuColour.FromHex("aa6880"),
OsuColour.FromHex("6fad9b"),
OsuColour.FromHex("f2e394"),
OsuColour.FromHex("f2ae72"),
OsuColour.FromHex("f98f8a"),
OsuColour.FromHex("7daef4"),
OsuColour.FromHex("a691f2"),
OsuColour.FromHex("c894d3"),
OsuColour.FromHex("d895b0"),
OsuColour.FromHex("53c4a1"),
OsuColour.FromHex("eace5c"),
OsuColour.FromHex("ea8c47"),
OsuColour.FromHex("fc4f4f"),
OsuColour.FromHex("3d94ea"),
OsuColour.FromHex("7760ea"),
OsuColour.FromHex("af52c6"),
OsuColour.FromHex("e25696"),
OsuColour.FromHex("677c66"),
OsuColour.FromHex("9b8732"),
OsuColour.FromHex("8c5129"),
OsuColour.FromHex("8c3030"),
OsuColour.FromHex("1f5d91"),
OsuColour.FromHex("4335a5"),
OsuColour.FromHex("812a96"),
OsuColour.FromHex("992861"),
};
} }
} }

View File

@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Chat
public class DrawableChannel : Container public class DrawableChannel : Container
{ {
public readonly Channel Channel; public readonly Channel Channel;
private readonly ChatLineContainer flow; protected readonly ChatLineContainer ChatLineFlow;
private readonly ScrollContainer scroll; private readonly ScrollContainer scroll;
public DrawableChannel(Channel channel) public DrawableChannel(Channel channel)
@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Chat
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Child = flow = new ChatLineContainer Child = ChatLineFlow = new ChatLineContainer
{ {
Padding = new MarginPadding { Left = 20, Right = 20 }, Padding = new MarginPadding { Left = 20, Right = 20 },
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
@ -72,17 +72,19 @@ namespace osu.Game.Overlays.Chat
Channel.PendingMessageResolved -= pendingMessageResolved; Channel.PendingMessageResolved -= pendingMessageResolved;
} }
protected virtual ChatLine CreateChatLine(Message m) => new ChatLine(m);
private void newMessagesArrived(IEnumerable<Message> newMessages) private void newMessagesArrived(IEnumerable<Message> newMessages)
{ {
// Add up to last Channel.MAX_HISTORY messages // Add up to last Channel.MAX_HISTORY messages
var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MaxHistory)); var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MaxHistory));
flow.AddRange(displayMessages.Select(m => new ChatLine(m))); ChatLineFlow.AddRange(displayMessages.Select(CreateChatLine));
if (scroll.IsScrolledToEnd(10) || !flow.Children.Any() || newMessages.Any(m => m is LocalMessage)) if (scroll.IsScrolledToEnd(10) || !ChatLineFlow.Children.Any() || newMessages.Any(m => m is LocalMessage))
scrollToEnd(); scrollToEnd();
var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); var staleMessages = ChatLineFlow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray();
int count = staleMessages.Length - Channel.MaxHistory; int count = staleMessages.Length - Channel.MaxHistory;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
@ -96,25 +98,25 @@ namespace osu.Game.Overlays.Chat
private void pendingMessageResolved(Message existing, Message updated) private void pendingMessageResolved(Message existing, Message updated)
{ {
var found = flow.Children.LastOrDefault(c => c.Message == existing); var found = ChatLineFlow.Children.LastOrDefault(c => c.Message == existing);
if (found != null) if (found != null)
{ {
Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID."); Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID.");
flow.Remove(found); ChatLineFlow.Remove(found);
found.Message = updated; found.Message = updated;
flow.Add(found); ChatLineFlow.Add(found);
} }
} }
private void messageRemoved(Message removed) private void messageRemoved(Message removed)
{ {
flow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire(); ChatLineFlow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire();
} }
private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd()); private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd());
private class ChatLineContainer : FillFlowContainer<ChatLine> protected class ChatLineContainer : FillFlowContainer<ChatLine>
{ {
protected override int Compare(Drawable x, Drawable y) protected override int Compare(Drawable x, Drawable y)
{ {

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -19,6 +20,7 @@ using osu.Game.Online.Chat;
using osu.Game.Overlays.Chat; using osu.Game.Overlays.Chat;
using osu.Game.Overlays.Chat.Selection; using osu.Game.Overlays.Chat.Selection;
using osu.Game.Overlays.Chat.Tabs; using osu.Game.Overlays.Chat.Tabs;
using osuTK.Input;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
@ -222,7 +224,7 @@ namespace osu.Game.Overlays
else else
{ {
currentChannelContainer.Clear(false); currentChannelContainer.Clear(false);
Scheduler.Add(() => currentChannelContainer.Add(loaded)); currentChannelContainer.Add(loaded);
} }
} }
@ -262,6 +264,39 @@ namespace osu.Game.Overlays
return base.OnDragEnd(e); return base.OnDragEnd(e);
} }
private void selectTab(int index)
{
var channel = channelTabControl.Items.Skip(index).FirstOrDefault();
if (channel != null && channel.Name != "+")
channelTabControl.Current.Value = channel;
}
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.AltPressed)
{
switch (e.Key)
{
case Key.Number1:
case Key.Number2:
case Key.Number3:
case Key.Number4:
case Key.Number5:
case Key.Number6:
case Key.Number7:
case Key.Number8:
case Key.Number9:
selectTab((int)e.Key - (int)Key.Number1);
return true;
case Key.Number0:
selectTab(9);
return true;
}
}
return base.OnKeyDown(e);
}
public override bool AcceptsFocus => true; public override bool AcceptsFocus => true;
protected override void OnFocus(FocusEvent e) protected override void OnFocus(FocusEvent e)

View File

@ -82,6 +82,7 @@ namespace osu.Game.Overlays.Profile
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Masking = true, Masking = true,
CornerRadius = 5, CornerRadius = 5,
OpenOnClick = { Value = false },
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
{ {
Type = EdgeEffectType.Shadow, Type = EdgeEffectType.Shadow,
@ -114,7 +115,7 @@ namespace osu.Game.Overlays.Profile
Y = -48, Y = -48,
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText usernameText = new OsuSpriteText
{ {
Text = user.Username, Text = user.Username,
Font = @"Exo2.0-RegularItalic", Font = @"Exo2.0-RegularItalic",
@ -316,6 +317,8 @@ namespace osu.Game.Overlays.Profile
levelBadge.Texture = textures.Get(@"Profile/levelbadge"); levelBadge.Texture = textures.Get(@"Profile/levelbadge");
} }
private readonly OsuSpriteText usernameText;
private User user; private User user;
public User User public User User
@ -343,6 +346,8 @@ namespace osu.Game.Overlays.Profile
if (user.IsSupporter) if (user.IsSupporter)
SupporterTag.Show(); SupporterTag.Show();
usernameText.Text = user.Username;
if (!string.IsNullOrEmpty(user.Colour)) if (!string.IsNullOrEmpty(user.Colour))
{ {
colourBar.Colour = OsuColour.FromHex(user.Colour); colourBar.Colour = OsuColour.FromHex(user.Colour);

View File

@ -6,8 +6,8 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;

View File

@ -10,7 +10,7 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using osu.Game.Screens.Select.Leaderboards; using osu.Game.Online.Leaderboards;
namespace osu.Game.Overlays.Profile.Sections.Recent namespace osu.Game.Overlays.Profile.Sections.Recent
{ {

View File

@ -31,6 +31,7 @@ namespace osu.Game.Overlays.Toolbar
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
CornerRadius = 4, CornerRadius = 4,
OpenOnClick = { Value = false },
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
{ {
Type = EdgeEffectType.Shadow, Type = EdgeEffectType.Shadow,

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
@ -18,7 +19,20 @@ namespace osu.Game.Overlays.Volume
{ {
public class MuteButton : Container, IHasCurrentValue<bool> public class MuteButton : Container, IHasCurrentValue<bool>
{ {
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 hoveredColour, unhoveredColour; private Color4 hoveredColour, unhoveredColour;
private const float width = 100; private const float width = 100;

View File

@ -68,6 +68,11 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
public Playfield Playfield => playfield.Value; public Playfield Playfield => playfield.Value;
/// <summary>
/// Place to put drawables above hit objects but below UI.
/// </summary>
public Container Overlays { get; protected set; }
/// <summary> /// <summary>
/// The cursor provided by this <see cref="RulesetContainer"/>. May be null if no cursor is provided. /// The cursor provided by this <see cref="RulesetContainer"/>. May be null if no cursor is provided.
/// </summary> /// </summary>
@ -215,7 +220,6 @@ namespace osu.Game.Rulesets.UI
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private Container content; private Container content;
private IEnumerable<Mod> mods;
/// <summary> /// <summary>
/// Whether to assume the beatmap passed into this <see cref="RulesetContainer{TObject}"/> is for the current ruleset. /// Whether to assume the beatmap passed into this <see cref="RulesetContainer{TObject}"/> is for the current ruleset.
@ -245,17 +249,24 @@ namespace osu.Game.Rulesets.UI
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
KeyBindingInputManager.Add(content = new Container KeyBindingInputManager.Children = new Drawable[]
{
content = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}); },
Playfield
AddInternal(KeyBindingInputManager); };
KeyBindingInputManager.Add(Playfield);
if (Cursor != null) if (Cursor != null)
KeyBindingInputManager.Add(Cursor); KeyBindingInputManager.Add(Cursor);
InternalChildren = new Drawable[]
{
KeyBindingInputManager,
Overlays = new Container { RelativeSizeAxes = Axes.Both }
};
// Apply mods // Apply mods
applyRulesetMods(Mods, config); applyRulesetMods(Mods, config);
@ -330,7 +341,6 @@ namespace osu.Game.Rulesets.UI
Playfield.Add(drawableObject); Playfield.Add(drawableObject);
} }
/// <summary> /// <summary>
/// Creates a DrawableHitObject from a HitObject. /// Creates a DrawableHitObject from a HitObject.
/// </summary> /// </summary>

View File

@ -1,7 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -11,7 +13,6 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osuTK; using osuTK;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using System.Linq;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
namespace osu.Game.Screens.Play.HUD namespace osu.Game.Screens.Play.HUD
@ -20,9 +21,20 @@ namespace osu.Game.Screens.Play.HUD
{ {
private const int fade_duration = 1000; private const int fade_duration = 1000;
private readonly Bindable<IEnumerable<Mod>> mods = new Bindable<IEnumerable<Mod>>(); private readonly Bindable<IEnumerable<Mod>> current = new Bindable<IEnumerable<Mod>>();
public Bindable<IEnumerable<Mod>> Current => mods; public Bindable<IEnumerable<Mod>> Current
{
get => current;
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
current.UnbindBindings();
current.BindTo(value);
}
}
private readonly FillFlowContainer<ModIcon> iconsContainer; private readonly FillFlowContainer<ModIcon> iconsContainer;
private readonly OsuSpriteText unrankedText; private readonly OsuSpriteText unrankedText;
@ -50,7 +62,7 @@ namespace osu.Game.Screens.Play.HUD
} }
}; };
mods.ValueChanged += mods => Current.ValueChanged += mods =>
{ {
iconsContainer.Clear(); iconsContainer.Clear();
foreach (Mod mod in mods) foreach (Mod mod in mods)
@ -66,7 +78,7 @@ namespace osu.Game.Screens.Play.HUD
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);
mods.UnbindAll(); Current.UnbindAll();
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -77,7 +89,7 @@ namespace osu.Game.Screens.Play.HUD
private void appearTransform() private void appearTransform()
{ {
if (mods.Value.Any(m => !m.Ranked)) if (Current.Value.Any(m => !m.Ranked))
unrankedText.FadeInFromZero(fade_duration, Easing.OutQuint); unrankedText.FadeInFromZero(fade_duration, Easing.OutQuint);
else else
unrankedText.Hide(); unrankedText.Hide();

View File

@ -28,7 +28,6 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Storyboards.Drawables; using osu.Game.Storyboards.Drawables;
@ -288,7 +287,7 @@ namespace osu.Game.Screens.Play
if (RulesetContainer.Replay == null) if (RulesetContainer.Replay == null)
scoreManager.Import(score, true); scoreManager.Import(score, true);
Push(new Results(score)); Push(new SoloResults(score));
onCompletionEvent = null; onCompletionEvent = null;
}); });

View 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 System.Collections.Generic;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Types;
namespace osu.Game.Screens.Play
{
public class SoloResults : Results
{
public SoloResults(ScoreInfo score)
: base(score)
{
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new IResultPageInfo[]
{
new ScoreOverviewPageInfo(Score, Beatmap),
new BeatmapLeaderboardPageInfo(Score, Beatmap)
};
}
}

View File

@ -1,12 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
public enum ResultMode public interface IResultPageInfo
{ {
Summary, FontAwesome Icon { get; }
Ranking,
Share string Name { get; }
ResultsPage CreatePage();
} }
} }

View File

@ -3,18 +3,19 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards; using osu.Game.Screens.Select.Leaderboards;
using osuTK; using osuTK;
using osu.Framework.Graphics.Shapes;
using osu.Game.Scoring;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking.Pages
{ {
public class ResultsPageRanking : ResultsPage public class RankingResultsPage : ResultsPage
{ {
public ResultsPageRanking(ScoreInfo score, WorkingBeatmap beatmap = null) : base(score, beatmap) public RankingResultsPage(ScoreInfo score, WorkingBeatmap beatmap = null)
: base(score, beatmap)
{ {
} }
@ -28,7 +29,7 @@ namespace osu.Game.Screens.Ranking
Colour = colours.GrayE, Colour = colours.GrayE,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
new Leaderboard new BeatmapLeaderboard
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,

View File

@ -4,36 +4,39 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions;
using osu.Framework.Localisation;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking.Pages
{ {
public class ResultsPageScore : ResultsPage public class ScoreResultsPage : ResultsPage
{ {
private Container scoreContainer; private Container scoreContainer;
private ScoreCounter scoreCounter; private ScoreCounter scoreCounter;
public ResultsPageScore(ScoreInfo score, WorkingBeatmap beatmap) : base(score, beatmap) { } public ScoreResultsPage(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
}
private FillFlowContainer<DrawableScoreStatistic> statisticsContainer; private FillFlowContainer<DrawableScoreStatistic> statisticsContainer;

View File

@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics; using osu.Game.Graphics;
using osuTK; using osuTK;
@ -13,27 +14,18 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
public class ResultModeButton : TabItem<ResultMode> public class ResultModeButton : TabItem<IResultPageInfo>, IHasTooltip
{ {
private readonly FontAwesome icon; private readonly FontAwesome icon;
private Color4 activeColour; private Color4 activeColour;
private Color4 inactiveColour; private Color4 inactiveColour;
private CircularContainer colouredPart; private CircularContainer colouredPart;
public ResultModeButton(ResultMode mode) : base(mode) public ResultModeButton(IResultPageInfo mode)
: base(mode)
{ {
switch (mode) icon = mode.Icon;
{ TooltipText = mode.Name;
case ResultMode.Summary:
icon = FontAwesome.fa_asterisk;
break;
case ResultMode.Ranking:
icon = FontAwesome.fa_list;
break;
case ResultMode.Share:
icon = FontAwesome.fa_camera;
break;
}
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -95,5 +87,7 @@ namespace osu.Game.Screens.Ranking
protected override void OnActivated() => colouredPart.FadeColour(activeColour, 200, Easing.OutQuint); protected override void OnActivated() => colouredPart.FadeColour(activeColour, 200, Easing.OutQuint);
protected override void OnDeactivated() => colouredPart.FadeColour(inactiveColour, 200, Easing.OutQuint); protected override void OnDeactivated() => colouredPart.FadeColour(inactiveColour, 200, Easing.OutQuint);
public string TooltipText { get; private set; }
} }
} }

View File

@ -7,7 +7,7 @@ using osuTK;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
public class ResultModeTabControl : TabControl<ResultMode> public class ResultModeTabControl : TabControl<IResultPageInfo>
{ {
public ResultModeTabControl() public ResultModeTabControl()
{ {
@ -19,9 +19,9 @@ namespace osu.Game.Screens.Ranking
TabContainer.Padding = new MarginPadding(5); TabContainer.Padding = new MarginPadding(5);
} }
protected override Dropdown<ResultMode> CreateDropdown() => null; protected override Dropdown<IResultPageInfo> CreateDropdown() => null;
protected override TabItem<ResultMode> CreateTabItem(ResultMode value) => new ResultModeButton(value) protected override TabItem<IResultPageInfo> CreateTabItem(IResultPageInfo value) => new ResultModeButton(value)
{ {
Anchor = TabContainer.Anchor, Anchor = TabContainer.Anchor,
Origin = TabContainer.Origin Origin = TabContainer.Origin

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
@ -21,9 +22,8 @@ using osu.Game.Scoring;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
public class Results : OsuScreen public abstract class Results : OsuScreen
{ {
private readonly ScoreInfo score;
private Container circleOuterBackground; private Container circleOuterBackground;
private Container circleOuter; private Container circleOuter;
private Container circleInner; private Container circleInner;
@ -34,6 +34,8 @@ namespace osu.Game.Screens.Ranking
public override bool AllowBeatmapRulesetChange => false; public override bool AllowBeatmapRulesetChange => false;
protected readonly ScoreInfo Score;
private Container currentPage; private Container currentPage;
private static readonly Vector2 background_blur = new Vector2(20); private static readonly Vector2 background_blur = new Vector2(20);
@ -44,9 +46,9 @@ namespace osu.Game.Screens.Ranking
private const float circle_outer_scale = 0.96f; private const float circle_outer_scale = 0.96f;
public Results(ScoreInfo score) protected Results(ScoreInfo score)
{ {
this.score = score; Score = score;
} }
private const float transition_time = 800; private const float transition_time = 800;
@ -67,7 +69,7 @@ namespace osu.Game.Screens.Ranking
backgroundParallax.FadeOut(); backgroundParallax.FadeOut();
modeChangeButtons.FadeOut(); modeChangeButtons.FadeOut();
currentPage.FadeOut(); currentPage?.FadeOut();
circleOuterBackground circleOuterBackground
.FadeIn(transition_time, Easing.OutQuint) .FadeIn(transition_time, Easing.OutQuint)
@ -90,7 +92,7 @@ namespace osu.Game.Screens.Ranking
using (BeginDelayedSequence(transition_time * 0.4f, true)) using (BeginDelayedSequence(transition_time * 0.4f, true))
{ {
modeChangeButtons.FadeIn(transition_time, Easing.OutQuint); modeChangeButtons.FadeIn(transition_time, Easing.OutQuint);
currentPage.FadeIn(transition_time, Easing.OutQuint); currentPage?.FadeIn(transition_time, Easing.OutQuint);
} }
} }
} }
@ -188,7 +190,7 @@ namespace osu.Game.Screens.Ranking
}, },
new OsuSpriteText new OsuSpriteText
{ {
Text = $"{score.MaxCombo}x", Text = $"{Score.MaxCombo}x",
TextSize = 40, TextSize = 40,
RelativePositionAxes = Axes.X, RelativePositionAxes = Axes.X,
Font = @"Exo2.0-Bold", Font = @"Exo2.0-Bold",
@ -209,7 +211,7 @@ namespace osu.Game.Screens.Ranking
}, },
new OsuSpriteText new OsuSpriteText
{ {
Text = $"{score.Accuracy:P2}", Text = $"{Score.Accuracy:P2}",
TextSize = 40, TextSize = 40,
RelativePositionAxes = Axes.X, RelativePositionAxes = Axes.X,
Font = @"Exo2.0-Bold", Font = @"Exo2.0-Bold",
@ -262,30 +264,22 @@ namespace osu.Game.Screens.Ranking
}, },
}; };
modeChangeButtons.AddItem(ResultMode.Summary); foreach (var t in CreateResultPages())
modeChangeButtons.AddItem(ResultMode.Ranking); modeChangeButtons.AddItem(t);
//modeChangeButtons.AddItem(ResultMode.Share); modeChangeButtons.Current.Value = modeChangeButtons.Items.FirstOrDefault();
modeChangeButtons.Current.ValueChanged += mode => modeChangeButtons.Current.BindValueChanged(m =>
{ {
currentPage?.FadeOut(); currentPage?.FadeOut();
currentPage?.Expire(); currentPage?.Expire();
switch (mode) currentPage = m?.CreatePage();
{
case ResultMode.Summary:
currentPage = new ResultsPageScore(score, Beatmap.Value);
break;
case ResultMode.Ranking:
currentPage = new ResultsPageRanking(score, Beatmap.Value);
break;
}
if (currentPage != null) if (currentPage != null)
circleInner.Add(currentPage); circleInner.Add(currentPage);
}; }, true);
}
modeChangeButtons.Current.TriggerChange(); protected abstract IEnumerable<IResultPageInfo> CreateResultPages();
}
} }
} }

View File

@ -14,7 +14,7 @@ using osuTK.Graphics;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
public class ResultsPage : Container public abstract class ResultsPage : Container
{ {
protected readonly ScoreInfo Score; protected readonly ScoreInfo Score;
protected readonly WorkingBeatmap Beatmap; protected readonly WorkingBeatmap Beatmap;
@ -23,7 +23,7 @@ namespace osu.Game.Screens.Ranking
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
public ResultsPage(ScoreInfo score, WorkingBeatmap beatmap) protected ResultsPage(ScoreInfo score, WorkingBeatmap beatmap)
{ {
Score = score; Score = score;
Beatmap = beatmap; Beatmap = beatmap;

View File

@ -0,0 +1,28 @@
// 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.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Pages;
namespace osu.Game.Screens.Ranking.Types
{
public class BeatmapLeaderboardPageInfo : IResultPageInfo
{
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
public BeatmapLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
{
this.score = score;
this.beatmap = beatmap;
}
public FontAwesome Icon => FontAwesome.fa_list;
public string Name => @"Beatmap Leaderboard";
public ResultsPage CreatePage() => new RankingResultsPage(score, beatmap);
}
}

View 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 osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Pages;
namespace osu.Game.Screens.Ranking.Types
{
public class ScoreOverviewPageInfo : IResultPageInfo
{
public FontAwesome Icon => FontAwesome.fa_asterisk;
public string Name => "Overview";
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
public ScoreOverviewPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
{
this.score = score;
this.beatmap = beatmap;
}
public ResultsPage CreatePage()
{
return new ScoreResultsPage(score, beatmap);
}
}
}

View File

@ -17,7 +17,7 @@ namespace osu.Game.Screens.Select
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
public readonly BeatmapDetails Details; public readonly BeatmapDetails Details;
public readonly Leaderboard Leaderboard; public readonly BeatmapLeaderboard Leaderboard;
private WorkingBeatmap beatmap; private WorkingBeatmap beatmap;
public WorkingBeatmap Beatmap public WorkingBeatmap Beatmap
@ -52,7 +52,7 @@ namespace osu.Game.Screens.Select
default: default:
Details.Hide(); Details.Hide();
Leaderboard.Scope = (LeaderboardScope)tab - 1; Leaderboard.Scope = (BeatmapLeaderboardScope)tab - 1;
Leaderboard.Show(); Leaderboard.Show();
break; break;
} }
@ -73,7 +73,7 @@ namespace osu.Game.Screens.Select
Alpha = 0, Alpha = 0,
Margin = new MarginPadding { Top = details_padding }, Margin = new MarginPadding { Top = details_padding },
}, },
Leaderboard = new Leaderboard Leaderboard = new BeatmapLeaderboard
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
} }

View File

@ -0,0 +1,87 @@
// 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.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets;
using osu.Game.Scoring;
namespace osu.Game.Screens.Select.Leaderboards
{
public class BeatmapLeaderboard : Leaderboard<BeatmapLeaderboardScope, ScoreInfo>
{
public Action<ScoreInfo> ScoreSelected;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (beatmap == value)
return;
beatmap = value;
Scores = null;
UpdateScores();
}
}
[Resolved]
private ScoreManager scoreManager { get; set; }
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
[Resolved]
private APIAccess api { get; set; }
[BackgroundDependencyLoader]
private void load()
{
ruleset.ValueChanged += _ => UpdateScores();
}
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback)
{
if (Scope == BeatmapLeaderboardScope.Local)
{
Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray();
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
return null;
}
if (Beatmap?.OnlineBeatmapID == null)
{
PlaceholderState = PlaceholderState.Unavailable;
return null;
}
if (Scope != BeatmapLeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
{
PlaceholderState = PlaceholderState.NotSupporter;
return null;
}
var req = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope);
req.Success += r => scoresCallback?.Invoke(r.Scores);
return req;
}
protected override LeaderboardScore CreateDrawableScore(ScoreInfo model, int index) => new LeaderboardScore(model, index)
{
Action = () => ScoreSelected?.Invoke(model)
};
}
}

View File

@ -3,7 +3,7 @@
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Screens.Select.Leaderboards
{ {
public enum LeaderboardScope public enum BeatmapLeaderboardScope
{ {
Local, Local,
Country, Country,

View File

@ -28,7 +28,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Menu; using osu.Game.Screens.Menu;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Play;
using osu.Game.Screens.Select.Options; using osu.Game.Screens.Select.Options;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -210,7 +210,7 @@ namespace osu.Game.Screens.Select
}); });
} }
BeatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s)); BeatmapDetails.Leaderboard.ScoreSelected += s => Push(new SoloResults(s));
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]

View File

@ -3,17 +3,29 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
namespace osu.Game.Users namespace osu.Game.Users
{ {
public class Avatar : Container public class Avatar : Container
{ {
/// <summary>
/// Whether to open the user's profile when clicked.
/// </summary>
public readonly BindableBool OpenOnClick = new BindableBool(true);
private readonly User user; private readonly User user;
[Resolved(CanBeNull = true)]
private OsuGame game { get; set; }
/// <summary> /// <summary>
/// An avatar for specified user. /// An avatar for specified user.
/// </summary> /// </summary>
@ -33,14 +45,43 @@ namespace osu.Game.Users
if (user != null && user.Id > 1) texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); if (user != null && user.Id > 1) texture = textures.Get($@"https://a.ppy.sh/{user.Id}");
if (texture == null) texture = textures.Get(@"Online/avatar-guest"); if (texture == null) texture = textures.Get(@"Online/avatar-guest");
Add(new Sprite ClickableArea clickableArea;
Add(clickableArea = new ClickableArea
{
RelativeSizeAxes = Axes.Both,
Child = new Sprite
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Texture = texture, Texture = texture,
FillMode = FillMode.Fit, FillMode = FillMode.Fit,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre Origin = Anchor.Centre
},
Action = openProfile
}); });
clickableArea.Enabled.BindTo(OpenOnClick);
}
private void openProfile()
{
if (!OpenOnClick)
return;
if (user != null)
game?.ShowUser(user.Id);
}
private class ClickableArea : OsuClickableContainer, IHasTooltip
{
public string TooltipText => Enabled.Value ? @"View Profile" : null;
protected override bool OnClick(ClickEvent e)
{
if (!Enabled)
return false;
return base.OnClick(e);
}
} }
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -35,6 +36,11 @@ namespace osu.Game.Users
} }
} }
/// <summary>
/// Whether to open the user's profile when clicked.
/// </summary>
public readonly BindableBool OpenOnClick = new BindableBool(true);
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
@ -45,15 +51,18 @@ namespace osu.Game.Users
{ {
displayedAvatar?.FadeOut(300); displayedAvatar?.FadeOut(300);
displayedAvatar?.Expire(); displayedAvatar?.Expire();
if (user != null || ShowGuestOnNull) if (user != null || ShowGuestOnNull)
{ {
Add(displayedAvatar = new DelayedLoadWrapper( var avatar = new Avatar(user)
new Avatar(user)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint),
}) };
);
avatar.OpenOnClick.BindTo(OpenOnClick);
Add(displayedAvatar = new DelayedLoadWrapper(avatar));
} }
} }
} }

View File

@ -99,6 +99,7 @@ namespace osu.Game.Users
User = user, User = user,
Masking = true, Masking = true,
CornerRadius = 5, CornerRadius = 5,
OpenOnClick = { Value = false },
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
{ {
Type = EdgeEffectType.Shadow, Type = EdgeEffectType.Shadow,

View File

@ -18,7 +18,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Framework" Version="2018.1219.0" /> <PackageReference Include="ppy.osu.Framework" Version="2018.1221.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />