1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 07:22:54 +08:00

Merge remote-tracking branch 'upstream/master' into tournament-tools

This commit is contained in:
Dean Herbert 2019-06-17 19:30:17 +09:00
commit b259710a62
52 changed files with 685 additions and 247 deletions

View File

@ -43,10 +43,13 @@ namespace osu.Game.Rulesets.Catch.Replays
float positionChange = Math.Abs(lastPosition - h.X); float positionChange = Math.Abs(lastPosition - h.X);
double timeAvailable = h.StartTime - lastTime; double timeAvailable = h.StartTime - lastTime;
//So we can either make it there without a dash or not. // So we can either make it there without a dash or not.
double speedRequired = positionChange / timeAvailable; // If positionChange is 0, we don't need to move, so speedRequired should also be 0 (could be NaN if timeAvailable is 0 too)
// The case where positionChange > 0 and timeAvailable == 0 results in PositiveInfinity which provides expected beheaviour.
double speedRequired = positionChange == 0 ? 0 : positionChange / timeAvailable;
bool dashRequired = speedRequired > movement_speed && h.StartTime != 0; bool dashRequired = speedRequired > movement_speed;
bool impossibleJump = speedRequired > movement_speed * 2;
// todo: get correct catcher size, based on difficulty CS. // todo: get correct catcher size, based on difficulty CS.
const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f; const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f;
@ -59,9 +62,8 @@ namespace osu.Game.Rulesets.Catch.Replays
return; return;
} }
if (h is Banana) if (impossibleJump)
{ {
// auto bananas unrealistically warp to catch 100% combo.
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
} }
else if (h.HyperDash) else if (h.HyperDash)

View File

@ -0,0 +1,109 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using osuTK;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneGameplayRewinding : PlayerTestScene
{
private RulesetExposingPlayer player => (RulesetExposingPlayer)Player;
[Resolved]
private AudioManager audioManager { get; set; }
public TestSceneGameplayRewinding()
: base(new OsuRuleset())
{
}
private Track track;
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
{
var working = new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
track = working.Track;
return working;
}
[Test]
public void TestNoJudgementsOnRewind()
{
AddUntilStep("wait for track to start running", () => track.IsRunning);
addSeekStep(3000);
AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
AddStep("clear results", () => player.AppliedResults.Clear());
addSeekStep(0);
AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
AddAssert("no results triggered", () => player.AppliedResults.Count == 0);
}
private void addSeekStep(double time)
{
AddStep($"seek to {time}", () => track.Seek(time));
// Allow a few frames of lenience
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
}
protected override Player CreatePlayer(Ruleset ruleset)
{
Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new RulesetExposingPlayer();
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = { BaseDifficulty = { ApproachRate = 9 } },
};
for (int i = 0; i < 15; i++)
{
beatmap.HitObjects.Add(new HitCircle
{
Position = new Vector2(256, 192),
StartTime = 1000 + 30 * i
});
}
return beatmap;
}
private class RulesetExposingPlayer : Player
{
public readonly List<JudgementResult> AppliedResults = new List<JudgementResult>();
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
public RulesetExposingPlayer()
: base(false, false)
{
}
[BackgroundDependencyLoader]
private void load()
{
ScoreProcessor.NewJudgement += r => AppliedResults.Add(r);
}
}
}
}

View File

