1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 13:22:55 +08:00

Merge branch 'master' into disable-keycounter-breaks

This commit is contained in:
Dean Herbert 2020-03-06 17:48:24 +09:00
commit 90dbb90470
110 changed files with 2537 additions and 977 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:**

42
.vscode/launch.json vendored
View File

@ -11,11 +11,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)", "preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -28,11 +23,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)", "preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -45,11 +35,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Debug)", "preLaunchTask": "Build tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -62,11 +47,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Release)", "preLaunchTask": "Build tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -80,11 +60,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)", "preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -98,11 +73,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)", "preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -116,11 +86,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tournament tests (Debug)", "preLaunchTask": "Build tournament tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -134,11 +99,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tournament tests (Release)", "preLaunchTask": "Build tournament tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -169,4 +129,4 @@
"externalConsole": false "externalConsole": false
} }
] ]
} }

View File

@ -27,7 +27,7 @@ If you are looking to install or test osu! without setting up a development envi
**Latest build:** **Latest build:**
| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.x86_64.AppImage) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) | [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
| ------------- | ------------- | ------------- | ------------- | ------------- | | ------------- | ------------- | ------------- | ------------- | ------------- |
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore31&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs. - When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore31&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.

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.228.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2020.302.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -30,11 +30,6 @@ namespace osu.Android
} }
} }
protected override void LoadComplete() protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
{
base.LoadComplete();
Add(new SimpleUpdateManager());
}
} }
} }

View File

@ -47,20 +47,25 @@ namespace osu.Desktop
return null; return null;
} }
protected override UpdateManager CreateUpdateManager()
{
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.Windows:
return new SquirrelUpdateManager();
default:
return new SimpleUpdateManager();
}
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
if (!noVersionOverlay) if (!noVersionOverlay)
{
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, Add); LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, Add);
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
Add(new SquirrelUpdateManager());
else
Add(new SimpleUpdateManager());
}
LoadComponentAsync(new DiscordRichPresence(), Add); LoadComponentAsync(new DiscordRichPresence(), Add);
} }

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

@ -3,8 +3,8 @@
using System; using System;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Layout;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osuTK; using osuTK;
@ -22,21 +22,13 @@ namespace osu.Game.Rulesets.Mania.Mods
private class ManiaFlashlight : Flashlight private class ManiaFlashlight : Flashlight
{ {
private readonly Cached flashlightProperties = new Cached(); private readonly LayoutValue flashlightProperties = new LayoutValue(Invalidation.DrawSize);
public ManiaFlashlight() public ManiaFlashlight()
{ {
FlashlightSize = new Vector2(0, default_flashlight_size); FlashlightSize = new Vector2(0, default_flashlight_size);
}
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) AddLayout(flashlightProperties);
{
if ((invalidation & Invalidation.DrawSize) > 0)
{
flashlightProperties.Invalidate();
}
return base.Invalidate(invalidation, source, shallPropagate);
} }
protected override void Update() protected override void Update()

View File

@ -2,13 +2,13 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using osu.Framework.Caching;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; 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.Layout;
using osu.Game.Graphics; using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
@ -65,6 +65,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
} }
} }
}; };
AddLayout(subtractionCache);
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -100,15 +102,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
} }
} }
private readonly Cached subtractionCache = new Cached(); private readonly LayoutValue subtractionCache = new LayoutValue(Invalidation.DrawSize);
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) > 0)
subtractionCache.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void Update() protected override void Update()
{ {

View File

@ -115,9 +115,8 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Y = HIT_TARGET_POSITION + 150, Y = HIT_TARGET_POSITION + 150,
BypassAutoSizeAxes = Axes.Both
}, },
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both } topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
} }

View File

@ -0,0 +1,86 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests.Mods
{
public class TestSceneOsuModDifficultyAdjust : ModTestScene
{
public TestSceneOsuModDifficultyAdjust()
: base(new OsuRuleset())
{
}
[Test]
public void TestNoAdjustment() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust(),
Autoplay = true,
PassCondition = checkSomeHit
});
[Test]
public void TestCircleSize1() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { CircleSize = { Value = 1 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsScale(0.78f)
});
[Test]
public void TestCircleSize10() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { CircleSize = { Value = 10 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsScale(0.15f)
});
[Test]
public void TestApproachRate1() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { ApproachRate = { Value = 1 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsPreempt(1680)
});
[Test]
public void TestApproachRate10() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { ApproachRate = { Value = 10 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsPreempt(450)
});
private bool checkObjectsPreempt(double target)
{
var objects = Player.ChildrenOfType<DrawableHitCircle>();
if (!objects.Any())
return false;
return objects.All(o => o.HitObject.TimePreempt == target);
}
private bool checkObjectsScale(float target)
{
var objects = Player.ChildrenOfType<DrawableHitCircle>();
if (!objects.Any())
return false;
return objects.All(o => Precision.AlmostEquals(o.ChildrenOfType<ShakeContainer>().First().Children.OfType<Container>().Single().Scale.X, target));
}
private bool checkSomeHit()
{
return Player.ScoreProcessor.JudgedHits >= 2;
}
}
}

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

