1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-28 18:12:56 +08:00

Merge branch 'less-test-players' into mod-testscene

This commit is contained in:
Dean Herbert 2020-03-05 11:26:33 +09:00
commit 77fddcd2c2
78 changed files with 2024 additions and 671 deletions

View File

@ -9,6 +9,8 @@ about: Issues regarding encountered bugs.
**osu!lazer version:** **osu!lazer version:**
**Logs:** **Logs:**
<!--
*please attach logs here, which are located at:* *please attach logs here, which are located at:*
- `%AppData%/osu/logs` *(on Windows),* - `%AppData%/osu/logs` *(on Windows),*
- `~/.local/share/osu/logs` *(on Linux & macOS).* - `~/.local/share/osu/logs` *(on Linux & macOS).*
-->

View File

@ -9,8 +9,10 @@ about: Issues regarding crashes or permanent freezes.
**osu!lazer version:** **osu!lazer version:**
**Logs:** **Logs:**
<!--
*please attach logs here, which are located at:* *please attach logs here, which are located at:*
- `%AppData%/osu/logs` *(on Windows),* - `%AppData%/osu/logs` *(on Windows),*
- `~/.local/share/osu/logs` *(on Linux & macOS).* - `~/.local/share/osu/logs` *(on Linux & macOS).*
-->
**Computer Specifications:** **Computer Specifications:**

View File

@ -51,7 +51,7 @@
<Reference Include="Java.Interop" /> <Reference Include="Java.Interop" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.221.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.304.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.302.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2020.302.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -7,7 +7,6 @@ using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using osuTK; using osuTK;
@ -51,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Tests
return beatmap; return beatmap;
} }
protected override Player CreatePlayer(Ruleset ruleset) protected override TestPlayer CreatePlayer(Ruleset ruleset)
{ {
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return base.CreatePlayer(ruleset); return base.CreatePlayer(ruleset);

View File

@ -2,9 +2,12 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
@ -114,6 +117,22 @@ namespace osu.Game.Rulesets.Osu.Tests
assertGroups(); assertGroups();
} }
[Test]
public void TestStackedObjects()
{
addObjectsStep(() => new OsuHitObject[]
{
new HitCircle { Position = new Vector2(300, 100) },
new HitCircle
{
Position = new Vector2(300, 300),
StackHeight = 20
},
});
assertDirections();
}
private void addMultipleObjectsStep() => addObjectsStep(() => new OsuHitObject[] private void addMultipleObjectsStep() => addObjectsStep(() => new OsuHitObject[]
{ {
new HitCircle { Position = new Vector2(100, 100) }, new HitCircle { Position = new Vector2(100, 100) },
@ -207,6 +226,33 @@ namespace osu.Game.Rulesets.Osu.Tests
}); });
} }
private void assertDirections()
{
AddAssert("group directions are correct", () =>
{
for (int i = 0; i < hitObjectContainer.Count; i++)
{
DrawableOsuHitObject expectedStart = getObject(i);
DrawableOsuHitObject expectedEnd = i < hitObjectContainer.Count - 1 ? getObject(i + 1) : null;
if (expectedEnd == null)
continue;
var points = getGroup(i).ChildrenOfType<FollowPoint>().ToArray();
if (points.Length == 0)
continue;
float expectedDirection = MathF.Atan2(expectedStart.Position.Y - expectedEnd.Position.Y, expectedStart.Position.X - expectedEnd.Position.X);
float realDirection = MathF.Atan2(expectedStart.Position.Y - points[^1].Position.Y, expectedStart.Position.X - points[^1].Position.X);
if (!Precision.AlmostEquals(expectedDirection, realDirection))
throw new AssertionException($"Expected group {i} in direction {expectedDirection}, but was {realDirection}.");
}
return true;
});
}
private DrawableOsuHitObject getObject(int index) => hitObjectContainer[index]; private DrawableOsuHitObject getObject(int index) => hitObjectContainer[index];
private FollowPointConnection getGroup(int index) => followPointRenderer.Connections[index]; private FollowPointConnection getGroup(int index) => followPointRenderer.Connections[index];

View File

@ -3,13 +3,13 @@
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Play; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestSceneOsuFlashlight : TestSceneOsuPlayer public class TestSceneOsuFlashlight : TestSceneOsuPlayer
{ {
protected override Player CreatePlayer(Ruleset ruleset) protected override TestPlayer CreatePlayer(Ruleset ruleset)
{ {
SelectedMods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), }; SelectedMods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), };

View File

@ -18,7 +18,6 @@ using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Screens.Play;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Storyboards; using osu.Game.Storyboards;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
@ -56,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private void checkNextHitObject(string skin) => private void checkNextHitObject(string skin) =>
AddUntilStep($"check skin from {skin}", () => AddUntilStep($"check skin from {skin}", () =>
{ {
var firstObject = ((TestPlayer)Player).DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.OfType<DrawableHitCircle>().FirstOrDefault(); var firstObject = Player.DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.OfType<DrawableHitCircle>().FirstOrDefault();
if (firstObject == null) if (firstObject == null)
return false; return false;
@ -75,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[Resolved] [Resolved]
private AudioManager audio { get; set; } private AudioManager audio { get; set; }
protected override Player CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin); protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin);
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, audio, testBeatmapSkin); protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, audio, testBeatmapSkin);

View File

@ -11,7 +11,6 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
using osuTK; using osuTK;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests
base.SetUpSteps(); base.SetUpSteps();
AddUntilStep("wait for track to start running", () => track.IsRunning); AddUntilStep("wait for track to start running", () => track.IsRunning);
AddStep("retrieve spinner", () => drawableSpinner = (DrawableSpinner)((TestPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.First()); AddStep("retrieve spinner", () => drawableSpinner = (DrawableSpinner)Player.DrawableRuleset.Playfield.AllHitObjects.First());
} }
[Test] [Test]
@ -89,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
AddStep($"seek to {time}", () => track.Seek(time)); AddStep($"seek to {time}", () => track.Seek(time));
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, ((TestPlayer)Player).DrawableRuleset.FrameStableClock.CurrentTime, 100)); AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
} }
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap

View File

@ -104,8 +104,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
return; return;
} }
Vector2 startPosition = osuStart.EndPosition; Vector2 startPosition = osuStart.StackedEndPosition;
Vector2 endPosition = osuEnd.Position; Vector2 endPosition = osuEnd.StackedPosition;
double endTime = osuEnd.StartTime; double endTime = osuEnd.StartTime;
Vector2 distanceVector = endPosition - startPosition; Vector2 distanceVector = endPosition - startPosition;

View File

@ -1,23 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests namespace osu.Game.Rulesets.Taiko.Tests
{ {
public class TestSceneSwellJudgements : PlayerTestScene public class TestSceneSwellJudgements : PlayerTestScene
{ {
protected new TestPlayer Player => (TestPlayer)base.Player;
public TestSceneSwellJudgements() public TestSceneSwellJudgements()
: base(new TaikoRuleset()) : base(new TaikoRuleset())
{ {
@ -49,25 +42,5 @@ namespace osu.Game.Rulesets.Taiko.Tests
return beatmap; return beatmap;
} }
protected override Player CreatePlayer(Ruleset ruleset) => new TestPlayer();
protected class TestPlayer : Player
{
public readonly List<JudgementResult> Results = new List<JudgementResult>();
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public TestPlayer()
: base(false, false)
{
}
[BackgroundDependencyLoader]
private void load()
{
ScoreProcessor.NewJudgement += r => Results.Add(r);
}
}
} }
} }

View File

@ -4,11 +4,9 @@
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests namespace osu.Game.Rulesets.Taiko.Tests
@ -22,10 +20,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
protected override bool AllowFail => true; protected override bool AllowFail => true;
protected override Player CreatePlayer(Ruleset ruleset) protected override TestPlayer CreatePlayer(Ruleset ruleset)
{ {
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray(); SelectedMods.Value = SelectedMods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray();
return new ScoreAccessiblePlayer(); return base.CreatePlayer(ruleset);
} }
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) =>
@ -49,20 +47,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
AddStep("Setup judgements", () => AddStep("Setup judgements", () =>
{ {
judged = false; judged = false;
((ScoreAccessiblePlayer)Player).ScoreProcessor.NewJudgement += b => judged = true; Player.ScoreProcessor.NewJudgement += b => judged = true;
}); });
AddUntilStep("swell judged", () => judged); AddUntilStep("swell judged", () => judged);
AddAssert("not failed", () => !Player.HasFailed); AddAssert("not failed", () => !Player.HasFailed);
} }
private class ScoreAccessiblePlayer : TestPlayer
{
public ScoreAccessiblePlayer()
: base(false, false)
{
}
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
}
} }
} }

View File

@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Background
private DummySongSelect songSelect; private DummySongSelect songSelect;
private TestPlayerLoader playerLoader; private TestPlayerLoader playerLoader;
private TestPlayer player; private LoadBlockingTestPlayer player;
private BeatmapManager manager; private BeatmapManager manager;
private RulesetStore rulesets; private RulesetStore rulesets;
@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Background
public void PlayerLoaderSettingsHoverTest() public void PlayerLoaderSettingsHoverTest()
{ {
setupUserSettings(); setupUserSettings();
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer { BlockLoad = true }))); AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new LoadBlockingTestPlayer { BlockLoad = true })));
AddUntilStep("Wait for Player Loader to load", () => playerLoader?.IsLoaded ?? false); AddUntilStep("Wait for Player Loader to load", () => playerLoader?.IsLoaded ?? false);
AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent()); AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
AddStep("Trigger background preview", () => AddStep("Trigger background preview", () =>
@ -268,7 +268,7 @@ namespace osu.Game.Tests.Visual.Background
{ {
setupUserSettings(); setupUserSettings();
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer(allowPause)))); AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new LoadBlockingTestPlayer(allowPause))));
AddUntilStep("Wait for Player Loader to load", () => playerLoader.IsLoaded); AddUntilStep("Wait for Player Loader to load", () => playerLoader.IsLoaded);
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
@ -347,7 +347,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
} }
private class TestPlayer : Visual.TestPlayer private class LoadBlockingTestPlayer : TestPlayer
{ {
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
@ -360,7 +360,7 @@ namespace osu.Game.Tests.Visual.Background
public readonly Bindable<bool> ReplacesBackground = new Bindable<bool>(); public readonly Bindable<bool> ReplacesBackground = new Bindable<bool>();
public readonly Bindable<bool> IsPaused = new Bindable<bool>(); public readonly Bindable<bool> IsPaused = new Bindable<bool>();
public TestPlayer(bool allowPause = true) public LoadBlockingTestPlayer(bool allowPause = true)
: base(allowPause) : base(allowPause)
{ {
} }

View File

@ -5,7 +5,6 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Storyboards; using osu.Game.Storyboards;
@ -14,20 +13,22 @@ namespace osu.Game.Tests.Visual.Gameplay
[Description("Player instantiated with an autoplay mod.")] [Description("Player instantiated with an autoplay mod.")]
public class TestSceneAutoplay : TestSceneAllRulesetPlayers public class TestSceneAutoplay : TestSceneAllRulesetPlayers
{ {
protected new TestPlayer Player => (TestPlayer)base.Player;
private ClockBackedTestWorkingBeatmap.TrackVirtualManual track; private ClockBackedTestWorkingBeatmap.TrackVirtualManual track;
protected override Player CreatePlayer(Ruleset ruleset) protected override Player CreatePlayer(Ruleset ruleset)
{ {
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new ScoreAccessiblePlayer(); return new TestPlayer(false, false);
} }
protected override void AddCheckSteps() protected override void AddCheckSteps()
{ {
AddUntilStep("score above zero", () => ((ScoreAccessiblePlayer)Player).ScoreProcessor.TotalScore.Value > 0); AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2)); AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
AddStep("rewind", () => track.Seek(-10000)); AddStep("rewind", () => track.Seek(-10000));
AddUntilStep("key counter reset", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
} }
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
@ -38,18 +39,5 @@ namespace osu.Game.Tests.Visual.Gameplay
return working; return working;
} }
private class ScoreAccessiblePlayer : TestPlayer
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public ScoreAccessiblePlayer()
: base(false, false)
{
}
}
} }
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -11,12 +10,8 @@ using osu.Framework.Utils;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using osu.Game.Storyboards; using osu.Game.Storyboards;
using osuTK; using osuTK;
@ -24,8 +19,6 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestSceneGameplayRewinding : PlayerTestScene public class TestSceneGameplayRewinding : PlayerTestScene
{ {
private RulesetExposingPlayer player => (RulesetExposingPlayer)Player;
[Resolved] [Resolved]
private AudioManager audioManager { get; set; } private AudioManager audioManager { get; set; }
@ -48,13 +41,13 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
AddUntilStep("wait for track to start running", () => track.IsRunning); AddUntilStep("wait for track to start running", () => track.IsRunning);
addSeekStep(3000); addSeekStep(3000);
AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); AddAssert("all judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
AddUntilStep("key counter counted keys", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7)); AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7));
AddStep("clear results", () => player.AppliedResults.Clear()); AddStep("clear results", () => Player.Results.Clear());
addSeekStep(0); addSeekStep(0);
AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged)); AddAssert("none judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
AddUntilStep("key counters reset", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); AddUntilStep("key counters reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
AddAssert("no results triggered", () => player.AppliedResults.Count == 0); AddAssert("no results triggered", () => Player.Results.Count == 0);
} }
private void addSeekStep(double time) private void addSeekStep(double time)
@ -62,13 +55,13 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep($"seek to {time}", () => track.Seek(time)); AddStep($"seek to {time}", () => track.Seek(time));
// Allow a few frames of lenience // Allow a few frames of lenience
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100)); AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
} }
protected override Player CreatePlayer(Ruleset ruleset) protected override TestPlayer CreatePlayer(Ruleset ruleset)
{ {
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new RulesetExposingPlayer(); return base.CreatePlayer(ruleset);
} }
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
@ -89,29 +82,5 @@ namespace osu.Game.Tests.Visual.Gameplay
return beatmap; return beatmap;
} }
private class RulesetExposingPlayer : Player
{
public readonly List<JudgementResult> AppliedResults = new List<JudgementResult>();
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
public RulesetExposingPlayer()
: base(false, false)
{
}
[BackgroundDependencyLoader]
private void load()
{
ScoreProcessor.NewJudgement += r => AppliedResults.Add(r);
}
}
} }
} }