@ -41,6 +41,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(SuccessRate), typeof(SuccessRate),
}; };
private RulesetInfo maniaRuleset;
private RulesetInfo taikoRuleset;
public TestSceneBeatmapSetOverlay() public TestSceneBeatmapSetOverlay()
{ {
Add(overlay = new BeatmapSetOverlay()); Add(overlay = new BeatmapSetOverlay());
@ -49,13 +52,25 @@ namespace osu.Game.Tests.Visual.Online
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(RulesetStore rulesets) private void load(RulesetStore rulesets)
{ {
var mania = rulesets.GetRuleset(3); maniaRuleset = rulesets.GetRuleset(3);
var taiko = rulesets.GetRuleset(1); taikoRuleset = rulesets.GetRuleset(1);
}
[Test]
public void TestLoading()
{
AddStep(@"show loading", () => overlay.ShowBeatmapSet(null)); AddStep(@"show loading", () => overlay.ShowBeatmapSet(null));
}
[Test]
public void TestOnline()
{
AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55)); AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55));
}
[Test]
public void TestLocalBeatmaps()
{
AddStep(@"show first", () => AddStep(@"show first", () =>
{ {
overlay.ShowBeatmapSet(new BeatmapSetInfo overlay.ShowBeatmapSet(new BeatmapSetInfo
@ -87,13 +102,14 @@ namespace osu.Game.Tests.Visual.Online
Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778", Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778",
}, },
}, },
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
Beatmaps = new List<BeatmapInfo> Beatmaps = new List<BeatmapInfo>
{ {
new BeatmapInfo new BeatmapInfo
{ {
StarDifficulty = 1.36, StarDifficulty = 1.36,
Version = @"BASIC", Version = @"BASIC",
Ruleset = mania, Ruleset = maniaRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
CircleSize = 4, CircleSize = 4,
@ -111,16 +127,15 @@ namespace osu.Game.Tests.Visual.Online
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}, },
new BeatmapInfo new BeatmapInfo
{ {
StarDifficulty = 2.22, StarDifficulty = 2.22,
Version = @"NOVICE", Version = @"NOVICE",
Ruleset = mania, Ruleset = maniaRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
CircleSize = 4, CircleSize = 4,
@ -138,16 +153,15 @@ namespace osu.Game.Tests.Visual.Online
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}, },
new BeatmapInfo new BeatmapInfo
{ {
StarDifficulty = 3.49, StarDifficulty = 3.49,
Version = @"ADVANCED", Version = @"ADVANCED",
Ruleset = mania, Ruleset = maniaRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
CircleSize = 4, CircleSize = 4,
@ -165,16 +179,15 @@ namespace osu.Game.Tests.Visual.Online
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}, },
new BeatmapInfo new BeatmapInfo
{ {
StarDifficulty = 4.24, StarDifficulty = 4.24,
Version = @"EXHAUST", Version = @"EXHAUST",
Ruleset = mania, Ruleset = maniaRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
CircleSize = 4, CircleSize = 4,
@ -192,16 +205,15 @@ namespace osu.Game.Tests.Visual.Online
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}, },
new BeatmapInfo new BeatmapInfo
{ {
StarDifficulty = 5.26, StarDifficulty = 5.26,
Version = @"GRAVITY", Version = @"GRAVITY",
Ruleset = mania, Ruleset = maniaRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
CircleSize = 4, CircleSize = 4,
@ -219,9 +231,8 @@ namespace osu.Game.Tests.Visual.Online
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}, },
}, },
@ -258,13 +269,14 @@ namespace osu.Game.Tests.Visual.Online
Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472", Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472",
}, },
}, },
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
Beatmaps = new List<BeatmapInfo> Beatmaps = new List<BeatmapInfo>
{ {
new BeatmapInfo new BeatmapInfo
{ {
StarDifficulty = 1.40, StarDifficulty = 1.40,
Version = @"yzrin's Kantan", Version = @"yzrin's Kantan",
Ruleset = taiko, Ruleset = taikoRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
CircleSize = 2, CircleSize = 2,
@ -282,16 +294,15 @@ namespace osu.Game.Tests.Visual.Online
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}, },
new BeatmapInfo new BeatmapInfo
{ {
StarDifficulty = 2.23, StarDifficulty = 2.23,
Version = @"Futsuu", Version = @"Futsuu",
Ruleset = taiko, Ruleset = taikoRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
CircleSize = 2, CircleSize = 2,
@ -309,16 +320,15 @@ namespace osu.Game.Tests.Visual.Online
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}, },
new BeatmapInfo new BeatmapInfo
{ {
StarDifficulty = 3.19, StarDifficulty = 3.19,
Version = @"Muzukashii", Version = @"Muzukashii",
Ruleset = taiko, Ruleset = taikoRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
CircleSize = 2, CircleSize = 2,
@ -336,16 +346,15 @@ namespace osu.Game.Tests.Visual.Online
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}, },
new BeatmapInfo new BeatmapInfo
{ {
StarDifficulty = 3.97, StarDifficulty = 3.97,
Version = @"Charlotte's Oni", Version = @"Charlotte's Oni",
Ruleset = taiko, Ruleset = taikoRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
CircleSize = 5, CircleSize = 5,
@ -363,16 +372,15 @@ namespace osu.Game.Tests.Visual.Online
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}, },
new BeatmapInfo new BeatmapInfo
{ {
StarDifficulty = 5.08, StarDifficulty = 5.08,
Version = @"Labyrinth Oni", Version = @"Labyrinth Oni",
Ruleset = taiko, Ruleset = taikoRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
CircleSize = 5, CircleSize = 5,
@ -390,16 +398,24 @@ namespace osu.Game.Tests.Visual.Online
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}, },
}, },
}); });
}); });
}
[Test]
public void TestHide()
{
AddStep(@"hide", overlay.Hide); AddStep(@"hide", overlay.Hide);
}
[Test]
public void TestShowWithNoReload()
{
AddStep(@"show without reload", overlay.Show); AddStep(@"show without reload", overlay.Show);
} }
} }

View File

@ -0,0 +1,69 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Screens.Select.Details;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneBeatmapSetOverlayDetails : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Details)
};
private RatingsExposingDetails details;
[SetUp]
public void Setup() => Schedule(() =>
{
Child = details = new RatingsExposingDetails
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
};
});
[Test]
public void TestMetrics()
{
var firstSet = createSet();
var secondSet = createSet();
AddStep("set first set", () => details.BeatmapSet = firstSet);
AddAssert("ratings set", () => details.Ratings.Metrics == firstSet.Metrics);
AddStep("set second set", () => details.BeatmapSet = secondSet);
AddAssert("ratings set", () => details.Ratings.Metrics == secondSet.Metrics);
BeatmapSetInfo createSet() => new BeatmapSetInfo
{
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray() },
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
Metrics = new BeatmapMetrics
{
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
}
}
}
};
}
private class RatingsExposingDetails : Details
{
public new UserRatings Ratings => base.Ratings;
}
}
}

View File

@ -0,0 +1,82 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Screens.Select.Details;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneBeatmapSetOverlaySuccessRate : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Details)
};
private GraphExposingSuccessRate successRate;
[SetUp]
public void Setup() => Schedule(() =>
{
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(275, 220),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Gray,
},
successRate = new GraphExposingSuccessRate
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(275, 220),
Padding = new MarginPadding(20)
}
}
};
});
[Test]
public void TestMetrics()
{
var firstBeatmap = createBeatmap();
var secondBeatmap = createBeatmap();
AddStep("set first set", () => successRate.Beatmap = firstBeatmap);
AddAssert("ratings set", () => successRate.Graph.Metrics == firstBeatmap.Metrics);
AddStep("set second set", () => successRate.Beatmap = secondBeatmap);
AddAssert("ratings set", () => successRate.Graph.Metrics == secondBeatmap.Metrics);
BeatmapInfo createBeatmap() => new BeatmapInfo
{
Metrics = new BeatmapMetrics
{
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
}
};
}
private class GraphExposingSuccessRate : SuccessRate
{
public new FailRetryGraph Graph => base.Graph;
}
}
}

View File

@ -5,9 +5,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Profile.Sections; using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Historical; using osu.Game.Overlays.Profile.Sections.Historical;
using osu.Game.Users; using osu.Game.Users;
@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Online
Colour = OsuColour.Gray(0.2f) Colour = OsuColour.Gray(0.2f)
}); });
Add(new ScrollContainer Add(new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = section = new HistoricalSection(), Child = section = new HistoricalSection(),

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
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.Overlays.Profile.Sections; using osu.Game.Overlays.Profile.Sections;
@ -36,7 +37,7 @@ namespace osu.Game.Tests.Visual.Online
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f) Colour = OsuColour.Gray(0.2f)
}, },
new ScrollContainer new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer<DrawableRecentActivity> Child = new FillFlowContainer<DrawableRecentActivity>

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Profile.Sections; using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Users; using osu.Game.Users;
@ -33,7 +34,7 @@ namespace osu.Game.Tests.Visual.Online
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f) Colour = OsuColour.Gray(0.2f)
}, },
new ScrollContainer new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = ranks = new RanksSection(), Child = ranks = new RanksSection(),