@ -5,7 +5,6 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Batches; using osu.Framework.Graphics.Batches;
using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.OpenGL.Vertices;
@ -14,6 +13,7 @@ using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Layout;
using osu.Framework.Timing; using osu.Framework.Timing;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -43,6 +43,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
// -1 signals that the part is unusable, and should not be drawn // -1 signals that the part is unusable, and should not be drawn
parts[i].InvalidationID = -1; parts[i].InvalidationID = -1;
} }
AddLayout(partSizeCache);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -72,20 +74,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
} }
} }
private readonly Cached<Vector2> partSizeCache = new Cached<Vector2>(); private readonly LayoutValue<Vector2> partSizeCache = new LayoutValue<Vector2>(Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit | Invalidation.Presence);
private Vector2 partSize => partSizeCache.IsValid private Vector2 partSize => partSizeCache.IsValid
? partSizeCache.Value ? partSizeCache.Value
: (partSizeCache.Value = new Vector2(Texture.DisplayWidth, Texture.DisplayHeight) * DrawInfo.Matrix.ExtractScale().Xy); : (partSizeCache.Value = new Vector2(Texture.DisplayWidth, Texture.DisplayHeight) * DrawInfo.Matrix.ExtractScale().Xy);
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & (Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit | Invalidation.Presence)) > 0)
partSizeCache.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
/// <summary> /// <summary>
/// The amount of time to fade the cursor trail pieces. /// The amount of time to fade the cursor trail pieces.
/// </summary> /// </summary>
@ -97,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{ {
base.Update(); base.Update();
Invalidate(Invalidation.DrawNode, shallPropagate: false); Invalidate(Invalidation.DrawNode);
const int fade_clock_reset_threshold = 1000000; const int fade_clock_reset_threshold = 1000000;

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

@ -2,8 +2,8 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Layout;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.Taiko.UI;
@ -30,13 +30,15 @@ namespace osu.Game.Rulesets.Taiko.Mods
private class TaikoFlashlight : Flashlight private class TaikoFlashlight : Flashlight
{ {
private readonly Cached flashlightProperties = new Cached(); private readonly LayoutValue flashlightProperties = new LayoutValue(Invalidation.DrawSize);
private readonly TaikoPlayfield taikoPlayfield; private readonly TaikoPlayfield taikoPlayfield;
public TaikoFlashlight(TaikoPlayfield taikoPlayfield) public TaikoFlashlight(TaikoPlayfield taikoPlayfield)
{ {
this.taikoPlayfield = taikoPlayfield; this.taikoPlayfield = taikoPlayfield;
FlashlightSize = new Vector2(0, getSizeFor(0)); FlashlightSize = new Vector2(0, getSizeFor(0));
AddLayout(flashlightProperties);
} }
private float getSizeFor(int combo) private float getSizeFor(int combo)
@ -56,16 +58,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
protected override string FragmentShader => "CircularFlashlight"; protected override string FragmentShader => "CircularFlashlight";
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) > 0)
{
flashlightProperties.Invalidate();
}
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();

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,25 +13,24 @@ 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;
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()
{ {
ScoreAccessiblePlayer scoreAccessiblePlayer = null; AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
AddUntilStep("player loaded", () => (scoreAccessiblePlayer = (ScoreAccessiblePlayer)Player) != null); AddStep("seek to break time", () => Player.GameplayClockContainer.Seek(Player.BreakOverlay.Breaks.First().StartTime));
AddUntilStep("score above zero", () => scoreAccessiblePlayer.ScoreProcessor.TotalScore.Value > 0);
AddUntilStep("key counter counted keys", () => scoreAccessiblePlayer.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
AddStep("seek to break time", () => scoreAccessiblePlayer.GameplayClockContainer.Seek(scoreAccessiblePlayer.BreakOverlay.Breaks.First().StartTime));
AddUntilStep("wait for seek to complete", () => AddUntilStep("wait for seek to complete", () =>
scoreAccessiblePlayer.HUDOverlay.Progress.ReferenceClock.CurrentTime >= scoreAccessiblePlayer.BreakOverlay.Breaks.First().StartTime); Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= Player.BreakOverlay.Breaks.First().StartTime);
AddAssert("test keys not counting", () => !scoreAccessiblePlayer.HUDOverlay.KeyCounter.IsCounting); AddAssert("test keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
AddStep("rewind", () => scoreAccessiblePlayer.GameplayClockContainer.Seek(-80000)); AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
AddUntilStep("key counter reset", () => scoreAccessiblePlayer.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)
@ -41,19 +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 BreakOverlay BreakOverlay => base.BreakOverlay;
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

@ -62,14 +62,7 @@ namespace osu.Game.Tests.Visual.Navigation
var frameworkConfig = host.Dependencies.Get<FrameworkConfigManager>(); var frameworkConfig = host.Dependencies.Get<FrameworkConfigManager>();
frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity).Disabled = false; frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity).Disabled = false;
Game = new TestOsuGame(LocalStorage, API); CreateGame();
Game.SetHost(host);
// todo: this can be removed once we can run audio tracks without a device present
// see https://github.com/ppy/osu/issues/1302
Game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
Add(Game);
}); });
AddUntilStep("Wait for load", () => Game.IsLoaded); AddUntilStep("Wait for load", () => Game.IsLoaded);
@ -78,6 +71,18 @@ namespace osu.Game.Tests.Visual.Navigation
ConfirmAtMainMenu(); ConfirmAtMainMenu();
} }
protected void CreateGame()
{
Game = new TestOsuGame(LocalStorage, API);
Game.SetHost(host);
// todo: this can be removed once we can run audio tracks without a device present
// see https://github.com/ppy/osu/issues/1302
Game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
Add(Game);
}
protected void PushAndConfirm(Func<Screen> newScreen) protected void PushAndConfirm(Func<Screen> newScreen)
{ {
Screen screen = null; Screen screen = null;
@ -97,12 +102,17 @@ namespace osu.Game.Tests.Visual.Navigation
public new SettingsPanel Settings => base.Settings; public new SettingsPanel Settings => base.Settings;
public new MusicController MusicController => base.MusicController;
public new OsuConfigManager LocalConfig => base.LocalConfig; public new OsuConfigManager LocalConfig => base.LocalConfig;
public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap; public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap;
public new Bindable<RulesetInfo> Ruleset => base.Ruleset; public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
// if we don't do this, when running under nUnit the version that gets populated is that of nUnit.
public override string Version => "test game";
protected override Loader CreateLoader() => new TestLoader(); protected override Loader CreateLoader() => new TestLoader();
public new void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null) => base.PerformFromScreen(action, validScreens); public new void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null) => base.PerformFromScreen(action, validScreens);