View File

@ -11,7 +11,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osuTK; using osuTK;
using osuTK.Input; using osuTK.Input;
@ -282,14 +281,10 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override bool AllowFail => true; protected override bool AllowFail => true;
protected override Player CreatePlayer(Ruleset ruleset) => new PausePlayer(); protected override TestPlayer CreatePlayer(Ruleset ruleset) => new PausePlayer();
protected class PausePlayer : TestPlayer protected class PausePlayer : TestPlayer
{ {
public new HealthProcessor HealthProcessor => base.HealthProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public bool FailOverlayVisible => FailOverlay.State.Value == Visibility.Visible; public bool FailOverlayVisible => FailOverlay.State.Value == Visibility.Visible;
public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible; public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible;

View File

@ -9,15 +9,12 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[HeadlessTest] // we alter unsafe properties on the game host to test inactive window state. [HeadlessTest] // we alter unsafe properties on the game host to test inactive window state.
public class TestScenePauseWhenInactive : PlayerTestScene public class TestScenePauseWhenInactive : PlayerTestScene
{ {
protected new TestPlayer Player => (TestPlayer)base.Player;
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{ {
var beatmap = (Beatmap)base.CreateBeatmap(ruleset); var beatmap = (Beatmap)base.CreateBeatmap(ruleset);
@ -46,6 +43,6 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("time of pause is after gameplay start time", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= Player.DrawableRuleset.GameplayStartTime); AddAssert("time of pause is after gameplay start time", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= Player.DrawableRuleset.GameplayStartTime);
} }
protected override Player CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true); protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true);
} }
} }

View File

@ -9,7 +9,6 @@ using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Utils; using osu.Framework.Utils;
@ -307,17 +306,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
} }
private class TestPlayer : Visual.TestPlayer protected class SlowLoadPlayer : TestPlayer
{
public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods;
public TestPlayer(bool allowPause = true, bool showResults = true)
: base(allowPause, showResults)
{
}
}
protected class SlowLoadPlayer : Visual.TestPlayer
{ {
public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim(false); public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim(false);

View File

@ -2,9 +2,11 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
@ -13,13 +15,19 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture] [TestFixture]
public class TestSceneUserPanel : OsuTestScene public class TestSceneUserPanel : OsuTestScene
{ {
private readonly UserPanel peppy; private readonly Bindable<UserActivity> activity = new Bindable<UserActivity>();
public TestSceneUserPanel() private UserPanel peppy;
[Resolved]
private RulesetStore rulesetStore { get; set; }
[SetUp]
public void SetUp() => Schedule(() =>
{ {
UserPanel flyte; UserPanel flyte;
Add(new FillFlowContainer Child = new FillFlowContainer
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -44,34 +52,38 @@ namespace osu.Game.Tests.Visual.Online
SupportLevel = 3, SupportLevel = 3,
}) { Width = 300 }, }) { Width = 300 },
}, },
}); };
flyte.Status.Value = new UserStatusOnline(); flyte.Status.Value = new UserStatusOnline();
peppy.Status.Value = null; peppy.Status.Value = null;
}
[Test]
public void UserStatusesTests()
{
AddStep("online", () => { peppy.Status.Value = new UserStatusOnline(); });
AddStep(@"do not disturb", () => { peppy.Status.Value = new UserStatusDoNotDisturb(); });
AddStep(@"offline", () => { peppy.Status.Value = new UserStatusOffline(); });
AddStep(@"null status", () => { peppy.Status.Value = null; });
}
[Test]
public void UserActivitiesTests()
{
Bindable<UserActivity> activity = new Bindable<UserActivity>();
peppy.Activity.BindTo(activity); peppy.Activity.BindTo(activity);
});
AddStep("idle", () => { activity.Value = null; }); [Test]
AddStep("spectating", () => { activity.Value = new UserActivity.Spectating(); }); public void TestUserStatus()
AddStep("solo", () => { activity.Value = new UserActivity.SoloGame(null, null); }); {
AddStep("choosing", () => { activity.Value = new UserActivity.ChoosingBeatmap(); }); AddStep("online", () => peppy.Status.Value = new UserStatusOnline());
AddStep("editing", () => { activity.Value = new UserActivity.Editing(null); }); AddStep("do not disturb", () => peppy.Status.Value = new UserStatusDoNotDisturb());
AddStep("modding", () => { activity.Value = new UserActivity.Modding(); }); AddStep("offline", () => peppy.Status.Value = new UserStatusOffline());
AddStep("null status", () => peppy.Status.Value = null);
} }
[Test]
public void TestUserActivity()
{
AddStep("set online status", () => peppy.Status.Value = new UserStatusOnline());
AddStep("idle", () => activity.Value = null);
AddStep("spectating", () => activity.Value = new UserActivity.Spectating());
AddStep("solo (osu!)", () => activity.Value = soloGameStatusForRuleset(0));
AddStep("solo (osu!taiko)", () => activity.Value = soloGameStatusForRuleset(1));
AddStep("solo (osu!catch)", () => activity.Value = soloGameStatusForRuleset(2));
AddStep("solo (osu!mania)", () => activity.Value = soloGameStatusForRuleset(3));
AddStep("choosing", () => activity.Value = new UserActivity.ChoosingBeatmap());
AddStep("editing", () => activity.Value = new UserActivity.Editing(null));
AddStep("modding", () => activity.Value = new UserActivity.Modding());
}
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.SoloGame(null, rulesetStore.GetRuleset(rulesetId));
} }
} }

View File

@ -0,0 +1,25 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Editors;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneSeedingEditorScreen : LadderTestScene
{
[Cached]
private readonly LadderInfo ladder = new LadderInfo();
public TestSceneSeedingEditorScreen()
{
var match = TestSceneSeedingScreen.CreateSampleSeededMatch();
Add(new SeedingEditorScreen(match.Team1.Value)
{
Width = 0.85f // create room for control panel
});
}
}
}

View File

@ -0,0 +1,127 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.TeamIntro;
using osu.Game.Users;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneSeedingScreen : LadderTestScene
{
[Cached]
private readonly LadderInfo ladder = new LadderInfo();
[BackgroundDependencyLoader]
private void load()
{
ladder.CurrentMatch.Value = CreateSampleSeededMatch();
Add(new SeedingScreen
{
FillMode = FillMode.Fit,
FillAspectRatio = 16 / 9f
});
}
public static TournamentMatch CreateSampleSeededMatch() => new TournamentMatch
{
Team1 =
{
Value = new TournamentTeam
{
FlagName = { Value = "JP" },
FullName = { Value = "Japan" },
LastYearPlacing = { Value = 10 },
Seed = { Value = "Low" },
SeedingResults =
{
new SeedingResult
{
Mod = { Value = "NM" },
Seed = { Value = 10 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 12345672,
Seed = { Value = 24 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 1234567,
Seed = { Value = 12 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 1234567,
Seed = { Value = 16 },
}
}
},
new SeedingResult
{
Mod = { Value = "DT" },
Seed = { Value = 5 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 234567,
Seed = { Value = 3 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 234567,
Seed = { Value = 6 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 234567,
Seed = { Value = 12 },
}
}
}
},
Players =
{
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 12 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 16 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 20 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 30 } } },
}
}
},
Team2 =
{
Value = new TournamentTeam
{
FlagName = { Value = "US" },
FullName = { Value = "United States" },
Players =
{
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
}
}
},
Round =
{
Value = new TournamentRound { Name = { Value = "Quarterfinals" } }
}
};
}
}

View File

@ -21,6 +21,7 @@ namespace osu.Game.Tournament.Tests.Screens
match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA"); match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA");
match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN"); match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN");
match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals");
match.Completed.Value = true;
ladder.CurrentMatch.Value = match; ladder.CurrentMatch.Value = match;
Add(new TeamWinScreen Add(new TeamWinScreen

View File

@ -5,7 +5,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -35,7 +34,7 @@ namespace osu.Game.Tournament.Components
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = new Color4(54, 54, 54, 255) Colour = new Color4(54, 54, 54, 255)
}, },
new OsuSpriteText new TournamentSpriteText
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
namespace osu.Game.Tournament.Components namespace osu.Game.Tournament.Components
@ -19,7 +18,7 @@ namespace osu.Game.Tournament.Components
public readonly TournamentTeam Team; public readonly TournamentTeam Team;
protected readonly Sprite Flag; protected readonly Sprite Flag;
protected readonly OsuSpriteText AcronymText; protected readonly TournamentSpriteText AcronymText;
[UsedImplicitly] [UsedImplicitly]
private Bindable<string> acronym; private Bindable<string> acronym;
@ -37,9 +36,9 @@ namespace osu.Game.Tournament.Components
FillMode = FillMode.Fit FillMode = FillMode.Fit
}; };
AcronymText = new OsuSpriteText AcronymText = new TournamentSpriteText
{ {
Font = OsuFont.GetFont(weight: FontWeight.Regular), Font = OsuFont.Torus.With(weight: FontWeight.Regular),
}; };
} }

View File

@ -13,7 +13,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Legacy;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Screens.Menu; using osu.Game.Screens.Menu;
using osuTK; using osuTK;
@ -262,7 +261,7 @@ namespace osu.Game.Tournament.Components
static void cp(SpriteText s, Color4 colour) static void cp(SpriteText s, Color4 colour)
{ {
s.Colour = colour; s.Colour = colour;
s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 15); s.Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 15);
} }
for (var i = 0; i < tuples.Length; i++) for (var i = 0; i < tuples.Length; i++)
@ -278,9 +277,9 @@ namespace osu.Game.Tournament.Components
}); });
} }
AddText(new OsuSpriteText { Text = heading }, s => cp(s, OsuColour.Gray(0.33f))); AddText(new TournamentSpriteText { Text = heading }, s => cp(s, OsuColour.Gray(0.33f)));
AddText(" ", s => cp(s, OsuColour.Gray(0.33f))); AddText(" ", s => cp(s, OsuColour.Gray(0.33f)));
AddText(new OsuSpriteText { Text = content }, s => cp(s, OsuColour.Gray(0.5f))); AddText(new TournamentSpriteText { Text = content }, s => cp(s, OsuColour.Gray(0.5f)));
} }
} }
} }