View File

@ -32,6 +32,10 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{ {
BeatmapSetInfo =
{
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
},
BeatmapInfo = BeatmapInfo =
{ {
Version = "All Metrics", Version = "All Metrics",
@ -50,9 +54,8 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 5.3f, StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
} }
} }
@ -60,6 +63,10 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{ {
BeatmapSetInfo =
{
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
},
BeatmapInfo = BeatmapInfo =
{ {
Version = "All Metrics", Version = "All Metrics",
@ -77,15 +84,18 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 5.3f, StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
} }
}); });
AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null) AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{ {
BeatmapSetInfo =
{
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
},
BeatmapInfo = BeatmapInfo =
{ {
Version = "Only Ratings", Version = "Only Ratings",
@ -101,11 +111,7 @@ namespace osu.Game.Tests.Visual.SongSelect
OverallDifficulty = 6, OverallDifficulty = 6,
ApproachRate = 6, ApproachRate = 6,
}, },
StarDifficulty = 4.8f, StarDifficulty = 4.8f
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 11),
},
} }
}); });
@ -129,8 +135,8 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 2.91f, StarDifficulty = 2.91f,
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
}, },
} }
}); });

View File

@ -1,28 +1,38 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.ComponentModel;
using System.Linq; using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
namespace osu.Game.Tests.Visual.SongSelect namespace osu.Game.Tests.Visual.SongSelect
{ {
[Description("PlaySongSelect beatmap details")] [System.ComponentModel.Description("PlaySongSelect beatmap details")]
public class TestSceneBeatmapDetails : OsuTestScene public class TestSceneBeatmapDetails : OsuTestScene
{ {
public TestSceneBeatmapDetails() private BeatmapDetails details;
[SetUp]
public void Setup() => Schedule(() =>
{ {
BeatmapDetails details; Child = details = new BeatmapDetails
Add(details = new BeatmapDetails
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(150), Padding = new MarginPadding(150),
}); };
});
[Test]
public void TestAllMetrics()
{
AddStep("all metrics", () => details.Beatmap = new BeatmapInfo AddStep("all metrics", () => details.Beatmap = new BeatmapInfo
{ {
BeatmapSet = new BeatmapSetInfo
{
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
},
Version = "All Metrics", Version = "All Metrics",
Metadata = new BeatmapMetadata Metadata = new BeatmapMetadata
{ {
@ -39,14 +49,21 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 5.3f, StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}); });
}
[Test]
public void TestAllMetricsExceptSource()
{
AddStep("all except source", () => details.Beatmap = new BeatmapInfo AddStep("all except source", () => details.Beatmap = new BeatmapInfo
{ {
BeatmapSet = new BeatmapSetInfo
{
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
},
Version = "All Metrics", Version = "All Metrics",
Metadata = new BeatmapMetadata Metadata = new BeatmapMetadata
{ {
@ -62,14 +79,21 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 5.3f, StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}); });
}
[Test]
public void TestOnlyRatings()
{
AddStep("ratings", () => details.Beatmap = new BeatmapInfo AddStep("ratings", () => details.Beatmap = new BeatmapInfo
{ {
BeatmapSet = new BeatmapSetInfo
{
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
},
Version = "Only Ratings", Version = "Only Ratings",
Metadata = new BeatmapMetadata Metadata = new BeatmapMetadata
{ {
@ -84,12 +108,12 @@ namespace osu.Game.Tests.Visual.SongSelect
ApproachRate = 6, ApproachRate = 6,
}, },
StarDifficulty = 4.8f, StarDifficulty = 4.8f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 11),
},
}); });
}
[Test]
public void TestOnlyFailsAndRetries()
{
AddStep("fails retries", () => details.Beatmap = new BeatmapInfo AddStep("fails retries", () => details.Beatmap = new BeatmapInfo
{ {
Version = "Only Retries and Fails", Version = "Only Retries and Fails",
@ -108,11 +132,15 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 2.91f, StarDifficulty = 2.91f,
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
}, },
}); });
}
[Test]
public void TestNoMetrics()
{
AddStep("no metrics", () => details.Beatmap = new BeatmapInfo AddStep("no metrics", () => details.Beatmap = new BeatmapInfo
{ {
Version = "No Metrics", Version = "No Metrics",
@ -129,10 +157,22 @@ namespace osu.Game.Tests.Visual.SongSelect
ApproachRate = 6.5f, ApproachRate = 6.5f,
}, },
StarDifficulty = 1.97f, StarDifficulty = 1.97f,
Metrics = new BeatmapMetrics(),
}); });
}
[Test]
public void TestNullBeatmap()
{
AddStep("null beatmap", () => details.Beatmap = null); AddStep("null beatmap", () => details.Beatmap = null);
} }
[Test]
public void TestOnlineMetrics()
{
AddStep("online ratings/retries/fails", () => details.Beatmap = new BeatmapInfo
{
OnlineBeatmapID = 162,
});
}
} }
} }

View File

@ -270,9 +270,8 @@ namespace osu.Game.Tests.Visual.SongSelect
}, },
Metrics = new BeatmapMetrics Metrics = new BeatmapMetrics
{ {
Ratings = Enumerable.Range(0, 11), Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
}, },
}; };
} }

View File

@ -18,6 +18,7 @@ using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
@ -100,8 +101,11 @@ namespace osu.Game.Tests.Visual.SongSelect
} }
[SetUp] [SetUp]
public virtual void SetUp() => public virtual void SetUp() => Schedule(() =>
Schedule(() => { manager?.Delete(manager.GetAllUsableBeatmapSets()); }); {
Ruleset.Value = new OsuRuleset().RulesetInfo;
manager?.Delete(manager.GetAllUsableBeatmapSets());
});
[Test] [Test]
public void TestDummy() public void TestDummy()
@ -185,7 +189,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("empty mods", () => !Mods.Value.Any()); AddAssert("empty mods", () => !Mods.Value.Any());
void onModChange(ValueChangedEvent<IReadOnlyList<Mod>> e) => modChangeIndex = actionIndex++; void onModChange(ValueChangedEvent<IReadOnlyList<Mod>> e) => modChangeIndex = actionIndex++;
void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex--; void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex++;
} }
[Test] [Test]