View File

@ -114,6 +114,22 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("Options overlay was closed", () => Game.Settings.State.Value == Visibility.Hidden); AddAssert("Options overlay was closed", () => Game.Settings.State.Value == Visibility.Hidden);
} }
[Test]
public void TestWaitForNextTrackInMenu()
{
bool trackCompleted = false;
AddUntilStep("Wait for music controller", () => Game.MusicController.IsLoaded);
AddStep("Seek close to end", () =>
{
Game.MusicController.SeekTo(Game.Beatmap.Value.Track.Length - 1000);
Game.Beatmap.Value.Track.Completed += () => trackCompleted = true;
});
AddUntilStep("Track was completed", () => trackCompleted);
AddUntilStep("Track was restarted", () => Game.Beatmap.Value.Track.IsRunning);
}
private void pushEscape() => private void pushEscape() =>
AddStep("Press escape", () => pressAndRelease(Key.Escape)); AddStep("Press escape", () => pressAndRelease(Key.Escape));

View File

@ -0,0 +1,41 @@
// 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 NUnit.Framework;
using osu.Framework.Utils;
using osu.Game.Configuration;
namespace osu.Game.Tests.Visual.Navigation
{
public class TestSettingsMigration : OsuGameTestScene
{
public override void RecycleLocalStorage()
{
base.RecycleLocalStorage();
using (var config = new OsuConfigManager(LocalStorage))
{
config.Set(OsuSetting.Version, "2020.101.0");
config.Set(OsuSetting.DisplayStarsMaximum, 10.0);
}
}
[Test]
public void TestDisplayStarsMigration()
{
AddAssert("config has migrated value", () => Precision.AlmostEquals(Game.LocalConfig.Get<double>(OsuSetting.DisplayStarsMaximum), 10.1));
AddStep("set value again", () => Game.LocalConfig.Set<double>(OsuSetting.DisplayStarsMaximum, 10));
AddStep("force save config", () => Game.LocalConfig.Save());
AddStep("remove game", () => Remove(Game));
AddStep("create game again", CreateGame);
AddUntilStep("Wait for load", () => Game.IsLoaded);
AddAssert("config did not migrate value", () => Precision.AlmostEquals(Game.LocalConfig.Get<double>(OsuSetting.DisplayStarsMaximum), 10));
}
}
}

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

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking; using osu.Framework.Configuration.Tracking;
using osu.Framework.Extensions; using osu.Framework.Extensions;
@ -126,6 +127,35 @@ namespace osu.Game.Configuration
public OsuConfigManager(Storage storage) public OsuConfigManager(Storage storage)
: base(storage) : base(storage)
{ {
Migrate();
}
public void Migrate()
{
// arrives as 2020.123.0
var rawVersion = Get<string>(OsuSetting.Version);
if (rawVersion.Length < 6)
return;
var pieces = rawVersion.Split('.');
// on a fresh install or when coming from a non-release build, execution will end here.
// we don't want to run migrations in such cases.
if (!int.TryParse(pieces[0], out int year)) return;
if (!int.TryParse(pieces[1], out int monthDay)) return;
int combined = (year * 10000) + monthDay;
if (combined < 20200305)
{
// the maximum value of this setting was changed.
// if we don't manually increase this, it causes song select to filter out beatmaps the user expects to see.
var maxStars = (BindableDouble)GetOriginalBindable<double>(OsuSetting.DisplayStarsMaximum);
if (maxStars.Value == 10)
maxStars.Value = maxStars.MaxValue;
}
} }
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings public override TrackedSettings CreateTrackedSettings() => new TrackedSettings