View File

@ -15,7 +15,6 @@ using osu.Framework.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -77,14 +76,14 @@ namespace osu.Game.Tournament.Components
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText new TournamentSpriteText
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Text = new LocalisedString(( Text = new LocalisedString((
$"{Beatmap.Metadata.ArtistUnicode ?? Beatmap.Metadata.Artist} - {Beatmap.Metadata.TitleUnicode ?? Beatmap.Metadata.Title}", $"{Beatmap.Metadata.ArtistUnicode ?? Beatmap.Metadata.Artist} - {Beatmap.Metadata.TitleUnicode ?? Beatmap.Metadata.Title}",
$"{Beatmap.Metadata.Artist} - {Beatmap.Metadata.Title}")), $"{Beatmap.Metadata.Artist} - {Beatmap.Metadata.Title}")),
Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true), Font = OsuFont.Torus.With(weight: FontWeight.Bold),
}, },
new FillFlowContainer new FillFlowContainer
{ {
@ -95,28 +94,28 @@ namespace osu.Game.Tournament.Components
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText new TournamentSpriteText
{ {
Text = "mapper", Text = "mapper",
Padding = new MarginPadding { Right = 5 }, Padding = new MarginPadding { Right = 5 },
Font = OsuFont.GetFont(italics: true, weight: FontWeight.Regular, size: 14) Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: 14)
}, },
new OsuSpriteText new TournamentSpriteText
{ {
Text = Beatmap.Metadata.AuthorString, Text = Beatmap.Metadata.AuthorString,
Padding = new MarginPadding { Right = 20 }, Padding = new MarginPadding { Right = 20 },
Font = OsuFont.GetFont(italics: true, weight: FontWeight.Bold, size: 14) Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14)
}, },
new OsuSpriteText new TournamentSpriteText
{ {
Text = "difficulty", Text = "difficulty",
Padding = new MarginPadding { Right = 5 }, Padding = new MarginPadding { Right = 5 },
Font = OsuFont.GetFont(italics: true, weight: FontWeight.Regular, size: 14) Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: 14)
}, },
new OsuSpriteText new TournamentSpriteText
{ {
Text = Beatmap.Version, Text = Beatmap.Version,
Font = OsuFont.GetFont(italics: true, weight: FontWeight.Bold, size: 14) Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14)
}, },
} }
} }

View File

@ -0,0 +1,23 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
namespace osu.Game.Tournament.Models
{
public class SeedingBeatmap
{
public int ID;
public BeatmapInfo BeatmapInfo;
public long Score;
public Bindable<int> Seed = new BindableInt
{
MinValue = 1,
MaxValue = 64
};
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Bindables;
namespace osu.Game.Tournament.Models
{
public class SeedingResult
{
public List<SeedingBeatmap> Beatmaps = new List<SeedingBeatmap>();
public Bindable<string> Mod = new Bindable<string>();
public Bindable<int> Seed = new BindableInt
{
MinValue = 1,
MaxValue = 64
};
}
}

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Users; using osu.Game.Users;
@ -29,6 +30,32 @@ namespace osu.Game.Tournament.Models
/// </summary> /// </summary>
public Bindable<string> Acronym = new Bindable<string>(string.Empty); public Bindable<string> Acronym = new Bindable<string>(string.Empty);
public BindableList<SeedingResult> SeedingResults = new BindableList<SeedingResult>();
public double AverageRank
{
get
{
var ranks = Players.Select(p => p.Statistics?.Ranks.Global)
.Where(i => i.HasValue)
.Select(i => i.Value)
.ToArray();
if (ranks.Length == 0)
return 0;
return ranks.Average();
}
}
public Bindable<string> Seed = new Bindable<string>(string.Empty);
public Bindable<int> LastYearPlacing = new BindableInt
{
MinValue = 1,
MaxValue = 64
};
[JsonProperty] [JsonProperty]
public BindableList<User> Players { get; set; } = new BindableList<User>(); public BindableList<User> Players { get; set; } = new BindableList<User>();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osuTK; using osuTK;
@ -43,7 +42,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
Colour = new Color4(54, 54, 54, 255) Colour = new Color4(54, 54, 54, 255)
}, },
// Group name // Group name
new OsuSpriteText new TournamentSpriteText
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
@ -51,7 +50,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
Position = new Vector2(0, 7f), Position = new Vector2(0, 7f),
Text = $"GROUP {name.ToUpperInvariant()}", Text = $"GROUP {name.ToUpperInvariant()}",
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 8), Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 8),
Colour = new Color4(255, 204, 34, 255), Colour = new Color4(255, 204, 34, 255),
}, },
teams = new FillFlowContainer<GroupTeam> teams = new FillFlowContainer<GroupTeam>
@ -134,7 +133,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
AcronymText.Anchor = Anchor.TopCentre; AcronymText.Anchor = Anchor.TopCentre;
AcronymText.Origin = Anchor.TopCentre; AcronymText.Origin = Anchor.TopCentre;
AcronymText.Text = team.Acronym.Value.ToUpperInvariant(); AcronymText.Text = team.Acronym.Value.ToUpperInvariant();
AcronymText.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 10); AcronymText.Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 10);
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {

View File

@ -14,7 +14,6 @@ using osu.Framework.Graphics.Textures;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Drawings.Components; using osu.Game.Tournament.Screens.Drawings.Components;
@ -29,7 +28,7 @@ namespace osu.Game.Tournament.Screens.Drawings
private ScrollingTeamContainer teamsContainer; private ScrollingTeamContainer teamsContainer;
private GroupContainer groupsContainer; private GroupContainer groupsContainer;
private OsuSpriteText fullTeamNameText; private TournamentSpriteText fullTeamNameText;
private readonly List<TournamentTeam> allTeams = new List<TournamentTeam>(); private readonly List<TournamentTeam> allTeams = new List<TournamentTeam>();
@ -109,18 +108,18 @@ namespace osu.Game.Tournament.Screens.Drawings
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
}, },
// Scrolling team name // Scrolling team name
fullTeamNameText = new OsuSpriteText fullTeamNameText = new TournamentSpriteText
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Position = new Vector2(0, 45f), Position = new Vector2(0, 45f),
Colour = OsuColour.Gray(0.33f), Colour = OsuColour.Gray(0.95f),
Alpha = 0, Alpha = 0,
Font = OsuFont.GetFont(weight: FontWeight.Light, size: 42), Font = OsuFont.Torus.With(weight: FontWeight.Light, size: 42),
} }
} }
}, },

View File

@ -0,0 +1,288 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
using osuTK;
namespace osu.Game.Tournament.Screens.Editors
{
public class SeedingEditorScreen : TournamentEditorScreen<SeedingEditorScreen.SeeingResultRow, SeedingResult>
{
private readonly TournamentTeam team;
protected override BindableList<SeedingResult> Storage => team.SeedingResults;
public SeedingEditorScreen(TournamentTeam team)
{
this.team = team;
}
public class SeeingResultRow : CompositeDrawable, IModelBacked<SeedingResult>
{
public SeedingResult Model { get; }
[Resolved]
private LadderInfo ladderInfo { get; set; }
public SeeingResultRow(TournamentTeam team, SeedingResult round)
{
Model = round;
Masking = true;
CornerRadius = 10;
SeedingBeatmapEditor beatmapEditor = new SeedingBeatmapEditor(round)
{
Width = 0.95f
};
InternalChildren = new Drawable[]
{
new Box
{
Colour = OsuColour.Gray(0.1f),
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
Margin = new MarginPadding(5),
Padding = new MarginPadding { Right = 160 },
Spacing = new Vector2(5),
Direction = FillDirection.Full,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new SettingsTextBox
{
LabelText = "Mod",
Width = 0.33f,
Bindable = Model.Mod
},
new SettingsSlider<int>
{
LabelText = "Seed",
Width = 0.33f,
Bindable = Model.Seed
},
new SettingsButton
{
Width = 0.2f,
Margin = new MarginPadding(10),
Text = "Add beatmap",
Action = () => beatmapEditor.CreateNew()
},
beatmapEditor
}
},
new DangerousSettingsButton
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.None,
Width = 150,
Text = "Delete result",
Action = () =>
{
Expire();
team.SeedingResults.Remove(Model);
},
}
};
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
}
public class SeedingBeatmapEditor : CompositeDrawable
{
private readonly SeedingResult round;
private readonly FillFlowContainer flow;
public SeedingBeatmapEditor(SeedingResult round)
{
this.round = round;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChild = flow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = 200,
LayoutEasing = Easing.OutQuint,
ChildrenEnumerable = round.Beatmaps.Select(p => new SeedingBeatmapRow(round, p))
};
}
public void CreateNew()
{
var user = new SeedingBeatmap();
round.Beatmaps.Add(user);
flow.Add(new SeedingBeatmapRow(round, user));
}
public class SeedingBeatmapRow : CompositeDrawable
{
private readonly SeedingResult result;
public SeedingBeatmap Model { get; }
[Resolved]
protected IAPIProvider API { get; private set; }
private readonly Bindable<string> beatmapId = new Bindable<string>();
private readonly Bindable<string> score = new Bindable<string>();
private readonly Container drawableContainer;
public SeedingBeatmapRow(SeedingResult result, SeedingBeatmap beatmap)
{
this.result = result;
Model = beatmap;
Margin = new MarginPadding(10);
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = 5;
InternalChildren = new Drawable[]
{
new Box
{
Colour = OsuColour.Gray(0.2f),
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
Margin = new MarginPadding(5),
Padding = new MarginPadding { Right = 160 },
Spacing = new Vector2(5),
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new SettingsNumberBox
{
LabelText = "Beatmap ID",
RelativeSizeAxes = Axes.None,
Width = 200,
Bindable = beatmapId,
},
new SettingsSlider<int>
{
LabelText = "Seed",
RelativeSizeAxes = Axes.None,
Width = 200,
Bindable = beatmap.Seed
},
new SettingsTextBox
{
LabelText = "Score",
RelativeSizeAxes = Axes.None,
Width = 200,
Bindable = score,
},
drawableContainer = new Container
{
Size = new Vector2(100, 70),
},
}
},
new DangerousSettingsButton
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.None,
Width = 150,
Text = "Delete Beatmap",
Action = () =>
{
Expire();
result.Beatmaps.Remove(beatmap);
},
}
};
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
beatmapId.Value = Model.ID.ToString();
beatmapId.BindValueChanged(idString =>
{
int parsed;
int.TryParse(idString.NewValue, out parsed);
Model.ID = parsed;
if (idString.NewValue != idString.OldValue)
Model.BeatmapInfo = null;
if (Model.BeatmapInfo != null)
{
updatePanel();
return;
}
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = Model.ID });
req.Success += res =>
{
Model.BeatmapInfo = res.ToBeatmap(rulesets);
updatePanel();
};
req.Failure += _ =>
{
Model.BeatmapInfo = null;
updatePanel();
};
API.Queue(req);
}, true);
score.Value = Model.Score.ToString();
score.BindValueChanged(str => long.TryParse(str.NewValue, out Model.Score));
}
private void updatePanel()
{
drawableContainer.Clear();
if (Model.BeatmapInfo != null)
{
drawableContainer.Child = new TournamentBeatmapPanel(Model.BeatmapInfo, result.Mod.Value)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Width = 300
};
}
}
}
}
}
protected override SeeingResultRow CreateDrawable(SeedingResult model) => new SeeingResultRow(team, model);
}
}

View File

