diff --git a/osu.Android.props b/osu.Android.props
index 3cd4dc48bf..252570a150 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -54,6 +54,6 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index 18785d65ea..f67ca1213e 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -2,23 +2,21 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.Scoring
{
- public class CatchScoreProcessor : ScoreProcessor
+ public class CatchScoreProcessor : ScoreProcessor
{
- public CatchScoreProcessor(DrawableRuleset drawableRuleset)
- : base(drawableRuleset)
+ public CatchScoreProcessor(IBeatmap beatmap)
+ : base(beatmap)
{
}
private float hpDrainRate;
- protected override void ApplyBeatmap(Beatmap beatmap)
+ protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
index 6b7f00c5d0..f5bddeb2cb 100644
--- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.UI
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
}
- public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
+ public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(Beatmap);
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 49894a644c..a678ef60e7 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -3,13 +3,11 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Scoring
{
- internal class ManiaScoreProcessor : ScoreProcessor
+ internal class ManiaScoreProcessor : ScoreProcessor
{
///
/// The hit HP multiplier at OD = 0.
@@ -51,12 +49,12 @@ namespace osu.Game.Rulesets.Mania.Scoring
///
private double hpMultiplier = 1;
- public ManiaScoreProcessor(DrawableRuleset drawableRuleset)
- : base(drawableRuleset)
+ public ManiaScoreProcessor(IBeatmap beatmap)
+ : base(beatmap)
{
}
- protected override void ApplyBeatmap(Beatmap beatmap)
+ protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
@@ -65,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
}
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void SimulateAutoplay(IBeatmap beatmap)
{
while (true)
{
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
index d371c1f7a8..0607bf0abd 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
@@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages);
- public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
+ public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(Beatmap);
public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns;
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index affe18a30d..6779271cb3 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -5,22 +5,20 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
-using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu.Scoring
{
- internal class OsuScoreProcessor : ScoreProcessor
+ internal class OsuScoreProcessor : ScoreProcessor
{
- public OsuScoreProcessor(DrawableRuleset drawableRuleset)
- : base(drawableRuleset)
+ public OsuScoreProcessor(IBeatmap beatmap)
+ : base(beatmap)
{
}
private float hpDrainRate;
- protected override void ApplyBeatmap(Beatmap beatmap)
+ protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
index 49aea52902..5bb728a9b0 100644
--- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.UI
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor
- public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this);
+ public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(Beatmap);
protected override Playfield CreatePlayfield() => new OsuPlayfield();
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index 75a27ff639..ae593d2e3a 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -1,15 +1,15 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Taiko.Scoring
{
- internal class TaikoScoreProcessor : ScoreProcessor
+ internal class TaikoScoreProcessor : ScoreProcessor
{
///
/// A value used for calculating .
@@ -31,16 +31,16 @@ namespace osu.Game.Rulesets.Taiko.Scoring
///
private double hpMissMultiplier;
- public TaikoScoreProcessor(DrawableRuleset drawableRuleset)
- : base(drawableRuleset)
+ public TaikoScoreProcessor(IBeatmap beatmap)
+ : base(beatmap)
{
}
- protected override void ApplyBeatmap(Beatmap beatmap)
+ protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
- hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
+ hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.OfType().Count() * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
}
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
index fc109bf6a6..d4ea9a043a 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.UI
new BarLineGenerator(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
}
- public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
+ public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(Beatmap);
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer();
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
index c0da605cdb..e708934bc3 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
@@ -68,9 +68,7 @@ namespace osu.Game.Tests.Visual.Online
};
AddStep("Set country", () => countryBindable.Value = country);
- AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
- AddAssert("Check country is Null", () => countryBindable.Value == null);
AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry);
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs
index 849ca2defc..0edf104da0 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs
@@ -43,11 +43,6 @@ namespace osu.Game.Tests.Visual.Online
FullName = "United States"
};
- AddStep("Set country", () => countryBindable.Value = countryA);
- AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
- AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
- AddAssert("Check country is Null", () => countryBindable.Value == null);
-
AddStep("Set country 1", () => countryBindable.Value = countryA);
AddStep("Set country 2", () => countryBindable.Value = countryB);
AddStep("Set null country", () => countryBindable.Value = null);
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs
new file mode 100644
index 0000000000..568e36df4c
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs
@@ -0,0 +1,86 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Game.Overlays.Rankings.Tables;
+using osu.Framework.Allocation;
+using osu.Game.Overlays;
+using NUnit.Framework;
+using osu.Game.Users;
+using osu.Framework.Bindables;
+using osu.Game.Overlays.Rankings;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsOverlay : OsuTestScene
+ {
+ protected override bool UseOnlineAPI => true;
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(PerformanceTable),
+ typeof(ScoresTable),
+ typeof(CountriesTable),
+ typeof(TableRowBackground),
+ typeof(UserBasedTable),
+ typeof(RankingsTable<>),
+ typeof(RankingsOverlay)
+ };
+
+ [Cached]
+ private RankingsOverlay rankingsOverlay;
+
+ private readonly Bindable countryBindable = new Bindable();
+ private readonly Bindable scope = new Bindable();
+
+ public TestSceneRankingsOverlay()
+ {
+ Add(rankingsOverlay = new TestRankingsOverlay
+ {
+ Country = { BindTarget = countryBindable },
+ Scope = { BindTarget = scope },
+ });
+ }
+
+ [Test]
+ public void TestShow()
+ {
+ AddStep("Show", rankingsOverlay.Show);
+ }
+
+ [Test]
+ public void TestFlagScopeDependency()
+ {
+ AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
+ AddAssert("Check country is Null", () => countryBindable.Value == null);
+ AddStep("Set country", () => countryBindable.Value = us_country);
+ AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
+ }
+
+ [Test]
+ public void TestShowCountry()
+ {
+ AddStep("Show US", () => rankingsOverlay.ShowCountry(us_country));
+ }
+
+ [Test]
+ public void TestHide()
+ {
+ AddStep("Hide", rankingsOverlay.Hide);
+ }
+
+ private static readonly Country us_country = new Country
+ {
+ FlagName = "US",
+ FullName = "United States"
+ };
+
+ private class TestRankingsOverlay : RankingsOverlay
+ {
+ public new Bindable Country => base.Country;
+
+ public new Bindable Scope => base.Scope;
+ }
+ }
+}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 838b1c2f07..f8275ec4f6 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using osu.Framework.IO.File;
+using osu.Framework.Extensions;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
@@ -112,7 +112,7 @@ namespace osu.Game.Beatmaps.Formats
switch (pair.Key)
{
case @"AudioFilename":
- metadata.AudioFile = FileSafety.PathStandardise(pair.Value);
+ metadata.AudioFile = pair.Value.ToStandardisedPath();
break;
case @"AudioLeadIn":
@@ -300,12 +300,12 @@ namespace osu.Game.Beatmaps.Formats
{
case EventType.Background:
string bgFilename = split[2].Trim('"');
- beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(bgFilename);
+ beatmap.BeatmapInfo.Metadata.BackgroundFile = bgFilename.ToStandardisedPath();
break;
case EventType.Video:
string videoFilename = split[2].Trim('"');
- beatmap.BeatmapInfo.Metadata.VideoFile = FileSafety.PathStandardise(videoFilename);
+ beatmap.BeatmapInfo.Metadata.VideoFile = videoFilename.ToStandardisedPath();
break;
case EventType.Break:
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index f94ab3f27b..ccd46ab559 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -8,8 +8,8 @@ using System.IO;
using System.Linq;
using osuTK;
using osuTK.Graphics;
+using osu.Framework.Extensions;
using osu.Framework.Graphics;
-using osu.Framework.IO.File;
using osu.Game.IO;
using osu.Game.Storyboards;
@@ -335,6 +335,6 @@ namespace osu.Game.Beatmaps.Formats
}
}
- private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('"'));
+ private string cleanFilename(string path) => path.Trim('"').ToStandardisedPath();
}
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 44d6d33cef..4452d26fcd 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -7,7 +7,6 @@ using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
using osu.Game.Storyboards;
-using osu.Framework.IO.File;
using System.IO;
using System.Linq;
using System.Threading;
@@ -83,7 +82,10 @@ namespace osu.Game.Beatmaps
/// The absolute path of the output file.
public string Save()
{
- var path = FileSafety.GetTempPath(Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json");
+ string directory = Path.Combine(Path.GetTempPath(), @"osu!");
+ Directory.CreateDirectory(directory);
+
+ var path = Path.Combine(directory, Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json");
using (var sw = new StreamWriter(path))
sw.WriteLine(Beatmap.Serialize());
return path;
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 7cce2fb92f..fd455d7cd5 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -13,7 +13,6 @@ using Microsoft.EntityFrameworkCore;
using osu.Framework;
using osu.Framework.Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
-using osu.Framework.IO.File;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Threading;
@@ -493,7 +492,7 @@ namespace osu.Game.Database
{
fileInfos.Add(new TFileModel
{
- Filename = FileSafety.PathStandardise(file.Substring(prefix.Length)),
+ Filename = file.Substring(prefix.Length).ToStandardisedPath(),
FileInfo = files.Add(s)
});
}
diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs
index bb6bef1c50..1ed5fb3268 100644
--- a/osu.Game/Database/DatabaseContextFactory.cs
+++ b/osu.Game/Database/DatabaseContextFactory.cs
@@ -149,7 +149,15 @@ namespace osu.Game.Database
lock (writeLock)
{
recycleThreadContexts();
- storage.DeleteDatabase(database_name);
+
+ try
+ {
+ storage.DeleteDatabase(database_name);
+ }
+ catch
+ {
+ // for now we are not sure why file handles are kept open by EF, but this is generally only used in testing
+ }
}
}
}
diff --git a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
index 9c3eba9fdc..143d21e40d 100644
--- a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
@@ -8,13 +8,14 @@ namespace osu.Game.Online.API.Requests
{
public class GetUserRankingsRequest : GetRankingsRequest
{
+ public readonly UserRankingsType Type;
+
private readonly string country;
- private readonly UserRankingsType type;
public GetUserRankingsRequest(RulesetInfo ruleset, UserRankingsType type = UserRankingsType.Performance, int page = 1, string country = null)
: base(ruleset, page)
{
- this.type = type;
+ Type = type;
this.country = country;
}
@@ -28,7 +29,7 @@ namespace osu.Game.Online.API.Requests
return req;
}
- protected override string TargetPostfix() => type.ToString().ToLowerInvariant();
+ protected override string TargetPostfix() => Type.ToString().ToLowerInvariant();
}
public enum UserRankingsType
diff --git a/osu.Game/Overlays/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs
index a1a893fa6b..b08a2a3900 100644
--- a/osu.Game/Overlays/Rankings/HeaderTitle.cs
+++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs
@@ -74,13 +74,7 @@ namespace osu.Game.Overlays.Rankings
base.LoadComplete();
}
- private void onScopeChanged(ValueChangedEvent scope)
- {
- scopeText.Text = scope.NewValue.ToString();
-
- if (scope.NewValue != RankingsScope.Performance)
- Country.Value = null;
- }
+ private void onScopeChanged(ValueChangedEvent scope) => scopeText.Text = scope.NewValue.ToString();
private void onCountryChanged(ValueChangedEvent country)
{
@@ -90,8 +84,6 @@ namespace osu.Game.Overlays.Rankings
return;
}
- Scope.Value = RankingsScope.Performance;
-
flag.Country = country.NewValue;
flag.Show();
}
diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs
new file mode 100644
index 0000000000..c8874ef891
--- /dev/null
+++ b/osu.Game/Overlays/RankingsOverlay.cs
@@ -0,0 +1,214 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Rankings;
+using osu.Game.Users;
+using osu.Game.Rulesets;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
+using System.Threading;
+using osu.Game.Online.API.Requests;
+using osu.Game.Overlays.Rankings.Tables;
+
+namespace osu.Game.Overlays
+{
+ public class RankingsOverlay : FullscreenOverlay
+ {
+ protected readonly Bindable Country = new Bindable();
+ protected readonly Bindable Scope = new Bindable();
+ private readonly Bindable ruleset = new Bindable();
+
+ private readonly BasicScrollContainer scrollFlow;
+ private readonly Box background;
+ private readonly Container tableContainer;
+ private readonly DimmedLoadingLayer loading;
+
+ private APIRequest lastRequest;
+ private CancellationTokenSource cancellationToken;
+
+ [Resolved]
+ private IAPIProvider api { get; set; }
+
+ public RankingsOverlay()
+ {
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ scrollFlow = new BasicScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ ScrollbarVisible = false,
+ Child = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new RankingsHeader
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Country = { BindTarget = Country },
+ Scope = { BindTarget = Scope },
+ Ruleset = { BindTarget = ruleset }
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ tableContainer = new Container
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Margin = new MarginPadding { Vertical = 10 }
+ },
+ loading = new DimmedLoadingLayer(),
+ }
+ }
+ }
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colour)
+ {
+ Waves.FirstWaveColour = colour.Green;
+ Waves.SecondWaveColour = colour.GreenLight;
+ Waves.ThirdWaveColour = colour.GreenDark;
+ Waves.FourthWaveColour = colour.GreenDarker;
+
+ background.Colour = OsuColour.Gray(0.1f);
+ }
+
+ protected override void LoadComplete()
+ {
+ Country.BindValueChanged(_ =>
+ {
+ // if a country is requested, force performance scope.
+ if (Country.Value != null)
+ Scope.Value = RankingsScope.Performance;
+
+ Scheduler.AddOnce(loadNewContent);
+ }, true);
+
+ Scope.BindValueChanged(_ =>
+ {
+ // country filtering is only valid for performance scope.
+ if (Scope.Value != RankingsScope.Performance)
+ Country.Value = null;
+
+ Scheduler.AddOnce(loadNewContent);
+ }, true);
+
+ ruleset.BindValueChanged(_ => Scheduler.AddOnce(loadNewContent), true);
+
+ base.LoadComplete();
+ }
+
+ public void ShowCountry(Country requested)
+ {
+ if (requested == null)
+ return;
+
+ Show();
+
+ Country.Value = requested;
+ }
+
+ private void loadNewContent()
+ {
+ loading.Show();
+
+ cancellationToken?.Cancel();
+ lastRequest?.Cancel();
+
+ var request = createScopedRequest();
+ lastRequest = request;
+
+ if (request == null)
+ {
+ loadTable(null);
+ return;
+ }
+
+ request.Success += () => loadTable(createTableFromResponse(request));
+ request.Failure += _ => loadTable(null);
+
+ api.Queue(request);
+ }
+
+ private APIRequest createScopedRequest()
+ {
+ switch (Scope.Value)
+ {
+ case RankingsScope.Performance:
+ return new GetUserRankingsRequest(ruleset.Value, country: Country.Value?.FlagName);
+
+ case RankingsScope.Country:
+ return new GetCountryRankingsRequest(ruleset.Value);
+
+ case RankingsScope.Score:
+ return new GetUserRankingsRequest(ruleset.Value, UserRankingsType.Score);
+ }
+
+ return null;
+ }
+
+ private Drawable createTableFromResponse(APIRequest request)
+ {
+ switch (request)
+ {
+ case GetUserRankingsRequest userRequest:
+ switch (userRequest.Type)
+ {
+ case UserRankingsType.Performance:
+ return new PerformanceTable(1, userRequest.Result.Users);
+
+ case UserRankingsType.Score:
+ return new ScoresTable(1, userRequest.Result.Users);
+ }
+
+ return null;
+
+ case GetCountryRankingsRequest countryRequest:
+ return new CountriesTable(1, countryRequest.Result.Countries);
+ }
+
+ return null;
+ }
+
+ private void loadTable(Drawable table)
+ {
+ scrollFlow.ScrollToStart();
+
+ if (table == null)
+ {
+ tableContainer.Clear();
+ loading.Hide();
+ return;
+ }
+
+ LoadComponentAsync(table, t =>
+ {
+ loading.Hide();
+ tableContainer.Child = table;
+ }, (cancellationToken = new CancellationTokenSource()).Token);
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 18c2a2ca01..a8a2294498 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -13,13 +13,16 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Scoring
{
- public abstract class ScoreProcessor
+ public class ScoreProcessor
{
+ private const double base_portion = 0.3;
+ private const double combo_portion = 0.7;
+ private const double max_score = 1000000;
+
///
/// Invoked when the is in a failed state.
/// This may occur regardless of whether an event is invoked.
@@ -67,11 +70,6 @@ namespace osu.Game.Rulesets.Scoring
///
public readonly Bindable> Mods = new Bindable>(Array.Empty());
- ///
- /// Create a for this processor.
- ///
- public virtual HitWindows CreateHitWindows() => new HitWindows();
-
///
/// The current rank.
///
@@ -90,132 +88,23 @@ namespace osu.Game.Rulesets.Scoring
///
/// Whether all s have been processed.
///
- public virtual bool HasCompleted => false;
-
- ///
- /// The total number of judged s at the current point in time.
- ///
- public int JudgedHits { get; protected set; }
+ public bool HasCompleted => JudgedHits == MaxHits;
///
/// Whether this ScoreProcessor has already triggered the failed state.
///
- public virtual bool HasFailed { get; private set; }
+ public bool HasFailed { get; private set; }
///
- /// The default conditions for failing.
+ /// The maximum number of hits that can be judged.
///
- protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value);
-
- protected ScoreProcessor()
- {
- Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); };
- Accuracy.ValueChanged += delegate
- {
- Rank.Value = rankFrom(Accuracy.Value);
- foreach (var mod in Mods.Value.OfType())
- Rank.Value = mod.AdjustRank(Rank.Value, Accuracy.Value);
- };
- }
-
- private ScoreRank rankFrom(double acc)
- {
- if (acc == 1)
- return ScoreRank.X;
- if (acc > 0.95)
- return ScoreRank.S;
- if (acc > 0.9)
- return ScoreRank.A;
- if (acc > 0.8)
- return ScoreRank.B;
- if (acc > 0.7)
- return ScoreRank.C;
-
- return ScoreRank.D;
- }
-
- ///
- /// Resets this ScoreProcessor to a default state.
- ///
- /// Whether to store the current state of the for future use.
- protected virtual void Reset(bool storeResults)
- {
- TotalScore.Value = 0;
- Accuracy.Value = 1;
- Health.Value = 1;
- Combo.Value = 0;
- Rank.Value = ScoreRank.X;
- HighestCombo.Value = 0;
-
- JudgedHits = 0;
-
- HasFailed = false;
- }
-
- ///
- /// Checks if the score is in a failed state and notifies subscribers.
- ///
- /// This can only ever notify subscribers once.
- ///
- ///
- protected void UpdateFailed(JudgementResult result)
- {
- if (HasFailed)
- return;
-
- if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true)
- return;
-
- if (Failed?.Invoke() != false)
- HasFailed = true;
- }
-
- ///
- /// Notifies subscribers of that a new judgement has occurred.
- ///
- /// The judgement scoring result to notify subscribers of.
- protected void NotifyNewJudgement(JudgementResult result)
- {
- NewJudgement?.Invoke(result);
-
- if (HasCompleted)
- AllJudged?.Invoke();
- }
-
- ///
- /// Retrieve a score populated with data for the current play this processor is responsible for.
- ///
- public virtual void PopulateScore(ScoreInfo score)
- {
- score.TotalScore = (long)Math.Round(TotalScore.Value);
- score.Combo = Combo.Value;
- score.MaxCombo = HighestCombo.Value;
- score.Accuracy = Math.Round(Accuracy.Value, 4);
- score.Rank = Rank.Value;
- score.Date = DateTimeOffset.Now;
-
- var hitWindows = CreateHitWindows();
-
- foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
- score.Statistics[result] = GetStatistic(result);
- }
-
- public abstract int GetStatistic(HitResult result);
-
- public abstract double GetStandardisedScore();
- }
-
- public class ScoreProcessor : ScoreProcessor
- where TObject : HitObject
- {
- private const double base_portion = 0.3;
- private const double combo_portion = 0.7;
- private const double max_score = 1000000;
-
- public sealed override bool HasCompleted => JudgedHits == MaxHits;
-
protected int MaxHits { get; private set; }
+ ///
+ /// The total number of judged s at the current point in time.
+ ///
+ public int JudgedHits { get; private set; }
+
private double maxHighestCombo;
private double maxBaseScore;
@@ -225,17 +114,22 @@ namespace osu.Game.Rulesets.Scoring
private double scoreMultiplier = 1;
- public ScoreProcessor(DrawableRuleset drawableRuleset)
+ public ScoreProcessor(IBeatmap beatmap)
{
Debug.Assert(base_portion + combo_portion == 1.0);
- drawableRuleset.OnNewResult += applyResult;
- drawableRuleset.OnRevertResult += revertResult;
+ Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue);
+ Accuracy.ValueChanged += accuracy =>
+ {
+ Rank.Value = rankFrom(accuracy.NewValue);
+ foreach (var mod in Mods.Value.OfType())
+ Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue);
+ };
- ApplyBeatmap(drawableRuleset.Beatmap);
+ ApplyBeatmap(beatmap);
Reset(false);
- SimulateAutoplay(drawableRuleset.Beatmap);
+ SimulateAutoplay(beatmap);
Reset(true);
if (maxBaseScore == 0 || maxHighestCombo == 0)
@@ -257,19 +151,19 @@ namespace osu.Game.Rulesets.Scoring
}
///
- /// Applies any properties of the which affect scoring to this .
+ /// Applies any properties of the which affect scoring to this .
///
- /// The to read properties from.
- protected virtual void ApplyBeatmap(Beatmap beatmap)
+ /// The to read properties from.
+ protected virtual void ApplyBeatmap(IBeatmap beatmap)
{
}
///
- /// Simulates an autoplay of the to determine scoring values.
+ /// Simulates an autoplay of the to determine scoring values.
///
/// This provided temporarily. DO NOT USE.
- /// The to simulate.
- protected virtual void SimulateAutoplay(Beatmap beatmap)
+ /// The to simulate.
+ protected virtual void SimulateAutoplay(IBeatmap beatmap)
{
foreach (var obj in beatmap.HitObjects)
simulate(obj);
@@ -289,7 +183,7 @@ namespace osu.Game.Rulesets.Scoring
result.Type = judgement.MaxResult;
- applyResult(result);
+ ApplyResult(result);
}
}
@@ -297,22 +191,26 @@ namespace osu.Game.Rulesets.Scoring
/// Applies the score change of a to this .
///
/// The to apply.
- private void applyResult(JudgementResult result)
+ public void ApplyResult(JudgementResult result)
{
- ApplyResult(result);
- updateScore();
+ ApplyResultInternal(result);
- UpdateFailed(result);
- NotifyNewJudgement(result);
+ updateScore();
+ updateFailed(result);
+
+ NewJudgement?.Invoke(result);
+
+ if (HasCompleted)
+ AllJudged?.Invoke();
}
///
/// Reverts the score change of a that was applied to this .
///
/// The judgement scoring result.
- private void revertResult(JudgementResult result)
+ public void RevertResult(JudgementResult result)
{
- RevertResult(result);
+ RevertResultInternal(result);
updateScore();
}
@@ -322,10 +220,10 @@ namespace osu.Game.Rulesets.Scoring
/// Applies the score change of a to this .
///
///
- /// Any changes applied via this method can be reverted via .
+ /// Any changes applied via this method can be reverted via .
///
/// The to apply.
- protected virtual void ApplyResult(JudgementResult result)
+ protected virtual void ApplyResultInternal(JudgementResult result)
{
result.ComboAtJudgement = Combo.Value;
result.HighestComboAtJudgement = HighestCombo.Value;
@@ -372,10 +270,10 @@ namespace osu.Game.Rulesets.Scoring
}
///
- /// Reverts the score change of a that was applied to this via .
+ /// Reverts the score change of a that was applied to this via .
///
/// The judgement scoring result.
- protected virtual void RevertResult(JudgementResult result)
+ protected virtual void RevertResultInternal(JudgementResult result)
{
Combo.Value = result.ComboAtJudgement;
HighestCombo.Value = result.HighestComboAtJudgement;
@@ -432,11 +330,49 @@ namespace osu.Game.Rulesets.Scoring
}
}
- public override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result);
+ ///
+ /// Checks if the score is in a failed state and notifies subscribers.
+ ///
+ /// This can only ever notify subscribers once.
+ ///
+ ///
+ private void updateFailed(JudgementResult result)
+ {
+ if (HasFailed)
+ return;
- public override double GetStandardisedScore() => getScore(ScoringMode.Standardised);
+ if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true)
+ return;
- protected override void Reset(bool storeResults)
+ if (Failed?.Invoke() != false)
+ HasFailed = true;
+ }
+
+ private ScoreRank rankFrom(double acc)
+ {
+ if (acc == 1)
+ return ScoreRank.X;
+ if (acc > 0.95)
+ return ScoreRank.S;
+ if (acc > 0.9)
+ return ScoreRank.A;
+ if (acc > 0.8)
+ return ScoreRank.B;
+ if (acc > 0.7)
+ return ScoreRank.C;
+
+ return ScoreRank.D;
+ }
+
+ public int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result);
+
+ public double GetStandardisedScore() => getScore(ScoringMode.Standardised);
+
+ ///
+ /// Resets this ScoreProcessor to a default state.
+ ///
+ /// Whether to store the current state of the for future use.
+ protected virtual void Reset(bool storeResults)
{
scoreResultCounts.Clear();
@@ -447,13 +383,49 @@ namespace osu.Game.Rulesets.Scoring
maxBaseScore = baseScore;
}
- base.Reset(storeResults);
-
+ JudgedHits = 0;
baseScore = 0;
rollingMaxBaseScore = 0;
bonusScore = 0;
+
+ TotalScore.Value = 0;
+ Accuracy.Value = 1;
+ Health.Value = 1;
+ Combo.Value = 0;
+ Rank.Value = ScoreRank.X;
+ HighestCombo.Value = 0;
+
+ HasFailed = false;
}
+ ///
+ /// Retrieve a score populated with data for the current play this processor is responsible for.
+ ///
+ public virtual void PopulateScore(ScoreInfo score)
+ {
+ score.TotalScore = (long)Math.Round(TotalScore.Value);
+ score.Combo = Combo.Value;
+ score.MaxCombo = HighestCombo.Value;
+ score.Accuracy = Math.Round(Accuracy.Value, 4);
+ score.Rank = Rank.Value;
+ score.Date = DateTimeOffset.Now;
+
+ var hitWindows = CreateHitWindows();
+
+ foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
+ score.Statistics[result] = GetStatistic(result);
+ }
+
+ ///
+ /// The default conditions for failing.
+ ///
+ protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value);
+
+ ///
+ /// Create a for this processor.
+ ///
+ public virtual HitWindows CreateHitWindows() => new HitWindows();
+
///
/// Creates the that represents the scoring result for a .
///
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index a856974292..10657f6b39 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -45,6 +45,10 @@ namespace osu.Game.Rulesets.UI
public abstract class DrawableRuleset : DrawableRuleset, IProvideCursor, ICanAttachKeyCounter
where TObject : HitObject
{
+ public override event Action OnNewResult;
+
+ public override event Action OnRevertResult;
+
///
/// The selected variant.
///
@@ -91,16 +95,6 @@ namespace osu.Game.Rulesets.UI
}
}
- ///
- /// Invoked when a has been applied by a .
- ///
- public event Action OnNewResult;
-
- ///
- /// Invoked when a is being reverted by a .
- ///
- public event Action OnRevertResult;
-
///
/// The beatmap.
///
@@ -309,7 +303,7 @@ namespace osu.Game.Rulesets.UI
/// The Playfield.
protected abstract Playfield CreatePlayfield();
- public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this);
+ public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(Beatmap);
///
/// Applies the active mods to this DrawableRuleset.
@@ -366,6 +360,16 @@ namespace osu.Game.Rulesets.UI
///
public abstract class DrawableRuleset : CompositeDrawable
{
+ ///
+ /// Invoked when a has been applied by a .
+ ///
+ public abstract event Action OnNewResult;
+
+ ///
+ /// Invoked when a is being reverted by a .
+ ///
+ public abstract event Action OnRevertResult;
+
///
/// Whether a replay is currently loaded.
///
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 33a4c48d28..1b4964c068 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -4,7 +4,6 @@
using System;
using osuTK.Graphics;
using osu.Framework.Screens;
-using osu.Game.Screens.Backgrounds;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -29,14 +28,13 @@ using osu.Game.Input.Bindings;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Edit.Timing;
+using osu.Game.Screens.Play;
using osu.Game.Users;
namespace osu.Game.Screens.Edit
{
- public class Editor : OsuScreen, IKeyBindingHandler
+ public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler
{
- protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
-
public override float BackgroundParallaxAmount => 0.1f;
public override bool AllowBackButton => false;
@@ -250,8 +248,12 @@ namespace osu.Game.Screens.Edit
{
base.OnEntering(last);
+ // todo: temporary. we want to be applying dim using the UserDimContainer eventually.
Background.FadeColour(Color4.DarkGray, 500);
+ Background.EnableUserDim.Value = false;
+ Background.BlurAmount.Value = 0;
+
resetTrack(true);
}
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 9feee82989..9f4ca7d817 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -140,6 +140,9 @@ namespace osu.Game.Screens.Play
// bind clock into components that require it
DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);
+ DrawableRuleset.OnNewResult += ScoreProcessor.ApplyResult;
+ DrawableRuleset.OnRevertResult += ScoreProcessor.RevertResult;
+
// Bind ScoreProcessor to ourselves
ScoreProcessor.AllJudged += onCompletion;
ScoreProcessor.Failed += onFail;
diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs
index 1dcce6e870..a9fcd69286 100644
--- a/osu.Game/Users/Country.cs
+++ b/osu.Game/Users/Country.cs
@@ -1,11 +1,12 @@
// Copyright (c) ppy Pty Ltd . 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.Users
{
- public class Country
+ public class Country : IEquatable
{
///
/// The name of this country.
@@ -18,5 +19,7 @@ namespace osu.Game.Users
///
[JsonProperty(@"code")]
public string FlagName;
+
+ public bool Equals(Country other) => FlagName == other?.FlagName;
}
}
diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs
index abc16b2390..1d30720889 100644
--- a/osu.Game/Users/Drawables/UpdateableFlag.cs
+++ b/osu.Game/Users/Drawables/UpdateableFlag.cs
@@ -1,8 +1,11 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// 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.Input.Events;
+using osu.Game.Overlays;
namespace osu.Game.Users.Drawables
{
@@ -34,5 +37,14 @@ namespace osu.Game.Users.Drawables
RelativeSizeAxes = Axes.Both,
};
}
+
+ [Resolved(canBeNull: true)]
+ private RankingsOverlay rankingsOverlay { get; set; }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ rankingsOverlay?.ShowCountry(Country);
+ return true;
+ }
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 530d62f583..a07348b57c 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -23,7 +23,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index fb753b8c6f..544bba3963 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -74,7 +74,7 @@
-
+
@@ -82,7 +82,7 @@
-
+