View File

@ -129,7 +129,7 @@ namespace osu.Game.Graphics.Backgrounds
{ {
base.Update(); base.Update();
Invalidate(Invalidation.DrawNode, shallPropagate: false); Invalidate(Invalidation.DrawNode);
if (CreateNewTriangles) if (CreateNewTriangles)
addTriangles(false); addTriangles(false);

View File

@ -6,6 +6,7 @@ using System.Linq;
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.Layout;
namespace osu.Game.Graphics.Containers namespace osu.Game.Graphics.Containers
{ {
@ -142,15 +143,17 @@ namespace osu.Game.Graphics.Containers
public void ScrollToTop() => scrollContainer.ScrollTo(0); public void ScrollToTop() => scrollContainer.ScrollTo(0);
public override void InvalidateFromChild(Invalidation invalidation, Drawable source = null) protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
{ {
base.InvalidateFromChild(invalidation, source); var result = base.OnInvalidate(invalidation, source);
if ((invalidation & Invalidation.DrawSize) != 0) if (source == InvalidationSource.Child && (invalidation & Invalidation.DrawSize) != 0)
{ {
if (source == ExpandableHeader) //We need to recalculate the positions if the ExpandableHeader changed its size lastKnownScroll = -1;
lastKnownScroll = -1; result = true;
} }
return result;
} }
private float lastKnownScroll; private float lastKnownScroll;

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

@ -13,17 +13,17 @@ namespace osu.Game.Graphics.UserInterface
/// </summary> /// </summary>
public class ExpandingBar : Circle public class ExpandingBar : Circle
{ {
private bool isCollapsed; private bool expanded = true;
public bool IsCollapsed public bool Expanded
{ {
get => isCollapsed; get => expanded;
set set
{ {
if (value == isCollapsed) if (value == expanded)
return; return;
isCollapsed = value; expanded = value;
updateState(); updateState();
} }
} }
@ -83,19 +83,21 @@ namespace osu.Game.Graphics.UserInterface
updateState(); updateState();
} }
public void Collapse() => IsCollapsed = true; public void Collapse() => Expanded = false;
public void Expand() => IsCollapsed = false; public void Expand() => Expanded = true;
private void updateState() private void updateState()
{ {
float newSize = IsCollapsed ? CollapsedSize : ExpandedSize; float newSize = expanded ? ExpandedSize : CollapsedSize;
Easing easingType = IsCollapsed ? Easing.Out : Easing.OutElastic; Easing easingType = expanded ? Easing.OutElastic : Easing.Out;
if (RelativeSizeAxes == Axes.X) if (RelativeSizeAxes == Axes.X)
this.ResizeHeightTo(newSize, 400, easingType); this.ResizeHeightTo(newSize, 400, easingType);
else else
this.ResizeWidthTo(newSize, 400, easingType); this.ResizeWidthTo(newSize, 400, easingType);
this.FadeTo(expanded ? 1 : 0.5f, 100, Easing.OutQuint);
} }
} }
} }

View File