View File

@ -26,8 +26,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
new NamedIconButton("No change", new IconButton()), new NamedIconButton("No change", new IconButton()),
new NamedIconButton("Background colours", new ColouredIconButton()), new NamedIconButton("Background colours", new ColouredIconButton()),
new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }), new NamedIconButton("Full-width", new IconButton { Size = new Vector2(200, 30) }),
new NamedIconButton("Unchanging size", new IconButton(), false),
new NamedIconButton("Icon colours", new IconButton new NamedIconButton("Icon colours", new IconButton
{ {
IconColour = Color4.Green, IconColour = Color4.Green,
@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private class NamedIconButton : Container private class NamedIconButton : Container
{ {
public NamedIconButton(string name, IconButton button, bool allowSizeChange = true) public NamedIconButton(string name, IconButton button)
{ {
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
Width = 200; Width = 200;
@ -101,13 +100,7 @@ namespace osu.Game.Tests.Visual.UserInterface
} }
}; };
if (allowSizeChange) iconContainer.AutoSizeAxes = Axes.Both;
iconContainer.AutoSizeAxes = Axes.Both;
else
{
iconContainer.RelativeSizeAxes = Axes.X;
iconContainer.Height = 30;
}
button.Anchor = Anchor.Centre; button.Anchor = Anchor.Centre;
button.Origin = Anchor.Centre; button.Origin = Anchor.Centre;

View File

@ -3,7 +3,6 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -23,9 +22,9 @@ namespace osu.Game.Tests.Visual.UserInterface
}; };
Add(mc); Add(mc);
AddToggleStep(@"toggle visibility", state => mc.State.Value = state ? Visibility.Visible : Visibility.Hidden);
AddStep(@"show", () => mc.Show()); AddStep(@"show", () => mc.Show());
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state); AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
AddStep(@"show", () => mc.Hide());
} }
} }
} }

View File