@ -57,6 +57,9 @@ namespace osu.Game.Tournament.Screens.Editors
private readonly Container drawableContainer; private readonly Container drawableContainer;
[Resolved(canBeNull: true)]
private TournamentSceneManager sceneManager { get; set; }
[Resolved] [Resolved]
private LadderInfo ladderInfo { get; set; } private LadderInfo ladderInfo { get; set; }
@ -113,6 +116,18 @@ namespace osu.Game.Tournament.Screens.Editors
Width = 0.2f, Width = 0.2f,
Bindable = Model.FlagName Bindable = Model.FlagName
}, },
new SettingsTextBox
{
LabelText = "Seed",
Width = 0.2f,
Bindable = Model.Seed
},
new SettingsSlider<int>
{
LabelText = "Last Year Placement",
Width = 0.33f,
Bindable = Model.LastYearPlacing
},
new SettingsButton new SettingsButton
{ {
Width = 0.11f, Width = 0.11f,
@ -131,7 +146,17 @@ namespace osu.Game.Tournament.Screens.Editors
ladderInfo.Teams.Remove(Model); ladderInfo.Teams.Remove(Model);
}, },
}, },
playerEditor playerEditor,
new SettingsButton
{
Width = 0.2f,
Margin = new MarginPadding(10),
Text = "Edit seeding results",
Action = () =>
{
sceneManager?.SetScreen(new SeedingEditorScreen(team));
}
},
} }
}, },
}; };

View File

@ -5,9 +5,9 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
@ -30,7 +30,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
new TournamentLogo(), new TournamentLogo(),
new RoundDisplay new RoundDisplay
{ {
Y = 10, Y = 5,
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
}, },
@ -51,9 +51,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{ {
private readonly TeamColour teamColour; private readonly TeamColour teamColour;
private readonly Color4 red = new Color4(129, 68, 65, 255);
private readonly Color4 blue = new Color4(41, 91, 97, 255);
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>(); private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
private readonly Bindable<TournamentTeam> currentTeam = new Bindable<TournamentTeam>(); private readonly Bindable<TournamentTeam> currentTeam = new Bindable<TournamentTeam>();
private readonly Bindable<int?> currentTeamScore = new Bindable<int?>(); private readonly Bindable<int?> currentTeamScore = new Bindable<int?>();
@ -106,7 +103,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
private void teamChanged(TournamentTeam team) private void teamChanged(TournamentTeam team)
{ {
var colour = teamColour == TeamColour.Red ? red : blue; var colour = teamColour == TeamColour.Red ? TournamentGame.COLOUR_RED : TournamentGame.COLOUR_BLUE;
var flip = teamColour != TeamColour.Red; var flip = teamColour != TeamColour.Red;
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
@ -169,13 +166,13 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
Children = new Drawable[] Children = new Drawable[]
{ {
Flag, Flag,
new OsuSpriteText new TournamentSpriteText
{ {
Text = team?.FullName.Value.ToUpper() ?? "???", Text = team?.FullName.Value.ToUpper() ?? "???",
X = (flip ? -1 : 1) * 90, X = (flip ? -1 : 1) * 90,
Y = -10, Y = -10,
Colour = colour, Colour = colour,
Font = TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 20), Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: 20),
Origin = anchor, Origin = anchor,
Anchor = anchor, Anchor = anchor,
}, },
@ -188,10 +185,31 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{ {
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>(); private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
private readonly TournamentSpriteText text;
public RoundDisplay() public RoundDisplay()
{ {
Width = 200; Width = 200;
Height = 20; Height = 20;
Masking = true;
CornerRadius = 10;
InternalChildren = new Drawable[]
{
new Box
{
Colour = OsuColour.Gray(0.18f),
RelativeSizeAxes = Axes.Both,
},
text = new TournamentSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = Color4.White,
Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: 16),
},
};
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -201,20 +219,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
currentMatch.BindTo(ladder.CurrentMatch); currentMatch.BindTo(ladder.CurrentMatch);
} }
private void matchChanged(ValueChangedEvent<TournamentMatch> match) private void matchChanged(ValueChangedEvent<TournamentMatch> match) =>
{ text.Text = match.NewValue.Round.Value?.Name.Value ?? "Unknown Round";
InternalChildren = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = Color4.White,
Text = match.NewValue.Round.Value?.Name.Value ?? "Unknown Round",
Font = TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 18),
},
};
}
} }
} }
} }

View File

@ -123,8 +123,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
public bool Winning public bool Winning
{ {
set => DisplayedCountSpriteText.Font = value set => DisplayedCountSpriteText.Font = value
? TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 60) ? OsuFont.Torus.With(weight: FontWeight.Regular, size: 60)
: TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Light, size: 40); : OsuFont.Torus.With(weight: FontWeight.Light, size: 40);
} }
} }
} }

View File

@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
@ -26,7 +25,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
{ {
private readonly TournamentMatch match; private readonly TournamentMatch match;
private readonly bool losers; private readonly bool losers;
private OsuSpriteText scoreText; private TournamentSpriteText scoreText;
private Box background; private Box background;
private readonly Bindable<int?> score = new Bindable<int?>(); private readonly Bindable<int?> score = new Bindable<int?>();
@ -69,7 +68,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
AcronymText.Anchor = AcronymText.Origin = Anchor.CentreLeft; AcronymText.Anchor = AcronymText.Origin = Anchor.CentreLeft;
AcronymText.Padding = new MarginPadding { Left = 50 }; AcronymText.Padding = new MarginPadding { Left = 50 };
AcronymText.Font = OsuFont.GetFont(size: 24); AcronymText.Font = OsuFont.Torus.With(size: 24);
if (match != null) if (match != null)
{ {
@ -119,11 +118,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
Alpha = 0.8f, Alpha = 0.8f,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
scoreText = new OsuSpriteText scoreText = new TournamentSpriteText
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 20), Font = OsuFont.Torus.With(size: 20),
} }
} }
} }
@ -184,7 +183,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
background.FadeColour(winner ? colourWinner : colourNormal, winner ? 500 : 0, Easing.OutQuint); background.FadeColour(winner ? colourWinner : colourNormal, winner ? 500 : 0, Easing.OutQuint);
scoreText.Font = AcronymText.Font = OsuFont.GetFont(weight: winner ? FontWeight.Bold : FontWeight.Regular); scoreText.Font = AcronymText.Font = OsuFont.Torus.With(weight: winner ? FontWeight.Bold : FontWeight.Regular);
} }
public MenuItem[] ContextMenuItems public MenuItem[] ContextMenuItems

View File

@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osuTK.Graphics; using osuTK.Graphics;
@ -22,8 +21,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
public DrawableTournamentRound(TournamentRound round, bool losers = false) public DrawableTournamentRound(TournamentRound round, bool losers = false)
{ {
OsuSpriteText textName; TournamentSpriteText textName;
OsuSpriteText textDescription; TournamentSpriteText textDescription;
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer InternalChild = new FillFlowContainer
@ -32,15 +31,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
textDescription = new OsuSpriteText textDescription = new TournamentSpriteText
{ {
Colour = Color4.Black, Colour = Color4.Black,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre Anchor = Anchor.TopCentre
}, },
textName = new OsuSpriteText textName = new TournamentSpriteText
{ {
Font = OsuFont.GetFont(weight: FontWeight.Bold), Font = OsuFont.Torus.With(weight: FontWeight.Bold),
Colour = Color4.Black, Colour = Color4.Black,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre Anchor = Anchor.TopCentre

View File

@ -9,7 +9,6 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings;
using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
@ -126,66 +125,5 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
}); });
} }
} }
private class SettingsTeamDropdown : LadderSettingsDropdown<TournamentTeam>
{
public SettingsTeamDropdown(BindableList<TournamentTeam> teams)
{
foreach (var t in teams.Prepend(new TournamentTeam()))
add(t);
teams.CollectionChanged += (_, args) =>
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
args.NewItems.Cast<TournamentTeam>().ForEach(add);
break;
case NotifyCollectionChangedAction.Remove:
args.OldItems.Cast<TournamentTeam>().ForEach(i => Control.RemoveDropdownItem(i));
break;
}
};
}
private readonly List<IUnbindable> refBindables = new List<IUnbindable>();
private T boundReference<T>(T obj)
where T : IBindable
{
obj = (T)obj.GetBoundCopy();
refBindables.Add(obj);
return obj;
}
private void add(TournamentTeam team)
{
Control.AddDropdownItem(team);
boundReference(team.FullName).BindValueChanged(_ =>
{
Control.RemoveDropdownItem(team);
Control.AddDropdownItem(team);
});
}
}
private class LadderSettingsDropdown<T> : SettingsDropdown<T>
{
protected override OsuDropdown<T> CreateDropdown() => new DropdownControl();
private new class DropdownControl : SettingsDropdown<T>.DropdownControl
{
protected override DropdownMenu CreateMenu() => new Menu();
private new class Menu : OsuDropdownMenu
{
public Menu()
{
MaxHeight = 200;
}
}
}
}
} }
} }

View File

@ -0,0 +1,26 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
namespace osu.Game.Tournament.Screens.Ladder.Components
{
public class LadderSettingsDropdown<T> : SettingsDropdown<T>
{
protected override OsuDropdown<T> CreateDropdown() => new DropdownControl();
private new class DropdownControl : SettingsDropdown<T>.DropdownControl
{
protected override DropdownMenu CreateMenu() => new Menu();
private new class Menu : OsuDropdownMenu
{
public Menu()
{
MaxHeight = 200;
}
}
}
}
}

View File

@ -0,0 +1,55 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Tournament.Models;
namespace osu.Game.Tournament.Screens.Ladder.Components
{
public class SettingsTeamDropdown : LadderSettingsDropdown<TournamentTeam>
{
public SettingsTeamDropdown(BindableList<TournamentTeam> teams)
{
foreach (var t in teams.Prepend(new TournamentTeam()))
add(t);
teams.CollectionChanged += (_, args) =>
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
args.NewItems.Cast<TournamentTeam>().ForEach(add);
break;
case NotifyCollectionChangedAction.Remove:
args.OldItems.Cast<TournamentTeam>().ForEach(i => Control.RemoveDropdownItem(i));
break;
}
};
}
private readonly List<IUnbindable> refBindables = new List<IUnbindable>();
private T boundReference<T>(T obj)
where T : IBindable
{
obj = (T)obj.GetBoundCopy();
refBindables.Add(obj);
return obj;
}
private void add(TournamentTeam team)
{
Control.AddDropdownItem(team);
boundReference(team.FullName).BindValueChanged(_ =>
{
Control.RemoveDropdownItem(team);
Control.AddDropdownItem(team);
});
}
}
}

View File