@ -4,11 +4,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Caching;
using osuTK; using osuTK;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Lines;
using osu.Framework.Layout;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
@ -83,17 +83,11 @@ namespace osu.Game.Graphics.UserInterface
PathRadius = 1 PathRadius = 1
} }
}); });
AddLayout(pathCached);
} }
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) private readonly LayoutValue pathCached = new LayoutValue(Invalidation.DrawSize);
{
if ((invalidation & Invalidation.DrawSize) > 0)
pathCached.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
private readonly Cached pathCached = new Cached();
protected override void Update() protected override void Update()
{ {

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;
@ -42,6 +43,7 @@ using osu.Game.Overlays.Volume;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Game.Updater;
using osu.Game.Utils; using osu.Game.Utils;
using LogLevel = osu.Framework.Logging.LogLevel; using LogLevel = osu.Framework.Logging.LogLevel;
@ -389,24 +391,35 @@ namespace osu.Game
protected virtual Loader CreateLoader() => new Loader(); protected virtual Loader CreateLoader() => new Loader();
protected virtual UpdateManager CreateUpdateManager() => new UpdateManager();
protected override Container CreateScalingContainer() => new ScalingContainer(ScalingMode.Everything); protected override Container CreateScalingContainer() => new ScalingContainer(ScalingMode.Everything);
#region Beatmap progression #region Beatmap progression
private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> beatmap) private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> beatmap)
{ {
var nextBeatmap = beatmap.NewValue; beatmap.OldValue?.CancelAsyncLoad();
if (nextBeatmap?.Track != null)
nextBeatmap.Track.Completed += currentTrackCompleted;
var oldBeatmap = beatmap.OldValue;
if (oldBeatmap?.Track != null)
oldBeatmap.Track.Completed -= currentTrackCompleted;
updateModDefaults(); updateModDefaults();
oldBeatmap?.CancelAsyncLoad(); var newBeatmap = beatmap.NewValue;
nextBeatmap?.BeginAsyncLoad();
if (newBeatmap != null)
{
newBeatmap.Track.Completed += () => Scheduler.AddOnce(() => trackCompleted(newBeatmap));
newBeatmap.BeginAsyncLoad();
}
void trackCompleted(WorkingBeatmap b)
{
// the source of track completion is the audio thread, so the beatmap may have changed before firing.
if (Beatmap.Value != b)
return;
if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled)
MusicController.NextTrack();
}
} }
private void modsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods) private void modsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
@ -427,12 +440,6 @@ namespace osu.Game
} }
} }
private void currentTrackCompleted() => Schedule(() =>
{
if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled)
musicController.NextTrack();
});
#endregion #endregion
private ScheduledDelegate performFromMainMenuTask; private ScheduledDelegate performFromMainMenuTask;
@ -584,7 +591,7 @@ namespace osu.Game
loadComponentSingleFile(new OnScreenDisplay(), Add, true); loadComponentSingleFile(new OnScreenDisplay(), Add, true);
loadComponentSingleFile(musicController = new MusicController(), Add, true); loadComponentSingleFile(MusicController = new MusicController(), Add, true);
loadComponentSingleFile(notifications = new NotificationOverlay loadComponentSingleFile(notifications = new NotificationOverlay
{ {
@ -627,6 +634,7 @@ namespace osu.Game
chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible; chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible;
Add(externalLinkOpener = new ExternalLinkOpener()); Add(externalLinkOpener = new ExternalLinkOpener());
Add(CreateUpdateManager()); // dependency on notification overlay
// side overlays which cancel each other. // side overlays which cancel each other.
var singleDisplaySideOverlays = new OverlayContainer[] { Settings, notifications }; var singleDisplaySideOverlays = new OverlayContainer[] { Settings, notifications };
@ -805,6 +813,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;
@ -885,7 +900,7 @@ namespace osu.Game
private ScalingContainer screenContainer; private ScalingContainer screenContainer;
private MusicController musicController; protected MusicController MusicController { get; private set; }
protected override bool OnExiting() protected override bool OnExiting()
{ {
@ -943,7 +958,7 @@ namespace osu.Game
{ {
OverlayActivationMode.Value = newOsuScreen.InitialOverlayActivationMode; OverlayActivationMode.Value = newOsuScreen.InitialOverlayActivationMode;
musicController.AllowRateAdjustments = newOsuScreen.AllowRateAdjustments; MusicController.AllowRateAdjustments = newOsuScreen.AllowRateAdjustments;
if (newOsuScreen.HideOverlaysOnEnter) if (newOsuScreen.HideOverlaysOnEnter)
CloseAllOverlays(); CloseAllOverlays();

View File

@ -97,7 +97,7 @@ namespace osu.Game
public bool IsDeployedBuild => AssemblyVersion.Major > 0; public bool IsDeployedBuild => AssemblyVersion.Major > 0;
public string Version public virtual string Version
{ {
get get
{ {
@ -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();
@ -206,6 +211,10 @@ namespace osu.Game
Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap); Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap);
// ScheduleAfterChildren is safety against something in the current frame accessing the previous beatmap's track
// and potentially causing a reload of it after just unloading.
// Note that the reason for this being added *has* been resolved, so it may be feasible to removed this if required.
Beatmap.BindValueChanged(b => ScheduleAfterChildren(() => Beatmap.BindValueChanged(b => ScheduleAfterChildren(() =>
{ {
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)

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

@ -4,9 +4,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
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.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
@ -22,6 +24,8 @@ namespace osu.Game.Overlays.Changelog
private const string listing_string = "listing"; private const string listing_string = "listing";
private Box streamsBackground;
public ChangelogHeader() public ChangelogHeader()
{ {
TabControl.AddItem(listing_string); TabControl.AddItem(listing_string);
@ -40,6 +44,12 @@ namespace osu.Game.Overlays.Changelog
}; };
} }
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
streamsBackground.Colour = colourProvider.Background5;
}
private ChangelogHeaderTitle title; private ChangelogHeaderTitle title;
private void showBuild(ValueChangedEvent<APIChangelogBuild> e) private void showBuild(ValueChangedEvent<APIChangelogBuild> e)
@ -72,7 +82,21 @@ namespace osu.Game.Overlays.Changelog
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Children = new Drawable[] Children = new Drawable[]
{ {
Streams = new UpdateStreamBadgeArea(), streamsBackground = new Box
{
RelativeSizeAxes = Axes.Both
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Horizontal = 65,
Vertical = 20
},
Child = Streams = new UpdateStreamBadgeArea()
}
} }
}; };

View File

@ -26,7 +26,6 @@ namespace osu.Game.Overlays.Changelog
private readonly APIUpdateStream stream; private readonly APIUpdateStream stream;
private Container fadeContainer;
private FillFlowContainer<SpriteText> text; private FillFlowContainer<SpriteText> text;
private ExpandingBar expandingBar; private ExpandingBar expandingBar;
@ -44,47 +43,39 @@ namespace osu.Game.Overlays.Changelog
AddRange(new Drawable[] AddRange(new Drawable[]
{ {
fadeContainer = new Container text = new FillFlowContainer<SpriteText>
{ {
RelativeSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Children = new Drawable[] Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 6 },
Children = new[]
{ {
text = new FillFlowContainer<SpriteText> new OsuSpriteText
{ {
AutoSizeAxes = Axes.X, Text = stream.DisplayName,
RelativeSizeAxes = Axes.Y, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black),
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 6 },
Children = new[]
{
new OsuSpriteText
{
Text = stream.DisplayName,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black),
},
new OsuSpriteText
{
Text = stream.LatestBuild.DisplayVersion,
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular),
},
new OsuSpriteText
{
Text = stream.LatestBuild.Users > 0 ? $"{"user".ToQuantity(stream.LatestBuild.Users, "N0")} online" : null,
Font = OsuFont.GetFont(size: 10),
Colour = colourProvider.Foreground1
},
}
}, },
expandingBar = new ExpandingBar new OsuSpriteText
{ {
Anchor = Anchor.TopCentre, Text = stream.LatestBuild.DisplayVersion,
Colour = stream.Colour, Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular),
ExpandedSize = 4, },
CollapsedSize = 2, new OsuSpriteText
IsCollapsed = true {
Text = stream.LatestBuild.Users > 0 ? $"{"user".ToQuantity(stream.LatestBuild.Users, "N0")} online" : null,
Font = OsuFont.GetFont(size: 10),
Colour = colourProvider.Foreground1
}, },
} }
}, },
expandingBar = new ExpandingBar
{
Anchor = Anchor.TopCentre,
Colour = stream.Colour,
ExpandedSize = 4,
CollapsedSize = 2,
Expanded = true
},
new HoverClickSounds() new HoverClickSounds()
}); });
@ -109,38 +100,41 @@ namespace osu.Game.Overlays.Changelog
private void updateState() private void updateState()
{ {
// Expand based on the local state // highlighted regardless if we are hovered
bool shouldExpand = Active.Value || IsHovered; bool textHighlighted = IsHovered;
bool barExpanded = IsHovered;
// Expand based on whether no build is selected and the badge area is hovered if (SelectedTab.Value == null)
shouldExpand |= SelectedTab.Value == null && !externalDimRequested;
if (shouldExpand)
{ {
expandingBar.Expand(); // at listing, all badges are highlighted when user is not hovering any badge.
fadeContainer.FadeTo(1, transition_duration); textHighlighted |= !userHoveringArea;
barExpanded |= !userHoveringArea;
} }
else else
{ {
expandingBar.Collapse(); // bar is always expanded when active
fadeContainer.FadeTo(0.5f, transition_duration); barExpanded |= Active.Value;
// text is highlighted only when hovered or active (but not if in selection mode)
textHighlighted |= Active.Value && !userHoveringArea;
} }
text.FadeTo(externalDimRequested && !IsHovered ? 0.5f : 1, transition_duration); expandingBar.Expanded = barExpanded;
text.FadeTo(textHighlighted ? 1 : 0.5f, transition_duration, Easing.OutQuint);
} }
private bool externalDimRequested; private bool userHoveringArea;
public void EnableDim() public bool UserHoveringArea
{ {
externalDimRequested = true; set
updateState(); {
} if (value == userHoveringArea)
return;
public void DisableDim() userHoveringArea = value;
{ updateState();
externalDimRequested = false; }
updateState();
} }
} }
} }