@ -10,6 +10,7 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -29,7 +30,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Colour = Color4.Teal, Colour = Color4.Teal,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
new ScrollContainer new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = flow = new FillFlowContainer<Icon> Child = flow = new FillFlowContainer<Icon>

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -92,13 +93,13 @@ namespace osu.Game.Tests.Visual.UserInterface
public void TestUnloadAndReload() public void TestUnloadAndReload()
{ {
var backgrounds = new List<TestUpdateableBeatmapBackgroundSprite>(); var backgrounds = new List<TestUpdateableBeatmapBackgroundSprite>();
ScrollContainer scrollContainer = null; OsuScrollContainer scrollContainer = null;
AddStep("create backgrounds hierarchy", () => AddStep("create backgrounds hierarchy", () =>
{ {
FillFlowContainer backgroundFlow; FillFlowContainer backgroundFlow;
Child = scrollContainer = new ScrollContainer Child = scrollContainer = new OsuScrollContainer
{ {
Size = new Vector2(500), Size = new Vector2(500),
Child = backgroundFlow = new FillFlowContainer Child = backgroundFlow = new FillFlowContainer

View File

@ -1,31 +1,26 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
/// <summary> /// <summary>
/// Beatmap metrics based on acculumated online data from community plays. /// Beatmap metrics based on accumulated online data from community plays.
/// </summary> /// </summary>
public class BeatmapMetrics public class BeatmapMetrics
{ {
/// <summary>
/// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?).
/// </summary>
public IEnumerable<int> Ratings { get; set; }
/// <summary> /// <summary>
/// Points of failure on a relative time scale (usually 0..100). /// Points of failure on a relative time scale (usually 0..100).
/// </summary> /// </summary>
[JsonProperty(@"fail")] [JsonProperty(@"fail")]
public IEnumerable<int> Fails { get; set; } public int[] Fails { get; set; } = Array.Empty<int>();
/// <summary> /// <summary>
/// Points of retry on a relative time scale (usually 0..100). /// Points of retry on a relative time scale (usually 0..100).
/// </summary> /// </summary>
[JsonProperty(@"exit")] [JsonProperty(@"exit")]
public IEnumerable<int> Retries { get; set; } public int[] Retries { get; set; } = Array.Empty<int>();
} }
} }

View File

@ -32,6 +32,9 @@ namespace osu.Game.Beatmaps
[NotMapped] [NotMapped]
public BeatmapSetOnlineInfo OnlineInfo { get; set; } public BeatmapSetOnlineInfo OnlineInfo { get; set; }
[NotMapped]
public BeatmapSetMetrics Metrics { get; set; }
public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0; public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0;
[NotMapped] [NotMapped]

View File

@ -0,0 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using Newtonsoft.Json;
namespace osu.Game.Beatmaps
{
public class BeatmapSetMetrics
{
/// <summary>
/// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?).
/// </summary>
[JsonProperty("ratings")]
public int[] Ratings { get; set; } = Array.Empty<int>();
}
}

View File

@ -1,13 +1,18 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osuTK;
using osuTK.Graphics;
using osuTK.Input; using osuTK.Input;
namespace osu.Game.Graphics.Containers namespace osu.Game.Graphics.Containers
{ {
public class OsuScrollContainer : ScrollContainer public class OsuScrollContainer : ScrollContainer<Drawable>
{ {
/// <summary> /// <summary>
/// Allows controlling the scroll bar from any position in the container using the right mouse button. /// Allows controlling the scroll bar from any position in the container using the right mouse button.
@ -28,6 +33,11 @@ namespace osu.Game.Graphics.Containers
protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging; protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging;
public OsuScrollContainer(Direction scrollDirection = Direction.Vertical)
: base(scrollDirection)
{
}
protected override bool OnMouseDown(MouseDownEvent e) protected override bool OnMouseDown(MouseDownEvent e)
{ {
if (shouldPerformRightMouseScroll(e)) if (shouldPerformRightMouseScroll(e))
@ -71,5 +81,87 @@ namespace osu.Game.Graphics.Containers
return base.OnDragEnd(e); return base.OnDragEnd(e);
} }
protected override ScrollbarContainer CreateScrollbar(Direction direction) => new OsuScrollbar(direction);
protected class OsuScrollbar : ScrollbarContainer
{
private const float dim_size = 10;
private Color4 hoverColour;
private Color4 defaultColour;
private Color4 highlightColour;
private readonly Box box;
public OsuScrollbar(Direction scrollDir)
: base(scrollDir)
{
Blending = BlendingMode.Additive;
CornerRadius = 5;
const float margin = 3;
Margin = new MarginPadding
{
Left = scrollDir == Direction.Vertical ? margin : 0,
Right = scrollDir == Direction.Vertical ? margin : 0,
Top = scrollDir == Direction.Horizontal ? margin : 0,
Bottom = scrollDir == Direction.Horizontal ? margin : 0,
};
Masking = true;
Child = box = new Box { RelativeSizeAxes = Axes.Both };
ResizeTo(1);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = defaultColour = colours.Gray8;
hoverColour = colours.GrayF;
highlightColour = colours.Green;
}
public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None)
{
Vector2 size = new Vector2(dim_size)
{
[(int)ScrollDirection] = val
};
this.ResizeTo(size, duration, easing);
}
protected override bool OnHover(HoverEvent e)
{
this.FadeColour(hoverColour, 100);
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
this.FadeColour(defaultColour, 100);
}
protected override bool OnMouseDown(MouseDownEvent e)
{
if (!base.OnMouseDown(e)) return false;
//note that we are changing the colour of the box here as to not interfere with the hover effect.
box.FadeColour(highlightColour, 100);
return true;
}
protected override bool OnMouseUp(MouseUpEvent e)
{
if (e.Button != MouseButton.Left) return false;
box.FadeColour(Color4.White, 100);
return base.OnMouseUp(e);
}
}
} }
} }

View File

@ -16,7 +16,7 @@ namespace osu.Game.Graphics.Containers
where T : Drawable where T : Drawable
{ {
private Drawable expandableHeader, fixedHeader, footer, headerBackground; private Drawable expandableHeader, fixedHeader, footer, headerBackground;
private readonly ScrollContainer scrollContainer; private readonly OsuScrollContainer scrollContainer;
private readonly Container headerBackgroundContainer; private readonly Container headerBackgroundContainer;
private readonly FlowContainer<T> scrollContentContainer; private readonly FlowContainer<T> scrollContentContainer;
@ -124,7 +124,7 @@ namespace osu.Game.Graphics.Containers
public SectionsContainer() public SectionsContainer()
{ {
AddInternal(scrollContainer = new ScrollContainer AddInternal(scrollContainer = new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Masking = true, Masking = true,

View File

@ -11,7 +11,7 @@ namespace osu.Game.Graphics.UserInterface
{ {
public class IconButton : OsuAnimatedButton public class IconButton : OsuAnimatedButton
{ {
public const float BUTTON_SIZE = 30; public const float DEFAULT_BUTTON_SIZE = 30;
private Color4? iconColour; private Color4? iconColour;
@ -57,26 +57,11 @@ namespace osu.Game.Graphics.UserInterface
set => icon.Scale = value; set => icon.Scale = value;
} }
/// <summary>
/// The size of the <see cref="IconButton"/> while it is not being pressed.
/// </summary>
public Vector2 ButtonSize
{
get => Content.Size;
set
{
Content.RelativeSizeAxes = Axes.None;
Content.AutoSizeAxes = Axes.None;
Content.Size = value;
}
}
private readonly SpriteIcon icon; private readonly SpriteIcon icon;
public IconButton() public IconButton()
{ {
AutoSizeAxes = Axes.Both; Size = new Vector2(DEFAULT_BUTTON_SIZE);
ButtonSize = new Vector2(BUTTON_SIZE);
Add(icon = new SpriteIcon Add(icon = new SpriteIcon
{ {

View File

@ -262,8 +262,9 @@ namespace osu.Game.Online.API
handleWebException(we); handleWebException(we);
return false; return false;
} }
catch catch (Exception ex)
{ {
Logger.Error(ex, "Error occurred while handling an API request.");
return false; return false;
} }
} }

View File

@ -10,9 +10,18 @@ namespace osu.Game.Online.API
{ {
private string filename; private string filename;
/// <summary>
/// Used to set the extension of the file returned by this request.
/// </summary>
protected virtual string FileExtension { get; } = @".tmp";
protected override WebRequest CreateWebRequest() protected override WebRequest CreateWebRequest()
{ {
var request = new FileWebRequest(filename = Path.GetTempFileName(), Uri); var file = Path.GetTempFileName();
File.Move(file, filename = Path.ChangeExtension(file, FileExtension));
var request = new FileWebRequest(filename, Uri);
request.DownloadProgress += request_Progress; request.DownloadProgress += request_Progress;
return request; return request;
} }

View File

@ -1,20 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
public class GetBeatmapDetailsRequest : APIRequest<APIBeatmapMetrics>
{
private readonly BeatmapInfo beatmap;
public GetBeatmapDetailsRequest(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
}
protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}";
}
}

View File

@ -57,6 +57,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"version")] [JsonProperty(@"version")]
private string version { get; set; } private string version { get; set; }
[JsonProperty(@"failtimes")]
private BeatmapMetrics metrics { get; set; }
public BeatmapInfo ToBeatmap(RulesetStore rulesets) public BeatmapInfo ToBeatmap(RulesetStore rulesets)
{ {
var set = BeatmapSet?.ToBeatmapSet(rulesets) ?? new BeatmapSetInfo var set = BeatmapSet?.ToBeatmapSet(rulesets) ?? new BeatmapSetInfo
@ -74,6 +77,7 @@ namespace osu.Game.Online.API.Requests.Responses
Version = version, Version = version,
Status = Status, Status = Status,
BeatmapSet = set, BeatmapSet = set,
Metrics = metrics,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
DrainRate = drainRate, DrainRate = drainRate,

View File

@ -1,29 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Newtonsoft.Json;
using osu.Game.Beatmaps;
namespace osu.Game.Online.API.Requests.Responses
{
public class APIBeatmapMetrics : BeatmapMetrics
{
//the online API returns some metrics as a nested object.
[JsonProperty(@"failtimes")]
private BeatmapMetrics failTimes
{
set
{
Fails = value.Fails;
Retries = value.Retries;
}
}
//and other metrics in the beatmap set.
[JsonProperty(@"beatmapset")]
private BeatmapMetrics beatmapSet
{
set => Ratings = value.Ratings;
}
}
}

View File

@ -54,6 +54,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"last_updated")] [JsonProperty(@"last_updated")]
private DateTimeOffset lastUpdated { get; set; } private DateTimeOffset lastUpdated { get; set; }
[JsonProperty(@"ratings")]
private int[] ratings { get; set; }
[JsonProperty(@"user_id")] [JsonProperty(@"user_id")]
private long creatorId private long creatorId
{ {
@ -70,6 +73,7 @@ namespace osu.Game.Online.API.Requests.Responses
OnlineBeatmapSetID = OnlineBeatmapSetID, OnlineBeatmapSetID = OnlineBeatmapSetID,
Metadata = this, Metadata = this,
Status = Status, Status = Status,
Metrics = ratings == null ? null : new BeatmapSetMetrics { Ratings = ratings },
OnlineInfo = new BeatmapSetOnlineInfo OnlineInfo = new BeatmapSetOnlineInfo
{ {
Covers = covers, Covers = covers,

View File

@ -31,9 +31,9 @@ namespace osu.Game.Online.API.Requests.Responses
public List<APIChangelogEntry> ChangelogEntries { get; set; } public List<APIChangelogEntry> ChangelogEntries { get; set; }
[JsonProperty("versions")] [JsonProperty("versions")]
public VersionNatigation Versions { get; set; } public VersionNavigation Versions { get; set; }
public class VersionNatigation public class VersionNavigation
{ {
[JsonProperty("next")] [JsonProperty("next")]
public APIChangelogBuild Next { get; set; } public APIChangelogBuild Next { get; set; }

View File

@ -30,7 +30,10 @@ namespace osu.Game.Online.API.Requests
req.ContentType = "application/json"; req.ContentType = "application/json";
req.Method = HttpMethod.Put; req.Method = HttpMethod.Put;
req.AddRaw(JsonConvert.SerializeObject(scoreInfo)); req.AddRaw(JsonConvert.SerializeObject(scoreInfo, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
}));
return req; return req;
} }

View File

@ -23,7 +23,7 @@ namespace osu.Game.Online.Leaderboards
{ {
private const double fade_duration = 300; private const double fade_duration = 300;
private readonly ScrollContainer scrollContainer; private readonly OsuScrollContainer scrollContainer;
private readonly Container placeholderContainer; private readonly Container placeholderContainer;
private FillFlowContainer<LeaderboardScore> scrollFlow; private FillFlowContainer<LeaderboardScore> scrollFlow;

View File

@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapSet
private void updateDisplay() private void updateDisplay()
{ {
bpm.Value = BeatmapSet?.OnlineInfo.BPM.ToString(@"0.##") ?? "-"; bpm.Value = BeatmapSet?.OnlineInfo?.BPM.ToString(@"0.##") ?? "-";
if (beatmap == null) if (beatmap == null)
{ {

View File

@ -16,10 +16,11 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
public class Details : FillFlowContainer public class Details : FillFlowContainer
{ {
protected readonly UserRatings Ratings;
private readonly PreviewButton preview; private readonly PreviewButton preview;
private readonly BasicStats basic; private readonly BasicStats basic;
private readonly AdvancedStats advanced; private readonly AdvancedStats advanced;
private readonly UserRatings ratings;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
@ -33,6 +34,7 @@ namespace osu.Game.Overlays.BeatmapSet
beatmapSet = value; beatmapSet = value;
basic.BeatmapSet = preview.BeatmapSet = BeatmapSet; basic.BeatmapSet = preview.BeatmapSet = BeatmapSet;
updateDisplay();
} }
} }
@ -46,13 +48,12 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmap) return; if (value == beatmap) return;
basic.Beatmap = advanced.Beatmap = beatmap = value; basic.Beatmap = advanced.Beatmap = beatmap = value;
updateDisplay();
} }
} }
private void updateDisplay() private void updateDisplay()
{ {
ratings.Metrics = Beatmap?.Metrics; Ratings.Metrics = BeatmapSet?.Metrics;
} }
public Details() public Details()
@ -87,7 +88,7 @@ namespace osu.Game.Overlays.BeatmapSet
}, },
new DetailBox new DetailBox
{ {
Child = ratings = new UserRatings Child = Ratings = new UserRatings
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 95, Height = 95,

View File

@ -14,11 +14,12 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
public class SuccessRate : Container public class SuccessRate : Container
{ {
protected readonly FailRetryGraph Graph;
private readonly FillFlowContainer header; private readonly FillFlowContainer header;
private readonly OsuSpriteText successRateLabel, successPercent, graphLabel; private readonly OsuSpriteText successRateLabel, successPercent, graphLabel;
private readonly Bar successRate; private readonly Bar successRate;
private readonly Container percentContainer; private readonly Container percentContainer;
private readonly FailRetryGraph graph;
private BeatmapInfo beatmap; private BeatmapInfo beatmap;
@ -37,15 +38,15 @@ namespace osu.Game.Overlays.BeatmapSet
private void updateDisplay() private void updateDisplay()
{ {
int passCount = beatmap?.OnlineInfo.PassCount ?? 0; int passCount = beatmap?.OnlineInfo?.PassCount ?? 0;
int playCount = beatmap?.OnlineInfo.PlayCount ?? 0; int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0;
var rate = playCount != 0 ? (float)passCount / playCount : 0; var rate = playCount != 0 ? (float)passCount / playCount : 0;
successPercent.Text = rate.ToString("P0"); successPercent.Text = rate.ToString("P0");
successRate.Length = rate; successRate.Length = rate;
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
graph.Metrics = beatmap?.Metrics; Graph.Metrics = beatmap?.Metrics;
} }
public SuccessRate() public SuccessRate()
@ -94,7 +95,7 @@ namespace osu.Game.Overlays.BeatmapSet
}, },
}, },
}, },
graph = new FailRetryGraph Graph = new FailRetryGraph
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
@ -117,7 +118,7 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
base.UpdateAfterChildren(); base.UpdateAfterChildren();
graph.Padding = new MarginPadding { Top = header.DrawHeight }; Graph.Padding = new MarginPadding { Top = header.DrawHeight };
} }
} }
} }

View File

@ -30,7 +30,7 @@ namespace osu.Game.Overlays
private RulesetStore rulesets; private RulesetStore rulesets;
private readonly ScrollContainer scroll; private readonly OsuScrollContainer scroll;
private readonly Bindable<BeatmapSetInfo> beatmapSet = new Bindable<BeatmapSetInfo>(); private readonly Bindable<BeatmapSetInfo> beatmapSet = new Bindable<BeatmapSetInfo>();
@ -49,7 +49,7 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f) Colour = OsuColour.Gray(0.2f)
}, },
scroll = new ScrollContainer scroll = new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false, ScrollbarVisible = false,

View File

@ -51,7 +51,7 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colour.PurpleDarkAlternative, Colour = colour.PurpleDarkAlternative,
}, },
new ScrollContainer new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false, ScrollbarVisible = false,