@ -42,7 +42,7 @@ namespace osu.Game.Tournament.Screens.Ladder
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
new TourneyVideo(storage.GetStream(@"BG Side Logo - OWC.m4v")) new TourneyVideo(storage.GetStream(@"videos/ladder.m4v"))
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Loop = true, Loop = true,
@ -80,7 +80,7 @@ namespace osu.Game.Tournament.Screens.Ladder
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
foreach (var p in args.NewItems.Cast<TournamentMatch>()) foreach (var p in args.OldItems.Cast<TournamentMatch>())
{ {
foreach (var d in MatchesContainer.Where(d => d.Match == p)) foreach (var d in MatchesContainer.Where(d => d.Match == p))
d.Expire(); d.Expire();

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.IPC; using osu.Game.Tournament.IPC;
@ -56,7 +55,7 @@ namespace osu.Game.Tournament.Screens.MapPool
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText new TournamentSpriteText
{ {
Text = "Current Mode" Text = "Current Mode"
}, },

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Tournament.Screens.Ladder.Components;
@ -34,7 +33,7 @@ namespace osu.Game.Tournament.Screens.Schedule
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new TourneyVideo(storage.GetStream(@"BG Side Logo - OWC.m4v")) new TourneyVideo(storage.GetStream(@"videos/schedule.m4v"))
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Loop = true, Loop = true,
@ -107,20 +106,20 @@ namespace osu.Game.Tournament.Screens.Schedule
Height = 0.25f, Height = 0.25f,
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText new TournamentSpriteText
{ {
Margin = new MarginPadding { Left = -10, Bottom = 10, Top = -5 }, Margin = new MarginPadding { Left = -10, Bottom = 10, Top = -5 },
Spacing = new Vector2(10, 0), Spacing = new Vector2(10, 0),
Text = match.NewValue.Round.Value?.Name.Value, Text = match.NewValue.Round.Value?.Name.Value,
Colour = Color4.Black, Colour = Color4.Black,
Font = OsuFont.GetFont(size: 20) Font = OsuFont.Torus.With(size: 20)
}, },
new ScheduleMatch(match.NewValue, false), new ScheduleMatch(match.NewValue, false),
new OsuSpriteText new TournamentSpriteText
{ {
Text = "Start Time " + match.NewValue.Date.Value.ToUniversalTime().ToString("HH:mm UTC"), Text = "Start Time " + match.NewValue.Date.Value.ToUniversalTime().ToString("HH:mm UTC"),
Colour = Color4.Black, Colour = Color4.Black,
Font = OsuFont.GetFont(size: 20) Font = OsuFont.Torus.With(size: 20)
}, },
} }
} }
@ -150,7 +149,7 @@ namespace osu.Game.Tournament.Screens.Schedule
Alpha = conditional ? 0.6f : 1, Alpha = conditional ? 0.6f : 1,
Margin = new MarginPadding { Horizontal = 10, Vertical = 5 }, Margin = new MarginPadding { Horizontal = 10, Vertical = 5 },
}); });
AddInternal(new OsuSpriteText AddInternal(new TournamentSpriteText
{ {
Anchor = Anchor.BottomRight, Anchor = Anchor.BottomRight,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
@ -174,13 +173,13 @@ namespace osu.Game.Tournament.Screens.Schedule
Padding = new MarginPadding { Left = 30, Top = 30 }; Padding = new MarginPadding { Left = 30, Top = 30 };
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new OsuSpriteText new TournamentSpriteText
{ {
X = 30, X = 30,
Text = title, Text = title,
Colour = Color4.Black, Colour = Color4.Black,
Spacing = new Vector2(10, 0), Spacing = new Vector2(10, 0),
Font = OsuFont.GetFont(size: 30) Font = OsuFont.Torus.With(size: 30)
}, },
content = new FillFlowContainer content = new FillFlowContainer
{ {

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Online.API; using osu.Game.Online.API;
@ -147,7 +146,7 @@ namespace osu.Game.Tournament.Screens
public Action Action; public Action Action;
private OsuSpriteText valueText; private TournamentSpriteText valueText;
protected override Drawable CreateComponent() => new Container protected override Drawable CreateComponent() => new Container
{ {
@ -155,7 +154,7 @@ namespace osu.Game.Tournament.Screens
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Children = new Drawable[] Children = new Drawable[]
{ {
valueText = new OsuSpriteText valueText = new TournamentSpriteText
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,

View File

@ -10,7 +10,7 @@ namespace osu.Game.Tournament.Screens.Showcase
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
AddInternal(new TournamentLogo(false)); AddInternal(new TournamentLogo());
} }
} }
} }

View File

@ -11,20 +11,12 @@ namespace osu.Game.Tournament.Screens.Showcase
{ {
public class TournamentLogo : CompositeDrawable public class TournamentLogo : CompositeDrawable
{ {
public TournamentLogo(bool includeRoundBackground = true) public TournamentLogo()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Margin = new MarginPadding { Vertical = 5 }; Margin = new MarginPadding { Vertical = 5 };
if (includeRoundBackground) Height = 100;
{
AutoSizeAxes = Axes.Y;
}
else
{
Masking = true;
Height = 100;
}
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -32,9 +24,11 @@ namespace osu.Game.Tournament.Screens.Showcase
{ {
InternalChild = new Sprite InternalChild = new Sprite
{ {
Texture = textures.Get("game-screen-logo"),
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
FillMode = FillMode.Fit,
RelativeSizeAxes = Axes.Both,
Texture = textures.Get("game-screen-logo"),
}; };
} }
} }

View File

@ -0,0 +1,316 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Platform;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Ladder.Components;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tournament.Screens.TeamIntro
{
public class SeedingScreen : TournamentScreen, IProvideVideo
{
private Container mainContainer;
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
private readonly Bindable<TournamentTeam> currentTeam = new Bindable<TournamentTeam>();
[BackgroundDependencyLoader]
private void load(Storage storage)
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new TourneyVideo(storage.GetStream(@"videos/seeding.m4v"))
{
RelativeSizeAxes = Axes.Both,
Loop = true,
},
mainContainer = new Container
{
RelativeSizeAxes = Axes.Both,
},
new ControlPanel
{
Children = new Drawable[]
{
new TourneyButton
{
RelativeSizeAxes = Axes.X,
Text = "Show first team",
Action = () => currentTeam.Value = currentMatch.Value.Team1.Value,
},
new TourneyButton
{
RelativeSizeAxes = Axes.X,
Text = "Show second team",
Action = () => currentTeam.Value = currentMatch.Value.Team2.Value,
},
new SettingsTeamDropdown(LadderInfo.Teams)
{
LabelText = "Show specific team",
Bindable = currentTeam,
}
}
}
};
currentMatch.BindValueChanged(matchChanged);
currentMatch.BindTo(LadderInfo.CurrentMatch);
currentTeam.BindValueChanged(teamChanged, true);
}
private void teamChanged(ValueChangedEvent<TournamentTeam> team)
{
if (team.NewValue == null)
{
mainContainer.Clear();
return;
}
showTeam(team.NewValue);
}
private void matchChanged(ValueChangedEvent<TournamentMatch> match) =>
currentTeam.Value = currentMatch.Value.Team1.Value;
private void showTeam(TournamentTeam team)
{
mainContainer.Children = new Drawable[]
{
new LeftInfo(team) { Position = new Vector2(55, 150), },
new RightInfo(team) { Position = new Vector2(500, 150), },
};
}
private class RightInfo : CompositeDrawable
{
public RightInfo(TournamentTeam team)
{
FillFlowContainer fill;
Width = 400;
InternalChildren = new Drawable[]
{
fill = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
},
};
foreach (var seeding in team.SeedingResults)
{
fill.Add(new ModRow(seeding.Mod.Value, seeding.Seed.Value));
foreach (var beatmap in seeding.Beatmaps)
fill.Add(new BeatmapScoreRow(beatmap));
}
}
private class BeatmapScoreRow : CompositeDrawable
{
public BeatmapScoreRow(SeedingBeatmap beatmap)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Children = new Drawable[]
{
new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Title, Colour = Color4.Black, },
new TournamentSpriteText { Text = "by", Colour = Color4.Black, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Artist, Colour = Color4.Black, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
}
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(40),
Children = new Drawable[]
{
new TournamentSpriteText { Text = beatmap.Score.ToString("#,0"), Colour = Color4.Black, Width = 80 },
new TournamentSpriteText { Text = "#" + beatmap.Seed.Value.ToString("#,0"), Colour = Color4.Black, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
}
},
};
}
}
private class ModRow : CompositeDrawable
{
private readonly string mods;
private readonly int seeding;
public ModRow(string mods, int seeding)
{
this.mods = mods;
this.seeding = seeding;
Padding = new MarginPadding { Vertical = 10 };
AutoSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Children = new Drawable[]
{
new Sprite
{
Texture = textures.Get($"mods/{mods.ToLower()}"),
Scale = new Vector2(0.5f)
},
new Container
{
Size = new Vector2(50, 16),
CornerRadius = 10,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
new TournamentSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = seeding.ToString("#,0"),
},
}
},
}
},
};
}
}
}
private class LeftInfo : CompositeDrawable
{
public LeftInfo(TournamentTeam team)
{
FillFlowContainer fill;
Width = 200;
if (team == null) return;
InternalChildren = new Drawable[]
{
fill = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new TeamDisplay(team) { Margin = new MarginPadding { Bottom = 30 } },
new RowDisplay("Average Rank:", $"#{team.AverageRank:#,0}"),
new RowDisplay("Seed:", team.Seed.Value),
new RowDisplay("Last year's placing:", team.LastYearPlacing.Value > 0 ? $"#{team.LastYearPlacing:#,0}" : "0"),
new Container { Margin = new MarginPadding { Bottom = 30 } },
}
},
};
foreach (var p in team.Players)
fill.Add(new RowDisplay(p.Username, p.Statistics?.Ranks.Global?.ToString("\\##,0") ?? "-"));
}
internal class RowDisplay : CompositeDrawable
{
public RowDisplay(string left, string right)
{
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
var colour = OsuColour.Gray(0.3f);
InternalChildren = new Drawable[]
{
new TournamentSpriteText
{
Text = left,
Colour = colour,
Font = OsuFont.Torus.With(size: 22),
},
new TournamentSpriteText
{
Text = right,
Colour = colour,
Anchor = Anchor.TopRight,
Origin = Anchor.TopLeft,
Font = OsuFont.Torus.With(size: 22, weight: FontWeight.Regular),
},
};
}
}
private class TeamDisplay : DrawableTournamentTeam
{
public TeamDisplay(TournamentTeam team)
: base(team)
{
AutoSizeAxes = Axes.Both;
Flag.RelativeSizeAxes = Axes.None;
Flag.Size = new Vector2(300, 200);
Flag.Scale = new Vector2(0.3f);
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
Flag,
new OsuSpriteText
{
Text = team?.FullName.Value ?? "???",
Font = OsuFont.Torus.With(size: 32, weight: FontWeight.SemiBold),
Colour = Color4.Black,
},
}
};
}
}
}
}
}

View File

