1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-19 10:52:55 +08:00

Merge branch 'master' into rewrite-standalone-chat

This commit is contained in:
Dan Balasescu 2018-12-22 16:07:27 +09:00 committed by GitHub
commit 2c97409e2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 352 additions and 161 deletions

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

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

@ -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

@ -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

@ -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

@ -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

@ -28,7 +28,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

@ -19,11 +19,11 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users; using osu.Game.Users;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Online.Leaderboards;
using osu.Game.Scoring; using osu.Game.Scoring;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking

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

@ -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, RelativeSizeAxes = Axes.Both,
Texture = texture, Child = new Sprite
FillMode = FillMode.Fit, {
Anchor = Anchor.Centre, RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre 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>. // 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,