1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 10:12:54 +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">
<configuration default="false" name="RulesetTests (catch)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />

View File

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

View File

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

View File

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

View File

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

16
.vscode/launch.json vendored
View File

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

6
.vscode/tasks.json vendored
View File

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

View File

@ -10,7 +10,7 @@ We are accepting bug reports (please report with as much detail as possible). Fe
# Requirements
- A desktop platform with the [.NET Core SDK 2.1](https://www.microsoft.com/net/learn/get-started) or higher installed.
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio Code](https://code.visualstudio.com/) (with the C# plugin installed) or [Jetbrains Rider](https://www.jetbrains.com/rider/) (commercial).
# Building and running

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

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

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)
return;
slider.HeadCircle.Position = new Vector2(slider.HeadCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.HeadCircle.Position.Y);
slider.TailCircle.Position = new Vector2(slider.TailCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.TailCircle.Position.Y);
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));

View File

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

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Taiko.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Taiko.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

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

View File

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

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

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
@ -99,7 +100,20 @@ namespace osu.Game.Graphics.UserInterface
}
}
public Bindable<bool> Current { get; } = new Bindable<bool>();
private readonly Bindable<bool> current = new Bindable<bool>();
public Bindable<bool> Current
{
get => current;
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
current.UnbindBindings();
current.BindTo(value);
}
}
private Color4 accentColour;
public Color4 AccentColour

View File

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

View File