@ -7,12 +7,9 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Showcase;
using osuTK; using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tournament.Screens.TeamIntro namespace osu.Game.Tournament.Screens.TeamIntro
{ {
@ -29,12 +26,11 @@ namespace osu.Game.Tournament.Screens.TeamIntro
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new TourneyVideo(storage.GetStream(@"BG Team - Both OWC.m4v")) new TourneyVideo(storage.GetStream(@"videos/teamintro.m4v"))
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Loop = true, Loop = true,
}, },
new TournamentLogo(false),
mainContainer = new Container mainContainer = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -75,8 +71,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Height = 0.25f, Height = 0.25f,
Anchor = Anchor.BottomCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre, Origin = Anchor.TopCentre,
Y = 180,
} }
}; };
} }
@ -85,8 +82,6 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{ {
public RoundDisplay(TournamentMatch match) public RoundDisplay(TournamentMatch match)
{ {
var col = OsuColour.Gray(0.33f);
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new FillFlowContainer new FillFlowContainer
@ -98,31 +93,13 @@ namespace osu.Game.Tournament.Screens.TeamIntro
Spacing = new Vector2(0, 10), Spacing = new Vector2(0, 10),
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText new TournamentSpriteText
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Colour = col, Colour = OsuColour.Gray(0.33f),
Text = "COMING UP NEXT",
Spacing = new Vector2(2, 0),
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Black)
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = col,
Text = match.Round.Value?.Name.Value ?? "Unknown Round", Text = match.Round.Value?.Name.Value ?? "Unknown Round",
Spacing = new Vector2(10, 0), Font = OsuFont.Torus.With(size: 26, weight: FontWeight.Light)
Font = OsuFont.GetFont(size: 50, weight: FontWeight.Light)
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = col,
Text = match.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"),
Font = OsuFont.GetFont(size: 20)
}, },
} }
} }
@ -132,21 +109,19 @@ namespace osu.Game.Tournament.Screens.TeamIntro
private class TeamWithPlayers : CompositeDrawable private class TeamWithPlayers : CompositeDrawable
{ {
private readonly Color4 red = new Color4(129, 68, 65, 255);
private readonly Color4 blue = new Color4(41, 91, 97, 255);
public TeamWithPlayers(TournamentTeam team, bool left = false) public TeamWithPlayers(TournamentTeam team, bool left = false)
{ {
FillFlowContainer players; FillFlowContainer players;
var colour = left ? red : blue; var colour = left ? TournamentGame.COLOUR_RED : TournamentGame.COLOUR_BLUE;
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new TeamDisplay(team, left ? "Team Red" : "Team Blue", colour) new TeamDisplay(team)
{ {
Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft,
Origin = Anchor.Centre, Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.Both, RelativePositionAxes = Axes.Both,
X = (left ? -1 : 1) * 0.36f, X = (left ? -1 : 1) * 0.3145f,
Y = -0.077f,
}, },
players = new FillFlowContainer players = new FillFlowContainer
{ {
@ -157,7 +132,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft,
Origin = left ? Anchor.CentreRight : Anchor.CentreLeft, Origin = left ? Anchor.CentreRight : Anchor.CentreLeft,
RelativePositionAxes = Axes.Both, RelativePositionAxes = Axes.Both,
X = (left ? -1 : 1) * 0.66f, X = (left ? -1 : 1) * 0.58f,
}, },
}; };
@ -165,10 +140,10 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{ {
foreach (var p in team.Players) foreach (var p in team.Players)
{ {
players.Add(new OsuSpriteText players.Add(new TournamentSpriteText
{ {
Text = p.Username, Text = p.Username,
Font = OsuFont.GetFont(size: 24), Font = OsuFont.Torus.With(size: 24),
Colour = colour, Colour = colour,
Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft,
Origin = left ? Anchor.CentreRight : Anchor.CentreLeft, Origin = left ? Anchor.CentreRight : Anchor.CentreLeft,
@ -179,7 +154,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
private class TeamDisplay : DrawableTournamentTeam private class TeamDisplay : DrawableTournamentTeam
{ {
public TeamDisplay(TournamentTeam team, string teamName, Color4 colour) public TeamDisplay(TournamentTeam team)
: base(team) : base(team)
{ {
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
@ -187,33 +162,24 @@ namespace osu.Game.Tournament.Screens.TeamIntro
Flag.Anchor = Flag.Origin = Anchor.TopCentre; Flag.Anchor = Flag.Origin = Anchor.TopCentre;
Flag.RelativeSizeAxes = Axes.None; Flag.RelativeSizeAxes = Axes.None;
Flag.Size = new Vector2(300, 200); Flag.Size = new Vector2(300, 200);
Flag.Scale = new Vector2(0.4f); Flag.Scale = new Vector2(0.32f);
Flag.Margin = new MarginPadding { Bottom = 20 };
InternalChild = new FillFlowContainer InternalChild = new FillFlowContainer
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5), Spacing = new Vector2(160),
Children = new Drawable[] Children = new Drawable[]
{ {
Flag, Flag,
new OsuSpriteText new TournamentSpriteText
{ {
Text = team?.FullName.Value.ToUpper() ?? "???", Text = team?.FullName.Value ?? "???",
Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 40, FontWeight.Light), Font = OsuFont.Torus.With(size: 20, weight: FontWeight.Regular),
Colour = Color4.Black, Colour = OsuColour.Gray(0.2f),
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
}, },
new OsuSpriteText
{
Text = teamName.ToUpper(),
Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 20, FontWeight.Regular),
Colour = colour,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
}
} }
}; };
} }

View File

@ -7,10 +7,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Showcase;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -33,22 +31,18 @@ namespace osu.Game.Tournament.Screens.TeamWin
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
blueWinVideo = new TourneyVideo(storage.GetStream(@"BG Team - Win Blue.m4v")) blueWinVideo = new TourneyVideo(storage.GetStream(@"videos/teamwin-blue.m4v"))
{ {
Alpha = 1, Alpha = 1,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Loop = true, Loop = true,
}, },
redWinVideo = new TourneyVideo(storage.GetStream(@"BG Team - Win Red.m4v")) redWinVideo = new TourneyVideo(storage.GetStream(@"videos/teamwin-red.m4v"))
{ {
Alpha = 0, Alpha = 0,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Loop = true, Loop = true,
}, },
new TournamentLogo(false)
{
Y = 40,
},
mainContainer = new Container mainContainer = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -85,141 +79,99 @@ namespace osu.Game.Tournament.Screens.TeamWin
mainContainer.Children = new Drawable[] mainContainer.Children = new Drawable[]
{ {
new TeamFlagDisplay(match.Winner)
{
Size = new Vector2(300, 200),
Scale = new Vector2(0.5f),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
X = -387,
},
new TournamentSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.TopLeft,
Position = new Vector2(78, -70),
Colour = OsuColour.Gray(0.33f),
Text = match.Round.Value?.Name.Value ?? "Unknown Round",
Font = OsuFont.Torus.With(size: 30, weight: FontWeight.Regular)
},
new TeamWithPlayers(match.Winner, redWin) new TeamWithPlayers(match.Winner, redWin)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Width = 0.5f, Width = 0.5f,
Height = 0.6f, Height = 0.6f,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre Origin = Anchor.TopLeft,
Position = new Vector2(78, 0),
}, },
new RoundDisplay(match)
{
RelativeSizeAxes = Axes.Both,
Height = 0.25f,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
}
}; };
} }
private class RoundDisplay : CompositeDrawable
{
public RoundDisplay(TournamentMatch match)
{
var col = OsuColour.Gray(0.33f);
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = col,
Text = "WINNER",
Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 15, FontWeight.Regular),
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = col,
Text = match.Round.Value?.Name.Value ?? "Unknown Round",
Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 50, FontWeight.Light),
Spacing = new Vector2(10, 0),
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = col,
Text = match.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"),
Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 20, FontWeight.Light),
},
}
}
};
}
}
private class TeamWithPlayers : CompositeDrawable private class TeamWithPlayers : CompositeDrawable
{ {
private readonly Color4 red = new Color4(129, 68, 65, 255);
private readonly Color4 blue = new Color4(41, 91, 97, 255);
public TeamWithPlayers(TournamentTeam team, bool left = false) public TeamWithPlayers(TournamentTeam team, bool left = false)
{ {
var colour = left ? red : blue; FillFlowContainer players;
var colour = left ? TournamentGame.COLOUR_RED : TournamentGame.COLOUR_BLUE;
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new TeamDisplay(team, left ? "Team Red" : "Team Blue", colour)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new FillFlowContainer new FillFlowContainer
{ {
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Spacing = new Vector2(0, 5),
Padding = new MarginPadding(20),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Both,
},
};
}
private class TeamDisplay : DrawableTournamentTeam
{
public TeamDisplay(TournamentTeam team, string teamName, Color4 colour)
: base(team)
{
AutoSizeAxes = Axes.Both;
Flag.Anchor = Flag.Origin = Anchor.TopCentre;
Flag.RelativeSizeAxes = Axes.None;
Flag.Size = new Vector2(300, 200);
Flag.Scale = new Vector2(0.4f);
Flag.Margin = new MarginPadding { Bottom = 20 };
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[] Children = new Drawable[]
{ {
Flag, new TournamentSpriteText
new OsuSpriteText
{ {
Text = team?.FullName.Value.ToUpper() ?? "???", Text = "WINNER",
Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 40, FontWeight.Light), Font = OsuFont.Torus.With(size: 24, weight: FontWeight.SemiBold),
Colour = Color4.Black, Colour = Color4.Black,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
}, },
new OsuSpriteText new TournamentSpriteText
{ {
Text = teamName.ToUpper(), Text = team?.FullName.Value ?? "???",
Font = OsuFont.GetFont(size: 20), Font = OsuFont.Torus.With(size: 30, weight: FontWeight.SemiBold),
Colour = colour, Colour = Color4.Black,
Origin = Anchor.TopCentre, },
Anchor = Anchor.TopCentre, players = new FillFlowContainer
} {
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 10 },
},
} }
}; },
};
if (team != null)
{
foreach (var p in team.Players)
{
players.Add(new TournamentSpriteText
{
Text = p.Username,
Font = OsuFont.Torus.With(size: 24),
Colour = colour,
Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft,
Origin = left ? Anchor.CentreRight : Anchor.CentreLeft,
});
}
} }
} }
} }
private class TeamFlagDisplay : DrawableTournamentTeam
{
public TeamFlagDisplay(TournamentTeam team)
: base(team)
{
InternalChildren = new Drawable[]
{
Flag
};
}
}
} }
} }

View File

@ -1,75 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
namespace osu.Game.Tournament
{
public static class TournamentFont
{
/// <summary>
/// The default font size.
/// </summary>
public const float DEFAULT_FONT_SIZE = 16;
/// <summary>
/// Retrieves a <see cref="FontUsage"/>.
/// </summary>
/// <param name="typeface">The font typeface.</param>
/// <param name="size">The size of the text in local space. For a value of 16, a single line will have a height of 16px.</param>
/// <param name="weight">The font weight.</param>
/// <param name="italics">Whether the font is italic.</param>
/// <param name="fixedWidth">Whether all characters should be spaced the same distance apart.</param>
/// <returns>The <see cref="FontUsage"/>.</returns>
public static FontUsage GetFont(TournamentTypeface typeface = TournamentTypeface.Aquatico, float size = DEFAULT_FONT_SIZE, FontWeight weight = FontWeight.Medium, bool italics = false, bool fixedWidth = false)
=> new FontUsage(GetFamilyString(typeface), size, GetWeightString(typeface, weight), italics, fixedWidth);
/// <summary>
/// Retrieves the string representation of a <see cref="TournamentTypeface"/>.
/// </summary>
/// <param name="typeface">The <see cref="TournamentTypeface"/>.</param>
/// <returns>The string representation.</returns>
public static string GetFamilyString(TournamentTypeface typeface)
{
switch (typeface)
{
case TournamentTypeface.Aquatico:
return "Aquatico";
}
return null;
}
/// <summary>
/// Retrieves the string representation of a <see cref="FontWeight"/>.
/// </summary>
/// <param name="typeface">The <see cref="TournamentTypeface"/>.</param>
/// <param name="weight">The <see cref="FontWeight"/>.</param>
/// <returns>The string representation of <paramref name="weight"/> in the specified <paramref name="typeface"/>.</returns>
public static string GetWeightString(TournamentTypeface typeface, FontWeight weight)
=> GetWeightString(GetFamilyString(typeface), weight);
/// <summary>
/// Retrieves the string representation of a <see cref="FontWeight"/>.
/// </summary>
/// <param name="family">The family string.</param>
/// <param name="weight">The <see cref="FontWeight"/>.</param>
/// <returns>The string representation of <paramref name="weight"/> in the specified <paramref name="family"/>.</returns>
public static string GetWeightString(string family, FontWeight weight)
{
string weightString = weight.ToString();
// Only exo has an explicit "regular" weight, other fonts do not
if (weight == FontWeight.Regular && family != GetFamilyString(TournamentTypeface.Aquatico))
weightString = string.Empty;
return weightString;
}
}
public enum TournamentTypeface
{
Aquatico
}
}

View File

