1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 12:17:26 +08:00

Merge remote-tracking branch 'origin/leaderboard-toggle' into leaderboard-toggle

This commit is contained in:
QuantumSno 2023-08-12 00:19:48 -04:00
commit e9bb9434e9
88 changed files with 642 additions and 541 deletions

View File

@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
@ -25,6 +26,22 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly List<Stage> stages = new List<Stage>();
public override Quad SkinnableComponentScreenSpaceDrawQuad
{
get
{
if (Stages.Count == 1)
return Stages.First().ScreenSpaceDrawQuad;
RectangleF area = RectangleF.Empty;
foreach (var stage in Stages)
area = RectangleF.Union(area, stage.ScreenSpaceDrawQuad.AABBFloat);
return area;
}
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => stages.Any(s => s.ReceivePositionalInputAt(screenSpacePos));
public ManiaPlayfield(List<StageDefinition> stageDefinitions)

View File

@ -125,13 +125,13 @@ namespace osu.Game.Tests.Visual.Background
createFakeStoryboard();
AddStep("Enable Storyboard", () =>
{
player.ReplacesBackground.Value = true;
player.StoryboardReplacesBackground.Value = true;
player.StoryboardEnabled.Value = true;
});
AddUntilStep("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible);
AddUntilStep("Background is black, storyboard is visible", () => songSelect.IsBackgroundVisible() && songSelect.IsBackgroundBlack() && player.IsStoryboardVisible);
AddStep("Disable Storyboard", () =>
{
player.ReplacesBackground.Value = false;
player.StoryboardReplacesBackground.Value = false;
player.StoryboardEnabled.Value = false;
});
AddUntilStep("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible);
@ -173,7 +173,7 @@ namespace osu.Game.Tests.Visual.Background
createFakeStoryboard();
AddStep("Enable Storyboard", () =>
{
player.ReplacesBackground.Value = true;
player.StoryboardReplacesBackground.Value = true;
player.StoryboardEnabled.Value = true;
});
AddStep("Enable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = false);
@ -188,7 +188,7 @@ namespace osu.Game.Tests.Visual.Background
{
performFullSetup();
createFakeStoryboard();
AddStep("Enable replacing background", () => player.ReplacesBackground.Value = true);
AddStep("Enable replacing background", () => player.StoryboardReplacesBackground.Value = true);
AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible);
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible());
@ -199,11 +199,11 @@ namespace osu.Game.Tests.Visual.Background
player.DimmableStoryboard.IgnoreUserSettings.Value = true;
});
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
AddUntilStep("Background is invisible", () => songSelect.IsBackgroundInvisible());
AddUntilStep("Background is dimmed", () => songSelect.IsBackgroundVisible() && songSelect.IsBackgroundBlack());
AddStep("Disable background replacement", () => player.ReplacesBackground.Value = false);
AddStep("Disable background replacement", () => player.StoryboardReplacesBackground.Value = false);
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible());
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible() && !songSelect.IsBackgroundBlack());
}
/// <summary>
@ -257,7 +257,7 @@ namespace osu.Game.Tests.Visual.Background
private void createFakeStoryboard() => AddStep("Create storyboard", () =>
{
player.StoryboardEnabled.Value = false;
player.ReplacesBackground.Value = false;
player.StoryboardReplacesBackground.Value = false;
player.DimmableStoryboard.Add(new OsuSpriteText
{
Size = new Vector2(500, 50),
@ -323,6 +323,8 @@ namespace osu.Game.Tests.Visual.Background
config.BindWith(OsuSetting.BlurLevel, BlurLevel);
}
public bool IsBackgroundBlack() => background.CurrentColour == OsuColour.Gray(0);
public bool IsBackgroundDimmed() => background.CurrentColour == OsuColour.Gray(1f - background.CurrentDim);
public bool IsBackgroundUndimmed() => background.CurrentColour == Color4.White;
@ -331,8 +333,6 @@ namespace osu.Game.Tests.Visual.Background
public bool IsUserBlurDisabled() => background.CurrentBlur == new Vector2(0);
public bool IsBackgroundInvisible() => background.CurrentAlpha == 0;
public bool IsBackgroundVisible() => background.CurrentAlpha == 1;
public bool IsBackgroundBlur() => Precision.AlmostEquals(background.CurrentBlur, new Vector2(BACKGROUND_BLUR), 0.1f);
@ -367,7 +367,7 @@ namespace osu.Game.Tests.Visual.Background
{
base.OnEntering(e);
ApplyToBackground(b => ReplacesBackground.BindTo(b.StoryboardReplacesBackground));
ApplyToBackground(b => StoryboardReplacesBackground.BindTo(b.StoryboardReplacesBackground));
}
public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard;
@ -376,7 +376,7 @@ namespace osu.Game.Tests.Visual.Background
public bool BlockLoad;
public Bindable<bool> StoryboardEnabled;
public readonly Bindable<bool> ReplacesBackground = new Bindable<bool>();
public readonly Bindable<bool> StoryboardReplacesBackground = new Bindable<bool>();
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
public LoadBlockingTestPlayer(bool allowPause = true)

View File

@ -18,6 +18,7 @@ using osu.Game.Database;
using osu.Game.Overlays.Dialog;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
@ -453,6 +454,51 @@ namespace osu.Game.Tests.Visual.Editing
});
}
[Test]
public void TestExitBlockedWhenSavingBeatmapWithSameNamedDifficulties()
{
Guid setId = Guid.Empty;
const string duplicate_difficulty_name = "duplicate";
AddStep("retrieve set ID", () => setId = EditorBeatmap.BeatmapInfo.BeatmapSet!.ID);
AddStep("set difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = duplicate_difficulty_name);
AddStep("save beatmap", () => Editor.Save());
AddAssert("new beatmap persisted", () =>
{
var set = beatmapManager.QueryBeatmapSet(s => s.ID == setId);
return set != null && set.PerformRead(s => s.Beatmaps.Count == 1 && s.Files.Count == 1);
});
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new CatchRuleset().RulesetInfo));
AddUntilStep("wait for created", () =>
{
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
return difficultyName != null && difficultyName != duplicate_difficulty_name;
});
AddUntilStep("wait for editor load", () => Editor.IsLoaded && DialogOverlay.IsLoaded);
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[]
{
new Fruit
{
StartTime = 0
},
new Fruit
{
StartTime = 1000
}
}));
AddStep("set difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = duplicate_difficulty_name);
AddUntilStep("wait for has unsaved changes", () => Editor.HasUnsavedChanges);
AddStep("exit", () => Editor.Exit());
AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is PromptForSaveDialog);
AddStep("attempt to save", () => DialogOverlay.CurrentDialog.PerformOkAction());
AddAssert("editor is still current", () => Editor.IsCurrentScreen());
}
[Test]
public void TestCreateNewDifficultyForInconvertibleRuleset()
{

View File

@ -8,6 +8,7 @@ using osu.Game.Overlays;
using osu.Game.Overlays.SkinEditor;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay
{
@ -19,7 +20,9 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestToggleEditor()
{
AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox
var skinComponentsContainer = new SkinComponentsContainer(new SkinComponentsContainerLookup(SkinComponentsContainerLookup.TargetArea.SongSelect));
AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox(skinComponentsContainer, null)
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,

View File

@ -67,6 +67,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[TestCase(1)]
[TestCase(4)]
[TestCase(9)]
public void TestGeneral(int count)
{
int[] userIds = getPlayerIds(count);
@ -78,6 +79,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddWaitStep("wait a bit", 20);
}
[Test]
public void TestMultipleStartRequests()
{
int[] userIds = getPlayerIds(2);
start(userIds);
loadSpectateScreen();
sendFrames(userIds, 1000);
AddWaitStep("wait a bit", 20);
start(userIds);
}
[Test]
public void TestDelayedStart()
{

View File

@ -154,7 +154,14 @@ namespace osu.Game.Tests.Visual.Online
Type = ChangelogEntryType.Misc,
Category = "Code quality",
Title = "Clean up another thing"
}
},
new APIChangelogEntry
{
Type = ChangelogEntryType.Add,
Category = "osu!",
Title = "Add entry with news url",
Url = "https://osu.ppy.sh/home/news/2023-07-27-summer-splash"
},
}
});

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using NUnit.Framework;
using osu.Game.Tests.Visual;
using osu.Game.Tournament.Components;
@ -13,7 +11,7 @@ namespace osu.Game.Tournament.Tests.Components
{
public partial class TestSceneDateTextBox : OsuManualInputManagerTestScene
{
private DateTextBox textBox;
private DateTextBox textBox = null!;
[SetUp]
public void Setup() => Schedule(() =>

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@ -22,7 +20,7 @@ namespace osu.Game.Tournament.Tests.Components
[Test]
public void TestSongBar()
{
SongBar songBar = null;
SongBar songBar = null!;
AddStep("create bar", () => Child = songBar = new SongBar
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -19,12 +17,12 @@ namespace osu.Game.Tournament.Tests.Components
public partial class TestSceneTournamentModDisplay : TournamentTestScene
{
[Resolved]
private IAPIProvider api { get; set; }
private IAPIProvider api { get; set; } = null!;
[Resolved]
private IRulesetStore rulesets { get; set; }
private IRulesetStore rulesets { get; set; } = null!;
private FillFlowContainer<TournamentBeatmapPanel> fillFlow;
private FillFlowContainer<TournamentBeatmapPanel> fillFlow = null!;
[BackgroundDependencyLoader]
private void load()
@ -45,7 +43,7 @@ namespace osu.Game.Tournament.Tests.Components
private void success(APIBeatmap beatmap)
{
var ruleset = rulesets.GetRuleset(Ladder.Ruleset.Value.OnlineID);
var ruleset = rulesets.GetRuleset(Ladder.Ruleset.Value?.OnlineID ?? -1);
if (ruleset == null)
return;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.IO;
using System.Threading.Tasks;
@ -81,11 +79,11 @@ namespace osu.Game.Tournament.Tests.NonVisual
public partial class TestTournament : TournamentGameBase
{
private readonly bool resetRuleset;
private readonly Action runOnLoadComplete;
private readonly Action? runOnLoadComplete;
public new Task BracketLoadTask => base.BracketLoadTask;
public TestTournament(bool resetRuleset = false, [InstantHandle] Action runOnLoadComplete = null)
public TestTournament(bool resetRuleset = false, [InstantHandle] Action? runOnLoadComplete = null)
{
this.resetRuleset = resetRuleset;
this.runOnLoadComplete = runOnLoadComplete;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.IO;
using System.Linq;
using NUnit.Framework;
@ -36,11 +34,11 @@ namespace osu.Game.Tournament.Tests.NonVisual
{
var osu = LoadTournament(host);
TournamentStorage storage = (TournamentStorage)osu.Dependencies.Get<Storage>();
FileBasedIPC ipc = null;
FileBasedIPC? ipc = null;
WaitForOrAssert(() => (ipc = osu.Dependencies.Get<MatchIPCInfo>() as FileBasedIPC)?.IsLoaded == true, @"ipc could not be populated in a reasonable amount of time");
Assert.True(ipc.SetIPCLocation(testStableInstallDirectory));
Assert.True(ipc!.SetIPCLocation(testStableInstallDirectory));
Assert.True(storage.AllTournaments.Exists("stable.json"));
}
finally

View File

@ -35,8 +35,8 @@ namespace osu.Game.Tournament.Tests.NonVisual
PlayersPerTeam = { Value = 4 },
Teams =
{
match.Team1.Value,
match.Team2.Value,
match.Team1.Value!,
match.Team2.Value!,
},
Rounds =
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Threading;
using System.Threading.Tasks;
@ -13,7 +11,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
{
public abstract class TournamentHostTest
{
public static TournamentGameBase LoadTournament(GameHost host, TournamentGameBase tournament = null)
public static TournamentGameBase LoadTournament(GameHost host, TournamentGameBase? tournament = null)
{
tournament ??= new TournamentGameBase();
Task.Factory.StartNew(() => host.Run(tournament), TaskCreationOptions.LongRunning)

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;

View File

@ -94,7 +94,7 @@ namespace osu.Game.Tournament.Tests.Screens
AddStep("release mouse button", () => InputManager.ReleaseButton(MouseButton.Left));
AddAssert("assert ladder teams reset", () => Ladder.CurrentMatch.Value.Team1.Value == null && Ladder.CurrentMatch.Value.Team2.Value == null);
AddAssert("assert ladder teams reset", () => Ladder.CurrentMatch.Value?.Team1.Value == null && Ladder.CurrentMatch.Value?.Team2.Value == null);
}
}
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
@ -16,7 +14,7 @@ namespace osu.Game.Tournament.Tests.Screens
{
public partial class TestSceneMapPoolScreen : TournamentScreenTestScene
{
private MapPoolScreen screen;
private MapPoolScreen screen = null!;
[BackgroundDependencyLoader]
private void load()
@ -32,7 +30,7 @@ namespace osu.Game.Tournament.Tests.Screens
{
AddStep("load few maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
for (int i = 0; i < 8; i++)
addBeatmap();
@ -52,7 +50,7 @@ namespace osu.Game.Tournament.Tests.Screens
{
AddStep("load just enough maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
for (int i = 0; i < 18; i++)
addBeatmap();
@ -72,7 +70,7 @@ namespace osu.Game.Tournament.Tests.Screens
{
AddStep("load many maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
for (int i = 0; i < 19; i++)
addBeatmap();
@ -92,7 +90,7 @@ namespace osu.Game.Tournament.Tests.Screens
{
AddStep("load many maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
for (int i = 0; i < 11; i++)
addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM");
@ -118,7 +116,7 @@ namespace osu.Game.Tournament.Tests.Screens
{
AddStep("load many maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
for (int i = 0; i < 12; i++)
addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM");
@ -138,7 +136,7 @@ namespace osu.Game.Tournament.Tests.Screens
{
AddStep("load many maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
for (int i = 0; i < 12; i++)
addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM");
@ -155,7 +153,7 @@ namespace osu.Game.Tournament.Tests.Screens
private void addBeatmap(string mods = "NM")
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Add(new RoundBeatmap
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Add(new RoundBeatmap
{
Beatmap = CreateSampleBeatmap(),
Mods = mods

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using NUnit.Framework;
using osu.Framework.Allocation;

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Editors;
@ -17,7 +18,7 @@ namespace osu.Game.Tournament.Tests.Screens
{
var match = CreateSampleMatch();
Add(new SeedingEditorScreen(match.Team1.Value, new TeamEditorScreen())
Add(new SeedingEditorScreen(match.Team1.Value.AsNonNull(), new TeamEditorScreen())
{
Width = 0.85f // create room for control panel
});

View File

@ -17,7 +17,7 @@ namespace osu.Game.Tournament.Tests.Screens
{
var match = Ladder.CurrentMatch.Value!;
match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals");
match.Round.Value = Ladder.Rounds.First(g => g.Name.Value == "Quarterfinals");
match.Completed.Value = true;
});

View File

@ -40,10 +40,10 @@ namespace osu.Game.Tournament.Tests
match = CreateSampleMatch();
Ladder.Rounds.Add(match.Round.Value);
Ladder.Rounds.Add(match.Round.Value!);
Ladder.Matches.Add(match);
Ladder.Teams.Add(match.Team1.Value);
Ladder.Teams.Add(match.Team2.Value);
Ladder.Teams.Add(match.Team1.Value!);
Ladder.Teams.Add(match.Team2.Value!);
Ruleset.BindTo(Ladder.Ruleset);
Dependencies.CacheAs(new StableInfo(storage));
@ -152,7 +152,7 @@ namespace osu.Game.Tournament.Tests
},
Round =
{
Value = new TournamentRound { Name = { Value = "Quarterfinals" } }
Value = new TournamentRound { Name = { Value = "Quarterfinals" } },
}
};

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -17,14 +15,14 @@ namespace osu.Game.Tournament.Components
{
public partial class DrawableTeamFlag : Container
{
private readonly TournamentTeam team;
private readonly TournamentTeam? team;
[UsedImplicitly]
private Bindable<string> flag;
private Bindable<string>? flag;
private Sprite flagSprite;
private Sprite? flagSprite;
public DrawableTeamFlag(TournamentTeam team)
public DrawableTeamFlag(TournamentTeam? team)
{
this.team = team;
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -12,12 +10,12 @@ namespace osu.Game.Tournament.Components
{
public partial class DrawableTeamTitle : TournamentSpriteTextWithBackground
{
private readonly TournamentTeam team;
private readonly TournamentTeam? team;
[UsedImplicitly]
private Bindable<string> acronym;
private Bindable<string>? acronym;
public DrawableTeamTitle(TournamentTeam team)
public DrawableTeamTitle(TournamentTeam? team)
{
this.team = team;
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -14,15 +12,15 @@ namespace osu.Game.Tournament.Components
{
public abstract partial class DrawableTournamentTeam : CompositeDrawable
{
public readonly TournamentTeam Team;
public readonly TournamentTeam? Team;
protected readonly Container Flag;
protected readonly TournamentSpriteText AcronymText;
[UsedImplicitly]
private Bindable<string> acronym;
private Bindable<string>? acronym;
protected DrawableTournamentTeam(TournamentTeam team)
protected DrawableTournamentTeam(TournamentTeam? team)
{
Team = team;
@ -36,7 +34,8 @@ namespace osu.Game.Tournament.Components
[BackgroundDependencyLoader]
private void load()
{
if (Team == null) return;
if (Team == null)
return;
(acronym = Team.Acronym.GetBoundCopy()).BindValueChanged(_ => AcronymText.Text = Team?.Acronym.Value?.ToUpperInvariant() ?? string.Empty, true);
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -24,14 +22,14 @@ namespace osu.Game.Tournament.Components
{
public partial class SongBar : CompositeDrawable
{
private TournamentBeatmap beatmap;
private TournamentBeatmap? beatmap;
public const float HEIGHT = 145 / 2f;
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
public TournamentBeatmap Beatmap
public TournamentBeatmap? Beatmap
{
set
{
@ -55,7 +53,7 @@ namespace osu.Game.Tournament.Components
}
}
private FillFlowContainer flow;
private FillFlowContainer flow = null!;
private bool expanded;

View File

@ -146,7 +146,12 @@ namespace osu.Game.Tournament.Components
private void updateState()
{
var newChoice = currentMatch.Value?.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap?.OnlineID);
if (currentMatch.Value == null)
{
return;
}
var newChoice = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap?.OnlineID);
bool shouldFlash = newChoice != choice;

View File

@ -92,9 +92,9 @@ namespace osu.Game.Tournament.Components
{
if (info.CurrentMatch.Value is TournamentMatch match)
{
if (match.Team1.Value.Players.Any(u => u.OnlineID == Message.Sender.OnlineID))
if (match.Team1.Value?.Players.Any(u => u.OnlineID == Message.Sender.OnlineID) == true)
UsernameColour = TournamentGame.COLOUR_RED;
else if (match.Team2.Value.Players.Any(u => u.OnlineID == Message.Sender.OnlineID))
else if (match.Team2.Value?.Players.Any(u => u.OnlineID == Message.Sender.OnlineID) == true)
UsernameColour = TournamentGame.COLOUR_BLUE;
}
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@ -19,8 +17,8 @@ namespace osu.Game.Tournament.Components
{
private readonly string filename;
private readonly bool drawFallbackGradient;
private Video video;
private ManualClock manualClock;
private Video? video;
private ManualClock? manualClock;
public bool VideoAvailable => video != null;

View File

@ -1,12 +1,9 @@
// 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.
#nullable disable
using System;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Win32;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
@ -24,36 +21,35 @@ namespace osu.Game.Tournament.IPC
{
public partial class FileBasedIPC : MatchIPCInfo
{
public Storage IPCStorage { get; private set; }
public Storage? IPCStorage { get; private set; }
[Resolved]
protected IAPIProvider API { get; private set; }
protected IAPIProvider API { get; private set; } = null!;
[Resolved]
protected IRulesetStore Rulesets { get; private set; }
protected IRulesetStore Rulesets { get; private set; } = null!;
[Resolved]
private GameHost host { get; set; }
private GameHost host { get; set; } = null!;
[Resolved]
private LadderInfo ladder { get; set; }
private LadderInfo ladder { get; set; } = null!;
[Resolved]
private StableInfo stableInfo { get; set; }
private StableInfo stableInfo { get; set; } = null!;
private int lastBeatmapId;
private ScheduledDelegate scheduled;
private GetBeatmapRequest beatmapLookupRequest;
private ScheduledDelegate? scheduled;
private GetBeatmapRequest? beatmapLookupRequest;
[BackgroundDependencyLoader]
private void load()
{
string stablePath = stableInfo.StablePath ?? findStablePath();
string? stablePath = stableInfo.StablePath ?? findStablePath();
initialiseIPCStorage(stablePath);
}
[CanBeNull]
private Storage initialiseIPCStorage(string path)
private Storage? initialiseIPCStorage(string? path)
{
scheduled?.Cancel();
@ -89,7 +85,7 @@ namespace osu.Game.Tournament.IPC
lastBeatmapId = beatmapId;
var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.Beatmap != null);
var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId);
if (existing != null)
Beatmap.Value = existing.Beatmap;
@ -97,6 +93,7 @@ namespace osu.Game.Tournament.IPC
{
beatmapLookupRequest = new GetBeatmapRequest(new APIBeatmap { OnlineID = beatmapId });
beatmapLookupRequest.Success += b => Beatmap.Value = new TournamentBeatmap(b);
beatmapLookupRequest.Failure += _ => Beatmap.Value = null;
API.Queue(beatmapLookupRequest);
}
}
@ -114,7 +111,7 @@ namespace osu.Game.Tournament.IPC
using (var stream = IPCStorage.GetStream(file_ipc_channel_filename))
using (var sr = new StreamReader(stream))
{
ChatChannel.Value = sr.ReadLine();
ChatChannel.Value = sr.ReadLine().AsNonNull();
}
}
catch (Exception)
@ -140,8 +137,8 @@ namespace osu.Game.Tournament.IPC
using (var stream = IPCStorage.GetStream(file_ipc_scores_filename))
using (var sr = new StreamReader(stream))
{
Score1.Value = int.Parse(sr.ReadLine());
Score2.Value = int.Parse(sr.ReadLine());
Score1.Value = int.Parse(sr.ReadLine().AsNonNull());
Score2.Value = int.Parse(sr.ReadLine().AsNonNull());
}
}
catch (Exception)
@ -164,7 +161,7 @@ namespace osu.Game.Tournament.IPC
/// </summary>
/// <param name="path">Path to the IPC directory</param>
/// <returns>Whether the supplied path was a valid IPC directory.</returns>
public bool SetIPCLocation(string path)
public bool SetIPCLocation(string? path)
{
if (path == null || !ipcFileExistsInDirectory(path))
return false;
@ -184,29 +181,28 @@ namespace osu.Game.Tournament.IPC
/// <returns>Whether an IPC directory was successfully auto-detected.</returns>
public bool AutoDetectIPCLocation() => SetIPCLocation(findStablePath());
private static bool ipcFileExistsInDirectory(string p) => p != null && File.Exists(Path.Combine(p, "ipc.txt"));
private static bool ipcFileExistsInDirectory(string? p) => p != null && File.Exists(Path.Combine(p, "ipc.txt"));
[CanBeNull]
private string findStablePath()
private string? findStablePath()
{
string stableInstallPath = findFromEnvVar() ??
findFromRegistry() ??
findFromLocalAppData() ??
findFromDotFolder();
string? stableInstallPath = findFromEnvVar() ??
findFromRegistry() ??
findFromLocalAppData() ??
findFromDotFolder();
Logger.Log($"Stable path for tourney usage: {stableInstallPath}");
return stableInstallPath;
}
private string findFromEnvVar()
private string? findFromEnvVar()
{
try
{
Logger.Log("Trying to find stable with environment variables");
string stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH");
string? stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH");
if (ipcFileExistsInDirectory(stableInstallPath))
return stableInstallPath;
return stableInstallPath!;
}
catch
{
@ -215,7 +211,7 @@ namespace osu.Game.Tournament.IPC
return null;
}
private string findFromLocalAppData()
private string? findFromLocalAppData()
{
Logger.Log("Trying to find stable in %LOCALAPPDATA%");
string stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
@ -226,7 +222,7 @@ namespace osu.Game.Tournament.IPC
return null;
}
private string findFromDotFolder()
private string? findFromDotFolder()
{
Logger.Log("Trying to find stable in dotfolders");
string stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
@ -237,16 +233,16 @@ namespace osu.Game.Tournament.IPC
return null;
}
private string findFromRegistry()
private string? findFromRegistry()
{
Logger.Log("Trying to find stable in registry");
try
{
string stableInstallPath;
string? stableInstallPath;
#pragma warning disable CA1416
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
using (RegistryKey? key = Registry.ClassesRoot.OpenSubKey("osu"))
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", "");
#pragma warning restore CA1416

View File

@ -10,7 +10,7 @@ namespace osu.Game.Tournament.IPC
{
public partial class MatchIPCInfo : Component
{
public Bindable<TournamentBeatmap> Beatmap { get; } = new Bindable<TournamentBeatmap>();
public Bindable<TournamentBeatmap?> Beatmap { get; } = new Bindable<TournamentBeatmap?>();
public Bindable<LegacyMods> Mods { get; } = new Bindable<LegacyMods>();
public Bindable<TourneyState> State { get; } = new Bindable<TourneyState>();
public Bindable<string> ChatChannel { get; } = new Bindable<string>();

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Diagnostics;
using System.Drawing;
@ -28,7 +26,7 @@ namespace osu.Game.Tournament
if (reader.TokenType != JsonToken.StartObject)
{
// if there's no object present then this is using string representation (System.Drawing.Point serializes to "x,y")
string str = (string)reader.Value;
string? str = (string?)reader.Value;
Debug.Assert(str != null);
@ -45,9 +43,12 @@ namespace osu.Game.Tournament
if (reader.TokenType == JsonToken.PropertyName)
{
string name = reader.Value?.ToString();
string? name = reader.Value?.ToString();
int? val = reader.ReadAsInt32();
if (name == null)
continue;
if (val == null)
continue;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
@ -17,7 +15,7 @@ namespace osu.Game.Tournament.Models
[Serializable]
public class LadderInfo
{
public Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
public Bindable<RulesetInfo?> Ruleset = new Bindable<RulesetInfo?>();
public BindableList<TournamentMatch> Matches = new BindableList<TournamentMatch>();
public BindableList<TournamentRound> Rounds = new BindableList<TournamentRound>();
@ -27,7 +25,7 @@ namespace osu.Game.Tournament.Models
public List<TournamentProgression> Progressions = new List<TournamentProgression>();
[JsonIgnore] // updated manually in TournamentGameBase
public Bindable<TournamentMatch> CurrentMatch = new Bindable<TournamentMatch>();
public Bindable<TournamentMatch?> CurrentMatch = new Bindable<TournamentMatch?>();
public Bindable<int> ChromaKeyWidth = new BindableInt(1024)
{

View File

@ -8,7 +8,6 @@ namespace osu.Game.Tournament.Models
public class RoundBeatmap
{
public int ID;
public string Mods = string.Empty;
[JsonProperty("BeatmapInfo")]

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.IO;
using Newtonsoft.Json;
@ -20,12 +18,12 @@ namespace osu.Game.Tournament.Models
/// <summary>
/// Path to the IPC directory used by the stable (cutting-edge) install.
/// </summary>
public string StablePath { get; set; }
public string? StablePath { get; set; }
/// <summary>
/// Fired whenever stable info is successfully saved to file.
/// </summary>
public event Action OnStableInfoSaved;
public event Action? OnStableInfoSaved;
private const string config_path = "stable.json";

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@ -33,16 +31,16 @@ namespace osu.Game.Tournament.Models
}
[JsonIgnore]
public readonly Bindable<TournamentTeam> Team1 = new Bindable<TournamentTeam>();
public readonly Bindable<TournamentTeam?> Team1 = new Bindable<TournamentTeam?>();
public string Team1Acronym;
public string? Team1Acronym;
public readonly Bindable<int?> Team1Score = new Bindable<int?>();
[JsonIgnore]
public readonly Bindable<TournamentTeam> Team2 = new Bindable<TournamentTeam>();
public readonly Bindable<TournamentTeam?> Team2 = new Bindable<TournamentTeam?>();
public string Team2Acronym;
public string? Team2Acronym;
public readonly Bindable<int?> Team2Score = new Bindable<int?>();
@ -53,13 +51,13 @@ namespace osu.Game.Tournament.Models
public readonly ObservableCollection<BeatmapChoice> PicksBans = new ObservableCollection<BeatmapChoice>();
[JsonIgnore]
public readonly Bindable<TournamentRound> Round = new Bindable<TournamentRound>();
public readonly Bindable<TournamentRound?> Round = new Bindable<TournamentRound?>();
[JsonIgnore]
public readonly Bindable<TournamentMatch> Progression = new Bindable<TournamentMatch>();
public readonly Bindable<TournamentMatch?> Progression = new Bindable<TournamentMatch?>();
[JsonIgnore]
public readonly Bindable<TournamentMatch> LosersProgression = new Bindable<TournamentMatch>();
public readonly Bindable<TournamentMatch?> LosersProgression = new Bindable<TournamentMatch?>();
/// <summary>
/// Should not be set directly. Use LadderInfo.CurrentMatch.Value = this instead.
@ -79,7 +77,7 @@ namespace osu.Game.Tournament.Models
Team2.BindValueChanged(t => Team2Acronym = t.NewValue?.Acronym.Value, true);
}
public TournamentMatch(TournamentTeam team1 = null, TournamentTeam team2 = null)
public TournamentMatch(TournamentTeam? team1 = null, TournamentTeam? team2 = null)
: this()
{
Team1.Value = team1;
@ -87,10 +85,10 @@ namespace osu.Game.Tournament.Models
}
[JsonIgnore]
public TournamentTeam Winner => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value;
public TournamentTeam? Winner => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value;
[JsonIgnore]
public TournamentTeam Loser => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team2.Value : Team1.Value;
public TournamentTeam? Loser => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team2.Value : Team1.Value;
public TeamColour WinnerColour => Winner == Team1.Value ? TeamColour.Red : TeamColour.Blue;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Linq;
using Newtonsoft.Json;
@ -39,7 +37,7 @@ namespace osu.Game.Tournament.Models
{
int[] ranks = Players.Select(p => p.Rank)
.Where(i => i.HasValue)
.Select(i => i.Value)
.Select(i => i!.Value)
.ToArray();
if (ranks.Length == 0)
@ -66,14 +64,14 @@ namespace osu.Game.Tournament.Models
{
// use a sane default flag name based on acronym.
if (val.OldValue.StartsWith(FlagName.Value, StringComparison.InvariantCultureIgnoreCase))
FlagName.Value = val.NewValue.Length >= 2 ? val.NewValue?.Substring(0, 2).ToUpperInvariant() : string.Empty;
FlagName.Value = val.NewValue?.Length >= 2 ? val.NewValue.Substring(0, 2).ToUpperInvariant() : string.Empty;
};
FullName.ValueChanged += val =>
{
// use a sane acronym based on full name.
if (val.OldValue.StartsWith(Acronym.Value, StringComparison.InvariantCultureIgnoreCase))
Acronym.Value = val.NewValue.Length >= 3 ? val.NewValue?.Substring(0, 3).ToUpperInvariant() : string.Empty;
Acronym.Value = val.NewValue?.Length >= 3 ? val.NewValue.Substring(0, 3).ToUpperInvariant() : string.Empty;
};
}

View File

@ -37,7 +37,7 @@ namespace osu.Game.Tournament.Screens
SongBar.Mods = mods.NewValue;
}
private void beatmapChanged(ValueChangedEvent<TournamentBeatmap> beatmap)
private void beatmapChanged(ValueChangedEvent<TournamentBeatmap?> beatmap)
{
SongBar.FadeInFromZero(300, Easing.OutQuint);
SongBar.Beatmap = beatmap.NewValue;

View File

@ -84,7 +84,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
public bool ContainsTeam(string fullName)
{
return allTeams.Any(t => t.Team.FullName.Value == fullName);
return allTeams.Any(t => t.Team?.FullName.Value == fullName);
}
public bool RemoveTeam(TournamentTeam team)
@ -112,7 +112,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
{
StringBuilder sb = new StringBuilder();
foreach (GroupTeam gt in allTeams)
sb.AppendLine(gt.Team.FullName.Value);
sb.AppendLine(gt.Team?.FullName.Value);
return sb.ToString();
}
}

View File

@ -1,12 +1,11 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@ -22,8 +21,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
{
public partial class ScrollingTeamContainer : Container
{
public event Action OnScrollStarted;
public event Action<TournamentTeam> OnSelected;
public event Action? OnScrollStarted;
public event Action<TournamentTeam>? OnSelected;
private readonly List<TournamentTeam> availableTeams = new List<TournamentTeam>();
@ -42,7 +41,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
private double lastTime;
private ScheduledDelegate delayedStateChangeDelegate;
private ScheduledDelegate? delayedStateChangeDelegate;
public ScrollingTeamContainer()
{
@ -117,7 +116,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
if (!Children.Any())
break;
ScrollingTeam closest = null;
ScrollingTeam? closest = null;
foreach (var c in Children)
{
@ -137,9 +136,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
closest = stc;
}
Trace.Assert(closest != null, "closest != null");
Debug.Assert(closest != null, "closest != null");
// ReSharper disable once PossibleNullReferenceException
offset += DrawWidth / 2f - (closest.Position.X + closest.DrawWidth / 2f);
ScrollingTeam st = closest;
@ -147,7 +145,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
availableTeams.RemoveAll(at => at == st.Team);
st.Selected = true;
OnSelected?.Invoke(st.Team);
OnSelected?.Invoke(st.Team.AsNonNull());
delayedStateChangeDelegate = Scheduler.AddDelayed(() => setScrollState(ScrollState.Idle), 10000);
break;
@ -174,7 +172,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
setScrollState(ScrollState.Idle);
}
public void AddTeams(IEnumerable<TournamentTeam> teams)
public void AddTeams(IEnumerable<TournamentTeam>? teams)
{
if (teams == null)
return;
@ -311,6 +309,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
public partial class ScrollingTeam : DrawableTournamentTeam
{
public new TournamentTeam Team => base.Team.AsNonNull();
public const float WIDTH = 58;
public const float HEIGHT = 44;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
@ -39,7 +37,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
{
while (sr.Peek() != -1)
{
string line = sr.ReadLine()?.Trim();
string? line = sr.ReadLine()?.Trim();
if (string.IsNullOrEmpty(line))
continue;
@ -56,7 +54,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
teams.Add(new TournamentTeam
{
FullName = { Value = split[1], },
Acronym = { Value = split.Length >= 3 ? split[2] : null, },
Acronym = { Value = split.Length >= 3 ? split[2] : string.Empty, },
FlagName = { Value = split[0] }
});
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
@ -72,7 +70,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
private float leftPos => -(float)((Time.Current + Offset) / CycleTime) + expiredCount;
private Texture texture;
private Texture texture = null!;
private int expiredCount;

View File

@ -1,14 +1,13 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -28,28 +27,29 @@ namespace osu.Game.Tournament.Screens.Drawings
{
private const string results_filename = "drawings_results.txt";
private ScrollingTeamContainer teamsContainer;
private GroupContainer groupsContainer;
private TournamentSpriteText fullTeamNameText;
private ScrollingTeamContainer teamsContainer = null!;
private GroupContainer groupsContainer = null!;
private TournamentSpriteText fullTeamNameText = null!;
private readonly List<TournamentTeam> allTeams = new List<TournamentTeam>();
private DrawingsConfigManager drawingsConfig;
private DrawingsConfigManager drawingsConfig = null!;
private Task writeOp;
private Task? writeOp;
private Storage storage;
private Storage storage = null!;
public ITeamList TeamList;
public ITeamList TeamList = null!;
[BackgroundDependencyLoader]
private void load(Storage storage)
{
RelativeSizeAxes = Axes.Both;
this.storage = storage;
TeamList ??= new StorageBackedTeamList(storage);
RelativeSizeAxes = Axes.Both;
if (TeamList.IsNull())
TeamList = new StorageBackedTeamList(storage);
if (!TeamList.Teams.Any())
{
@ -251,7 +251,7 @@ namespace osu.Game.Tournament.Screens.Drawings
using (Stream stream = storage.GetStream(results_filename, FileAccess.Read, FileMode.Open))
using (StreamReader sr = new StreamReader(stream))
{
string line;
string? line;
while ((line = sr.ReadLine()?.Trim()) != null)
{
@ -261,8 +261,7 @@ namespace osu.Game.Tournament.Screens.Drawings
if (line.ToUpperInvariant().StartsWith("GROUP", StringComparison.Ordinal))
continue;
// ReSharper disable once AccessToModifiedClosure
TournamentTeam teamToAdd = allTeams.FirstOrDefault(t => t.FullName.Value == line);
TournamentTeam? teamToAdd = allTeams.FirstOrDefault(t => t.FullName.Value == line);
if (teamToAdd == null)
continue;

View File

@ -1,12 +1,9 @@
// 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.
#nullable disable
using System;
using System.Drawing;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -35,13 +32,12 @@ namespace osu.Game.Tournament.Screens.Editors
[Cached]
private LadderEditorInfo editorInfo = new LadderEditorInfo();
private WarningBox rightClickMessage;
private WarningBox rightClickMessage = null!;
private RectangularPositionSnapGrid grid;
private RectangularPositionSnapGrid grid = null!;
[Resolved(canBeNull: true)]
[CanBeNull]
private IDialogOverlay dialogOverlay { get; set; }
[Resolved]
private IDialogOverlay? dialogOverlay { get; set; }
protected override bool DrawLoserPaths => true;
@ -94,36 +90,28 @@ namespace osu.Game.Tournament.Screens.Editors
ScrollContent.Add(new JoinVisualiser(MatchesContainer, match, losers, UpdateLayout));
}
public MenuItem[] ContextMenuItems
{
get
public MenuItem[] ContextMenuItems =>
new MenuItem[]
{
if (editorInfo == null)
return Array.Empty<MenuItem>();
return new MenuItem[]
new OsuMenuItem("Create new match", MenuItemType.Highlighted, () =>
{
new OsuMenuItem("Create new match", MenuItemType.Highlighted, () =>
Vector2 pos = MatchesContainer.Count == 0 ? Vector2.Zero : lastMatchesContainerMouseDownPosition;
TournamentMatch newMatch = new TournamentMatch { Position = { Value = new Point((int)pos.X, (int)pos.Y) } };
LadderInfo.Matches.Add(newMatch);
editorInfo.Selected.Value = newMatch;
}),
new OsuMenuItem("Reset teams", MenuItemType.Destructive, () =>
{
dialogOverlay?.Push(new LadderResetTeamsDialog(() =>
{
Vector2 pos = MatchesContainer.Count == 0 ? Vector2.Zero : lastMatchesContainerMouseDownPosition;
TournamentMatch newMatch = new TournamentMatch { Position = { Value = new Point((int)pos.X, (int)pos.Y) } };
LadderInfo.Matches.Add(newMatch);
editorInfo.Selected.Value = newMatch;
}),
new OsuMenuItem("Reset teams", MenuItemType.Destructive, () =>
{
dialogOverlay?.Push(new LadderResetTeamsDialog(() =>
{
foreach (var p in MatchesContainer)
p.Match.Reset();
}));
})
};
}
}
foreach (var p in MatchesContainer)
p.Match.Reset();
}));
})
};
public void Remove(TournamentMatch match)
{
@ -135,11 +123,11 @@ namespace osu.Game.Tournament.Screens.Editors
private readonly Container<DrawableTournamentMatch> matchesContainer;
public readonly TournamentMatch Source;
private readonly bool losers;
private readonly Action complete;
private readonly Action? complete;
private ProgressionPath path;
private ProgressionPath? path;
public JoinVisualiser(Container<DrawableTournamentMatch> matchesContainer, TournamentMatch source, bool losers, Action complete)
public JoinVisualiser(Container<DrawableTournamentMatch> matchesContainer, TournamentMatch source, bool losers, Action? complete)
{
this.matchesContainer = matchesContainer;
RelativeSizeAxes = Axes.Both;
@ -153,7 +141,7 @@ namespace osu.Game.Tournament.Screens.Editors
Source.Progression.Value = null;
}
private DrawableTournamentMatch findTarget(InputState state)
private DrawableTournamentMatch? findTarget(InputState state)
{
return matchesContainer.FirstOrDefault(d => d.ReceivePositionalInputAt(state.Mouse.Position));
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -14,9 +12,9 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{
public partial class MatchHeader : Container
{
private TeamScoreDisplay teamDisplay1;
private TeamScoreDisplay teamDisplay2;
private DrawableTournamentHeaderLogo logo;
private TeamScoreDisplay teamDisplay1 = null!;
private TeamScoreDisplay teamDisplay2 = null!;
private DrawableTournamentHeaderLogo logo = null!;
private bool showScores = true;

View File

@ -35,7 +35,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
}
}
public TeamDisplay(TournamentTeam team, TeamColour colour, Bindable<int?> currentTeamScore, int pointsToWin)
public TeamDisplay(TournamentTeam? team, TeamColour colour, Bindable<int?> currentTeamScore, int pointsToWin)
: base(team)
{
AutoSizeAxes = Axes.Both;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -17,16 +15,22 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{
private readonly TeamColour teamColour;
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
private readonly Bindable<TournamentTeam> currentTeam = new Bindable<TournamentTeam>();
private readonly Bindable<TournamentMatch?> currentMatch = new Bindable<TournamentMatch?>();
private readonly Bindable<TournamentTeam?> currentTeam = new Bindable<TournamentTeam?>();
private readonly Bindable<int?> currentTeamScore = new Bindable<int?>();
private TeamDisplay teamDisplay;
private TeamDisplay? teamDisplay;
public bool ShowScore
{
get => teamDisplay.ShowScore;
set => teamDisplay.ShowScore = value;
get => teamDisplay?.ShowScore ?? false;
set
{
if (teamDisplay != null)
{
teamDisplay.ShowScore = value;
}
}
}
public TeamScoreDisplay(TeamColour teamColour)
@ -48,7 +52,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
updateMatch();
}
private void matchChanged(ValueChangedEvent<TournamentMatch> match)
private void matchChanged(ValueChangedEvent<TournamentMatch?> match)
{
currentTeamScore.UnbindBindings();
currentTeam.UnbindBindings();
@ -78,7 +82,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
switch (e.Button)
{
case MouseButton.Left:
if (currentTeamScore.Value < currentMatch.Value.PointsToWin)
if (currentTeamScore.Value < currentMatch.Value?.PointsToWin)
currentTeamScore.Value++;
return true;
@ -91,7 +95,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
return base.OnMouseDown(e);
}
private void teamChanged(ValueChangedEvent<TournamentTeam> team)
private void teamChanged(ValueChangedEvent<TournamentTeam?> team)
{
bool wasShowingScores = teamDisplay?.ShowScore ?? false;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -146,7 +144,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
private partial class MatchScoreCounter : CommaSeparatedScoreCounter
{
private OsuSpriteText displayedSpriteText;
private OsuSpriteText displayedSpriteText = null!;
public MatchScoreCounter()
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -26,16 +24,16 @@ namespace osu.Game.Tournament.Screens.Gameplay
private readonly BindableBool warmup = new BindableBool();
public readonly Bindable<TourneyState> State = new Bindable<TourneyState>();
private OsuButton warmupButton;
private MatchIPCInfo ipc;
[Resolved(canBeNull: true)]
private TournamentSceneManager sceneManager { get; set; }
private OsuButton warmupButton = null!;
private MatchIPCInfo ipc = null!;
[Resolved]
private TournamentMatchChatDisplay chat { get; set; }
private TournamentSceneManager? sceneManager { get; set; }
private Drawable chroma;
[Resolved]
private TournamentMatchChatDisplay chat { get; set; } = null!;
private Drawable chroma = null!;
[BackgroundDependencyLoader]
private void load(LadderInfo ladder, MatchIPCInfo ipc)
@ -142,7 +140,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
State.BindValueChanged(_ => updateState(), true);
}
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch?> match)
{
base.CurrentMatchChanged(match);
@ -153,29 +151,35 @@ namespace osu.Game.Tournament.Screens.Gameplay
scheduledScreenChange?.Cancel();
}
private ScheduledDelegate scheduledScreenChange;
private ScheduledDelegate scheduledContract;
private ScheduledDelegate? scheduledScreenChange;
private ScheduledDelegate? scheduledContract;
private TournamentMatchScoreDisplay scoreDisplay;
private TournamentMatchScoreDisplay scoreDisplay = null!;
private TourneyState lastState;
private MatchHeader header;
private MatchHeader header = null!;
private void contract()
{
if (!IsLoaded)
return;
scheduledContract?.Cancel();
SongBar.Expanded = false;
scoreDisplay.FadeOut(100);
using (chat?.BeginDelayedSequence(500))
chat?.Expand();
using (chat.BeginDelayedSequence(500))
chat.Expand();
}
private void expand()
{
if (!IsLoaded)
return;
scheduledContract?.Cancel();
chat?.Contract();
chat.Contract();
using (BeginDelayedSequence(300))
{
@ -252,7 +256,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
private partial class ChromaArea : CompositeDrawable
{
[Resolved]
private LadderInfo ladder { get; set; }
private LadderInfo ladder { get; set; } = null!;
[BackgroundDependencyLoader]
private void load()

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -28,20 +26,20 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
{
private readonly TournamentMatch match;
private readonly bool losers;
private TournamentSpriteText scoreText;
private Box background;
private Box backgroundRight;
private TournamentSpriteText scoreText = null!;
private Box background = null!;
private Box backgroundRight = null!;
private readonly Bindable<int?> score = new Bindable<int?>();
private readonly BindableBool completed = new BindableBool();
private Color4 colourWinner;
private readonly Func<bool> isWinner;
private LadderEditorScreen ladderEditor;
private readonly Func<bool>? isWinner;
private LadderEditorScreen ladderEditor = null!;
[Resolved(canBeNull: true)]
private LadderInfo ladderInfo { get; set; }
[Resolved]
private LadderInfo? ladderInfo { get; set; }
private void setCurrent()
{
@ -55,10 +53,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
ladderInfo.CurrentMatch.Value.Current.Value = true;
}
[Resolved(CanBeNull = true)]
private LadderEditorInfo editorInfo { get; set; }
[Resolved]
private LadderEditorInfo? editorInfo { get; set; }
public DrawableMatchTeam(TournamentTeam team, TournamentMatch match, bool losers)
public DrawableMatchTeam(TournamentTeam? team, TournamentMatch match, bool losers)
: base(team)
{
this.match = match;
@ -72,14 +70,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
AcronymText.Padding = new MarginPadding { Left = 50 };
AcronymText.Font = OsuFont.Torus.With(size: 22, weight: FontWeight.Bold);
if (match != null)
{
isWinner = () => match.Winner == Team;
isWinner = () => match.Winner == Team;
completed.BindTo(match.Completed);
if (team != null)
score.BindTo(team == match.Team1.Value ? match.Team1Score : match.Team2Score);
}
completed.BindTo(match.Completed);
if (team != null)
score.BindTo(team == match.Team1.Value ? match.Team1Score : match.Team2Score);
}
[BackgroundDependencyLoader(true)]

View File

@ -1,10 +1,9 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -27,13 +26,13 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
protected readonly FillFlowContainer<DrawableMatchTeam> Flow;
private readonly Drawable selectionBox;
private readonly Drawable currentMatchSelectionBox;
private Bindable<TournamentMatch> globalSelection;
private Bindable<TournamentMatch>? globalSelection;
[Resolved(CanBeNull = true)]
private LadderEditorInfo editorInfo { get; set; }
[Resolved]
private LadderEditorInfo? editorInfo { get; set; }
[Resolved(CanBeNull = true)]
private LadderInfo ladderInfo { get; set; }
[Resolved]
private LadderInfo? ladderInfo { get; set; }
public DrawableTournamentMatch(TournamentMatch match, bool editor = false)
{
@ -129,7 +128,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
/// <summary>
/// Fired when something changed that requires a ladder redraw.
/// </summary>
public Action Changed;
public Action? Changed;
private readonly List<IUnbindable> refBindables = new List<IUnbindable>();
@ -201,20 +200,22 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
}
else
{
transferProgression(Match.Progression?.Value, Match.Winner);
transferProgression(Match.LosersProgression?.Value, Match.Loser);
Debug.Assert(Match.Winner != null);
transferProgression(Match.Progression.Value, Match.Winner);
Debug.Assert(Match.Loser != null);
transferProgression(Match.LosersProgression.Value, Match.Loser);
}
Changed?.Invoke();
}
private void transferProgression(TournamentMatch destination, TournamentTeam team)
private void transferProgression(TournamentMatch? destination, TournamentTeam team)
{
if (destination == null) return;
bool progressionAbove = destination.ID < Match.ID;
Bindable<TournamentTeam> destinationTeam;
Bindable<TournamentTeam?> destinationTeam;
// check for the case where we have already transferred out value
if (destination.Team1.Value == team)
@ -268,8 +269,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
{
foreach (var conditional in Match.ConditionalMatches)
{
bool team1Match = conditional.Acronyms.Contains(Match.Team1Acronym);
bool team2Match = conditional.Acronyms.Contains(Match.Team2Acronym);
bool team1Match = Match.Team1Acronym != null && conditional.Acronyms.Contains(Match.Team1Acronym);
bool team2Match = Match.Team2Acronym != null && conditional.Acronyms.Contains(Match.Team2Acronym);
if (team1Match && team2Match)
Match.Date.Value = conditional.Date.Value;
@ -344,6 +345,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
Match.Progression.Value = null;
Match.LosersProgression.Value = null;
if (ladderInfo == null)
return;
ladderInfo.Matches.Remove(Match);
foreach (var m in ladderInfo.Matches)

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using JetBrains.Annotations;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -52,7 +50,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
name.BindValueChanged(_ => textName.Text = ((losers ? "Losers " : "") + round.Name).ToUpperInvariant(), true);
description = round.Description.GetBoundCopy();
description.BindValueChanged(_ => textDescription.Text = round.Description.Value?.ToUpperInvariant(), true);
description.BindValueChanged(_ => textDescription.Text = round.Description.Value?.ToUpperInvariant() ?? string.Empty, true);
}
}
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
@ -23,17 +21,17 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
{
public partial class LadderEditorSettings : CompositeDrawable
{
private SettingsDropdown<TournamentRound> roundDropdown;
private PlayerCheckbox losersCheckbox;
private DateTextBox dateTimeBox;
private SettingsTeamDropdown team1Dropdown;
private SettingsTeamDropdown team2Dropdown;
private SettingsDropdown<TournamentRound?> roundDropdown = null!;
private PlayerCheckbox losersCheckbox = null!;
private DateTextBox dateTimeBox = null!;
private SettingsTeamDropdown team1Dropdown = null!;
private SettingsTeamDropdown team2Dropdown = null!;
[Resolved]
private LadderEditorInfo editorInfo { get; set; }
private LadderEditorInfo editorInfo { get; set; } = null!;
[Resolved]
private LadderInfo ladderInfo { get; set; }
private LadderInfo ladderInfo { get; set; } = null!;
[BackgroundDependencyLoader]
private void load()
@ -77,7 +75,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
};
}
private void roundDropdownChanged(ValueChangedEvent<TournamentRound> round)
private void roundDropdownChanged(ValueChangedEvent<TournamentRound?> round)
{
if (editorInfo.Selected.Value?.Date.Value < round.NewValue?.StartDate.Value)
{
@ -101,11 +99,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
{
}
private partial class SettingsRoundDropdown : SettingsDropdown<TournamentRound>
private partial class SettingsRoundDropdown : SettingsDropdown<TournamentRound?>
{
public SettingsRoundDropdown(BindableList<TournamentRound> rounds)
{
Current = new Bindable<TournamentRound>();
Current = new Bindable<TournamentRound?>();
foreach (var r in rounds.Prepend(new TournamentRound()))
add(r);

View File

@ -12,7 +12,7 @@ using osu.Game.Tournament.Models;
namespace osu.Game.Tournament.Screens.Ladder.Components
{
public partial class SettingsTeamDropdown : SettingsDropdown<TournamentTeam>
public partial class SettingsTeamDropdown : SettingsDropdown<TournamentTeam?>
{
public SettingsTeamDropdown(BindableList<TournamentTeam> teams)
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
@ -22,13 +20,13 @@ namespace osu.Game.Tournament.Screens.Ladder
{
public partial class LadderScreen : TournamentScreen
{
protected Container<DrawableTournamentMatch> MatchesContainer;
private Container<Path> paths;
private Container headings;
protected Container<DrawableTournamentMatch> MatchesContainer = null!;
private Container<Path> paths = null!;
private Container headings = null!;
protected LadderDragContainer ScrollContent;
protected LadderDragContainer ScrollContent = null!;
protected Container Content;
protected Container Content = null!;
[BackgroundDependencyLoader]
private void load()

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -24,20 +22,20 @@ namespace osu.Game.Tournament.Screens.MapPool
{
public partial class MapPoolScreen : TournamentMatchScreen
{
private FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>> mapFlows;
private FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>> mapFlows = null!;
[Resolved(canBeNull: true)]
private TournamentSceneManager sceneManager { get; set; }
[Resolved]
private TournamentSceneManager? sceneManager { get; set; }
private TeamColour pickColour;
private ChoiceType pickType;
private OsuButton buttonRedBan;
private OsuButton buttonBlueBan;
private OsuButton buttonRedPick;
private OsuButton buttonBluePick;
private OsuButton buttonRedBan = null!;
private OsuButton buttonBlueBan = null!;
private OsuButton buttonRedPick = null!;
private OsuButton buttonBluePick = null!;
private ScheduledDelegate scheduledScreenChange;
private ScheduledDelegate? scheduledScreenChange;
[BackgroundDependencyLoader]
private void load(MatchIPCInfo ipc)
@ -113,7 +111,7 @@ namespace osu.Game.Tournament.Screens.MapPool
ipc.Beatmap.BindValueChanged(beatmapChanged);
}
private Bindable<bool> splitMapPoolByMods;
private Bindable<bool>? splitMapPoolByMods;
protected override void LoadComplete()
{
@ -123,13 +121,13 @@ namespace osu.Game.Tournament.Screens.MapPool
splitMapPoolByMods.BindValueChanged(_ => updateDisplay());
}
private void beatmapChanged(ValueChangedEvent<TournamentBeatmap> beatmap)
private void beatmapChanged(ValueChangedEvent<TournamentBeatmap?> beatmap)
{
if (CurrentMatch.Value == null || CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2)
return;
// if bans have already been placed, beatmap changes result in a selection being made autoamtically
if (beatmap.NewValue.OnlineID > 0)
if (beatmap.NewValue?.OnlineID > 0)
addForBeatmap(beatmap.NewValue.OnlineID);
}
@ -148,6 +146,9 @@ namespace osu.Game.Tournament.Screens.MapPool
private void setNextMode()
{
if (CurrentMatch.Value == null)
return;
const TeamColour roll_winner = TeamColour.Red; //todo: draw from match
var nextColour = (CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red;
@ -169,11 +170,11 @@ namespace osu.Game.Tournament.Screens.MapPool
addForBeatmap(map.Beatmap.OnlineID);
else
{
var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap?.OnlineID);
var existing = CurrentMatch.Value?.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap?.OnlineID);
if (existing != null)
{
CurrentMatch.Value.PicksBans.Remove(existing);
CurrentMatch.Value?.PicksBans.Remove(existing);
setNextMode();
}
}
@ -186,13 +187,13 @@ namespace osu.Game.Tournament.Screens.MapPool
private void reset()
{
CurrentMatch.Value.PicksBans.Clear();
CurrentMatch.Value?.PicksBans.Clear();
setNextMode();
}
private void addForBeatmap(int beatmapId)
{
if (CurrentMatch.Value == null)
if (CurrentMatch.Value?.Round.Value == null)
return;
if (CurrentMatch.Value.Round.Value.Beatmaps.All(b => b.Beatmap?.OnlineID != beatmapId))
@ -228,7 +229,7 @@ namespace osu.Game.Tournament.Screens.MapPool
base.Hide();
}
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch?> match)
{
base.CurrentMatchChanged(match);
updateDisplay();
@ -245,8 +246,8 @@ namespace osu.Game.Tournament.Screens.MapPool
if (CurrentMatch.Value.Round.Value != null)
{
FillFlowContainer<TournamentBeatmapPanel> currentFlow = null;
string currentMods = null;
FillFlowContainer<TournamentBeatmapPanel>? currentFlow = null;
string? currentMods = null;
int flowCount = 0;
foreach (var b in CurrentMatch.Value.Round.Value.Beatmaps)

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Linq;
using osu.Framework.Allocation;
@ -21,9 +19,9 @@ namespace osu.Game.Tournament.Screens.Schedule
{
public partial class ScheduleScreen : TournamentScreen
{
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
private Container mainContainer;
private LadderInfo ladder;
private readonly Bindable<TournamentMatch?> currentMatch = new Bindable<TournamentMatch?>();
private Container mainContainer = null!;
private LadderInfo ladder = null!;
[BackgroundDependencyLoader]
private void load(LadderInfo ladder)
@ -107,7 +105,7 @@ namespace osu.Game.Tournament.Screens.Schedule
currentMatch.BindValueChanged(matchChanged, true);
}
private void matchChanged(ValueChangedEvent<TournamentMatch> match)
private void matchChanged(ValueChangedEvent<TournamentMatch?> match)
{
var upcoming = ladder.Matches.Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4);
var conditionals = ladder

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
@ -14,9 +12,9 @@ namespace osu.Game.Tournament.Screens.Setup
private const int minimum_window_height = 480;
private const int maximum_window_height = 2160;
public new Action<int> Action;
public new Action<int>? Action;
private OsuNumberBox numberBox;
private OsuNumberBox? numberBox;
protected override Drawable CreateComponent()
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Drawing;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -24,27 +22,27 @@ namespace osu.Game.Tournament.Screens.Setup
{
public partial class SetupScreen : TournamentScreen
{
private FillFlowContainer fillFlow;
private FillFlowContainer fillFlow = null!;
private LoginOverlay loginOverlay;
private ResolutionSelector resolution;
private LoginOverlay? loginOverlay;
private ResolutionSelector resolution = null!;
[Resolved]
private MatchIPCInfo ipc { get; set; }
private MatchIPCInfo ipc { get; set; } = null!;
[Resolved]
private StableInfo stableInfo { get; set; }
private StableInfo stableInfo { get; set; } = null!;
[Resolved]
private IAPIProvider api { get; set; }
private IAPIProvider api { get; set; } = null!;
[Resolved]
private RulesetStore rulesets { get; set; }
private RulesetStore rulesets { get; set; } = null!;
[Resolved(canBeNull: true)]
private TournamentSceneManager sceneManager { get; set; }
[Resolved]
private TournamentSceneManager? sceneManager { get; set; }
private Bindable<Size> windowSize;
private Bindable<Size> windowSize = null!;
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager frameworkConfig)
@ -115,7 +113,7 @@ namespace osu.Game.Tournament.Screens.Setup
Failing = api.IsLoggedIn != true,
Description = "In order to access the API and display metadata, signing in is required."
},
new LabelledDropdown<RulesetInfo>
new LabelledDropdown<RulesetInfo?>
{
Label = "Ruleset",
Description = "Decides what stats are displayed and which ranks are retrieved for players. This requires a restart to reload data for an existing bracket.",

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.IO;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@ -23,20 +21,20 @@ namespace osu.Game.Tournament.Screens.Setup
{
public partial class StablePathSelectScreen : TournamentScreen
{
[Resolved(canBeNull: true)]
private TournamentSceneManager sceneManager { get; set; }
[Resolved]
private TournamentSceneManager? sceneManager { get; set; }
[Resolved]
private MatchIPCInfo ipc { get; set; }
private MatchIPCInfo ipc { get; set; } = null!;
private OsuDirectorySelector directorySelector;
private DialogOverlay overlay;
private OsuDirectorySelector directorySelector = null!;
private DialogOverlay? overlay;
[BackgroundDependencyLoader(true)]
private void load(Storage storage, OsuColour colours)
{
var initialStorage = (ipc as FileBasedIPC)?.IPCStorage ?? storage;
string initialPath = new DirectoryInfo(initialStorage.GetFullPath(string.Empty)).Parent?.FullName;
string? initialPath = new DirectoryInfo(initialStorage.GetFullPath(string.Empty)).Parent?.FullName;
AddRangeInternal(new Drawable[]
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
@ -13,12 +11,12 @@ namespace osu.Game.Tournament.Screens.Setup
{
internal partial class TournamentSwitcher : ActionableInfo
{
private OsuDropdown<string> dropdown;
private OsuButton folderButton;
private OsuButton reloadTournamentsButton;
private OsuDropdown<string> dropdown = null!;
private OsuButton folderButton = null!;
private OsuButton reloadTournamentsButton = null!;
[Resolved]
private TournamentGameBase game { get; set; }
private TournamentGameBase game { get; set; } = null!;
[BackgroundDependencyLoader]
private void load(TournamentStorage storage)

View File

@ -42,7 +42,7 @@ namespace osu.Game.Tournament.Screens.Showcase
});
}
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch?> match)
{
// showcase screen doesn't care about a match being selected.
// base call intentionally omitted to not show match warning.

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -22,12 +20,12 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{
public partial class SeedingScreen : TournamentMatchScreen
{
private Container mainContainer;
private Container mainContainer = null!;
private readonly Bindable<TournamentTeam> currentTeam = new Bindable<TournamentTeam>();
private readonly Bindable<TournamentTeam?> currentTeam = new Bindable<TournamentTeam?>();
private TourneyButton showFirstTeamButton;
private TourneyButton showSecondTeamButton;
private TourneyButton showFirstTeamButton = null!;
private TourneyButton showSecondTeamButton = null!;
[BackgroundDependencyLoader]
private void load()
@ -53,13 +51,13 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{
RelativeSizeAxes = Axes.X,
Text = "Show first team",
Action = () => currentTeam.Value = CurrentMatch.Value.Team1.Value,
Action = () => currentTeam.Value = CurrentMatch.Value?.Team1.Value,
},
showSecondTeamButton = new TourneyButton
{
RelativeSizeAxes = Axes.X,
Text = "Show second team",
Action = () => currentTeam.Value = CurrentMatch.Value.Team2.Value,
Action = () => currentTeam.Value = CurrentMatch.Value?.Team2.Value,
},
new SettingsTeamDropdown(LadderInfo.Teams)
{
@ -73,7 +71,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
currentTeam.BindValueChanged(teamChanged, true);
}
private void teamChanged(ValueChangedEvent<TournamentTeam> team) => updateTeamDisplay();
private void teamChanged(ValueChangedEvent<TournamentTeam?> team) => updateTeamDisplay();
public override void Show()
{
@ -84,7 +82,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
updateTeamDisplay();
}
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch?> match)
{
base.CurrentMatchChanged(match);
@ -256,7 +254,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
private partial class LeftInfo : CompositeDrawable
{
public LeftInfo(TournamentTeam team)
public LeftInfo(TournamentTeam? team)
{
FillFlowContainer fill;
@ -315,7 +313,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
private partial class TeamDisplay : DrawableTournamentTeam
{
public TeamDisplay(TournamentTeam team)
public TeamDisplay(TournamentTeam? team)
: base(team)
{
AutoSizeAxes = Axes.Both;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -15,7 +13,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{
public partial class TeamIntroScreen : TournamentMatchScreen
{
private Container mainContainer;
private Container mainContainer = null!;
[BackgroundDependencyLoader]
private void load()
@ -36,7 +34,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
};
}
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch?> match)
{
base.CurrentMatchChanged(match);

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -16,12 +14,12 @@ namespace osu.Game.Tournament.Screens.TeamWin
{
public partial class TeamWinScreen : TournamentMatchScreen
{
private Container mainContainer;
private Container mainContainer = null!;
private readonly Bindable<bool> currentCompleted = new Bindable<bool>();
private TourneyVideo blueWinVideo;
private TourneyVideo redWinVideo;
private TourneyVideo blueWinVideo = null!;
private TourneyVideo redWinVideo = null!;
[BackgroundDependencyLoader]
private void load()
@ -51,7 +49,7 @@ namespace osu.Game.Tournament.Screens.TeamWin
currentCompleted.BindValueChanged(_ => update());
}
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch?> match)
{
base.CurrentMatchChanged(match);
@ -70,7 +68,7 @@ namespace osu.Game.Tournament.Screens.TeamWin
{
var match = CurrentMatch.Value;
if (match.Winner == null)
if (match?.Winner == null)
{
mainContainer.Clear();
return;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Bindables;
using osu.Game.Tournament.Models;
@ -10,8 +8,8 @@ namespace osu.Game.Tournament.Screens
{
public abstract partial class TournamentMatchScreen : TournamentScreen
{
protected readonly Bindable<TournamentMatch> CurrentMatch = new Bindable<TournamentMatch>();
private WarningBox noMatchWarning;
protected readonly Bindable<TournamentMatch?> CurrentMatch = new Bindable<TournamentMatch?>();
private WarningBox? noMatchWarning;
protected override void LoadComplete()
{
@ -21,7 +19,7 @@ namespace osu.Game.Tournament.Screens
CurrentMatch.BindValueChanged(CurrentMatchChanged, true);
}
protected virtual void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
protected virtual void CurrentMatchChanged(ValueChangedEvent<TournamentMatch?> match)
{
if (match.NewValue == null)
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Drawing;
using System.Linq;
using osu.Framework.Allocation;
@ -35,12 +33,12 @@ namespace osu.Game.Tournament
public static readonly Color4 ELEMENT_FOREGROUND_COLOUR = Color4Extensions.FromHex("#000");
public static readonly Color4 TEXT_COLOUR = Color4Extensions.FromHex("#fff");
private Drawable heightWarning;
private Drawable heightWarning = null!;
private Bindable<WindowMode> windowMode;
private Bindable<WindowMode> windowMode = null!;
private readonly BindableSize windowSize = new BindableSize();
private LoadingSpinner loadingSpinner;
private LoadingSpinner loadingSpinner = null!;
[Cached(typeof(IDialogOverlay))]
private readonly DialogOverlay dialogOverlay = new DialogOverlay();

View File

@ -1,14 +1,13 @@
// 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.
#nullable disable
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
@ -31,11 +30,11 @@ namespace osu.Game.Tournament
public partial class TournamentGameBase : OsuGameBase
{
public const string BRACKET_FILENAME = @"bracket.json";
private LadderInfo ladder;
private TournamentStorage storage;
private DependencyContainer dependencies;
private FileBasedIPC ipc;
private BeatmapLookupCache beatmapCache;
private LadderInfo ladder = new LadderInfo();
private TournamentStorage storage = null!;
private DependencyContainer dependencies = null!;
private FileBasedIPC ipc = null!;
private BeatmapLookupCache beatmapCache = null!;
protected Task BracketLoadTask => bracketLoadTaskCompletionSource.Task;
@ -54,7 +53,7 @@ namespace osu.Game.Tournament
return new ProductionEndpointConfiguration();
}
private TournamentSpriteText initialisationText;
private TournamentSpriteText initialisationText = null!;
[BackgroundDependencyLoader]
private void load(Storage baseStorage)
@ -100,11 +99,11 @@ namespace osu.Game.Tournament
{
using (Stream stream = storage.GetStream(BRACKET_FILENAME, FileAccess.Read, FileMode.Open))
using (var sr = new StreamReader(stream))
ladder = JsonConvert.DeserializeObject<LadderInfo>(await sr.ReadToEndAsync().ConfigureAwait(false), new JsonPointConverter());
{
ladder = JsonConvert.DeserializeObject<LadderInfo>(await sr.ReadToEndAsync().ConfigureAwait(false), new JsonPointConverter()) ?? ladder;
}
}
ladder ??= new LadderInfo();
var resolvedRuleset = ladder.Ruleset.Value != null
? RulesetStore.GetRuleset(ladder.Ruleset.Value.ShortName)
: RulesetStore.AvailableRulesets.First();
@ -283,7 +282,7 @@ namespace osu.Game.Tournament
private void updateLoadProgressMessage(string s) => Schedule(() => initialisationText.Text = s);
public void PopulatePlayer(TournamentUser user, Action success = null, Action failure = null, bool immediate = false)
public void PopulatePlayer(TournamentUser user, Action? success = null, Action? failure = null, bool immediate = false)
{
var req = new GetUserRequest(user.OnlineID, ladder.Ruleset.Value);
@ -348,8 +347,8 @@ namespace osu.Game.Tournament
foreach (var r in ladder.Rounds)
r.Matches = ladder.Matches.Where(p => p.Round.Value == r).Select(p => p.ID).ToList();
ladder.Progressions = ladder.Matches.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat(
ladder.Matches.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true)))
ladder.Progressions = ladder.Matches.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.AsNonNull().ID)).Concat(
ladder.Matches.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.AsNonNull().ID, true)))
.ToList();
return JsonConvert.SerializeObject(ladder,

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Linq;
using osu.Framework.Allocation;
@ -35,8 +33,8 @@ namespace osu.Game.Tournament
[Cached]
public partial class TournamentSceneManager : CompositeDrawable
{
private Container screens;
private TourneyVideo video;
private Container screens = null!;
private TourneyVideo video = null!;
public const int CONTROL_AREA_WIDTH = 200;
@ -50,8 +48,8 @@ namespace osu.Game.Tournament
[Cached]
private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay();
private Container chatContainer;
private FillFlowContainer buttons;
private Container chatContainer = null!;
private FillFlowContainer buttons = null!;
public TournamentSceneManager()
{
@ -166,10 +164,10 @@ namespace osu.Game.Tournament
private float depth;
private Drawable currentScreen;
private ScheduledDelegate scheduledHide;
private Drawable? currentScreen;
private ScheduledDelegate? scheduledHide;
private Drawable temporaryScreen;
private Drawable? temporaryScreen;
public void SetScreen(Drawable screen)
{
@ -284,7 +282,7 @@ namespace osu.Game.Tournament
Y = -2,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = shortcutKey.ToString(),
Text = shortcutKey.Value.ToString(),
}
}
});
@ -304,7 +302,7 @@ namespace osu.Game.Tournament
private bool isSelected;
public Action<Type> RequestSelection;
public Action<Type>? RequestSelection;
public bool IsSelected
{

View File

@ -124,7 +124,7 @@ namespace osu.Game.Beatmaps
{
base.Update();
if (Source != null && Source is not IAdjustableClock && Source.CurrentTime < decoupledClock.CurrentTime)
if (Source != null && Source is not IAdjustableClock && Source.CurrentTime < decoupledClock.CurrentTime - 100)
{
// InterpolatingFramedClock won't interpolate backwards unless its source has an ElapsedFrameTime.
// See https://github.com/ppy/osu-framework/blob/ba1385330cc501f34937e08257e586c84e35d772/osu.Framework/Timing/InterpolatingFramedClock.cs#L91-L93

View File

@ -82,7 +82,7 @@ namespace osu.Game.Database
/// 30 2023-06-16 Run migration of old lazer scores again. This time with more correct rounding considerations.
/// 31 2023-06-26 Add Version and LegacyTotalScore to ScoreInfo, set Version to 30000002 and copy TotalScore into LegacyTotalScore for legacy scores.
/// 32 2023-07-09 Populate legacy scores with the ScoreV2 mod (and restore TotalScore to the legacy total for such scores) using replay files.
/// 32 2023-08-01 Added a new keybind that toggles the in-game leaderboard on and off
/// 33 2023-08-01 Reset default chat toggle keybind to avoid conflict with leaderboard toggle keybind.
/// </summary>
private const int schema_version = 33;
@ -1020,12 +1020,11 @@ namespace osu.Game.Database
case 33:
{
// Get all current keybinds, and find the toggle chat bind
// (by default it would to overlap the new toggle leaderboard)
// Clear default bindings for the chat focus toggle,
// as they would conflict with the newly-added leaderboard toggle.
var newKeyBindings = migration.NewRealm.All<RealmKeyBinding>().ToList();
var toggleChatBind = newKeyBindings.FirstOrDefault(bind => bind.ActionInt == (int)GlobalAction.ToggleChatFocus);
// If we have a bind for it, and that bind is still tab, remove it
if (toggleChatBind != default && toggleChatBind.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Tab }))
migration.NewRealm.Remove(toggleChatBind);

View File

@ -24,15 +24,13 @@ namespace osu.Game.Graphics.Containers
public const double BACKGROUND_FADE_DURATION = 800;
/// <summary>
/// Whether or not user-configured settings relating to brightness of elements should be ignored
/// Whether or not user-configured settings relating to brightness of elements should be ignored.
/// </summary>
/// <remarks>
/// For best or worst, this also bypasses storyboard disable. Not sure this is correct but leaving it as to not break anything.
/// </remarks>
public readonly Bindable<bool> IgnoreUserSettings = new Bindable<bool>();
/// <summary>
/// Whether or not the storyboard loaded should completely hide the background behind it.
/// </summary>
public readonly Bindable<bool> StoryboardReplacesBackground = new Bindable<bool>();
/// <summary>
/// Whether player is in break time.
/// Must be bound to <see cref="BreakTracker.IsBreakTime"/> to allow for dim adjustments in gameplay.
@ -57,7 +55,7 @@ namespace osu.Game.Graphics.Containers
private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0;
protected float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : DimWhenUserSettingsIgnored.Value, 0);
protected virtual float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : DimWhenUserSettingsIgnored.Value, 0);
protected override Container<Drawable> Content => dimContent;
@ -83,7 +81,6 @@ namespace osu.Game.Graphics.Containers
LightenDuringBreaks.ValueChanged += _ => UpdateVisuals();
IsBreakTime.ValueChanged += _ => UpdateVisuals();
ShowStoryboard.ValueChanged += _ => UpdateVisuals();
StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals();
IgnoreUserSettings.ValueChanged += _ => UpdateVisuals();
}

View File

@ -31,7 +31,7 @@ namespace osu.Game.IO.Archives
{
ZipArchiveEntry entry = archive.Entries.SingleOrDefault(e => e.Key == name);
if (entry == null)
throw new FileNotFoundException();
return null;
var owner = MemoryAllocator.Default.Allocate<byte>((int)entry.Size);

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online;
using osu.Game.Online.API.Requests.Responses;
using osuTK;
using osuTK.Graphics;
@ -29,6 +30,9 @@ namespace osu.Game.Overlays.Changelog
[Resolved]
private OverlayColourProvider colourProvider { get; set; } = null!;
[Resolved]
private ILinkHandler? linkHandler { get; set; }
private FontUsage fontLarge;
private FontUsage fontMedium;
@ -86,11 +90,21 @@ namespace osu.Game.Overlays.Changelog
}
};
title.AddText(entry.Title, t =>
if (string.IsNullOrEmpty(entry.Url))
{
t.Font = fontLarge;
t.Colour = entryColour;
});
title.AddText(entry.Title, t =>
{
t.Font = fontLarge;
t.Colour = entryColour;
});
}
else
{
title.AddLink(entry.Title, () => linkHandler?.HandleLink(entry.Url), entry.Url, t =>
{
t.Font = fontLarge;
});
}
if (!string.IsNullOrEmpty(entry.Repository) && !string.IsNullOrEmpty(entry.GithubUrl))
addRepositoryReference(title, entryColour);

View File

@ -13,6 +13,7 @@ using osu.Framework.Threading;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation;
using osu.Game.Rulesets;
using osu.Game.Screens.Edit.Components;
using osu.Game.Skinning;
using osuTK;
@ -23,14 +24,22 @@ namespace osu.Game.Overlays.SkinEditor
{
public Action<Type>? RequestPlacement;
private readonly SkinComponentsContainer? target;
private readonly SkinComponentsContainer target;
private readonly RulesetInfo? ruleset;
private FillFlowContainer fill = null!;
public SkinComponentToolbox(SkinComponentsContainer? target = null)
: base(target?.Lookup.Ruleset == null ? SkinEditorStrings.Components : LocalisableString.Interpolate($"{SkinEditorStrings.Components} ({target.Lookup.Ruleset.Name})"))
/// <summary>
/// Create a new component toolbox for the specified taget.
/// </summary>
/// <param name="target">The target. This is mainly used as a dependency source to find candidate components.</param>
/// <param name="ruleset">A ruleset to filter components by. If null, only components which are not ruleset-specific will be included.</param>
public SkinComponentToolbox(SkinComponentsContainer target, RulesetInfo? ruleset)
: base(ruleset == null ? SkinEditorStrings.Components : LocalisableString.Interpolate($"{SkinEditorStrings.Components} ({ruleset.Name})"))
{
this.target = target;
this.ruleset = ruleset;
}
[BackgroundDependencyLoader]
@ -51,7 +60,7 @@ namespace osu.Game.Overlays.SkinEditor
{
fill.Clear();
var skinnableTypes = SerialisedDrawableInfo.GetAllAvailableDrawables(target?.Lookup.Ruleset);
var skinnableTypes = SerialisedDrawableInfo.GetAllAvailableDrawables(ruleset);
foreach (var type in skinnableTypes)
attemptAddComponent(type);
}

View File

@ -356,7 +356,7 @@ namespace osu.Game.Overlays.SkinEditor
{
new SettingsDropdown<SkinComponentsContainerLookup?>
{
Items = availableTargets.Select(t => t.Lookup),
Items = availableTargets.Select(t => t.Lookup).Distinct(),
Current = selectedTarget,
}
}
@ -366,14 +366,14 @@ namespace osu.Game.Overlays.SkinEditor
// If the new target has a ruleset, let's show ruleset-specific items at the top, and the rest below.
if (target.NewValue.Ruleset != null)
{
componentsSidebar.Add(new SkinComponentToolbox(skinComponentsContainer)
componentsSidebar.Add(new SkinComponentToolbox(skinComponentsContainer, target.NewValue.Ruleset)
{
RequestPlacement = requestPlacement
});
}
// Remove the ruleset from the lookup to get base components.
componentsSidebar.Add(new SkinComponentToolbox(getTarget(new SkinComponentsContainerLookup(target.NewValue.Target)))
componentsSidebar.Add(new SkinComponentToolbox(skinComponentsContainer, null)
{
RequestPlacement = requestPlacement
});

View File

@ -23,6 +23,7 @@ using osu.Game.Skinning;
using osuTK;
using osu.Game.Rulesets.Objects.Pooling;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Primitives;
namespace osu.Game.Rulesets.UI
{
@ -94,6 +95,16 @@ namespace osu.Game.Rulesets.UI
/// </summary>
public readonly BindableBool DisplayJudgements = new BindableBool(true);
/// <summary>
/// A screen space draw quad which resembles the edges of the playfield for skinning purposes.
/// This will allow users / components to snap objects to the "edge" of the playfield.
/// </summary>
/// <remarks>
/// Rulesets which reduce the visible area further than the full relative playfield space itself
/// should retarget this to the ScreenSpaceDrawQuad of the appropriate container.
/// </remarks>
public virtual Quad SkinnableComponentScreenSpaceDrawQuad => ScreenSpaceDrawQuad;
[Resolved(CanBeNull = true)]
[CanBeNull]
protected IReadOnlyList<Mod> Mods { get; private set; }

View File

@ -36,6 +36,9 @@ namespace osu.Game.Screens.Backgrounds
/// </remarks>
public readonly Bindable<bool> IgnoreUserSettings = new Bindable<bool>(true);
/// <summary>
/// Whether or not the storyboard loaded should completely hide the background behind it.
/// </summary>
public readonly Bindable<bool> StoryboardReplacesBackground = new Bindable<bool>();
/// <summary>
@ -60,12 +63,11 @@ namespace osu.Game.Screens.Backgrounds
InternalChild = dimmable = CreateFadeContainer();
dimmable.StoryboardReplacesBackground.BindTo(StoryboardReplacesBackground);
dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings);
dimmable.IsBreakTime.BindTo(IsBreakTime);
dimmable.BlurAmount.BindTo(BlurAmount);
dimmable.DimWhenUserSettingsIgnored.BindTo(DimWhenUserSettingsIgnored);
StoryboardReplacesBackground.BindTo(dimmable.StoryboardReplacesBackground);
}
[BackgroundDependencyLoader]
@ -144,6 +146,8 @@ namespace osu.Game.Screens.Backgrounds
/// </remarks>
public readonly Bindable<float> BlurAmount = new BindableFloat();
public readonly Bindable<bool> StoryboardReplacesBackground = new Bindable<bool>();
public Background Background
{
get => background;
@ -187,11 +191,19 @@ namespace osu.Game.Screens.Backgrounds
userBlurLevel.ValueChanged += _ => UpdateVisuals();
BlurAmount.ValueChanged += _ => UpdateVisuals();
StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals();
}
protected override bool ShowDimContent
// The background needs to be hidden in the case of it being replaced by the storyboard
=> (!ShowStoryboard.Value && !IgnoreUserSettings.Value) || !StoryboardReplacesBackground.Value;
protected override float DimLevel
{
get
{
if ((IgnoreUserSettings.Value || ShowStoryboard.Value) && StoryboardReplacesBackground.Value)
return 1;
return base.DimLevel;
}
}
protected override void UpdateVisuals()
{

View File

@ -425,7 +425,8 @@ namespace osu.Game.Screens.Edit
{
dialogOverlay.Push(new SaveBeforeGameplayTestDialog(() =>
{
Save();
if (!Save()) return;
pushEditorPlayer();
}));
}
@ -764,7 +765,7 @@ namespace osu.Game.Screens.Edit
private void confirmExitWithSave()
{
Save();
if (!Save()) return;
ExitConfirmed = true;
this.Exit();
@ -1021,13 +1022,15 @@ namespace osu.Game.Screens.Edit
private void exportBeatmap()
{
Save();
if (!Save()) return;
beatmapManager.Export(Beatmap.Value.BeatmapSetInfo);
}
private void exportLegacyBeatmap()
{
Save();
if (!Save()) return;
beatmapManager.ExportLegacy(Beatmap.Value.BeatmapSetInfo);
}

View File

@ -371,9 +371,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private void onLoadRequested()
{
if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable)
return;
// In the case of spectating, IMultiplayerClient.LoadRequested can be fired while the game is still spectating a previous session.
// For now, we want to game to switch to the new game so need to request exiting from the play screen.
if (!ParentScreen.IsCurrentScreen())
@ -391,6 +388,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
if (client.LocalUser?.State == MultiplayerUserState.Spectating && (SelectedItem.Value == null || Beatmap.IsDefault))
return;
if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable)
return;
StartPlay();
}

View File

@ -18,6 +18,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
{
}
protected override void LoadComplete()
{
base.LoadComplete();
Scheduler.AddDelayed(() => StatisticsPanel.ToggleVisibility(), 1000);
}
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback) => null;
protected override APIRequest FetchNextPage(int direction, Action<IEnumerable<ScoreInfo>> scoresCallback) => null;

View File

@ -2,12 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Game.Graphics;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
@ -198,15 +199,29 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
private void performInitialSeek()
{
// Seek the master clock to the gameplay time.
// This is chosen as the first available frame in the players' replays, which matches the seek by each individual SpectatorPlayer.
double startTime = instances.Where(i => i.Score != null)
.SelectMany(i => i.Score.AsNonNull().Replay.Frames)
.Select(f => f.Time)
.DefaultIfEmpty(0)
.Min();
// We want to start showing gameplay as soon as possible.
// Each client may be in a different place in the beatmap, so we need to do our best to find a common
// starting point.
//
// Preferring a lower value ensures that we don't have some clients stuttering to keep up.
List<double> minFrameTimes = new List<double>();
foreach (var instance in instances)
{
if (instance.Score == null)
continue;
minFrameTimes.Add(instance.Score.Replay.Frames.MinBy(f => f.Time)?.Time ?? 0);
}
// Remove any outliers (only need to worry about removing those lower than the mean since we will take a Min() after).
double mean = minFrameTimes.Average();
minFrameTimes.RemoveAll(t => mean - t > 1000);
double startTime = minFrameTimes.Min();
masterClockContainer.Reset(startTime, true);
Logger.Log($"Multiplayer spectator seeking to initial time of {startTime}");
}
protected override void OnNewPlayingUserState(int userId, SpectatorState spectatorState)
@ -214,7 +229,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
}
protected override void StartGameplay(int userId, SpectatorGameplayState spectatorGameplayState)
=> instances.Single(i => i.UserId == userId).LoadScore(spectatorGameplayState.Score);
{
var playerArea = instances.Single(i => i.UserId == userId);
// The multiplayer spectator flow requires the client to return to a higher level screen
// (ie. StartGameplay should only be called once per player).
//
// Meanwhile, the solo spectator flow supports multiple `StartGameplay` calls.
// To ensure we don't crash out in an edge case where this is called more than once in multiplayer,
// guard against re-entry for the same player.
if (playerArea.Score != null)
return;
playerArea.LoadScore(spectatorGameplayState.Score);
}
protected override void QuitGameplay(int userId)
{
@ -232,6 +260,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
return base.OnBackButton();
// On a manual exit, set the player back to idle unless gameplay has finished.
// Of note, this doesn't cover exiting using alt-f4 or menu home option.
if (multiplayerClient.Room.State != MultiplayerRoomState.Open)
multiplayerClient.ChangeState(MultiplayerUserState.Idle);

View File

@ -74,7 +74,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
);
// If we don't track the animating state, the animation will also occur when resizing the window.
isAnimating &= !Precision.AlmostEquals(Position, targetPos, 0.01f);
isAnimating &= !Precision.AlmostEquals(Size, targetSize, 0.5f);
}
/// <summary>

View File

@ -182,7 +182,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
return;
masterState = newState;
Logger.Log($"{nameof(SpectatorSyncManager)}'s master clock become {masterState}");
Logger.Log($"{nameof(SpectatorSyncManager)}'s master clock became {masterState}");
switch (masterState)
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -23,47 +21,43 @@ namespace osu.Game.Screens.Play.Break
public Bindable<T> Current = new Bindable<T>();
private readonly OsuSpriteText text;
private readonly OsuSpriteText valueText;
private readonly LocalisableString name;
private readonly string prefix;
private OsuSpriteText valueText = null!;
public BreakInfoLine(LocalisableString name, string prefix = @"")
public BreakInfoLine(LocalisableString name)
{
this.prefix = prefix;
this.name = name;
AutoSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
text = new OsuSpriteText
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.CentreRight,
Text = name,
Font = OsuFont.GetFont(size: 17),
Margin = new MarginPadding { Right = margin }
Margin = new MarginPadding { Right = margin },
Colour = colours.Yellow,
},
valueText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.CentreLeft,
Text = prefix + @"-",
Text = @"-",
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17),
Margin = new MarginPadding { Left = margin }
Margin = new MarginPadding { Left = margin },
Colour = colours.YellowLight,
}
};
Current.ValueChanged += currentValueChanged;
}
private void currentValueChanged(ValueChangedEvent<T> e)
{
string newText = prefix + Format(e.NewValue);
if (valueText.Text == newText)
return;
valueText.Text = newText;
Current.BindValueChanged(text => valueText.Text = Format(text.NewValue));
}
protected virtual LocalisableString Format(T count)
@ -71,21 +65,14 @@ namespace osu.Game.Screens.Play.Break
if (count is Enum countEnum)
return countEnum.GetDescription();
return count.ToString();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
text.Colour = colours.Yellow;
valueText.Colour = colours.YellowLight;
return count.ToString() ?? string.Empty;
}
}
public partial class PercentageBreakInfoLine : BreakInfoLine<double>
{
public PercentageBreakInfoLine(LocalisableString name, string prefix = "")
: base(name, prefix)
public PercentageBreakInfoLine(LocalisableString name)
: base(name)
{
}

View File

@ -46,13 +46,14 @@ namespace osu.Game.Screens.Play
private readonly Container remainingTimeBox;
private readonly RemainingTimeCounter remainingTimeCounter;
private readonly BreakArrows breakArrows;
private readonly ScoreProcessor scoreProcessor;
private readonly BreakInfo info;
public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor)
{
this.scoreProcessor = scoreProcessor;
RelativeSizeAxes = Axes.Both;
BreakInfo info;
Child = fadeContainer = new Container
{
Alpha = 0,
@ -102,18 +103,18 @@ namespace osu.Game.Screens.Play
}
}
};
if (scoreProcessor != null)
{
info.AccuracyDisplay.Current.BindTo(scoreProcessor.Accuracy);
info.GradeDisplay.Current.BindTo(scoreProcessor.Rank);
}
}
protected override void LoadComplete()
{
base.LoadComplete();
initializeBreaks();
if (scoreProcessor != null)
{
info.AccuracyDisplay.Current.BindTo(scoreProcessor.Accuracy);
info.GradeDisplay.Current.BindTo(scoreProcessor.Rank);
}
}
private void initializeBreaks()

View File

@ -12,6 +12,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Configuration;
@ -69,7 +70,9 @@ namespace osu.Game.Screens.Play
public Bindable<bool> ShowHealthBar = new Bindable<bool>(true);
[CanBeNull]
private readonly DrawableRuleset drawableRuleset;
private readonly IReadOnlyList<Mod> mods;
/// <summary>
@ -104,10 +107,11 @@ namespace osu.Game.Screens.Play
private readonly List<Drawable> hideTargets;
public HUDOverlay(DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods, bool alwaysShowLeaderboard = true)
private readonly Drawable playfieldComponents;
public HUDOverlay([CanBeNull] DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods, bool alwaysShowLeaderboard = true)
{
Drawable rulesetComponents;
this.drawableRuleset = drawableRuleset;
this.mods = mods;
@ -124,6 +128,9 @@ namespace osu.Game.Screens.Play
rulesetComponents = drawableRuleset != null
? new HUDComponentsContainer(drawableRuleset.Ruleset.RulesetInfo) { AlwaysPresent = true, }
: Empty(),
playfieldComponents = drawableRuleset != null
? new SkinComponentsContainer(new SkinComponentsContainerLookup(SkinComponentsContainerLookup.TargetArea.Playfield, drawableRuleset.Ruleset.RulesetInfo)) { AlwaysPresent = true, }
: Empty(),
topRightElements = new FillFlowContainer
{
Anchor = Anchor.TopRight,
@ -163,7 +170,7 @@ namespace osu.Game.Screens.Play
},
};
hideTargets = new List<Drawable> { mainComponents, rulesetComponents, topRightElements };
hideTargets = new List<Drawable> { mainComponents, rulesetComponents, playfieldComponents, topRightElements };
if (!alwaysShowLeaderboard)
hideTargets.Add(LeaderboardFlow);
@ -232,6 +239,16 @@ namespace osu.Game.Screens.Play
{
base.Update();
if (drawableRuleset != null)
{
Quad playfieldScreenSpaceDrawQuad = drawableRuleset.Playfield.SkinnableComponentScreenSpaceDrawQuad;
playfieldComponents.Position = ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft);
playfieldComponents.Width = (ToLocalSpace(playfieldScreenSpaceDrawQuad.TopRight) - ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft)).Length;
playfieldComponents.Height = (ToLocalSpace(playfieldScreenSpaceDrawQuad.BottomLeft) - ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft)).Length;
playfieldComponents.Rotation = drawableRuleset.Playfield.Rotation;
}
float? lowestTopScreenSpaceLeft = null;
float? lowestTopScreenSpaceRight = null;

View File

@ -1049,8 +1049,6 @@ namespace osu.Game.Screens.Play
DimmableStoryboard.IsBreakTime.BindTo(breakTracker.IsBreakTime);
DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
storyboardReplacesBackground.Value = Beatmap.Value.Storyboard.ReplacesBackground && Beatmap.Value.Storyboard.HasDrawable;
foreach (var mod in GameplayState.Mods.OfType<IApplicableToPlayer>())

View File

@ -55,7 +55,8 @@ namespace osu.Game.Screens.Ranking
[Resolved]
private IAPIProvider api { get; set; }
private StatisticsPanel statisticsPanel;
protected StatisticsPanel StatisticsPanel { get; private set; }
private Drawable bottomPanel;
private Container<ScorePanel> detachedPanelContainer;
@ -98,7 +99,7 @@ namespace osu.Game.Screens.Ranking
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
statisticsPanel = CreateStatisticsPanel().With(panel =>
StatisticsPanel = CreateStatisticsPanel().With(panel =>
{
panel.RelativeSizeAxes = Axes.Both;
panel.Score.BindTarget = SelectedScore;
@ -107,7 +108,7 @@ namespace osu.Game.Screens.Ranking
{
RelativeSizeAxes = Axes.Both,
SelectedScore = { BindTarget = SelectedScore },
PostExpandAction = () => statisticsPanel.ToggleVisibility()
PostExpandAction = () => StatisticsPanel.ToggleVisibility()
},
detachedPanelContainer = new Container<ScorePanel>
{
@ -194,7 +195,7 @@ namespace osu.Game.Screens.Ranking
if (req != null)
api.Queue(req);
statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true);
StatisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true);
}
protected override void Update()
@ -234,7 +235,7 @@ namespace osu.Game.Screens.Ranking
protected virtual APIRequest FetchNextPage(int direction, Action<IEnumerable<ScoreInfo>> scoresCallback) => null;
/// <summary>
/// Creates the <see cref="StatisticsPanel"/> to be used to display extended information about scores.
/// Creates the <see cref="Statistics.StatisticsPanel"/> to be used to display extended information about scores.
/// </summary>
protected virtual StatisticsPanel CreateStatisticsPanel() => new StatisticsPanel();
@ -272,9 +273,9 @@ namespace osu.Game.Screens.Ranking
public override bool OnBackButton()
{
if (statisticsPanel.State.Value == Visibility.Visible)
if (StatisticsPanel.State.Value == Visibility.Visible)
{
statisticsPanel.Hide();
StatisticsPanel.Hide();
return true;
}
@ -353,7 +354,7 @@ namespace osu.Game.Screens.Ranking
switch (e.Action)
{
case GlobalAction.Select:
statisticsPanel.ToggleVisibility();
StatisticsPanel.ToggleVisibility();
return true;
}

View File

@ -68,7 +68,10 @@ namespace osu.Game.Skinning
MainHUDComponents,
[Description("Song select")]
SongSelect
SongSelect,
[Description("Playfield")]
Playfield
}
}
}