@ -96,12 +96,14 @@ namespace osu.Game.Online.Chat
/// </summary>
/// <param name="text">The message text that is going to be posted</param>
/// <param name="isAction">Is true if the message is an action, e.g.: user is currently eating </param>
public void PostMessage(string text, bool isAction = false)
/// <param name="target">An optional target channel. If null, <see cref="CurrentChannel"/> will be used.</param>
public void PostMessage(string text, bool isAction = false, Channel target = null)
{
if (CurrentChannel.Value == null)
return;
if (target == null)
target = CurrentChannel.Value;
var currentChannel = CurrentChannel.Value;
if (target == null)
return;
void dequeueAndRun()
{
@ -113,7 +115,7 @@ namespace osu.Game.Online.Chat
{
if (!api.IsLoggedIn)
{
currentChannel.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!"));
target.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!"));
return;
}
@ -121,29 +123,29 @@ namespace osu.Game.Online.Chat
{
Sender = api.LocalUser.Value,
Timestamp = DateTimeOffset.Now,
ChannelId = CurrentChannel.Value.Id,
ChannelId = target.Id,
IsAction = isAction,
Content = text
};
currentChannel.AddLocalEcho(message);
target.AddLocalEcho(message);
// if this is a PM and the first message, we need to do a special request to create the PM channel
if (currentChannel.Type == ChannelType.PM && !currentChannel.Joined)
if (target.Type == ChannelType.PM && !target.Joined)
{
var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(currentChannel.Users.First(), message);
var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(target.Users.First(), message);
createNewPrivateMessageRequest.Success += createRes =>
{
currentChannel.Id = createRes.ChannelID;
currentChannel.ReplaceMessage(message, createRes.Message);
target.Id = createRes.ChannelID;
target.ReplaceMessage(message, createRes.Message);
dequeueAndRun();
};
createNewPrivateMessageRequest.Failure += exception =>
{
Logger.Error(exception, "Posting message failed.");
currentChannel.ReplaceMessage(message, null);
target.ReplaceMessage(message, null);
dequeueAndRun();
};
@ -155,14 +157,14 @@ namespace osu.Game.Online.Chat
req.Success += m =>
{
currentChannel.ReplaceMessage(message, m);
target.ReplaceMessage(message, m);
dequeueAndRun();
};
req.Failure += exception =>
{
Logger.Error(exception, "Posting message failed.");
currentChannel.ReplaceMessage(message, null);
target.ReplaceMessage(message, null);
dequeueAndRun();
};
@ -178,9 +180,13 @@ namespace osu.Game.Online.Chat
/// Posts a command locally. Commands like /help will result in a help message written in the current channel.
/// </summary>
/// <param name="text">the text containing the command identifier and command parameters.</param>
public void PostCommand(string text)
/// <param name="target">An optional target channel. If null, <see cref="CurrentChannel"/> will be used.</param>
public void PostCommand(string text, Channel target = null)
{
if (CurrentChannel.Value == null)
if (target == null)
target = CurrentChannel.Value;
if (target == null)
return;
var parameters = text.Split(new[] { ' ' }, 2);
@ -192,7 +198,7 @@ namespace osu.Game.Online.Chat
case "me":
if (string.IsNullOrWhiteSpace(content))
{
CurrentChannel.Value.AddNewMessages(new ErrorMessage("Usage: /me [action]"));
target.AddNewMessages(new ErrorMessage("Usage: /me [action]"));
break;
}
@ -200,11 +206,11 @@ namespace osu.Game.Online.Chat
break;
case "help":
CurrentChannel.Value.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]"));
target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]"));
break;
default:
CurrentChannel.Value.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help"));
target.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help"));
break;
}
}

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
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Extensions;
using osu.Game.Scoring;
namespace osu.Game.Screens.Select.Leaderboards
namespace osu.Game.Online.Leaderboards
{
public class DrawableRank : Container
{

View File

@ -3,27 +3,22 @@
using System;
using System.Collections.Generic;
using osuTK;
using osuTK.Graphics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using System.Linq;
using osu.Framework.Configuration;
using osu.Game.Rulesets;
using osu.Game.Scoring;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Select.Leaderboards
namespace osu.Game.Online.Leaderboards
{
public class Leaderboard : Container
public abstract class Leaderboard<TScope, ScoreInfo> : Container
{
private const double fade_duration = 300;
@ -32,10 +27,6 @@ namespace osu.Game.Screens.Select.Leaderboards
private FillFlowContainer<LeaderboardScore> scrollFlow;
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
public Action<ScoreInfo> ScoreSelected;
private readonly LoadingAnimation loading;
private ScheduledDelegate showScoresDelegate;
@ -70,7 +61,7 @@ namespace osu.Game.Screens.Select.Leaderboards
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 5f),
Padding = new MarginPadding { Top = 10, Bottom = 5 },
ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1))
};
// 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; }
set
{
if (value == scope)
if (value.Equals(scope))
return;
scope = value;
updateScores();
UpdateScores();
}
}
@ -137,7 +128,7 @@ namespace osu.Game.Screens.Select.Leaderboards
case PlaceholderState.NetworkFailure:
replacePlaceholder(new RetrievalFailurePlaceholder
{
OnRetry = updateScores,
OnRetry = UpdateScores,
});
break;
case PlaceholderState.Unavailable:
@ -159,7 +150,7 @@ namespace osu.Game.Screens.Select.Leaderboards
}
}
public Leaderboard()
protected Leaderboard()
{
Children = new Drawable[]
{
@ -177,36 +168,14 @@ namespace osu.Game.Screens.Select.Leaderboards
}
private APIAccess api;
private BeatmapInfo beatmap;
[Resolved]
private ScoreManager scoreManager { get; set; }
private ScheduledDelegate pendingUpdateScores;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (beatmap == value)
return;
beatmap = value;
Scores = null;
updateScores();
}
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, IBindable<RulesetInfo> parentRuleset)
[BackgroundDependencyLoader(true)]
private void load(APIAccess api)
{
this.api = api;
ruleset.BindTo(parentRuleset);
ruleset.ValueChanged += _ => updateScores();
if (api != null)
api.OnStateChange += handleApiStateChange;
}
@ -219,21 +188,17 @@ namespace osu.Game.Screens.Select.Leaderboards
api.OnStateChange -= handleApiStateChange;
}
public void RefreshScores() => updateScores();
public void RefreshScores() => UpdateScores();
private GetScoresRequest getScoresRequest;
private APIRequest getScoresRequest;
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)
updateScores();
UpdateScores();
}
private void updateScores()
protected void UpdateScores()
{
// don't display any scores or placeholder until the first Scores_Set has been called.
// this avoids scope changes flickering a "no scores" placeholder before initialisation of song select is finished.
@ -245,40 +210,23 @@ namespace osu.Game.Screens.Select.Leaderboards
pendingUpdateScores?.Cancel();
pendingUpdateScores = Schedule(() =>
{
if (Scope == LeaderboardScope.Local)
{
Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray();
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
return;
}
if (Beatmap?.OnlineBeatmapID == null)
{
PlaceholderState = PlaceholderState.Unavailable;
return;
}
if (api?.IsLoggedIn != true)
{
PlaceholderState = PlaceholderState.NotLoggedIn;
return;
}
if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
{
PlaceholderState = PlaceholderState.NotSupporter;
return;
}
PlaceholderState = PlaceholderState.Retrieving;
loading.Show();
getScoresRequest = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope);
getScoresRequest.Success += r => Schedule(() =>
getScoresRequest = FetchScores(scores => Schedule(() =>
{
Scores = r.Scores;
Scores = scores;
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
});
}));
if (getScoresRequest == null)
return;
getScoresRequest.Failure += e => Schedule(() =>
{
@ -292,6 +240,8 @@ namespace osu.Game.Screens.Select.Leaderboards
});
}
protected abstract APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback);
private Placeholder currentPlaceholder;
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>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@ -17,35 +16,40 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Select.Leaderboards
namespace osu.Game.Online.Leaderboards
{
public class LeaderboardScore : OsuClickableContainer
{
public static readonly float HEIGHT = 60;
public readonly int RankPosition;
public readonly ScoreInfo Score;
public const float HEIGHT = 60;
private const float corner_radius = 5;
private const float edge_margin = 5;
private const float background_alpha = 0.25f;
private const float rank_width = 30;
protected Container RankContainer { get; private set; }
private readonly ScoreInfo score;
private Box background;
private Container content;
private Drawable avatar;
private DrawableRank scoreRank;
private Drawable scoreRank;
private OsuSpriteText nameLabel;
private GlowingSpriteText scoreLabel;
private ScoreComponentLabel maxCombo;
private ScoreComponentLabel accuracy;
private Container flagBadgeContainer;
private FillFlowContainer<ModIcon> modsContainer;
private List<ScoreComponentLabel> statisticsLabels;
public LeaderboardScore(ScoreInfo score, int rank)
{
Score = score;
this.score = score;
RankPosition = rank;
RelativeSizeAxes = Axes.X;
@ -55,6 +59,10 @@ namespace osu.Game.Screens.Select.Leaderboards
[BackgroundDependencyLoader]
private void load()
{
var user = score.User;
statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s)).ToList();
Children = new Drawable[]
{
new Container
@ -102,7 +110,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Children = new[]
{
avatar = new DelayedLoadWrapper(
new Avatar(Score.User)
new Avatar(user)
{
RelativeSizeAxes = Axes.Both,
CornerRadius = corner_radius,
@ -128,7 +136,7 @@ namespace osu.Game.Screens.Select.Leaderboards
{
nameLabel = new OsuSpriteText
{
Text = Score.User.Username,
Text = user.Username,
Font = @"Exo2.0-BoldItalic",
TextSize = 23,
},
@ -149,7 +157,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Masking = true,
Children = new Drawable[]
{
new DrawableFlag(Score.User?.Country)
new DrawableFlag(user.Country)
{
Width = 30,
RelativeSizeAxes = Axes.Y,
@ -164,11 +172,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f),
Margin = new MarginPadding { Left = edge_margin },
Children = new Drawable[]
{
maxCombo = new ScoreComponentLabel(FontAwesome.fa_link, Score.MaxCombo.ToString(), "Max Combo"),
accuracy = new ScoreComponentLabel(FontAwesome.fa_crosshairs, string.Format(Score.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", Score.Accuracy), "Accuracy"),
},
Children = statisticsLabels
},
},
},
@ -183,17 +187,17 @@ namespace osu.Game.Screens.Select.Leaderboards
Spacing = new Vector2(5f, 0f),
Children = new Drawable[]
{
scoreLabel = new GlowingSpriteText(Score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")),
new Container
scoreLabel = new GlowingSpriteText(score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")),
RankContainer = new Container
{
Size = new Vector2(40f, 20f),
Children = new[]
{
scoreRank = new DrawableRank(Score.Rank)
scoreRank = new DrawableRank(score.Rank)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(40f),
Size = new Vector2(40f)
},
},
},
@ -205,7 +209,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
ChildrenEnumerable = Score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
ChildrenEnumerable = score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
},
},
},
@ -216,7 +220,7 @@ namespace osu.Game.Screens.Select.Leaderboards
public override void Show()
{
foreach (var d in new[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer })
foreach (var d in new[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, modsContainer }.Concat(statisticsLabels))
d.FadeOut();
Alpha = 0;
@ -243,7 +247,7 @@ namespace osu.Game.Screens.Select.Leaderboards
using (BeginDelayedSequence(50, true))
{
var drawables = new Drawable[] { flagBadgeContainer, maxCombo, accuracy, modsContainer, };
var drawables = new Drawable[] { flagBadgeContainer, modsContainer }.Concat(statisticsLabels).ToArray();
for (int i = 0; i < drawables.Length; i++)
drawables[i].FadeIn(100 + i * 50);
}
@ -251,6 +255,12 @@ namespace osu.Game.Screens.Select.Leaderboards
}
}
protected virtual IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[]
{
new LeaderboardScoreStatistic(FontAwesome.fa_link, "Max Combo", model.MaxCombo.ToString()),
new LeaderboardScoreStatistic(FontAwesome.fa_crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy))
};
protected override bool OnHover(HoverEvent e)
{
background.FadeTo(0.5f, 300, Easing.OutQuint);
@ -321,11 +331,10 @@ namespace osu.Game.Screens.Select.Leaderboards
public string TooltipText => name;
public ScoreComponentLabel(FontAwesome icon, string value, string name)
public ScoreComponentLabel(LeaderboardScoreStatistic statistic)
{
this.name = name;
AutoSizeAxes = Axes.Y;
Width = 60;
name = statistic.Name;
AutoSizeAxes = Axes.Both;
Child = content = new FillFlowContainer
{
@ -356,11 +365,11 @@ namespace osu.Game.Screens.Select.Leaderboards
Origin = Anchor.Centre,
Size = new Vector2(icon_size - 6),
Colour = OsuColour.FromHex(@"a4edff"),
Icon = icon,
Icon = statistic.Icon,
},
},
},
new GlowingSpriteText(value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa"))
new GlowingSpriteText(statistic.Value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa"))
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
@ -369,5 +378,19 @@ namespace osu.Game.Screens.Select.Leaderboards
};
}
}
public class LeaderboardScoreStatistic
{
public FontAwesome Icon;
public string Value;
public string Name;
public LeaderboardScoreStatistic(FontAwesome icon, string name, string value)
{
Icon = icon;
Name = name;
Value = value;
}
}
}
}

View File

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

View File

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

View File

@ -1,7 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Screens.Select.Leaderboards
namespace osu.Game.Online.Leaderboards
{
public enum PlaceholderState
{

View File

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

View File

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

View File

@ -10,11 +10,11 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
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.Sprites;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
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
using System;
using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat;
using osu.Game.Users;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.Chat
{
public class ChatLine : Container
public class ChatLine : CompositeDrawable
{
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"),
public const float LEFT_PADDING = default_message_padding + default_horizontal_padding * 2;
OsuColour.FromHex("6fad9b"),
OsuColour.FromHex("f2e394"),
OsuColour.FromHex("f2ae72"),
OsuColour.FromHex("f98f8a"),
OsuColour.FromHex("7daef4"),
OsuColour.FromHex("a691f2"),
OsuColour.FromHex("c894d3"),
OsuColour.FromHex("d895b0"),
private const float default_message_padding = 200;
OsuColour.FromHex("53c4a1"),
OsuColour.FromHex("eace5c"),
OsuColour.FromHex("ea8c47"),
OsuColour.FromHex("fc4f4f"),
OsuColour.FromHex("3d94ea"),
OsuColour.FromHex("7760ea"),
OsuColour.FromHex("af52c6"),
OsuColour.FromHex("e25696"),
protected virtual float MessagePadding => default_message_padding;
OsuColour.FromHex("677c66"),
OsuColour.FromHex("9b8732"),
OsuColour.FromHex("8c5129"),
OsuColour.FromHex("8c3030"),
OsuColour.FromHex("1f5d91"),
OsuColour.FromHex("4335a5"),
OsuColour.FromHex("812a96"),
OsuColour.FromHex("992861"),
};
private const float default_horizontal_padding = 15;
public const float LEFT_PADDING = message_padding + padding * 2;
protected virtual float HorizontalPadding => default_horizontal_padding;
private const float padding = 15;
private const float message_padding = 200;
private const float action_padding = 3;
private const float text_size = 20;
protected virtual float TextSize => 20;
private Color4 customUsernameColour;
@ -75,14 +41,13 @@ namespace osu.Game.Overlays.Chat
public ChatLine(Message message)
{
Message = message;
Padding = new MarginPadding { Left = HorizontalPadding, Right = HorizontalPadding };
RelativeSizeAxes = Axes.X;
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 OsuSpriteText username;
@ -106,10 +71,9 @@ namespace osu.Game.Overlays.Chat
}
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, ChannelManager chatManager)
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
this.chatManager = chatManager;
customUsernameColour = colours.ChatBlue;
}
@ -125,7 +89,7 @@ namespace osu.Game.Overlays.Chat
{
Font = @"Exo2.0-BoldItalic",
Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length],
TextSize = text_size,
TextSize = TextSize,
};
if (hasBackground)
@ -163,11 +127,11 @@ namespace osu.Game.Overlays.Chat
};
}
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new Container
{
Size = new Vector2(message_padding, text_size),
Size = new Vector2(MessagePadding, TextSize),
Children = new Drawable[]
{
timestamp = new OsuSpriteText
@ -176,7 +140,7 @@ namespace osu.Game.Overlays.Chat
Origin = Anchor.CentreLeft,
Font = @"Exo2.0-SemiBold",
FixedWidth = true,
TextSize = text_size * 0.75f,
TextSize = TextSize * 0.75f,
},
new MessageSender(message.Sender)
{
@ -191,7 +155,7 @@ namespace osu.Game.Overlays.Chat
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = message_padding + padding },
Padding = new MarginPadding { Left = MessagePadding + HorizontalPadding },
Children = new Drawable[]
{
contentFlow = new LinkFlowContainer(t =>
@ -204,7 +168,7 @@ namespace osu.Game.Overlays.Chat
t.Colour = OsuColour.FromHex(message.Sender.Colour);
}
t.TextSize = text_size;
t.TextSize = TextSize;
})
{
AutoSizeAxes = Axes.Y,
@ -257,5 +221,44 @@ namespace osu.Game.Overlays.Chat
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 readonly Channel Channel;
private readonly ChatLineContainer flow;
protected readonly ChatLineContainer ChatLineFlow;
private readonly ScrollContainer scroll;
public DrawableChannel(Channel channel)
@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Chat
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = flow = new ChatLineContainer
Child = ChatLineFlow = new ChatLineContainer
{
Padding = new MarginPadding { Left = 20, Right = 20 },
RelativeSizeAxes = Axes.X,
@ -72,17 +72,19 @@ namespace osu.Game.Overlays.Chat
Channel.PendingMessageResolved -= pendingMessageResolved;
}
protected virtual ChatLine CreateChatLine(Message m) => new ChatLine(m);
private void newMessagesArrived(IEnumerable<Message> newMessages)
{
// Add up to last Channel.MAX_HISTORY messages
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();
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;
for (int i = 0; i < count; i++)
@ -96,25 +98,25 @@ namespace osu.Game.Overlays.Chat
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)
{
Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID.");
flow.Remove(found);
ChatLineFlow.Remove(found);
found.Message = updated;
flow.Add(found);
ChatLineFlow.Add(found);
}
}
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 class ChatLineContainer : FillFlowContainer<ChatLine>
protected class ChatLineContainer : FillFlowContainer<ChatLine>
{
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
using System.Collections.Generic;
using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
@ -19,6 +20,7 @@ using osu.Game.Online.Chat;
using osu.Game.Overlays.Chat;
using osu.Game.Overlays.Chat.Selection;
using osu.Game.Overlays.Chat.Tabs;
using osuTK.Input;
namespace osu.Game.Overlays
{
@ -222,7 +224,7 @@ namespace osu.Game.Overlays
else
{
currentChannelContainer.Clear(false);
Scheduler.Add(() => currentChannelContainer.Add(loaded));
currentChannelContainer.Add(loaded);
}
}
@ -262,6 +264,39 @@ namespace osu.Game.Overlays
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;
protected override void OnFocus(FocusEvent e)

View File

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

View File

@ -6,8 +6,8 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Rulesets.UI;
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.Responses;
using osu.Game.Online.Chat;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Online.Leaderboards;
namespace osu.Game.Overlays.Profile.Sections.Recent
{

View File

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

View File

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

View File

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

View File

@ -1,7 +1,9 @@
// 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.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -11,7 +13,6 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osuTK;
using osu.Game.Graphics.Containers;
using System.Linq;
using osu.Framework.Input.Events;
namespace osu.Game.Screens.Play.HUD
@ -20,9 +21,20 @@ namespace osu.Game.Screens.Play.HUD
{
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 OsuSpriteText unrankedText;
@ -50,7 +62,7 @@ namespace osu.Game.Screens.Play.HUD
}
};
mods.ValueChanged += mods =>
Current.ValueChanged += mods =>
{
iconsContainer.Clear();
foreach (Mod mod in mods)
@ -66,7 +78,7 @@ namespace osu.Game.Screens.Play.HUD
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
mods.UnbindAll();
Current.UnbindAll();
}
protected override void LoadComplete()
@ -77,7 +89,7 @@ namespace osu.Game.Screens.Play.HUD
private void appearTransform()
{
if (mods.Value.Any(m => !m.Ranked))
if (Current.Value.Any(m => !m.Ranked))
unrankedText.FadeInFromZero(fade_duration, Easing.OutQuint);
else
unrankedText.Hide();

View File

@ -28,7 +28,6 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
using osu.Game.Storyboards.Drawables;
@ -288,7 +287,7 @@ namespace osu.Game.Screens.Play
if (RulesetContainer.Replay == null)
scoreManager.Import(score, true);
Push(new Results(score));
Push(new SoloResults(score));
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>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
namespace osu.Game.Screens.Ranking
{
public enum ResultMode
public interface IResultPageInfo
{
Summary,
Ranking,
Share
FontAwesome Icon { get; }
string Name { get; }
ResultsPage CreatePage();
}
}

View File

@ -3,18 +3,19 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards;
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,
RelativeSizeAxes = Axes.Both,
},
new Leaderboard
new BeatmapLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,

View File

@ -4,36 +4,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Leaderboards;
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.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 ScoreCounter scoreCounter;
public ResultsPageScore(ScoreInfo score, WorkingBeatmap beatmap) : base(score, beatmap) { }
public ScoreResultsPage(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
}
private FillFlowContainer<DrawableScoreStatistic> statisticsContainer;

View File

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

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
@ -21,9 +22,8 @@ using osu.Game.Scoring;
namespace osu.Game.Screens.Ranking
{
public class Results : OsuScreen
public abstract class Results : OsuScreen
{
private readonly ScoreInfo score;
private Container circleOuterBackground;
private Container circleOuter;
private Container circleInner;
@ -34,6 +34,8 @@ namespace osu.Game.Screens.Ranking
public override bool AllowBeatmapRulesetChange => false;
protected readonly ScoreInfo Score;
private Container currentPage;
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;
public Results(ScoreInfo score)
protected Results(ScoreInfo score)
{
this.score = score;
Score = score;
}
private const float transition_time = 800;
@ -67,7 +69,7 @@ namespace osu.Game.Screens.Ranking
backgroundParallax.FadeOut();
modeChangeButtons.FadeOut();
currentPage.FadeOut();
currentPage?.FadeOut();
circleOuterBackground
.FadeIn(transition_time, Easing.OutQuint)
@ -90,7 +92,7 @@ namespace osu.Game.Screens.Ranking
using (BeginDelayedSequence(transition_time * 0.4f, true))
{
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
{
Text = $"{score.MaxCombo}x",
Text = $"{Score.MaxCombo}x",
TextSize = 40,
RelativePositionAxes = Axes.X,
Font = @"Exo2.0-Bold",
@ -209,7 +211,7 @@ namespace osu.Game.Screens.Ranking
},
new OsuSpriteText
{
Text = $"{score.Accuracy:P2}",
Text = $"{Score.Accuracy:P2}",
TextSize = 40,
RelativePositionAxes = Axes.X,
Font = @"Exo2.0-Bold",
@ -262,30 +264,22 @@ namespace osu.Game.Screens.Ranking
},
};
modeChangeButtons.AddItem(ResultMode.Summary);
modeChangeButtons.AddItem(ResultMode.Ranking);
//modeChangeButtons.AddItem(ResultMode.Share);
foreach (var t in CreateResultPages())
modeChangeButtons.AddItem(t);
modeChangeButtons.Current.Value = modeChangeButtons.Items.FirstOrDefault();
modeChangeButtons.Current.ValueChanged += mode =>
modeChangeButtons.Current.BindValueChanged(m =>
{
currentPage?.FadeOut();
currentPage?.Expire();
switch (mode)
{
case ResultMode.Summary:
currentPage = new ResultsPageScore(score, Beatmap.Value);
break;
case ResultMode.Ranking:
currentPage = new ResultsPageRanking(score, Beatmap.Value);
break;
}
currentPage = m?.CreatePage();
if (currentPage != null)
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
{
public class ResultsPage : Container
public abstract class ResultsPage : Container
{
protected readonly ScoreInfo Score;
protected readonly WorkingBeatmap Beatmap;
@ -23,7 +23,7 @@ namespace osu.Game.Screens.Ranking
protected override Container<Drawable> Content => content;
public ResultsPage(ScoreInfo score, WorkingBeatmap beatmap)
protected ResultsPage(ScoreInfo score, WorkingBeatmap beatmap)
{
Score = score;
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;
public readonly BeatmapDetails Details;
public readonly Leaderboard Leaderboard;
public readonly BeatmapLeaderboard Leaderboard;
private WorkingBeatmap beatmap;
public WorkingBeatmap Beatmap
@ -52,7 +52,7 @@ namespace osu.Game.Screens.Select
default:
Details.Hide();
Leaderboard.Scope = (LeaderboardScope)tab - 1;
Leaderboard.Scope = (BeatmapLeaderboardScope)tab - 1;
Leaderboard.Show();
break;
}
@ -73,7 +73,7 @@ namespace osu.Game.Screens.Select
Alpha = 0,
Margin = new MarginPadding { Top = details_padding },
},
Leaderboard = new Leaderboard
Leaderboard = new BeatmapLeaderboard
{
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
{
public enum LeaderboardScope
public enum BeatmapLeaderboardScope
{
Local,
Country,

View File

@ -28,7 +28,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select.Options;
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)]

View File

@ -3,17 +3,29 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
namespace osu.Game.Users
{
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;
[Resolved(CanBeNull = true)]
private OsuGame game { get; set; }
/// <summary>
/// An avatar for specified user.
/// </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 (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,
Texture = texture,
FillMode = FillMode.Fit,
Anchor = 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>.
// 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.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()
{
base.LoadComplete();
@ -45,15 +51,18 @@ namespace osu.Game.Users
{
displayedAvatar?.FadeOut(300);
displayedAvatar?.Expire();
if (user != null || ShowGuestOnNull)
{
Add(displayedAvatar = new DelayedLoadWrapper(
new Avatar(user)
var avatar = new Avatar(user)
{
RelativeSizeAxes = Axes.Both,
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,
Masking = true,
CornerRadius = 5,
OpenOnClick = { Value = false },
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,

View File

@ -18,7 +18,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.0" />
<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="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />