mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 12:57:36 +08:00
Merge branch 'master' into fix-pofl-on-cooldown
This commit is contained in:
commit
6bac83964a
@ -52,6 +52,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.215.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.222.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -17,7 +17,7 @@ using osu.Game.Database;
|
|||||||
|
|
||||||
namespace osu.Android
|
namespace osu.Android
|
||||||
{
|
{
|
||||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance)]
|
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
||||||
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed" })]
|
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed" })]
|
||||||
|
@ -105,7 +105,7 @@ namespace osu.Desktop
|
|||||||
if (privacyMode.Value == DiscordRichPresenceMode.Limited)
|
if (privacyMode.Value == DiscordRichPresenceMode.Limited)
|
||||||
presence.Assets.LargeImageText = string.Empty;
|
presence.Assets.LargeImageText = string.Empty;
|
||||||
else
|
else
|
||||||
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty);
|
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.GlobalRank > 0 ? $" (rank #{user.Value.Statistics.GlobalRank:N0})" : string.Empty);
|
||||||
|
|
||||||
// update ruleset
|
// update ruleset
|
||||||
presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom";
|
presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom";
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Difficulty;
|
using osu.Game.Rulesets.Catch.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Catch.Mods;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
@ -17,6 +18,10 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
|
||||||
|
[TestCase(5.0565038923984691d, "diffcalc-test")]
|
||||||
|
public void TestClockRateAdjusted(double expected, string name)
|
||||||
|
=> Test(expected, name, new CatchModDoubleTime());
|
||||||
|
|
||||||
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset(), beatmap);
|
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset(), beatmap);
|
||||||
|
|
||||||
protected override Ruleset CreateRuleset() => new CatchRuleset();
|
protected override Ruleset CreateRuleset() => new CatchRuleset();
|
||||||
|
@ -5,6 +5,7 @@ using NUnit.Framework;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mania.Difficulty;
|
using osu.Game.Rulesets.Mania.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
@ -17,6 +18,10 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
|
||||||
|
[TestCase(2.7646128945056723d, "diffcalc-test")]
|
||||||
|
public void TestClockRateAdjusted(double expected, string name)
|
||||||
|
=> Test(expected, name, new ManiaModDoubleTime());
|
||||||
|
|
||||||
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset(), beatmap);
|
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset(), beatmap);
|
||||||
|
|
||||||
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||||
|
@ -5,6 +5,7 @@ using NUnit.Framework;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Osu.Difficulty;
|
using osu.Game.Rulesets.Osu.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
@ -19,6 +20,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
|
||||||
|
[TestCase(8.6228371119393064d, "diffcalc-test")]
|
||||||
|
[TestCase(1.2864585434597433d, "zero-length-sliders")]
|
||||||
|
public void TestClockRateAdjusted(double expected, string name)
|
||||||
|
=> Test(expected, name, new OsuModDoubleTime());
|
||||||
|
|
||||||
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap);
|
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap);
|
||||||
|
|
||||||
protected override Ruleset CreateRuleset() => new OsuRuleset();
|
protected override Ruleset CreateRuleset() => new OsuRuleset();
|
||||||
|
@ -5,6 +5,7 @@ using NUnit.Framework;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty;
|
using osu.Game.Rulesets.Taiko.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Taiko.Mods;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
@ -18,6 +19,11 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
|
||||||
|
[TestCase(3.1473940254109078d, "diffcalc-test")]
|
||||||
|
[TestCase(3.1473940254109078d, "diffcalc-test-strong")]
|
||||||
|
public void TestClockRateAdjusted(double expected, string name)
|
||||||
|
=> Test(expected, name, new TaikoModDoubleTime());
|
||||||
|
|
||||||
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap);
|
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap);
|
||||||
|
|
||||||
protected override Ruleset CreateRuleset() => new TaikoRuleset();
|
protected override Ruleset CreateRuleset() => new TaikoRuleset();
|
||||||
|
@ -118,9 +118,13 @@ namespace osu.Game.Tests.Rulesets
|
|||||||
public BindableNumber<double> Frequency => throw new NotImplementedException();
|
public BindableNumber<double> Frequency => throw new NotImplementedException();
|
||||||
public BindableNumber<double> Tempo => throw new NotImplementedException();
|
public BindableNumber<double> Tempo => throw new NotImplementedException();
|
||||||
|
|
||||||
public void AddAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable) => throw new NotImplementedException();
|
public void BindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
|
||||||
|
|
||||||
public void RemoveAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable) => throw new NotImplementedException();
|
public void UnbindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public void AddAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public void RemoveAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => throw new NotImplementedException();
|
||||||
|
|
||||||
public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotImplementedException();
|
public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
@ -56,9 +56,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
resume();
|
resume();
|
||||||
|
|
||||||
confirmClockRunning(false);
|
confirmPausedWithNoOverlay();
|
||||||
confirmPauseOverlayShown(false);
|
|
||||||
|
|
||||||
AddStep("click to resume", () => InputManager.Click(MouseButton.Left));
|
AddStep("click to resume", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
confirmClockRunning(true);
|
confirmClockRunning(true);
|
||||||
@ -71,15 +69,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddUntilStep("wait for hitobjects", () => Player.HealthProcessor.Health.Value < 1);
|
AddUntilStep("wait for hitobjects", () => Player.HealthProcessor.Health.Value < 1);
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
|
|
||||||
resume();
|
resume();
|
||||||
confirmClockRunning(false);
|
|
||||||
confirmPauseOverlayShown(false);
|
|
||||||
|
|
||||||
|
confirmPausedWithNoOverlay();
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
|
|
||||||
AddUntilStep("resume overlay is not active", () => Player.DrawableRuleset.ResumeOverlay.State.Value == Visibility.Hidden);
|
AddUntilStep("resume overlay is not active", () => Player.DrawableRuleset.ResumeOverlay.State.Value == Visibility.Hidden);
|
||||||
confirmPaused();
|
confirmPaused();
|
||||||
|
confirmNotExited();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -94,33 +91,54 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestPauseTooSoon()
|
public void TestUserPauseWhenPauseNotAllowed()
|
||||||
|
{
|
||||||
|
AddStep("disable pause support", () => Player.Configuration.AllowPause = false);
|
||||||
|
|
||||||
|
pauseFromUserExitKey();
|
||||||
|
confirmExited();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUserPauseDuringCooldownTooSoon()
|
||||||
{
|
{
|
||||||
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
|
|
||||||
resume();
|
resume();
|
||||||
pause();
|
pauseFromUserExitKey();
|
||||||
|
|
||||||
confirmClockRunning(true);
|
confirmResumed();
|
||||||
confirmPauseOverlayShown(false);
|
confirmNotExited();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestExitTooSoon()
|
public void TestQuickExitDuringCooldownTooSoon()
|
||||||
|
{
|
||||||
|
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||||
|
|
||||||
|
pauseAndConfirm();
|
||||||
|
|
||||||
|
resume();
|
||||||
|
AddStep("pause via exit key", () => Player.ExitViaQuickExit());
|
||||||
|
|
||||||
|
confirmResumed();
|
||||||
|
AddAssert("exited", () => !Player.IsCurrentScreen());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestExitSoonAfterResumeSucceeds()
|
||||||
{
|
{
|
||||||
AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000));
|
AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000));
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
resume();
|
resume();
|
||||||
|
|
||||||
AddStep("exit too soon", () => Player.Exit());
|
AddStep("exit quick", () => Player.Exit());
|
||||||
|
|
||||||
confirmClockRunning(true);
|
confirmResumed();
|
||||||
confirmPauseOverlayShown(false);
|
AddAssert("exited", () => !Player.IsCurrentScreen());
|
||||||
|
|
||||||
AddAssert("not exited", () => Player.IsCurrentScreen());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -131,22 +149,37 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
confirmClockRunning(false);
|
confirmClockRunning(false);
|
||||||
|
|
||||||
pause();
|
AddStep("pause via forced pause", () => Player.Pause());
|
||||||
|
|
||||||
confirmClockRunning(false);
|
|
||||||
confirmPauseOverlayShown(false);
|
|
||||||
|
|
||||||
|
confirmPausedWithNoOverlay();
|
||||||
AddAssert("fail overlay still shown", () => Player.FailOverlayVisible);
|
AddAssert("fail overlay still shown", () => Player.FailOverlayVisible);
|
||||||
|
|
||||||
exitAndConfirm();
|
exitAndConfirm();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestExitFromFailedGameplay()
|
public void TestExitFromFailedGameplayAfterFailAnimation()
|
||||||
{
|
{
|
||||||
AddUntilStep("wait for fail", () => Player.HasFailed);
|
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||||
AddStep("exit", () => Player.Exit());
|
AddUntilStep("wait for fail overlay shown", () => Player.FailOverlayVisible);
|
||||||
|
|
||||||
|
confirmClockRunning(false);
|
||||||
|
|
||||||
|
AddStep("exit via user pause", () => Player.ExitViaPause());
|
||||||
|
confirmExited();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestExitFromFailedGameplayDuringFailAnimation()
|
||||||
|
{
|
||||||
|
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||||
|
|
||||||
|
// will finish the fail animation and show the fail/pause screen.
|
||||||
|
AddStep("attempt exit via pause key", () => Player.ExitViaPause());
|
||||||
|
AddAssert("fail overlay shown", () => Player.FailOverlayVisible);
|
||||||
|
|
||||||
|
// will actually exit.
|
||||||
|
AddStep("exit via pause key", () => Player.ExitViaPause());
|
||||||
confirmExited();
|
confirmExited();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +278,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private void pauseAndConfirm()
|
private void pauseAndConfirm()
|
||||||
{
|
{
|
||||||
pause();
|
pauseFromUserExitKey();
|
||||||
confirmPaused();
|
confirmPaused();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +290,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private void exitAndConfirm()
|
private void exitAndConfirm()
|
||||||
{
|
{
|
||||||
AddUntilStep("player not exited", () => Player.IsCurrentScreen());
|
confirmNotExited();
|
||||||
AddStep("exit", () => Player.Exit());
|
AddStep("exit", () => Player.Exit());
|
||||||
confirmExited();
|
confirmExited();
|
||||||
confirmNoTrackAdjustments();
|
confirmNoTrackAdjustments();
|
||||||
@ -266,7 +299,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
private void confirmPaused()
|
private void confirmPaused()
|
||||||
{
|
{
|
||||||
confirmClockRunning(false);
|
confirmClockRunning(false);
|
||||||
AddAssert("player not exited", () => Player.IsCurrentScreen());
|
confirmNotExited();
|
||||||
AddAssert("player not failed", () => !Player.HasFailed);
|
AddAssert("player not failed", () => !Player.HasFailed);
|
||||||
AddAssert("pause overlay shown", () => Player.PauseOverlayVisible);
|
AddAssert("pause overlay shown", () => Player.PauseOverlayVisible);
|
||||||
}
|
}
|
||||||
@ -277,18 +310,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
confirmPauseOverlayShown(false);
|
confirmPauseOverlayShown(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void confirmExited()
|
private void confirmPausedWithNoOverlay()
|
||||||
{
|
{
|
||||||
AddUntilStep("player exited", () => !Player.IsCurrentScreen());
|
confirmClockRunning(false);
|
||||||
|
confirmPauseOverlayShown(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void confirmExited() => AddUntilStep("player exited", () => !Player.IsCurrentScreen());
|
||||||
|
private void confirmNotExited() => AddAssert("player not exited", () => Player.IsCurrentScreen());
|
||||||
|
|
||||||
private void confirmNoTrackAdjustments()
|
private void confirmNoTrackAdjustments()
|
||||||
{
|
{
|
||||||
AddAssert("track has no adjustments", () => Beatmap.Value.Track.AggregateFrequency.Value == 1);
|
AddAssert("track has no adjustments", () => Beatmap.Value.Track.AggregateFrequency.Value == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restart() => AddStep("restart", () => Player.Restart());
|
private void restart() => AddStep("restart", () => Player.Restart());
|
||||||
private void pause() => AddStep("pause", () => Player.Pause());
|
private void pauseFromUserExitKey() => AddStep("user pause", () => Player.ExitViaPause());
|
||||||
private void resume() => AddStep("resume", () => Player.Resume());
|
private void resume() => AddStep("resume", () => Player.Resume());
|
||||||
|
|
||||||
private void confirmPauseOverlayShown(bool isShown) =>
|
private void confirmPauseOverlayShown(bool isShown) =>
|
||||||
@ -307,6 +344,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible;
|
public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible;
|
||||||
|
|
||||||
|
public void ExitViaPause() => PerformExit(true);
|
||||||
|
|
||||||
|
public void ExitViaQuickExit() => PerformExit(false);
|
||||||
|
|
||||||
public override void OnEntering(IScreen last)
|
public override void OnEntering(IScreen last)
|
||||||
{
|
{
|
||||||
base.OnEntering(last);
|
base.OnEntering(last);
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Testing;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Online;
|
using osu.Game.Online;
|
||||||
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.Spectator;
|
using osu.Game.Online.Spectator;
|
||||||
using osu.Game.Replays.Legacy;
|
using osu.Game.Replays.Legacy;
|
||||||
using osu.Game.Rulesets.Osu.Scoring;
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
@ -50,6 +51,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
{
|
{
|
||||||
|
AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = lookupCache.GetUserAsync(1).Result);
|
||||||
|
|
||||||
AddStep("create leaderboard", () =>
|
AddStep("create leaderboard", () =>
|
||||||
{
|
{
|
||||||
leaderboard?.Expire();
|
leaderboard?.Expire();
|
||||||
@ -85,6 +88,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
public void TestScoreUpdates()
|
public void TestScoreUpdates()
|
||||||
{
|
{
|
||||||
AddRepeatStep("update state", () => streamingClient.RandomlyUpdateState(), 100);
|
AddRepeatStep("update state", () => streamingClient.RandomlyUpdateState(), 100);
|
||||||
|
AddToggleStep("switch compact mode", expanded => leaderboard.Expanded.Value = expanded);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -155,7 +156,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
Id = i,
|
Id = i,
|
||||||
Username = $"User {i}",
|
Username = $"User {i}",
|
||||||
CurrentModeRank = RNG.Next(1, 100000),
|
RulesetsStatistics = new Dictionary<string, UserStatistics>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Ruleset.Value.ShortName,
|
||||||
|
new UserStatistics { GlobalRank = RNG.Next(1, 100000), }
|
||||||
|
}
|
||||||
|
},
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -193,7 +200,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
Id = 0,
|
Id = 0,
|
||||||
Username = "User 0",
|
Username = "User 0",
|
||||||
CurrentModeRank = RNG.Next(1, 100000),
|
RulesetsStatistics = new Dictionary<string, UserStatistics>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Ruleset.Value.ShortName,
|
||||||
|
new UserStatistics { GlobalRank = RNG.Next(1, 100000), }
|
||||||
|
}
|
||||||
|
},
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
@ -115,6 +116,8 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
|
public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
|
||||||
|
|
||||||
|
public new Bindable<IReadOnlyList<Mod>> SelectedMods => base.SelectedMods;
|
||||||
|
|
||||||
// if we don't do this, when running under nUnit the version that gets populated is that of nUnit.
|
// if we don't do this, when running under nUnit the version that gets populated is that of nUnit.
|
||||||
public override string Version => "test game";
|
public override string Version => "test game";
|
||||||
|
|
||||||
|
@ -11,7 +11,9 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Overlays.Toolbar;
|
using osu.Game.Overlays.Toolbar;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
using osu.Game.Screens.Select.Options;
|
using osu.Game.Screens.Select.Options;
|
||||||
using osu.Game.Tests.Beatmaps.IO;
|
using osu.Game.Tests.Beatmaps.IO;
|
||||||
@ -41,6 +43,30 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
exitViaEscapeAndConfirm();
|
exitViaEscapeAndConfirm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRetryFromResults()
|
||||||
|
{
|
||||||
|
Player player = null;
|
||||||
|
ResultsScreen results = null;
|
||||||
|
|
||||||
|
WorkingBeatmap beatmap() => Game.Beatmap.Value;
|
||||||
|
|
||||||
|
PushAndConfirm(() => new TestSongSelect());
|
||||||
|
|
||||||
|
AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait());
|
||||||
|
|
||||||
|
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||||
|
|
||||||
|
AddStep("set autoplay", () => Game.SelectedMods.Value = new[] { new OsuModAutoplay() });
|
||||||
|
|
||||||
|
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||||
|
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
|
||||||
|
AddStep("seek to end", () => beatmap().Track.Seek(beatmap().Track.Length));
|
||||||
|
AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded);
|
||||||
|
AddStep("attempt to retry", () => results.ChildrenOfType<HotkeyRetryOverlay>().First().Action());
|
||||||
|
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != player && Game.ScreenStack.CurrentScreen is Player);
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public void TestSongContinuesAfterExitPlayer(bool withUserPause)
|
public void TestSongContinuesAfterExitPlayer(bool withUserPause)
|
||||||
|
@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
graph.Statistics.Value = new UserStatistics
|
graph.Statistics.Value = new UserStatistics
|
||||||
{
|
{
|
||||||
Ranks = new UserStatistics.UserRanks { Global = 123456 },
|
GlobalRank = 123456,
|
||||||
PP = 12345,
|
PP = 12345,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
graph.Statistics.Value = new UserStatistics
|
graph.Statistics.Value = new UserStatistics
|
||||||
{
|
{
|
||||||
Ranks = new UserStatistics.UserRanks { Global = 89000 },
|
GlobalRank = 89000,
|
||||||
PP = 12345,
|
PP = 12345,
|
||||||
RankHistory = new User.RankHistoryData
|
RankHistory = new User.RankHistoryData
|
||||||
{
|
{
|
||||||
@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
graph.Statistics.Value = new UserStatistics
|
graph.Statistics.Value = new UserStatistics
|
||||||
{
|
{
|
||||||
Ranks = new UserStatistics.UserRanks { Global = 89000 },
|
GlobalRank = 89000,
|
||||||
PP = 12345,
|
PP = 12345,
|
||||||
RankHistory = new User.RankHistoryData
|
RankHistory = new User.RankHistoryData
|
||||||
{
|
{
|
||||||
@ -105,7 +105,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
graph.Statistics.Value = new UserStatistics
|
graph.Statistics.Value = new UserStatistics
|
||||||
{
|
{
|
||||||
Ranks = new UserStatistics.UserRanks { Global = 12000 },
|
GlobalRank = 12000,
|
||||||
PP = 12345,
|
PP = 12345,
|
||||||
RankHistory = new User.RankHistoryData
|
RankHistory = new User.RankHistoryData
|
||||||
{
|
{
|
||||||
@ -118,7 +118,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
graph.Statistics.Value = new UserStatistics
|
graph.Statistics.Value = new UserStatistics
|
||||||
{
|
{
|
||||||
Ranks = new UserStatistics.UserRanks { Global = 12000 },
|
GlobalRank = 12000,
|
||||||
PP = 12345,
|
PP = 12345,
|
||||||
RankHistory = new User.RankHistoryData
|
RankHistory = new User.RankHistoryData
|
||||||
{
|
{
|
||||||
|
@ -33,7 +33,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
ProfileOrder = new[] { "me" },
|
ProfileOrder = new[] { "me" },
|
||||||
Statistics = new UserStatistics
|
Statistics = new UserStatistics
|
||||||
{
|
{
|
||||||
Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 },
|
GlobalRank = 2148,
|
||||||
|
CountryRank = 1,
|
||||||
PP = 4567.89m,
|
PP = 4567.89m,
|
||||||
Level = new UserStatistics.LevelInfo
|
Level = new UserStatistics.LevelInfo
|
||||||
{
|
{
|
||||||
|
@ -113,11 +113,11 @@ namespace osu.Game.Tournament.Tests
|
|||||||
},
|
},
|
||||||
Players =
|
Players =
|
||||||
{
|
{
|
||||||
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 12 } } },
|
new User { Username = "Hello", Statistics = new UserStatistics { GlobalRank = 12 } },
|
||||||
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 16 } } },
|
new User { Username = "Hello", Statistics = new UserStatistics { GlobalRank = 16 } },
|
||||||
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 20 } } },
|
new User { Username = "Hello", Statistics = new UserStatistics { GlobalRank = 20 } },
|
||||||
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24 } } },
|
new User { Username = "Hello", Statistics = new UserStatistics { GlobalRank = 24 } },
|
||||||
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 30 } } },
|
new User { Username = "Hello", Statistics = new UserStatistics { GlobalRank = 30 } },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Tournament.Models
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var ranks = Players.Select(p => p.Statistics?.Ranks.Global)
|
var ranks = Players.Select(p => p.Statistics?.GlobalRank)
|
||||||
.Where(i => i.HasValue)
|
.Where(i => i.HasValue)
|
||||||
.Select(i => i.Value)
|
.Select(i => i.Value)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
@ -250,7 +250,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
|
|||||||
};
|
};
|
||||||
|
|
||||||
foreach (var p in team.Players)
|
foreach (var p in team.Players)
|
||||||
fill.Add(new RowDisplay(p.Username, p.Statistics?.Ranks.Global?.ToString("\\##,0") ?? "-"));
|
fill.Add(new RowDisplay(p.Username, p.Statistics?.GlobalRank?.ToString("\\##,0") ?? "-"));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class RowDisplay : CompositeDrawable
|
internal class RowDisplay : CompositeDrawable
|
||||||
|
@ -150,9 +150,9 @@ namespace osu.Game.Tournament
|
|||||||
{
|
{
|
||||||
foreach (var p in t.Players)
|
foreach (var p in t.Players)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(p.Username) || p.Statistics == null)
|
if (string.IsNullOrEmpty(p.Username) || p.Statistics?.GlobalRank == null)
|
||||||
{
|
{
|
||||||
PopulateUser(p);
|
PopulateUser(p, immediate: true);
|
||||||
addedInfo = true;
|
addedInfo = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,12 +211,14 @@ namespace osu.Game.Tournament
|
|||||||
return addedInfo;
|
return addedInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PopulateUser(User user, Action success = null, Action failure = null)
|
public void PopulateUser(User user, Action success = null, Action failure = null, bool immediate = false)
|
||||||
{
|
{
|
||||||
var req = new GetUserRequest(user.Id, Ruleset.Value);
|
var req = new GetUserRequest(user.Id, Ruleset.Value);
|
||||||
|
|
||||||
req.Success += res =>
|
req.Success += res =>
|
||||||
{
|
{
|
||||||
|
user.Id = res.Id;
|
||||||
|
|
||||||
user.Username = res.Username;
|
user.Username = res.Username;
|
||||||
user.Statistics = res.Statistics;
|
user.Statistics = res.Statistics;
|
||||||
user.Country = res.Country;
|
user.Country = res.Country;
|
||||||
@ -231,7 +233,10 @@ namespace osu.Game.Tournament
|
|||||||
failure?.Invoke();
|
failure?.Invoke();
|
||||||
};
|
};
|
||||||
|
|
||||||
API.Queue(req);
|
if (immediate)
|
||||||
|
API.Perform(req);
|
||||||
|
else
|
||||||
|
API.Queue(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -29,6 +29,14 @@ namespace osu.Game.Collections
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual bool ShowManageCollectionsItem => true;
|
protected virtual bool ShowManageCollectionsItem => true;
|
||||||
|
|
||||||
|
private readonly BindableWithCurrent<CollectionFilterMenuItem> current = new BindableWithCurrent<CollectionFilterMenuItem>();
|
||||||
|
|
||||||
|
public new Bindable<CollectionFilterMenuItem> Current
|
||||||
|
{
|
||||||
|
get => current.Current;
|
||||||
|
set => current.Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly IBindableList<BeatmapCollection> collections = new BindableList<BeatmapCollection>();
|
private readonly IBindableList<BeatmapCollection> collections = new BindableList<BeatmapCollection>();
|
||||||
private readonly IBindableList<BeatmapInfo> beatmaps = new BindableList<BeatmapInfo>();
|
private readonly IBindableList<BeatmapInfo> beatmaps = new BindableList<BeatmapInfo>();
|
||||||
private readonly BindableList<CollectionFilterMenuItem> filters = new BindableList<CollectionFilterMenuItem>();
|
private readonly BindableList<CollectionFilterMenuItem> filters = new BindableList<CollectionFilterMenuItem>();
|
||||||
@ -36,25 +44,28 @@ namespace osu.Game.Collections
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private ManageCollectionsDialog manageCollectionsDialog { get; set; }
|
private ManageCollectionsDialog manageCollectionsDialog { get; set; }
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private CollectionManager collectionManager { get; set; }
|
||||||
|
|
||||||
public CollectionFilterDropdown()
|
public CollectionFilterDropdown()
|
||||||
{
|
{
|
||||||
ItemSource = filters;
|
ItemSource = filters;
|
||||||
}
|
Current.Value = new AllBeatmapsCollectionFilterMenuItem();
|
||||||
|
|
||||||
[BackgroundDependencyLoader(permitNulls: true)]
|
|
||||||
private void load([CanBeNull] CollectionManager collectionManager)
|
|
||||||
{
|
|
||||||
if (collectionManager != null)
|
|
||||||
collections.BindTo(collectionManager.Collections);
|
|
||||||
|
|
||||||
collections.CollectionChanged += (_, __) => collectionsChanged();
|
|
||||||
collectionsChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
|
if (collectionManager != null)
|
||||||
|
collections.BindTo(collectionManager.Collections);
|
||||||
|
|
||||||
|
// Dropdown has logic which triggers a change on the bindable with every change to the contained items.
|
||||||
|
// This is not desirable here, as it leads to multiple filter operations running even though nothing has changed.
|
||||||
|
// An extra bindable is enough to subvert this behaviour.
|
||||||
|
base.Current = Current;
|
||||||
|
|
||||||
|
collections.BindCollectionChanged((_, __) => collectionsChanged(), true);
|
||||||
Current.BindValueChanged(filterChanged, true);
|
Current.BindValueChanged(filterChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
|
||||||
@ -9,7 +10,7 @@ namespace osu.Game.Collections
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A <see cref="BeatmapCollection"/> filter.
|
/// A <see cref="BeatmapCollection"/> filter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CollectionFilterMenuItem
|
public class CollectionFilterMenuItem : IEquatable<CollectionFilterMenuItem>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The collection to filter beatmaps from.
|
/// The collection to filter beatmaps from.
|
||||||
@ -33,6 +34,11 @@ namespace osu.Game.Collections
|
|||||||
Collection = collection;
|
Collection = collection;
|
||||||
CollectionName = Collection?.Name.GetBoundCopy() ?? new Bindable<string>("All beatmaps");
|
CollectionName = Collection?.Name.GetBoundCopy() ?? new Bindable<string>("All beatmaps");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Equals(CollectionFilterMenuItem other)
|
||||||
|
=> other != null && CollectionName.Value == other.CollectionName.Value;
|
||||||
|
|
||||||
|
public override int GetHashCode() => CollectionName.Value.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AllBeatmapsCollectionFilterMenuItem : CollectionFilterMenuItem
|
public class AllBeatmapsCollectionFilterMenuItem : CollectionFilterMenuItem
|
||||||
|
@ -16,12 +16,12 @@ namespace osu.Game.Configuration
|
|||||||
[Description("Hit Error (right)")]
|
[Description("Hit Error (right)")]
|
||||||
HitErrorRight,
|
HitErrorRight,
|
||||||
|
|
||||||
[Description("Hit Error (bottom)")]
|
|
||||||
HitErrorBottom,
|
|
||||||
|
|
||||||
[Description("Hit Error (left+right)")]
|
[Description("Hit Error (left+right)")]
|
||||||
HitErrorBoth,
|
HitErrorBoth,
|
||||||
|
|
||||||
|
[Description("Hit Error (bottom)")]
|
||||||
|
HitErrorBottom,
|
||||||
|
|
||||||
[Description("Colour (left)")]
|
[Description("Colour (left)")]
|
||||||
ColourLeft,
|
ColourLeft,
|
||||||
|
|
||||||
|
@ -36,18 +36,23 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
[Key(5)]
|
[Key(5)]
|
||||||
public IEnumerable<APIMod> AllowedMods { get; set; } = Enumerable.Empty<APIMod>();
|
public IEnumerable<APIMod> AllowedMods { get; set; } = Enumerable.Empty<APIMod>();
|
||||||
|
|
||||||
|
[Key(6)]
|
||||||
|
public long PlaylistItemId { get; set; }
|
||||||
|
|
||||||
public bool Equals(MultiplayerRoomSettings other)
|
public bool Equals(MultiplayerRoomSettings other)
|
||||||
=> BeatmapID == other.BeatmapID
|
=> BeatmapID == other.BeatmapID
|
||||||
&& BeatmapChecksum == other.BeatmapChecksum
|
&& BeatmapChecksum == other.BeatmapChecksum
|
||||||
&& RequiredMods.SequenceEqual(other.RequiredMods)
|
&& RequiredMods.SequenceEqual(other.RequiredMods)
|
||||||
&& AllowedMods.SequenceEqual(other.AllowedMods)
|
&& AllowedMods.SequenceEqual(other.AllowedMods)
|
||||||
&& RulesetID == other.RulesetID
|
&& RulesetID == other.RulesetID
|
||||||
&& Name.Equals(other.Name, StringComparison.Ordinal);
|
&& Name.Equals(other.Name, StringComparison.Ordinal)
|
||||||
|
&& PlaylistItemId == other.PlaylistItemId;
|
||||||
|
|
||||||
public override string ToString() => $"Name:{Name}"
|
public override string ToString() => $"Name:{Name}"
|
||||||
+ $" Beatmap:{BeatmapID} ({BeatmapChecksum})"
|
+ $" Beatmap:{BeatmapID} ({BeatmapChecksum})"
|
||||||
+ $" RequiredMods:{string.Join(',', RequiredMods)}"
|
+ $" RequiredMods:{string.Join(',', RequiredMods)}"
|
||||||
+ $" AllowedMods:{string.Join(',', AllowedMods)}"
|
+ $" AllowedMods:{string.Join(',', AllowedMods)}"
|
||||||
+ $" Ruleset:{RulesetID}";
|
+ $" Ruleset:{RulesetID}"
|
||||||
|
+ $" Item:{PlaylistItemId}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,8 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly BindableList<int> CurrentMatchPlayingUserIds = new BindableList<int>();
|
public readonly BindableList<int> CurrentMatchPlayingUserIds = new BindableList<int>();
|
||||||
|
|
||||||
|
public readonly Bindable<PlaylistItem?> CurrentMatchPlayingItem = new Bindable<PlaylistItem?>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="MultiplayerRoomUser"/> corresponding to the local player, if available.
|
/// The <see cref="MultiplayerRoomUser"/> corresponding to the local player, if available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -92,10 +94,11 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private RulesetStore rulesets { get; set; } = null!;
|
private RulesetStore rulesets { get; set; } = null!;
|
||||||
|
|
||||||
private Room? apiRoom;
|
// Only exists for compatibility with old osu-server-spectator build.
|
||||||
|
// Todo: Can be removed on 2021/02/26.
|
||||||
|
private long defaultPlaylistItemId;
|
||||||
|
|
||||||
// Todo: This is temporary, until the multiplayer server returns the item id on match start or otherwise.
|
private Room? apiRoom;
|
||||||
private long playlistItemId;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
@ -142,7 +145,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
{
|
{
|
||||||
Room = joinedRoom;
|
Room = joinedRoom;
|
||||||
apiRoom = room;
|
apiRoom = room;
|
||||||
playlistItemId = room.Playlist.SingleOrDefault()?.ID ?? 0;
|
defaultPlaylistItemId = apiRoom.Playlist.FirstOrDefault()?.ID ?? 0;
|
||||||
}, cancellationSource.Token);
|
}, cancellationSource.Token);
|
||||||
|
|
||||||
// Update room settings.
|
// Update room settings.
|
||||||
@ -218,7 +221,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash,
|
BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash,
|
||||||
RulesetID = item.GetOr(existingPlaylistItem).RulesetID,
|
RulesetID = item.GetOr(existingPlaylistItem).RulesetID,
|
||||||
RequiredMods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.RequiredMods,
|
RequiredMods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.RequiredMods,
|
||||||
AllowedMods = item.HasValue ? item.Value.AsNonNull().AllowedMods.Select(m => new APIMod(m)).ToList() : Room.Settings.AllowedMods
|
AllowedMods = item.HasValue ? item.Value.AsNonNull().AllowedMods.Select(m => new APIMod(m)).ToList() : Room.Settings.AllowedMods,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,14 +509,13 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
Room.Settings = settings;
|
Room.Settings = settings;
|
||||||
apiRoom.Name.Value = Room.Settings.Name;
|
apiRoom.Name.Value = Room.Settings.Name;
|
||||||
|
|
||||||
// The playlist update is delayed until an online beatmap lookup (below) succeeds.
|
// The current item update is delayed until an online beatmap lookup (below) succeeds.
|
||||||
// In-order for the client to not display an outdated beatmap, the playlist is forcefully cleared here.
|
// In-order for the client to not display an outdated beatmap, the current item is forcefully cleared here.
|
||||||
apiRoom.Playlist.Clear();
|
CurrentMatchPlayingItem.Value = null;
|
||||||
|
|
||||||
RoomUpdated?.Invoke();
|
RoomUpdated?.Invoke();
|
||||||
|
|
||||||
var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId);
|
var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId);
|
||||||
|
|
||||||
req.Success += res =>
|
req.Success += res =>
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
@ -540,18 +542,30 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
var mods = settings.RequiredMods.Select(m => m.ToMod(ruleset));
|
var mods = settings.RequiredMods.Select(m => m.ToMod(ruleset));
|
||||||
var allowedMods = settings.AllowedMods.Select(m => m.ToMod(ruleset));
|
var allowedMods = settings.AllowedMods.Select(m => m.ToMod(ruleset));
|
||||||
|
|
||||||
PlaylistItem playlistItem = new PlaylistItem
|
// Try to retrieve the existing playlist item from the API room.
|
||||||
|
var playlistItem = apiRoom.Playlist.FirstOrDefault(i => i.ID == settings.PlaylistItemId);
|
||||||
|
|
||||||
|
if (playlistItem != null)
|
||||||
|
updateItem(playlistItem);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
ID = playlistItemId,
|
// An existing playlist item does not exist, so append a new one.
|
||||||
Beatmap = { Value = beatmap },
|
updateItem(playlistItem = new PlaylistItem());
|
||||||
Ruleset = { Value = ruleset.RulesetInfo },
|
apiRoom.Playlist.Add(playlistItem);
|
||||||
};
|
}
|
||||||
|
|
||||||
playlistItem.RequiredMods.AddRange(mods);
|
CurrentMatchPlayingItem.Value = playlistItem;
|
||||||
playlistItem.AllowedMods.AddRange(allowedMods);
|
|
||||||
|
|
||||||
apiRoom.Playlist.Clear(); // Clearing should be unnecessary, but here for sanity.
|
void updateItem(PlaylistItem item)
|
||||||
apiRoom.Playlist.Add(playlistItem);
|
{
|
||||||
|
item.ID = settings.PlaylistItemId == 0 ? defaultPlaylistItemId : settings.PlaylistItemId;
|
||||||
|
item.Beatmap.Value = beatmap;
|
||||||
|
item.Ruleset.Value = ruleset.RulesetInfo;
|
||||||
|
item.RequiredMods.Clear();
|
||||||
|
item.RequiredMods.AddRange(mods);
|
||||||
|
item.AllowedMods.Clear();
|
||||||
|
item.AllowedMods.AddRange(allowedMods);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -23,6 +23,12 @@ namespace osu.Game.Online.Rooms
|
|||||||
[JsonProperty("ruleset_id")]
|
[JsonProperty("ruleset_id")]
|
||||||
public int RulesetID { get; set; }
|
public int RulesetID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this <see cref="PlaylistItem"/> is still a valid selection for the <see cref="Room"/>.
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("expired")]
|
||||||
|
public bool Expired { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
|
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
|
||||||
|
|
||||||
|
@ -153,6 +153,12 @@ namespace osu.Game.Online.Rooms
|
|||||||
if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value)
|
if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value)
|
||||||
Status.Value = new RoomStatusEnded();
|
Status.Value = new RoomStatusEnded();
|
||||||
|
|
||||||
|
// Todo: This is not the best way/place to do this, but the intention is to display all playlist items when the room has ended,
|
||||||
|
// and display only the non-expired playlist items while the room is still active. In order to achieve this, all expired items are removed from the source Room.
|
||||||
|
// More refactoring is required before this can be done locally instead - DrawableRoomPlaylist is currently directly bound to the playlist to display items in the room.
|
||||||
|
if (!(Status.Value is RoomStatusEnded))
|
||||||
|
other.Playlist.RemoveAll(i => i.Expired);
|
||||||
|
|
||||||
if (!Playlist.SequenceEqual(other.Playlist))
|
if (!Playlist.SequenceEqual(other.Playlist))
|
||||||
{
|
{
|
||||||
Playlist.Clear();
|
Playlist.Clear();
|
||||||
|
@ -383,7 +383,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
Ruleset.Value = selection.Ruleset;
|
Ruleset.Value = selection.Ruleset;
|
||||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(selection);
|
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(selection);
|
||||||
}, validScreens: new[] { typeof(PlaySongSelect) });
|
}, validScreens: new[] { typeof(SongSelect) });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Overlays
|
|||||||
protected List<APIUpdateStream> Streams;
|
protected List<APIUpdateStream> Streams;
|
||||||
|
|
||||||
public ChangelogOverlay()
|
public ChangelogOverlay()
|
||||||
: base(OverlayColourScheme.Purple)
|
: base(OverlayColourScheme.Purple, false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Overlays
|
|||||||
private readonly Bindable<string> article = new Bindable<string>(null);
|
private readonly Bindable<string> article = new Bindable<string>(null);
|
||||||
|
|
||||||
public NewsOverlay()
|
public NewsOverlay()
|
||||||
: base(OverlayColourScheme.Purple)
|
: base(OverlayColourScheme.Purple, false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
@ -16,10 +17,16 @@ namespace osu.Game.Overlays
|
|||||||
protected readonly LoadingLayer Loading;
|
protected readonly LoadingLayer Loading;
|
||||||
private readonly Container content;
|
private readonly Container content;
|
||||||
|
|
||||||
protected OnlineOverlay(OverlayColourScheme colourScheme)
|
protected OnlineOverlay(OverlayColourScheme colourScheme, bool requiresSignIn = true)
|
||||||
: base(colourScheme)
|
: base(colourScheme)
|
||||||
{
|
{
|
||||||
base.Content.AddRange(new Drawable[]
|
var mainContent = requiresSignIn
|
||||||
|
? new OnlineViewContainer($"Sign in to view the {Header.Title.Title}")
|
||||||
|
: new Container();
|
||||||
|
|
||||||
|
mainContent.RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
mainContent.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
ScrollFlow = new OverlayScrollContainer
|
ScrollFlow = new OverlayScrollContainer
|
||||||
{
|
{
|
||||||
@ -43,6 +50,8 @@ namespace osu.Game.Overlays
|
|||||||
},
|
},
|
||||||
Loading = new LoadingLayer(true)
|
Loading = new LoadingLayer(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
base.Content.Add(mainContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,8 +144,8 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
|
|
||||||
private void updateDisplay(User user)
|
private void updateDisplay(User user)
|
||||||
{
|
{
|
||||||
hiddenDetailGlobal.Content = user?.Statistics?.Ranks.Global?.ToString("\\##,##0") ?? "-";
|
hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank?.ToString("\\##,##0") ?? "-";
|
||||||
hiddenDetailCountry.Content = user?.Statistics?.Ranks.Country?.ToString("\\##,##0") ?? "-";
|
hiddenDetailCountry.Content = user?.Statistics?.CountryRank?.ToString("\\##,##0") ?? "-";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,8 +176,8 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
foreach (var scoreRankInfo in scoreRankInfos)
|
foreach (var scoreRankInfo in scoreRankInfos)
|
||||||
scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0;
|
scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0;
|
||||||
|
|
||||||
detailGlobalRank.Content = user?.Statistics?.Ranks.Global?.ToString("\\##,##0") ?? "-";
|
detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToString("\\##,##0") ?? "-";
|
||||||
detailCountryRank.Content = user?.Statistics?.Ranks.Country?.ToString("\\##,##0") ?? "-";
|
detailCountryRank.Content = user?.Statistics?.CountryRank?.ToString("\\##,##0") ?? "-";
|
||||||
|
|
||||||
rankGraph.Statistics.Value = user?.Statistics;
|
rankGraph.Statistics.Value = user?.Statistics;
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
LoadComponentAsync(display, loaded =>
|
LoadComponentAsync(display, loaded =>
|
||||||
{
|
{
|
||||||
if (API.IsLoggedIn)
|
Loading.Hide();
|
||||||
Loading.Hide();
|
|
||||||
|
|
||||||
Child = loaded;
|
Child = loaded;
|
||||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
}, (cancellationToken = new CancellationTokenSource()).Token);
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Dialog;
|
using osu.Game.Overlays.Dialog;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
|
using osu.Game.Screens;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
|
|
||||||
namespace osu.Game
|
namespace osu.Game
|
||||||
@ -81,24 +82,45 @@ namespace osu.Game
|
|||||||
|
|
||||||
game?.CloseAllOverlays(false);
|
game?.CloseAllOverlays(false);
|
||||||
|
|
||||||
// we may already be at the target screen type.
|
findValidTarget(current);
|
||||||
if (validScreens.Contains(current.GetType()) && !beatmap.Disabled)
|
}
|
||||||
|
|
||||||
|
private bool findValidTarget(IScreen current)
|
||||||
|
{
|
||||||
|
var type = current.GetType();
|
||||||
|
|
||||||
|
// check if we are already at a valid target screen.
|
||||||
|
if (validScreens.Any(t => t.IsAssignableFrom(type)) && !beatmap.Disabled)
|
||||||
{
|
{
|
||||||
finalAction(current);
|
finalAction(current);
|
||||||
Cancel();
|
Cancel();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (current != null)
|
while (current != null)
|
||||||
{
|
{
|
||||||
if (validScreens.Contains(current.GetType()))
|
// if this has a sub stack, recursively check the screens within it.
|
||||||
|
if (current is IHasSubScreenStack currentSubScreen)
|
||||||
|
{
|
||||||
|
if (findValidTarget(currentSubScreen.SubScreenStack.CurrentScreen))
|
||||||
|
{
|
||||||
|
// should be correct in theory, but currently untested/unused in existing implementations.
|
||||||
|
current.MakeCurrent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validScreens.Any(t => t.IsAssignableFrom(type)))
|
||||||
{
|
{
|
||||||
current.MakeCurrent();
|
current.MakeCurrent();
|
||||||
break;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
current = current.GetParentScreen();
|
current = current.GetParentScreen();
|
||||||
|
type = current?.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -110,9 +110,9 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public IEnumerable<string> GetAvailableResources() => throw new NotSupportedException();
|
public IEnumerable<string> GetAvailableResources() => throw new NotSupportedException();
|
||||||
|
|
||||||
public void AddAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable) => throw new NotSupportedException();
|
public void AddAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => throw new NotSupportedException();
|
||||||
|
|
||||||
public void RemoveAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable) => throw new NotSupportedException();
|
public void RemoveAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => throw new NotSupportedException();
|
||||||
|
|
||||||
public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotSupportedException();
|
public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotSupportedException();
|
||||||
|
|
||||||
@ -134,6 +134,10 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public IBindable<double> AggregateTempo => throw new NotSupportedException();
|
public IBindable<double> AggregateTempo => throw new NotSupportedException();
|
||||||
|
|
||||||
|
public void BindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public void UnbindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
|
||||||
|
|
||||||
public int PlaybackConcurrency
|
public int PlaybackConcurrency
|
||||||
{
|
{
|
||||||
get => throw new NotSupportedException();
|
get => throw new NotSupportedException();
|
||||||
|
15
osu.Game/Screens/IHasSubScreenStack.cs
Normal file
15
osu.Game/Screens/IHasSubScreenStack.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// 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.Framework.Screens;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A screen which manages a nested stack of screens within itself.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasSubScreenStack
|
||||||
|
{
|
||||||
|
ScreenStack SubScreenStack { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -8,11 +8,14 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
@ -21,14 +24,20 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
[Cached(typeof(IPreviewTrackOwner))]
|
[Cached(typeof(IPreviewTrackOwner))]
|
||||||
public abstract class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner
|
public abstract class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner
|
||||||
{
|
{
|
||||||
|
[Cached(typeof(IBindable<PlaylistItem>))]
|
||||||
protected readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();
|
protected readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();
|
||||||
|
|
||||||
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
||||||
|
|
||||||
private Sample sampleStart;
|
private readonly ModSelectOverlay userModsSelectOverlay;
|
||||||
|
|
||||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
/// <summary>
|
||||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
/// A container that provides controls for selection of user mods.
|
||||||
|
/// This will be shown/hidden automatically when applicable.
|
||||||
|
/// </summary>
|
||||||
|
protected Drawable UserModsSection;
|
||||||
|
|
||||||
|
private Sample sampleStart;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Any mods applied by/to the local user.
|
/// Any mods applied by/to the local user.
|
||||||
@ -53,9 +62,26 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
|
|
||||||
protected RoomSubScreen()
|
protected RoomSubScreen()
|
||||||
{
|
{
|
||||||
AddInternal(BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
SelectedItem = { BindTarget = SelectedItem }
|
BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker
|
||||||
|
{
|
||||||
|
SelectedItem = { BindTarget = SelectedItem }
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Depth = float.MinValue,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = 0.5f,
|
||||||
|
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING },
|
||||||
|
Child = userModsSelectOverlay = new UserModSelectOverlay
|
||||||
|
{
|
||||||
|
SelectedMods = { BindTarget = UserMods },
|
||||||
|
IsValidMod = _ => false
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +99,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged));
|
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged));
|
||||||
SelectedItem.Value = Playlist.FirstOrDefault();
|
|
||||||
|
|
||||||
managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy();
|
managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy();
|
||||||
managerUpdated.BindValueChanged(beatmapUpdated);
|
managerUpdated.BindValueChanged(beatmapUpdated);
|
||||||
@ -81,6 +106,19 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods));
|
UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool OnBackButton()
|
||||||
|
{
|
||||||
|
if (userModsSelectOverlay.State.Value == Visibility.Visible)
|
||||||
|
{
|
||||||
|
userModsSelectOverlay.Hide();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnBackButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ShowUserModSelect() => userModsSelectOverlay.Show();
|
||||||
|
|
||||||
public override void OnEntering(IScreen last)
|
public override void OnEntering(IScreen last)
|
||||||
{
|
{
|
||||||
base.OnEntering(last);
|
base.OnEntering(last);
|
||||||
@ -120,17 +158,31 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
{
|
{
|
||||||
updateWorkingBeatmap();
|
updateWorkingBeatmap();
|
||||||
|
|
||||||
if (SelectedItem.Value == null)
|
var selected = SelectedItem.Value;
|
||||||
|
|
||||||
|
if (selected == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remove any user mods that are no longer allowed.
|
// Remove any user mods that are no longer allowed.
|
||||||
UserMods.Value = UserMods.Value
|
UserMods.Value = UserMods.Value
|
||||||
.Where(m => SelectedItem.Value.AllowedMods.Any(a => m.GetType() == a.GetType()))
|
.Where(m => selected.AllowedMods.Any(a => m.GetType() == a.GetType()))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
UpdateMods();
|
UpdateMods();
|
||||||
|
|
||||||
Ruleset.Value = SelectedItem.Value.Ruleset.Value;
|
Ruleset.Value = selected.Ruleset.Value;
|
||||||
|
|
||||||
|
if (!selected.AllowedMods.Any())
|
||||||
|
{
|
||||||
|
UserModsSection?.Hide();
|
||||||
|
userModsSelectOverlay.Hide();
|
||||||
|
userModsSelectOverlay.IsValidMod = _ => false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UserModsSection?.Show();
|
||||||
|
userModsSelectOverlay.IsValidMod = m => selected.AllowedMods.Any(a => a.GetType() == m.GetType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet) => Schedule(updateWorkingBeatmap);
|
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet) => Schedule(updateWorkingBeatmap);
|
||||||
@ -185,5 +237,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
if (track != null)
|
if (track != null)
|
||||||
track.Looping = false;
|
track.Looping = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class UserModSelectOverlay : LocalPlayerModSelectOverlay
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// 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.Specialized;
|
|
||||||
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;
|
||||||
@ -13,7 +11,7 @@ using osu.Game.Screens.OnlinePlay.Match.Components;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||||
{
|
{
|
||||||
public class BeatmapSelectionControl : OnlinePlayComposite
|
public class BeatmapSelectionControl : RoomSubScreenComposite
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private MultiplayerMatchSubScreen matchSubScreen { get; set; }
|
private MultiplayerMatchSubScreen matchSubScreen { get; set; }
|
||||||
@ -60,7 +58,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
SelectedItem.BindValueChanged(_ => updateBeatmap(), true);
|
||||||
Host.BindValueChanged(host =>
|
Host.BindValueChanged(host =>
|
||||||
{
|
{
|
||||||
if (RoomID.Value == null || host.NewValue?.Equals(api.LocalUser.Value) == true)
|
if (RoomID.Value == null || host.NewValue?.Equals(api.LocalUser.Value) == true)
|
||||||
@ -70,12 +68,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e)
|
private void updateBeatmap()
|
||||||
{
|
{
|
||||||
if (Playlist.Any())
|
if (SelectedItem.Value == null)
|
||||||
beatmapPanelContainer.Child = new DrawableRoomPlaylistItem(Playlist.Single(), false, false);
|
|
||||||
else
|
|
||||||
beatmapPanelContainer.Clear();
|
beatmapPanelContainer.Clear();
|
||||||
|
else
|
||||||
|
beatmapPanelContainer.Child = new DrawableRoomPlaylistItem(SelectedItem.Value, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
@ -16,7 +15,6 @@ using osu.Framework.Threading;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays.Mods;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.OnlinePlay.Components;
|
using osu.Game.Screens.OnlinePlay.Components;
|
||||||
using osu.Game.Screens.OnlinePlay.Match;
|
using osu.Game.Screens.OnlinePlay.Match;
|
||||||
@ -43,11 +41,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OngoingOperationTracker ongoingOperationTracker { get; set; }
|
private OngoingOperationTracker ongoingOperationTracker { get; set; }
|
||||||
|
|
||||||
private ModSelectOverlay userModsSelectOverlay;
|
|
||||||
private MultiplayerMatchSettingsOverlay settingsOverlay;
|
private MultiplayerMatchSettingsOverlay settingsOverlay;
|
||||||
private Drawable userModsSection;
|
|
||||||
|
|
||||||
private IBindable<bool> isConnected;
|
private readonly IBindable<bool> isConnected = new Bindable<bool>();
|
||||||
|
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
private IDisposable readyClickOperation;
|
private IDisposable readyClickOperation;
|
||||||
@ -155,7 +151,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
new BeatmapSelectionControl { RelativeSizeAxes = Axes.X }
|
new BeatmapSelectionControl { RelativeSizeAxes = Axes.X }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
userModsSection = new FillFlowContainer
|
UserModsSection = new FillFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
@ -176,7 +172,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Width = 90,
|
Width = 90,
|
||||||
Text = "Select",
|
Text = "Select",
|
||||||
Action = () => userModsSelectOverlay.Show()
|
Action = ShowUserModSelect,
|
||||||
},
|
},
|
||||||
new ModDisplay
|
new ModDisplay
|
||||||
{
|
{
|
||||||
@ -231,19 +227,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
new Dimension(GridSizeMode.AutoSize),
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Height = 0.5f,
|
|
||||||
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING },
|
|
||||||
Child = userModsSelectOverlay = new UserModSelectOverlay
|
|
||||||
{
|
|
||||||
SelectedMods = { BindTarget = UserMods },
|
|
||||||
IsValidMod = _ => false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
settingsOverlay = new MultiplayerMatchSettingsOverlay
|
settingsOverlay = new MultiplayerMatchSettingsOverlay
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -269,14 +252,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
SelectedItem.BindTo(client.CurrentMatchPlayingItem);
|
||||||
|
|
||||||
BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true);
|
BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true);
|
||||||
UserMods.BindValueChanged(onUserModsChanged);
|
UserMods.BindValueChanged(onUserModsChanged);
|
||||||
|
|
||||||
client.LoadRequested += onLoadRequested;
|
client.LoadRequested += onLoadRequested;
|
||||||
client.RoomUpdated += onRoomUpdated;
|
client.RoomUpdated += onRoomUpdated;
|
||||||
|
|
||||||
isConnected = client.IsConnected.GetBoundCopy();
|
isConnected.BindTo(client.IsConnected);
|
||||||
isConnected.BindValueChanged(connected =>
|
isConnected.BindValueChanged(connected =>
|
||||||
{
|
{
|
||||||
if (!connected.NewValue)
|
if (!connected.NewValue)
|
||||||
@ -303,32 +287,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userModsSelectOverlay.State.Value == Visibility.Visible)
|
|
||||||
{
|
|
||||||
userModsSelectOverlay.Hide();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnBackButton();
|
return base.OnBackButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
SelectedItem.Value = Playlist.FirstOrDefault();
|
|
||||||
|
|
||||||
if (SelectedItem.Value?.AllowedMods.Any() != true)
|
|
||||||
{
|
|
||||||
userModsSection.Hide();
|
|
||||||
userModsSelectOverlay.Hide();
|
|
||||||
userModsSelectOverlay.IsValidMod = _ => false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
userModsSection.Show();
|
|
||||||
userModsSelectOverlay.IsValidMod = m => SelectedItem.Value.AllowedMods.Any(a => a.GetType() == m.GetType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ModSettingChangeTracker modSettingChangeTracker;
|
private ModSettingChangeTracker modSettingChangeTracker;
|
||||||
private ScheduledDelegate debouncedModSettingsUpdate;
|
private ScheduledDelegate debouncedModSettingsUpdate;
|
||||||
|
|
||||||
@ -433,9 +394,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
|
|
||||||
modSettingChangeTracker?.Dispose();
|
modSettingChangeTracker?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UserModSelectOverlay : LocalPlayerModSelectOverlay
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
Debug.Assert(client.Room != null);
|
Debug.Assert(client.Room != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
((IBindable<bool>)leaderboard.Expanded).BindTo(IsBreakTime);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void StartGameplay()
|
protected override void StartGameplay()
|
||||||
{
|
{
|
||||||
// block base call, but let the server know we are ready to start.
|
// block base call, but let the server know we are ready to start.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
@ -35,9 +36,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private RulesetStore rulesets { get; set; }
|
private RulesetStore rulesets { get; set; }
|
||||||
|
|
||||||
|
private SpriteIcon crown;
|
||||||
|
private OsuSpriteText userRankText;
|
||||||
private ModDisplay userModsDisplay;
|
private ModDisplay userModsDisplay;
|
||||||
private StateDisplay userStateDisplay;
|
private StateDisplay userStateDisplay;
|
||||||
private SpriteIcon crown;
|
|
||||||
|
|
||||||
public ParticipantPanel(MultiplayerRoomUser user)
|
public ParticipantPanel(MultiplayerRoomUser user)
|
||||||
{
|
{
|
||||||
@ -119,12 +121,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18),
|
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18),
|
||||||
Text = user?.Username
|
Text = user?.Username
|
||||||
},
|
},
|
||||||
new OsuSpriteText
|
userRankText = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Font = OsuFont.GetFont(size: 14),
|
Font = OsuFont.GetFont(size: 14),
|
||||||
Text = user?.CurrentModeRank != null ? $"#{user.CurrentModeRank}" : string.Empty
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -162,6 +163,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
|
|
||||||
const double fade_time = 50;
|
const double fade_time = 50;
|
||||||
|
|
||||||
|
var ruleset = rulesets.GetRuleset(Room.Settings.RulesetID).CreateInstance();
|
||||||
|
|
||||||
|
var currentModeRank = User.User?.RulesetsStatistics?.GetValueOrDefault(ruleset.ShortName)?.GlobalRank;
|
||||||
|
userRankText.Text = currentModeRank != null ? $"#{currentModeRank.Value:N0}" : string.Empty;
|
||||||
|
|
||||||
userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability);
|
userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability);
|
||||||
|
|
||||||
if (Room.Host?.Equals(User) == true)
|
if (Room.Host?.Equals(User) == true)
|
||||||
@ -171,11 +177,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
|
|
||||||
// If the mods are updated at the end of the frame, the flow container will skip a reflow cycle: https://github.com/ppy/osu-framework/issues/4187
|
// If the mods are updated at the end of the frame, the flow container will skip a reflow cycle: https://github.com/ppy/osu-framework/issues/4187
|
||||||
// This looks particularly jarring here, so re-schedule the update to that start of our frame as a fix.
|
// This looks particularly jarring here, so re-schedule the update to that start of our frame as a fix.
|
||||||
Schedule(() =>
|
Schedule(() => userModsDisplay.Current.Value = User.Mods.Select(m => m.ToMod(ruleset)).ToList());
|
||||||
{
|
|
||||||
var ruleset = rulesets.GetRuleset(Room.Settings.RulesetID).CreateInstance();
|
|
||||||
userModsDisplay.Current.Value = User.Mods.Select(m => m.ToMod(ruleset)).ToList();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MenuItem[] ContextMenuItems
|
public MenuItem[] ContextMenuItems
|
||||||
|
@ -2,14 +2,19 @@
|
|||||||
// 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;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Screens.OnlinePlay.Match;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay
|
namespace osu.Game.Screens.OnlinePlay
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="CompositeDrawable"/> that exposes bindables for <see cref="Room"/> properties.
|
||||||
|
/// </summary>
|
||||||
public class OnlinePlayComposite : CompositeDrawable
|
public class OnlinePlayComposite : CompositeDrawable
|
||||||
{
|
{
|
||||||
[Resolved(typeof(Room))]
|
[Resolved(typeof(Room))]
|
||||||
@ -53,5 +58,23 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
[Resolved(typeof(Room))]
|
[Resolved(typeof(Room))]
|
||||||
protected Bindable<TimeSpan?> Duration { get; private set; }
|
protected Bindable<TimeSpan?> Duration { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The currently selected item in the <see cref="RoomSubScreen"/>, or the first item from <see cref="Playlist"/>
|
||||||
|
/// if this <see cref="OnlinePlayComposite"/> is not within a <see cref="RoomSubScreen"/>.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Playlist.BindCollectionChanged((_, __) => UpdateSelectedItem(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateSelectedItem()
|
||||||
|
{
|
||||||
|
SelectedItem.Value = Playlist.FirstOrDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ using osuTK;
|
|||||||
namespace osu.Game.Screens.OnlinePlay
|
namespace osu.Game.Screens.OnlinePlay
|
||||||
{
|
{
|
||||||
[Cached]
|
[Cached]
|
||||||
public abstract class OnlinePlayScreen : OsuScreen
|
public abstract class OnlinePlayScreen : OsuScreen, IHasSubScreenStack
|
||||||
{
|
{
|
||||||
public override bool CursorVisible => (screenStack.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true;
|
public override bool CursorVisible => (screenStack.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true;
|
||||||
|
|
||||||
@ -355,5 +355,7 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
protected override double TransformDuration => 200;
|
protected override double TransformDuration => 200;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScreenStack IHasSubScreenStack.SubScreenStack => screenStack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -31,7 +32,12 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||||
|
|
||||||
private readonly Bindable<IReadOnlyList<Mod>> freeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
protected readonly Bindable<IReadOnlyList<Mod>> FreeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private IBindable<PlaylistItem> selectedItem { get; set; }
|
||||||
|
|
||||||
private readonly FreeModSelectOverlay freeModSelectOverlay;
|
private readonly FreeModSelectOverlay freeModSelectOverlay;
|
||||||
|
|
||||||
private WorkingBeatmap initialBeatmap;
|
private WorkingBeatmap initialBeatmap;
|
||||||
@ -45,7 +51,7 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
freeModSelectOverlay = new FreeModSelectOverlay
|
freeModSelectOverlay = new FreeModSelectOverlay
|
||||||
{
|
{
|
||||||
SelectedMods = { BindTarget = freeMods },
|
SelectedMods = { BindTarget = FreeMods },
|
||||||
IsValidMod = IsValidFreeMod,
|
IsValidMod = IsValidFreeMod,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -66,15 +72,15 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
// At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
|
// At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
|
||||||
// Similarly, freeMods is currently empty but should only contain the allowed mods.
|
// Similarly, freeMods is currently empty but should only contain the allowed mods.
|
||||||
Mods.Value = Playlist.FirstOrDefault()?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
||||||
freeMods.Value = Playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
||||||
|
|
||||||
Ruleset.BindValueChanged(onRulesetChanged);
|
Ruleset.BindValueChanged(onRulesetChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
|
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
|
||||||
{
|
{
|
||||||
freeMods.Value = Array.Empty<Mod>();
|
FreeMods.Value = Array.Empty<Mod>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override bool OnStart()
|
protected sealed override bool OnStart()
|
||||||
@ -90,7 +96,7 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
||||||
|
|
||||||
item.AllowedMods.Clear();
|
item.AllowedMods.Clear();
|
||||||
item.AllowedMods.AddRange(freeMods.Value.Select(m => m.CreateCopy()));
|
item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy()));
|
||||||
|
|
||||||
SelectItem(item);
|
SelectItem(item);
|
||||||
return true;
|
return true;
|
||||||
@ -133,7 +139,7 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons()
|
protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons()
|
||||||
{
|
{
|
||||||
var buttons = base.CreateFooterButtons().ToList();
|
var buttons = base.CreateFooterButtons().ToList();
|
||||||
buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = freeMods }, freeModSelectOverlay));
|
buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = FreeMods }, freeModSelectOverlay));
|
||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,9 @@ using osu.Game.Online.Rooms;
|
|||||||
using osu.Game.Screens.OnlinePlay.Components;
|
using osu.Game.Screens.OnlinePlay.Components;
|
||||||
using osu.Game.Screens.OnlinePlay.Match;
|
using osu.Game.Screens.OnlinePlay.Match;
|
||||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
using osuTK;
|
||||||
using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer;
|
using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Playlists
|
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||||
@ -27,6 +29,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
[Resolved(typeof(Room), nameof(Room.RoomID))]
|
[Resolved(typeof(Room), nameof(Room.RoomID))]
|
||||||
private Bindable<long?> roomId { get; set; }
|
private Bindable<long?> roomId { get; set; }
|
||||||
|
|
||||||
|
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||||
|
private BindableList<PlaylistItem> playlist { get; set; }
|
||||||
|
|
||||||
private MatchSettingsOverlay settingsOverlay;
|
private MatchSettingsOverlay settingsOverlay;
|
||||||
private MatchLeaderboard leaderboard;
|
private MatchLeaderboard leaderboard;
|
||||||
|
|
||||||
@ -117,7 +122,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
new DrawableRoomPlaylistWithResults
|
new DrawableRoomPlaylistWithResults
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Items = { BindTarget = Playlist },
|
Items = { BindTarget = playlist },
|
||||||
SelectedItem = { BindTarget = SelectedItem },
|
SelectedItem = { BindTarget = SelectedItem },
|
||||||
RequestShowResults = item =>
|
RequestShowResults = item =>
|
||||||
{
|
{
|
||||||
@ -140,13 +145,55 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Content = new[]
|
Content = new[]
|
||||||
{
|
{
|
||||||
new Drawable[] { new OverlinedHeader("Leaderboard"), },
|
new[]
|
||||||
|
{
|
||||||
|
UserModsSection = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Margin = new MarginPadding { Bottom = 10 },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OverlinedHeader("Extra mods"),
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(10, 0),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new PurpleTriangleButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Width = 90,
|
||||||
|
Text = "Select",
|
||||||
|
Action = ShowUserModSelect,
|
||||||
|
},
|
||||||
|
new ModDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
DisplayUnrankedText = false,
|
||||||
|
Current = UserMods,
|
||||||
|
Scale = new Vector2(0.8f),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new OverlinedHeader("Leaderboard")
|
||||||
|
},
|
||||||
new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, },
|
new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, },
|
||||||
new Drawable[] { new OverlinedHeader("Chat"), },
|
new Drawable[] { new OverlinedHeader("Chat"), },
|
||||||
new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } }
|
new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } }
|
||||||
},
|
},
|
||||||
RowDimensions = new[]
|
RowDimensions = new[]
|
||||||
{
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
new Dimension(),
|
new Dimension(),
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
@ -222,7 +269,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
|
|
||||||
// Set the first playlist item.
|
// Set the first playlist item.
|
||||||
// This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()).
|
// This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()).
|
||||||
Schedule(() => SelectedItem.Value = Playlist.FirstOrDefault());
|
Schedule(() => SelectedItem.Value = playlist.FirstOrDefault());
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
|
|
||||||
item.RequiredMods.Clear();
|
item.RequiredMods.Clear();
|
||||||
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
||||||
|
|
||||||
|
item.AllowedMods.Clear();
|
||||||
|
item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
osu.Game/Screens/OnlinePlay/RoomSubScreenComposite.cs
Normal file
38
osu.Game/Screens/OnlinePlay/RoomSubScreenComposite.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// 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.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Screens.OnlinePlay.Match;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.OnlinePlay
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="OnlinePlayComposite"/> with additional logic tracking the currently-selected <see cref="PlaylistItem"/> inside a <see cref="RoomSubScreen"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class RoomSubScreenComposite : OnlinePlayComposite
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private IBindable<PlaylistItem> subScreenSelectedItem { get; set; }
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
subScreenSelectedItem.BindValueChanged(_ => UpdateSelectedItem(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateSelectedItem()
|
||||||
|
{
|
||||||
|
if (RoomID.Value == null)
|
||||||
|
{
|
||||||
|
// If the room hasn't been created yet, fall-back to the base logic.
|
||||||
|
base.UpdateSelectedItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedItem.Value = subScreenSelectedItem.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -16,6 +17,8 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
{
|
{
|
||||||
private readonly Cached sorting = new Cached();
|
private readonly Cached sorting = new Cached();
|
||||||
|
|
||||||
|
public Bindable<bool> Expanded = new Bindable<bool>();
|
||||||
|
|
||||||
public GameplayLeaderboard()
|
public GameplayLeaderboard()
|
||||||
{
|
{
|
||||||
Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH;
|
Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH;
|
||||||
@ -47,8 +50,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
{
|
{
|
||||||
var drawable = new GameplayLeaderboardScore(user, isTracked)
|
var drawable = new GameplayLeaderboardScore(user, isTracked)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Expanded = { BindTarget = Expanded },
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
base.Add(drawable);
|
base.Add(drawable);
|
||||||
|
@ -20,16 +20,33 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
{
|
{
|
||||||
public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore
|
public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore
|
||||||
{
|
{
|
||||||
public const float EXTENDED_WIDTH = 255f;
|
public const float EXTENDED_WIDTH = regular_width + top_player_left_width_extension;
|
||||||
|
|
||||||
private const float regular_width = 235f;
|
private const float regular_width = 235f;
|
||||||
|
|
||||||
|
// a bit hand-wavy, but there's a lot of hard-coded paddings in each of the grid's internals.
|
||||||
|
private const float compact_width = 77.5f;
|
||||||
|
|
||||||
|
private const float top_player_left_width_extension = 20f;
|
||||||
|
|
||||||
public const float PANEL_HEIGHT = 35f;
|
public const float PANEL_HEIGHT = 35f;
|
||||||
|
|
||||||
public const float SHEAR_WIDTH = PANEL_HEIGHT * panel_shear;
|
public const float SHEAR_WIDTH = PANEL_HEIGHT * panel_shear;
|
||||||
|
|
||||||
private const float panel_shear = 0.15f;
|
private const float panel_shear = 0.15f;
|
||||||
|
|
||||||
|
private const float rank_text_width = 35f;
|
||||||
|
|
||||||
|
private const float score_components_width = 85f;
|
||||||
|
|
||||||
|
private const float avatar_size = 25f;
|
||||||
|
|
||||||
|
private const double panel_transition_duration = 500;
|
||||||
|
|
||||||
|
private const double text_transition_duration = 200;
|
||||||
|
|
||||||
|
public Bindable<bool> Expanded = new Bindable<bool>();
|
||||||
|
|
||||||
private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText;
|
private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText;
|
||||||
|
|
||||||
public BindableDouble TotalScore { get; } = new BindableDouble();
|
public BindableDouble TotalScore { get; } = new BindableDouble();
|
||||||
@ -63,8 +80,15 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
private readonly bool trackedPlayer;
|
private readonly bool trackedPlayer;
|
||||||
|
|
||||||
private Container mainFillContainer;
|
private Container mainFillContainer;
|
||||||
|
|
||||||
private Box centralFill;
|
private Box centralFill;
|
||||||
|
|
||||||
|
private Container backgroundPaddingAdjustContainer;
|
||||||
|
|
||||||
|
private GridContainer gridContainer;
|
||||||
|
|
||||||
|
private Container scoreComponents;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="GameplayLeaderboardScore"/>.
|
/// Creates a new <see cref="GameplayLeaderboardScore"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -75,7 +99,8 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
User = user;
|
User = user;
|
||||||
this.trackedPlayer = trackedPlayer;
|
this.trackedPlayer = trackedPlayer;
|
||||||
|
|
||||||
Size = new Vector2(EXTENDED_WIDTH, PANEL_HEIGHT);
|
AutoSizeAxes = Axes.X;
|
||||||
|
Height = PANEL_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -85,147 +110,167 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
mainFillContainer = new Container
|
new Container
|
||||||
{
|
{
|
||||||
Width = regular_width,
|
AutoSizeAxes = Axes.X,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Anchor = Anchor.TopRight,
|
Margin = new MarginPadding { Left = top_player_left_width_extension },
|
||||||
Origin = Anchor.TopRight,
|
Children = new Drawable[]
|
||||||
Masking = true,
|
|
||||||
CornerRadius = 5f,
|
|
||||||
Shear = new Vector2(panel_shear, 0f),
|
|
||||||
Child = new Box
|
|
||||||
{
|
{
|
||||||
Alpha = 0.5f,
|
backgroundPaddingAdjustContainer = new Container
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new GridContainer
|
|
||||||
{
|
|
||||||
Width = regular_width,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
ColumnDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(GridSizeMode.Absolute, 35f),
|
|
||||||
new Dimension(),
|
|
||||||
new Dimension(GridSizeMode.Absolute, 85f),
|
|
||||||
},
|
|
||||||
Content = new[]
|
|
||||||
{
|
|
||||||
new Drawable[]
|
|
||||||
{
|
{
|
||||||
positionText = new OsuSpriteText
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Right = SHEAR_WIDTH / 2 },
|
mainFillContainer = new Container
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Colour = Color4.White,
|
|
||||||
Font = OsuFont.Torus.With(size: 14, weight: FontWeight.Bold),
|
|
||||||
Shadow = false,
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Padding = new MarginPadding { Horizontal = SHEAR_WIDTH / 3 },
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new Container
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 5f,
|
||||||
|
Shear = new Vector2(panel_shear, 0f),
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Masking = true,
|
new Box
|
||||||
CornerRadius = 5f,
|
|
||||||
Shear = new Vector2(panel_shear, 0f),
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new[]
|
|
||||||
{
|
{
|
||||||
centralFill = new Box
|
Alpha = 0.5f,
|
||||||
{
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0.5f,
|
},
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4Extensions.FromHex("3399cc"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
Padding = new MarginPadding { Left = SHEAR_WIDTH },
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Spacing = new Vector2(4f, 0f),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
avatarContainer = new CircularContainer
|
|
||||||
{
|
|
||||||
Masking = true,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Size = new Vector2(25f),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Name = "Placeholder while avatar loads",
|
|
||||||
Alpha = 0.3f,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = colours.Gray4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
usernameText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Width = 0.6f,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Colour = Color4.White,
|
|
||||||
Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold),
|
|
||||||
Text = User?.Username,
|
|
||||||
Truncate = true,
|
|
||||||
Shadow = false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Padding = new MarginPadding { Top = 2f, Right = 17.5f, Bottom = 5f },
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Colour = Color4.White,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
scoreText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Spacing = new Vector2(-1f, 0f),
|
|
||||||
Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold, fixedWidth: true),
|
|
||||||
Shadow = false,
|
|
||||||
},
|
|
||||||
accuracyText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold, fixedWidth: true),
|
|
||||||
Spacing = new Vector2(-1f, 0f),
|
|
||||||
Shadow = false,
|
|
||||||
},
|
|
||||||
comboText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomRight,
|
|
||||||
Origin = Anchor.BottomRight,
|
|
||||||
Spacing = new Vector2(-1f, 0f),
|
|
||||||
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold, fixedWidth: true),
|
|
||||||
Shadow = false,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
gridContainer = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = compact_width, // will be updated by expanded state.
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.Absolute, rank_text_width),
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.AutoSize, maxSize: score_components_width),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
positionText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding { Right = SHEAR_WIDTH / 2 },
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Colour = Color4.White,
|
||||||
|
Font = OsuFont.Torus.With(size: 14, weight: FontWeight.Bold),
|
||||||
|
Shadow = false,
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding { Horizontal = SHEAR_WIDTH / 3 },
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 5f,
|
||||||
|
Shear = new Vector2(panel_shear, 0f),
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
centralFill = new Box
|
||||||
|
{
|
||||||
|
Alpha = 0.5f,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4Extensions.FromHex("3399cc"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding { Left = SHEAR_WIDTH },
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(4f, 0f),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
avatarContainer = new CircularContainer
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Size = new Vector2(avatar_size),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Name = "Placeholder while avatar loads",
|
||||||
|
Alpha = 0.3f,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colours.Gray4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
usernameText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Width = 0.6f,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Colour = Color4.White,
|
||||||
|
Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold),
|
||||||
|
Text = User?.Username,
|
||||||
|
Truncate = true,
|
||||||
|
Shadow = false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scoreComponents = new Container
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding { Top = 2f, Right = 17.5f, Bottom = 5f },
|
||||||
|
AlwaysPresent = true, // required to smoothly animate autosize after hidden early.
|
||||||
|
Masking = true,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Colour = Color4.White,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
scoreText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Spacing = new Vector2(-1f, 0f),
|
||||||
|
Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold, fixedWidth: true),
|
||||||
|
Shadow = false,
|
||||||
|
},
|
||||||
|
accuracyText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold, fixedWidth: true),
|
||||||
|
Spacing = new Vector2(-1f, 0f),
|
||||||
|
Shadow = false,
|
||||||
|
},
|
||||||
|
comboText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
Spacing = new Vector2(-1f, 0f),
|
||||||
|
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold, fixedWidth: true),
|
||||||
|
Shadow = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
LoadComponentAsync(new DrawableAvatar(User), avatarContainer.Add);
|
LoadComponentAsync(new DrawableAvatar(User), avatarContainer.Add);
|
||||||
@ -241,18 +286,43 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
updateState();
|
updateState();
|
||||||
|
Expanded.BindValueChanged(changeExpandedState, true);
|
||||||
|
|
||||||
FinishTransforms(true);
|
FinishTransforms(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private const double panel_transition_duration = 500;
|
private void changeExpandedState(ValueChangedEvent<bool> expanded)
|
||||||
|
{
|
||||||
|
scoreComponents.ClearTransforms();
|
||||||
|
|
||||||
|
if (expanded.NewValue)
|
||||||
|
{
|
||||||
|
gridContainer.ResizeWidthTo(regular_width, panel_transition_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
scoreComponents.ResizeWidthTo(score_components_width, panel_transition_duration, Easing.OutQuint);
|
||||||
|
scoreComponents.FadeIn(panel_transition_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
usernameText.FadeIn(panel_transition_duration, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gridContainer.ResizeWidthTo(compact_width, panel_transition_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
scoreComponents.ResizeWidthTo(0, panel_transition_duration, Easing.OutQuint);
|
||||||
|
scoreComponents.FadeOut(text_transition_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
usernameText.FadeOut(text_transition_duration, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateState()
|
private void updateState()
|
||||||
{
|
{
|
||||||
|
bool widthExtension = false;
|
||||||
|
|
||||||
if (HasQuit.Value)
|
if (HasQuit.Value)
|
||||||
{
|
{
|
||||||
// we will probably want to display this in a better way once we have a design.
|
// we will probably want to display this in a better way once we have a design.
|
||||||
// and also show states other than quit.
|
// and also show states other than quit.
|
||||||
mainFillContainer.ResizeWidthTo(regular_width, panel_transition_duration, Easing.OutElastic);
|
|
||||||
panelColour = Color4.Gray;
|
panelColour = Color4.Gray;
|
||||||
textColour = Color4.White;
|
textColour = Color4.White;
|
||||||
return;
|
return;
|
||||||
@ -260,22 +330,29 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
if (scorePosition == 1)
|
if (scorePosition == 1)
|
||||||
{
|
{
|
||||||
mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, panel_transition_duration, Easing.OutElastic);
|
widthExtension = true;
|
||||||
panelColour = Color4Extensions.FromHex("7fcc33");
|
panelColour = Color4Extensions.FromHex("7fcc33");
|
||||||
textColour = Color4.White;
|
textColour = Color4.White;
|
||||||
}
|
}
|
||||||
else if (trackedPlayer)
|
else if (trackedPlayer)
|
||||||
{
|
{
|
||||||
mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, panel_transition_duration, Easing.OutElastic);
|
widthExtension = true;
|
||||||
panelColour = Color4Extensions.FromHex("ffd966");
|
panelColour = Color4Extensions.FromHex("ffd966");
|
||||||
textColour = Color4Extensions.FromHex("2e576b");
|
textColour = Color4Extensions.FromHex("2e576b");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mainFillContainer.ResizeWidthTo(regular_width, panel_transition_duration, Easing.OutElastic);
|
|
||||||
panelColour = Color4Extensions.FromHex("3399cc");
|
panelColour = Color4Extensions.FromHex("3399cc");
|
||||||
textColour = Color4.White;
|
textColour = Color4.White;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.TransformTo(nameof(SizeContainerLeftPadding), widthExtension ? -top_player_left_width_extension : 0, panel_transition_duration, Easing.OutElastic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float SizeContainerLeftPadding
|
||||||
|
{
|
||||||
|
get => backgroundPaddingAdjustContainer.Padding.Left;
|
||||||
|
set => backgroundPaddingAdjustContainer.Padding = new MarginPadding { Left = value };
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 panelColour
|
private Color4 panelColour
|
||||||
@ -287,8 +364,6 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const double text_transition_duration = 200;
|
|
||||||
|
|
||||||
private Color4 textColour
|
private Color4 textColour
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
|
@ -339,7 +339,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
HoldToQuit =
|
HoldToQuit =
|
||||||
{
|
{
|
||||||
Action = performUserRequestedExit,
|
Action = () => PerformExit(true),
|
||||||
IsPaused = { BindTarget = GameplayClockContainer.IsPaused }
|
IsPaused = { BindTarget = GameplayClockContainer.IsPaused }
|
||||||
},
|
},
|
||||||
PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } },
|
PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } },
|
||||||
@ -363,14 +363,14 @@ namespace osu.Game.Screens.Play
|
|||||||
FailOverlay = new FailOverlay
|
FailOverlay = new FailOverlay
|
||||||
{
|
{
|
||||||
OnRetry = Restart,
|
OnRetry = Restart,
|
||||||
OnQuit = performUserRequestedExit,
|
OnQuit = () => PerformExit(true),
|
||||||
},
|
},
|
||||||
PauseOverlay = new PauseOverlay
|
PauseOverlay = new PauseOverlay
|
||||||
{
|
{
|
||||||
OnResume = Resume,
|
OnResume = Resume,
|
||||||
Retries = RestartCount,
|
Retries = RestartCount,
|
||||||
OnRetry = Restart,
|
OnRetry = Restart,
|
||||||
OnQuit = performUserRequestedExit,
|
OnQuit = () => PerformExit(true),
|
||||||
},
|
},
|
||||||
new HotkeyExitOverlay
|
new HotkeyExitOverlay
|
||||||
{
|
{
|
||||||
@ -379,7 +379,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (!this.IsCurrentScreen()) return;
|
if (!this.IsCurrentScreen()) return;
|
||||||
|
|
||||||
fadeOut(true);
|
fadeOut(true);
|
||||||
PerformExit(true);
|
PerformExit(false);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, },
|
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, },
|
||||||
@ -432,9 +432,9 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
if (gameActive.Value == false)
|
if (gameActive.Value == false)
|
||||||
{
|
{
|
||||||
if (canPause)
|
bool paused = Pause();
|
||||||
Pause();
|
|
||||||
else
|
if (!paused)
|
||||||
Scheduler.AddOnce(updatePauseOnFocusLostState);
|
Scheduler.AddOnce(updatePauseOnFocusLostState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -483,23 +483,47 @@ namespace osu.Game.Screens.Play
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exits the <see cref="Player"/>.
|
/// Exits the <see cref="Player"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userRequested">
|
/// <param name="showDialogFirst">
|
||||||
/// Whether the exit is requested by the user, or a higher-level game component.
|
/// Whether the pause or fail dialog should be shown before performing an exit.
|
||||||
/// Pausing is allowed only in the former case.
|
/// If true and a dialog is not yet displayed, the exit will be blocked the the relevant dialog will display instead.
|
||||||
/// </param>
|
/// </param>
|
||||||
protected void PerformExit(bool userRequested)
|
protected void PerformExit(bool showDialogFirst)
|
||||||
{
|
{
|
||||||
// if a restart has been requested, cancel any pending completion (user has shown intent to restart).
|
// if a restart has been requested, cancel any pending completion (user has shown intent to restart).
|
||||||
completionProgressDelegate?.Cancel();
|
completionProgressDelegate?.Cancel();
|
||||||
|
|
||||||
ValidForResume = false;
|
// there is a chance that the exit was performed after the transition to results has started.
|
||||||
|
// we want to give the user what they want, so forcefully return to this screen (to proceed with the upwards exit process).
|
||||||
|
if (!this.IsCurrentScreen())
|
||||||
|
{
|
||||||
|
ValidForResume = false;
|
||||||
|
this.MakeCurrent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.IsCurrentScreen()) return;
|
bool pauseOrFailDialogVisible =
|
||||||
|
PauseOverlay.State.Value == Visibility.Visible || FailOverlay.State.Value == Visibility.Visible;
|
||||||
|
|
||||||
if (userRequested)
|
if (showDialogFirst && !pauseOrFailDialogVisible)
|
||||||
performUserRequestedExit();
|
{
|
||||||
else
|
// if the fail animation is currently in progress, accelerate it (it will show the pause dialog on completion).
|
||||||
this.Exit();
|
if (ValidForResume && HasFailed)
|
||||||
|
{
|
||||||
|
failAnimation.FinishTransforms(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's a chance the pausing is not supported in the current state, at which point immediate exit should be preferred.
|
||||||
|
if (pausingSupportedByCurrentState)
|
||||||
|
{
|
||||||
|
// in the case a dialog needs to be shown, attempt to pause and show it.
|
||||||
|
// this may fail (see internal checks in Pause()) but the fail cases are temporary, so don't fall through to Exit().
|
||||||
|
Pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performUserRequestedSkip()
|
private void performUserRequestedSkip()
|
||||||
@ -513,20 +537,6 @@ namespace osu.Game.Screens.Play
|
|||||||
updateSampleDisabledState();
|
updateSampleDisabledState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performUserRequestedExit()
|
|
||||||
{
|
|
||||||
if (ValidForResume && HasFailed && !FailOverlay.IsPresent)
|
|
||||||
{
|
|
||||||
failAnimation.FinishTransforms(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canPause)
|
|
||||||
Pause();
|
|
||||||
else
|
|
||||||
this.Exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restart gameplay via a parent <see cref="PlayerLoader"/>.
|
/// Restart gameplay via a parent <see cref="PlayerLoader"/>.
|
||||||
/// <remarks>This can be called from a child screen in order to trigger the restart process.</remarks>
|
/// <remarks>This can be called from a child screen in order to trigger the restart process.</remarks>
|
||||||
@ -543,10 +553,7 @@ namespace osu.Game.Screens.Play
|
|||||||
sampleRestart?.Play();
|
sampleRestart?.Play();
|
||||||
RestartRequested?.Invoke();
|
RestartRequested?.Invoke();
|
||||||
|
|
||||||
if (this.IsCurrentScreen())
|
PerformExit(false);
|
||||||
PerformExit(true);
|
|
||||||
else
|
|
||||||
this.MakeCurrent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate completionProgressDelegate;
|
private ScheduledDelegate completionProgressDelegate;
|
||||||
@ -675,15 +682,18 @@ namespace osu.Game.Screens.Play
|
|||||||
protected bool PauseCooldownActive =>
|
protected bool PauseCooldownActive =>
|
||||||
lastPauseActionTime.HasValue && GameplayClockContainer.GameplayClock.CurrentTime < lastPauseActionTime + pause_cooldown;
|
lastPauseActionTime.HasValue && GameplayClockContainer.GameplayClock.CurrentTime < lastPauseActionTime + pause_cooldown;
|
||||||
|
|
||||||
private bool canPause =>
|
/// <summary>
|
||||||
|
/// A set of conditionals which defines whether the current game state and configuration allows for
|
||||||
|
/// pausing to be attempted via <see cref="Pause"/>. If false, the game should generally exit if a user pause
|
||||||
|
/// is attempted.
|
||||||
|
/// </summary>
|
||||||
|
private bool pausingSupportedByCurrentState =>
|
||||||
// must pass basic screen conditions (beatmap loaded, instance allows pause)
|
// must pass basic screen conditions (beatmap loaded, instance allows pause)
|
||||||
LoadedBeatmapSuccessfully && Configuration.AllowPause && ValidForResume
|
LoadedBeatmapSuccessfully && Configuration.AllowPause && ValidForResume
|
||||||
// replays cannot be paused and exit immediately
|
// replays cannot be paused and exit immediately
|
||||||
&& !DrawableRuleset.HasReplayLoaded.Value
|
&& !DrawableRuleset.HasReplayLoaded.Value
|
||||||
// cannot pause if we are already in a fail state
|
// cannot pause if we are already in a fail state
|
||||||
&& !HasFailed
|
&& !HasFailed;
|
||||||
// cannot pause if already paused (or in a cooldown state) unless we are in a resuming state.
|
|
||||||
&& (IsResuming || (GameplayClockContainer.IsPaused.Value == false && !PauseCooldownActive));
|
|
||||||
|
|
||||||
private bool canResume =>
|
private bool canResume =>
|
||||||
// cannot resume from a non-paused state
|
// cannot resume from a non-paused state
|
||||||
@ -693,9 +703,12 @@ namespace osu.Game.Screens.Play
|
|||||||
// already resuming
|
// already resuming
|
||||||
&& !IsResuming;
|
&& !IsResuming;
|
||||||
|
|
||||||
public void Pause()
|
public bool Pause()
|
||||||
{
|
{
|
||||||
if (!canPause) return;
|
if (!pausingSupportedByCurrentState) return false;
|
||||||
|
|
||||||
|
if (!IsResuming && PauseCooldownActive)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (IsResuming)
|
if (IsResuming)
|
||||||
{
|
{
|
||||||
@ -706,6 +719,7 @@ namespace osu.Game.Screens.Play
|
|||||||
GameplayClockContainer.Stop();
|
GameplayClockContainer.Stop();
|
||||||
PauseOverlay.Show();
|
PauseOverlay.Show();
|
||||||
lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime;
|
lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resume()
|
public void Resume()
|
||||||
@ -814,14 +828,6 @@ namespace osu.Game.Screens.Play
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidForResume is false when restarting
|
|
||||||
if (ValidForResume)
|
|
||||||
{
|
|
||||||
if (PauseCooldownActive && !GameplayClockContainer.IsPaused.Value)
|
|
||||||
// still want to block if we are within the cooldown period and not already paused.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GameplayClockContainer performs seeks / start / stop operations on the beatmap's track.
|
// GameplayClockContainer performs seeks / start / stop operations on the beatmap's track.
|
||||||
// as we are no longer the current screen, we cannot guarantee the track is still usable.
|
// as we are no longer the current screen, we cannot guarantee the track is still usable.
|
||||||
GameplayClockContainer?.StopUsingBeatmapClock();
|
GameplayClockContainer?.StopUsingBeatmapClock();
|
||||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Screens.Select
|
|||||||
Sort = sortMode.Value,
|
Sort = sortMode.Value,
|
||||||
AllowConvertedBeatmaps = showConverted.Value,
|
AllowConvertedBeatmaps = showConverted.Value,
|
||||||
Ruleset = ruleset.Value,
|
Ruleset = ruleset.Value,
|
||||||
Collection = collectionDropdown?.Current.Value.Collection
|
Collection = collectionDropdown?.Current.Value?.Collection
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!minimumStars.IsDefault)
|
if (!minimumStars.IsDefault)
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Skinning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sample corresponding to an <see cref="ISampleInfo"/> that supports being pooled and responding to skin changes.
|
/// A sample corresponding to an <see cref="ISampleInfo"/> that supports being pooled and responding to skin changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PoolableSkinnableSample : SkinReloadableDrawable, IAggregateAudioAdjustment, IAdjustableAudioComponent
|
public class PoolableSkinnableSample : SkinReloadableDrawable, IAdjustableAudioComponent
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently-loaded <see cref="DrawableSample"/>.
|
/// The currently-loaded <see cref="DrawableSample"/>.
|
||||||
@ -165,9 +165,13 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public BindableNumber<double> Tempo => sampleContainer.Tempo;
|
public BindableNumber<double> Tempo => sampleContainer.Tempo;
|
||||||
|
|
||||||
public void AddAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable) => sampleContainer.AddAdjustment(type, adjustBindable);
|
public void BindAdjustments(IAggregateAudioAdjustment component) => sampleContainer.BindAdjustments(component);
|
||||||
|
|
||||||
public void RemoveAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable) => sampleContainer.RemoveAdjustment(type, adjustBindable);
|
public void UnbindAdjustments(IAggregateAudioAdjustment component) => sampleContainer.UnbindAdjustments(component);
|
||||||
|
|
||||||
|
public void AddAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => sampleContainer.AddAdjustment(type, adjustBindable);
|
||||||
|
|
||||||
|
public void RemoveAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => sampleContainer.RemoveAdjustment(type, adjustBindable);
|
||||||
|
|
||||||
public void RemoveAllAdjustments(AdjustableProperty type) => sampleContainer.RemoveAllAdjustments(type);
|
public void RemoveAllAdjustments(AdjustableProperty type) => sampleContainer.RemoveAllAdjustments(type);
|
||||||
|
|
||||||
|
@ -176,10 +176,20 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public BindableNumber<double> Tempo => samplesContainer.Tempo;
|
public BindableNumber<double> Tempo => samplesContainer.Tempo;
|
||||||
|
|
||||||
public void AddAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable)
|
public void BindAdjustments(IAggregateAudioAdjustment component)
|
||||||
|
{
|
||||||
|
samplesContainer.BindAdjustments(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnbindAdjustments(IAggregateAudioAdjustment component)
|
||||||
|
{
|
||||||
|
samplesContainer.UnbindAdjustments(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddAdjustment(AdjustableProperty type, IBindable<double> adjustBindable)
|
||||||
=> samplesContainer.AddAdjustment(type, adjustBindable);
|
=> samplesContainer.AddAdjustment(type, adjustBindable);
|
||||||
|
|
||||||
public void RemoveAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable)
|
public void RemoveAdjustment(AdjustableProperty type, IBindable<double> adjustBindable)
|
||||||
=> samplesContainer.RemoveAdjustment(type, adjustBindable);
|
=> samplesContainer.RemoveAdjustment(type, adjustBindable);
|
||||||
|
|
||||||
public void RemoveAllAdjustments(AdjustableProperty type)
|
public void RemoveAllAdjustments(AdjustableProperty type)
|
||||||
@ -192,6 +202,14 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public bool IsPlayed => samplesContainer.Any(s => s.Played);
|
public bool IsPlayed => samplesContainer.Any(s => s.Played);
|
||||||
|
|
||||||
|
public IBindable<double> AggregateVolume => samplesContainer.AggregateVolume;
|
||||||
|
|
||||||
|
public IBindable<double> AggregateBalance => samplesContainer.AggregateBalance;
|
||||||
|
|
||||||
|
public IBindable<double> AggregateFrequency => samplesContainer.AggregateFrequency;
|
||||||
|
|
||||||
|
public IBindable<double> AggregateTempo => samplesContainer.AggregateTempo;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
// 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;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
|
||||||
namespace osu.Game.Users
|
namespace osu.Game.Users
|
||||||
{
|
{
|
||||||
@ -178,6 +181,10 @@ namespace osu.Game.Users
|
|||||||
|
|
||||||
private UserStatistics statistics;
|
private UserStatistics statistics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// User statistics for the requested ruleset (in the case of a <see cref="GetUserRequest"/> response).
|
||||||
|
/// Otherwise empty.
|
||||||
|
/// </summary>
|
||||||
[JsonProperty(@"statistics")]
|
[JsonProperty(@"statistics")]
|
||||||
public UserStatistics Statistics
|
public UserStatistics Statistics
|
||||||
{
|
{
|
||||||
@ -228,14 +235,14 @@ namespace osu.Game.Users
|
|||||||
[JsonProperty("replays_watched_counts")]
|
[JsonProperty("replays_watched_counts")]
|
||||||
public UserHistoryCount[] ReplaysWatchedCounts;
|
public UserHistoryCount[] ReplaysWatchedCounts;
|
||||||
|
|
||||||
public class UserHistoryCount
|
/// <summary>
|
||||||
{
|
/// All user statistics per ruleset's short name (in the case of a <see cref="GetUsersRequest"/> response).
|
||||||
[JsonProperty("start_date")]
|
/// Otherwise empty. Can be altered for testing purposes.
|
||||||
public DateTime Date;
|
/// </summary>
|
||||||
|
// todo: this should likely be moved to a separate UserCompact class at some point.
|
||||||
[JsonProperty("count")]
|
[JsonProperty("statistics_rulesets")]
|
||||||
public long Count;
|
[CanBeNull]
|
||||||
}
|
public Dictionary<string, UserStatistics> RulesetsStatistics { get; set; }
|
||||||
|
|
||||||
public override string ToString() => Username;
|
public override string ToString() => Username;
|
||||||
|
|
||||||
@ -249,6 +256,14 @@ namespace osu.Game.Users
|
|||||||
Id = 0
|
Id = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public bool Equals(User other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, other)) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
|
||||||
|
return Id == other.Id;
|
||||||
|
}
|
||||||
|
|
||||||
public enum PlayStyle
|
public enum PlayStyle
|
||||||
{
|
{
|
||||||
[Description("Keyboard")]
|
[Description("Keyboard")]
|
||||||
@ -264,12 +279,13 @@ namespace osu.Game.Users
|
|||||||
Touch,
|
Touch,
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(User other)
|
public class UserHistoryCount
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, other)) return false;
|
[JsonProperty("start_date")]
|
||||||
if (ReferenceEquals(this, other)) return true;
|
public DateTime Date;
|
||||||
|
|
||||||
return Id == other.Id;
|
[JsonProperty("count")]
|
||||||
|
public long Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,17 +26,24 @@ namespace osu.Game.Users
|
|||||||
public int Progress;
|
public int Progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty(@"pp")]
|
[JsonProperty(@"global_rank")]
|
||||||
public decimal? PP;
|
public int? GlobalRank;
|
||||||
|
|
||||||
[JsonProperty(@"pp_rank")] // the API sometimes only returns this value in condensed user responses
|
public int? CountryRank;
|
||||||
private int? rank
|
|
||||||
{
|
|
||||||
set => Ranks.Global = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty(@"rank")]
|
[JsonProperty(@"rank")]
|
||||||
public UserRanks Ranks;
|
private UserRanks ranks
|
||||||
|
{
|
||||||
|
// eventually that will also become an own json property instead of reading from a `rank` object.
|
||||||
|
// see https://github.com/ppy/osu-web/blob/cb79bb72186c8f1a25f6a6f5ef315123decb4231/app/Transformers/UserStatisticsTransformer.php#L53.
|
||||||
|
set => CountryRank = value.Country;
|
||||||
|
}
|
||||||
|
|
||||||
|
// populated via User model, as that's where the data currently lives.
|
||||||
|
public RankHistoryData RankHistory;
|
||||||
|
|
||||||
|
[JsonProperty(@"pp")]
|
||||||
|
public decimal? PP;
|
||||||
|
|
||||||
[JsonProperty(@"ranked_score")]
|
[JsonProperty(@"ranked_score")]
|
||||||
public long RankedScore;
|
public long RankedScore;
|
||||||
@ -113,15 +120,12 @@ namespace osu.Game.Users
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct UserRanks
|
#pragma warning disable 649
|
||||||
|
private struct UserRanks
|
||||||
{
|
{
|
||||||
[JsonProperty(@"global")]
|
|
||||||
public int? Global;
|
|
||||||
|
|
||||||
[JsonProperty(@"country")]
|
[JsonProperty(@"country")]
|
||||||
public int? Country;
|
public int? Country;
|
||||||
}
|
}
|
||||||
|
#pragma warning restore 649
|
||||||
public RankHistoryData RankHistory;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.215.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.222.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
||||||
<PackageReference Include="Sentry" Version="3.0.1" />
|
<PackageReference Include="Sentry" Version="3.0.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.27.1" />
|
<PackageReference Include="SharpCompress" Version="0.27.1" />
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.215.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.222.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
@ -91,7 +91,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.215.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.222.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.27.1" />
|
<PackageReference Include="SharpCompress" Version="0.27.1" />
|
||||||
<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" />
|
||||||
|
Loading…
Reference in New Issue
Block a user