View File

@ -6,25 +6,16 @@ using osu.Framework.Input.Events;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.Changelog namespace osu.Game.Overlays.Changelog
{ {
public class UpdateStreamBadgeArea : TabControl<APIUpdateStream> public class UpdateStreamBadgeArea : TabControl<APIUpdateStream>
{ {
[BackgroundDependencyLoader] public UpdateStreamBadgeArea()
private void load(OverlayColourProvider colourProvider)
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
AddInternal(new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background5,
});
} }
public void Populate(List<APIUpdateStream> streams) public void Populate(List<APIUpdateStream> streams)
@ -36,7 +27,7 @@ namespace osu.Game.Overlays.Changelog
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)
{ {
foreach (var streamBadge in TabContainer.Children.OfType<UpdateStreamBadge>()) foreach (var streamBadge in TabContainer.Children.OfType<UpdateStreamBadge>())
streamBadge.EnableDim(); streamBadge.UserHoveringArea = true;
return base.OnHover(e); return base.OnHover(e);
} }
@ -44,26 +35,17 @@ namespace osu.Game.Overlays.Changelog
protected override void OnHoverLost(HoverLostEvent e) protected override void OnHoverLost(HoverLostEvent e)
{ {
foreach (var streamBadge in TabContainer.Children.OfType<UpdateStreamBadge>()) foreach (var streamBadge in TabContainer.Children.OfType<UpdateStreamBadge>())
streamBadge.DisableDim(); streamBadge.UserHoveringArea = false;
base.OnHoverLost(e); base.OnHoverLost(e);
} }
protected override TabFillFlowContainer CreateTabFlow() protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{ {
var flow = base.CreateTabFlow(); RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
flow.RelativeSizeAxes = Axes.X; AllowMultiline = true,
flow.AutoSizeAxes = Axes.Y; };
flow.AllowMultiline = true;
flow.Padding = new MarginPadding
{
Vertical = 20,
Horizontal = 85,
};
return flow;
}
protected override Dropdown<APIUpdateStream> CreateDropdown() => null; protected override Dropdown<APIUpdateStream> CreateDropdown() => null;

View File

@ -80,7 +80,7 @@ namespace osu.Game.Overlays.SearchableList
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 }, Padding = new MarginPadding { Horizontal = 10, Bottom = 50 },
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
}, },
}, },

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

@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Caching; using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Layout;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -19,11 +20,13 @@ namespace osu.Game.Rulesets.UI.Scrolling
[Resolved] [Resolved]
private IScrollingInfo scrollingInfo { get; set; } private IScrollingInfo scrollingInfo { get; set; }
private readonly Cached initialStateCache = new Cached(); private readonly LayoutValue initialStateCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo);
public ScrollingHitObjectContainer() public ScrollingHitObjectContainer()
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
AddLayout(initialStateCache);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -55,14 +58,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
return result; return result;
} }
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & (Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo)) > 0)
initialStateCache.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
private float scrollLength; private float scrollLength;
protected override void Update() protected override void Update()

View File

@ -3,10 +3,10 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Layout;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osuTK; using osuTK;
@ -51,7 +51,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
[Resolved] [Resolved]
private BindableBeatDivisor beatDivisor { get; set; } private BindableBeatDivisor beatDivisor { get; set; }
private readonly Cached gridCache = new Cached(); private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
private readonly double? endTime; private readonly double? endTime;
/// <summary> /// <summary>
@ -67,6 +67,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
StartTime = startTime; StartTime = startTime;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
AddLayout(gridCache);
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -92,14 +94,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
gridCache.Invalidate(); gridCache.Invalidate();
} }
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.RequiredParentSizeToFit) > 0)
gridCache.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();