@ -3,11 +3,15 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osuTK.Graphics;
namespace osu.Game.Tournament namespace osu.Game.Tournament
{ {
public class TournamentGame : TournamentGameBase public class TournamentGame : TournamentGameBase
{ {
public static readonly Color4 COLOUR_RED = new Color4(144, 0, 0, 255);
public static readonly Color4 COLOUR_BLUE = new Color4(0, 84, 144, 255);
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();

View File

@ -18,7 +18,6 @@ using osu.Framework.IO.Stores;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Tournament.IPC; using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
@ -54,9 +53,6 @@ namespace osu.Game.Tournament
{ {
Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly)); Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly));
AddFont(Resources, @"Resources/Fonts/Aquatico-Regular");
AddFont(Resources, @"Resources/Fonts/Aquatico-Light");
Textures.AddStore(new TextureLoaderStore(new ResourceStore<byte[]>(new StorageBackedResourceStore(storage)))); Textures.AddStore(new TextureLoaderStore(new ResourceStore<byte[]>(new StorageBackedResourceStore(storage))));
this.storage = storage; this.storage = storage;
@ -104,10 +100,10 @@ namespace osu.Game.Tournament
Colour = Color4.Red, Colour = Color4.Red,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
new OsuSpriteText new TournamentSpriteText
{ {
Text = "Please make the window wider", Text = "Please make the window wider",
Font = OsuFont.Default.With(weight: "bold"), Font = OsuFont.Torus.With(weight: FontWeight.Bold),
Colour = Color4.White, Colour = Color4.White,
Padding = new MarginPadding(20) Padding = new MarginPadding(20)
} }
@ -124,10 +120,9 @@ namespace osu.Game.Tournament
using (var sr = new StreamReader(stream)) using (var sr = new StreamReader(stream))
ladder = JsonConvert.DeserializeObject<LadderInfo>(sr.ReadToEnd()); ladder = JsonConvert.DeserializeObject<LadderInfo>(sr.ReadToEnd());
} }
else
{ if (ladder == null)
ladder = new LadderInfo(); ladder = new LadderInfo();
}
if (ladder.Ruleset.Value == null) if (ladder.Ruleset.Value == null)
ladder.Ruleset.Value = RulesetStore.AvailableRulesets.First(); ladder.Ruleset.Value = RulesetStore.AvailableRulesets.First();
@ -205,9 +200,11 @@ namespace osu.Game.Tournament
{ {
foreach (var p in t.Players) foreach (var p in t.Players)
{ {
if (p.Username == null || p.Statistics == null) if (string.IsNullOrEmpty(p.Username) || p.Statistics == null)
{
PopulateUser(p); PopulateUser(p);
addedInfo = true; addedInfo = true;
}
} }
} }
@ -243,6 +240,24 @@ namespace osu.Game.Tournament
} }
} }
foreach (var t in ladder.Teams)
{
foreach (var s in t.SeedingResults)
{
foreach (var b in s.Beatmaps)
{
if (b.BeatmapInfo == null && b.ID > 0)
{
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
req.Perform(API);
b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore);
addedInfo = true;
}
}
}
}
return addedInfo; return addedInfo;
} }

View File

@ -61,7 +61,7 @@ namespace osu.Game.Tournament
//Masking = true, //Masking = true,
Children = new Drawable[] Children = new Drawable[]
{ {
video = new TourneyVideo(storage.GetStream("BG Logoless - OWC.m4v")) video = new TourneyVideo(storage.GetStream("videos/main.m4v"))
{ {
Loop = true, Loop = true,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -80,6 +80,7 @@ namespace osu.Game.Tournament
new ShowcaseScreen(), new ShowcaseScreen(),
new MapPoolScreen(), new MapPoolScreen(),
new TeamIntroScreen(), new TeamIntroScreen(),
new SeedingScreen(),
new DrawingsScreen(), new DrawingsScreen(),
new GameplayScreen(), new GameplayScreen(),
new TeamWinScreen() new TeamWinScreen()
@ -121,6 +122,7 @@ namespace osu.Game.Tournament
new ScreenButton(typeof(LadderScreen)) { Text = "Bracket", RequestSelection = SetScreen }, new ScreenButton(typeof(LadderScreen)) { Text = "Bracket", RequestSelection = SetScreen },
new Separator(), new Separator(),
new ScreenButton(typeof(TeamIntroScreen)) { Text = "TeamIntro", RequestSelection = SetScreen }, new ScreenButton(typeof(TeamIntroScreen)) { Text = "TeamIntro", RequestSelection = SetScreen },
new ScreenButton(typeof(SeedingScreen)) { Text = "Seeding", RequestSelection = SetScreen },
new Separator(), new Separator(),
new ScreenButton(typeof(MapPoolScreen)) { Text = "MapPool", RequestSelection = SetScreen }, new ScreenButton(typeof(MapPoolScreen)) { Text = "MapPool", RequestSelection = SetScreen },
new ScreenButton(typeof(GameplayScreen)) { Text = "Gameplay", RequestSelection = SetScreen }, new ScreenButton(typeof(GameplayScreen)) { Text = "Gameplay", RequestSelection = SetScreen },
@ -146,8 +148,20 @@ namespace osu.Game.Tournament
private Drawable currentScreen; private Drawable currentScreen;
private ScheduledDelegate scheduledHide; private ScheduledDelegate scheduledHide;
private Drawable temporaryScreen;
public void SetScreen(Drawable screen)
{
currentScreen?.Hide();
currentScreen = null;
screens.Add(temporaryScreen = screen);
}
public void SetScreen(Type screenType) public void SetScreen(Type screenType)
{ {
temporaryScreen?.Expire();
var target = screens.FirstOrDefault(s => s.GetType() == screenType); var target = screens.FirstOrDefault(s => s.GetType() == screenType);
if (target == null || currentScreen == target) return; if (target == null || currentScreen == target) return;

View File

@ -0,0 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Tournament
{
public class TournamentSpriteText : OsuSpriteText
{
public TournamentSpriteText()
{
Font = OsuFont.Torus;
}
}
}

View File

@ -17,7 +17,9 @@ namespace osu.Game.Graphics
/// </summary> /// </summary>
public static FontUsage Default => GetFont(); public static FontUsage Default => GetFont();
public static FontUsage Numeric => GetFont(Typeface.Venera, weight: FontWeight.Regular); public static FontUsage Numeric => GetFont(Typeface.Venera, weight: FontWeight.Bold);
public static FontUsage Torus => GetFont(Typeface.Torus, weight: FontWeight.Regular);
/// <summary> /// <summary>
/// Retrieves a <see cref="FontUsage"/>. /// Retrieves a <see cref="FontUsage"/>.
@ -45,6 +47,9 @@ namespace osu.Game.Graphics
case Typeface.Venera: case Typeface.Venera:
return "Venera"; return "Venera";
case Typeface.Torus:
return "Torus";
} }
return null; return null;
@ -65,16 +70,7 @@ namespace osu.Game.Graphics
/// <param name="family">The family string.</param> /// <param name="family">The family string.</param>
/// <param name="weight">The <see cref="FontWeight"/>.</param> /// <param name="weight">The <see cref="FontWeight"/>.</param>
/// <returns>The string representation of <paramref name="weight"/> in the specified <paramref name="family"/>.</returns> /// <returns>The string representation of <paramref name="weight"/> in the specified <paramref name="family"/>.</returns>
public static string GetWeightString(string family, FontWeight weight) public static string GetWeightString(string family, FontWeight weight) => weight.ToString();
{
string weightString = weight.ToString();
// Only exo has an explicit "regular" weight, other fonts do not
if (family != GetFamilyString(Typeface.Exo) && weight == FontWeight.Regular)
weightString = string.Empty;
return weightString;
}
} }
public static class OsuFontExtensions public static class OsuFontExtensions
@ -102,15 +98,39 @@ namespace osu.Game.Graphics
{ {
Exo, Exo,
Venera, Venera,
Torus
} }
public enum FontWeight public enum FontWeight
{ {
Light, /// <summary>
Regular, /// Equivalent to weight 300.
Medium, /// </summary>
SemiBold, Light = 300,
Bold,
Black /// <summary>
/// Equivalent to weight 400.
/// </summary>
Regular = 400,
/// <summary>
/// Equivalent to weight 500.
/// </summary>
Medium = 500,
/// <summary>
/// Equivalent to weight 600.
/// </summary>
SemiBold = 600,
/// <summary>
/// Equivalent to weight 700.
/// </summary>
Bold = 700,
/// <summary>
/// Equivalent to weight 900.
/// </summary>
Black = 900
} }
} }

View File

@ -31,8 +31,9 @@ namespace osu.Game.Input.Bindings
/// <param name="ruleset">A reference to identify the current <see cref="Ruleset"/>. Used to lookup mappings. Null for global mappings.</param> /// <param name="ruleset">A reference to identify the current <see cref="Ruleset"/>. Used to lookup mappings. Null for global mappings.</param>
/// <param name="variant">An optional variant for the specified <see cref="Ruleset"/>. Used when a ruleset has more than one possible keyboard layouts.</param> /// <param name="variant">An optional variant for the specified <see cref="Ruleset"/>. Used when a ruleset has more than one possible keyboard layouts.</param>
/// <param name="simultaneousMode">Specify how to deal with multiple matches of <see cref="KeyCombination"/>s and <typeparamref name="T"/>s.</param> /// <param name="simultaneousMode">Specify how to deal with multiple matches of <see cref="KeyCombination"/>s and <typeparamref name="T"/>s.</param>
public DatabasedKeyBindingContainer(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None) /// <param name="matchingMode">Specify how to deal with exact <see cref="KeyCombination"/> matches.</param>
: base(simultaneousMode) public DatabasedKeyBindingContainer(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None, KeyCombinationMatchingMode matchingMode = KeyCombinationMatchingMode.Any)
: base(simultaneousMode, matchingMode)
{ {
this.ruleset = ruleset; this.ruleset = ruleset;
this.variant = variant; this.variant = variant;

View File

@ -15,6 +15,7 @@ namespace osu.Game.Input.Bindings
private readonly Drawable handler; private readonly Drawable handler;
public GlobalActionContainer(OsuGameBase game) public GlobalActionContainer(OsuGameBase game)
: base(matchingMode: KeyCombinationMatchingMode.Modifiers)
{ {
if (game is IKeyBindingHandler<GlobalAction>) if (game is IKeyBindingHandler<GlobalAction>)
handler = game; handler = game;
@ -38,6 +39,9 @@ namespace osu.Game.Input.Bindings
new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.Escape, GlobalAction.Back),
new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back), new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back),
new KeyBinding(InputKey.Up, GlobalAction.SelectPrevious),
new KeyBinding(InputKey.Down, GlobalAction.SelectNext),
new KeyBinding(InputKey.Space, GlobalAction.Select), new KeyBinding(InputKey.Space, GlobalAction.Select),
new KeyBinding(InputKey.Enter, GlobalAction.Select), new KeyBinding(InputKey.Enter, GlobalAction.Select),
new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select), new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select),
@ -54,10 +58,11 @@ namespace osu.Game.Input.Bindings
public IEnumerable<KeyBinding> AudioControlKeyBindings => new[] public IEnumerable<KeyBinding> AudioControlKeyBindings => new[]
{ {
new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume), new KeyBinding(new[] { InputKey.Alt, InputKey.Up }, GlobalAction.IncreaseVolume),
new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume), new KeyBinding(new[] { InputKey.Alt, InputKey.MouseWheelUp }, GlobalAction.IncreaseVolume),
new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume), new KeyBinding(new[] { InputKey.Alt, InputKey.Down }, GlobalAction.DecreaseVolume),
new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume), new KeyBinding(new[] { InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.DecreaseVolume),
new KeyBinding(InputKey.F4, GlobalAction.ToggleMute), new KeyBinding(InputKey.F4, GlobalAction.ToggleMute),
new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev), new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev),
@ -141,5 +146,11 @@ namespace osu.Game.Input.Bindings
[Description("Toggle now playing overlay")] [Description("Toggle now playing overlay")]
ToggleNowPlaying, ToggleNowPlaying,
[Description("Previous Selection")]
SelectPrevious,
[Description("Next Selection")]
SelectNext,
} }
} }

View File