View File

@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Chat
{ {
public readonly Channel Channel; public readonly Channel Channel;
protected readonly ChatLineContainer ChatLineFlow; protected readonly ChatLineContainer ChatLineFlow;
private readonly ScrollContainer scroll; private readonly OsuScrollContainer scroll;
public DrawableChannel(Channel channel) public DrawableChannel(Channel channel)
{ {

View File

@ -464,12 +464,26 @@ namespace osu.Game.Overlays
private class MusicIconButton : IconButton private class MusicIconButton : IconButton
{ {
public MusicIconButton()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
HoverColour = colours.YellowDark.Opacity(0.6f); HoverColour = colours.YellowDark.Opacity(0.6f);
FlashColour = colours.Yellow; FlashColour = colours.Yellow;
} }
protected override void LoadComplete()
{
base.LoadComplete();
// works with AutoSizeAxes above to make buttons autosize with the scale animation.
Content.AutoSizeAxes = Axes.None;
Content.Size = new Vector2(DEFAULT_BUTTON_SIZE);
}
} }
private class Background : BufferedContainer private class Background : BufferedContainer

View File

@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Toolbar; using osu.Game.Overlays.Toolbar;
namespace osu.Game.Overlays.Settings namespace osu.Game.Overlays.Settings
@ -76,7 +77,7 @@ namespace osu.Game.Overlays.Settings
return base.OnMouseMove(e); return base.OnMouseMove(e);
} }
private class SidebarScrollContainer : ScrollContainer private class SidebarScrollContainer : OsuScrollContainer
{ {
public SidebarScrollContainer() public SidebarScrollContainer()
{ {

View File

@ -244,6 +244,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <returns>Whether a scoring result has occurred from this <see cref="DrawableHitObject"/> or any nested <see cref="DrawableHitObject"/>.</returns> /// <returns>Whether a scoring result has occurred from this <see cref="DrawableHitObject"/> or any nested <see cref="DrawableHitObject"/>.</returns>
protected bool UpdateResult(bool userTriggered) protected bool UpdateResult(bool userTriggered)
{ {
// It's possible for input to get into a bad state when rewinding gameplay, so results should not be processed
if (Time.Elapsed < 0)
return false;
judgementOccurred = false; judgementOccurred = false;
if (AllJudged) if (AllJudged)

View File

@ -36,12 +36,11 @@ namespace osu.Game.Screens.Edit.Components
playButton = new IconButton playButton = new IconButton
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre, Origin = Anchor.CentreLeft,
Scale = new Vector2(1.4f), Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f), IconScale = new Vector2(1.4f),
Icon = FontAwesome.Regular.PlayCircle, Icon = FontAwesome.Regular.PlayCircle,
Action = togglePause, Action = togglePause,
Padding = new MarginPadding { Left = 20 }
}, },
new OsuSpriteText new OsuSpriteText
{ {

View File

@ -171,7 +171,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
// Small offset to look a bit better centered along with the divisor text // Small offset to look a bit better centered along with the divisor text
Y = 1; Y = 1;
ButtonSize = new Vector2(20); Size = new Vector2(20);
IconScale = new Vector2(0.6f); IconScale = new Vector2(0.6f);
} }

View File

@ -31,14 +31,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
InternalChild = button = new TimelineIconButton { Action = () => Action?.Invoke() }; InternalChild = button = new TimelineIconButton { Action = () => Action?.Invoke() };
button.Enabled.BindTo(Enabled); button.Enabled.BindTo(Enabled);
Width = button.ButtonSize.X; Width = button.Width;
} }
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
button.ButtonSize = new Vector2(button.ButtonSize.X, DrawHeight); button.Size = new Vector2(button.Width, DrawHeight);
} }
private class TimelineIconButton : IconButton private class TimelineIconButton : IconButton