View File

@ -150,7 +150,7 @@ namespace osu.Game.Screens.Menu
frequencyAmplitudes[i] = 0; frequencyAmplitudes[i] = 0;
} }
Invalidate(Invalidation.DrawNode, shallPropagate: false); Invalidate(Invalidation.DrawNode);
} }
protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this); protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this);

View File

@ -246,10 +246,11 @@ namespace osu.Game.Screens.Multi
FillMode = FillMode.Fill, FillMode = FillMode.Fill,
Beatmap = { BindTarget = Beatmap } Beatmap = { BindTarget = Beatmap }
}, },
new Container new FillFlowContainer
{ {
Depth = -1, Depth = -1,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
// This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle // This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle
Shear = new Vector2(0.8f, 0), Shear = new Vector2(0.8f, 0),
Alpha = 0.5f, Alpha = 0.5f,
@ -259,7 +260,6 @@ namespace osu.Game.Screens.Multi
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = Color4.Black, Colour = Color4.Black,
Width = 0.4f, Width = 0.4f,
}, },
@ -267,26 +267,20 @@ namespace osu.Game.Screens.Multi
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(Color4.Black, new Color4(0f, 0f, 0f, 0.9f)), Colour = ColourInfo.GradientHorizontal(Color4.Black, new Color4(0f, 0f, 0f, 0.9f)),
Width = 0.05f, Width = 0.05f,
X = 0.4f,
}, },
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.9f), new Color4(0f, 0f, 0f, 0.1f)), Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.9f), new Color4(0f, 0f, 0f, 0.1f)),
Width = 0.2f, Width = 0.2f,
X = 0.45f,
}, },
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.1f), new Color4(0, 0, 0, 0)), Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.1f), new Color4(0, 0, 0, 0)),
Width = 0.05f, Width = 0.05f,
X = 0.65f,
}, },
} }
} }

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

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using osu.Framework; using osu.Framework;
using osu.Framework.Caching;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -14,6 +13,7 @@ using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Layout;
using osu.Framework.Threading; using osu.Framework.Threading;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
@ -22,6 +22,11 @@ namespace osu.Game.Screens.Play
{ {
private BufferedContainer<Column> columns; private BufferedContainer<Column> columns;
public SquareGraph()
{
AddLayout(layout);
}
public int ColumnCount => columns?.Children.Count ?? 0; public int ColumnCount => columns?.Children.Count ?? 0;
private int progress; private int progress;
@ -68,14 +73,7 @@ namespace osu.Game.Screens.Play
} }
} }
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) private readonly LayoutValue layout = new LayoutValue(Invalidation.DrawSize);
{
if ((invalidation & Invalidation.DrawSize) > 0)
layout.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
private readonly Cached layout = new Cached();
private ScheduledDelegate scheduledCreate; private ScheduledDelegate scheduledCreate;
protected override void Update() protected override void Update()

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

@ -159,11 +159,11 @@ namespace osu.Game.Screens.Select.Carousel
Origin = Anchor.Centre, Origin = Anchor.Centre,
FillMode = FillMode.Fill, FillMode = FillMode.Fill,
}, },
// Todo: This should be a fill flow, but has invalidation issues (see https://github.com/ppy/osu-framework/issues/223) new FillFlowContainer
new Container
{ {
Depth = -1, Depth = -1,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
// This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle // This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle
Shear = new Vector2(0.8f, 0), Shear = new Vector2(0.8f, 0),
Alpha = 0.5f, Alpha = 0.5f,
@ -173,7 +173,6 @@ namespace osu.Game.Screens.Select.Carousel
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = Color4.Black, Colour = Color4.Black,
Width = 0.4f, Width = 0.4f,
}, },
@ -181,26 +180,20 @@ namespace osu.Game.Screens.Select.Carousel
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(Color4.Black, new Color4(0f, 0f, 0f, 0.9f)), Colour = ColourInfo.GradientHorizontal(Color4.Black, new Color4(0f, 0f, 0f, 0.9f)),
Width = 0.05f, Width = 0.05f,
X = 0.4f,
}, },
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.9f), new Color4(0f, 0f, 0f, 0.1f)), Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.9f), new Color4(0f, 0f, 0f, 0.1f)),
Width = 0.2f, Width = 0.2f,
X = 0.45f,
}, },
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.1f), new Color4(0, 0, 0, 0)), Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.1f), new Color4(0, 0, 0, 0)),
Width = 0.05f, Width = 0.05f,
X = 0.65f,
}, },
} }
}, },

Some files were not shown because too many files have changed in this diff Show More