@ -0,0 +1,506 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20200302094919_RefreshVolumeBindings")]
partial class RefreshVolumeBindings
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("AudioLeadIn");
b.Property<double>("BPM");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<double>("Length");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.Property<string>("VideoFile");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Key")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int?>("ScoreInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("ScoreInfoID");
b.ToTable("ScoreFileInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("Accuracy")
.HasColumnType("DECIMAL(1,4)");
b.Property<int>("BeatmapInfoID");
b.Property<int>("Combo");
b.Property<DateTimeOffset>("Date");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int>("MaxCombo");
b.Property<string>("ModsJson")
.HasColumnName("Mods");
b.Property<long?>("OnlineScoreID");
b.Property<double?>("PP");
b.Property<int>("Rank");
b.Property<int>("RulesetID");
b.Property<string>("StatisticsJson")
.HasColumnName("Statistics");
b.Property<long>("TotalScore");
b.Property<long?>("UserID")
.HasColumnName("UserID");
b.Property<string>("UserString")
.HasColumnName("User");
b.HasKey("ID");
b.HasIndex("BeatmapInfoID");
b.HasIndex("OnlineScoreID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("ScoreInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Settings")
.HasForeignKey("SkinInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Scoring.ScoreInfo")
.WithMany("Files")
.HasForeignKey("ScoreInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class RefreshVolumeBindings : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("DELETE FROM KeyBinding WHERE action in (6,7)");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@ -43,7 +43,7 @@ namespace osu.Game.Migrations
b.Property<int>("ID") b.Property<int>("ID")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn"); b.Property<double>("AudioLeadIn");
b.Property<double>("BPM"); b.Property<double>("BPM");

View File

@ -58,7 +58,7 @@ namespace osu.Game.Online.Leaderboards
Spacing = new Vector2(-3, 0), Spacing = new Vector2(-3, 0),
Padding = new MarginPadding { Top = 5 }, Padding = new MarginPadding { Top = 5 },
Colour = getRankNameColour(), Colour = getRankNameColour(),
Font = OsuFont.GetFont(Typeface.Venera, 25), Font = OsuFont.Numeric.With(size: 25),
Text = getRankName(), Text = getRankName(),
ShadowColour = Color4.Black.Opacity(0.3f), ShadowColour = Color4.Black.Opacity(0.3f),
ShadowOffset = new Vector2(0, 0.08f), ShadowOffset = new Vector2(0, 0.08f),

View File

@ -25,6 +25,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -805,6 +806,13 @@ namespace osu.Game
return d; return d;
} }
protected override bool OnScroll(ScrollEvent e)
{
// forward any unhandled mouse scroll events to the volume control.
volume.Adjust(GlobalAction.IncreaseVolume, e.ScrollDelta.Y, e.IsPrecise);
return true;
}
public bool OnPressed(GlobalAction action) public bool OnPressed(GlobalAction action)
{ {
if (introScreen == null) return false; if (introScreen == null) return false;

View File

@ -157,9 +157,14 @@ namespace osu.Game
AddFont(Resources, @"Fonts/Exo2.0-Black"); AddFont(Resources, @"Fonts/Exo2.0-Black");
AddFont(Resources, @"Fonts/Exo2.0-BlackItalic"); AddFont(Resources, @"Fonts/Exo2.0-BlackItalic");
AddFont(Resources, @"Fonts/Venera"); AddFont(Resources, @"Fonts/Torus-SemiBold");
AddFont(Resources, @"Fonts/Torus-Bold");
AddFont(Resources, @"Fonts/Torus-Regular");
AddFont(Resources, @"Fonts/Torus-Light");
AddFont(Resources, @"Fonts/Venera-Light"); AddFont(Resources, @"Fonts/Venera-Light");
AddFont(Resources, @"Fonts/Venera-Medium"); AddFont(Resources, @"Fonts/Venera-Bold");
AddFont(Resources, @"Fonts/Venera-Black");
runMigrations(); runMigrations();

View File

@ -59,7 +59,7 @@ namespace osu.Game.Overlays.BeatmapSet
if (online.Ranked.HasValue) if (online.Ranked.HasValue)
{ {
fields.Add(new Field("ranked", online.Ranked.Value, OsuFont.GetFont(weight: FontWeight.Bold))); fields.Add(new Field(online.Status.ToString().ToLowerInvariant(), online.Ranked.Value, OsuFont.GetFont(weight: FontWeight.Bold)));
} }
else if (online.LastUpdated.HasValue) else if (online.LastUpdated.HasValue)
{ {

View File

@ -15,7 +15,7 @@ namespace osu.Game.Overlays.SearchableList
{ {
public abstract class SearchableListOverlay : FullscreenOverlay public abstract class SearchableListOverlay : FullscreenOverlay
{ {
public const float WIDTH_PADDING = 80; public const float WIDTH_PADDING = 10;
protected SearchableListOverlay(OverlayColourScheme colourScheme) protected SearchableListOverlay(OverlayColourScheme colourScheme)
: base(colourScheme) : base(colourScheme)

View File

@ -14,8 +14,25 @@ namespace osu.Game.Overlays.Volume
public Func<GlobalAction, bool> ActionRequested; public Func<GlobalAction, bool> ActionRequested;
public Func<GlobalAction, float, bool, bool> ScrollActionRequested; public Func<GlobalAction, float, bool, bool> ScrollActionRequested;
public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false; public bool OnPressed(GlobalAction action)
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false; {
// if nothing else handles selection actions in the game, it's safe to let volume be adjusted.
switch (action)
{
case GlobalAction.SelectPrevious:
action = GlobalAction.IncreaseVolume;
break;
case GlobalAction.SelectNext:
action = GlobalAction.DecreaseVolume;
break;
}
return ActionRequested?.Invoke(action) ?? false;
}
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) =>
ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
public void OnReleased(GlobalAction action) public void OnReleased(GlobalAction action)
{ {

View File

@ -11,16 +11,18 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Input.Bindings;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Overlays.Volume namespace osu.Game.Overlays.Volume
{ {
public class VolumeMeter : Container public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
{ {
private CircularProgress volumeCircle; private CircularProgress volumeCircle;
private CircularProgress volumeCircleGlow; private CircularProgress volumeCircleGlow;
@ -260,5 +262,28 @@ namespace osu.Game.Overlays.Volume
{ {
this.ScaleTo(1f, transition_length, Easing.OutExpo); this.ScaleTo(1f, transition_length, Easing.OutExpo);
} }
public bool OnPressed(GlobalAction action)
{
if (!IsHovered)
return false;
switch (action)
{
case GlobalAction.SelectPrevious:
adjust(1, false);
return true;
case GlobalAction.SelectNext:
adjust(-1, false);
return true;
}
return false;
}
public void OnReleased(GlobalAction action)
{
}
} }
} }

View File

@ -123,6 +123,10 @@ namespace osu.Game.Screens.Play
public void Restart() public void Restart()
{ {
// The Reset() call below causes speed adjustments to be reset in an async context, leading to deadlocks.
// The deadlock can be prevented by resetting the track synchronously before entering the async context.
track.ResetSpeedAdjustments();
Task.Run(() => Task.Run(() =>
{ {
track.Reset(); track.Reset();

View File

@ -12,7 +12,6 @@ using osu.Game.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osuTK.Input;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
@ -204,35 +203,24 @@ namespace osu.Game.Screens.Play
InternalButtons[selectionIndex].Selected.Value = true; InternalButtons[selectionIndex].Selected.Value = true;
} }
protected override bool OnKeyDown(KeyDownEvent e)
{
if (!e.Repeat)
{
switch (e.Key)
{
case Key.Up:
if (selectionIndex == -1 || selectionIndex == 0)
setSelected(InternalButtons.Count - 1);
else
setSelected(selectionIndex - 1);
return true;
case Key.Down:
if (selectionIndex == -1 || selectionIndex == InternalButtons.Count - 1)
setSelected(0);
else
setSelected(selectionIndex + 1);
return true;
}
}
return base.OnKeyDown(e);
}
public bool OnPressed(GlobalAction action) public bool OnPressed(GlobalAction action)
{ {
switch (action) switch (action)
{ {
case GlobalAction.SelectPrevious:
if (selectionIndex == -1 || selectionIndex == 0)
setSelected(InternalButtons.Count - 1);
else
setSelected(selectionIndex - 1);
return true;
case GlobalAction.SelectNext:
if (selectionIndex == -1 || selectionIndex == InternalButtons.Count - 1)
setSelected(0);
else
setSelected(selectionIndex + 1);
return true;
case GlobalAction.Back: case GlobalAction.Back:
BackAction.Invoke(); BackAction.Invoke();
return true; return true;

View File

@ -16,15 +16,17 @@ using osu.Framework.Bindables;
using osu.Framework.Caching; using osu.Framework.Caching;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Input.Bindings;
using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Carousel;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select
{ {
public class BeatmapCarousel : CompositeDrawable public class BeatmapCarousel : CompositeDrawable, IKeyBindingHandler<GlobalAction>
{ {
private const float bleed_top = FilterControl.HEIGHT; private const float bleed_top = FilterControl.HEIGHT;
private const float bleed_bottom = Footer.HEIGHT; private const float bleed_bottom = Footer.HEIGHT;
@ -435,41 +437,38 @@ namespace osu.Game.Screens.Select
protected override bool OnKeyDown(KeyDownEvent e) protected override bool OnKeyDown(KeyDownEvent e)
{ {
// allow for controlling volume when alt is held.
// this is required as the VolumeControlReceptor uses OnPressed, which is
// executed after all OnKeyDown events.
if (e.AltPressed)
return base.OnKeyDown(e);
int direction = 0;
bool skipDifficulties = false;
switch (e.Key) switch (e.Key)
{ {
case Key.Up:
direction = -1;
break;
case Key.Down:
direction = 1;
break;
case Key.Left: case Key.Left:
direction = -1; SelectNext(-1, true);
skipDifficulties = true; return true;
break;
case Key.Right: case Key.Right:
direction = 1; SelectNext(1, true);
skipDifficulties = true; return true;
break;
} }
if (direction == 0) return false;
return base.OnKeyDown(e); }
SelectNext(direction, skipDifficulties); public bool OnPressed(GlobalAction action)
return true; {
switch (action)
{
case GlobalAction.SelectNext:
SelectNext(1, false);
return true;
case GlobalAction.SelectPrevious:
SelectNext(-1, false);
return true;
}
return false;
}
public void OnReleased(GlobalAction action)
{
} }
protected override void Update() protected override void Update()

View File

@ -4,6 +4,7 @@
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
@ -49,6 +50,20 @@ namespace osu.Game.Screens.Select
} }
} }
protected override bool OnKeyDown(KeyDownEvent e)
{
switch (e.Key)
{
case Key.Enter:
// this is a special hard-coded case; we can't rely on OnPressed (of SongSelect) as GlobalActionContainer is
// matching with exact modifier consideration (so Ctrl+Enter would be ignored).
FinaliseSelection();
return true;
}
return base.OnKeyDown(e);
}
protected override bool OnStart() protected override bool OnStart()
{ {
if (player != null) return false; if (player != null) return false;

View File

@ -9,7 +9,6 @@ using osu.Framework.Testing;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
@ -22,7 +21,7 @@ namespace osu.Game.Tests.Visual
private readonly Ruleset ruleset; private readonly Ruleset ruleset;
protected Player Player; protected TestPlayer Player;
protected PlayerTestScene(Ruleset ruleset) protected PlayerTestScene(Ruleset ruleset)
{ {
@ -88,6 +87,6 @@ namespace osu.Game.Tests.Visual
LoadScreen(Player); LoadScreen(Player);
} }
protected virtual Player CreatePlayer(Ruleset ruleset) => new TestPlayer(false, false); protected virtual TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false, false);
} }
} }

View File

@ -1,23 +1,51 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
/// <summary>
/// A player that exposes many components that would otherwise not be available, for testing purposes.
/// </summary>
public class TestPlayer : Player public class TestPlayer : Player
{ {
protected override bool PauseOnFocusLost { get; } protected override bool PauseOnFocusLost { get; }
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
/// <summary>
/// Mods from *player* (not OsuScreen).
/// </summary>
public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HealthProcessor HealthProcessor => base.HealthProcessor;
public readonly List<JudgementResult> Results = new List<JudgementResult>();
public TestPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false) public TestPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false)
: base(allowPause, showResults) : base(allowPause, showResults)
{ {
PauseOnFocusLost = pauseOnFocusLost; PauseOnFocusLost = pauseOnFocusLost;
} }
[BackgroundDependencyLoader]
private void load()
{
ScoreProcessor.NewJudgement += r => Results.Add(r);
}
} }
} }

View File

@ -22,7 +22,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.221.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.304.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.302.0" /> <PackageReference Include="ppy.osu.Framework" Version="2020.302.0" />
<PackageReference Include="Sentry" Version="2.1.0" /> <PackageReference Include="Sentry" Version="2.1.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.24.0" />

View File

@ -70,7 +70,7 @@
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.221.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.304.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.302.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2020.302.0" />
</ItemGroup> </ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. --> <!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->