View File

@ -7,11 +7,12 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.Transforms;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Graphics.Containers;
using osuTK; using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components.Timeline namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{ {
public class ZoomableScrollContainer : ScrollContainer public class ZoomableScrollContainer : OsuScrollContainer
{ {
/// <summary> /// <summary>
/// The time to zoom into/out of a point. /// The time to zoom into/out of a point.

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
@ -226,7 +227,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
{ {
Padding = new MarginPadding { Horizontal = 10 }; Padding = new MarginPadding { Horizontal = 10 };
InternalChild = new ScrollContainer InternalChild = new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = fill = new FillFlowContainer Child = fill = new FillFlowContainer

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Overlays.SearchableList; using osu.Game.Overlays.SearchableList;
@ -43,7 +44,7 @@ namespace osu.Game.Screens.Multi.Lounge
Width = 0.55f, Width = 0.55f,
Children = new Drawable[] Children = new Drawable[]
{ {
new ScrollContainer new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
ScrollbarOverlapsContent = false, ScrollbarOverlapsContent = false,

View File

@ -10,6 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
@ -91,7 +92,7 @@ namespace osu.Game.Screens.Multi.Match.Components
{ {
new Drawable[] new Drawable[]
{ {
new ScrollContainer new OsuScrollContainer
{ {
Padding = new MarginPadding Padding = new MarginPadding
{ {

View File

@ -5,6 +5,7 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.SearchableList; using osu.Game.Overlays.SearchableList;
using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Components;
using osu.Game.Users; using osu.Game.Users;
@ -25,7 +26,7 @@ namespace osu.Game.Screens.Multi.Match.Components
Padding = new MarginPadding { Horizontal = SearchableListOverlay.WIDTH_PADDING }, Padding = new MarginPadding { Horizontal = SearchableListOverlay.WIDTH_PADDING },
Children = new Drawable[] Children = new Drawable[]
{ {
new ScrollContainer new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 10 }, Padding = new MarginPadding { Top = 10 },

View File

@ -10,7 +10,6 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using System.Linq; using System.Linq;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
@ -18,6 +17,8 @@ using osu.Game.Screens.Select.Details;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select
{ {
@ -30,7 +31,7 @@ namespace osu.Game.Screens.Select
private readonly AdvancedStats advanced; private readonly AdvancedStats advanced;
private readonly DetailBox ratingsContainer; private readonly DetailBox ratingsContainer;
private readonly UserRatings ratings; private readonly UserRatings ratings;
private readonly ScrollContainer metadataScroll; private readonly OsuScrollContainer metadataScroll;
private readonly MetadataSection description, source, tags; private readonly MetadataSection description, source, tags;
private readonly Container failRetryContainer; private readonly Container failRetryContainer;
private readonly FailRetryGraph failRetryGraph; private readonly FailRetryGraph failRetryGraph;
@ -40,6 +41,9 @@ namespace osu.Game.Screens.Select
private ScheduledDelegate pendingBeatmapSwitch; private ScheduledDelegate pendingBeatmapSwitch;
[Resolved]
private RulesetStore rulesets { get; set; }
private BeatmapInfo beatmap; private BeatmapInfo beatmap;
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
@ -107,7 +111,7 @@ namespace osu.Game.Screens.Select
}, },
}, },
}, },
metadataScroll = new ScrollContainer metadataScroll = new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Width = 0.5f, Width = 0.5f,
@ -181,57 +185,79 @@ namespace osu.Game.Screens.Select
tags.Text = Beatmap?.Metadata?.Tags; tags.Text = Beatmap?.Metadata?.Tags;
// metrics may have been previously fetched // metrics may have been previously fetched
if (Beatmap?.Metrics != null) if (Beatmap?.BeatmapSet?.Metrics != null && Beatmap?.Metrics != null)
{ {
updateMetrics(Beatmap.Metrics); updateMetrics();
return; return;
} }
// metrics may not be fetched but can be // for now, let's early abort if an OnlineBeatmapID is not present (should have been populated at import time).
if (Beatmap?.OnlineBeatmapID != null) if (Beatmap?.OnlineBeatmapID == null)
{ {
var requestedBeatmap = Beatmap; updateMetrics();
var lookup = new GetBeatmapDetailsRequest(requestedBeatmap); return;
lookup.Success += res => }
var requestedBeatmap = Beatmap;
var lookup = new GetBeatmapRequest(requestedBeatmap);
lookup.Success += res =>
{
Schedule(() =>
{ {
if (beatmap != requestedBeatmap) if (beatmap != requestedBeatmap)
//the beatmap has been changed since we started the lookup. //the beatmap has been changed since we started the lookup.
return; return;
requestedBeatmap.Metrics = res; var b = res.ToBeatmap(rulesets);
Schedule(() => updateMetrics(res));
};
lookup.Failure += e => Schedule(() => updateMetrics());
api.Queue(lookup);
loading.Show();
return;
}
updateMetrics(); if (requestedBeatmap.BeatmapSet == null)
requestedBeatmap.BeatmapSet = b.BeatmapSet;
else
requestedBeatmap.BeatmapSet.Metrics = b.BeatmapSet.Metrics;
requestedBeatmap.Metrics = b.Metrics;
updateMetrics();
});
};
lookup.Failure += e =>
{
Schedule(() =>
{
if (beatmap != requestedBeatmap)
//the beatmap has been changed since we started the lookup.
return;
updateMetrics();
});
};
api.Queue(lookup);
loading.Show();
} }
private void updateMetrics(BeatmapMetrics metrics = null) private void updateMetrics()
{ {
var hasRatings = metrics?.Ratings?.Any() ?? false; var hasRatings = beatmap?.BeatmapSet?.Metrics?.Ratings?.Any() ?? false;
var hasRetriesFails = (metrics?.Retries?.Any() ?? false) && (metrics.Fails?.Any() ?? false); var hasRetriesFails = (beatmap?.Metrics?.Retries?.Any() ?? false) && (beatmap?.Metrics.Fails?.Any() ?? false);
if (hasRatings) if (hasRatings)
{ {
ratings.Metrics = metrics; ratings.Metrics = beatmap.BeatmapSet.Metrics;
ratingsContainer.FadeIn(transition_duration); ratingsContainer.FadeIn(transition_duration);
} }
else else
{ {
ratings.Metrics = new BeatmapMetrics ratings.Metrics = new BeatmapSetMetrics { Ratings = new int[10] };
{
Ratings = new int[10],
};
ratingsContainer.FadeTo(0.25f, transition_duration); ratingsContainer.FadeTo(0.25f, transition_duration);
} }
if (hasRetriesFails) if (hasRetriesFails)
{ {
failRetryGraph.Metrics = metrics; failRetryGraph.Metrics = beatmap.Metrics;
failRetryContainer.FadeIn(transition_duration); failRetryContainer.FadeIn(transition_duration);
} }
else else

View File

@ -20,9 +20,9 @@ namespace osu.Game.Screens.Select.Details
private readonly Container graphContainer; private readonly Container graphContainer;
private readonly BarGraph graph; private readonly BarGraph graph;
private BeatmapMetrics metrics; private BeatmapSetMetrics metrics;
public BeatmapMetrics Metrics public BeatmapSetMetrics Metrics
{ {
get => metrics; get => metrics;
set set

View File

@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.4" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.609.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.609.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.611.1" /> <PackageReference Include="ppy.osu.Framework" Version="2019.615.0" />
<PackageReference Include="SharpCompress" Version="0.23.0" /> <PackageReference Include="SharpCompress" Version="0.23.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />

View File

@ -105,8 +105,8 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.609.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.609.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.611.1" /> <PackageReference Include="ppy.osu.Framework" Version="2019.615.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.611.1" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2019.615.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />